Skip to content

Commit a021423

Browse files
Fix polyslab tests and add interior_angle property to PolySlab
1 parent 275b709 commit a021423

File tree

3 files changed

+50
-10
lines changed

3 files changed

+50
-10
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1010
### Added
1111
- `fill` and `fill_structures` argument in `td.Simulation.plot_structures()` and `td.Simulation.plot()` respectively to disable fill and plot outlines of structures only.
1212
- New subpixel averaging option `ContourPathAveraging` applied to dielectric material boundaries.
13+
- A property `interior_angle` in `PolySlab` that stores angles formed inside polygon by two adjacent edges.
1314

1415
### Fixed
1516
- Compatibility with `xarray>=2025.03`.

tests/test_components/test_sidewall.py

Lines changed: 26 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -259,6 +259,18 @@ def test_edge_events():
259259
)
260260

261261

262+
@pytest.mark.parametrize("execution_number", range(50))
263+
def test_interior_angle(execution_number):
264+
"""
265+
Validate that the sum of interior angels are (n-2) * np.pi.
266+
"""
267+
N = 10 # number of vertices
268+
vertices = convert_valid_polygon(np.random.random((N, 2)) * 10)
269+
s = setup_polyslab(vertices, dilation=0, angle=0, bounds=(0, 0.5))
270+
n_vertices = s.reference_polygon.shape[0]
271+
assert np.isclose((n_vertices - 2) * np.pi, np.sum(s.interior_angle))
272+
273+
262274
@pytest.mark.parametrize("execution_number", range(50))
263275
def test_max_erosion_polygon(execution_number):
264276
"""
@@ -275,18 +287,22 @@ def test_max_erosion_polygon(execution_number):
275287
# compute maximal allowed erosion distance
276288
max_dist = s._neighbor_vertices_crossing_detection(s.reference_polygon, -100)
277289
# verify it is indeed maximal allowed
278-
dilation = -max_dist + 1e-10
290+
dilation = -max_dist + 1e-8
279291
# avoid polygon splitting etc. case
280-
if not s._edge_events_detection(s.reference_polygon, dilation, ignore_at_dist=False):
281-
s = setup_polyslab(vertices, dilation, angle, bounds)
282-
assert np.isclose(minimal_edge_length(s.reference_polygon), 0, atol=1e-4)
292+
if s._edge_events_detection(s.reference_polygon, dilation, ignore_at_dist=False):
293+
pytest.skip("Self-intersecting polygon.")
294+
# avoid checking if interior angle too small
295+
if any(s.interior_angle < 1e-2):
296+
pytest.skip("Interior angle < 1e-2.")
297+
s = setup_polyslab(vertices, dilation, angle, bounds)
298+
assert np.isclose(minimal_edge_length(s.reference_polygon), 0, atol=1e-4)
283299

284-
# verify it is indeed maximal allowed
285-
dilation = 0.0
286-
bounds = (0, max_dist - 1e-10)
287-
angle = np.pi / 4
288-
s = setup_polyslab(vertices, dilation, angle, bounds)
289-
assert np.isclose(minimal_edge_length(s.top_polygon), 0, atol=1e-4)
300+
# verify it is indeed maximal allowed
301+
dilation = 0.0
302+
bounds = (0, max_dist - 1e-10)
303+
angle = np.pi / 4
304+
s = setup_polyslab(vertices, dilation, angle, bounds)
305+
assert np.isclose(minimal_edge_length(s.top_polygon), 0, atol=1e-4)
290306

291307

292308
@pytest.mark.parametrize("execution_number", range(50))

tidy3d/components/geometry/polyslab.py

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
from ..base import cached_property, skip_if_fields_missing
2222
from ..transformation import RotationAroundAxis
2323
from ..types import (
24+
ArrayFloat1D,
2425
ArrayFloat2D,
2526
ArrayLike,
2627
Axis,
@@ -1216,6 +1217,28 @@ def vertices_to_array(vertices_tuple: ArrayFloat2D) -> np.ndarray:
12161217
"""Converts a list of tuples (vertices) to a numpy array."""
12171218
return np.array(vertices_tuple)
12181219

1220+
@cached_property
1221+
def interior_angle(self) -> ArrayFloat1D:
1222+
"""Angle formed inside polygon by two adjacent edges."""
1223+
1224+
def normalize(v):
1225+
return v / np.linalg.norm(v, axis=0)
1226+
1227+
vs_orig = self.reference_polygon.T
1228+
vs_next = np.roll(vs_orig, axis=-1, shift=-1)
1229+
vs_previous = np.roll(vs_orig, axis=-1, shift=+1)
1230+
1231+
asp = normalize(vs_next - vs_orig)
1232+
asm = normalize(vs_previous - vs_orig)
1233+
1234+
cos_angle = asp[0] * asm[0] + asp[1] * asm[1]
1235+
sin_angle = asp[0] * asm[1] - asp[1] * asm[0]
1236+
1237+
angle = np.arccos(cos_angle)
1238+
# concave angles
1239+
angle[sin_angle < 0] = 2 * np.pi - angle[sin_angle < 0]
1240+
return angle
1241+
12191242
@staticmethod
12201243
def _shift_vertices(
12211244
vertices: np.ndarray, dist

0 commit comments

Comments
 (0)