P3 Back To Cell Average
This document describes the back_to_cell_average bookkeeping step in P3 and
its unit-test coverage.
Physical Formulation
P3 process rates are often computed for in-cloud or overlap regions. The model state update uses cell-mean tendencies, so each tendency is mapped by the appropriate cloud fraction:
Cloud overlap factors used in the implementation are:
In code terms, the mapping tags used by tests are:
L=cld_frac_l= \(f_l\)R=cld_frac_r= \(f_r\)I=cld_frac_i= \(f_i\)IL=min(cld_frac_i, cld_frac_l)= \(f_{il}\)IR=min(cld_frac_i, cld_frac_r)= \(f_{ir}\)LR=min(cld_frac_l, cld_frac_r)= \(f_{lr}\)
Runtime branch behavior:
use_separate_ice_liq_frac=false: use \(f_i\) forqi2qv_sublim_tend,qv2qi_vapdep_tend, andni_sublim_tend.use_separate_ice_liq_frac=true: use \(f_{\text{glaciated}}\) for those same three tendencies.
Because \(f_{\text{glaciated}}=\max(10^{-4}, f_i-f_{il})\), a minimum factor of \(10^{-4}\) is applied even when there is no pure-ice-only portion (\(f_i \le f_{il}\)).
Implementation Details
- Source:
components/eamxx/src/physics/p3/impl/p3_back_to_cell_average_impl.hpp. - Runtime option:
use_separate_ice_liq_frac. - Threshold:
cld_frac_glaciatedhas a hard minimum of1e-4. - Two nucleation tendencies are currently passed through unchanged:
qv2qi_nucleat_tendni_nucleat_tend
Process Mapping
| Process | Variable | Cloud Fraction | Rationale |
|---|---|---|---|
| Accretion cloud->rain mass | qc2qr_accret_tend |
\(f_{lr}\) | Requires liquid-rain overlap |
| Rain evaporation mass | qr2qv_evap_tend |
\(f_r\) | Occurs in rain region |
| Autoconversion mass | qc2qr_autoconv_tend |
\(f_l\) | Occurs in liquid cloud |
| Accretion cloud number | nc_accret_tend |
\(f_{lr}\) | Number sink tied to accretion overlap |
| Cloud self-collection number | nc_selfcollect_tend |
\(f_l\) | Liquid-cloud process |
| Autoconversion cloud number transfer | nc2nr_autoconv_tend |
\(f_l\) | Liquid-cloud process |
| Rain self-collection number | nr_selfcollect_tend |
\(f_r\) | Rain-cloud process |
| Rain evaporation number sink | nr_evap_tend |
\(f_r\) | Rain-cloud process |
| Autoconversion rain number source | ncautr |
\(f_{lr}\) | Liquid-rain overlap |
| Ice sublimation mass | qi2qv_sublim_tend |
\(f_{\text{glaciated}}\) or \(f_i\) | Runtime branch |
| Ice shedding rain number source | nr_ice_shed_tend |
\(f_{il}\) | Mixed-phase interaction |
| Heterogeneous freezing mass | qc2qi_hetero_freeze_tend |
\(f_{il}\) | Mixed-phase interaction |
| Rain collected by ice mass | qr2qi_collect_tend |
\(f_{ir}\) | Ice-rain overlap |
| Ice shedding rain mass source | qc2qr_ice_shed_tend |
\(f_{il}\) | Mixed-phase interaction |
| Ice melting mass | qi2qr_melt_tend |
\(f_i\) | Ice-cloud process |
| Cloud collected by ice mass | qc2qi_collect_tend |
\(f_{il}\) | Mixed-phase interaction |
| Rain immersion freezing mass | qr2qi_immers_freeze_tend |
\(f_r\) | Rain-cloud process |
| Ice melting rain number source | ni2nr_melt_tend |
\(f_i\) | Ice-cloud process |
| Cloud collected by ice number | nc_collect_tend |
\(f_{il}\) | Mixed-phase interaction |
| Shedding cloud number sink | ncshdc |
\(f_{il}\) | Mixed-phase interaction |
| Cloud immersion-freezing number | nc2ni_immers_freeze_tend |
\(f_l\) | Implemented as liquid-cloud process |
| Rain collected by ice number | nr_collect_tend |
\(f_{ir}\) | Ice-rain overlap |
| Ice self-collection number | ni_selfcollect_tend |
\(f_i\) | Ice-cloud process |
| Vapor deposition to ice mass | qv2qi_vapdep_tend |
\(f_{\text{glaciated}}\) or \(f_i\) | Runtime branch |
| Rain immersion-freezing number | nr2ni_immers_freeze_tend |
\(f_r\) | Rain-cloud process |
| Ice sublimation number sink | ni_sublim_tend |
\(f_{\text{glaciated}}\) or \(f_i\) | Runtime branch |
| Ice nucleation mass | qv2qi_nucleat_tend |
unchanged | Already cell-averaged |
| Ice nucleation number | ni_nucleat_tend |
unchanged | Already cell-averaged |
| Bergeron mass transfer | qc2qi_berg_tend |
\(f_{il}\) | Mixed-phase process |
| Het-freezing number counter | ncheti_cnt |
\(f_l\) | Counter mapped over liquid cloud |
| Het-freezing mass counter | qcheti_cnt |
\(f_l\) | Counter mapped over liquid cloud |
| Contact nucleation number counter | nicnt |
\(f_l\) | Counter mapped over liquid cloud |
| Contact nucleation mass counter | qicnt |
\(f_l\) | Counter mapped over liquid cloud |
| Nucleation number counter | ninuc_cnt |
\(f_l\) | Counter mapped over liquid cloud |
| Nucleation mass counter | qinuc_cnt |
\(f_l\) | Counter mapped over liquid cloud |
Property Tests
The unit tests live in
components/eamxx/src/physics/p3/tests/p3_back_to_cell_average_unit_tests.cpp.
Test Organization
The Catch2 test case p3_back_to_cell_average is split into independent
sections:
process_locationknown_answer_testsruntime_optioncontext_maskedge_casesextreme_valuesbfb
Known-Answer Philosophy
The sweep-based process_location checks use a shared mapping table
(ScaleKind/cloud_factor) for broad coverage. To avoid mirrored bugs where
both implementation and helper table are wrong in the same way, the
known_answer_tests section adds hand-computed reference cases with hardcoded
expected values.
These checks intentionally do not use the shared helper and cover:
- L, R, I, IL, IR, LR mappings (for example:
qc2qr_accret_tend -> LR,nr_evap_tend -> R) GLACIATED_OR_Iin both runtime modesUNCHANGEDpass-through behavior forqv2qi_nucleat_tendandni_nucleat_tend- explicit glaciated-floor activation (
f_glaciated = 1e-4)
Tolerance Philosophy
| Test Category | Tolerance Type | Value | Rationale |
|---|---|---|---|
| Process-location mapping | Identity | 10 * epsilon |
Exact implementation formula |
| Known-answer mapping | Identity | 10 * epsilon |
Independent hand-calculated reference checks |
| Runtime branch mapping | Identity | 10 * epsilon |
Exact branch behavior |
| Context mask behavior | Identity | 10 * epsilon |
context=false lanes are not written |
| Glaciated threshold | Identity + floor | 10 * epsilon, >= 1e-4 |
Explicit implementation floor in edge/known-answer checks |
| Extreme-value finite checks | Finite | isfinite |
Guard against NaN/Inf under very large/small magnitudes |
Here, epsilon is machine epsilon for EAMxx Real
(std::numeric_limits<Real>::epsilon()).
Parameter Sweep Strategy
A 3D linear sweep over cloud fractions is used:
- \(f_i, f_l, f_r \in [0,1]\)
n_frac = 15per dimensionnum_cases = 15^3 = 3375
All tendency inputs are deterministic and distinct. The process_location
section also runs a mixed-sign sweep variant to cover both source and sink
tendencies.
Context-Mask Coverage
The implementation applies updates with .set(context, ...). The context_mask
section verifies that lanes with context=false are unchanged (not written),
including an explicit all-zero cloud-fraction lane.
Number-Tendency Validation Policy
The suite does not enforce a global number-conservation identity (\(\sum \partial N / \partial t = 0\)). In P3, self-collection, evaporation/ sublimation, nucleation, and shedding are not globally number-conservative.
Instead, number tendencies are validated via process-location mapping checks: for each number tendency, the test verifies the exact cloud-fraction factor used by the implementation.
BFB Serialization Scope
The BFB compare list intentionally follows the
BackToCellAverageData::PTD_RW_SCALARS_ONLY serialized subset:
- Includes legacy pass-through fields
qcnucandnc_nuceat_tendfromBackToCellAverageDataserialization. In the current kernel argument list, the corresponding pass-through tendencies areqv2qi_nucleat_tendandni_nucleat_tend. - Excludes
*_cntfields from this serialized BFB subset, even though those counters are scaled inback_to_cell_average.