Skip to content

Commit 9bff29c

Browse files
committed
feat: add EMESimulationData.coeffs (FXC-4275)
1 parent 603c8a8 commit 9bff29c

File tree

7 files changed

+128
-10
lines changed

7 files changed

+128
-10
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
99

1010
### Added
1111
- Added `symmetrize_mirror`, `symmetrize_rotation`, `symmetrize_diagonal` functions to the autograd plugin. They can be used for enforcing symmetries in topology optimization.
12+
- Added `EMESimulationData.coeffs` to store coefficients from the EME solver, including interface S matrices and effective propagation indices.
1213

1314
### Changed
1415
- Removed validator that would warn if `PerturbationMedium` values could become numerically unstable, since an error will anyway be raised if this actually happens when the medium is converted using actual perturbation data.

tests/test_components/test_eme.py

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -311,16 +311,16 @@ def test_eme_simulation():
311311

312312
# test warning for not providing wavelength in autogrid
313313
grid_spec = td.GridSpec.auto(min_steps_per_wvl=20)
314+
sim = sim.updated_copy(grid_spec=grid_spec)
314315
with AssertLogLevel("INFO", contains_str="wavelength"):
315-
sim = sim.updated_copy(grid_spec=grid_spec)
316+
_ = sim.updated_copy(monitors=[])
316317
# multiple freqs are ok, but not for autogrid
317318
_ = sim.updated_copy(
318319
grid_spec=td.GridSpec.uniform(dl=0.2), freqs=[10000000000.0, *list(sim.freqs)]
319320
)
320321
with AssertLogLevel("INFO", contains_str="wavelength"):
321322
_ = sim.updated_copy(
322-
freqs=[*list(sim.freqs), 10000000000.0],
323-
grid_spec=grid_spec,
323+
freqs=[*list(sim.freqs), 10000000000.0], grid_spec=grid_spec, monitors=[]
324324
)
325325

326326
# test port offsets
@@ -558,12 +558,14 @@ def test_eme_simulation():
558558
constraint="passive",
559559
eme_grid_spec=td.EMEUniformGrid(num_cells=1, mode_spec=td.EMEModeSpec(num_modes=40)),
560560
grid_spec=sim.grid_spec.updated_copy(wavelength=1),
561+
monitors=[],
561562
)
562563
sim_good.validate_pre_upload()
563564
sim_good = sim.updated_copy(
564565
constraint=None,
565566
eme_grid_spec=td.EMEUniformGrid(num_cells=1, mode_spec=td.EMEModeSpec(num_modes=60)),
566567
grid_spec=sim.grid_spec.updated_copy(wavelength=1),
568+
monitors=[],
567569
)
568570
sim_good.validate_pre_upload()
569571
# warn about num modes with constraint
@@ -1305,7 +1307,7 @@ def test_eme_periodicity():
13051307

13061308
# remove the field monitor, now it passes
13071309
desired_cell_index_pairs = set([(i, i + 1) for i in range(6)] + [(5, 1)])
1308-
with AssertLogLevel(None):
1310+
with AssertLogLevel("WARNING", contains_str="deprecated"):
13091311
sim = sim.updated_copy(
13101312
monitors=[m for m in sim.monitors if not isinstance(m, td.EMEFieldMonitor)]
13111313
)

tidy3d/__init__.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -185,6 +185,8 @@
185185
ChargeDataArray,
186186
DiffractionDataArray,
187187
EMECoefficientDataArray,
188+
EMEFluxDataArray,
189+
EMEInterfaceSMatrixDataArray,
188190
EMEModeIndexDataArray,
189191
EMEScalarFieldDataArray,
190192
EMEScalarModeFieldDataArray,
@@ -605,8 +607,10 @@ def set_logging_level(level: str) -> None:
605607
"EMEFieldData",
606608
"EMEFieldDataset",
607609
"EMEFieldMonitor",
610+
"EMEFluxDataArray",
608611
"EMEFreqSweep",
609612
"EMEGrid",
613+
"EMEInterfaceSMatrixDataArray",
610614
"EMELengthSweep",
611615
"EMEModeIndexDataArray",
612616
"EMEModeSolverData",

tidy3d/components/data/data_array.py

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1188,6 +1188,31 @@ class EMESMatrixDataArray(DataArray):
11881188
_data_attrs = {"long_name": "scattering matrix element"}
11891189

11901190

1191+
class EMEInterfaceSMatrixDataArray(DataArray):
1192+
"""Scattering matrix elements at a single cell interface for a fixed pair of ports,
1193+
possibly with an extra sweep index.
1194+
Example
1195+
-------
1196+
>>> mode_index_in = [0, 1]
1197+
>>> mode_index_out = [0, 1, 2]
1198+
>>> eme_cell_index = [2, 4]
1199+
>>> f = [2e14]
1200+
>>> sweep_index = np.arange(10)
1201+
>>> coords = dict(
1202+
... f=f,
1203+
... sweep_index=sweep_index,
1204+
... eme_cell_index=eme_cell_index,
1205+
... mode_index_out=mode_index_out,
1206+
... mode_index_in=mode_index_in,
1207+
... )
1208+
>>> fd = EMEInterfaceSMatrixDataArray((1 + 1j) * np.random.random((1, 10, 2, 3, 2)), coords=coords)
1209+
"""
1210+
1211+
__slots__ = ()
1212+
_dims = ("f", "sweep_index", "eme_cell_index", "mode_index_out", "mode_index_in")
1213+
_data_attrs = {"long_name": "scattering matrix element"}
1214+
1215+
11911216
class EMEModeIndexDataArray(DataArray):
11921217
"""Complex-valued effective propagation index of an EME mode,
11931218
also indexed by EME cell.
@@ -1206,6 +1231,23 @@ class EMEModeIndexDataArray(DataArray):
12061231
_data_attrs = {"long_name": "Propagation index"}
12071232

12081233

1234+
class EMEFluxDataArray(DataArray):
1235+
"""Power flux of an EME mode, also indexed by EME cell.
1236+
1237+
Example
1238+
-------
1239+
>>> f = [2e14, 3e14]
1240+
>>> mode_index = np.arange(4)
1241+
>>> eme_cell_index = np.arange(5)
1242+
>>> coords = dict(f=f, mode_index=mode_index, eme_cell_index=eme_cell_index)
1243+
>>> data = EMEModeIndexDataArray((1+1j) * np.random.random((2,4,5)), coords=coords)
1244+
"""
1245+
1246+
__slots__ = ()
1247+
_dims = ("f", "sweep_index", "eme_cell_index", "mode_index")
1248+
_data_attrs = {"units": WATT, "long_name": "flux"}
1249+
1250+
12091251
class ChargeDataArray(DataArray):
12101252
"""Charge data array.
12111253
@@ -1595,8 +1637,10 @@ def _make_impedance_data_array(result: DataArray) -> ImpedanceResultType:
15951637
EMEScalarFieldDataArray,
15961638
EMEScalarModeFieldDataArray,
15971639
EMESMatrixDataArray,
1640+
EMEInterfaceSMatrixDataArray,
15981641
EMECoefficientDataArray,
15991642
EMEModeIndexDataArray,
1643+
EMEFluxDataArray,
16001644
EMEFreqModeDataArray,
16011645
ChargeDataArray,
16021646
SteadyVoltageDataArray,

tidy3d/components/eme/data/dataset.py

Lines changed: 60 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,14 @@
22

33
from __future__ import annotations
44

5+
from typing import Optional
6+
57
import pydantic.v1 as pd
68

79
from tidy3d.components.data.data_array import (
810
EMECoefficientDataArray,
11+
EMEFluxDataArray,
12+
EMEInterfaceSMatrixDataArray,
913
EMEModeIndexDataArray,
1014
EMEScalarFieldDataArray,
1115
EMEScalarModeFieldDataArray,
@@ -40,21 +44,72 @@ class EMESMatrixDataset(Dataset):
4044

4145

4246
class EMECoefficientDataset(Dataset):
43-
"""Dataset storing expansion coefficients for the modes in a cell.
47+
"""Dataset storing various coefficients related to the EME simulation.
48+
These coefficients can be used for debugging or optimization.
49+
50+
The ``A`` and ``B`` fields store the expansion coefficients for the modes in a cell.
4451
These are defined at the cell centers.
52+
53+
The ``n_complex`` and ``flux`` fields respectively store the complex-valued effective
54+
propagation index and the power flux associated with the modes used in the
55+
EME calculation.
56+
57+
The ``interface_Sij`` fields store the S matrices associated with the interfaces
58+
between EME cells.
4559
"""
4660

47-
A: EMECoefficientDataArray = pd.Field(
48-
...,
61+
A: Optional[EMECoefficientDataArray] = pd.Field(
62+
None,
4963
title="A coefficient",
5064
description="Coefficient for forward mode in this cell.",
5165
)
52-
B: EMECoefficientDataArray = pd.Field(
53-
...,
66+
67+
B: Optional[EMECoefficientDataArray] = pd.Field(
68+
None,
5469
title="B coefficient",
5570
description="Coefficient for backward mode in this cell.",
5671
)
5772

73+
n_complex: Optional[EMEModeIndexDataArray] = pd.Field(
74+
None,
75+
title="Propagation Index",
76+
description="Complex-valued effective propagation indices associated with the EME modes.",
77+
)
78+
79+
flux: Optional[EMEFluxDataArray] = pd.Field(
80+
None,
81+
title="Flux",
82+
description="Power flux of the EME modes.",
83+
)
84+
85+
interface_S11: Optional[EMEInterfaceSMatrixDataArray] = pd.Field(
86+
None,
87+
title="Interface S11",
88+
description="S matrix relating output modes at port 1 to input modes at port 1 "
89+
"at a given interface between EME cells.",
90+
)
91+
92+
interface_S12: Optional[EMEInterfaceSMatrixDataArray] = pd.Field(
93+
None,
94+
title="Interface S12",
95+
description="S matrix relating output modes at port 1 to input modes at port 2 "
96+
"at a given interface between EME cells.",
97+
)
98+
99+
interface_S21: Optional[EMEInterfaceSMatrixDataArray] = pd.Field(
100+
None,
101+
title="Interface S21",
102+
description="S matrix relating output modes at port 2 to input modes at port 1 "
103+
"at a given interface between EME cells.",
104+
)
105+
106+
interface_S22: Optional[EMEInterfaceSMatrixDataArray] = pd.Field(
107+
None,
108+
title="Interface S22",
109+
description="S matrix relating output modes at port 2 to input modes at port 2 "
110+
"at a given interface between EME cells.",
111+
)
112+
58113

59114
class EMEFieldDataset(ElectromagneticFieldDataset):
60115
"""Dataset storing scalar components of E and H fields as a function of freq, mode_index, and port_index."""

tidy3d/components/eme/data/sim_data.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
from tidy3d.exceptions import SetupError
1818
from tidy3d.log import log
1919

20-
from .dataset import EMESMatrixDataset
20+
from .dataset import EMECoefficientDataset, EMESMatrixDataset
2121
from .monitor_data import EMEFieldData, EMEModeSolverData, EMEMonitorDataType
2222

2323

@@ -39,6 +39,12 @@ class EMESimulationData(AbstractYeeGridSimulationData):
3939
None, title="S Matrix", description="Scattering matrix of the EME simulation."
4040
)
4141

42+
coeffs: Optional[EMECoefficientDataset] = pd.Field(
43+
None,
44+
title="Coefficients",
45+
description="Coefficients from the EME simulation. Useful for debugging and optimization.",
46+
)
47+
4248
port_modes_raw: Optional[EMEModeSolverData] = pd.Field(
4349
None,
4450
title="Port Modes",

tidy3d/components/eme/simulation.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -752,6 +752,12 @@ def _validate_sweep_spec(self) -> None:
752752
def _validate_monitor_setup(self) -> None:
753753
"""Check monitor setup."""
754754
for i, monitor in enumerate(self.monitors):
755+
if isinstance(monitor, EMECoefficientMonitor):
756+
log.warning(
757+
"'EMECoefficientMonitor' is deprecated. "
758+
"The full coefficient data is stored in "
759+
"'EMESimulationData.coeffs'."
760+
)
755761
if isinstance(monitor, EMEMonitor):
756762
_ = self._monitor_eme_cell_indices(monitor=monitor)
757763
if (

0 commit comments

Comments
 (0)