Skip to content

Commit

Permalink
Revise and improve visualization
Browse files Browse the repository at this point in the history
  • Loading branch information
weiliangjin2021 committed Jan 27, 2025
1 parent e32a9fd commit 0c4c009
Show file tree
Hide file tree
Showing 4 changed files with 148 additions and 71 deletions.
12 changes: 6 additions & 6 deletions tests/test_components/test_layerrefinement.py
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ def test_layerrefinement():

# classmethod
for axis in range(3):
layer = LayerRefinementSpec.from_unbounded_layer(axis=axis, bounds=(0, 1))
layer = LayerRefinementSpec.from_unbounded(axis=axis, bounds=(0, 1))
assert layer.center[axis] == 0.5
assert layer.size[axis] == 1
assert layer.size[(axis + 1) % 3] == td.inf
Expand All @@ -91,18 +91,18 @@ def test_layerrefinement():
layer = LayerRefinementSpec.from_bounds(axis=axis, rmin=(0, 0, 0), rmax=(1, 2, 3))

with pytest.raises(pydantic.ValidationError):
_ = LayerRefinementSpec.from_unbounded_layer(axis=axis, bounds=(0, td.inf))
_ = LayerRefinementSpec.from_unbounded(axis=axis, bounds=(0, td.inf))
with pytest.raises(pydantic.ValidationError):
_ = LayerRefinementSpec.from_unbounded_layer(axis=axis, bounds=(td.inf, 0))
_ = LayerRefinementSpec.from_unbounded(axis=axis, bounds=(td.inf, 0))
with pytest.raises(pydantic.ValidationError):
_ = LayerRefinementSpec.from_unbounded_layer(axis=axis, bounds=(-td.inf, 0))
_ = LayerRefinementSpec.from_unbounded(axis=axis, bounds=(-td.inf, 0))
with pytest.raises(pydantic.ValidationError):
_ = LayerRefinementSpec.from_unbounded_layer(axis=axis, bounds=(1, -1))
_ = LayerRefinementSpec.from_unbounded(axis=axis, bounds=(1, -1))


def test_layerrefinement_inplane_inside():
# inplane inside
layer = LayerRefinementSpec.from_unbounded_layer(axis=2, bounds=(0, 1))
layer = LayerRefinementSpec.from_unbounded(axis=2, bounds=(0, 1))
assert layer._inplane_inside([3e3, 4e4])
layer = LayerRefinementSpec(axis=1, size=(1, 0, 1))
assert layer._inplane_inside([0, 0])
Expand Down
70 changes: 55 additions & 15 deletions tidy3d/components/grid/grid_spec.py
Original file line number Diff line number Diff line change
Expand Up @@ -1010,7 +1010,7 @@ def _finite_size_along_axis(cls, val, values):
return val

@classmethod
def from_unbounded_layer(
def from_unbounded(
cls,
axis: Axis,
bounds: Tuple[float, float],
Expand Down Expand Up @@ -1047,7 +1047,7 @@ def from_unbounded_layer(
Example
-------
>>> layer = LayerRefinementSpec.from_unbounded_layer(axis=2, bounds=(0,1))
>>> layer = LayerRefinementSpec.from_unbounded(axis=2, bounds=(0,1))
"""
center = Box.unpop_axis((bounds[0] + bounds[1]) / 2, (0, 0), axis)
Expand Down Expand Up @@ -1487,9 +1487,8 @@ def override_structures_used(self) -> List[bool, bool, bool]:
override_used[dl_axis] = True
return override_used

def all_snapping_points(self, structures: List[Structure]) -> List[CoordinateOptional]:
"""Internal and external snapping points. External snapping points take higher priority.
So far, internal snapping points are generated by `layer_refinement_specs`.
def internal_snapping_points(self, structures: List[Structure]) -> List[CoordinateOptional]:
"""Internal snapping points. So far, internal snapping points are generated by `layer_refinement_specs`.
Parameters
----------
Expand All @@ -1501,25 +1500,42 @@ def all_snapping_points(self, structures: List[Structure]) -> List[CoordinateOpt
List[CoordinateOptional]
List of snapping points coordinates.
"""

# layer refinement spec only takes effect if AutoGrid is applied.
if not (self.auto_grid_used and self.layer_refinement_used):
return self.snapping_points
return []

snapping_points = []
for layer_spec in self.layer_refinement_specs:
snapping_points += layer_spec.generate_snapping_points(list(structures))
return snapping_points + list(self.snapping_points)
return snapping_points

def all_snapping_points(self, structures: List[Structure]) -> List[CoordinateOptional]:
"""Internal and external snapping points. External snapping points take higher priority.
So far, internal snapping points are generated by `layer_refinement_specs`.
Parameters
----------
structures : List[Structure]
List of physical structures.
Returns
-------
List[CoordinateOptional]
List of snapping points coordinates.
"""

return self.internal_snapping_points(structures) + list(self.snapping_points)

@property
def _external_override_structures(self) -> List[StructureType]:
def external_override_structures(self) -> List[StructureType]:
"""External supplied override structure list."""
return [s.to_static() for s in self.override_structures]

def all_override_structures(
def internal_override_structures(
self, structures: List[Structure], wavelength: pd.PositiveFloat, sim_size: Tuple[float, 3]
) -> List[StructureType]:
"""Internal and external mesh override structures. External override structures take higher priority.
So far, internal override structures all come from `layer_refinement_specs`.
"""Internal mesh override structures. So far, internal override structures all come from `layer_refinement_specs`.
Parameters
----------
Expand All @@ -1533,17 +1549,41 @@ def all_override_structures(
List[StructureType]
List of override structures.
"""

# layer refinement spec only takes effect if AutoGrid is applied.
if not (self.auto_grid_used and self.layer_refinement_used):
return self._external_override_structures
return []

override_structures = []
for layer_spec in self.layer_refinement_specs:
override_structures += layer_spec.generate_override_structures(
self._min_vacuum_dl_in_autogrid(wavelength, sim_size), list(structures)
)
# External override structures come in the end
return override_structures + self._external_override_structures
return override_structures

def all_override_structures(
self, structures: List[Structure], wavelength: pd.PositiveFloat, sim_size: Tuple[float, 3]
) -> List[StructureType]:
"""Internal and external mesh override structures. External override structures take higher priority.
So far, internal override structures all come from `layer_refinement_specs`.
Parameters
----------
structures : List[Structure]
List of structures, with the simulation structure being the first item.
wavelength : pd.PositiveFloat
Wavelength to use for minimal step size in vaccum.
Returns
-------
List[StructureType]
List of override structures.
"""

return (
self.internal_override_structures(structures, wavelength, sim_size)
+ self.external_override_structures
)

def _min_vacuum_dl_in_autogrid(self, wavelength: float, sim_size: Tuple[float, 3]) -> float:
"""Compute grid step size in vacuum for Autogrd. If AutoGrid is applied along more than 1 dimension,
Expand Down Expand Up @@ -1686,7 +1726,7 @@ def make_grid(
break
if update_dl_min:
new_dl_min = self._dl_min(
wavelength, list(structures) + self._external_override_structures, sim_size
wavelength, list(structures) + self.external_override_structures, sim_size
)
for ind, grid in enumerate(grids_1d):
if isinstance(grid, AutoGrid) and grid._undefined_dl_min:
Expand Down
5 changes: 5 additions & 0 deletions tidy3d/components/scene.py
Original file line number Diff line number Diff line change
Expand Up @@ -241,6 +241,11 @@ def background_structure(self) -> Structure:
geometry = Box(size=(inf, inf, inf))
return Structure(geometry=geometry, medium=self.medium)

@cached_property
def all_structures(self) -> List[Structure]:
"""List of all structures in the simulation including the background."""
return [self.background_structure] + list(self.structures)

@staticmethod
def intersecting_media(
test_object: Box, structures: Tuple[Structure, ...]
Expand Down
132 changes: 82 additions & 50 deletions tidy3d/components/simulation.py
Original file line number Diff line number Diff line change
Expand Up @@ -772,6 +772,8 @@ def plot_grid(
ax: Ax = None,
hlim: Tuple[float, float] = None,
vlim: Tuple[float, float] = None,
override_structures_alpha: float = 1,
snapping_points_alpha: float = 1,
**kwargs,
) -> Ax:
"""Plot the cell boundaries as lines on a plane defined by one nonzero x,y,z coordinate.
Expand All @@ -788,6 +790,10 @@ def plot_grid(
The x range if plotting on xy or xz planes, y range if plotting on yz plane.
vlim : Tuple[float, float] = None
The z range if plotting on xz or yz planes, y plane if plotting on xy plane.
override_structures_alpha : float = 1
Opacity of the override structures.
snapping_points_alpha : float = 1
Opacity of the snapping points.
ax : matplotlib.axes._subplots.Axes = None
Matplotlib axes to plot on, if not specified, one is created.
**kwargs
Expand All @@ -802,6 +808,9 @@ def plot_grid(
"""
kwargs.setdefault("linewidth", 0.2)
kwargs.setdefault("colors", "black")
kwargs.setdefault("colors_internal", "darkmagenta")
kwargs.setdefault("dashes", (10, 10))
kwargs.setdefault("override_linestyle", "--")
cell_boundaries = self.grid.boundaries
axis, _ = self.parse_xyz_kwargs(x=x, y=y, z=z)
_, (axis_x, axis_y) = self.pop_axis([0, 1, 2], axis=axis)
Expand All @@ -817,51 +826,75 @@ def plot_grid(
ax.axhline(y=b, linewidth=kwargs["linewidth"], color=kwargs["colors"])

# Plot bounding boxes of override structures
plot_params = plot_params_override_structures.include_kwargs(
linewidth=2 * kwargs["linewidth"], edgecolor=kwargs["colors"]
)
plot_params = [
plot_params_override_structures.include_kwargs(
linewidth=4 * kwargs["linewidth"],
edgecolor=kwargs["colors"],
alpha=override_structures_alpha,
),
] * 2
plot_params[0] = plot_params[0].include_kwargs(edgecolor=kwargs["colors_internal"])

if self.grid_spec.auto_grid_used:
wavelength = self.grid_spec.get_wavelength(self.sources)
override_structures = self.grid_spec.all_override_structures(
self.all_structures, wavelength, self.geometry.size
internal_override_structures = self.grid_spec.internal_override_structures(
self.scene.all_structures, wavelength, self.geometry.size
)
for structure in override_structures:
bounds = list(zip(*structure.geometry.bounds))
_, ((xmin, xmax), (ymin, ymax)) = structure.geometry.pop_axis(bounds, axis=axis)
xmin, xmax, ymin, ymax = (self._evaluate_inf(v) for v in (xmin, xmax, ymin, ymax))
rect = mpl.patches.Rectangle(
xy=(xmin, ymin),
width=(xmax - xmin),
height=(ymax - ymin),
**plot_params.to_kwargs(),
)
ax.add_patch(rect)
all_override_structures = [
internal_override_structures,
self.grid_spec.external_override_structures,
]
for structures, plot_param in zip(all_override_structures, plot_params):
for structure in structures:
bounds = list(zip(*structure.geometry.bounds))
_, ((xmin, xmax), (ymin, ymax)) = structure.geometry.pop_axis(bounds, axis=axis)
xmin, xmax, ymin, ymax = (
self._evaluate_inf(v) for v in (xmin, xmax, ymin, ymax)
)
rect = mpl.patches.Rectangle(
xy=(xmin, ymin),
width=(xmax - xmin),
height=(ymax - ymin),
linestyle=kwargs["override_linestyle"],
**plot_param.to_kwargs(),
)
ax.add_patch(rect)

# Plot snapping points
for point in self.grid_spec.all_snapping_points(self.all_structures):
_, (x_point, y_point) = Geometry.pop_axis(point, axis=axis)
if x_point is None and y_point is None:
continue
if x_point is None:
ax.axhline(
y=self._evaluate_inf(y_point),
xmin=0.1,
xmax=0.9,
linewidth=2 * kwargs["linewidth"],
color=plot_params.edgecolor,
)
continue
if y_point is None:
ax.axvline(
x=self._evaluate_inf(x_point),
ymin=0.1,
ymax=0.9,
linewidth=2 * kwargs["linewidth"],
color=plot_params.edgecolor,
internal_snapping_points = self.grid_spec.internal_snapping_points(
self.scene.all_structures
)
for points, plot_param in zip(
[internal_snapping_points, self.grid_spec.snapping_points], plot_params
):
for point in points:
_, (x_point, y_point) = Geometry.pop_axis(point, axis=axis)
if x_point is None and y_point is None:
continue
if x_point is None:
ax.axhline(
y=self._evaluate_inf(y_point),
linewidth=4 * kwargs["linewidth"],
color=plot_param.edgecolor,
alpha=snapping_points_alpha,
linestyle=kwargs["override_linestyle"],
dashes=kwargs["dashes"],
)
continue
if y_point is None:
ax.axvline(
x=self._evaluate_inf(x_point),
linewidth=4 * kwargs["linewidth"],
color=plot_param.edgecolor,
alpha=snapping_points_alpha,
linestyle=kwargs["override_linestyle"],
dashes=kwargs["dashes"],
)
continue
x_point, y_point = (self._evaluate_inf(v) for v in (x_point, y_point))
ax.scatter(
x_point, y_point, color=plot_param.edgecolor, alpha=snapping_points_alpha
)
continue
x_point, y_point = (self._evaluate_inf(v) for v in (x_point, y_point))
ax.scatter(x_point, y_point, color=plot_params.edgecolor)

ax = Scene._set_plot_bounds(
bounds=self.simulation_bounds, ax=ax, x=x, y=y, z=z, hlim=hlim, vlim=vlim
Expand Down Expand Up @@ -1089,17 +1122,6 @@ def num_pml_layers(self) -> List[Tuple[float, float]]:

return num_layers

@cached_property
def self_structure(self) -> Structure:
"""The simulation background as a ``Structure``."""
geometry = Box(size=(inf, inf, inf), center=self.center)
return Structure(geometry=geometry, medium=self.medium)

@cached_property
def all_structures(self) -> List[Structure]:
"""List of all structures in the simulation (including the ``Simulation.medium``)."""
return [self.self_structure] + list(self.structures)

def _snap_zero_dim(self, grid: Grid):
"""Snap a grid to the simulation center along any dimension along which simulation is
effectively 0D, defined as having a single pixel. This is more general than just checking
Expand Down Expand Up @@ -4510,6 +4532,16 @@ def num_time_steps(self) -> int:

return len(self.tmesh)

@cached_property
def self_structure(self) -> Structure:
"""The simulation background as a ``Structure``."""
return self.scene.background_structure

@cached_property
def all_structures(self) -> List[Structure]:
"""List of all structures in the simulation (including the ``Simulation.medium``)."""
return self.scene.all_structures

def _grid_corrections_2dmaterials(self, grid: Grid) -> Grid:
"""Correct the grid if 2d materials are present, using their volumetric equivalents."""
if not any(isinstance(structure.medium, Medium2D) for structure in self.structures):
Expand Down

0 comments on commit 0c4c009

Please sign in to comment.