In this notebook, we will use inverse design and Tidy3D to create an integrated photonics component to convert a fundamental waveguide mode to a higher order mode.
from typing import Listimport autograd.numpy as anpimport matplotlib.pylab as pltimport numpy as np# import regular tidy3dimport tidy3d as tdimport tidy3d.web as webfrom autograd import value_and_gradfrom tidy3d.plugins.mode import ModeSolver# set random seed to get same resultsnp.random.seed(2)
Setup
We wish to recreate a device like the diagram below:
A mode source is injected into a waveguide on the left-hand side. The light propagates through a rectangular region with pixelated permittivity with the value of each pixel independently tunable between 1 (vacuum) and some maximum permittivity. Finally, we measure the transmission of the light into a waveguide on the right-hand side.
The goal of the inverse design exercise is to find the best distribution of permittivities (\(\epsilon_{ij}\)) in the coupling region to maximize the power conversion between the input mode and the output mode.
We also apply our built-in smoothening and binarization filters to ensure that the final device has smooth features, and permittivity values that are all either 1, or the maximum permittivity of the waveguide material.
Parameters
First we will define some parameters.
# wavelength and frequencywavelength =1.0freq0 = td.C_0 / wavelengthk0 =2* np.pi * freq0 / td.C_0# resolution controlmin_steps_per_wvl =16# in the design region, we set uniform grid resolution,# and define the design parameters on the same griddl_design_region =0.01# space between boxes and PMLbuffer=1.0* wavelength# optimize region sizelz = td.inflx =5.0ly =3.0# position of source and monitor (constant for all)source_x =-lx /2-buffer*0.8meas_x = lx /2+buffer*0.8# total sizeLx = lx +2*bufferLy = ly +2*bufferLz =0.0# permittivity and width of the input/output waveguideeps_wg =2.75wg_width =0.7# random starting parameters between 0 and 1nx =int(lx / dl_design_region)ny =int(ly / dl_design_region)params0 = np.random.random((nx, ny))# frequency width and run timefreqw = freq0 /10run_time =50/ freqw
Static Components
Next, we will set up the static parts of the geometry, the input source, and the output monitor using these parameters.
Next, we write a function to return the pixelated array given our flattened tuple of permittivity values \(\epsilon_{ij}\) using the tidy3d.plugins.autograd plugin.
We start with an array of parameters between 0 and 1, apply a conic filter and tanh projection to compute smooth, well-binarized features.
from tidy3d.plugins.autograd import make_filter_and_project, rescale# radius of the circular filter (um) and the threshold strengthradius =0.120beta =50filter_project = make_filter_and_project(radius, lx / nx)def get_eps(params, beta):"""Get the permittivity values (1, eps_wg) array as a function of the parameters (0, 1)""" processed_params = filter_project(params, beta) eps = rescale(processed_params, 1, eps_wg)return epsdef make_input_structures(params, beta) -> List[td.Structure]: box = td.Box(center=(0, 0, 0), size=(lx, ly, lz)) eps_data = get_eps(params, beta=beta).reshape((nx, ny, 1)) custom_structure = td.Structure.from_permittivity_array(geometry=box, eps_data=eps_data)return [custom_structure]
Making the Simulation
Next, we write a function to return a basic td.Simulation as a function of our parameter values.
We make sure to add the pixelated td.Structure list to input_structures but leave out the sources and monitors for now as we’ll want to add those after the mode solver is run so we can inspect them.
Next, let’s visualize the first 4 mode profiles so we can select which mode indices we want to inject and transmit.
from tidy3d.plugins.mode.web import run as run_mode_solvernum_modes =4mode_spec = td.ModeSpec(num_modes=num_modes)mode_solver = ModeSolver( simulation=sim_start, plane=source_plane, mode_spec=td.ModeSpec(num_modes=num_modes), freqs=[freq0],)modes = run_mode_solver(mode_solver, reduce_simulation=True)
02:24:48 UTC Mode solver created with
task_id='fdve-5bd80912-3dbd-491e-9870-8af29f02a42c',
solver_id='mo-bca9295e-30a3-4465-a505-b4114fc25a37'.
02:24:53 UTC Mode solver status: success
Let’s visualize the modes next.
fig, axs = plt.subplots(num_modes, 3, figsize=(12, 12), tight_layout=True)for mode_index inrange(num_modes): vmax =1.1*max(abs(modes.field_components[n].sel(mode_index=mode_index)).max() for n in ("Ex", "Ey", "Ez") )for field_name, ax inzip(("Ex", "Ey", "Ez"), axs[mode_index]): field = modes.field_components[field_name].sel(mode_index=mode_index) field.real.plot(label="Real", ax=ax) field.imag.plot(ls="--", label="Imag", ax=ax) ax.set_title(f"index={mode_index}, {field_name}") ax.set_ylim(-vmax, vmax)axs[0, 0].legend()print("Effective index of computed modes: ", np.array(modes.n_eff))
Effective index of computed modes: [[1.57207961 1.53546344 1.30320219 1.18494033]]
We want to inject the fundamental, Ez-polarized input into the 1st order Ez-polarized input.
From the plots, we see that these modes correspond to the first and third rows, or mode_index=0 and mode_index=2, respectively.
So we make sure that the mode_index_in and mode_index_out variables are set appropriately and we set a ModeSpec with 3 modes to be able to capture the mode_index_out in our output data.
Then it is straightforward to generate our source and monitor.
# source seeding the simulationforward_source = td.ModeSource( source_time=td.GaussianPulse(freq0=freq0, fwidth=freqw), center=[source_x, 0, 0], size=mode_size, mode_index=mode_index_in, mode_spec=mode_spec, direction="+",)# we'll refer to the measurement monitor by this name oftenmeasurement_monitor_name ="measurement"# monitor where we compute the objective function frommeasurement_monitor = td.ModeMonitor( center=[meas_x, 0, 0], size=mode_size, freqs=[freq0], mode_spec=mode_spec, name=measurement_monitor_name,)
Finally, we create a new function that calls our make_sim_base() function and adds the source and monitor to the result. This is the function we will use in our objective function to generate our td.Simulation given the input parameters.
Next, we will define a function to tell us how we want to postprocess the output td.SimulationData object to give the conversion power that we are interested in maximizing.
def measure_power(sim_data: td.SimulationData) ->float:"""Return the power in the output_data amplitude at the mode index of interest.""" output_amps = sim_data["measurement"].amps amp = output_amps.sel(direction="+", f=freq0, mode_index=mode_index_out).valuesreturn anp.sum(anp.abs(amp) **2)
Then, we add a penalty to produce structures that are invariant under erosion and dilation, which is a useful approach to implementing minimum length scale features.
from tidy3d.plugins.autograd import make_erosion_dilation_penaltypenalty = make_erosion_dilation_penalty(radius, lx / nx)
Define Objective Function
Finally, we need to define the objective function that we want to maximize as a function of our input parameters (permittivity of each pixel) that returns the conversion power. This is the function we will differentiate later.
We use the autograd.value_and_grad function to get the gradient of J with respect to the permittivity of each Box, while also returning the converted power associated with the current iteration, so we can record this value for later.
Let’s try running this function once to make sure it works.
dJ_fn = value_and_grad(J)
val, grad = dJ_fn(params0, beta=1, verbose=True)print(grad.shape)
02:24:56 UTC Created task 'inv_des' with resource_id
'fdve-a9bb133b-c5a2-4013-8f33-c137c817e3f9' and task_type 'FDTD'.
02:29:27 UTC Estimated FlexCredit cost: 0.025. This assumes the FDTD solver runs
for the full simulation time; if early shutoff is reached, the
billed cost can be lower. Use 'web.real_cost(task_id)' to get the
billed FlexCredit cost after a simulation run.
02:29:28 UTC status = success
02:29:30 UTC Loading results from simulation_data.hdf5
We notice that the behavior is as expected and the device performs exactly how we intended!
The Simulation object has the .to_gds_file convenience function to export the final design to a GDS file. In addition to a file name, it is necessary to set a cross-sectional plane (z = 0 in this case) on which to evaluate the geometry, a frequency to evaluate the permittivity, and a permittivity_threshold to define the shape boundaries in custom mediums. See the GDS export notebook for a detailed example on using .to_gds_file and other GDS related functions.
We use necessary cookies to run this website. With your permission, we
also use analytics cookies to understand site usage and marketing
cookies for advertising, retargeting, and HubSpot tracking.
Learn more about our cookie policy.
Privacy choices
Choose which optional cookies Flexcompute may use. Necessary cookies
are always on because they support core website behavior, security, and
saving your consent record.
Your browser is sending a Global Privacy Control signal, so marketing
cookies are disabled.
Subscribe
Thanks for subscribing
Publish Your Notebook
Thank you for publishingA confirmation email has been sent to your inbox. Your notebook will be available within the next 48 hours.
Community Library
TERMS & CONDITIONS
EFFECTIVE DATE: January 1, 2025
Terms and Conditions for User-Submitted Content
By submitting content to Flexcompute, you agree to the following terms:
1. Ownership and Copyright
All content, including but not limited to text, data, images, and other materials submitted by users,
remains the sole property of the original creator. Flexcompute does not claim ownership of the submitted
content or any intellectual property rights associated with it.
Users affirm that they own the copyright or have obtained all necessary permissions for the submitted
content and are fully responsible for ensuring that their submissions do not infringe on any third-party
rights.
2. Responsibility for Content
Users are solely responsible for the content they submit. Flexcompute does not endorse, guarantee,
or verify the accuracy, legality, or appropriateness of any submitted content. Users are responsible
for ensuring that their content complies with all applicable laws and regulations.
Users declare that they have obtained authorization from all co-authors and collaborators associated
with the submitted content, granting them the right to submit and sign on behalf of all contributors.
Users agree not to submit content that is illegal, defamatory, obscene, or violates the rights
of others, including privacy and intellectual property rights.
3. Modification and Presentation
Flexcompute reserves the right to review, edit, or modify submitted content to improve clarity,
formatting, and presentation while maintaining the original intent and message.
These modifications are made to enhance the overall quality and readability of the published material.
Users acknowledge that Flexcompute may format or present the content in a way
that aligns with our editorial and visual standards.
4. Liability Disclaimer
Flexcompute shall not be held liable for any disputes arising from user-submitted content, including
but not limited to copyright claims, inaccuracies, or damages resulting from the publication of user content.
Users agree to indemnify Flexcompute against any claims or legal actions resulting from their submitted content.
5. Content Use Rights
By submitting content, users grant Flexcompute a non-exclusive, royalty-free, worldwide license to publish, distribute,
and promote the content for the purposes of showcasing user contributions and marketing our services.
This license does not transfer ownership of the content or any copyright to Flexcompute.
Users retain the right to withdraw their submissions at any time.
Upon request, Flexcompute will remove the content from its platform.
6. Privacy and Confidentiality
Users acknowledge that submitted content may be publicly accessible and therefore waive any
rights to confidentiality or privacy regarding the published material.
Flexcompute will not share personal information of users without
their consent, in accordance with our privacy policy.
Community Library
How the process works:
You submit your notebook.
You will get a permanent URL to promote your work.
Cite the URL as a reference for others in your upcoming papers to enhance the impact.
When someone clicks your URL, your notebook will be displayed.
Enter your email address below to receive the presentation slides. In the future, we’ll share you very few emails when we have new tutorial release, development updates, valuable toolkits and technical guidance . You can unsubscribe at any time by clicking the link at the bottom of every email. We’ll never share your information.