Surface ODE solver

chombo-discharge provides a simple solver for ODE equations

\[\frac{\partial \vec{\phi}}{\partial t} = \vec{F},\]

where \(\vec{\phi}\) represents \(N\) unknowns on the EB. Note that the underlying data type for \(\vec{\phi}\) and \(\vec{F}\) is EBAMRIVData, see Mesh data. Such a solver is useful, for example, as a surface charge solver where \(\phi\) is the surface charge density and \(F\) is the charge flux onto the EB.

The surface charge solver is implemented as

template <int N = 1>
class SurfaceODESolver;

where \(N\) indicates the number of variables stored in each cut cell.

Instantiation

To instantiate the solver, use the default constructor

template <int N>
SurfaceODESolver<N>::SurfaceODESolver();

The solver also requires a reference to AmrMesh, and the computational geometry such that a full instantiation example is

SurfaceODESolver<1>* solver = new SurfaceODESolver<1>();

solver->setAmr(...);
solver->setComputationalGeometry(...);

Setting \(\vec{\phi}\)

Mesh-based

To set \(\vec{\phi}\) on the mesh, one can fetch the underlying data by calling

template <int N>
EBAMRIVData&
SurfaceODESolver<N>::getPhi() noexcept;

This returns a reference to the underlying data which is defined on all cut-cells. The user can then iterate through this data and set the values accordingly, see Iterating over patches.

Function-based

To set the data directly, SurfaceODESolver defines a function

template <int N>
void
SurfaceODESolver<N>::setPhi(std::function<std::array<Real, N>(const RealVect pos)>& a_func);

where the input argument represents a function \(\vec{f} = \vec{f}\left(\mathbf{x}\right)\) that returns a value for each component in \(\vec{\phi}\).

Setting \(\vec{F}\)

Mesh-based

To set \(\vec{F}\) on the mesh, one can fetch the underlying data by calling

template <int N>
EBAMRIVData&
SurfaceODESolver<N>::getRHS() noexcept;

This returns a reference to the underlying data which is defined on all cut-cells. The user can then iterate through this data and set the values accordingly, see Iterating over patches.

Function-based

To set the right-hand side directly, SurfaceODESolver defines a function

template <int N>
void
SurfaceODESolver<N>::setRHS(std::function<std::array<Real, N>(const RealVect pos)>& a_func);

where the input argument represents a function \(\vec{f} = \vec{f}\left(\mathbf{x}\right)\) that returns a value for each component in \(\vec{F}\).

Resetting cells

SurfaceODESolver has functions for setting values in the subset of the cut-cells representing dielectrics or electrodes. The function signatures are

template <int N>
void
SurfaceODESolver<N>::resetElectrodeCells(const Real a_value);

template <int N>
void
SurfaceODESolver<N>::resetDielectricCells(const Real a_value);

Calling these functions will set the data value in electrode or dielectric cells to a_value. Note that one can always call SurfaceODESolver<N>::getPhi() to iterate over other types of cell subsets.

Regridding

When regridding the SurfaceODESolver, one should call

template <int N>
void
SurfaceODESolver<N>::preRegrid(const int a_lbase, const int a_oldFinestLevel) noexcept;

before AmrMesh creates the new grids. This will store \(\vec{\phi}\) on the old mesh. After AmrMesh has generated the new grids, \(\vec{\phi}\) can be interpolated onto the new grids by calling

template <int N>
SurfaceODESolver<N>::regrid(const int a_lmin, const int a_oldFinestLevel, const int a_newFinestLevel) noexcept;

Note that when interpolating to the new grids one can choose to initialize data in the new cells using the value in the underlying coarse cells, i.e.

\[\vec{\phi}_{\mathbf{i}_{\textrm{fine}}} = \vec{\phi}_{\mathbf{i}_{\textrm{coarse}}}\]

Alternatively one can initialize the fine-grid data such that the area-weighted value of \(\vec{\phi}\) is conserved, i.e.

\[\sum_{\mathbf{i}_{\textrm{fine}}}\alpha_{\mathbf{i}_{\textrm{fine}}}\Delta x_{\textrm{fine}}^{D-1}\vec{\phi}_{\mathbf{i}_{\textrm{fine}}} = \alpha_{\mathbf{i}_{\textrm{coar}}}\Delta x_{\textrm{coar}}^{D-1}\vec{\phi}_{\mathbf{i}_{\textrm{coar}}}\]

which gives

\[\vec{\phi}_{\mathbf{i}_{\textrm{fine}}} = r^{D-1}\frac{\alpha_{\mathbf{i}_{\textrm{coar}}}}{\sum_{\mathbf{i}_{\textrm{fine}}}\alpha_{\mathbf{i}_{\textrm{fine}}}}\vec{\phi}_{\mathbf{i}_{\textrm{coar}}},\]

where \(\mathbf{i}_{\textrm{fine}}\) is set of cut-cells that occur when refining the coarse-grid cut-cell \(\mathbf{i}_{\textrm{coar}}\) and \(r\) is the refinement factor between the two grid levels. In this case \(\vec{\phi}\) is strictly conserved. Users can switch between these two methods by specifying the regridding type in the input script:

SurfaceODESolver.regridding = arithmetic

or

SurfaceODESolver.regridding = conservative

I/O

The user can add \(\vec{\phi}\) and \(\vec{F}\) to output files by specifying these in the input script. These variables are named

SurfaceODESolver.plt_vars = phi rhs

Only phi and rhs are recognized as valid arguments. If choosing to omit output variables for the solver, one can put e.g. SurfaceODESolver.plt_vars = -1.

Note

SurfaceODESolver checkpoint files only contain \(\vec{\phi}\).