diff --git a/tests/test_components/test_grid.py b/tests/test_components/test_grid.py index 6e20f5443..3df1165d8 100644 --- a/tests/test_components/test_grid.py +++ b/tests/test_components/test_grid.py @@ -211,7 +211,12 @@ def test_sim_nonuniform_large(): def test_sim_grid(): - sim = td.Simulation(size=(4, 4, 4), grid_spec=td.GridSpec.uniform(1.0), run_time=1e-12) + sim = td.Simulation( + size=(4, 4, 4), + grid_spec=td.GridSpec.uniform(1.0), + run_time=1e-12, + boundary_spec=td.BoundarySpec.all_sides(boundary=td.Periodic()), + ) for c in sim.grid.centers.dict(exclude={TYPE_TAG_STR}).values(): assert np.all(c == np.array([-1.5, -0.5, 0.5, 1.5])) @@ -272,7 +277,12 @@ def test_sim_pml_grid(): def test_sim_discretize_vol(): - sim = td.Simulation(size=(4, 4, 4), grid_spec=td.GridSpec.uniform(1.0), run_time=1e-12) + sim = td.Simulation( + size=(4, 4, 4), + grid_spec=td.GridSpec.uniform(1.0), + run_time=1e-12, + boundary_spec=td.BoundarySpec.all_sides(boundary=td.Periodic()), + ) vol = td.Box(size=(1.9, 1.9, 1.9)) @@ -289,7 +299,12 @@ def test_sim_discretize_vol(): def test_sim_discretize_plane(): - sim = td.Simulation(size=(4, 4, 4), grid_spec=td.GridSpec.uniform(1.0), run_time=1e-12) + sim = td.Simulation( + size=(4, 4, 4), + grid_spec=td.GridSpec.uniform(1.0), + run_time=1e-12, + boundary_spec=td.BoundarySpec.all_sides(boundary=td.Periodic()), + ) plane = td.Box(size=(6, 6, 0)) @@ -312,6 +327,7 @@ def test_grid_auto_uniform(): grid_spec=td.GridSpec.uniform(0.1), run_time=1e-12, medium=td.Medium(permittivity=4), + boundary_spec=td.BoundarySpec.all_sides(boundary=td.Periodic()), ) sim_auto = td.Simulation( @@ -319,6 +335,7 @@ def test_grid_auto_uniform(): grid_spec=td.GridSpec.auto(wavelength=2.4, min_steps_per_wvl=12), run_time=1e-12, medium=td.Medium(permittivity=4), + boundary_spec=td.BoundarySpec.all_sides(boundary=td.Periodic()), ) bounds_uniform = sim_uniform.grid.boundaries.to_list diff --git a/tests/test_components/test_simulation.py b/tests/test_components/test_simulation.py index 845a60457..220ae3e7d 100644 --- a/tests/test_components/test_simulation.py +++ b/tests/test_components/test_simulation.py @@ -91,6 +91,15 @@ def test_sim_init(): sim.epsilon(m) +# TODO: remove for 2.0 +def test_deprecation_defaults(caplog): + """Make sure deprecation warnings thrown if defaults used.""" + s = td.Simulation( + size=(1, 1, 1), run_time=1e-12, grid_spec=td.GridSpec.uniform(dl=0.1), boundary_spec=None + ) + assert_log_level(caplog, 30) + + def test_sim_bounds(): """make sure bounds are working correctly""" @@ -111,6 +120,7 @@ def place_box(center_offset): geometry=td.Box(size=(1, 1, 1), center=shifted_center), medium=td.Medium() ) ], + boundary_spec=td.BoundarySpec.all_sides(boundary=td.Periodic()), ) # create all permutations of squares being shifted 1, -1, or zero in all three directions @@ -141,11 +151,20 @@ def test_sim_size(): grid_spec = td.GridSpec(grid_x=mesh1d, grid_y=mesh1d, grid_z=mesh1d) with pytest.raises(SetupError): - s = td.Simulation(size=(1, 1, 1), grid_spec=grid_spec, run_time=1e-12) + s = td.Simulation( + size=(1, 1, 1), + grid_spec=grid_spec, + run_time=1e-12, + boundary_spec=td.BoundarySpec.all_sides(boundary=td.Periodic()), + ) s._validate_size() with pytest.raises(SetupError): - s = td.Simulation(size=(1, 1, 1), run_time=1e-7) + s = td.Simulation( + size=(1, 1, 1), + run_time=1e-7, + boundary_spec=td.BoundarySpec.all_sides(boundary=td.Periodic()), + ) s._validate_size() @@ -161,6 +180,7 @@ def _test_monitor_size(): ) ], run_time=1e-12, + boundary_spec=td.BoundarySpec.all_sides(boundary=td.Periodic()), ) s.validate_pre_upload() @@ -184,6 +204,7 @@ def test_monitor_medium_frequency_range(caplog, freq, log_level): monitors=[mnt], sources=[src], run_time=1e-12, + boundary_spec=td.BoundarySpec.all_sides(boundary=td.Periodic()), ) assert_log_level(caplog, log_level) @@ -199,7 +220,13 @@ def test_monitor_simulation_frequency_range(caplog, fwidth, log_level): polarization="Ex", ) mnt = td.FieldMonitor(size=(0, 0, 0), name="freq", freqs=[1.5]) - sim = td.Simulation(size=(1, 1, 1), monitors=[mnt], sources=[src], run_time=1e-12) + sim = td.Simulation( + size=(1, 1, 1), + monitors=[mnt], + sources=[src], + run_time=1e-12, + boundary_spec=td.BoundarySpec.all_sides(boundary=td.Periodic()), + ) assert_log_level(caplog, log_level) @@ -489,6 +516,7 @@ def test_large_grid_size(caplog, grid_size, log_level): structures=[box], sources=[src], run_time=1e-12, + boundary_spec=td.BoundarySpec.all_sides(boundary=td.Periodic()), ) assert_log_level(caplog, log_level) @@ -543,6 +571,7 @@ def test_sim_plane_wave_error(): structures=[box_transparent], sources=[src], run_time=1e-12, + boundary_spec=td.BoundarySpec.all_sides(boundary=td.Periodic()), ) # with non-transparent box, raise @@ -552,6 +581,7 @@ def test_sim_plane_wave_error(): medium=medium_bg, structures=[box_transparent, box], sources=[src], + boundary_spec=td.BoundarySpec.all_sides(boundary=td.Periodic()), ) @@ -611,6 +641,7 @@ def test_sim_monitor_homogeneous(): sources=[src], run_time=1e-12, monitors=[monitor], + boundary_spec=td.BoundarySpec.all_sides(boundary=td.Periodic()), ) # with non-transparent box, raise @@ -622,6 +653,7 @@ def test_sim_monitor_homogeneous(): sources=[src], monitors=[monitor], run_time=1e-12, + boundary_spec=td.BoundarySpec.all_sides(boundary=td.Periodic()), ) mediums = td.Simulation.intersecting_media(monitor_n2f_vol, sim1.structures) @@ -653,6 +685,7 @@ def test_sim_monitor_homogeneous(): sources=[src], monitors=[monitor_n2f_vol_exclude], run_time=1e-12, + boundary_spec=td.BoundarySpec.all_sides(boundary=td.Periodic()), ) @@ -709,6 +742,7 @@ def test_proj_monitor_distance(caplog): sources=[src], run_time=1e-12, monitors=[monitor_n2f_far], + boundary_spec=td.BoundarySpec.all_sides(boundary=td.Periodic()), ) assert_log_level(caplog, 30) @@ -719,6 +753,7 @@ def test_proj_monitor_distance(caplog): sources=[src], run_time=1e-12, monitors=[monitor_n2f], + boundary_spec=td.BoundarySpec.all_sides(boundary=td.Periodic()), ) # proj_distance large but using approximations - don't warn @@ -728,6 +763,7 @@ def test_proj_monitor_distance(caplog): sources=[src], run_time=1e-12, monitors=[monitor_n2f_approx], + boundary_spec=td.BoundarySpec.all_sides(boundary=td.Periodic()), ) @@ -763,6 +799,7 @@ def test_diffraction_medium(): sources=[src], run_time=1e-12, monitors=[monitor], + boundary_spec=td.BoundarySpec.all_sides(boundary=td.Periodic()), ) with pytest.raises(SetupError): @@ -772,6 +809,7 @@ def test_diffraction_medium(): sources=[src], monitors=[monitor], run_time=1e-12, + boundary_spec=td.BoundarySpec.all_sides(boundary=td.Periodic()), ) @@ -788,7 +826,13 @@ def test_sim_structure_extent(caplog, box_size, log_level): polarization="Ex", ) box = td.Structure(geometry=td.Box(size=box_size), medium=td.Medium(permittivity=2)) - sim = td.Simulation(size=(1, 1, 1), structures=[box], sources=[src], run_time=1e-12) + sim = td.Simulation( + size=(1, 1, 1), + structures=[box], + sources=[src], + run_time=1e-12, + boundary_spec=td.BoundarySpec.all_sides(boundary=td.Periodic()), + ) assert_log_level(caplog, log_level) @@ -802,7 +846,13 @@ def test_num_mediums(): structures.append( td.Structure(geometry=td.Box(size=(1, 1, 1)), medium=td.Medium(permittivity=i + 1)) ) - sim = td.Simulation(size=(5, 5, 5), grid_spec=grid_spec, structures=structures, run_time=1e-12) + sim = td.Simulation( + size=(5, 5, 5), + grid_spec=grid_spec, + structures=structures, + run_time=1e-12, + boundary_spec=td.BoundarySpec.all_sides(boundary=td.Periodic()), + ) with pytest.raises(SetupError): structures.append( @@ -861,6 +911,7 @@ def _test_names_default(): td.FluxMonitor(size=(0, 1, 1), center=(0, -0.5, 0), freqs=[1], name="mon2"), td.FluxMonitor(size=(1, 0, 1), center=(0, -0.5, 0), freqs=[1], name="mon3"), ], + boundary_spec=td.BoundarySpec.all_sides(boundary=td.Periodic()), ) for i, structure in enumerate(sim.structures): @@ -888,6 +939,7 @@ def test_names_unique(): name="struct1", ), ], + boundary_spec=td.BoundarySpec.all_sides(boundary=td.Periodic()), ) with pytest.raises(SetupError) as e: @@ -910,6 +962,7 @@ def test_names_unique(): name="source1", ), ], + boundary_spec=td.BoundarySpec.all_sides(boundary=td.Periodic()), ) with pytest.raises(SetupError) as e: @@ -920,6 +973,7 @@ def test_names_unique(): td.FluxMonitor(size=(1, 1, 0), center=(0, -0.5, 0), freqs=[1], name="mon1"), td.FluxMonitor(size=(0, 1, 1), center=(0, -0.5, 0), freqs=[1], name="mon1"), ], + boundary_spec=td.BoundarySpec.all_sides(boundary=td.Periodic()), ) @@ -936,6 +990,7 @@ def test_mode_object_syms(): run_time=1e-12, symmetry=(1, -1, 0), sources=[td.ModeSource(size=(2, 2, 0), direction="+", source_time=g)], + boundary_spec=td.BoundarySpec.all_sides(boundary=td.Periodic()), ) # wrong mode monitor @@ -949,6 +1004,7 @@ def test_mode_object_syms(): monitors=[ td.ModeMonitor(size=(2, 2, 0), name="mnt", freqs=[2], mode_spec=td.ModeSpec()) ], + boundary_spec=td.BoundarySpec.all_sides(boundary=td.Periodic()), ) # right mode source (centered on the symmetry) @@ -959,6 +1015,7 @@ def test_mode_object_syms(): run_time=1e-12, symmetry=(1, -1, 0), sources=[td.ModeSource(center=(1, -1, 1), size=(2, 2, 0), direction="+", source_time=g)], + boundary_spec=td.BoundarySpec.all_sides(boundary=td.Periodic()), ) # right mode monitor (entirely in the main quadrant) @@ -973,4 +1030,5 @@ def test_mode_object_syms(): center=(2, 0, 1), size=(2, 2, 0), name="mnt", freqs=[2], mode_spec=td.ModeSpec() ) ], + boundary_spec=td.BoundarySpec.all_sides(boundary=td.Periodic()), ) diff --git a/tests/test_data/test_data_arrays.py b/tests/test_data/test_data_arrays.py index 34a59795a..b36b0ee8b 100644 --- a/tests/test_data/test_data_arrays.py +++ b/tests/test_data/test_data_arrays.py @@ -18,6 +18,7 @@ from tidy3d.components.monitor import MonitorType from tidy3d.components.structure import Structure from tidy3d.components.geometry import Box +from tidy3d.components.boundary import BoundarySpec, Periodic from tidy3d import material_library from tidy3d.constants import inf @@ -101,6 +102,7 @@ sources=SOURCES, monitors=MONITORS, structures=STRUCTURES, + boundary_spec=BoundarySpec.all_sides(boundary=Periodic()), ) SIM = Simulation( @@ -111,6 +113,7 @@ sources=SOURCES, monitors=MONITORS, structures=STRUCTURES, + boundary_spec=BoundarySpec.all_sides(boundary=Periodic()), ) """ Generate the data arrays (used in other test files) """ diff --git a/tests/utils.py b/tests/utils.py index bb465ce55..2bccf7f94 100644 --- a/tests/utils.py +++ b/tests/utils.py @@ -53,6 +53,7 @@ def prepend_tmp(path): name="mode", ), ], + boundary_spec=BoundarySpec.all_sides(boundary=Periodic()), ) SIM_FULL = Simulation( diff --git a/tidy3d/components/boundary.py b/tidy3d/components/boundary.py index 3a4c7075a..5cb24e155 100644 --- a/tidy3d/components/boundary.py +++ b/tidy3d/components/boundary.py @@ -366,8 +366,8 @@ def _deprecation_2_0_missing_defaults(cls, values): log.warning( f"'Boundary.{field}' uses default value, which is 'Periodic()' " "but will change to 'PML()' in Tidy3D version 2.0. " - "We recommend you change your 'BoundarySpec' to explicitly set " - "the boundary conditions ahead of this release to avoid unexpected results." + "We recommend explicitly setting all boundary conditions " + "ahead of this release to avoid unexpected results." ) return values @@ -574,21 +574,44 @@ class BoundarySpec(Tidy3dBaseModel): """Specifies boundary conditions on each side of the domain and along each dimension.""" x: Boundary = pd.Field( - Boundary(), + None, title="Boundary condition along x.", - description="Boundary condition on the plus and minus sides along the x axis.", + description="Boundary condition on the plus and minus sides along the x axis. " + "If ``None`, periodic boundaries are applied. Default will change to PML in 2.0 " + "so explicitly setting the boundaries is recommended.", ) y: Boundary = pd.Field( - Boundary(), + None, title="Boundary condition along y.", - description="Boundary condition on the plus and minus sides along the y axis.", + description="Boundary condition on the plus and minus sides along the y axis. " + "If ``None`, periodic boundaries are applied. Default will change to PML in 2.0 " + "so explicitly setting the boundaries is recommended.", ) z: Boundary = pd.Field( - Boundary(), + None, title="Boundary condition along z.", - description="Boundary condition on the plus and minus sides along the z axis.", + description="Boundary condition on the plus and minus sides along the z axis. " + "If ``None`, periodic boundaries are applied. Default will change to PML in 2.0 " + "so explicitly setting the boundaries is recommended.", ) + # TODO: remove for 2.0 + @pd.root_validator(pre=True) + def _deprecation_2_0_missing_defaults(cls, values): + """Raise deprecation warning if a default boundary condition is used.""" + + for field in "xyz": + if values.get(field) is None: + log.warning( + f"'BoundarySpec.{field}' uses default value, which is 'Periodic()' " + "but will change to 'PML()' in Tidy3D version 2.0. " + "We recommend explicitly setting all boundary conditions " + "ahead of this release to avoid unexpected results." + ) + values[field] = Boundary.periodic() + + return values + def __getitem__(self, field_name: str) -> Boundary: """Get the :class:`Boundary` field by name (``boundary_spec[field_name]``). diff --git a/tidy3d/components/simulation.py b/tidy3d/components/simulation.py index f34866d46..645980495 100644 --- a/tidy3d/components/simulation.py +++ b/tidy3d/components/simulation.py @@ -143,9 +143,11 @@ class Simulation(Box): # pylint:disable=too-many-public-methods ) boundary_spec: BoundarySpec = pydantic.Field( - BoundarySpec(), + None, title="Boundaries", - description="Specification of boundary conditions along each dimension.", + description="Specification of boundary conditions along each dimension. If ``None``, " + "periodic boundary conditions are applied on all sides. Default will change to PML in 2.0 " + "so explicitly setting the boundaries is recommended.", ) monitors: Tuple[annotate_type(MonitorType), ...] = pydantic.Field( @@ -216,6 +218,22 @@ def _update_simulation(cls, values): updater = Updater(sim_dict=values) return updater.update_to_current() + # TODO: remove for 2.0 + @pydantic.root_validator(pre=True) + def _deprecation_2_0_missing_defaults(cls, values): + """Raise deprecation warning if a default ``boundary_spec`` is used.""" + + if values.get("boundary_spec") is None: + log.warning( + "'Simulation.boundary_spec' uses default value, which is 'Periodic()' on all " + "sides but will change to 'PML()' in Tidy3D version 2.0. " + "We recommend explicitly setting all boundary conditions " + "ahead of this release to avoid unexpected results." + ) + values["boundary_spec"] = BoundarySpec.all_sides(boundary=Periodic()) + + return values + @pydantic.validator("grid_spec", always=True) def _validate_auto_grid_wavelength(cls, val, values): """Check that wavelength can be defined if there is auto grid spec."""