Skip to content

refactor(Common,P3): consolidate the Chen 2022 velocity curves#762

Draft
haakon-e wants to merge 18 commits into
he/ice-selfcol-trianglefrom
he/velocity-unify
Draft

refactor(Common,P3): consolidate the Chen 2022 velocity curves#762
haakon-e wants to merge 18 commits into
he/ice-selfcol-trianglefrom
he/velocity-unify

Conversation

@haakon-e

@haakon-e haakon-e commented Jul 10, 2026

Copy link
Copy Markdown
Member

Purpose

group Chen2022VelocityCurve coefficients by term

Content

Store terms::NTuple{N, NTuple{3, FT}} of (a, b, c) per summand of v(D) = sum(a D^b exp(-c D)) instead of parallel ai/bi/ci tuples. Add a Chen2022_vel_coeffs method on the curve returning the (ai, bi, ci) tuples; the closed-form rain consumers read coefficients through it.

P3IceParticleVelocityFunctor holds the aspect-ratio treatment as a field, set from state.params.aspect_ratio at construction, instead of reading it through the state at call time.

Move Chen2022_vel_coeffs, Chen2022VelocityCurve, particle_terminal_velocity, and the Chen 2022 pdf helpers from Common.jl to TerminalVelocityCurves.jl, included inside the Common module. No code change.

@haakon-e

Copy link
Copy Markdown
Member Author

@haakon-e haakon-e changed the title refactor(Common): group Chen2022VelocityCurve coefficients by term refactor(Common,P3): consolidate the Chen 2022 velocity curves Jul 10, 2026
@codecov

codecov Bot commented Jul 10, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 97.53086% with 2 lines in your changes missing coverage. Please review.
✅ Project coverage is 92.83%. Comparing base (8ef7fe6) to head (05249eb).

Additional details and impacted files
@@                     Coverage Diff                     @@
##           he/ice-selfcol-triangle     #762      +/-   ##
===========================================================
+ Coverage                    92.81%   92.83%   +0.01%     
===========================================================
  Files                           56       57       +1     
  Lines                         2952     2957       +5     
===========================================================
+ Hits                          2740     2745       +5     
  Misses                         212      212              
Components Coverage Δ
src 93.60% <97.53%> (+0.01%) ⬆️
ext 69.47% <ø> (ø)
🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

haakon-e added 13 commits July 10, 2026 17:00
…unds

Store a size-distribution quadrature rule on `P3IceParams` (Gauss-Legendre
by default; set the order via `quadrature_order`, or pass a full rule via
`quad`). `quad` is required by `integrate` and the P3 process functions;
`build_quadrature` is removed.

Integration bounds place the Chen 2022 small/large-ice velocity breakpoint
on a subinterval boundary (`velocity_integral_bounds`), so a fixed-order
rule stays accurate. This applies to the velocity-weighted integrals and to
`ice_melt`, whose ventilation factor carries the regime break.

The weighted-velocity integrals run the integral unconditionally and select
the degenerate-state result branchlessly; `compute_max_freeze_rate` selects
its limit values branchlessly for the same reason. Ice self-collection
integrates over the upper triangle `D₁ ≤ D₂`, which puts the `D₁ = D₂`
derivative discontinuity of the relative fall speed on a subinterval
boundary and halves the integrand evaluations.
…ion benchmark

Un-gate the 2M+P3 GPU kernel test and the P3 collision benchmark from
VERSION >= v"1.12", set their allocation and memory budgets to zero, and
recalibrate three time budgets from observed CI timings.
Check the triangular ice self-collection integral against the full-square
double integral with the pair-counting factor, replacing the reference
TODO. Cover number_tendency_from_mass_limits with x_min = 0, where the
lower-bound relaxation never fires.
Assert zero allocations, type stability, and a time budget on the
production entry point, not only its collision sub-integral.
Place the known derivative discontinuities of the liquid-ice collision
integrands on subinterval boundaries: the ice terminal-velocity regime
break at D_cutoff in the outer (ice) integral, and the ice-liquid
fall-speed crossing in the inner (liquid) integrals. The rain N and M
components already split at the crossing through the closed form; the
cloud path and the rain B_rim quadrature did not.

The crossover diameter is computed once per outer node and shared
between the closed-form N and M components and the B_rim bounds.
The upper integration bound is the (1 - p) quantile of the size
distribution, so the last subinterval can span most of the log(1/p)
decay lengths of the exponential tail; in large-mean-size states it
covers several decades and dominates the low-order quadrature error
(ice self-collection error at Gauss-Legendre order 12 is 2.4% in a
hail-core state). A breakpoint at three decay lengths, clamped into
the integration interval, keeps each subinterval resolvable at low
order (the same state and order reach 7e-5).

Update the collision smoke references: with the crossing and
decay-scale breakpoints the quadrature resolves the previously
unresolved integrand structure, which shifts the converged values.
The wet-growth indicator component moves the most; its integrand is
a step function in the ice diameter, and its quadrature error is
dominated by the step at any order.
The freeze/shed partition min() and the wet-growth indicator change
branch at the diameter where the collected liquid mass rate balances
the Musil freeze limit; interior to a subinterval, the indicator step
limits quadrature convergence of the shedding and wet-densification
components at ~1e-2 relative error regardless of order.

wet_growth_onset_diameter locates up to two balance points (the
wet-growth window closes again at large sizes where the freeze limit
outgrows collection) from a closed-form balance: the cloud collection
term neglects the droplet fall speed, making it a polynomial moment of
the cloud size distribution, and the rain term is the closed-form mass
component. The onset placement is within 0.7% of the exact balance
across tested regimes; the step components' error at order 12 drops
from 1.5e-3 to 4e-5 (shedding) and from 1e-1 to 7e-3 (wet indicator).
With the crossing, decay-scale, and wet-growth-onset breakpoints, the
three-level error study (17 regime states, GL(128) reference) splits the
bulk-tendency error at Gauss-Legendre order 6 into two families. The
transport components (sedimentation velocities, melt) converge to at most
0.45%. Every error above that sits in a component proportional to the
collision efficiency (collision sources, ice self-collection, rime), with
a worst case of 1.4%; these rates carry the E = 1 assumption, whose
parameterization uncertainty exceeds the quadrature error by an order of
magnitude. Order 5 exceeds 2% in the transport family and is rejected.

The quadrature-order sweep locks orders 6 through 12 in as a regression
test against a GL(200) reference, with graupel/hail-core states in the
state set.
VolumetricCollisionRate carries the ice and liquid terminal-velocity
closures as fields, so its consumers query them instead of receiving
the velocities separately: the fall-speed crossing bounds, the
closed-form rain integrals (whose velocity-curve coefficients now come
from the closure rather than a second coefficient evaluation), and the
wet-growth onset balance. Integrands without the velocity structure
integrate over unmodified bounds.

velocity_breakpoints(v_term) returns the diameters where a
terminal-velocity closure changes functional form;
velocity_integral_bounds takes the closure and no longer reaches into
the Chen 2022 cutoff field.

The wet-growth onset bisection runs through the RootSolvers API in log
diameter. The freeze-limit and weighted-velocity denominators divide
directly; the final selection discards the non-finite branch, so the
intermediate clipping is unnecessary. The liquid collision integrand
names its number, mass, and rime-volume rates. The documentation plots
use the package-default quadrature rule.
The study behind the default quadrature order lives in
test/p3_quadrature_error_study.jl: run_quadrature_error_study measures
each quadrature-consuming P3 quantity against a high-order reference
across regime states, attributed to transport, collision-efficiency, and
bulk levels. The order-sweep regression test includes it for the shared
column-state set, and the developers guide documents when to rerun it.
Replace ChebyshevGauss with Gauss-Legendre to match the scheme default.
Scheme-path calls use the default GL(6); the standalone reference
integrals use the smallest GL order meeting their existing tolerance.
Raise the rain ND tolerance to 3e-6 to reflect the 2p truncation of the
integration bounds. Drop the redundant Gauss-Legendre convergence
testset already covered by the dedicated quadrature testset.
…ax-freeze clamp

Add a docstring for `collision_cross_section_ice_ice` matching the sibling
`collision_cross_section_ice_liquid` convention, and list the symbol in the
P3Scheme API-docs block.

The `compute_max_freeze_rate` closure returns `floatmax(FT)` when the Musil
(1970) denominator is non-positive. ForwardDiff propagation was verified to be
AD-safe: the `ifelse` discards the non-finite branch (finite derivative in
Float32/Float64), `floatmax` and `zero` resolve for the `Dual` type, and both
branches infer to the same concrete type. No numerical change.
@haakon-e
haakon-e force-pushed the he/ice-selfcol-triangle branch from bbb3da8 to 8a2faf4 Compare July 11, 2026 00:58
@haakon-e
haakon-e force-pushed the he/velocity-unify branch from b9b0cd9 to dfc5e53 Compare July 11, 2026 00:58
haakon-e added 5 commits July 10, 2026 19:25
The collision_cross_section_ice_ice docstring references
[`ice_self_collection`](@ref), but the binding was absent from every
@docs block, so Documenter could not resolve the cross-reference and the
docs build failed. Add P3Scheme.ice_self_collection to the collision
@docs block in docs/src/API.md.
Store terms::NTuple{N, NTuple{3, FT}} of (a, b, c) per summand of
v(D) = sum(a D^b exp(-c D)) instead of parallel ai/bi/ci tuples. Add a
Chen2022_vel_coeffs method on the curve returning the (ai, bi, ci)
tuples; the closed-form rain consumers read coefficients through it.
…nctor

P3IceParticleVelocityFunctor holds the aspect-ratio treatment as a field,
set from state.params.aspect_ratio at construction, instead of reading it
through the state at call time.
Move Chen2022_vel_coeffs, Chen2022VelocityCurve, particle_terminal_velocity,
and the Chen 2022 pdf helpers from Common.jl to TerminalVelocityCurves.jl,
included inside the Common module. No code change.
Change the Chen 2022 terminal-velocity coefficient representation from
parallel tuples `(ai, bi, ci)` to per-term triples
`terms::NTuple{N, NTuple{3, FT}}` of `(aₖ, bₖ, cₖ)`, aligning the return
of `Chen2022_vel_coeffs` with the `terms` already stored on
`Chen2022VelocityCurve` and with its evaluation `∑ₖ aₖ D^bₖ exp(-cₖ D)`.

- `Chen2022_vel_coeffs(coeffs, ρₐ[, ρᵢ])` returns `terms`; the same unit
  conversions are applied per term, so values are unchanged.
- `Chen2022VelocityCurve` builds `terms` directly; the parallel-array
  constructor is replaced by one taking `terms` and promoting to a common
  `FT`. `Chen2022_vel_coeffs(v::Chen2022VelocityCurve)` returns `v.terms`.
- `closed_rain_inner_NM` takes `terms` and loops over the triples instead
  of indexing parallel arrays; call sites pass `terms` and drop the
  `SVector` wrapping.
- Update the Microphysics1M/2M consumers to sum over `terms`, preserving
  the exact summation order (per-term `ϕ^κ` factors kept inside the sum).

Representation change only: `Chen2022_vel_coeffs`, curve evaluation, and a
representative `closed_rain_inner_NM` call are byte-identical before and
after in Float64 and Float32.
@haakon-e
haakon-e force-pushed the he/velocity-unify branch from dfc5e53 to c89f4e8 Compare July 11, 2026 02:32
@haakon-e
haakon-e force-pushed the he/ice-selfcol-triangle branch from dc1db96 to 355b541 Compare July 11, 2026 03:28
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