Change comments to new format

This commit is contained in:
Jan Petykiewicz 2020-01-07 21:31:16 -08:00
parent dcc9e7c2ad
commit f69b8c9f11
4 changed files with 208 additions and 150 deletions

View File

@ -20,9 +20,10 @@ def perturbed_l3(a: float, radius: float, **kwargs) -> Pattern:
""" """
Generate a masque.Pattern object containing a perturbed L3 cavity. Generate a masque.Pattern object containing a perturbed L3 cavity.
:param a: Lattice constant. Args:
:param radius: Hole radius, in units of a (lattice constant). a: Lattice constant.
:param kwargs: Keyword arguments: radius: Hole radius, in units of a (lattice constant).
**kwargs: Keyword arguments:
hole_dose, trench_dose, hole_layer, trench_layer: Shape properties for Pattern. hole_dose, trench_dose, hole_layer, trench_layer: Shape properties for Pattern.
Defaults *_dose=1, hole_layer=0, trench_layer=1. Defaults *_dose=1, hole_layer=0, trench_layer=1.
shifts_a, shifts_r: passed to pcgen.l3_shift; specifies lattice constant (1 - shifts_a, shifts_r: passed to pcgen.l3_shift; specifies lattice constant (1 -
@ -30,11 +31,13 @@ def perturbed_l3(a: float, radius: float, **kwargs) -> Pattern:
holes adjacent to the defect (same row). Defaults are 0.15 shift for holes adjacent to the defect (same row). Defaults are 0.15 shift for
first hole, 0.075 shift for third hole, and no radius change. first hole, 0.075 shift for third hole, and no radius change.
xy_size: [x, y] number of mirror periods in each direction; total size is xy_size: [x, y] number of mirror periods in each direction; total size is
2 * n + 1 holes in each direction. Default [10, 10]. `2 * n + 1` holes in each direction. Default `[10, 10]`.
perturbed_radius: radius of holes perturbed to form an upwards-driected beam perturbed_radius: radius of holes perturbed to form an upwards-driected beam
(multiplicative factor). Default 1.1. (multiplicative factor). Default 1.1.
trench width: Width of the undercut trenches. Default 1.2e3. trench width: Width of the undercut trenches. Default 1.2e3.
:return: masque.Pattern object containing the L3 design
Return:
`masque.Pattern` object containing the L3 design
""" """
default_args = {'hole_dose': 1, default_args = {'hole_dose': 1,

View File

@ -4,47 +4,54 @@ Bloch eigenmode solver/operators
This module contains functions for generating and solving the This module contains functions for generating and solving the
3D Bloch eigenproblem. The approach is to transform the problem 3D Bloch eigenproblem. The approach is to transform the problem
into the (spatial) fourier domain, transforming the equation into the (spatial) fourier domain, transforming the equation
1/mu * curl(1/eps * curl(H)) = (w/c)^2 H
1/mu * curl(1/eps * curl(H)) = (w/c)^2 H
into into
conv(1/mu_k, ik x conv(1/eps_k, ik x H_k)) = (w/c)^2 H_k
conv(1/mu_k, ik x conv(1/eps_k, ik x H_k)) = (w/c)^2 H_k
where: where:
- the _k subscript denotes a 3D fourier transformed field
- each component of H_k corresponds to a plane wave with wavevector k
- x is the cross product
- conv denotes convolution
Since k and H are orthogonal for each plane wave, we can use each - the `_k` subscript denotes a 3D fourier transformed field
k to create an orthogonal basis (k, m, n), with k x m = n, and - each component of `H_k` corresponds to a plane wave with wavevector `k`
|m| = |n| = 1. The cross products are then simplified with - `x` is the cross product
- `conv()` denotes convolution
k @ h = kx hx + ky hy + kz hz = 0 = hk Since `k` and `H` are orthogonal for each plane wave, we can use each
h = hk + hm + hn = hm + hn `k` to create an orthogonal basis (k, m, n), with `k x m = n`, and
k = kk + km + kn = kk = |k| `|m| = |n| = 1`. The cross products are then simplified with
k x h = (ky hz - kz hy, k @ h = kx hx + ky hy + kz hz = 0 = hk
kz hx - kx hz, h = hk + hm + hn = hm + hn
kx hy - ky hx) k = kk + km + kn = kk = |k|
= ((k x h) @ k, (k x h) @ m, (k x h) @ n)_kmn
= (0, (m x k) @ h, (n x k) @ h)_kmn # triple product ordering
= (0, kk (-n @ h), kk (m @ h))_kmn # (m x k) = -|k| n, etc.
= |k| (0, -h @ n, h @ m)_kmn
k x h = (km hn - kn hm, k x h = (ky hz - kz hy,
kn hk - kk hn, kz hx - kx hz,
kk hm - km hk)_kmn kx hy - ky hx)
= (0, -kk hn, kk hm)_kmn = ((k x h) @ k, (k x h) @ m, (k x h) @ n)_kmn
= (-kk hn)(mx, my, mz) + (kk hm)(nx, ny, nz) = (0, (m x k) @ h, (n x k) @ h)_kmn # triple product ordering
= |k| (hm * (nx, ny, nz) - hn * (mx, my, mz)) = (0, kk (-n @ h), kk (m @ h))_kmn # (m x k) = -|k| n, etc.
= |k| (0, -h @ n, h @ m)_kmn
where h is shorthand for H_k, (...)_kmn deontes the (k, m, n) basis, k x h = (km hn - kn hm,
and e.g. hm is the component of h in the m direction. kn hk - kk hn,
kk hm - km hk)_kmn
= (0, -kk hn, kk hm)_kmn
= (-kk hn)(mx, my, mz) + (kk hm)(nx, ny, nz)
= |k| (hm * (nx, ny, nz) - hn * (mx, my, mz))
We can also simplify conv(X_k, Y_k) as fftn(X * ifftn(Y_k)). where `h` is shorthand for `H_k`, `(...)_kmn` deontes the `(k, m, n)` basis,
and e.g. `hm` is the component of `h` in the `m` direction.
We can also simplify `conv(X_k, Y_k)` as `fftn(X * ifftn(Y_k))`.
Using these results and storing `H_k` as `h = (hm, hn)`, we have
e_xyz = fftn(1/eps * ifftn(|k| (hm * n - hn * m)))
b_mn = |k| (-e_xyz @ n, e_xyz @ m)
h_mn = fftn(1/mu * ifftn(b_m * m + b_n * n))
Using these results and storing H_k as h = (hm, hn), we have
e_xyz = fftn(1/eps * ifftn(|k| (hm * n - hn * m)))
b_mn = |k| (-e_xyz @ n, e_xyz @ m)
h_mn = fftn(1/mu * ifftn(b_m * m + b_n * n))
which forms the operator from the left side of the equation. which forms the operator from the left side of the equation.
We can then use a preconditioned block Rayleigh iteration algorithm, as in We can then use a preconditioned block Rayleigh iteration algorithm, as in
@ -120,12 +127,15 @@ def generate_kmn(k0: numpy.ndarray,
""" """
Generate a (k, m, n) orthogonal basis for each k-vector in the simulation grid. Generate a (k, m, n) orthogonal basis for each k-vector in the simulation grid.
:param k0: [k0x, k0y, k0z], Bloch wavevector, in G basis. Args:
:param G_matrix: 3x3 matrix, with reciprocal lattice vectors as columns. k0: [k0x, k0y, k0z], Bloch wavevector, in G basis.
:param shape: [nx, ny, nz] shape of the simulation grid. G_matrix: 3x3 matrix, with reciprocal lattice vectors as columns.
:return: (|k|, m, n) where |k| has shape tuple(shape) + (1,) shape: [nx, ny, nz] shape of the simulation grid.
and m, n have shape tuple(shape) + (3,).
All are given in the xyz basis (e.g. |k|[0,0,0] = norm(G_matrix @ k0)). Returns:
`(|k|, m, n)` where `|k|` has shape `tuple(shape) + (1,)`
and `m`, `n` have shape `tuple(shape) + (3,)`.
All are given in the xyz basis (e.g. `|k|[0,0,0] = norm(G_matrix @ k0)`).
""" """
k0 = numpy.array(k0) k0 = numpy.array(k0)
@ -159,21 +169,27 @@ def maxwell_operator(k0: numpy.ndarray,
) -> Callable[[numpy.ndarray], numpy.ndarray]: ) -> Callable[[numpy.ndarray], numpy.ndarray]:
""" """
Generate the Maxwell operator Generate the Maxwell operator
conv(1/mu_k, ik x conv(1/eps_k, ik x ___)) conv(1/mu_k, ik x conv(1/eps_k, ik x ___))
which is the spatial-frequency-space representation of which is the spatial-frequency-space representation of
1/mu * curl(1/eps * curl(___)) 1/mu * curl(1/eps * curl(___))
The operator is a function that acts on a vector h_mn of size (2 * epsilon[0].size) The operator is a function that acts on a vector h_mn of size `2 * epsilon[0].size`
See the module-level docstring for more information. See the `meanas.fdfd.bloch` docstring for more information.
:param k0: Bloch wavevector, [k0x, k0y, k0z]. Args:
:param G_matrix: 3x3 matrix, with reciprocal lattice vectors as columns. k0: Bloch wavevector, `[k0x, k0y, k0z]`.
:param epsilon: Dielectric constant distribution for the simulation. G_matrix: 3x3 matrix, with reciprocal lattice vectors as columns.
All fields are sampled at cell centers (i.e., NOT Yee-gridded) epsilon: Dielectric constant distribution for the simulation.
:param mu: Magnetic permability distribution for the simulation. All fields are sampled at cell centers (i.e., NOT Yee-gridded)
Default None (1 everywhere). mu: Magnetic permability distribution for the simulation.
:return: Function which applies the maxwell operator to h_mn. Default None (1 everywhere).
Returns:
Function which applies the maxwell operator to h_mn.
""" """
shape = epsilon[0].shape + (1,) shape = epsilon[0].shape + (1,)
@ -189,8 +205,11 @@ def maxwell_operator(k0: numpy.ndarray,
h is complex 2-field in (m, n) basis, vectorized h is complex 2-field in (m, n) basis, vectorized
:param h: Raveled h_mn; size (2 * epsilon[0].size). Args:
:return: Raveled conv(1/mu_k, ik x conv(1/eps_k, ik x h_mn)). h: Raveled h_mn; size `2 * epsilon[0].size`.
Returns:
Raveled conv(1/mu_k, ik x conv(1/eps_k, ik x h_mn)).
""" """
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)]
@ -231,18 +250,22 @@ def hmn_2_exyz(k0: numpy.ndarray,
) -> Callable[[numpy.ndarray], fdfield_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.
ifft(conv(1/eps_k, ik x h_mn)) ifft(conv(1/eps_k, ik x h_mn))
The operator is a function that acts on a vector h_mn of size (2 * epsilon[0].size) The operator is a function that acts on a vector `h_mn` of size `2 * epsilon[0].size`.
See the module-level docstring for more information. See the `meanas.fdfd.bloch` docstring for more information.
:param k0: Bloch wavevector, [k0x, k0y, k0z]. Args:
:param G_matrix: 3x3 matrix, with reciprocal lattice vectors as columns. k0: Bloch wavevector, `[k0x, k0y, k0z]`.
:param epsilon: Dielectric constant distribution for the simulation. G_matrix: 3x3 matrix, with reciprocal lattice vectors as columns.
All fields are sampled at cell centers (i.e., NOT Yee-gridded) epsilon: Dielectric constant distribution for the simulation.
:return: Function for converting h_mn into E_xyz All fields are sampled at cell centers (i.e., NOT Yee-gridded)
Returns:
Function for converting `h_mn` into `E_xyz`
""" """
shape = epsilon[0].shape + (1,) shape = epsilon[0].shape + (1,)
epsilon = numpy.stack(epsilon, 3) epsilon = numpy.stack(epsilon, 3)
@ -266,18 +289,22 @@ def hmn_2_hxyz(k0: numpy.ndarray,
) -> Callable[[numpy.ndarray], fdfield_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.
ifft(h_mn) ifft(h_mn)
The operator is a function that acts on a vector h_mn of size (2 * epsilon[0].size) The operator is a function that acts on a vector `h_mn` of size `2 * epsilon[0].size`.
See the module-level docstring for more information. See the `meanas.fdfd.bloch` docstring for more information.
:param k0: Bloch wavevector, [k0x, k0y, k0z]. Args:
:param G_matrix: 3x3 matrix, with reciprocal lattice vectors as columns. k0: Bloch wavevector, `[k0x, k0y, k0z]`.
:param epsilon: Dielectric constant distribution for the simulation. G_matrix: 3x3 matrix, with reciprocal lattice vectors as columns.
Only epsilon[0].shape is used. epsilon: Dielectric constant distribution for the simulation.
:return: Function for converting h_mn into H_xyz Only `epsilon[0].shape` is used.
Returns:
Function for converting `h_mn` into `H_xyz`
""" """
shape = epsilon[0].shape + (1,) shape = epsilon[0].shape + (1,)
_k_mag, m, n = generate_kmn(k0, G_matrix, shape) _k_mag, m, n = generate_kmn(k0, G_matrix, shape)
@ -298,18 +325,23 @@ def inverse_maxwell_operator_approx(k0: numpy.ndarray,
) -> 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,
ik x conv(eps_k, ik x conv(mu_k, ___)) ik x conv(eps_k, ik x conv(mu_k, ___))
which can be used to improve the speed of ARPACK in shift-invert mode. which can be used to improve the speed of ARPACK in shift-invert mode.
See the module-level docstring for more information. See the `meanas.fdfd.bloch` docstring for more information.
:param k0: Bloch wavevector, [k0x, k0y, k0z]. Args:
:param G_matrix: 3x3 matrix, with reciprocal lattice vectors as columns. k0: Bloch wavevector, `[k0x, k0y, k0z]`.
:param epsilon: Dielectric constant distribution for the simulation. G_matrix: 3x3 matrix, with reciprocal lattice vectors as columns.
All fields are sampled at cell centers (i.e., NOT Yee-gridded) epsilon: Dielectric constant distribution for the simulation.
:param mu: Magnetic permability distribution for the simulation. All fields are sampled at cell centers (i.e., NOT Yee-gridded)
Default None (1 everywhere). mu: Magnetic permability distribution for the simulation.
:return: Function which applies the approximate inverse of the maxwell operator to h_mn. Default None (1 everywhere).
Returns:
Function which applies the approximate inverse of the maxwell operator to `h_mn`.
""" """
shape = epsilon[0].shape + (1,) shape = epsilon[0].shape + (1,)
epsilon = numpy.stack(epsilon, 3) epsilon = numpy.stack(epsilon, 3)
@ -325,8 +357,11 @@ def inverse_maxwell_operator_approx(k0: numpy.ndarray,
h is complex 2-field in (m, n) basis, vectorized h is complex 2-field in (m, n) basis, vectorized
:param h: Raveled h_mn; size (2 * epsilon[0].size). Args:
:return: Raveled ik x conv(eps_k, ik x conv(mu_k, h_mn)) h: Raveled h_mn; size `2 * epsilon[0].size`.
Returns:
Raveled ik x conv(eps_k, ik x conv(mu_k, h_mn))
""" """
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)]
@ -376,16 +411,20 @@ def find_k(frequency: float,
""" """
Search for a bloch vector that has a given frequency. Search for a bloch vector that has a given frequency.
:param frequency: Target frequency. Args:
:param tolerance: Target frequency tolerance. frequency: Target frequency.
:param direction: k-vector direction to search along. tolerance: Target frequency tolerance.
:param G_matrix: 3x3 matrix, with reciprocal lattice vectors as columns. direction: k-vector direction to search along.
:param epsilon: Dielectric constant distribution for the simulation. G_matrix: 3x3 matrix, with reciprocal lattice vectors as columns.
All fields are sampled at cell centers (i.e., NOT Yee-gridded) epsilon: Dielectric constant distribution for the simulation.
:param mu: Magnetic permability distribution for the simulation. All fields are sampled at cell centers (i.e., NOT Yee-gridded)
Default None (1 everywhere). mu: Magnetic permability distribution for the simulation.
:param band: Which band to search in. Default 0 (lowest frequency). Default None (1 everywhere).
return: (k, actual_frequency) The found k-vector and its frequency band: Which band to search in. Default 0 (lowest frequency).
Returns:
`(k, actual_frequency)`
The found k-vector and its frequency.
""" """
direction = numpy.array(direction) / norm(direction) direction = numpy.array(direction) / norm(direction)
@ -419,16 +458,19 @@ def eigsolve(num_modes: int,
Find the first (lowest-frequency) num_modes eigenmodes with Bloch wavevector Find the first (lowest-frequency) num_modes eigenmodes with Bloch wavevector
k0 of the specified structure. k0 of the specified structure.
:param k0: Bloch wavevector, [k0x, k0y, k0z]. Args:
:param G_matrix: 3x3 matrix, with reciprocal lattice vectors as columns. k0: Bloch wavevector, `[k0x, k0y, k0z]`.
:param epsilon: Dielectric constant distribution for the simulation. G_matrix: 3x3 matrix, with reciprocal lattice vectors as columns.
All fields are sampled at cell centers (i.e., NOT Yee-gridded) epsilon: Dielectric constant distribution for the simulation.
:param mu: Magnetic permability distribution for the simulation. All fields are sampled at cell centers (i.e., NOT Yee-gridded)
Default None (1 everywhere). mu: Magnetic permability distribution for the simulation.
:param tolerance: Solver stops when fractional change in the objective Default `None` (1 everywhere).
trace(Z.H @ A @ Z @ inv(Z Z.H)) is smaller than the tolerance tolerance: Solver stops when fractional change in the objective
:return: (eigenvalues, eigenvectors) where eigenvalues[i] corresponds to the `trace(Z.H @ A @ Z @ inv(Z Z.H))` is smaller than the tolerance
vector eigenvectors[i, :]
Returns:
`(eigenvalues, eigenvectors)` where `eigenvalues[i]` corresponds to the
vector `eigenvectors[i, :]`
""" """
h_size = 2 * epsilon[0].size h_size = 2 * epsilon[0].size

View File

@ -21,27 +21,31 @@ def near_to_farfield(E_near: fdfield_t,
The input fields should be complex phasors. The input fields should be complex phasors.
:param E_near: List of 2 ndarrays containing the 2D phasor field slices for the transverse Args:
E fields (e.g. [Ex, Ey] for calculating the farfield toward the z-direction). E_near: List of 2 ndarrays containing the 2D phasor field slices for the transverse
:param H_near: List of 2 ndarrays containing the 2D phasor field slices for the transverse E fields (e.g. [Ex, Ey] for calculating the farfield toward the z-direction).
H fields (e.g. [Hx, hy] for calculating the farfield towrad the z-direction). H_near: List of 2 ndarrays containing the 2D phasor field slices for the transverse
:param dx: Cell size along x-dimension, in units of wavelength. H fields (e.g. [Hx, hy] for calculating the farfield towrad the z-direction).
:param dy: Cell size along y-dimension, in units of wavelength. dx: Cell size along x-dimension, in units of wavelength.
:param padded_size: Shape of the output. A single integer `n` will be expanded to `(n, n)`. dy: Cell size along y-dimension, in units of wavelength.
Powers of 2 are most efficient for FFT computation. padded_size: Shape of the output. A single integer `n` will be expanded to `(n, n)`.
Default is the smallest power of 2 larger than the input, for each axis. Powers of 2 are most efficient for FFT computation.
:returns: Dict with keys Default is the smallest power of 2 larger than the input, for each axis.
'E_far': Normalized E-field farfield; multiply by
(i k exp(-i k r) / (4 pi r)) to get the actual field value. Returns:
'H_far': Normalized H-field farfield; multiply by Dict with keys
(i k exp(-i k r) / (4 pi r)) to get the actual field value.
'kx', 'ky': Wavevector values corresponding to the x- and y- axes in E_far and H_far, - `E_far`: Normalized E-field farfield; multiply by
normalized to wavelength (dimensionless). (i k exp(-i k r) / (4 pi r)) to get the actual field value.
'dkx', 'dky': step size for kx and ky, normalized to wavelength. - `H_far`: Normalized H-field farfield; multiply by
'theta': arctan2(ky, kx) corresponding to each (kx, ky). (i k exp(-i k r) / (4 pi r)) to get the actual field value.
This is the angle in the x-y plane, counterclockwise from above, starting from +x. - `kx`, `ky`: Wavevector values corresponding to the x- and y- axes in E_far and H_far,
'phi': arccos(kz / k) corresponding to each (kx, ky). normalized to wavelength (dimensionless).
This is the angle away from +z. - `dkx`, `dky`: step size for kx and ky, normalized to wavelength.
- `theta`: arctan2(ky, kx) corresponding to each (kx, ky).
This is the angle in the x-y plane, counterclockwise from above, starting from +x.
- `phi`: arccos(kz / k) corresponding to each (kx, ky).
This is the angle away from +z.
""" """
if not len(E_near) == 2: if not len(E_near) == 2:
@ -129,23 +133,27 @@ def far_to_nearfield(E_far: fdfield_t,
The input fields should be complex phasors. The input fields should be complex phasors.
:param E_far: List of 2 ndarrays containing the 2D phasor field slices for the transverse Args:
E fields (e.g. [Ex, Ey] for calculating the nearfield toward the z-direction). E_far: List of 2 ndarrays containing the 2D phasor field slices for the transverse
Fields should be normalized so that E fields (e.g. [Ex, Ey] for calculating the nearfield toward the z-direction).
E_far = E_far_actual / (i k exp(-i k r) / (4 pi r)) Fields should be normalized so that
:param H_far: List of 2 ndarrays containing the 2D phasor field slices for the transverse E_far = E_far_actual / (i k exp(-i k r) / (4 pi r))
H fields (e.g. [Hx, hy] for calculating the nearfield toward the z-direction). H_far: List of 2 ndarrays containing the 2D phasor field slices for the transverse
Fields should be normalized so that H fields (e.g. [Hx, hy] for calculating the nearfield toward the z-direction).
H_far = H_far_actual / (i k exp(-i k r) / (4 pi r)) Fields should be normalized so that
:param dkx: kx discretization, in units of wavelength. H_far = H_far_actual / (i k exp(-i k r) / (4 pi r))
:param dky: ky discretization, in units of wavelength. dkx: kx discretization, in units of wavelength.
:param padded_size: Shape of the output. A single integer `n` will be expanded to `(n, n)`. dky: ky discretization, in units of wavelength.
Powers of 2 are most efficient for FFT computation. padded_size: Shape of the output. A single integer `n` will be expanded to `(n, n)`.
Default is the smallest power of 2 larger than the input, for each axis. Powers of 2 are most efficient for FFT computation.
:returns: Dict with keys Default is the smallest power of 2 larger than the input, for each axis.
'E': E-field nearfield
'H': H-field nearfield Returns:
'dx', 'dy': spatial discretization, normalized to wavelength (dimensionless) Dict with keys
- `E`: E-field nearfield
- `H`: H-field nearfield
- `dx`, `dy`: spatial discretization, normalized to wavelength (dimensionless)
""" """
if not len(E_far) == 2: if not len(E_far) == 2:

View File

@ -1,6 +1,6 @@
""" """
Functions for moving between a vector field (list of 3 ndarrays, [f_x, f_y, f_z]) Functions for moving between a vector field (list of 3 ndarrays, `[f_x, f_y, f_z]`)
and a 1D array representation of that field [f_x0, f_x1, f_x2,... f_y0,... f_z0,...]. and a 1D array representation of that field `[f_x0, f_x1, f_x2,... f_y0,... f_z0,...]`.
Vectorized versions of the field use row-major (ie., C-style) ordering. Vectorized versions of the field use row-major (ie., C-style) ordering.
""" """
@ -17,11 +17,14 @@ 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.
Returns None if called with f=None. Returns `None` if called with `f=None`.
:param f: A vector field, [f_x, f_y, f_z] where each f_ component is a 1 to Args:
3D ndarray (f_* should all be the same size). Doesn't fail with f=None. f: A vector field, `[f_x, f_y, f_z]` where each `f_` component is a 1- to
:return: A 1D ndarray containing the linearized field (or None) 3-D ndarray (`f_*` should all be the same size). Doesn't fail with `f=None`.
Returns:
1D ndarray containing the linearized field (or `None`)
""" """
if numpy.any(numpy.equal(f, None)): if numpy.any(numpy.equal(f, None)):
return None return None
@ -31,15 +34,17 @@ def vec(f: fdfield_t) -> vfdfield_t:
def unvec(v: vfdfield_t, shape: numpy.ndarray) -> fdfield_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
ndarray. ndarray.
Returns None if called with v=None. Returns `None` if called with `v=None`.
:param v: 1D ndarray representing a 3D vector field of shape shape (or None) Args:
:param shape: shape of the vector field v: 1D ndarray representing a 3D vector field of shape shape (or None)
:return: [f_x, f_y, f_z] where each f_ is a len(shape) dimensional ndarray shape: shape of the vector field
(or None)
Returns:
`[f_x, f_y, f_z]` where each `f_` is a `len(shape)` dimensional ndarray (or `None`)
""" """
if numpy.any(numpy.equal(v, None)): if numpy.any(numpy.equal(v, None)):
return None return None