Skip to content

Commit b2f4608

Browse files
pecframe class
1 parent 5ce8b70 commit b2f4608

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 (
@@ -616,6 +619,7 @@ def set_logging_level(level: str) -> None:
616619
"NonlinearSusceptibility",
617620
"PECBoundary",
618621
"PECConformal",
622+
"PECFrame",
619623
"PECMedium",
620624
"PMCBoundary",
621625
"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
----------
@@ -5181,7 +5178,7 @@ def dt(self) -> float:
51815178
Time step (seconds).
51825179
"""
51835180

5184-
return self.with_pec_frames._dt
5181+
return self.with_port_frames._dt
51855182

51865183
@cached_property
51875184
def _dt(self) -> float:
@@ -5675,7 +5672,7 @@ def _make_pec_frame(self, obj) -> Structure:
56755672
direction = obj.direction
56765673
if isinstance(obj, ModeSource):
56775674
axis = obj.injection_axis
5678-
length = obj.pec_frame
5675+
length = obj.frame.length
56795676
else:
56805677
axis = obj.size.index(0.0)
56815678
length = 1
@@ -5716,13 +5713,13 @@ def _make_pec_frame(self, obj) -> Structure:
57165713
return structure
57175714

57185715
@cached_property
5719-
def with_pec_frames(self) -> Simulation:
5716+
def with_port_frames(self) -> Simulation:
57205717
"""Return an instance with added pec frames around mode sources."""
57215718

57225719
pec_frames = [
57235720
self._make_pec_frame(src)
57245721
for src in self.sources
5725-
if isinstance(src, ModeSource) and src.pec_frame > 0
5722+
if isinstance(src, ModeSource) and isinstance(src.frame, PECFrame)
57265723
]
57275724

57285725
pec_frames = pec_frames + [
@@ -5739,13 +5736,13 @@ def with_pec_frames(self) -> Simulation:
57395736
grid_spec=GridSpec.from_grid(self.grid), structures=list(self.structures) + pec_frames
57405737
)
57415738

5742-
def _validate_with_pec_frames(self):
5739+
def _validate_with_port_frames(self):
57435740
"""Validate that after adding pec frames simulation setup is still valid."""
57445741

57455742
try:
5746-
_ = self.with_pec_frames
5743+
_ = self.with_port_frames
57475744
except Exception:
57485745
log.error(
57495746
"Simulation fails after requested mode source PEC frames are added. "
5750-
"Please inspect '.with_pec_frames'."
5747+
"Please inspect '.with_port_frames'."
57515748
)

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)