Limiters

The limiters supertype is

QuasiMonotoneLimiter acts on the horizontal spectral-element structure of a field; VerticalMassBorrowingLimiter acts along each column. Both are applied to the state after a step or stage (Limit tracers).

Interfaces

ClimaCore.Limiters.QuasiMonotoneLimiterType
QuasiMonotoneLimiter

Quasi-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 of q in each element.
  • q_bounds_nbr: min and max of q over each element and its neighbors.
  • ghost_buffer: buffer for exchanging q_bounds with neighboring processes.
  • rtol: relative tolerance for the tracer mass change per element [-].
  • convergence_stats: LimiterConvergenceStats (or NoConvergenceStats) accumulated by apply_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)
source
ClimaCore.Limiters.VerticalMassBorrowingLimiterType
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].

source
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:

  1. compute_element_bounds! computes the min and max of q in each element.
  2. If distributed, start the ghost exchange of the element bounds.
  3. compute_neighbor_bounds_local! widens the bounds with the local neighbors.
  4. If distributed, complete the ghost exchange and widen the bounds with the ghost neighbors in compute_neighbor_bounds_ghost!.
source
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.

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

source
ClimaCore.Limiters.print_convergence_statsFunction
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().

source