Skip to content

Commit

Permalink
lots more integrator doc fixest
Browse files Browse the repository at this point in the history
  • Loading branch information
zingale committed Dec 5, 2024
1 parent 2afa043 commit 1a81667
Show file tree
Hide file tree
Showing 4 changed files with 196 additions and 78 deletions.
2 changes: 0 additions & 2 deletions integration/integrator_setup_strang.H
Original file line number Diff line number Diff line change
Expand Up @@ -90,8 +90,6 @@ IntegratorT integrator_setup (BurnT& state, amrex::Real dt, bool is_retry)

burn_to_integrator(state, int_state);

// Save the initial composition, temperature, and energy for our later diagnostics.

return int_state;
}

Expand Down
99 changes: 66 additions & 33 deletions sphinx_docs/source/integrators.rst
Original file line number Diff line number Diff line change
Expand Up @@ -105,30 +105,50 @@ passed into the integration routines. For this reason, we often need
to pass both the specific integrator's type (e.g. ``dvode_t``) and
``burn_t`` objects into the lower-level network routines.

The overall flow of the integrator is (using VODE as the example):
Below we outline the overall flow of the integrator (using VODE as the
example). Most of the setup and cleanup after calling the particular
integration routine is the same for all integrators, and is handled by
the functions ``integrator_setup()`` and ``integrator_cleanup()``.

#. Call the EOS directly on the input ``burn_t`` state using :math:`\rho` and :math:`T` as inputs.
.. index:: integrator.scale_system, burn_to_integrator, integrator_to_burn
.. index:: integrator.call_eos_in_rhs, integrator.subtract_internal_energy, integrator.burner_verbose

#. Call the EOS directly on the input ``burn_t`` state using
:math:`\rho` and :math:`T` as inputs.

#. Scale the absolute energy tolerance if we are using
``integrator.scale_system``

#. Fill the integrator type by calling ``burn_to_integrator()`` to create a
``dvode_t``.

#. call the ODE integrator, ``dvode()``, passing in the ``dvode_t`` _and_ the
#. Save the initial thermodynamic state for diagnostics and optionally
subtracting off the initial energy later.

#. Call the ODE integrator, ``dvode()``, passing in the ``dvode_t`` *and* the
``burn_t`` --- as noted above, the auxiliary information that is
not part of the integration state will be obtained from the
``burn_t``.

#. subtract off the energy offset---we now store just the energy released
in the ``dvode_t`` integration state.
#. Convert back to a ``burn_t`` by calling ``integrator_to_burn``

#. convert back to a ``burn_t`` by calling ``integrator_to_burn``
#. Recompute the temperature if we are using ``integrator.call_eos_in_rhs``.

#. normalize the abundances so they sum to 1.
#. If we set ``integrator.subtract_internal_energy``, then subtract
off the energy offset, the energy stored is now just that generated
by reactions.

#. Normalize the abundances so they sum to 1.

#. Output statistics on the integration if we set ``integrator.burner_verbose``.
This is not recommended for big simulations, as it will output information
for every zone's burn.

.. index:: integrator.subtract_internal_energy

.. note::
.. important::

Upon exit, ``burn_t burn_state.e`` is the energy *released* during
By default, upon exit, ``burn_t burn_state.e`` is the energy *released* during
the burn, and not the actual internal energy of the state.

Optionally, by setting ``integrator.subtract_internal_energy=0``
Expand All @@ -155,7 +175,8 @@ The righthand side of the network is implemented by

.. code-block:: c++

void actual_rhs(burn_t& state, Array1D<Real, 1, neqs>& ydot)
AMREX_GPU_HOST_DEVICE AMREX_INLINE
void actual_rhs(burn_t& state, amrex::Array1D<amrex::Real, 1, neqs>& ydot)

All of the necessary integration data comes in through state, as:

Expand Down Expand Up @@ -245,7 +266,11 @@ The analytic Jacobian is specific to each network and is provided by

.. code-block:: c++

void actual_jac(burn_t& state, MathArray2D<1, neqs, 1, neqs>& jac)
template<class MatrixType>
AMREX_GPU_HOST_DEVICE AMREX_INLINE
void actual_jac(const burn_t& state, MatrixType& jac)

where the ``MatrixType`` is most commonly ``MathArray2D<1, neqs, 1, neqs>``

The Jacobian matrix elements are stored in ``jac`` as:

Expand Down Expand Up @@ -316,13 +341,9 @@ Thermodynamics and :math:`e` Evolution
======================================

The thermodynamic equation in our system is the evolution of the internal energy,
:math:`e`.

.. note::

When the system is integrated in an operator-split approach, the
energy equation accounts for only the nuclear energy release and
not pdV work.
:math:`e`. During the course of the integration, we ensure that the temperature stay
below the value ``integrator.MAX_TEMP`` (defaulting to ``1.e11``) by clamping the
temperature if necessary.

At initialization, :math:`e` is set to the value from the EOS consistent
with the initial temperature, density, and composition:
Expand All @@ -331,28 +352,40 @@ with the initial temperature, density, and composition:
e_0 = e(\rho_0, T_0, {X_k}_0)
In the integration routines, this is termed the *energy offset*.

As the system is integrated, :math:`e` is updated to account for the
nuclear energy release,
nuclear energy release (and thermal neutrino losses),

.. math:: e(t) = e_0 + \int_{t_0}^t f(\dot{Y}_k) dt

As noted above, upon exit, we subtract off this initial offset, so ``state.e`` in
the returned ``burn_t`` type from the ``actual_integrator``
call represents the energy *release* during the burn.
.. note::

When the system is integrated in an operator-split approach, the
energy equation accounts for only the nuclear energy release and
not pdV work.

If ``integrator.subtract_internal_energy`` is set, then, on exit, we
subtract off this initial $e_0$, so ``state.e`` in the returned
``burn_t`` type from the ``actual_integrator`` call represents the
energy *release* during the burn.

Integration of Equation :eq:`eq:enuc_integrate`
requires an evaluation of the temperature at each integration step
(since the RHS for the species is given in terms of :math:`T`, not :math:`e`).
This involves an EOS call and is the default behavior of the integration.
Note also that for the Jacobian, we need the specific heat, :math:`c_v`, since we
usually calculate derivatives with respect to temperature (as this is the form
the rates are commonly provided in).
Integration of Equation :eq:`eq:enuc_integrate` requires an evaluation
of the temperature at each integration step (since the RHS for the
species is given in terms of :math:`T`, not :math:`e`). This involves
an EOS call and is the default behavior of the integration.

Note also that for the Jacobian, we need the specific heat,
:math:`c_v`, since we usually calculate derivatives with respect to
temperature (as this is the form the rates are commonly provided in).

.. index:: integrator.call_eos_in_rhs

.. note::

If desired, the EOS call can be skipped and the temperature and $c_v$ kept
frozen over the entire time interval of the integration by setting ``integrator.call_eos_in_rhs=0``.
If desired, the EOS call can be skipped and the temperature and
$c_v$ kept frozen over the entire time interval of the integration
by setting ``integrator.call_eos_in_rhs=0``.

.. index:: integrator.integrate_energy

We also provide the option to completely remove the energy equation from
the system by setting ``integrator.integrate_energy=0``.
125 changes: 82 additions & 43 deletions sphinx_docs/source/nse.rst
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,12 @@
NSE
***

.. important::

NSE is only supported with the simplified-SDC method for
coupling hydrodynamics and reactions. We do not support
operator-splitting (Strang) coupling with NSE.

The reaction networks in Microphysics have the ability to use NSE
instead of integrating the entire network when the conditions are
appropriate. There are 2 different implementations of NSE in
Expand All @@ -23,8 +29,11 @@ Microphysics, that have slightly different use cases.
:math:`\langle B/A\rangle`. All of the EOS calls will work with
these quantities.

This algorithm was described in :cite:`sdc-nse`.

This is enabled via ``USE_NSE_TABLE``


* :ref:`self_consistent_nse` : this adds an NSE solver to the network that
can be called to find the equilibrium abundances of each of the
species defined in the network. It works with any of the
Expand Down Expand Up @@ -61,18 +70,19 @@ standard ``aprox19`` network with a table for nuclear statistic
equilibrium resulting from a much larger network at high density and
temperatures. This option is enabled by building with:

.. prompt:: bash
.. code:: bash
NETWORK_DIR=aprox19 USE_NSE_TABLE=TRUE
Composition and EOS
-------------------

The NSE table was generated using a 125 nuclei reaction network
(described in :cite:`ma:2013`), and includes electron-capture rates,
so the compositional quantities it carries, :math:`\bar{A}` and
:math:`Y_e` and not representable from the 19 isotopes we carry in the
network. In particular, it can attain a lower :math:`Y_e` than
The NSE table was generated using `pynucastro
<https://pynucastro.github.io/pynucastro/>` using 96 nuclei and
electron/positron capture/decay rates from :cite:`langanke:2001`. The
table takes $Y_e$ as the primary composition variable and provides a
set of mass fractions that is mapped into those used by ``aprox19``.
Using the value allows us to attain a lower :math:`Y_e` than
``aprox19`` can represent.

For this reason, when we are using the NSE network, we always take the
Expand All @@ -86,35 +96,39 @@ NSE Table Outputs
-----------------

The NSE table provides values for the auxiliary composition,
:math:`Y_e`, :math:`\bar{A}`, and :math:`\langle B/A \rangle`
:math:`\bar{A}`, and :math:`\langle B/A \rangle`
resulting from the full 125 nuclei network. It also provides a set of 19
:math:`X_k` that map into the isotopes carried by ``aprox19``.


These three quantities are stored as ``aux`` data in the network and
are indexed as ``iye``, ``iabar``, and ``ibea``. Additionally, when
coupling to hydrodynamics, we need to advect these auxiliary
quantities.

For Strang split coupling of hydro and reactions, :math:`DX_k/Dt = 0`,
and our evolution equations are:
The evolution equations for the auxiliary variables are:

.. math::
\begin{align*}
\frac{DY_e}{Dt} &= \sum_k \frac{Z_k}{A_k} \frac{DX_k}{Dt} = 0 \\
\frac{D}{Dt} \frac{1}{\bar{A}} &= - \frac{1}{\bar{A}^2} \frac{D\bar{A}}{Dt} = \sum_k \frac{1}{A_k} \frac{DX_k}{Dt} = 0 \rightarrow \frac{D\bar{A}}{Dt} = 0 \\
\frac{D}{Dt} \left (\frac{B}{A} \right ) &= \sum_k \frac{B_k}{A_k} \frac{DX_k}{Dt} = 0
\frac{DY_e}{Dt} &= \sum_k \frac{Z_k}{A_k} \dot{\omega}_k \\
\frac{D\bar{A}}{Dt} &= -\bar{A}^2 \sum_k \frac{1}{A_k} \dot{\omega}_k \\
\frac{D}{Dt} \left (\frac{B}{A} \right ) &= \sum_k \frac{B_k}{A_k} \dot{\omega}_k
\end{align*}
Therefore each of these auxiliary equations obeys an advection equation
in the hydro part of the advancement.

The table also provides $dY_e/dt$, $(d\langle
B/A\rangle/dt)_\mathrm{weak}$, and $\epsilon_{\nu,\mathrm{react}$, the
weak rate neutrino losses. These quantities are used to update the
thermodynamic state as we integrate.

NSE Flow
--------

The basic flow of a simulation using ``aprox19`` + the NSE table is as follows:
.. index:: integrator.nse_deriv_dt_factor, integrator.nse_include_enu_weak

The time integration algorithm is described in detail in :cite:`sdc-nse`. Here
we provide an outline:

* initialize the problem, including :math:`X_k`

Expand All @@ -129,34 +143,56 @@ The basic flow of a simulation using ``aprox19`` + the NSE table is as follows:

* if we are in an NSE region:

* use :math:`\rho`, :math:`T`, and :math:`Y_e` to call the table.
This returns: :math:`dY_e/dt`, :math:`(B/A)_{\rm out}`, and :math:`\bar{A}_{\rm out}`.
* Compute the initial temperature given $\rho$, $e$, and $Y_e$,
using an EOS inversion algorithm that understands NSE (in
particular that the composition depends on $T$ in NSE)

* update :math:`Y_e` :
* Compute the plasma neutrino losses, $\epsilon_{\nu,\mathrm{thermal}}$

.. math::
* Use :math:`\rho`, :math:`T`, and :math:`Y_e` to evaluate the NSE
state and construct $[\Rb(\Uc^\prime)]^n$, the source term from reactions to the
reduced conserved state $\Uc^\prime$ (this is the state used by the SDC algorithm
and includes the internal energy density, mass fractions, and auxiliary variables).

(Y_e)_{\rm out} = (Y_e)_{\rm in} + \Delta t \frac{dY_e}{dt}
This is done via finite differencing in time (through a step
$\tau \ll \Delta t$), and the reactive sources are constructed
to exclude the advective contributions. The size of $\tau$ is
controlled via ``integrator.nse_deriv_dt_factor``.

* :math:`\bar{A}_{\rm out}` is simply the value returned from the table

* the energy generation rate, :math:`e_{\rm nuc}` is:
In particular, the energy source is constructed as:

.. math::
e_{\rm nuc} = \eta \left [ \left ( \frac{B}{A} \right )_{\rm out} -
\left ( \frac{B}{A} \right )_{\rm in} \right ] * \frac{1.602 \times 10^{-6} {\rm erg}}{{\rm MeV}} N_A \frac{1}{\Delta t}
R(\rho e) = N_A \frac{\Delta (\rho \langle B/A\rangle)}{\tau} + N_A \Delta m_{np} c^2 \rho \frac{dY_e}{dt} - \rho (\epsilon_{\nu,\mathrm{thermal}} + \epsilon{\nu,\mathrm{react}})
where $\Delta m_{np}$ is the difference between the neutron and H atom mass.

.. important::

It only makes sense to include the weak rate neutrino losses, $\epsilon{\nu,\mathrm{react}}$,
if the initial model that you are using in your simulation also included those losses.
Otherwise, the energy loss from our NSE table will likely be too great and that simulation
will not be in equilibrium. This is an issue, for example, when using a MESA model
constructed with ``aprox21``, which does not have all of the weak rates we model here.

The weak rate neutrino losses can be disabled by ``integrator.nse_include_enu_weak``.

where :math:`\eta` is an inertia term < 1 to prevent the energy changing too much in one set.
* Predict $\Uc^\prime$ to the midpoint in time, $n+1/2$ and construct
$[\Rb(\Uc^\prime)]^{n+1/2}$.

* the new binding energy for the zone is then:
* Do the final update to time $n$ as:

.. math::
\left ( \frac{B}{A} \right )_{\rm out} = \left ( \frac{B}{A} \right )_{\rm in} + \eta \left [ \left ( \frac{B}{A} \right )_{\rm out} - \left ( \frac{B}{A} \right )_{\rm in} \right ]
\Uc^{\prime,n+1/2} = \Uc^{\prime,n} + \frac{\Delta t}{2} [\Advs{\Uc^\prime}]^{n+1/2} + \frac{\Delta t}{2} [\Rb(\Uc^\prime)]^{n+1/2}
* update the mass fractions, :math:`X_k`, using the values from the table
where $[\Advs{\Uc^\prime}]^{n+1/2}$ are the advective updates carried by the SDC
algorithm.

* Compute the energy generation rate from the change in internal energy from $\Uc^{\prime,n}$ to $\Uc^{\prime,n+1}$, excluding advection.

* Update the total energy.

* if we are not in NSE:

Expand All @@ -168,35 +204,38 @@ The basic flow of a simulation using ``aprox19`` + the NSE table is as follows:
NSE check
---------

For a zone to be consider in NSE, we require $\rho$ > ``rho_nse`` and *either*
.. index:: network.rho_nse, network.T_nse, network.T_always_nse
.. index:: network.He_Fe_nse, network.C_nse, network.O_nse, network.Si_nse

For a zone to be consider in NSE, we require $\rho$ > ``network.rho_nse`` and *either*

* $T$ > ``T_nse`` together with the composition check
* $T$ > ``network.T_nse`` together with the composition check

* $T$ > ``T_always_nse``
* $T$ > ``network.T_always_nse``

where we assume that ``T_always_nse`` > ``T_nse``.

The composition check considers the following nuclei groups:

* ``He_group``: atomic numbers 1 to 2 (H to He)
* He-group: atomic numbers 1 to 2 (H to He)

* ``C_group``: atomic numbers 6 to 7 (C to N)
* C-group: atomic numbers 6 to 7 (C to N)

* ``O_group``: atomic number 8 (O)
* O-group: atomic number 8 (O)

* ``Si_group``: atomic number 14 (Si)
* Si-group: atomic number 14 (Si)

* ``Fe_group``: atomic numbers 24 to 30 (Cr to Zn)
* Fe-group: atomic numbers 24 to 30 (Cr to Zn)

and we then say that a composition supports NSE if:

* :math:`X(C_\mathrm{group})` < ``C_nse``
* :math:`X(C_\mathrm{group})` < ``network.C_nse``

* :math:`X(O_\mathrm{group})` < ``O_nse``
* :math:`X(O_\mathrm{group})` < ``network.O_nse``

* :math:`X(Si_\mathrm{group})` < ``Si_nse``
* :math:`X(Si_\mathrm{group})` < ``network.Si_nse``

* :math:`X(Fe_\mathrm{group}) + X(He_\mathrm{group})` > ``He_Fe_nse``
* :math:`X(Fe_\mathrm{group}) + X(He_\mathrm{group})` > ``network.He_Fe_nse``



Expand All @@ -205,9 +244,9 @@ NSE table ranges

The NSE table was created for:

* :math:`9 < \log_{10}(T) < 10.4`
* :math:`9.4 < \log_{10}(T) < 10.4`
* :math:`7 < \log_{10}(\rho) < 10`
* :math:`0.4 < Y_e < 0.5`
* :math:`0.43 < Y_e < 0.5`



Expand Down
Loading

0 comments on commit 1a81667

Please sign in to comment.