Skip to content

Commit 730166f

Browse files
committed
Differentiate raw 2M+P3 tendency in RosenbrockAverage
The Rosenbrock mode previously linearized a limited instantaneous tendency: a supersaturation cap and 1/dt coupled mass-number sink limits were applied before the ForwardDiff Jacobian was taken. Those clips are explicit-Euler stabilization devices, not model physics, and they do not belong inside an implicit update. The published P3 condensation/deposition scheme (Morrison and Milbrandt 2015, J. Atmos. Sci., 72, 287-311, doi:10.1175/JAS-D-14-0065.1, Appendix C) is an analytic time-averaged relaxation with no tendency clip, so the raw relaxation ODE is the model physics. The one-stage Rosenbrock update is L-stable and damps the stiff vapor-exchange subsystem monotonically, so the saturation overshoot and ringing those limiters suppress cannot occur. The clips are also harmful: 1/dt tendency limits inject dt-independent error and break convergence under refinement (Wan et al. 2020, J. Adv. Model. Earth Syst., 12, e2019MS001982), and a hard saturation clip structurally forbids the mixed-phase quasi-steady vapor pressure, which lies between liquid and ice saturation (Korolev and Mazin 2003, J. Atmos. Sci., 60, 2957-2974). The differentiated functor now evaluates the unmodified Microphysics2Moment process rates. The discrete safeguards that remain are h-free conditioning and projection of the linear solve, not model terms: the channel mask, equilibration, and positivity clamp. The cap and sink helpers, used only by the Rosenbrock path, are removed. Convergence-test references are rebuilt against the raw tendency and tolerances re-derived from measured errors. The rime-volume and rime-mass error floors are raised to genesis scale: once ice has melted, a single-precision residual in the collapsing coupled (q_rim, b_rim) pair is physically negligible but unbounded against an eps-scale floor.
1 parent 0398400 commit 730166f

5 files changed

Lines changed: 81 additions & 163 deletions

File tree

docs/bibliography.bib

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -819,4 +819,15 @@ @article{Horn2012
819819
pages={2635--2660},
820820
year={2011},
821821
publisher={G{\"o}ttingen, Germany}
822+
}
823+
824+
@article{Wan2020,
825+
author = {Wan, Hui and Zhang, Shixuan and Rasch, Philip J. and Larson, Vincent E. and Zeng, Xubin and Yan, Huiping},
826+
title = {Quantifying and attributing time step sensitivities in present-day climate simulations conducted with EAMv1},
827+
journal = {Journal of Advances in Modeling Earth Systems},
828+
year = {2020},
829+
volume = {12},
830+
number = {12},
831+
doi = {10.1029/2019MS001982},
832+
pages = {e2019MS001982}
822833
}

docs/src/BulkTendencies.md

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -162,11 +162,14 @@ For the 2-moment + P3 configuration, `RosenbrockAverage` replaces the hand-built
162162
\left(\frac{I}{h} - P J P\right) \Delta x = f(x), \qquad x \leftarrow \max(x + \Delta x,\, 0)
163163
```
164164

165-
where ``f`` is the *limited* instantaneous tendency (the supersaturation cap and the coupled mass–number sink limits are inside the differentiated function, so the implicit update respects them), and ``J = \partial f / \partial x`` is its exact 8×8 `ForwardDiff` Jacobian.
165+
where ``f`` is the **raw instantaneous tendency**the unmodified `Microphysics2Moment` process rates, with no timestep-dependent clipping — and ``J = \partial f / \partial x`` is its exact 8×8 `ForwardDiff` Jacobian.
166166

167-
Two structural safeguards:
167+
The differentiated tendency is the model physics, not a stabilized variant of it. The P3 condensation/deposition scheme is an analytic time-averaged relaxation [MorrisonMilbrandt2015](@cite) with no tendency clip; a supersaturation cap and ``1/h`` sink limits are explicit-Euler stabilization devices, not physical terms. They are unnecessary here because the one-stage Rosenbrock update is L-stable: it damps the stiff vapor-exchange subsystem monotonically, so the saturation overshoot and ringing those limiters suppress cannot occur. They are also harmful: ``1/h`` tendency clips inject ``h``-independent error and break convergence under refinement [Wan2020](@cite), and a hard saturation clip structurally forbids the mixed-phase quasi-steady vapor pressure, which lies between liquid and ice saturation [KorolevMazin2003](@cite).
168168

169-
- **Channel projection** ``P = \mathrm{Diag}(z)``: channels whose condensed mass is below ``10^{-10}`` are projected out of the Jacobian. Their rows of ``I/h - PJP`` reduce to the identity, so the solve returns exactly a forward-Euler update for those channels while healthy channels stay implicit — an IMEX-style splitting at channel granularity. Near-empty channels otherwise produce finite but very large Jacobian entries (through the sink limiter) whose linearized steady state fabricates phantom number concentrations that substep refinement cannot remove.
169+
The discrete safeguards are therefore conditioning and projection devices, all ``h``-free and applied to the linear solve rather than to the physics:
170+
171+
- **Channel projection** ``P = \mathrm{Diag}(z)``: channels whose condensed mass is below ``10^{-10}`` are projected out of the Jacobian. Their rows of ``I/h - PJP`` reduce to the identity, so the solve returns exactly a forward-Euler update for those channels while healthy channels stay implicit — an IMEX-style splitting at channel granularity. Near-empty channels otherwise produce finite but very large Jacobian entries whose linearized steady state fabricates phantom number concentrations that substep refinement cannot remove.
170172
- **Equilibration** ``S = \mathrm{Diag}(|x| + h|f| + \epsilon)``: the linear system is solved as ``S^{-1} A S`` so the rows, which span roughly nine orders of magnitude across number and mass species, become O(1)-conditioned. This keeps single-precision roundoff relative to each species' own scale; an unscaled Float32 factorization deposits roundoff from the large rows into empty species as phantom mass.
173+
- **Positivity clamp** ``x \leftarrow \max(x + \Delta x, 0)``: a projection onto the physical nonnegative orthant after each substep.
171174

172-
The local temperature is advanced each substep from the latent heating of the realized increments. `logλ` and `q_tot` are held fixed across the interval, matching the explicit-substepping semantics; non-finite states or Jacobians fall back to forward-Euler substeps of the limited tendency. The implicit update makes the stiff ice-process path insensitive to the substep length; at very large substeps (``h \gtrsim 100`` s) the limiter switching degrades the single linearization, so increase `nsub` rather than relying on the limiters.
175+
The local temperature is advanced each substep from the latent heating of the realized increments. `logλ` and `q_tot` are held fixed across the interval, matching the explicit-substepping semantics; non-finite states or Jacobians fall back to forward-Euler substeps of the raw tendency. The implicit update makes the stiff ice-process path insensitive to the substep length; at very large substeps (``h \gtrsim 100`` s) the single linearization carries the usual first-order error of a one-stage method, so increase `nsub` to refine.

src/BMT_rosenbrock.jl

Lines changed: 37 additions & 138 deletions
Original file line numberDiff line numberDiff line change
@@ -23,140 +23,41 @@ SA.similar_type(::Type{<:MicroState2MP3}, ::Type{FT}, ::SA.Size{(8,)}) where {FT
2323
MicroState2MP3{FT}
2424

2525
"""
26-
_sink_limit(q, dt, n = 3)
27-
28-
Largest admissible sink rate for `q`: deplete at most `1/n` of it over `dt`.
29-
"""
30-
@inline _sink_limit(q, dt, n = 3) = max(zero(q), q) / dt / n
31-
32-
"""
33-
_coupled_sink_factor(S1, S2, q1, q2, dt)
34-
35-
Common scaling factor in `(0, 1]` for a coupled (mass, number) sink pair
36-
`(S1, S2)`, such that neither `f S1` nor `f S2` exceeds its
37-
[`_sink_limit`](@ref) over `dt`. Scaling the pair by one common factor
38-
preserves the mean particle mass implied by the pair; limiting the members
39-
independently would not.
40-
"""
41-
@inline function _coupled_sink_factor(S1, S2, q1, q2, dt)
42-
M1 = _sink_limit(q1, dt)
43-
M2 = _sink_limit(q2, dt)
44-
f1 = ifelse((S1 < zero(S1)) & (-S1 > M1), M1 / -S1, one(S1))
45-
f2 = ifelse((S2 < zero(S2)) & (-S2 > M2), M2 / -S2, one(S2))
46-
return min(f1, f2)
47-
end
48-
49-
"""
50-
_cap_supersaturation(tend, tps, T, ρ, q_tot, q_lcl, q_rai, q_ice, dt)
51-
52-
Cap the net condensation/deposition tendencies in `tend` so that one substep of
53-
length `dt` cannot overshoot saturation: the liquid channel
54-
(`dq_lcl + dq_rai`) is capped against the analytic saturation-adjustment
55-
condensation increment, then the ice channel (`dq_ice + dq_rim`) against the
56-
deposition increment computed from the vapor remaining after the liquid
57-
step. Each channel is scaled by a common, sign-preserving ratio in `[0, 1]`
58-
(both members of a channel are scaled together, like
59-
[`_coupled_sink_factor`](@ref)). Branch-free: `ifelse` evaluates both arms,
60-
which is safe here and keeps the kernel GPU-friendly.
61-
"""
62-
@inline function _cap_supersaturation(tend, tps, T, ρ, q_tot, q_lcl, q_rai, q_ice, dt)
63-
(; dq_lcl_dt, dn_lcl_dt, dq_rai_dt, dn_rai_dt,
64-
dq_ice_dt, dn_ice_dt, dq_rim_dt, db_rim_dt) = tend
65-
FT = typeof(q_tot)
66-
T_safe = max(150, T)
67-
q_vap = max(0, q_tot - q_lcl - q_rai - q_ice)
68-
69-
Rᵥ = TDI.Rᵥ(tps)
70-
cp_d = TDI.TD.Parameters.cp_d(tps)
71-
L_v = TDI.Lᵥ(tps, T_safe)
72-
L_s = TDI.Lₛ(tps, T_safe)
73-
qv_sat_liq = TDI.saturation_vapor_specific_content_over_liquid(tps, T_safe, ρ)
74-
qv_sat_ice = TDI.saturation_vapor_specific_content_over_ice(tps, T_safe, ρ)
75-
76-
# liquid channel vs the saturation-adjustment condensation increment
77-
qcon_satadj =
78-
(q_vap - qv_sat_liq) / (1 + L_v^2 * qv_sat_liq / (cp_d * Rᵥ * T_safe^2)) / dt
79-
net_liq = dq_lcl_dt + dq_rai_dt
80-
target_liq = ifelse(
81-
net_liq > 0, min(net_liq, max(zero(qcon_satadj), qcon_satadj)),
82-
ifelse(net_liq < 0, max(net_liq, min(zero(qcon_satadj), qcon_satadj)), zero(net_liq)),
83-
)
84-
ratio_liq = clamp(
85-
ifelse(abs(net_liq) > eps(FT), target_liq / net_liq, one(net_liq)),
86-
zero(net_liq), one(net_liq),
87-
)
88-
dq_lcl_dt *= ratio_liq
89-
dq_rai_dt *= ratio_liq
90-
dn_lcl_dt *= ratio_liq
91-
dn_rai_dt *= ratio_liq
92-
93-
# ice channel vs deposition of the vapor remaining after the liquid step
94-
qv_after_liq = max(0, q_vap - target_liq * dt)
95-
qdep_satadj =
96-
(qv_after_liq - qv_sat_ice) /
97-
(1 + L_s^2 * qv_sat_ice / (cp_d * Rᵥ * T_safe^2)) / dt
98-
net_ice = dq_ice_dt + dq_rim_dt
99-
target_ice = ifelse(
100-
net_ice > 0, min(net_ice, max(zero(qdep_satadj), qdep_satadj)),
101-
ifelse(net_ice < 0, max(net_ice, min(zero(qdep_satadj), qdep_satadj)), zero(net_ice)),
102-
)
103-
ratio_ice = clamp(
104-
ifelse(abs(net_ice) > eps(FT), target_ice / net_ice, one(net_ice)),
105-
zero(net_ice), one(net_ice),
106-
)
107-
dq_ice_dt *= ratio_ice
108-
dq_rim_dt *= ratio_ice
109-
dn_ice_dt *= ratio_ice
110-
db_rim_dt *= ratio_ice
111-
112-
return (; dq_lcl_dt, dn_lcl_dt, dq_rai_dt, dn_rai_dt,
113-
dq_ice_dt, dn_ice_dt, dq_rim_dt, db_rim_dt)
114-
end
115-
116-
"""
117-
_limited_2mp3_tendency(mp, tps, ρ, T, q_tot,
118-
q_lcl, n_lcl, q_rai, n_rai, q_ice, n_ice, q_rim, b_rim, logλ, dt)
119-
120-
The instantaneous 2M+P3 tendency projected onto the eight prognostic
121-
species, with [`_cap_supersaturation`](@ref) and the coupled
122-
[`_coupled_sink_factor`](@ref) mass-number limits applied for a substep of
123-
length `dt`.
124-
125-
This is the function the Rosenbrock step differentiates: the limiting sits
126-
inside the Jacobian so the implicit update respects it. Interrogated
127-
empirically: at substep lengths up to tens of seconds the supersaturation
128-
cap improves accuracy substantially (the coupled-sink limits are neutral
129-
there and protect the explicit fallback paths); at very large substeps
130-
(h ≳ 100 s) the limiter switching degrades the single linearization —
131-
increase `nsub` rather than relying on the limiters.
132-
"""
133-
@inline function _limited_2mp3_tendency(mp, tps,
26+
_instantaneous_2mp3_tendency(mp, tps, ρ, T, q_tot,
27+
q_lcl, n_lcl, q_rai, n_rai, q_ice, n_ice, q_rim, b_rim, logλ)
28+
29+
The raw instantaneous 2M+P3 tendency projected onto the eight prognostic
30+
species: the unlimited process rates of the `Microphysics2Moment` entry,
31+
without timestep-dependent clipping.
32+
33+
This is the function the Rosenbrock step differentiates. It is the model
34+
physics — an analytic time-averaged relaxation (Morrison and Milbrandt
35+
2015, J. Atmos. Sci., 72, 287-311, Appendix C) carries no tendency clip;
36+
the L-stable one-stage Rosenbrock update damps the stiff vapor-exchange
37+
subsystem monotonically, so the overshoot an explicit-Euler supersaturation
38+
cap suppresses cannot arise, while a 1/dt clip would add dt-independent
39+
error and break convergence under refinement (Wan et al. 2020, J. Adv.
40+
Model. Earth Syst., 12, e2019MS001982).
41+
"""
42+
@inline function _instantaneous_2mp3_tendency(mp, tps,
13443
ρ, T, q_tot,
13544
q_lcl, n_lcl, q_rai, n_rai, q_ice, n_ice, q_rim, b_rim, logλ,
136-
dt,
13745
)
13846
full = bulk_microphysics_tendencies(Microphysics2Moment(), mp, tps,
13947
ρ, T, q_tot,
14048
q_lcl, n_lcl, q_rai, n_rai, q_ice, n_ice, q_rim, b_rim, logλ,
14149
)
142-
tend = (; full.dq_lcl_dt, full.dn_lcl_dt, full.dq_rai_dt, full.dn_rai_dt,
50+
return (; full.dq_lcl_dt, full.dn_lcl_dt, full.dq_rai_dt, full.dn_rai_dt,
14351
full.dq_ice_dt, full.dn_ice_dt, full.dq_rim_dt, full.db_rim_dt)
144-
tend = _cap_supersaturation(tend, tps, T, ρ, q_tot, q_lcl, q_rai, q_ice, dt)
145-
f_liq = _coupled_sink_factor(tend.dq_lcl_dt, tend.dn_lcl_dt, q_lcl, n_lcl, dt)
146-
f_rai = _coupled_sink_factor(tend.dq_rai_dt, tend.dn_rai_dt, q_rai, n_rai, dt)
147-
return (;
148-
dq_lcl_dt = tend.dq_lcl_dt * f_liq, dn_lcl_dt = tend.dn_lcl_dt * f_liq,
149-
dq_rai_dt = tend.dq_rai_dt * f_rai, dn_rai_dt = tend.dn_rai_dt * f_rai,
150-
tend.dq_ice_dt, tend.dn_ice_dt, tend.dq_rim_dt, tend.db_rim_dt,
151-
)
15252
end
15353

15454
"""
155-
Limited2MP3Tendency(mp, tps, ρ, T, q_tot, logλ, dt)
55+
Instantaneous2MP3Tendency(mp, tps, ρ, T, q_tot, logλ)
15656
15757
Callable bundling the frozen per-substep context; applying it to the species
158-
vector evaluates [`_limited_2mp3_tendency`](@ref). A top-level struct rather
159-
than a closure, so `ForwardDiff` differentiates a concretely-typed callable.
58+
vector evaluates [`_instantaneous_2mp3_tendency`](@ref). A top-level struct
59+
rather than a closure, so `ForwardDiff` differentiates a concretely-typed
60+
callable.
16061
16162
The frozen `q_tot` is promoted to the state's element type at the call: a
16263
zero-partial Dual is exact for a constant, and it keeps
@@ -166,21 +67,19 @@ slots never carry derivatives (the forward `gamma_inc` has no shape rule);
16667
`T` and `ρ` stay plain because they feed working-type computations that must
16768
remain floats.
16869
"""
169-
struct Limited2MP3Tendency{P, H, F}
70+
struct Instantaneous2MP3Tendency{P, H, F}
17071
mp::P
17172
tps::H
17273
ρ::F
17374
T::F
17475
q_tot::F
17576
logλ::F
176-
dt::F
17777
end
178-
@inline function (g::Limited2MP3Tendency)(x::SA.StaticVector{8})
78+
@inline function (g::Instantaneous2MP3Tendency)(x::SA.StaticVector{8})
17979
(q_lcl, n_lcl, q_rai, n_rai, q_ice, n_ice, q_rim, b_rim) = x
180-
tend = _limited_2mp3_tendency(g.mp, g.tps,
80+
tend = _instantaneous_2mp3_tendency(g.mp, g.tps,
18181
g.ρ, g.T, eltype(x)(g.q_tot),
18282
q_lcl, n_lcl, q_rai, n_rai, q_ice, n_ice, q_rim, b_rim, g.logλ,
183-
g.dt,
18483
)
18584
return MicroState2MP3(values(tend)...)
18685
end
@@ -196,10 +95,9 @@ This is a scheme partition, not a preconditioner: with `P J P` in place of
19695
`J`, a masked channel's row of `(I/h - P J P)` is the identity row, so the
19796
solve returns exactly the forward-Euler update for that channel while
19897
healthy channels stay implicit (an IMEX-style splitting at channel
199-
granularity). The gate is load-bearing: through the coupled-sink limiter,
200-
near-empty channels produce finite but enormous Jacobian entries whose
201-
linearized steady state fabricates phantom number concentrations that
202-
substep refinement cannot remove.
98+
granularity). The gate is load-bearing: near-empty channels produce finite
99+
but enormous Jacobian entries whose linearized steady state fabricates
100+
phantom number concentrations that substep refinement cannot remove.
203101
"""
204102
@inline function _rosenbrock_channel_mask(x::MicroState2MP3{FT}) where {FT}
205103
ϵ_empty = FT(1e-10)
@@ -253,13 +151,14 @@ end
253151
Δt, nsub = 1)
254152
255153
Compute average 2M+P3 microphysics tendencies over `Δt` using `nsub`
256-
linearized-implicit (Rosenbrock-Euler) substeps.
154+
linearized-implicit (Rosenbrock-Euler) substeps of the raw instantaneous
155+
tendency.
257156
258157
# Algorithm
259158
260159
For each substep of `h = Δt / nsub`:
261160
262-
1. Evaluate the limited tendency `f` ([`_limited_2mp3_tendency`](@ref)) and
161+
1. Evaluate the raw tendency `f` ([`_instantaneous_2mp3_tendency`](@ref)) and
263162
its exact 8×8 Jacobian `J` via `ForwardDiff` at the current state.
264163
2. Advance with [`_rosenbrock_update`](@ref): solve `(I/h - P J P) Δx = f`
265164
in equilibrated variables, where the projection `P` routes near-empty
@@ -269,11 +168,11 @@ For each substep of `h = Δt / nsub`:
269168
270169
A non-finite state cannot be differentiated and a non-finite Jacobian (an
271170
exotic state escaping the channel mask) cannot be linearized; both fall back
272-
to a forward-Euler substep of the limited tendency, which is safe to step
273-
explicitly. `logλ` and `q_tot` are held fixed across substeps, matching the
274-
explicit-substepping semantics; the limiting (supersaturation cap and
275-
coupled mass-number sinks) applies the explicit-timestepping semantics
276-
unconditionally and sits inside the differentiated function.
171+
to a forward-Euler substep of the raw tendency. `logλ` and `q_tot` are held
172+
fixed across substeps, matching the explicit-substepping semantics. The
173+
discrete safeguards are h-free conditioning and projection (channel mask,
174+
equilibration, positivity clamp), not model terms: the differentiated
175+
tendency is the unmodified relaxation physics.
277176
278177
Returns the net change in the species over `Δt` divided by `Δt`, in the same
279178
fields as the `Instantaneous` entry (without the activation diagnostic).
@@ -293,7 +192,7 @@ fields as the `Instantaneous` entry (without the activation diagnostic).
293192
x₀ = x
294193
Tsub = T
295194
for _ in 1:nsub_eff
296-
g = Limited2MP3Tendency(mp, tps, ρ, Tsub, q_tot, logλ, h)
195+
g = Instantaneous2MP3Tendency(mp, tps, ρ, Tsub, q_tot, logλ)
297196
f = g(x)
298197
x_prev = x
299198
x = if all(isfinite, x)

src/BulkMicrophysicsTendencies.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,7 @@ struct LinearizedAverage <: TendencyMode end
122122
123123
Return time-averaged tendencies computed via repeated linearized-implicit
124124
(Rosenbrock-Euler) substeps, linearizing with the exact `ForwardDiff` Jacobian
125-
of the limited pointwise tendency at each substep. Implemented for the
125+
of the raw instantaneous pointwise tendency at each substep. Implemented for the
126126
2-moment + P3 configuration.
127127
"""
128128
struct RosenbrockAverage <: TendencyMode end

0 commit comments

Comments
 (0)