Skip to content

Commit 3676cf9

Browse files
committed
Add interpolation to EME
1 parent f3b119b commit 3676cf9

File tree

9 files changed

+60
-16
lines changed

9 files changed

+60
-16
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
3636
- `tidy3d.plugins.design.DesignSpace.run(..., fn_post=...)` now accepts a `priority` keyword to propagate vGPU queue priority to all automatically batched simulations.
3737
- Introduced `BroadbandPulse` for exciting simulations across a wide frequency spectrum.
3838
- Added `interp_spec` in `ModeSpec` to allow downsampling and interpolation of waveguide modes in frequency.
39+
- Added `interp_spec` in `EMEModeSpec` to enable faster multi-frequency EME simulations.
3940

4041
### Breaking Changes
4142
- Edge singularity correction at PEC and lossy metal edges defaults to `True`.

tests/test_components/test_eme.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -911,7 +911,9 @@ def _get_mode_solver_data(modes_out=False, num_modes=3):
911911
size=(td.inf, td.inf, 0),
912912
center=(0, 0, offset),
913913
freqs=[td.C_0],
914-
mode_spec=td.ModeSpec(num_modes=num_modes),
914+
mode_spec=td.ModeSpec(
915+
num_modes=num_modes, interp_spec=td.ModeInterpSpec.cheb(num_points=3, reduce_data=True)
916+
),
915917
name=name,
916918
)
917919
eme_mode_data = _get_eme_mode_solver_data()

tidy3d/components/data/dataset.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,10 @@ def _interp_dataarray_in_freq(
139139
DataArray
140140
Interpolated data array with the same structure but new frequency points.
141141
"""
142+
# if dataarray is already stored at the correct frequencies, do nothing
143+
if np.array_equal(freqs, data.f):
144+
return data
145+
142146
# Map 'poly' to xarray's 'barycentric' method
143147
xr_method = "barycentric" if method == "poly" else method
144148

tidy3d/components/eme/data/sim_data.py

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -197,11 +197,27 @@ def smatrix_in_basis(
197197
modes1 = port_modes1
198198
if not modes2_provided:
199199
modes2 = port_modes2
200-
f1 = list(modes1.field_components.values())[0].f.values
201-
f2 = list(modes2.field_components.values())[0].f.values
200+
f1 = list(modes1.monitor.freqs)
201+
f2 = list(modes2.monitor.freqs)
202202

203203
f = np.array(sorted(set(f1).intersection(f2).intersection(self.simulation.freqs)))
204204

205+
interp_spec1 = (
206+
modes1.monitor.mode_spec.interp_spec if isinstance(modes1, ModeData) else None
207+
)
208+
interp_spec2 = (
209+
modes2.monitor.mode_spec.interp_spec if isinstance(modes2, ModeData) else None
210+
)
211+
212+
interp_overlaps = False
213+
if interp_spec1 is not None and interp_spec2 is not None and interp_spec1 == interp_spec2:
214+
interp_overlaps = True
215+
else:
216+
if interp_spec1 is not None:
217+
modes1 = modes1.interpolated_copy
218+
if interp_spec2 is not None:
219+
modes2 = modes2.interpolated_copy
220+
205221
modes_in_1 = "mode_index" in list(modes1.field_components.values())[0].coords
206222
modes_in_2 = "mode_index" in list(modes2.field_components.values())[0].coords
207223

@@ -259,6 +275,10 @@ def smatrix_in_basis(
259275
overlaps1 = modes1.outer_dot(port_modes1, conjugate=False)
260276
if not modes_in_1:
261277
overlaps1 = overlaps1.expand_dims(dim={"mode_index_0": mode_index_1}, axis=1)
278+
if interp_overlaps:
279+
overlaps1 = modes1._interp_dataarray_in_freq(
280+
overlaps1, freqs=f, method=interp_spec1.method
281+
)
262282
O1 = overlaps1.sel(f=f, mode_index_1=keep_mode_inds1)
263283

264284
O1out = O1.rename(mode_index_0="mode_index_out", mode_index_1="mode_index_out_old")
@@ -288,6 +308,10 @@ def smatrix_in_basis(
288308
overlaps2 = modes2.outer_dot(port_modes2, conjugate=False)
289309
if not modes_in_2:
290310
overlaps2 = overlaps2.expand_dims(dim={"mode_index_0": mode_index_2}, axis=1)
311+
if interp_overlaps:
312+
overlaps2 = modes2._interp_dataarray_in_freq(
313+
overlaps2, freqs=f, method=interp_spec2.method
314+
)
291315
O2 = overlaps2.sel(f=f, mode_index_1=keep_mode_inds2)
292316

293317
O2out = O2.rename(mode_index_0="mode_index_out", mode_index_1="mode_index_out_old")

tidy3d/components/eme/grid.py

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,9 @@
1111
from tidy3d.components.base import Tidy3dBaseModel, skip_if_fields_missing
1212
from tidy3d.components.geometry.base import Box
1313
from tidy3d.components.grid.grid import Coords1D
14-
from tidy3d.components.mode_spec import ModeSpec
14+
from tidy3d.components.mode_spec import ModeInterpSpec, ModeSpec
1515
from tidy3d.components.structure import Structure
16-
from tidy3d.components.types import ArrayFloat1D, Axis, Coordinate, Size, TrackFreq
16+
from tidy3d.components.types import ArrayFloat1D, Axis, Coordinate, Size
1717
from tidy3d.constants import RADIAN, fp_eps, inf
1818
from tidy3d.exceptions import SetupError, ValidationError
1919

@@ -26,13 +26,14 @@
2626
class EMEModeSpec(ModeSpec):
2727
"""Mode spec for EME cells. Overrides some of the defaults and allowed values."""
2828

29-
track_freq: Union[TrackFreq, None] = pd.Field(
30-
None,
31-
title="Mode Tracking Frequency",
32-
description="Parameter that turns on/off mode tracking based on their similarity. "
33-
"Can take values ``'lowest'``, ``'central'``, or ``'highest'``, which correspond to "
34-
"mode tracking based on the lowest, central, or highest frequency. "
35-
"If ``None`` no mode tracking is performed, which is the default for best performance.",
29+
interp_spec: Optional[ModeInterpSpec] = pd.Field(
30+
ModeInterpSpec.cheb(num_points=3, reduce_data=True),
31+
title="Mode frequency interpolation specification",
32+
description="Specification for computing modes at a reduced set of frequencies and "
33+
"interpolating to obtain results at all requested frequencies. This can significantly "
34+
"reduce computational cost for broadband simulations where modes vary smoothly with "
35+
"frequency. Requires frequency tracking to be enabled (``sort_spec.track_freq`` must "
36+
"not be ``None``) to ensure consistent mode ordering across frequencies.",
3637
)
3738

3839
angle_theta: Literal[0.0] = pd.Field(

tidy3d/components/eme/simulation.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1007,6 +1007,18 @@ def _monitor_freqs(self, monitor: Monitor) -> list[pd.NonNegativeFloat]:
10071007
return list(self.freqs)
10081008
return list(monitor.freqs)
10091009

1010+
def _monitor_mode_freqs(self, monitor: EMEModeSolverMonitor) -> list[pd.NonNegativeFloat]:
1011+
"""Monitor frequencies."""
1012+
freqs = set()
1013+
cell_inds = self._monitor_eme_cell_indices(monitor=monitor)
1014+
for cell_ind in cell_inds:
1015+
interp_spec = self.eme_grid.mode_specs[cell_ind].interp_spec
1016+
if interp_spec is None:
1017+
freqs |= set(self.freqs)
1018+
else:
1019+
freqs |= set(interp_spec.sampling_points(self.freqs))
1020+
return list(freqs)
1021+
10101022
def _monitor_num_freqs(self, monitor: Monitor) -> int:
10111023
"""Total number of freqs included in monitor."""
10121024
return len(self._monitor_freqs(monitor=monitor))

tidy3d/components/mode/mode_solver.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -515,8 +515,8 @@ def data_raw(self) -> ModeSolverDataType:
515515
A mode solver data type object containing the effective index and mode fields.
516516
"""
517517

518-
if self.mode_spec.interp_spec is not None:
519-
_warn_interp_num_points(self.mode_spec.interp_spec, self.freqs)
518+
# if self.mode_spec.interp_spec is not None:
519+
# _warn_interp_num_points(self.mode_spec.interp_spec, self.freqs)
520520

521521
if self.mode_spec.angle_rotation and np.abs(self.mode_spec.angle_theta) > 0:
522522
return self.rotated_mode_solver_data

tidy3d/components/mode_spec.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -433,7 +433,7 @@ def sampling_points(self, freqs: FreqArray) -> FreqArray:
433433
>>> interp_spec = ModeInterpSpec.cheb(num_points=10)
434434
>>> sampling_freqs = interp_spec.sampling_points(freqs)
435435
"""
436-
if self.num_points > len(freqs):
436+
if self.num_points >= len(freqs):
437437
return freqs
438438
return self.sampling_spec.sampling_points(freqs)
439439

tidy3d/components/monitor.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -437,7 +437,7 @@ def _storage_size_solver(self, num_cells: int, tmesh: ArrayFloat1D) -> int:
437437
return 2 * bytes_single
438438
return bytes_single
439439

440-
_warn_interp_num_points = validate_interp_num_points()
440+
# _warn_interp_num_points = validate_interp_num_points()
441441

442442

443443
class FieldMonitor(AbstractFieldMonitor, FreqMonitor):

0 commit comments

Comments
 (0)