Skip to content

Commit 8da1ce8

Browse files
committed
fix(rf): fix frequency sampling in MicrowaveModeData when group index is calculated 👻
1 parent 784d3c9 commit 8da1ce8

File tree

4 files changed

+127
-6
lines changed

4 files changed

+127
-6
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
6464
- Bug in `TerminalComponentModelerData.get_antenna_metrics_data()` where `WavePort` mode indices were not properly handled. Improved docstrings and type hints to make the usage clearer.
6565
- Improved type hints for `Tidy3dBaseModel`, so that all derived classes will have more accurate return types.
6666
- More robust method for suppressing RF license warnings during tests.
67+
- Fixed frequency sampling of `TransmissionLineDataset` within `MicrowaveModeData` when using group index calculation.
6768

6869
### Removed
6970
- Removed deprecated `use_complex_fields` parameter from `TwoPhotonAbsorption` and `KerrNonlinearity`. Parameters `beta` and `n2` are now real-valued only, as is `n0` if specified.

tests/test_components/test_microwave.py

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1156,6 +1156,88 @@ def test_mode_solver_with_microwave_mode_spec():
11561156
)
11571157

11581158

1159+
def test_mode_solver_with_microwave_group_index():
1160+
"""Test that group_index calculation with MicrowaveModeSpec correctly filters frequencies."""
1161+
1162+
width = 1.0 * mm
1163+
height = 0.5 * mm
1164+
metal_thickness = 0.1 * mm
1165+
1166+
stripline_sim = make_mw_sim(
1167+
transmission_line_type="stripline",
1168+
width=width,
1169+
height=height,
1170+
metal_thickness=metal_thickness,
1171+
)
1172+
dl = 0.05 * mm
1173+
stripline_sim = stripline_sim.updated_copy(grid_spec=td.GridSpec.uniform(dl=dl))
1174+
1175+
plane = td.Box(center=(0, 0, 0), size=(0, 10 * width, 2 * height + metal_thickness))
1176+
num_modes = 1
1177+
1178+
# Define original frequencies that we want in the final result
1179+
original_freqs = [1e9, 5e9, 10e9]
1180+
1181+
# Create custom impedance spec (AutoImpedanceSpec won't work with local mode solver)
1182+
custom_spec = td.CustomImpedanceSpec(
1183+
voltage_spec=None,
1184+
current_spec=td.AxisAlignedCurrentIntegralSpec(
1185+
size=(0, width + dl, metal_thickness + dl), sign="+"
1186+
),
1187+
)
1188+
1189+
# Enable group_index calculation
1190+
mode_spec = td.MicrowaveModeSpec(
1191+
num_modes=num_modes,
1192+
target_neff=2.2,
1193+
impedance_specs=custom_spec,
1194+
group_index_step=True, # This will expand frequencies to triplets
1195+
)
1196+
1197+
mms = ModeSolver(
1198+
simulation=stripline_sim,
1199+
plane=plane,
1200+
mode_spec=mode_spec,
1201+
colocate=False,
1202+
freqs=original_freqs,
1203+
)
1204+
1205+
# Get the mode solver data
1206+
mms_data: td.MicrowaveModeSolverData = mms.data
1207+
1208+
# Verify that group index was calculated
1209+
assert mms_data.n_group is not None, "Group index should be calculated"
1210+
1211+
# Verify that transmission line data exists
1212+
assert mms_data.transmission_line_data is not None, "Transmission line data should exist"
1213+
1214+
# Verify that the frequencies in transmission_line_data match the original frequencies
1215+
# (not the expanded triplet used internally for group index calculation)
1216+
tl_freqs_Z0 = mms_data.transmission_line_data.Z0.coords["f"].values
1217+
tl_freqs_voltage = mms_data.transmission_line_data.voltage_coeffs.coords["f"].values
1218+
tl_freqs_current = mms_data.transmission_line_data.current_coeffs.coords["f"].values
1219+
1220+
assert len(tl_freqs_Z0) == len(original_freqs), (
1221+
f"Z0 should have {len(original_freqs)} frequencies, got {len(tl_freqs_Z0)}"
1222+
)
1223+
assert len(tl_freqs_voltage) == len(original_freqs), (
1224+
f"voltage_coeffs should have {len(original_freqs)} frequencies, got {len(tl_freqs_voltage)}"
1225+
)
1226+
assert len(tl_freqs_current) == len(original_freqs), (
1227+
f"current_coeffs should have {len(original_freqs)} frequencies, got {len(tl_freqs_current)}"
1228+
)
1229+
1230+
assert np.allclose(tl_freqs_Z0, original_freqs), (
1231+
f"Z0 frequencies {tl_freqs_Z0} should match original {original_freqs}"
1232+
)
1233+
assert np.allclose(tl_freqs_voltage, original_freqs), (
1234+
f"voltage_coeffs frequencies {tl_freqs_voltage} should match original {original_freqs}"
1235+
)
1236+
assert np.allclose(tl_freqs_current, original_freqs), (
1237+
f"current_coeffs frequencies {tl_freqs_current} should match original {original_freqs}"
1238+
)
1239+
1240+
11591241
@pytest.mark.parametrize("axis", [0, 1, 2])
11601242
def test_voltage_integral_axes(axis):
11611243
"""Check AxisAlignedVoltageIntegral runs."""

tidy3d/components/data/monitor_data.py

Lines changed: 20 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1890,6 +1890,24 @@ def _find_closest_pairs(arr: Numpy) -> tuple[Numpy, Numpy]:
18901890

18911891
return pairs, values
18921892

1893+
def _group_index_freq_slices(self) -> tuple[slice, slice, slice]:
1894+
"""Get frequency slices for group index numerical differentiation.
1895+
1896+
Group index calculation uses three-point finite differences, requiring
1897+
backward, center, and forward frequency points organized as triplets.
1898+
1899+
Returns
1900+
-------
1901+
tuple[slice, slice, slice]
1902+
Slices for (backward, center, forward) frequencies from the frequency array.
1903+
"""
1904+
freqs = self.n_complex.coords["f"].values
1905+
num_freqs = freqs.size
1906+
back = slice(0, num_freqs, 3)
1907+
center = slice(1, num_freqs, 3)
1908+
fwd = slice(2, num_freqs, 3)
1909+
return back, center, fwd
1910+
18931911
def _group_index_post_process(self, frequency_step: float) -> ModeData:
18941912
"""Calculate group index and remove added frequencies used only for this calculation.
18951913
@@ -1904,12 +1922,8 @@ def _group_index_post_process(self, frequency_step: float) -> ModeData:
19041922
Filtered data with calculated group index.
19051923
"""
19061924

1907-
freqs = self.n_complex.coords["f"].values
1908-
num_freqs = freqs.size
1909-
back = slice(0, num_freqs, 3)
1910-
center = slice(1, num_freqs, 3)
1911-
fwd = slice(2, num_freqs, 3)
1912-
freqs = freqs[center]
1925+
back, center, fwd = self._group_index_freq_slices()
1926+
freqs = self.n_complex.coords["f"].values[center]
19131927

19141928
# calculate group index
19151929
n_center = self.n_eff.isel(f=center).values

tidy3d/components/microwave/data/monitor_data.py

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -281,6 +281,30 @@ def modes_info(self) -> xr.Dataset:
281281
super_info["Im(Z0)"] = self.transmission_line_data.Z0.imag
282282
return super_info
283283

284+
def _group_index_post_process(self, frequency_step: float) -> ModeData:
285+
"""Calculate group index and remove added frequencies used only for this calculation.
286+
287+
Parameters
288+
----------
289+
frequency_step: float
290+
Fractional frequency step used to calculate the group index.
291+
292+
Returns
293+
-------
294+
:class:`.ModeData`
295+
Filtered data with calculated group index.
296+
"""
297+
super_data = super()._group_index_post_process(frequency_step)
298+
if self.transmission_line_data is not None:
299+
_, center_inds, _ = self._group_index_freq_slices()
300+
update_dict = {
301+
"Z0": self.transmission_line_data.Z0.isel(f=center_inds),
302+
"voltage_coeffs": self.transmission_line_data.voltage_coeffs.isel(f=center_inds),
303+
"current_coeffs": self.transmission_line_data.current_coeffs.isel(f=center_inds),
304+
}
305+
super_data = super_data.updated_copy(**update_dict, path="transmission_line_data")
306+
return super_data
307+
284308

285309
class MicrowaveModeSolverData(ModeSolverData, MicrowaveModeData):
286310
"""

0 commit comments

Comments
 (0)