Skip to content
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
60 changes: 59 additions & 1 deletion tests/test_meshgenerate.py
Original file line number Diff line number Diff line change
Expand Up @@ -326,4 +326,62 @@ def test_grid_refinement():
)


# # print(max_shrink)
def test_parse_structures():
"""Test some aspects of the structure parsing."""

source = td.PointDipole(
source_time=td.GaussianPulse(freq0=1e14, fwidth=1e13),
size=(0, 0, 0),
polarization="Ex",
)

box1 = td.Structure(
geometry=td.Box(center=(0, 0, 0), size=(2, 2, 2)), medium=td.Medium(permittivity=9)
)
# covers box1 along x and y but not z, smaller permittivity
box2 = td.Structure(
geometry=td.Box(center=(0, 0, 0), size=(200, 200, 1)), medium=td.Medium(permittivity=4)
)
# covers box1 along x only, smaller permittivity
box3 = td.Structure(
geometry=td.Box(center=(0, 1.5, 0), size=(200, 4, 1)), medium=td.Medium(permittivity=4)
)
# fully covers one edge of box1
box4 = td.Structure(
geometry=td.Box(center=(0, 1.01, 0), size=(200, 0.2, 2)), medium=td.Medium(permittivity=2)
)

# Test that the box2 permittivity is used along z in the region where it fully covers box1
sim = td.Simulation(
size=(3, 3, 3),
grid_spec=td.GridSpec.auto(),
run_time=1e-13,
structures=[box1, box2],
sources=[source],
)
sizes = sim.grid.sizes.to_list[2]
assert sizes[sizes.size // 2] > 0.1

# Test that the box3 permittivity is not used along z as it doesn't fully cover box1
sim = td.Simulation(
size=(3, 3, 3),
grid_spec=td.GridSpec.auto(),
run_time=1e-13,
structures=[box1, box3],
sources=[source],
)
sizes = sim.grid.sizes.to_list[2]
assert sizes[sizes.size // 2] < 0.1

# Test that there is no grid boundary along y at the box1 right side covered by box4
boundaries = sim.grid.boundaries.to_list[1]
assert 1.0 in boundaries
sim = td.Simulation(
size=(3, 3, 3),
grid_spec=td.GridSpec.auto(),
run_time=1e-13,
structures=[box1, box4],
sources=[source],
)
boundaries = sim.grid.boundaries.to_list[1]
assert 1.0 not in boundaries
123 changes: 65 additions & 58 deletions tidy3d/components/grid/grid_spec.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
from ..types import Axis, Symmetry
from ..source import SourceType
from ..structure import Structure
from ..geometry import Box
from ...log import SetupError, log
from ...constants import C_0, MICROMETER

Expand All @@ -22,11 +23,9 @@ class GridSpec1d(Tidy3dBaseModel, ABC):

def make_coords( # pylint:disable = too-many-arguments
self,
center: float,
size: float,
axis: Axis,
structures: List[Structure],
symmetry: Symmetry,
symmetry: Tuple[Symmetry, Symmetry, Symmetry],
wavelength: pd.PositiveFloat,
num_pml_layers: Tuple[pd.NonNegativeInt, pd.NonNegativeInt],
) -> Coords1D:
Expand All @@ -35,17 +34,13 @@ def make_coords( # pylint:disable = too-many-arguments

Parameters
----------
center : float
Center of simulation domain along a given axis.
size : float
Size of simulation domain along a given axis.
axis : Axis
Axis of this direction.
structures : List[Structure]
List of structures present in simulation.
symmetry : Symmetry
Reflection symmetry across a plane bisecting the simulation domain normal
to a given axis.
List of structures present in simulation, the first one being the simulation domain.
symmetry : Tuple[Symmetry, Symmetry, Symmetry]
Reflection symmetry across a plane bisecting the simulation domain
normal to each of the three axes.
wavelength : float
Free-space wavelength.
num_pml_layers : Tuple[int, int]
Expand All @@ -59,16 +54,21 @@ def make_coords( # pylint:disable = too-many-arguments

# Determine if one should apply periodic boundary condition.
# This should only affect auto nonuniform mesh generation for now.
is_periodic = sum(num_pml_layers) == 0
is_periodic = sum(num_pml_layers) == 0 and symmetry[axis] == 0

# generate boundaries
bound_coords = self._make_coords_initial(
center, size, axis, structures, wavelength, is_periodic
axis=axis,
structures=structures,
wavelength=wavelength,
symmetry=symmetry,
is_periodic=is_periodic,
)

# incooperate symmetries
if symmetry != 0:
if symmetry[axis] != 0:
# Offset to center if symmetry present
center = structures[0].geometry.center[axis]
center_ind = np.argmin(np.abs(center - bound_coords))
bound_coords += center - bound_coords[center_ind]
bound_coords = bound_coords[bound_coords >= center]
Expand All @@ -81,9 +81,9 @@ def make_coords( # pylint:disable = too-many-arguments
@abstractmethod
def _make_coords_initial(
self,
center: float,
size: pd.NonNegativeFloat,
*args,
axis: Axis,
structures: List[Structure],
**kwargs,
) -> Coords1D:
"""Generate 1D coords to be used as grid boundaries, based on simulation parameters.
Symmetry, PML etc. are not considered in this method.
Expand All @@ -92,11 +92,9 @@ def _make_coords_initial(

Parameters
----------
center : float
Center of simulation domain along a given axis.
size : float
Sie of simulation domain along a given axis.
*args
structures : List[Structure]
List of structures present in simulation, the first one being the simulation domain.
**kwargs
Other arguments

Returns
Expand Down Expand Up @@ -152,19 +150,19 @@ class UniformGrid(GridSpec1d):

def _make_coords_initial(
self,
center: float,
size: float,
*args,
axis: Axis,
structures: List[Structure],
**kwargs,
) -> Coords1D:
"""Uniform 1D coords to be used as grid boundaries.

Parameters
----------
center : float
Center of simulation domain along a given axis.
size : float
Size of simulation domain along a given axis.
*args:
axis : Axis
Axis of this direction.
structures : List[Structure]
List of structures present in simulation, the first one being the simulation domain.
**kwargs:
Other arguments all go here.

Returns
Expand All @@ -173,6 +171,8 @@ def _make_coords_initial(
1D coords to be used as grid boundaries.
"""

center, size = structures[0].geometry.center[axis], structures[0].geometry.size[axis]

# Take a number of steps commensurate with the size; make dl a bit smaller if needed
num_cells = int(np.ceil(size / self.dl))

Expand Down Expand Up @@ -210,19 +210,19 @@ class CustomGrid(GridSpec1d):

def _make_coords_initial(
self,
center: float,
size: float,
*args,
axis: Axis,
structures: List[Structure],
**kwargs,
) -> Coords1D:
"""Customized 1D coords to be used as grid boundaries.

Parameters
----------
center : float
Center of simulation domain along a given axis.
size : float
Size of simulation domain along a given axis.
*args
axis : Axis
Axis of this direction.
structures : List[Structure]
List of structures present in simulation, the first one being the simulation domain.
*kwargs
Other arguments all go here.

Returns
Expand All @@ -231,6 +231,8 @@ def _make_coords_initial(
1D coords to be used as grid boundaries.
"""

center, size = structures[0].geometry.center[axis], structures[0].geometry.size[axis]

# get bounding coordinates
dl = np.array(self.dl)
bound_coords = np.append(0.0, np.cumsum(dl))
Expand Down Expand Up @@ -284,29 +286,27 @@ class AutoGrid(GridSpec1d):
description="The type of mesher to use to generate the grid automatically.",
)

def _make_coords_initial( # pylint:disable = arguments-differ, too-many-arguments
def _make_coords_initial( # pylint:disable=too-many-arguments,arguments-differ,too-many-locals
self,
center: float,
size: float,
axis: Axis,
structures: List[Structure],
wavelength: float,
symmetry: Symmetry,
is_periodic: bool,
) -> Coords1D:
"""Customized 1D coords to be used as grid boundaries.

Parameters
----------
center : float
Center of simulation domain along a given axis.
size : float
Size of simulation domain along a given axis.
axis : Axis
Axis of this direction.
structures : List[Structure]
List of structures present in simulation.
wavelength : float
Free-space wavelength.
symmetry : Tuple[Symmetry, Symmetry, Symmetry]
Reflection symmetry across a plane bisecting the simulation domain
normal to each of the three axes.
is_periodic : bool
Apply periodic boundary condition or not.

Expand All @@ -316,14 +316,29 @@ def _make_coords_initial( # pylint:disable = arguments-differ, too-many-argumen
1D coords to be used as grid boundaries.
"""

sim_cent = list(structures[0].geometry.center)
sim_size = list(structures[0].geometry.size)
for dim, sym in enumerate(symmetry):
if sym != 0:
sim_cent[dim] += sim_size[dim] / 4
sim_size[dim] /= 2

struct_list = [
Structure(geometry=Box(center=sim_cent, size=sim_size), medium=structures[0].medium)
]
# Remove structures that are outside the simulation domain (with symmetry applied)
for structure in structures[1:]:
if struct_list[0].geometry.intersects(structure.geometry):
struct_list.append(structure)

# parse structures
interval_coords, max_dl_list = self.mesher.parse_structures(
axis, structures, wavelength, self.min_steps_per_wvl
axis, struct_list, wavelength, self.min_steps_per_wvl
)
# Put just a single pixel if 2D-like simulation
if interval_coords.size == 1:
dl = wavelength / self.min_steps_per_wvl
return np.array([center - dl / 2, center + dl / 2])
return np.array([sim_cent[axis] - dl / 2, sim_cent[axis] + dl / 2])

# generate mesh steps
interval_coords = np.array(interval_coords).flatten()
Expand Down Expand Up @@ -444,38 +459,30 @@ def make_grid(
Entire simulation grid.
"""

center, size = structures[0].geometry.center, structures[0].geometry.size

# Set up wavelength for automatic mesh generation if needed.
wavelength = self.wavelength
if self.wavelength is None and self.auto_grid_used:
wavelength = self.wavelength_from_sources(sources)
log.info(f"Auto meshing using wavelength {wavelength:1.4f} defined from sources.")

coords_x = self.grid_x.make_coords(
center=center[0],
size=size[0],
axis=0,
structures=structures + self.override_structures,
symmetry=symmetry[0],
symmetry=symmetry,
wavelength=wavelength,
num_pml_layers=num_pml_layers[0],
)
coords_y = self.grid_y.make_coords(
center=center[1],
size=size[1],
axis=1,
structures=structures + self.override_structures,
symmetry=symmetry[1],
symmetry=symmetry,
wavelength=wavelength,
num_pml_layers=num_pml_layers[1],
)
coords_z = self.grid_z.make_coords(
center=center[2],
size=size[2],
axis=2,
structures=structures + self.override_structures,
symmetry=symmetry[2],
symmetry=symmetry,
wavelength=wavelength,
num_pml_layers=num_pml_layers[2],
)
Expand Down
Loading