Adaptive Mesh Refinement
Our approach to adaptive refinement in Castro uses a nested hierarchy of logically-rectangular grids with simultaneous refinement of the grids in both space and time. The integration algorithm on the grid hierarchy is a recursive procedure in which coarse grids are advanced in time, fine grids are advanced multiple steps to reach the same time as the coarse grids and the data at different levels are then synchronized.
During the regridding step, increasingly finer grids are recursively embedded in coarse grids until the solution is sufficiently resolved. An error estimation procedure based on user-specified criteria (described in Section 1) evaluates where additional refinement is needed and grid generation procedures dynamically create or remove rectangular fine grid patches as resolution requirements change.
Tagging for Refinement
Castro determines what zones should be tagged for refinement at the
next regridding step by using a set of built-in routines that test on
quantities such as the density and pressure and determining whether
the quantities themselves or their gradients pass a user-specified
threshold. This may then be extended if amr.n_error_buf
We also provide a mechanism for defining a limited set of refinement schemes from the inputs file; for example,
amr.refinement_indicators = dens temp
amr.refine.dens.max_level = 1
amr.refine.dens.value_greater = 2.0
amr.refine.dens.field_name = density
amr.refine.temp.max_level = 2
amr.refine.temp.value_less = 1.0
amr.refine.temp.field_name = Temp
amr.refinement_indicators
is a list of user-defined names for refinement
schemes. For each defined name, amr.refine.<name>
accepts predefined fields
describing when to tag. These are:
max_level
: maximum level to refine tostart_time
: when to start taggingend_time
: when to stop taggingvalue_greater
: value above which we refinevalue_less
: value below which to refinegradient
: absolute value of the difference between adjacent cells above which we refinerelative_gradient
: relative value of the difference between adjacent cells above which we refinefield_name
: name of the string defining the field in the code
If a refinement indicator is added, either
value_greater
, value_less
, gradient
or relative_gradient
must be provided.
Note
Zones adjacent to a physical boundary cannot be tagged for refinement when using the Poisson gravity solver. If your tagging criteria are met in these zones, they will be ignored.
Sometimes, we wish to force the code to derefine based on a criteria,
even if other indicators tagged a zone for refinement. This is
accomplished by created another refinement indicator and setting the
derefine
field to 1
. For example, to derefine any zone where
the density is less than 1.e4
, we could do:
amr.refine.dencutoff.derefine = 1
amr.refine.dencutoff.field_name = density
amr.refine.dencutoff.value_less = 1.e4
where dencutoff
is lised under amr.refinement_indicators
.
Note
Any derefinement indicators should appear after those that tag for
refinement in the amr.refinement_indicators
list, so it is
applied after all the refinement tagging is done.
We provide also the ability for the user to define their own tagging criteria.
This is done through the C++ function problem_tagging
in the file problem_tagging.H
. This function is provided the entire
state (including density, temperature, velocity, etc.) and the array
of tagging status for every zone.
Synchronization Algorithm
Here we present the AMR algorithm for the compressible equations with self-gravity. The gravity component of the algorithm is closely related to (but not identical to) that in Miniati and Colella, JCP, 2007. The content here is largely based on the content in the original Castro paper ([19]). The most significant difference is the addition of a different strategy for when to employ the synchronization; but regardless of whether the original or new strategy is used, the fundamental synchronization step is identical.
Synchronization Methodology
Over a coarse grid time step we collect flux register information for the hyperbolic part of the synchronization:
Analogously, at the end of a coarse grid time step we store the
mismatch in normal gradients of
We want the composite
At the end of a coarse grid time step we can define
The synchronization consists of two parts:
Step 1: Hyperbolic reflux
In the hyperbolic reflux step, we update the conserved variables with the flux synchronization and adjust the gravitational terms to reflect the changes in
and .where
is the volume of the cell and the correction from is supported only on coarse cells adjacent to fine grids.Note: this can be enabled/disabled via
castro.do_reflux
. Generally, it should be enabled (1).Also note that for axisymmetric or 1D spherical coordinates, the reflux of the pressure gradient is different, since it cannot be expressed as a divergence in those geometries. We use a separate flux register in the hydro code to store the pressure term in these cases.
Step 2: Gravitational synchronization
In this step we correct for the mismatch in normal derivative in
at the coarse-fine interface, as well as accounting for the changes in source terms for and due to the change inOn the coarse grid only, we define
We then form the composite residual, which is composed of two contributions. The first is the degree to which the current
does not satisfy the original equation on a composite grid (since we have solved for separately on the coarse and fine levels). The second is the response of to the change in We defineThen we solve
as a two level solve at the coarse and fine levels. We define the update to gravity,
Finally, we need to
add
directly to to and and interpolate to any finer levels and add to the current at those levels.if level
is not the coarsest level in the calculation, then we must transmit the effect of this change in to the coarser levels by updating the flux register between level and the next coarser level, In particular, we set
The gravity synchronization algorithm can be disabled with gravity.no_sync = 1. This should be done with care. Generally, it is okay only if he refluxing happens in regions of low density that don’t affect the gravity substantially.
Source Terms
After a synchronization has been applied, the state on the coarse grid has changed, due to the change in fluxes at the coarse-fine boundary as well as the change in the gravitational field. This poses a problem regarding the source terms, all of which generally rely either on the state itself, or on the global variables affected by the synchronization such as the gravitational field. The new-time sources constructed on the coarse grid all depended on what the state was after the coarse-grid hydrodynamic update, but the synchronization and associated flux correction step retroactively changed that hydrodynamic update. So one can imagine that in a perfect world, we would have calculated the hydrodynamic update first, including the coarse-fine mismatch corrections, and only then computed the source terms at the new time. Indeed, an algorithm that did not subcycle, but marched every zone along at the same timestep, could do so – and some codes, like FLASH, actually do this, where no new-time source terms are computed on any level until the hydrodynamic update has been fully completed and the coarse-fine mismatches corrected. But in Castro we cannot do this; in general we assume the ability to subcycle, so the architecture is set up to always calculate the new-time source terms on a given level immediately after the hydrodynamic update on that level. Hence on the coarse level we calculate the new-time source terms before any fine grid timesteps occur.
One way to fix this, as suggested by Miniati and Colella for the case
of gravity, is to explicitly compute what the difference in the source
term is as a result of any flux corrections across coarse-fine
boundaries. They work out the form of this update for the case of a
cell-centered gravitational force, which has contributions from both
the density advected across the coarse-fine interfaces
(i.e.
Instead we choose a more general approach. On the coarse level, we save
the new-time source terms that were applied until the end of the fine
timesteps. We also save the fine level new-time source terms. Then, when
we do the AMR synchronization after a fine timestep, we first subtract
the previously applied new-time source terms to both the coarse and the
fine level, then do the flux correction and associated gravitational
sync solve, and then re-compute the new-time source terms on both the
coarse and the fine level [1]. In this way, we get almost
the ideal behavior – if we aren’t subcycling, then we get essentially
the same state at the end of the fine timestep as we would in a code
that explicitly had no subcycling. The cost is re-computing the new-time
source terms that second time on each level. For most common source
terms such as gravity, this is not a serious problem – the cost of
re-computing
Note that at present nuclear reactions are not enabled as part of this scheme, and at present are not automatically updated after an AMR synchronization. This will be amended in a future release of Castro.
Synchronization Timing
The goal of the synchronization step is for the coarse and fine grid to
match at the end of a coarse timesteps, after all subcycled fine grid
timesteps have been completed and the two levels have reached the same
simulation time. If subcycling is disabled, so that the coarse and fine
grid take the same timestep, then this is sufficient. However, in the
general subcycling case, the situation is more complicated. Consider the
discussion about source terms in 2.2. If
we have a coarse level and one fine level with a refinement ratio of
two, then for normal subcycling the fine grid takes two timesteps for
every one timestep taken by the coarse level. The strategy advocated by
the original Castro paper (and Miniati and Colella) is to only do the
AMR synchronization at the actual synchronization time between coarse
and fine levels, that is, at the end of the second fine timestep.
Consequently, we actually only update the source terms after that second
fine timestep. Thus note that on the fine grid, only the new-time
source terms in the second fine timestep are updated. But a
moment’s thought should reveal a limitation of this. The first fine grid
timestep was also responsible for modifying the fluxes on the coarse
grid, but the algorithm as presented above didn’t take full account of
this information. So, the gravitational field at the old time in
the second fine timestep is actually missing information that would have
been present if we had updated the coarse grid already. Is there a way
to use this information? For the assumptions we make in Castro, the
answer is actually yes. If we apply the effect of the synchronization
not at the synchronization time but at the end of every fine
timestep, then every fine timestep always has the most up-to-date
information possible about the state of the gravitational field. Now, of
course, in fine timesteps before the last one, we have not actually
reached the synchronization time. But we already know at the end of the
first fine timestep what the synchronization correction will be from
that fine timestep: it will be equal to 1/2 of the coarse contribution
to the flux register and the normal contribution to the flux register
for just that timestep. This is true because in Castro, we assume that
the fluxes provided by the hydrodynamic solver are piecewise-constant
over the timestep, which is all that is needed to be second-order
accurate in time if the fluxes are time centered [3]. So it is fair to say
that halfway through the coarse timestep, half of the coarse flux has
been advected, and we can mathematically split the flux register into
two contributions that have equal weighting from the coarse flux. (In
general, of course, the coarse flux contribution at each fine timestep
is weighted by
In practice, this just means calling the synchronization routine described in 2.1, with the only modification being that the flux register contribution from the coarse grid is appropriately weighted by the fine grid timestep instead of the coarse grid timestep, and we only include the current fine step:
The form of the
Note that one does not need to be using self-gravity for this to be beneficial. Even in pure hydrodynamics this can matter. If a regrid occurs on the fine level, new zones on the boundaries of the current fine level are filled by interpolation from the coarse level. In the old method, that interpolation is not using the most up-to-date data that accounts for the synchronization.
For multiple levels of refinement, the scheme extends naturally. In
the old method, we always call the synchronization at the
synchronization time between any two levels. So for example with two
jumps in refinement by a factor of two, there is a synchronization at
the end of the first two timesteps on level 2 (between level 1 and
level 2), a synchronization after the next two timesteps on level 2
(again between level 1 and level 2), and then a synchronization
between level 0 and level 1. In the new method, we always call the
synchronization at the end of every timestep on the finest level
only, and we simultaneously do the synchronization on every
level. The timestep