From dceb0e0c8c7d1923d3ecc575741444db7f4a3398 Mon Sep 17 00:00:00 2001 From: Weiliang Jin Date: Wed, 8 May 2024 11:24:46 -0700 Subject: [PATCH] Introduce SubpixelSpec to allow for selecting subpixel averaging methods for different materials Validate dielectric subpixel in adjoint plugin Enable VolumetricAveraging to automatically switch to Staircasing if the field is substantially normal to the interface --- CHANGELOG.md | 2 +- docs/api/discretization.rst | 11 -- docs/api/index.rst | 2 + docs/api/subpixel_averaging.rst | 23 +++ tests/sims/simulation_sample.h5 | Bin 458448 -> 458360 bytes tests/sims/simulation_sample.json | 4 - tests/test_components/test_simulation.py | 22 +-- tidy3d/__init__.py | 16 +- tidy3d/components/eme/simulation.py | 7 - tidy3d/components/grid/grid_spec.py | 57 +------ tidy3d/components/medium.py | 10 +- tidy3d/components/simulation.py | 98 ++++------- tidy3d/components/subpixel_spec.py | 154 ++++++++++++++++++ .../plugins/adjoint/components/simulation.py | 10 +- 14 files changed, 247 insertions(+), 169 deletions(-) create mode 100644 docs/api/subpixel_averaging.rst create mode 100644 tidy3d/components/subpixel_spec.py diff --git a/CHANGELOG.md b/CHANGELOG.md index 1bae15f78..753a17c24 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - A batch of `ModeSolver` objects can be run concurrently using `tidy3d.plugins.mode.web.run_batch()` - `RectangularWaveguide.plot_field` optionally draws geometry edges over fields. - `RectangularWaveguide` supports layered cladding above and below core. +- `SubpixelSpec` accepted by `Simulation.subpixel` to select subpixel averaging methods separately for dielectric, metal, and PEC materials. Specifically, added support for conformal mesh methods near PEC structures that can be specified through the field `pec` in the `SubpixelSpec` class. Note: previously, `subpixel=False` was implementing staircasing for every material except PEC. Now, `subpixel=False` implements direct staircasing for all materials. For PEC, the behavior of `subpixel=False` in Tidy3D < 2.7 is now achieved through `subpixel=SubpixelSpec(pec=HeuristicPECStaircasing())`, while `subpixel=True` in Tidy3D < 2.7 is now achieved through `subpixel=SubpixelSpec(pec=Staircasing())`. The default is `subpixel=SubpixelSpec(pec=PECConformal())` for more accurate PEC modelling. ### Fixed - `ModeSolver.plot_field` correctly returning the plot axes. @@ -52,7 +53,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Introduces the `microwave` plugin which includes `ImpedanceCalculator` for computing the characteristic impedance of transmission lines. - `Simulation` now accepts `LumpedElementType`, which currently only supports the `LumpedResistor` type. `LumpedPort` together with `LumpedResistor` make up the new `TerminalComponentModeler` in the `smatrix` plugin. - Uniaxial medium Lithium Niobate to material library. -- Added support for conformal mesh methods near PEC structures that can be specified through the field `pec_conformal_mesh_spec` in the `Simulation` class. Note: previously, `subpixel=False` was implementing staircasing for every material except PEC. Now, `subpixel=False` implements direct staircasing for all materials. For PEC, the behavior of `subpixel=False` in Tidy3D < 2.7 is now achieved through `subpixel=True` and `pec_conformal_mesh_spec=HeuristicConformalMeshSpec()`, while `subpixel=True` in Tidy3D < 2.7 is now achieved through the default settings `subpixel=True` and `pec_conformal_mesh_spec=StaircasingConformalMeshSpec()`. Additionally, a new `BenklerConformalMeshSpec()` was introduced for more accurate PEC modelling. - Properties `num_time_steps_adjoint` and `tmesh_adjoint` to `JaxSimulation` to estimate adjoint run time. - Ability to add `path` to `updated_copy()` method to recursively update sub-components of a tidy3d model. For example `sim2 = sim.updated_copy(size=new_size, path="structures/0/geometry")` creates a recursively updated copy of `sim` where `sim.structures[0].geometry` is updated with `size=new_size`. - Python 3.12 support. Python 3.8 deprecation. Updated dependencies. diff --git a/docs/api/discretization.rst b/docs/api/discretization.rst index 2df4e0521..b9a3be7d9 100644 --- a/docs/api/discretization.rst +++ b/docs/api/discretization.rst @@ -15,14 +15,3 @@ Discretization tidy3d.FieldGrid tidy3d.YeeGrid tidy3d.Grid - -PEC Conformal Mesh Spec ------------------------ - -.. autosummary:: - :toctree: _autosummary/ - :template: module.rst - - tidy3d.StaircasingConformalMeshSpec - tidy3d.HeuristicConformalMeshSpec - tidy3d.BenklerConformalMeshSpec diff --git a/docs/api/index.rst b/docs/api/index.rst index d4799d204..b53e54ca6 100644 --- a/docs/api/index.rst +++ b/docs/api/index.rst @@ -16,6 +16,7 @@ API |:computer:| mode lumped_elements discretization + subpixel_averaging output_data scene logging @@ -38,6 +39,7 @@ API |:computer:| .. include:: /api/mode.rst .. include:: /api/lumped_elements.rst .. include:: /api/discretization.rst +.. include:: /api/subpixel_averaging.rst .. include:: /api/output_data.rst .. include:: /api/scene.rst .. include:: /api/logging.rst diff --git a/docs/api/subpixel_averaging.rst b/docs/api/subpixel_averaging.rst new file mode 100644 index 000000000..8bfbf9f96 --- /dev/null +++ b/docs/api/subpixel_averaging.rst @@ -0,0 +1,23 @@ +.. currentmodule:: tidy3d + +Subpixel Averaging +================== + +.. autosummary:: + :toctree: _autosummary/ + :template: module.rst + + tidy3d.SubpixelSpec + +Types of Subpixel Averaging Methods +----------------------------------- + +.. autosummary:: + :toctree: _autosummary/ + :template: module.rst + + tidy3d.Staircasing + tidy3d.VolumetricAveraging + tidy3d.HeuristicPECStaircasing + tidy3d.PolarizedAveraging + tidy3d.PECConformal diff --git a/tests/sims/simulation_sample.h5 b/tests/sims/simulation_sample.h5 index 8dd823406aac30fe9202ff8dc82242ae9d336fd3..420ec7fae8a81cc0a4c3dbb2ebe8d48fc792a816 100644 GIT binary patch delta 15872 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 delta 16008 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 b*(pOw3TD7}=?kv;FN0Ka-YfP0IPCub8I-3) diff --git a/tests/sims/simulation_sample.json b/tests/sims/simulation_sample.json index 13473bcaf..850bcac8c 100644 --- a/tests/sims/simulation_sample.json +++ b/tests/sims/simulation_sample.json @@ -2722,9 +2722,5 @@ "courant": 0.8, "normalize_index": 0, "shutoff": 0.0001, - "pec_conformal_mesh_spec": { - "attrs": {}, - "type": "StaircasingConformalMeshSpec" - }, "run_time": 1e-12 } \ No newline at end of file diff --git a/tests/test_components/test_simulation.py b/tests/test_components/test_simulation.py index d08480fb2..4669d1925 100644 --- a/tests/test_components/test_simulation.py +++ b/tests/test_components/test_simulation.py @@ -1969,30 +1969,32 @@ def test_dt(): def test_conformal_dt(): - """make sure dt is reduced when BenklerConformalMeshSpec is applied.""" + """make sure dt is reduced when PEC structures are present and PECConformal is used.""" + box = td.Structure( + geometry=td.Box(size=(1, 1, 1)), + medium=td.PECMedium(), + ) sim = td.Simulation( size=(2.0, 2.0, 2.0), run_time=1e-12, + structures=[box], grid_spec=td.GridSpec.uniform(dl=0.1), + subpixel=td.SubpixelSpec(pec=td.Staircasing()), ) dt = sim.dt - # Benkler - sim_conformal = sim.updated_copy(pec_conformal_mesh_spec=td.BenklerConformalMeshSpec()) + # Conformal + sim_conformal = sim.updated_copy(subpixel=td.SubpixelSpec(pec=td.PECConformal())) assert sim_conformal.dt < dt - # Benkler: same courant + # Conformal: same courant sim_conformal2 = sim.updated_copy( - pec_conformal_mesh_spec=td.BenklerConformalMeshSpec(timestep_reduction=0) + subpixel=td.SubpixelSpec(pec=td.PECConformal(timestep_reduction=0)) ) assert sim_conformal2.dt == dt - # staircasing - sim_staircasing = sim.updated_copy(pec_conformal_mesh_spec=td.StaircasingConformalMeshSpec()) - assert sim_staircasing.dt == dt - # heuristic - sim_heuristic = sim.updated_copy(pec_conformal_mesh_spec=td.StaircasingConformalMeshSpec()) + sim_heuristic = sim.updated_copy(subpixel=td.SubpixelSpec(pec=td.HeuristicPECStaircasing())) assert sim_heuristic.dt == dt diff --git a/tidy3d/__init__.py b/tidy3d/__init__.py index b993e75d3..2d43d7e8e 100644 --- a/tidy3d/__init__.py +++ b/tidy3d/__init__.py @@ -3,8 +3,11 @@ # grid from .components.grid.grid import Grid, Coords from .components.grid.grid_spec import GridSpec, UniformGrid, CustomGrid, AutoGrid -from .components.grid.grid_spec import BenklerConformalMeshSpec, StaircasingConformalMeshSpec -from .components.grid.grid_spec import HeuristicConformalMeshSpec + +# subpixel +from .components.subpixel_spec import SubpixelSpec, Staircasing +from .components.subpixel_spec import VolumetricAveraging, PolarizedAveraging +from .components.subpixel_spec import HeuristicPECStaircasing, PECConformal # geometry from .components.geometry.base import Box, Transformed, ClipOperation, GeometryGroup @@ -352,9 +355,12 @@ def set_logging_level(level: str) -> None: "TriangularGridDataset", "TetrahedralGridDataset", "medium_from_nk", - "BenklerConformalMeshSpec", - "StaircasingConformalMeshSpec", - "HeuristicConformalMeshSpec", + "SubpixelSpec", + "Staircasing", + "VolumetricAveraging", + "PolarizedAveraging", + "HeuristicPECStaircasing", + "PECConformal", "EMESimulation", "EMESimulationData", "EMEMonitor", diff --git a/tidy3d/components/eme/simulation.py b/tidy3d/components/eme/simulation.py index 3b32a1d55..7d39cb121 100644 --- a/tidy3d/components/eme/simulation.py +++ b/tidy3d/components/eme/simulation.py @@ -175,13 +175,6 @@ class EMESimulation(AbstractYeeGridSimulation): validate_default=True, ) - subpixel: bool = pd.Field( - True, - title="Subpixel Averaging", - description="If ``True``, uses subpixel averaging of the permittivity " - "based on structure definition, resulting in much higher accuracy for a given grid size.", - ) - store_port_modes: bool = pd.Field( True, title="Store Port Modes", diff --git a/tidy3d/components/grid/grid_spec.py b/tidy3d/components/grid/grid_spec.py index 968e7d9ff..7eb28a934 100644 --- a/tidy3d/components/grid/grid_spec.py +++ b/tidy3d/components/grid/grid_spec.py @@ -9,7 +9,7 @@ from .grid import Coords1D, Coords, Grid from .mesher import GradedMesher, MesherType -from ..base import Tidy3dBaseModel, cached_property +from ..base import Tidy3dBaseModel from ..types import Axis, Symmetry, annotate_type, TYPE_TAG_STR from ..source import SourceType from ..structure import Structure, StructureType @@ -18,61 +18,6 @@ from ...exceptions import SetupError from ...constants import MICROMETER, C_0, fp_eps -# Default Courant number reduction rate in Benkler's scheme -DEFAULT_COURANT_REDUCTION_BENKLER = 0.3 - - -class ConformalMeshSpec(Tidy3dBaseModel, ABC): - """Base class defining conformal mesh specifications.""" - - @cached_property - def courant_ratio(self) -> float: - """The scaling ratio applied to Courant number so that the courant number - in the simulation is ``sim.courant * courant_ratio``. - """ - return 1.0 - - -class StaircasingConformalMeshSpec(ConformalMeshSpec): - """Simple staircasing scheme based on - [Taflove, The Electrical Engineering Handbook 3.629-670 (2005): 15.]. - """ - - -class HeuristicConformalMeshSpec(ConformalMeshSpec): - """Slightly different from the staircasing scheme: the field component near PEC - is considered to be outside PEC if it's substantially normal to the interface. - """ - - -class BenklerConformalMeshSpec(ConformalMeshSpec): - """Conformal mesh scheme based on - [S. Benkler, IEEE Transactions on Antennas and Propagation 54.6, 1843 (2006)], which is similar - to the approach described in [S. Dey, R. Mittra, IEEE Microwave and Guided Wave Letters 7.9, 273 (1997)]. - """ - - timestep_reduction: float = pd.Field( - DEFAULT_COURANT_REDUCTION_BENKLER, - title="Time Step Size Reduction Rate", - description="Reduction factor between 0 and 1 such that the simulation's time step size " - "will be ``1 - timestep_reduction`` times its default value. " - "Accuracy can be improved with a smaller time step size; but simulation time increased as well.", - lt=1, - ge=0, - ) - - @cached_property - def courant_ratio(self) -> float: - """The scaling ratio applied to Courant number so that the courant number - in the simulation is ``sim.courant * courant_ratio``. - """ - return 1 - self.timestep_reduction - - -ConformalMeshSpecType = Union[ - BenklerConformalMeshSpec, StaircasingConformalMeshSpec, HeuristicConformalMeshSpec -] - class GridSpec1d(Tidy3dBaseModel, ABC): diff --git a/tidy3d/components/medium.py b/tidy3d/components/medium.py index 26e6e3b76..62c3159c9 100644 --- a/tidy3d/components/medium.py +++ b/tidy3d/components/medium.py @@ -1107,9 +1107,9 @@ class AbstractCustomMedium(AbstractMedium, ABC): subpixel: bool = pd.Field( False, title="Subpixel averaging", - description="If ``True`` and simulation's ``subpixel`` is also ``True``, " - "applies subpixel averaging of the permittivity " - "on the interface of the structure, including exterior boundary and " + description="If ``True``, apply the subpixel averaging method specified by " + "``Simulation``'s field ``subpixel`` for this type of material on the " + "interface of the structure, including exterior boundary and " "intersection interfaces with other structures.", ) @@ -5249,8 +5249,8 @@ class AbstractPerturbationMedium(ABC, Tidy3dBaseModel): True, title="Subpixel averaging", description="This value will be transferred to the resulting custom medium. That is, " - "if ``True``, the subpixel averaging will be applied to the custom medium provided " - "the corresponding ``Simulation``'s field ``subpixel`` is set to ``True`` as well. " + "if ``True``, the subpixel averaging will be applied to the custom medium. The type " + "of subpixel averaging method applied is specified in ``Simulation``'s field ``subpixel``. " "If the resulting medium is not a custom medium (no perturbations), this field does not " "have an effect.", ) diff --git a/tidy3d/components/simulation.py b/tidy3d/components/simulation.py index 077dc7e57..4640b2dc8 100644 --- a/tidy3d/components/simulation.py +++ b/tidy3d/components/simulation.py @@ -27,7 +27,6 @@ from .types import Literal, TYPE_TAG_STR from .grid.grid import Coords1D, Grid, Coords from .grid.grid_spec import GridSpec, UniformGrid, AutoGrid, CustomGrid -from .grid.grid_spec import ConformalMeshSpecType, StaircasingConformalMeshSpec from .medium import MediumType, AbstractMedium from .medium import AbstractCustomMedium, Medium, Medium2D, MediumType3D from .medium import AnisotropicMedium, FullyAnisotropicMedium, AbstractPerturbationMedium @@ -46,6 +45,7 @@ from .viz import add_ax_if_none, equal_aspect from .scene import Scene, MAX_NUM_MEDIUMS from .run_time_spec import RunTimeSpec +from .subpixel_spec import SubpixelSpec from .viz import PlotParams from .viz import plot_params_pml, plot_params_override_structures from .viz import plot_params_pec, plot_params_pmc, plot_params_bloch, plot_sim_3d @@ -152,15 +152,20 @@ class AbstractYeeGridSimulation(AbstractSimulation, ABC): * `Using automatic nonuniform meshing <../../notebooks/AutoGrid.html>`_ """ - subpixel: bool = pydantic.Field( - True, + subpixel: Union[bool, SubpixelSpec] = pydantic.Field( + SubpixelSpec(), title="Subpixel Averaging", - description="If ``True``, uses subpixel averaging of the permittivity " - "based on structure definition, resulting in much higher accuracy for a given grid size.", + description="Apply subpixel averaging methods of the permittivity on structure interfaces " + "to result in much higher accuracy for a given grid size. Supply a :class:`SubpixelSpec` " + "to this field to select subpixel averaging methods separately on dielectric, metal, and " + "PEC material interfaces. Alternatively, user may supply a boolean value: " + "``True`` to apply the default subpixel averaging methods corresponding to ``SubpixelSpec()`` " + ", or ``False`` to apply staircasing.", ) """ - If ``True``, uses subpixel averaging of the permittivity based on structure definition, resulting in much - higher accuracy for a given grid size., + Supply :class:`SubpixelSpec` to select subpixel averaging methods separately for dielectric, metal, and + PEC material interfaces. Alternatively, supply ``True`` to use default subpixel averaging methods, + or ``False`` to staircase all structure interfaces. **1D Illustration** @@ -254,6 +259,19 @@ def num_cells_in_monitor(monitor: Monitor) -> int: return sum(num_cells_in_monitor(mnt) for mnt in monitor.integration_surfaces) return num_cells_in_monitor(monitor) + @cached_property + def _subpixel(self) -> SubpixelSpec: + """Subpixel averaging method evaluated based on self.subpixel.""" + if isinstance(self.subpixel, SubpixelSpec): + return self.subpixel + + # self.subpixel is boolean + # 1) if it's true, use the default dielectric=True, metal=Staircasing, PEC=Benkler + if self.subpixel: + return SubpixelSpec() + # 2) if it's false, apply staircasing on all material boundaries + return SubpixelSpec.staircasing() + @equal_aspect @add_ax_if_none def plot( @@ -1857,55 +1875,6 @@ class Simulation(AbstractYeeGridSimulation): * `Structures `_ """ - subpixel: bool = pydantic.Field( - True, - title="Subpixel Averaging", - description="If ``True``, uses subpixel averaging of the permittivity " - "based on structure definition, resulting in much higher accuracy for a given grid size.", - ) - """ - If ``True``, uses subpixel averaging of the permittivity based on structure definition, resulting in much - higher accuracy for a given grid size., - - **1D Illustration** - - For example, in the image below, two silicon slabs with thicknesses 150nm and 175nm centered in a grid with - spatial discretization :math:`\\Delta z = 25\\text{nm}` compute the effective permittivity of each grid point as the - average permittivity between the grid points. A simplified equation based on the ratio :math:`\\eta` between the - permittivity of the two materials at the interface in this case: - - .. math:: - - \\epsilon_{eff} = \\eta \\epsilon_{si} + (1 - \\eta) \\epsilon_{air} - - .. TODO check the actual implementation to be accurate here. - - .. image:: ../../_static/img/subpixel_permittivity_1d.png - - However, in this 1D case, this averaging is accurate because the dominant electric field is parallel to the - dielectric grid points. - - You can learn more about the subpixel averaging derivation from Maxwell's equations in 1D in this lecture: - `Introduction to subpixel averaging `_. - - **2D & 3D Usage Caveats** - - * In 2D, the subpixel averaging implementation depends on the polarization (:math:`s` or :math:`p`) of the - incident electric field on the interface. - - * In 3D, the subpixel averaging is implemented with tensorial averaging due to arbitrary surface and field - spatial orientations. - - - See Also - -------- - - **Lectures:** - * `Introduction to subpixel averaging `_ - * `Dielectric constant assignment on Yee grids `_ - """ - symmetry: Tuple[Symmetry, Symmetry, Symmetry] = pydantic.Field( (0, 0, 0), title="Symmetries", @@ -1917,14 +1886,6 @@ class Simulation(AbstractYeeGridSimulation): "Note that the vectorial nature of the fields must be taken into account to correctly " "determine the symmetry value.", ) - - pec_conformal_mesh_spec: ConformalMeshSpecType = pydantic.Field( - StaircasingConformalMeshSpec(), - title="Conformal mesh specifications", - description="Conformal mesh specifications applied to PEC strucures.", - discriminator=TYPE_TAG_STR, - ) - """ You should set the ``symmetry`` parameter in your :class:`Simulation` object using a tuple of integers defining reflection symmetry across a plane bisecting the simulation domain normal to the x-, y-, and z-axis. @@ -3760,9 +3721,12 @@ def plot_3d(self, width=800, height=800) -> None: @cached_property def scaled_courant(self) -> float: """When conformal mesh is applied, courant number is scaled down depending on `conformal_mesh_spec`.""" - if self.subpixel: - return self.courant * self.pec_conformal_mesh_spec.courant_ratio - return self.courant + + mediums = self.scene.mediums + contain_pec_structures = any(medium.is_pec for medium in mediums) + return self.courant * self._subpixel.courant_ratio( + contain_pec_structures=contain_pec_structures + ) @cached_property def dt(self) -> float: diff --git a/tidy3d/components/subpixel_spec.py b/tidy3d/components/subpixel_spec.py new file mode 100644 index 000000000..39e53e13e --- /dev/null +++ b/tidy3d/components/subpixel_spec.py @@ -0,0 +1,154 @@ +# Defines specifications for subpixel averaging +from __future__ import annotations + +import pydantic.v1 as pd +from typing import Union + +from .base import Tidy3dBaseModel, cached_property +from .types import TYPE_TAG_STR + +# Default Courant number reduction rate in PEC conformal's scheme +DEFAULT_COURANT_REDUCTION_PEC_CONFORMAL = 0.3 + + +class AbstractSubpixelAveragingMethod(Tidy3dBaseModel): + """Base class defining how to handle material assignment on structure interfaces.""" + + @cached_property + def courant_ratio(self) -> float: + """The scaling ratio applied to Courant number so that the courant number + in the simulation is ``sim.courant * courant_ratio``. + """ + return 1.0 + + +class Staircasing(AbstractSubpixelAveragingMethod): + """Apply staircasing scheme to material assignment of Yee grids on structure boundaries. + + Note + ---- + For PEC interface, the algorithm is based on: + + A. Taflove and S. C. Hagness, "Computational electromagnetics: the + finite-difference time-domain method", Chapter 10.3 (2005). + """ + + +class PolarizedAveraging(AbstractSubpixelAveragingMethod): + """Apply a polarized subpixel averaging method to dielectric boundaries. + + Note + ---- + The algorithm is based on: + + A. Mohammadi, H. Nadgaran and M. Agio, "Contour-path effective + permittivities for the two-dimensional finite-difference + time-domain method", Optics express, 13(25), 10367-10381 (2005). + """ + + +DielectricSubpixelType = Union[Staircasing, PolarizedAveraging] + + +class VolumetricAveraging(AbstractSubpixelAveragingMethod): + """Apply volumetric averaging scheme to material properties of Yee grids on structure boundaries. + The material property is averaged in the volume surrounding the Yee grid. + """ + + staircase_normal_component: bool = pd.Field( + True, + title="Staircasing for field components substantially normal to interface", + description="Volumetric averaging works accurately if the electric field component " + "is substantially tangential to the interface. If ``True``, apply volumetric averaging only " + "if the field component is largely tangential to the interface; if ``False``, apply volumetric " + "averaging regardless of how field component orients with the interface.", + ) + + +MetalSubpixelType = Union[Staircasing, VolumetricAveraging] + + +class HeuristicPECStaircasing(AbstractSubpixelAveragingMethod): + """Apply a variant of staircasing scheme to PEC boundaries: the electric field grid is set to PEC + if the field is substantially parallel to the interface. + """ + + +class PECConformal(AbstractSubpixelAveragingMethod): + """Apply a subpixel averaging method known as conformal mesh scheme to PEC boundaries. + + Note + ---- + The algorithm is based on: + + S. Dey and R. Mittra, "A locally conformal finite-difference + time-domain (FDTD) algorithm for modeling three-dimensional + perfectly conducting objects", + IEEE Microwave and Guided Wave Letters, 7(9), 273 (1997). + + S. Benkler, N. Chavannes and N. Kuster, "A new 3-D conformal + PEC FDTD scheme with user-defined geometric precision and derived + stability criterion", + IEEE Transactions on Antennas and Propagation, 54(6), 1843 (2006). + """ + + timestep_reduction: float = pd.Field( + DEFAULT_COURANT_REDUCTION_PEC_CONFORMAL, + title="Time Step Size Reduction Rate", + description="Reduction factor between 0 and 1 such that the simulation's time step size " + "will be ``1 - timestep_reduction`` times its default value. " + "Accuracy can be improved with a smaller time step size, but simulation time increased as well.", + lt=1, + ge=0, + ) + + @cached_property + def courant_ratio(self) -> float: + """The scaling ratio applied to Courant number so that the courant number + in the simulation is ``sim.courant * courant_ratio``. + """ + return 1 - self.timestep_reduction + + +PECSubpixelType = Union[Staircasing, HeuristicPECStaircasing, PECConformal] + + +class SubpixelSpec(Tidy3dBaseModel): + """Defines specification for subpixel averaging schemes when added to ``Simulation.subpixel``.""" + + dielectric: DielectricSubpixelType = pd.Field( + PolarizedAveraging(), + title="Subpixel averaging method on dielectric material interfaces", + description="Subpixel averaging method applied to dielectric material interfaces.", + discriminator=TYPE_TAG_STR, + ) + + metal: MetalSubpixelType = pd.Field( + Staircasing(), + title="Subpixel averaging method on metallic material interfaces", + description="Subpixel averaging method applied to metallic structure interfaces. " + "A material is considered as metallic if its real part of relative permittivity " + "is less than 1 at the central frequency.", + discriminator=TYPE_TAG_STR, + ) + + pec: PECSubpixelType = pd.Field( + PECConformal(), + title="Subpixel averaging method on PEC interfaces", + description="Subpixel averaging method applied to PEC structure interfaces.", + discriminator=TYPE_TAG_STR, + ) + + @classmethod + def staircasing(cls) -> SubpixelSpec: + """Apply staircasing on all material boundaries.""" + return cls(dielectric=Staircasing(), metal=Staircasing(), pec=Staircasing()) + + def courant_ratio(self, contain_pec_structures: bool) -> float: + """The scaling ratio applied to Courant number so that the courant number + in the simulation is ``sim.courant * courant_ratio``. So far only PEC subpixel averaging + scheme requires deduction of Courant number. + """ + if contain_pec_structures: + return self.pec.courant_ratio + return 1.0 diff --git a/tidy3d/plugins/adjoint/components/simulation.py b/tidy3d/plugins/adjoint/components/simulation.py index e71d5bc94..ddd87b9c0 100644 --- a/tidy3d/plugins/adjoint/components/simulation.py +++ b/tidy3d/plugins/adjoint/components/simulation.py @@ -17,6 +17,7 @@ from ....components.monitor import FieldMonitor, PermittivityMonitor from ....components.monitor import ModeMonitor, DiffractionMonitor, Monitor from ....components.simulation import Simulation +from ....components.subpixel_spec import SubpixelSpec, Staircasing from ....components.data.monitor_data import FieldData, PermittivityData from ....components.structure import Structure from ....components.medium import AbstractMedium @@ -177,9 +178,12 @@ def _output_monitors_colocate_false(cls, val): @pd.validator("subpixel", always=True) def _subpixel_is_on(cls, val): - """Assert subpixel is on.""" - if not val: - raise AdjointError("'JaxSimulation.subpixel' must be 'True' to use adjoint plugin.") + """Assert dielectric subpixel is on.""" + if (isinstance(val, SubpixelSpec) and isinstance(val.dielectric, Staircasing)) or not val: + raise AdjointError( + "'JaxSimulation.subpixel' must be 'True' or a specific 'SubpixelSpec' " + "with no dielectric staircasing to use adjoint plugin." + ) return val @pd.validator("input_structures", always=True)