From 1242e8794bd10cde1662957db1c7b715deeab212 Mon Sep 17 00:00:00 2001 From: Jan Petykiewicz Date: Tue, 26 Nov 2019 01:47:52 -0800 Subject: [PATCH] documentation updates --- meanas/fdmath/__init__.py | 94 +++++++++++++++++++++++++++++++++++++++ meanas/types.py | 31 +++++++++---- 2 files changed, 117 insertions(+), 8 deletions(-) create mode 100644 meanas/fdmath/__init__.py diff --git a/meanas/fdmath/__init__.py b/meanas/fdmath/__init__.py new file mode 100644 index 0000000..74deff1 --- /dev/null +++ b/meanas/fdmath/__init__.py @@ -0,0 +1,94 @@ +""" +Basic discrete calculus for finite difference (fd) simulations. + +This documentation and approach is roughly based on W.C. Chew's excellent +"Electromagnetic Theory on a Lattice" (doi:10.1063/1.355770), +which covers a superset of this material with similar notation and more detail. + + +Define the discrete forward derivative as + + Dx_forward(f)[i] = (f[i + 1] - f[i]) / dx[i] + +or + $$ [\\tilde{\\partial}_x f ]_{m + \\frac{1}{2}} = \\frac{1}{\\Delta_{x, m}} (f_{m + 1} - f_m) $$ + +Likewise, discrete reverse derivative is + + Dx_back(f)[i] = (f[i] - f[i - 1]) / dx[i] + +or + $$ [\\hat{\\partial}_x f ]_{m - \\frac{1}{2}} = \\frac{1}{\\Delta_{x, m}} (f_{m} - f_{m - 1}) $$ + + +The derivatives are shifted by a half-cell relative to the original function: + + _________________________ + | | | | | + | f0 | f1 | f2 | f3 | + |_____|_____|_____|_____| + | | | | + | Df0 | Df1 | Df2 | Df3 + ___|_____|_____|_____|____ + +Periodic boundaries are used unless otherwise noted. + + +Expanding to three dimensions, we can define two gradients + $$ [\\tilde{\\nabla} f]_{n,m,p} = \\vec{x} [\\tilde{\\partial}_x f]_{m + \\frac{1}{2},n,p} + + \\vec{y} [\\tilde{\\partial}_y f]_{m,n + \\frac{1}{2},p} + + \\vec{z} [\\tilde{\\partial}_z f]_{m,n,p + \\frac{1}{2}} $$ + $$ [\\hat{\\nabla} f]_{m,n,p} = \\vec{x} [\\hat{\\partial}_x f]_{m + \\frac{1}{2},n,p} + + \\vec{y} [\\hat{\\partial}_y f]_{m,n + \\frac{1}{2},p} + + \\vec{z} [\\hat{\\partial}_z f]_{m,n,p + \\frac{1}{2}} $$ + +The three derivatives in the gradient cause shifts in different +directions, so the x/y/z components of the resulting "vector" are defined +at different points: the x-component is shifted in the x-direction, +y in y, and z in z. + +We call the resulting object a "fore-vector" or "back-vector", depending +on the direction of the shift. We write it as + $$ \\tilde{g}_{m,n,p} = \\vec{x} g^x_{m + \\frac{1}{2},n,p} + + \\vec{y} g^y_{m,n + \\frac{1}{2},p} + + \\vec{z} g^z_{m,n,p + \\frac{1}{2}} $$ + $$ \\hat{g}_{m,n,p} = \\vec{x} g^x_{m - \\frac{1}{2},n,p} + + \\vec{y} g^y_{m,n - \\frac{1}{2},p} + + \\vec{z} g^z_{m,n,p - \\frac{1}{2}} $$ + + +There are also two divergences, + + $$ d_{n,m,p} = [\\tilde{\\nabla} \\cdot \\hat{g}]_{n,m,p} + = [\\tilde{\\partial}_x g^x]_{m,n,p} + + [\\tilde{\\partial}_y g^y]_{m,n,p} + + [\\tilde{\\partial}_z g^z]_{m,n,p} $$ + + $$ d_{n,m,p} = [\\hat{\\nabla} \\cdot \\tilde{g}]_{n,m,p} + = [\\hat{\\partial}_x g^x]_{m,n,p} + + [\\hat{\\partial}_y g^y]_{m,n,p} + + [\\hat{\\partial}_z g^z]_{m,n,p} $$ + +Since we applied the forward divergence to the back-vector (and vice-versa), the resulting scalar value +is defined at the back-vector's (fore-vectors) location \\( (m,n,p) \\) and not at the locations of its components +\\( (m \\pm \\frac{1}{2},n,p) \\) etc. + + +The two curls are then + $$ \\begin{align} + \\hat{h}_{m + \\frac{1}{2}, n + \\frac{1}{2}, p + \\frac{1}{2}} &= \\\\ + [\\tilde{\\nabla} \\times \\tilde{g}]_{m + \\frac{1}{2}, n + \\frac{1}{2}, p + \\frac{1}{2}} &= + \\vec{x} (\\tilde{\\partial}_y g^z_{m,n,p + \\frac{1}{2}} - \\tilde{\\partial}_z g^y_{m,n + \\frac{1}{2},p}) \\\\ + &+ \\vec{y} (\\tilde{\\partial}_z g^x_{m + \\frac{1}{2},n,p} - \\tilde{\\partial}_x g^z_{m,n,p + \\frac{1}{2}}) \\\\ + &+ \\vec{z} (\\tilde{\\partial}_x g^y_{m,n + \\frac{1}{2},p} - \\tilde{\\partial}_x g^z_{m + \\frac{1}{2},n,p}) + \\end{align}$$ +and + $$ \\tilde{h}_{m - \\frac{1}{2}, n - \\frac{1}{2}, p - \\frac{1}{2}} = + [\\hat{\\nabla} \\times \\hat{g}]_{m - \\frac{1}{2}, n - \\frac{1}{2}, p - \\frac{1}{2}} $$ + + where \\( \\hat{g} \\) and \\( \\tilde{g} \\) are located at \\((m,n,p)\\) + with components at \\( (m \\pm \\frac{1}{2},n,p) \\) etc., + while \\( \\hat{h} \\) and \\( \\tilde{h} \\) are located at \\((m \pm \\frac{1}{2}, n \\pm \\frac{1}{2}, p \\pm \\frac{1}{2})\\) + with components at \\((m, n \\pm \\frac{1}{2}, p \\pm \\frac{1}{2})\\) etc. + +""" diff --git a/meanas/types.py b/meanas/types.py index 667a286..2a1351f 100644 --- a/meanas/types.py +++ b/meanas/types.py @@ -6,19 +6,34 @@ from typing import List, Callable # Field types -field_t = numpy.ndarray -"""vector field with shape (3, X, Y, Z) (e.g. `[E_x, E_y, E_z]`)""" +# TODO: figure out a better way to set the docstrings without creating actual subclasses? +# Probably not a big issue since they're only used for type hinting +class field_t(numpy.ndarray): + """ + Vector field with shape (3, X, Y, Z) (e.g. `[E_x, E_y, E_z]`) + + This is actually is just an unaltered `numpy.ndarray` + """ + pass + +class vfield_t(numpy.ndarray): + """ + Linearized vector field (single vector of length 3*X*Y*Z) + + This is actually just an unaltered `numpy.ndarray` + """ + pass -vfield_t = numpy.ndarray -"""Linearized vector field (vector of length 3*X*Y*Z)""" dx_lists_t = List[List[numpy.ndarray]] ''' 'dxes' datastructure which contains grid cell width information in the following format: - `[[[dx_e_0, dx_e_1, ...], [dy_e_0, ...], [dz_e_0, ...]], - [[dx_h_0, dx_h_1, ...], [dy_h_0, ...], [dz_h_0, ...]]]` - where `dx_e_0` is the x-width of the `x=0` cells, as used when calculating dE/dx, - and `dy_h_0` is the y-width of the `y=0` cells, as used when calculating dH/dy, etc. + + [[[dx_e_0, dx_e_1, ...], [dy_e_0, ...], [dz_e_0, ...]], + [[dx_h_0, dx_h_1, ...], [dy_h_0, ...], [dz_h_0, ...]]] + + where `dx_e_0` is the x-width of the `x=0` cells, as used when calculating dE/dx, + and `dy_h_0` is the y-width of the `y=0` cells, as used when calculating dH/dy, etc. '''