Skip to content

Commit aafa10c

Browse files
preliminary version
1 parent 392a871 commit aafa10c

File tree

4 files changed

+82
-17
lines changed

4 files changed

+82
-17
lines changed

tests/test_plugins/smatrix/test_terminal_component_modeler.py

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
import xarray as xr
88

99
import tidy3d as td
10+
from tidy3d.components.boundary import BroadbandModeABCSpec
1011
from tidy3d.components.data.data_array import FreqDataArray
1112
from tidy3d.exceptions import SetupError, Tidy3dError, Tidy3dKeyError
1213
from tidy3d.plugins.microwave import (
@@ -900,3 +901,45 @@ def test_get_combined_antenna_parameters_data(monkeypatch, tmp_path):
900901
assert not np.allclose(
901902
antenna_params.radiation_efficiency, single_port_params.radiation_efficiency
902903
)
904+
905+
906+
def test_wave_port_to_absorber(tmp_path):
907+
"""Test that wave port absorber can be specified as a boolean, ABCBoundary, or ModeABCBoundary."""
908+
909+
# test automatic absorber
910+
modeler = make_coaxial_component_modeler(
911+
path_dir=str(tmp_path), port_types=(WavePort, WavePort)
912+
)
913+
sim = list(modeler.sim_dict.values())[0]
914+
915+
absorber = sim.internal_absorbers[0]
916+
917+
assert absorber.boundary_spec.mode_spec == modeler.ports[0].mode_spec
918+
assert absorber.boundary_spec.mode_index == modeler.ports[0].mode_index
919+
assert absorber.boundary_spec.plane == modeler.ports[0].geometry
920+
assert absorber.boundary_spec.freq_spec == BroadbandModeABCSpec(
921+
frequency_range=(np.min(modeler.freqs), np.max(modeler.freqs))
922+
)
923+
924+
# test to_absorber()
925+
absorber = modeler.ports[0].to_absorber(freq_spec=1e9)
926+
assert absorber.boundary_spec.freq_spec == 1e9
927+
928+
absorber = modeler.ports[0].to_absorber(
929+
freq_spec=BroadbandModeABCSpec(frequency_range=(1e9, 2e9))
930+
)
931+
assert absorber.boundary_spec.freq_spec == BroadbandModeABCSpec(frequency_range=(1e9, 2e9))
932+
933+
# test no automatic absorber
934+
modeler = modeler.updated_copy(ports=[modeler.ports[0].updated_copy(absorber=False)])
935+
sim = list(modeler.sim_dict.values())[0]
936+
assert len(sim.internal_absorbers) == 0
937+
938+
# test custom boundary spec
939+
custom_boundary_spec = td.ModeABCBoundary(plane=td.Box(size=(0.1, 0.1, 0)), freq_spec=1e9)
940+
modeler = modeler.updated_copy(
941+
ports=[modeler.ports[0].updated_copy(absorber=custom_boundary_spec)]
942+
)
943+
sim = list(modeler.sim_dict.values())[0]
944+
absorber = sim.internal_absorbers[0]
945+
assert absorber.boundary_spec == custom_boundary_spec

tidy3d/components/boundary.py

Lines changed: 22 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ def _warn_num_layers(cls, val):
5959
DEFAULT_BROADBAND_MODE_ABC_NUM_FREQS = 15
6060
DEFAULT_BROADBAND_MODE_ABC_NUM_POLES = 5
6161
MAX_BROADBAND_MODE_ABC_NUM_POLES = 10
62-
MAX_BROADBAND_MODE_ABC_NUM_FREQS = 21
62+
MAX_BROADBAND_MODE_ABC_NUM_FREQS = 101
6363

6464

6565
class BoundaryEdge(ABC, Tidy3dBaseModel):
@@ -175,7 +175,7 @@ class BroadbandModeABCSpec(Tidy3dBaseModel):
175175
176176
Example
177177
-------
178-
>>> broadband_mode_abc_spec = BroadbandModeABCSpec(frequency_range=(fmin=100e12, fmax=120e12), fit_param=BroadbandModeABCFitterParam())
178+
>>> broadband_mode_abc_spec = BroadbandModeABCSpec(frequency_range=(100e12, 120e12), fit_param=BroadbandModeABCFitterParam())
179179
"""
180180

181181
frequency_range: FreqBound = pd.Field(
@@ -273,13 +273,19 @@ def is_plane(cls, val):
273273
return val
274274

275275
@classmethod
276-
def from_source(cls, source: ModeSource) -> ModeABCBoundary:
276+
def from_source(
277+
cls,
278+
source: ModeSource,
279+
freq_spec: Optional[Union[pd.PositiveFloat, BroadbandModeABCSpec]] = None,
280+
) -> ModeABCBoundary:
277281
"""Instantiate from a ``ModeSource``.
278282
279283
Parameters
280284
----------
281285
source : :class:`ModeSource`
282286
Mode source.
287+
freq_spec : Optional[Union[pd.PositiveFloat, BroadbandModeABCSpec]] = None
288+
Specifies the frequency at which field is absorbed. If ``None``, then the central frequency of the source is used. If ``BroadbandModeABCSpec``, then the field is absorbed over the specified frequency range.
283289
284290
Returns
285291
-------
@@ -294,11 +300,14 @@ def from_source(cls, source: ModeSource) -> ModeABCBoundary:
294300
>>> abc_boundary = ModeABCBoundary.from_source(source=source)
295301
"""
296302

303+
if freq_spec is None:
304+
freq_spec = source.source_time.freq0
305+
297306
return cls(
298307
plane=source.bounding_box,
299308
mode_spec=source.mode_spec,
300309
mode_index=source.mode_index,
301-
freq_spec=source.source_time.freq0,
310+
freq_spec=freq_spec,
302311
)
303312

304313
@classmethod
@@ -1135,13 +1144,19 @@ def mode_abc(
11351144
return cls(plus=plus, minus=minus)
11361145

11371146
@classmethod
1138-
def mode_abc_from_source(cls, source: ModeSource):
1147+
def mode_abc_from_source(
1148+
cls,
1149+
source: ModeSource,
1150+
freq_spec: Optional[Union[pd.PositiveFloat, BroadbandModeABCSpec]] = None,
1151+
):
11391152
"""One-way wave equation mode ABC boundary specification on both sides along a dimension constructed from a mode source.
11401153
11411154
Parameters
11421155
----------
11431156
source : :class:`ModeSource`
11441157
Mode source.
1158+
freq_spec : Optional[Union[pd.PositiveFloat, BroadbandModeABCSpec]] = None
1159+
Specifies the frequency at which field is absorbed. If ``None``, then the central frequency of the source is used. If ``BroadbandModeABCSpec``, then the field is absorbed over the specified frequency range.
11451160
11461161
Example
11471162
-------
@@ -1150,8 +1165,8 @@ def mode_abc_from_source(cls, source: ModeSource):
11501165
>>> source = ModeSource(size=(1, 1, 0), source_time=pulse, direction='+')
11511166
>>> abc = Boundary.mode_abc_from_source(source=source)
11521167
"""
1153-
plus = ModeABCBoundary.from_source(source=source)
1154-
minus = ModeABCBoundary.from_source(source=source)
1168+
plus = ModeABCBoundary.from_source(source=source, freq_spec=freq_spec)
1169+
minus = ModeABCBoundary.from_source(source=source, freq_spec=freq_spec)
11551170
return cls(plus=plus, minus=minus)
11561171

11571172
@classmethod

tidy3d/plugins/smatrix/component_modelers/terminal.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -162,7 +162,9 @@ def sim_dict(self) -> dict[str, Simulation]:
162162
] + self._shift_value_signed(wave_port)
163163
port_absorber = wave_port.to_absorber(
164164
snap_center=mode_src_pos,
165-
freq_spec=BroadbandModeABCSpec(frequency_range=(self.freqs, self.freqs)),
165+
freq_spec=BroadbandModeABCSpec(
166+
frequency_range=(np.min(self.freqs), np.max(self.freqs))
167+
),
166168
)
167169
new_absorbers.append(port_absorber)
168170

tidy3d/plugins/smatrix/ports/wave.py

Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
import pydantic.v1 as pd
99

1010
from tidy3d.components.base import cached_property, skip_if_fields_missing
11-
from tidy3d.components.boundary import InternalAbsorber, ModeABCBoundary
11+
from tidy3d.components.boundary import ABCBoundary, InternalAbsorber, ModeABCBoundary
1212
from tidy3d.components.data.data_array import FreqDataArray, FreqModeDataArray
1313
from tidy3d.components.data.monitor_data import ModeData
1414
from tidy3d.components.data.sim_data import SimulationData
@@ -91,10 +91,11 @@ class WavePort(AbstractTerminalPort, Box):
9191
description="Use conjugated or non-conjugated dot product for mode decomposition.",
9292
)
9393

94-
absorber: bool = pd.Field(
94+
absorber: Union[bool, ABCBoundary, ModeABCBoundary] = pd.Field(
9595
True,
9696
title="Absorber.",
97-
description="Place a mode absorber in the port.",
97+
description="Place a mode absorber in the port. If ``True``, an automatically generated mode absorber is placed in the port. "
98+
"If ``ABCBoundary`` or ``ModeABCBoundary``, a mode absorber is placed in the port with the specified boundary conditions.",
9899
)
99100

100101
def _mode_voltage_coefficients(self, mode_data: ModeData) -> FreqModeDataArray:
@@ -197,15 +198,19 @@ def to_absorber(
197198
center = list(self.center)
198199
if snap_center:
199200
center[self.injection_axis] = snap_center
201+
if isinstance(self.absorber, (ABCBoundary, ModeABCBoundary)):
202+
boundary_spec = self.absorber
203+
else:
204+
boundary_spec = ModeABCBoundary(
205+
mode_spec=self.mode_spec,
206+
mode_index=self.mode_index,
207+
plane=self.geometry,
208+
freq_spec=freq_spec,
209+
)
200210
return InternalAbsorber(
201211
center=center,
202212
size=self.size,
203-
boundary_spec=ModeABCBoundary(
204-
mode_spec=self.mode_spec,
205-
mode_index=self.mode_index,
206-
plane=self.bounding_box,
207-
frequency=freq_spec,
208-
),
213+
boundary_spec=boundary_spec,
209214
direction="-"
210215
if self.direction == "+"
211216
else "+", # absorb in the opposite direction of source

0 commit comments

Comments
 (0)