Skip to content

Make GEOS-Chem H2O a met-driven constant (fixed) species - #227

Open
xk-y wants to merge 3 commits into
EarthSciML:mainfrom
xk-y:pr/deffix-h2o
Open

Make GEOS-Chem H2O a met-driven constant (fixed) species#227
xk-y wants to merge 3 commits into
EarthSciML:mainfrom
xk-y:pr/deffix-h2o

Conversation

@xk-y

@xk-y xk-y commented Jul 10, 2026

Copy link
Copy Markdown
Contributor

STATE-VECTOR CHANGE · breaking (274 → 273 states)

Breaking: H2O moves from @species (a dynamic state) to @parameters (isconstantspecies=true). Compiled coupled states drop 274 → 273. Code that indexes H2O by state position, or sets an H2O initial condition, must migrate: H2O is now a met-driven observed variable (coupled from GEOSFP RH via the Magnus equation), matching GEOS-Chem Fortran's met-driven fixed-species behavior.

Motivation

Atmospheric H2O is determined by meteorology, not by gas-phase chemistry — GEOS-Chem Fortran accordingly treats it (with H2/N2/O2) as a fixed species set from met each timestep (met-driven fixed species), never integrated. The port instead carries H2O as a chemistry state, and since no chemical reaction meaningfully produces or destroys it, it simply stays frozen at its IC of 1.84e7 ppb at every model level. That is a boundary-layer mixing ratio; real upper-troposphere humidity is 100–1000× lower. The concrete failure: the explicit O1D + H2O → 2OH source then sees boundary-layer water aloft and grossly overproduces OH in the free troposphere in coupled multi-level runs.

Change

  • geoschem_fullchem.jl: H2O @species@parameters with isconstantspecies=true (alongside H2/N2/O2, which upstream already treats exactly this way — H2O was the one fixed species left behind as a state).
  • EarthSciDataExt.jl: GEOSFP coupling adds param_to_var(:H2O) + Magnus-equation H2O ~ f(A3dyn₊RH, P, T), so coupled runs get real per-cell humidity.

Why this departs from the current design

The species-vs-parameter choice is not cosmetic: as a state, H2O can only be set by a (single, scalar) initial condition, so every coupled run inherits the frozen-constant error; as an isconstantspecies parameter it is promoted to a met-driven observed variable by param_to_var — the same mechanism the SuperFast↔GEOSFP coupling already uses. Uncoupled behavior is unchanged (the parameter default is the same 1.84e7 ppb the state was frozen at); only the coupled system gains correct humidity.

Upstream test change (O1D reference value)

The O1D sensitivity to O3 reference value moves 8.1606e-7 → −4.4112e-7, with a robustified protocol (reltol=abstol=1e-10, 50% perturbation, rtol 0.01) — the same treatment the merged FastJX-interpolation PR (#222) applied to the OH/HO2 sensitivity tests in this file. The quantity is a near-cancellation finite difference of two O3 ≈ 20 ppb endpoints; at the old default tolerance + 10% perturbation the difference sits at the solver accuracy floor, so the reference value pinned solver noise (it does not even reproduce its sign under converged integration) and drifts with any structural change to the system — including this PR's 274→273 state change, which is numerically a no-op for this box test. Under the tightened protocol the value is converged and reproducible: −4.4112e-7 on one branch (which additionally adds the heterogeneous-uptake parameters) and −4.4110e-7 on this branch — two structurally different compiled systems agreeing to <5e-5 relative, well inside the rtol=0.01 test margin. The committed reference constant deliberately pins the shared cross-branch converged value (−4.411157e-7) rather than this branch's own last digit, so the same test ships unchanged across the related branches; either value passes at rtol 0.01.

Validation

New test (EarthSciData_test.jl) asserts the invariant directly: H2O is not a state, and H2O appears as an observed variable in the coupled system. The compiled coupled state count drops 274 → 273 (measured on this branch, not separately asserted — the not-a-state test is the stable form of the same guarantee). Full Pkg.test green (rc=0) in the upstream environment on this branch.

Notes

Shipped as its own PR (not bundled with the NEI mapping change) so it can be reviewed / discussed independently. Minor version bump.

Figure

Domain+time-mean H2O: frozen 1.84e7 at every level vs GEOSFP-coupled profile; frozen/real up to x158 aloft

Left: H2O carried as a chemistry state freezes at its boundary-layer IC (1.84e7 ppb) at every level, while met-driven H2O follows GEOSFP humidity down ~2 orders of magnitude with altitude. Right: the frozen/real ratio reaches ~x158 near the model top, where O1D + H2O -> 2OH then over-produces OH in the free troposphere. (Domain+time-mean, Mar 10-12 reference run.)


Part of the coordinated cross-repo update tracked in #225.

xk-y and others added 3 commits July 8, 2026 01:22
… coupling

* GEOSChemGasPhase H2O moves @species -> @parameters
  (isconstantspecies=true, alongside H2/N2/O2). Compiled coupled states 274->273.
* GC<->GEOSFP: param_to_var(:T,:num_density,:H2O) + Magnus-equation
  c.H2O ~ f(A3dyn RH). The explicit O1D + H2O --> 2OH OH source now sees real
  per-cell humidity instead of holding H2O at a single constant (1.84e7 ppb),
  which overestimates OH aloft where H2O is 100-1000x lower.

The NEI mapping expansion is submitted separately.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
H2O must be observed (met-driven), not a state, in the coupled
GC+NEI+GEOSFP system.

Met-driven H2O assertions only; the NEI assertions are submitted separately.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…rence)

The 274->273 met-driven H2O state change shifts solver roundoff on the O1D->O3
finite-difference sensitivity, a near-cancellation quantity that at the old
default tolerance + 10% perturbation sat at/below the solver accuracy floor
(so it was noise that drifted with any structural change). Same fix as PR EarthSciML#222
for OH/HO2: reltol=abstol=1e-10 + 50% perturbation lifts the signal ~80x above
the floor; the converged reference value is -4.411157e-7 (measured on this
branch: -4.411015e-7, 3e-5 rel diff, rtol=0.01 has ample margin).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@codecov

codecov Bot commented Jul 10, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 70.00000% with 3 lines in your changes missing coverage. Please review.
✅ Project coverage is 87.29%. Comparing base (4cb6241) to head (214e0e3).

Files with missing lines Patch % Lines
ext/EarthSciDataExt.jl 70.00% 3 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main     #227      +/-   ##
==========================================
- Coverage   87.42%   87.29%   -0.13%     
==========================================
  Files          20       20              
  Lines        1463     1472       +9     
==========================================
+ Hits         1279     1285       +6     
- Misses        184      187       +3     
Flag Coverage Δ
docs 70.81% <0.00%> (-0.45%) ⬇️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant