fdfd_tools/meanas/fdfd/functional.py

222 lines
6.6 KiB
Python
Raw Normal View History

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.
The functions generated here expect `field_t` inputs with shape (3, X, Y, Z),
2019-08-04 13:48:41 -07:00
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, Tuple
2016-05-30 22:30:45 -07:00
import numpy
2019-08-05 00:20:06 -07:00
from .. import dx_lists_t, field_t
from ..fdmath.functional import curl_forward, curl_back
2016-05-30 22:30:45 -07:00
__author__ = 'Jan Petykiewicz'
field_transform_t = Callable[[field_t], field_t]
2016-05-30 22:30:45 -07:00
def e_full(omega: complex,
dxes: dx_lists_t,
epsilon: field_t,
mu: field_t = None
) -> field_transform_t:
2016-05-30 22:30:45 -07:00
"""
Wave operator for use with E-field. See `operators.e_full` for details.
Args:
omega: Angular frequency of the simulation
dxes: Grid parameters [dx_e, dx_h] as described in meanas.types
epsilon: Dielectric constant
mu: Magnetic permeability (default 1 everywhere)
Return:
Function `f` implementing the wave operator
`f(E)` -> `-i * omega * J`
2016-05-30 22:30:45 -07:00
"""
ch = curl_back(dxes[1])
ce = curl_forward(dxes[0])
2016-05-30 22:30:45 -07:00
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
) -> Callable[[field_t, field_t], Tuple[field_t, field_t]]:
2016-05-30 22:30:45 -07:00
"""
Wave operator for full (both E and H) field representation.
See `operators.eh_full`.
Args:
omega: Angular frequency of the simulation
dxes: Grid parameters [dx_e, dx_h] as described in meanas.types
epsilon: Dielectric constant
mu: Magnetic permeability (default 1 everywhere)
2016-05-30 22:30:45 -07:00
Returns:
Function `f` implementing the wave operator
`f(E, H)` -> `(J, -M)`
2016-05-30 22:30:45 -07:00
"""
ch = curl_back(dxes[1])
ce = curl_forward(dxes[0])
2016-05-30 22:30:45 -07:00
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,
) -> field_transform_t:
2016-05-30 22:30:45 -07:00
"""
Utility operator for converting the `E` field into the `H` field.
For use with `e_full` -- assumes that there is no magnetic current `M`.
2016-05-30 22:30:45 -07:00
Args:
omega: Angular frequency of the simulation
dxes: Grid parameters `[dx_e, dx_h]` as described in `meanas.types`
mu: Magnetic permeability (default 1 everywhere)
Return:
Function `f` for converting `E` to `H`,
`f(E)` -> `H`
"""
ce = curl_forward(dxes[0])
2016-05-30 22:30:45 -07:00
2016-07-03 01:20:51 -07:00
def e2h_1_1(e):
return ce(e) / (-1j * omega)
2016-05-30 22:30:45 -07:00
2016-07-03 01:20:51 -07:00
def e2h_mu(e):
return ce(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,
) -> field_transform_t:
2019-07-09 20:13:07 -07:00
"""
Utility operator for converting magnetic current `M` distribution
into equivalent electric current distribution `J`.
For use with e.g. `e_full`.
Args:
omega: Angular frequency of the simulation
dxes: Grid parameters `[dx_e, dx_h]` as described in `meanas.types`
mu: Magnetic permeability (default 1 everywhere)
Returns:
Function `f` for converting `M` to `J`,
`f(M)` -> `J`
"""
ch = curl_back(dxes[1])
2019-07-09 20:13:07 -07:00
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,
) -> field_transform_t:
2019-08-26 00:15:34 -07:00
"""
Operator that turns an E-field distribution into a total-field/scattered-field
2019-08-26 00:15:34 -07:00
(TFSF) source.
Args:
TF_region: mask which is set to 1 in the total-field region, and 0 elsewhere
(i.e. in the scattered-field region).
Should have the same shape as the simulation grid, e.g. `epsilon[0].shape`.
omega: Angular frequency of the simulation
dxes: Grid parameters `[dx_e, dx_h]` as described in `meanas.types`
epsilon: Dielectric constant distribution
mu: Magnetic permeability (default 1 everywhere)
Returns:
Function `f` which takes an E field and returns a current distribution,
`f(E)` -> `J`
2019-08-26 00:15:34 -07:00
"""
# 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) -> Callable[[field_t, field_t], field_t]:
"""
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
for the Poynting vector, \\( S = E \\times H \\)
Note:
This function also shifts the input `E` field by one cell as required
for computing the Poynting cross product (see `meanas.fdfd` module docs).
Note:
If `E` and `H` are peak amplitudes as assumed elsewhere in this code,
the time-average of the poynting vector is `<S> = Re(S)/2 = Re(E x H) / 2`.
The factor of `1/2` can be omitted if root-mean-square quantities are used
instead.
Args:
dxes: Grid parameters `[dx_e, dx_h]` as described in `meanas.types`
Returns:
Function `f` that returns E x H as required for the poynting vector.
"""
2019-10-27 12:41:08 -07:00
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