From 6005cdedc7e9ea449ee9f9493495891399a4c1e6 Mon Sep 17 00:00:00 2001 From: rahul-flex Date: Tue, 13 May 2025 15:16:49 -0400 Subject: [PATCH] feat: add validation for grid spacing in AutoGrid --- CHANGELOG.md | 1 + tests/test_components/test_grid_spec.py | 38 +++++++++++++++++++++++++ tidy3d/components/simulation.py | 26 +++++++++++++++++ 3 files changed, 65 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2b7d566676..d228e44fef 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - The method `Geometry.reflected` can be used to create a reflected copy of any geometry off a plane. As for other transformations, for efficiency, `reflected` `PolySlab` directly returns an updated `PolySlab` object rather than a `Transformed` object, except when the normal of the plane of reflection has a non-zero component along the slab axis, in which case `Transformed` is still returned. - Validation check for unit error in grid spacing. - Validation that when symmetry is imposed along a given axis, the boundary conditions on each side of the axis are identical. +- Validation check for unit error in grid spacing for `AutoGrid`. ### Changed - Supplying autograd-traced values to geometric fields (`center`, `size`) of simulations, monitors, and sources now logs a warning and falls back to the static value instead of erroring. diff --git a/tests/test_components/test_grid_spec.py b/tests/test_components/test_grid_spec.py index 73096b289c..c146cccd10 100644 --- a/tests/test_components/test_grid_spec.py +++ b/tests/test_components/test_grid_spec.py @@ -541,3 +541,41 @@ def test_uniform_grid_dl_validation(dl, expect_exception): grid_spec=td.GridSpec.uniform(dl=dl), run_time=1e-12, ) + + +@pytest.mark.parametrize( + ("freq0", "expect_error"), + [ + (3e21, True), # dl<1e-6 → fail + (1e14, False), # dl>1e-7 → pass + ], +) +def test_autogrid_min_spacing(freq0, expect_error): + """ + Test that Simulation.post_init catches too‐fine spacing produced by AutoGrid + (i.e. min grid spacing < 1e-7 µm). + """ + L = 10.0 + buffer = 1.0 + source = td.PointDipole( + center=(-L / 2 + buffer, 0, 0), + source_time=td.GaussianPulse(freq0=freq0, fwidth=freq0 / 10.0), + polarization="Ez", + ) + + kwargs = dict( + size=(L, L, L), + sources=[source], + grid_spec=td.GridSpec( + grid_x=td.AutoGrid(min_steps_per_wvl=10), + grid_y=td.AutoGrid(min_steps_per_wvl=10), + grid_z=td.AutoGrid(min_steps_per_wvl=10), + ), + run_time=1e-11, + ) + + if expect_error: + with pytest.raises(SetupError): + _ = td.Simulation(**kwargs) + else: + _ = td.Simulation(**kwargs) diff --git a/tidy3d/components/simulation.py b/tidy3d/components/simulation.py index bd99b16d6c..ab496119e6 100644 --- a/tidy3d/components/simulation.py +++ b/tidy3d/components/simulation.py @@ -228,6 +228,29 @@ def boundaries_for_zero_dims(cls, val, values): return boundaries_for_zero_dims +def _validate_min_grid_spacing(grid, threshold=1e-7, msg_prefix=""): + """ + Ensure that the smallest grid spacing in any direction of the simulation grid + is not below `threshold` (µm). + """ + # grab the 1D cell‐boundary arrays + xs, ys, zs = grid.boundaries.x, grid.boundaries.y, grid.boundaries.z + dx = np.diff(xs) + dy = np.diff(ys) + dz = np.diff(zs) + # ignore zero‐length dims + min_dx = dx[dx > 0].min() if np.any(dx > 0) else np.inf + min_dy = dy[dy > 0].min() if np.any(dy > 0) else np.inf + min_dz = dz[dz > 0].min() if np.any(dz > 0) else np.inf + overall = min(min_dx, min_dy, min_dz) + if (overall < threshold) and not np.isclose(overall, threshold, rtol=1e-6): + raise SetupError( + f"{msg_prefix} grid spacing is {overall:.2e} µm. " + "Please check your units! For more info on Tidy3D units, see: " + "https://docs.flexcompute.com/projects/tidy3d/en/latest/faq/docs/faq/What-are-the-units-used-in-the-simulation.html" + ) + + class AbstractYeeGridSimulation(AbstractSimulation, ABC): """ Abstract class for a simulation involving electromagnetic fields defined on a Yee grid. @@ -3596,6 +3619,9 @@ def _post_init_validators(self) -> None: self._validate_custom_source_time() self._validate_mode_object_bends() self._warn_mode_object_pml() + _validate_min_grid_spacing( + self.grid, threshold=1e-7, msg_prefix=f"'{self.__class__.__name__}'" + ) def _warn_mode_object_pml(self) -> None: """Warn if any mode objects have large pml."""