2016-05-30 22:30:45 -07:00
|
|
|
"""
|
|
|
|
Functional versions of many FDFD operators. These can be useful for performing
|
|
|
|
FDFD calculations without needing to construct large matrices in memory.
|
|
|
|
|
2019-08-04 13:48:41 -07:00
|
|
|
The functions generated here expect field inputs with shape (3, X, Y, Z),
|
|
|
|
e.g. E = [E_x, E_y, E_z] where each component has shape (X, Y, Z)
|
2016-05-30 22:30:45 -07:00
|
|
|
"""
|
|
|
|
from typing import List, Callable
|
|
|
|
import numpy
|
|
|
|
|
2019-08-05 00:20:06 -07:00
|
|
|
from .. import dx_lists_t, field_t
|
2016-05-30 22:30:45 -07:00
|
|
|
|
|
|
|
__author__ = 'Jan Petykiewicz'
|
|
|
|
|
|
|
|
|
2016-07-03 01:20:51 -07:00
|
|
|
functional_matrix = Callable[[field_t], field_t]
|
2016-05-30 22:30:45 -07:00
|
|
|
|
|
|
|
|
|
|
|
def curl_h(dxes: dx_lists_t) -> functional_matrix:
|
|
|
|
"""
|
|
|
|
Curl operator for use with the H field.
|
|
|
|
|
2019-08-04 13:48:41 -07:00
|
|
|
:param dxes: Grid parameters [dx_e, dx_h] as described in meanas.types
|
2016-05-30 22:30:45 -07:00
|
|
|
:return: Function for taking the discretized curl of the H-field, F(H) -> curlH
|
|
|
|
"""
|
|
|
|
dxyz_b = numpy.meshgrid(*dxes[1], indexing='ij')
|
|
|
|
|
2016-07-03 03:01:01 -07:00
|
|
|
def dh(f, ax):
|
2016-05-30 22:30:45 -07:00
|
|
|
return (f - numpy.roll(f, 1, axis=ax)) / dxyz_b[ax]
|
|
|
|
|
2016-07-03 01:20:51 -07:00
|
|
|
def ch_fun(h: field_t) -> field_t:
|
2019-08-05 01:09:52 -07:00
|
|
|
e = numpy.empty_like(h)
|
|
|
|
e[0] = dh(h[2], 1)
|
|
|
|
e[0] -= dh(h[1], 2)
|
|
|
|
e[1] = dh(h[0], 2)
|
|
|
|
e[1] -= dh(h[2], 0)
|
|
|
|
e[2] = dh(h[1], 0)
|
|
|
|
e[2] -= dh(h[0], 1)
|
2016-07-03 01:20:51 -07:00
|
|
|
return e
|
2016-05-30 22:30:45 -07:00
|
|
|
|
|
|
|
return ch_fun
|
|
|
|
|
|
|
|
|
|
|
|
def curl_e(dxes: dx_lists_t) -> functional_matrix:
|
|
|
|
"""
|
|
|
|
Curl operator for use with the E field.
|
|
|
|
|
2019-08-04 13:48:41 -07:00
|
|
|
:param dxes: Grid parameters [dx_e, dx_h] as described in meanas.types
|
2016-05-30 22:30:45 -07:00
|
|
|
:return: Function for taking the discretized curl of the E-field, F(E) -> curlE
|
|
|
|
"""
|
|
|
|
dxyz_a = numpy.meshgrid(*dxes[0], indexing='ij')
|
|
|
|
|
2016-07-03 03:01:01 -07:00
|
|
|
def de(f, ax):
|
2016-05-30 22:30:45 -07:00
|
|
|
return (numpy.roll(f, -1, axis=ax) - f) / dxyz_a[ax]
|
|
|
|
|
2016-07-03 01:20:51 -07:00
|
|
|
def ce_fun(e: field_t) -> field_t:
|
2019-08-05 01:09:52 -07:00
|
|
|
h = numpy.empty_like(e)
|
|
|
|
h[0] = de(e[2], 1)
|
|
|
|
h[0] -= de(e[1], 2)
|
|
|
|
h[1] = de(e[0], 2)
|
|
|
|
h[1] -= de(e[2], 0)
|
|
|
|
h[2] = de(e[1], 0)
|
|
|
|
h[2] -= de(e[0], 1)
|
2016-07-03 01:20:51 -07:00
|
|
|
return h
|
2016-05-30 22:30:45 -07:00
|
|
|
|
|
|
|
return ce_fun
|
|
|
|
|
|
|
|
|
|
|
|
def e_full(omega: complex,
|
|
|
|
dxes: dx_lists_t,
|
|
|
|
epsilon: field_t,
|
|
|
|
mu: field_t = None
|
|
|
|
) -> functional_matrix:
|
|
|
|
"""
|
|
|
|
Wave operator del x (1/mu * del x) - omega**2 * epsilon, for use with E-field,
|
|
|
|
with wave equation
|
|
|
|
(del x (1/mu * del x) - omega**2 * epsilon) E = -i * omega * J
|
|
|
|
|
|
|
|
:param omega: Angular frequency of the simulation
|
2019-08-04 13:48:41 -07:00
|
|
|
:param dxes: Grid parameters [dx_e, dx_h] as described in meanas.types
|
2016-05-30 22:30:45 -07:00
|
|
|
:param epsilon: Dielectric constant
|
|
|
|
:param mu: Magnetic permeability (default 1 everywhere)
|
|
|
|
:return: Function implementing the wave operator A(E) -> E
|
|
|
|
"""
|
|
|
|
ch = curl_h(dxes)
|
|
|
|
ce = curl_e(dxes)
|
|
|
|
|
2016-07-03 01:20:51 -07:00
|
|
|
def op_1(e):
|
|
|
|
curls = ch(ce(e))
|
2019-08-05 01:09:52 -07:00
|
|
|
return curls - omega ** 2 * epsilon * e
|
2016-05-30 22:30:45 -07:00
|
|
|
|
2016-07-03 01:20:51 -07:00
|
|
|
def op_mu(e):
|
2019-08-05 01:09:52 -07:00
|
|
|
curls = ch(mu * ce(e))
|
|
|
|
return curls - omega ** 2 * epsilon * e
|
2016-05-30 22:30:45 -07:00
|
|
|
|
|
|
|
if numpy.any(numpy.equal(mu, None)):
|
|
|
|
return op_1
|
|
|
|
else:
|
|
|
|
return op_mu
|
|
|
|
|
|
|
|
|
|
|
|
def eh_full(omega: complex,
|
|
|
|
dxes: dx_lists_t,
|
|
|
|
epsilon: field_t,
|
|
|
|
mu: field_t = None
|
|
|
|
) -> functional_matrix:
|
|
|
|
"""
|
|
|
|
Wave operator for full (both E and H) field representation.
|
|
|
|
|
|
|
|
:param omega: Angular frequency of the simulation
|
2019-08-04 13:48:41 -07:00
|
|
|
:param dxes: Grid parameters [dx_e, dx_h] as described in meanas.types
|
2016-05-30 22:30:45 -07:00
|
|
|
:param epsilon: Dielectric constant
|
|
|
|
:param mu: Magnetic permeability (default 1 everywhere)
|
|
|
|
:return: Function implementing the wave operator A(E, H) -> (E, H)
|
|
|
|
"""
|
|
|
|
ch = curl_h(dxes)
|
|
|
|
ce = curl_e(dxes)
|
|
|
|
|
2016-07-03 01:20:51 -07:00
|
|
|
def op_1(e, h):
|
2019-08-05 01:09:52 -07:00
|
|
|
return (ch(h) - 1j * omega * epsilon * e,
|
|
|
|
ce(e) + 1j * omega * h)
|
2016-05-30 22:30:45 -07:00
|
|
|
|
2016-07-03 01:20:51 -07:00
|
|
|
def op_mu(e, h):
|
2019-08-05 01:09:52 -07:00
|
|
|
return (ch(h) - 1j * omega * epsilon * e,
|
|
|
|
ce(e) + 1j * omega * mu * h)
|
2016-05-30 22:30:45 -07:00
|
|
|
|
|
|
|
if numpy.any(numpy.equal(mu, None)):
|
|
|
|
return op_1
|
|
|
|
else:
|
|
|
|
return op_mu
|
|
|
|
|
|
|
|
|
|
|
|
def e2h(omega: complex,
|
|
|
|
dxes: dx_lists_t,
|
|
|
|
mu: field_t = None,
|
|
|
|
) -> functional_matrix:
|
|
|
|
"""
|
|
|
|
Utility operator for converting the E field into the H field.
|
|
|
|
For use with e_full -- assumes that there is no magnetic current M.
|
|
|
|
|
|
|
|
:param omega: Angular frequency of the simulation
|
2019-08-04 13:48:41 -07:00
|
|
|
:param dxes: Grid parameters [dx_e, dx_h] as described in meanas.types
|
2016-05-30 22:30:45 -07:00
|
|
|
:param mu: Magnetic permeability (default 1 everywhere)
|
|
|
|
:return: Function for converting E to H
|
|
|
|
"""
|
|
|
|
A2 = curl_e(dxes)
|
|
|
|
|
2016-07-03 01:20:51 -07:00
|
|
|
def e2h_1_1(e):
|
2019-08-05 01:09:52 -07:00
|
|
|
return A2(e) / (-1j * omega)
|
2016-05-30 22:30:45 -07:00
|
|
|
|
2016-07-03 01:20:51 -07:00
|
|
|
def e2h_mu(e):
|
2019-08-05 01:09:52 -07:00
|
|
|
return A2(e) / (-1j * omega * mu)
|
2016-05-30 22:30:45 -07:00
|
|
|
|
|
|
|
if numpy.any(numpy.equal(mu, None)):
|
|
|
|
return e2h_1_1
|
|
|
|
else:
|
|
|
|
return e2h_mu
|
2019-07-09 20:13:07 -07:00
|
|
|
|
|
|
|
|
|
|
|
def m2j(omega: complex,
|
|
|
|
dxes: dx_lists_t,
|
|
|
|
mu: field_t = None,
|
|
|
|
) -> functional_matrix:
|
|
|
|
"""
|
|
|
|
Utility operator for converting magnetic current (M) distribution
|
|
|
|
into equivalent electric current distribution (J).
|
|
|
|
For use with e.g. e_full().
|
|
|
|
|
|
|
|
:param omega: Angular frequency of the simulation
|
2019-08-04 13:48:41 -07:00
|
|
|
:param dxes: Grid parameters [dx_e, dx_h] as described in meanas.types
|
2019-07-09 20:13:07 -07:00
|
|
|
:param mu: Magnetic permeability (default 1 everywhere)
|
|
|
|
:return: Function for converting M to J
|
|
|
|
"""
|
|
|
|
ch = curl_h(dxes)
|
|
|
|
|
|
|
|
def m2j_mu(m):
|
2019-08-05 01:09:52 -07:00
|
|
|
J = ch(m / mu) / (-1j * omega)
|
2019-07-09 20:13:07 -07:00
|
|
|
return J
|
|
|
|
|
|
|
|
def m2j_1(m):
|
2019-08-05 01:09:52 -07:00
|
|
|
J = ch(m) / (-1j * omega)
|
2019-07-09 20:13:07 -07:00
|
|
|
return J
|
|
|
|
|
|
|
|
if numpy.any(numpy.equal(mu, None)):
|
|
|
|
return m2j_1
|
|
|
|
else:
|
|
|
|
return m2j_mu
|
|
|
|
|
|
|
|
|
2019-08-26 00:15:34 -07:00
|
|
|
def e_tfsf_source(TF_region: field_t,
|
|
|
|
omega: complex,
|
|
|
|
dxes: dx_lists_t,
|
|
|
|
epsilon: field_t,
|
|
|
|
mu: field_t = None,
|
|
|
|
) -> functional_matrix:
|
|
|
|
"""
|
|
|
|
Operator that turuns an E-field distribution into a total-field/scattered-field
|
|
|
|
(TFSF) source.
|
|
|
|
"""
|
|
|
|
# TODO documentation
|
|
|
|
A = e_full(omega, dxes, epsilon, mu)
|
|
|
|
|
|
|
|
def op(e):
|
|
|
|
neg_iwj = A(TF_region * e) - TF_region * A(e)
|
|
|
|
return neg_iwj / (-1j * omega)
|
|
|
|
|
2019-10-27 12:41:08 -07:00
|
|
|
|
|
|
|
def poynting_e_cross_h(dxes: dx_lists_t):
|
|
|
|
def exh(e: field_t, h: field_t):
|
|
|
|
s = numpy.empty_like(e)
|
|
|
|
ex = e[0] * dxes[0][0][:, None, None]
|
|
|
|
ey = e[1] * dxes[0][1][None, :, None]
|
|
|
|
ez = e[2] * dxes[0][2][None, None, :]
|
|
|
|
hx = h[0] * dxes[1][0][:, None, None]
|
|
|
|
hy = h[1] * dxes[1][1][None, :, None]
|
|
|
|
hz = h[2] * dxes[1][2][None, None, :]
|
|
|
|
s[0] = numpy.roll(ey, -1, axis=0) * hz - numpy.roll(ez, -1, axis=0) * hy
|
|
|
|
s[1] = numpy.roll(ez, -1, axis=1) * hx - numpy.roll(ex, -1, axis=1) * hz
|
|
|
|
s[2] = numpy.roll(ex, -1, axis=2) * hy - numpy.roll(ey, -1, axis=2) * hx
|
|
|
|
return s
|
|
|
|
return exh
|
|
|
|
|