DREAM supports the inclusion of an arbitrary number of ions species. Since we
solve for the density of each ion charge state, each ion contributes
\(Z+1\) independent fluid (i.e. radially varying) variables to the equation
system. This suggests that a special representation should be used for ions in
the code, in order to keep similar density variables close in memory. As a
result, DREAM combines all ion species into a single ion density, n_i
.
To keep track of and work with this ion density, a number of helper classes
are introduced, and this section of the documentation describes those.
While the details of the implementation are discussed below, it can be worth just stating what actually constitutes an ion in DREAM. To add a new ion to the simulation, the user must provide three things:
The name of the ion species (only used for communicating with the user)
The atomic charge \(Z\) of the ion species
The radial density profile of every charge state of the ion species
In addition to these, the user will also specify how to evolve the ions during the simulation. DREAM supports three methods for evolving ions in time, namely
Prescribed time evolution
Coronal equilibrium
Dynamic ionization/recombination balance
The first of these requires the user to provide the full spatiotemporal evolution of every charge state of the ion species to the code.
The second and third option represent variations of solving the ion rate equation
where \(n_i^{(j)}\) denotes the radial density of charge state \(j\) of ion species i, \(I_i^{(j)}\) is the corresponding ionization rate, \(R_i^{(j)}\) the corresponding radiative recombination rate, and \(\mathcal{I}_i^{(j)}\) the corresponding fast electron impact ionization coefficient. The difference between the “equilibrium” and “dynamic” options above is merely in the transient term on the LHS. In equilibrium mode, the ions are assumed to be in equilibrium, and so the transient term vanishes. In the dynamic mode, on the other hand, transient effects are included.
As mentioned above, all ion species and charge states are stored in a single
unknown quantity called n_i
. Given \(N\) ion species with atomic charges
\(Z_i\in\{ Z_1, Z_2, \ldots, Z_N \}\), defined on a radial grid with
\(n_r\) grid points, n_i
is stored as a single vector with
\(N_{n_i} = n_r\left[ \sum_i (Z_i+1) \right]\) elements. The vector is laid
out so that \(n_r\) adjacent elements in memory correspond to the same
radial density (i.e. to the same ion charge state).
A more graphical illustration is presented below.
Address |
0-\(n_r-1\) |
\(n_r\)-\(2n_r-1\) |
\(2n_r\)-\(3n_r-1\) |
… |
Contents |
\(n_1^{(0)}\) |
\(n_1^{(1)}\) |
\(n_1^{(2)}\) |
… |
Since the unknown quantity n_i
is stored in the same way as every any other
unknown quantity in DREAM, we cannot directly store information about the layout
of the ion densities with that unknown quantity. Instead, an IonHandler
object is introduced for that purpose. The IonHandler
keeps tracks of ion
names, atomic charges, and their order in the n_i
solution vector. Via calls
to GetIndex(iZ, Z0)
, we can then translate a set consisting of an ion
species index (iZ
) and the charge state number (Z0
) to an index into the
n_i
vector containing the actual ion densities. The example below shows how
to access the radial density \(n_3^{(5)}\), corresponding to the fifth
charge state of the ion with index 3:
const real_t *n_i = eqsys->GetUnknownData(id_n_i);
len_t n35offs = ionHandler->GetIndex(3, 5);
const real_t *n35 = n_i + n35offs;
Due to the monolithic way in which ion densities are stored, regular equation
terms cannot be directly applied to n_i
. Instead, a class called
IonEquationTerm
has been derived to accomodate this need. The
IonEquationTerm
has all the same methods as the regular EquationTerm
,
but replaces the SetJacobianBlock()
, SetMatrixElements()
and
SetVectorElements()
methods with the more specific
SetCSJacobianBlock()
, SetCSMatrixElements()
and
SetCSVectorElements()
. The CS
in the names of these methods stands for
charge state, and hence these methods set the matrix/vector elements for a
specified ion species charge state. The regular SetXXX()
methods are as
usual called by the EquationSystem
class when it is time to rebuild the
equation system matrix/vector, and these methods in turn iterate over all the
ion species charge states to which the equation term applies and calls the
SetCSXXX()
methods.
In addition to the regular input for the SetXXX()
methods, the
SetCSXXX()
take three additional arguments as input:
len_t iIon
— the ion species identifier
len_t Z0
— the ion charge state number
len_t rOffset
— the offset in the n_i
vector that should be added in
order to get the density corresponding to this ion.
When implementing a new ion equation term, it is the SetCSXXX()
methods
which should be overridden, instead of the usual SetXXX()
methods for the
regular EquationTerm
. Apart from this, the same rules apply to any
IonEquationTerm
as for the regular EquationTerm
, i.e. you will need to
also override the Rebuild()
and GetNumberOfNonZerosPerRow()
methods.