move stuff under fdmath
This commit is contained in:
parent
1242e8794b
commit
a956323b94
@ -3,14 +3,18 @@ import numpy
|
|||||||
from numpy.linalg import norm
|
from numpy.linalg import norm
|
||||||
|
|
||||||
import meanas
|
import meanas
|
||||||
from meanas import vec, unvec, fdtd
|
from meanas import fdtd
|
||||||
from meanas.fdfd import waveguide_mode, functional, scpml, operators
|
from meanas.fdmath import vec, unvec
|
||||||
|
from meanas.fdfd import waveguide_3d, functional, scpml, operators
|
||||||
from meanas.fdfd.solvers import generic as generic_solver
|
from meanas.fdfd.solvers import generic as generic_solver
|
||||||
|
|
||||||
import gridlock
|
import gridlock
|
||||||
|
|
||||||
from matplotlib import pyplot
|
from matplotlib import pyplot
|
||||||
|
|
||||||
|
import logging
|
||||||
|
|
||||||
|
logging.basicConfig(level=logging.DEBUG)
|
||||||
|
|
||||||
__author__ = 'Jan Petykiewicz'
|
__author__ = 'Jan Petykiewicz'
|
||||||
|
|
||||||
@ -134,10 +138,10 @@ def test1(solver=generic_solver):
|
|||||||
'polarity': +1,
|
'polarity': +1,
|
||||||
}
|
}
|
||||||
|
|
||||||
wg_results = waveguide_mode.solve_waveguide_mode(mode_number=0, omega=omega, epsilon=grid.grids, **wg_args)
|
wg_results = waveguide_3d.solve_mode(mode_number=0, omega=omega, epsilon=grid.grids, **wg_args)
|
||||||
J = waveguide_mode.compute_source(E=wg_results['E'], wavenumber=wg_results['wavenumber'],
|
J = waveguide_3d.compute_source(E=wg_results['E'], wavenumber=wg_results['wavenumber'],
|
||||||
omega=omega, epsilon=grid.grids, **wg_args)
|
omega=omega, epsilon=grid.grids, **wg_args)
|
||||||
e_overlap = waveguide_mode.compute_overlap_e(E=wg_results['E'], wavenumber=wg_results['wavenumber'], **wg_args)
|
e_overlap = waveguide_3d.compute_overlap_e(E=wg_results['E'], wavenumber=wg_results['wavenumber'], **wg_args)
|
||||||
|
|
||||||
pecg = gridlock.Grid(edge_coords, initial=0.0, num_grids=3)
|
pecg = gridlock.Grid(edge_coords, initial=0.0, num_grids=3)
|
||||||
# pecg.draw_cuboid(center=[700, 0, 0], dimensions=[80, 1e8, 1e8], eps=1)
|
# pecg.draw_cuboid(center=[700, 0, 0], dimensions=[80, 1e8, 1e8], eps=1)
|
||||||
|
@ -6,9 +6,6 @@ See the readme or `import meanas; help(meanas)` for more info.
|
|||||||
|
|
||||||
import pathlib
|
import pathlib
|
||||||
|
|
||||||
from .types import dx_lists_t, field_t, vfield_t, field_updater
|
|
||||||
from .vectorization import vec, unvec
|
|
||||||
|
|
||||||
__author__ = 'Jan Petykiewicz'
|
__author__ = 'Jan Petykiewicz'
|
||||||
|
|
||||||
with open(pathlib.Path(__file__).parent / 'VERSION', 'r') as f:
|
with open(pathlib.Path(__file__).parent / 'VERSION', 'r') as f:
|
||||||
|
@ -37,20 +37,17 @@ def power_iteration(operator: sparse.spmatrix,
|
|||||||
|
|
||||||
|
|
||||||
def rayleigh_quotient_iteration(operator: sparse.spmatrix or spalg.LinearOperator,
|
def rayleigh_quotient_iteration(operator: sparse.spmatrix or spalg.LinearOperator,
|
||||||
guess_vectors: numpy.ndarray,
|
guess_vector: numpy.ndarray,
|
||||||
iterations: int = 40,
|
iterations: int = 40,
|
||||||
tolerance: float = 1e-13,
|
tolerance: float = 1e-13,
|
||||||
solver=None,
|
solver = None,
|
||||||
) -> Tuple[complex, numpy.ndarray]:
|
) -> Tuple[complex, numpy.ndarray]:
|
||||||
"""
|
"""
|
||||||
Use Rayleigh quotient iteration to refine an eigenvector guess.
|
Use Rayleigh quotient iteration to refine an eigenvector guess.
|
||||||
|
|
||||||
TODO:
|
|
||||||
Need to test this for more than one guess_vectors.
|
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
operator: Matrix to analyze.
|
operator: Matrix to analyze.
|
||||||
guess_vectors: Eigenvectors to refine.
|
guess_vector: Eigenvector to refine.
|
||||||
iterations: Maximum number of iterations to perform. Default 40.
|
iterations: Maximum number of iterations to perform. Default 40.
|
||||||
tolerance: Stop iteration if `(A - I*eigenvalue) @ v < num_vectors * tolerance`,
|
tolerance: Stop iteration if `(A - I*eigenvalue) @ v < num_vectors * tolerance`,
|
||||||
Default 1e-13.
|
Default 1e-13.
|
||||||
@ -73,16 +70,16 @@ def rayleigh_quotient_iteration(operator: sparse.spmatrix or spalg.LinearOperato
|
|||||||
if solver is None:
|
if solver is None:
|
||||||
solver = lambda A, b: spalg.bicgstab(A, b)[0]
|
solver = lambda A, b: spalg.bicgstab(A, b)[0]
|
||||||
|
|
||||||
v = numpy.atleast_2d(guess_vectors)
|
v = numpy.squeeze(guess_vector)
|
||||||
v /= norm(v)
|
v /= norm(v)
|
||||||
for _ in range(iterations):
|
for _ in range(iterations):
|
||||||
eigval = v.conj() @ (operator @ v)
|
eigval = v.conj() @ (operator @ v)
|
||||||
if norm(operator @ v - eigval * v) < v.shape[1] * tolerance:
|
if norm(operator @ v - eigval * v) < tolerance:
|
||||||
break
|
break
|
||||||
|
|
||||||
shifted_operator = operator - shift(eigval)
|
shifted_operator = operator - shift(eigval)
|
||||||
v = solver(shifted_operator, v)
|
v = solver(shifted_operator, v)
|
||||||
v /= norm(v, axis=0)
|
v /= norm(v)
|
||||||
return eigval, v
|
return eigval, v
|
||||||
|
|
||||||
|
|
||||||
|
@ -83,7 +83,7 @@ import scipy.optimize
|
|||||||
from scipy.linalg import norm
|
from scipy.linalg import norm
|
||||||
import scipy.sparse.linalg as spalg
|
import scipy.sparse.linalg as spalg
|
||||||
|
|
||||||
from .. import field_t
|
from ..fdmath import fdfield_t
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
@ -154,8 +154,8 @@ def generate_kmn(k0: numpy.ndarray,
|
|||||||
|
|
||||||
def maxwell_operator(k0: numpy.ndarray,
|
def maxwell_operator(k0: numpy.ndarray,
|
||||||
G_matrix: numpy.ndarray,
|
G_matrix: numpy.ndarray,
|
||||||
epsilon: field_t,
|
epsilon: fdfield_t,
|
||||||
mu: field_t = None
|
mu: fdfield_t = None
|
||||||
) -> Callable[[numpy.ndarray], numpy.ndarray]:
|
) -> Callable[[numpy.ndarray], numpy.ndarray]:
|
||||||
"""
|
"""
|
||||||
Generate the Maxwell operator
|
Generate the Maxwell operator
|
||||||
@ -227,8 +227,8 @@ def maxwell_operator(k0: numpy.ndarray,
|
|||||||
|
|
||||||
def hmn_2_exyz(k0: numpy.ndarray,
|
def hmn_2_exyz(k0: numpy.ndarray,
|
||||||
G_matrix: numpy.ndarray,
|
G_matrix: numpy.ndarray,
|
||||||
epsilon: field_t,
|
epsilon: fdfield_t,
|
||||||
) -> Callable[[numpy.ndarray], field_t]:
|
) -> Callable[[numpy.ndarray], fdfield_t]:
|
||||||
"""
|
"""
|
||||||
Generate an operator which converts a vectorized spatial-frequency-space
|
Generate an operator which converts a vectorized spatial-frequency-space
|
||||||
h_mn into an E-field distribution, i.e.
|
h_mn into an E-field distribution, i.e.
|
||||||
@ -249,7 +249,7 @@ def hmn_2_exyz(k0: numpy.ndarray,
|
|||||||
|
|
||||||
k_mag, m, n = generate_kmn(k0, G_matrix, shape)
|
k_mag, m, n = generate_kmn(k0, G_matrix, shape)
|
||||||
|
|
||||||
def operator(h: numpy.ndarray) -> field_t:
|
def operator(h: numpy.ndarray) -> fdfield_t:
|
||||||
hin_m, hin_n = [hi.reshape(shape) for hi in numpy.split(h, 2)]
|
hin_m, hin_n = [hi.reshape(shape) for hi in numpy.split(h, 2)]
|
||||||
d_xyz = (n * hin_m -
|
d_xyz = (n * hin_m -
|
||||||
m * hin_n) * k_mag
|
m * hin_n) * k_mag
|
||||||
@ -262,8 +262,8 @@ def hmn_2_exyz(k0: numpy.ndarray,
|
|||||||
|
|
||||||
def hmn_2_hxyz(k0: numpy.ndarray,
|
def hmn_2_hxyz(k0: numpy.ndarray,
|
||||||
G_matrix: numpy.ndarray,
|
G_matrix: numpy.ndarray,
|
||||||
epsilon: field_t
|
epsilon: fdfield_t
|
||||||
) -> Callable[[numpy.ndarray], field_t]:
|
) -> Callable[[numpy.ndarray], fdfield_t]:
|
||||||
"""
|
"""
|
||||||
Generate an operator which converts a vectorized spatial-frequency-space
|
Generate an operator which converts a vectorized spatial-frequency-space
|
||||||
h_mn into an H-field distribution, i.e.
|
h_mn into an H-field distribution, i.e.
|
||||||
@ -293,8 +293,8 @@ def hmn_2_hxyz(k0: numpy.ndarray,
|
|||||||
|
|
||||||
def inverse_maxwell_operator_approx(k0: numpy.ndarray,
|
def inverse_maxwell_operator_approx(k0: numpy.ndarray,
|
||||||
G_matrix: numpy.ndarray,
|
G_matrix: numpy.ndarray,
|
||||||
epsilon: field_t,
|
epsilon: fdfield_t,
|
||||||
mu: field_t = None
|
mu: fdfield_t = None
|
||||||
) -> Callable[[numpy.ndarray], numpy.ndarray]:
|
) -> Callable[[numpy.ndarray], numpy.ndarray]:
|
||||||
"""
|
"""
|
||||||
Generate an approximate inverse of the Maxwell operator,
|
Generate an approximate inverse of the Maxwell operator,
|
||||||
@ -366,8 +366,8 @@ def find_k(frequency: float,
|
|||||||
tolerance: float,
|
tolerance: float,
|
||||||
direction: numpy.ndarray,
|
direction: numpy.ndarray,
|
||||||
G_matrix: numpy.ndarray,
|
G_matrix: numpy.ndarray,
|
||||||
epsilon: field_t,
|
epsilon: fdfield_t,
|
||||||
mu: field_t = None,
|
mu: fdfield_t = None,
|
||||||
band: int = 0,
|
band: int = 0,
|
||||||
k_min: float = 0,
|
k_min: float = 0,
|
||||||
k_max: float = 0.5,
|
k_max: float = 0.5,
|
||||||
@ -409,8 +409,8 @@ def find_k(frequency: float,
|
|||||||
def eigsolve(num_modes: int,
|
def eigsolve(num_modes: int,
|
||||||
k0: numpy.ndarray,
|
k0: numpy.ndarray,
|
||||||
G_matrix: numpy.ndarray,
|
G_matrix: numpy.ndarray,
|
||||||
epsilon: field_t,
|
epsilon: fdfield_t,
|
||||||
mu: field_t = None,
|
mu: fdfield_t = None,
|
||||||
tolerance: float = 1e-20,
|
tolerance: float = 1e-20,
|
||||||
max_iters: int = 10000,
|
max_iters: int = 10000,
|
||||||
reset_iters: int = 100,
|
reset_iters: int = 100,
|
||||||
|
@ -6,11 +6,11 @@ import numpy
|
|||||||
from numpy.fft import fft2, fftshift, fftfreq, ifft2, ifftshift
|
from numpy.fft import fft2, fftshift, fftfreq, ifft2, ifftshift
|
||||||
from numpy import pi
|
from numpy import pi
|
||||||
|
|
||||||
from .. import field_t
|
from .. import fdfield_t
|
||||||
|
|
||||||
|
|
||||||
def near_to_farfield(E_near: field_t,
|
def near_to_farfield(E_near: fdfield_t,
|
||||||
H_near: field_t,
|
H_near: fdfield_t,
|
||||||
dx: float,
|
dx: float,
|
||||||
dy: float,
|
dy: float,
|
||||||
padded_size: List[int] = None
|
padded_size: List[int] = None
|
||||||
@ -117,8 +117,8 @@ def near_to_farfield(E_near: field_t,
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
def far_to_nearfield(E_far: field_t,
|
def far_to_nearfield(E_far: fdfield_t,
|
||||||
H_far: field_t,
|
H_far: fdfield_t,
|
||||||
dkx: float,
|
dkx: float,
|
||||||
dky: float,
|
dky: float,
|
||||||
padded_size: List[int] = None
|
padded_size: List[int] = None
|
||||||
|
@ -2,32 +2,30 @@
|
|||||||
Functional versions of many FDFD operators. These can be useful for performing
|
Functional versions of many FDFD operators. These can be useful for performing
|
||||||
FDFD calculations without needing to construct large matrices in memory.
|
FDFD calculations without needing to construct large matrices in memory.
|
||||||
|
|
||||||
The functions generated here expect `field_t` inputs with shape (3, X, Y, Z),
|
The functions generated here expect `fdfield_t` inputs with shape (3, X, Y, Z),
|
||||||
e.g. E = [E_x, E_y, E_z] where each component has shape (X, Y, Z)
|
e.g. E = [E_x, E_y, E_z] where each component has shape (X, Y, Z)
|
||||||
"""
|
"""
|
||||||
from typing import List, Callable, Tuple
|
from typing import List, Callable, Tuple
|
||||||
import numpy
|
import numpy
|
||||||
|
|
||||||
from .. import dx_lists_t, field_t
|
from ..fdmath import dx_lists_t, fdfield_t, fdfield_updater_t
|
||||||
from ..fdmath.functional import curl_forward, curl_back
|
from ..fdmath.functional import curl_forward, curl_back
|
||||||
|
|
||||||
|
|
||||||
__author__ = 'Jan Petykiewicz'
|
__author__ = 'Jan Petykiewicz'
|
||||||
|
|
||||||
|
|
||||||
field_transform_t = Callable[[field_t], field_t]
|
|
||||||
|
|
||||||
|
|
||||||
def e_full(omega: complex,
|
def e_full(omega: complex,
|
||||||
dxes: dx_lists_t,
|
dxes: dx_lists_t,
|
||||||
epsilon: field_t,
|
epsilon: fdfield_t,
|
||||||
mu: field_t = None
|
mu: fdfield_t = None
|
||||||
) -> field_transform_t:
|
) -> fdfield_updater_t:
|
||||||
"""
|
"""
|
||||||
Wave operator for use with E-field. See `operators.e_full` for details.
|
Wave operator for use with E-field. See `operators.e_full` for details.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
omega: Angular frequency of the simulation
|
omega: Angular frequency of the simulation
|
||||||
dxes: Grid parameters [dx_e, dx_h] as described in meanas.types
|
dxes: Grid parameters `[dx_e, dx_h]` as described in `meanas.fdmath.types`
|
||||||
epsilon: Dielectric constant
|
epsilon: Dielectric constant
|
||||||
mu: Magnetic permeability (default 1 everywhere)
|
mu: Magnetic permeability (default 1 everywhere)
|
||||||
|
|
||||||
@ -54,16 +52,16 @@ def e_full(omega: complex,
|
|||||||
|
|
||||||
def eh_full(omega: complex,
|
def eh_full(omega: complex,
|
||||||
dxes: dx_lists_t,
|
dxes: dx_lists_t,
|
||||||
epsilon: field_t,
|
epsilon: fdfield_t,
|
||||||
mu: field_t = None
|
mu: fdfield_t = None
|
||||||
) -> Callable[[field_t, field_t], Tuple[field_t, field_t]]:
|
) -> Callable[[fdfield_t, fdfield_t], Tuple[fdfield_t, fdfield_t]]:
|
||||||
"""
|
"""
|
||||||
Wave operator for full (both E and H) field representation.
|
Wave operator for full (both E and H) field representation.
|
||||||
See `operators.eh_full`.
|
See `operators.eh_full`.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
omega: Angular frequency of the simulation
|
omega: Angular frequency of the simulation
|
||||||
dxes: Grid parameters [dx_e, dx_h] as described in meanas.types
|
dxes: Grid parameters `[dx_e, dx_h]` as described in `meanas.fdmath.types`
|
||||||
epsilon: Dielectric constant
|
epsilon: Dielectric constant
|
||||||
mu: Magnetic permeability (default 1 everywhere)
|
mu: Magnetic permeability (default 1 everywhere)
|
||||||
|
|
||||||
@ -90,15 +88,15 @@ def eh_full(omega: complex,
|
|||||||
|
|
||||||
def e2h(omega: complex,
|
def e2h(omega: complex,
|
||||||
dxes: dx_lists_t,
|
dxes: dx_lists_t,
|
||||||
mu: field_t = None,
|
mu: fdfield_t = None,
|
||||||
) -> field_transform_t:
|
) -> fdfield_updater_t:
|
||||||
"""
|
"""
|
||||||
Utility operator for converting the `E` field into the `H` field.
|
Utility operator for converting the `E` field into the `H` field.
|
||||||
For use with `e_full` -- assumes that there is no magnetic current `M`.
|
For use with `e_full` -- assumes that there is no magnetic current `M`.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
omega: Angular frequency of the simulation
|
omega: Angular frequency of the simulation
|
||||||
dxes: Grid parameters `[dx_e, dx_h]` as described in `meanas.types`
|
dxes: Grid parameters `[dx_e, dx_h]` as described in `meanas.fdmath.types`
|
||||||
mu: Magnetic permeability (default 1 everywhere)
|
mu: Magnetic permeability (default 1 everywhere)
|
||||||
|
|
||||||
Return:
|
Return:
|
||||||
@ -121,8 +119,8 @@ def e2h(omega: complex,
|
|||||||
|
|
||||||
def m2j(omega: complex,
|
def m2j(omega: complex,
|
||||||
dxes: dx_lists_t,
|
dxes: dx_lists_t,
|
||||||
mu: field_t = None,
|
mu: fdfield_t = None,
|
||||||
) -> field_transform_t:
|
) -> fdfield_updater_t:
|
||||||
"""
|
"""
|
||||||
Utility operator for converting magnetic current `M` distribution
|
Utility operator for converting magnetic current `M` distribution
|
||||||
into equivalent electric current distribution `J`.
|
into equivalent electric current distribution `J`.
|
||||||
@ -130,7 +128,7 @@ def m2j(omega: complex,
|
|||||||
|
|
||||||
Args:
|
Args:
|
||||||
omega: Angular frequency of the simulation
|
omega: Angular frequency of the simulation
|
||||||
dxes: Grid parameters `[dx_e, dx_h]` as described in `meanas.types`
|
dxes: Grid parameters `[dx_e, dx_h]` as described in `meanas.fdmath.types`
|
||||||
mu: Magnetic permeability (default 1 everywhere)
|
mu: Magnetic permeability (default 1 everywhere)
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
@ -153,12 +151,12 @@ def m2j(omega: complex,
|
|||||||
return m2j_mu
|
return m2j_mu
|
||||||
|
|
||||||
|
|
||||||
def e_tfsf_source(TF_region: field_t,
|
def e_tfsf_source(TF_region: fdfield_t,
|
||||||
omega: complex,
|
omega: complex,
|
||||||
dxes: dx_lists_t,
|
dxes: dx_lists_t,
|
||||||
epsilon: field_t,
|
epsilon: fdfield_t,
|
||||||
mu: field_t = None,
|
mu: fdfield_t = None,
|
||||||
) -> field_transform_t:
|
) -> fdfield_updater_t:
|
||||||
"""
|
"""
|
||||||
Operator that turns an E-field distribution into a total-field/scattered-field
|
Operator that turns an E-field distribution into a total-field/scattered-field
|
||||||
(TFSF) source.
|
(TFSF) source.
|
||||||
@ -168,7 +166,7 @@ def e_tfsf_source(TF_region: field_t,
|
|||||||
(i.e. in the scattered-field region).
|
(i.e. in the scattered-field region).
|
||||||
Should have the same shape as the simulation grid, e.g. `epsilon[0].shape`.
|
Should have the same shape as the simulation grid, e.g. `epsilon[0].shape`.
|
||||||
omega: Angular frequency of the simulation
|
omega: Angular frequency of the simulation
|
||||||
dxes: Grid parameters `[dx_e, dx_h]` as described in `meanas.types`
|
dxes: Grid parameters `[dx_e, dx_h]` as described in `meanas.fdmath.types`
|
||||||
epsilon: Dielectric constant distribution
|
epsilon: Dielectric constant distribution
|
||||||
mu: Magnetic permeability (default 1 everywhere)
|
mu: Magnetic permeability (default 1 everywhere)
|
||||||
|
|
||||||
@ -184,7 +182,7 @@ def e_tfsf_source(TF_region: field_t,
|
|||||||
return neg_iwj / (-1j * omega)
|
return neg_iwj / (-1j * omega)
|
||||||
|
|
||||||
|
|
||||||
def poynting_e_cross_h(dxes: dx_lists_t) -> Callable[[field_t, field_t], field_t]:
|
def poynting_e_cross_h(dxes: dx_lists_t) -> Callable[[fdfield_t, fdfield_t], fdfield_t]:
|
||||||
"""
|
"""
|
||||||
Generates a function that takes the single-frequency `E` and `H` fields
|
Generates a function that takes the single-frequency `E` and `H` fields
|
||||||
and calculates the cross product `E` x `H` = \\( E \\times H \\) as required
|
and calculates the cross product `E` x `H` = \\( E \\times H \\) as required
|
||||||
@ -201,12 +199,12 @@ def poynting_e_cross_h(dxes: dx_lists_t) -> Callable[[field_t, field_t], field_t
|
|||||||
instead.
|
instead.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
dxes: Grid parameters `[dx_e, dx_h]` as described in `meanas.types`
|
dxes: Grid parameters `[dx_e, dx_h]` as described in `meanas.fdmath.types`
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
Function `f` that returns E x H as required for the poynting vector.
|
Function `f` that returns E x H as required for the poynting vector.
|
||||||
"""
|
"""
|
||||||
def exh(e: field_t, h: field_t):
|
def exh(e: fdfield_t, h: fdfield_t):
|
||||||
s = numpy.empty_like(e)
|
s = numpy.empty_like(e)
|
||||||
ex = e[0] * dxes[0][0][:, None, None]
|
ex = e[0] * dxes[0][0][:, None, None]
|
||||||
ey = e[1] * dxes[0][1][None, :, None]
|
ey = e[1] * dxes[0][1][None, :, None]
|
||||||
|
@ -9,7 +9,7 @@ E- and H-field values are defined on a Yee cell; `epsilon` values should be calc
|
|||||||
cells centered at each E component (`mu` at each H component).
|
cells centered at each E component (`mu` at each H component).
|
||||||
|
|
||||||
Many of these functions require a `dxes` parameter, of type `dx_lists_t`; see
|
Many of these functions require a `dxes` parameter, of type `dx_lists_t`; see
|
||||||
the `meanas.types` submodule for details.
|
the `meanas.fdmath.types` submodule for details.
|
||||||
|
|
||||||
|
|
||||||
The following operators are included:
|
The following operators are included:
|
||||||
@ -31,7 +31,7 @@ from typing import List, Tuple
|
|||||||
import numpy
|
import numpy
|
||||||
import scipy.sparse as sparse
|
import scipy.sparse as sparse
|
||||||
|
|
||||||
from .. import vec, dx_lists_t, vfield_t
|
from ..fdmath import vec, dx_lists_t, vfdfield_t
|
||||||
from ..fdmath.operators import shift_with_mirror, rotation, curl_forward, curl_back
|
from ..fdmath.operators import shift_with_mirror, rotation, curl_forward, curl_back
|
||||||
|
|
||||||
|
|
||||||
@ -40,10 +40,10 @@ __author__ = 'Jan Petykiewicz'
|
|||||||
|
|
||||||
def e_full(omega: complex,
|
def e_full(omega: complex,
|
||||||
dxes: dx_lists_t,
|
dxes: dx_lists_t,
|
||||||
epsilon: vfield_t,
|
epsilon: vfdfield_t,
|
||||||
mu: vfield_t = None,
|
mu: vfdfield_t = None,
|
||||||
pec: vfield_t = None,
|
pec: vfdfield_t = None,
|
||||||
pmc: vfield_t = None,
|
pmc: vfdfield_t = None,
|
||||||
) -> sparse.spmatrix:
|
) -> sparse.spmatrix:
|
||||||
"""
|
"""
|
||||||
Wave operator
|
Wave operator
|
||||||
@ -60,7 +60,7 @@ def e_full(omega: complex,
|
|||||||
|
|
||||||
Args:
|
Args:
|
||||||
omega: Angular frequency of the simulation
|
omega: Angular frequency of the simulation
|
||||||
dxes: Grid parameters `[dx_e, dx_h]` as described in `meanas.types`
|
dxes: Grid parameters `[dx_e, dx_h]` as described in `meanas.fdmath.types`
|
||||||
epsilon: Vectorized dielectric constant
|
epsilon: Vectorized dielectric constant
|
||||||
mu: Vectorized magnetic permeability (default 1 everywhere).
|
mu: Vectorized magnetic permeability (default 1 everywhere).
|
||||||
pec: Vectorized mask specifying PEC cells. Any cells where `pec != 0` are interpreted
|
pec: Vectorized mask specifying PEC cells. Any cells where `pec != 0` are interpreted
|
||||||
@ -107,7 +107,7 @@ def e_full_preconditioners(dxes: dx_lists_t
|
|||||||
The preconditioner matrices are diagonal and complex, with `Pr = 1 / Pl`
|
The preconditioner matrices are diagonal and complex, with `Pr = 1 / Pl`
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
dxes: Grid parameters `[dx_e, dx_h]` as described in `meanas.types`
|
dxes: Grid parameters `[dx_e, dx_h]` as described in `meanas.fdmath.types`
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
Preconditioner matrices `(Pl, Pr)`.
|
Preconditioner matrices `(Pl, Pr)`.
|
||||||
@ -124,10 +124,10 @@ def e_full_preconditioners(dxes: dx_lists_t
|
|||||||
|
|
||||||
def h_full(omega: complex,
|
def h_full(omega: complex,
|
||||||
dxes: dx_lists_t,
|
dxes: dx_lists_t,
|
||||||
epsilon: vfield_t,
|
epsilon: vfdfield_t,
|
||||||
mu: vfield_t = None,
|
mu: vfdfield_t = None,
|
||||||
pec: vfield_t = None,
|
pec: vfdfield_t = None,
|
||||||
pmc: vfield_t = None,
|
pmc: vfdfield_t = None,
|
||||||
) -> sparse.spmatrix:
|
) -> sparse.spmatrix:
|
||||||
"""
|
"""
|
||||||
Wave operator
|
Wave operator
|
||||||
@ -142,7 +142,7 @@ def h_full(omega: complex,
|
|||||||
|
|
||||||
Args:
|
Args:
|
||||||
omega: Angular frequency of the simulation
|
omega: Angular frequency of the simulation
|
||||||
dxes: Grid parameters `[dx_e, dx_h]` as described in `meanas.types`
|
dxes: Grid parameters `[dx_e, dx_h]` as described in `meanas.fdmath.types`
|
||||||
epsilon: Vectorized dielectric constant
|
epsilon: Vectorized dielectric constant
|
||||||
mu: Vectorized magnetic permeability (default 1 everywhere)
|
mu: Vectorized magnetic permeability (default 1 everywhere)
|
||||||
pec: Vectorized mask specifying PEC cells. Any cells where `pec != 0` are interpreted
|
pec: Vectorized mask specifying PEC cells. Any cells where `pec != 0` are interpreted
|
||||||
@ -180,10 +180,10 @@ def h_full(omega: complex,
|
|||||||
|
|
||||||
def eh_full(omega: complex,
|
def eh_full(omega: complex,
|
||||||
dxes: dx_lists_t,
|
dxes: dx_lists_t,
|
||||||
epsilon: vfield_t,
|
epsilon: vfdfield_t,
|
||||||
mu: vfield_t = None,
|
mu: vfdfield_t = None,
|
||||||
pec: vfield_t = None,
|
pec: vfdfield_t = None,
|
||||||
pmc: vfield_t = None
|
pmc: vfdfield_t = None
|
||||||
) -> sparse.spmatrix:
|
) -> sparse.spmatrix:
|
||||||
"""
|
"""
|
||||||
Wave operator for `[E, H]` field representation. This operator implements Maxwell's
|
Wave operator for `[E, H]` field representation. This operator implements Maxwell's
|
||||||
@ -210,7 +210,7 @@ def eh_full(omega: complex,
|
|||||||
|
|
||||||
Args:
|
Args:
|
||||||
omega: Angular frequency of the simulation
|
omega: Angular frequency of the simulation
|
||||||
dxes: Grid parameters `[dx_e, dx_h]` as described in `meanas.types`
|
dxes: Grid parameters `[dx_e, dx_h]` as described in `meanas.fdmath.types`
|
||||||
epsilon: Vectorized dielectric constant
|
epsilon: Vectorized dielectric constant
|
||||||
mu: Vectorized magnetic permeability (default 1 everywhere)
|
mu: Vectorized magnetic permeability (default 1 everywhere)
|
||||||
pec: Vectorized mask specifying PEC cells. Any cells where `pec != 0` are interpreted
|
pec: Vectorized mask specifying PEC cells. Any cells where `pec != 0` are interpreted
|
||||||
@ -249,8 +249,8 @@ def eh_full(omega: complex,
|
|||||||
|
|
||||||
def e2h(omega: complex,
|
def e2h(omega: complex,
|
||||||
dxes: dx_lists_t,
|
dxes: dx_lists_t,
|
||||||
mu: vfield_t = None,
|
mu: vfdfield_t = None,
|
||||||
pmc: vfield_t = None,
|
pmc: vfdfield_t = None,
|
||||||
) -> sparse.spmatrix:
|
) -> sparse.spmatrix:
|
||||||
"""
|
"""
|
||||||
Utility operator for converting the E field into the H field.
|
Utility operator for converting the E field into the H field.
|
||||||
@ -258,7 +258,7 @@ def e2h(omega: complex,
|
|||||||
|
|
||||||
Args:
|
Args:
|
||||||
omega: Angular frequency of the simulation
|
omega: Angular frequency of the simulation
|
||||||
dxes: Grid parameters `[dx_e, dx_h]` as described in `meanas.types`
|
dxes: Grid parameters `[dx_e, dx_h]` as described in `meanas.fdmath.types`
|
||||||
mu: Vectorized magnetic permeability (default 1 everywhere)
|
mu: Vectorized magnetic permeability (default 1 everywhere)
|
||||||
pmc: Vectorized mask specifying PMC cells. Any cells where `pmc != 0` are interpreted
|
pmc: Vectorized mask specifying PMC cells. Any cells where `pmc != 0` are interpreted
|
||||||
as containing a perfect magnetic conductor (PMC).
|
as containing a perfect magnetic conductor (PMC).
|
||||||
@ -280,7 +280,7 @@ def e2h(omega: complex,
|
|||||||
|
|
||||||
def m2j(omega: complex,
|
def m2j(omega: complex,
|
||||||
dxes: dx_lists_t,
|
dxes: dx_lists_t,
|
||||||
mu: vfield_t = None
|
mu: vfdfield_t = None
|
||||||
) -> sparse.spmatrix:
|
) -> sparse.spmatrix:
|
||||||
"""
|
"""
|
||||||
Operator for converting a magnetic current M into an electric current J.
|
Operator for converting a magnetic current M into an electric current J.
|
||||||
@ -288,7 +288,7 @@ def m2j(omega: complex,
|
|||||||
|
|
||||||
Args:
|
Args:
|
||||||
omega: Angular frequency of the simulation
|
omega: Angular frequency of the simulation
|
||||||
dxes: Grid parameters `[dx_e, dx_h]` as described in `meanas.types`
|
dxes: Grid parameters `[dx_e, dx_h]` as described in `meanas.fdmath.types`
|
||||||
mu: Vectorized magnetic permeability (default 1 everywhere)
|
mu: Vectorized magnetic permeability (default 1 everywhere)
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
@ -302,14 +302,14 @@ def m2j(omega: complex,
|
|||||||
return op
|
return op
|
||||||
|
|
||||||
|
|
||||||
def poynting_e_cross(e: vfield_t, dxes: dx_lists_t) -> sparse.spmatrix:
|
def poynting_e_cross(e: vfdfield_t, dxes: dx_lists_t) -> sparse.spmatrix:
|
||||||
"""
|
"""
|
||||||
Operator for computing the Poynting vector, containing the
|
Operator for computing the Poynting vector, containing the
|
||||||
(E x) portion of the Poynting vector.
|
(E x) portion of the Poynting vector.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
e: Vectorized E-field for the ExH cross product
|
e: Vectorized E-field for the ExH cross product
|
||||||
dxes: Grid parameters `[dx_e, dx_h]` as described in `meanas.types`
|
dxes: Grid parameters `[dx_e, dx_h]` as described in `meanas.fdmath.types`
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
Sparse matrix containing (E x) portion of Poynting cross product.
|
Sparse matrix containing (E x) portion of Poynting cross product.
|
||||||
@ -331,13 +331,13 @@ def poynting_e_cross(e: vfield_t, dxes: dx_lists_t) -> sparse.spmatrix:
|
|||||||
return P
|
return P
|
||||||
|
|
||||||
|
|
||||||
def poynting_h_cross(h: vfield_t, dxes: dx_lists_t) -> sparse.spmatrix:
|
def poynting_h_cross(h: vfdfield_t, dxes: dx_lists_t) -> sparse.spmatrix:
|
||||||
"""
|
"""
|
||||||
Operator for computing the Poynting vector, containing the (H x) portion of the Poynting vector.
|
Operator for computing the Poynting vector, containing the (H x) portion of the Poynting vector.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
h: Vectorized H-field for the HxE cross product
|
h: Vectorized H-field for the HxE cross product
|
||||||
dxes: Grid parameters `[dx_e, dx_h]` as described in `meanas.types`
|
dxes: Grid parameters `[dx_e, dx_h]` as described in `meanas.fdmath.types`
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
Sparse matrix containing (H x) portion of Poynting cross product.
|
Sparse matrix containing (H x) portion of Poynting cross product.
|
||||||
@ -358,11 +358,11 @@ def poynting_h_cross(h: vfield_t, dxes: dx_lists_t) -> sparse.spmatrix:
|
|||||||
return P
|
return P
|
||||||
|
|
||||||
|
|
||||||
def e_tfsf_source(TF_region: vfield_t,
|
def e_tfsf_source(TF_region: vfdfield_t,
|
||||||
omega: complex,
|
omega: complex,
|
||||||
dxes: dx_lists_t,
|
dxes: dx_lists_t,
|
||||||
epsilon: vfield_t,
|
epsilon: vfdfield_t,
|
||||||
mu: vfield_t = None,
|
mu: vfdfield_t = None,
|
||||||
) -> sparse.spmatrix:
|
) -> sparse.spmatrix:
|
||||||
"""
|
"""
|
||||||
Operator that turns a desired E-field distribution into a
|
Operator that turns a desired E-field distribution into a
|
||||||
@ -374,7 +374,7 @@ def e_tfsf_source(TF_region: vfield_t,
|
|||||||
TF_region: Mask, which is set to 1 inside the total-field region and 0 in the
|
TF_region: Mask, which is set to 1 inside the total-field region and 0 in the
|
||||||
scattered-field region
|
scattered-field region
|
||||||
omega: Angular frequency of the simulation
|
omega: Angular frequency of the simulation
|
||||||
dxes: Grid parameters `[dx_e, dx_h]` as described in `meanas.types`
|
dxes: Grid parameters `[dx_e, dx_h]` as described in `meanas.fdmath.types`
|
||||||
epsilon: Vectorized dielectric constant
|
epsilon: Vectorized dielectric constant
|
||||||
mu: Vectorized magnetic permeability (default 1 everywhere).
|
mu: Vectorized magnetic permeability (default 1 everywhere).
|
||||||
|
|
||||||
@ -388,11 +388,11 @@ def e_tfsf_source(TF_region: vfield_t,
|
|||||||
return (A @ Q - Q @ A) / (-1j * omega)
|
return (A @ Q - Q @ A) / (-1j * omega)
|
||||||
|
|
||||||
|
|
||||||
def e_boundary_source(mask: vfield_t,
|
def e_boundary_source(mask: vfdfield_t,
|
||||||
omega: complex,
|
omega: complex,
|
||||||
dxes: dx_lists_t,
|
dxes: dx_lists_t,
|
||||||
epsilon: vfield_t,
|
epsilon: vfdfield_t,
|
||||||
mu: vfield_t = None,
|
mu: vfdfield_t = None,
|
||||||
periodic_mask_edges: bool = False,
|
periodic_mask_edges: bool = False,
|
||||||
) -> sparse.spmatrix:
|
) -> sparse.spmatrix:
|
||||||
"""
|
"""
|
||||||
@ -405,7 +405,7 @@ def e_boundary_source(mask: vfield_t,
|
|||||||
i.e. any points where shifting the mask by one cell in any direction
|
i.e. any points where shifting the mask by one cell in any direction
|
||||||
would change its value.
|
would change its value.
|
||||||
omega: Angular frequency of the simulation
|
omega: Angular frequency of the simulation
|
||||||
dxes: Grid parameters `[dx_e, dx_h]` as described in `meanas.types`
|
dxes: Grid parameters `[dx_e, dx_h]` as described in `meanas.fdmath.types`
|
||||||
epsilon: Vectorized dielectric constant
|
epsilon: Vectorized dielectric constant
|
||||||
mu: Vectorized magnetic permeability (default 1 everywhere).
|
mu: Vectorized magnetic permeability (default 1 everywhere).
|
||||||
|
|
||||||
|
@ -5,14 +5,14 @@ Functions for creating stretched coordinate perfectly matched layer (PML) absorb
|
|||||||
from typing import List, Callable
|
from typing import List, Callable
|
||||||
import numpy
|
import numpy
|
||||||
|
|
||||||
from .. import dx_lists_t
|
from ..fdmath import dx_lists_t
|
||||||
|
|
||||||
|
|
||||||
__author__ = 'Jan Petykiewicz'
|
__author__ = 'Jan Petykiewicz'
|
||||||
|
|
||||||
|
|
||||||
s_function_t = Callable[[float], float]
|
s_function_t = Callable[[float], float]
|
||||||
"""Typedef for s-functions"""
|
"""Typedef for s-functions, see `prepare_s_function()`"""
|
||||||
|
|
||||||
|
|
||||||
def prepare_s_function(ln_R: float = -16,
|
def prepare_s_function(ln_R: float = -16,
|
||||||
@ -63,7 +63,7 @@ def uniform_grid_scpml(shape: numpy.ndarray or List[int],
|
|||||||
Default uses `prepare_s_function()` with no parameters.
|
Default uses `prepare_s_function()` with no parameters.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
Complex cell widths (dx_lists_t) as discussed in `meanas.types`.
|
Complex cell widths (dx_lists_t) as discussed in `meanas.fdmath.types`.
|
||||||
"""
|
"""
|
||||||
if s_function is None:
|
if s_function is None:
|
||||||
s_function = prepare_s_function()
|
s_function = prepare_s_function()
|
||||||
@ -102,7 +102,7 @@ def stretch_with_scpml(dxes: dx_lists_t,
|
|||||||
Stretch dxes to contain a stretched-coordinate PML (SCPML) in one direction along one axis.
|
Stretch dxes to contain a stretched-coordinate PML (SCPML) in one direction along one axis.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
dxes: Grid parameters `[dx_e, dx_h]` as described in `meanas.types`
|
dxes: Grid parameters `[dx_e, dx_h]` as described in `meanas.fdmath.types`
|
||||||
axis: axis to stretch (0=x, 1=y, 2=z)
|
axis: axis to stretch (0=x, 1=y, 2=z)
|
||||||
polarity: direction to stretch (-1 for -ve, +1 for +ve)
|
polarity: direction to stretch (-1 for -ve, +1 for +ve)
|
||||||
omega: Angular frequency for the simulation
|
omega: Angular frequency for the simulation
|
||||||
@ -113,7 +113,7 @@ def stretch_with_scpml(dxes: dx_lists_t,
|
|||||||
of pml parameters. Default uses `prepare_s_function()` with no parameters.
|
of pml parameters. Default uses `prepare_s_function()` with no parameters.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
Complex cell widths (dx_lists_t) as discussed in `meanas.types`.
|
Complex cell widths (dx_lists_t) as discussed in `meanas.fdmath.types`.
|
||||||
Multiple calls to this function may be necessary if multiple absorpbing boundaries are needed.
|
Multiple calls to this function may be necessary if multiple absorpbing boundaries are needed.
|
||||||
"""
|
"""
|
||||||
if s_function is None:
|
if s_function is None:
|
||||||
|
@ -9,6 +9,7 @@ import numpy
|
|||||||
from numpy.linalg import norm
|
from numpy.linalg import norm
|
||||||
import scipy.sparse.linalg
|
import scipy.sparse.linalg
|
||||||
|
|
||||||
|
from ..fdmath import dx_lists_t, vfdfield_t
|
||||||
from . import operators
|
from . import operators
|
||||||
|
|
||||||
|
|
||||||
@ -60,16 +61,16 @@ def _scipy_qmr(A: scipy.sparse.csr_matrix,
|
|||||||
|
|
||||||
|
|
||||||
def generic(omega: complex,
|
def generic(omega: complex,
|
||||||
dxes: List[List[numpy.ndarray]],
|
dxes: dx_lists_t,
|
||||||
J: numpy.ndarray,
|
J: vfdfield_t,
|
||||||
epsilon: numpy.ndarray,
|
epsilon: vfdfield_t,
|
||||||
mu: numpy.ndarray = None,
|
mu: vfdfield_t = None,
|
||||||
pec: numpy.ndarray = None,
|
pec: vfdfield_t = None,
|
||||||
pmc: numpy.ndarray = None,
|
pmc: vfdfield_t = None,
|
||||||
adjoint: bool = False,
|
adjoint: bool = False,
|
||||||
matrix_solver: Callable[..., numpy.ndarray] = _scipy_qmr,
|
matrix_solver: Callable[..., numpy.ndarray] = _scipy_qmr,
|
||||||
matrix_solver_opts: Dict[str, Any] = None,
|
matrix_solver_opts: Dict[str, Any] = None,
|
||||||
) -> numpy.ndarray:
|
) -> vfdfield_t:
|
||||||
"""
|
"""
|
||||||
Conjugate gradient FDFD solver using CSR sparse matrices.
|
Conjugate gradient FDFD solver using CSR sparse matrices.
|
||||||
|
|
||||||
@ -78,7 +79,7 @@ def generic(omega: complex,
|
|||||||
Args:
|
Args:
|
||||||
omega: Complex frequency to solve at.
|
omega: Complex frequency to solve at.
|
||||||
dxes: `[[dx_e, dy_e, dz_e], [dx_h, dy_h, dz_h]]` (complex cell sizes) as
|
dxes: `[[dx_e, dy_e, dz_e], [dx_h, dy_h, dz_h]]` (complex cell sizes) as
|
||||||
discussed in `meanas.types`
|
discussed in `meanas.fdmath.types`
|
||||||
J: Electric current distribution (at E-field locations)
|
J: Electric current distribution (at E-field locations)
|
||||||
epsilon: Dielectric constant distribution (at E-field locations)
|
epsilon: Dielectric constant distribution (at E-field locations)
|
||||||
mu: Magnetic permeability distribution (at H-field locations)
|
mu: Magnetic permeability distribution (at H-field locations)
|
||||||
|
@ -14,7 +14,8 @@ import numpy
|
|||||||
from numpy.linalg import norm
|
from numpy.linalg import norm
|
||||||
import scipy.sparse as sparse
|
import scipy.sparse as sparse
|
||||||
|
|
||||||
from .. import vec, unvec, dx_lists_t, field_t, vfield_t
|
from ..fdmath.operators import deriv_forward, deriv_back, curl_forward, curl_back, cross
|
||||||
|
from ..fdmath import vec, unvec, dx_lists_t, fdfield_t, vfdfield_t
|
||||||
from ..eigensolvers import signed_eigensolve, rayleigh_quotient_iteration
|
from ..eigensolvers import signed_eigensolve, rayleigh_quotient_iteration
|
||||||
from . import operators
|
from . import operators
|
||||||
|
|
||||||
@ -24,8 +25,8 @@ __author__ = 'Jan Petykiewicz'
|
|||||||
|
|
||||||
def operator_e(omega: complex,
|
def operator_e(omega: complex,
|
||||||
dxes: dx_lists_t,
|
dxes: dx_lists_t,
|
||||||
epsilon: vfield_t,
|
epsilon: vfdfield_t,
|
||||||
mu: vfield_t = None,
|
mu: vfdfield_t = None,
|
||||||
) -> sparse.spmatrix:
|
) -> sparse.spmatrix:
|
||||||
"""
|
"""
|
||||||
Waveguide operator of the form
|
Waveguide operator of the form
|
||||||
@ -66,7 +67,7 @@ def operator_e(omega: complex,
|
|||||||
|
|
||||||
Args:
|
Args:
|
||||||
omega: The angular frequency of the system.
|
omega: The angular frequency of the system.
|
||||||
dxes: Grid parameters `[dx_e, dx_h]` as described in `meanas.types` (2D)
|
dxes: Grid parameters `[dx_e, dx_h]` as described in `meanas.fdmath.types` (2D)
|
||||||
epsilon: Vectorized dielectric constant grid
|
epsilon: Vectorized dielectric constant grid
|
||||||
mu: Vectorized magnetic permeability grid (default 1 everywhere)
|
mu: Vectorized magnetic permeability grid (default 1 everywhere)
|
||||||
|
|
||||||
@ -76,8 +77,8 @@ def operator_e(omega: complex,
|
|||||||
if numpy.any(numpy.equal(mu, None)):
|
if numpy.any(numpy.equal(mu, None)):
|
||||||
mu = numpy.ones_like(epsilon)
|
mu = numpy.ones_like(epsilon)
|
||||||
|
|
||||||
Dfx, Dfy = operators.deriv_forward(dxes[0])
|
Dfx, Dfy = deriv_forward(dxes[0])
|
||||||
Dbx, Dby = operators.deriv_back(dxes[1])
|
Dbx, Dby = deriv_back(dxes[1])
|
||||||
|
|
||||||
eps_parts = numpy.split(epsilon, 3)
|
eps_parts = numpy.split(epsilon, 3)
|
||||||
eps_xy = sparse.diags(numpy.hstack((eps_parts[0], eps_parts[1])))
|
eps_xy = sparse.diags(numpy.hstack((eps_parts[0], eps_parts[1])))
|
||||||
@ -95,8 +96,8 @@ def operator_e(omega: complex,
|
|||||||
|
|
||||||
def operator_h(omega: complex,
|
def operator_h(omega: complex,
|
||||||
dxes: dx_lists_t,
|
dxes: dx_lists_t,
|
||||||
epsilon: vfield_t,
|
epsilon: vfdfield_t,
|
||||||
mu: vfield_t = None,
|
mu: vfdfield_t = None,
|
||||||
) -> sparse.spmatrix:
|
) -> sparse.spmatrix:
|
||||||
"""
|
"""
|
||||||
Waveguide operator of the form
|
Waveguide operator of the form
|
||||||
@ -137,7 +138,7 @@ def operator_h(omega: complex,
|
|||||||
|
|
||||||
Args:
|
Args:
|
||||||
omega: The angular frequency of the system.
|
omega: The angular frequency of the system.
|
||||||
dxes: Grid parameters `[dx_e, dx_h]` as described in `meanas.types` (2D)
|
dxes: Grid parameters `[dx_e, dx_h]` as described in `meanas.fdmath.types` (2D)
|
||||||
epsilon: Vectorized dielectric constant grid
|
epsilon: Vectorized dielectric constant grid
|
||||||
mu: Vectorized magnetic permeability grid (default 1 everywhere)
|
mu: Vectorized magnetic permeability grid (default 1 everywhere)
|
||||||
|
|
||||||
@ -169,10 +170,10 @@ def normalized_fields_e(e_xy: numpy.ndarray,
|
|||||||
wavenumber: complex,
|
wavenumber: complex,
|
||||||
omega: complex,
|
omega: complex,
|
||||||
dxes: dx_lists_t,
|
dxes: dx_lists_t,
|
||||||
epsilon: vfield_t,
|
epsilon: vfdfield_t,
|
||||||
mu: vfield_t = None,
|
mu: vfdfield_t = None,
|
||||||
prop_phase: float = 0,
|
prop_phase: float = 0,
|
||||||
) -> Tuple[vfield_t, vfield_t]:
|
) -> Tuple[vfdfield_t, vfdfield_t]:
|
||||||
"""
|
"""
|
||||||
Given a vector `e_xy` containing the vectorized E_x and E_y fields,
|
Given a vector `e_xy` containing the vectorized E_x and E_y fields,
|
||||||
returns normalized, vectorized E and H fields for the system.
|
returns normalized, vectorized E and H fields for the system.
|
||||||
@ -182,7 +183,7 @@ def normalized_fields_e(e_xy: numpy.ndarray,
|
|||||||
wavenumber: Wavenumber assuming fields have z-dependence of `exp(-i * wavenumber * z)`.
|
wavenumber: Wavenumber assuming fields have z-dependence of `exp(-i * wavenumber * z)`.
|
||||||
It should satisfy `operator_e() @ e_xy == wavenumber**2 * e_xy`
|
It should satisfy `operator_e() @ e_xy == wavenumber**2 * e_xy`
|
||||||
omega: The angular frequency of the system
|
omega: The angular frequency of the system
|
||||||
dxes: Grid parameters `[dx_e, dx_h]` as described in `meanas.types` (2D)
|
dxes: Grid parameters `[dx_e, dx_h]` as described in `meanas.fdmath.types` (2D)
|
||||||
epsilon: Vectorized dielectric constant grid
|
epsilon: Vectorized dielectric constant grid
|
||||||
mu: Vectorized magnetic permeability grid (default 1 everywhere)
|
mu: Vectorized magnetic permeability grid (default 1 everywhere)
|
||||||
prop_phase: Phase shift `(dz * corrected_wavenumber)` over 1 cell in propagation direction.
|
prop_phase: Phase shift `(dz * corrected_wavenumber)` over 1 cell in propagation direction.
|
||||||
@ -203,10 +204,10 @@ def normalized_fields_h(h_xy: numpy.ndarray,
|
|||||||
wavenumber: complex,
|
wavenumber: complex,
|
||||||
omega: complex,
|
omega: complex,
|
||||||
dxes: dx_lists_t,
|
dxes: dx_lists_t,
|
||||||
epsilon: vfield_t,
|
epsilon: vfdfield_t,
|
||||||
mu: vfield_t = None,
|
mu: vfdfield_t = None,
|
||||||
prop_phase: float = 0,
|
prop_phase: float = 0,
|
||||||
) -> Tuple[vfield_t, vfield_t]:
|
) -> Tuple[vfdfield_t, vfdfield_t]:
|
||||||
"""
|
"""
|
||||||
Given a vector `h_xy` containing the vectorized H_x and H_y fields,
|
Given a vector `h_xy` containing the vectorized H_x and H_y fields,
|
||||||
returns normalized, vectorized E and H fields for the system.
|
returns normalized, vectorized E and H fields for the system.
|
||||||
@ -216,7 +217,7 @@ def normalized_fields_h(h_xy: numpy.ndarray,
|
|||||||
wavenumber: Wavenumber assuming fields have z-dependence of `exp(-i * wavenumber * z)`.
|
wavenumber: Wavenumber assuming fields have z-dependence of `exp(-i * wavenumber * z)`.
|
||||||
It should satisfy `operator_h() @ h_xy == wavenumber**2 * h_xy`
|
It should satisfy `operator_h() @ h_xy == wavenumber**2 * h_xy`
|
||||||
omega: The angular frequency of the system
|
omega: The angular frequency of the system
|
||||||
dxes: Grid parameters `[dx_e, dx_h]` as described in `meanas.types` (2D)
|
dxes: Grid parameters `[dx_e, dx_h]` as described in `meanas.fdmath.types` (2D)
|
||||||
epsilon: Vectorized dielectric constant grid
|
epsilon: Vectorized dielectric constant grid
|
||||||
mu: Vectorized magnetic permeability grid (default 1 everywhere)
|
mu: Vectorized magnetic permeability grid (default 1 everywhere)
|
||||||
prop_phase: Phase shift `(dz * corrected_wavenumber)` over 1 cell in propagation direction.
|
prop_phase: Phase shift `(dz * corrected_wavenumber)` over 1 cell in propagation direction.
|
||||||
@ -237,10 +238,10 @@ def _normalized_fields(e: numpy.ndarray,
|
|||||||
h: numpy.ndarray,
|
h: numpy.ndarray,
|
||||||
omega: complex,
|
omega: complex,
|
||||||
dxes: dx_lists_t,
|
dxes: dx_lists_t,
|
||||||
epsilon: vfield_t,
|
epsilon: vfdfield_t,
|
||||||
mu: vfield_t = None,
|
mu: vfdfield_t = None,
|
||||||
prop_phase: float = 0,
|
prop_phase: float = 0,
|
||||||
) -> Tuple[vfield_t, vfield_t]:
|
) -> Tuple[vfdfield_t, vfdfield_t]:
|
||||||
# TODO documentation
|
# TODO documentation
|
||||||
shape = [s.size for s in dxes[0]]
|
shape = [s.size for s in dxes[0]]
|
||||||
dxes_real = [[numpy.real(d) for d in numpy.meshgrid(*dxes[v], indexing='ij')] for v in (0, 1)]
|
dxes_real = [[numpy.real(d) for d in numpy.meshgrid(*dxes[v], indexing='ij')] for v in (0, 1)]
|
||||||
@ -276,8 +277,8 @@ def _normalized_fields(e: numpy.ndarray,
|
|||||||
def exy2h(wavenumber: complex,
|
def exy2h(wavenumber: complex,
|
||||||
omega: complex,
|
omega: complex,
|
||||||
dxes: dx_lists_t,
|
dxes: dx_lists_t,
|
||||||
epsilon: vfield_t,
|
epsilon: vfdfield_t,
|
||||||
mu: vfield_t = None
|
mu: vfdfield_t = None
|
||||||
) -> sparse.spmatrix:
|
) -> sparse.spmatrix:
|
||||||
"""
|
"""
|
||||||
Operator which transforms the vector `e_xy` containing the vectorized E_x and E_y fields,
|
Operator which transforms the vector `e_xy` containing the vectorized E_x and E_y fields,
|
||||||
@ -287,7 +288,7 @@ def exy2h(wavenumber: complex,
|
|||||||
wavenumber: Wavenumber assuming fields have z-dependence of `exp(-i * wavenumber * z)`.
|
wavenumber: Wavenumber assuming fields have z-dependence of `exp(-i * wavenumber * z)`.
|
||||||
It should satisfy `operator_e() @ e_xy == wavenumber**2 * e_xy`
|
It should satisfy `operator_e() @ e_xy == wavenumber**2 * e_xy`
|
||||||
omega: The angular frequency of the system
|
omega: The angular frequency of the system
|
||||||
dxes: Grid parameters `[dx_e, dx_h]` as described in `meanas.types` (2D)
|
dxes: Grid parameters `[dx_e, dx_h]` as described in `meanas.fdmath.types` (2D)
|
||||||
epsilon: Vectorized dielectric constant grid
|
epsilon: Vectorized dielectric constant grid
|
||||||
mu: Vectorized magnetic permeability grid (default 1 everywhere)
|
mu: Vectorized magnetic permeability grid (default 1 everywhere)
|
||||||
|
|
||||||
@ -301,8 +302,8 @@ def exy2h(wavenumber: complex,
|
|||||||
def hxy2e(wavenumber: complex,
|
def hxy2e(wavenumber: complex,
|
||||||
omega: complex,
|
omega: complex,
|
||||||
dxes: dx_lists_t,
|
dxes: dx_lists_t,
|
||||||
epsilon: vfield_t,
|
epsilon: vfdfield_t,
|
||||||
mu: vfield_t = None
|
mu: vfdfield_t = None
|
||||||
) -> sparse.spmatrix:
|
) -> sparse.spmatrix:
|
||||||
"""
|
"""
|
||||||
Operator which transforms the vector `h_xy` containing the vectorized H_x and H_y fields,
|
Operator which transforms the vector `h_xy` containing the vectorized H_x and H_y fields,
|
||||||
@ -312,7 +313,7 @@ def hxy2e(wavenumber: complex,
|
|||||||
wavenumber: Wavenumber assuming fields have z-dependence of `exp(-i * wavenumber * z)`.
|
wavenumber: Wavenumber assuming fields have z-dependence of `exp(-i * wavenumber * z)`.
|
||||||
It should satisfy `operator_h() @ h_xy == wavenumber**2 * h_xy`
|
It should satisfy `operator_h() @ h_xy == wavenumber**2 * h_xy`
|
||||||
omega: The angular frequency of the system
|
omega: The angular frequency of the system
|
||||||
dxes: Grid parameters `[dx_e, dx_h]` as described in `meanas.types` (2D)
|
dxes: Grid parameters `[dx_e, dx_h]` as described in `meanas.fdmath.types` (2D)
|
||||||
epsilon: Vectorized dielectric constant grid
|
epsilon: Vectorized dielectric constant grid
|
||||||
mu: Vectorized magnetic permeability grid (default 1 everywhere)
|
mu: Vectorized magnetic permeability grid (default 1 everywhere)
|
||||||
|
|
||||||
@ -325,7 +326,7 @@ def hxy2e(wavenumber: complex,
|
|||||||
|
|
||||||
def hxy2h(wavenumber: complex,
|
def hxy2h(wavenumber: complex,
|
||||||
dxes: dx_lists_t,
|
dxes: dx_lists_t,
|
||||||
mu: vfield_t = None
|
mu: vfdfield_t = None
|
||||||
) -> sparse.spmatrix:
|
) -> sparse.spmatrix:
|
||||||
"""
|
"""
|
||||||
Operator which transforms the vector `h_xy` containing the vectorized H_x and H_y fields,
|
Operator which transforms the vector `h_xy` containing the vectorized H_x and H_y fields,
|
||||||
@ -334,13 +335,13 @@ def hxy2h(wavenumber: complex,
|
|||||||
Args:
|
Args:
|
||||||
wavenumber: Wavenumber assuming fields have z-dependence of `exp(-i * wavenumber * z)`.
|
wavenumber: Wavenumber assuming fields have z-dependence of `exp(-i * wavenumber * z)`.
|
||||||
It should satisfy `operator_h() @ h_xy == wavenumber**2 * h_xy`
|
It should satisfy `operator_h() @ h_xy == wavenumber**2 * h_xy`
|
||||||
dxes: Grid parameters `[dx_e, dx_h]` as described in `meanas.types` (2D)
|
dxes: Grid parameters `[dx_e, dx_h]` as described in `meanas.fdmath.types` (2D)
|
||||||
mu: Vectorized magnetic permeability grid (default 1 everywhere)
|
mu: Vectorized magnetic permeability grid (default 1 everywhere)
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
Sparse matrix representing the operator.
|
Sparse matrix representing the operator.
|
||||||
"""
|
"""
|
||||||
Dfx, Dfy = operators.deriv_forward(dxes[0])
|
Dfx, Dfy = deriv_forward(dxes[0])
|
||||||
hxy2hz = sparse.hstack((Dfx, Dfy)) / (1j * wavenumber)
|
hxy2hz = sparse.hstack((Dfx, Dfy)) / (1j * wavenumber)
|
||||||
|
|
||||||
if not numpy.any(numpy.equal(mu, None)):
|
if not numpy.any(numpy.equal(mu, None)):
|
||||||
@ -358,7 +359,7 @@ def hxy2h(wavenumber: complex,
|
|||||||
|
|
||||||
def exy2e(wavenumber: complex,
|
def exy2e(wavenumber: complex,
|
||||||
dxes: dx_lists_t,
|
dxes: dx_lists_t,
|
||||||
epsilon: vfield_t,
|
epsilon: vfdfield_t,
|
||||||
) -> sparse.spmatrix:
|
) -> sparse.spmatrix:
|
||||||
"""
|
"""
|
||||||
Operator which transforms the vector `e_xy` containing the vectorized E_x and E_y fields,
|
Operator which transforms the vector `e_xy` containing the vectorized E_x and E_y fields,
|
||||||
@ -367,13 +368,13 @@ def exy2e(wavenumber: complex,
|
|||||||
Args:
|
Args:
|
||||||
wavenumber: Wavenumber assuming fields have z-dependence of `exp(-i * wavenumber * z)`
|
wavenumber: Wavenumber assuming fields have z-dependence of `exp(-i * wavenumber * z)`
|
||||||
It should satisfy `operator_e() @ e_xy == wavenumber**2 * e_xy`
|
It should satisfy `operator_e() @ e_xy == wavenumber**2 * e_xy`
|
||||||
dxes: Grid parameters `[dx_e, dx_h]` as described in `meanas.types` (2D)
|
dxes: Grid parameters `[dx_e, dx_h]` as described in `meanas.fdmath.types` (2D)
|
||||||
epsilon: Vectorized dielectric constant grid
|
epsilon: Vectorized dielectric constant grid
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
Sparse matrix representing the operator.
|
Sparse matrix representing the operator.
|
||||||
"""
|
"""
|
||||||
Dbx, Dby = operators.deriv_back(dxes[1])
|
Dbx, Dby = deriv_back(dxes[1])
|
||||||
exy2ez = sparse.hstack((Dbx, Dby)) / (1j * wavenumber)
|
exy2ez = sparse.hstack((Dbx, Dby)) / (1j * wavenumber)
|
||||||
|
|
||||||
if not numpy.any(numpy.equal(epsilon, None)):
|
if not numpy.any(numpy.equal(epsilon, None)):
|
||||||
@ -392,7 +393,7 @@ def exy2e(wavenumber: complex,
|
|||||||
def e2h(wavenumber: complex,
|
def e2h(wavenumber: complex,
|
||||||
omega: complex,
|
omega: complex,
|
||||||
dxes: dx_lists_t,
|
dxes: dx_lists_t,
|
||||||
mu: vfield_t = None
|
mu: vfdfield_t = None
|
||||||
) -> sparse.spmatrix:
|
) -> sparse.spmatrix:
|
||||||
"""
|
"""
|
||||||
Returns an operator which, when applied to a vectorized E eigenfield, produces
|
Returns an operator which, when applied to a vectorized E eigenfield, produces
|
||||||
@ -401,7 +402,7 @@ def e2h(wavenumber: complex,
|
|||||||
Args:
|
Args:
|
||||||
wavenumber: Wavenumber assuming fields have z-dependence of `exp(-i * wavenumber * z)`
|
wavenumber: Wavenumber assuming fields have z-dependence of `exp(-i * wavenumber * z)`
|
||||||
omega: The angular frequency of the system
|
omega: The angular frequency of the system
|
||||||
dxes: Grid parameters `[dx_e, dx_h]` as described in `meanas.types` (2D)
|
dxes: Grid parameters `[dx_e, dx_h]` as described in `meanas.fdmath.types` (2D)
|
||||||
mu: Vectorized magnetic permeability grid (default 1 everywhere)
|
mu: Vectorized magnetic permeability grid (default 1 everywhere)
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
@ -416,7 +417,7 @@ def e2h(wavenumber: complex,
|
|||||||
def h2e(wavenumber: complex,
|
def h2e(wavenumber: complex,
|
||||||
omega: complex,
|
omega: complex,
|
||||||
dxes: dx_lists_t,
|
dxes: dx_lists_t,
|
||||||
epsilon: vfield_t
|
epsilon: vfdfield_t
|
||||||
) -> sparse.spmatrix:
|
) -> sparse.spmatrix:
|
||||||
"""
|
"""
|
||||||
Returns an operator which, when applied to a vectorized H eigenfield, produces
|
Returns an operator which, when applied to a vectorized H eigenfield, produces
|
||||||
@ -425,7 +426,7 @@ def h2e(wavenumber: complex,
|
|||||||
Args:
|
Args:
|
||||||
wavenumber: Wavenumber assuming fields have z-dependence of `exp(-i * wavenumber * z)`
|
wavenumber: Wavenumber assuming fields have z-dependence of `exp(-i * wavenumber * z)`
|
||||||
omega: The angular frequency of the system
|
omega: The angular frequency of the system
|
||||||
dxes: Grid parameters `[dx_e, dx_h]` as described in `meanas.types` (2D)
|
dxes: Grid parameters `[dx_e, dx_h]` as described in `meanas.fdmath.types` (2D)
|
||||||
epsilon: Vectorized dielectric constant grid
|
epsilon: Vectorized dielectric constant grid
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
@ -441,7 +442,7 @@ def curl_e(wavenumber: complex, dxes: dx_lists_t) -> sparse.spmatrix:
|
|||||||
|
|
||||||
Args:
|
Args:
|
||||||
wavenumber: Wavenumber assuming fields have z-dependence of `exp(-i * wavenumber * z)`
|
wavenumber: Wavenumber assuming fields have z-dependence of `exp(-i * wavenumber * z)`
|
||||||
dxes: Grid parameters `[dx_e, dx_h]` as described in `meanas.types` (2D)
|
dxes: Grid parameters `[dx_e, dx_h]` as described in `meanas.fdmath.types` (2D)
|
||||||
|
|
||||||
Return:
|
Return:
|
||||||
Sparse matrix representation of the operator.
|
Sparse matrix representation of the operator.
|
||||||
@ -450,9 +451,10 @@ def curl_e(wavenumber: complex, dxes: dx_lists_t) -> sparse.spmatrix:
|
|||||||
for d in dxes[0]:
|
for d in dxes[0]:
|
||||||
n *= len(d)
|
n *= len(d)
|
||||||
|
|
||||||
|
print(wavenumber, n)
|
||||||
Bz = -1j * wavenumber * sparse.eye(n)
|
Bz = -1j * wavenumber * sparse.eye(n)
|
||||||
Dfx, Dfy = operators.deriv_forward(dxes[0])
|
Dfx, Dfy = deriv_forward(dxes[0])
|
||||||
return operators.cross([Dfx, Dfy, Bz])
|
return cross([Dfx, Dfy, Bz])
|
||||||
|
|
||||||
|
|
||||||
def curl_h(wavenumber: complex, dxes: dx_lists_t) -> sparse.spmatrix:
|
def curl_h(wavenumber: complex, dxes: dx_lists_t) -> sparse.spmatrix:
|
||||||
@ -461,7 +463,7 @@ def curl_h(wavenumber: complex, dxes: dx_lists_t) -> sparse.spmatrix:
|
|||||||
|
|
||||||
Args:
|
Args:
|
||||||
wavenumber: Wavenumber assuming fields have z-dependence of `exp(-i * wavenumber * z)`
|
wavenumber: Wavenumber assuming fields have z-dependence of `exp(-i * wavenumber * z)`
|
||||||
dxes: Grid parameters `[dx_e, dx_h]` as described in `meanas.types` (2D)
|
dxes: Grid parameters `[dx_e, dx_h]` as described in `meanas.fdmath.types` (2D)
|
||||||
|
|
||||||
Return:
|
Return:
|
||||||
Sparse matrix representation of the operator.
|
Sparse matrix representation of the operator.
|
||||||
@ -471,16 +473,16 @@ def curl_h(wavenumber: complex, dxes: dx_lists_t) -> sparse.spmatrix:
|
|||||||
n *= len(d)
|
n *= len(d)
|
||||||
|
|
||||||
Bz = -1j * wavenumber * sparse.eye(n)
|
Bz = -1j * wavenumber * sparse.eye(n)
|
||||||
Dbx, Dby = operators.deriv_back(dxes[1])
|
Dbx, Dby = deriv_back(dxes[1])
|
||||||
return operators.cross([Dbx, Dby, Bz])
|
return cross([Dbx, Dby, Bz])
|
||||||
|
|
||||||
|
|
||||||
def h_err(h: vfield_t,
|
def h_err(h: vfdfield_t,
|
||||||
wavenumber: complex,
|
wavenumber: complex,
|
||||||
omega: complex,
|
omega: complex,
|
||||||
dxes: dx_lists_t,
|
dxes: dx_lists_t,
|
||||||
epsilon: vfield_t,
|
epsilon: vfdfield_t,
|
||||||
mu: vfield_t = None
|
mu: vfdfield_t = None
|
||||||
) -> float:
|
) -> float:
|
||||||
"""
|
"""
|
||||||
Calculates the relative error in the H field
|
Calculates the relative error in the H field
|
||||||
@ -489,7 +491,7 @@ def h_err(h: vfield_t,
|
|||||||
h: Vectorized H field
|
h: Vectorized H field
|
||||||
wavenumber: Wavenumber assuming fields have z-dependence of `exp(-i * wavenumber * z)`
|
wavenumber: Wavenumber assuming fields have z-dependence of `exp(-i * wavenumber * z)`
|
||||||
omega: The angular frequency of the system
|
omega: The angular frequency of the system
|
||||||
dxes: Grid parameters `[dx_e, dx_h]` as described in `meanas.types` (2D)
|
dxes: Grid parameters `[dx_e, dx_h]` as described in `meanas.fdmath.types` (2D)
|
||||||
epsilon: Vectorized dielectric constant grid
|
epsilon: Vectorized dielectric constant grid
|
||||||
mu: Vectorized magnetic permeability grid (default 1 everywhere)
|
mu: Vectorized magnetic permeability grid (default 1 everywhere)
|
||||||
|
|
||||||
@ -509,12 +511,12 @@ def h_err(h: vfield_t,
|
|||||||
return norm(op) / norm(h)
|
return norm(op) / norm(h)
|
||||||
|
|
||||||
|
|
||||||
def e_err(e: vfield_t,
|
def e_err(e: vfdfield_t,
|
||||||
wavenumber: complex,
|
wavenumber: complex,
|
||||||
omega: complex,
|
omega: complex,
|
||||||
dxes: dx_lists_t,
|
dxes: dx_lists_t,
|
||||||
epsilon: vfield_t,
|
epsilon: vfdfield_t,
|
||||||
mu: vfield_t = None
|
mu: vfdfield_t = None
|
||||||
) -> float:
|
) -> float:
|
||||||
"""
|
"""
|
||||||
Calculates the relative error in the E field
|
Calculates the relative error in the E field
|
||||||
@ -523,7 +525,7 @@ def e_err(e: vfield_t,
|
|||||||
e: Vectorized E field
|
e: Vectorized E field
|
||||||
wavenumber: Wavenumber assuming fields have z-dependence of `exp(-i * wavenumber * z)`
|
wavenumber: Wavenumber assuming fields have z-dependence of `exp(-i * wavenumber * z)`
|
||||||
omega: The angular frequency of the system
|
omega: The angular frequency of the system
|
||||||
dxes: Grid parameters `[dx_e, dx_h]` as described in `meanas.types` (2D)
|
dxes: Grid parameters `[dx_e, dx_h]` as described in `meanas.fdmath.types` (2D)
|
||||||
epsilon: Vectorized dielectric constant grid
|
epsilon: Vectorized dielectric constant grid
|
||||||
mu: Vectorized magnetic permeability grid (default 1 everywhere)
|
mu: Vectorized magnetic permeability grid (default 1 everywhere)
|
||||||
|
|
||||||
@ -545,17 +547,17 @@ def e_err(e: vfield_t,
|
|||||||
def solve_modes(mode_numbers: List[int],
|
def solve_modes(mode_numbers: List[int],
|
||||||
omega: complex,
|
omega: complex,
|
||||||
dxes: dx_lists_t,
|
dxes: dx_lists_t,
|
||||||
epsilon: vfield_t,
|
epsilon: vfdfield_t,
|
||||||
mu: vfield_t = None,
|
mu: vfdfield_t = None,
|
||||||
mode_margin: int = 2,
|
mode_margin: int = 2,
|
||||||
) -> Tuple[List[vfield_t], List[complex]]:
|
) -> Tuple[List[vfdfield_t], List[complex]]:
|
||||||
"""
|
"""
|
||||||
Given a 2D region, attempts to solve for the eigenmode with the specified mode numbers.
|
Given a 2D region, attempts to solve for the eigenmode with the specified mode numbers.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
mode_numbers: List of 0-indexed mode numbers to solve for
|
mode_numbers: List of 0-indexed mode numbers to solve for
|
||||||
omega: Angular frequency of the simulation
|
omega: Angular frequency of the simulation
|
||||||
dxes: Grid parameters `[dx_e, dx_h]` as described in `meanas.types`
|
dxes: Grid parameters `[dx_e, dx_h]` as described in `meanas.fdmath.types`
|
||||||
epsilon: Dielectric constant
|
epsilon: Dielectric constant
|
||||||
mu: Magnetic permeability (default 1 everywhere)
|
mu: Magnetic permeability (default 1 everywhere)
|
||||||
mode_margin: The eigensolver will actually solve for `(max(mode_number) + mode_margin)`
|
mode_margin: The eigensolver will actually solve for `(max(mode_number) + mode_margin)`
|
||||||
@ -570,17 +572,18 @@ def solve_modes(mode_numbers: List[int],
|
|||||||
Solve for the largest-magnitude eigenvalue of the real operator
|
Solve for the largest-magnitude eigenvalue of the real operator
|
||||||
'''
|
'''
|
||||||
dxes_real = [[numpy.real(dx) for dx in dxi] for dxi in dxes]
|
dxes_real = [[numpy.real(dx) for dx in dxi] for dxi in dxes]
|
||||||
A_r = waveguide.operator_e(numpy.real(omega), dxes_real, numpy.real(epsilon), numpy.real(mu))
|
A_r = operator_e(numpy.real(omega), dxes_real, numpy.real(epsilon), numpy.real(mu))
|
||||||
|
|
||||||
eigvals, eigvecs = signed_eigensolve(A_r, max(mode_number) + mode_margin)
|
eigvals, eigvecs = signed_eigensolve(A_r, max(mode_numbers) + mode_margin)
|
||||||
e_xys = eigvecs[:, -(numpy.array(mode_number) + 1)]
|
e_xys = eigvecs[:, -(numpy.array(mode_numbers) + 1)]
|
||||||
|
|
||||||
'''
|
'''
|
||||||
Now solve for the eigenvector of the full operator, using the real operator's
|
Now solve for the eigenvector of the full operator, using the real operator's
|
||||||
eigenvector as an initial guess for Rayleigh quotient iteration.
|
eigenvector as an initial guess for Rayleigh quotient iteration.
|
||||||
'''
|
'''
|
||||||
A = waveguide.operator_e(omega, dxes, epsilon, mu)
|
A = operator_e(omega, dxes, epsilon, mu)
|
||||||
eigvals, e_xys = rayleigh_quotient_iteration(A, e_xys)
|
for nn in range(len(mode_numbers)):
|
||||||
|
eigvals[nn], e_xys[:, nn] = rayleigh_quotient_iteration(A, e_xys[:, nn])
|
||||||
|
|
||||||
# Calculate the wave-vector (force the real part to be positive)
|
# Calculate the wave-vector (force the real part to be positive)
|
||||||
wavenumbers = numpy.sqrt(eigvals)
|
wavenumbers = numpy.sqrt(eigvals)
|
||||||
@ -592,7 +595,7 @@ def solve_modes(mode_numbers: List[int],
|
|||||||
def solve_mode(mode_number: int,
|
def solve_mode(mode_number: int,
|
||||||
*args,
|
*args,
|
||||||
**kwargs
|
**kwargs
|
||||||
) -> Tuple[vfield_t, complex]:
|
) -> Tuple[vfdfield_t, complex]:
|
||||||
"""
|
"""
|
||||||
Wrapper around `solve_modes()` that solves for a single mode.
|
Wrapper around `solve_modes()` that solves for a single mode.
|
||||||
|
|
||||||
@ -604,4 +607,5 @@ def solve_mode(mode_number: int,
|
|||||||
Returns:
|
Returns:
|
||||||
(e_xy, wavenumber)
|
(e_xy, wavenumber)
|
||||||
"""
|
"""
|
||||||
return solve_modes(mode_numbers=[mode_number], *args, **kwargs)
|
e_xys, wavenumbers = solve_modes(mode_numbers=[mode_number], *args, **kwargs)
|
||||||
|
return e_xys[:, 0], wavenumbers[0]
|
||||||
|
@ -8,7 +8,7 @@ from typing import Dict, List, Tuple
|
|||||||
import numpy
|
import numpy
|
||||||
import scipy.sparse as sparse
|
import scipy.sparse as sparse
|
||||||
|
|
||||||
from .. import vec, unvec, dx_lists_t, vfield_t, field_t
|
from ..fdmath import vec, unvec, dx_lists_t, vfdfield_t, fdfield_t
|
||||||
from . import operators, waveguide_2d, functional
|
from . import operators, waveguide_2d, functional
|
||||||
|
|
||||||
|
|
||||||
@ -18,8 +18,8 @@ def solve_mode(mode_number: int,
|
|||||||
axis: int,
|
axis: int,
|
||||||
polarity: int,
|
polarity: int,
|
||||||
slices: List[slice],
|
slices: List[slice],
|
||||||
epsilon: field_t,
|
epsilon: fdfield_t,
|
||||||
mu: field_t = None,
|
mu: fdfield_t = None,
|
||||||
) -> Dict[str, complex or numpy.ndarray]:
|
) -> Dict[str, complex or numpy.ndarray]:
|
||||||
"""
|
"""
|
||||||
Given a 3D grid, selects a slice from the grid and attempts to
|
Given a 3D grid, selects a slice from the grid and attempts to
|
||||||
@ -28,7 +28,7 @@ def solve_mode(mode_number: int,
|
|||||||
Args:
|
Args:
|
||||||
mode_number: Number of the mode, 0-indexed
|
mode_number: Number of the mode, 0-indexed
|
||||||
omega: Angular frequency of the simulation
|
omega: Angular frequency of the simulation
|
||||||
dxes: Grid parameters `[dx_e, dx_h]` as described in `meanas.types`
|
dxes: Grid parameters `[dx_e, dx_h]` as described in `meanas.fdmath.types`
|
||||||
axis: Propagation axis (0=x, 1=y, 2=z)
|
axis: Propagation axis (0=x, 1=y, 2=z)
|
||||||
polarity: Propagation direction (+1 for +ve, -1 for -ve)
|
polarity: Propagation direction (+1 for +ve, -1 for -ve)
|
||||||
slices: `epsilon[tuple(slices)]` is used to select the portion of the grid to use
|
slices: `epsilon[tuple(slices)]` is used to select the portion of the grid to use
|
||||||
@ -71,7 +71,7 @@ def solve_mode(mode_number: int,
|
|||||||
wavenumber = 2/dx_prop * numpy.arcsin(wavenumber_2d * dx_prop/2)
|
wavenumber = 2/dx_prop * numpy.arcsin(wavenumber_2d * dx_prop/2)
|
||||||
|
|
||||||
shape = [d.size for d in args_2d['dxes'][0]]
|
shape = [d.size for d in args_2d['dxes'][0]]
|
||||||
ve, vh = waveguide.normalized_fields_e(e_xy, wavenumber=wavenumber_2d, **args_2d, prop_phase=dx_prop * wavenumber)
|
ve, vh = waveguide_2d.normalized_fields_e(e_xy, wavenumber=wavenumber_2d, **args_2d, prop_phase=dx_prop * wavenumber)
|
||||||
e = unvec(ve, shape)
|
e = unvec(ve, shape)
|
||||||
h = unvec(vh, shape)
|
h = unvec(vh, shape)
|
||||||
|
|
||||||
@ -98,16 +98,16 @@ def solve_mode(mode_number: int,
|
|||||||
return results
|
return results
|
||||||
|
|
||||||
|
|
||||||
def compute_source(E: field_t,
|
def compute_source(E: fdfield_t,
|
||||||
wavenumber: complex,
|
wavenumber: complex,
|
||||||
omega: complex,
|
omega: complex,
|
||||||
dxes: dx_lists_t,
|
dxes: dx_lists_t,
|
||||||
axis: int,
|
axis: int,
|
||||||
polarity: int,
|
polarity: int,
|
||||||
slices: List[slice],
|
slices: List[slice],
|
||||||
epsilon: field_t,
|
epsilon: fdfield_t,
|
||||||
mu: field_t = None,
|
mu: fdfield_t = None,
|
||||||
) -> field_t:
|
) -> fdfield_t:
|
||||||
"""
|
"""
|
||||||
Given an eigenmode obtained by `solve_mode`, returns the current source distribution
|
Given an eigenmode obtained by `solve_mode`, returns the current source distribution
|
||||||
necessary to position a unidirectional source at the slice location.
|
necessary to position a unidirectional source at the slice location.
|
||||||
@ -116,7 +116,7 @@ def compute_source(E: field_t,
|
|||||||
E: E-field of the mode
|
E: E-field of the mode
|
||||||
wavenumber: Wavenumber of the mode
|
wavenumber: Wavenumber of the mode
|
||||||
omega: Angular frequency of the simulation
|
omega: Angular frequency of the simulation
|
||||||
dxes: Grid parameters `[dx_e, dx_h]` as described in `meanas.types`
|
dxes: Grid parameters `[dx_e, dx_h]` as described in `meanas.fdmath.types`
|
||||||
axis: Propagation axis (0=x, 1=y, 2=z)
|
axis: Propagation axis (0=x, 1=y, 2=z)
|
||||||
polarity: Propagation direction (+1 for +ve, -1 for -ve)
|
polarity: Propagation direction (+1 for +ve, -1 for -ve)
|
||||||
slices: `epsilon[tuple(slices)]` is used to select the portion of the grid to use
|
slices: `epsilon[tuple(slices)]` is used to select the portion of the grid to use
|
||||||
@ -143,13 +143,13 @@ def compute_source(E: field_t,
|
|||||||
return J
|
return J
|
||||||
|
|
||||||
|
|
||||||
def compute_overlap_e(E: field_t,
|
def compute_overlap_e(E: fdfield_t,
|
||||||
wavenumber: complex,
|
wavenumber: complex,
|
||||||
dxes: dx_lists_t,
|
dxes: dx_lists_t,
|
||||||
axis: int,
|
axis: int,
|
||||||
polarity: int,
|
polarity: int,
|
||||||
slices: List[slice],
|
slices: List[slice],
|
||||||
) -> field_t: # TODO DOCS
|
) -> fdfield_t: # TODO DOCS
|
||||||
"""
|
"""
|
||||||
Given an eigenmode obtained by `solve_mode`, calculates an overlap_e for the
|
Given an eigenmode obtained by `solve_mode`, calculates an overlap_e for the
|
||||||
mode orthogonality relation Integrate(((E x H_mode) + (E_mode x H)) dot dn)
|
mode orthogonality relation Integrate(((E x H_mode) + (E_mode x H)) dot dn)
|
||||||
@ -160,7 +160,7 @@ def compute_overlap_e(E: field_t,
|
|||||||
H: H-field of the mode (advanced by half of a Yee cell from E)
|
H: H-field of the mode (advanced by half of a Yee cell from E)
|
||||||
wavenumber: Wavenumber of the mode
|
wavenumber: Wavenumber of the mode
|
||||||
omega: Angular frequency of the simulation
|
omega: Angular frequency of the simulation
|
||||||
dxes: Grid parameters `[dx_e, dx_h]` as described in `meanas.types`
|
dxes: Grid parameters `[dx_e, dx_h]` as described in `meanas.fdmath.types`
|
||||||
axis: Propagation axis (0=x, 1=y, 2=z)
|
axis: Propagation axis (0=x, 1=y, 2=z)
|
||||||
polarity: Propagation direction (+1 for +ve, -1 for -ve)
|
polarity: Propagation direction (+1 for +ve, -1 for -ve)
|
||||||
slices: `epsilon[tuple(slices)]` is used to select the portion of the grid to use
|
slices: `epsilon[tuple(slices)]` is used to select the portion of the grid to use
|
||||||
@ -188,13 +188,13 @@ def compute_overlap_e(E: field_t,
|
|||||||
return Etgt
|
return Etgt
|
||||||
|
|
||||||
|
|
||||||
def expand_e(E: field_t,
|
def expand_e(E: fdfield_t,
|
||||||
wavenumber: complex,
|
wavenumber: complex,
|
||||||
dxes: dx_lists_t,
|
dxes: dx_lists_t,
|
||||||
axis: int,
|
axis: int,
|
||||||
polarity: int,
|
polarity: int,
|
||||||
slices: List[slice],
|
slices: List[slice],
|
||||||
) -> field_t:
|
) -> fdfield_t:
|
||||||
"""
|
"""
|
||||||
Given an eigenmode obtained by `solve_mode`, expands the E-field from the 2D
|
Given an eigenmode obtained by `solve_mode`, expands the E-field from the 2D
|
||||||
slice where the mode was calculated to the entire domain (along the propagation
|
slice where the mode was calculated to the entire domain (along the propagation
|
||||||
@ -205,7 +205,7 @@ def expand_e(E: field_t,
|
|||||||
Args:
|
Args:
|
||||||
E: E-field of the mode
|
E: E-field of the mode
|
||||||
wavenumber: Wavenumber of the mode
|
wavenumber: Wavenumber of the mode
|
||||||
dxes: Grid parameters `[dx_e, dx_h]` as described in `meanas.types`
|
dxes: Grid parameters `[dx_e, dx_h]` as described in `meanas.fdmath.types`
|
||||||
axis: Propagation axis (0=x, 1=y, 2=z)
|
axis: Propagation axis (0=x, 1=y, 2=z)
|
||||||
polarity: Propagation direction (+1 for +ve, -1 for -ve)
|
polarity: Propagation direction (+1 for +ve, -1 for -ve)
|
||||||
slices: `epsilon[tuple(slices)]` is used to select the portion of the grid to use
|
slices: `epsilon[tuple(slices)]` is used to select the portion of the grid to use
|
||||||
|
@ -13,7 +13,7 @@ import numpy
|
|||||||
from numpy.linalg import norm
|
from numpy.linalg import norm
|
||||||
import scipy.sparse as sparse
|
import scipy.sparse as sparse
|
||||||
|
|
||||||
from .. import vec, unvec, dx_lists_t, field_t, vfield_t
|
from ..fdmath import vec, unvec, dx_lists_t, fdfield_t, vfdfield_t
|
||||||
from ..eigensolvers import signed_eigensolve, rayleigh_quotient_iteration
|
from ..eigensolvers import signed_eigensolve, rayleigh_quotient_iteration
|
||||||
from . import operators
|
from . import operators
|
||||||
|
|
||||||
@ -23,7 +23,7 @@ __author__ = 'Jan Petykiewicz'
|
|||||||
|
|
||||||
def cylindrical_operator(omega: complex,
|
def cylindrical_operator(omega: complex,
|
||||||
dxes: dx_lists_t,
|
dxes: dx_lists_t,
|
||||||
epsilon: vfield_t,
|
epsilon: vfdfield_t,
|
||||||
r0: float,
|
r0: float,
|
||||||
) -> sparse.spmatrix:
|
) -> sparse.spmatrix:
|
||||||
"""
|
"""
|
||||||
@ -41,7 +41,7 @@ def cylindrical_operator(omega: complex,
|
|||||||
|
|
||||||
Args:
|
Args:
|
||||||
omega: The angular frequency of the system
|
omega: The angular frequency of the system
|
||||||
dxes: Grid parameters `[dx_e, dx_h]` as described in `meanas.types` (2D)
|
dxes: Grid parameters `[dx_e, dx_h]` as described in `meanas.fdmath.types` (2D)
|
||||||
epsilon: Vectorized dielectric constant grid
|
epsilon: Vectorized dielectric constant grid
|
||||||
r0: Radius of curvature for the simulation. This should be the minimum value of
|
r0: Radius of curvature for the simulation. This should be the minimum value of
|
||||||
r within the simulation domain.
|
r within the simulation domain.
|
||||||
@ -83,9 +83,9 @@ def cylindrical_operator(omega: complex,
|
|||||||
def solve_mode(mode_number: int,
|
def solve_mode(mode_number: int,
|
||||||
omega: complex,
|
omega: complex,
|
||||||
dxes: dx_lists_t,
|
dxes: dx_lists_t,
|
||||||
epsilon: vfield_t,
|
epsilon: vfdfield_t,
|
||||||
r0: float,
|
r0: float,
|
||||||
) -> Dict[str, complex or field_t]:
|
) -> Dict[str, complex or fdfield_t]:
|
||||||
"""
|
"""
|
||||||
TODO: fixup
|
TODO: fixup
|
||||||
Given a 2d (r, y) slice of epsilon, attempts to solve for the eigenmode
|
Given a 2d (r, y) slice of epsilon, attempts to solve for the eigenmode
|
||||||
@ -94,7 +94,7 @@ def solve_mode(mode_number: int,
|
|||||||
Args:
|
Args:
|
||||||
mode_number: Number of the mode, 0-indexed
|
mode_number: Number of the mode, 0-indexed
|
||||||
omega: Angular frequency of the simulation
|
omega: Angular frequency of the simulation
|
||||||
dxes: Grid parameters [dx_e, dx_h] as described in meanas.types.
|
dxes: Grid parameters [dx_e, dx_h] as described in meanas.fdmath.types.
|
||||||
The first coordinate is assumed to be r, the second is y.
|
The first coordinate is assumed to be r, the second is y.
|
||||||
epsilon: Dielectric constant
|
epsilon: Dielectric constant
|
||||||
r0: Radius of curvature for the simulation. This should be the minimum value of
|
r0: Radius of curvature for the simulation. This should be the minimum value of
|
||||||
|
@ -91,4 +91,12 @@ and
|
|||||||
while \\( \\hat{h} \\) and \\( \\tilde{h} \\) are located at \\((m \pm \\frac{1}{2}, n \\pm \\frac{1}{2}, p \\pm \\frac{1}{2})\\)
|
while \\( \\hat{h} \\) and \\( \\tilde{h} \\) are located at \\((m \pm \\frac{1}{2}, n \\pm \\frac{1}{2}, p \\pm \\frac{1}{2})\\)
|
||||||
with components at \\((m, n \\pm \\frac{1}{2}, p \\pm \\frac{1}{2})\\) etc.
|
with components at \\((m, n \\pm \\frac{1}{2}, p \\pm \\frac{1}{2})\\) etc.
|
||||||
|
|
||||||
|
TODO: Explain fdfield_t vs vfdfield_t / operators vs functional
|
||||||
|
TODO: explain dxes
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
from .types import fdfield_t, vfdfield_t, dx_lists_t, fdfield_updater_t
|
||||||
|
from .vectorization import vec, unvec
|
||||||
|
from . import operators, functional, types, vectorization
|
||||||
|
|
||||||
|
@ -6,10 +6,10 @@ Basic discrete calculus etc.
|
|||||||
from typing import List, Callable, Tuple, Dict
|
from typing import List, Callable, Tuple, Dict
|
||||||
import numpy
|
import numpy
|
||||||
|
|
||||||
from .. import field_t, field_updater
|
from .types import fdfield_t, fdfield_updater_t
|
||||||
|
|
||||||
|
|
||||||
def deriv_forward(dx_e: List[numpy.ndarray] = None) -> field_updater:
|
def deriv_forward(dx_e: List[numpy.ndarray] = None) -> fdfield_updater_t:
|
||||||
"""
|
"""
|
||||||
Utility operators for taking discretized derivatives (backward variant).
|
Utility operators for taking discretized derivatives (backward variant).
|
||||||
|
|
||||||
@ -31,7 +31,7 @@ def deriv_forward(dx_e: List[numpy.ndarray] = None) -> field_updater:
|
|||||||
return derivs
|
return derivs
|
||||||
|
|
||||||
|
|
||||||
def deriv_back(dx_h: List[numpy.ndarray] = None) -> field_updater:
|
def deriv_back(dx_h: List[numpy.ndarray] = None) -> fdfield_updater_t:
|
||||||
"""
|
"""
|
||||||
Utility operators for taking discretized derivatives (forward variant).
|
Utility operators for taking discretized derivatives (forward variant).
|
||||||
|
|
||||||
@ -53,7 +53,7 @@ def deriv_back(dx_h: List[numpy.ndarray] = None) -> field_updater:
|
|||||||
return derivs
|
return derivs
|
||||||
|
|
||||||
|
|
||||||
def curl_forward(dx_e: List[numpy.ndarray] = None) -> field_updater:
|
def curl_forward(dx_e: List[numpy.ndarray] = None) -> fdfield_updater_t:
|
||||||
"""
|
"""
|
||||||
Curl operator for use with the E field.
|
Curl operator for use with the E field.
|
||||||
|
|
||||||
@ -67,7 +67,7 @@ def curl_forward(dx_e: List[numpy.ndarray] = None) -> field_updater:
|
|||||||
"""
|
"""
|
||||||
Dx, Dy, Dz = deriv_forward(dx_e)
|
Dx, Dy, Dz = deriv_forward(dx_e)
|
||||||
|
|
||||||
def ce_fun(e: field_t) -> field_t:
|
def ce_fun(e: fdfield_t) -> fdfield_t:
|
||||||
output = numpy.empty_like(e)
|
output = numpy.empty_like(e)
|
||||||
output[0] = Dy(e[2])
|
output[0] = Dy(e[2])
|
||||||
output[1] = Dz(e[0])
|
output[1] = Dz(e[0])
|
||||||
@ -80,7 +80,7 @@ def curl_forward(dx_e: List[numpy.ndarray] = None) -> field_updater:
|
|||||||
return ce_fun
|
return ce_fun
|
||||||
|
|
||||||
|
|
||||||
def curl_back(dx_h: List[numpy.ndarray] = None) -> field_updater:
|
def curl_back(dx_h: List[numpy.ndarray] = None) -> fdfield_updater_t:
|
||||||
"""
|
"""
|
||||||
Create a function which takes the backward curl of a field.
|
Create a function which takes the backward curl of a field.
|
||||||
|
|
||||||
@ -94,7 +94,7 @@ def curl_back(dx_h: List[numpy.ndarray] = None) -> field_updater:
|
|||||||
"""
|
"""
|
||||||
Dx, Dy, Dz = deriv_back(dx_h)
|
Dx, Dy, Dz = deriv_back(dx_h)
|
||||||
|
|
||||||
def ch_fun(h: field_t) -> field_t:
|
def ch_fun(h: fdfield_t) -> fdfield_t:
|
||||||
output = numpy.empty_like(h)
|
output = numpy.empty_like(h)
|
||||||
output[0] = Dy(h[2])
|
output[0] = Dy(h[2])
|
||||||
output[1] = Dz(h[0])
|
output[1] = Dz(h[0])
|
||||||
|
@ -7,7 +7,7 @@ from typing import List, Callable, Tuple, Dict
|
|||||||
import numpy
|
import numpy
|
||||||
import scipy.sparse as sparse
|
import scipy.sparse as sparse
|
||||||
|
|
||||||
from .. import field_t, vfield_t
|
from .types import fdfield_t, vfdfield_t
|
||||||
|
|
||||||
|
|
||||||
def rotation(axis: int, shape: List[int], shift_distance: int=1) -> sparse.spmatrix:
|
def rotation(axis: int, shape: List[int], shift_distance: int=1) -> sparse.spmatrix:
|
||||||
@ -155,7 +155,7 @@ def cross(B: List[sparse.spmatrix]) -> sparse.spmatrix:
|
|||||||
[-B[1], B[0], zero]])
|
[-B[1], B[0], zero]])
|
||||||
|
|
||||||
|
|
||||||
def vec_cross(b: vfield_t) -> sparse.spmatrix:
|
def vec_cross(b: vfdfield_t) -> sparse.spmatrix:
|
||||||
"""
|
"""
|
||||||
Vector cross product operator
|
Vector cross product operator
|
||||||
|
|
||||||
|
@ -8,7 +8,7 @@ from typing import List, Callable
|
|||||||
# Field types
|
# Field types
|
||||||
# TODO: figure out a better way to set the docstrings without creating actual subclasses?
|
# TODO: figure out a better way to set the docstrings without creating actual subclasses?
|
||||||
# Probably not a big issue since they're only used for type hinting
|
# Probably not a big issue since they're only used for type hinting
|
||||||
class field_t(numpy.ndarray):
|
class fdfield_t(numpy.ndarray):
|
||||||
"""
|
"""
|
||||||
Vector field with shape (3, X, Y, Z) (e.g. `[E_x, E_y, E_z]`)
|
Vector field with shape (3, X, Y, Z) (e.g. `[E_x, E_y, E_z]`)
|
||||||
|
|
||||||
@ -16,7 +16,7 @@ class field_t(numpy.ndarray):
|
|||||||
"""
|
"""
|
||||||
pass
|
pass
|
||||||
|
|
||||||
class vfield_t(numpy.ndarray):
|
class vfdfield_t(numpy.ndarray):
|
||||||
"""
|
"""
|
||||||
Linearized vector field (single vector of length 3*X*Y*Z)
|
Linearized vector field (single vector of length 3*X*Y*Z)
|
||||||
|
|
||||||
@ -37,4 +37,4 @@ dx_lists_t = List[List[numpy.ndarray]]
|
|||||||
'''
|
'''
|
||||||
|
|
||||||
|
|
||||||
field_updater = Callable[[field_t], field_t]
|
fdfield_updater_t = Callable[[fdfield_t], fdfield_t]
|
@ -7,13 +7,13 @@ Vectorized versions of the field use row-major (ie., C-style) ordering.
|
|||||||
from typing import List
|
from typing import List
|
||||||
import numpy
|
import numpy
|
||||||
|
|
||||||
from .types import field_t, vfield_t
|
from .types import fdfield_t, vfdfield_t
|
||||||
|
|
||||||
|
|
||||||
__author__ = 'Jan Petykiewicz'
|
__author__ = 'Jan Petykiewicz'
|
||||||
|
|
||||||
|
|
||||||
def vec(f: field_t) -> vfield_t:
|
def vec(f: fdfield_t) -> vfdfield_t:
|
||||||
"""
|
"""
|
||||||
Create a 1D ndarray from a 3D vector field which spans a 1-3D region.
|
Create a 1D ndarray from a 3D vector field which spans a 1-3D region.
|
||||||
|
|
||||||
@ -28,7 +28,7 @@ def vec(f: field_t) -> vfield_t:
|
|||||||
return numpy.ravel(f, order='C')
|
return numpy.ravel(f, order='C')
|
||||||
|
|
||||||
|
|
||||||
def unvec(v: vfield_t, shape: numpy.ndarray) -> field_t:
|
def unvec(v: vfdfield_t, shape: numpy.ndarray) -> fdfield_t:
|
||||||
"""
|
"""
|
||||||
Perform the inverse of vec(): take a 1D ndarray and output a 3D field
|
Perform the inverse of vec(): take a 1D ndarray and output a 3D field
|
||||||
of form [f_x, f_y, f_z] where each of f_* is a len(shape)-dimensional
|
of form [f_x, f_y, f_z] where each of f_* is a len(shape)-dimensional
|
@ -4,27 +4,33 @@ Basic FDTD field updates
|
|||||||
from typing import List, Callable, Tuple, Dict
|
from typing import List, Callable, Tuple, Dict
|
||||||
import numpy
|
import numpy
|
||||||
|
|
||||||
from .. import dx_lists_t, field_t, field_updater
|
from ..fdmath import dx_lists_t, fdfield_t, fdfield_updater_t
|
||||||
from ..fdmath.functional import curl_forward, curl_back
|
from ..fdmath.functional import curl_forward, curl_back
|
||||||
|
|
||||||
|
|
||||||
__author__ = 'Jan Petykiewicz'
|
__author__ = 'Jan Petykiewicz'
|
||||||
|
|
||||||
|
|
||||||
def maxwell_e(dt: float, dxes: dx_lists_t = None) -> field_updater:
|
def maxwell_e(dt: float, dxes: dx_lists_t = None) -> fdfield_updater_t:
|
||||||
|
if dxes is not None:
|
||||||
curl_h_fun = curl_back(dxes[1])
|
curl_h_fun = curl_back(dxes[1])
|
||||||
|
else:
|
||||||
|
curl_h_fun = curl_back()
|
||||||
|
|
||||||
def me_fun(e: field_t, h: field_t, epsilon: field_t):
|
def me_fun(e: fdfield_t, h: fdfield_t, epsilon: fdfield_t):
|
||||||
e += dt * curl_h_fun(h) / epsilon
|
e += dt * curl_h_fun(h) / epsilon
|
||||||
return e
|
return e
|
||||||
|
|
||||||
return me_fun
|
return me_fun
|
||||||
|
|
||||||
|
|
||||||
def maxwell_h(dt: float, dxes: dx_lists_t = None) -> field_updater:
|
def maxwell_h(dt: float, dxes: dx_lists_t = None) -> fdfield_updater_t:
|
||||||
|
if dxes is not None:
|
||||||
curl_e_fun = curl_forward(dxes[0])
|
curl_e_fun = curl_forward(dxes[0])
|
||||||
|
else:
|
||||||
|
curl_e_fun = curl_forward()
|
||||||
|
|
||||||
def mh_fun(e: field_t, h: field_t):
|
def mh_fun(e: fdfield_t, h: fdfield_t):
|
||||||
h -= dt * curl_e_fun(e)
|
h -= dt * curl_e_fun(e)
|
||||||
return h
|
return h
|
||||||
|
|
||||||
|
@ -5,12 +5,12 @@ Boundary conditions
|
|||||||
from typing import List, Callable, Tuple, Dict
|
from typing import List, Callable, Tuple, Dict
|
||||||
import numpy
|
import numpy
|
||||||
|
|
||||||
from .. import dx_lists_t, field_t, field_updater
|
from ..fdmath import dx_lists_t, fdfield_t, fdfield_updater_t
|
||||||
|
|
||||||
|
|
||||||
def conducting_boundary(direction: int,
|
def conducting_boundary(direction: int,
|
||||||
polarity: int
|
polarity: int
|
||||||
) -> Tuple[field_updater, field_updater]:
|
) -> Tuple[fdfield_updater_t, fdfield_updater_t]:
|
||||||
dirs = [0, 1, 2]
|
dirs = [0, 1, 2]
|
||||||
if direction not in dirs:
|
if direction not in dirs:
|
||||||
raise Exception('Invalid direction: {}'.format(direction))
|
raise Exception('Invalid direction: {}'.format(direction))
|
||||||
@ -23,13 +23,13 @@ def conducting_boundary(direction: int,
|
|||||||
boundary_slice[direction] = 0
|
boundary_slice[direction] = 0
|
||||||
shifted1_slice[direction] = 1
|
shifted1_slice[direction] = 1
|
||||||
|
|
||||||
def en(e: field_t):
|
def en(e: fdfield_t):
|
||||||
e[direction][boundary_slice] = 0
|
e[direction][boundary_slice] = 0
|
||||||
e[u][boundary_slice] = e[u][shifted1_slice]
|
e[u][boundary_slice] = e[u][shifted1_slice]
|
||||||
e[v][boundary_slice] = e[v][shifted1_slice]
|
e[v][boundary_slice] = e[v][shifted1_slice]
|
||||||
return e
|
return e
|
||||||
|
|
||||||
def hn(h: field_t):
|
def hn(h: fdfield_t):
|
||||||
h[direction][boundary_slice] = h[direction][shifted1_slice]
|
h[direction][boundary_slice] = h[direction][shifted1_slice]
|
||||||
h[u][boundary_slice] = 0
|
h[u][boundary_slice] = 0
|
||||||
h[v][boundary_slice] = 0
|
h[v][boundary_slice] = 0
|
||||||
@ -45,14 +45,14 @@ def conducting_boundary(direction: int,
|
|||||||
shifted1_slice[direction] = -2
|
shifted1_slice[direction] = -2
|
||||||
shifted2_slice[direction] = -3
|
shifted2_slice[direction] = -3
|
||||||
|
|
||||||
def ep(e: field_t):
|
def ep(e: fdfield_t):
|
||||||
e[direction][boundary_slice] = -e[direction][shifted2_slice]
|
e[direction][boundary_slice] = -e[direction][shifted2_slice]
|
||||||
e[direction][shifted1_slice] = 0
|
e[direction][shifted1_slice] = 0
|
||||||
e[u][boundary_slice] = e[u][shifted1_slice]
|
e[u][boundary_slice] = e[u][shifted1_slice]
|
||||||
e[v][boundary_slice] = e[v][shifted1_slice]
|
e[v][boundary_slice] = e[v][shifted1_slice]
|
||||||
return e
|
return e
|
||||||
|
|
||||||
def hp(h: field_t):
|
def hp(h: fdfield_t):
|
||||||
h[direction][boundary_slice] = h[direction][shifted1_slice]
|
h[direction][boundary_slice] = h[direction][shifted1_slice]
|
||||||
h[u][boundary_slice] = -h[u][shifted2_slice]
|
h[u][boundary_slice] = -h[u][shifted2_slice]
|
||||||
h[u][shifted1_slice] = 0
|
h[u][shifted1_slice] = 0
|
||||||
|
@ -2,12 +2,13 @@
|
|||||||
from typing import List, Callable, Tuple, Dict
|
from typing import List, Callable, Tuple, Dict
|
||||||
import numpy
|
import numpy
|
||||||
|
|
||||||
from .. import dx_lists_t, field_t, field_updater, fdmath
|
from ..fdmath import dx_lists_t, fdfield_t, fdfield_updater_t
|
||||||
|
from ..fdmath.functional import deriv_back, deriv_forward
|
||||||
|
|
||||||
def poynting(e: field_t,
|
def poynting(e: fdfield_t,
|
||||||
h: field_t,
|
h: fdfield_t,
|
||||||
dxes: dx_lists_t = None,
|
dxes: dx_lists_t = None,
|
||||||
) -> field_t:
|
) -> fdfield_t:
|
||||||
if dxes is None:
|
if dxes is None:
|
||||||
dxes = tuple(tuple(numpy.ones(1) for _ in range(3)) for _ in range(2))
|
dxes = tuple(tuple(numpy.ones(1) for _ in range(3)) for _ in range(2))
|
||||||
|
|
||||||
@ -25,51 +26,51 @@ def poynting(e: field_t,
|
|||||||
return s
|
return s
|
||||||
|
|
||||||
|
|
||||||
def poynting_divergence(s: field_t = None,
|
def poynting_divergence(s: fdfield_t = None,
|
||||||
*,
|
*,
|
||||||
e: field_t = None,
|
e: fdfield_t = None,
|
||||||
h: field_t = None,
|
h: fdfield_t = None,
|
||||||
dxes: dx_lists_t = None,
|
dxes: dx_lists_t = None,
|
||||||
) -> field_t:
|
) -> fdfield_t:
|
||||||
if s is None:
|
if s is None:
|
||||||
s = poynting(e, h, dxes=dxes)
|
s = poynting(e, h, dxes=dxes)
|
||||||
|
|
||||||
Dx, Dy, Dz = fdmath.functional.deriv_back()
|
Dx, Dy, Dz = deriv_back()
|
||||||
ds = Dx(s[0]) + Dy(s[1]) + Dz(s[2])
|
ds = Dx(s[0]) + Dy(s[1]) + Dz(s[2])
|
||||||
return ds
|
return ds
|
||||||
|
|
||||||
|
|
||||||
def energy_hstep(e0: field_t,
|
def energy_hstep(e0: fdfield_t,
|
||||||
h1: field_t,
|
h1: fdfield_t,
|
||||||
e2: field_t,
|
e2: fdfield_t,
|
||||||
epsilon: field_t = None,
|
epsilon: fdfield_t = None,
|
||||||
mu: field_t = None,
|
mu: fdfield_t = None,
|
||||||
dxes: dx_lists_t = None,
|
dxes: dx_lists_t = None,
|
||||||
) -> field_t:
|
) -> fdfield_t:
|
||||||
u = dxmul(e0 * e2, h1 * h1, epsilon, mu, dxes)
|
u = dxmul(e0 * e2, h1 * h1, epsilon, mu, dxes)
|
||||||
return u
|
return u
|
||||||
|
|
||||||
|
|
||||||
def energy_estep(h0: field_t,
|
def energy_estep(h0: fdfield_t,
|
||||||
e1: field_t,
|
e1: fdfield_t,
|
||||||
h2: field_t,
|
h2: fdfield_t,
|
||||||
epsilon: field_t = None,
|
epsilon: fdfield_t = None,
|
||||||
mu: field_t = None,
|
mu: fdfield_t = None,
|
||||||
dxes: dx_lists_t = None,
|
dxes: dx_lists_t = None,
|
||||||
) -> field_t:
|
) -> fdfield_t:
|
||||||
u = dxmul(e1 * e1, h0 * h2, epsilon, mu, dxes)
|
u = dxmul(e1 * e1, h0 * h2, epsilon, mu, dxes)
|
||||||
return u
|
return u
|
||||||
|
|
||||||
|
|
||||||
def delta_energy_h2e(dt: float,
|
def delta_energy_h2e(dt: float,
|
||||||
e0: field_t,
|
e0: fdfield_t,
|
||||||
h1: field_t,
|
h1: fdfield_t,
|
||||||
e2: field_t,
|
e2: fdfield_t,
|
||||||
h3: field_t,
|
h3: fdfield_t,
|
||||||
epsilon: field_t = None,
|
epsilon: fdfield_t = None,
|
||||||
mu: field_t = None,
|
mu: fdfield_t = None,
|
||||||
dxes: dx_lists_t = None,
|
dxes: dx_lists_t = None,
|
||||||
) -> field_t:
|
) -> fdfield_t:
|
||||||
"""
|
"""
|
||||||
This is just from (e2 * e2 + h3 * h1) - (h1 * h1 + e0 * e2)
|
This is just from (e2 * e2 + h3 * h1) - (h1 * h1 + e0 * e2)
|
||||||
"""
|
"""
|
||||||
@ -80,14 +81,14 @@ def delta_energy_h2e(dt: float,
|
|||||||
|
|
||||||
|
|
||||||
def delta_energy_e2h(dt: float,
|
def delta_energy_e2h(dt: float,
|
||||||
h0: field_t,
|
h0: fdfield_t,
|
||||||
e1: field_t,
|
e1: fdfield_t,
|
||||||
h2: field_t,
|
h2: fdfield_t,
|
||||||
e3: field_t,
|
e3: fdfield_t,
|
||||||
epsilon: field_t = None,
|
epsilon: fdfield_t = None,
|
||||||
mu: field_t = None,
|
mu: fdfield_t = None,
|
||||||
dxes: dx_lists_t = None,
|
dxes: dx_lists_t = None,
|
||||||
) -> field_t:
|
) -> fdfield_t:
|
||||||
"""
|
"""
|
||||||
This is just from (h2 * h2 + e3 * e1) - (e1 * e1 + h0 * h2)
|
This is just from (h2 * h2 + e3 * e1) - (e1 * e1 + h0 * h2)
|
||||||
"""
|
"""
|
||||||
@ -97,7 +98,7 @@ def delta_energy_e2h(dt: float,
|
|||||||
return du
|
return du
|
||||||
|
|
||||||
|
|
||||||
def delta_energy_j(j0: field_t, e1: field_t, dxes: dx_lists_t = None) -> field_t:
|
def delta_energy_j(j0: fdfield_t, e1: fdfield_t, dxes: dx_lists_t = None) -> fdfield_t:
|
||||||
if dxes is None:
|
if dxes is None:
|
||||||
dxes = tuple(tuple(numpy.ones(1) for _ in range(3)) for _ in range(2))
|
dxes = tuple(tuple(numpy.ones(1) for _ in range(3)) for _ in range(2))
|
||||||
|
|
||||||
@ -108,12 +109,12 @@ def delta_energy_j(j0: field_t, e1: field_t, dxes: dx_lists_t = None) -> field_t
|
|||||||
return du
|
return du
|
||||||
|
|
||||||
|
|
||||||
def dxmul(ee: field_t,
|
def dxmul(ee: fdfield_t,
|
||||||
hh: field_t,
|
hh: fdfield_t,
|
||||||
epsilon: field_t = None,
|
epsilon: fdfield_t = None,
|
||||||
mu: field_t = None,
|
mu: fdfield_t = None,
|
||||||
dxes: dx_lists_t = None
|
dxes: dx_lists_t = None
|
||||||
) -> field_t:
|
) -> fdfield_t:
|
||||||
if epsilon is None:
|
if epsilon is None:
|
||||||
epsilon = 1
|
epsilon = 1
|
||||||
if mu is None:
|
if mu is None:
|
||||||
|
@ -7,7 +7,7 @@ PML implementations
|
|||||||
from typing import List, Callable, Tuple, Dict
|
from typing import List, Callable, Tuple, Dict
|
||||||
import numpy
|
import numpy
|
||||||
|
|
||||||
from .. import dx_lists_t, field_t, field_updater
|
from ..fdmath import dx_lists_t, fdfield_t, fdfield_updater_t
|
||||||
|
|
||||||
|
|
||||||
__author__ = 'Jan Petykiewicz'
|
__author__ = 'Jan Petykiewicz'
|
||||||
@ -16,7 +16,7 @@ __author__ = 'Jan Petykiewicz'
|
|||||||
def cpml(direction: int,
|
def cpml(direction: int,
|
||||||
polarity: int,
|
polarity: int,
|
||||||
dt: float,
|
dt: float,
|
||||||
epsilon: field_t,
|
epsilon: fdfield_t,
|
||||||
thickness: int = 8,
|
thickness: int = 8,
|
||||||
ln_R_per_layer: float = -1.6,
|
ln_R_per_layer: float = -1.6,
|
||||||
epsilon_eff: float = 1,
|
epsilon_eff: float = 1,
|
||||||
@ -25,7 +25,7 @@ def cpml(direction: int,
|
|||||||
ma: float = 1,
|
ma: float = 1,
|
||||||
cfs_alpha: float = 0,
|
cfs_alpha: float = 0,
|
||||||
dtype: numpy.dtype = numpy.float32,
|
dtype: numpy.dtype = numpy.float32,
|
||||||
) -> Tuple[Callable, Callable, Dict[str, field_t]]:
|
) -> Tuple[Callable, Callable, Dict[str, fdfield_t]]:
|
||||||
|
|
||||||
if direction not in range(3):
|
if direction not in range(3):
|
||||||
raise Exception('Invalid direction: {}'.format(direction))
|
raise Exception('Invalid direction: {}'.format(direction))
|
||||||
@ -58,6 +58,7 @@ def cpml(direction: int,
|
|||||||
|
|
||||||
expand_slice = [None] * 3
|
expand_slice = [None] * 3
|
||||||
expand_slice[direction] = slice(None)
|
expand_slice[direction] = slice(None)
|
||||||
|
expand_slice = tuple(expand_slice)
|
||||||
|
|
||||||
def par(x):
|
def par(x):
|
||||||
scaling = (x / thickness) ** m
|
scaling = (x / thickness) ** m
|
||||||
@ -79,6 +80,7 @@ def cpml(direction: int,
|
|||||||
region[direction] = slice(-thickness, None)
|
region[direction] = slice(-thickness, None)
|
||||||
else:
|
else:
|
||||||
raise Exception('Bad polarity!')
|
raise Exception('Bad polarity!')
|
||||||
|
region = tuple(region)
|
||||||
|
|
||||||
se = 1 if direction == 1 else -1
|
se = 1 if direction == 1 else -1
|
||||||
|
|
||||||
@ -97,7 +99,7 @@ def cpml(direction: int,
|
|||||||
|
|
||||||
# Note that this is kinda slow -- would be faster to reuse dHv*p2h for the original
|
# Note that this is kinda slow -- would be faster to reuse dHv*p2h for the original
|
||||||
# H update, but then you have multiple arrays and a monolithic (field + pml) update operation
|
# H update, but then you have multiple arrays and a monolithic (field + pml) update operation
|
||||||
def pml_e(e: field_t, h: field_t, epsilon: field_t) -> Tuple[field_t, field_t]:
|
def pml_e(e: fdfield_t, h: fdfield_t, epsilon: fdfield_t) -> Tuple[fdfield_t, fdfield_t]:
|
||||||
dHv = h[v][region] - numpy.roll(h[v], 1, axis=direction)[region]
|
dHv = h[v][region] - numpy.roll(h[v], 1, axis=direction)[region]
|
||||||
dHu = h[u][region] - numpy.roll(h[u], 1, axis=direction)[region]
|
dHu = h[u][region] - numpy.roll(h[u], 1, axis=direction)[region]
|
||||||
psi_e[0] *= p0e
|
psi_e[0] *= p0e
|
||||||
@ -108,7 +110,7 @@ def cpml(direction: int,
|
|||||||
e[v][region] -= se * dt / epsilon[v][region] * (psi_e[1] + (p2e - 1) * dHu)
|
e[v][region] -= se * dt / epsilon[v][region] * (psi_e[1] + (p2e - 1) * dHu)
|
||||||
return e, h
|
return e, h
|
||||||
|
|
||||||
def pml_h(e: field_t, h: field_t) -> Tuple[field_t, field_t]:
|
def pml_h(e: fdfield_t, h: fdfield_t) -> Tuple[fdfield_t, fdfield_t]:
|
||||||
dEv = (numpy.roll(e[v], -1, axis=direction)[region] - e[v][region])
|
dEv = (numpy.roll(e[v], -1, axis=direction)[region] - e[v][region])
|
||||||
dEu = (numpy.roll(e[u], -1, axis=direction)[region] - e[u][region])
|
dEu = (numpy.roll(e[u], -1, axis=direction)[region] - e[u][region])
|
||||||
psi_h[0] *= p0h
|
psi_h[0] *= p0h
|
||||||
|
@ -5,7 +5,8 @@ import pytest
|
|||||||
import numpy
|
import numpy
|
||||||
#from numpy.testing import assert_allclose, assert_array_equal
|
#from numpy.testing import assert_allclose, assert_array_equal
|
||||||
|
|
||||||
from .. import fdfd, vec, unvec
|
from .. import fdfd
|
||||||
|
from ..fdmath import vec, unvec
|
||||||
from .utils import assert_close, assert_fields_close
|
from .utils import assert_close, assert_fields_close
|
||||||
|
|
||||||
|
|
||||||
@ -20,6 +21,10 @@ def test_poynting_planes(sim):
|
|||||||
mask = (sim.j != 0).any(axis=0)
|
mask = (sim.j != 0).any(axis=0)
|
||||||
if mask.sum() != 2:
|
if mask.sum() != 2:
|
||||||
pytest.skip(f'test_poynting_planes will only test 2-point sources, got {mask.sum()}')
|
pytest.skip(f'test_poynting_planes will only test 2-point sources, got {mask.sum()}')
|
||||||
|
# for dxg in sim.dxes:
|
||||||
|
# for dxa in dxg:
|
||||||
|
# if not (dxa == sim.dxes[0][0][0]).all():
|
||||||
|
# pytest.skip('test_poynting_planes skips nonuniform dxes')
|
||||||
points = numpy.where(mask)
|
points = numpy.where(mask)
|
||||||
mask[points[0][0], points[1][0], points[2][0]] = 0
|
mask[points[0][0], points[1][0], points[2][0]] = 0
|
||||||
|
|
||||||
@ -43,7 +48,6 @@ def test_poynting_planes(sim):
|
|||||||
assert_close(sum(planes), src_energy.sum())
|
assert_close(sum(planes), src_energy.sum())
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#####################################
|
#####################################
|
||||||
# Test fixtures
|
# Test fixtures
|
||||||
#####################################
|
#####################################
|
||||||
@ -102,6 +106,15 @@ def sim(request, shape, epsilon, dxes, j_distribution, omega, pec, pmc):
|
|||||||
# if is3d:
|
# if is3d:
|
||||||
# pytest.skip('Skipping dt != 0.3 because test is 3D (for speed)')
|
# pytest.skip('Skipping dt != 0.3 because test is 3D (for speed)')
|
||||||
|
|
||||||
|
# # If no edge currents, add pmls
|
||||||
|
# src_mask = j_distribution.any(axis=0)
|
||||||
|
# th = 10
|
||||||
|
# #if src_mask.sum() - src_mask[th:-th, th:-th, th:-th].sum() == 0:
|
||||||
|
# if src_mask.sum() - src_mask[th:-th, :, :].sum() == 0:
|
||||||
|
# for axis in (0,):
|
||||||
|
# for polarity in (-1, 1):
|
||||||
|
# dxes = fdfd.scpml.stretch_with_scpml(dxes, axis=axis, polarity=polarity,
|
||||||
|
|
||||||
j_vec = vec(j_distribution)
|
j_vec = vec(j_distribution)
|
||||||
eps_vec = vec(epsilon)
|
eps_vec = vec(epsilon)
|
||||||
e_vec = fdfd.solvers.generic(J=j_vec, omega=omega, dxes=dxes, epsilon=eps_vec,
|
e_vec = fdfd.solvers.generic(J=j_vec, omega=omega, dxes=dxes, epsilon=eps_vec,
|
||||||
|
@ -6,7 +6,8 @@ import pytest
|
|||||||
import numpy
|
import numpy
|
||||||
from numpy.testing import assert_allclose, assert_array_equal
|
from numpy.testing import assert_allclose, assert_array_equal
|
||||||
|
|
||||||
from .. import fdfd, vec, unvec
|
from .. import fdfd
|
||||||
|
from ..fdmath import vec, unvec
|
||||||
from .utils import assert_close, assert_fields_close
|
from .utils import assert_close, assert_fields_close
|
||||||
from .test_fdfd import FDResult
|
from .test_fdfd import FDResult
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user