This document provides an explanation of adding a physics application through example. Here we discuss the augmentation of the ideal MHD equations for the inclusion of divergence cleaning, as per Dedner.
WARPXM is designed to solve partial differential equations describing flow a plasma in an from an Eulerian point of view, written in conservative form. Thus, for a conserved variable \(\boldsymbol{q}\), equations are written in the form
\begin{align*}
\frac{\partial \boldsymbol{q}}{\partial t} + \nabla\cdot \mathcal{F}(\boldsymbol{q})
=&
\boldsymbol{S}
\end{align*}
where \(\mathcal{F}\) represents a flux and \(\boldsymbol{S}\) represents a source. In terms of actual index notation this is written
\begin{align*}
\frac{\partial q_{i}}{\partial t} + \frac{\partial \mathcal{F}_{ij}}{\partial x_{j}}
=&
S_{i}
\end{align*}
These equations are solved on an unstructured grid, using the discontinuous Galerkin finite elmement method. When implementing a physics application, intimate details of the method are not required, but instead the flux and terms of the particular model must be written in. Specifically one needs to implement the flux term, dubbed "internal flux", tthe source term, and the "numerical flux". The numerical flux, as the name implies is the part that requires some understanding of the numerical method. This is a resolution of internal flux at element boundaries which must be consistent between elements.
Development Process
- Create a files generating internal flux, numerical flux, and sources
- Register the files
- Add to CMake
- Add to WARPY
- Add warpy function to init
- Add warpy function to CMake
Example 1 - Linear Advection
In this case our system for a single component q linearly advected with no source, written as:
\begin{align*}
\frac{\partial q}{\partial t} + \nabla\cdot \left(\boldsymbol{c}q\right)
=&
0
\end{align*}
or
\begin{align*}
\frac{\partial q}{\partial t}
+ \frac{\partial \left(\mathcal{c}_{1} q\right)}{\partial x}
+ \frac{\partial \left(\mathcal{c}_{2} q\right)}{\partial y}
+ \frac{\partial \left(\mathcal{c}_{3} q\right)}{\partial z}
=&
0
\end{align*}
or
\begin{align*}
\frac{\partial q}{\partial t}
+
\begin{pmatrix}
c_{1}q & c_{2}q & c_{3}q
\end{pmatrix}
\begin{pmatrix}
\frac{\partial}{\partial x} \\
\frac{\partial}{\partial y} \\
\frac{\partial}{\partial z}
\end{pmatrix}
=&
0
\end{align*}
Implementation of the internal flux, \(\mathcal{F}\), looks is written as a function developing the 2-dimensinal flux array of [number of dimensions] x [number of conserved variables] for a given set of conserved variables \(q\) and auxialliary variables \(\text{aux}\). NOTE: This is actually the transpose of the form as written in the flux equation (i is conserved variables, j is dimensions).
Internal and numerical flux
(src/apps/simple/advection.h)
#ifndef WXM_APPS_ADVECTION_H
#define WXM_APPS_ADVECTION_H
{
namespace apps
{
{
public:
{
}
{
}
real* numericalFlux)
const override;
std::vector<std::vector<real>>& internalFlux) const override;
protected:
private:
};
}
}
#endif
Base Class for physics applications.
Definition: wmapplication.h:93
WxCryptSet extends WxCrypt by providing, in addition to name-value pairs, an set of named WxCryptSets...
Definition: wxcryptset.h:35
std::vector< real > _velocity
Definition: advection.h:43
const std::vector< int > & getOutputVariableIndexes(int flag) const override
Definition: advection.h:26
void setup(const WxCryptSet &wxc) override
real internal_flux(const real *q, const real *aux, const solverVariables_t *pSV, std::vector< std::vector< real > > &internalFlux) const override
real numerical_flux(const real *q_l, const real *q_r, const real *aux_l, const real *aux_r, const solverVariables_t *pFV, real *numericalFlux) const override
std::vector< int > _variables
Definition: advection.h:45
const std::vector< int > & getInputVariableIndexes(int flag) const override
Definition: advection.h:21
Base namespace for everything not included in the global namespace.
Definition: field_source.h:8
Definition: wmapplication.h:38
#define real
Definition: wmoclunstructuredreconstruction.h:11
(src/apps/simple/advection.cc)
#include "advection.h"
#include <cmath>
#include <iostream>
{
namespace apps
{
{
}
{
}
{
}
{
velocity_x * pSV->
R[0][0] + velocity_y * pSV->
R[0][1] + velocity_z * pSV->
R[0][2];
const real absc = fabs(nc);
numericalFlux[0] = 0.5 * (q_r[0] * (nc - absc) + q_l[0] * (nc + absc));
}
{
for (
size_t i = 0; i < pSV->
num_dims; ++i)
{
}
return INFINITY;
}
}
}
std::vector< int > _allowedFlags
Definition: wmapplication.h:234
Application_Variable WmApplication_Variable
Definition: wmapplication.h:95
virtual void setup(const WxCryptSet &wxc)
std::vector< VALUETYPE > getVec(const std::string &name) const
Retrieve list of values associated with name.
Definition: wxcrypt.h:162
std::vector< int > getIndexes() const
Definition: app_base.h:29
Definition: advection.h:13
warpy q
Definition: lobc_advection_1D.py:51
real R[3][3]
Definition: wmapplication.h:43
real dxn
Definition: wmapplication.h:42
real cfl_max
Definition: wmapplication.h:44
int num_dims
Definition: wmapplication.h:48
@ WMAPPLICATIONFLAG_INTERNALFLUX
Definition: wmapplication.h:22
@ WMAPPLICATIONFLAG_NUMERICALFLUX
Definition: wmapplication.h:19
#define REGISTER_CREATOR(name, specialization, base)
Definition: wxcreator.h:317
Registration - The last lines in the advection.cc file registers the scheme:
(src/apps/simple/advection.cc)
Add to Cmake
(src/apps/simple/CMakeLists.txt)
add_sources(
advection.cc
)
Add to warpy
(tools/warpy/apps/simple.py)
from .application import application
class advection(application):
'''
@brief Advection
@param name Name of module
@param variable Variable
@param velocity Velocity (list of 1, 2, or 3 values)
@param components Components of variable
'''
def __init__(self, name, velocity, variable, components=None):
super(advection, self).__init__(
name,
req_attrs=[('Kind', 'advection'),
('Velocity', velocity)],
variables=[('Density', variable, components)])
Add warpy function to init
(tools/warpy/apps/__init__.py)
from .simple import advection
Add warpy function to CMake
(tools/warpy/apps/CMakeLists.txt)
WARPY_FILES(__init__.py
application.py
simple.py
)
Example 2 - Ideal MHD
WARPXM has an Ideal MHD module upon which additional terms can be added, such as resistive diffusion, Hall terms, divergence cleaning, viscous terms, etc. The base Ideal MHD equations can be written as as conservation of:
Continuity
\begin{align*}
\frac{\partial \rho}{\partial t} + \nabla\cdot\left(\rho \boldsymbol{u}\right)
=&
0
\end{align*}
Momentum
\begin{align*}
\frac{\partial\left(\rho \boldsymbol{u}\right)}{\partial t}
+
\nabla\cdot
\left[\rho \boldsymbol{u}\boldsymbol{u} + p \mathcal{I} - \left(\boldsymbol{B}\boldsymbol{B}-\frac{1}{2}B^{2}\mathcal{I}\right)
\right]
=&
0
\end{align*}
Energy
\begin{align*}
\frac{\partial e_{t}}{\partial t}
+
\nabla\cdot
\left[\left(e_{t}+p + \frac{1}{2}B^{2}\right)\boldsymbol{u}-\left(\boldsymbol{u}\cdot\boldsymbol{B}\right)\boldsymbol{B}
\right]
=&
0
\end{align*}
Magnetic Field
\begin{align*}
\frac{\partial \boldsymbol{B}}{\partial t}
+
\nabla\cdot\left(\boldsymbol{u}\boldsymbol{B} - \boldsymbol{B}\boldsymbol{u}
\right)
=&
0
\end{align*}
where \(\boldsymbol{u}=\left(u,v,w\right)\) and \(\boldsymbol{B}=\left(B_{x},B_{y},B_{z}\right)\), total energy \(e_{t}\) is related to pressure, \(p\), by
\begin{align*}
e_{t}
=&
\frac{p}{\gamma-1} + \frac{1}{2}\rho \boldsymbol{u}\cdot\boldsymbol{u} + \frac{1}{2}\boldsymbol{B}\cdot\boldsymbol{B}
=
\frac{p}{\gamma-1} + \frac{1}{2}\rho \left(u^{2}+v^{2}+w^{2}\right) + \frac{1}{2}\left(B_{x}^{2}+B_{y}^{2}+B_{z}^2\right)
\end{align*}
Altogether this is written
\begin{align*}
\frac{\partial}{\partial t}
\begin{pmatrix}
\rho \\
\rho u \\
\rho v \\
\rho w \\
e_{t} \\
B_{x} \\
B_{y} \\
B_{z}
\end{pmatrix}
+
\frac{\partial}{\partial x}
\begin{pmatrix}
\rho u \\
\rho u u - (B_{x} B_{x} - \frac{1}{2}B^{2}) + p \\
\rho u v - B_{x} B_{y} \\
\rho u w - B_{x} B_{z} \\
\left(e_{t} + p + \frac{1}{2}B^{2}\right)u - \left(\boldsymbol{B}\cdot\boldsymbol{u}\right)B_{x} \\
0 \\
u B_{y} - B_{x} v \\
u B_{z} - B_{x} w \\
\end{pmatrix}
+
\frac{\partial}{\partial y}
\begin{pmatrix}
\rho v \\
\rho v u - B_{y} B_{x} \\
\rho v v - (B_{y} B_{y} - \frac{1}{2}B^{2}) + p \\
\rho v w - B_{y} B_{z} \\
\left(e_{t} + p + \frac{1}{2}B^{2}\right)v - \left(\boldsymbol{B}\cdot\boldsymbol{u}\right)B_{y} \\
v B_{x} - B_{y} u \\
0 \\
v B_{z} - B_{y} w \\
\end{pmatrix}
+
\frac{\partial}{\partial z}
\begin{pmatrix}
\rho w \\
\rho w u - B_{z} B_{x} \\
\rho w v - B_{z} B_{y} \\
\rho w w - (B_{z} B_{z} - \frac{1}{2}B^{2}) + p \\
\left(e_{t} + p + \frac{1}{2}B^{2}\right)w - \left(\boldsymbol{B}\cdot\boldsymbol{u}\right)B_{z} \\
w B_{x} - B_{z} u \\
w B_{y} - B_{z} v \\
0
\end{pmatrix}
=&
\begin{pmatrix}
0 \\
0 \\
0 \\
0 \\
0 \\
0 \\
0 \\
0
\end{pmatrix}
\end{align*}
or
\begin{align*}
\begin{pmatrix}
\rho \\
\rho u \\
\rho v \\
\rho w \\
e_{t} \\
B_{x} \\
B_{y} \\
B_{z}
\end{pmatrix}
+
\begin{pmatrix}
\rho u & \rho v & \rho w \\
\rho u u - (B_{x} B_{x} - \frac{1}{2}B^{2}) + p & \rho v u - B_{y} B_{x} & \rho w u - B_{z} B_{x} \\
\rho u v - B_{x} B_{y} & \rho v v - (B_{y} B_{y} - \frac{1}{2}B^{2}) + p & \rho w v - B_{z} B_{y} \\
\rho u w - B_{x} B_{z} & \rho v w - B_{y} B_{z} & \rho w w - (B_{z} B_{z} - \frac{1}{2}B^{2}) + p \\
\left(e_{t} + p + \frac{1}{2}B^{2}\right)u - \left(\boldsymbol{B}\cdot\boldsymbol{u}\right)B_{x} & \left(e_{t} + p + \frac{1}{2}B^{2}\right)v - \left(\boldsymbol{B}\cdot\boldsymbol{u}\right)B_{y} & \left(e_{t} + p + \frac{1}{2}B^{2}\right)w - \left(\boldsymbol{B}\cdot\boldsymbol{u}\right)B_{z} \\
0 & v B_{x} - B_{y} u & w B_{x} - B_{z} u \\
u B_{y} - B_{x} v & 0 & w B_{y} - B_{z} v \\
u B_{z} - B_{x} w & v B_{z} - B_{y} w & 0
\end{pmatrix}
\begin{pmatrix}
\frac{\partial}{\partial x} \\
\frac{\partial}{\partial y} \\
\frac{\partial}{\partial z}
\end{pmatrix}
=&
\begin{pmatrix}
0 \\
0 \\
0 \\
0 \\
0 \\
0 \\
0 \\
0
\end{pmatrix}
\end{align*}
NOTE: When we write the flux tensor in WARPXM we write its transpose (see code below).
Writing Internal Flux and Numerical Flux
(src/apps/imhd/wmapplication_imhd_flux.h)
#ifndef WMAPPLICATION_IMHD_FLUX_H
#define WMAPPLICATION_IMHD_FLUX_H
#include "apps/imhd/imhd.h"
{
public:
WmApplication_IMHD_Flux();
~WmApplication_IMHD_Flux();
{
return _inputVariables;
}
{
return _outputVariables;
}
real* numericalFlux)
const override;
std::vector<std::vector<real>>& internalFlux) const override;
protected:
std::string _numerical_flux_type;
std::vector<int> _inputVariables;
std::vector<int> _outputVariables;
real _min_pressure_floor;
private:
WmApplication_IMHD_Flux& operator=(const WmApplication_IMHD_Flux& var);
WmApplication_IMHD_Flux(const WmApplication_IMHD_Flux& var);
};
#endif
virtual real numerical_flux(const real *q_l, const real *q_r, const real *aux_l, const real *aux_r, const solverVariables_t *pFV, real *numericalFlux) const
Definition: wmapplication.h:126
virtual real internal_flux(const real *q, const real *aux, const solverVariables_t *pSV, std::vector< std::vector< real > > &internalFlux) const
Definition: wmapplication.h:137
virtual const std::vector< int > & getInputVariableIndexes(int flag=0) const
Definition: app_base.h:37
virtual const std::vector< int > & getOutputVariableIndexes(int flag=0) const
Definition: app_base.h:42
(src/apps/imhd/wmapplication_imhd_flux.cc)
#include <iostream>
#include <cmath>
WmApplication_IMHD_Flux::WmApplication_IMHD_Flux()
{
}
WmApplication_IMHD_Flux::~WmApplication_IMHD_Flux()
{
}
void
WmApplication_IMHD_Flux::setup(
const WxCryptSet& wxc)
{
WmApplication_Variable
field(wxc,
"Fluid", 8);
_gas_gamma = wxc.
get<
real>(GAS_GAMMA);
_numerical_flux_type = "Rusanov";
if (wxc.
has(
"NumericalFluxType"))
{
_numerical_flux_type = wxc.
get<std::string>(
"NumericalFluxType");
}
_min_density_floor = 0.0;
if (wxc.
has(
"MinDensity"))
{
_min_density_floor = wxc.
get<
real>(
"MinDensity");
}
_min_pressure_floor = 0.0;
if (wxc.
has(
"MinPressure"))
{
_min_pressure_floor = wxc.
get<
real>(
"MinPressure");
}
_inputVariables =
field.getIndexes();
_outputVariables =
field.getIndexes();
}
WmApplication_IMHD_Flux::numerical_flux(
const real * q_l,
real * numericalFlux)
const
{
constants_imhd_t pC;
populate_constants_imhd_t(&pC, _gas_gamma);
real dens_q_l[8], dens_q_r[8];
real mod_q_l[8], mod_q_r[8];
}
WmApplication_IMHD_Flux::internal_flux(
const real * q,
std::vector< std::vector<real> >& internalFlux
) const
{
constants_imhd_t pC;
populate_constants_imhd_t(&pC, _gas_gamma);
return INFINITY;
}
bool has(const std::string &name) const
Returns true if the 'name' exists in the crypt.
VALUETYPE get(const std::string &name) const
Retrieve the value associated with name.
Definition: wxcrypt.h:80
real imhd_numerical_flux(const real gas_gamma, const real *q_l, const real *q_r, const real *aux_l, const real *aux_r, const solverVariables_t *pFV, const std::string &flux_type, real *numericalFlux, const real rho_min=std::numeric_limits< real >::epsilon(), const real press_min=std::numeric_limits< real >::epsilon())
IMHD numerical flux vector calculated in global frame.
void imhd_internal_flux(const real gas_gamma, const solverVariables_t *pSV, const real *q, std::vector< std::vector< real > > &internalFlux, const real rho_min=std::numeric_limits< real >::epsilon(), const real press_min=std::numeric_limits< real >::epsilon())
IMHD internal flux tensor calculated in global frame.
void ensure_floor_pressure(const real *q, real num_q, real gas_gamma, real pressure_floor, real *mod_q)
void ensure_floor_density(const real *q, real num_q, real density_floor, real *mod_q)
warpy field
Definition: ein_bm.py:101
c
Definition: band_diagram_1d.py:67
where we are using helper functions for internal and numerical fluxes. For the ideal mhd internal flux we have from the file (src/apps/imhd/imhd.
{
real gas_gamma1 = pC->gas_gamma - 1;
real Eterm1 = E + p + 0.5*B2;
internalFlux[0][0] = rho*u;
internalFlux[0][1] = rho*u*u - (bx*bx-0.5*B2) + p;
internalFlux[0][2] = rho*u*v - bx*by;
internalFlux[0][3] = rho*u*w - bx*bz;
internalFlux[0][4] = Eterm1*u - Eterm2*bx;
internalFlux[0][5] = 0;
internalFlux[0][6] = u*by - bx*v;
internalFlux[0][7] = u*bz - bx*w;
{
internalFlux[1][0] = rho*v;
internalFlux[1][1] = rho*v*u - by*bx;
internalFlux[1][2] = rho*v*v - (by*by-0.5*B2) + p;
internalFlux[1][3] = rho*v*w - by*bz;
internalFlux[1][4] = Eterm1*v - Eterm2*by;
internalFlux[1][5] = v*bx - by*u;
internalFlux[1][6] = 0;
internalFlux[1][7] = v*bz - by*w;
}
{
internalFlux[2][0] = rho*w;
internalFlux[2][1] = rho*w*u - bz*bx;
internalFlux[2][2] = rho*w*v - bz*by;
internalFlux[2][3] = rho*w*w - (bz*bz-0.5*B2) + p;
internalFlux[2][4] = Eterm1*w - Eterm2*bz;
internalFlux[2][5] = w*bx - bz*u;
internalFlux[2][6] = w*by - bz*v;
internalFlux[2][7] = 0;
}
}
real dot_product(const real *v)
Definition: geometry.h:64
real get_p(real gas_gamma, real rho, real E, real V2, real B2)
Calculate pressure from MHD total energy.
The numerical flux for ideal mhd is given also with a helper
(src/apps/imhd/imhd.cc)
const constants_imhd_t * pC,
const std::string& flux_type,
{
r_q_l[0] = q_l[0];
r_q_l[4] = q_l[4];
r_q_r[0] = q_r[0];
r_q_r[4] = q_r[4];
if (flux_type == "Rusanov")
{
c =
rusanov_flux(pC, r_q_l, r_q_r, r_flux_l, r_flux_r, r_flux);
}
else if (flux_type == "HLL")
{
c =
hll_flux(pC, r_q_l, r_q_r, r_flux_l, r_flux_r, r_flux);
}
else if (flux_type == "HLLD")
{
c =
hlld_flux(pC, r_q_l, r_q_r, r_flux_l, r_flux_r, r_flux);
}
else if (flux_type == "Roe")
{
c =
roe_flux(pC, r_q_l, r_q_r, r_flux_l, r_flux_r, r_flux);
}
else
{
WxExcept wxe(
"Got bad IMHD Numerical Flux Name\n");
throw wxe;
}
numericalFlux[0] = r_flux[0];
numericalFlux[4] = r_flux[4];
}
wxm::lib::Except is the class to use for creating and throwing exceptions.
Definition: wxexcept.h:31
void rotate_vector(const real v[3], const solverVariables_t *pSV, real Rv[3])
Definition: geometry.h:6
void antirotate_vector(const real Rv[3], const solverVariables_t *pSV, real v[3])
Definition: geometry.h:14
real rusanov_flux(const real gas_gamma, const real *ql, const real *qr, const real *F_L, const real *F_R, real *F_num, const real rho_min=std::numeric_limits< real >::epsilon(), const real press_min=std::numeric_limits< real >::epsilon())
void imhd_numerical_flux_local_frame(const real gas_gamma, const real *q, real *f, const real rho_min=std::numeric_limits< real >::epsilon(), const real press_min=std::numeric_limits< real >::epsilon())
IMHD numerical flux vector calculated in frame rotated to element boundary.
real roe_flux(const real gas_gamma, const real *ql, const real *qr, const real *F_L, const real *F_R, real *F_num, const real rho_min=std::numeric_limits< real >::epsilon(), const real press_min=std::numeric_limits< real >::epsilon())
real hll_flux(const real gas_gamma, const real *ql, const real *qr, const real *F_L, const real *F_R, real *F_num, const real rho_min=std::numeric_limits< real >::epsilon(), const real press_min=std::numeric_limits< real >::epsilon())
real hlld_flux(const real gas_gamma, const real *ql, const real *qr, const real *F_L, const real *F_R, real *F_num, const real rho_min=std::numeric_limits< real >::epsilon(), const real press_min=std::numeric_limits< real >::epsilon())
where the specific flux functions are also given in imhd.cc, which can be looked up. Options are Rusanov, HLL, HLLD, and Roe (which requires the addition of a source term). Note that numerical fluxes are written in terms of a local face frame, which requires a dot with a normal or equivalently, a rotation operation, which is what is going on in this function. The procedure is:
- Rotate vector componants (may need to copy scalars if the vectors are rotated to different variables) from global frame to local face frame
- Apply numerical flux function based on left and right rotated states
- Antirotate vector componants back to global frame (copy any scalars if required)
Registration - this is at the bottom of wmapplication_imhd_flux.cc
(src/apps/imhd/wmapplication_imhd_flux.cc)
Add to CMakeLists.txt
(src/apps/imhd/CMakeLists.txt)
add_sources(
imhd.cc
wmapplication_imhd_flux.cc
)
Add to warpy
(tools/warpy/apps/imhd/imhd.py)
class imhd(application):
'''
@brief Ideal MHD
'''
def __init__(self, name, gamma,
variable, numerical_flux_type='Rusanov',floor_density=None, floor_pressure=None,components=None):
super(imhd, self).__init__(
name,
req_attrs=[('Kind', 'iMHDFlux'),
('gamma', gamma),
('NumericalFluxType',numerical_flux_type)],
opt_attrs=[('MinDensity', floor_density),
('MinPressure',floor_pressure)],
variables=[('Fluid', variable, components)])
Add warpy function to init.py
(tools/warpy/apps/__init__.py)
Add warpy function to CMake
(tools/warpy/apps/CMakeLists)
WARPY_FILES(__init__.py
application.py
)
add_subdirectory(imhd)
Example 3 Resistive MHD Formulation
Now we would like to add resistive diffusion terms. These appear in Ohm's law as an addition to the ideal term:
\begin{align*}
\boldsymbol{E}
=&
-\boldsymbol{u}\times\boldsymbol{B}
+ \color{red}{\left(\frac{\delta_{p}}{L}\right)\left(\nu_{p}\tau\right)\eta \boldsymbol{j}}
\end{align*}
where
\begin{align*}
\boldsymbol{j}
=&
\left(\frac{\delta_{p}}{L}\right)\nabla\times\boldsymbol{B}
\end{align*}
Also
\begin{align*}
\eta
=&
K\frac{A_{e}^{\frac{1}{2}}}{T_{e}^{\frac{3}{2}}}
\end{align*}
where for now \( KA_{e}^{\frac{1}{2}}\) is being lumped into the \(\left(\nu_{p}\tau\right)\) and
\begin{align*}
T_{e}
=&
\frac{p}{2\rho}
\end{align*}
where assumptions that \(T_{e}=T_{i}\) and singly-charged ions have been made.
Next, to actually incorporate these terms into the equation set we must notice where Ohm's Law fits into the MHD equations. This happens where \(\boldsymbol{E}\) appears, shows up in the energy and induction equation. The energy equation for the the electromagnetic terms
\begin{align*}
\frac{\partial e_{em}}{\partial t}
-
\nabla \cdot\left(\boldsymbol{B}\times\boldsymbol{E}\right)
=&
0
\end{align*}
and induction equation
\begin{align*}
\frac{\partial \boldsymbol{B}}{\partial t}
+
\nabla \times \boldsymbol{E}
=&
0
\end{align*}
where here we are adding the term \(\color{red}{\boldsymbol{E}=\left(\frac{\delta_{p}}{L}\right)\left(\nu_{p}\tau\right)\eta \boldsymbol{j}}\).
We can write the energy equation as
\begin{align*}
\frac{\partial e_{em}}{\partial t}
-\frac{\partial}{\partial x}
\begin{pmatrix}
B_{y}E_{z} - B_{z}E_{y}
\end{pmatrix}
-\frac{\partial}{\partial y}
\begin{pmatrix}
B_{z}E_{x} - B_{x}E_{z}
\end{pmatrix}
-\frac{\partial}{\partial z}
\begin{pmatrix}
B_{x}E_{y} - B_{y}E_{x}
\end{pmatrix}
=&
0
\end{align*}
We can write the induction equation in divergence form
\begin{align*}
\frac{\partial}{\partial t}
\begin{pmatrix}
B_{x}\\
B_{y}\\
B_{z}
\end{pmatrix}
+
\begin{pmatrix}
\partial_{y}E_{z} - \partial_{z}E_{y}\\
-\partial_{x}E_{z} + \partial_{z}E_{x}\\
\partial_{x}E_{y} - \partial_{y}E_{x}
\end{pmatrix}
=&
0
\nonumber
\\
\frac{\partial}{\partial t}
\begin{pmatrix}
B_{x}\\
B_{y}\\
B_{z}
\end{pmatrix}
+
\partial_{x}
\begin{pmatrix}
0 \\
-E_{z} \\
+E_{y}
\end{pmatrix}
+
\partial_{y}
\begin{pmatrix}
+E_{z} \\
0 \\
-E_{x}
\end{pmatrix}
+
\partial_{z}
\begin{pmatrix}
-E_{y} \\
+E_{x} \\
0
\end{pmatrix}
=&
0
\nonumber
\\
\frac{\partial}{\partial t}
\begin{pmatrix}
B_{x}\\
B_{y}\\
B_{z}
\end{pmatrix}
+
\begin{pmatrix}
0 & +E_{z} & -E_{y} \\
-E_{z} & 0 & +E_{x} \\
+E_{y} & -E_{x} & 0
\end{pmatrix}
\begin{pmatrix}
\frac{\partial}{\partial x} \\
\frac{\partial}{\partial y} \\
\frac{\partial}{\partial z}
\end{pmatrix}
=&
0
\end{align*}
Altogether this is written for resitive MHD
\begin{align*}
\frac{\partial}{\partial t}
\begin{pmatrix}
\rho \\
\rho u \\
\rho v \\
\rho w \\
e_{t} \\
B_{x} \\
B_{y} \\
B_{z}
\end{pmatrix}
+
\frac{\partial}{\partial x}
\begin{pmatrix}
\rho u \\
\rho u u - (B_{x} B_{x} - \frac{1}{2}B^{2}) + p \\
\rho u v - B_{x} B_{y} \\
\rho u w - B_{x} B_{z} \\
\left(e_{t} + p + \frac{1}{2}B^{2}\right)u - \left(\boldsymbol{B}\cdot\boldsymbol{u}\right)B_{x} \color{red}{- \left(\frac{\delta_{p}}{L}\right)\left(\nu_{p}\tau\right)\eta \left(B_{y}j_{z} - B_{z}j_{y}\right)}\\
0 \color{red}{+ 0} \\
u B_{y} - B_{x} v \color{red}{-\left(\frac{\delta_{p}}{L}\right)\left(\nu_{p}\tau\right)\eta j_{z}}\\
u B_{z} - B_{x} w \color{red}{+\left(\frac{\delta_{p}}{L}\right)\left(\nu_{p}\tau\right)\eta j_{y}}\\
\end{pmatrix}
+
\frac{\partial}{\partial y}
\begin{pmatrix}
\rho v \\
\rho v u - B_{y} B_{x} \\
\rho v v - (B_{y} B_{y} - \frac{1}{2}B^{2}) + p \\
\rho v w - B_{y} B_{z} \\
\left(e_{t} + p + \frac{1}{2}B^{2}\right)v - \left(\boldsymbol{B}\cdot\boldsymbol{u}\right)B_{y} \color{red}{- \left(\frac{\delta_{p}}{L}\right)\left(\nu_{p}\tau\right)\eta \left(B_{z}j_{x} - B_{x}j_{z}\right)}\\
v B_{x} - B_{y} u \color{red}{+\left(\frac{\delta_{p}}{L}\right)\left(\nu_{p}\tau\right)\eta j_{z}}\\
0 \color{red}{+ 0}\\
v B_{z} - B_{y} w \color{red}{-\left(\frac{\delta_{p}}{L}\right)\left(\nu_{p}\tau\right)\eta j_{x}}\\
\end{pmatrix}
+
\frac{\partial}{\partial z}
\begin{pmatrix}
\rho w \\
\rho w u - B_{z} B_{x} \\
\rho w v - B_{z} B_{y} \\
\rho w w - (B_{z} B_{z} - \frac{1}{2}B^{2}) + p \\
\left(e_{t} + p + \frac{1}{2}B^{2}\right)w - \left(\boldsymbol{B}\cdot\boldsymbol{u}\right)B_{z} \color{red}{- \left(\frac{\delta_{p}}{L}\right)\left(\nu_{p}\tau\right)\eta \left(B_{x}j_{y} - B_{y}j_{x}\right)}\\
w B_{x} - B_{z} u \color{red}{-\left(\frac{\delta_{p}}{L}\right)\left(\nu_{p}\tau\right)\eta j_{y}}\\
w B_{y} - B_{z} v \color{red}{+\left(\frac{\delta_{p}}{L}\right)\left(\nu_{p}\tau\right)\eta j_{x}}\\
0 \color{red}{+ 0}
\end{pmatrix}
=&
\begin{pmatrix}
0 \\
0 \\
0 \\
0 \\
0 \\
0 \\
0 \\
0
\end{pmatrix}
\end{align*}
or
\begin{align*}
\begin{pmatrix}
\rho \\
\rho u \\
\rho v \\
\rho w \\
e_{t} \\
B_{x} \\
B_{y} \\
B_{z}
\end{pmatrix}
+
\begin{pmatrix}
\rho u & \rho v & \rho w \\
\rho u u - (B_{x} B_{x} - \frac{1}{2}B^{2}) + p & \rho v u - B_{y} B_{x} & \rho w u - B_{z} B_{x} \\
\rho u v - B_{x} B_{y} & \rho v v - (B_{y} B_{y} - \frac{1}{2}B^{2}) + p & \rho w v - B_{z} B_{y} \\
\rho u w - B_{x} B_{z} & \rho v w - B_{y} B_{z} & \rho w w - (B_{z} B_{z} - \frac{1}{2}B^{2}) + p \\
\left(e_{t} + p + \frac{1}{2}B^{2}\right)u - \left(\boldsymbol{B}\cdot\boldsymbol{u}\right)B_{x} \color{red}{- \left(\frac{\delta_{p}}{L}\right)\left(\nu_{p}\tau\right)\eta \left(B_{y}j_{z} - B_{z}j_{y}\right)} &
\left(e_{t} + p + \frac{1}{2}B^{2}\right)v - \left(\boldsymbol{B}\cdot\boldsymbol{u}\right)B_{y} \color{red}{- \left(\frac{\delta_{p}}{L}\right)\left(\nu_{p}\tau\right)\eta \left(B_{z}j_{x} - B_{x}j_{z}\right)} &
\left(e_{t} + p + \frac{1}{2}B^{2}\right)w - \left(\boldsymbol{B}\cdot\boldsymbol{u}\right)B_{z} \color{red}{- \left(\frac{\delta_{p}}{L}\right)\left(\nu_{p}\tau\right)\eta \left(B_{x}j_{y} - B_{y}j_{x}\right)} \\
0 \color{red}{+ 0} & v B_{x} - B_{y} u \color{red}{+\left(\frac{\delta_{p}}{L}\right)\left(\nu_{p}\tau\right)\eta j_{z}} & w B_{x} - B_{z} u \color{red}{-\left(\frac{\delta_{p}}{L}\right)\left(\nu_{p}\tau\right)\eta j_{y}} \\
u B_{y} - B_{x} v \color{red}{-\left(\frac{\delta_{p}}{L}\right)\left(\nu_{p}\tau\right)\eta j_{z}} & 0 \color{red}{+ 0} & w B_{y} - B_{z} v \color{red}{+\left(\frac{\delta_{p}}{L}\right)\left(\nu_{p}\tau\right)\eta j_{x}} \\
u B_{z} - B_{x} w \color{red}{+\left(\frac{\delta_{p}}{L}\right)\left(\nu_{p}\tau\right)\eta j_{y}} & v B_{z} - B_{y} w \color{red}{-\left(\frac{\delta_{p}}{L}\right)\left(\nu_{p}\tau\right)\eta j_{x}} & 0 \color{red}{+ 0}
\end{pmatrix}
\begin{pmatrix}
\frac{\partial}{\partial x} \\
\frac{\partial}{\partial y} \\
\frac{\partial}{\partial z}
\end{pmatrix}
=&
\begin{pmatrix}
0 \\
0 \\
0 \\
0 \\
0 \\
0 \\
0 \\
0
\end{pmatrix}
\end{align*}
NOTE: When we write the flux tensor in WARPXM we write its transpose (see code below).
Now that we have the equations, we can add on these terms two ways:
- Write a single resistive mhd module that writes the entire equation set
- Make a module with only resistive diffusion and make a simulation solve both ideal mhd + resistive difffusion modules
Example 3.1 Resistive MHD in 1 module
In this example we write the resistive mhd module (wmapplication_rmhd.h/cc) using helpers rmhd.h/cc to write the entire resistive mhd equation set. It will consist of writing internal and numerical fluxes specifically for the resistive diffusion terms and adding them to the ideal mhd fluxes within the module.
Internal Flux, Numerical Flux
(src/apps/rmhd/wmapplication_rmhd.h)
#ifndef WMAPPLICATION_RMHD_H
#define WMAPPLICATION_RMHD_H
{
public:
WmApplication_RMHD();
~WmApplication_RMHD();
int flag) const override
{
return _inputVariables;
}
int flag) const override
{
return _auxVariables;
}
int flag) const override
{
return _outputVariables;
}
int flag) const override
{
return _inputVariables;
}
real* numericalFlux)
const override;
std::vector<std::vector<real>>& internalFlux) const override;
protected:
std::string _imhd_numerical_flux_type;
std::vector<int> _inputVariables;
std::vector<int> _auxVariables;
std::vector<int> _outputVariables;
real _min_pressure_floor;
private:
WmApplication_RMHD& operator=(const WmApplication_RMHD& var);
WmApplication_RMHD(const WmApplication_RMHD& var);
};
#endif
virtual const std::vector< int > & getAuxiliaryVariableIndexes(int flag=WMAPPLICATIONFLAG_NONE) const
Definition: wmapplication.h:106
virtual const std::vector< int > & getCrossVariableIndexes(int flag=WMAPPLICATIONFLAG_NONE) const
Definition: wmapplication.h:112
(src/apps/rmhd/wmapplication_rmhd.cc)
#include "wmapplication_rmhd.h"
#include <iostream>
WmApplication_RMHD::WmApplication_RMHD()
{
}
WmApplication_RMHD::~WmApplication_RMHD()
{
}
void
{
WmApplication_Variable
field(wxc,
"Fluid", 8);
WmApplication_Variable gradient_field(wxc, "FluidGradient", 24);
_gas_gamma = wxc.
get<
real>(GAS_GAMMA);
_skin_depth_norm = wxc.
get<
real>(SKIN_DEPTH_NORM);
_nu_p_norm = wxc.
get<
real>(NU_P_NORM);
_min_density_floor = 0.0;
if (wxc.
has(
"MinDensity"))
{
_min_density_floor = wxc.
get<
real>(
"MinDensity");
}
_min_pressure_floor = 0.0;
if (wxc.
has(
"MinPressure"))
{
_min_pressure_floor = wxc.
get<
real>(
"MinPressure");
}
_imhd_numerical_flux_type = "Rusanov";
if (wxc.
has(
"ImhdNumericalFluxType"))
{
_imhd_numerical_flux_type = wxc.
get<std::string>(
"ImhdNumericalFluxType");
}
_inputVariables =
field.getIndexes();
_outputVariables =
field.getIndexes();
_auxVariables = gradient_field.getIndexes();
}
WmApplication_RMHD::internal_flux(
const real * q,
std::vector< std::vector<real> >& internalFlux
) const
{
constants_rmhd_t pC;
populate_rmhd_t(&pC, _gas_gamma, _skin_depth_norm, _nu_p_norm);
std::vector<real> N_output_dimension_vector(8, 0.);
std::vector< std::vector<real> > internal_advective_flux(pSV->
num_dims, N_output_dimension_vector);
N_output_dimension_vector.assign(8, 0.);
std::vector< std::vector<real> > internal_diffusive_flux(pSV->
num_dims, N_output_dimension_vector);
const real * dB[3] = {aux+15, aux+18, aux+21};
real abs_j_coeff = current_density(&pC,dB,J);
{
for(int j=0;j<8;j++)
{
internalFlux[i][j] = internal_advective_flux[i][j] + internal_diffusive_flux[i][j];
}
}
}
WmApplication_RMHD::numerical_flux(
const real * q_l,
real * numericalFlux)
const
{
constants_imhd_t pCi;
populate_constants_imhd_t(&pCi, _gas_gamma);
real dens_q_l[8], dens_q_r[8];
real mod_q_l[8], mod_q_r[8];
real numerical_advective_flux[8];
for(int i=0;i<8;i++) { numerical_advective_flux[i] = 0; }
const real * dB_l[3] = {aux_l+15, aux_l+18, aux_l+21};
const real * dB_r[3] = {aux_r+15, aux_r+18, aux_r+21};
constants_rmhd_t pC;
populate_rmhd_t(&pC, _gas_gamma, _skin_depth_norm, _nu_p_norm);
real numerical_diffusive_flux[8];
for(int i=0;i<8;i++) { numerical_diffusive_flux[i] = 0; }
real abs_j_coeff_l = current_density(&pC,dB_l,J_l);
real abs_j_coeff_r = current_density(&pC,dB_r,J_r);
real abs_diff_coeff = rmhd_numerical_diffusion_flux(&pC,pFV,mod_q_l,mod_q_r,J_l,J_r,eta_l,eta_r,numerical_diffusive_flux);
for(int i=0;i<8;i++)
{
numericalFlux[i] = numerical_advective_flux[i] + numerical_diffusive_flux[i];
}
return set_time_step(pFV,fmax(abs_j_coeff_l,abs_j_coeff_r), abs_diff_coeff, fmax(eta_l,eta_r));
}
J
Definition: kinetic_equilibrium.py:453
real get_resistivity(const real *q, const real gas_gamma, const real lnlam=10, const real Ai=1.0, const real Zi=1.0, const real Ae=1.0/1836.0, const real rho_min=std::numeric_limits< real >::epsilon(), const real press_min=std::numeric_limits< real >::epsilon())
Compute Spitzer-like resistivity.
real set_time_step(const solverVariables_t *pSV, const real j_coefficient, const real diffusion_coefficient, const real eta, const real cfl_diff=0.5)
Suggested time step calculation for RMHD resistive diffusion.
real rmhd_internal_diffusion_flux(const real nu_p_norm, const real skin_depth_norm, const solverVariables_t *pSV, const real *q, const real *J, const real eta, std::vector< std::vector< real > > &internal_diffusion_flux)
the resistive diffusion flux functions are written in rmhd.cc. For internal flux of diffusive terms we have
(src/apps/rmhd/rmhd.cc)
real rmhd_internal_diffusion_flux(
const constants_rmhd_t * pC,
std::vector< std::vector<real> >& internal_diffusion_flux
)
{
real Bx = q[5], By = q[6], Bz = q[7];
real Jx = J[0], Jy = J[1], Jz = J[2];
real norm_factor_energy = pC->nu_p_norm * pC->skin_depth_norm;
real norm_factor_induction = pC->nu_p_norm * pC->skin_depth_norm;
{
for (int j=0;j<8;j++)
{
internal_diffusion_flux[i][j] = 0.;
}
}
internal_diffusion_flux[0][4] += -norm_factor_energy * eta * (
By*Jz -
Bz*Jy);
internal_diffusion_flux[0][5] += 0;
internal_diffusion_flux[0][6] += -norm_factor_induction * eta * Jz;
internal_diffusion_flux[0][7] += +norm_factor_induction * eta * Jy;
{
internal_diffusion_flux[1][4] += -norm_factor_energy * eta * (
Bz*Jx -
Bx*Jz); ;
internal_diffusion_flux[1][5] += +norm_factor_induction * eta * Jz; ;
internal_diffusion_flux[1][6] += 0;
internal_diffusion_flux[1][7] += -norm_factor_induction * eta * Jx; ;
}
{
internal_diffusion_flux[2][4] += -norm_factor_energy * eta * (
Bx*Jy -
By*Jx); ;
internal_diffusion_flux[2][5] += -norm_factor_induction * eta * Jy; ;
internal_diffusion_flux[2][6] += +norm_factor_induction * eta * Jx;
internal_diffusion_flux[2][7] += 0; ;
}
return fmax(fabs(norm_factor_energy), fabs(norm_factor_induction));
}
list Bx
Definition: brio_wu_shocktube_hall.py:75
list By
Definition: brio_wu_shocktube_hall.py:76
Bz
Definition: kinetic_equilibrium.py:441
These terms also have a numerical flux defined, according to the Local Discontinuous Galerkin formulation
(src/apps/rmhd/rmhd.cc)
real rmhd_numerical_diffusion_flux(
const constants_rmhd_t * pC,
real * numerical_diffusion_flux
)
{
const real * norm = pSV->
R[0];
std::vector<real> N_output_dimension_vector(8, 0.);
std::vector< std::vector<real> > internal_diffusion_flux(pSV->
num_dims, N_output_dimension_vector);
real abs_diff_coeff_l = rmhd_internal_diffusion_flux(pC,pSV,q_l,J_l,eta_l,internal_diffusion_flux);
for(int i = 0; i < 8; i++){
numerical_diffusion_flux[i] = 0.;
{
numerical_diffusion_flux[i] += alpha_l * internal_diffusion_flux[j][i] * norm[j];
}
}
N_output_dimension_vector.assign(8, 0.);
internal_diffusion_flux.assign(pSV->
num_dims, N_output_dimension_vector);
for(int i = 0; i < 8; i++)
{
numerical_diffusion_flux[i] += alpha_r * internal_diffusion_flux[j][i] * norm[j];
}
}
for(int i = 0; i < 8; i++)
{
{
numerical_diffusion_flux[i] -= eta * (q_l[i] - q_r[i]);
}
}
return fmax(abs_diff_coeff_l,abs_diff_coeff_r);
}
real eta
Definition: wmapplication.h:45
real beta_times_common_n_dot_n
Definition: wmapplication.h:50
- Registration - this is at the bottom of wmapplication_rmhd.cc (src/apps/imhd/wmapplication_rmhd.cc)
Add to CMakeLists.txt
(src/apps/rmhd/CMakeLists.txt)
add_sources(
rmhd.cc
wmapplication_rmhd.cc
)
Add to warpy
(tools/warpy/apps/rmhd/rmhd.py)
from ..application import application
class rmhd(application):
'''
@brief Resistive MHD
'''
def __init__(self, name, skin_depth_norm,nu_p_norm, gamma,
variable, gradient_variable, imhd_numerical_flux_type=None, floor_density=None, floor_pressure=None, components=None, gradient_variable_components=None):
super(rmhd, self).__init__(
name,
req_attrs=[('Kind', 'rMHD'),
('skin_depth_norm', skin_depth_norm),
('nu_p_norm', nu_p_norm),
('gamma', gamma),
('ImhdNumericalFluxType',imhd_numerical_flux_type)],
opt_attrs=[('MinDensity', floor_density),
('MinPressure',floor_pressure)],
variables=[('Fluid', variable, components),
('FluidGradient', gradient_variable, gradient_variable_components)])
Add warpy function to init
(tools/warpy/apps/__init__.py)
(tools/warpy/apps/rmhd/__init__.py)
Add warpy function to CMake
(tools/warpy/apps/CMakeLists.txt)
WARPY_FILES(__init__.py
application.py
)
add_subdirectory(rmhd)
(tools/warpy/apps/rmhd/CMakeLists.txt)
WARPY_FILES(
__init__.py
rmhd.py
)
Example 3.1.1 Initial Condition
In order to actually run a problem you'll want to set some intial condition and boundary conditions (unless periodic, which you set in the mesh). There is a library of initial conditions, many of which are for single variables, with various options, such as "set_to", "Heaviside", and "polynomial", while others are for equation sets, such as mhd. Here we will develop wmicfunction_mhd_custom. The steps are very similar to writing an application, but instead of writing functions for fluxes and sources, we write an "applyFunction", which for a given coordinate position \((x,y,z)\).
Writing a function class for initial condition. Here I make a custom file, defining
- Constructor
- Destructor
- setup
- applyFunction
- an required variables
(src/initialConditions/wmicfunction_mhd_custom.h)
#ifndef WMICFUNCTION_MHD_CUSTOM_H
#define WMICFUNCTION_MHD_CUSTOM_H
#include <vector>
#include <string>
{
public:
private:
};
#endif
Definition: wmicfunction_mhd_custom.h:12
WmICFunction_Mhd_Custom()
Create new WmICFunction_Mhd_Custom.
void setup(const WxCryptSet &wxc)
Setup WmICFunction_Mhd_Custom object using supplied cryptset.
void applyFunction(const real *position, real *value) const
Evaluate the function at a position and return the result through a pointer.
~WmICFunction_Mhd_Custom()
Destroy WmICFunction_Mhd_Custom.
Base class for functions used in generating initial conditions.
Definition: wmicfunction.h:25
Set the number of arguments, initialize variables from cryptset in setup, and define function in applyFunction. (src/initialConditions/wmicfunction_mhd_custom.cc)
#include <cmath>
{
}
{
}
void
{
_gas_gamma = wxc.
get<
real>(GAS_GAMMA);
_skin_depth_param = wxc.
get<
real>(SKIN_DEPTH_NORM);
}
void
{
const real x = position[0];
const real y = position[1];
const real z = position[2];
std::vector<real> average = std::vector<real>(3,0.);
int numDims = average.size();
real standardDeviation = 0.125;
for(int i = 0; i < numDims; i++){
C3 += (position[i] - average[i]) * (position[i] - average[i]);
}
real C2 = - C3 / (2. * standardDeviation * standardDeviation);
real gaussian_value = C0 + C1 * exp(C2);
real constant_value1 = 1.0;
real constant_value2 = 1.0;
real sinewave = std::sin(2*wxm::pi*x);
real P = constant_value2;
real gamma1 = _gas_gamma - 1.0;
real E =
P/gamma1 + 0.5*
rho*V2 + 0.5*B2;
qc[4] = E;
}
int _numArgs
Definition: wmicfunction.h:98
virtual void setup(const WxCryptSet &wxc)
Setup WmICFunction object using supplied cryptset.
float P
Definition: shock_tube.py:14
list rho
Definition: zpinch_5-moment_1D.py:239
np x
Definition: band_diagram_1d.py:62
list vz
Definition: dai_woodward_shocktube.py:73
list vx
Definition: dai_woodward_shocktube.py:71
list vy
Definition: dai_woodward_shocktube.py:72
np y
Definition: fvm_plotter_test.py:84
Registration - The last lines in the wmicfunction_mhd_custom.cc file registers the scheme:
(src/initialConditions/wmicfunction_mhd_custom.cc)
Add to CMakeLists.txt
(src/apps/imhd/CMakeLists.txt)
add_sources(
wmicfunction.cc
wmicfunction_mhd_custom.cc
)
Add to warpy
(tools/warpy/functions/custom_mhd.py)
from .. import generator as gen
class custom_mhd(gen.warpy_obj):
'''
'''
def __init__(self, name, skin_depth_norm, gamma):
'''
'''
super(custom_mhd, self).__init__(name,
req_attrs=[('Type','function'),
('Kind', 'custom_mhd'),
('skin_depth_norm', skin_depth_norm),
('gamma', gamma)])
Add warpy function to init
(tools/warpy/functions/__init__.py)
from .applicator import applicator
from .custom_mhd import custom_mhd
Add warpy function to CMake
(tools/warpy/functions/CMakeLists.txt)
WARPY_FILES(__init__.py
applicator.py
custom_mhd.py
)
Example 3.1.2 Boundary Condition
Boundary conditions are just another application, specific to a particular to a particular set of variables. Often they make sense physically for a specific equation set, e.g. no slip wall for Euler or Ideal MHD. One can also apply Dirichlet or Neumann conditions to particular variables. Here, I show a no-slip wall condition used by resistive mhd.
Write boundary condition
(src/apps/rmhd/wmapplication_rmhd_bc_noslipwall.h)
#ifndef WMAPPLICATION_RMHD_BC_NOSLIPWALL_H
#define WMAPPLICATION_RMHD_BC_NOSLIPWALL_H
{
public:
WmApplication_Rmhd_BC_NoslipWall();
~WmApplication_Rmhd_BC_NoslipWall();
{
return _inputVariables;
}
{
return _outputVariables;
}
real* q_out)
const override;
protected:
std::vector<real> _wallVelocity;
std::vector<real> _wallMagneticField;
std::vector<int> _inputVariables;
std::vector<int> _outputVariables;
private:
WmApplication_Rmhd_BC_NoslipWall& operator=(
const WmApplication_Rmhd_BC_NoslipWall& var);
WmApplication_Rmhd_BC_NoslipWall(
const WmApplication_Rmhd_BC_NoslipWall& var);
};
#endif
virtual void bc_q(const real *q_in, const real *aux_in, const real *aux_out, const solverVariables_t *pFV, real *q_out) const
Boundary Condition Application which sets the boundary condition on ghost nodes.
Definition: wmapplication.h:175
(src/apps/rmhd/wmapplication_rmhd_bc_noslipwall.cc)
#include <iostream>
WmApplication_Rmhd_BC_NoslipWall::WmApplication_Rmhd_BC_NoslipWall()
{
}
WmApplication_Rmhd_BC_NoslipWall::~WmApplication_Rmhd_BC_NoslipWall()
{
}
void
WmApplication_Rmhd_BC_NoslipWall::setup(
const WxCryptSet& wxc)
{
WmApplication_Variable
fluid(wxc,
"Fluid", 8);
_gas_gamma = wxc.
get<
real>(GAS_GAMMA);
_skin_depth_norm = wxc.
get<
real>(SKIN_DEPTH_NORM);
_nu_p_norm = wxc.
get<
real>(NU_P_NORM);
_wallVelocity.resize(3,0.);
if(wxc.
has(
"WallVelocity")){
}
_wallMagneticField.resize(3,0.);
if(wxc.
has(
"WallMagneticField")){
_wallMagneticField = wxc.
getVec<
real>(
"WallMagneticField");
}
_inputVariables =
fluid.getIndexes();
_outputVariables = _inputVariables;
}
void
WmApplication_Rmhd_BC_NoslipWall::bc_q(
const real * q_in,
{
constants_rmhd_t pC;
populate_rmhd_t(&pC, _gas_gamma, _skin_depth_norm, _nu_p_norm);
real wall_vx = _wallVelocity[0], wall_vy = _wallVelocity[1], wall_vz = _wallVelocity[2];
real wall_bx = _wallMagneticField[0], wall_by = _wallMagneticField[1], wall_bz = _wallMagneticField[2];
}
warpy fluid
Definition: shock_tube.py:22
void noslipWallCondition_MHD(const real gas_gamma, const solverVariables_t *pSV, const real *QC, const real vx_w, const real vy_w, const real vz_w, const real bx_w, const real by_w, const real bz_w, real *QC_w, const real rho_min=std::numeric_limits< real >::epsilon(), const real press_min=std::numeric_limits< real >::epsilon())
@ WMAPPLICATIONFLAG_BC_Q
Definition: wmapplication.h:28
where noslipWallCondition_MHD is defined with helper in rmhd.cc
(src/apps/rmhd/rmhd.cc)
void
noslipWallCondition_MHD(
const constants_rmhd_t * pC,
{
QNC[1] = vx_w;
QNC[2] = vy_w;
QNC[3] = vz_w;
QNC[5] = bx_w;
QNC[6] = by_w;
QNC[7] = bz_w;
}
void CtoNC_MHD(const real gas_gamma, const real QC[8], real QNC[8], const real rho_min=std::numeric_limits< real >::epsilon(), const real press_min=std::numeric_limits< real >::epsilon())
Converts conserved variables to nonconserved (primitive) variables.
void NCtoC_MHD(const real gas_gamma, const real QNC[8], real QC[8], const real rho_min=std::numeric_limits< real >::epsilon())
Converts nonconserved (primitive) variables to conserved variables.
Registration - this is at the bottom of wmapplication_rmhd_bc_noslipwall.cc
(src/apps/rmhd/wmapplication_rmhd_noslipwall.cc)
Add to CMakeLists.txt
(src/apps/rmhd/CMakeLists.txt)
add_sources(
rmhd.cc
wmapplication_rmhd.cc
wmapplication_resistive_diffusion.cc
wmapplication_rmhd_bc_noslipwall.cc
)
Add to warpy
(tools/warpy/apps/rmhd/rmhd_noslip_wall.py)
from ..application import application
class rmhd_noslip_wall(application):
def __init__(self, name, on_boundaries, skin_depth_norm, nu_p_norm,gamma,
variable, gradient_variable, wall_velocity=[0.0,0.0,0.0], wall_magnetic_field=[0.0,0.0,0.0], components=None, gradient_variable_components=None):
'''
@brief
@param name
@param boundary_subdomains list of boundary subdomains this BC should be applied to
@param skin_depth_norm
@param gamma
@param variable
@param components
'''
super(rmhd_noslip_wall, self).__init__(
name,
req_attrs=[('Kind', 'rMHDNoSlipWall'),
('OnBoundaries', on_boundaries),
('skin_depth_norm', skin_depth_norm),
('nu_p_norm', nu_p_norm),
('gamma', gamma),
("WallVelocity",wall_velocity),
("WallMagneticField",wall_magnetic_field)],
variables=[('Fluid', variable, components),
('FluidGradient', gradient_variable, gradient_variable_components)])
Add warpy function to init
(tools/warpy/apps/__init__.py)
(tools/warpy/apps/rmhd/__init__.py)
from .rmhd_noslip_wall import rmhd_noslip_wall
Add warpy function to CMake
(tools/warpy/apps/CMakeLists.txt)
WARPY_FILES(__init__.py
application.py
)
add_subdirectory(rmhd)
(tools/warpy/apps/rmhd/CMakeLists.txt)
WARPY_FILES(__init__.py
rmhd.py
rmhd_noslip_wall.py
)
Example 3.1.3 Putting it all together
Armed with an equation set, initial conditions, and boundary conditions, we can now write a python input file. Here we setup a simple 1D problem with constant density, pressure and By and set By to 0 at the boundaries. This is effectively running the diffusion equatoin on magnetic field where the boundary conditions are pulling down the magnetic field in the middle of the domain. The input file is written:
(examples/dg/mhd/resistive_mhd_pure_diffusion.py)
from __future__ import division, print_function
import warpy
torder = 'RK4'
sorder = 'secondOrder'
skin_depth_norm = 1.0
nu_p_norm = 1.0
gamma = 2.0
penalty_beta = -0.5
penalty_eta = 0.0
imhd_numerical_flux_type = "Rusanov"
cfl = 1.0
NumCells=[64],
NodeSets=['leftWall', 'rightWall'],
NumLayers=1,
basis_array_set=sorder)
component_names = ['rho', 'px', 'py', 'pz', 'e', 'Bx', 'By', 'Bz']
fluid =
warpy.variable(name=
'fluid',components=component_names,basis_array_set=sorder)
gradient_component_names = [prefix+"_"+suffix for prefix in component_names for suffix in ['x','y','z']]
fluid_gradient =
warpy.variable(name=
'fluid_gradient', components=gradient_component_names,basis_array_set=sorder)
ic_applicators = [warpy.applicator(spatial_order=sorder,
fun=pure_diffusion_ic,
var=fluid,components=None,
spatial_scheme='Nodal')]
apps = []
dirichlet_apps = []
apps.append(warpy.apps.rmhd.rmhd(name='rmhd',
skin_depth_norm=skin_depth_norm,
nu_p_norm=nu_p_norm,
gamma=gamma,
variable=fluid,
gradient_variable=fluid_gradient,
components=None,
gradient_variable_components=None,
imhd_numerical_flux_type=imhd_numerical_flux_type))
dirichlet_apps.append(warpy.apps.rmhd.rmhd_noslip_wall(name='noslip_wall_bc',
on_boundaries=['leftWall', 'rightWall'],
skin_depth_norm=skin_depth_norm,
nu_p_norm=nu_p_norm,
gamma=gamma,
wall_velocity=[0.0,0.0,0.0],
wall_magnetic_field=[0.0,0.0,0.0],
variable=fluid,
gradient_variable=fluid_gradient,
components=None,
gradient_variable_components=None))
ReadVars=[fluid,
fluid_gradient] )
spatial_solver = warpy.spatial_solvers.dg(name="dg",
spatial_order=sorder,
applications=apps,
penalty_beta=penalty_beta,
penalty_eta=penalty_eta,
cfl=cfl)
variable_adjusters = []
applications=dirichlet_apps,
priority=0,
spatial_order=sorder,
copy_variables=[fluid],
on_boundaries=['leftWall','rightWall']))
priority=2,
spatial_order=sorder,
penalty_beta=penalty_beta,
penalty_eta=penalty_eta,
input_variables=[fluid],
output_variables=[fluid_gradient]))
priority=3,
copy_variables=[fluid_gradient],
spatial_order=sorder,
copy_all=True,
on_boundaries=['leftWall','rightWall']))
adjust_bc_apps = []
values=[0.0]*6,
on_boundaries=['leftWall'],
variable=fluid_gradient,
components=['rho_x','rho_y','rho_z','e_x','e_y','e_z']))
values=[0.0]*6,
on_boundaries=['rightWall'],
variable=fluid_gradient,
components=['rho_x','rho_y','rho_z','e_x','e_y','e_z']))
priority=4,
spatial_order=sorder,
applications=adjust_bc_apps))
scheme=torder,
spatial_solvers=[spatial_solver],
variable_adjusters=variable_adjusters)
meshes=[mesh],
initial_conditions=ic_applicators,
temporal_solvers=[ti],
writers=[writer],
time=[0.,0.001],
dt_controller=dt_controller,
write_steps=100,
verbosity='info')
sim.run('mhd/resistive/jan26_2018', gen_xdmf=True, detect_nonscalar=True)
Dirichlet boundary condition.
Definition: simple.py:151
Discontinuous finite element RK simulation.
Definition: dg_sim.py:11
Definition: fixed_dt.py:4
Definition: custom_mhd.py:4
Explicit Runge-Kutta temporal solver Note: Dormand45 currently will not work correctly with limiters ...
Definition: erk.py:5
Writes out a list of variables.
Definition: writer.py:4
block mesh generator
Definition: mesh.py:87
Definition: variable.py:4
spatial solver - currently hardcoded for rk methods
Definition: boundary_conditions.py:4
dg_gradient solves for gradients for the dg interface
Definition: dg_gradient.py:4
Example 3.2 Resistive MHD in 2 modules
Instead of running 1 resitive mhd application which adds the ideal and resistive diffusion terms for you, you could also run these as separate applications stacked together. You need to write a specific resistive diffusion application with only those terms:
Resistive Diffusion Application
(src/apps/rmhd/wmapplication_resistive_diffusion.h)
#ifndef WMAPPLICATION_RESISTIVE_DIFFUSION_H
#define WMAPPLICATION_RESISTIVE_DIFFUSION_H
{
public:
WmApplication_Resistive_Diffusion();
~WmApplication_Resistive_Diffusion();
int flag) const override
{
return _inputVariables;
}
int flag) const override
{
return _auxVariables;
}
int flag) const override
{
return _outputVariables;
}
int flag) const override
{
return _inputVariables;
}
real* numericalFlux)
const override;
std::vector<std::vector<real>>& internalFlux) const override;
protected:
std::vector<int> _inputVariables;
std::vector<int> _auxVariables;
std::vector<int> _outputVariables;
real _min_pressure_floor;
private:
WmApplication_Resistive_Diffusion& operator=(const WmApplication_Resistive_Diffusion& var);
WmApplication_Resistive_Diffusion(const WmApplication_Resistive_Diffusion& var);
};
#endif
(src/apps/rmhd/wmapplication_resistive_diffusion.cc)
#include <iostream>
WmApplication_Resistive_Diffusion::WmApplication_Resistive_Diffusion()
{
}
WmApplication_Resistive_Diffusion::~WmApplication_Resistive_Diffusion()
{
}
void
WmApplication_Resistive_Diffusion::setup(
const WxCryptSet& wxc)
{
WmApplication_Variable
field(wxc,
"Fluid", 8);
WmApplication_Variable gradient_field(wxc, "FluidGradient", 24);
_gas_gamma = wxc.
get<
real>(GAS_GAMMA);
_skin_depth_norm = wxc.
get<
real>(SKIN_DEPTH_NORM);
_nu_p_norm = wxc.
get<
real>(NU_P_NORM);
_min_density_floor = 0.0;
if (wxc.
has(
"MinDensity"))
{
_min_density_floor = wxc.
get<
real>(
"MinDensity");
}
_min_pressure_floor = 0.0;
if (wxc.
has(
"MinPressure"))
{
_min_pressure_floor = wxc.
get<
real>(
"MinPressure");
}
_inputVariables =
field.getIndexes();
_outputVariables =
field.getIndexes();
_auxVariables = gradient_field.getIndexes();
}
WmApplication_Resistive_Diffusion::internal_flux(
const real * q,
std::vector< std::vector<real> >& internalFlux
) const
{
constants_rmhd_t pC;
populate_rmhd_t(&pC, _gas_gamma, _skin_depth_norm, _nu_p_norm);
const real * dB[3] = {aux+15, aux+18, aux+21};
real abs_j_coeff = current_density(&pC,dB,J);
}
WmApplication_Resistive_Diffusion::numerical_flux(
const real * q_l,
real * numericalFlux)
const
{
const real * dB_l[3] = {aux_l+15, aux_l+18, aux_l+21};
const real * dB_r[3] = {aux_r+15, aux_r+18, aux_r+21};
constants_rmhd_t pC;
populate_rmhd_t(&pC, _gas_gamma, _skin_depth_norm, _nu_p_norm);
real abs_j_coeff_l = current_density(&pC,dB_l,J_l);
real abs_j_coeff_r = current_density(&pC,dB_r,J_r);
real abs_diff_coeff = rmhd_numerical_diffusion_flux(&pC,pFV,q_l,q_r,J_l,J_r,eta_l,eta_r,numericalFlux);
return set_time_step(pFV,fmax(abs_j_coeff_l,abs_j_coeff_r), abs_diff_coeff, fmax(eta_l,eta_r));
}
- Registration - this is at the bottom of wmapplication_resistive_diffusion.cc (src/apps/imhd/wmapplication_resistive_diffusion.cc)
Add to CMakeLists.txt
(src/apps/rmhd/CMakeLists.txt)
add_sources(
rmhd.cc
wmapplication_rmhd.cc
wmapplication_resistive_diffusion.cc
wmapplication_rmhd_bc_noslipwall.cc
)
Add to warpy
(tools/warpy/apps/rmhd/rmhd.py)
from ..application import application
class resistive_diffusion(application):
'''
@brief Resistive Diffusion Terms for MHD Only
'''
def __init__(self, name, skin_depth_norm,nu_p_norm, gamma,
variable, gradient_variable, floor_density=None, floor_pressure=None, components=None, gradient_variable_components=None):
super(resistive_diffusion, self).__init__(
name,
req_attrs=[('Kind', 'resistive_diffusion'),
('skin_depth_norm', skin_depth_norm),
('nu_p_norm', nu_p_norm),
('gamma', gamma)],
opt_attrs=[('MinDensity', floor_density),
('MinPressure',floor_pressure)],
variables=[('Fluid', variable, components),
('FluidGradient', gradient_variable, gradient_variable_components)])
Add warpy function to init
(tools/warpy/apps/__init__.py)
(tools/warpy/apps/rmhd/__init__.py)
from .rmhd import rmhd
from .rmhd import resistive_diffusion
Add warpy function to CMake
(tools/warpy/apps/CMakeLists.txt)
WARPY_FILES(__init__.py
application.py
)
add_subdirectory(rmhd)
(tools/warpy/apps/rmhd/CMakeLists.txt)
WARPY_FILES(
__init__.py
rmhd.py
rmhd_noslip_wall.py
)
Example 3.2.1 Putting it together
Now we can use the same in put file as in the previous example, except that we swap the rmhd app:
(examples/dg/mhd/resistive_mhd_pure_diffusion.py)
apps.append(warpy.apps.rmhd.rmhd(name='rmhd',
skin_depth_norm=skin_depth_norm,
nu_p_norm=nu_p_norm,
gamma=gamma,
variable=fluid,
gradient_variable=fluid_gradient,
components=None,
gradient_variable_components=None,
imhd_numerical_flux_type=imhd_numerical_flux_type))
for an imhd app followed by a resistive_diffusion app: (examples/dg/mhd/resistive_mhd_pure_diffusion.py)
apps.append(warpy.apps.imhd.imhd(name='imhd',
gamma=gamma,
variable=fluid,
numerical_flux_type=imhd_numerical_flux_type,
components=None))
apps.append(warpy.apps.rmhd.resistive_diffusion(name='resistive_diffusion',
skin_depth_norm=skin_depth_norm,
nu_p_norm=nu_p_norm,gamma=gamma,
variable=fluid,
gradient_variable=fluid_gradient,
components=None,
gradient_variable_components=None))
Example 4 - Ideal MHD with Divergence Cleaning
The ideal mhd equations ammended with divergence cleaning terms (in green), as coded in WARPXM are as follows:
Continuity
\begin{align*}
\frac{\partial \rho}{\partial t} + \nabla\cdot\left(\rho \boldsymbol{u}\right)
=&
0
\end{align*}
Momentum
\begin{align*}
\frac{\partial\left(\rho \boldsymbol{u}\right)}{\partial t}
+
\nabla\cdot
\left[\rho \boldsymbol{u}\boldsymbol{u} + p \mathcal{I} - \left(\boldsymbol{B}\boldsymbol{B}-\frac{1}{2}B^{2}\mathcal{I}\right)
\right]
=&
0
\end{align*}
Energy
\begin{align*}
\frac{\partial e_{t}}{\partial t}
+
\nabla\cdot
\left[\left(e_{t}+p + \frac{1}{2}B^{2}\right)\boldsymbol{u}-\left(\boldsymbol{u}\cdot\boldsymbol{B}\right)\boldsymbol{B}
\right]
=&
0
\end{align*}
Induction
\begin{align*}
\frac{\partial \boldsymbol{B}}{\partial t}
+
\nabla\cdot\left(\boldsymbol{u}\boldsymbol{B} - \boldsymbol{B}\boldsymbol{u}
{\color{green}{+\psi\mathcal{I}}}
\right)
=&
0
\end{align*}
\(\boldsymbol{B}\) advection
\begin{align*}
\color{green}
{
\frac{\partial \psi}{\partial t}
+
\nabla\cdot\left(c_{h}^{2}\boldsymbol{B}\right)
}
=&
\color{green}
{
-\frac{c_{h}^{2}}{c_{p}^{2}}\psi
}
\end{align*}
Altogether this is written
\begin{align*}
\frac{\partial}{\partial t}
\begin{pmatrix}
\rho \\
\rho u \\
\rho v \\
\rho w \\
e_{t} \\
B_{x} \\
B_{y} \\
B_{z} \\
\color{green}{\psi}
\end{pmatrix}
+
\frac{\partial}{\partial x}
\begin{pmatrix}
\rho u \\
\rho u u - (B_{x} B_{x} - \frac{1}{2}B^{2}) + p \\
\rho u v - B_{x} B_{y} \\
\rho u w - B_{x} B_{z} \\
\left(e_{t} + p + \frac{1}{2}B^{2}\right)u - \left(\boldsymbol{B}\cdot\boldsymbol{u}\right)B_{x} \\
0 \color{green}{+\psi}\\
u B_{y} - B_{x} v \\
u B_{z} - B_{x} w \\
\color{green}{c_{h}^2 B_{x}}
\end{pmatrix}
+
\frac{\partial}{\partial y}
\begin{pmatrix}
\rho v \\
\rho v u - B_{y} B_{x} \\
\rho v v - (B_{y} B_{y} - \frac{1}{2}B^{2}) + p \\
\rho v w - B_{y} B_{z} \\
\left(e_{t} + p + \frac{1}{2}B^{2}\right)v - \left(\boldsymbol{B}\cdot\boldsymbol{u}\right)B_{y} \\
v B_{x} - B_{y} u \\
0 \color{green}{+\psi} \\
v B_{z} - B_{y} w \\
\color{green}{c_{h}^2 B_{y}}
\end{pmatrix}
+
\frac{\partial}{\partial z}
\begin{pmatrix}
\rho w \\
\rho w u - B_{z} B_{x} \\
\rho w v - B_{z} B_{y} \\
\rho w w - (B_{z} B_{z} - \frac{1}{2}B^{2}) + p \\
\left(e_{t} + p + \frac{1}{2}B^{2}\right)w - \left(\boldsymbol{B}\cdot\boldsymbol{u}\right)B_{z} \\
w B_{x} - B_{z} u \\
w B_{y} - B_{z} v \\
0 \color{green}{+\psi} \\
\color{green}{c_{h}^2 B_{z}}
\end{pmatrix}
=&
\begin{pmatrix}
0 \\
0 \\
0 \\
0 \\
0 \\
0 \\
0 \\
0 \\
\color{green}{-\frac{c_{h}^{2}}{c_{p}^{2}}\psi}
\end{pmatrix}
\end{align*}
or
\begin{align*}
\begin{pmatrix}
\rho \\
\rho u \\
\rho v \\
\rho w \\
e_{t} \\
B_{x} \\
B_{y} \\
B_{z} \\
\color{green}{\psi}
\end{pmatrix}
+
\begin{pmatrix}
\rho u & \rho v & \rho w \\
\rho u u - (B_{x} B_{x} - \frac{1}{2}B^{2}) + p & \rho v u - B_{y} B_{x} & \rho w u - B_{z} B_{x} \\
\rho u v - B_{x} B_{y} & \rho v v - (B_{y} B_{y} - \frac{1}{2}B^{2}) + p & \rho w v - B_{z} B_{y} \\
\rho u w - B_{x} B_{z} & \rho v w - B_{y} B_{z} & \rho w w - (B_{z} B_{z} - \frac{1}{2}B^{2}) + p \\
\left(e_{t} + p + \frac{1}{2}B^{2}\right)u - \left(\boldsymbol{B}\cdot\boldsymbol{u}\right)B_{x} & \left(e_{t} + p + \frac{1}{2}B^{2}\right)v - \left(\boldsymbol{B}\cdot\boldsymbol{u}\right)B_{y} & \left(e_{t} + p + \frac{1}{2}B^{2}\right)w - \left(\boldsymbol{B}\cdot\boldsymbol{u}\right)B_{z} \\
0 \color{green}{+\psi} & v B_{x} - B_{y} u & w B_{x} - B_{z} u \\
u B_{y} - B_{x} v & 0 \color{green}{+\psi} & w B_{y} - B_{z} v \\
u B_{z} - B_{x} w & v B_{z} - B_{y} w & 0 \color{green}{+\psi} \\
\color{green}{c_{h}^2 B_{x}} & \color{green}{c_{h}^2 B_{y}} & \color{green}{c_{h}^2 B_{z}}
\end{pmatrix}
\begin{pmatrix}
\frac{\partial}{\partial x} \\
\frac{\partial}{\partial y} \\
\frac{\partial}{\partial z}
\end{pmatrix}
=&
\begin{pmatrix}
0 \\
0 \\
0 \\
0 \\
0 \\
0 \\
0 \\
0 \\
\color{green}{-\frac{c_{h}^{2}}{c_{p}^{2}}\psi}
\end{pmatrix}
\end{align*}
NOTE: When we write the flux tensor in WARPXM we write its transpose (see code below).
Internal Flux, Numerical Flux, and Source
(src/apps/imhd/wmapplication_imhd_mixed_glm.h)
#ifndef WMAPPLICATION_IMHD_MIXED_GLM_H
#define WMAPPLICATION_IMHD_MIXED_GLM_H
#include "apps/imhd/imhd.h"
{
public:
WmApplication_IMHD_MIXED_GLM();
~WmApplication_IMHD_MIXED_GLM();
{
return _inputVariables;
}
{
return _outputVariables;
}
real* numericalFlux)
const override;
std::vector<std::vector<real>>& internalFlux) const override;
real* source)
const override;
protected:
std::string _numerical_flux_type;
std::vector<int> _inputVariables;
std::vector<int> _outputVariables;
real _min_pressure_floor;
private:
WmApplication_IMHD_MIXED_GLM& operator=(const WmApplication_IMHD_MIXED_GLM& var);
WmApplication_IMHD_MIXED_GLM(const WmApplication_IMHD_MIXED_GLM& var);
};
#endif
virtual real source(const real *q, const real *aux, const elementGeometry_t *pEG, real *source) const
Definition: wmapplication.h:146
(src/apps/imhd/wmapplication_imhd_mixed_glm.cc)
#include "wmapplication_imhd_mixed_glm.h"
#include "imhd_glm.h"
#include <iostream>
#include <cmath>
WmApplication_IMHD_MIXED_GLM::WmApplication_IMHD_MIXED_GLM()
{
}
WmApplication_IMHD_MIXED_GLM::~WmApplication_IMHD_MIXED_GLM()
{
}
void
WmApplication_IMHD_MIXED_GLM::setup(
const WxCryptSet& wxc)
{
WmApplication_Variable
field(wxc,
"Fluid", 9);
_gas_gamma = wxc.
get<
real>(GAS_GAMMA);
_numerical_flux_type = "Rusanov";
if (wxc.
has(
"NumericalFluxType"))
{
_numerical_flux_type = wxc.
get<std::string>(
"NumericalFluxType");
}
_min_density_floor = 0.0;
if (wxc.
has(
"MinDensity"))
{
_min_density_floor = wxc.
get<
real>(
"MinDensity");
}
_min_pressure_floor = 0.0;
if (wxc.
has(
"MinPressure"))
{
_min_pressure_floor = wxc.
get<
real>(
"MinPressure");
}
_c_h = 1.0;
{
}
_c_r = 0.18;
_c_p = std::sqrt(_c_r*_c_h);
_inputVariables =
field.getIndexes();
_outputVariables =
field.getIndexes();
}
WmApplication_IMHD_MIXED_GLM::numerical_flux(
const real * q_l,
real * numericalFlux)
const
{
constants_imhd_glm_t pC;
populate_constants_imhd_glm_t(&pC, _gas_gamma, _c_h, _c_p, _c_r);
real dens_q_l[9], dens_q_r[9];
real positivized_q_l[9], positivized_q_r[9];
r_q_l[0] = positivized_q_l[0];
r_q_l[4] = positivized_q_l[4];
r_q_l[8] = positivized_q_l[8];
r_q_r[0] = positivized_q_r[0];
r_q_r[4] = positivized_q_r[4];
r_q_r[8] = positivized_q_r[8];
r_q_l[5] = r_q_r[5] = Bxm;
mod_q_l[0] = r_q_l[0];
mod_q_l[4] = r_q_l[4];
mod_q_l[8] = r_q_l[8];
mod_q_r[0] = r_q_r[0];
mod_q_r[4] = r_q_r[4];
mod_q_r[8] = r_q_r[8];
for (int i =0;i<8;i++)
{
numericalFlux[i] = initial_flux[i] + dedner_flux[i];
}
numericalFlux[8] = dedner_flux[8];
return pFV->
cfl_max * pFV->
dxn / std::max(fabs(c),fabs(_c_h));
}
WmApplication_IMHD_MIXED_GLM::internal_flux(
const real * q,
std::vector< std::vector<real> >& internalFlux
) const
{
constants_imhd_glm_t pC;
populate_constants_imhd_glm_t(&pC, _gas_gamma, _c_h, _c_p, _c_r);
std::vector<real> N_output_dimension_vector(9, 0.);
std::vector< std::vector<real> > internal_ideal_flux(pSV->
num_dims, N_output_dimension_vector);
N_output_dimension_vector.assign(9, 0.);
std::vector< std::vector<real> > internal_glm_flux(pSV->
num_dims, N_output_dimension_vector);
{
for(int j=0;j<9;j++)
{
internalFlux[i][j] = internal_ideal_flux[i][j] + internal_glm_flux[i][j];
}
}
return INFINITY;
}
WmApplication_IMHD_MIXED_GLM::source(
{
source[0] = 0.0;
source[1] = 0.0;
source[2] = 0.0;
source[3] = 0.0;
source[4] = 0.0;
source[5] = 0.0;
source[6] = 0.0;
source[7] = 0.0;
source[8] = -_c_h*_c_h/(_c_p*_c_p)*psi;
return INFINITY;
}
void imhd_glm_internal_flux(const real c_h, const solverVariables_t *pSV, const real *q, std::vector< std::vector< real > > &internalFlux)
real calc_Bxm(const real *r_q_l, const real *r_q_r, real c_h)
void imhd_glm_numerical_flux(const real c_h, const real *q_l, const real *q_r, const real *aux_l, const real *aux_r, const solverVariables_t *pFV, real *numericalFlux)
@ WMAPPLICATIONFLAG_SOURCE
Definition: wmapplication.h:26
where we are using helper functions for internal fluxes for both the ideal and divergence cleaning corrections. For the ideal mhd internal flux we reuse the the function shown above for ideal mhd in (src/apps/imhd/imhd.cc). For the divergence cleaning we write another function specifically for those terms
(src/apps/imhd/imhd_glm.cc)
const constants_imhd_glm_t * pC,
std::vector< std::vector<real> >& internalFlux
)
{
real ch_factor = pC->c_h*pC->c_h;
{
for (int j=0;j<9;j++)
{
internalFlux[i][j] = 0.;
}
}
internalFlux[0][5] += psi;
internalFlux[0][8] = ch_factor * bx;
{
internalFlux[1][6] += psi;
internalFlux[1][8] = ch_factor * by;
}
{
internalFlux[2][7] += psi;
internalFlux[2][8] = ch_factor * bz;
}
}
The numerical flux for ideal mhd again done with a reuse from (src/apps/imhd/imhd.cc) where the specific flux functions are also given in (src/apps/imhd/imhd.cc), which can be looked up. The divergence cleaning addition is added in a file
(src/apps/imhd/imhd_glm.cc)
const constants_imhd_glm_t * pC,
{
r_q_l[0] = q_l[0];
r_q_l[4] = q_l[4];
r_q_l[8] = q_l[8];
r_q_r[0] = q_r[0];
r_q_r[4] = q_r[4];
r_q_r[8] = q_r[8];
real psi_m = r_q_l[8] + 0.5*(r_q_r[8] - r_q_l[8]) - 0.5*pC->c_h*(r_q_r[5]-r_q_l[5]);
for (int i=0;i<9;i++)
{
r_dedner_flux[i] = 0.0;
numericalFlux[i] = 0.0;
}
r_dedner_flux[5] = psi_m;
r_dedner_flux[8] = pC->c_h*pC->c_h*Bxm;
}
Registration - this is at the bottom of wmapplication_imhd_mixed_glm.cc
(src/apps/imhd/wmapplication_imhd_mixed_glm.cc)
Add to CMakeLists.txt
(src/apps/imhd/CMakeLists.txt)
add_sources(
imhd.cc
imhd_glm.cc
wmapplication_imhd_mixed_glm.cc
)
Add to warpy
(tools/warpy/apps/imhd/imhd.py)
class imhd_mixed_glm(application):
'''
@brief Ideal MHD with Mixed GLM formulation as per Dedner
'''
def __init__(self, name, gamma,
variable, c_h=1.0,numerical_flux_type='Rusanov',components=None, floor_density=None, floor_pressure=None):
super(imhd_mixed_glm, self).__init__(
name,
req_attrs=[('Kind', 'iMHD_MIXED_GLM'),
('gamma', gamma),
('C_H', c_h),
('NumericalFluxType',numerical_flux_type)],
opt_attrs=[('MinDensity', floor_density),
('MinPressure',floor_pressure)],
variables=[('Fluid', variable, components)])
Add warpy function to init
(tools/warpy/apps/__init__.py)
(tools/warpy/apps/imhd/__init__.py)
from .imhd import imhd_mixed_glm
Add warpy function to CMake
(tools/warpy/apps/CMakeLists.txt)
WARPY_FILES(__init__.py
application.py
)
add_subdirectory(imhd)
(tools/warpy/apps/imhd/CMakeLists.txt)
WARPY_FILES(
__init__.py
imhd.py
)