Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 39 additions & 0 deletions tests/test_components/material/test_multi_physics.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import copy

import pytest
import tidy3d as td


@pytest.fixture
def dummy_optical():
return td.Medium(permittivity=1.0)


def test_delegated_attributes_work(dummy_optical):
mp = td.MultiPhysicsMedium(optical=dummy_optical)

# delegated names resolve
assert mp.is_pec is dummy_optical.is_pec
assert mp._eps_plot == dummy_optical._eps_plot
assert mp.viz_spec == dummy_optical.viz_spec

# deepcopy still succeeds because __deepcopy__ is ignored
copy.deepcopy(mp)


def test_delegated_attribute_without_optical_raises():
mp_no_opt = td.MultiPhysicsMedium(optical=None)

with pytest.raises(AttributeError, match=r"optical medium is 'None'"):
_ = mp_no_opt.is_pec


def test_has_cached_props(dummy_optical):
mp = td.MultiPhysicsMedium(optical=dummy_optical)
mp._cached_properties


def test_unknown_attribute_error(dummy_optical):
mp = td.MultiPhysicsMedium(optical=dummy_optical)
with pytest.raises(AttributeError, match=r"Did you mean to access the attribute of one"):
_ = mp.not_a_real_attribute
23 changes: 18 additions & 5 deletions tidy3d/components/material/multi_physics.py
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,12 @@ def __getattr__(self, name: str):
Extend that mapping as additional cross-medium shim behaviour becomes
necessary.
"""
# first check whether the attribute is already present
try:
return super().__getattr__(name)
except AttributeError:
pass

IGNORED_ATTRIBUTES = ["__deepcopy__"]
Comment thread
frederikschubertflex marked this conversation as resolved.
if name in IGNORED_ATTRIBUTES:
return None
Expand All @@ -139,11 +145,18 @@ def __getattr__(self, name: str):
}

if name in DELEGATED_ATTRIBUTES:
return getattr(DELEGATED_ATTRIBUTES[name], name)
else:
raise ValueError(
f"MultiPhysicsMedium has no attribute called {name}. Did you mean to access the attribute of one of the optical, heat or charge media?"
)
sub = DELEGATED_ATTRIBUTES[name]
if sub is None:
raise AttributeError(
f"Requested attribute {name!r}, but the optical medium is 'None' "
" on this 'MultiPhysicsMedium' instance."
)
return getattr(sub, name)

raise AttributeError(
f"MultiPhysicsMedium has no attribute called {name}. "
"Did you mean to access the attribute of one of the optical, heat or charge media?"
)

@property
def heat_spec(self):
Expand Down