Skip to content

Commit a393c7b

Browse files
Add external current handling to Ohm's law solver (#4405)
* add `WarpX::getedgelengths` and WarpX::getfaceareas` functions to return pointers to those multifabs * add external current support to the hybrid-PIC solver * add external current specification (for hybrid-PIC scheme) to picmi * add RZ support for `FiniteDifferenceSolver::CalculateCurrentAmpere` * allow an initial Bz field to be set in RZ * code cleanup and addition of CI test * avoid lambda capture issue * update documentation to show external current implementation * revert unwanted changes * restore RZ support that was lost during rebase * fix segfault when EB support is OFF * fix codeQL issue * add some details to docs * the external current only needs to be calculated once per field solve step * add description of `hybrid_pic_model.J[x/y/z]_external_grid_function(x, y, z, t)` to the documentation
1 parent d2633ba commit a393c7b

File tree

9 files changed

+329
-35
lines changed

9 files changed

+329
-35
lines changed

Docs/source/theory/kinetic_fluid_hybrid_model.rst

Lines changed: 36 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -35,25 +35,51 @@ neglecting the displacement current term :cite:p:`c-NIELSON1976`, giving,
3535
3636
\mu_0\vec{J} = \vec{\nabla}\times\vec{B},
3737
38-
where :math:`\vec{J} = \vec{J}_i - \vec{J}_e` is the total electrical current, i.e.
39-
the sum of electron and ion currents. Since ions are treated in the regular
40-
PIC manner, the ion current, :math:`\vec{J}_i`, is known during a simulation. Therefore,
38+
where :math:`\vec{J} = \sum_{s\neq e}\vec{J}_s + \vec{J}_e + \vec{J}_{ext}` is the total electrical current,
39+
i.e. the sum of electron and ion currents as well as any external current (not captured through plasma
40+
particles). Since ions are treated in the regular
41+
PIC manner, the ion current, :math:`\sum_{s\neq e}\vec{J}_s`, is known during a simulation. Therefore,
4142
given the magnetic field, the electron current can be calculated.
4243

43-
If we now further assume electrons are inertialess, the electron momentum
44-
equation yields,
44+
The electron momentum transport equation (obtained from multiplying the Vlasov equation by mass and
45+
integrating over velocity), also called the generalized Ohm's law, is given by:
4546

4647
.. math::
4748
48-
\frac{d(n_em_e\vec{V}_e)}{dt} = 0 = -en_e\vec{E}-\vec{J}_e\times\vec{B}-\nabla\cdot\vec{P}_e+en_e\vec{\eta}\cdot\vec{J},
49+
en_e\vec{E} = \frac{m}{e}\frac{\partial \vec{J}_e}{\partial t} + \frac{m}{e^2}\left( \vec{U}_e\cdot\nabla \right) \vec{J}_e - \nabla\cdot {\overleftrightarrow P}_e - \vec{J}_e\times\vec{B}+\vec{R}_e
4950
50-
where :math:`\vec{V_e}=\vec{J}_e/(en_e)`, :math:`\vec{P}_e` is the electron pressure
51-
tensor and :math:`\vec{\eta}` is the resistivity tensor. An expression for the electric field
52-
(generalized Ohm's law) can be obtained from the above as:
51+
where :math:`\vec{U}_e = \vec{J}_e/(en_e)` is the electron fluid velocity,
52+
:math:`{\overleftrightarrow P}_e` is the electron pressure tensor and
53+
:math:`\vec{R}_e` is the drag force due to collisions between electrons and ions.
54+
Applying the above momentum equation to the Maxwell-Faraday equation (:math:`\frac{\partial\vec{B}}{\partial t} = -\nabla\times\vec{E}`)
55+
and substituting in :math:`\vec{J}` calculated from the Maxwell-Ampere equation, gives,
5356

5457
.. math::
5558
56-
\vec{E} = -\frac{1}{en_e}\left( \vec{J}_e\times\vec{B} + \nabla\cdot\vec{P}_e \right)+\vec{\eta}\cdot\vec{J}.
59+
\frac{\partial\vec{J}_e}{\partial t} = -\frac{1}{\mu_0}\nabla\times\left(\nabla\times\vec{E}\right) - \frac{\partial\vec{J}_{ext}}{\partial t} - \sum_{s\neq e}\frac{\partial\vec{J}_s}{\partial t}.
60+
61+
Plugging this back into the generalized Ohm' law gives:
62+
63+
.. math::
64+
65+
\left(en_e +\frac{m}{e\mu_0}\nabla\times\nabla\times\right)\vec{E} =&
66+
- \frac{m}{e}\left( \frac{\partial\vec{J}_{ext}}{\partial t} + \sum_{s\neq e}\frac{\partial\vec{J}_s}{\partial t} \right) \\
67+
&+ \frac{m}{e^2}\left( \vec{U}_e\cdot\nabla \right) \vec{J}_e - \nabla\cdot {\overleftrightarrow P}_e - \vec{J}_e\times\vec{B}+\vec{R}_e.
68+
69+
If we now further assume electrons are inertialess (i.e. :math:`m=0`), the above equation simplifies to,
70+
71+
.. math::
72+
73+
en_e\vec{E} = -\vec{J}_e\times\vec{B}-\nabla\cdot{\overleftrightarrow P}_e+\vec{R}_e.
74+
75+
Making the further simplifying assumptions that the electron pressure is isotropic and that
76+
the electron drag term can be written as a simple resistance
77+
i.e. :math:`\vec{R}_e = en_e\vec{\eta}\cdot\vec{J}`, brings us to the implemented form of
78+
Ohm's law:
79+
80+
.. math::
81+
82+
\vec{E} = -\frac{1}{en_e}\left( \vec{J}_e\times\vec{B} + \nabla P_e \right)+\vec{\eta}\cdot\vec{J}.
5783
5884
Lastly, if an electron temperature is given from which the electron pressure can
5985
be calculated, the model is fully constrained and can be evolved given initial

Docs/source/usage/parameters.rst

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2126,25 +2126,28 @@ Maxwell solver: kinetic-fluid hybrid
21262126
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
21272127

21282128
* ``hybrid_pic_model.elec_temp`` (`float`)
2129-
If ``algo.maxwell_solver`` is set to ``hybrid``, this sets the electron temperature, in eV, used to calculate
2130-
the electron pressure (see :ref:`here <theory-hybrid-model-elec-temp>`).
2129+
If ``algo.maxwell_solver`` is set to ``hybrid``, this sets the electron temperature, in eV, used to calculate
2130+
the electron pressure (see :ref:`here <theory-hybrid-model-elec-temp>`).
21312131

21322132
* ``hybrid_pic_model.n0_ref`` (`float`)
2133-
If ``algo.maxwell_solver`` is set to ``hybrid``, this sets the reference density, in :math:`m^{-3}`, used to calculate
2134-
the electron pressure (see :ref:`here <theory-hybrid-model-elec-temp>`).
2133+
If ``algo.maxwell_solver`` is set to ``hybrid``, this sets the reference density, in :math:`m^{-3}`, used to calculate
2134+
the electron pressure (see :ref:`here <theory-hybrid-model-elec-temp>`).
21352135

21362136
* ``hybrid_pic_model.gamma`` (`float`) optional (default ``5/3``)
2137-
If ``algo.maxwell_solver`` is set to ``hybrid``, this sets the exponent used to calculate
2138-
the electron pressure (see :ref:`here <theory-hybrid-model-elec-temp>`).
2137+
If ``algo.maxwell_solver`` is set to ``hybrid``, this sets the exponent used to calculate
2138+
the electron pressure (see :ref:`here <theory-hybrid-model-elec-temp>`).
21392139

21402140
* ``hybrid_pic_model.plasma_resistivity(rho)`` (`float` or `str`) optional (default ``0``)
2141-
If ``algo.maxwell_solver`` is set to ``hybrid``, this sets the plasma resistivity in :math:`\Omega m`.
2141+
If ``algo.maxwell_solver`` is set to ``hybrid``, this sets the plasma resistivity in :math:`\Omega m`.
2142+
2143+
* ``hybrid_pic_model.J[x/y/z]_external_grid_function(x, y, z, t)`` (`float` or `str`) optional (default ``0``)
2144+
If ``algo.maxwell_solver`` is set to ``hybrid``, this sets the external current (on the grid) in :math:`A/m^2`.
21422145

21432146
* ``hybrid_pic_model.n_floor`` (`float`) optional (default ``1``)
2144-
If ``algo.maxwell_solver`` is set to ``hybrid``, this sets the plasma density floor, in :math:`m^{-3}`, which is useful since the generalized Ohm's law used to calculate the E-field includes a :math:`1/n` term.
2147+
If ``algo.maxwell_solver`` is set to ``hybrid``, this sets the plasma density floor, in :math:`m^{-3}`, which is useful since the generalized Ohm's law used to calculate the E-field includes a :math:`1/n` term.
21452148

21462149
* ``hybrid_pic_model.substeps`` (`int`) optional (default ``100``)
2147-
If ``algo.maxwell_solver`` is set to ``hybrid``, this sets the number of sub-steps to take during the B-field update.
2150+
If ``algo.maxwell_solver`` is set to ``hybrid``, this sets the number of sub-steps to take during the B-field update.
21482151

21492152
.. note::
21502153

Python/pywarpx/picmi.py

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1136,9 +1136,14 @@ class HybridPICSolver(picmistandard.base._ClassWithInit):
11361136
11371137
substeps: int, default=100
11381138
Number of substeps to take when updating the B-field.
1139+
1140+
Jx/y/z_external_function: str
1141+
Function of space and time specifying external (non-plasma) currents.
11391142
"""
11401143
def __init__(self, grid, Te=None, n0=None, gamma=None,
1141-
n_floor=None, plasma_resistivity=None, substeps=None, **kw):
1144+
n_floor=None, plasma_resistivity=None, substeps=None,
1145+
Jx_external_function=None, Jy_external_function=None,
1146+
Jz_external_function=None, **kw):
11421147
self.grid = grid
11431148
self.method = "hybrid"
11441149

@@ -1150,6 +1155,10 @@ def __init__(self, grid, Te=None, n0=None, gamma=None,
11501155

11511156
self.substeps = substeps
11521157

1158+
self.Jx_external_function = Jx_external_function
1159+
self.Jy_external_function = Jy_external_function
1160+
self.Jz_external_function = Jz_external_function
1161+
11531162
self.handle_init(kw)
11541163

11551164
def initialize_inputs(self):
@@ -1166,6 +1175,15 @@ def initialize_inputs(self):
11661175
'plasma_resistivity(rho)', self.plasma_resistivity
11671176
)
11681177
pywarpx.hybridpicmodel.substeps = self.substeps
1178+
pywarpx.hybridpicmodel.__setattr__(
1179+
'Jx_external_grid_function(x,y,z,t)', self.Jx_external_function
1180+
)
1181+
pywarpx.hybridpicmodel.__setattr__(
1182+
'Jy_external_grid_function(x,y,z,t)', self.Jy_external_function
1183+
)
1184+
pywarpx.hybridpicmodel.__setattr__(
1185+
'Jz_external_grid_function(x,y,z,t)', self.Jz_external_function
1186+
)
11691187

11701188

11711189
class ElectrostaticSolver(picmistandard.PICMI_ElectrostaticSolver):

Source/FieldSolver/FiniteDifferenceSolver/FiniteDifferenceSolver.H

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,7 @@ class FiniteDifferenceSolver
139139
* \param[out] Efield vector of electric field MultiFabs updated at a given level
140140
* \param[in] Jfield vector of total current MultiFabs at a given level
141141
* \param[in] Jifield vector of ion current density MultiFabs at a given level
142+
* \param[in] Jextfield vector of external current density MultiFabs at a given level
142143
* \param[in] Bfield vector of magnetic field MultiFabs at a given level
143144
* \param[in] rhofield scalar ion charge density Multifab at a given level
144145
* \param[in] Pefield scalar electron pressure MultiFab at a given level
@@ -150,6 +151,7 @@ class FiniteDifferenceSolver
150151
void HybridPICSolveE ( std::array< std::unique_ptr<amrex::MultiFab>, 3>& Efield,
151152
std::array< std::unique_ptr<amrex::MultiFab>, 3>& Jfield,
152153
std::array< std::unique_ptr<amrex::MultiFab>, 3 > const& Jifield,
154+
std::array< std::unique_ptr<amrex::MultiFab>, 3 > const& Jextfield,
153155
std::array< std::unique_ptr<amrex::MultiFab>, 3> const& Bfield,
154156
std::unique_ptr<amrex::MultiFab> const& rhofield,
155157
std::unique_ptr<amrex::MultiFab> const& Pefield,
@@ -233,6 +235,7 @@ class FiniteDifferenceSolver
233235
std::array< std::unique_ptr<amrex::MultiFab>, 3>& Efield,
234236
std::array< std::unique_ptr<amrex::MultiFab>, 3> const& Jfield,
235237
std::array< std::unique_ptr<amrex::MultiFab>, 3> const& Jifield,
238+
std::array< std::unique_ptr<amrex::MultiFab>, 3 > const& Jextfield,
236239
std::array< std::unique_ptr<amrex::MultiFab>, 3> const& Bfield,
237240
std::unique_ptr<amrex::MultiFab> const& rhofield,
238241
std::unique_ptr<amrex::MultiFab> const& Pefield,
@@ -337,6 +340,7 @@ class FiniteDifferenceSolver
337340
std::array< std::unique_ptr<amrex::MultiFab>, 3>& Efield,
338341
std::array< std::unique_ptr<amrex::MultiFab>, 3> const& Jfield,
339342
std::array< std::unique_ptr<amrex::MultiFab>, 3> const& Jifield,
343+
std::array< std::unique_ptr<amrex::MultiFab>, 3 > const& Jextfield,
340344
std::array< std::unique_ptr<amrex::MultiFab>, 3> const& Bfield,
341345
std::unique_ptr<amrex::MultiFab> const& rhofield,
342346
std::unique_ptr<amrex::MultiFab> const& Pefield,

Source/FieldSolver/FiniteDifferenceSolver/HybridPICModel/HybridPICModel.H

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,20 @@ public:
4646

4747
void InitData ();
4848

49+
/**
50+
* \brief
51+
* Function to evaluate the external current expressions and populate the
52+
* external current multifab. Note the external current can be a function
53+
* of time and therefore this should be re-evaluated at every step.
54+
*/
55+
void GetCurrentExternal (
56+
amrex::Vector<std::array< std::unique_ptr<amrex::MultiFab>, 3>> const& edge_lengths
57+
);
58+
void GetCurrentExternal (
59+
std::array< std::unique_ptr<amrex::MultiFab>, 3> const& edge_lengths,
60+
int lev
61+
);
62+
4963
/**
5064
* \brief
5165
* Function to calculate the total current based on Ampere's law while
@@ -133,10 +147,19 @@ public:
133147
std::unique_ptr<amrex::Parser> m_resistivity_parser;
134148
amrex::ParserExecutor<1> m_eta;
135149

150+
/** External current */
151+
std::string m_Jx_ext_grid_function = "0.0";
152+
std::string m_Jy_ext_grid_function = "0.0";
153+
std::string m_Jz_ext_grid_function = "0.0";
154+
std::array< std::unique_ptr<amrex::Parser>, 3> m_J_external_parser;
155+
std::array< amrex::ParserExecutor<4>, 3> m_J_external;
156+
bool m_external_field_has_time_dependence = false;
157+
136158
// Declare multifabs specifically needed for the hybrid-PIC model
137159
amrex::Vector< std::unique_ptr<amrex::MultiFab> > rho_fp_temp;
138160
amrex::Vector<std::array< std::unique_ptr<amrex::MultiFab>, 3 > > current_fp_temp;
139161
amrex::Vector<std::array< std::unique_ptr<amrex::MultiFab>, 3 > > current_fp_ampere;
162+
amrex::Vector<std::array< std::unique_ptr<amrex::MultiFab>, 3 > > current_fp_external;
140163
amrex::Vector< std::unique_ptr<amrex::MultiFab> > electron_pressure_fp;
141164

142165
// Helper functions to retrieve hybrid-PIC multifabs

0 commit comments

Comments
 (0)