various fixes and improvements
This commit is contained in:
parent
94ff3f7853
commit
5951f2bdb1
@ -4,7 +4,7 @@ from numpy.linalg import norm
|
||||
|
||||
import meanas
|
||||
from meanas import vec, unvec
|
||||
from meanas.fdfd import waveguide_mode, functional, scpml
|
||||
from meanas.fdfd import waveguide_mode, functional, scpml, operators
|
||||
from meanas.fdfd.solvers import generic as generic_solver
|
||||
|
||||
import gridlock
|
||||
@ -56,18 +56,23 @@ def test0(solver=generic_solver):
|
||||
dxes = [grid.dxyz, grid.autoshifted_dxyz()]
|
||||
for a in (0, 1, 2):
|
||||
for p in (-1, 1):
|
||||
dxes = meanas.scpml.stretch_with_scpml(dxes, axis=a, polarity=p, omega=omega,
|
||||
thickness=pml_thickness)
|
||||
dxes = meanas.fdfd.scpml.stretch_with_scpml(dxes, axis=a, polarity=p, omega=omega,
|
||||
thickness=pml_thickness)
|
||||
|
||||
J = [numpy.zeros_like(grid.grids[0], dtype=complex) for _ in range(3)]
|
||||
J[1][15, grid.shape[1]//2, grid.shape[2]//2] = 1e5
|
||||
J[1][15, grid.shape[1]//2, grid.shape[2]//2] = 1
|
||||
|
||||
'''
|
||||
Solve!
|
||||
'''
|
||||
sim_args = {
|
||||
'omega': omega,
|
||||
'dxes': dxes,
|
||||
'epsilon': vec(grid.grids),
|
||||
}
|
||||
x = solver(J=vec(J), **sim_args)
|
||||
|
||||
A = functional.e_full(omega, dxes, vec(grid.grids)).tocsr()
|
||||
A = operators.e_full(omega, dxes, vec(grid.grids)).tocsr()
|
||||
b = -1j * omega * vec(J)
|
||||
print('Norm of the residual is ', norm(A @ x - b))
|
||||
|
||||
@ -208,7 +213,7 @@ def module_available(name):
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
# test0()
|
||||
test0()
|
||||
|
||||
if module_available('opencl_fdfd'):
|
||||
from opencl_fdfd import cg_solver as opencl_solver
|
||||
|
@ -6,9 +6,11 @@ import numpy
|
||||
from numpy.fft import fft2, fftshift, fftfreq, ifft2, ifftshift
|
||||
from numpy import pi
|
||||
|
||||
from .. import field_t
|
||||
|
||||
def near_to_farfield(E_near: List[numpy.ndarray],
|
||||
H_near: List[numpy.ndarray],
|
||||
|
||||
def near_to_farfield(E_near: field_t,
|
||||
H_near: field_t,
|
||||
dx: float,
|
||||
dy: float,
|
||||
padded_size: List[int] = None
|
||||
@ -115,8 +117,8 @@ def near_to_farfield(E_near: List[numpy.ndarray],
|
||||
|
||||
|
||||
|
||||
def far_to_nearfield(E_far: List[numpy.ndarray],
|
||||
H_far: List[numpy.ndarray],
|
||||
def far_to_nearfield(E_far: field_t,
|
||||
H_far: field_t,
|
||||
dkx: float,
|
||||
dky: float,
|
||||
padded_size: List[int] = None
|
||||
|
@ -8,7 +8,7 @@ e.g. E = [E_x, E_y, E_z] where each component has shape (X, Y, Z)
|
||||
from typing import List, Callable
|
||||
import numpy
|
||||
|
||||
from . import dx_lists_t, field_t
|
||||
from .. import dx_lists_t, field_t
|
||||
|
||||
__author__ = 'Jan Petykiewicz'
|
||||
|
||||
|
@ -32,7 +32,7 @@ from typing import List, Tuple
|
||||
import numpy
|
||||
import scipy.sparse as sparse
|
||||
|
||||
from . import vec, dx_lists_t, vfield_t
|
||||
from .. import vec, dx_lists_t, vfield_t
|
||||
|
||||
|
||||
__author__ = 'Jan Petykiewicz'
|
||||
|
@ -5,6 +5,8 @@ Functions for creating stretched coordinate PMLs.
|
||||
from typing import List, Callable
|
||||
import numpy
|
||||
|
||||
from .. import dx_lists_t
|
||||
|
||||
__author__ = 'Jan Petykiewicz'
|
||||
|
||||
|
||||
|
@ -23,7 +23,7 @@ import numpy
|
||||
from numpy.linalg import norm
|
||||
import scipy.sparse as sparse
|
||||
|
||||
from . import vec, unvec, dx_lists_t, field_t, vfield_t
|
||||
from .. import vec, unvec, dx_lists_t, field_t, vfield_t
|
||||
from . import operators
|
||||
|
||||
|
||||
@ -82,7 +82,8 @@ def normalized_fields(v: numpy.ndarray,
|
||||
omega: complex,
|
||||
dxes: dx_lists_t,
|
||||
epsilon: vfield_t,
|
||||
mu: vfield_t = None
|
||||
mu: vfield_t = None,
|
||||
dx_prop: float = 0,
|
||||
) -> Tuple[vfield_t, vfield_t]:
|
||||
"""
|
||||
Given a vector v containing the vectorized H_x and H_y fields,
|
||||
@ -94,6 +95,7 @@ def normalized_fields(v: numpy.ndarray,
|
||||
:param dxes: Grid parameters [dx_e, dx_h] as described in meanas.types (2D)
|
||||
:param epsilon: Vectorized dielectric constant grid
|
||||
:param mu: Vectorized magnetic permeability grid (default 1 everywhere)
|
||||
:param dxes_prop: Grid cell width in the propagation direction. Default 0 (continuous).
|
||||
:return: Normalized, vectorized (e, h) containing all vector components.
|
||||
"""
|
||||
e = v2e(v, wavenumber, omega, dxes, epsilon, mu=mu)
|
||||
@ -105,11 +107,10 @@ def normalized_fields(v: numpy.ndarray,
|
||||
E = unvec(e, shape)
|
||||
H = unvec(h, shape)
|
||||
|
||||
S1 = E[0] * numpy.roll(numpy.conj(H[1]), 1, axis=0) * dxes_real[0][1] * dxes_real[1][0]
|
||||
S2 = E[1] * numpy.roll(numpy.conj(H[0]), 1, axis=1) * dxes_real[0][0] * dxes_real[1][1]
|
||||
S = 0.25 * ((S1 + numpy.roll(S1, 1, axis=0)) -
|
||||
(S2 + numpy.roll(S2, 1, axis=1)))
|
||||
P = 0.5 * numpy.real(S.sum())
|
||||
phase = numpy.exp(-1j * wavenumber * dx_prop / 2)
|
||||
S1 = E[0] * numpy.conj(H[1] * phase) * dxes_real[0][1] * dxes_real[1][0]
|
||||
S2 = E[1] * numpy.conj(H[0] * phase) * dxes_real[0][0] * dxes_real[1][1]
|
||||
P = numpy.real(S1.sum() - S2.sum())
|
||||
assert P > 0, 'Found a mode propagating in the wrong direction! P={}'.format(P)
|
||||
|
||||
energy = epsilon * e.conj() * e
|
||||
@ -120,8 +121,6 @@ def normalized_fields(v: numpy.ndarray,
|
||||
# Try to break symmetry to assign a consistent sign [experimental]
|
||||
E_weighted = unvec(e * energy * numpy.exp(1j * norm_angle), shape)
|
||||
sign = numpy.sign(E_weighted[:, :max(shape[0]//2, 1), :max(shape[1]//2, 1)].real.sum())
|
||||
logger.debug('norm_angle = {}'.format(norm_angle))
|
||||
logger.debug('norm_sign = {}'.format(sign)
|
||||
|
||||
norm_factor = sign * norm_amplitude * numpy.exp(1j * norm_angle)
|
||||
|
||||
|
@ -2,9 +2,9 @@ from typing import Dict, List
|
||||
import numpy
|
||||
import scipy.sparse as sparse
|
||||
|
||||
from . import vec, unvec, dx_lists_t, vfield_t, field_t
|
||||
from .. import vec, unvec, dx_lists_t, vfield_t, field_t
|
||||
from . import operators, waveguide, functional
|
||||
from .eigensolvers import signed_eigensolve, rayleigh_quotient_iteration
|
||||
from ..eigensolvers import signed_eigensolve, rayleigh_quotient_iteration
|
||||
|
||||
|
||||
def solve_waveguide_mode_2d(mode_number: int,
|
||||
@ -12,7 +12,7 @@ def solve_waveguide_mode_2d(mode_number: int,
|
||||
dxes: dx_lists_t,
|
||||
epsilon: vfield_t,
|
||||
mu: vfield_t = None,
|
||||
wavenumber_correction: bool = True,
|
||||
dx_prop: float = 0,
|
||||
) -> Dict[str, complex or field_t]:
|
||||
"""
|
||||
Given a 2d region, attempts to solve for the eigenmode with the specified mode number.
|
||||
@ -22,8 +22,8 @@ def solve_waveguide_mode_2d(mode_number: int,
|
||||
:param dxes: Grid parameters [dx_e, dx_h] as described in meanas.types
|
||||
:param epsilon: Dielectric constant
|
||||
:param mu: Magnetic permeability (default 1 everywhere)
|
||||
:param wavenumber_correction: Whether to correct the wavenumber to
|
||||
account for numerical dispersion (default True)
|
||||
:param dx_prop: The cell width in the the propagation direction, used to apply a
|
||||
correction to the wavenumber. Default 0 (i.e. continuous propagation direction)
|
||||
:return: {'E': List[numpy.ndarray], 'H': List[numpy.ndarray], 'wavenumber': complex}
|
||||
"""
|
||||
|
||||
@ -51,15 +51,9 @@ def solve_waveguide_mode_2d(mode_number: int,
|
||||
|
||||
'''
|
||||
Perform correction on wavenumber to account for numerical dispersion.
|
||||
|
||||
See Numerical Dispersion in Taflove's FDTD book.
|
||||
This correction term reduces the error in emitted power, but additional
|
||||
error is introduced into the E_err and H_err terms. This effect becomes
|
||||
more pronounced as the wavenumber increases.
|
||||
'''
|
||||
if wavenumber_correction:
|
||||
dx_mean = (numpy.hstack(dxes[0]) + numpy.hstack(dxes[1])).mean() / 2 #TODO figure out what dx to use here
|
||||
wavenumber -= 2 * numpy.sin(numpy.real(wavenumber * dx_mean / 2)) / dx_mean - numpy.real(wavenumber)
|
||||
if dx_prop != 0:
|
||||
wavenumber = 2 / dx_prop * numpy.sin(wavenumber * dx_prop / 2)
|
||||
|
||||
shape = [d.size for d in dxes[0]]
|
||||
fields = {
|
||||
@ -79,7 +73,6 @@ def solve_waveguide_mode(mode_number: int,
|
||||
slices: List[slice],
|
||||
epsilon: field_t,
|
||||
mu: field_t = None,
|
||||
wavenumber_correction: bool = True
|
||||
) -> Dict[str, complex or numpy.ndarray]:
|
||||
"""
|
||||
Given a 3D grid, selects a slice from the grid and attempts to
|
||||
@ -94,8 +87,6 @@ def solve_waveguide_mode(mode_number: int,
|
||||
as the waveguide cross-section. slices[axis] should select only one
|
||||
:param epsilon: Dielectric constant
|
||||
:param mu: Magnetic permeability (default 1 everywhere)
|
||||
:param wavenumber_correction: Whether to correct the wavenumber to
|
||||
account for numerical dispersion (default True)
|
||||
:return: {'E': List[numpy.ndarray], 'H': List[numpy.ndarray], 'wavenumber': complex}
|
||||
"""
|
||||
if mu is None:
|
||||
@ -115,7 +106,7 @@ def solve_waveguide_mode(mode_number: int,
|
||||
'dxes': [[dx[i][slices[i]] for i in order[:2]] for dx in dxes],
|
||||
'epsilon': vec([epsilon[i][slices].transpose(order) for i in order]),
|
||||
'mu': vec([mu[i][slices].transpose(order) for i in order]),
|
||||
'wavenumber_correction': wavenumber_correction,
|
||||
'dx_prop': dxes[0][order[2]][slices[order[2]]],
|
||||
}
|
||||
fields_2d = solve_waveguide_mode_2d(mode_number, omega=omega, **args_2d)
|
||||
|
||||
@ -175,9 +166,6 @@ def compute_source(E: field_t,
|
||||
:param mu: Magnetic permeability (default 1 everywhere)
|
||||
:return: J distribution for the unidirectional source
|
||||
"""
|
||||
if mu is None:
|
||||
mu = numpy.ones(3)
|
||||
|
||||
J = numpy.zeros_like(E, dtype=complex)
|
||||
M = numpy.zeros_like(E, dtype=complex)
|
||||
|
||||
@ -275,9 +263,9 @@ def solve_waveguide_mode_cylindrical(mode_number: int,
|
||||
dxes: dx_lists_t,
|
||||
epsilon: vfield_t,
|
||||
r0: float,
|
||||
wavenumber_correction: bool = True,
|
||||
) -> Dict[str, complex or field_t]:
|
||||
"""
|
||||
TODO: fixup
|
||||
Given a 2d (r, y) slice of epsilon, attempts to solve for the eigenmode
|
||||
of the bent waveguide with the specified mode number.
|
||||
|
||||
@ -288,8 +276,6 @@ def solve_waveguide_mode_cylindrical(mode_number: int,
|
||||
:param epsilon: Dielectric constant
|
||||
:param r0: Radius of curvature for the simulation. This should be the minimum value of
|
||||
r within the simulation domain.
|
||||
:param wavenumber_correction: Whether to correct the wavenumber to
|
||||
account for numerical dispersion (default True)
|
||||
:return: {'E': List[numpy.ndarray], 'H': List[numpy.ndarray], 'wavenumber': complex}
|
||||
"""
|
||||
|
||||
@ -313,16 +299,7 @@ def solve_waveguide_mode_cylindrical(mode_number: int,
|
||||
wavenumber = numpy.sqrt(eigval)
|
||||
wavenumber *= numpy.sign(numpy.real(wavenumber))
|
||||
|
||||
'''
|
||||
Perform correction on wavenumber to account for numerical dispersion.
|
||||
|
||||
See Numerical Dispersion in Taflove's FDTD book.
|
||||
This correction term reduces the error in emitted power, but additional
|
||||
error is introduced into the E_err and H_err terms. This effect becomes
|
||||
more pronounced as the wavenumber increases.
|
||||
'''
|
||||
if wavenumber_correction:
|
||||
wavenumber -= 2 * numpy.sin(numpy.real(wavenumber / 2)) - numpy.real(wavenumber)
|
||||
# TODO: Perform correction on wavenumber to account for numerical dispersion.
|
||||
|
||||
shape = [d.size for d in dxes[0]]
|
||||
v = numpy.hstack((v, numpy.zeros(shape[0] * shape[1])))
|
||||
|
@ -9,6 +9,7 @@ from meanas import fdtd
|
||||
|
||||
prng = numpy.random.RandomState(12345)
|
||||
|
||||
|
||||
def assert_fields_close(a, b, *args, **kwargs):
|
||||
numpy.testing.assert_allclose(a, b, verbose=False, err_msg='Fields did not match:\n{}\n{}'.format(numpy.rollaxis(a, -1),
|
||||
numpy.rollaxis(b, -1)), *args, **kwargs)
|
||||
|
Loading…
Reference in New Issue
Block a user