def to_numpy_params(param_dict):
"""Detach autograd arrays into plain numpy arrays for analysis/export."""
return {
"widths_si": np.array(param_dict["widths_si"], dtype=float),
"gaps_si": np.array(param_dict["gaps_si"], dtype=float),
"widths_sin": np.array(param_dict["widths_sin"], dtype=float),
"gaps_sin": np.array(param_dict["gaps_sin"], dtype=float),
"first_gap_si": float(param_dict["first_gap_si"]),
"first_gap_sin": float(param_dict["first_gap_sin"]),
}
def run_bias_sweep(param_dict, task_prefix, bias=ETCH_BIAS):
"""Run nominal/over/under simulations and return spectra in linear scale."""
scenarios = [
("Over-etched (-20 nm)", apply_bias(param_dict, -bias)),
("Nominal", param_dict),
("Under-etched (+20 nm)", apply_bias(param_dict, bias)),
]
sims = {
f"{task_prefix}_{idx}": make_simulation(
scenario["widths_si"],
scenario["gaps_si"],
scenario["widths_sin"],
scenario["gaps_sin"],
first_gap_si=scenario["first_gap_si"],
first_gap_sin=scenario["first_gap_sin"],
)
for idx, (_, scenario) in enumerate(scenarios)
}
batch_data = web.run_async(sims, verbose=False)
wavelengths = None
spectra = {}
for idx, (label, _) in enumerate(scenarios):
sim_data = batch_data[f"{task_prefix}_{idx}"]
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 is None:
wavelengths = wl
spectra[label] = power
return wavelengths, spectra
params_initial = to_numpy_params(params0)
params_robust = to_numpy_params(params)
w_before, spectra_before = run_bias_sweep(params_initial, "gc_robust_bias_before", bias=ETCH_BIAS)
w_after, spectra_after = run_bias_sweep(params_robust, "gc_robust_bias_after", bias=ETCH_BIAS)