Skip to content

Commit de6c2ce

Browse files
yaugenst-flexmomchil-flex
authored andcommitted
fix multiphysics medium attribute lookup
1 parent 0a9cffe commit de6c2ce

File tree

2 files changed

+57
-5
lines changed

2 files changed

+57
-5
lines changed
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
import copy
2+
3+
import pytest
4+
import tidy3d as td
5+
6+
7+
@pytest.fixture
8+
def dummy_optical():
9+
return td.Medium(permittivity=1.0)
10+
11+
12+
def test_delegated_attributes_work(dummy_optical):
13+
mp = td.MultiPhysicsMedium(optical=dummy_optical)
14+
15+
# delegated names resolve
16+
assert mp.is_pec is dummy_optical.is_pec
17+
assert mp._eps_plot == dummy_optical._eps_plot
18+
assert mp.viz_spec == dummy_optical.viz_spec
19+
20+
# deepcopy still succeeds because __deepcopy__ is ignored
21+
copy.deepcopy(mp)
22+
23+
24+
def test_delegated_attribute_without_optical_raises():
25+
mp_no_opt = td.MultiPhysicsMedium(optical=None)
26+
27+
with pytest.raises(AttributeError, match=r"optical medium is 'None'"):
28+
_ = mp_no_opt.is_pec
29+
30+
31+
def test_has_cached_props(dummy_optical):
32+
mp = td.MultiPhysicsMedium(optical=dummy_optical)
33+
mp._cached_properties
34+
35+
36+
def test_unknown_attribute_error(dummy_optical):
37+
mp = td.MultiPhysicsMedium(optical=dummy_optical)
38+
with pytest.raises(AttributeError, match=r"Did you mean to access the attribute of one"):
39+
_ = mp.not_a_real_attribute

tidy3d/components/material/multi_physics.py

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,12 @@ def __getattr__(self, name: str):
128128
Extend that mapping as additional cross-medium shim behaviour becomes
129129
necessary.
130130
"""
131+
# first check whether the attribute is already present
132+
try:
133+
return super().__getattr__(name)
134+
except AttributeError:
135+
pass
136+
131137
IGNORED_ATTRIBUTES = ["__deepcopy__"]
132138
if name in IGNORED_ATTRIBUTES:
133139
return None
@@ -139,11 +145,18 @@ def __getattr__(self, name: str):
139145
}
140146

141147
if name in DELEGATED_ATTRIBUTES:
142-
return getattr(DELEGATED_ATTRIBUTES[name], name)
143-
else:
144-
raise ValueError(
145-
f"MultiPhysicsMedium has no attribute called {name}. Did you mean to access the attribute of one of the optical, heat or charge media?"
146-
)
148+
sub = DELEGATED_ATTRIBUTES[name]
149+
if sub is None:
150+
raise AttributeError(
151+
f"Requested attribute {name!r}, but the optical medium is 'None' "
152+
" on this 'MultiPhysicsMedium' instance."
153+
)
154+
return getattr(sub, name)
155+
156+
raise AttributeError(
157+
f"MultiPhysicsMedium has no attribute called {name}. "
158+
"Did you mean to access the attribute of one of the optical, heat or charge media?"
159+
)
147160

148161
@property
149162
def heat_spec(self):

0 commit comments

Comments
 (0)