From 3f087b5a20b999bc1fa79a06d741c87f1f386dae Mon Sep 17 00:00:00 2001 From: momchil Date: Mon, 4 Nov 2024 13:52:00 +0100 Subject: [PATCH] Validate that bend radius is not smaller than half the mode plane size --- tests/test_components/test_monitor.py | 18 ++++++++++++++++++ tests/test_components/test_source.py | 16 ++++++++++++++++ tests/test_plugins/test_mode_solver.py | 11 +++++++++++ tidy3d/components/simulation.py | 24 +++++++++++++++++++++++- tidy3d/components/validators.py | 19 +++++++++++++++++++ tidy3d/plugins/mode/mode_solver.py | 12 +++++++++++- 6 files changed, 98 insertions(+), 2 deletions(-) diff --git a/tests/test_components/test_monitor.py b/tests/test_components/test_monitor.py index 1daadc3ea..e77a29d46 100644 --- a/tests/test_components/test_monitor.py +++ b/tests/test_components/test_monitor.py @@ -296,6 +296,24 @@ def test_monitor_num_modes(log_capture, num_modes, log_level): assert_log_level(log_capture, log_level) +def test_mode_bend_radius(): + """Test that small bend radius fails.""" + + with pytest.raises(ValueError): + mnt = td.ModeMonitor( + size=(5, 0, 1), + freqs=np.linspace(1e14, 2e14, 100), + name="test", + mode_spec=td.ModeSpec(num_modes=1, bend_radius=1, bend_axis=1), + ) + _ = td.Simulation( + size=(2, 2, 2), + run_time=1e-12, + monitors=[mnt], + grid_spec=td.GridSpec.uniform(dl=0.1), + ) + + def test_diffraction_validators(): # ensure error if boundaries are not periodic boundary_spec = td.BoundarySpec( diff --git a/tests/test_components/test_source.py b/tests/test_components/test_source.py index 05ee39062..560559e33 100644 --- a/tests/test_components/test_source.py +++ b/tests/test_components/test_source.py @@ -46,6 +46,22 @@ def test_dir_vector(): assert DirectionalSource._dir_vector.fget(S) is None +def test_mode_bend_radius(): + """Test that small bend radius fails.""" + + with pytest.raises(ValueError): + src = td.ModeSource( + size=(1, 0, 5), + source_time=ST, + mode_spec=td.ModeSpec(num_modes=1, bend_radius=1, bend_axis=0), + ) + _ = td.Simulation( + size=(2, 2, 2), + run_time=1e-12, + sources=[src], + ) + + def test_UniformCurrentSource(): g = td.GaussianPulse(freq0=1e12, fwidth=0.1e12) diff --git a/tests/test_plugins/test_mode_solver.py b/tests/test_plugins/test_mode_solver.py index 2481116f9..1c2dca2ad 100644 --- a/tests/test_plugins/test_mode_solver.py +++ b/tests/test_plugins/test_mode_solver.py @@ -1034,3 +1034,14 @@ def test_modes_eme_sim(mock_remote_api, local): _ = msweb.run(solver.to_fdtd_mode_solver()) _ = solver.reduced_simulation_copy + + +def test_mode_bend_radius(): + """Test that small bend radius fails.""" + + with pytest.raises(ValueError): + ms = ModeSolver( + plane=PLANE, + freqs=np.linspace(1e14, 2e14, 100), + mode_spec=td.ModeSpec(num_modes=1, bend_radius=1, bend_axis=0), + ) diff --git a/tidy3d/components/simulation.py b/tidy3d/components/simulation.py index f7b7c5da8..72a58a7c4 100644 --- a/tidy3d/components/simulation.py +++ b/tidy3d/components/simulation.py @@ -85,7 +85,11 @@ from .structure import MeshOverrideStructure, Structure from .subpixel_spec import SubpixelSpec from .types import TYPE_TAG_STR, Ax, Axis, FreqBound, InterpMethod, Literal, Symmetry, annotate_type -from .validators import assert_objects_in_sim_bounds, validate_mode_objects_symmetry +from .validators import ( + assert_objects_in_sim_bounds, + validate_mode_objects_symmetry, + validate_mode_plane_radius, +) from .viz import ( PlotParams, add_ax_if_none, @@ -3245,6 +3249,24 @@ def _post_init_validators(self) -> None: self._validate_tfsf_nonuniform_grid() self._validate_nonlinear_specs() self._validate_custom_source_time() + self._validate_mode_object_bends() + + def _validate_mode_object_bends(self) -> None: + """Error if any mode sources or monitors with bends have a radius that is too small.""" + for imnt, monitor in enumerate(self.monitors): + if isinstance(monitor, AbstractModeMonitor): + validate_mode_plane_radius( + mode_spec=monitor.mode_spec, + plane=monitor.geometry, + msg_prefix=f"Monitor at 'monitors[{imnt}]' ", + ) + for isrc, source in enumerate(self.sources): + if isinstance(source, ModeSource): + validate_mode_plane_radius( + mode_spec=source.mode_spec, + plane=source.geometry, + msg_prefix=f"Source at 'sources[{isrc}]' ", + ) def _validate_custom_source_time(self): """Warn if all simulation times are outside CustomSourceTime definition range.""" diff --git a/tidy3d/components/validators.py b/tidy3d/components/validators.py index 2c8a7631b..c4c912e45 100644 --- a/tidy3d/components/validators.py +++ b/tidy3d/components/validators.py @@ -8,6 +8,7 @@ from .base import DATA_ARRAY_MAP, skip_if_fields_missing from .data.dataset import Dataset, FieldDataset from .geometry.base import Box +from .mode import ModeSpec from .types import Tuple """ Explanation of pydantic validators: @@ -396,3 +397,21 @@ def freqs_not_empty(cls, val): return val return freqs_not_empty + + +def validate_mode_plane_radius(mode_spec: ModeSpec, plane: Box, msg_prefix: str = ""): + """Validate that the radius of a mode spec with a bend is not smaller than half the size of + the plane along the radial direction.""" + + if not mode_spec.bend_radius: + return + + # radial axis is the plane axis that is not the bend axis + _, plane_axs = plane.pop_axis([0, 1, 2], plane.size.index(0.0)) + radial_ax = plane_axs[(mode_spec.bend_axis + 1) % 2] + + if np.abs(mode_spec.bend_radius) < plane.size[radial_ax] / 2: + raise ValueError( + f"{msg_prefix} bend radius is smaller than half the mode plane size " + "along the radial axis, which can produce wrong results." + ) diff --git a/tidy3d/plugins/mode/mode_solver.py b/tidy3d/plugins/mode/mode_solver.py index e1a9b06e5..72a5d5287 100644 --- a/tidy3d/plugins/mode/mode_solver.py +++ b/tidy3d/plugins/mode/mode_solver.py @@ -46,7 +46,11 @@ PlotScale, Symmetry, ) -from ...components.validators import validate_freqs_min, validate_freqs_not_empty +from ...components.validators import ( + validate_freqs_min, + validate_freqs_not_empty, + validate_mode_plane_radius, +) from ...components.viz import make_ax, plot_params_pml from ...constants import C_0 from ...exceptions import SetupError, ValidationError @@ -173,6 +177,11 @@ def plane_in_sim_bounds(cls, val, values): raise SetupError("'ModeSolver.plane' must intersect 'ModeSolver.simulation'.") return val + def _post_init_validators(self) -> None: + validate_mode_plane_radius( + mode_spec=self.mode_spec, plane=self.plane, msg_prefix="Mode solver" + ) + @cached_property def normal_axis(self) -> Axis: """Axis normal to the mode plane.""" @@ -1511,6 +1520,7 @@ def _validate_modes_size(self): ) def validate_pre_upload(self, source_required: bool = True): + """Validate the fully initialized mode solver is ok for upload to our servers.""" self._validate_modes_size() @cached_property