Skip to content

Commit c73c63f

Browse files
committed
feat(adjoint): Add conductivity gradient for CustomMedium
Introduces support for computing gradients with respect to the `conductivity` field in `CustomMedium`. To achieve this, the derivative computation logic was generalized: - The `_derivative_field_cmp` method is refactored to accept a `component` parameter ('real', 'imag', 'complex') to compute the VJP for different parts of the complex permittivity. - For the 'imag' component, the derivative is scaled by `1 / (omega * epsilon_0)` to convert from derivative w.r.t. complex permittivity to derivative w.r.t. conductivity. - The `_compute_derivatives` dispatcher now handles the new `"conductivity"` parameter path. - A new autograd test is added to validate the conductivity gradient calculation on a `CustomMedium` with constant permittivity.
1 parent 5e7ef85 commit c73c63f

File tree

4 files changed

+680
-29
lines changed

4 files changed

+680
-29
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,11 +18,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1818
- Added `InternalAbsorber` class for placing first-order absorbing boundary conditions on planes inside the simulation domain. Internal absorbers are automatically wrapped in a PEC frame with a backing PEC plate on the non-absorbing side.
1919
- Added `absorber` field (default: `True`) to `WavePort` for automatically placing an absorber behind the port.
2020
- Added `conjugated_dot_product` field in `ModeMonitor` (default: `True`) and `WavePort` (default: `False`) to allow selecting the conjugated or non-conjugated dot product for mode decomposition.
21+
- Support for gradients with respect to the `conductivity` of a `CustomMedium`.
2122

2223
### Changed
2324
- Validate mode solver object for large number of grid points on the modal plane.
2425
- Adaptive minimum spacing for `PolySlab` integration is now wavelength relative and a minimum discretization is set for computing gradients for cylinders.
2526
- The `TerminalComponentModeler` defaults to the pseudo wave definition of scattering parameters. The new field `s_param_def` can be used to switch between either pseudo or power wave definitions.
27+
- Add support for `np.unwrap` in `tidy3d.plugins.autograd`.
2628

2729
### Fixed
2830
- Fixed missing amplitude factor and handling of negative normal direction case when making adjoint sources from `DiffractionMonitor`.

tests/test_components/test_autograd.py

Lines changed: 75 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -319,30 +319,42 @@ def make_structures(params: anp.ndarray) -> dict[str, td.Structure]:
319319
eps_arr = 1.01 + 0.5 * (anp.tanh(matrix @ params).reshape(DA_SHAPE) + 1)
320320

321321
nx, ny, nz = eps_arr.shape
322+
da_coords = {
323+
"x": np.linspace(-0.5, 0.5, nx),
324+
"y": np.linspace(-0.5, 0.5, ny),
325+
"z": np.linspace(-0.5, 0.5, nz),
326+
}
322327

323328
custom_med = td.Structure(
324329
geometry=box,
325330
medium=td.CustomMedium(
326331
permittivity=td.SpatialDataArray(
327332
eps_arr,
328-
coords={
329-
"x": np.linspace(-0.5, 0.5, nx),
330-
"y": np.linspace(-0.5, 0.5, ny),
331-
"z": np.linspace(-0.5, 0.5, nz),
332-
},
333+
coords=da_coords,
334+
),
335+
),
336+
)
337+
338+
# custom medium with variable permittivity and conductivity data
339+
conductivity_arr = 0.01 * (anp.tanh(matrix @ params).reshape(DA_SHAPE) + 1)
340+
custom_med_with_conductivity = td.Structure(
341+
geometry=box,
342+
medium=td.CustomMedium(
343+
permittivity=td.SpatialDataArray(
344+
eps_arr,
345+
coords=da_coords,
346+
),
347+
conductivity=td.SpatialDataArray(
348+
conductivity_arr,
349+
coords=da_coords,
333350
),
334351
),
335352
)
336353

337354
# custom medium with vector valued permittivity data
338355
eps_ii = td.ScalarFieldDataArray(
339356
eps_arr.reshape(nx, ny, nz, 1),
340-
coords={
341-
"x": np.linspace(-0.5, 0.5, nx),
342-
"y": np.linspace(-0.5, 0.5, ny),
343-
"z": np.linspace(-0.5, 0.5, nz),
344-
"f": [td.C_0],
345-
},
357+
coords=da_coords | {"f": [td.C_0]},
346358
)
347359

348360
custom_med_vec = td.Structure(
@@ -484,6 +496,7 @@ def make_structures(params: anp.ndarray) -> dict[str, td.Structure]:
484496
"center_list": center_list,
485497
"size_element": size_element,
486498
"custom_med": custom_med,
499+
"custom_med_with_conductivity": custom_med_with_conductivity,
487500
"custom_med_vec": custom_med_vec,
488501
"polyslab": polyslab,
489502
"polyslab_dispersive": polyslab_dispersive,
@@ -581,6 +594,7 @@ def plot_sim(sim: td.Simulation, plot_eps: bool = True) -> None:
581594
"center_list",
582595
"size_element",
583596
"custom_med",
597+
"custom_med_with_conductivity",
584598
"custom_med_vec",
585599
"polyslab",
586600
"complex_polyslab",
@@ -1738,7 +1752,7 @@ def J(eps):
17381752
monkeypatch.setattr(
17391753
td.CustomPoleResidue,
17401754
"_derivative_field_cmp",
1741-
lambda self, E_der_map, eps_data, dim: dJ_deps / 3.0,
1755+
lambda self, E_der_map, spatial_data, dim, freqs, component="real": dJ_deps / 3.0,
17421756
)
17431757

17441758
import importlib
@@ -2434,3 +2448,52 @@ def objective(x):
24342448

24352449
with pytest.raises(ValueError):
24362450
g = ag.grad(objective)(1.0)
2451+
2452+
2453+
def test_custom_medium_conductivity_only_gradient(rng, use_emulated_run, tmp_path):
2454+
"""Test conductivity gradients for CustomMedium with constant permittivity."""
2455+
2456+
monitor, postprocess = make_monitors()["field_point"]
2457+
2458+
def objective(params):
2459+
"""Objective function testing only conductivity gradient (constant permittivity)."""
2460+
len_arr = np.prod(DA_SHAPE)
2461+
matrix = rng.random((len_arr, N_PARAMS))
2462+
2463+
# constant permittivity
2464+
eps_arr = np.ones(DA_SHAPE) * 2.0
2465+
2466+
# variable conductivity
2467+
conductivity_arr = 0.05 * (anp.tanh(3 * matrix @ params).reshape(DA_SHAPE) + 1)
2468+
2469+
nx, ny, nz = DA_SHAPE
2470+
coords = {
2471+
"x": np.linspace(-0.5, 0.5, nx),
2472+
"y": np.linspace(-0.5, 0.5, ny),
2473+
"z": np.linspace(-0.5, 0.5, nz),
2474+
}
2475+
2476+
custom_med_struct = td.Structure(
2477+
geometry=td.Box(center=(0, 0, 0), size=(1, 1, 1)),
2478+
medium=td.CustomMedium(
2479+
permittivity=td.SpatialDataArray(eps_arr, coords=coords),
2480+
conductivity=td.SpatialDataArray(conductivity_arr, coords=coords),
2481+
),
2482+
)
2483+
2484+
sim = SIM_BASE.updated_copy(
2485+
structures=[custom_med_struct],
2486+
monitors=[monitor],
2487+
)
2488+
2489+
data = run(
2490+
sim,
2491+
path=str(tmp_path / "sim_test.hdf5"),
2492+
task_name="conductivity_only_grad_test",
2493+
verbose=False,
2494+
)
2495+
return postprocess(data, data[monitor.name])
2496+
2497+
val, grad = ag.value_and_grad(objective)(params0)
2498+
2499+
assert anp.all(grad != 0.0), "some gradients are 0 for conductivity-only test"

0 commit comments

Comments
 (0)