Skip to content

Commit 54127cb

Browse files
pecframe class
1 parent 6ba8d2e commit 54127cb

File tree

12 files changed

+151
-32
lines changed

12 files changed

+151
-32
lines changed

tests/test_components/test_absorbers.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22

33
from __future__ import annotations
44

5-
import matplotlib.pyplot as plt
65
import pydantic.v1 as pydantic
76
import pytest
87

@@ -391,7 +390,9 @@ def test_abc_boundaries_simulations():
391390
),
392391
sources=[],
393392
medium=td.CustomMedium(
394-
permittivity=td.SpatialDataArray([[[2, 3]]], coords=dict(x=[0], y=[0], z=[0, 1]))
393+
permittivity=td.SpatialDataArray(
394+
[[[2, 3]]], coords={"x": [0], "y": [0], "z": [0, 1]}
395+
)
395396
),
396397
run_time=1e-20,
397398
boundary_spec=td.BoundarySpec.all_sides(td.ABCBoundary()),

tests/test_components/test_source.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -597,3 +597,17 @@ def test_broadband_angled_gaussian_warning():
597597
sources=[s],
598598
normalize_index=None,
599599
)
600+
601+
602+
def test_source_frame():
603+
_ = td.PECFrame()
604+
_ = td.PECFrame(length=4)
605+
with pytest.raises(pydantic.ValidationError):
606+
_ = td.PECFrame(length=0)
607+
608+
_ = td.ModeSource(
609+
source_time=td.GaussianPulse(freq0=td.C_0, fwidth=0.2 * td.C_0),
610+
direction="+",
611+
size=(1, 1, 0),
612+
frame=td.PECFrame(),
613+
)
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
"""Tests frames around sources and absorbers."""
2+
3+
from __future__ import annotations
4+
5+
import pydantic.v1 as pydantic
6+
import pytest
7+
8+
import tidy3d as td
9+
10+
11+
def test_source_absorber_frames():
12+
_ = td.PECFrame()
13+
with pytest.raises(pydantic.ValidationError):
14+
_ = td.PECFrame(length=0)
15+
16+
wvl_um = 1
17+
freq0 = td.C_0 / wvl_um
18+
mode_source = td.ModeSource(
19+
size=(1, 1, 0),
20+
source_time=td.GaussianPulse(freq0=freq0, fwidth=0.2 * freq0),
21+
mode_spec=td.ModeSpec(num_modes=2),
22+
mode_index=1,
23+
frame=td.PECFrame(length=3),
24+
direction="+",
25+
)
26+
sim = td.Simulation(
27+
center=[0, 0, 0],
28+
size=[1, 1, 1],
29+
grid_spec=td.GridSpec.auto(
30+
min_steps_per_wvl=10,
31+
wavelength=wvl_um,
32+
),
33+
sources=[mode_source],
34+
run_time=1e-20,
35+
absorbers=[
36+
td.PortAbsorber(
37+
size=(0.4, 0.5, 0), direction="-", boundary_spec=td.ABCBoundary(permittivity=1)
38+
)
39+
],
40+
)
41+
42+
_ = sim.with_port_frames
43+
44+
# added frame will collide with projection monitor which requires uniform medium
45+
bad_sim = td.Simulation(
46+
center=[0, 0, 0],
47+
size=[1, 1, 1],
48+
grid_spec=td.GridSpec.auto(
49+
min_steps_per_wvl=10,
50+
wavelength=wvl_um,
51+
),
52+
monitors=[
53+
td.FieldProjectionAngleMonitor(
54+
center=[0, 0.25, 0],
55+
size=[1, 0, 1],
56+
freqs=[freq0],
57+
name="n2f_angle",
58+
phi=[0],
59+
theta=[0],
60+
normal_dir="+",
61+
)
62+
],
63+
sources=[mode_source],
64+
run_time=1e-20,
65+
absorbers=[
66+
td.PortAbsorber(
67+
size=(0.4, 0.5, 0), direction="-", boundary_spec=td.ABCBoundary(permittivity=1)
68+
)
69+
],
70+
)
71+
with pytest.raises(pydantic.ValidationError):
72+
_ = bad_sim.with_port_frames

tidy3d/__init__.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -342,6 +342,9 @@
342342
ModeSource,
343343
PlaneWave,
344344
)
345+
from .components.source.frame import (
346+
PECFrame,
347+
)
345348

346349
# sources
347350
from .components.source.time import (
@@ -615,6 +618,7 @@ def set_logging_level(level: str) -> None:
615618
"NonlinearSusceptibility",
616619
"PECBoundary",
617620
"PECConformal",
621+
"PECFrame",
618622
"PECMedium",
619623
"PMCBoundary",
620624
"PMCMedium",

tidy3d/components/boundary.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -232,7 +232,7 @@ def from_monitor(
232232

233233

234234
class PortAbsorber(Box):
235-
"""One-way wave equation absorbing boundary conditions for absorbing a waveguide mode."""
235+
"""Internally placed plane with one-way wave equation boundary conditions for absorption of electromagnetic waves."""
236236

237237
direction: Direction = pd.Field(
238238
...,
@@ -249,7 +249,7 @@ class PortAbsorber(Box):
249249
boundary_spec: Union[ModeABCBoundary, ABCBoundary] = pd.Field(
250250
...,
251251
title="Boundary Specification",
252-
description="Boundary specification.",
252+
description="Boundary specification for defining effective propagation index in the one-way wave equation.",
253253
discriminator=TYPE_TAG_STR,
254254
)
255255

tidy3d/components/eme/simulation.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -211,8 +211,8 @@ class EMESimulation(AbstractYeeGridSimulation):
211211

212212
absorbers: tuple[()] = pd.Field(
213213
(),
214-
title="Inner Absorbers",
215-
description="Inner absorbers.",
214+
title="Port Absorbers",
215+
description="Absorbers based on the first order boundary conditions placed inside the computational domain.",
216216
)
217217

218218
grid_spec: GridSpec = pd.Field(

tidy3d/components/mode/simulation.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -170,8 +170,8 @@ class ModeSimulation(AbstractYeeGridSimulation):
170170

171171
absorbers: tuple[()] = pd.Field(
172172
(),
173-
title="Inner Absorbers",
174-
description="Inner absorbers.",
173+
title="Port Absorbers",
174+
description="Absorbers based on the first order boundary conditions placed inside the computational domain.",
175175
)
176176

177177
grid_spec: GridSpec = pd.Field(

tidy3d/components/simulation.py

Lines changed: 11 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,7 @@
103103
ModeSource,
104104
PlaneWave,
105105
)
106+
from .source.frame import PECFrame
106107
from .source.time import ContinuousWave, CustomSourceTime
107108
from .source.utils import SourceType
108109
from .structure import MeshOverrideStructure, Structure
@@ -178,10 +179,6 @@
178179
# RF frequency warning
179180
RF_FREQ_WARNING = 300e9
180181

181-
# length and thickness of optional PEC frames around mode sources (in cells)
182-
MODE_PEC_FRAME_LENGTH = 2
183-
MODE_PEC_FRAME_THICKNESS = 1e-5
184-
185182

186183
def validate_boundaries_for_zero_dims(warn_on_change: bool = True):
187184
"""Error if absorbing boundaries, bloch boundaries, unmatching pec/pmc, or symmetry is used along a zero dimension."""
@@ -370,8 +367,8 @@ class AbstractYeeGridSimulation(AbstractSimulation, ABC):
370367

371368
absorbers: tuple[PortAbsorber, ...] = pydantic.Field(
372369
(),
373-
title="Inner Absorbers",
374-
description="Inner absorbers.",
370+
title="Port Absorbers",
371+
description="Absorbers based on the first order boundary conditions placed inside the computational domain.",
375372
)
376373

377374
@pydantic.validator("simulation_type", always=True)
@@ -480,7 +477,7 @@ def plot_absorbers(
480477
ax: Ax = None,
481478
shifted: bool = False,
482479
) -> Ax:
483-
"""Plot each of simulation's inner absorbers on a plane defined by one nonzero x,y,z coordinate.
480+
"""Plot each of simulation's port absorbers on a plane defined by one nonzero x,y,z coordinate.
484481
485482
Parameters
486483
----------
@@ -5174,7 +5171,7 @@ def dt(self) -> float:
51745171
Time step (seconds).
51755172
"""
51765173

5177-
return self.with_pec_frames._dt
5174+
return self.with_port_frames._dt
51785175

51795176
@cached_property
51805177
def _dt(self) -> float:
@@ -5668,7 +5665,7 @@ def _make_pec_frame(self, obj) -> Structure:
56685665
direction = obj.direction
56695666
if isinstance(obj, ModeSource):
56705667
axis = obj.injection_axis
5671-
length = obj.pec_frame
5668+
length = obj.frame.length
56725669
else:
56735670
axis = obj.size.index(0.0)
56745671
length = 1
@@ -5709,13 +5706,13 @@ def _make_pec_frame(self, obj) -> Structure:
57095706
return structure
57105707

57115708
@cached_property
5712-
def with_pec_frames(self) -> Simulation:
5709+
def with_port_frames(self) -> Simulation:
57135710
"""Return an instance with added pec frames around mode sources."""
57145711

57155712
pec_frames = [
57165713
self._make_pec_frame(src)
57175714
for src in self.sources
5718-
if isinstance(src, ModeSource) and src.pec_frame > 0
5715+
if isinstance(src, ModeSource) and isinstance(src.frame, PECFrame)
57195716
]
57205717

57215718
pec_frames = pec_frames + [
@@ -5732,13 +5729,13 @@ def with_pec_frames(self) -> Simulation:
57325729
grid_spec=GridSpec.from_grid(self.grid), structures=list(self.structures) + pec_frames
57335730
)
57345731

5735-
def _validate_with_pec_frames(self):
5732+
def _validate_with_port_frames(self):
57365733
"""Validate that after adding pec frames simulation setup is still valid."""
57375734

57385735
try:
5739-
_ = self.with_pec_frames
5736+
_ = self.with_port_frames
57405737
except Exception:
57415738
log.error(
57425739
"Simulation fails after requested mode source PEC frames are added. "
5743-
"Please inspect '.with_pec_frames'."
5740+
"Please inspect '.with_port_frames'."
57445741
)

tidy3d/components/source/field.py

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
from tidy3d.components.data.dataset import FieldDataset
1313
from tidy3d.components.data.validators import validate_can_interpolate, validate_no_nans
1414
from tidy3d.components.mode_spec import ModeSpec
15+
from tidy3d.components.source.frame import PECFrame
1516
from tidy3d.components.types import TYPE_TAG_STR, Ax, Axis, Coordinate, Direction
1617
from tidy3d.components.validators import (
1718
assert_plane,
@@ -403,10 +404,10 @@ class ModeSource(DirectionalSource, PlanarSource, BroadbandSource):
403404
"``num_modes`` in the solver will be set to ``mode_index + 1``.",
404405
)
405406

406-
pec_frame: pydantic.NonNegativeInt = pydantic.Field(
407-
0,
408-
title="PEC Frame.",
409-
description="Add a thin pec frame around the source during FDTD run.",
407+
frame: Optional[PECFrame] = pydantic.Field(
408+
None,
409+
title="Source Frame.",
410+
description="Add a thin frame around the source during FDTD run for an improved injection.",
410411
)
411412

412413
@cached_property

tidy3d/components/source/frame.py

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
"""Defines specifications for source frames."""
2+
3+
from __future__ import annotations
4+
5+
from abc import ABC
6+
7+
import pydantic.v1 as pydantic
8+
9+
from tidy3d.components.base import Tidy3dBaseModel
10+
11+
12+
class AbstractSourceFrame(Tidy3dBaseModel, ABC):
13+
"""Abstract base class for all source frames."""
14+
15+
length: int = pydantic.Field(
16+
2,
17+
title="Length",
18+
description="Length of the frame in number of cells.",
19+
gt=0,
20+
)
21+
22+
23+
class PECFrame(AbstractSourceFrame):
24+
"""PEC source frame."""

0 commit comments

Comments
 (0)