Hydrodynamics
Introduction
The hydrodynamics scheme in Castro implements an unsplit second-order Godunov method. Characteristic tracing is used to time-center the input states to the Riemann solver. The same hydrodynamics routines are used for pure hydro and radiation hydrodynamics.
Some general notes:
Regardless of the dimensionality, we always carry around all 3 components of velocity/momentum—this allows for rotation sources easily.
When radiation is enabled (via RADIATION), we discuss the gas and radiation quantities separately. This generally applies to the temperature, pressure, internal energy, various adiabatic indices, and sound speed. When we refer to the “total” value of one of these, it means that both gas and radiation contributions are included. When we refer to the “gas” quantity, this is what the equation of state would return.
For continuity, we continue to use the “gas” qualifier even if we are not solving the radiation hydrodynamics equations. In this case, it still means that it comes through the equation of state, but note some of our equations of state (like the helmeos) include a radiation pressure contribution when we are running without radiation hydrodynamics enabled. In this case, we still refer to this as the “gas”.
Hydrodynamics Data Structures
Within the routines that implement the hydrodynamics, there are several main data structures that hold the state.
conserved state: these arrays generally begin with
u
, e.g.,uin
,uout
. TheNUM_STATE
components for the state data in the array are accessed using integer keys defined in Table 6.Table 6 The integer variables to index the conservative state array variable
quantity
note
URHO
UMX
UMY
UMZ
UEDEN
UEINT
this is computed from the other quantities using
UTEMP
this is computed from the other quantities using the EOS
UFA
the first advected quantity
UFS
the first species
UFX
the first auxiliary variable
USHK
a shock flag
(used for shock detection)
UMR
radial momentum
(if
HYBRID_MOMENTUM
is defined)UML
angular momentum
(if
HYBRID_MOMENTUM
is defined)UMP
vertical momentum
(if
HYBRID_MOMENTUM
is defined)primitive variable state: these arrays generally simply called
q
, and hasNQ
components.A related quantity is
NQSRC
which is the number of primitive variable source terms.NQSRC
≤NQ
.Note
if
RADIATION
is defined, then only the gas/hydro terms are present inNQSRC
.Table 7 gives the names of the primitive variable integer keys for accessing these arrays. Note, unless otherwise specified the quantities without a subscript are “gas” only and those with the “tot” subscript are “gas + radiation”.
Table 7 Primitive State Vector Integer Keys variable
quantity
note
QRHO
QU
QV
QW
QPRES
QREINT
QTEMP
QFA
the first advected quantity
QFS
the first species
QFX
the first auxiliary variable
QPTOT
the total pressure, gas + radiation
QREITOT
the total specific internal energy, gas + radiation
QRAD
the radiation energy (there are ngroups of these)
auxiliary primitive variables: these arrays are generally called qaux. The main difference between these and the regular primitive variables is that we do not attempt to do any reconstruction on their profiles. There are
NQAUX
quantities, indexed by the integer keys listed in Table 8. Note, unless otherwise specified the quantities without a subscript are “gas” only and those with the “tot” subscript are “gas + radiation”.Table 8 The integer variable keys for accessing the auxiliary primitive state vector, quax. variable
quantity
note
QGAMC
the first adiabatic exponent, as returned from the EOS
QC
the sound speed, as returned from the EOS
QGAMCG
includes radiation components (defined only if
RADIATION
is defined)QCG
total sound speed including radiation (defined only if
RADIATION
is defined)QLAMS
the
ngroups
flux limiters (defined only ifRADIATION
is defined)interface variables: these are the time-centered interface states returned by the Riemann solver. They are used to discretize some non-conservative terms in the equations. These arrays are generally called
q1
,q2
, andq3
for the x, y, and z interfaces respectively. There areNGDNV
components accessed with the integer keys defined in Table 9 Note, unless otherwise specified the quantities without a subscript are “gas” only and those with the “tot” subscript are “gas + radiation”.Table 9 The integer variable keys for accessing the Godunov interface state vectors. variable
quantity
note
GDRHO
GDU
GDV
GDW
GDPRES
regardless of whether
RADIATION
is defined, this is always just the gas pressureGDLAMS
the starting index for the flux limiter—there are ngroups components (defined only if
RADIATION
is defined)GDERADS
the starting index for the radiation energy—there are ngroups components (defined only if
RADIATION
is defined)
Conservation Forms
We begin with the fully compressible equations for the conserved state vector,
Here
In the code we also carry around
Some notes:
Regardless of the dimensionality of the problem, we always carry all 3 components of the velocity. This allows for, e.g., 2.5-d rotation (advecting the component of velocity out of the plane in axisymmetric coordinates).
You should always initialize all velocity components to zero, and always construct the kinetic energy with all three velocity components.
There are
NADV
advected quantities, which range fromUFA: UFA+nadv-1
. The advected quantities have no effect at all on the rest of the solution but can be useful as tracer quantities.There are
NSPEC
species (defined in the network directory), which range fromUFS: UFS+nspec-1
.There are
NAUX
auxiliary variables, fromUFX:UFX+naux-1
. The auxiliary variables are passed into the equation of state routines along with the species. An example of an auxiliary variable is the electron fraction, , in core collapse simulations. The number and names of the auxiliary variables are defined in the network.
Source Terms
We now compute explicit source terms for each variable in
Note
To reduce memory usage, we do not include source terms for the
advected quantities, species, and auxiliary variables in the conserved
state vector by default. If your application needs external source terms for
these variables, set USE_SPECIES_SOURCES=TRUE
when compiling so that space
will be allocated for them.
Primitive Forms
Castro uses the primitive form of the fluid equations, defined in terms of
the state
The primitive variable equations for density, velocity, and pressure are:
The advected quantities appear as:
All of the primitive variables are derived from the conservative state vector, as described in Section 6.1. When accessing the primitive variable state vector, the integer variable keys for the different quantities are listed in Table 7.
Internal energy and temperature
We augment the above system with an internal energy equation:
This has two benefits. First, for a general equation of state, carrying around an additional thermodynamic quantity allows us to avoid equation of state calls (in particular, in the Riemann solver, see e.g. [28]). Second, it is sometimes the case that the internal energy calculated as
is unreliable. This has two usual causes: one, for high Mach number flows, the kinetic energy can dominate the total gas energy, making the subtraction numerically unreliable; two, if you use gravity or other source terms, these can indirectly alter the value of the internal energy if obtained from the total energy.
To provide a more reasonable internal energy for defining the
thermodynamic state, we have implemented the dual energy formalism
from ENZO [24], [23], where we switch
between
: If , then we use for the purpose of calculating the pressure in the hydrodynamics update. Otherwise, we use the from the internal energy equation in our EOS call to get the pressure. : At the end of each hydro advance, we examine whether . If so, we reset to be equal to , discarding the results of the internal energy equation. Otherwise, we keep as it is. : Similar to , if , we use for the purposes of our nuclear reactions, otherwise, we use .
Note that our version of the internal energy equation does not require
an artificial viscosity, as used in some other hydrodynamics
codes. The update for
In the code we also carry around
Primitive Variable System
The full primitive variable form (without the advected or auxiliary quantities) is
For example, in 2D:
The eigenvalues are:
The right column eigenvectors are:
The left row eigenvectors, normalized so that
Hydrodynamics Update
There are four major steps in the hydrodynamics update:
Converting to primitive variables
Construction the edge states
Solving the Riemann problem
Doing the conservative update
Each of these steps has a variety of runtime parameters that affect their behavior. Additionally, there are some general runtime parameters for hydrodynamics:
castro.do_hydro
: time-advance the fluid dynamical equations (0 or 1; must be set)castro.add_ext_src
: include additional user-specified source term (0 or 1; default 0)castro.do_sponge
: call the sponge routine after the solution update (0 or 1; default: 0)See Sponge for more details on the sponge.
Several floors are imposed on the thermodynamic quantities to prevet unphysical behavior:
castro.small_dens
: (Real; default: -1.e20)castro.small_temp
: (Real; default: -1.e20)castro.small_pres
: (Real; default: -1.e20)
Compute Primitive Variables
We compute the primitive variables from the conserved variables.
: directly copy these from the conserved state vector : copy these from the conserved state vector, dividing by : use the EOS.First, we use the EOS to ensure
is no smaller than . Then we use the EOS to compute .
We also compute the flattening coefficient,
Define
Define
where
and are tunable parameters. We are essentially setting , and then constraining to lie in the range . Then, if either orwhere
is a tunable parameter, then set .Define
The following runtime parameters affect the behavior here:
castro.use_flattening turns on/off the flattening of parabola near shocks (0 or 1; default 1)
Edge State Prediction
We wish to compute a left and right state of primitive variables at each edge to be used as inputs to the Riemann problem. There are several reconstruction techniques, a piecewise linear method that follows the description in [27], the classic PPM limiters [30], and the new PPM limiters introduced in [29]. The choice of limiters is determined by castro.ppm_type.
For the new PPM limiters, we have further modified the method of [29] to eliminate sensitivity due to roundoff error (modifications via personal communication with Colella).
We also use characteristic tracing with corner coupling in 3D, as described in Miller & Colella (2002) [54]. We give full details of the new PPM algorithm, as it has not appeared before in the literature, and summarize the developments from Miller & Colella.
The PPM algorithm is used to compute time-centered edge states by
extrapolating the base-time data in space and time. The edge states
are dual-valued, i.e., at each face, there is a left state and a right
state estimate. The spatial extrapolation is one-dimensional, i.e.,
transverse derivatives are ignored. We also use a flattening
procedure to further limit the edge state values. The Miller &
Colella algorithm, which we describe later, incorporates the
transverse terms, and also describes the modifications required for
equations with additional characteristics besides the fluid velocity.
There are four steps to compute these dual-valued edge states (here,
we use
Step 1: Compute
and , which are spatial interpolations of to the hi and lo side of the face with special limiters, respectively. Begin by interpolating to edges using a 4th-order interpolation in space:Then, if
, we limit a nonlinear combination of approximations to the second derivative. The steps are as follows:Define:
Define
where
as used in Colella and Sekora 2009. The limited value of is
Now we implement an updated implementation of the Colella & Sekora algorithm which eliminates sensitivity to roundoff. First we need to detect whether a particular cell corresponds to an “extremum”. There are two tests.
For the first test, define
If
, then we are at an extremum.We only apply the second test if either
. If so, we define:If
, set . Otherwise, set . Finally, we are at an extreumum if .
Thus concludes the extremum tests. The remaining limiters depend on whether we are at an extremum.
If we are at an extremum, we modify
. First, we defineThen, define
Then,
If we are not at an extremum and
, then defineIf
, then we perform the following test. If , thenotherwise,
Finally,
.Step 2: Construct a quadratic profile using
, and .(3)- Step 3: Integrate quadratic profiles. We are essentially computing the average value swept out by the quadratic profile across the face assuming the profile is moving at a speed
.Define the following integrals, where : Step 4: Obtain 1D edge states by performing a 1D extrapolation to get left and right edge states. Note that we include an explicit source term contribution.
Here,
is the right column eigenvector of and is the left row eigenvector lf . The flattening coefficient is .
In order to add the transverse terms in an spatial operator unsplit framework, the details follow exactly as given in Section 4.2.1 in Miller & Colella, except for the details of the Riemann solver, which are given below.
For the reconstruction of the interface states, the following apply:
castro.ppm_type
: use piecewise linear vs PPM algorithm (0 or 1; default: 1). A value of 1 is the standard piecewise parabolic reconstruction.castro.ppm_temp_fix
does various attempts to use the temperature in the reconstruction of the interface states. See Temperature Fixes for an explanation of the allowed options.
The interface states are corrected with information from the transverse directions to make this a second-order update. These transverse directions involve separate Riemann solves. Sometimes, the update to the interface state from the transverse directions can make the state ill-posed. There are several parameters that help fix this:
castro.transverse_use_eos
: If this is 1, then we call the equation of state on the interface, using , , and , to get the interface pressure. This should result in a thermodynamically consistent interface state.castro.transverse_reset_density
: If the transverse corrections result in a negative density on the interface, then we reset all of the interface states to their values before the transverse corrections.castro.transverse_reset_rhoe
: The transverse updates operate on the conserved state. Usually, we construct the interface in the transverse update from total energy and the kinetic energy, however, if the interface is negative, andtransverse_reset_rhoe
= 1, then we explicitly discretize an equation for the evolution of , including its transverse update.
Riemann Problem
Castro has three main options for the Riemann solver—the Colella & Glaz solver [28] (the same solver used by Flash), a simpler solver described in an unpublished manuscript by Colella, Glaz, & Ferguson, and an HLLC solver. The first two are both two-shock approximate solvers, but differ in how they approximate the thermodynamics in the “star” region.
Note
These Riemann solvers are for Newtonian hydrodynamics, however, we enforce
that the interface velocity cannot exceed the speed of light in both the
Colella & Glaz and Colella, Glaz, & Ferguson solvers. This excessive speed
usually is a sign of low density regions and density resets or the flux limiter
kicking in. This behavior can be changed with the castro.riemann_speed_limit
parameter.
Inputs from the edge state prediction are
Here are the steps. First, define
Define star states:
If
and define
Then,
If
and constrain
To get the final “Godunov” state, for the transverse velocity, we
upwind based on
Then, define
Finally, if
If instead the Colella & Glaz solver is used, then we define
on each side of the interface and follow the rest of the algorithm as described in the original paper.
For the construction of the fluxes in the Riemann solver, the following parameters apply:
castro.riemann_solver
: this can be one of the following values:0: the Colella, Glaz, & Ferguson solver.
1: the Colella & Glaz solver
2: the HLLC solver.
Note
HLLC should only be used with Cartesian geometries because it relies on the pressure term being part of the flux in the momentum equation.
The default is to use the solver based on an unpublished Colella, Glaz, & Ferguson manuscript (it also appears in [8]), as described in the original Castro paper [19].
The Colella & Glaz solver is iterative, and two runtime parameters are used to control its behavior:
castro.cg_maxiter
: number of iterations for CG algorithm (Integer; default: 12)castro.cg_tol
: tolerance for CG solver when solving for the “star” state (Real; default: 1.0e-5)castro.cg_blend
: this controls what happens if the root finding in the CG solver fails. There is a nonlinear equation to find the pressure in the star region from the jump conditions for a shock (this is the two-shock approximation—the left and right states are linked to the star region each by a shock). The default root finding algorithm is a secant method, but this can sometimes fail.The options here are:
0 : do nothing. The pressure from each iteration is printed and the code aborts with a failure
1 : revert to the original guess for p-star and carry through on the remainder of the Riemann solve. This is almost like dropping down to the CGF solver. The p-star used is very approximate.
2 : switch to bisection and do an additional cg_maxiter iterations to find the root. Sometimes this can work where the secant method fails.
castro.hybrid_riemann
: switch to an HLL Riemann solver when we are in a zone with a shock (0 or 1; default 0)This eliminates an odd-even decoupling issue (see the oddeven problem). Note, this cannot be used with the HLLC solver.
Compute Fluxes and Update
Compute the fluxes as a function of the primitive variables, and then advance the solution:
Again, note that since the source term is not time centered, this is not a second-order method. After the advective update, we correct the solution, effectively time-centering the source term.
Temperature Fixes
There are a number of experimental options for improving the behavior
of the temperature in the reconstruction and interface state
prediction. The options are controlled by castro.ppm_temp_fix
,
which takes values:
0: the default method—temperature is not considered, and we do reconstruction and characteristic tracing on
. 1: do parabolic reconstruction on
, giving . We then derive the pressure and internal energy (gas portion) via the equation of state as: The remainder of the hydrodynamics algorithm then proceeds unchanged.
2: on entering the Riemann solver, we recompute the thermodynamics on the interfaces to ensure that they are all consistent. This is done by taking the interface values of
, , , and computing the corresponding pressure, from this.
Resets
Flux Limiting
Multi-dimensional hydrodynamic simulations often have numerical
artifacts that result from the sharp density gradients. A somewhat
common issue, especially at low resolution, is negative densities that
occur as a result of a hydro update. Castro contains a prescription
for dealing with negative densities, that resets the negative density
to be similar to nearby zones. Various choices exist for how to do
this, such as resetting it to the original zone density before the
update or resetting it to some linear combination of the density of
nearby zones. The reset is problematic because the strategy is not
unique and no choice is clearly better than the rest in all
cases. Additionally, it is not specified at all how to reset momenta
in such a case. Consequently, we desired to improve the situation by
limiting fluxes such that negative densities could not occur, so that
such a reset would in practice always be avoided. Our solution
implements the positivity-preserving method of [45]. This
behavior is controlled by
castro.limit_fluxes_on_small_dens
.
A hydrodynamical update to a zone can be broken down into an update
over every face of the zone where a flux crosses the face over the
timestep. The central insight of the positivity-preserving method is
that if the update over every face is positivity-preserving, then the
total update must be positivity-preserving as well. To guarantee
positivity preservation at the zone edge
where
where castro.small_dens
, and solving
for the value of
Hybrid Momentum
Castro implements the hybrid momentum scheme of [25].
In particular, this switches from using the Cartesian momenta,
The rotating_torus
problem gives a good test for this. This problem
originated with [60]. The
problem is initialized as a torus with constant specific angular
momentum, as shown below:

Fig. 2 Initial density (log scale) for the rotating_torus
problem with
For the standard hydrodynamics algorithm, the torus gets disrupted and spreads out into a disk:

Fig. 3 Density (log scale) for the rotating_torus
problem after 200
timesteps, using
The hybrid momentum algorithm is enabled by setting:
USE_HYBRID_MOMENTUM = TRUE
in your GNUmakefile
. With this enabled, we see that the torus remains intact:

Fig. 4 Density (log scale) for the rotating_torus
problem after 200
timesteps with the hybrid momentum algorithm, using