Skip to content

Commit cfa1630

Browse files
committed
feat(BMT): coupled Tier-3 donor linearization for the 2M+P3 ManualJacobian
Make the ManualJacobian usable in mixed-phase by donor-linearizing the mixed-phase quadrature transfers into the substep Jacobian. For a transfer of primal rate S from donor d to receiver r, add -D on the donor diagonal and +D on the (r, d) off-diagonal with D = S/max(floor, x_d), reusing the per-process rates of _per_process_2mp3 (no gamma_inc shape derivative). The dominant coupling is the ice->rain melt source (mass donor q_ice, number donor n_ice, rim mass/volume on their own donors); without it the rain melt source integrates explicitly and diverges with the step. The liquid-ice collision cloud/rain sinks self-limit on their own donors. Mixed-phase error vs the fine explicit reference drops from 88-2420 (non-convergent) to 0.0157-1.54 and converges under nsub, matching ExactJacobian to the same order of magnitude; ice-only is unchanged (melt/collision inactive below freezing). 0-alloc, type-stable (F32+F64); existing rosenbrock tests pass.
1 parent cf9bec0 commit cfa1630

1 file changed

Lines changed: 53 additions & 8 deletions

File tree

src/BMT_rosenbrock.jl

Lines changed: 53 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -638,9 +638,15 @@ The entries are tiered:
638638
freezing, linearized in their donor species through `D = rate /
639639
max(floor, q_donor)` reusing the per-process rates of
640640
[`_per_process_2mp3`](@ref).
641-
- Tier 3 (dropped): the quadrature liquid-ice collision, ice aggregation, and
642-
ice-melt couplings are kept in `f` but omitted from `J` (no `gamma_inc`
643-
shape derivative is taken).
641+
- Tier 3 (coupled donor): the mixed-phase quadrature transfers, donor-linearized
642+
through `D = rate / max(floor, x_donor)` with `−D` on the donor diagonal and
643+
`+D` on the receiver off-diagonal (the [`_linearize`](@ref) recipe), reusing the
644+
per-process rates of [`_per_process_2mp3`](@ref). The ice→rain melt transfer
645+
(mass donor `q_ice`, number donor `n_ice`, rim mass/volume on their own donors)
646+
is the dominant one: without it the rain melt source integrates ~explicitly and
647+
diverges with the step. The liquid-ice collision cloud/rain sinks self-limit on
648+
their own donors. No `gamma_inc` shape derivative is taken — the quadrature rate
649+
is frozen and only the donor dependence is linearized.
644650
"""
645651
@inline function _jacobian_2mp3_manual(
646652
g::Instantaneous2MP3Tendency, x::MicroState2MP3{FT},
@@ -805,17 +811,56 @@ The entries are tiered:
805811
nrai_nrai += pp.rain_freezing.n_rai * dnrai
806812
nice_nrai = pp.rain_freezing.n_ice * dnrai
807813

808-
# Tier 3 (liquid-ice collision, ice aggregation, ice melt): omitted from J.
814+
#####
815+
##### Tier 3 — coupled donor linearization of the mixed-phase quadrature transfers
816+
#####
817+
# The dominant mixed-phase coupling is the ice→rain melt source; without it the
818+
# rain source integrates ~explicitly and runs away. Donor-linearize each transfer
819+
# of primal rate `S` from donor `d` to receiver `r` as `D = S/max(floor, x_d)`
820+
# with `−D` on the donor diagonal and `+D` on the (r, d) off-diagonal (the 1M
821+
# `_linearize` recipe), reusing the per-process rates of `_per_process_2mp3`. No
822+
# `gamma_inc` shape derivative is taken: the quadrature rate is held frozen and
823+
# only the donor dependence is linearized, so the receiver source self-limits as
824+
# the donor empties within the implicit step.
825+
rai_ice = o
826+
nrai_nice = o
827+
rim_rim = o
828+
brim_brim = o
829+
830+
# ice melt (ice → rain): mass donor q_ice, number donor n_ice; the rim mass and
831+
# volume drain on their own donors. The melt vector is signed +source into rain /
832+
# −sink out of ice, so each `pp.ice_melting.<species>` already carries the rate.
833+
dice = 1 / max(q_floor, q_ice)
834+
dnice = 1 / max(n_floor, n_ice)
835+
drim = 1 / max(q_floor, q_rim)
836+
dbrim = 1 / max(n_floor, b_rim)
837+
D_melt_q = pp.ice_melting.q_rai * dice # ≥ 0
838+
ice_ice -= D_melt_q
839+
rai_ice += D_melt_q
840+
D_melt_n = pp.ice_melting.n_rai * dnice # ≥ 0
841+
nice_nice -= D_melt_n
842+
nrai_nice += D_melt_n
843+
rim_rim += pp.ice_melting.q_rim * drim # rim mass sink (donor q_rim)
844+
brim_brim += pp.ice_melting.b_rim * dbrim # rime volume sink (donor b_rim)
845+
846+
# liquid-ice collision (cloud/rain → ice): the cloud and rain mass/number sinks
847+
# self-limit on their own donors. Only the donor sinks are linearized; the small
848+
# ice wet-growth / riming receivers are left to the melt brake that bounds them.
849+
dlcl_c = 1 / max(q_floor, q_lcl)
850+
dnlcl_c = 1 / max(n_floor, n_lcl)
851+
lcl_lcl += min(pp.liquid_ice_collision.q_lcl, o) * dlcl_c
852+
nlcl_nlcl += min(pp.liquid_ice_collision.n_lcl, o) * dnlcl_c
853+
nrai_nrai += min(pp.liquid_ice_collision.n_rai, o) * dnrai
809854

810855
return _jacobian_2mp3(FT;
811856
lcl_lcl, lcl_rai, lcl_ice,
812857
nlcl_lcl, nlcl_nlcl,
813-
rai_lcl, rai_rai,
814-
nrai_nlcl, nrai_rai, nrai_nrai,
858+
rai_lcl, rai_rai, rai_ice,
859+
nrai_nlcl, nrai_rai, nrai_nrai, nrai_nice,
815860
ice_lcl, ice_rai, ice_ice,
816861
nice_lcl, nice_nlcl, nice_rai, nice_nrai, nice_ice, nice_nice,
817-
rim_lcl, rim_rai, rim_ice,
818-
brim_lcl, brim_rai, brim_ice,
862+
rim_lcl, rim_rai, rim_ice, rim_rim,
863+
brim_lcl, brim_rai, brim_ice, brim_brim,
819864
)
820865
end
821866

0 commit comments

Comments
 (0)