Skip to content

Commit 35ec6d8

Browse files
committed
update tests and add comments to code
1 parent 3349aee commit 35ec6d8

4 files changed

Lines changed: 139 additions & 104 deletions

File tree

python/adjoint/filters.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@
77
import meep as mp
88
from scipy import special
99
from scipy import signal
10+
import skfmm
11+
from scipy import interpolate
1012

1113
def _proper_pad(x,n):
1214
'''
@@ -854,3 +856,19 @@ def gray_indicator(x):
854856
density-based topology optimization. Archive of Applied Mechanics, 86(1-2), 189-218.
855857
'''
856858
return npa.mean(4 * x.flatten() * (1 - x.flatten())) * 100
859+
860+
def make_sdf(data):
861+
'''
862+
Assume the input, data, is the desired output shape
863+
(i.e. 1d, 2d, or 3d) and that it's values are between
864+
0 and 1.
865+
'''
866+
# create signed distance function
867+
sd = skfmm.distance(data- 0.5 , dx = 1)
868+
869+
# interpolate zero-levelset onto 0.5-levelset
870+
x = [np.min(sd.flatten()), 0, np.max(sd.flatten())]
871+
y = [0, 0.5, 1]
872+
f = interpolate.interp1d(x, y, kind='linear')
873+
874+
return f(sd)

python/requirements.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,3 +7,4 @@ numpy
77
parameterized
88
pytest
99
scipy
10+
scikit-fmm

python/tests/test_material_grid.py

Lines changed: 59 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@
1313
except:
1414
import adjoint as mpa
1515
import numpy as np
16-
from scipy.ndimage import gaussian_filter
1716
import unittest
1817

1918
rad = 0.301943
@@ -92,8 +91,7 @@ def compute_transmittance(matgrid_symmetry=False):
9291

9392
return tran
9493

95-
96-
def compute_resonant_mode_2d(res,default_mat=False):
94+
def compute_resonant_mode_2d(res,radius=rad,default_mat=False,cylinder=False,cylinder_matgrid=True,do_averaging=True):
9795
fcen = 0.3
9896
df = 0.2*fcen
9997
sources = [mp.Source(mp.GaussianSource(fcen,fwidth=df),
@@ -110,24 +108,37 @@ def compute_resonant_mode_2d(res,default_mat=False):
110108
x = np.linspace(-0.5*matgrid_size.x,0.5*matgrid_size.x,Nx)
111109
y = np.linspace(-0.5*matgrid_size.y,0.5*matgrid_size.y,Ny)
112110
xv, yv = np.meshgrid(x,y)
113-
weights = np.sqrt(np.square(xv) + np.square(yv)) < rad
114-
filtered_weights = gaussian_filter(weights,
115-
sigma=3.0,
116-
output=np.double)
117-
111+
weights = mpa.make_sdf(np.sqrt(np.square(xv) + np.square(yv)) < radius)
112+
if cylinder:
113+
weights = 0.0*weights+1.0
114+
118115
matgrid = mp.MaterialGrid(mp.Vector3(Nx,Ny),
119116
mp.air,
120117
Si,
121-
weights=filtered_weights,
122-
do_averaging=True,
123-
beta=1000,
118+
weights=weights,
119+
beta=np.inf,
124120
eta=0.5)
125-
126-
geometry = [mp.Block(center=mp.Vector3(),
121+
122+
# Use a cylinder object, not a block object
123+
if cylinder:
124+
# within the cylinder, use a (uniform) material grid
125+
if cylinder_matgrid:
126+
geometry = [mp.Cylinder(center=mp.Vector3(),
127+
radius=radius,
128+
material=matgrid)]
129+
# within the cylinder, just use a normal medium
130+
else:
131+
geometry = [mp.Cylinder(center=mp.Vector3(),
132+
radius=radius,
133+
material=Si)]
134+
# use a block object
135+
else:
136+
geometry = [mp.Block(center=mp.Vector3(),
127137
size=mp.Vector3(matgrid_size.x,matgrid_size.y,0),
128138
material=matgrid)]
129139

130140
sim = mp.Simulation(resolution=res,
141+
eps_averaging=do_averaging,
131142
cell_size=cell_size,
132143
default_material=matgrid if default_mat else mp.Medium(),
133144
geometry=geometry if not default_mat else [],
@@ -136,7 +147,7 @@ def compute_resonant_mode_2d(res,default_mat=False):
136147

137148
h = mp.Harminv(mp.Hz, mp.Vector3(0.3718,-0.2076), fcen, df)
138149
sim.run(mp.after_sources(h),
139-
until_after_sources=200)
150+
until_after_sources=300)
140151

141152
try:
142153
for m in h.modes:
@@ -147,13 +158,12 @@ def compute_resonant_mode_2d(res,default_mat=False):
147158

148159
return freq
149160

150-
151-
def compute_resonant_mode_mpb(resolution=1024):
161+
def compute_resonant_mode_mpb(resolution=512):
152162
geometry = [mp.Cylinder(rad, material=Si)]
153163
geometry_lattice = mp.Lattice(cell_size)
154164

155165
ms = mpb.ModeSolver(num_bands=1,
156-
k_points=[k_point],
166+
k_points=[mp.cartesian_to_reciprocal(k_point, geometry_lattice)],
157167
geometry=geometry,
158168
geometry_lattice=geometry_lattice,
159169
resolution=resolution)
@@ -165,23 +175,51 @@ def compute_resonant_mode_mpb(resolution=1024):
165175
class TestMaterialGrid(unittest.TestCase):
166176

167177
def test_subpixel_smoothing(self):
178+
res = 25
179+
180+
def subpixel_test_matrix(radius,do_averaging):
181+
freq_cylinder = compute_resonant_mode_2d(res, radius, default_mat=False, cylinder=True, cylinder_matgrid=False, do_averaging=do_averaging)
182+
freq_matgrid = compute_resonant_mode_2d(res, radius, default_mat=False, cylinder=False, do_averaging=do_averaging)
183+
freq_matgrid_cylinder = compute_resonant_mode_2d(res, radius, default_mat=False, cylinder=True, cylinder_matgrid=True, do_averaging=do_averaging)
184+
return [freq_cylinder,freq_matgrid,freq_matgrid_cylinder]
185+
186+
# when smoothing is off, all three tests should be identical to machine precision
187+
no_smoothing = subpixel_test_matrix(rad,False)
188+
self.assertEqual(no_smoothing[0], no_smoothing[1])
189+
self.assertEqual(no_smoothing[1], no_smoothing[2])
190+
191+
# when we slightly perturb the radius, the results should be the same as before.
192+
no_smoothing_perturbed = subpixel_test_matrix(rad+0.01/res,False)
193+
self.assertEqual(no_smoothing, no_smoothing_perturbed)
194+
195+
# when smoothing is on, the simple material results should be different from the matgrid results
196+
smoothing = subpixel_test_matrix(rad,True)
197+
self.assertNotEqual(smoothing[0], smoothing[1])
198+
self.assertAlmostEqual(smoothing[1], smoothing[2], 3)
199+
200+
# when we slighty perturb the radius, the results should all be different from before.
201+
smoothing_perturbed = subpixel_test_matrix(rad+0.01/res,True)
202+
self.assertNotEqual(smoothing, smoothing_perturbed)
203+
168204
# "exact" frequency computed using MPB
169205
freq_ref = compute_resonant_mode_mpb()
170-
171-
res = [25, 50]
206+
print(freq_ref)
207+
res = [50, 100]
172208
freq_matgrid = []
173209
for r in res:
174-
freq_matgrid.append(compute_resonant_mode_2d(r))
210+
freq_matgrid.append(compute_resonant_mode_2d(r,cylinder=False))
175211
# verify that the frequency of the resonant mode is
176212
# approximately equal to the reference value
177213
self.assertAlmostEqual(freq_ref, freq_matgrid[-1], 2)
214+
print("results: ",freq_ref,freq_matgrid)
178215

179216
# verify that the relative error is decreasing with increasing resolution
180217
# and is better than linear convergence because of subpixel smoothing
181218
self.assertLess(abs(freq_matgrid[1]-freq_ref)*(res[1]/res[0]),
182219
abs(freq_matgrid[0]-freq_ref))
183220

184-
freq_matgrid_default_mat = compute_resonant_mode_2d(res[0], True)
221+
# ensure that a material grid as a default material works
222+
freq_matgrid_default_mat = compute_resonant_mode_2d(res[0], rad, True)
185223
self.assertAlmostEqual(freq_matgrid[0], freq_matgrid_default_mat)
186224

187225
def test_symmetry(self):

src/meepgeom.cpp

Lines changed: 61 additions & 83 deletions
Original file line numberDiff line numberDiff line change
@@ -362,8 +362,14 @@ cvector3 to_geom_object_coords_VJP(cvector3 v, const geometric_object *o) {
362362
/* case geometric_object::CYLINDER:
363363
NOT YET IMPLEMENTED */
364364
case geometric_object::BLOCK: {
365-
vector3 v_real = cvector3_re(v);
366-
vector3 v_imag = cvector3_im(v);
365+
/* In order to leverage the underlying libctl infrastructure
366+
*and* the dual library that makes computing derivatives so easy,
367+
me perform a bit of a trick: we use the *complex* libctl vector,
368+
storing the real and dual parts of the dual library and *manually*
369+
propagate the dual through existing libraries as needed.
370+
*/
371+
vector3 v_real = cvector3_re(v); // real part
372+
vector3 v_imag = cvector3_im(v); // "dual" part
367373

368374
vector3 size = o->subclass.block_data->size;
369375
if (size.x != 0.0) {v_real.x /= size.x; v_imag.x /= size.x;}
@@ -379,6 +385,9 @@ cvector3 to_geom_object_coords_VJP(cvector3 v, const geometric_object *o) {
379385
}
380386

381387
cvector3 material_grid_grad(vector3 p, material_data *md, const geometric_object *o) {
388+
/* computes the actual spatial gradient at point `p`
389+
for the specified material grid `md`. */
390+
382391
if (!is_material_grid(md)) {meep::abort("Invalid material grid detected.\n"); }
383392

384393
cvector3 gradient = cvector_zero();
@@ -468,6 +477,14 @@ void map_lattice_coordinates(double &px, double &py, double &pz) {
468477
}
469478

470479
cvector3 matgrid_grad(vector3 p, geom_box_tree tp, int oi, material_data *md) {
480+
/* loops through all the material grids at a current point
481+
and computes the final *spatial* gradient (w.r.t. x,y,z)
482+
after all appropriate transformations (e.g. due to
483+
overlapping grids). Calls the helper function, `material_grid_grad`,
484+
which is what actually computes the spatial gradient.
485+
*/
486+
487+
// check for proper overlapping grids
471488
if (md->material_grid_kinds == material_data::U_MIN ||
472489
md->material_grid_kinds == material_data::U_PROD)
473490
meep::abort("%s:%i:matgrid_grad does not support overlapping grids with U_MIN or U_PROD\n",__FILE__,__LINE__);
@@ -493,6 +510,7 @@ cvector3 matgrid_grad(vector3 p, geom_box_tree tp, int oi, material_data *md) {
493510
++matgrid_val_count;
494511
}
495512

513+
// compensate for overlapping grids
496514
if (md->material_grid_kinds == material_data::U_MEAN)
497515
gradient = cvector3_scale(1.0/matgrid_val_count,gradient);
498516

@@ -1250,12 +1268,11 @@ duals::duald get_material_grid_fill(meep::ndim dim, duals::duald d, double r, du
12501268
return -1.0; // garbage fill
12511269
} else {
12521270
if (dim == meep::D1)
1253-
rel_fill = (r-d)/(2*r);
1254-
else if (dim == meep::D2 || dim == meep::Dcyl){
1255-
rel_fill = (1/(r*r*meep::pi)) * (r*r*acos(d/r)-d*sqrt(r*r-d*d));
1256-
}
1271+
return (r-d)/(2*r);
1272+
else if (dim == meep::D2 || dim == meep::Dcyl)
1273+
return (1/(r*r*meep::pi)) * (r*r*acos(d/r)-d*sqrt(r*r-d*d));
12571274
else if (dim == meep::D3)
1258-
rel_fill = (((r-d)*(r-d))/(4*meep::pi*r*r*r))*(2*r+d);
1275+
return (((r-d)*(r-d))/(4*meep::pi*r*r*r))*(2*r+d);
12591276
}
12601277

12611278
return rel_fill;
@@ -1593,20 +1610,7 @@ void geom_epsilon::fallback_chi1inv_row(meep::component c, double chi1inv_row[3]
15931610
material =
15941611
(material_type)material_of_unshifted_point_in_tree_inobject(p, restricted_tree, &inobject);
15951612
material_data *md = material;
1596-
meep::vec gradient(zero_vec(v.dim));
1597-
double uval = 0;
1598-
// TODO cleanup and remove
1599-
if (md->which_subclass == material_data::MATERIAL_GRID) {
1600-
geom_box_tree tp;
1601-
int oi;
1602-
tp = geom_tree_search(p, restricted_tree, &oi);
1603-
//gradient = matgrid_grad(p, tp, oi, md);
1604-
//uval = matgrid_val(p, tp, oi, md)+this->u_p;
1605-
}
1606-
else {
1607-
gradient = normal_vector(meep::type(c), v);
1608-
}
1609-
1613+
meep::vec gradient = normal_vector(meep::type(c), v);
16101614
get_material_pt(material, v.center());
16111615
material_epsmu(meep::type(c), material, &chi1p1, &chi1p1_inv);
16121616
material_gc(material);
@@ -1634,73 +1638,47 @@ void geom_epsilon::fallback_chi1inv_row(meep::component c, double chi1inv_row[3]
16341638
integer errflag;
16351639
double meps, minveps;
16361640

1637-
if (md->which_subclass == material_data::MATERIAL_GRID) {
1638-
number xmin[1], xmax[1];
1639-
matgrid_volavg mgva;
1640-
mgva.dim = v.dim;
1641-
mgva.ugrad_abs = meep::abs(gradient);
1642-
mgva.uval = uval;
1643-
mgva.rad = v.diameter()/2;
1644-
mgva.beta = md->beta;
1645-
mgva.eta = md->eta;
1646-
mgva.eps1 = (md->medium_1.epsilon_diag.x+md->medium_1.epsilon_diag.y+md->medium_1.epsilon_diag.z)/3;
1647-
mgva.eps2 = (md->medium_2.epsilon_diag.x+md->medium_2.epsilon_diag.y+md->medium_2.epsilon_diag.z)/3;
1648-
xmin[0] = -v.diameter()/2;
1649-
xmax[0] = v.diameter()/2;
1650-
#ifdef CTL_HAS_COMPLEX_INTEGRATION
1651-
cnumber ret = cadaptive_integration(matgrid_ceps_func, xmin, xmax, 1, (void *)&mgva, 0, tol, maxeval,
1652-
&esterr, &errflag);
1653-
meps = ret.re;
1654-
minveps = ret.im;
1655-
#else
1656-
meps = adaptive_integration(matgrid_eps_func, xmin, xmax, 1, (void *)&mgva, 0, tol, maxeval, &esterr,
1657-
&errflag);
1658-
minveps = adaptive_integration(matgrid_inveps_func, xmin, xmax, 1, (void *)&mgva, 0, tol, maxeval, &esterr,
1659-
&errflag);
1660-
#endif
1641+
integer n;
1642+
number xmin[3], xmax[3];
1643+
vector3 gvmin, gvmax;
1644+
gvmin = vec_to_vector3(v.get_min_corner());
1645+
gvmax = vec_to_vector3(v.get_max_corner());
1646+
xmin[0] = gvmin.x;
1647+
xmax[0] = gvmax.x;
1648+
if (dim == meep::Dcyl) {
1649+
xmin[1] = gvmin.z;
1650+
xmin[2] = gvmin.y;
1651+
xmax[1] = gvmax.z;
1652+
xmax[2] = gvmax.y;
16611653
}
16621654
else {
1663-
integer n;
1664-
number xmin[3], xmax[3];
1665-
vector3 gvmin, gvmax;
1666-
gvmin = vec_to_vector3(v.get_min_corner());
1667-
gvmax = vec_to_vector3(v.get_max_corner());
1668-
xmin[0] = gvmin.x;
1669-
xmax[0] = gvmax.x;
1670-
if (dim == meep::Dcyl) {
1671-
xmin[1] = gvmin.z;
1672-
xmin[2] = gvmin.y;
1673-
xmax[1] = gvmax.z;
1674-
xmax[2] = gvmax.y;
1675-
}
1676-
else {
1677-
xmin[1] = gvmin.y;
1678-
xmin[2] = gvmin.z;
1679-
xmax[1] = gvmax.y;
1680-
xmax[2] = gvmax.z;
1681-
}
1682-
if (xmin[2] == xmax[2])
1683-
n = xmin[1] == xmax[1] ? 1 : 2;
1684-
else
1685-
n = 3;
1686-
double vol = 1;
1687-
for (int i = 0; i < n; ++i)
1688-
vol *= xmax[i] - xmin[i];
1689-
if (dim == meep::Dcyl) vol *= (xmin[0] + xmax[0]) * 0.5;
1690-
eps_ever_negative = 0;
1691-
func_ft = meep::type(c);
1655+
xmin[1] = gvmin.y;
1656+
xmin[2] = gvmin.z;
1657+
xmax[1] = gvmax.y;
1658+
xmax[2] = gvmax.z;
1659+
}
1660+
if (xmin[2] == xmax[2])
1661+
n = xmin[1] == xmax[1] ? 1 : 2;
1662+
else
1663+
n = 3;
1664+
double vol = 1;
1665+
for (int i = 0; i < n; ++i)
1666+
vol *= xmax[i] - xmin[i];
1667+
if (dim == meep::Dcyl) vol *= (xmin[0] + xmax[0]) * 0.5;
1668+
eps_ever_negative = 0;
1669+
func_ft = meep::type(c);
16921670
#ifdef CTL_HAS_COMPLEX_INTEGRATION
1693-
cnumber ret = cadaptive_integration(ceps_func, xmin, xmax, n, (void *)this, 0, tol, maxeval,
1694-
&esterr, &errflag);
1695-
meps = ret.re / vol;
1696-
minveps = ret.im / vol;
1671+
cnumber ret = cadaptive_integration(ceps_func, xmin, xmax, n, (void *)this, 0, tol, maxeval,
1672+
&esterr, &errflag);
1673+
meps = ret.re / vol;
1674+
minveps = ret.im / vol;
16971675
#else
1698-
meps = adaptive_integration(eps_func, xmin, xmax, n, (void *)this, 0, tol, maxeval, &esterr,
1699-
&errflag) / vol;
1700-
minveps = adaptive_integration(inveps_func, xmin, xmax, n, (void *)this, 0, tol, maxeval, &esterr,
1701-
&errflag) / vol;
1676+
meps = adaptive_integration(eps_func, xmin, xmax, n, (void *)this, 0, tol, maxeval, &esterr,
1677+
&errflag) / vol;
1678+
minveps = adaptive_integration(inveps_func, xmin, xmax, n, (void *)this, 0, tol, maxeval, &esterr,
1679+
&errflag) / vol;
17021680
#endif
1703-
}
1681+
17041682
if (eps_ever_negative) // averaging negative eps causes instability
17051683
minveps = 1.0 / (meps = eps(v.center()));
17061684
{

0 commit comments

Comments
 (0)