Skip to content

Commit dfe3e19

Browse files
committed
fix: even more rebase changes
1 parent 59ada6c commit dfe3e19

10 files changed

Lines changed: 73 additions & 51 deletions

File tree

tests/test_components/test_lumped_element.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -341,7 +341,7 @@ def test_1d_lumped_element_not_allowed():
341341
RLC = td.RLCNetwork(resistance=50)
342342

343343
# Attempting to create a 1D lumped element should raise a validation error
344-
with pytest.raises(pydantic.ValidationError):
344+
with pytest.raises(ValidationError):
345345
td.LinearLumpedElement(
346346
center=[0, 0, 0],
347347
size=[1, 0, 0], # 1D element: only x has non-zero size (invalid)
@@ -351,7 +351,7 @@ def test_1d_lumped_element_not_allowed():
351351
)
352352

353353
# Other 1D configurations should also fail
354-
with pytest.raises(pydantic.ValidationError):
354+
with pytest.raises(ValidationError):
355355
td.LinearLumpedElement(
356356
center=[0, 0, 0],
357357
size=[0, 1, 0], # 1D element: only y has non-zero size (invalid)
@@ -360,7 +360,7 @@ def test_1d_lumped_element_not_allowed():
360360
name="1D_RLC",
361361
)
362362

363-
with pytest.raises(pydantic.ValidationError):
363+
with pytest.raises(ValidationError):
364364
td.LinearLumpedElement(
365365
center=[0, 0, 0],
366366
size=[0, 0, 1], # 1D element: only z has non-zero size (invalid)

tests/test_components/test_mode.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -490,21 +490,21 @@ def test_filter_pol_with_default_sort_spec():
490490
assert ms.filter_pol == "te"
491491

492492
# filter_pol should fail with custom sort_spec
493-
with pytest.raises(pydantic.ValidationError):
493+
with pytest.raises(pd.ValidationError):
494494
td.ModeSpec(
495495
num_modes=3,
496496
filter_pol="te",
497497
sort_spec=td.ModeSortSpec(sort_key="k_eff"),
498498
)
499499

500-
with pytest.raises(pydantic.ValidationError):
500+
with pytest.raises(pd.ValidationError):
501501
td.ModeSpec(
502502
num_modes=3,
503503
filter_pol="te",
504504
sort_spec=td.ModeSortSpec(filter_key="TE_fraction"),
505505
)
506506

507-
with pytest.raises(pydantic.ValidationError):
507+
with pytest.raises(pd.ValidationError):
508508
td.ModeSpec(
509509
num_modes=3,
510510
filter_pol="te",

tests/test_plugins/smatrix/test_terminal_component_modeler.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -576,13 +576,13 @@ def test_validate_port_must_be_planar():
576576
injection axis can be properly determined for the underlying lumped element.
577577
"""
578578
# 1D port (two zeros) should fail validation
579-
with pytest.raises(pd.ValidationError):
579+
with pytest.raises(ValidationError):
580580
LumpedPort(center=(0, 0, 0), size=(1, 0, 0), voltage_axis=0, impedance=50, name="1D_port")
581581

582-
with pytest.raises(pd.ValidationError):
582+
with pytest.raises(ValidationError):
583583
LumpedPort(center=(0, 0, 0), size=(0, 1, 0), voltage_axis=1, impedance=50, name="1D_port")
584584

585-
with pytest.raises(pd.ValidationError):
585+
with pytest.raises(ValidationError):
586586
LumpedPort(center=(0, 0, 0), size=(0, 0, 1), voltage_axis=2, impedance=50, name="1D_port")
587587

588588
# Planar port (one zero) should work fine

tests/test_plugins/test_mode_solver.py

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,9 @@
44

55
import matplotlib.pyplot as plt
66
import numpy as np
7+
import pydantic as pd
78
import pytest
89
import responses
9-
from pydantic import ValidationError
1010

1111
import tidy3d as td
1212
import tidy3d.plugins.mode.web as msweb
@@ -336,7 +336,7 @@ def test_mode_solver_validation():
336336
)
337337

338338
# frequency is too low
339-
with pytest.raises(ValidationError):
339+
with pytest.raises(pd.ValidationError):
340340
ms = ModeSolver(
341341
simulation=simulation,
342342
plane=PLANE,
@@ -356,7 +356,7 @@ def test_mode_solver_validation():
356356

357357
# num of modes * plane grid points too large
358358
# 1) number of modes too big
359-
with pytest.raises(ValidationError):
359+
with pytest.raises(pd.ValidationError):
360360
ms = ModeSolver(
361361
simulation=simulation,
362362
plane=PLANE,
@@ -365,7 +365,7 @@ def test_mode_solver_validation():
365365
direction="+",
366366
)
367367
# 2) number of grid points too big
368-
with pytest.raises(ValidationError):
368+
with pytest.raises(pd.ValidationError):
369369
ms = ModeSolver(
370370
simulation=simulation.updated_copy(grid_spec=td.GridSpec.uniform(dl=0.0001)),
371371
plane=PLANE,
@@ -1385,13 +1385,13 @@ def test_translated_dot():
13851385
def test_mode_spec_filter_pol_sort_spec_exclusive():
13861386
"""Ensure ModeSpec errors when both filter_pol and sort_spec are set."""
13871387
# Using a non-default sort_key triggers the exclusivity check
1388-
with pytest.raises(pydantic.ValidationError, match="simultaneously"):
1388+
with pytest.raises(pd.ValidationError, match="simultaneously"):
13891389
_ = td.ModeSpec(num_modes=1, filter_pol="te", sort_spec=td.ModeSortSpec(sort_key="k_eff"))
13901390
# Using a sort_reference also triggers the exclusivity check
1391-
with pytest.raises(pydantic.ValidationError, match="simultaneously"):
1391+
with pytest.raises(pd.ValidationError, match="simultaneously"):
13921392
_ = td.ModeSpec(num_modes=1, filter_pol="te", sort_spec=td.ModeSortSpec(sort_reference=1.5))
13931393
# Using a filter_key also triggers the exclusivity check
1394-
with pytest.raises(pydantic.ValidationError, match="simultaneously"):
1394+
with pytest.raises(pd.ValidationError, match="simultaneously"):
13951395
_ = td.ModeSpec(
13961396
num_modes=1, filter_pol="te", sort_spec=td.ModeSortSpec(filter_key="TE_fraction")
13971397
)
@@ -1783,13 +1783,13 @@ def test_mode_sort_spec_drop_modes_all_filtered():
17831783

17841784

17851785
def test_mode_sort_spec_drop_modes_requires_filter():
1786-
with pytest.raises(pydantic.ValidationError):
1786+
with pytest.raises(pd.ValidationError):
17871787
td.ModeSortSpec(keep_modes="filtered")
17881788

17891789

17901790
def test_mode_sort_spec_keep_modes_at_most_num_modes():
17911791
sort_spec = td.ModeSortSpec(keep_modes=4)
1792-
with pytest.raises(pydantic.ValidationError):
1792+
with pytest.raises(pd.ValidationError):
17931793
_ = td.ModeSpec(num_modes=2, sort_spec=sort_spec)
17941794

17951795

@@ -1815,7 +1815,7 @@ def test_mode_sort_spec_fill_fraction_box_filter_drops_modes():
18151815

18161816

18171817
def test_mode_sort_spec_fill_fraction_box_requires_bounding_box():
1818-
with pytest.raises(pydantic.ValidationError):
1818+
with pytest.raises(pd.ValidationError):
18191819
td.ModeSortSpec(filter_key="fill_fraction_box")
18201820

18211821

tidy3d/components/autograd/derivative_utils.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -248,7 +248,7 @@ def create_interpolators(self, dtype: Optional[np.dtype[Any]] = None) -> dict[st
248248
coord_cache = {}
249249

250250
def _make_lazy_interpolator_group(
251-
field_data_dict: Optional[FieldData],
251+
field_data_dict: Optional[FieldDataDict],
252252
group_key: Optional[str],
253253
is_field_group: bool = True,
254254
override_method: Optional[str] = None,
@@ -717,7 +717,7 @@ def _snap_coordinate_outside(
717717
718718
Parameters
719719
----------
720-
field_components: FieldData
720+
field_components: FieldDataDict
721721
The field components (i.e - Ex, Ey, Ez, Hx, Hy, Hz) that we would like to sample just
722722
outside the PEC surface using nearest interpolation.
723723

tidy3d/components/data/monitor_data.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@
1515

1616
from tidy3d.components.base import cached_property
1717
from tidy3d.components.base_sim.data.monitor_data import AbstractMonitorData
18-
from tidy3d.components.geometry.base import Box
1918
from tidy3d.components.grid.grid import Coords, Grid
2019
from tidy3d.components.medium import Medium, MediumType
2120
from tidy3d.components.monitor import (
@@ -91,6 +90,7 @@
9190
from pandas import DataFrame
9291

9392
from tidy3d.compat import Self
93+
from tidy3d.components.geometry.base import Box
9494
from tidy3d.components.mode_spec import ModeSortSpec, ModeSpec
9595
from tidy3d.components.source.base import Source
9696
from tidy3d.components.source.current import PointDipole
@@ -2554,7 +2554,9 @@ def sort_modes(
25542554
all_inds = np.arange(num_modes)
25552555

25562556
# Helper to compute ordered indices within a subset
2557-
def _order_indices(indices: NDArray, vals_all: DataArray, sort_order) -> NDArray:
2557+
def _order_indices(
2558+
indices: NDArray, vals_all: DataArray, sort_order: Literal["ascending", "descending"]
2559+
) -> NDArray:
25582560
if indices.size == 0:
25592561
return indices
25602562
vals = vals_all.isel(mode_index=indices)

tidy3d/components/medium.py

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1315,8 +1315,20 @@ def _interp_axis(
13151315
values = values.real
13161316

13171317
if sum_over_freqs:
1318-
return values.sum(axis=-1).reshape(eps_shape)
1319-
return values.reshape([*eps_shape, values.shape[-1]])
1318+
vjp_array = values.sum(axis=-1).reshape(eps_shape)
1319+
else:
1320+
vjp_array = values.reshape([*eps_shape, values.shape[-1]])
1321+
1322+
# match derivative dtype to the underlying dataset
1323+
target_array = getattr(spatial_data, "values", None)
1324+
if target_array is None and hasattr(spatial_data, "data"):
1325+
target_array = spatial_data.data
1326+
if target_array is not None:
1327+
target_dtype = np.asarray(target_array).dtype
1328+
if not np.issubdtype(target_dtype, np.complexfloating):
1329+
vjp_array = np.real(vjp_array).astype(target_dtype, copy=False)
1330+
1331+
return vjp_array
13201332

13211333

13221334
""" Dispersionless Medium """

tidy3d/components/mode_spec.py

Lines changed: 31 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55
from abc import ABC, abstractmethod
66
from math import isclose
7-
from typing import TYPE_CHECKING, Literal, Optional, Union
7+
from typing import TYPE_CHECKING, Any, Literal, Optional, Union
88

99
import numpy as np
1010
from pydantic import (
@@ -112,7 +112,7 @@ class ModeSortSpec(Tidy3dBaseModel):
112112
title="Filtering order",
113113
description="Select whether the first group contains values over or under the reference.",
114114
)
115-
bounding_box: Optional[Box] = pd.Field(
115+
bounding_box: Optional[Box] = Field(
116116
None,
117117
title="Bounding box",
118118
description=(
@@ -121,7 +121,7 @@ class ModeSortSpec(Tidy3dBaseModel):
121121
"still intersect the monitor plane. Required when filtering or sorting with that key."
122122
),
123123
)
124-
keep_modes: Union[Literal["all"], Literal["filtered"], pd.PositiveInt] = pd.Field(
124+
keep_modes: Union[Literal["all"], Literal["filtered"], PositiveInt] = Field(
125125
"all",
126126
title="Keep Modes",
127127
description=(
@@ -151,19 +151,22 @@ class ModeSortSpec(Tidy3dBaseModel):
151151
description=_build_sort_order_description(),
152152
)
153153

154-
@pd.validator("sort_order", always=True)
155-
@skip_if_fields_missing(["sort_key", "sort_reference"])
156-
def _set_default_sort_order(cls, val, values):
154+
@model_validator(mode="before")
155+
@classmethod
156+
def _set_default_sort_order(cls, data: dict[str, Any]) -> dict[str, Any]:
157157
"""Set default sort order based on sort_key and sort_reference."""
158+
val = data.get("sort_order")
158159
if val is not None:
159-
return val
160-
sort_reference = values.get("sort_reference")
160+
return data
161+
sort_reference = data.get("sort_reference")
161162
# When sorting by distance to a reference, ascending is natural (closest first)
162163
if sort_reference is not None:
163-
return "ascending"
164+
data["sort_order"] = "ascending"
165+
return data
164166
# Otherwise, use the natural default for each key
165-
sort_key = values.get("sort_key")
166-
return MODE_DATA_KEY_SORT_ORDER.get(sort_key, "ascending")
167+
sort_key = data.get("sort_key", "n_eff")
168+
data["sort_order"] = MODE_DATA_KEY_SORT_ORDER.get(sort_key, "ascending")
169+
return data
167170

168171
# Frequency tracking - applied after sorting and filtering
169172
track_freq: Optional[TrackFreq] = Field(
@@ -175,23 +178,25 @@ def _set_default_sort_order(cls, val, values):
175178
"while at other frequencies it can change depending on the mode tracking.",
176179
)
177180

178-
@pd.validator("keep_modes", always=True)
179-
def _drop_requires_filter(cls, val, values):
180-
if val == "filtered" and values.get("filter_key") is None:
181+
@model_validator(mode="after")
182+
def _drop_requires_filter(self: Self) -> Self:
183+
val = self.keep_modes
184+
if val == "filtered" and self.filter_key is None:
181185
raise ValidationError(
182186
"ModeSortSpec.keep_modes 'filtered' requires 'filter_key' to be set."
183187
)
184-
return val
188+
return self
185189

186-
@pd.root_validator(skip_on_failure=True)
187-
def _bounding_box_required_for_fill_fraction(cls, values):
188-
bbox = values.get("bounding_box")
189-
keys = (values.get("filter_key"), values.get("sort_key"))
190+
@model_validator(mode="before")
191+
@classmethod
192+
def _bounding_box_required_for_fill_fraction(cls, data: dict[str, Any]) -> dict[str, Any]:
193+
bbox = data.get("bounding_box")
194+
keys = (data.get("filter_key"), data.get("sort_key"))
190195
if any(key == "fill_fraction_box" for key in keys) and bbox is None:
191196
raise ValidationError(
192197
"ModeSortSpec.bounding_box must be set when using 'fill_fraction_box'."
193198
)
194-
return values
199+
return data
195200

196201
@property
197202
def has_custom_sort_or_filter(self) -> bool:
@@ -210,7 +215,7 @@ def has_custom_sort_or_filter(self) -> bool:
210215
self.filter_key is not None
211216
or self.sort_key != "n_eff"
212217
or self.sort_reference is not None
213-
or self.sort_order != "descending"
218+
or (self.sort_order is not None and self.sort_order != "descending")
214219
or self.keep_modes != "all"
215220
)
216221

@@ -692,11 +697,12 @@ class AbstractModeSpec(Tidy3dBaseModel, ABC):
692697
"not be ``None``) to ensure consistent mode ordering across frequencies.",
693698
)
694699

695-
@pd.validator("sort_spec", always=True)
696-
def _keep_modes_at_most_num_modes(cls, val, values):
700+
@model_validator(mode="after")
701+
def _keep_modes_at_most_num_modes(self: Self) -> Self:
702+
val = self.sort_spec
697703
if val is not None:
698704
if isinstance(val.keep_modes, int):
699-
num_modes = values.get("num_modes")
705+
num_modes = self.num_modes
700706
if val.keep_modes > num_modes:
701707
raise ValidationError(
702708
"ModeSortSpec.keep_modes cannot be larger than 'num_modes'. "
@@ -708,7 +714,7 @@ def _keep_modes_at_most_num_modes(cls, val, values):
708714
"'keep_modes=\"filtered\"' to keep exactly those modes matching "
709715
"the filter, or setting 'keep_modes=\"all\"' to keep all modes."
710716
)
711-
return val
717+
return self
712718

713719
@field_validator("group_index_step", mode="before")
714720
@classmethod

tidy3d/plugins/smatrix/component_modelers/terminal.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -213,7 +213,7 @@ class TerminalComponentModeler(AbstractComponentModeler, MicrowaveBaseModel):
213213
description="The low frequency smoothing parameters for the terminal component simulation.",
214214
)
215215

216-
structure_priority_mode: Optional[PriorityMode] = pd.Field(
216+
structure_priority_mode: Optional[PriorityMode] = Field(
217217
"conductor",
218218
title="Structure Priority Setting",
219219
description="If not `None`, override the structure priority mode in the simulation. "

tidy3d/plugins/smatrix/ports/rectangular_lumped.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,8 @@ def injection_axis(self) -> int:
104104
@model_validator(mode="after")
105105
def _voltage_axis_in_plane(self) -> Self:
106106
"""Ensure voltage integration axis is in the port's plane."""
107+
if self.size is None:
108+
return self
107109
if self.voltage_axis == self.size.index(0.0):
108110
raise ValidationError("'voltage_axis' must lie in the port's plane.")
109111
return self

0 commit comments

Comments
 (0)