Skip to content

Commit 8bb90ff

Browse files
vasilyzabelinmomchil-flex
authored andcommitted
Clean implementation of the mesh refinement structures
1 parent 6b0c432 commit 8bb90ff

3 files changed

Lines changed: 107 additions & 1 deletion

File tree

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
3030
- Pretty printing enabled with `rich.print` for the material library, materials, and their variants. In notebooks, this can be accessed using `rich.print` or `display`, or by evaluating the material library, a material, or a variant in a cell.
3131
- `FieldData` and `ModeData` support exporting E fields to a Zemax Beam File (ZBF) with `.to_zbf()` (warning: experimental feature).
3232
- `FieldDataset` supports reading E fields from a Zemax Beam File (ZBF) with `.from_zbf()` (warning: experimental feature).
33+
- Unstructured grid now supports 2D/3D box-shaped refinement regions and 1D refinement lines of arbitrary direction.
3334

3435
### Changed
3536
- Performance enhancement for adjoint gradient calculations by optimizing field interpolation.

tidy3d/__init__.py

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,12 @@
3939
)
4040
from tidy3d.components.tcad.doping import ConstantDoping, GaussianDoping
4141
from tidy3d.components.tcad.generation_recombination import FossumCarrierLifetime
42-
from tidy3d.components.tcad.grid import DistanceUnstructuredGrid, UniformUnstructuredGrid
42+
from tidy3d.components.tcad.grid import (
43+
DistanceUnstructuredGrid,
44+
GridRefinementLine,
45+
GridRefinementRegion,
46+
UniformUnstructuredGrid,
47+
)
4348
from tidy3d.components.tcad.monitors.charge import (
4449
SteadyCapacitanceMonitor,
4550
SteadyEnergyBandMonitor,
@@ -600,6 +605,8 @@ def set_logging_level(level: str) -> None:
600605
"HeatFromElectricSource",
601606
"UniformUnstructuredGrid",
602607
"DistanceUnstructuredGrid",
608+
"GridRefinementRegion",
609+
"GridRefinementLine",
603610
"TemperatureData",
604611
"TemperatureMonitor",
605612
"HeatChargeSimulation",

tidy3d/components/tcad/grid.py

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,16 @@
55
from abc import ABC
66
from typing import Tuple, Union
77

8+
import numpy as np
89
import pydantic.v1 as pd
910

1011
from tidy3d.components.base import Tidy3dBaseModel, skip_if_fields_missing
1112
from tidy3d.constants import MICROMETER
1213
from tidy3d.exceptions import ValidationError
1314

15+
from ..geometry.base import Box
16+
from ..types import Coordinate, annotate_type
17+
1418

1519
class UnstructuredGrid(Tidy3dBaseModel, ABC):
1620
"""Abstract unstructured grid."""
@@ -60,6 +64,92 @@ class UniformUnstructuredGrid(UnstructuredGrid):
6064
)
6165

6266

67+
class GridRefinementRegion(Box):
68+
"""Refinement region for the unstructured mesh. The cell size is enforced to be constant inside the region.
69+
The cell size outside of the region depends on the distance from the region."""
70+
71+
dl_internal: pd.PositiveFloat = pd.Field(
72+
...,
73+
title="Internal mesh cell size",
74+
description="Mesh cell size inside the refinement region",
75+
units=MICROMETER,
76+
)
77+
78+
transition_thickness: pd.NonNegativeFloat = pd.Field(
79+
...,
80+
title="Interface Distance",
81+
description="Thickness of a transition layer outside the box where the mesh cell size changes from the"
82+
"internal size to the external one.",
83+
units=MICROMETER,
84+
)
85+
86+
87+
class GridRefinementLine(Tidy3dBaseModel, ABC):
88+
"""Refinement line for the unstructured mesh. The cell size depends on the distance from the line."""
89+
90+
r1: Coordinate = pd.Field(
91+
...,
92+
title="Start point of the line",
93+
description="Start point of the line in x, y, and z.",
94+
units=MICROMETER,
95+
)
96+
97+
r2: Coordinate = pd.Field(
98+
...,
99+
title="End point of the line",
100+
description="End point of the line in x, y, and z.",
101+
units=MICROMETER,
102+
)
103+
104+
@pd.validator("r1", always=True)
105+
def _r1_not_inf(cls, val):
106+
"""Make sure the point is not infinitiy."""
107+
if any(np.isinf(v) for v in val):
108+
raise ValidationError("Point can not contain td.inf terms.")
109+
return val
110+
111+
@pd.validator("r2", always=True)
112+
def _r2_not_inf(cls, val):
113+
"""Make sure the point is not infinitiy."""
114+
if any(np.isinf(v) for v in val):
115+
raise ValidationError("Point can not contain td.inf terms.")
116+
return val
117+
118+
dl_near: pd.PositiveFloat = pd.Field(
119+
...,
120+
title="Mesh cell size near the line",
121+
description="Mesh cell size near the line",
122+
units=MICROMETER,
123+
)
124+
125+
distance_near: pd.NonNegativeFloat = pd.Field(
126+
...,
127+
title="Near distance",
128+
description="Distance from the line within which ``dl_near`` is enforced."
129+
"Typically the same as ``dl_near`` or its multiple.",
130+
units=MICROMETER,
131+
)
132+
133+
distance_bulk: pd.NonNegativeFloat = pd.Field(
134+
...,
135+
title="Bulk distance",
136+
description="Distance from the line outside of which ``dl_bulk`` is enforced."
137+
"Typically twice of ``dl_bulk`` or its multiple. Use larger values for a smoother "
138+
"transition from ``dl_near`` to ``dl_bulk``.",
139+
units=MICROMETER,
140+
)
141+
142+
@pd.validator("distance_bulk", always=True)
143+
@skip_if_fields_missing(["distance_near"])
144+
def names_exist_bcs(cls, val, values):
145+
"""Error if distance_bulk is less than distance_near"""
146+
distance_near = values.get("distance_near")
147+
if distance_near > val:
148+
raise ValidationError("'distance_bulk' cannot be smaller than 'distance_near'.")
149+
150+
return val
151+
152+
63153
class DistanceUnstructuredGrid(UnstructuredGrid):
64154
"""Adaptive grid based on distance to material interfaces. Currently not recommended for larger
65155
simulations.
@@ -126,6 +216,14 @@ class DistanceUnstructuredGrid(UnstructuredGrid):
126216
"``dl_bulk`` is used instead.",
127217
)
128218

219+
mesh_refinements: Tuple[annotate_type(Union[GridRefinementRegion, GridRefinementLine]), ...] = (
220+
pd.Field(
221+
(),
222+
title="Mesh refinement structures",
223+
description="List of regions/lines for which the mesh refinement will be applied",
224+
)
225+
)
226+
129227
@pd.validator("distance_bulk", always=True)
130228
@skip_if_fields_missing(["distance_interface"])
131229
def names_exist_bcs(cls, val, values):

0 commit comments

Comments
 (0)