Problems with point source python: step_db.cpp:40: void meep::fields::step_db(meep::field_type): Assertion 'changed_materials' failed.
#2787
-
Hello, i'm trying to modify the tutorials on adjoint optimization, in particular the 3rd one (https://nbviewer.org/github/NanoComp/meep/blob/master/python/examples/adjoint_optimization/03-Filtered_Waveguide_Bend.ipynb) I've set the waveguide to be straight and i'm trying to maximize the coupling of the waveguide with a source placed at the center. The optimization variables will be constrained to be fixed in a small radius around the source. The problem is that the code works with the source provided in the tutorial, i.e. source_size = mp.Vector3(0, Sy, 0)
kpoint = mp.Vector3(1, 0, 0)
src = mp.GaussianSource(frequency=fcen, fwidth=fwidth)
source = [
mp.EigenModeSource(
src,
eig_band=1,
direction=mp.NO_DIRECTION,
eig_kpoint=kpoint,
size=source_size,
center=design_region.center,
)
] but when using a point dipole source src = mp.GaussianSource(frequency=fcen, fwidth=fwidth)
source = [mp.Source(src, component=mp.Ez, center=design_region.center)] or even with a small size like the code throws the error
The design plot is while the code is import meep as mp
import meep.adjoint as mpa
from meep import Animate2D
import numpy as np
from autograd import numpy as npa
from autograd import tensor_jacobian_product
import nlopt
from matplotlib import pyplot as plt
mp.verbosity(1)
Si = mp.Medium(index=3.4)
SiO2 = mp.Medium(index=1.44)
waveguide_width = 0.5
design_region_width = 2.5
design_region_height = 2.5
protected_radius = 0.2
waveguide_length = 0.5
pml_size = 1.0
resolution = 20
minimum_length = 0.09 # minimum length scale (microns)
eta_i = (
0.5 # blueprint (or intermediate) design field thresholding point (between 0 and 1)
)
eta_e = 0.55 # erosion design field thresholding point (between 0 and 1)
eta_d = 1 - eta_e # dilation design field thresholding point (between 0 and 1)
filter_radius = mpa.get_conic_radius_from_eta_e(minimum_length, eta_e)
design_region_resolution = int(1 * resolution)
Sx = 2 * pml_size + 2 * waveguide_length + design_region_width
Sy = 2 * pml_size + design_region_height + 0.5
cell_size = mp.Vector3(Sx, Sy)
pml_layers = [mp.PML(pml_size)]
fcen = 1 / 1.55
width = 0.1
fwidth = width * fcen
freqs = 1 / np.linspace(1.5, 1.6, 10)
Nx = int(design_region_resolution * design_region_width) + 1
Ny = int(design_region_resolution * design_region_height) + 1
design_variables = mp.MaterialGrid(mp.Vector3(Nx, Ny), SiO2, Si, grid_type="U_MEAN", do_averaging=False)
design_region = mpa.DesignRegion(
design_variables,
volume=mp.Volume(
center=mp.Vector3(),
size=mp.Vector3(design_region_width, design_region_height, 0),
),
)
x_g = np.linspace(-design_region_width / 2, design_region_width / 2, Nx)
y_g = np.linspace(-design_region_height / 2, design_region_height / 2, Ny)
X_g, Y_g = np.meshgrid(x_g, y_g, sparse=True, indexing="ij")
left_wg_mask = (X_g == -design_region_width / 2) & (np.abs(Y_g) <= waveguide_width / 2)
right_wg_mask = (X_g == design_region_width / 2) & (np.abs(Y_g) <= waveguide_width / 2)
radius_mask = (X_g**2 + Y_g**2 < protected_radius**2) # force to have material around the dipole
Si_mask = left_wg_mask | right_wg_mask | radius_mask
border_mask = (
(X_g == -design_region_width / 2)
| (X_g == design_region_width / 2)
| (Y_g == -design_region_height / 2)
| (Y_g == design_region_height / 2)
)
SiO2_mask = border_mask.copy()
SiO2_mask[Si_mask] = False
def mapping(x, eta, beta):
x = npa.where(Si_mask.flatten(), 1, npa.where(SiO2_mask.flatten(), 0, x))
# filter
filtered_field = mpa.conic_filter(
x,
filter_radius,
design_region_width,
design_region_height,
design_region_resolution,
)
# projection
projected_field = mpa.tanh_projection(filtered_field, beta, eta)
projected_field = (npa.fliplr(projected_field) + projected_field) / 2 # up-down symmetry
projected_field = (npa.flipud(projected_field) + projected_field) / 2 # left-right symmetry
# interpolate to actual materials
return projected_field.flatten()
geometry = [
mp.Block(
center=mp.Vector3(x=-Sx / 4), material=Si, size=mp.Vector3(Sx / 2, 0.5, 0)
), # left waveguide
mp.Block(
center=mp.Vector3(x=Sx / 4), material=Si, size=mp.Vector3(Sx / 2, 0.5, 0)
), # right waveguide
mp.Block(
center=design_region.center, size=design_region.size, material=design_variables
), # design region
]
src = mp.GaussianSource(frequency=fcen, fwidth=fwidth)
source = [mp.Source(src, component=mp.Ez, center=design_region.center)]
""" # commenting the previous source and uncommentig this one is ok
source_size = mp.Vector3(0, Sy, 0)
kpoint = mp.Vector3(1, 0, 0)
src = mp.GaussianSource(frequency=fcen, fwidth=fwidth)
source = [
mp.EigenModeSource(
src,
eig_band=1,
direction=mp.NO_DIRECTION,
eig_kpoint=kpoint,
size=source_size,
center=design_region.center,
)
]
"""
sim = mp.Simulation(
cell_size=cell_size,
boundary_layers=pml_layers,
geometry=geometry,
sources=source,
default_material=SiO2,
resolution=resolution,
)
mode = 1
TE_left = mpa.EigenmodeCoefficient(
sim,
mp.Volume(
center=mp.Vector3(x=-Sx / 2 + pml_size + 2 * waveguide_length / 3),
size=mp.Vector3(y=Sy),
),
mode,
)
TE_right = mpa.EigenmodeCoefficient(
sim,
mp.Volume(
center=mp.Vector3(x=Sx / 2 - pml_size - 2 * waveguide_length / 3),
size=mp.Vector3(y=Sy),
),
mode,
)
ob_list = [TE_left, TE_right]
def J(left, right):
return npa.mean(npa.abs(left) ** 2 + npa.abs(right) ** 2)
opt = mpa.OptimizationProblem(
simulation=sim,
objective_functions=J,
objective_arguments=ob_list,
design_regions=[design_region],
frequencies=freqs
)
evaluation_history = []
cur_iter = [0]
def f(v, gradient, cur_beta):
print("Current iteration: {}".format(cur_iter[0] + 1))
f0, dJ_du = opt([mapping(v, eta_i, cur_beta)]) # compute objective and gradient
if gradient.size > 0:
gradient[:] = tensor_jacobian_product(mapping, 0)(
v, eta_i, cur_beta, np.sum(dJ_du, axis=1)
) # backprop
evaluation_history.append(np.real(f0))
cur_iter[0] = cur_iter[0] + 1
return np.real(f0)
algorithm = nlopt.LD_MMA
# Initial guess
x = np.ones((Nx,Ny)) * 0.5
x[Si_mask] = 1 # set the edges of waveguides to silicon
x[SiO2_mask] = 0 # set the other edges to SiO2
x = x.flatten()
# lower and upper bounds
lb = np.zeros((Nx*Ny,))
lb[Si_mask.flatten()] = 1
ub = np.ones((Nx * Ny,))
ub[SiO2_mask.flatten()] = 0
cur_beta = 4
beta_scale = 2
num_betas = 6
update_factor = 12
for iters in range(num_betas):
solver = nlopt.opt(algorithm, Nx*Ny)
solver.set_lower_bounds(lb)
solver.set_upper_bounds(ub)
solver.set_max_objective(lambda a, g: f(a, g, cur_beta))
solver.set_maxeval(update_factor)
x[:] = solver.optimize(x)
cur_beta = cur_beta * beta_scale
plt.figure()
plt.plot(10 * np.log10(evaluation_history), "o-")
plt.grid(True)
plt.xlabel("Iteration")
plt.ylabel("Average Insertion Loss (dB)")
plt.savefig("straightWG_evaluation_history_dipole.png")
opt.update_design([mapping(x, eta_i, cur_beta)])
f0, dJ_du = opt([mapping(x, eta_i, cur_beta // 2)], need_gradient=False)
frequencies = opt.frequencies
right_coef = opt.get_objective_arguments()
right_profile = np.abs(right_coef) ** 2
plt.figure()
plt.plot(1 / frequencies, right_profile * 100, "-o")
plt.grid(True)
plt.xlabel("Wavelength (microns)")
plt.ylabel("Transmission (%)")
plt.savefig("straightWG_transmission_dipole.png")
plt.figure()
plt.plot(1 / frequencies, 10 * np.log10(right_profile), "-o")
plt.grid(True)
plt.xlabel("Wavelength (microns)")
plt.ylabel("Insertion Loss (dB)")
plt.savefig("straightWG_insertion_loss_dipole.png")
|
Beta Was this translation helpful? Give feedback.
Replies: 4 comments 6 replies
-
Placing a source within the design region requires some care. This is something that @mochen4 faced when setting up the cavity-design problem which involved an objective function based on the LDOS at the center of the cavity. The details are described in Section E of JOSA B, Vol. 41, pp. A161-A176 (2024). Perhaps @mochen4 can provide some guidance for how to set this up. |
Beta Was this translation helpful? Give feedback.
-
Actually, I don't think we need any extra treatment for sources inside the design region. I used a point dipole source and it worked fine. I can check your code and see what is the issue. In the meantime, can you try putting a small circle of air around the source on top of the design region? Can you also try running the simulation itself, with some random configuration of design variables, without the EigenMode monitors? |
Beta Was this translation helpful? Give feedback.
-
Actually, I ran the code and found the error occurs during the adjoint run, so the issue is related to the mpa.EigenmodeCoefficient. Any ideas @smartalecH ? |
Beta Was this translation helpful? Give feedback.
-
This looks to be the same issue encountered in #2309. There's a simple hack, as described in my comment: set Hopefully we can fix the root someday... |
Beta Was this translation helpful? Give feedback.
This looks to be the same issue encountered in #2309. There's a simple hack, as described in my comment: set
force_all_components = True
in yourSimulation
constructor.Hopefully we can fix the root someday...