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. The NUM_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

    ρu

    UMY

    ρv

    UMZ

    ρw

    UEDEN

    ρE

    UEINT

    ρe

    this is computed from the other quantities using ρe=ρEρ|u|2/2

    UTEMP

    T

    this is computed from the other quantities using the EOS

    UFA

    ρA1

    the first advected quantity

    UFS

    ρX1

    the first species

    UFX

    ρY1

    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 has NQ components.

    A related quantity is NQSRC which is the number of primitive variable source terms. NQSRCNQ.

    Note

    if RADIATION is defined, then only the gas/hydro terms are present in NQSRC.

    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

    u

    QV

    v

    QW

    w

    QPRES

    p

    QREINT

    ρe

    QTEMP

    T

    QFA

    A1

    the first advected quantity

    QFS

    X1

    the first species

    QFX

    Y1

    the first auxiliary variable

    QPTOT

    ptot

    the total pressure, gas + radiation

    QREITOT

    etot

    the total specific internal energy, gas + radiation

    QRAD

    Er

    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

    γ1

    the first adiabatic exponent, as returned from the EOS

    QC

    cs

    the sound speed, as returned from the EOS

    QGAMCG

    Γ1tot

    includes radiation components (defined only if RADIATION is defined)

    QCG

    cstot

    total sound speed including radiation (defined only if RADIATION is defined)

    QLAMS

    λf

    the ngroups flux limiters (defined only if RADIATION 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, and q3 for the x, y, and z interfaces respectively. There are NGDNV 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

    u

    GDV

    v

    GDW

    w

    GDPRES

    p

    regardless of whether RADIATION is defined, this is always just the gas pressure

    GDLAMS

    λf

    the starting index for the flux limiter—there are ngroups components (defined only if RADIATION is defined)

    GDERADS

    Er

    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, U=(ρ,ρu,ρE,ρAk,ρXk,ρYk):

ρt=(ρu)+Sext,ρ,(ρu)t=(ρuu)p+ρg+Sext,ρu,(ρE)t=(ρuE+pu)+ρugkρqkω˙k+kthT+Sext,ρE,(ρAk)t=(ρuAk)+Sext,ρAk,(ρXk)t=(ρuXk)+ρω˙k+Sext,ρXk,(ρYk)t=(ρuYk)+Sext,ρYk.

Here ρ,u,T,p, and kth are the density, velocity, temperature, pressure, and thermal conductivity, respectively, and E=e+uu/2 is the total energy with e representing the internal energy. In addition, Xk is the abundance of the kth isotope, with associated production rate, ω˙k, and energy release, qk. Here g is the gravitational vector, and Sext,ρ,Sextρu, etc., are user-specified source terms. Ak is an advected quantity, i.e., a tracer. We also carry around auxiliary variables, Yk, which have a user-defined evolution equation, but by default are treated as advected quantities. These are meant to be defined in the network.

In the code we also carry around T and ρe in the conservative state vector even though they are derived from the other conserved quantities. The ordering of the elements within U is defined by integer variables into the array—see Table 6.

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 from UFA: 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 from UFS: UFS+nspec-1.

  • There are NAUX auxiliary variables, from UFX: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, Ye, 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 Q and U. The primitive variable source terms will be used to construct time-centered fluxes. The conserved variable source will be used to advance the solution. We neglect reaction source terms since they are accounted for in Steps 1 and 6. The source terms are:

SQn=(SρSuSpSρeSAkSXkSYk)n=(Sext,ρg+1ρSext,ρu1ρpeSext,ρE+pρSextρkthT+Sext,ρE1ρSext,ρAk1ρSext,ρXk1ρSext,ρYk)n,
SUn=(SρuSρESρAkSρXkSρYk)n=(ρg+Sext,ρuρug+kthT+Sext,ρESext,ρAkSext,ρXkSext,ρYk)n.

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 Q=(ρ,u,p,ρe,Ak,Xk,Yk), to construct the interface states that are input to the Riemann problem.

The primitive variable equations for density, velocity, and pressure are:

ρt=uρρu+Sext,ρut=uu1ρp+g+1ρ(Sext,ρuuSext,ρ)pt=upρc2u+(pρ)e,XSext,ρ+ 1ρk(pXk)ρ,e,Xj,jk(ρω˙k+Sext,ρXkXkSext,ρ)+ 1ρ(pe)ρ,X[eSext,ρkρqkω˙k+kthT+ Sext,ρEu(Sext,ρuu2Sext,ρ)]

The advected quantities appear as:

Akt=uAk+1ρ(Sext,ρAkAkSext,ρ),Xkt=uXk+ω˙k+1ρ(Sext,ρXkXkSext,ρ),Ykt=uYk+1ρ(Sext,ρYkYkSext,ρ).

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:

(ρe)t=u(ρe)(ρe+p)ukρqkω˙k+kthT+Sext,ρE u(Sext,ρu12Sext,ρu),

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

eTE12v2

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 (ρe) and (ρeT) depending on the local state of the fluid. To do so, we define parameters η1, η2, and η3, corresponding to the code parameters castro.dual_energy_eta1, castro.dual_energy_eta2, and castro.dual_energy_eta3. We then consider the ratio eT/E, the ratio of the internal energy (derived from the total energy) to the total energy. These parameters are used as follows:

  • η1: If eT>η1E, then we use eT for the purpose of calculating the pressure in the hydrodynamics update. Otherwise, we use the e from the internal energy equation in our EOS call to get the pressure.

  • η2: At the end of each hydro advance, we examine whether eT>η2E. If so, we reset e to be equal to eT, discarding the results of the internal energy equation. Otherwise, we keep e as it is.

  • η3: Similar to η1, if eT>η3E, we use eT for the purposes of our nuclear reactions, otherwise, we use e.

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 (ρe) uses information from the Riemann solve to calculate the fluxes, which contains the information intrinsic to the shock-capturing part of the scheme.

In the code we also carry around T in the primitive state vector.

Primitive Variable System

The full primitive variable form (without the advected or auxiliary quantities) is

Qt+dAdQxd=SQ.

For example, in 2D:

(ρuvpρeXk)t+(uρ00000u01ρ0000u0000ρc20u000ρe+p00u000000u)(ρuvpρeXk)x+(v0ρ0000v000000v1ρ0000ρc2v0000ρe+p0v000000v)(ρuvpρeXk)y=SQ

The eigenvalues are:

Λ(Ax)={uc,u,u,u,u,u+c},Λ(Ay)={vc,v,v,v,v,v+c}.

The right column eigenvectors are:

R(Ax)=(110001cρ0000cρ001000c20000c2h0010h000010),R(Ay)=(110001001000cρ0000cρc20000c2h0010h000010).

The left row eigenvectors, normalized so that RdLd=I are:

Lx=(0ρ2c012c2001001c200001000000hc2100000010ρ2c012c200),Ly=(00ρ2c12c2001001c200010000000hc21000000100ρ2c12c200).

Hydrodynamics Update

There are four major steps in the hydrodynamics update:

  1. Converting to primitive variables

  2. Construction the edge states

  3. Solving the Riemann problem

  4. 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.

  • ρ,ρe: directly copy these from the conserved state vector

  • u,Ak,Xk,Yk: copy these from the conserved state vector, dividing by ρ

  • p,T: use the EOS.

    First, we use the EOS to ensure e is no smaller than e(ρ,Tsmall,Xk). Then we use the EOS to compute p,T=p,T(ρ,e,Xk).

We also compute the flattening coefficient, χ[0,1], used in the edge state prediction to further limit slopes near strong shocks. We use the same flattening procedure described in the the the original PPM paper [30] and the Flash paper [38]. A flattening coefficient of 1 indicates that no additional limiting takes place; a flattening coefficient of 0 means we effectively drop order to a first-order Godunov scheme (this convention is opposite of that used in the Flash paper). For each cell, we compute the flattening coefficient for each spatial direction, and choose the minimum value over all directions. As an example, to compute the flattening for the x-direction, here are the steps:

  1. Define ζ

    ζi=pi+1pi1max(psmall,|pi+2pi2|).
  2. Define χ~

    χ~i=min{1,max[0,a(ζib)]},

    where a=10 and b=0.75 are tunable parameters. We are essentially setting χ~i=a(ζib), and then constraining χ~i to lie in the range [0,1]. Then, if either ui+1ui1<0 or

    pi+1pi1min(pi+1,pi1)c,

    where c=1/3 is a tunable parameter, then set χ~i=0.

  3. Define χ

    χi={1max(χ~i,χ~i1)pi+1pi1>01max(χ~i,χ~i+1)otherwise.

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 s to denote an arbitrary scalar from Q, and we write the equations in 1D, for simplicity):

  • Step 1: Compute si,+ and si,, which are spatial interpolations of s to the hi and lo side of the face with special limiters, respectively. Begin by interpolating s to edges using a 4th-order interpolation in space:

    si+12=712(si+1+si)112(si+2+si1).

    Then, if (si+12si)(si+1si+12)<0, we limit si+12 a nonlinear combination of approximations to the second derivative. The steps are as follows:

    1. Define:

      (D2s)i+12=3(si2si+12+si+1)(D2s)i+12,L=si12si+si+1(D2s)i+12,R=si2si+1+si+2
    2. Define

      s=sign[(D2s)i+12],
      (D2s)i+12,lim=smax{min[Cs|(D2s)i+12,L|,Cs|(D2s)i+12,R|,s|(D2s)i+12|],0},

      where C=1.25 as used in Colella and Sekora 2009. The limited value of si+12 is

      si+12=12(si+si+1)16(D2s)i+12,lim.

    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

      αi,±=si±12si.

      If αi,+αi,0, then we are at an extremum.

    • We only apply the second test if either |αi,±|>2|αi,|. If so, we define:

      (Ds)i,face,=si1/2si3/2(Ds)i,face,+=si+3/2si1/2
      (Ds)i,face,min=min[|(Ds)i,face,|,|(Ds)i,face,+|].
      (Ds)i,cc,=sisi1(Ds)i,cc,+=si+1si
      (Ds)i,cc,min=min[|(Ds)i,cc,|,|(Ds)i,cc,+|].

      If (Ds)i,face,min(Ds)i,cc,min, set (Ds)i,±=(Ds)i,face,±. Otherwise, set (Ds)i,±=(Ds)i,cc,±. Finally, we are at an extreumum if (Ds)i,+(Ds)i,0.

    Thus concludes the extremum tests. The remaining limiters depend on whether we are at an extremum.

    • If we are at an extremum, we modify αi,±. First, we define

      (D2s)i=6(αi,++αi,)(D2s)i,L=si22si1+si(D2s)i,R=si2si+1+si+2(D2s)i,C=si12si+si+1

      Then, define

      s=sign[(D2s)i],
      (D2s)i,lim=max{min[s(D2s)i,Cs|(D2s)i,L|,Cs|(D2s)i,R|,Cs|(D2s)i,C|],0}.

      Then,

      αi,±=αi,±(D2s)i,limmax[(D2s)i,1×1010]
    • If we are not at an extremum and |αi,±|>2|αi,|, then define

      s=sign(αi,)
      δIext=αi,±24(αj,++αj,),
      δs=si1si,

      If sδIextsδs, then we perform the following test. If sδsαi,1×1010, then

      αi,±=2δs2s[(δs)2δsαi,]12

      otherwise,

      αi,±=2αi,

    Finally, si,±=si+αi,±.

  • Step 2: Construct a quadratic profile using si,,si, and si,+.

    (3)siI(x)=si,+ξ[si,+si,+s6,i(1ξ)],
    s6=6si3(si,+si,+),
    ξ=xihh, 0ξ1.
  • 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 λk.
    Define the following integrals, where σk=|λk|Δt/h:
    I+(k)(si)=1σkh(i+12)hσkh(i+12)hsiI(x)dxI(k)(si)=1σkh(i12)h(i12)h+σkhsiI(x)dx

    Plugging in (3) gives:

    I+(k)(si)=si,+σk2[si,+si,(123σk)s6,i],I(k)(si)=si,+σk2[si,+si,+(123σk)s6,i].
  • 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.

    sL,i+12=siχik:λk0lk[siI+(k)(si)]rk+Δt2Sin,sR,i12=siχik:λk<0lk[siI(k)(si)]rk+Δt2Sin.

    Here, rk is the kth right column eigenvector of R(Ad) and lk is the kth left row eigenvector lf L(Ad). The flattening coefficient is χi.

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 ρ, e, and Xk, 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 (ρe) in the transverse update from total energy and the kinetic energy, however, if the interface (rhoe) is negative, and transverse_reset_rhoe = 1, then we explicitly discretize an equation for the evolution of (ρe), 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 ρL/R,uL/R,vL/R,pL/R, and (ρe)L/R (v represents all of the transverse velocity components). We also compute Γdlogp/dlogρ|s at cell centers and copy these to edges directly to get the left and right states, ΓL/R. We also define cavg as a face-centered value that is the average of the neighboring cell-centered values of c. We have also computed ρsmall,psmall, and csmall using cell-centered data.

Here are the steps. First, define (ρc)small=ρsmallcsmall. Then, define:

(ρc)L/R=max[(ρc)small,|ΓL/R,pL/R,ρL/R|].

Define star states:

p=max[psmall,[(ρc)LpR+(ρc)RpL]+(ρc)L(ρc)R(uLuR)(ρc)L+(ρc)R],
u=[(ρc)LuL+(ρc)RuR]+(pLpR)(ρc)L+(ρc)R.

If u0 then define ρ0,u0,p0,(ρe)0 and Γ0 to be the left state. Otherwise, define them to be the right state. Then, set

ρ0=max(ρsmall,ρ0),

and define

c0=max(csmall,Γ0p0ρ0),
ρ=ρ0+pp0c02,
(ρe)=(ρe)0+(pp0)(ρe)0+p0ρ0c02,
c=max(csmall,|Γ0pρ|)

Then,

cout=c0sign(u)u0,cin=csign(u)u,cshock=cin+cout2.

If pp00, then cin=cout=cshock. Then, if cout=cin, we define ctemp=ϵcavg. Otherwise, ctemp=coutcin. We define the fraction

f=12[1+cout+cinctemp],

and constrain f to lie in the range f[0,1].

To get the final “Godunov” state, for the transverse velocity, we upwind based on u.

vgdnv={vL,u0vR,otherwise.

Then, define

ρgdnv=fρ+(1f)ρ0,ugdnv=fu+(1f)u0,pgdnv=fp+(1f)p0,(ρe)gdnv=f(ρe)+(1f)(ρe)0.

Finally, if cout<0, set ρgdnv=ρ0,ugdnv=u0,pgdnv=p0, and (ρe)gdnv=(ρe)0. If cin0, set ρgdnv=ρ,ugdnv=u,pgdnv=p, and (ρe)gdnv=(ρe).

If instead the Colella & Glaz solver is used, then we define

γpρe+1

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:

Un+1=UnΔtFn+12+ΔtSn.

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 ρ,u,p,(ρe).

  • 1: do parabolic reconstruction on T, giving I+(k)(Ti). We then derive the pressure and internal energy (gas portion) via the equation of state as:

    I+(k)(pi)=p(I+(k)(ρi),I+(k)(Ti))I+(k)((ρe)i)=(ρe)(I+(k)(ρi),I+(k)(Ti))

    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 ρ, e, Xk, and computing the corresponding pressure, p 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 i+1/2, the flux Fi+1/2n+1/2 at that face is modified to become:

Fi+1/2n+1/2θi+1/2Fi+1/2n+1/2+(1θi+1/2)Fi+1/2LF,

where 0θi+1/21 is a scalar, and Fi+1/2LF is the Lax-Friedrichs flux,

Fi+1/2LF=12[Fin+Fi+1n+CFLΔxΔt1α(UinUi+1n)],

where 0<CFL<1 is the CFL safety factor (the method is guaranteed to preserve positivity as long as CFL<1/2), and α is a scalar that ensures multi-dimensional correctness (α=1 in 1D, 1/2 in 2D, 1/3 in 3D). Fi is the flux of material evaluated at the zone center i using the cell-centered quantities U. The scalar θi+1/2 is chosen at every interface by calculating the update that would be obtained from , setting the density component equal to a value just larger than the density floor, castro.small_dens, and solving for the value of θ at the interface that makes the equality hold. In regions where the density is not at risk of going negative, θ1 and the original hydrodynamic update is recovered. Further discussion, including a proof of the method, a description of multi-dimensional effects, and test verification problems, can be found in [45].

Hybrid Momentum

Castro implements the hybrid momentum scheme of [25]. In particular, this switches from using the Cartesian momenta, (ρu), (ρv), and (ρw), to a cylindrical momentum set, (ρvR), (ρRvϕ), and (ρvz). This latter component is identical to the Cartesian value. We translate between these sets of momentum throughout the code, ultimately doing the conservative update in terms of the cylindrical momentum. Additional source terms appear in this formulation, which are written out in [25].

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:

rotating torus initial density

Fig. 2 Initial density (log scale) for the rotating_torus problem with 643 zones.

For the standard hydrodynamics algorithm, the torus gets disrupted and spreads out into a disk:

rotating torus normal hydro

Fig. 3 Density (log scale) for the rotating_torus problem after 200 timesteps, using 643 zones. Notice that the initial torus has become disrupted into a disk.

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:

rotating torus with hybrid momentum

Fig. 4 Density (log scale) for the rotating_torus problem after 200 timesteps with the hybrid momentum algorithm, using 643 zones. With this angular-momentum preserving scheme we see that the initial torus is largely intact.