Skip to content

Commit

Permalink
Bringing in upgraded mode solver
Browse files Browse the repository at this point in the history
Latest solver updates from old tidy3d
bend modes in ModeSpec
Mode solver always takes grid boundaries instead of grid_size so that it works with nonuniform coords
Changed structure close to PML warning a bit
  • Loading branch information
momchil-flex committed Jan 28, 2022
1 parent d1074d4 commit a938111
Show file tree
Hide file tree
Showing 11 changed files with 469 additions and 146 deletions.
2 changes: 1 addition & 1 deletion .pylintrc
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
[MASTER]
extension-pkg-allow-list=pydantic
ignore=material_library.py, plugins
good-names=ax, im, Lx, Ly, Lz, x0, y0, z0, x, y, z, f, t, y1, y2, x1, x2, xs, ys, zs, Ax, Nx, Ny, Nz, dl, rr, E, H, xx, yy, zz, dx, dy, Jx, Jy, Jz, Ex, Ey, Ez, Mx, My, Mz, Hx, Hy, Hz, dz, e, fp, dt, a, c, k0, Jx_k, Jy_k, My_k, Mx_k, N_theta, N_phi, L_theta, L_phi
good-names=ax, im, Lx, Ly, Lz, x0, y0, z0, x, y, z, u, v, w, f, t, y1, y2, x1, x2, xs, ys, zs, Ax, Nx, Ny, Nz, N, dl, rr, E, H, xx, yy, zz, dx, dy, Jx, Jy, Jz, Ex, Ey, Ez, Mx, My, Mz, Hx, Hy, Hz, dz, e, fp, dt, a, c, kx, ky, kz, k0, Jx_k, Jy_k, My_k, Mx_k, N_theta, N_phi, L_theta, L_phi

[BASIC]

Expand Down
5 changes: 4 additions & 1 deletion lint.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,10 @@ def main():

results = Run([path], do_exit=False)

final_score = results.linter.stats.global_note
try:
final_score = results.linter.stats.global_note
except AttributeError:
final_score = results.linter.stats["global_note"]

if final_score < threshold:

Expand Down
11 changes: 5 additions & 6 deletions test_static.sh
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,10 @@

black .
python lint.py
pytest -rA tests/test_components.py
pytest -rA tests/test_grid.py
pytest -rA tests/test_IO.py
pytest -rA tests/test_material_library.py
# pytest -rA tests/test_core.py
pytest -rA tests/test_plugins.py
pytest -ra tests/test_components.py
pytest -ra tests/test_grid.py
pytest -ra tests/test_IO.py
pytest -ra tests/test_material_library.py
pytest -ra tests/test_plugins.py

pytest --doctest-modules tidy3d/components --ignore=tidy3d/components/base.py
9 changes: 8 additions & 1 deletion tests/test_plugins.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,14 @@ def test_mode_solver():
simulation = td.Simulation(size=(2, 2, 2), grid_size=(0.1, 0.1, 0.1), structures=[waveguide])
plane = td.Box(center=(0, 0, 0), size=(0, 1, 1))
ms = ModeSolver(simulation=simulation, plane=plane, freq=td.constants.C_0 / 1.5)
modes = ms.solve(mode_spec=td.ModeSpec(num_modes=2))
mode_spec = td.ModeSpec(
num_modes=3,
target_neff=2.0,
bend_radius=3.0,
bend_axis=0,
num_pml=(10, 10),
)
modes = ms.solve(mode_spec=mode_spec)


def _test_coeffs():
Expand Down
27 changes: 26 additions & 1 deletion tidy3d/components/mode.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,10 @@

import pydantic as pd

from ..constants import MICROMETER
from .base import Tidy3dBaseModel
from .types import Symmetry
from .types import Symmetry, Axis2D
from ..log import SetupError


class ModeSpec(Tidy3dBaseModel):
Expand Down Expand Up @@ -38,3 +40,26 @@ class ModeSpec(Tidy3dBaseModel):
title="Number of PML layers",
description="Number of standard pml layers to add in the first two non-propagation axes.",
)

bend_radius: float = pd.Field(
None,
title="Bend radius",
description="A curvature radius for simulation of waveguide bends. Can be negative, in "
"which case the mode plane center has a smaller value than the curvature center along the "
"axis that is perpendicular to both the normal axis and the bend axis.",
units=MICROMETER,
)

bend_axis: Axis2D = pd.Field(
None,
title="Bend axis",
description="Index into the first two non-propagating axes defining the normal to the "
"plane in which the bend lies. This must be provided if ``bend_radius`` is not ``None``.",
)

@pd.validator("bend_axis", always=True)
def bend_axis_given(cls, val, values):
"""check that ``bend_axis`` is provided if ``bend_radius`` is not ``None``"""
if val is None and values.get("bend_radius") is not None:
raise SetupError("bend_axis must also be defined if bend_radius is defined.")
return val
8 changes: 1 addition & 7 deletions tidy3d/components/monitor.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

import pydantic

from .types import Literal, Ax, Direction, EMField, ArrayLike
from .types import Literal, Ax, EMField, ArrayLike
from .geometry import Box
from .validators import assert_plane
from .mode import ModeSpec
Expand Down Expand Up @@ -265,12 +265,6 @@ class ModeMonitor(PlanarMonitor, FreqMonitor):
... name='mode_monitor')
"""

direction: List[Direction] = pydantic.Field(
["+", "-"],
title="Direction",
description="Specifies which direction(s) of mode propagation for the monitor to measure.",
)

mode_spec: ModeSpec = pydantic.Field(
...,
title="Mode Specification",
Expand Down
18 changes: 10 additions & 8 deletions tidy3d/components/simulation.py
Original file line number Diff line number Diff line change
Expand Up @@ -265,32 +265,34 @@ def _structures_not_close_pml(cls, val, values): # pylint:disable=too-many-loca
if (not structures) or (not sources):
return val

def warn(structure):
def warn(istruct, side):
"""Warning message for a structure too close to PML."""
log.warning(
f"a structure\n\n{structure}\n\nwas detected as being less "
"than half of a central wavelength from a PML on - side"
f"Structure at structures[{istruct}] was detected as being less "
f"than half of a central wavelength from a PML on side {side}. "
"To avoid inaccurate results, please increase gap between "
"any structures and PML or fully extend structure through the pml."
)

for structure in structures:
for istruct, structure in enumerate(structures):
struct_bound_min, struct_bound_max = structure.geometry.bounds

for source in sources:
fmin_src, fmax_src = source.source_time.frequency_range
f_average = (fmin_src + fmax_src) / 2.0
lambda0 = C_0 / f_average

for sim_val, struct_val, pml in zip(sim_bound_min, struct_bound_min, val):
zipped = zip(["x", "y", "z"], sim_bound_min, struct_bound_min, val)
for axis, sim_val, struct_val, pml in zipped:
if pml.num_layers > 0 and struct_val > sim_val:
if abs(sim_val - struct_val) < lambda0 / 2:
warn(structure)
warn(istruct, axis + "-min")

for sim_val, struct_val, pml in zip(sim_bound_max, struct_bound_max, val):
zipped = zip(["x", "y", "z"], sim_bound_max, struct_bound_max, val)
for axis, sim_val, struct_val, pml in zipped:
if pml.num_layers > 0 and struct_val < sim_val:
if abs(sim_val - struct_val) < lambda0 / 2:
warn(structure)
warn(istruct, axis + "-max")

return val

Expand Down
1 change: 1 addition & 0 deletions tidy3d/components/types.py
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,7 @@ def __modify_schema__(cls, field_schema):
Bound = Tuple[Coordinate, Coordinate]
GridSize = Union[pydantic.PositiveFloat, List[pydantic.PositiveFloat]]
Axis = Literal[0, 1, 2]
Axis2D = Literal[0, 1]
Vertices = List[Coordinate2D]
Shapely = BaseGeometry

Expand Down
29 changes: 13 additions & 16 deletions tidy3d/plugins/mode/mode_solver.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,7 @@
from ...components import ModeMonitor
from ...components import ModeSource, GaussianPulse
from ...components.types import Direction
from ...components.data import ScalarFieldData, FieldData, Tidy3dData
from ...log import SetupError
from ...constants import C_0
from ...components.data import ScalarFieldData, FieldData

from .solver import compute_modes

Expand Down Expand Up @@ -56,9 +54,9 @@ class ModeInfo(Tidy3dBaseModel):
"""stores information about a (solved) mode.
Attributes
----------
field_data: xr.Dataset
field_data: FieldData
Contains information about the fields of the modal profile.
mode: Mode
mode_spec: ModeSpec
Specifications of the mode.
n_eff: float
Real part of the effective refractive index of mode.
Expand Down Expand Up @@ -100,13 +98,13 @@ def solve(self, mode_spec: ModeSpec) -> List[ModeInfo]:
Parameters
----------
mode : Mode
``Mode`` object containing specifications of mode.
mode_spec : ModeSpec
``ModeSpec`` object containing specifications of the mode solver.
Returns
-------
ModeInfo
Object containing mode profile and effective index data.
List[ModeInfo]
A list of ``ModeInfo`` objects for each mode.
"""

normal_axis = self.plane.size.index(0.0)
Expand All @@ -124,6 +122,10 @@ def solve(self, mode_spec: ModeSpec) -> List[ModeInfo]:
(eps_xx, eps_yy, eps_zz), axis=normal_axis
)

# get the in-plane grid coordinates on which eps and the mode fields live
plane_grid = self.simulation.discretize(self.plane)
_, plane_coords = self.plane.pop_axis(plane_grid.boundaries.to_list, axis=normal_axis)

# note: from this point on, in waveguide coordinates (propagating in z)

# construct eps_cross section to feed to mode solver
Expand All @@ -135,16 +137,11 @@ def solve(self, mode_spec: ModeSpec) -> List[ModeInfo]:
# if mode_spec.symmetries[1] != 0:
# eps_cross = np.stack(tuple(e[:, Ny // 2] for e in eps_cross))

# note, internally discretizing, need to make consistent.
mode_fields, n_eff_complex = compute_modes(
eps_cross=eps_cross,
coords=plane_coords,
freq=self.freq,
grid_size=self.simulation.grid_size,
pml_layers=mode_spec.num_pml,
num_modes=mode_spec.num_modes,
target_neff=mode_spec.target_neff,
symmetries=mode_spec.symmetries,
coords=None,
mode_spec=mode_spec,
)

def rotate_field_coords(e_field, h_field):
Expand Down
Loading

0 comments on commit a938111

Please sign in to comment.