Skip to content

Commit c9c965a

Browse files
mode source pec frame
1 parent f1bc896 commit c9c965a

File tree

2 files changed

+79
-1
lines changed

2 files changed

+79
-1
lines changed

tidy3d/components/simulation.py

Lines changed: 73 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@
4444
from .data.unstructured.tetrahedral import TetrahedralGridDataset
4545
from .data.unstructured.triangular import TriangularGridDataset
4646
from .data.utils import CustomSpatialDataType
47-
from .geometry.base import Box, Geometry
47+
from .geometry.base import Box, ClipOperation, Geometry
4848
from .geometry.mesh import TriangleMesh
4949
from .geometry.utils import flatten_groups, traverse_geometries
5050
from .geometry.utils_2d import get_bounds, get_thickened_geom, snap_coordinate_to_grid, subdivide
@@ -62,6 +62,7 @@
6262
Medium2D,
6363
MediumType,
6464
MediumType3D,
65+
PECMedium,
6566
)
6667
from .monitor import (
6768
AbstractFieldProjectionMonitor,
@@ -174,6 +175,10 @@
174175
# additional (safety) time step reduction factor for fixed angle simulations
175176
FIXED_ANGLE_DT_SAFETY_FACTOR = 0.9
176177

178+
# length and thickness of optional PEC frames around mode sources (in cells)
179+
MODE_PEC_FRAME_LENGTH = 2
180+
MODE_PEC_FRAME_THICKNESS = 1e-3
181+
177182

178183
def validate_boundaries_for_zero_dims():
179184
"""Error if absorbing boundaries, bloch boundaries, unmatching pec/pmc, or symmetry is used along a zero dimension."""
@@ -5209,3 +5214,70 @@ def from_scene(cls, scene: Scene, **kwargs) -> Simulation:
52095214
)
52105215

52115216
_boundaries_for_zero_dims = validate_boundaries_for_zero_dims()
5217+
5218+
def _make_pec_frame(self, mode_source) -> Structure:
5219+
"""Make a pec frame around a mode source."""
5220+
5221+
coords = self.grid.boundaries.to_list
5222+
axis = mode_source.injection_axis
5223+
direction = mode_source.direction
5224+
5225+
span_inds = np.array(self.grid.discretize_inds(mode_source))
5226+
if direction == "+":
5227+
span_inds[axis][1] += MODE_PEC_FRAME_LENGTH - 1
5228+
else:
5229+
span_inds[axis][0] -= MODE_PEC_FRAME_LENGTH - 1
5230+
bounds_outer = [
5231+
[
5232+
(1 - MODE_PEC_FRAME_THICKNESS) * c[beg]
5233+
+ MODE_PEC_FRAME_THICKNESS * c[max(0, beg - 1)],
5234+
(1 - MODE_PEC_FRAME_THICKNESS) * c[end]
5235+
+ MODE_PEC_FRAME_THICKNESS * c[min(len(c) - 1, end + 1)],
5236+
]
5237+
for c, (beg, end) in zip(coords, span_inds)
5238+
]
5239+
bounds_inner = [
5240+
[
5241+
(1 - MODE_PEC_FRAME_THICKNESS) * c[beg] + MODE_PEC_FRAME_THICKNESS * c[beg + 1],
5242+
(1 - MODE_PEC_FRAME_THICKNESS) * c[end] + MODE_PEC_FRAME_THICKNESS * c[end - 1],
5243+
]
5244+
for c, (beg, end) in zip(coords, span_inds)
5245+
]
5246+
bounds_inner[axis] = [-inf, inf]
5247+
structure = Structure(
5248+
geometry=ClipOperation(
5249+
geometry_a=Box.from_bounds(*np.transpose(bounds_outer)),
5250+
geometry_b=Box.from_bounds(*np.transpose(bounds_inner)),
5251+
operation="difference",
5252+
),
5253+
medium=PECMedium(),
5254+
)
5255+
return structure
5256+
5257+
@cached_property
5258+
def with_mode_source_pec_frames(self) -> Simulation:
5259+
"""Return an instance with added pec frames around mode sources."""
5260+
5261+
pec_frames = [
5262+
self._make_pec_frame(src)
5263+
for src in self.sources
5264+
if isinstance(src, ModeSource) and src.pec_frame
5265+
]
5266+
5267+
if len(pec_frames) == 0:
5268+
return self
5269+
5270+
return self.updated_copy(
5271+
grid_spec=GridSpec.from_grid(self.grid), structures=list(self.structures) + pec_frames
5272+
)
5273+
5274+
def _validate_with_mode_source_pec_frames(self):
5275+
"""Validate that after adding pec frames simulation setup is still valid."""
5276+
5277+
try:
5278+
_ = self.with_mode_source_pec_frames
5279+
except Exception:
5280+
log.error(
5281+
"Simulation fails after requested mode source PEC frames are added. "
5282+
"Please inspec '.with_mode_source_pec_frames'."
5283+
)

tidy3d/components/source/field.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -437,6 +437,12 @@ class ModeSource(DirectionalSource, PlanarSource, BroadbandSource):
437437
le=99,
438438
)
439439

440+
pec_frame: bool = pydantic.Field(
441+
False,
442+
title="PEC Frame.",
443+
description="Add a thin pec frame around the source during FDTD run.",
444+
)
445+
440446
@cached_property
441447
def angle_theta(self):
442448
"""Polar angle of propagation."""

0 commit comments

Comments
 (0)