From e01b21b63d80000c9a055d9a329189dc220aa8a6 Mon Sep 17 00:00:00 2001 From: momchil Date: Fri, 15 Mar 2024 15:34:12 -0700 Subject: [PATCH] Restrict number of time points for non-0D field time monitors --- CHANGELOG.md | 1 + tests/test_components/test_simulation.py | 33 ++++++++++++++++++++++++ tidy3d/components/simulation.py | 23 +++++++++++++++-- 3 files changed, 55 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index daa533145..ccf2d259c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed - `run_time` of the adjoint simulation is set more robustly based on the adjoint sources and the forward simulation `run_time` as `sim_fwd.run_time + c / fwdith_adj` where `c=10`. +- `FieldTimeMonitor` restriction to record at a maximum of 5000 time steps if the monitor is not zero-dimensional, to avoid creating unnecessarily large amounts of data. ### Fixed - `tidy3d.plugins.design.Results` store the `BatchData` for batch runs in the `.batch_data` field. diff --git a/tests/test_components/test_simulation.py b/tests/test_components/test_simulation.py index 4472f2cc2..9d144ff3e 100644 --- a/tests/test_components/test_simulation.py +++ b/tests/test_components/test_simulation.py @@ -1850,6 +1850,39 @@ def test_error_large_monitors(monitor): sim_large.validate_pre_upload() +def test_error_max_time_monitor_steps(): + """Test if a time monitor with too many time steps causes pre upload error.""" + + sim = td.Simulation( + size=(5, 5, 5), + run_time=1e-12, + grid_spec=td.GridSpec.uniform(dl=0.01), + sources=[ + td.ModeSource( + size=(0.1, 0.1, 0), + direction="+", + source_time=td.GaussianPulse(freq0=2e14, fwidth=0.1e14), + ) + ], + ) + + # simulation with a 0D time monitor should not error + monitor = td.FieldTimeMonitor(center=(0, 0, 0), size=(0, 0, 0), name="time") + sim = sim.updated_copy(monitors=[monitor]) + sim.validate_pre_upload() + + # 1D monitor should error + with pytest.raises(SetupError): + monitor = monitor.updated_copy(size=(1, 0, 0)) + sim = sim.updated_copy(monitors=[monitor]) + sim.validate_pre_upload() + + # setting a large enough interval should again not error + monitor = monitor.updated_copy(interval=20) + sim = sim.updated_copy(monitors=[monitor]) + sim.validate_pre_upload() + + def test_monitor_num_cells(): """Test the computation of number of cells in monitor.""" sim = td.Simulation( diff --git a/tidy3d/components/simulation.py b/tidy3d/components/simulation.py index 302d39f03..2dd82ebd7 100644 --- a/tidy3d/components/simulation.py +++ b/tidy3d/components/simulation.py @@ -33,7 +33,7 @@ from .source import CustomCurrentSource, CustomSourceTime, ContinuousWave from .source import TFSF, Source, ModeSource from .monitor import MonitorType, Monitor, FreqMonitor, SurfaceIntegrationMonitor -from .monitor import AbstractModeMonitor, FieldMonitor, TimeMonitor +from .monitor import AbstractModeMonitor, FieldMonitor, TimeMonitor, FieldTimeMonitor from .monitor import PermittivityMonitor, DiffractionMonitor, AbstractFieldProjectionMonitor from .monitor import FieldProjectionAngleMonitor, FieldProjectionKSpaceMonitor from .lumped_element import LumpedElementType, LumpedResistor @@ -71,11 +71,14 @@ # maximum number of sources MAX_NUM_SOURCES = 1000 -# maximum numbers of simulation parameters +# restrictions on simulation number of cells and number of time steps MAX_TIME_STEPS = 1e7 WARN_TIME_STEPS = 1e6 MAX_GRID_CELLS = 20e9 MAX_CELLS_TIMES_STEPS = 1e16 + +# monitor warnings and restrictions +MAX_TIME_MONITOR_STEPS = 5000 # does not apply to 0D monitors WARN_MONITOR_DATA_SIZE_GB = 10 MAX_MONITOR_INTERNAL_DATA_SIZE_GB = 50 MAX_SIMULATION_DATA_SIZE_GB = 50 @@ -1743,6 +1746,7 @@ def validate_pre_upload(self, source_required: bool = True) -> None: self._validate_datasets_not_none() self._validate_tfsf_structure_intersections() self._warn_time_monitors_outside_run_time() + self._validate_time_monitors_num_steps() _ = self.volumetric_structures log.end_capture(self) if source_required and len(self.sources) == 0: @@ -1854,6 +1858,21 @@ def warn_mode_size(monitor: AbstractModeMonitor, msg_header: str, custom_loc: Li custom_loc = ["monitors", mnt_ind] warn_mode_size(monitor=monitor, msg_header=msg_header, custom_loc=custom_loc) + def _validate_time_monitors_num_steps(self) -> None: + """Raise an error if non-0D time monitors have too many time steps.""" + for monitor in self.monitors: + if not isinstance(monitor, FieldTimeMonitor) or len(monitor.zero_dims) == 3: + continue + num_time_steps = monitor.num_steps(self.tmesh) + if num_time_steps > MAX_TIME_MONITOR_STEPS: + raise SetupError( + f"Time monitor '{monitor.name}' records at {num_time_steps} time steps, which " + f"is larger than the maximum allowed value of {MAX_TIME_MONITOR_STEPS} when " + "the monitor is not zero-dimensional. Change the geometry to a point monitor, " + "or use 'start', 'stop', and 'interval' to reduce the number of time steps " + "at which the monitor stores data." + ) + @cached_property def monitors_data_size(self) -> Dict[str, float]: """Dictionary mapping monitor names to their estimated storage size in bytes."""