Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

get_epsilon_grid function for evaluating ε on user-specified grid #1522

Merged
merged 7 commits into from
Mar 18, 2021

Conversation

oskooi
Copy link
Collaborator

@oskooi oskooi commented Mar 8, 2021

Closes #1509. Follows the outline suggested by @stevengj in #1509 (comment). Also includes documentation.

This feature is mostly working as demonstrated in the results shown below involving a cylinder as a MaterialGrid (with the projection step turned off via beta = 0) at a resolution of 50 which is sampled at three different resolutions (20, 50, and 100).

Ideally, Simulation.init_sim() should not be necessary (as demonstrated in the example below) since get_material_pt just requires a geom_epsilon object which itself does not involve initializing the Yee grid via structure::set_materials. However, whenever the geometry contains Prism objects (including those loaded from a GDS file), it turns out that Simulation.init_sim() is necessary otherwise there is a segmentation fault due to uninitialized materials. Also, this PR does not seem to work when the geometry consists of GeometricObjects such as Cylinders, etc. It doesn't seem to be obvious why this is the case. This PR should therefore not be merged until these two issues are resolved.

matgrid_eps_grid

import numpy as np
from scipy.ndimage import gaussian_filter
import meep as mp
import matplotlib
matplotlib.use('agg')
import matplotlib.pyplot as plt

cell_size = mp.Vector3(1,1,0)

rad = 0.301943

design_shape = mp.Vector3(1,1,0)
design_region_resolution = 20
Nx = int(design_region_resolution*design_shape.x)
Ny = int(design_region_resolution*design_shape.y)
x = np.linspace(-0.5*cell_size.x,0.5*cell_size.x,Nx)
y = np.linspace(-0.5*cell_size.y,0.5*cell_size.y,Ny)
xv, yv = np.meshgrid(x,y)
design_params = np.sqrt(np.square(xv) + np.square(yv)) < rad
filtered_design_params = gaussian_filter(design_params, sigma=3.0, output=np.double)

matgrid = mp.MaterialGrid(mp.Vector3(Nx,Ny),
                          mp.air,
                          mp.Medium(index=3.5),
                          weights=design_params,
                          do_averaging=True,
                          beta=0,
                          eta=0.5)

geometry = [mp.Block(center=mp.Vector3(),
                     size=mp.Vector3(design_shape.x,design_shape.y,0),
                     material=matgrid)]

sim = mp.Simulation(resolution=20,
                    cell_size=cell_size,
                    geometry=geometry)

x = np.linspace(-0.5*cell_size.x,0.5*cell_size.x,500)
y = np.linspace(-0.5*cell_size.y,0.5*cell_size.y,500)
z = np.array([0])
eps_grid = sim.get_epsilon_grid(x,y,z)
plt.figure()
plt.pcolor(x,y,eps_grid,cmap='Blues')
plt.gca().axis('equal')
plt.gca().set_aspect('equal','box')
plt.title("sampling {} x {} MaterialGrid using {} x {} grid".format(Nx,Ny,len(x),len(y)))
plt.savefig('matgrid_eps_grid_nx{}_ny{}.png'.format(len(x),len(y)),dpi=120,bbox_inches='tight')

@smartalecH
Copy link
Collaborator

smartalecH commented Mar 8, 2021

Odd, I'm getting a segfault while running your example script. Is your local build based off master?

Got it working.

@smartalecH
Copy link
Collaborator

If you pass a list of normal geometries without a material grid, you get a segfault. gdb stops at meepgeom.cpp:702.

Seems like there's a problem with the material not being assigned the proper type after it traverses the tree at meepgeom.cpp:699. I had similar issues when debugging the gradient feature.

Make sure the geometry list is properly saved and the right tree is stored when you call geom_epsilon::chi1p1. IIRC, I had to create another tree, even though I was using the same meep_geom object as before (see meepgeom:2453).

@oskooi
Copy link
Collaborator Author

oskooi commented Mar 9, 2021

Thanks for looking into this. I had also used gdb for the failing test involving a Cylinder (shown at the very bottom) to identify the segmentation fault stemming from the NULL pointer returned on:

meep/src/meepgeom.cpp

Lines 698 to 699 in 1d59e6b

material =
(material_type)material_of_unshifted_point_in_tree_inobject(p, restricted_tree, &inobject);

In gdb, I investigated the properties of the restricted_tree parameter which is passed to the function material_of_unshifted_point_in_tree_inobject and discovered that its two branches t1 and t2 are both NULL which is unexpected.

For reference, the geom_box_tree struct (the type for the geom_epsilon member objects restricted_tree and geometry_tree) is defined in libctl/utils/ctlgeom.h:

https://github.com/NanoComp/libctl/blob/aa56a410f33fb2fd80605faf35dfa7906785edef/utils/ctlgeom.h#L107-L112

The strange thing is: why does this PR work with a MaterialGrid and a material function (shown below for an example involving a ring resonator) but not for a GeometricObject (i.e., Cylinder, Prism, etc.). What is it about the geometry tree that is different in these two cases?

matfunc_eps_grid

import meep as mp
import numpy as np
import matplotlib
matplotlib.use('agg')
import matplotlib.pyplot as plt

n = 3.4                 # index of waveguide
w = 1                   # width of waveguide
r = 1                   # inner radius of ring
pad = 4                 # padding between waveguide and edge of PML
dpml = 2                # thickness of PML
sxy = 2*(r+w+pad+dpml)  # cell size

def ring_resonator(p):
    rr = (p.x**2+p.y**2)**0.5
    if (rr > r) and (rr < r+w):
        return mp.Medium(index=n)
    return mp.air

geometry = [mp.Block(center=mp.Vector3(),
                     size=mp.Vector3(sxy,sxy),
                     material=ring_resonator)]

sim = mp.Simulation(cell_size=mp.Vector3(sxy,sxy),
                    geometry=geometry,
                    resolution=20)

cell_size = mp.Vector3(sxy,sxy,0)
x = np.linspace(-3,3,50)
y = np.linspace(-3,3,50)
z = np.array([0])
eps_grid = sim.get_epsilon_grid(x,y,z)
plt.figure()
plt.pcolor(x,y,eps_grid,cmap='Blues')
plt.gca().axis('equal')
plt.gca().set_aspect('equal','box')
plt.title("sampling material function using {} x {} grid".format(len(x),len(x)))
plt.savefig('ring_matfunc_eps_grid_nx{}_ny{}.png'.format(len(x),len(y)),dpi=120,bbox_inches='tight')

For reference, the test which produces the segmentation fault is:

import meep as mp
import numpy as np
import matplotlib
matplotlib.use('agg')
import matplotlib.pyplot as plt

mp.verbosity(3)

n = 3.4                 # index of waveguide
w = 1                   # width of waveguide
r = 1                   # inner radius of ring 
pad = 4                 # padding between waveguide and edge of PML
dpml = 2                # thickness of PML 
sxy = 2*(r+w+pad+dpml)  # cell size    

c1 = mp.Cylinder(radius=r+w,
                 center=mp.Vector3(),
                 material=mp.Medium(index=n))
c2 = mp.Cylinder(radius=r,
                 center=mp.Vector3(),
                 material=mp.air)

sim = mp.Simulation(cell_size=mp.Vector3(sxy, sxy),
                    geometry=[c1, c2],
                    resolution=40)

x = np.linspace(-0.5*sxy,0.5*sxy,50)
y = np.linspace(-0.5*sxy,0.5*sxy,50)
z = np.array([0])

## triggers segmentation fault
eps_grid = sim.get_epsilon_grid(x,y,z)

@oskooi
Copy link
Collaborator Author

oskooi commented Mar 10, 2021

Output from gdb from the segmentation fault:

Thread 1 "python3.5" received signal SIGSEGV, Segmentation fault.
0x00007ffff5d2d58a in meep_geom::geom_epsilon::get_material_pt (this=0x7fffffffd3b0, material=@0x7fffffffd1d8: 0x0, r=...) at meepgeom.cpp:702
702	  switch (md->which_subclass) {
(gdb) p restricted_tree
$1 = (geom_box_tree) 0x17b5540
(gdb) list
697	  boolean inobject;
698	  material =
699	      (material_type)material_of_unshifted_point_in_tree_inobject(p, restricted_tree, &inobject);
700	  material_data *md = material;
701	
702	  switch (md->which_subclass) {
703	    // material grid: interpolate onto user specified material grid to get properties at r
704	    case material_data::MATERIAL_GRID:
705	      meep::realnum u;
706	      int oi;
(gdb) p material
$2 = (meep_geom::material_type &) @0x7fffffffd1d8: 0x0
(gdb) p *material
Cannot access memory at address 0x0
(gdb) p restricted_tree
$3 = (geom_box_tree) 0x17b5540
(gdb) p *restricted_tree
$4 = {b = {low = {x = -8, y = -8, z = 6.9533077760808062e-310}, high = {x = 8, y = 8, z = 6.9533558072682122e-310}}, b1 = {low = {x = 6.9533060868940782e-310, 
      y = 6.9533060868976355e-310, z = 6.9533060869011928e-310}, high = {x = 6.9533060869047501e-310, y = 6.9533060869083073e-310, z = 6.9533060869118646e-310}}, b2 = {low = {
      x = 6.9533060869185839e-310, y = 6.9533060869221412e-310, z = 6.9533060869256984e-310}, high = {x = 6.9533060868798491e-310, y = 0, z = 0}}, t1 = 0x0, t2 = 0x0, 
  nobjects = 2, objects = 0x13dd600}
(gdb) p p
$5 = {x = -8, y = -8, z = 0}
(gdb) p default_material
$6 = (void *) 0x0
(gdb) p in_object
No symbol "in_object" in current context.
(gdb) p inobject
$7 = 0

@stevengj
Copy link
Collaborator

stevengj commented Mar 10, 2021

Similar to set_materials_from_geometry, you need to pass (and set) the libctl global variables default_material, ensure_periodicity, geometry_lattice, and you should probably also call set_dimensions(sim_dims) .... best to refactor set_materials_from_geometry and pull out an init_geometry(_default_material, _ensure_periodicity, user_volume) function

@stevengj
Copy link
Collaborator

Or just factor fragment_stats::init_libctl out of the fragment_stats class.

@smartalecH
Copy link
Collaborator

Once we debug the issues with the default_material, there is one more (sneaky) bug in the main material grid routine:

meep/src/meepgeom.cpp

Lines 325 to 351 in 32788d7

if (tp) {
do {
u = material_grid_val(to_geom_box_coords(p, &tp->objects[oi]),
(material_data *)tp->objects[oi].o->material);
if (matgrid_val_count == 0) udefault = u;
if (u < umin) umin = u;
uprod *= u;
usum += u;
++matgrid_val_count;
tp = geom_tree_search_next(p, tp, &oi);
} while (tp && is_material_grid((material_data *)tp->objects[oi].o->material));
}
// perhaps there is not object tree and the default material is a material grid
if (!tp && is_material_grid(&default_material)) {
p.x = geometry_lattice.size.x == 0 ? 0
: 0.5 + (p.x - geometry_center.x) / geometry_lattice.size.x;
p.y = geometry_lattice.size.y == 0 ? 0
: 0.5 + (p.y - geometry_center.y) / geometry_lattice.size.y;
p.z = geometry_lattice.size.z == 0 ? 0
: 0.5 + (p.z - geometry_center.z) / geometry_lattice.size.z;
u = material_grid_val(p, (material_data *)default_material);
if (matgrid_val_count == 0) udefault = u;
if (u < umin) umin = u;
uprod *= u;
usum += u;
++matgrid_val_count;
}

This piece of code is called whenever there is a material grid present in a tree. The first if statement traverses through the tree and will interpolate as needed. The second if statement is a special case, which is called if the tree is done being traversed and the default_material is a material grid.

We need to account for the final case, where the current point doesn't have any material grids (e.g. it could be another medium or the default_material).

it will require some debugging and trial and error.

@oskooi
Copy link
Collaborator Author

oskooi commented Mar 11, 2021

I tried refactoring fragment_stats::init_libctl out of the fragment_stats class and while there is no longer a segmentation fault occurring for the test case involving Cylinders, the return value from geps.chi1p1(meep::E_stuff, meep::vec(x[i],y[j],z[k])) in get_epsilon_grid is for some reason always 1.0 (i.e., the Cylinder object with n=3.4 is just ignored). It's not obvious why this is happening.

The main parts of the diff are:

--- a/python/simulation.py
+++ b/python/simulation.py
@@ -2102,7 +2102,13 @@ class Simulation(object):
         equivalent to `numpy.meshgrid(xtics,ytics,ztics)`. Empty dimensions are collapsed.
         """
         grid_vals = np.squeeze(np.empty((len(xtics), len(ytics), len(ztics))))
-        mp._get_epsilon_grid(self.geometry, self.extra_materials,
+        gv = self._create_grid_volume(False)
+        mp._get_epsilon_grid(self.geometry,
+                             self.extra_materials,
+                             self.default_material,
+                             self.ensure_periodicity and not not self.k_point,
+                             gv,
+                             self.cell_size, self.geometry_center,
                              len(xtics), xtics,
                              len(ytics), ytics,
                              len(ztics), ztics,
--- a/src/meepgeom.cpp
+++ b/src/meepgeom.cpp
 
 void get_epsilon_grid(geometric_object_list gobj_list,
                       material_type_list mlist,
+                      material_type _default_material,
+                      bool _ensure_periodicity,
+                      meep::grid_volume gv,
+                      vector3 cell_size,
+                      vector3 cell_center,
                       int nx, double *x,
                       int ny, double *y,
                       int nz, double *z,
@@ -2595,6 +2600,8 @@ void get_epsilon_grid(geometric_object_list gobj_list,
   }
   const meep::volume vol(meep::vec(min_val[0],min_val[1],min_val[2]),
                          meep::vec(max_val[0],max_val[1],max_val[2]));
+  init_libctl(_default_material, _ensure_periodicity, &gv,
+              cell_size, cell_center, &gobj_list);
   geom_epsilon geps(gobj_list, mlist, vol);
   for (int i = 0; i < nx; ++i)
     for (int j = 0; j < ny; ++j)

Separately (but perhaps related), I noticed a (minor?) difference in the function init_libctl compared to set_materials_from_geometry:

set_materials_from_geometry sets the global libctl variable dim via:

set_dimensions(sim_dims);

However, init_libctl sets the global libctl variable dimension via:

dimensions = meep::number_of_directions(gv->dim);

@oskooi
Copy link
Collaborator Author

oskooi commented Mar 11, 2021

Actually, this seems to be working now. The fix involved only a single change: within the function get_epsilon_grid, the creation of the geom_epsilon object geps required passing as the volume argument to the constructor not vol (the volume enclosing the user-specified region) but rather gv.pad().surroundings() (the entire cell volume):

  init_libctl(_default_material, _ensure_periodicity, &gv,
              cell_size, cell_center, &gobj_list);
  geom_epsilon geps(gobj_list, mlist, gv.pad().surroundings());

This is similar to the geom_epsilon object in set_materials_from_geometry:

geom_epsilon geps(g, extra_materials, gv.pad().surroundings());

The test case involving the Cylinder objects works as expected:

ring_cyl_grid

@oskooi oskooi changed the title WIP: get_epsilon_grid function for evaluating ε on user-specified grid get_epsilon_grid function for evaluating ε on user-specified grid Mar 11, 2021
@oskooi oskooi requested a review from smartalecH March 11, 2021 06:58
src/meepgeom.cpp Outdated Show resolved Hide resolved
@oskooi
Copy link
Collaborator Author

oskooi commented Mar 17, 2021

If I revert to passing the volume of only the user-specified subregion to the geom_epsilon constructor (rather than the entire volume), the test case involving the GeometricObjects works fine but the MaterialGrid test case returns all 1 values from get_epsilon_grid

+std::complex<double> find_array_min_max(int n, const double *data) {
+  double min_val = data[0], max_val = data[0];
+  for (int i = 1; i < n; ++i) {
+    if (data[i] < min_val)
+      min_val = data[i];
+    if (data[i] > max_val)
+      max_val = data[i];
+  }
+  return std::complex<double>(min_val,max_val);
+}
 void get_epsilon_grid(geometric_object_list gobj_list,
                       material_type_list mlist,
                       material_type _default_material,
@@ -2574,13 +2585,29 @@ void get_epsilon_grid(geometric_object_list gobj_list,
                       meep::grid_volume gv,
                       vector3 cell_size,
                       vector3 cell_center,
-                      int nx, double *x,
-                      int ny, double *y,
-                      int nz, double *z,
+                      int nx, const double *x,
+                      int ny, const double *y,
+                      int nz, const double *z,
                       double *grid_vals) {

+  std::complex<double> min_max;
+  double min_val[3], max_val[3];
+  for (int n = 0; n < 3; ++n) {
+    if (((n == 0) ? nx : ((n == 1) ? ny : nz)) > 1) {
+      min_max = find_array_min_max(((n == 0) ? nx : ((n == 1) ? ny : nz)),
+                                   ((n == 0) ? x : ((n == 1) ? y : z)));
+      min_val[n] = real(min_max); max_val[n] = imag(min_max);
+    }
+  }
+  const meep::volume vol(meep::vec(min_val[0],min_val[1],min_val[2]),
+                         meep::vec(max_val[0],max_val[1],max_val[2]));
+  master_printf("vol: (%0.3f,%0.3f,%0.3f), (%0.3f,%0.3f,%0.3f)\n",vol.in_direction_min(meep::X),vol.in_direction_min(meep::Y),vol.in_direction_min(meep::Z),vol.in_direction_max(meep::X),vol.in_direction_max(meep::Y),vol.in_direction_max(meep::Z));
+  meep::volume vol2 = gv.pad().surroundings();
+  master_printf("vol2: (%0.3f,%0.3f,%0.3f), (%0.3f,%0.3f,%0.3f)\n",vol2.in_direction_min(meep::X),vol2.in_direction_min(meep::Y),vol2.in_direction_min(meep::Z),vol2.in_direction_max(meep::X),vol2.in_direction_max(meep::Y),vol2.in_direction_max(meep::Z));
   init_libctl(_default_material, _ensure_periodicity, &gv,
               cell_size, cell_center, &gobj_list);
-  geom_epsilon geps(gobj_list, mlist, gv.pad().surroundings());
+  dim = gv.dim;
+  // geom_epsilon geps(gobj_list, mlist, gv.pad().surroundings());
+  geom_epsilon geps(gobj_list, mlist, vol);

@oskooi
Copy link
Collaborator Author

oskooi commented Mar 18, 2021

I have discovered some strange behavior which may provide some clues for debugging. For a test case involving two overlapping Cylinder objects (to create a ring), get_epsilon_grid products random results as shown below. This behavior is independent of the size of the user-specified volume (i.e., it does not matter whether the user volume spans the entire cell or not).

This behavior can be demonstrated by running the same script 20 separate times and printing out how many values in the array returned by get_epsilon_grid contain values larger than 1.0. The incorrect answer is 0 which means that the array contains ε=1 everywhere. The correct answer is non-zero (in this case 8) since there must be at least some grid points with ε=11.56.

$ for i in `seq 20`; do python3 ring_grid.py; done |grep num.
num. grid points with epsilon > 1.0: (0,)
num. grid points with epsilon > 1.0: (0,)
num. grid points with epsilon > 1.0: (0,)
num. grid points with epsilon > 1.0: (8,)
num. grid points with epsilon > 1.0: (8,)
num. grid points with epsilon > 1.0: (0,)
num. grid points with epsilon > 1.0: (8,)
num. grid points with epsilon > 1.0: (0,)
num. grid points with epsilon > 1.0: (8,)
num. grid points with epsilon > 1.0: (8,)
num. grid points with epsilon > 1.0: (0,)
num. grid points with epsilon > 1.0: (0,)
num. grid points with epsilon > 1.0: (0,)
num. grid points with epsilon > 1.0: (0,)
num. grid points with epsilon > 1.0: (0,)
num. grid points with epsilon > 1.0: (8,)
num. grid points with epsilon > 1.0: (8,)
num. grid points with epsilon > 1.0: (8,)
num. grid points with epsilon > 1.0: (8,)
num. grid points with epsilon > 1.0: (0,)

A clue to what could be causing this behavior is shown in the details of the geometry tree produced by the verbose output from three separate runs for the two test cases of incorrect and correct results:

incorrect 1

Geometric-object bounding-box tree:
     ==> box (-2..2, -2..2, 3.42056e-277..1) <==
          bounding box (-1..1, -1..1, -5e+19..5e+19)
          shift object by (0, 0, 0)
          cylinder, center = (0,0,0)
               radius 1, height 1e+20, axis (0, 0, 1)
          bounding box (-2..2, -2..2, -5e+19..5e+19)
          shift object by (0, 0, 0)
          cylinder, center = (0,0,0)
               radius 2, height 1e+20, axis (0, 0, 1)
Geometric object tree has depth 1 and 2 object nodes (vs. 2 actual objects)
num. grid points with epsilon > 1.0: (0,)

incorrect 2

Geometric-object bounding-box tree:
     ==>  box (-2..2, -2..2, 1.03189e-307..1) <==
          bounding box (-1..1, -1..1, -5e+19..5e+19)
          shift object by (0, 0, 0)
          cylinder, center = (0,0,0)
               radius 1, height 1e+20, axis (0, 0, 1)
          bounding box (-2..2, -2..2, -5e+19..5e+19)
          shift object by (0, 0, 0)
          cylinder, center = (0,0,0)
               radius 2, height 1e+20, axis (0, 0, 1)
Geometric object tree has depth 1 and 2 object nodes (vs. 2 actual objects)
num. grid points with epsilon > 1.0: (0,)

incorrect 3

Geometric-object bounding-box tree:
     ==> box (-2..2, -2..2, 1..1.01212e+301) <==
          bounding box (-1..1, -1..1, -5e+19..5e+19)
          shift object by (0, 0, 0)
          cylinder, center = (0,0,0)
               radius 1, height 1e+20, axis (0, 0, 1)
          bounding box (-2..2, -2..2, -5e+19..5e+19)
          shift object by (0, 0, 0)
          cylinder, center = (0,0,0)
               radius 2, height 1e+20, axis (0, 0, 1)
Geometric object tree has depth 1 and 2 object nodes (vs. 2 actual objects)
num. grid points with epsilon > 1.0: (0,)

correct 1

Geometric-object bounding-box tree:
     ==> box (-2..2, -2..2, -2.65175e+92..1) <==
          bounding box (-1..1, -1..1, -5e+19..5e+19)
          shift object by (0, 0, 0)
          cylinder, center = (0,0,0)
               radius 1, height 1e+20, axis (0, 0, 1)
          bounding box (-2..2, -2..2, -5e+19..5e+19)
          shift object by (0, 0, 0)
          cylinder, center = (0,0,0)
               radius 2, height 1e+20, axis (0, 0, 1)
Geometric object tree has depth 1 and 2 object nodes (vs. 2 actual objects)
num. grid points with epsilon > 1.0: (8,)

correct 2

     ==> box (-2..2, -2..2, -1.30006e-250..1) <==
          bounding box (-1..1, -1..1, -5e+19..5e+19)
          shift object by (0, 0, 0)
          cylinder, center = (0,0,0)
               radius 1, height 1e+20, axis (0, 0, 1)
          bounding box (-2..2, -2..2, -5e+19..5e+19)
          shift object by (0, 0, 0)
          cylinder, center = (0,0,0)
               radius 2, height 1e+20, axis (0, 0, 1)
Geometric object tree has depth 1 and 2 object nodes (vs. 2 actual objects)
num. grid points with epsilon > 1.0: (8,)

correct 3

Geometric-object bounding-box tree:
     ==> box (-2..2, -2..2, -4.89682e+296..1) <==
          bounding box (-1..1, -1..1, -5e+19..5e+19)
          shift object by (0, 0, 0)
          cylinder, center = (0,0,0)
               radius 1, height 1e+20, axis (0, 0, 1)
          bounding box (-2..2, -2..2, -5e+19..5e+19)
          shift object by (0, 0, 0)
          cylinder, center = (0,0,0)
               radius 2, height 1e+20, axis (0, 0, 1)
Geometric object tree has depth 1 and 2 object nodes (vs. 2 actual objects)
num. grid points with epsilon > 1.0: (8,)

The difference in the geometry trees is highlighted by the lines with ==> and <==. It seems the z coordinate range of the box is changing randomly.

These results were generated using this test script:

import meep as mp
import numpy as np

mp.verbosity(3)

n = 3.4                 # index of waveguide     
w = 1                   # width of waveguide  
r = 1                   # inner radius of ring 
pad = 4                 # padding between waveguide and edge of PML  
dpml = 2                # thickness of PML 
sxy = 2*(r+w+pad+dpml)  # cell size

c1 = mp.Cylinder(radius=r+w,
                 center=mp.Vector3(),
                 material=mp.Medium(index=n),
                 height=mp.inf)
c2 = mp.Cylinder(radius=r,
                 center=mp.Vector3(),
                 material=mp.air,
                 height=mp.inf)

sim = mp.Simulation(cell_size=mp.Vector3(sxy, sxy),
                    geometry=[c1,c2],
                    resolution=20)

x = np.linspace(-2.0,2.0,5)
y = np.linspace(-2.0,2.0,5)
z = np.array([0])

eps_grid = sim.get_epsilon_grid(x,y,z)
print("num. grid points with epsilon > 1.0: {}".format(np.where(eps_grid > 1.0)[0].shape))

@oskooi
Copy link
Collaborator Author

oskooi commented Mar 18, 2021

Turns out the random results were due to uninitialized values of the user-specified volume in the z coordinate direction (as part of the min_corner and max_corner parameters of the volume class object) for the tests which all involved 2d xy area slices. These uninitialized values were then being passed to the geom_epsilon constructor where they produced random results during the generation of the geometry tree.

The latest commit fixes this bug by initializing the coordinates of the empty dimensions of the user-specified volume to 0. With this fix, the geometry tree can now be created for the user-specified volume rather than the entire cell volume. The results from get_epsilon_grid are consistently correct for MaterialGrid, GeometricObject, etc.

src/meepgeom.cpp Outdated Show resolved Hide resolved
src/meepgeom.cpp Outdated Show resolved Hide resolved
src/meepgeom.cpp Outdated Show resolved Hide resolved
src/meepgeom.cpp Outdated Show resolved Hide resolved
@stevengj stevengj merged commit 272bd0c into NanoComp:master Mar 18, 2021
@oskooi oskooi deleted the epsilon_grid branch March 18, 2021 20:52
bencbartlett pushed a commit to bencbartlett/meep that referenced this pull request Sep 9, 2021
…noComp#1522)

* get_epsilon_grid function for evaluating ε on user-specified grid

* refactor fragment_stats::init_libctl out of class fragment_stats

* clean up

* initialize coordinates of user-specified volume with empty dimensions to zero

* remove if statment from get_epsilon_grid

* modify find_array_min_max to accept arguments by reference rather than return complex double

* fixes
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

meepgeom function to evaluate ε on a given grid
3 participants