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
3 changes: 1 addition & 2 deletions .pylintrc
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
[MASTER]
extension-pkg-allow-list=pydantic
ignore=material_library.py, plugins
good-names=ax, im, Lx, Ly, Lz, x0, y0, z0, x, y, z, u, v, w, f, t, y1, y2, x1, x2, x3, x4, y3, y4, 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, ux, uy, uz
good-names=ax, im, Lx, Ly, Lz, x0, y0, z0, x, y, z, u, v, w, f, t, y1, y2, x1, x2, x3, x4, y3, y4, xs, ys, zs, Ax, Ay, Az, 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, ux, uy, uz, nk, i, j, k, J, M, _N_th, _N_ph, _L_th, _L_ph, r, Et_array, Ep_array, Er_array, Ht_array, Hp_array, Hr_array, Et, Ep, Er, Ht, Hp, Hr, Ex_data, Ey_data, Ez_data, Hx_data, Hy_data, Hz_data, Ar, Atheta, Aphi, mu

[BASIC]

Expand Down
2 changes: 2 additions & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ PyYAML
boto3
requests
plotly==5.5.0
dash
jupyter_dash

# required to get xarray to not complain
dask
5 changes: 2 additions & 3 deletions tidy3d/material_library.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
""" Holds dispersive models for several commonly used optical materials."""
"""Holds dispersive models for several commonly used optical materials."""
import json

from .components.medium import PoleResidue


def export_matlib_to_file(fname: str = "matlib.json") -> None:
"""Write the material library to a .json file."""

import json

mat_lib_dict = {}
for mat_name, mat in material_library.items():
mat_lib_dict[mat_name] = {}
Expand Down
2 changes: 1 addition & 1 deletion tidy3d/plugins/__init__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# import the specific classes / functions needed for the plugins
"""External plugins that have tidy3d as dependency and add functionality."""

from .dispersion.fit import DispersionFitter
from .dispersion.fit_web import StableDispersionFitter, AdvancedFitterParam
Expand Down
4 changes: 2 additions & 2 deletions tidy3d/plugins/dispersion/fit_web.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ class AdvancedFitterParam(BaseModel):
description="Stability constraint: 'hard' constraints are generally recommended since "
"they are faster to compute per iteration, and they often require fewer iterations to "
"converge since the search space is smaller. But sometimes the search space is "
"so restrictive that all good solutions are missed, then please try the 'soft' constraints "
"so restrictive that all good solutions are missed, then please try the 'soft' constraints "
"for larger search space. However, both constraints improve stability equally well.",
)
nlopt_maxeval: PositiveInt = Field(
Expand Down Expand Up @@ -175,7 +175,7 @@ def _setup_server(url_server: str):

return headers

def fit( # pylint:disable=arguments-differ
def fit( # pylint:disable=arguments-differ, too-many-locals
self,
num_poles: PositiveInt = 1,
num_tries: PositiveInt = 50,
Expand Down
160 changes: 82 additions & 78 deletions tidy3d/plugins/mode/derivatives.py
Original file line number Diff line number Diff line change
@@ -1,162 +1,166 @@
""" Finite-difference derivatives and PML absorption operators expressed as sparse matrices. """

import numpy as np
import scipy.sparse as sp

from ...constants import EPSILON_0, ETA_0


def make_Dxf(dLs, shape):
def make_dxf(dls, shape):
"""Forward derivative in x."""
Nx, Ny = shape
if Nx == 1:
return sp.csr_matrix((Ny, Ny))
Dxf = sp.diags([-1, 1], [0, 1], shape=(Nx, Nx))
Dxf = sp.diags(1 / dLs).dot(Dxf)
Dxf = sp.kron(Dxf, sp.eye(Ny))
return Dxf
dxf = sp.diags([-1, 1], [0, 1], shape=(Nx, Nx))
dxf = sp.diags(1 / dls).dot(dxf)
dxf = sp.kron(dxf, sp.eye(Ny))
return dxf


def make_Dxb(dLs, shape, pmc):
def make_dxb(dls, shape, pmc):
"""Backward derivative in x."""
Nx, Ny = shape
if Nx == 1:
return sp.csr_matrix((Ny, Ny))
Dxb = sp.diags([1, -1], [0, -1], shape=(Nx, Nx))
if pmc == True:
Dxb = sp.csr_matrix(Dxb)
Dxb[0, 0] = 2.0
Dxb = sp.diags(1 / dLs).dot(Dxb)
Dxb = sp.kron(Dxb, sp.eye(Ny))
return Dxb
dxb = sp.diags([1, -1], [0, -1], shape=(Nx, Nx))
if pmc:
dxb = sp.csr_matrix(dxb)
dxb[0, 0] = 2.0
dxb = sp.diags(1 / dls).dot(dxb)
dxb = sp.kron(dxb, sp.eye(Ny))
return dxb


def make_Dyf(dLs, shape):
def make_dyf(dls, shape):
"""Forward derivative in y."""
Nx, Ny = shape
if Ny == 1:
return sp.csr_matrix((Nx, Nx))
Dyf = sp.diags([-1, 1], [0, 1], shape=(Ny, Ny))
Dyf = sp.diags(1 / dLs).dot(Dyf)
Dyf = sp.kron(sp.eye(Nx), Dyf)
return Dyf
dyf = sp.diags([-1, 1], [0, 1], shape=(Ny, Ny))
dyf = sp.diags(1 / dls).dot(dyf)
dyf = sp.kron(sp.eye(Nx), dyf)
return dyf


def make_Dyb(dLs, shape, pmc):
def make_dyb(dls, shape, pmc):
"""Backward derivative in y."""
Nx, Ny = shape
if Ny == 1:
return sp.csr_matrix((Nx, Nx))
Dyb = sp.diags([1, -1], [0, -1], shape=(Ny, Ny))
if pmc == True:
Dyb = sp.csr_matrix(Dyb)
Dyb[0, 0] = 2.0
Dyb = sp.diags(1 / dLs).dot(Dyb)
Dyb = sp.kron(sp.eye(Nx), Dyb)
return Dyb
dyb = sp.diags([1, -1], [0, -1], shape=(Ny, Ny))
if pmc:
dyb = sp.csr_matrix(dyb)
dyb[0, 0] = 2.0
dyb = sp.diags(1 / dls).dot(dyb)
dyb = sp.kron(sp.eye(Nx), dyb)
return dyb


def create_D_matrices(shape, dLf, dLb, dmin_pmc=(False, False)):
def create_d_matrices(shape, dlf, dlb, dmin_pmc=(False, False)):
"""Make the derivative matrices without PML. If dmin_pmc is True, the
'backward' derivative in that dimension will be set to implement PMC
symmetry."""

Dxf = make_Dxf(dLf[0], shape)
Dxb = make_Dxb(dLb[0], shape, dmin_pmc[0])
Dyf = make_Dyf(dLf[1], shape)
Dyb = make_Dyb(dLb[1], shape, dmin_pmc[1])
dxf = make_dxf(dlf[0], shape)
dxb = make_dxb(dlb[0], shape, dmin_pmc[0])
dyf = make_dyf(dlf[1], shape)
dyb = make_dyb(dlb[1], shape, dmin_pmc[1])

return (Dxf, Dxb, Dyf, Dyb)
return (dxf, dxb, dyf, dyb)


def create_S_matrices(omega, shape, npml, dLf, dLb, dmin_pml=(True, True)):
# pylint:disable=too-many-locals, too-many-arguments
def create_s_matrices(omega, shape, npml, dlf, dlb, dmin_pml=(True, True)):
"""Makes the 'S-matrices'. When dotted with derivative matrices, they add
PML. If dmin_pml is set to False, PML will not be applied on the "bottom"
side of the domain."""

# strip out some information needed
Nx, Ny = shape
N = Nx * Ny
Nx_pml, Ny_pml = npml
nx_pml, ny_pml = npml

# Create the sfactor in each direction and for 'f' and 'b'
s_vector_x_f = create_sfactor("f", omega, dLf[0], Nx, Nx_pml, dmin_pml[0])
s_vector_x_b = create_sfactor("b", omega, dLb[0], Nx, Nx_pml, dmin_pml[0])
s_vector_y_f = create_sfactor("f", omega, dLf[1], Ny, Ny_pml, dmin_pml[1])
s_vector_y_b = create_sfactor("b", omega, dLb[1], Ny, Ny_pml, dmin_pml[1])
s_vector_x_f = create_sfactor("f", omega, dlf[0], Nx, nx_pml, dmin_pml[0])
s_vector_x_b = create_sfactor("b", omega, dlb[0], Nx, nx_pml, dmin_pml[0])
s_vector_y_f = create_sfactor("f", omega, dlf[1], Ny, ny_pml, dmin_pml[1])
s_vector_y_b = create_sfactor("b", omega, dlb[1], Ny, ny_pml, dmin_pml[1])

# Fill the 2D space with layers of appropriate s-factors
Sx_f_2D = np.zeros(shape, dtype=np.complex128)
Sx_b_2D = np.zeros(shape, dtype=np.complex128)
Sy_f_2D = np.zeros(shape, dtype=np.complex128)
Sy_b_2D = np.zeros(shape, dtype=np.complex128)
# Fill the 2d space with layers of appropriate s-factors
sx_f_2d = np.zeros(shape, dtype=np.complex128)
sx_b_2d = np.zeros(shape, dtype=np.complex128)
sy_f_2d = np.zeros(shape, dtype=np.complex128)
sy_b_2d = np.zeros(shape, dtype=np.complex128)

# Insert the cross sections into the S-grids (could be done more elegantly)
for i in range(0, Ny):
Sx_f_2D[:, i] = 1 / s_vector_x_f
Sx_b_2D[:, i] = 1 / s_vector_x_b
sx_f_2d[:, i] = 1 / s_vector_x_f
sx_b_2d[:, i] = 1 / s_vector_x_b
for i in range(0, Nx):
Sy_f_2D[i, :] = 1 / s_vector_y_f
Sy_b_2D[i, :] = 1 / s_vector_y_b
sy_f_2d[i, :] = 1 / s_vector_y_f
sy_b_2d[i, :] = 1 / s_vector_y_b

# Reshape the 2D s-factors into a 1D s-vecay
Sx_f_vec = Sx_f_2D.flatten()
Sx_b_vec = Sx_b_2D.flatten()
Sy_f_vec = Sy_f_2D.flatten()
Sy_b_vec = Sy_b_2D.flatten()
# Reshape the 2d s-factors into a 1D s-vecay
sx_f_vec = sx_f_2d.flatten()
sx_b_vec = sx_b_2d.flatten()
sy_f_vec = sy_f_2d.flatten()
sy_b_vec = sy_b_2d.flatten()

# Construct the 1D total s-vector into a diagonal matrix
Sx_f = sp.spdiags(Sx_f_vec, 0, N, N)
Sx_b = sp.spdiags(Sx_b_vec, 0, N, N)
Sy_f = sp.spdiags(Sy_f_vec, 0, N, N)
Sy_b = sp.spdiags(Sy_b_vec, 0, N, N)
sx_f = sp.spdiags(sx_f_vec, 0, N, N)
sx_b = sp.spdiags(sx_b_vec, 0, N, N)
sy_f = sp.spdiags(sy_f_vec, 0, N, N)
sy_b = sp.spdiags(sy_b_vec, 0, N, N)

return Sx_f, Sx_b, Sy_f, Sy_b
return sx_f, sx_b, sy_f, sy_b


def create_sfactor(direction, omega, dLs, N, N_pml, dmin_pml):
# pylint:disable=too-many-arguments
def create_sfactor(direction, omega, dls, N, n_pml, dmin_pml):
"""Creates the S-factor cross section needed in the S-matrices"""

# For no PNL, this should just be identity matrix.
if N_pml == 0:
if n_pml == 0:
return np.ones(N, dtype=np.complex128)

# Otherwise, get different profiles for forward and reverse derivatives.
if direction == "f":
return create_sfactor_f(omega, dLs, N, N_pml, dmin_pml)
elif direction == "b":
return create_sfactor_b(omega, dLs, N, N_pml, dmin_pml)
else:
raise ValueError("Direction value {} not recognized".format(direction))
return create_sfactor_f(omega, dls, N, n_pml, dmin_pml)
if direction == "b":
return create_sfactor_b(omega, dls, N, n_pml, dmin_pml)

raise ValueError(f"Direction value {direction} not recognized")


def create_sfactor_f(omega, dLs, N, N_pml, dmin_pml):
def create_sfactor_f(omega, dls, N, n_pml, dmin_pml):
"""S-factor profile for forward derivative matrix"""
sfactor_array = np.ones(N, dtype=np.complex128)
for i in range(N):
if i <= N_pml and dmin_pml:
sfactor_array[i] = s_value(dLs[0], (N_pml - i + 0.5) / N_pml, omega)
elif i > N - N_pml:
sfactor_array[i] = s_value(dLs[-1], (i - (N - N_pml) - 0.5) / N_pml, omega)
if i <= n_pml and dmin_pml:
sfactor_array[i] = s_value(dls[0], (n_pml - i + 0.5) / n_pml, omega)
elif i > N - n_pml:
sfactor_array[i] = s_value(dls[-1], (i - (N - n_pml) - 0.5) / n_pml, omega)
return sfactor_array


def create_sfactor_b(omega, dLs, N, N_pml, dmin_pml):
def create_sfactor_b(omega, dls, N, n_pml, dmin_pml):
"""S-factor profile for backward derivative matrix"""
sfactor_array = np.ones(N, dtype=np.complex128)
for i in range(N):
if i <= N_pml and dmin_pml:
sfactor_array[i] = s_value(dLs[0], (N_pml - i + 1) / N_pml, omega)
elif i > N - N_pml:
sfactor_array[i] = s_value(dLs[-1], (i - (N - N_pml) - 1) / N_pml, omega)
if i <= n_pml and dmin_pml:
sfactor_array[i] = s_value(dls[0], (n_pml - i + 1) / n_pml, omega)
elif i > N - n_pml:
sfactor_array[i] = s_value(dls[-1], (i - (N - n_pml) - 1) / n_pml, omega)
return sfactor_array


def sig_w(dL, step, sorder=3):
def sig_w(dl, step, sorder=3):
"""Fictional conductivity, note that these values might need tuning"""
sig_max = 0.8 * (sorder + 1) / (ETA_0 * dL)
sig_max = 0.8 * (sorder + 1) / (ETA_0 * dl)
return sig_max * step**sorder


def s_value(dL, step, omega):
def s_value(dl, step, omega):
"""S-value to use in the S-matrices"""
# print(step)
return 1 - 1j * sig_w(dL, step) / (omega * EPSILON_0)
return 1 - 1j * sig_w(dl, step) / (omega * EPSILON_0)
2 changes: 2 additions & 0 deletions tidy3d/plugins/mode/mode_solver.py
Original file line number Diff line number Diff line change
Expand Up @@ -280,6 +280,7 @@ def plane_sym(self):
"""Potentially smaller plane if symmetries present in the simulation."""
return self.simulation.min_sym_box(self.plane)

# pylint:disable=too-many-locals
def solve(self) -> ModeSolverData:
"""Finds the modal profile and effective index of the modes.

Expand Down Expand Up @@ -398,6 +399,7 @@ def rotate_field_coords(self, field):
f_rot = np.stack(self.plane.unpop_axis(f_z, (f_x, f_y), axis=self.normal_axis), axis=0)
return f_rot

# pylint:disable=too-many-locals
def process_fields(
self, mode_fields: Array[complex], freq_index: int, mode_index: int
) -> Tuple[FIELD, FIELD]:
Expand Down
Loading