While we have already demonstrated second-order accuracy for subpixel smoothing of the MaterialGrid in a "forward" simulation (see results in #1503), following #1780 which enabled support for backpropagating the gradients from the Yee grid to the MaterialGrid, we have yet to validate the second-order accuracy of the gradients computed using an adjoint simulation.
ToDos
The following 2d test computes the directional derivative using the adjoint gradient over a design region consisting of a ring structure which connects an input and output port/waveguide. (This is a similar setup to the existing unit tests for the adjoint solver in which the design region contains random pixel values and the directional derivative is computed in a random direction.) The objective function is a single DFT field point in the output waveguide at a single frequency (the red dot in the schematic below). (We would also eventually like to adapt this test to the other two objective functions: mode coefficients and near-to-far fields.) The ring structure is a MaterialGrid at a fixed resolution of 640 pixels/μm while the Meep grid resolution is repeatedly doubled: 20, 40, 80, and 160 pixels/μm. The directional derivative should be converging to a fixed value with increasing resolution but this does not appear to be the case. The output from running the script below shows the resolution in the second column and directional derivative in the third column.
grad:, 20.0, -8.3007345310497
grad:, 40.0, -9.140322608657096
grad:, 80.0, -8.32203124442412
grad:, 160.0, -7.0622182341423985
Before we can validate the accuracy of the gradients, we need to ensure that the results are converging to a fixed value.

import meep as mp
import meep.adjoint as mpa
import numpy as np
from autograd import numpy as npa
silicon = mp.Medium(epsilon=12)
sxy = 5.0
cell_size = mp.Vector3(sxy,sxy,0)
dpml = 1.0
pml_xy = [mp.PML(thickness=dpml)]
eig_parity = mp.EVEN_Y + mp.ODD_Z
design_region_size = mp.Vector3(1.5,1.5)
design_region_resolution = 640
Nx = int(design_region_resolution*design_region_size.x) + 1
Ny = int(design_region_resolution*design_region_size.y) + 1
w = 1.0
waveguide_geometry = [mp.Block(material=silicon,
center=mp.Vector3(),
size=mp.Vector3(mp.inf,w,mp.inf))]
fcen = 1/1.55
df = 0.23*fcen
wvg_source = [mp.EigenModeSource(src=mp.GaussianSource(fcen,fwidth=df),
center=mp.Vector3(-0.5*sxy+dpml,0),
size=mp.Vector3(0,sxy-2*dpml),
eig_parity=eig_parity)]
x = np.linspace(-0.5*design_region_size.x,0.5*design_region_size.x,Nx)
y = np.linspace(-0.5*design_region_size.y,0.5*design_region_size.y,Ny)
xv, yv = np.meshgrid(x,y)
rad = 0.538948295
wdt = 0.194838432
ring_weights = np.logical_and(np.sqrt(np.square(xv) + np.square(yv)) > rad,
np.sqrt(np.square(xv) + np.square(yv)) < rad+wdt,
dtype=np.double)
ring_weights = ring_weights.flatten()
def adjoint_solver(resolution):
matgrid = mp.MaterialGrid(mp.Vector3(Nx,Ny),
mp.air,
silicon,
weights=np.ones((Nx,Ny)))
matgrid_region = mpa.DesignRegion(matgrid,
volume=mp.Volume(center=mp.Vector3(),
size=mp.Vector3(design_region_size.x,
design_region_size.y,
0)))
matgrid_geometry = [mp.Block(center=matgrid_region.center,
size=matgrid_region.size,
material=matgrid)]
geometry = waveguide_geometry + matgrid_geometry
sim = mp.Simulation(resolution=resolution,
cell_size=cell_size,
boundary_layers=pml_xy,
sources=wvg_source,
geometry=geometry)
obj_list = [mpa.FourierFields(sim,
mp.Volume(center=mp.Vector3(1.25),
size=mp.Vector3()),
mp.Ez)]
def J(mode_mon):
return npa.power(npa.abs(mode_mon),2)
opt = mpa.OptimizationProblem(
simulation=sim,
objective_functions=J,
objective_arguments=obj_list,
design_regions=[matgrid_region],
frequencies=[fcen])
f, dJ_du = opt([ring_weights])
sim.reset_meep()
return f, dJ_du
for r in [20., 40., 80., 160.]:
adjsol_obj, adjsol_grad = adjoint_solver(r)
print("grad:, {}, {}".format(r,np.sum(adjsol_grad)))
While we have already demonstrated second-order accuracy for subpixel smoothing of the
MaterialGridin a "forward" simulation (see results in #1503), following #1780 which enabled support for backpropagating the gradients from the Yee grid to theMaterialGrid, we have yet to validate the second-order accuracy of the gradients computed using an adjoint simulation.ToDos
The following 2d test computes the directional derivative using the adjoint gradient over a design region consisting of a ring structure which connects an input and output port/waveguide. (This is a similar setup to the existing unit tests for the adjoint solver in which the design region contains random pixel values and the directional derivative is computed in a random direction.) The objective function is a single DFT field point in the output waveguide at a single frequency (the red dot in the schematic below). (We would also eventually like to adapt this test to the other two objective functions: mode coefficients and near-to-far fields.) The ring structure is a
MaterialGridat a fixed resolution of 640 pixels/μm while the Meep grid resolution is repeatedly doubled: 20, 40, 80, and 160 pixels/μm. The directional derivative should be converging to a fixed value with increasing resolution but this does not appear to be the case. The output from running the script below shows the resolution in the second column and directional derivative in the third column.Before we can validate the accuracy of the gradients, we need to ensure that the results are converging to a fixed value.