Archived seminar material. These saved outputs were produced with Tidy3D 2.9.1
The adjoint-optimized grating from the previous notebook delivers excellent nominal performance. In practice, however, fabrication variability means the manufactured device rarely matches the design exactly. Here we quantify how the current design responds to some assumed process deviations to see whether it is robust or brittle.
In the adjoint notebook we purposefully focused on maximizing performance at the nominal geometry. The natural follow-up question is: how does that optimized design behave once it leaves the computer? Photonic fabrication processes inevitably introduce small deviations in etched dimensions. Even a well-controlled foundry run can exhibit ±20 nm variations in tooth widths and gaps due to lithography or etch bias. A design that is overly sensitive to these changes might look great in simulation yet fail to meet targets on wafer, so our immediate goal is to measure that sensitivity before pursuing robustness improvements.
Modeling Fabrication Errors with a Bias
We begin by reloading the best adjoint design and defining a simple bias model. A ±20 nm shift in feature dimensions is a realistic foundry tolerance, so we will simulate three cases: the nominal geometry, an over-etched device (features narrower than intended), and an under-etched device (features wider than intended). This gives an intuitive first look at the design’s sensitivity before launching a full Monte Carlo analysis.
import jsonfrom pathlib import Pathimport autograd.numpy as npimport matplotlib.pyplot as pltimport pandas as pdimport tidy3d as tdfrom autograd import value_and_gradfrom scipy.stats import normfrom setup import ( center_wavelength, default_spacer_thickness, get_mode_monitor_power, make_simulation,)from tidy3d import web
def load_nominal_parameters(path):"""Load a design JSON (Bayes or adjoint) into numpy-friendly fields.""" data = json.loads(Path(path).read_text(encoding="utf-8"))return {"widths_si": np.array(data["widths_si"]),"gaps_si": np.array(data["gaps_si"]),"widths_sin": np.array(data["widths_sin"]),"gaps_sin": np.array(data["gaps_sin"]),"first_gap_si": data["first_gap_si"],"first_gap_sin": data["first_gap_sin"],"spacer_thickness": default_spacer_thickness, }
def make_variation_builder(nominal):"""Return a closure that maps process deltas to a tidy3d Simulation.""" base_widths_si = np.array(nominal["widths_si"]) base_gaps_si = np.array(nominal["gaps_si"])def builder(overlay_delta=0.0, spacer_delta=0.0, etch_bias=0.0):# Etch bias widens features when positive and narrows them when# negative, so widths grow with the bias while gaps shrink, mirroring# the fabrication effect of over/under etching. pert_widths_si = base_widths_si + etch_bias pert_gaps_si = base_gaps_si - etch_biasreturn make_simulation( pert_widths_si, pert_gaps_si, nominal["widths_sin"], nominal["gaps_sin"], first_gap_si=nominal["first_gap_si"] + overlay_delta, first_gap_sin=nominal["first_gap_sin"], spacer_thickness=nominal["spacer_thickness"] + spacer_delta, )return builder
design_path = Path("./results") /"gc_adjoint_best.json"# Load the best apodized design from the previous notebook.# This will be our nominal, or central, design point for the analysis.nominal = load_nominal_parameters(design_path)builder = make_variation_builder(nominal)# Define the fabrication bias in microns (20 nm).bias =0.02# Create simulations for each fabrication scenario: over-etched, nominal,# and under-etched. Positive bias widens features, while a negative bias# corresponds to over-etching that narrows them.bias_cases = {"Over-etched (-20 nm)": builder(etch_bias=-bias),"Nominal": builder(),"Under-etched (+20 nm)": builder(etch_bias=bias),}
bias_data = web.run_async(bias_cases, verbose=False)bias_wavelengths =Nonebias_spectra = {}for label, sim_data in bias_data.items(): power_da = get_mode_monitor_power(sim_data) freqs = power_da.coords["f"].values wavelengths = td.C_0 / freqs power = np.asarray(power_da.data).squeeze() order = np.argsort(wavelengths) wavelengths = wavelengths[order] power = power[order]if bias_wavelengths isNone: bias_wavelengths = wavelengths bias_spectra[label] = power
Interpreting the Sensitivity Plot
The curves below compare the nominal spectrum to ±20 nm biased geometries. The separation between them conveys how quickly our high-efficiency design degrades under realistic fabrication shifts in tooth width and gap. Watch for both a drop in peak efficiency and a shift of the optimal wavelength.
After inspecting the deterministic bias sweep, we broaden the analysis with a Monte Carlo study. We randomly sample overlay, spacer, and width variations according to foundry-provided sigma values to estimate the distribution of coupling efficiency across a wafer.
We draw overlay, spacer, and silicon-width perturbations from independent Gaussian models whose sigmas come straight from the (hypothetical) foundry tolerance table. Each row in the samples array represents one die that we will feed into the simulation pipeline.
sims = {"nominal": builder()}sims.update({f"sample_{idx +1}": builder(*tuple(sample)) for idx, sample inenumerate(samples)})
The closure returned by make_variation_builder maps each sampled triplet into a full tidy3d Simulation. We keep the nominal design in the dictionary so the subsequent analysis can always reference the baseline spectrum.
batch_data = web.run_async(sims, verbose=False)
We submit the entire batch with web.run_async so Tidy3D executes the jobs in parallel since they are all independent.
ordered_names =list(sims.keys())wavelengths =Nonelinear_spectra = []for name in ordered_names: sim_data = batch_data[name] power_da = get_mode_monitor_power(sim_data) freqs = power_da.coords["f"].values wl = td.C_0 / freqs power = np.asarray(power_da.data).squeeze() order = np.argsort(wl) wl = wl[order] power = power[order]if wavelengths isNone: wavelengths = wl linear_spectra.append(power)linear_array = np.vstack(linear_spectra)nominal_index = ordered_names.index("nominal")nominal_spectrum = linear_array[nominal_index]
Once the solver responses return, we stack them into a 2D array and compute statistics such as the mean trace, percentile envelope, and nominal curve for direct comparison.
The helper converts the center-wavelength transmission into dB loss and aggregates mean, standard deviation, and percentile values. These single-number metrics offer a quick dashboard before moving on to more detailed adjoint sensitivities.
eta_center_db = linear_to_loss_db(eta_center)fig, ax = plt.subplots(figsize=(6, 4))ax.hist(eta_center_db[1:], bins="auto", color="tab:blue", alpha=0.7, label="Samples")ax.axvline(eta_center_db[0], color="black", linewidth=2, label="Nominal")ax.set_xlabel(f"Transmission at {wavelengths[idx_center]:.3f} µm (dB)")ax.set_ylabel("Count")ax.set_title("Monte Carlo Transmission at Center Wavelength")ax.grid(True, alpha=0.3)ax.legend()plt.show()
Adjoint
Linearized Sensitivity via Adjoint
Before launching a full robust optimization we want directional information: which fabrication knobs most strongly impact coupling efficiency near the nominal point? The objective below evaluates a single perturbed simulation and, through value_and_grad, returns both the power and its gradient with respect to the overlay, spacer, and silicon-width errors.
Normalizing the gradient-scaled sigmas reveals how much each parameter contributes to the linearized variance. Plotting the breakdown highlights the dominant sensitivities we should target when we redesign for robustness.
Finally we line up the Monte Carlo results with the adjoint prediction. Agreement between the two lenses justifies replacing expensive sampling with cheaper gradient estimates in the next notebook, while any mismatch would signal nonlinearity that the linearized model misses.
The ±20 nm sweep already hinted that the design is somewhat brittle: the peak efficiency drops by roughly a dB and the optimal wavelength shifts under bias. The Monte Carlo and adjoint statistics confirm that fabrication variability will erode performance across a wafer. To address this we need to optimize directly for robustness.
Next Step: Designing for Robustness
In the next notebook we will incorporate the process variations into the objective function itself, searching for geometries that maintain high efficiency across the biased scenarios rather than just at the nominal point.
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.