Limiters
The limiters supertype is
ClimaCore.Limiters.AbstractLimiter — Type
AbstractLimiterSupertype for all limiters.
Subtypes:
QuasiMonotoneLimiter: horizontal quasi-monotone flux limiter for spectral element advection.PositivityLimiter: Zhang-Shu mean-preserving positivity limiter for conserved DG states.VerticalMassBorrowingLimiter: vertical mass-borrowing limiter that removes negative tracer mass.
Subtypes implement apply_limiter!.
QuasiMonotoneLimiter acts on the horizontal spectral-element structure of a field; VerticalMassBorrowingLimiter acts along each column; PositivityLimiter acts on the conserved state of a DG discretization, element by element. All are applied to the state after a step or stage (Limit tracers).
Interfaces
ClimaCore.Limiters.QuasiMonotoneLimiter — Type
QuasiMonotoneLimiterQuasi-monotone limiter for tracer densities on spectral element spaces, after the OP1 limiter of [14], eqs. (37)-(40). Quasi-monotone means monotone with respect to the spectral element nodal values.
For each element, the limiter finds the nodal field closest, in the mass-weighted l2 norm, to the input field that satisfies min/max bounds on the concentration q = ρq / ρ. As in HOMME, it clips the nodal values that violate the bounds to the nearest bound, which changes the tracer mass of the element, and then redistributes the mass change over the nodes that are not at a bound so that the l2 error is smallest. Redistribution can violate the bounds again, so the two steps are iterated until abs(Δtracer_mass) <= rtol * tracer_mass or Nq^2 iterations have been done. The optimization is local to each element; the neighbors enter only through the bounds.
Fields
q_bounds: min and max ofqin each element.q_bounds_nbr: min and max ofqover each element and its neighbors.ghost_buffer: buffer for exchangingq_boundswith neighboring processes.rtol: relative tolerance for the tracer mass change per element [-].convergence_stats:LimiterConvergenceStats(orNoConvergenceStats) accumulated byapply_limiter!.
Constructor
QuasiMonotoneLimiter(
ρq::Field;
rtol = eps(eltype(parent(ρq))),
convergence_stats = LimiterConvergenceStats{eltype(parent(ρq))}(),
)Create a limiter for the tracer density field ρq, where q is the tracer concentration per unit mass; ρq can be a scalar-valued or a struct-valued Field. convergence_stats records the number of apply_limiter! calls in which some element failed to converge, the largest relative mass error, and the smallest tracer mass; print_convergence_stats(limiter) prints them.
Examples
Call compute_bounds! on the fields at the start of the step, then apply_limiter! on the updated fields:
limiter = QuasiMonotoneLimiter(ρq)
compute_bounds!(limiter, ρq, ρ)
# ... advance ρq and ρ ...
apply_limiter!(ρq, ρ, limiter)ClimaCore.Limiters.VerticalMassBorrowingLimiter — Type
VerticalMassBorrowingLimiter(q_min)Vertical-only mass-borrowing limiter.
The limiter borrows tracer mass from adjacent lower layers to raise each layer's tracer value to at least its minimum. It conserves the total tracer mass in the column.
q_min is a tuple with one minimum tracer value per component of the limited field.
At level k, the limiter first borrows mass from layer k+1 (the lower level). If the mass in layer k+1 is not sufficient, it borrows from layer k+2, and so on down to the bottom layer. If the tracer mass in the bottom layer goes below the minimum, the limiter repeats the process from the bottom to the top. This makes the limiter work for any shape of mass profile.
Examples
ρ = fill(1.0, space)
q = fill((a = 0.1, b = 0.1), space)
limiter = VerticalMassBorrowingLimiter((0.0, 0.0))
Limiters.apply_limiter!(q, ρ, limiter)Adapted from the E3SM mass borrower; see [15].
ClimaCore.Limiters.PositivityLimiter — Type
PositivityLimiter{FT, G, F} <: AbstractLimiterMean-preserving positivity limiter of [16] for the conserved state of a DG discretization, applied with apply_limiter!.
In each element, with Ū the WJ-weighted mean:
Each field with a linear floor is scaled toward its own mean,
x_j ← x̄ + θ₁ (x_j − x̄), by the largestθ₁ ∈ [0, 1]that keeps its nodal minimum at the floor (per component forNamedTuple-valued fields). Fields without a floor are untouched.If
gis given, the whole conserved vector is then scaled toward the mean,U_j ← Ū + θ₂ (U_j − Ū), by the largestθ₂ ∈ [0, 1]withg(U_j) ≥ g_minat every node (bisection,maxitersteps per node).
Both steps are convex combinations with the element mean, so every element mean is preserved exactly.
FT is the floating-point type of the limited states, G the type of g, and F the type of floors.
Fields
g: Concave constraintg(U, aux), ornothingto skip step 2.Uis one node's state as aNamedTuplewith the names of the limited states, andauxthe node's value of the auxiliary field.floors:NamedTupleof linear floors keyed by state names, in the units of each state. A floor applies to every component of aNamedTuple-valued field.g_min: Lower bound of the nonlinear constraintg(U, aux) ≥ g_min, in the units ofg.maxiter: Bisection steps per node forθ₂[-];θ₂is accurate to2^-maxiterand always on the admissible side.
Constructor
PositivityLimiter(FT; floors = (;), g = nothing, g_min = 0, maxiter = 10)Each keyword sets the field of the same name; floors and g_min are converted to FT.
Examples
# Shallow water: depth floor only.
limiter = PositivityLimiter(Float64; floors = (; h = 0))
# Compressible Euler: density and tracer floors, and a pressure floor, with
# the geopotential Φ as the auxiliary field.
pressure(U, Φ) = 0.4 * (U.ρe - (U.ρu1^2 + U.ρu2^2 + U.ρu3^2) / 2U.ρ - U.ρ * Φ)
limiter = PositivityLimiter(
Float64;
floors = (; ρ = 1e-6, ρq = 0),
g = pressure,
g_min = 1e-3,
)Notes
- The limiter only redistributes within an element, so it cannot fix a bad mean. Such an element is set to its mean (
θ = 0) and stays below the floor. With an SSP time integrator, the mean stays admissible under the CFL condition of [16]. - The bounds hold at the nodes only; the interpolating polynomial may still dip below a floor between nodes.
gmust be concave inU(as pressure and depth are), so that the admissibleθs at a node form an interval[0, θ*]and bisection findsθ*.- States may hold
ForwardDiff.Dualvalues. Derivatives are exact except where a floor switches on or off; the bisectedθ₂is differentiated implicitly throughg(U(θ₂)) = g_min.
See also apply_limiter! and the Zhang–Shu positivity limiter (DG) how-to.
ClimaCore.Limiters.compute_bounds! — Function
compute_bounds!(limiter::QuasiMonotoneLimiter, ρq::Field, ρ::Field)Compute the bounds on the tracer concentration q = ρq / ρ that apply_limiter! enforces, from the tracer density ρq and the density ρ, and store them in limiter.q_bounds_nbr.
The steps are:
compute_element_bounds!computes the min and max ofqin each element.- If distributed, start the ghost exchange of the element bounds.
compute_neighbor_bounds_local!widens the bounds with the local neighbors.- If distributed, complete the ghost exchange and widen the bounds with the ghost neighbors in
compute_neighbor_bounds_ghost!.
ClimaCore.Limiters.apply_limiter! — Function
apply_limiter!(ρq, ρ, limiter::QuasiMonotoneLimiter; warn = true)Limit the tracer density ρq in place so that the concentration q = ρq / ρ at each node lies within the bounds computed by compute_bounds!, while preserving the tracer mass of each element up to the relative tolerance limiter.rtol.
Each element is processed by apply_limit_slab!, with the density ρ times the quadrature weights as the weights of the least-squares redistribution. When some element fails to converge, limiter.convergence_stats is updated. On CPU devices with warn = true, the accumulated convergence statistics are printed with @warn after each call.
Return ρq on CPU devices and nothing on CUDA devices.
apply_limiter!(states::NamedTuple, aux, limiter::PositivityLimiter)Apply the PositivityLimiter limiter to states, element by element. Mutates the fields of states; returns nothing.
Arguments
states:NamedTupleof the conservedFields on one space, e.g.(; ρ, ρe, ρu1, ρu2, ρu3), with the limiter's eltype. Its names must include every key oflimiter.floors, and are the nameslimiter.greads. Field elements must be scalars or flatNamedTuples of scalars (several tracers in one field); pass vector quantities such as momentum as separate scalar component fields.aux:Fieldof non-conserved data passed unscaled tolimiter.g, ornothing; e.g. the geopotential [m²/s²].limiter: ThePositivityLimiter.
Examples
# h, hu1, hu2: depth and momentum `Field`s on one spectral-element space.
limiter = PositivityLimiter(Float64; floors = (; h = 0))
apply_limiter!((; h, hu1, hu2), nothing, limiter)See Zhang–Shu positivity limiter (DG) for a runnable shallow-water example and the compressible Euler setup.
apply_limiter!(q::Fields.Field, ρ::Fields.Field, lim::VerticalMassBorrowingLimiter)Apply the vertical mass-borrowing limiter lim to the tracer field q in place, given the density field ρ.
Each component of q is limited column by column with the corresponding entry of lim.q_min, using the cell volume from the local geometry of ρ. Returns nothing.
ClimaCore.Limiters.print_convergence_stats — Function
print_convergence_stats(limiter::QuasiMonotoneLimiter)
print_convergence_stats(io::IO, stats)Print the convergence statistics accumulated by the Limiters.QuasiMonotoneLimiter limiter (to stdout), or the statistics object stats (to io): the number of elements in which the limiter iteration did not converge, the maximum relative error, and the minimum tracer mass. Nothing is printed if the limiter was constructed with convergence_stats = NoConvergenceStats().