move stuff under fdmath

This commit is contained in:
Jan Petykiewicz 2019-11-27 22:59:52 -08:00
commit a956323b94
23 changed files with 304 additions and 272 deletions

View file

@ -14,7 +14,8 @@ import numpy
from numpy.linalg import norm
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 . import operators
@ -24,8 +25,8 @@ __author__ = 'Jan Petykiewicz'
def operator_e(omega: complex,
dxes: dx_lists_t,
epsilon: vfield_t,
mu: vfield_t = None,
epsilon: vfdfield_t,
mu: vfdfield_t = None,
) -> sparse.spmatrix:
"""
Waveguide operator of the form
@ -66,7 +67,7 @@ def operator_e(omega: complex,
Args:
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
mu: Vectorized magnetic permeability grid (default 1 everywhere)
@ -76,8 +77,8 @@ def operator_e(omega: complex,
if numpy.any(numpy.equal(mu, None)):
mu = numpy.ones_like(epsilon)
Dfx, Dfy = operators.deriv_forward(dxes[0])
Dbx, Dby = operators.deriv_back(dxes[1])
Dfx, Dfy = deriv_forward(dxes[0])
Dbx, Dby = deriv_back(dxes[1])
eps_parts = numpy.split(epsilon, 3)
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,
dxes: dx_lists_t,
epsilon: vfield_t,
mu: vfield_t = None,
epsilon: vfdfield_t,
mu: vfdfield_t = None,
) -> sparse.spmatrix:
"""
Waveguide operator of the form
@ -137,7 +138,7 @@ def operator_h(omega: complex,
Args:
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
mu: Vectorized magnetic permeability grid (default 1 everywhere)
@ -169,10 +170,10 @@ def normalized_fields_e(e_xy: numpy.ndarray,
wavenumber: complex,
omega: complex,
dxes: dx_lists_t,
epsilon: vfield_t,
mu: vfield_t = None,
epsilon: vfdfield_t,
mu: vfdfield_t = None,
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,
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)`.
It should satisfy `operator_e() @ e_xy == wavenumber**2 * e_xy`
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
mu: Vectorized magnetic permeability grid (default 1 everywhere)
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,
omega: complex,
dxes: dx_lists_t,
epsilon: vfield_t,
mu: vfield_t = None,
epsilon: vfdfield_t,
mu: vfdfield_t = None,
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,
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)`.
It should satisfy `operator_h() @ h_xy == wavenumber**2 * h_xy`
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
mu: Vectorized magnetic permeability grid (default 1 everywhere)
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,
omega: complex,
dxes: dx_lists_t,
epsilon: vfield_t,
mu: vfield_t = None,
epsilon: vfdfield_t,
mu: vfdfield_t = None,
prop_phase: float = 0,
) -> Tuple[vfield_t, vfield_t]:
) -> Tuple[vfdfield_t, vfdfield_t]:
# TODO documentation
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)]
@ -276,8 +277,8 @@ def _normalized_fields(e: numpy.ndarray,
def exy2h(wavenumber: complex,
omega: complex,
dxes: dx_lists_t,
epsilon: vfield_t,
mu: vfield_t = None
epsilon: vfdfield_t,
mu: vfdfield_t = None
) -> sparse.spmatrix:
"""
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)`.
It should satisfy `operator_e() @ e_xy == wavenumber**2 * e_xy`
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
mu: Vectorized magnetic permeability grid (default 1 everywhere)
@ -301,8 +302,8 @@ def exy2h(wavenumber: complex,
def hxy2e(wavenumber: complex,
omega: complex,
dxes: dx_lists_t,
epsilon: vfield_t,
mu: vfield_t = None
epsilon: vfdfield_t,
mu: vfdfield_t = None
) -> sparse.spmatrix:
"""
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)`.
It should satisfy `operator_h() @ h_xy == wavenumber**2 * h_xy`
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
mu: Vectorized magnetic permeability grid (default 1 everywhere)
@ -325,7 +326,7 @@ def hxy2e(wavenumber: complex,
def hxy2h(wavenumber: complex,
dxes: dx_lists_t,
mu: vfield_t = None
mu: vfdfield_t = None
) -> sparse.spmatrix:
"""
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:
wavenumber: Wavenumber assuming fields have z-dependence of `exp(-i * wavenumber * z)`.
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)
Returns:
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)
if not numpy.any(numpy.equal(mu, None)):
@ -358,7 +359,7 @@ def hxy2h(wavenumber: complex,
def exy2e(wavenumber: complex,
dxes: dx_lists_t,
epsilon: vfield_t,
epsilon: vfdfield_t,
) -> sparse.spmatrix:
"""
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:
wavenumber: Wavenumber assuming fields have z-dependence of `exp(-i * wavenumber * z)`
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
Returns:
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)
if not numpy.any(numpy.equal(epsilon, None)):
@ -392,7 +393,7 @@ def exy2e(wavenumber: complex,
def e2h(wavenumber: complex,
omega: complex,
dxes: dx_lists_t,
mu: vfield_t = None
mu: vfdfield_t = None
) -> sparse.spmatrix:
"""
Returns an operator which, when applied to a vectorized E eigenfield, produces
@ -401,7 +402,7 @@ def e2h(wavenumber: complex,
Args:
wavenumber: Wavenumber assuming fields have z-dependence of `exp(-i * wavenumber * z)`
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)
Returns:
@ -416,7 +417,7 @@ def e2h(wavenumber: complex,
def h2e(wavenumber: complex,
omega: complex,
dxes: dx_lists_t,
epsilon: vfield_t
epsilon: vfdfield_t
) -> sparse.spmatrix:
"""
Returns an operator which, when applied to a vectorized H eigenfield, produces
@ -425,7 +426,7 @@ def h2e(wavenumber: complex,
Args:
wavenumber: Wavenumber assuming fields have z-dependence of `exp(-i * wavenumber * z)`
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
Returns:
@ -441,7 +442,7 @@ def curl_e(wavenumber: complex, dxes: dx_lists_t) -> sparse.spmatrix:
Args:
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:
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]:
n *= len(d)
print(wavenumber, n)
Bz = -1j * wavenumber * sparse.eye(n)
Dfx, Dfy = operators.deriv_forward(dxes[0])
return operators.cross([Dfx, Dfy, Bz])
Dfx, Dfy = deriv_forward(dxes[0])
return cross([Dfx, Dfy, Bz])
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:
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:
Sparse matrix representation of the operator.
@ -471,16 +473,16 @@ def curl_h(wavenumber: complex, dxes: dx_lists_t) -> sparse.spmatrix:
n *= len(d)
Bz = -1j * wavenumber * sparse.eye(n)
Dbx, Dby = operators.deriv_back(dxes[1])
return operators.cross([Dbx, Dby, Bz])
Dbx, Dby = deriv_back(dxes[1])
return cross([Dbx, Dby, Bz])
def h_err(h: vfield_t,
def h_err(h: vfdfield_t,
wavenumber: complex,
omega: complex,
dxes: dx_lists_t,
epsilon: vfield_t,
mu: vfield_t = None
epsilon: vfdfield_t,
mu: vfdfield_t = None
) -> float:
"""
Calculates the relative error in the H field
@ -489,7 +491,7 @@ def h_err(h: vfield_t,
h: Vectorized H field
wavenumber: Wavenumber assuming fields have z-dependence of `exp(-i * wavenumber * z)`
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
mu: Vectorized magnetic permeability grid (default 1 everywhere)
@ -509,12 +511,12 @@ def h_err(h: vfield_t,
return norm(op) / norm(h)
def e_err(e: vfield_t,
def e_err(e: vfdfield_t,
wavenumber: complex,
omega: complex,
dxes: dx_lists_t,
epsilon: vfield_t,
mu: vfield_t = None
epsilon: vfdfield_t,
mu: vfdfield_t = None
) -> float:
"""
Calculates the relative error in the E field
@ -523,7 +525,7 @@ def e_err(e: vfield_t,
e: Vectorized E field
wavenumber: Wavenumber assuming fields have z-dependence of `exp(-i * wavenumber * z)`
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
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],
omega: complex,
dxes: dx_lists_t,
epsilon: vfield_t,
mu: vfield_t = None,
epsilon: vfdfield_t,
mu: vfdfield_t = None,
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.
Args:
mode_numbers: List of 0-indexed mode numbers to solve for
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
mu: Magnetic permeability (default 1 everywhere)
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
'''
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)
e_xys = eigvecs[:, -(numpy.array(mode_number) + 1)]
eigvals, eigvecs = signed_eigensolve(A_r, max(mode_numbers) + mode_margin)
e_xys = eigvecs[:, -(numpy.array(mode_numbers) + 1)]
'''
Now solve for the eigenvector of the full operator, using the real operator's
eigenvector as an initial guess for Rayleigh quotient iteration.
'''
A = waveguide.operator_e(omega, dxes, epsilon, mu)
eigvals, e_xys = rayleigh_quotient_iteration(A, e_xys)
A = operator_e(omega, dxes, epsilon, mu)
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)
wavenumbers = numpy.sqrt(eigvals)
@ -592,7 +595,7 @@ def solve_modes(mode_numbers: List[int],
def solve_mode(mode_number: int,
*args,
**kwargs
) -> Tuple[vfield_t, complex]:
) -> Tuple[vfdfield_t, complex]:
"""
Wrapper around `solve_modes()` that solves for a single mode.
@ -604,4 +607,5 @@ def solve_mode(mode_number: int,
Returns:
(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]