Form of the Jacobian#
The exported networks include the Jacobian needed to use implicit integration methods. When energy evolution is included, the full form of the Jacobian looks like:
which we note as Regions I through IV:
Here we describe how and where each of these contributions are computed.
Note
For a RateCollection, the Jacobian
is only used for visualization, via plot_jacobian
and for the stiffness assessment in spectral_radius.
Note
For a PythonNetwork,
temperature is integrated instead of specific internal energy, \(e\).
Furthermore, we presently require a numerical (finite-difference) Jacobian when
evolving energy in a PythonNetwork.
Note
A SimpleCxxNetwork
does not include the energy evolution terms, so its Jacobian is species-only.
Region I: \(\partial \dot{Y}_i/\partial Y_j\)#
For most rates, this is simply the derivative of the composition factors that appear explicitly in the reactive flux. There are a few instances where the composition is internal to the rate itself that we also need to capture.
Basic rates#
For a 2-body strong-mediated reaction rate (with distinct reactants \(A\) and \(B\)), the reactive flux has the form:
and the contributions to \(\partial \dot{Y}_i/\partial Y_j\) would be \(\partial F_{AB}/\partial Y_j\),
For weak rates, the explicit composition dependence of the parent nucleus is also computed this way. E.g., for a decay with a parent nucleus \(P\), the flux is:
The composition derivative is:
and the contribution to the Jacobian is:
where \(n_i\) are the stoichiometric coefficients for species \(i\).
This is computed as:
RateCollection: directly viaRate.eval_jacobian_termPythonNetwork: as a string viaRate.jacobian_string_pyAmrexAstroCxxNetwork/SimpleCxxNetwork: symbolically using the SymPy methods inSympyRates.jacobian_term_symbol
ApproximateRate#
For some rate approximations, the composition appears explicitly in
the effective rate, \(\lambda\). In this case, we need to also need to
compute \(\partial\lambda/\partial Y_j\). An example of such a rate is the
ApproximateRate for \((nn,\gamma)\).
For this type of rate, the flux is:
and the contribution to the Jacobian is then:
In a rate class, we set Rate.rate_comp_dependence to a list of the
nuclei that the rate (\(\lambda\)) depends on internally. This
indicates that we need to compute this derivative.
The rate class itself will then compute the explicit \(\partial\lambda/\partial Y_j\) term
and store it in the python RateEval class or the C++ rate_derivs_t struct.
Status of this term:
RateCollection: included directly inApproximateRate.eval_jacobian_termPythonNetwork: included when the Jacobian is assembled inPythonNetwork.full_jacobian_element_stringAmrexAstroCxxNetwork/SimpleCxxNetwork: stored inrate_derivs_tin theApproximateRateevaluation and used symbolically using the SymPy methods inBaseCxxNetwork.compose_jacobian
Weak-tabulated rates#
TabularWeakRate store the rate
data as functions of \(T\) and \(\rho Y_e\), where
is the electron fraction. The flux for a tabulated weak-rate decay is:
this means that in addition to the derivative with respect to the explicit \(Y(P)\) composition term,
we also need to account for the \(Y_e\) in the tabulation. For the parent \(P\) and child \(C\) of the decay,
we need to accumulate the contributions due to \(Y_e\). We do this in an array dweak_ydot_dYe
that is part of RateEval or rate_derivs_t:
After all of the contributions are accumulated, they are added to every species column:
Important
This contribution affects all charged species, not just the parent and child. As a result, the Jacobian with weak rates in it will not be nearly as sparse.
Status of this term:
RateCollection: implemented directly inTabularWeakRate.eval_jacobian_termPythonNetwork: stored inRateEvalwhen the tabular rates are evaluated and added to the Jacobian during the final construction of the Jacobian.AmrexAstroCxxNetwork/SimpleCxxNetwork: stored inrate_derivs_tin theTabularWeakRateevaluation and explicitly added to the Jacobian during the final construction of the Jacobian in the template C++ code.
Screening#
The screening function applied to rates is a function of composition, through the plasma state. This contribution enters as \(Y_e\) and \(\zeta\):
Note
\(\zeta\) is related to the quantity \(\overline{Z^2}\) that the plasma_state_t calls z2bar as:
This means that for each rate flux (where we now explicitly include the screening factor, \(f\)):
the contribution to the Jacobian would be:
with
Note
This contribution affects all composition Jacobian elements for charged species, which means that the Jacobian is no longer really sparse.
Important
This contribution is not currently implemented.
Region II: \(\partial \dot{e}/\partial Y_j\)#
The energy evolution equation is:
Important
The neutrino contribution in \(\epsilon_{\nu,\mathrm{weak}}\) is constructed to be negative in the table interpolation routines. In general, \(\epsilon_{\nu,\mathrm{weak}}\) can also include a positive \(\gamma\) contribution, hence the \(+\) in this evolution equation. This term will represents an energy loss when neutrinos dominate.
Each of these energy terms depends on temperature and composition.
Binding energy contribution#
The dominant contribution to energy comes from the mass difference between reactants and products:
Differentiating with respect to \(Y_j\), we get the contribution to each column of the \(\partial \dot{e}/\partial Y_j\) row:
We already computed the elements \(\partial \dot{Y}_i/\partial Y_j\) in Region I, so we can interpret this sum as simply summing down a column, weighting by \(m_i c^2\).
Status of this term:
RateCollection: N/A (energy not considered)PythonNetwork: N/A (numerical Jacobian is used with self-heating networks)SimpleCxxNetwork: N/A (energy not considered)AmrexAstroCxxNetwork: computed directly in the template C++ code.
Weak rate energy contribution#
For a decay \(P \rightarrow C\), there is an energy release:
where \(\dot{e}_\nu\) is the neutrino energy release rate (erg/s) from the rate table, and \(\dot{e}_\gamma\) is the gamma energy release rate (erg/s) (note: most rate tabulations do not provide this).
There are 2 contributions we need to account for here.
Explicit Y dependence#
First we account for the explicit dependence on the
parent nucleus. We accumulate this in rate_derivs_t.denuc_weak_dY:
Important
\(\dot{e}_\nu\) is negative, so this quantity represents an energy loss.
This term is then added as \(\partial \epsilon_{\nu,\mathrm{weak}}/\partial Y_j\) to the respective species column in the energy row.
Electron fraction dependence#
The second term reflects the fact that \(\dot{e}_\nu = \dot{e}_\nu(T, \rho Y_e)\),
so we have an additional species contribution through \(Y_e\). We accumulate this
in rate_derivs_t.denuc_weak_dYe:
This term is then added as \(\texttt{denuc\_weak\_dYe} \, Z_j\) in the same place as the term above.
Status of these terms#
RateCollection: N/A (energy not considered)PythonNetwork: N/A (numerical Jacobian is used with self-heating networks)SimpleCxxNetwork: N/A (energy not considered)AmrexAstroCxxNetwork: stored inrate_derivs_tin theTabularWeakRateevaluation and explicitly added to the Jacobian during the final construction of the Jacobian in the template C++ code.
Thermal neutrinos#
The thermal neutrino loss rate, \(\epsilon_{\nu,\mathrm{thermal}}\), is a positive
energy loss per unit mass (erg/g/s). In AmrexAstroCxxNetwork, its composition
dependence enters through the mean mass number and mean charge:
At fixed \(T\) and \(\rho\), their composition derivatives are:
Since the thermal neutrino loss is subtracted in the energy equation, its contribution to the energy row is:
In actual_jac in the actual_rhs.H template, the call to
neutrino_cooling<1> returns these derivatives as dsnuda and dsnudz.
This term is then computed and added to the Jacobian.
Region III: \(\partial \dot{Y}_i/\partial e\)#
All rate objects can compute their temperature derivative, so we can compute the temperature derivative of the flux of each rate, e.g., for our 2-body strong-mediated reaction rate:
we have:
likewise, for tabulate weak rates, we can compute the derivative with respect to temperature by differentiating the interpolant.
We take advantage of the fact that each rate’s flux contributing to \(\partial Y_i/\partial t\) is linear in
\(\lambda\), and simply construct the algebraic form of \(\partial Y_i/\partial t\) using
\(\partial \lambda/\partial T\) instead of \(\lambda\) by calling the rhs_nuc function
in actual_rhs.H. This gives us \(\partial \dot{Y}_i/\partial T\).
Screening#
There is an additional contribution from the temperature derivative of screening. Taking into account screening \(f\), the flux is:
and the total temperature derivative
We store the quantity in \([ \ldots ]\) in rate_derivs_t.dscreened_rates_dT when we evaluate the rates.
Status of these terms#
RateCollection: N/A (energy not considered)PythonNetwork: N/A (numerical Jacobian is used with self-heating networks)SimpleCxxNetwork: N/A (energy not considered)AmrexAstroCxxNetwork: computed directly in the template C++ code using the rate derivatives with respect to \(T\).
As energy derivative#
We convert this to an energy derivative as:
Region IV: \(\partial \dot{e}/\partial e\)#
Binding energy contribution#
Starting with
and differentiating with respect to temperature, we have:
we have \(\frac{\partial \dot{Y}_i}{\partial T}\) from Region III. So we can just compute \(\frac{\partial \epsilon_\mathrm{nuc}}{\partial T}\) from these.
This is computed as:
RateCollection: N/A (energy not considered)PythonNetwork: N/A (numerical Jacobian is used with self-heating networks)SimpleCxxNetwork: N/A (energy not considered)AmrexAstroCxxNetwork: directly in the C++ template.
Weak-rate neutrino contribution#
The weak rate energy is:
We can differentiate this with respect to temperature (ignoring \(\dot{e}_\gamma\):
we accumulate this contribution in rate_derivs_t.denuc_weak_dT when we evaluate the
tabular rates as:
and then add it to the \(\partial \dot{e} /\partial T\) term in the Jacobian function.
This is computed as:
RateCollection: N/A (energy not considered)PythonNetwork: N/A (numerical Jacobian is used with self-heating networks)SimpleCxxNetwork: N/A (energy not considered)AmrexAstroCxxNetwork: directly in the C++ template.
Thermal neutrinos#
At fixed density and composition, the thermal neutrino contribution to the temperature derivative of the energy RHS is:
The same neutrino_cooling<1> call in the AmrexAstroCxxNetwork template
returns this loss-rate derivative as dsneutdt. In actual_jac, we subtract
it from jac_e_T.
As energy derivative#
We convert this to an energy derivative as:
Final conversion to energy#
There is one last part of the conversion from \(T\) to \(e\). If we take the derivative with respect to species, with \(e\) and \(\rho\) held constant, then \(T\) will change. We need to take into account how this affects the reactions.
This is handled by the integration wrappers in AMReX Microphysics.