Skip to content

Commit f6579c0

Browse files
Nearly all tests
1 parent b40978a commit f6579c0

File tree

7 files changed

+121
-29
lines changed

7 files changed

+121
-29
lines changed

tests/test_plugins/smatrix/test_terminal_component_modeler.py

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -40,8 +40,8 @@ def run_component_modeler(
4040
sim_dict = modeler.sim_dict
4141
batch_data = {task_name: run_emulated(sim) for task_name, sim in sim_dict.items()}
4242
port_data = PortSimulationData(
43-
ports=[port for port in batch_data.keys()],
44-
data=[sim_data for sim_data in batch_data.values()],
43+
ports=list(batch_data.keys()),
44+
data=list(batch_data.values()),
4545
)
4646
modeler_data = TerminalComponentModelerData(modeler=modeler, data=port_data)
4747
monkeypatch.setattr(AbstractComponentModeler, "inv", lambda matrix: np.eye(len(modeler.ports)))
@@ -727,11 +727,11 @@ def test_port_impedance_check():
727727
Z_numpy = np.ones((50, 3))
728728
Z_numpy[:, 1] = -1.0
729729
# All ok if same sign for every frequency
730-
TerminalComponentModeler.check_port_impedance_sign(Z_numpy)
730+
TerminalComponentModelerData.check_port_impedance_sign(Z_numpy)
731731
Z_numpy[25, 1] = 1.0
732732
# Change of sign is unexpected
733733
with pytest.raises(Tidy3dError):
734-
TerminalComponentModeler.check_port_impedance_sign(Z_numpy)
734+
TerminalComponentModelerData.check_port_impedance_sign(Z_numpy)
735735

736736

737737
def test_antenna_helpers(monkeypatch, tmp_path):
@@ -756,6 +756,7 @@ def test_antenna_helpers(monkeypatch, tmp_path):
756756
# Run simulation to get data
757757
modeler_data = run_component_modeler(monkeypatch, modeler)
758758
rad_mon_data = modeler_data.data[radiation_monitor.name]
759+
sim_data = modeler_data.data[modeler_data.modeler.get_task_name(modeler.ports[0])]
759760

760761
# Test monitor helper
761762
found_mon = modeler.get_radiation_monitor_by_name(radiation_monitor.name)

tidy3d/plugins/smatrix/analysis/antenna.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -67,12 +67,14 @@ def get_antenna_metrics_data(
6767
# Retrieve associated simulation data
6868
combined_directivity_data = None
6969
for port, amplitude in port_dict.items():
70-
sim_data_port = terminal_component_modeler_data.data.data[port]
70+
sim_data_port = terminal_component_modeler_data.data[
71+
terminal_component_modeler_data.modeler.get_task_name(port)
72+
]
7173
radiation_data = sim_data_port[rad_mon.name]
7274

7375
a, b = compute_power_wave_amplitudes_at_each_port(
7476
modeler=terminal_component_modeler_data.modeler,
75-
port_reference_impedances=terminal_component_modeler_data.modeler.port_reference_impedances,
77+
port_reference_impedances=terminal_component_modeler_data.port_reference_impedances,
7678
sim_data=sim_data_port,
7779
)
7880
# Select a possible subset of frequencies

tidy3d/plugins/smatrix/analysis/terminal.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ def terminal_construct_smatrix(modeler_data: TerminalComponentModelerData) -> Te
5454

5555
# loop through source ports
5656
for port_in in modeler_data.modeler.ports:
57-
sim_data = modeler_data.data[port_in]
57+
sim_data = modeler_data.data[modeler_data.modeler.get_task_name(port=port_in)]
5858
a, b = compute_power_wave_amplitudes_at_each_port(
5959
modeler_data.modeler, port_impedances, sim_data
6060
)
@@ -95,7 +95,7 @@ def port_reference_impedances(modeler_data: TerminalComponentModelerData) -> Por
9595
for port in modeler_data.modeler.ports:
9696
if isinstance(port, WavePort):
9797
# Mode solver data for each wave port is stored in its associated SimulationData
98-
sim_data_port = modeler_data.data[port]
98+
sim_data_port = modeler_data.data[modeler_data.modeler.get_task_name(port=port)]
9999
# WavePorts have a port impedance calculated from its associated modal field distribution
100100
# and is frequency dependent.
101101
impedances = port.compute_port_impedance(sim_data_port).values

tidy3d/plugins/smatrix/data/modal.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,24 @@ class PortSimulationData(Tidy3dBaseModel):
1515
ports: tuple[PortReferenceType, ...]
1616
data: tuple[SimulationData, ...]
1717

18+
def __getitem__(self, port_name: str) -> SimulationData:
19+
"""
20+
Allows retrieving simulation data by the port name.
21+
22+
Args:
23+
port_name: The string name of the port to look up.
24+
25+
Returns:
26+
The SimulationData object corresponding to the given port name.
27+
28+
Raises:
29+
KeyError: If no port with the given name is found.
30+
"""
31+
for i, port_i in enumerate(self.ports):
32+
if port_i == port_name:
33+
return self.data[i]
34+
raise KeyError(f"Port '{port_name}' not found.")
35+
1836

1937
class ComponentModelerData(Tidy3dBaseModel):
2038
modeler: ComponentModeler = pd.Field(

tidy3d/plugins/smatrix/data/terminal.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,7 @@ def _monitor_data_at_port_amplitude(
110110
incident from the port into the system.
111111
"""
112112
a_raw, _ = self.compute_power_wave_amplitudes_at_each_port(
113-
self.port_reference_impedances, sim_data
113+
self.port_reference_impedances, sim_data=sim_data
114114
)
115115
a_raw_port = a_raw.sel(port=port.name)
116116
if not isinstance(a_port, FreqDataArray):
@@ -173,7 +173,7 @@ def port_reference_impedances(self) -> PortDataArray:
173173
"""
174174
from tidy3d.plugins.smatrix.analysis.terminal import port_reference_impedances
175175

176-
return port_reference_impedances(self.modeler)
176+
return port_reference_impedances(self)
177177

178178
def compute_power_wave_amplitudes_at_each_port(
179179
self, port_reference_impedances: PortDataArray, sim_data: SimulationData

tidy3d/plugins/smatrix/data/types.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
from __future__ import annotations
2+
3+
from typing import Union
4+
5+
from tidy3d.plugins.smatrix.data.modal import ComponentModelerData
6+
from tidy3d.plugins.smatrix.data.terminal import TerminalComponentModelerData
7+
8+
ComponentModelerDataType = Union[TerminalComponentModelerData, ComponentModelerData]

tidy3d/plugins/smatrix/run.py

Lines changed: 82 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
11
from __future__ import annotations
22

3+
import os
4+
35
from tidy3d.plugins.smatrix.component_modelers.modal import ComponentModeler
46
from tidy3d.plugins.smatrix.component_modelers.terminal import TerminalComponentModeler
57
from tidy3d.plugins.smatrix.component_modelers.types import (
68
ComponentModelerType,
79
)
8-
from tidy3d.plugins.smatrix.data.modal import ComponentModelerData, PortSimulationMap
10+
from tidy3d.plugins.smatrix.data.modal import ComponentModelerData, PortSimulationData
911
from tidy3d.plugins.smatrix.data.terminal import TerminalComponentModelerData
1012
from tidy3d.plugins.smatrix.data.types import ComponentModelerDataType
1113
from tidy3d.web import Batch, BatchData
@@ -19,47 +21,96 @@ def create_batch(
1921
file_name: str = "batch.hdf5",
2022
**kwargs,
2123
) -> Batch:
22-
batch = Batch(simulations=modeler.sim_dict, **kwargs)
23-
batch.to_file(file_name)
24+
"""Creates a simulation Batch from a component modeler and saves it to a file.
25+
26+
Args:
27+
modeler: The component modeler that defines the set of simulations.
28+
path_dir: Directory where the batch file will be saved.
29+
file_name: Name for the HDF5 file where the batch is stored.
30+
**kwargs: Additional keyword arguments passed to the `Batch` constructor.
2431
32+
Returns:
33+
The configured `Batch` object ready for execution.
34+
"""
35+
filepath = os.path.join(path_dir, file_name)
36+
batch = Batch(simulations=modeler.sim_dict, **kwargs)
37+
batch.to_file(filepath)
2538
return batch
2639

2740

2841
def compose_terminal_modeler_data(
2942
modeler: TerminalComponentModeler,
30-
batch_data: BatchData = None,
43+
batch_data: BatchData,
3144
) -> TerminalComponentModelerData:
32-
port_to_sim_data_map = {
33-
port_i: batch_data[modeler.get_task_name(port=port_i)] for port_i in modeler.ports
34-
}
35-
port_simulation_data = PortSimulationMap(data=port_to_sim_data_map)
45+
"""Assembles `TerminalComponentModelerData` from simulation results.
46+
47+
This function maps the simulation data from a completed batch run back to the
48+
ports of the terminal component modeler.
49+
50+
Args:
51+
modeler: The `TerminalComponentModeler` used to generate the simulations.
52+
batch_data: The results obtained from running the simulation `Batch`.
53+
54+
Returns:
55+
A `TerminalComponentModelerData` object containing the results mapped to
56+
their respective ports.
57+
"""
58+
ports = [modeler.get_task_name(port=port_i) for port_i in modeler.ports]
59+
data = [batch_data[modeler.get_task_name(port=port_i)] for port_i in modeler.ports]
60+
port_simulation_data = PortSimulationData(ports=ports, data=data)
3661
return TerminalComponentModelerData(modeler=modeler, data=port_simulation_data)
3762

3863

3964
def compose_component_modeler_data(
4065
modeler: ComponentModeler,
41-
batch_data: BatchData = None,
66+
batch_data: BatchData,
4267
) -> ComponentModelerData:
43-
port_to_sim_data_map = {
44-
port_i: batch_data[modeler.get_task_name(port=port_i)] for port_i in modeler.ports
45-
}
46-
port_simulation_data = PortSimulationMap(data=port_to_sim_data_map)
68+
"""Assembles `ComponentModelerData` from simulation results.
69+
70+
This function maps the simulation data from a completed batch run back to the
71+
ports of the component modeler.
72+
73+
Args:
74+
modeler: The `ComponentModeler` used to generate the simulations.
75+
batch_data: The results obtained from running the simulation `Batch`.
76+
77+
Returns:
78+
A `ComponentModelerData` object containing the results mapped to
79+
their respective ports.
80+
"""
81+
ports = [modeler.get_task_name(port=port_i) for port_i in modeler.ports]
82+
data = [batch_data[modeler.get_task_name(port=port_i)] for port_i in modeler.ports]
83+
port_simulation_data = PortSimulationData(ports=ports, data=data)
4784
return ComponentModelerData(modeler=modeler, data=port_simulation_data)
4885

4986

5087
def compose_modeler_data(
5188
modeler: ComponentModelerType,
52-
batch_data: BatchData = None,
89+
batch_data: BatchData,
5390
) -> ComponentModelerDataType:
54-
"""
55-
This method internally determines which functions to run to compose the corresponding ComponentModelerDataType.
91+
"""Selects the correct composer based on the modeler type and creates the data object.
92+
93+
This method acts as a dispatcher, inspecting the type of `modeler` to determine
94+
which composer function (`compose_component_modeler_data` or
95+
`compose_terminal_modeler_data`) to invoke.
96+
97+
Args:
98+
modeler: The component modeler, which can be either a `ComponentModeler` or
99+
a `TerminalComponentModeler`.
100+
batch_data: The results obtained from running the simulation `Batch`.
101+
102+
Returns:
103+
The appropriate `ComponentModelerDataType` object containing the simulation results.
104+
105+
Raises:
106+
TypeError: If the provided `modeler` is not a recognized type.
56107
"""
57108
if isinstance(modeler, ComponentModeler):
58109
modeler_data = compose_component_modeler_data(modeler=modeler, batch_data=batch_data)
59110
elif isinstance(modeler, TerminalComponentModeler):
60111
modeler_data = compose_terminal_modeler_data(modeler=modeler, batch_data=batch_data)
61112
else:
62-
raise Exception("sasas")
113+
raise TypeError(f"Unsupported modeler type: {type(modeler).__name__}")
63114

64115
return modeler_data
65116

@@ -68,8 +119,20 @@ def run(
68119
modeler: ComponentModelerType,
69120
path_dir: str = DEFAULT_DATA_DIR,
70121
) -> ComponentModelerDataType:
71-
"""
72-
This method internally determines which functions to run to compose the corresponding ComponentModelerDataType.
122+
"""Executes the full simulation workflow for a given component modeler.
123+
124+
This function orchestrates the end-to-end process:
125+
1. Creates a `Batch` of simulations from the `modeler`.
126+
2. Submits the `Batch` for execution and waits for results.
127+
3. Composes the results into a structured `ComponentModelerDataType` object.
128+
129+
Args:
130+
modeler: The component modeler defining the simulations to be run.
131+
path_dir: The directory where the batch file will be saved.
132+
133+
Returns:
134+
A `ComponentModelerDataType` object containing the processed simulation data,
135+
ready for S-parameter extraction and analysis.
73136
"""
74137
batch = create_batch(modeler=modeler, path_dir=path_dir)
75138
batch_data = batch.run()

0 commit comments

Comments
 (0)