Skip to content

Commit 9b686b9

Browse files
FilipeFcpbzhangflex
authored andcommitted
fix(zbf): Support single frequency FieldMonitor and use correct flatten order
1 parent 3ab9256 commit 9b686b9

File tree

4 files changed

+58
-11
lines changed

4 files changed

+58
-11
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
2828
- Polygon vertices cleanup in `ClipOperation.intersections_plane`.
2929
- Removed sources from `sim_inf_structure` simulation object in `postprocess_adj` to avoid source and background medium validation errors.
3030
- Revert overly restrictive validation of `freqs` in the `ComponentModeler` and `TerminalComponentModeler`.
31+
- Fixed `ElectromagneticFieldData.to_zbf()` to support single frequency monitors and apply the correct flattening order.
3132

3233
## [2.9.0] - 2025-08-04
3334

tests/test_data/test_monitor_data.py

Lines changed: 44 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -906,6 +906,17 @@ def field_data(self) -> td.FieldData:
906906
)
907907
return self.simdata(monitor)["fields"]
908908

909+
@pytest.fixture(scope="class")
910+
def field_data_single_frequency(self) -> td.FieldData:
911+
"""Make random field data with single frequency from an emulated simulation run."""
912+
monitor = td.FieldMonitor(
913+
size=(td.inf, td.inf, 0),
914+
freqs=self.freqs[0],
915+
name="fields",
916+
colocate=True,
917+
)
918+
return self.simdata(monitor)["fields"]
919+
909920
@pytest.fixture(scope="class")
910921
def mode_data(self) -> td.ModeData:
911922
"""Make random ModeData from an emulated simulation run."""
@@ -919,18 +930,41 @@ def mode_data(self) -> td.ModeData:
919930
)
920931
return self.simdata(monitor)["modes"]
921932

933+
@pytest.fixture(scope="class")
934+
def mode_data_single_frequency(self) -> td.ModeData:
935+
"""Make random ModeData from an emulated simulation run."""
936+
monitor = td.ModeMonitor(
937+
size=(td.inf, td.inf, 0),
938+
freqs=self.freqs[0],
939+
name="modes",
940+
colocate=True,
941+
mode_spec=td.ModeSpec(num_modes=2, target_neff=4.0),
942+
store_fields_direction="+",
943+
)
944+
return self.simdata(monitor)["modes"]
945+
946+
@pytest.mark.parametrize("field_data_fixture", ["field_data", "field_data_single_frequency"])
922947
@pytest.mark.parametrize("background_index", [1, 2, 3])
923948
@pytest.mark.parametrize("freq", [*list(freqs), None])
924949
@pytest.mark.parametrize("n_x", [2**5, 2**6])
925950
@pytest.mark.parametrize("n_y", [2**5, 2**6])
926951
@pytest.mark.parametrize("units", ["mm", "cm", "in", "m"])
927952
def test_fielddata_tozbf_readzbf(
928-
self, tmp_path, field_data, background_index, freq, n_x, n_y, units
953+
self,
954+
tmp_path,
955+
request,
956+
field_data_fixture,
957+
background_index,
958+
freq,
959+
n_x,
960+
n_y,
961+
units,
929962
):
930963
"""Test that FieldData.to_zbf() -> ZBFData.read_zbf() works"""
931964
zbf_filename = tmp_path / "testzbf.zbf"
932965

933966
# write to zbf and then load it back in
967+
field_data = request.getfixturevalue(field_data_fixture)
934968
ex, ey = field_data.to_zbf(
935969
fname=zbf_filename,
936970
background_refractive_index=background_index,
@@ -960,13 +994,20 @@ def test_fielddata_tozbf_readzbf(
960994
assert np.allclose(ex.values, zbfdata.Ex)
961995
assert np.allclose(ey.values, zbfdata.Ey)
962996

997+
@pytest.mark.parametrize("mode_data_fixture", ["mode_data", "mode_data_single_frequency"])
963998
@pytest.mark.parametrize("mode_index", [0, 1])
964-
def test_tozbf_modedata(self, tmp_path, mode_data, mode_index):
999+
def test_tozbf_modedata(
1000+
self,
1001+
tmp_path,
1002+
request,
1003+
mode_data_fixture,
1004+
mode_index,
1005+
):
9651006
"""Tests ModeData.to_zbf()"""
9661007
zbf_filename = tmp_path / "testzbf_modedata.zbf"
9671008

9681009
# write to zbf and then load it back in
969-
ex, ey = mode_data.to_zbf(
1010+
ex, ey = request.getfixturevalue(mode_data_fixture).to_zbf(
9701011
fname=zbf_filename,
9711012
background_refractive_index=1,
9721013
freq=self.freq0,

tidy3d/components/data/monitor_data.py

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1163,9 +1163,14 @@ def to_zbf(
11631163
else:
11641164
freq = freq.item()
11651165

1166-
mode_area = mode_area.interp(f=freq)
1167-
e_x = e_x.interp(f=freq)
1168-
e_y = e_y.interp(f=freq)
1166+
# If the data has just one frequency, avoid Nans at the interpolation
1167+
if len(e_x.f) > 1:
1168+
mode_area = mode_area.interp(f=freq)
1169+
e_x = e_x.interp(f=freq)
1170+
e_y = e_y.interp(f=freq)
1171+
else:
1172+
e_x = e_x.isel(f=0, drop=True)
1173+
e_y = e_y.isel(f=0, drop=True)
11691174

11701175
# If the data is ModeData, choose one of the modes to save
11711176
if "mode_index" in e_x.coords:
@@ -1241,7 +1246,7 @@ def to_zbf(
12411246
)
12421247
fout.write(struct.pack("<8d", 0, 0, 0, 0, 0, 0, 0, 0)) # unused values
12431248
for e in (e_x, e_y):
1244-
e_flat = e.values.flatten(order="C")
1249+
e_flat = e.values.flatten(order="F")
12451250
# Interweave real and imaginary parts
12461251
e_values = np.ravel(np.column_stack((e_flat.real, e_flat.imag)))
12471252
fout.write(struct.pack(f"<{2 * n_x * n_y}d", *e_values))

tidy3d/components/data/zbf.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -121,11 +121,11 @@ def read_zbf(filename: str) -> ZBFData:
121121
) from None
122122

123123
# load E field
124-
Ex_real = np.asarray(rawx[0::2]).reshape(nx, ny)
125-
Ex_imag = np.asarray(rawx[1::2]).reshape(nx, ny)
124+
Ex_real = np.asarray(rawx[0::2]).reshape(nx, ny, order="F")
125+
Ex_imag = np.asarray(rawx[1::2]).reshape(nx, ny, order="F")
126126
if ispol:
127-
Ey_real = np.asarray(rawy[0::2]).reshape(nx, ny)
128-
Ey_imag = np.asarray(rawy[1::2]).reshape(nx, ny)
127+
Ey_real = np.asarray(rawy[0::2]).reshape(nx, ny, order="F")
128+
Ey_imag = np.asarray(rawy[1::2]).reshape(nx, ny, order="F")
129129
else:
130130
Ey_real = np.zeros((nx, ny))
131131
Ey_imag = np.zeros((nx, ny))

0 commit comments

Comments
 (0)