From 0fef33dc59d7bbf1e6e187f477f864ac8538522f Mon Sep 17 00:00:00 2001 From: Casey Wojcik Date: Thu, 6 Nov 2025 13:21:21 -0800 Subject: [PATCH] feat: add interpolation to EME (FXC-4152) --- CHANGELOG.md | 2 ++ schemas/EMESimulation.json | 13 ++++++++- tests/sims/full_fdtd.h5 | Bin 479304 -> 479344 bytes tests/sims/full_fdtd.json | 2 ++ tests/test_components/test_eme.py | 7 +++-- tests/test_components/test_mode_interp.py | 22 +++++++-------- tidy3d/components/data/dataset.py | 4 +++ tidy3d/components/data/monitor_data.py | 18 ++++++++++++ tidy3d/components/eme/data/sim_data.py | 20 ++++++++++++-- tidy3d/components/eme/grid.py | 19 +++++++------ tidy3d/components/eme/simulation.py | 18 ++++++++++++ tidy3d/components/mode/mode_solver.py | 4 --- tidy3d/components/mode/simulation.py | 3 -- tidy3d/components/mode_spec.py | 10 ++++++- tidy3d/components/monitor.py | 12 +++++--- tidy3d/components/validators.py | 32 ---------------------- 16 files changed, 117 insertions(+), 69 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 44f1a125d5..6af6d50b02 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -38,6 +38,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Added `interp_spec` in `ModeSpec` to allow downsampling and interpolation of waveguide modes in frequency. - Added warning if port mesh refinement is incompatible with the `GridSpec` in the `TerminalComponentModeler`. - Various types, e.g. different `Simulation` or `SimulationData` sub-classes, can be loaded from file directly with `Tidy3dBaseModel.from_file()`. +- Added `interp_spec` in `EMEModeSpec` to enable faster multi-frequency EME simulations. Note that the default is now `ModeInterpSpec.cheb(num_points=3, reduce_data=True)`; previously the computation was repeated at all frequencies. ### Breaking Changes - Edge singularity correction at PEC and lossy metal edges defaults to `True`. @@ -64,6 +65,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Simulation data of batch jobs are now automatically downloaded upon their individual completion in `Batch.run()`, avoiding waiting for the entire batch to reach completion. - Port names in `ModalComponentModeler` and `TerminalComponentModeler` can no longer include the `@` symbol. - Improved speed of convolutions for large inputs. +- Default value of `EMEModeSpec.interp_spec` is `ModeInterpSpec.cheb(num_points=3, reduce_data=True)` for faster multi-frequency EME simulations. ### Fixed - Ensured the legacy `Env` proxy mirrors `config.web` profile switches and preserves API URL. diff --git a/schemas/EMESimulation.json b/schemas/EMESimulation.json index c70989201a..8ff177f3c4 100644 --- a/schemas/EMESimulation.json +++ b/schemas/EMESimulation.json @@ -5294,7 +5294,18 @@ { "$ref": "#/definitions/ModeInterpSpec" } - ] + ], + "default": { + "attrs": {}, + "method": "poly", + "reduce_data": true, + "sampling_spec": { + "attrs": {}, + "num_points": 3, + "type": "ChebSampling" + }, + "type": "ModeInterpSpec" + } }, "num_modes": { "default": 1, diff --git a/tests/sims/full_fdtd.h5 b/tests/sims/full_fdtd.h5 index b0f831d307360d2548859d0970dc98103ebc1704..40730d6e309c40556deab984ca160fb53797057b 100644 GIT binary patch delta 15610 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 TNJn~i@Ixp?Iv3Dk$>9G31wWcd delta 16063 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 num_freqs.""" + """Test that ModeSolver does not warn when num_points >= num_freqs.""" sim = get_simple_sim() mode_spec = td.ModeSpec( num_modes=2, @@ -363,14 +363,14 @@ def test_mode_solver_warns_num_points(): ) plane = td.Box(center=(0, 0, 0), size=SIZE_2D) - with AssertLogLevel("WARNING", contains_str="Interpolation will be skipped"): + with AssertLogLevel(None): ms = ModeSolver( simulation=sim, plane=plane, freqs=FREQS_DENSE, mode_spec=mode_spec, ) - _ = ms.data_raw + _ = ms.data_raw def test_mode_solver_interp_spec_none(): @@ -1041,7 +1041,7 @@ def test_mode_solver_monitor_with_interp_spec(): def test_mode_monitor_warns_redundant_num_points(): - """Test warning when num_points >= number of frequencies in ModeMonitor.""" + """Test no warning when num_points >= number of frequencies in ModeMonitor.""" freqs = np.linspace(1e14, 2e14, 5) mode_spec = td.ModeSpec( num_modes=2, @@ -1049,7 +1049,7 @@ def test_mode_monitor_warns_redundant_num_points(): interp_spec=td.ModeInterpSpec.uniform(num_points=5, method="linear"), ) - with AssertLogLevel("WARNING", contains_str="Interpolation will be skipped"): + with AssertLogLevel(None): td.ModeMonitor( center=(0, 0, 0), size=SIZE_2D, @@ -1060,7 +1060,7 @@ def test_mode_monitor_warns_redundant_num_points(): def test_mode_solver_monitor_warns_redundant_num_points(): - """Test warning when num_points >= number of frequencies in ModeSolverMonitor.""" + """Test no warning when num_points >= number of frequencies in ModeSolverMonitor.""" freqs = np.linspace(1e14, 2e14, 5) mode_spec = td.ModeSpec( num_modes=2, @@ -1068,7 +1068,7 @@ def test_mode_solver_monitor_warns_redundant_num_points(): interp_spec=td.ModeInterpSpec.uniform(num_points=6, method="linear"), ) - with AssertLogLevel("WARNING", contains_str="Interpolation will be skipped"): + with AssertLogLevel(None): td.ModeSolverMonitor( center=(0, 0, 0), size=SIZE_2D, diff --git a/tidy3d/components/data/dataset.py b/tidy3d/components/data/dataset.py index e3e4307fc2..aed00f8aa6 100644 --- a/tidy3d/components/data/dataset.py +++ b/tidy3d/components/data/dataset.py @@ -139,6 +139,10 @@ def _interp_dataarray_in_freq( DataArray Interpolated data array with the same structure but new frequency points. """ + # if dataarray is already stored at the correct frequencies, do nothing + if np.array_equal(freqs, data.f): + return data + # Map 'poly' to xarray's 'barycentric' method xr_method = "barycentric" if method == "poly" else method diff --git a/tidy3d/components/data/monitor_data.py b/tidy3d/components/data/monitor_data.py index 79cd62b655..e99d8881c7 100644 --- a/tidy3d/components/data/monitor_data.py +++ b/tidy3d/components/data/monitor_data.py @@ -1286,6 +1286,22 @@ def to_zbf( return e_x, e_y + def _interpolated_copies_if_needed( + self, other: ElectromagneticFieldData + ) -> tuple[ElectromagneticFieldData, ElectromagneticFieldData]: + """Return interpolated copies of self, other if needed (different interp_spec).""" + mode_spec1 = self.monitor.mode_spec if isinstance(self, ModeSolverData) else None + mode_spec2 = other.monitor.mode_spec if isinstance(other, ModeSolverData) else None + if ( + mode_spec1 is not None + and mode_spec2 is not None + and self.monitor.mode_spec._same_nontrivial_interp_spec(other=other.monitor.mode_spec) + ): + return self, other + self_copy = self.interpolated_copy if isinstance(self, ModeSolverData) else self + other_copy = other.interpolated_copy if isinstance(other, ModeSolverData) else other + return self_copy, other_copy + class FieldData(FieldDataset, ElectromagneticFieldData): """ @@ -2685,6 +2701,8 @@ def _reduced_data(self) -> bool: @property def interpolated_copy(self) -> ModeSolverData: """Return a copy of the data with interpolated fields.""" + if self.monitor.mode_spec.interp_spec is None: + return self if not self._reduced_data: return self interpolated_data = self.interp_in_freq( diff --git a/tidy3d/components/eme/data/sim_data.py b/tidy3d/components/eme/data/sim_data.py index 03d16e65d9..05c1e6e739 100644 --- a/tidy3d/components/eme/data/sim_data.py +++ b/tidy3d/components/eme/data/sim_data.py @@ -197,11 +197,19 @@ def smatrix_in_basis( modes1 = port_modes1 if not modes2_provided: modes2 = port_modes2 - f1 = list(modes1.field_components.values())[0].f.values - f2 = list(modes2.field_components.values())[0].f.values + f1 = list(modes1.monitor.freqs) + f2 = list(modes2.monitor.freqs) f = np.array(sorted(set(f1).intersection(f2).intersection(self.simulation.freqs))) + mode_spec1 = modes1.monitor.mode_spec if isinstance(modes1, ModeData) else None + mode_spec2 = modes2.monitor.mode_spec if isinstance(modes2, ModeData) else None + + interp_spec1 = mode_spec1.interp_spec if mode_spec1 is not None else None + interp_spec2 = mode_spec2.interp_spec if mode_spec2 is not None else None + + modes1, modes2 = modes1._interpolated_copies_if_needed(other=modes2) + modes_in_1 = "mode_index" in list(modes1.field_components.values())[0].coords modes_in_2 = "mode_index" in list(modes2.field_components.values())[0].coords @@ -259,6 +267,10 @@ def smatrix_in_basis( overlaps1 = modes1.outer_dot(port_modes1, conjugate=False) if not modes_in_1: overlaps1 = overlaps1.expand_dims(dim={"mode_index_0": mode_index_1}, axis=1) + if interp_spec1 is not None: + overlaps1 = modes1._interp_dataarray_in_freq( + overlaps1, freqs=f, method=interp_spec1.method + ) O1 = overlaps1.sel(f=f, mode_index_1=keep_mode_inds1) O1out = O1.rename(mode_index_0="mode_index_out", mode_index_1="mode_index_out_old") @@ -288,6 +300,10 @@ def smatrix_in_basis( overlaps2 = modes2.outer_dot(port_modes2, conjugate=False) if not modes_in_2: overlaps2 = overlaps2.expand_dims(dim={"mode_index_0": mode_index_2}, axis=1) + if interp_spec2 is not None: + overlaps2 = modes2._interp_dataarray_in_freq( + overlaps2, freqs=f, method=interp_spec2.method + ) O2 = overlaps2.sel(f=f, mode_index_1=keep_mode_inds2) O2out = O2.rename(mode_index_0="mode_index_out", mode_index_1="mode_index_out_old") diff --git a/tidy3d/components/eme/grid.py b/tidy3d/components/eme/grid.py index 2ed22e26ca..7b1b548784 100644 --- a/tidy3d/components/eme/grid.py +++ b/tidy3d/components/eme/grid.py @@ -11,9 +11,9 @@ from tidy3d.components.base import Tidy3dBaseModel, skip_if_fields_missing from tidy3d.components.geometry.base import Box from tidy3d.components.grid.grid import Coords1D -from tidy3d.components.mode_spec import ModeSpec +from tidy3d.components.mode_spec import ModeInterpSpec, ModeSpec from tidy3d.components.structure import Structure -from tidy3d.components.types import ArrayFloat1D, Axis, Coordinate, Size, TrackFreq +from tidy3d.components.types import ArrayFloat1D, Axis, Coordinate, Size from tidy3d.constants import RADIAN, fp_eps, inf from tidy3d.exceptions import SetupError, ValidationError @@ -26,13 +26,14 @@ class EMEModeSpec(ModeSpec): """Mode spec for EME cells. Overrides some of the defaults and allowed values.""" - track_freq: Union[TrackFreq, None] = pd.Field( - None, - title="Mode Tracking Frequency", - description="Parameter that turns on/off mode tracking based on their similarity. " - "Can take values ``'lowest'``, ``'central'``, or ``'highest'``, which correspond to " - "mode tracking based on the lowest, central, or highest frequency. " - "If ``None`` no mode tracking is performed, which is the default for best performance.", + interp_spec: Optional[ModeInterpSpec] = pd.Field( + ModeInterpSpec.cheb(num_points=3, reduce_data=True), + title="Mode frequency interpolation specification", + description="Specification for computing modes at a reduced set of frequencies and " + "interpolating to obtain results at all requested frequencies. This can significantly " + "reduce computational cost for broadband simulations where modes vary smoothly with " + "frequency. Requires frequency tracking to be enabled (``sort_spec.track_freq`` must " + "not be ``None``) to ensure consistent mode ordering across frequencies.", ) angle_theta: Literal[0.0] = pd.Field( diff --git a/tidy3d/components/eme/simulation.py b/tidy3d/components/eme/simulation.py index 84b3fec261..6569d58a73 100644 --- a/tidy3d/components/eme/simulation.py +++ b/tidy3d/components/eme/simulation.py @@ -722,6 +722,12 @@ def _validate_sweep_spec(self) -> None: "which is not compatible with 'EMELengthSweep'." ) elif isinstance(self.sweep_spec, EMEFreqSweep): + log.warning( + "'EMEFreqSweep' is deprecated. Instead, it is recommended to use " + "'EMESimulation.freqs' directly, and set " + "'EMEModeSpec.interp_spec' as desired to balance " + "performance and accuracy." + ) for i, scale_factor in enumerate(self.sweep_spec.freq_scale_factors): scaled_freqs = np.array(self.freqs) * scale_factor if np.min(scaled_freqs) < MIN_FREQUENCY: @@ -1007,6 +1013,18 @@ def _monitor_freqs(self, monitor: Monitor) -> list[pd.NonNegativeFloat]: return list(self.freqs) return list(monitor.freqs) + def _monitor_mode_freqs(self, monitor: EMEModeSolverMonitor) -> list[pd.NonNegativeFloat]: + """Monitor frequencies.""" + freqs = set() + cell_inds = self._monitor_eme_cell_indices(monitor=monitor) + for cell_ind in cell_inds: + interp_spec = self.eme_grid.mode_specs[cell_ind].interp_spec + if interp_spec is None: + freqs |= set(self.freqs) + else: + freqs |= set(interp_spec.sampling_points(self.freqs)) + return list(freqs) + def _monitor_num_freqs(self, monitor: Monitor) -> int: """Total number of freqs included in monitor.""" return len(self._monitor_freqs(monitor=monitor)) diff --git a/tidy3d/components/mode/mode_solver.py b/tidy3d/components/mode/mode_solver.py index 74218e96b8..3d41c2ffb8 100644 --- a/tidy3d/components/mode/mode_solver.py +++ b/tidy3d/components/mode/mode_solver.py @@ -75,7 +75,6 @@ from tidy3d.components.types.mode_spec import ModeSpecType from tidy3d.components.types.monitor_data import ModeSolverDataType from tidy3d.components.validators import ( - _warn_interp_num_points, validate_freqs_min, validate_freqs_not_empty, ) @@ -515,9 +514,6 @@ def data_raw(self) -> ModeSolverDataType: A mode solver data type object containing the effective index and mode fields. """ - if self.mode_spec.interp_spec is not None: - _warn_interp_num_points(self.mode_spec.interp_spec, self.freqs) - if self.mode_spec.angle_rotation and np.abs(self.mode_spec.angle_theta) > 0: return self.rotated_mode_solver_data diff --git a/tidy3d/components/mode/simulation.py b/tidy3d/components/mode/simulation.py index e964ce0d87..2e836c55ec 100644 --- a/tidy3d/components/mode/simulation.py +++ b/tidy3d/components/mode/simulation.py @@ -26,7 +26,6 @@ from tidy3d.components.source.field import ModeSource from tidy3d.components.types import TYPE_TAG_STR, Ax, Direction, EMField, FreqArray from tidy3d.components.types.mode_spec import ModeSpecType -from tidy3d.components.validators import validate_interp_num_points from tidy3d.constants import C_0 from tidy3d.exceptions import SetupError, ValidationError from tidy3d.log import log @@ -235,8 +234,6 @@ def plane_in_sim_bounds(cls, val, values): raise SetupError("'ModeSimulation.plane' must intersect 'ModeSimulation.geometry.") return val - _warn_interp_num_points = validate_interp_num_points() - def _post_init_validators(self) -> None: """Call validators taking `self` that get run after init.""" _ = self._mode_solver diff --git a/tidy3d/components/mode_spec.py b/tidy3d/components/mode_spec.py index 0ae0256c80..0dfb12b531 100644 --- a/tidy3d/components/mode_spec.py +++ b/tidy3d/components/mode_spec.py @@ -433,7 +433,7 @@ def sampling_points(self, freqs: FreqArray) -> FreqArray: >>> interp_spec = ModeInterpSpec.cheb(num_points=10) >>> sampling_freqs = interp_spec.sampling_points(freqs) """ - if self.num_points > len(freqs): + if self.num_points >= len(freqs): return freqs return self.sampling_spec.sampling_points(freqs) @@ -738,6 +738,14 @@ def _is_interp_spec_applied(self, freqs: FreqArray) -> bool: """Whether interp_spec is used to compute modes at the given frequencies.""" return self.interp_spec is not None and self.interp_spec.num_points < len(freqs) + def _same_nontrivial_interp_spec(self, other: ModeSpec) -> bool: + """Whether two mode specs have identical nontrivial interp specs.""" + return ( + self.interp_spec is not None + and other.interp_spec is not None + and self.interp_spec == other.interp_spec + ) + class ModeSpec(AbstractModeSpec): """ diff --git a/tidy3d/components/monitor.py b/tidy3d/components/monitor.py index 1739dbe40f..df4ec33049 100644 --- a/tidy3d/components/monitor.py +++ b/tidy3d/components/monitor.py @@ -37,7 +37,6 @@ assert_plane, validate_freqs_min, validate_freqs_not_empty, - validate_interp_num_points, ) from .viz import ARROW_ALPHA, ARROW_COLOR_MONITOR @@ -429,16 +428,21 @@ def _warn_num_modes(cls, val, values): ) return val + @property + def _stored_freqs(self) -> list[float]: + """Return actually stored frequencies of the data.""" + return self.mode_spec._sampling_freqs_mode_solver_data(freqs=self.freqs) + def _storage_size_solver(self, num_cells: int, tmesh: ArrayFloat1D) -> int: """Size of intermediate data recorded by the monitor during a solver run.""" # Need to store all fields on the mode surface - bytes_single = BYTES_COMPLEX * num_cells * len(self.freqs) * self.mode_spec.num_modes * 6 + bytes_single = ( + BYTES_COMPLEX * num_cells * len(self._stored_freqs) * self.mode_spec.num_modes * 6 + ) if self.mode_spec.precision == "double": return 2 * bytes_single return bytes_single - _warn_interp_num_points = validate_interp_num_points() - class FieldMonitor(AbstractFieldMonitor, FreqMonitor): """:class:`Monitor` that records electromagnetic fields in the frequency domain. diff --git a/tidy3d/components/validators.py b/tidy3d/components/validators.py index d1efc03018..c6a9d9c640 100644 --- a/tidy3d/components/validators.py +++ b/tidy3d/components/validators.py @@ -494,35 +494,3 @@ def _warn_traced_arg(cls, val, values): return val return _warn_traced_arg - - -def _warn_interp_num_points(interp_spec, freqs) -> None: - """Warn if the number of sampling points for interpolation is greater than or equal to the number of target frequencies.""" - - num_freqs = len(freqs) - - if interp_spec.num_points >= num_freqs: - log.warning( - f"'interp_spec.num_points' ({interp_spec.num_points}) is greater than or equal to " - f"the number of frequencies ({num_freqs}). Interpolation will be skipped and " - f"modes will be computed at all {num_freqs} frequencies.", - custom_loc=["mode_spec", "interp_spec", "num_points"], - ) - - -def validate_interp_num_points(): - @pydantic.root_validator(allow_reuse=True) - @skip_if_fields_missing(["freqs", "mode_spec"], root=True) - def _validate_warn_interp_num_points(cls, values): - """Warn if the number of sampling points for interpolation is greater than or equal to the number of target frequencies.""" - - interp_spec = values.get("mode_spec").interp_spec - if interp_spec is None: - return values - - freqs = values.get("freqs") - _warn_interp_num_points(interp_spec, freqs) - - return values - - return _validate_warn_interp_num_points