Skip to content

Commit 413cb05

Browse files
fix: consistency between mode source and mode frame
1 parent f8e598b commit 413cb05

File tree

3 files changed

+64
-6
lines changed

3 files changed

+64
-6
lines changed

tests/test_components/test_grid.py

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -402,3 +402,37 @@ def test_get_geo_inds_with_span():
402402

403403
inds = g._get_geo_inds(geo_full, span_inds=span_inds)
404404
assert np.array_equal(inds, expected)
405+
406+
407+
def test_discretize_inds_relax_precision():
408+
"""Test that relax_precision handles floating-point precision issues at cell boundaries."""
409+
g = make_grid()
410+
411+
# Create a box where boundaries are exactly on cell boundaries
412+
box_exact = td.Box(center=(0, 0, 0), size=(2, 2, 2))
413+
inds_exact = g.discretize_inds(box=box_exact, extend=False, relax_precision=False)
414+
415+
# Create a box where min boundary is slightly below the cell boundary (precision issue)
416+
# The grid has boundaries at x = -1, 0, 1; y = -2, -1, 0, 1, 2; z = -3, -2, -1, 0, 1, 2, 3
417+
# A box from -1 to 1 should span indices [0, 2] in x
418+
eps = 1e-14 # Small epsilon to simulate floating-point precision issues
419+
box_min_below = td.Box(center=(0, 0, 0), size=(2 + eps, 2 + eps, 2 + eps))
420+
421+
# Without relax_precision, the slightly larger box may include extra cells
422+
inds_no_relax = g.discretize_inds(box=box_min_below, extend=False, relax_precision=False)
423+
424+
# With relax_precision, the indices should match the exact case since boundaries are "close enough"
425+
inds_with_relax = g.discretize_inds(box=box_min_below, extend=False, relax_precision=True)
426+
427+
# The relaxed precision should produce the same result as the exact box
428+
assert inds_exact == inds_with_relax
429+
assert inds_no_relax != inds_with_relax
430+
431+
# Test case where box max is slightly below a cell boundary
432+
box_max_below = td.Box(center=(0, 0, 0), size=(2 - eps, 2 - eps, 2 - eps))
433+
inds_max_no_relax = g.discretize_inds(box=box_max_below, extend=False, relax_precision=False)
434+
inds_max_with_relax = g.discretize_inds(box=box_max_below, extend=False, relax_precision=True)
435+
436+
# With relaxed precision, the boundaries close to cell boundaries should be treated as equal
437+
assert inds_exact == inds_max_with_relax
438+
assert inds_max_no_relax != inds_max_with_relax

tidy3d/components/grid/grid.py

Lines changed: 29 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -605,7 +605,9 @@ def _yee_h(self, axis: Axis):
605605

606606
return Coords(**yee_coords)
607607

608-
def discretize_inds(self, box: Box, extend: bool = False) -> list[tuple[int, int]]:
608+
def discretize_inds(
609+
self, box: Box, extend: bool = False, relax_precision: bool = False
610+
) -> list[tuple[int, int]]:
609611
"""Start and stopping indexes for the cells that intersect with a :class:`Box`.
610612
611613
Parameters
@@ -616,6 +618,9 @@ def discretize_inds(self, box: Box, extend: bool = False) -> list[tuple[int, int
616618
If ``True``, ensure that the returned indexes extend sufficiently in every direction to
617619
be able to interpolate any field component at any point within the ``box``, for field
618620
components sampled on the Yee grid.
621+
relax_precision : bool = False
622+
If ``True``, relax the precision of the discretization to allow for small numerical
623+
differences between the box boundaries and the cell boundaries.
619624
620625
Returns
621626
-------
@@ -646,17 +651,36 @@ def discretize_inds(self, box: Box, extend: bool = False) -> list[tuple[int, int
646651
# handle extensions
647652
if ind_max > ind_min and extend:
648653
# Left side
649-
if box.bounds[0][axis] < self.centers.to_list[axis][ind_min]:
654+
if pts_min[axis] < self.centers.to_list[axis][ind_min]:
650655
# Box bounds on the left side are to the left of the closest grid center
651656
ind_min -= 1
652657

653658
# We always need an extra pixel on the right for the tangential components
654659
ind_max += 1
655660

656661
# store indexes
657-
inds_list.append((ind_min, ind_max))
658-
659-
return inds_list
662+
inds_list.append([ind_min, ind_max])
663+
664+
if relax_precision:
665+
for dim in range(3):
666+
# Fix some corner cases when the box boundary is very close to
667+
# cell boundaries but due to finite precision is slightly smaller or larger
668+
cell_bounds = np.array(boundaries.to_list[dim])
669+
num_cells = len(cell_bounds) - 1
670+
min_ind = inds_list[dim][0]
671+
max_ind = inds_list[dim][1]
672+
box_min = pts_min[dim]
673+
box_max = pts_max[dim]
674+
# Check if the cell boundary after current min is close to the box min,
675+
# if it is close enough then choose that to be the new minimum cell index
676+
if min_ind + 1 < num_cells and np.isclose(box_min, cell_bounds[min_ind + 1]):
677+
inds_list[dim][0] += 1
678+
# Same but for the max cell boundary. If the current cell boundary is close to the box bounds,
679+
# then it is considered equal and the stop index should be incremented
680+
if max_ind < num_cells and np.isclose(box_max, cell_bounds[max_ind]):
681+
inds_list[dim][1] += 1
682+
683+
return [(ind_min, ind_max) for ind_min, ind_max in inds_list]
660684

661685
def extended_subspace(
662686
self,

tidy3d/components/simulation.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2212,7 +2212,7 @@ def _pec_frame_box(
22122212
) -> tuple[Box, int, str]:
22132213
"""Return pec bounding box, frame axis and object's direction"""
22142214

2215-
span_inds = np.array(self.grid.discretize_inds(obj))
2215+
span_inds = np.array(self.grid.discretize_inds(obj, relax_precision=True))
22162216
coords = self.grid.boundaries.to_list
22172217
direction = obj.direction
22182218
if isinstance(obj, ModeSource):

0 commit comments

Comments
 (0)