From 85fdc8b36a968564b3fec2d4f44279b49650f005 Mon Sep 17 00:00:00 2001 From: Tyler Hughes Date: Wed, 13 Nov 2024 09:43:39 -0500 Subject: [PATCH] add Structure.background_medium --- CHANGELOG.md | 1 + tests/sims/simulation_sample.json | 72 +++++++++++++------------- tests/test_components/test_autograd.py | 51 +++++++++++++++++- tidy3d/components/structure.py | 42 ++++++++++++++- tidy3d/plugins/autograd/README.md | 2 +- tidy3d/web/api/autograd/autograd.py | 4 +- 6 files changed, 130 insertions(+), 42 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0b071bc82..3ca77b1c9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,6 +19,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - `CustomMedium` design regions require far less data when performing inverse design by reducing adjoint field monitor size for dims with one pixel. - Calling `.values` on `DataArray` no longer raises a `DeprecationWarning` during automatic differentiation. - Minimum number of PML layers set to 6. +- `Structure.background_permittivity : float` for specifying background medium for shape differentiation deprecated in favor of `Structure.background_medium : Medium` for more generality. ### Fixed - Regression in local field projection leading to incorrect results for `far_field_approx=True`. diff --git a/tests/sims/simulation_sample.json b/tests/sims/simulation_sample.json index 2154786a3..70955b969 100644 --- a/tests/sims/simulation_sample.json +++ b/tests/sims/simulation_sample.json @@ -41,7 +41,7 @@ "length": 1.0 }, "name": "traced_dieletric_cylinder", - "background_permittivity": null, + "background_medium": null, "type": "Structure", "medium": { "attrs": {}, @@ -73,7 +73,7 @@ ] }, "name": "traced_dieletric_box", - "background_permittivity": null, + "background_medium": null, "type": "Structure", "medium": { "attrs": {}, @@ -117,7 +117,7 @@ ] }, "name": "traced custom polyslab", - "background_permittivity": null, + "background_medium": null, "type": "Structure", "medium": { "attrs": {}, @@ -152,7 +152,7 @@ ] }, "name": "dieletric_box", - "background_permittivity": null, + "background_medium": null, "type": "Structure", "medium": { "attrs": {}, @@ -184,7 +184,7 @@ ] }, "name": "lossy_box", - "background_permittivity": null, + "background_medium": null, "type": "Structure", "medium": { "attrs": {}, @@ -212,7 +212,7 @@ ] }, "name": "sellmeier_sphere", - "background_permittivity": null, + "background_medium": null, "type": "Structure", "medium": { "attrs": {}, @@ -252,7 +252,7 @@ ] }, "name": "lorentz_box", - "background_permittivity": null, + "background_medium": null, "type": "Structure", "medium": { "attrs": {}, @@ -290,7 +290,7 @@ ] }, "name": null, - "background_permittivity": null, + "background_medium": null, "type": "Structure", "medium": { "attrs": {}, @@ -322,7 +322,7 @@ } }, "name": null, - "background_permittivity": null, + "background_medium": null, "type": "Structure", "medium": { "attrs": {}, @@ -359,7 +359,7 @@ ] }, "name": "drude_box", - "background_permittivity": null, + "background_medium": null, "type": "Structure", "medium": { "attrs": {}, @@ -396,7 +396,7 @@ ] }, "name": null, - "background_permittivity": null, + "background_medium": null, "type": "Structure", "medium": { "attrs": {}, @@ -472,7 +472,7 @@ ] }, "name": null, - "background_permittivity": null, + "background_medium": null, "type": "Structure", "medium": { "attrs": {}, @@ -522,7 +522,7 @@ ] }, "name": null, - "background_permittivity": null, + "background_medium": null, "type": "Structure", "medium": { "attrs": {}, @@ -592,7 +592,7 @@ ] }, "name": "pec_group", - "background_permittivity": null, + "background_medium": null, "type": "Structure", "medium": { "attrs": {}, @@ -622,7 +622,7 @@ "length": 2.0 }, "name": "anisotopic_cylinder", - "background_permittivity": null, + "background_medium": null, "type": "Structure", "medium": { "attrs": {}, @@ -700,7 +700,7 @@ ] }, "name": "pole_slab", - "background_permittivity": null, + "background_medium": null, "type": "Structure", "medium": { "attrs": {}, @@ -743,7 +743,7 @@ ] }, "name": null, - "background_permittivity": null, + "background_medium": null, "type": "Structure", "medium": { "attrs": {}, @@ -778,7 +778,7 @@ ] }, "name": null, - "background_permittivity": null, + "background_medium": null, "type": "Structure", "medium": { "attrs": {}, @@ -817,7 +817,7 @@ ] }, "name": null, - "background_permittivity": null, + "background_medium": null, "type": "Structure", "medium": { "attrs": {}, @@ -857,7 +857,7 @@ ] }, "name": null, - "background_permittivity": null, + "background_medium": null, "type": "Structure", "medium": { "attrs": {}, @@ -896,7 +896,7 @@ ] }, "name": null, - "background_permittivity": null, + "background_medium": null, "type": "Structure", "medium": { "attrs": {}, @@ -935,7 +935,7 @@ ] }, "name": null, - "background_permittivity": null, + "background_medium": null, "type": "Structure", "medium": { "attrs": {}, @@ -973,7 +973,7 @@ ] }, "name": null, - "background_permittivity": null, + "background_medium": null, "type": "Structure", "medium": { "attrs": {}, @@ -1014,7 +1014,7 @@ ] }, "name": null, - "background_permittivity": null, + "background_medium": null, "type": "Structure", "medium": { "attrs": {}, @@ -1071,7 +1071,7 @@ ] }, "name": null, - "background_permittivity": null, + "background_medium": null, "type": "Structure", "medium": { "attrs": {}, @@ -1135,7 +1135,7 @@ ] }, "name": null, - "background_permittivity": null, + "background_medium": null, "type": "Structure", "medium": { "attrs": {}, @@ -1192,7 +1192,7 @@ ] }, "name": null, - "background_permittivity": null, + "background_medium": null, "type": "Structure", "medium": { "attrs": {}, @@ -1249,7 +1249,7 @@ ] }, "name": null, - "background_permittivity": null, + "background_medium": null, "type": "Structure", "medium": { "attrs": {}, @@ -1299,7 +1299,7 @@ ] }, "name": null, - "background_permittivity": null, + "background_medium": null, "type": "Structure", "medium": { "attrs": {}, @@ -1336,7 +1336,7 @@ ] }, "name": null, - "background_permittivity": null, + "background_medium": null, "type": "Structure", "medium": { "attrs": {}, @@ -1414,7 +1414,7 @@ ] }, "name": null, - "background_permittivity": null, + "background_medium": null, "type": "Structure", "medium": { "attrs": {}, @@ -1452,7 +1452,7 @@ } }, "name": "dieletric_mesh", - "background_permittivity": null, + "background_medium": null, "type": "Structure", "medium": { "attrs": {}, @@ -1494,7 +1494,7 @@ ] }, "name": null, - "background_permittivity": null, + "background_medium": null, "type": "Structure", "medium": { "attrs": {}, @@ -1545,7 +1545,7 @@ } }, "name": "clip_operation", - "background_permittivity": null, + "background_medium": null, "type": "Structure", "medium": { "attrs": {}, @@ -1607,7 +1607,7 @@ ] }, "name": "transformed_box", - "background_permittivity": null, + "background_medium": null, "type": "Structure", "medium": { "attrs": {}, @@ -2842,7 +2842,7 @@ ] }, "name": null, - "background_permittivity": null, + "background_medium": null, "type": "Structure", "medium": { "attrs": {}, diff --git a/tests/test_components/test_autograd.py b/tests/test_components/test_autograd.py index d9f461aef..7efd6e581 100644 --- a/tests/test_components/test_autograd.py +++ b/tests/test_components/test_autograd.py @@ -281,7 +281,7 @@ def make_structures(params: anp.ndarray) -> dict[str, td.Structure]: size_element = td.Structure( geometry=td.Box(center=(0, 0, 0), size=(1, size_y, 1)), medium=med, - background_permittivity=5.0, + background_medium=td.Medium(permittivity=5.0), ) # custom medium with variable permittivity data @@ -1710,6 +1710,55 @@ def objective(params): g = ag.grad(objective)(params0) +def test_background_medium(log_capture): + geo = td.Box(size=(1, 1, 1), center=(0, 0, 0)) + med = td.Medium(permittivity=2.0) + + background_permittivity = 5.0 + background_medium = td.Medium(permittivity=background_permittivity) + + # nothing + s = td.Structure( + geometry=geo, + medium=med, + ) + + # both supplied, consistent + td.Structure( + geometry=geo, + medium=med, + background_permittivity=background_permittivity, + background_medium=background_medium, + ) + + # both supplied, inconsistent + with pytest.raises(ValueError): + td.Structure( + geometry=geo, + medium=med, + background_permittivity=background_permittivity + 1, + background_medium=background_medium, + ) + + # background medium (preferred) + s = td.Structure( + geometry=geo, + medium=med, + background_medium=background_medium, + ) + + # background permittivity (deprecated) + with AssertLogLevel(log_capture, "WARNING", contains_str="deprecated"): + s_warn = td.Structure( + geometry=geo, + medium=med, + background_permittivity=background_permittivity, + ) + + assert s_warn.background_medium is not None + assert s_warn.background_medium.permittivity == background_permittivity + + class TestTidyArrayBox: def test_is_tidy_box(self): da = DataArray(tracer_arr, dims=map(str, range(tracer_arr.ndim))) diff --git a/tidy3d/components/structure.py b/tidy3d/components/structure.py index 9a397c37c..c8fc67b0c 100644 --- a/tidy3d/components/structure.py +++ b/tidy3d/components/structure.py @@ -12,6 +12,7 @@ from ..constants import MICROMETER from ..exceptions import SetupError, Tidy3dError, Tidy3dImportError +from ..log import log from .autograd.derivative_utils import DerivativeInfo from .autograd.types import AutogradFieldMap, Box from .autograd.utils import get_static @@ -20,7 +21,7 @@ from .geometry.polyslab import PolySlab from .geometry.utils import GeometryType, validate_no_transformed_polyslabs from .grid.grid import Coords -from .medium import AbstractCustomMedium, CustomMedium, Medium2D, MediumType +from .medium import AbstractCustomMedium, CustomMedium, Medium, Medium2D, MediumType from .monitor import FieldMonitor, PermittivityMonitor from .types import TYPE_TAG_STR, Ax, Axis from .validators import validate_name_str @@ -57,10 +58,47 @@ class AbstractStructure(Tidy3dBaseModel): None, ge=1.0, title="Background Permittivity", - description="Relative permittivity used for the background of this structure " + description="DEPRECATED: Use ``Structure.background_medium``. " + "Relative permittivity used for the background of this structure " "when performing shape optimization with autograd.", ) + background_medium: MediumType = pydantic.Field( + None, + title="Background Medium", + description="Medium used for the background of this structure " + "when performing shape optimization with autograd. This is required when the " + "structure is embedded in another structure as autograd will use the permittivity of the " + "``Simulation`` by default to compute the shape derivatives.", + ) + + @pydantic.root_validator(skip_on_failure=True) + def _handle_background_mediums(cls, values): + """Handle background medium combinations, including deprecation.""" + + background_permittivity = values.get("background_permittivity") + background_medium = values.get("background_medium") + + # old case, only permittivity supplied, warn and set the Medium automatically + if background_medium is None and background_permittivity is not None: + log.warning( + "'Structure.background_permittivity' is deprecated, " + "set the 'Structure.background_medium' directly using a 'Medium'. " + "Handling automatically using the supplied relative permittivity." + ) + values["background_medium"] = Medium(permittivity=background_permittivity) + + # both present, just make sure they are consistent, error if not + if background_medium is not None and background_permittivity is not None: + is_medium = isinstance(background_medium, Medium) + if not (is_medium and background_medium.permittivity == background_permittivity): + raise ValueError( + "Inconsistent 'background_permittivity' and 'background_medium'. " + "Use 'background_medium' only as 'background_permittivity' is deprecated." + ) + + return values + _name_validator = validate_name_str() @pydantic.validator("geometry") diff --git a/tidy3d/plugins/autograd/README.md b/tidy3d/plugins/autograd/README.md index c010aff41..eac0364ce 100644 --- a/tidy3d/plugins/autograd/README.md +++ b/tidy3d/plugins/autograd/README.md @@ -209,7 +209,7 @@ The following components are traceable as outputs of the `td.SimulationData` We also support the following high-level features: -- To manually set the background permittivity of a structure for purposes of shape optimization, one can set `Structure.background_permittivity`. +- To manually set the background permittivity of a structure for purposes of shape optimization, one can set `Structure.background_medium`. This is useful when there is a substrate or multiple overlapping structures as some geometries, such as `PolySlab`, do not automatically detect background permittivity and instead use the `Simulation.medium` by default. - Compute gradients for objective functions that rely on multi-frequency data using a single broadband adjoint source. - Enable server-side gradient processing by setting `local_gradient=False` in the web functions. diff --git a/tidy3d/web/api/autograd/autograd.py b/tidy3d/web/api/autograd/autograd.py index 38fbcbaa3..ffd2c25d0 100644 --- a/tidy3d/web/api/autograd/autograd.py +++ b/tidy3d/web/api/autograd/autograd.py @@ -859,8 +859,8 @@ def postprocess_adj( eps_out = np.mean(sim_data_orig.simulation.medium.eps_model(freq_adj)) # manually override simulation medium as the background structure - if structure.background_permittivity is not None: - eps_out = structure.background_permittivity + if structure.background_medium is not None: + eps_out = structure.background_medium.eps_model(freq_adj) derivative_info = DerivativeInfo( paths=structure_paths,