Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Warning if a nonuniform custom medium is intersecting certain sources and monitors #1588

Merged
merged 1 commit into from
May 1, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

### Added
- Support for `.gz` files in `Simulation` version updater.
- Warning if a nonuniform custom medium is intersecting `PlaneWave`, `GaussianBeam`, `AstigmaticGaussianBeam`, `FieldProjectionCartesianMonitor`, `FieldProjectionAngleMonitor`, `FieldProjectionKSpaceMonitor`, and `DiffractionMonitor`.

### Changed

Expand Down
108 changes: 105 additions & 3 deletions tests/test_components/test_custom.py
Original file line number Diff line number Diff line change
Expand Up @@ -64,12 +64,16 @@ def make_custom_current_source():
return td.CustomCurrentSource(size=SIZE, source_time=ST, current_dataset=current_dataset)


def make_spatial_data(value=0, dx=0, unstructured=False, seed=None):
def make_spatial_data(value=0, dx=0, unstructured=False, seed=None, uniform=False):
"""Makes a spatial data array."""
data = np.random.random((Nx, Ny, Nz)) + value
if uniform:
data = value * np.ones((Nx, Ny, Nz))
else:
data = np.random.random((Nx, Ny, Nz)) + value
arr = td.SpatialDataArray(data, coords=dict(x=X + dx, y=Y, z=Z))
if unstructured:
return cartesian_to_unstructured(arr, seed=seed)
method = "direct" if uniform else "linear"
return cartesian_to_unstructured(arr, seed=seed, method=method)
return arr


Expand Down Expand Up @@ -634,12 +638,21 @@ def test_custom_isotropic_medium(unstructured):
mat = CustomMedium(permittivity=permittivity, conductivity=sigmatmp)
mat = CustomMedium(permittivity=permittivity, conductivity=sigmatmp, allow_gain=True)
verify_custom_medium_methods(mat, ["permittivity", "conductivity"])
assert not mat.is_spatially_uniform

# inconsistent coords
with pytest.raises(pydantic.ValidationError):
sigmatmp = make_spatial_data(value=0, dx=1, unstructured=unstructured, seed=seed)
mat = CustomMedium(permittivity=permittivity, conductivity=sigmatmp)

# uniform
permittivity = make_spatial_data(value=1, unstructured=unstructured, seed=seed, uniform=True)
mat = CustomMedium(permittivity=permittivity)
assert mat.is_spatially_uniform

mat = CustomAnisotropicMedium(xx=mat, yy=mat, zz=mat)
assert mat.is_spatially_uniform


def verify_custom_dispersive_medium_methods(mat, reduced_fields=[]):
"""Verify that the methods in custom dispersive medium is producing expected results."""
Expand Down Expand Up @@ -709,6 +722,7 @@ def test_custom_pole_residue(unstructured):
mat = CustomPoleResidue(eps_inf=eps_inf, poles=((a, c),))
verify_custom_dispersive_medium_methods(mat, ["eps_inf", "poles"])
assert mat.n_cfl > 1
assert not mat.is_spatially_uniform

# to custom non-dispersive medium
# dispersive failure
Expand Down Expand Up @@ -775,6 +789,7 @@ def test_custom_sellmeier(unstructured):
mat = CustomSellmeier(coeffs=((b1, c1), (b2, c2)))
verify_custom_dispersive_medium_methods(mat, ["coeffs"])
assert mat.n_cfl == 1
assert not mat.is_spatially_uniform

# from dispersion
n = make_spatial_data(value=2, unstructured=unstructured, seed=seed)
Expand Down Expand Up @@ -839,6 +854,7 @@ def test_custom_lorentz(unstructured):
verify_custom_dispersive_medium_methods(mat, ["eps_inf", "coeffs"])
assert mat.n_cfl > 1
assert mat.pole_residue.subpixel
assert not mat.is_spatially_uniform


@pytest.mark.parametrize("unstructured", [False, True])
Expand Down Expand Up @@ -876,6 +892,7 @@ def test_custom_drude(unstructured):
mat = CustomDrude(eps_inf=eps_inf, coeffs=((f1, delta1), (f2, delta2)))
verify_custom_dispersive_medium_methods(mat, ["eps_inf", "coeffs"])
assert mat.n_cfl > 1
assert not mat.is_spatially_uniform


@pytest.mark.parametrize("unstructured", [False, True])
Expand Down Expand Up @@ -926,6 +943,7 @@ def test_custom_debye(unstructured):
mat = CustomDebye(eps_inf=eps_inf, coeffs=((eps1, tau1), (eps2, tau2)))
verify_custom_dispersive_medium_methods(mat, ["eps_inf", "coeffs"])
assert mat.n_cfl > 1
assert not mat.is_spatially_uniform


@pytest.mark.parametrize("unstructured", [True])
Expand Down Expand Up @@ -957,6 +975,7 @@ def test_custom_anisotropic_medium(log_capture, unstructured):
# anisotropic
mat = CustomAnisotropicMedium(xx=mat_xx, yy=mat_yy, zz=mat_zz)
verify_custom_medium_methods(mat)
assert not mat.is_spatially_uniform

mat = CustomAnisotropicMedium(xx=mat_xx, yy=mat_yy, zz=mat_zz, subpixel=True)
assert_log_level(log_capture, "WARNING")
Expand Down Expand Up @@ -1075,3 +1094,86 @@ def test_io_dispersive(tmp_path, unstructured, z_custom):
sim_load = td.Simulation.from_file(filename)

assert sim_load == sim


def test_warn_planewave_intersection(log_capture):
"""Warn that if a nonuniform custom medium is intersecting PlaneWave source."""
src = td.PlaneWave(
source_time=td.GaussianPulse(freq0=3e14, fwidth=1e13),
center=(0, 0, 0),
size=(td.inf, td.inf, 0),
direction="+",
)

# uniform custom medium
permittivity = make_spatial_data(value=1, unstructured=False, seed=0, uniform=True)
mat = CustomMedium(permittivity=permittivity)
box = td.Structure(
geometry=td.Box(size=(td.inf, td.inf, 1)),
medium=mat,
)

sim = td.Simulation(
size=(1, 1, 2),
structures=[box],
grid_spec=td.GridSpec.auto(wavelength=1),
sources=[src],
run_time=1e-12,
boundary_spec=td.BoundarySpec.all_sides(boundary=td.Periodic()),
)
assert_log_level(log_capture, None)

# nonuniform custom medium
permittivity = make_spatial_data(value=1, unstructured=False, seed=0, uniform=False)
mat = CustomMedium(permittivity=permittivity)
box = td.Structure(
geometry=td.Box(size=(td.inf, td.inf, 1)),
medium=mat,
)
sim.updated_copy(structures=[box])
assert_log_level(log_capture, "WARNING")


def test_warn_diffraction_monitor_intersection(log_capture):
"""Warn that if a nonuniform custom medium is intersecting Diffraction Monitor."""
src = td.PointDipole(
source_time=td.GaussianPulse(freq0=2.5e14, fwidth=1e13),
center=(0, 0, 0.6),
polarization="Ex",
)
monitor = td.DiffractionMonitor(
center=(0, 0, 0),
size=(td.inf, td.inf, 0),
freqs=[250e12],
name="monitor_diffraction",
normal_dir="+",
)

# uniform custom medium
permittivity = make_spatial_data(value=1, unstructured=False, seed=0, uniform=True)
mat = CustomMedium(permittivity=permittivity)
box = td.Structure(
geometry=td.Box(size=(td.inf, td.inf, 1)),
medium=mat,
)

sim = td.Simulation(
size=(1, 1, 2),
structures=[box],
grid_spec=td.GridSpec.auto(wavelength=1),
monitors=[monitor],
sources=[src],
run_time=1e-12,
boundary_spec=td.BoundarySpec.all_sides(boundary=td.Periodic()),
)
assert_log_level(log_capture, None)

# nonuniform custom medium
permittivity = make_spatial_data(value=1, unstructured=False, seed=0, uniform=False)
mat = CustomMedium(permittivity=permittivity)
box = td.Structure(
geometry=td.Box(size=(td.inf, td.inf, 1)),
medium=mat,
)
sim.updated_copy(structures=[box])
assert_log_level(log_capture, "WARNING")
22 changes: 22 additions & 0 deletions tests/test_data/test_data_arrays.py
Original file line number Diff line number Diff line change
Expand Up @@ -381,3 +381,25 @@ def test_sel_inside(nx):

with pytest.raises(DataError):
_ = arr.does_cover([[0.1, 3, 2], [1, 2.5, 2]])


def test_uniform_check():
"""check if each element in the array is of equal value."""
arr = td.SpatialDataArray(
np.ones((2, 2, 2), dtype=np.complex128),
coords=dict(x=[0, 1], y=[1, 2], z=[2, 3]),
)
assert arr.is_uniform

# small variation is still considered as uniform
arr = td.SpatialDataArray(
np.ones((2, 2, 2)) + np.random.random((2, 2, 2)) * 1e-6,
coords=dict(x=[0, 1], y=[1, 2], z=[2, 3]),
)
assert arr.is_uniform

arr = td.SpatialDataArray(
np.ones((2, 2, 2)) + np.random.random((2, 2, 2)) * 1e-4,
coords=dict(x=[0, 1], y=[1, 2], z=[2, 3]),
)
assert not arr.is_uniform
31 changes: 30 additions & 1 deletion tests/test_data/test_datasets.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ def test_triangular_dataset(log_capture, tmp_path, ds_name, no_vtk=False):
cells=tri_grid_cells,
values=tri_grid_values,
)

assert not tri_grid.is_uniform
# test name redirect
assert tri_grid.name == ds_name

Expand Down Expand Up @@ -594,3 +594,32 @@ def test_cartesian_to_unstructured(nz, use_vtk, fill_value):
assert sample_outside.values.item() == values[0, 0, 0]
else:
assert sample_outside.values.item() == fill_value


def test_triangular_dataset_uniform():
import tidy3d as td

# basic create
tri_grid_points = td.PointDataArray(
[[0.0, 0.0], [1.0, 0.0], [0.0, 1.0], [1.0, 1.0]],
dims=("index", "axis"),
)

tri_grid_cells = td.CellDataArray(
[[0, 1, 2], [1, 2, 3]],
dims=("cell_index", "vertex_index"),
)

tri_grid_values = td.IndexedDataArray(
[1.0, 1.0, 1.0, 1.0],
dims=("index"),
)

tri_grid = td.TriangularGridDataset(
normal_axis=1,
normal_pos=0,
points=tri_grid_points,
cells=tri_grid_cells,
values=tri_grid_values,
)
assert tri_grid.is_uniform
6 changes: 6 additions & 0 deletions tidy3d/components/data/data_array.py
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,12 @@ def abs(self):
"""Absolute value of data array."""
return abs(self)

@property
def is_uniform(self):
"""Whether each element is of equal value in the data array"""
raw_data = self.data.ravel()
return np.allclose(raw_data, raw_data[0])

def to_hdf5(self, fname: Union[str, h5py.File], group_path: str) -> None:
"""Save an xr.DataArray to the hdf5 file or file handle with a given path to the group."""

Expand Down
5 changes: 5 additions & 0 deletions tidy3d/components/data/dataset.py
Original file line number Diff line number Diff line change
Expand Up @@ -563,6 +563,11 @@ def points_right_dims(cls, val):
)
return val

@property
def is_uniform(self):
"""Whether each element is of equal value in ``values``."""
return self.values.is_uniform

@pd.validator("points", always=True)
def points_right_indexing(cls, val):
"""Check that points are indexed corrrectly."""
Expand Down
Loading
Loading