feat(topology): register CAM_MAM4 and CAM_MAM5 — MAM5 now has data, not just a shape - #73
Open
aliakherati wants to merge 1 commit into
Open
feat(topology): register CAM_MAM4 and CAM_MAM5 — MAM5 now has data, not just a shape#73aliakherati wants to merge 1 commit into
aliakherati wants to merge 1 commit into
Conversation
…ot just a shape
Topology already accepted nmodes=5, but nothing was registered, so "MAM5-JAX"
was a validated shape with no data behind it. This lands the data.
registered: ('cam_mam4', 'cam_mam5', 'e3sm_mam4_mom')
cam_mam5: nmodes=5 variant=cesm is_mam5=True pcnst=108
mode 5 = coarse_strat, so4 only, sigma_g 1.2, dgnum 9e-7
mam4_jax/core/cam_topologies.py is GENERATED in the sibling repo
mam-box-fortran and vendored here. That indirection is not laziness:
numptr_amode, lmassptr_amode and the per-slot species properties are not
tabulated in any source file -- modal_aero_data.F90 builds them at init from
the chemistry preprocessor's species list -- so they can only be observed,
not transcribed. The header carries the regeneration commands and a sha256
of each input dump.
THE DESIGN QUESTION, settled with data. CAM has no lspectype_amode: it
resolves each (mode, slot) straight to specdens_amode(l,m) and
spechygro(l,m), while Topology keeps per-TYPE tables plus an index. Rather
than give Topology two shapes, the generator synthesises the type list from
species-name prefixes -- legitimate only if lossless, so it was checked
rather than assumed. Across both topologies each of the 6 prefixes (so4,
pom, soa, bc, dst, ncl) carries exactly one (density, hygroscopicity) pair
in every mode it appears in. The generator reconstructs CAM's per-slot
arrays and exits non-zero on mismatch, and tests/test_cam_topology.py
re-checks it from the COMMITTED data, since a generator-time assertion says
nothing about what landed in the repo.
Full precision mattered. pom and bc both read 1.0e-10 hygroscopicity at one
decimal place but are 1.000000082740371e-10 and 1.000000013351432e-10 --
float32 round-trips of 1e-10 differing in the last bits. Had the dump stayed
at es12.5 the two types would have collapsed into one and the synthesis
would have been silently wrong. There is a test for it.
One assumption worth killing: MAM5 is NOT simply MAM4 plus a mode. The strat
size variant also changes accum's width, 1.8 -> 1.6. Asserted, because
"MAM4 + 1" is the natural intuition and it is wrong.
Also pins the numbers the upstream stale-dumfac bug report quotes, straight
from these tables: the last mode's dumfac gives +32.5% under MAM5 (last mode
is coarse_strat, sigma_g 1.2) against +20.6% under MAM4 (whose last mode
happens to share aitken's width). Those figures now have a test rather than
living only in a markdown file.
Registration deliberately does not activate: E3SM stays the default, so
importing the CAM tables cannot silently repoint existing code. Verified the
E3SM derived quantities stay bit-identical.
Suite: 176 passed (was 164; +12).
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01A3zu6nmLHsAUzTkDJFokjv
aliakherati
added a commit
that referenced
this pull request
Aug 27, 2026
…it consumes A6 (sub-stepping ON by default) approved by owner 2026-08-26; substep count to be picked empirically in G5. Records the source-read findings that reshape the reuse table: CAM's gas_aer_uptkrates is a third variant (fixed beta=2, ac=0.65 literals, truncated constants — port, don't reuse); the legacy 8.0-monolayer aging runs inside BOTH CAM's gasaerexch and coag_sub (closing the #75 attribution question — CAM genuinely ages at 8.0, amicphys at 3.0); DGNUM pbuf-inits to 0.0 so end-to-end parity needs topology-threaded calcsize+wateruptake while the microphysics sequence validates against isolated captures first; MAM5 references pin nl_acc_crs=0 (rename-A2 stays deferred); merges feat/cam-mam5-topology (PR #73) into this branch as the driver's data layer.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Topologyalready acceptednmodes=5, but nothing was registered — so "MAM5-JAX" was a validated shape with no data behind it. This lands the data.Suite: 176 passed (was 164). E3SM derived quantities verified bit-identical.
Where the data comes from
mam4_jax/core/cam_topologies.pyis generated in the sibling repomam-box-fortranand vendored here. The indirection isn't laziness:numptr_amode,lmassptr_amodeand the per-slot species properties are not tabulated in any source file —modal_aero_data.F90builds them at init from the chemistry preprocessor's species list. They can only be observed, not transcribed. The header carries the regeneration commands and a sha256 of each input dump.The design question, settled with data
CAM has no
lspectype_amode. It resolves each (mode, slot) straight tospecdens_amode(l,m)/spechygro(l,m), whileTopologykeeps per-type tables plus an index. Two options were on the table: giveTopologyper-slot arrays as a second shape, or synthesise a type list for CAM.Synthesis wins, but only because it's lossless — checked, not assumed. Across both topologies each of the 6 prefixes (
so4,pom,soa,bc,dst,ncl) carries exactly one (density, hygroscopicity) pair in every mode it appears in. The generator reconstructs CAM's per-slot arrays and exits non-zero on mismatch, andtests/test_cam_topology.pyre-checks it from the committed data — a generator-time assertion says nothing about what actually landed in the repo.Full precision mattered
pomandbcboth read1.0e-10hygroscopicity at one decimal place, but are1.000000082740371e-10and1.000000013351432e-10— float32 round-trips of 1e-10 differing in the last bits. Had the dump stayed ates12.5, the two types would have collapsed into one and the synthesis would have been silently wrong. There's a test for it.One assumption worth killing
MAM5 is not simply MAM4 plus a mode. The strat size variant also changes accumulation's width, 1.8 → 1.6. Asserted explicitly, because "MAM4 + 1" is the natural intuition and it's wrong.
Bonus
The numbers the upstream stale-
dumfacbug report quotes are now derived from these tables in a test rather than living only in markdown: the last mode'sdumfacgives +32.5 % under MAM5 (last mode iscoarse_strat, σ_g 1.2) against +20.6 % under MAM4 (whose last mode happens to share aitken's width).Registration deliberately does not activate — E3SM stays the default, so importing the CAM tables can't silently repoint existing code.
Not in this PR
The topologies exist and validate; no physics has been run under
cam_mam5yet. That needs the CAM driver (plan 024 PR G) plus reference capture againstmam-box-fortran. This is the data layer.🤖 Generated with Claude Code
https://claude.ai/code/session_01A3zu6nmLHsAUzTkDJFokjv