Skip to content

Commit aedc3a9

Browse files
more tests, remove sporading warnings
1 parent de9d595 commit aedc3a9

File tree

5 files changed

+394
-11
lines changed

5 files changed

+394
-11
lines changed

tests/test_components/test_microwave.py

Lines changed: 334 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1876,3 +1876,337 @@ def test_RF_license_suppression():
18761876
with AssertLogLevel(None):
18771877
mode_spec = td.MicrowaveModeSpec._default_without_license_warning()
18781878
td.config.microwave.suppress_rf_license_warning = original_setting
1879+
1880+
1881+
def test_microwave_mode_data_reordering_with_transmission_line_data():
1882+
"""Test that transmission_line_data is correctly reordered when modes are reordered."""
1883+
from tidy3d.components.data.data_array import (
1884+
CurrentFreqModeDataArray,
1885+
ImpedanceFreqModeDataArray,
1886+
ModeIndexDataArray,
1887+
ScalarModeFieldDataArray,
1888+
VoltageFreqModeDataArray,
1889+
)
1890+
from tidy3d.components.microwave.data.dataset import TransmissionLineDataset
1891+
1892+
# Setup coordinates
1893+
x = [-1, 1, 3]
1894+
y = [-2, 0]
1895+
z = [-3, -1, 1, 3, 5]
1896+
f = [2e14, 3e14]
1897+
mode_index = np.arange(3)
1898+
1899+
grid = td.Grid(boundaries=td.Coords(x=x, y=y, z=z))
1900+
field_coords = {"x": x[:-1], "y": y[:-1], "z": z[:-1], "f": f, "mode_index": mode_index}
1901+
index_coords = {"f": f, "mode_index": mode_index}
1902+
1903+
# Create field data with distinct values for each mode
1904+
field_values = np.zeros((2, 1, 4, 2, 3), dtype=complex)
1905+
for mode_idx in range(3):
1906+
# Each mode gets a unique value to track reordering
1907+
field_values[:, :, :, :, mode_idx] = (mode_idx + 1) * (1 + 1j)
1908+
1909+
field = ScalarModeFieldDataArray(field_values, coords=field_coords)
1910+
1911+
# Create mode index data with distinct values for each mode
1912+
index_values = np.zeros((2, 3), dtype=complex)
1913+
for mode_idx in range(3):
1914+
index_values[:, mode_idx] = (mode_idx + 1) * 1.5 + 0.1j
1915+
index_data = ModeIndexDataArray(index_values, coords=index_coords)
1916+
1917+
# Create transmission line data with distinct values for each mode
1918+
impedance_values = np.zeros((2, 3))
1919+
voltage_values = np.zeros((2, 3), dtype=complex)
1920+
current_values = np.zeros((2, 3), dtype=complex)
1921+
1922+
for mode_idx in range(3):
1923+
# Each mode gets unique impedance, voltage, and current values
1924+
impedance_values[:, mode_idx] = 50 * (mode_idx + 1)
1925+
voltage_values[:, mode_idx] = (mode_idx + 1) * (10 + 5j)
1926+
current_values[:, mode_idx] = (mode_idx + 1) * (0.2 + 0.1j)
1927+
1928+
impedance_data = ImpedanceFreqModeDataArray(impedance_values, coords=index_coords)
1929+
voltage_data = VoltageFreqModeDataArray(voltage_values, coords=index_coords)
1930+
current_data = CurrentFreqModeDataArray(current_values, coords=index_coords)
1931+
1932+
tl_data = TransmissionLineDataset(
1933+
Z0=impedance_data, voltage_coeffs=voltage_data, current_coeffs=current_data
1934+
)
1935+
1936+
# Create monitor
1937+
monitor = td.MicrowaveModeSolverMonitor(
1938+
center=(0, 0, 0),
1939+
size=(2, 0, 6),
1940+
freqs=[2e14, 3e14],
1941+
mode_spec=td.MicrowaveModeSpec(num_modes=3, impedance_specs=td.AutoImpedanceSpec()),
1942+
name="microwave_mode_solver",
1943+
)
1944+
1945+
# Create MicrowaveModeSolverData
1946+
data = td.MicrowaveModeSolverData(
1947+
monitor=monitor,
1948+
Ex=field,
1949+
Ey=field,
1950+
Ez=field,
1951+
Hx=field,
1952+
Hy=field,
1953+
Hz=field,
1954+
n_complex=index_data,
1955+
grid_expanded=grid,
1956+
transmission_line_data=tl_data,
1957+
)
1958+
1959+
# Define a reordering: reverse the mode order for each frequency
1960+
# Shape: (num_freqs, num_modes) = (2, 3)
1961+
# Original order: [0, 1, 2] -> New order: [2, 1, 0]
1962+
sort_inds_2d = np.array([[2, 1, 0], [2, 1, 0]])
1963+
1964+
# Apply mode reordering
1965+
reordered_data = data._apply_mode_reorder(sort_inds_2d)
1966+
1967+
# Verify that the main mode data is reordered correctly
1968+
# Original mode 2 should now be at index 0
1969+
original_mode_2_value = (2 + 1) * (1 + 1j) # Mode 2 had value 3*(1+1j)
1970+
assert np.allclose(
1971+
reordered_data.Ex.isel(mode_index=0, x=0, y=0, z=0).values, original_mode_2_value
1972+
), "Main field data not reordered correctly"
1973+
1974+
# Original mode 0 should now be at index 2
1975+
original_mode_0_value = (0 + 1) * (1 + 1j) # Mode 0 had value 1*(1+1j)
1976+
assert np.allclose(
1977+
reordered_data.Ex.isel(mode_index=2, x=0, y=0, z=0).values, original_mode_0_value
1978+
), "Main field data not reordered correctly"
1979+
1980+
# Verify that transmission_line_data is also reordered correctly
1981+
assert reordered_data.transmission_line_data is not None, (
1982+
"transmission_line_data should not be None"
1983+
)
1984+
1985+
# Check Z0 reordering
1986+
# Original mode 2 had Z0 = 50 * 3 = 150
1987+
assert np.allclose(reordered_data.transmission_line_data.Z0.isel(mode_index=0).values, 150.0), (
1988+
"transmission_line_data.Z0 not reordered correctly"
1989+
)
1990+
1991+
# Original mode 0 had Z0 = 50 * 1 = 50
1992+
assert np.allclose(reordered_data.transmission_line_data.Z0.isel(mode_index=2).values, 50.0), (
1993+
"transmission_line_data.Z0 not reordered correctly"
1994+
)
1995+
1996+
# Check voltage_coeffs reordering
1997+
# Original mode 2 had voltage = 3 * (10 + 5j)
1998+
assert np.allclose(
1999+
reordered_data.transmission_line_data.voltage_coeffs.isel(mode_index=0).values,
2000+
3 * (10 + 5j),
2001+
), "transmission_line_data.voltage_coeffs not reordered correctly"
2002+
2003+
# Original mode 0 had voltage = 1 * (10 + 5j)
2004+
assert np.allclose(
2005+
reordered_data.transmission_line_data.voltage_coeffs.isel(mode_index=2).values,
2006+
1 * (10 + 5j),
2007+
), "transmission_line_data.voltage_coeffs not reordered correctly"
2008+
2009+
# Check current_coeffs reordering
2010+
# Original mode 2 had current = 3 * (0.2 + 0.1j)
2011+
assert np.allclose(
2012+
reordered_data.transmission_line_data.current_coeffs.isel(mode_index=0).values,
2013+
3 * (0.2 + 0.1j),
2014+
), "transmission_line_data.current_coeffs not reordered correctly"
2015+
2016+
# Original mode 0 had current = 1 * (0.2 + 0.1j)
2017+
assert np.allclose(
2018+
reordered_data.transmission_line_data.current_coeffs.isel(mode_index=2).values,
2019+
1 * (0.2 + 0.1j),
2020+
), "transmission_line_data.current_coeffs not reordered correctly"
2021+
2022+
# Verify mode index data is also reordered
2023+
# Original mode 2 had n_complex = 3 * 1.5 + 0.1j
2024+
assert np.allclose(reordered_data.n_complex.isel(mode_index=0).values, 3 * 1.5 + 0.1j), (
2025+
"n_complex not reordered correctly"
2026+
)
2027+
2028+
2029+
def test_microwave_mode_data_interpolation():
2030+
"""Test that MicrowaveModeSolverData interpolation correctly handles transmission_line_data."""
2031+
from tidy3d.components.data.data_array import (
2032+
CurrentFreqModeDataArray,
2033+
ImpedanceFreqModeDataArray,
2034+
ModeIndexDataArray,
2035+
ScalarModeFieldDataArray,
2036+
VoltageFreqModeDataArray,
2037+
)
2038+
from tidy3d.components.microwave.data.dataset import TransmissionLineDataset
2039+
2040+
# Setup coordinates with sparse frequencies
2041+
x = [-1, 1, 3]
2042+
y = [-2, 0]
2043+
z = [-3, -1, 1, 3, 5]
2044+
f_sparse = np.array([1e14, 1.5e14, 2e14]) # 3 source frequencies
2045+
mode_index = np.arange(2)
2046+
2047+
grid = td.Grid(boundaries=td.Coords(x=x, y=y, z=z))
2048+
field_coords = {"x": x[:-1], "y": y[:-1], "z": z[:-1], "f": f_sparse, "mode_index": mode_index}
2049+
index_coords = {"f": f_sparse, "mode_index": mode_index}
2050+
2051+
# Create field data with frequency-dependent values
2052+
field_values = np.zeros(
2053+
(len(x) - 1, len(y) - 1, len(z) - 1, len(f_sparse), len(mode_index)), dtype=complex
2054+
)
2055+
for f_idx, freq in enumerate(f_sparse):
2056+
for mode_idx in range(len(mode_index)):
2057+
# Value depends on both frequency and mode: (freq/1e14) * (mode+1) * (1+1j)
2058+
field_values[:, :, :, f_idx, mode_idx] = (freq / 1e14) * (mode_idx + 1) * (1 + 1j)
2059+
2060+
field = ScalarModeFieldDataArray(field_values, coords=field_coords)
2061+
2062+
# Create mode index data with frequency dependence
2063+
index_values = np.zeros((len(f_sparse), len(mode_index)), dtype=complex)
2064+
for f_idx, freq in enumerate(f_sparse):
2065+
for mode_idx in range(len(mode_index)):
2066+
# n_eff increases with frequency: 1.5 + (freq/1e14)*0.1 + mode_idx*0.2
2067+
index_values[f_idx, mode_idx] = 1.5 + (freq / 1e14) * 0.1 + mode_idx * 0.2 + 0.01j
2068+
index_data = ModeIndexDataArray(index_values, coords=index_coords)
2069+
2070+
# Create transmission line data with frequency dependence
2071+
impedance_values = np.zeros((len(f_sparse), len(mode_index)))
2072+
voltage_values = np.zeros((len(f_sparse), len(mode_index)), dtype=complex)
2073+
current_values = np.zeros((len(f_sparse), len(mode_index)), dtype=complex)
2074+
2075+
for f_idx, freq in enumerate(f_sparse):
2076+
for mode_idx in range(len(mode_index)):
2077+
# Impedance varies with frequency: 50 + (freq/1e14)*10 + mode_idx*20
2078+
impedance_values[f_idx, mode_idx] = 50 + (freq / 1e14) * 10 + mode_idx * 20
2079+
# Voltage varies with frequency
2080+
voltage_values[f_idx, mode_idx] = ((freq / 1e14) + mode_idx) * (10 + 5j)
2081+
# Current varies with frequency
2082+
current_values[f_idx, mode_idx] = ((freq / 1e14) + mode_idx) * (0.2 + 0.1j)
2083+
2084+
impedance_data = ImpedanceFreqModeDataArray(impedance_values, coords=index_coords)
2085+
voltage_data = VoltageFreqModeDataArray(voltage_values, coords=index_coords)
2086+
current_data = CurrentFreqModeDataArray(current_values, coords=index_coords)
2087+
2088+
tl_data = TransmissionLineDataset(
2089+
Z0=impedance_data, voltage_coeffs=voltage_data, current_coeffs=current_data
2090+
)
2091+
2092+
# Create monitor
2093+
monitor = td.MicrowaveModeSolverMonitor(
2094+
center=(0, 0, 0),
2095+
size=(2, 0, 6),
2096+
freqs=f_sparse,
2097+
mode_spec=td.MicrowaveModeSpec(num_modes=2, impedance_specs=td.AutoImpedanceSpec()),
2098+
name="microwave_mode_solver",
2099+
)
2100+
2101+
# Create MicrowaveModeSolverData
2102+
data = td.MicrowaveModeSolverData(
2103+
monitor=monitor,
2104+
Ex=field,
2105+
Ey=field,
2106+
Ez=field,
2107+
Hx=field,
2108+
Hy=field,
2109+
Hz=field,
2110+
n_complex=index_data,
2111+
grid_expanded=grid,
2112+
transmission_line_data=tl_data,
2113+
)
2114+
2115+
# Interpolate to denser frequency grid
2116+
f_dense = np.linspace(1e14, 2e14, 11)
2117+
2118+
# Test linear interpolation
2119+
data_interp_linear = data.interp_in_freq(freqs=f_dense, method="linear", renormalize=False)
2120+
2121+
# Verify that interpolated data has correct shape
2122+
assert len(data_interp_linear.monitor.freqs) == len(f_dense), (
2123+
"Interpolated data should have new frequency count"
2124+
)
2125+
assert data_interp_linear.Ex.shape[-1] == len(mode_index), "Mode count should be preserved"
2126+
assert data_interp_linear.Ex.shape[-2] == len(f_dense), (
2127+
"Frequency dimension should match target"
2128+
)
2129+
2130+
# Verify that transmission_line_data is also interpolated
2131+
assert data_interp_linear.transmission_line_data is not None, (
2132+
"transmission_line_data should be interpolated"
2133+
)
2134+
assert len(data_interp_linear.transmission_line_data.Z0.coords["f"]) == len(f_dense), (
2135+
"transmission_line_data.Z0 should be interpolated to new frequencies"
2136+
)
2137+
assert len(data_interp_linear.transmission_line_data.voltage_coeffs.coords["f"]) == len(
2138+
f_dense
2139+
), "transmission_line_data.voltage_coeffs should be interpolated to new frequencies"
2140+
assert len(data_interp_linear.transmission_line_data.current_coeffs.coords["f"]) == len(
2141+
f_dense
2142+
), "transmission_line_data.current_coeffs should be interpolated to new frequencies"
2143+
2144+
# Test interpolation accuracy at midpoint
2145+
f_mid = 1.5e14
2146+
2147+
# Check that field interpolation is reasonable
2148+
# At f=1.5e14, mode 0 should have value approximately (1.5) * 1 * (1+1j) = 1.5*(1+1j)
2149+
field_at_mid_mode0 = data_interp_linear.Ex.sel(
2150+
f=f_mid, mode_index=0, x=0, y=0, z=0, method="nearest"
2151+
).values
2152+
expected_field_mode0 = 1.5 * 1 * (1 + 1j)
2153+
assert np.allclose(field_at_mid_mode0, expected_field_mode0, rtol=0.01), (
2154+
f"Field interpolation for mode 0: expected {expected_field_mode0}, got {field_at_mid_mode0}"
2155+
)
2156+
2157+
# Check that n_complex interpolation is reasonable
2158+
# At f=1.5e14, mode 0 should have n_eff approximately 1.5 + 1.5*0.1 + 0*0.2 = 1.65
2159+
n_complex_at_mid_mode0 = data_interp_linear.n_complex.sel(
2160+
f=f_mid, mode_index=0, method="nearest"
2161+
).values
2162+
expected_n_complex_mode0 = 1.5 + 1.5 * 0.1 + 0 * 0.2 + 0.01j
2163+
assert np.allclose(n_complex_at_mid_mode0, expected_n_complex_mode0, rtol=0.01), (
2164+
f"n_complex interpolation for mode 0: expected {expected_n_complex_mode0}, got {n_complex_at_mid_mode0}"
2165+
)
2166+
2167+
# Check that transmission line data interpolation is reasonable
2168+
# At f=1.5e14, mode 0 should have Z0 approximately 50 + 1.5*10 + 0*20 = 65
2169+
Z0_at_mid_mode0 = data_interp_linear.transmission_line_data.Z0.sel(
2170+
f=f_mid, mode_index=0, method="nearest"
2171+
).values
2172+
expected_Z0_mode0 = 50 + 1.5 * 10 + 0 * 20
2173+
assert np.allclose(Z0_at_mid_mode0, expected_Z0_mode0, rtol=0.01), (
2174+
f"Z0 interpolation for mode 0: expected {expected_Z0_mode0}, got {Z0_at_mid_mode0}"
2175+
)
2176+
2177+
# At f=1.5e14, mode 0 should have voltage approximately (1.5 + 0) * (10 + 5j) = 1.5*(10+5j)
2178+
voltage_at_mid_mode0 = data_interp_linear.transmission_line_data.voltage_coeffs.sel(
2179+
f=f_mid, mode_index=0, method="nearest"
2180+
).values
2181+
expected_voltage_mode0 = 1.5 * (10 + 5j)
2182+
assert np.allclose(voltage_at_mid_mode0, expected_voltage_mode0, rtol=0.01), (
2183+
f"voltage_coeffs interpolation for mode 0: expected {expected_voltage_mode0}, got {voltage_at_mid_mode0}"
2184+
)
2185+
2186+
# At f=1.5e14, mode 0 should have current approximately (1.5 + 0) * (0.2 + 0.1j) = 1.5*(0.2+0.1j)
2187+
current_at_mid_mode0 = data_interp_linear.transmission_line_data.current_coeffs.sel(
2188+
f=f_mid, mode_index=0, method="nearest"
2189+
).values
2190+
expected_current_mode0 = 1.5 * (0.2 + 0.1j)
2191+
assert np.allclose(current_at_mid_mode0, expected_current_mode0, rtol=0.01), (
2192+
f"current_coeffs interpolation for mode 0: expected {expected_current_mode0}, got {current_at_mid_mode0}"
2193+
)
2194+
2195+
# Test at endpoints to ensure they match original values
2196+
# At f=1e14, mode 1 should have Z0 = 50 + 1*10 + 1*20 = 80
2197+
Z0_at_start_mode1 = data_interp_linear.transmission_line_data.Z0.sel(
2198+
f=1e14, mode_index=1, method="nearest"
2199+
).values
2200+
expected_Z0_start_mode1 = 50 + 1 * 10 + 1 * 20
2201+
assert np.allclose(Z0_at_start_mode1, expected_Z0_start_mode1, rtol=1e-6), (
2202+
f"Z0 at endpoint should match original: expected {expected_Z0_start_mode1}, got {Z0_at_start_mode1}"
2203+
)
2204+
2205+
# At f=2e14, mode 1 should have Z0 = 50 + 2*10 + 1*20 = 90
2206+
Z0_at_end_mode1 = data_interp_linear.transmission_line_data.Z0.sel(
2207+
f=2e14, mode_index=1, method="nearest"
2208+
).values
2209+
expected_Z0_end_mode1 = 50 + 2 * 10 + 1 * 20
2210+
assert np.allclose(Z0_at_end_mode1, expected_Z0_end_mode1, rtol=1e-6), (
2211+
f"Z0 at endpoint should match original: expected {expected_Z0_end_mode1}, got {Z0_at_end_mode1}"
2212+
)

tests/test_components/test_mode_interp.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,8 @@ def test_interp_spec_sampling_points_uniform():
103103
# Check uniform spacing
104104
diffs = np.diff(sampling)
105105
assert np.allclose(diffs, diffs[0])
106+
# Check ascending order
107+
assert np.all(np.diff(sampling) > 0), "Uniform frequencies should be in ascending order"
106108

107109

108110
def test_interp_spec_sampling_points_cheb():
@@ -124,6 +126,21 @@ def test_interp_spec_sampling_points_cheb():
124126
assert np.allclose(np.sort(sampling), np.sort(expected))
125127

126128

129+
def test_cheb_sampling_ascending_order():
130+
"""Test that Chebyshev sampling returns frequencies in ascending order."""
131+
spec = td.ModeInterpSpec.cheb(num_points=10)
132+
freqs = np.linspace(1e14, 2e14, 100)
133+
sampling = spec.sampling_points(freqs)
134+
135+
# Verify the frequencies are in strictly ascending order
136+
assert len(sampling) == 10
137+
assert np.all(np.diff(sampling) > 0), "Chebyshev frequencies should be in ascending order"
138+
139+
# Verify endpoints are correct
140+
assert np.isclose(sampling[0], 1e14), "First frequency should be f_min"
141+
assert np.isclose(sampling[-1], 2e14), "Last frequency should be f_max"
142+
143+
127144
def test_interp_spec_sampling_points_custom():
128145
"""Test sampling_points for custom sampling."""
129146
custom_freqs = np.array([1e14, 1.3e14, 1.7e14, 2e14])

0 commit comments

Comments
 (0)