Skip to content

Commit 11f2640

Browse files
nearly passing now
1 parent b5d2b4e commit 11f2640

File tree

3 files changed

+160
-131
lines changed

3 files changed

+160
-131
lines changed

tidy3d/plugins/smatrix/component_modelers/terminal.py

Lines changed: 14 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
from __future__ import annotations
44

55
import os
6+
import typing
67
from typing import Optional, Union
78

89
import numpy as np
@@ -196,41 +197,6 @@ def _source_time(self):
196197
fmin=min(self.freqs), fmax=max(self.freqs), remove_dc_component=self.remove_dc_component
197198
)
198199

199-
def _construct_smatrix(self) -> TerminalPortDataArray:
200-
"""Post process :class:`.BatchData` to generate scattering matrix."""
201-
return self._internal_construct_smatrix(batch_data=self.batch_data)
202-
203-
def _internal_construct_smatrix(self, batch_data) -> TerminalPortDataArray:
204-
"""Post process :class:`.BatchData` to generate scattering matrix, for internal use only."""
205-
206-
port_names = [port.name for port in self.ports]
207-
208-
values = np.zeros(
209-
(len(self.freqs), len(port_names), len(port_names)),
210-
dtype=complex,
211-
)
212-
coords = {
213-
"f": np.array(self.freqs),
214-
"port_out": port_names,
215-
"port_in": port_names,
216-
}
217-
a_matrix = TerminalPortDataArray(values, coords=coords)
218-
b_matrix = a_matrix.copy(deep=True)
219-
220-
# Tabulate the reference impedances at each port and frequency
221-
port_impedances = self._port_reference_impedances(batch_data=batch_data)
222-
223-
# loop through source ports
224-
for port_in in self.ports:
225-
sim_data = batch_data[self._task_name(port=port_in)]
226-
a, b = self.compute_power_wave_amplitudes_at_each_port(port_impedances, sim_data)
227-
indexer = {"f": a.f, "port_in": port_in.name, "port_out": a.port}
228-
a_matrix.loc[indexer] = a
229-
b_matrix.loc[indexer] = b
230-
231-
s_matrix = self.ab_to_s(a_matrix, b_matrix)
232-
return s_matrix
233-
234200
@pd.validator("simulation")
235201
def _validate_3d_simulation(cls, val):
236202
"""Error if :class:`.Simulation` is not a 3D simulation"""
@@ -337,13 +303,10 @@ def run(self, path_dir: str = DEFAULT_DATA_DIR):
337303
def compute_power_wave_amplitudes_at_each_port(
338304
self, port_reference_impedances: PortDataArray, sim_data: SimulationData
339305
) -> tuple[PortDataArray, PortDataArray]:
340-
from tidy3d.plugins.smatrix.data.data import SMatrixData, TerminalComponentModelerData
306+
from tidy3d.plugins.smatrix.run import compute_power_wave_amplitudes_at_each_port
341307

342-
a = SMatrixData(a=self._construct_smatrix())
343-
data = TerminalComponentModelerData(
344-
simulation=self, data=a
345-
).compute_power_wave_amplitudes_at_each_port(
346-
port_reference_impedances=port_reference_impedances, sim_data=sim_data
308+
data = compute_power_wave_amplitudes_at_each_port(
309+
simulation=self, port_reference_impedances=port_reference_impedances, sim_data=sim_data
347310
)
348311
return data
349312

@@ -469,3 +432,13 @@ def _upload_terminal_modeler(self):
469432
solver_version=self.solver_version,
470433
simulation_type="microwave",
471434
)
435+
436+
def _construct_smatrix(self) -> TerminalPortDataArray:
437+
from tidy3d.plugins.smatrix.run import _construct_smatrix
438+
439+
return _construct_smatrix(self)
440+
441+
def _internal_construct_smatrix(self, batch_data: typing.Any) -> TerminalPortDataArray:
442+
from tidy3d.plugins.smatrix.run import _internal_construct_smatrix
443+
444+
return _internal_construct_smatrix(self)

tidy3d/plugins/smatrix/data/data.py

Lines changed: 6 additions & 90 deletions
Original file line numberDiff line numberDiff line change
@@ -18,12 +18,6 @@
1818
)
1919
from tidy3d.plugins.smatrix.component_modelers.terminal import TerminalComponentModeler
2020
from tidy3d.plugins.smatrix.data.terminal import PortDataArray, TerminalPortDataArray
21-
from tidy3d.plugins.smatrix.ports.wave import WavePort
22-
from tidy3d.plugins.smatrix.utils import (
23-
_check_port_impedance_sign,
24-
_compute_F,
25-
compute_port_VI,
26-
)
2721

2822

2923
class SMatrixData(Tidy3dBaseModel):
@@ -178,92 +172,14 @@ def port_reference_impedances(self) -> PortDataArray:
178172
"""The reference impedance used at each port for definining power wave amplitudes."""
179173
return self._port_reference_impedances(self.simulation.batch_data)
180174

181-
def _port_reference_impedances(self, batch_data) -> PortDataArray:
182-
"""Tabulates the reference impedance of each port at each frequency using the
183-
supplied :class:`.BatchData`.
184-
"""
185-
# TODO properly refactor, plugins data types should not have web methods.
186-
187-
port_names = [port.name for port in self.simulation.ports]
188-
189-
values = np.zeros(
190-
(len(self.simulation.freqs), len(port_names)),
191-
dtype=complex,
192-
)
193-
coords = {"f": np.array(self.simulation.freqs), "port": port_names}
194-
port_impedances = PortDataArray(values, coords=coords)
195-
for port in self.simulation.ports:
196-
if isinstance(port, WavePort):
197-
# Mode solver data for each wave port is stored in its associated SimulationData
198-
sim_data_port = batch_data[self.simulation._task_name(port=port)]
199-
# WavePorts have a port impedance calculated from its associated modal field distribution
200-
# and is frequency dependent.
201-
impedances = port.compute_port_impedance(sim_data_port).values
202-
port_impedances.loc[{"port": port.name}] = impedances.squeeze()
203-
else:
204-
# LumpedPorts have a constant reference impedance
205-
port_impedances.loc[{"port": port.name}] = np.full(
206-
len(self.simulation.freqs), port.impedance
207-
)
208-
209-
port_impedances = self.simulation._set_port_data_array_attributes(port_impedances)
210-
return port_impedances
211-
212175
def compute_power_wave_amplitudes_at_each_port(
213176
self, port_reference_impedances: PortDataArray, sim_data: SimulationData
214177
) -> tuple[PortDataArray, PortDataArray]:
215-
"""Compute the incident and reflected power wave amplitudes at each port.
216-
The computed amplitudes have not been normalized.
217-
218-
Parameters
219-
----------
220-
port_reference_impedances : :class:`.PortDataArray`
221-
Reference impedance at each port.
222-
sim_data : :class:`.SimulationData`
223-
Results from the simulation.
178+
from tidy3d.plugins.smatrix.run import compute_power_wave_amplitudes_at_each_port
224179

225-
Returns
226-
-------
227-
tuple[:class:`.PortDataArray`, :class:`.PortDataArray`]
228-
Incident (a) and reflected (b) power wave amplitudes at each port.
229-
"""
230-
port_names = [port.name for port in self.simulation.ports]
231-
values = np.zeros(
232-
(len(self.simulation.freqs), len(port_names)),
233-
dtype=complex,
180+
data = compute_power_wave_amplitudes_at_each_port(
181+
simulation=self.simulation,
182+
port_reference_impedances=port_reference_impedances,
183+
sim_data=sim_data,
234184
)
235-
coords = {
236-
"f": np.array(self.simulation.freqs),
237-
"port": port_names,
238-
}
239-
240-
V_matrix = PortDataArray(values, coords=coords)
241-
I_matrix = V_matrix.copy(deep=True)
242-
a = V_matrix.copy(deep=True)
243-
b = V_matrix.copy(deep=True)
244-
245-
for port_out in self.simulation.ports:
246-
V_out, I_out = compute_port_VI(port_out, sim_data)
247-
indexer = {"port": port_out.name}
248-
V_matrix.loc[indexer] = V_out
249-
I_matrix.loc[indexer] = I_out
250-
251-
V_numpy = V_matrix.values
252-
I_numpy = I_matrix.values
253-
Z_numpy = port_reference_impedances.values
254-
255-
# Check to make sure sign is consistent for all impedance values
256-
_check_port_impedance_sign(Z_numpy)
257-
258-
# # Check for negative real part of port impedance and flip the V and Z signs accordingly
259-
negative_real_Z = np.real(Z_numpy) < 0
260-
V_numpy = np.where(negative_real_Z, -V_numpy, V_numpy)
261-
Z_numpy = np.where(negative_real_Z, -Z_numpy, Z_numpy)
262-
263-
F_numpy = _compute_F(Z_numpy)
264-
265-
# Equation 4.67 - Pozar - Microwave Engineering 4ed
266-
a.values = F_numpy * (V_numpy + Z_numpy * I_numpy)
267-
b.values = F_numpy * (V_numpy - np.conj(Z_numpy) * I_numpy)
268-
269-
return a, b
185+
return data

tidy3d/plugins/smatrix/run.py

Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
1+
"""Tool for generating an S matrix automatically from a Tidy3d simulation and lumped port definitions."""
2+
3+
from __future__ import annotations
4+
5+
import numpy as np
6+
7+
from tidy3d.components.data.sim_data import SimulationData
8+
from tidy3d.plugins.smatrix.data.terminal import PortDataArray, TerminalPortDataArray
9+
from tidy3d.plugins.smatrix.ports.wave import WavePort
10+
from tidy3d.plugins.smatrix.utils import _check_port_impedance_sign, _compute_F, compute_port_VI
11+
12+
13+
def _construct_smatrix(simulation) -> TerminalPortDataArray:
14+
"""Post process :class:`.BatchData` to generate scattering matrix."""
15+
return _internal_construct_smatrix(simulation=simulation)
16+
17+
18+
def _port_reference_impedances(simulation) -> PortDataArray:
19+
"""Tabulates the reference impedance of each port at each frequency using the
20+
supplied :class:`.BatchData`.
21+
"""
22+
# TODO properly refactor, plugins data types should not have web methods.
23+
24+
port_names = [port.name for port in simulation.ports]
25+
26+
values = np.zeros(
27+
(len(simulation.freqs), len(port_names)),
28+
dtype=complex,
29+
)
30+
coords = {"f": np.array(simulation.freqs), "port": port_names}
31+
port_impedances = PortDataArray(values, coords=coords)
32+
for port in simulation.ports:
33+
if isinstance(port, WavePort):
34+
# Mode solver data for each wave port is stored in its associated SimulationData
35+
sim_data_port = simulation.batch_data[simulation._task_name(port=port)]
36+
# WavePorts have a port impedance calculated from its associated modal field distribution
37+
# and is frequency dependent.
38+
impedances = port.compute_port_impedance(sim_data_port).values
39+
port_impedances.loc[{"port": port.name}] = impedances.squeeze()
40+
else:
41+
# LumpedPorts have a constant reference impedance
42+
port_impedances.loc[{"port": port.name}] = np.full(
43+
len(simulation.freqs), port.impedance
44+
)
45+
46+
port_impedances = simulation._set_port_data_array_attributes(port_impedances)
47+
return port_impedances
48+
49+
50+
def _internal_construct_smatrix(simulation) -> TerminalPortDataArray:
51+
"""Post process :class:`.BatchData` to generate scattering matrix, for internal use only."""
52+
from tidy3d.plugins.smatrix.utils import ab_to_s
53+
54+
port_names = [port.name for port in simulation.ports]
55+
56+
values = np.zeros(
57+
(len(simulation.freqs), len(port_names), len(port_names)),
58+
dtype=complex,
59+
)
60+
coords = {
61+
"f": np.array(simulation.freqs),
62+
"port_out": port_names,
63+
"port_in": port_names,
64+
}
65+
a_matrix = TerminalPortDataArray(values, coords=coords)
66+
b_matrix = a_matrix.copy(deep=True)
67+
68+
# Tabulate the reference impedances at each port and frequency
69+
port_impedances = _port_reference_impedances(simulation=simulation)
70+
71+
# loop through source ports
72+
for port_in in simulation.ports:
73+
sim_data = simulation.batch_data[simulation._task_name(port=port_in)]
74+
a, b = simulation.compute_power_wave_amplitudes_at_each_port(port_impedances, sim_data)
75+
indexer = {"f": a.f, "port_in": port_in.name, "port_out": a.port}
76+
a_matrix.loc[indexer] = a
77+
b_matrix.loc[indexer] = b
78+
79+
s_matrix = ab_to_s(a_matrix, b_matrix)
80+
return s_matrix
81+
82+
83+
def compute_power_wave_amplitudes_at_each_port(
84+
simulation, port_reference_impedances: PortDataArray, sim_data: SimulationData
85+
) -> tuple[PortDataArray, PortDataArray]:
86+
"""Compute the incident and reflected power wave amplitudes at each port.
87+
The computed amplitudes have not been normalized.
88+
89+
Parameters
90+
----------
91+
port_reference_impedances : :class:`.PortDataArray`
92+
Reference impedance at each port.
93+
sim_data : :class:`.SimulationData`
94+
Results from the simulation.
95+
96+
Returns
97+
-------
98+
tuple[:class:`.PortDataArray`, :class:`.PortDataArray`]
99+
Incident (a) and reflected (b) power wave amplitudes at each port.
100+
"""
101+
port_names = [port.name for port in simulation.ports]
102+
values = np.zeros(
103+
(len(simulation.freqs), len(port_names)),
104+
dtype=complex,
105+
)
106+
coords = {
107+
"f": np.array(simulation.freqs),
108+
"port": port_names,
109+
}
110+
111+
V_matrix = PortDataArray(values, coords=coords)
112+
I_matrix = V_matrix.copy(deep=True)
113+
a = V_matrix.copy(deep=True)
114+
b = V_matrix.copy(deep=True)
115+
116+
for port_out in simulation.ports:
117+
V_out, I_out = compute_port_VI(port_out, sim_data)
118+
indexer = {"port": port_out.name}
119+
V_matrix.loc[indexer] = V_out
120+
I_matrix.loc[indexer] = I_out
121+
122+
V_numpy = V_matrix.values
123+
I_numpy = I_matrix.values
124+
Z_numpy = port_reference_impedances.values
125+
126+
# Check to make sure sign is consistent for all impedance values
127+
_check_port_impedance_sign(Z_numpy)
128+
129+
# # Check for negative real part of port impedance and flip the V and Z signs accordingly
130+
negative_real_Z = np.real(Z_numpy) < 0
131+
V_numpy = np.where(negative_real_Z, -V_numpy, V_numpy)
132+
Z_numpy = np.where(negative_real_Z, -Z_numpy, Z_numpy)
133+
134+
F_numpy = _compute_F(Z_numpy)
135+
136+
# Equation 4.67 - Pozar - Microwave Engineering 4ed
137+
a.values = F_numpy * (V_numpy + Z_numpy * I_numpy)
138+
b.values = F_numpy * (V_numpy - np.conj(Z_numpy) * I_numpy)
139+
140+
return a, b

0 commit comments

Comments
 (0)