Skip to content

Commit 1f5ca74

Browse files
committed
Reverting pop axis for box and splitting the mode symmetry validator
1 parent 5a9c100 commit 1f5ca74

File tree

5 files changed

+43
-23
lines changed

5 files changed

+43
-23
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
77

88
### Added
99
- Clarified license terms to not include scripts written using the tidy3d python API.
10+
- Simulation symmetries are now enabled but currently only affect the mode solver, if the mode plane lies on the simulation center and there's a symmetry.
11+
- Validator that mode objects with symmetries are either entirely in the main quadrant, or lie on the symmetry axis.
1012

1113
### Changed
1214
- Fixed a bug in python 3.6 where polyslab vertices loaded as List of List.

tidy3d/components/geometry.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -705,7 +705,6 @@ def _arrow_length(self, ax: Ax, length_factor: float = ARROW_LENGTH_FACTOR) -> f
705705
# apply length factor to the minimum size to get arrow width
706706
return length_factor * min(ax_width, ax_height)
707707

708-
709708
class Sphere(Circular):
710709
"""Spherical geometry.
711710

tidy3d/components/simulation.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
from descartes import PolygonPatch
1212

1313
from .validators import assert_unique_names, assert_objects_in_sim_bounds
14+
from .validators import validate_mode_objects_symmetry
1415
from .geometry import Box
1516
from .types import Symmetry, Ax, Shapely, FreqBound, GridSize
1617
from .grid import Coords1D, Grid, Coords
@@ -196,6 +197,8 @@ def set_none_to_zero_layers(cls, val):
196197
_structures_in_bounds = assert_objects_in_sim_bounds("structures")
197198
_sources_in_bounds = assert_objects_in_sim_bounds("sources")
198199
_monitors_in_bounds = assert_objects_in_sim_bounds("monitors")
200+
_mode_sources_symmetries = validate_mode_objects_symmetry("sources")
201+
_mode_monitors_symmetries = validate_mode_objects_symmetry("monitors")
199202

200203
# assign names to unnamed structures, sources, and mediums
201204
# _structure_names = set_names("structures")

tidy3d/components/validators.py

Lines changed: 32 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,37 @@ def field_has_unique_names(cls, val):
6464
return field_has_unique_names
6565

6666

67+
def validate_mode_objects_symmetry(field_name: str):
68+
"""If a Mode object, this checks that the object is fully in the main quadrant in the presence
69+
of symmetry along a given axis, or else centered on the symmetry center."""
70+
71+
obj_type = "ModeSource" if field_name == "sources" else "ModeMonitor"
72+
73+
@pydantic.validator(field_name, allow_reuse=True, always=True)
74+
def check_symmetry(cls, val, values):
75+
"""check for intersection of each structure with simulation bounds."""
76+
sim_center = values.get("center")
77+
for position_index, geometric_object in enumerate(val):
78+
if geometric_object.type == obj_type:
79+
bounds_min, _ = geometric_object.bounds
80+
for dim, sym in enumerate(values.get("symmetry")):
81+
if (
82+
sym != 0
83+
and bounds_min[dim] < sim_center[dim]
84+
and geometric_object.center[dim] != sim_center[dim]
85+
):
86+
raise SetupError(
87+
f"Mode object '{geometric_object}' "
88+
f"(at `simulation.{field_name}[{position_index}]`) "
89+
"in presence of symmetries must be in the main quadrant, "
90+
"or centered on the symmetry axis."
91+
)
92+
93+
return val
94+
95+
return check_symmetry
96+
97+
6798
def assert_unique_names(field_name: str, check_mediums=False):
6899
"""makes sure all elements of a field have unique .name values"""
69100

@@ -85,9 +116,7 @@ def field_has_unique_names(cls, val, values):
85116

86117

87118
def assert_objects_in_sim_bounds(field_name: str):
88-
"""Makes sure all objects in field are at least partially inside of simulation bounds.
89-
If a Mode object, this checks that the object is fully in the main quadrant in the presence
90-
of symmetry along a given axis, or else centered on the symmetry center."""
119+
"""Makes sure all objects in field are at least partially inside of simulation bounds."""
91120

92121
@pydantic.validator(field_name, allow_reuse=True, always=True)
93122
def objects_in_sim_bounds(cls, val, values):
@@ -103,21 +132,6 @@ def objects_in_sim_bounds(cls, val, values):
103132
"is completely outside of simulation domain."
104133
)
105134

106-
if geometric_object.type in ["ModeSource", "ModeMonitor"]:
107-
bounds_min, _ = geometric_object.bounds
108-
for dim, sym in enumerate(values.get("symmetry")):
109-
if (
110-
sym != 0
111-
and bounds_min[dim] < sim_center[dim]
112-
and geometric_object.center[dim] != sim_center[dim]
113-
):
114-
raise SetupError(
115-
f"Mode object '{geometric_object}' "
116-
f"(at `simulation.{field_name}[{position_index}]`) "
117-
"in presence of symmetries must be in the main quadrant, "
118-
"or centered on the symmetry axis."
119-
)
120-
121135
return val
122136

123137
return objects_in_sim_bounds

tidy3d/plugins/mode/mode_solver.py

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -118,12 +118,12 @@ def solve(self, mode_spec: ModeSpec) -> List[ModeInfo]:
118118
plane_grid_sym = self.simulation.discretize(plane_sym)
119119

120120
# Coords and symmetry arguments to the solver (restricted to in-plane)
121-
_, solver_coords = self.plane.pop_axis(plane_grid_sym.boundaries.to_list)
121+
_, solver_coords = self.plane.pop_axis(plane_grid_sym.boundaries.to_list, axis=normal_axis)
122122
mode_symmetry = list(self.simulation.symmetry)
123123
for dim in range(3):
124124
if self.simulation.center[dim] != self.plane.center[dim]:
125125
mode_symmetry[dim] = 0
126-
_, solver_symmetry = self.plane.pop_axis(mode_symmetry)
126+
_, solver_symmetry = self.plane.pop_axis(mode_symmetry, axis=normal_axis)
127127

128128
# Get diagonal epsilon components in the plane
129129
(eps_xx, eps_yy, eps_zz) = self.get_epsilon(plane_sym)
@@ -134,7 +134,9 @@ def solve(self, mode_spec: ModeSpec) -> List[ModeInfo]:
134134
eps_zz = np.squeeze(eps_zz, axis=normal_axis)
135135

136136
# swap axes to waveguide coordinates (propagating in z)
137-
eps_wg_zz, (eps_wg_xx, eps_wg_yy) = self.plane.pop_axis((eps_xx, eps_yy, eps_zz))
137+
eps_wg_zz, (eps_wg_xx, eps_wg_yy) = self.plane.pop_axis(
138+
(eps_xx, eps_yy, eps_zz), axis=normal_axis
139+
)
138140

139141
# construct eps_cross section to feed to mode solver
140142
eps_cross = np.stack((eps_wg_xx, eps_wg_yy, eps_wg_zz))
@@ -151,7 +153,7 @@ def solve(self, mode_spec: ModeSpec) -> List[ModeInfo]:
151153
def rotate_field_coords(field):
152154
"""move the propagation axis=z to the proper order in the array"""
153155
f_x, f_y, f_z = np.moveaxis(field, source=3, destination=1 + normal_axis)
154-
f_rot = np.stack(self.plane.unpop_axis(f_z, (f_x, f_y)), axis=0)
156+
f_rot = np.stack(self.plane.unpop_axis(f_z, (f_x, f_y), axis=normal_axis), axis=0)
155157
return f_rot
156158

157159
modes = []

0 commit comments

Comments
 (0)