Skip to content

Generalize the Rosenbrock substep kernel over state dimension#730

Closed
haakon-e wants to merge 3 commits into
he/diagnostic-ncfrom
he/1m-unification
Closed

Generalize the Rosenbrock substep kernel over state dimension#730
haakon-e wants to merge 3 commits into
he/diagnostic-ncfrom
he/1m-unification

Conversation

@haakon-e

@haakon-e haakon-e commented Jun 14, 2026

Copy link
Copy Markdown
Member

What this does

This generalizes the linearized-implicit (Rosenbrock-Euler) substep kernel so it operates on a prognostic state of any static length, in preparation for reusing it across microphysics schemes. The kernel previously hard-coded the eight-component 2-moment + P3 state; it now dispatches on a StaticArrays.StaticVector{N}, sizes its identity and dense diagonal matrices from the static length N, and takes the channel mask as an argument instead of computing it internally. Mask construction stays scheme-specific, so the kernel itself carries no scheme knowledge and is pure linear algebra over the state vector.

Why

With this in place a 1-moment Rosenbrock mode, and other schemes, can plug into the same solve by supplying a state FieldVector and a matching channel mask, rather than duplicating the equilibration, channel projection, and update logic. This is a behavior-preserving refactor on its own; the 2-moment + P3 RosenbrockAverage path is unchanged.

Testing

2-moment + P3 results are bit-for-bit identical to before across the regime, substep-count, and precision matrix (raw IEEE comparison). The existing RosenbrockAverage tests pass unchanged in Float64 and Float32, the hot call stays allocation-free, and the entry is JET-clean.

Notes

Draft, low in the stack. It adds no new public behavior by itself; the 1-moment mode that consumes this kernel is in the PR above.

haakon-e added 3 commits June 13, 2026 19:35
The P3 size-distribution quadrature was configured through a single
quadrature_order knob, with build_quadrature silently mapping a magic
set of orders {16, 32, 40, 64} to GaussLegendre and everything else to
ChebyshevGauss. The scheme — with its parameters, notably the order —
is now the explicit choice: pass a scheme instance,

    Microphysics2MParams(FT; with_ice = true,
        quadrature = Quadrature.GaussLegendre(40))

and build_quadrature only materializes it in the working float type
(GaussLegendre nodes are rebuilt in FT; ChebyshevGauss is closed-form
and passes through). The default ChebyshevGauss(100) preserves the
previous default behavior.
Restore the activation-closure dispatch dropped before the P3 merge,
trimmed to the consumer-ready pieces: an AbstractActivationScheme with
NoActivation (the default null source) and DiagnosticNc, which relaxes
the prognostic droplet number toward a prescribed target on a fixed
physical timescale while cloud mass is present (the RCEMIP-I closure).
The fused 2M tendency dispatches through activation_source and folds
the result into dn_lcl_dt; the w/p ambient inputs stay positional for
the supersaturation-driven closures to come.

This mirrors the options pattern of the 1M scheme's PrescribedNd
(which shapes autoconversion from a prescribed droplet number instead
of sourcing a prognostic one). Twomey and Abdul-Razzak-Ghan tiers stay
deferred to a broader activation API.
`_rosenbrock_update` hardcoded the 8-component 2M+P3 state: it built
`one(SMatrix{8,8,FT})` and computed the channel mask internally from the
named `MicroState2MP3` fields. Re-express it over any
`StaticArrays.StaticVector{N}`, sizing the identity and the dense diagonal
matrices (P, S, S⁻¹) from the static length `N`, and accept the channel
mask `z` as an argument so the kernel carries no scheme-specific channel
knowledge. The equilibration, `(I/h − P J P)` solve, and positivity clamp
are unchanged.

The 2M+P3 channel mask (`_rosenbrock_channel_mask`) stays scheme-specific;
the `RosenbrockAverage` entry now computes it and threads it into the
generic kernel. This makes a future fixed-dimension state (e.g. a 4-species
1M vector with its own mask) a drop-in without touching the solver.

2M+P3 behavior is preserved bit-for-bit: the operations are identical, only
generic over dimension. The `RosenbrockAverage` regression suite passes for
Float64 and Float32 with unchanged tolerances, the 0-byte/call allocation
assertion still holds, and JET is report-free on the 2M entry for both
float types.
@haakon-e

Copy link
Copy Markdown
Member Author

Folded into #726 — the kernel generalization is small and part of the RosenbrockAverage infrastructure, so it does not warrant a separate stack entry. The change is preserved as a commit in #726.

@haakon-e haakon-e closed this Jun 14, 2026
@haakon-e
haakon-e deleted the he/1m-unification branch June 14, 2026 03:43
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant