From b03fa1c02034657c4eb1ce644899523a062bf92e Mon Sep 17 00:00:00 2001 From: Casey Wojcik Date: Wed, 31 Jul 2024 11:00:53 +0100 Subject: [PATCH] Move some EME validators to pre_upload --- CHANGELOG.md | 1 + tests/test_components/test_eme.py | 82 +++++++++++++++++------------ tests/test_web/test_webapi_eme.py | 8 +++ tidy3d/components/eme/simulation.py | 25 ++++++--- tidy3d/web/api/tidy3d_stub.py | 2 + 5 files changed, 76 insertions(+), 42 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5a341df4d..6958301ea 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,6 +18,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed - Error if field projection monitors found in 2D simulations, except `FieldProjectionAngleMonitor` with `far_field_approx = True`. Support for other monitors and for exact field projection will be coming in a subsequent Tidy3D version. - Mode solver now always operates on a reduced simulation copy. +- Moved `EMESimulation` size limit validators to preupload. ### Fixed - Error when loading a previously run `Batch` or `ComponentModeler` containing custom data. diff --git a/tests/test_components/test_eme.py b/tests/test_components/test_eme.py index c6a07b723..1d2379566 100644 --- a/tests/test_components/test_eme.py +++ b/tests/test_components/test_eme.py @@ -432,20 +432,25 @@ def test_eme_simulation(log_capture): _ = sim.updated_copy(boundary_spec=td.BoundarySpec.all_sides(td.Periodic())) # test max sim size and freqs + sim_bad = sim.updated_copy(size=(1000, 1000, 1000)) with pytest.raises(SetupError): - _ = sim.updated_copy(size=(1000, 1000, 1000)) + sim_bad.validate_pre_upload() + sim_bad = sim.updated_copy(size=(1000, 500, 3), monitors=[], store_port_modes=True) with pytest.raises(SetupError): - _ = sim.updated_copy(size=(1000, 500, 3), monitors=[], store_port_modes=True) + sim_bad.validate_pre_upload() + sim_bad = sim.updated_copy(size=(1000, 500, 3), monitors=[], store_port_modes=False) with pytest.raises(SetupError): - _ = sim.updated_copy(size=(1000, 500, 3), monitors=[], store_port_modes=False) + sim_bad.validate_pre_upload() + sim_bad = sim.updated_copy(size=(500, 500, 3), monitors=[]) with AssertLogLevel(log_capture, "WARNING", "slow-down"): - _ = sim.updated_copy(size=(500, 500, 3), monitors=[]) + sim_bad.validate_pre_upload() + sim_bad = sim.updated_copy( + freqs=list(sim.freqs) + list(1e14 * np.linspace(1, 2, 1000)), + grid_spec=sim.grid_spec.updated_copy(wavelength=1), + ) with pytest.raises(SetupError): - _ = sim.updated_copy( - freqs=list(sim.freqs) + list(1e14 * np.linspace(1, 2, 1000)), - grid_spec=sim.grid_spec.updated_copy(wavelength=1), - ) + sim_bad.validate_pre_upload() large_monitor = sim.monitors[2].updated_copy(size=(td.inf, td.inf, td.inf)) _ = sim.updated_copy( size=(10, 10, 10), @@ -453,27 +458,30 @@ def test_eme_simulation(log_capture): freqs=list(1e14 * np.linspace(1, 2, 1)), grid_spec=sim.grid_spec.updated_copy(wavelength=1), ) + sim_bad = sim.updated_copy( + size=(10, 10, 10), + monitors=[large_monitor], + freqs=list(1e14 * np.linspace(1, 2, 5)), + grid_spec=sim.grid_spec.updated_copy(wavelength=1), + ) with AssertLogLevel(log_capture, "WARNING", contains_str="estimated storage"): - _ = sim.updated_copy( - size=(10, 10, 10), - monitors=[large_monitor], - freqs=list(1e14 * np.linspace(1, 2, 5)), - grid_spec=sim.grid_spec.updated_copy(wavelength=1), - ) + sim_bad.validate_pre_upload() + sim_bad = sim.updated_copy( + size=(10, 10, 10), + monitors=[large_monitor], + freqs=list(1e14 * np.linspace(1, 2, 20)), + grid_spec=sim.grid_spec.updated_copy(wavelength=1), + ) with pytest.raises(SetupError): - _ = sim.updated_copy( - size=(10, 10, 10), - monitors=[large_monitor], - freqs=list(1e14 * np.linspace(1, 2, 20)), - grid_spec=sim.grid_spec.updated_copy(wavelength=1), - ) + sim_bad.validate_pre_upload() + sim_bad = sim.updated_copy( + size=(10, 10, 10), + monitors=[large_monitor, large_monitor.updated_copy(name="lmon2")], + freqs=list(1e14 * np.linspace(1, 2, 5)), + grid_spec=sim.grid_spec.updated_copy(wavelength=1), + ) with pytest.raises(SetupError): - _ = sim.updated_copy( - size=(10, 10, 10), - monitors=[large_monitor, large_monitor.updated_copy(name="lmon2")], - freqs=list(1e14 * np.linspace(1, 2, 5)), - grid_spec=sim.grid_spec.updated_copy(wavelength=1), - ) + sim_bad.validate_pre_upload() # test monitor that does not intersect any EME cells mode_monitor = td.EMEModeSolverMonitor( @@ -532,32 +540,36 @@ def test_eme_simulation(log_capture): # test sweep size limit with pytest.raises(SetupError): _ = sim.updated_copy(sweep_spec=td.EMELengthSweep(scale_factors=[])) + sim_bad = sim.updated_copy( + sweep_spec=td.EMELengthSweep(scale_factors=list(np.linspace(1, 2, 200))) + ) with pytest.raises(SetupError): - _ = sim.updated_copy( - sweep_spec=td.EMELengthSweep(scale_factors=list(np.linspace(1, 2, 200))) - ) + sim_bad.validate_pre_upload() # can't exceed max num modes with pytest.raises(SetupError): _ = sim.updated_copy(sweep_spec=td.EMEModeSweep(num_modes=list(np.arange(150, 200)))) # don't warn in these two cases with AssertLogLevel(log_capture, None): - _ = sim.updated_copy( + sim_good = sim.updated_copy( constraint="passive", eme_grid_spec=td.EMEUniformGrid(num_cells=1, mode_spec=td.EMEModeSpec(num_modes=40)), grid_spec=sim.grid_spec.updated_copy(wavelength=1), ) - _ = sim.updated_copy( + sim_good.validate_pre_upload() + sim_good = sim.updated_copy( constraint=None, eme_grid_spec=td.EMEUniformGrid(num_cells=1, mode_spec=td.EMEModeSpec(num_modes=60)), grid_spec=sim.grid_spec.updated_copy(wavelength=1), ) + sim_good.validate_pre_upload() # warn about num modes with constraint + sim_bad = sim.updated_copy( + constraint="passive", + eme_grid_spec=td.EMEUniformGrid(num_cells=1, mode_spec=td.EMEModeSpec(num_modes=60)), + ) with AssertLogLevel(log_capture, "WARNING", contains_str="constraint"): - _ = sim.updated_copy( - constraint="passive", - eme_grid_spec=td.EMEUniformGrid(num_cells=1, mode_spec=td.EMEModeSpec(num_modes=60)), - ) + sim_bad.validate_pre_upload() _ = sim.port_modes_monitor diff --git a/tests/test_web/test_webapi_eme.py b/tests/test_web/test_webapi_eme.py index a36bbf12e..93fcf43dd 100644 --- a/tests/test_web/test_webapi_eme.py +++ b/tests/test_web/test_webapi_eme.py @@ -6,6 +6,7 @@ from botocore.exceptions import ClientError from responses import matchers from tidy3d import EMESimulation +from tidy3d.exceptions import SetupError from tidy3d.web.api.asynchronous import run_async from tidy3d.web.api.container import Batch, Job from tidy3d.web.api.webapi import ( @@ -240,6 +241,13 @@ def mock_webapi( """Mocks all webapi operation.""" +@responses.activate +def test_preupload_validation(mock_upload): + sim = make_eme_sim().updated_copy(size=(1000, 1000, 1000)) + with pytest.raises(SetupError): + upload(sim, TASK_NAME, PROJECT_NAME) + + @responses.activate def test_upload(mock_upload): sim = make_eme_sim() diff --git a/tidy3d/components/eme/simulation.py b/tidy3d/components/eme/simulation.py index 3b9236983..8a3ca8d94 100644 --- a/tidy3d/components/eme/simulation.py +++ b/tidy3d/components/eme/simulation.py @@ -558,15 +558,19 @@ def _post_init_validators(self) -> None: _ = self.grid _ = self.eme_grid _ = self.mode_solver_monitors - log.begin_capture() self._validate_too_close_to_edges() self._validate_sweep_spec() + self._validate_symmetry() self._validate_monitor_setup() self._validate_sources_and_boundary() + + def validate_pre_upload(self) -> None: + """Validate the fully initialized EME simulation is ok for upload to our servers.""" + log.begin_capture() + self._validate_sweep_spec_size() self._validate_size() self._validate_monitor_size() self._validate_modes_size() - self._validate_symmetry() self._validate_constraint() # self._warn_monitor_interval() log.end_capture(self) @@ -639,19 +643,24 @@ def _validate_symmetry(self): # "it always monitors every EME cell." # ) - def _validate_sweep_spec(self): - """Validate sweep spec.""" + def _validate_sweep_spec_size(self): + """Make sure sweep spec is not too large.""" if self.sweep_spec is None: return - # mode sweep can't exceed max num modes num_sweep = self.sweep_spec.num_sweep - if num_sweep == 0: - raise SetupError("Simulation 'sweep_spec' has 'num_sweep=0'.") if num_sweep > MAX_NUM_SWEEP: raise SetupError( f"Simulation 'sweep_spec' has 'num_sweep={num_sweep}, " f"which exceeds the maximum allowed '{MAX_NUM_SWEEP}'." ) + + def _validate_sweep_spec(self): + """Validate sweep spec.""" + if self.sweep_spec is None: + return + num_sweep = self.sweep_spec.num_sweep + if num_sweep == 0: + raise SetupError("Simulation 'sweep_spec' has 'num_sweep=0'.") if isinstance(self.sweep_spec, EMEModeSweep): if any(self.sweep_spec.num_modes > self.max_num_modes): raise SetupError( @@ -690,6 +699,8 @@ def _validate_sweep_spec(self): def _validate_monitor_setup(self): """Check monitor setup.""" for monitor in self.monitors: + if isinstance(monitor, EMEMonitor): + _ = self._monitor_eme_cell_indices(monitor=monitor) if ( hasattr(monitor, "freqs") and monitor.freqs is not None diff --git a/tidy3d/web/api/tidy3d_stub.py b/tidy3d/web/api/tidy3d_stub.py index b02424e5a..14531c1c8 100644 --- a/tidy3d/web/api/tidy3d_stub.py +++ b/tidy3d/web/api/tidy3d_stub.py @@ -132,6 +132,8 @@ def validate_pre_upload(self, source_required) -> None: """Perform some pre-checks on instances of component""" if isinstance(self.simulation, Simulation): self.simulation.validate_pre_upload(source_required) + elif isinstance(self.simulation, EMESimulation): + self.simulation.validate_pre_upload() class Tidy3dStubData(BaseModel, TaskStubData):