Add two-way land-ice coupling to driver-moab - #8661
Conversation
Linking the netcdf interface target (which bundles libpnetcdf.a) directly onto e3sm.exe pinned it ahead of piof/pioc on the static link line, since CMake collapses repeated references to the same target to one position. GNU ld resolves static archives in a single pass, so piof/pioc's calls into PnetCDF (ncmpi_*) went unresolved, failing the link. Only pull netcdf's include dirs onto the cpl target directly, and let its link libraries reach the executable exclusively through csm_share -> spio, where PnetCDF is already ordered after piof/pioc. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Handle MPAS meshes that are not on a sphere (on_a_sphere false or sphere_radius == 0), such as the MALI planar Greenland mesh. Build vertex coordinates from latVertex/lonVertex projected onto the unit sphere, and normalize areaCell by the Earth radius squared so cell areas are in steradians, consistent with every other coupler mesh. Spherical meshes are unaffected. Co-authored-by: Iulian Grindeanu <iulian@mcs.anl.gov> Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Register the MALI mesh with iMOAB on the component PEs (app id mbglid) via init_moab_mpas, define and zero the g2x, x2g, and domain tags, and add glc_domain_moab to set lon/lat/area/mask/frac on the mesh. MALI planar-mesh cell areas are converted to steradians. Refactor glc_import_mct and glc_export_mct to take raw real(:,:) arrays instead of MCT attribute vectors, following the mpas-ocean pattern, so a single copy of the field logic serves both the MCT and MOAB drivers. The MOAB path exchanges data with the tag storage through transposed twin arrays; the MCT path passes x2g%rAttr and g2x%rAttr directly. Declare the GLC iMOAB app ids (mbglid, mbgxid, mbintxlg, mbintxgl) in seq_comm_mct and initialize them to -1. Co-authored-by: Iulian Grindeanu <iulian@mcs.anl.gov> Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Add cplcomp_moab_init_glc to send the MALI mesh from the component PEs and receive it on the coupler PEs as app COUPLE_MPASLNDICE (mbgxid), define the g2x, x2g, and domain tags there, and exchange the domain tags so aream defaults to area until a map file provides it. Dispatch to it from cplcomp_moab_init for the 'g' component. Initialize the glc fractions on the coupler mesh: define gfrac and lfrac tags on mbgxid and set gfrac from the migrated domain frac. Enable the glc domain, fraction, g2x, and x2g writes in the coupler history file, and fix seq_diag_glc_moab to read from mbgxid. After this change the GLC mesh reaches the coupler and appears in cpl history files; no fields flow yet. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Refresh prep_glc_mod from driver-mct (prep_glc_mrg_ocn, the thermal forcing/ice shelf split, l2gacc averaging counters, the lfrin fix) and add the MOAB implementation of the lnd->glc path: accumulate the per-elevation-class land fields in an array accumulator following the prep_rof pattern, average and set them back into the land coupler tags (which also keeps the l2x1yrg auxiliary history working), map all fields in one batched lfrac-normalized seq_map_map, downscale with array-based vertical interpolation between elevation classes, pre-multiply qice by area/aream to preserve conservation through the component-side area correction, and renormalize SMB so the global integral over the ice sheet matches the land integral. Results are written directly into the x2g tag names so the merge is a no-op. Add map_glc2lnd_ec_moab for the glc->lnd direction: because each elevation class needs a different weight field, build pre-multiplied numerator tags (frac_n*icemask, topo*w_n, icemask) on the glc mesh, map them all raw in one call, and divide on the land mesh, filling virtual columns with the class mean elevation where a class has no ice. Wire it into prep_lnd, which registers the GLC_LND_COU map and reads glc2lnd_fmapname through iMOAB. Read the lnd2glc and glc2lnd map files with moab_map_init_rcfile (weight ids scalar_l2g, flux_l2g, flux_g2l), taking aream on the glc coupler mesh from the lnd2glc map area_b as driver-mct does. Replace the inherited seq_map_init_rcfile calls for the glc mappers with seq_map_mapinit, since driver-moab never populates the coupler-side MCT gsmaps those need (they segfaulted in MCT sparse-matrix init). Restart the lnd->glc accumulator, the glc fractions, and g2x through the coupler restart file, and wire the accumulate/average/map/merge, component exchanges, area correction, zeroing, and glc budget call into the driver run loop. Validated on anlgce-ub22 (gnu): ERS.ne30pg2_r05_IcoswISC30E3r5_gis4to40.MALISIA and ERS_Ld5.ne30pg2_r05_IcoswISC30E3r5_gis4to40.IGELM_MLI (with MALI_USE_ALBANY=FALSE) both pass with bit-for-bit restarts; IGELM SMB renormalization factors are 1.006-1.018 and MALI globalStats shows the coupler SMB arriving (avgNetAccumulation ~0.33 m/yr). The ocn->glc thermal forcing and ice shelf paths, glc->ocn/ice runoff mapping, and the x2gacc restart are deferred to a follow-up. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Store the per-elevation-class arrays used by the lnd->glc vertical interpolation in default real precision, as the mct map_ice_covered does, instead of keeping them in r8. The demotion is what mct does today, so reproducing it is what makes the downscaled fields (notably x2g_Sl_tsrf) agree; with the same weights, the iMOAB ApplyScalarProjectionWeights result then matches mct_sMat_avMult exactly. Stop setting the averaged lnd->glc accumulation back into the coupler land mesh tags. Instead, prep_glc_calc_l2x_gx_moab saves the instantaneous l2x values, stages the averages into the tags for the duration of the batched map, and restores them right after, so the coupler history keeps showing instantaneous l2x fields the way the mct driver does. Add an optional matrix argument to seq_hist_writeaux so the l2x1yrg auxiliary file, whose data is the accumulator rather than the tags, can still be written from the array. Do the glc->lnd elevation-class normalization as a reciprocal multiply with a zero denominator passing through as a multiply by zero, matching the mct seq_map_avNormArr idiom, which removes the ~1e-15 differences in the per-class frac and topo fields. Validated on anlgce-ub22 (gnu) by comparing 2-day IGELM_MLI twins with cprnc: of 286 coupler history fields, all are bit for bit except x2g_Flgl_qice (~4e-15 normalized) and the two MALI g2x fields that echo it. That residual is a uniform 1-ulp factor across all 17352 glc cells, i.e. shr_mpi_sum ordering in the SMB renormalization over the different moab and mct decompositions, not a difference in the coupling itself; glc_renormalize_smb='off' gives strict bit-for-bit. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
bishtgautam
left a comment
There was a problem hiding this comment.
@rljacob, This isn't touching anything in ELM for me to review.
| # propagate up to this exe target. Find/link it explicitly here too. | ||
| find_package(NETCDF REQUIRED) | ||
| target_link_libraries(${TARGET_NAME} netcdf) | ||
|
|
|
Reviewers who aren't coupler developers should just check the changes in glc_comp_mct.F90 and maybe test that other cases currently run with driver-mct are not broken (I'll run the land-ice-developer suite on this). Also see the comments from Claude about slight difference in Surface speed between the mct and moab cases. Results are BFB identical between the two cases otherwise. |
matthewhoffman
left a comment
There was a problem hiding this comment.
I inspected the change to glc_comp_mct.F and, other than the one item I was uncertain about, I didn't see anything that concerned me - all of the MOAB functionality seems cleanly gated off.
Two other items:
-
As for the need to support planar meshes, if I understand correctly, that's because MOAB no longer requires pre-built mapping files and needs a mesh object at runtime. How is this handling distortion from map projection? That was a complication that had to be handled carefully when creating mapping files.
-
The slight difference in surfaceSpeed is curious, but, as noted, does not seem to be related to the moab coupling. Still, I would be more comfortable if we knew where this originated in this case. Are the runs being compared run with a different number of processors? Albany is not BFB across processor count. Other than that, I would expect surfaceSpeed to be BFB, so if that isn't the explanation it would be good for use to identify where it's coming from. (cc: @mperego )
I'm approving now but I'm happy to re-review if the above items lead to further changes.
|
|
No they were on the same number of processors. This is what Claude thinks:
We could also try linking with debug builds of Albany and MOAB (debug build of E3SM isn't enough). |
|
Yes, I would not worry about it. I also checked with @bartgol and he agrees. Thanks! |

Port GLC (MALI land ice) coupling to driver-moab so it works the same way as
driver-mct. This first version covers two-way lnd-glc coupling: per-elevation-class surface
mass balance and surface temperature downscaled to the ice sheet, and
per-elevation-class ice cover, topography and ice mask upscaled back to land.
It also adds glc mesh migration to the coupler, fractions, exchange, coupler
history, restart and budgets. Planar MPAS meshes are now supported in the MOAB
mesh instance, which the MALI Greenland mesh requires.
Ocean-glc thermal forcing and ice shelf coupling, glc-to-ocean and glc-to-ice
runoff mapping, and x2gacc restart are deferred to a follow-up pull request.
Their MCT code is left in place and inert.
Validated on chrysalis with gnu and the real Albany first-order velocity
solver, and on anlgce-ub22 with the SIA solver. The moab coupler history
matches driver-mct bit for bit for every field sent to the ice sheet.
[BFB]
What this adds
Two-way
lnd<->glccoupling with elevation classes in driver-moab:iMOAB_SendMesh/ReceiveMesh, building on @iulian787 old branchiulian787/landice_moab_driver. Planar MALI meshes (sphere_radius = 0) getunit-sphere coordinates from
latVertex/lonVertex, and areas are convertedto steradians so they are consistent with every other coupler mesh.
mbglid(MALI on component PEs),mbgxid(glc on coupler,COUPLE_MPASLNDICE), andmbintxlg/mbintxglas read-map holder apps(
LND_GLC_COU/GLC_LND_COU). Weight idsscalar_l2g,flux_l2g,flux_g2l.seq_maps.rcentries driver-mctuses, loaded with
iMOAB_LoadMapFile. Only GLOBAL_IDs (indexToCellID)matter, so existing lnd2glc/glc2lnd map files work unchanged. glc
areamcomes from the lnd2glc fmap
area_b(arearead=2) as in driver-mct, with anaream=areafallback when land is absent.glc_import_mct/glc_export_mctnow take raw
real(:,:)arrays, and the moab path moves data through tags onmbglidwith transpose twins. Newglc_domain_moabsets lat/lon/area/mask/frac.l2gacc_lmfollowing theprep_rof pattern. At the glc coupling step the averages are staged into the
land-mesh tags for the duration of one batched lfrac-weighted normalized map
(instantaneous values are saved and restored around it), then elevation-class
vertical interpolation runs on arrays, followed by the qice
area/areampre-adjustment and global SMB renormalization. Results are written directly
into the x2g tag names, which makes the merge a no-op as it is in mct.
map_glc2lnd_ec_moabstages pre-multiplied numerator tags plus
Sg_icemsk_num, does one raw map,and divides on the land side with reciprocal-multiplies matching mct's
seq_map_avNormArr.Inherited MCT glc mapper inits (
seq_map_init_rcfile) segfault in driver-moabbecause the coupler gsmaps are unpopulated, so they are replaced with
seq_map_mapinitstubs — the same treatment every other ported mapper received.Matching driver-mct bit for bit
Three changes were needed to make the coupler history agree with driver-mct:
default
real, replicating what mct'smap_ice_covereddoes today (thedemotion itself is documented in driver-mct: lnd->glc elevation-class downscaling demotes fields to single precision #8657). This is what makes
x2g_Sl_tsrfagree, and it demonstrates that iMOAB's
ApplyScalarProjectionWeightsmatches
mct_sMat_avMultexactly for the same weights.land mesh tags, so the coupler history keeps showing instantaneous l2x
fields the way driver-mct does.
seq_hist_writeauxgains an optionalmatrixargument so thel2x1yrgauxiliary file, whose data lives in theaccumulator rather than the tags, can still be written.
with a zero denominator passing through as multiplication by zero, matching
mct's
seq_map_avNormArridiom. This removes the ~1e-15 differences in theper-class frac and topo fields.
Validation
chrysalis, gnu + openmpi, Albany first-order solver (
IGELM_MLIuses plainMALI, soMALI_USE_ALBANYdefaults to TRUE):SMS.ne30pg2_r05_IcoswISC30E3r5_gis4to40.IGELM_MLI.mali-landiceIGERS_Ld3sameCOMPARE_base_restSMS_Vmct/ERS_Vmct_Ld3sameSMS_D(debug)Coupler history, moab vs mct, same machine and compiler. With
glc_renormalize_smb='off', everyx2gfield is exactly bit for bit —including
x2g_Flgl_qiceandx2g_Sl_tsrf. That is the substantive result:the mapping delivers bit-identical forcing to the ice sheet.
Seven fields still differ at 1.2e-15 or below, all traceable to a single root
cause outside this change. MALI's own history, written after
li_core_initial_solveand before any coupling, is bit for bit on 15 of 16fields; the exception is
surfaceSpeedat 2.4e-15. So Albany's first velocitysolve returns a marginally different answer in a MOAB-linked executable, and
the residual
g2xandtopo01differences are downstream of that. Twoidentical moab runs are bit for bit across all 286 fields, so this is
reproducible rather than solver noise.
With the default
glc_renormalize_smb='on_if_glc_coupled_fluxes', nine fieldsdiffer instead of seven; the two extra are
x2g_Flgl_qiceandx2g_Sl_tsrf,from the SMB renormalization global sums adding identical terms in different
orders under the moab RCB partition versus the mct gsmap decomposition. This is
the same reason driver-mct is not reproducible across PE layouts for these sums.
histaux_l2x1yrg, a 1-yearIGELM_MLIrun, exercises the newmatrixargument — the only path in this branch not covered by the tests above. It
writes 33 fields (11 elevation classes of
Sl_tsrf,Sl_topo,Flgl_qice),time_bnds = 0, 365, with physical values:Sl_tsrfcapped exactly at 273.15 K,Sl_topomonotonic across classes, andFlgl_qiceexactly zero for the bareland class. Note that
GLC_AVG_PERIOD=yearlyis required for this feature; withthe default the driver aborts at the first averaging step. That is inherited
driver-mct behaviour, not new here, but it is easy to trip over.
anlgce-ub22, gnu, SIA solver:
ERS.ne30pg2_r05_IcoswISC30E3r5_gis4to40.MALISIAand
ERS_Ld5IGELM_MLIboth pass with bit-for-bit restart, and the MALISIAcoupler history is identical to driver-mct across all 35 fields.
Note on a pre-existing MOAB/Trilinos conflict
Running MALI with Albany under driver-moab currently segfaults on chrysalis
because MOAB ships a static Zoltan 3.901 whose symbols preempt the Zoltan 15.1.1
in Trilinos'
libzoltan.sothat Albany was compiled against. This predates thisbranch and is unrelated to it — the crash happens in MALI's initial velocity
solve, before any coupling exchange, and needs only MOAB and Albany in the same
executable. It is reported as #8660 and will be fixed by rebuilding MOAB against
Trilinos' Zoltan. The Albany validation above was done with a local
-Wl,--exclude-libs,libzoltan.aworkaround that is deliberately not includedin this pull request.
Not included
Ocean-glc thermal forcing and ice shelf coupling, glc-to-ocean and glc-to-ice
runoff mapping, and
x2gaccrestart are deferred to a follow-up. The prep_ocnand prep_ice glc mapper inits still need their moab context there. The existing
MCT code for these is left in place and inert, and the tests covering that scope
(
tests.py:154-155,:353) remain pinned toVmct.Bit-for-bit status
Expected to be BFB for driver-mct: the only shared code touched is
glc_comp_mct.F(an import/export refactor to raw arrays, with the moab pathbehind
HAVE_MOAB) andmpas_moabmesh.F(planar mesh support, which only runswhen a MOAB mesh instance is created). The
Vmcttests above all pass. This hasnot yet been confirmed against master baselines, which should be done before
merge.