Skip to content

Port IRRAD GridComp to MAPL3 - #94

Merged
pchakraborty merged 18 commits into
release/MAPL-v3from
feature/pchakrab/port-to-mapl3
Aug 13, 2026
Merged

Port IRRAD GridComp to MAPL3#94
pchakraborty merged 18 commits into
release/MAPL-v3from
feature/pchakrab/port-to-mapl3

Conversation

@pchakraborty

@pchakraborty pchakraborty commented Aug 13, 2026

Copy link
Copy Markdown

Issues that need to be resolved - #95

Summary

  • First MAPL3 port of GEOS_RadiationGridComp/GEOS_IrradGridComp (LW RRTMGP path), including Edge-field indexing fixes, refresh-alarm handling, and formatting cleanup.
  • Adds a standalone irrad-sa regression test (root/cap/data yaml config, FakeGOCART_GridComp test-double for the AERO provider, cmpchk.py comparison script) that now achieves zero-diff against a MAPL2 baseline.
  • Fixes an undef-sentinel mismatch (MAPL2_UNDEF vs MAPL3's MAPL_UNDEF) and a missing set_inhomogeneity() call that were driving the last of the MAPL2/MAPL3 numerical divergence in the McICA cloud-sampling path.
  • Various LW_Driver restructuring (aerosol optics routed through compute_provider_aerosol_optics, scheme-local setup moved closer to use, timer renaming) and StateSpecs.rc/SetServices cleanup along the way.

Test plan

  • irrad-sa regression test passes with zero-diff against the MAPL2 baseline
  • Full GEOSgcm regression suite (SOLAR/SATSIM not yet ported to MAPL3; only longwave-only Radiation exports are populated for now)

pchakraborty and others added 16 commits July 22, 2026 00:24
MAPL always creates Edge-staggered (VLOC=E) fields with Fortran bounds
1:LM+1, never 0:LM (confirmed in FieldCreate.F90's
make_bounds_from_args). Both IRRAD and the Radiation container assume
the 0:LM layer-interface convention throughout (e.g.
FLX_INT(:,:,0)/(:,:,LM), PLE(:,:,0:LM-1)/(:,:,1:LM)), so every Edge
pointer was silently off-by-one - undetected until now because the
physics hadn't actually executed under -fcheck=all.

Fixed by remapping each Edge pointer once, right after it's fetched,
via a bounds-remap routed through a distinct CONTIGUOUS-declared
scratch pointer (gfortran-15 rejects a self-referencing remap even on
a CONTIGUOUS pointer, and POINTER+TARGET together is illegal Fortran -
both confirmed with isolated test compiles):

- GEOS_IrradGridComp.F90: all internal/import Edge fields in Run, all
  export Edge fields in Update_Flx (associated()-guarded, since
  exports can be null), and the RATS-diagnostics 4D arrays at both
  fetch sites.
- GEOS_RadiationGridComp.F90: PLE/FLW/FLWCLR/FLWNA/FLA in Run.
- Also fixes MAPL_GridGetInterior, which doesn't exist in MAPL3 -
  replaced with MAPL_GridGet(..., interior=...).

Also: build GEOSradiation_GridComp (and GEOSirrad_GridComp) as SHARED
libraries, matching the rest of MAPL3.

See mapl3-porting-notes.md (this dir and GEOSirrad_GridComp/) for the
full investigation and verification detail.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…ad code

- Add a real Initialize entry point that creates a clock-attached
  "irrad_lw_alarm" (ring interval from IRRAD_DT). Run now looks the
  alarm up by name each call and gates the full LW_Driver refresh on
  ESMF_AlarmIsRinging/ESMF_AlarmRingerOff, replacing the previous
  workaround of calling LW_Driver unconditionally every step (the
  MAPL2-style alarm fetch had been commented out with no MAPL3
  equivalent noted at the time).

- Collapse the NUM_BANDS/TOTAL_RAD_BANDS mismatch diagnostic into a
  single _FAIL(band_msg) call carrying one combined message, removing
  the three pflogger logger%info() calls it replaces. Drops the now-
  unused `use pflogger, only: logger_t => logger`, the `logger` local,
  and the `logger=` argument to MAPL_GridCompGet.

- SetServices: replace the `if (n .ne. 0) then ... endif` wrapper
  around the RATS-diagnostics dynamic spec block with an early
  `_RETURN_UNLESS(n > 0)`, and register the Initialize/Run entry
  points unconditionally near the top of the routine so they no
  longer sit inside that conditional block.

- Remove a broad set of unused locals and dead scaffolding from Run
  and its contained procedures: leftover MKL/mt19937 RNG types and
  their #ifdef HAVE_MKL branch, obsolete AERO/RRTMGP block-processing
  variables, RATNAMES, and a redundant "TOTAL" timer stop with no
  matching start.

- Irrad_StateSpecs.rc: widen the ALIAS column for readability (no
  functional change).

- Update mapl3-porting-notes.md (this dir and GEOSirrad_GridComp/)
  with this session's investigation and decisions.
- AERO import: 'AERO' is a nested State (aerosol-optics-provider
  bundle), not a plain Field, so it can't be expressed via
  Irrad_StateSpecs.rc (ITEMTYPE column only supports 'F'/'V'). Add it
  manually and unconditionally in SetServices via
  MAPL_GridCompAddSpec(itemtype=mapl_STATEITEM_STATE), and drop the
  row from the .rc file with a note explaining why. Previously the
  Field-typed spec left AERO unconnected, so ESMF_StateGet(import,
  'AERO', ...) in Run/LW_Driver failed at runtime.

- implements_aerosol_optics: temporarily hardcode to .false. instead
  of reading it via ESMF_InfoGet on the AERO state's Info, since the
  aerosol provider isn't populating that key in this configuration
  yet and the read was failing. Marked TODO pending a real fix.

- TAUDIAG (LW_Driver): guard every read of TAUDIAG (used for TAUIR/
  CLDTMP/CLDPRS diagnostics) behind USE_CHOU. TAUDIAG is a local,
  uninitialized array only filled by the IRRAD() call in the Chou
  branch; under RRTMG/RRTMGP (the current hardcoded default) it was
  read anyway, causing an FPE (floating invalid) on garbage stack
  values whenever any of those exports were requested.

- irrad_lw_alarm interval: irrad_dt was read via
  MAPL_GridCompGetResource(gc, "IRRAD_DT", irrad_dt, _RC) with no
  default, so on configs without an explicit IRRAD_DT key it stayed
  uninitialized. That garbage became the alarm's ringInterval, which
  made ESMF_ClockAdvance hang on IRRAD's clock during the run loop
  (bisected via temporary debug prints in Cap.F90 and
  run_clock_advance.F90, since removed). Fix: get the real heartbeat
  straight from the clock via MAPL_ClockGet(clock, dt=run_dt, _RC),
  and use that (rounded) as the default for the IRRAD_DT override.

- RRTMGP_cmake/CMakeLists.txt: link MKL_LIBRARIES into the RRTMGP
  target. The mklvsl RNG sources (mo_rng_mklvsl.F90/
  mo_rng_mklvsl_plus.F90, built whenever MKL_FOUND) call vslNewStreamEx,
  but only NetCDF::NetCDF_Fortran was listed as a DEPENDENCIES, so
  the shared lib built fine but failed at runtime with "undefined
  symbol: vslnewstreamex_" the first time that code path ran.
Apply consistent Fortran style: normalize `end if`/`end do` keyword
forms, spacing around operators and commas, relational operators
(.eq./.ne./.gt. -> ==//=/>), array constructors ((/.../) -> [...]),
and lowercase local variable/loop-index naming. No functional changes.
CTest-based standalone regression test for IRRAD, driven by a
synthetic DataIRRAD import component, following the same
esma_add_regression_tests pattern used by GWD and GOCART2G.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Wrap remaining overlong lines to the file's 5-space continuation
convention, fix TEST_ macro call indentation, lowercase MIN/MAX
intrinsics, update stale AGCM.rc comments to irrad.yaml, and move
the CO2_3d vmr-override call next to its sibling gas_concs%set_vmr
calls for readability.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Read implements_aerosol_optics_method from the provider's Info
instead of hardcoding .false. Add a minimal FakeGOCART test double
(itemtype=STATE AERO export, implements_aerosol_optics_method=false)
and wire it into irrad-sa so IRRAD's AERO import is actually satisfied.
The RATS_DIAGNOSTICS toggle list was parsed lazily on Run()'s first
call via a save'd  flag, a workaround from when this component
had no Initialize() method. Now that it does, parse it there once and
stash nRATS/nameRATS in the existing RRTMGP private state for Run() to
read on every call.
Radiation_StateSpecs.rc: drop the ALIAS and COND columns, empty in
every row.

Irrad_StateSpecs.rc: re-align columns to their actual content width
and drop the redundant duplicate "VARIABLE | DIMENSION | Additional
Metadata" comment line above each table header. No schema/data
changes.
parse_child in MAPL's ComponentSpecParser previously matched a
truncated 'SetService' key due to a string-length bug (fixed in
GEOS-ESM/MAPL#5307). These regression yamls were written to match
that truncated key and need the trailing 's' restored now that the
parser correctly requires the full 'setservices' key.
Replace the inline RADIATIVELY_ACTIVE_AEROSOLS block in LW_Driver with a
call to the new compute_provider_aerosol_optics helper, and consolidate
all aerosol-optics setup (SORAD band offset, TAUA/SSAA/ASYA allocation,
and the provider call) into a single AEROSOLS-timed block that now runs
right after the RATS-toggle setup, ahead of the unrelated cloud/emissivity
bookkeeping it used to be interleaved with.
nameRATS/nRATS/TMP_R and the CLDTTLW/CLDHILW/CLDMDLW/CLDLOLW export
pointers are only read inside the USE_RRTMG branch; the EG/FS/TG/TV/EV/RV
emissivity and vegetation hardwiring is only read by the USE_CHOU branch's
IRRAD() call; and PRS_LOW_MID/PRS_MID_HIGH aren't read until the
LCLDMH/LCLDLM calc. All were being computed unconditionally before the
SCHEME dispatch regardless of which radiation scheme was actually
selected, or earlier than needed. Move each next to where it's used.

CO2_FIXED, CWC/REFF, and LCLDMH/LCLDLM stay in the shared setup since
they're read by more than one of the CHOU/RRTMGP/RRTMG branches, so
relocating them would mean duplicating the setup rather than moving it.

This is in the same spirit as the earlier (reverted) work that extracted
the RRTMGP and RRTMG branches into their own Run_RRTMGP_LW/Run_RRTMG_LW
routines: that attempt aimed for full isolation via extraction into
separate subroutines, which turned out to be a bigger and riskier
refactor than wanted. This keeps everything inline in LW_Driver and
just relocates each SCHEME-specific setup step next to its own branch,
getting the same "setup close to where it's needed" locality without
the extraction risk.
RH's computation moved next to its only dependency, PL, and the whole
AEROSOLS block (PL/RH setup, SORAD band offset, TAUA/SSAA/ASYA
allocation, and the compute_provider_aerosol_optics call) moved to right
after the MISC timer starts, ahead of the FCLD copy and CO2_FIXED
handling that used to precede it.

Neither block depends on the other in either direction, so this is a
pure reorder: the AEROSOLS block only needs PLE/Q/T/IM/JM/LM/NB_IRRAD/
USE_RRTMGP_SORAD/USE_RRTMG_SORAD/import, none of which the FCLD/CO2_FIXED
setup touches, and nothing downstream reads FCLD or CO2_FIXED before the
SCHEME dispatch either.
LW_Driver has two separate blocks both timed as "MISC": the pre-SCHEME
setup block (aerosol optics prep, FCLD/CO2_FIXED handling, CWC/REFF,
LCLDMH/LCLDLM) and a separate post-SCHEME block (export pointers,
TSREFF/SFCEM0/DSFDTS0/LWS0, aerosol array deallocation). Since MAPL
timers accumulate by name, these were being lumped into one bucket in
profiling output despite running at different points and doing
different things. Rename only the first (setup) one to "LW-Setup" to
tell them apart; the post-processing block stays "MISC".
…egression

- Add MAPL2_UNDEF (1.0e15) and use it in place of MAPL_UNDEF throughout
  GEOS_IrradGridComp.F90. MAPL3's MAPL_UNDEF constant differs from
  MAPL2's original undef sentinel, so every undef comparison/fill
  (REFF defaults, CLDTMP/CLDPRS, OLCC5/LCSC5, the RRTMG-branch
  no-aerosol fallback fills, OLRA/OLA, LWSA/LAS, FLNSNA/FLNSA, and the
  OLRB band pointer fallback) was silently wrong.

- Call set_inhomogeneity() from Initialize(), borrowed from
  GEOSradiation_GridCompMod::Initialize(). Without it,
  cloud_condensate_inhomogeneity's module-level state was never set,
  which drove a cloud_props_gpt%tau divergence in the McICA
  sub-gridscale condensate scaling step (zcw_lookup). TODO: this
  belongs to IRRAD's parent GridComp in production; revisit once the
  irrad-sa harness has a proper composite parent to own it.

- irrad-sa: add IRRAD_DT to config, switch cmpchk.py to abs() diffs
  with masked-fill handling, and keep expdir around after a run
  instead of deleting it, for easier debugging.

Together with additional exports in the MAPL2 baseline's History.rc
(external to this repo), this achieves zero-diff for the irrad-sa
regression test against the MAPL2 baseline.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@pchakraborty
pchakraborty requested review from a team as code owners August 13, 2026 14:35
@github-actions

Copy link
Copy Markdown

Label error. Requires at least 1 of: 0 diff, 0 diff trivial, Non 0-diff, 0 diff structural, 0-diff trivial, Not 0-diff, 0-diff, automatic, 0-diff uncoupled. Found:

@pchakraborty pchakraborty added 0 diff The changes in this pull request have verified to be zero-diff with the target branch. mapl3-migration Migrate a GridComp to MAPL3 APIs (late phase) labels Aug 13, 2026
Point the GEOSgcm fixture checkout at release/MAPL-v3 instead of
develop, since this branch targets the MAPL3 port and needs to build
against MAPL3-compatible GEOSgcm sources. Turn off mepodevelop and
persist_workspace (not needed without the GCM jobs), and comment out
the AMIP and coupled GCM run jobs for now - the MAPL3 port isn't
expected to run a full GCM yet, so CI stays scoped to the build and
component-level regression tests (e.g. irrad-sa) instead of burning
CI minutes on GCM runs that aren't ready to pass.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@pchakraborty pchakraborty changed the title Port GEOSradiation_GridComp (IRRAD) to MAPL3 Port IRRAD GridComp to MAPL3 Aug 13, 2026
Mirrors the .circleci fixture change: pin the GEOSgcm fixture checkout
to release/MAPL-v3 and turn off run-mepo-develop for both the
standard and spack builds, since this branch needs to build against
MAPL3-compatible GEOSgcm sources rather than develop.

Also renames spack_build to spack_build_gcm, adds patch-esmf, and
skips the spack install step (weird install issues on spack builds
for now).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@pchakraborty
pchakraborty merged commit 0b1e189 into release/MAPL-v3 Aug 13, 2026
13 of 14 checks passed
@pchakraborty
pchakraborty deleted the feature/pchakrab/port-to-mapl3 branch August 13, 2026 15:37
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

0 diff The changes in this pull request have verified to be zero-diff with the target branch. mapl3-migration Migrate a GridComp to MAPL3 APIs (late phase)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant