Skip to content

fix(biogeophys): Correct diagnostic array initialization for exact restarts - #4172

Merged
slevis-lmwg merged 6 commits into
ESCOMP:b4b-devfrom
johnpaulalex:fix/4124-3661-correct-restart-inits
Oct 23, 2026
Merged

slevis-lmwg merged 6 commits into
ESCOMP:b4b-devfrom
johnpaulalex:fix/4124-3661-correct-restart-inits

Conversation

@johnpaulalex

@johnpaulalex johnpaulalex commented Aug 12, 2026 •

Copy link
Copy Markdown
Contributor

Description of changes

This change fixes exact restart (ERP) failures when hist_all_fields = .true. by ensuring diagnostic variables across BiogeophysPreFluxCalcsMod, CanopyFluxesMod, FrictionVelocityMod, SoilHydrologyMod, SoilMoistStressMod, SoilStateType, and SoilTemperatureMod are properly initialized to spval each timestep on inactive layers or patches. Without explicit per-timestep initialization, uninitialized array space on inactive points retained memory across timestep boundaries, causing state discrepancies when comparing base vs. restart history output.

Linked issues addressed, if any:

  • Truly resolves ESCOMP/CTSM#4142 (Get all_outputs tests working cleanly)
  • Resolves ESCOMP/CTSM#3661 (Fixes CTSM diagnostic array resetting on deactivated patches during transient landuse)
  • Pre-requisite for ESCOMP/CTSM#3660 (Fixes CTSM diagnostic array portion of FatesColdAllVars restart failure)

Dependencies / Blockers:

Description of generative AI usage:

  • Google Antigravity was used to write the code and tests, followed by human-guided verification.

Answer Changes & Scientific Impact

  • Bit-for-Bit (B4B) with baseline master

User Interface & Namelist Changes

  • Namelist / Defaults modified? No
  • XML / Build script changes? No

Testing planned or performed, if any:

  • Executed pFUnit / CIME regression tests

CTSM / CESM baseline hash-tag: 7409d32
PR branch hash-tag: 3a0dc8a

Requirements before merge:

  • I have followed the CTSM contribution guidelines.
  • The code in this PR branch builds with no errors.
  • The code in this PR branch runs with no errors. Briefly describe tested configuration(s): macOS gfortran pFUnit / CIME ERP regression tests
  • This either (a) does not change answers, (b) it only changes answers at roundoff level, or (c) I have performed a scientific evaluation of the answer changes. Which?: (a) Bit-for-bit (B4B) with baseline master
  • I have reviewed relevant parts of the CLM documentation Tech Note or User's Guide to determine if anything needs to be changed or added.
  • This PR either (a) does not create a need to update the documentation or (b) includes required documentation updates. Which?: (a) Internal fix/test; no documentation updates required.

@johnpaulalex
johnpaulalex force-pushed the fix/4124-3661-correct-restart-inits branch 2 times, most recently from b149868 to f6919a7 Compare August 12, 2026 22:02
@slevis-lmwg slevis-lmwg added the blocker another issue/PR depends on this one label Aug 13, 2026
@slevis-lmwg

Copy link
Copy Markdown
Contributor

@johnpaulalex I think this PR blocks #4171, so I added the corresponding label. Pls correct me if I'm wrong.

@johnpaulalex johnpaulalex added closed: wontfix We won't fix this issue, because it would be too difficult and/or isn't important enough to fix and removed closed: wontfix We won't fix this issue, because it would be too difficult and/or isn't important enough to fix labels Aug 14, 2026
@johnpaulalex

Copy link
Copy Markdown
Contributor Author

Thanks @slevis-lmwg ! True, but also note that this issue itself depends on NGEET/fates#1596. Should I mark 1596 as a blocker? And then does 1596 get submitted, then cesm is pointed to the updated FATES version, then this PR can be submitted, and finally 4171?

@slevis-lmwg

Copy link
Copy Markdown
Contributor

That sounds correct.

@ekluzek ekluzek left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Interesting, this allows us to not have to add the diagnostic variables to restarts which is good. I'm also impressed that the AI figured that out.

It has the cost of resetting diagnostic variables to spval each time step. I doubt it has to be redone every time step though. The pattern of where the spval need to be shouldn't change for most of these variables. Only if it's a transient simulation in some cases, or if they are bound to something like whether snow is on the ground or not. And I don't think either applies here. At least in general there are a couple cases that I spell out in comments where it does need to be each timestep. Others I think can probably be done only once.

I suggest maybe changing the name of TimeStepInit to SetValue with spval passed in (matches biogeochem code). And I think spinning off some of these things into tiny subroutines might be appropriate.

So marking this with requesting changes, but also happy to discuss if you disagree.

But, there must be something going on where the pattern can be wrong on restart. From the earlier PR #4142 we can surmise that it's correct when written to the restart file and when read in from restart. But, if not read on restart, the pattern must not be correct and it causes problems. So maybe this just has to be done at initialization?

@johnpaulalex could you try (or did you try) having this done just at initialization and not each timestep? Although that should be taken care of by the normal initial setting of things to spval because they are output to history files. But, maybe it needs to be reset after that point and before used? What if TimeStepInit was only done once? So you added a flag to see if it's already been set and it only allows it to be set the first time?

Comment thread src/biogeophys/SoilTemperatureMod.F90 Outdated
Comment thread src/biogeophys/SoilStateType.F90 Outdated
Comment thread src/biogeophys/SoilMoistStressMod.F90
Comment thread src/biogeophys/SoilHydrologyMod.F90 Outdated
Comment thread src/biogeophys/FrictionVelocityMod.F90 Outdated
Comment thread src/biogeophys/CanopyFluxesMod.F90 Outdated
Comment thread src/biogeophys/BiogeophysPreFluxCalcsMod.F90 Outdated
Comment thread src/biogeochem/VOCEmissionMod.F90 Outdated
Comment thread src/biogeochem/NutrientCompetitionFlexibleCNMod.F90
@ekluzek
ekluzek changed the base branch from master to b4b-dev August 15, 2026 20:41
@ekluzek

ekluzek commented Aug 15, 2026

Copy link
Copy Markdown
Contributor

@johnpaulalex I reset the PR to go to b4b-dev as we talked about. It looks like you don't need to rebase the branch, but you should at least pull in the latest b4b-dev. And b4b-dev and master is going to be reconciled early next week, so you could wait for that.

@ekluzek ekluzek added this to the ctsm6.0.0 (code freeze) milestone Aug 15, 2026
@johnpaulalex

johnpaulalex commented Aug 18, 2026 •

Copy link
Copy Markdown
Contributor Author

yeah good point @ekluzek . ~3 vars truly needed to be reset every time step. The rest are now done at init time. And for the transisent case, that could apparently happen for all those other vars? See the second new commit for that, ResetDeactivatedDiagnostics in dynPatchStateUpdaterMod.F90. I also switched the name to SetValue and added helper subroutines.

@johnpaulalex
johnpaulalex marked this pull request as ready for review August 18, 2026 13:35
@johnpaulalex

Copy link
Copy Markdown
Contributor Author

That sounds correct.

Ah I can't set labels on the FATES repo. @ekluzek fyi, not sure if I should have permissions or ask you to do it.

@johnpaulalex

Copy link
Copy Markdown
Contributor Author

@johnpaulalex I reset the PR to go to b4b-dev as we talked about. It looks like you don't need to rebase the branch, but you should at least pull in the latest b4b-dev. And b4b-dev and master is going to be reconciled early next week, so you could wait for that.

ack, I just rebased to b4b-dev @ekluzek

@johnpaulalex

johnpaulalex commented Aug 18, 2026 •

Copy link
Copy Markdown
Contributor Author

@slevis-lmwg @ekluzek @glemieux I have updated description of CTSM PRs 4171, 4172, and FATES 1596 to establish their relationship to fixing CTSM issues 3660, 3661, and 4142.

TL;DR the PRs need to be submitted in 1596->4172->4171 order. PR 4172 resolves Issues 3661 and 4142, and PR 4171 resolves issue 3660.

Comment thread src/biogeophys/SoilStateType.F90
Comment thread src/biogeophys/SoilStateType.F90

@slevis-lmwg slevis-lmwg left a comment •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My initial testing

  1. Uncomment the fields that I commented out in #4136 for the all_outputs test to pass:
    ./create_test ERP_P64x2_D_Ld3.f10_f10_mt232.IHistClm60BgcCropCrujra.derecho_gnu.clm-default--clm-all_outputs -c /glade/campaign/cgd/tss/ctsm_baselines/ctsm5.4.058: FAIL
  2. Revert to the commented out code (git stash): FAIL
  3. Update branch with the latest b4b-dev: FAIL
  4. ctsm5.4.058-39-g703c29e42 which was the lastest b4b-dev at the time: PASS
  5. Once the above cases work, run with fates code that includes NGEET/fates#1596: WAIT

Comment thread src/biogeophys/SoilStateType.F90
@johnpaulalex
johnpaulalex force-pushed the fix/4124-3661-correct-restart-inits branch 5 times, most recently from 39ae88f to af199ce Compare September 30, 2026 09:39
@johnpaulalex
johnpaulalex force-pushed the fix/4124-3661-correct-restart-inits branch from af199ce to 39ae88f Compare September 30, 2026 19:57
@johnpaulalex

johnpaulalex commented Sep 30, 2026 •

Copy link
Copy Markdown
Contributor Author

@slevis-lmwg ok rereading my AI chat history, I thought the system test passed, but it didn't. I now have it passing, and have two decisions to make:

  • How to push the new code to github. I'm inclined to do number 2 but WDYT:
    1. Add 2 more commits to the existing 10, including reversion of some code from 5 of the earlier commits. This seems really hard to review.
    2. force-push to overwrite everything with 4 new, consolidated commits - the first 3 each fix a particular issue, and the last one uncomments the fields.
    3. Make a new PR with the 4 squashed commits, and close this PR (but it's there for reference)
  • How to fix PREV_DAYL. This diagnostic variable is actually fine in the current system test, but causes a diff if you restart exactly at the Jan 1 boundary. (I happened to be running a more limited system test with the decStart testmod, to trigger transient land use changes with a really short # of simulated days, and stumbled across this). To properly fix it means changing results slightly, but not in a way that the system tests can see. It changes results if:
    • the run segment begins at Jan 1 00:00 (startup, restart, branch or hybrid), AND
    • orbital mode is variable_year (transient compsets like IHist; fixed_year and fixed_parameters runs are untouched because the year does not enter), AND
    • PREV_DAYL is on a history tape for the effect to be visible at all, which in the aux_clm suite means only the all_outputs test
    • ....so is this still ok for b4b-dev? Should I split it into its own PR?

More on PREV_DAYL per Claude: PREV_DAYL is the previous time step's daylength, used by phenology and not stored on the restart file, so at startup CTSM rebuilds it from the calendar one step back using the orbital parameters of the current year. When a run restarts exactly at Jan 1 00:00 in variable-year orbital mode, that reconstruction uses the new year's obliquity and eccentricity while the continuous run computed the same value with the old year's, giving about a 0.1-second mismatch on that one step and a failed exact-restart comparison.

@slevis-lmwg slevis-lmwg moved this from Stalled to In Progress in LMWG: Sprint Planning Board Oct 12, 2026
John Alex and others added 5 commits October 21, 2026 11:34
… at year boundary

PREV_DAYL is the previous timestep's daylength, reconstructed at startup
from calendar declination. When restarting exactly at Jan 1 00:00 in
variable-year orbital mode (e.g. IHist compsets), the restart run
computed the previous step's declination using the new year's orbital
parameters, while the continuous run used the old year's parameters,
causing a small discrepancy on the first timestep.

Store previous time step orbital parameters in clm_varorb module variables
to compute caldaym1 declination consistently on restart.
… on restart

Snow thermal conductivity diagnostics (SNO_TK, bw, thk) are computed
in SoilThermProp before new snow layers are created or divided later in
the timestep, causing uninitialized or stale layer entries to leak into
history output.

Add InitSnowThermPropDiagnostics to initialize bw and thk to spval across
snow layers, passing clump sections explicitly to prevent corruption in
multi-clump threaded runs.
…xact restart

Exact-restart divergence occurs when history fields persist state across
timesteps on patches or conditions that stop being updated, because these
fields are not stored in restart files:

1. Patches leaving the exposed-vegetation filter in CanopyFluxes stopped
   having their diagnostics updated, retaining stale memory in continuous runs
   while being initialized to spval on restart. Add CanopyFluxesResetDiagnostics
   to reset these fields when frac_veg_nosno transitions from 1 to 0.
2. Restrict FrictionVelocity%SetValues to reset only the 10 diagnostic arrays
   calculated exclusively within CanopyFluxes (rah1, rah2, raw1, raw2, ustar,
   um, uaf, taf, qaf, vpd), deliberately preserving prognostic restart
   variables (obu) and general boundary-layer fields computed across all
   patch types.
3. Add SoilStateType%SetValues to reset root and soil conductance diagnostics
   (KROOT, KSOIL) when patches leave the exposed-vegetation filter.
4. Set RRESIS (root resistance) to 0._r8 in SoilMoistStressMod when soil layers
   are frozen or dry; previously frozen layers retained pre-freeze values,
   causing divergence against winter restarts.
Exact-restart divergence occurs when diagnostic history fields retain stale
values across timesteps because they are not stored in restart files:

1. In VOCEmissionMod, reset MEGAN activity factor diagnostics (gamma, gammaP,
   gammaT, alpha, topt, etc.) to spval via an explicit else branch when
   gamma is out of range (imeg==1). Previously, out-of-range patches retained
   the prior timestep's values, whereas restarted runs initialized them to spval.
2. In NutrientCompetitionFlexibleCNMod, set actual_storage_leafcn to spval
   when flexible CN storage is inactive or non-positive (leafc_storage_patch <= 0
   or leafn_storage_patch <= 0). Previously, zero-storage patches retained
   the last non-zero ratio indefinitely.
… disabled by ESCOMP#3661

Uncomment all 35 history fields across VOCEmissionMod, FrictionVelocityMod,
SoilStateType, WaterDiagnosticBulkType, EnergyFluxType, and
NutrientCompetitionFlexibleCNMod that were temporarily disabled in ESCOMP#3661
pending exact restart resolution.
@johnpaulalex

johnpaulalex commented Oct 21, 2026 •

Copy link
Copy Markdown
Contributor Author

@slevis-lmwg Here's the diff against squashed clean commits, I think it's much easier to review than the original commits + deltas. I reran against derecho and it passed[1]

Sorry again for not having my ducks in a row in the initial submit.

[1] Path: /glade/derecho/scratch/jpalex/tests_preview_4172/ERP_P64x2_D_Ld3.f10_f10_mt232.IHistClm60BgcCropCrujra.derecho_gnu.clm-default--clm-all_outputs.C.20261021_103322_t07a5e
/glade/derecho/scratch/jpalex/ERP_P64x2_D_Ld3.f10_f10_mt232.IHistClm60BgcCropCrujra.derecho_gnu.clm-default--clm-all_outputs.t4124_prev

@slevis-lmwg

slevis-lmwg commented Oct 23, 2026 •

Copy link
Copy Markdown
Contributor

I'm testing in the same order that I tested before (note that the testname has the grid updated to t233):
./create_test ERP_P64x2_D_Ld3.f10_f10_mt233.IHistClm60BgcCropCrujra.derecho_gnu.clm-default--clm-all_outputs -c /glade/campaign/cgd/tss/ctsm_baselines/ctsm5.4.064

  1. FAIL ERP_P64x2_D_Ld3.f10_f10_mt233.IHistClm60BgcCropCrujra.derecho_gnu.clm-default--clm-all_outputs RUN
  2. Same as (3) since I have already updated the branch to the latest b4b-dev
    FAIL ERP_P64x2_D_Ld3.f10_f10_mt233.IHistClm60BgcCropCrujra.derecho_gnu.clm-default--clm-all_outputs RUN
  3. Same as (2)
  4. ctsm5.4.064-5-g31cf37e4f is the latest b4b-dev: PASS

I'm afraid this does not work for me, yet. I'm working here in case you want to see what's different between mine and yours:
/glade/work/slevis/git_people/johnpaulalex/4124-3661-correct-restart-inits

Also, I could not find your test that you said had passed.

@slevis-lmwg slevis-lmwg moved this from In Progress to Stalled in LMWG: Sprint Planning Board Oct 23, 2026
@johnpaulalex
johnpaulalex force-pushed the fix/4124-3661-correct-restart-inits branch from bac78c6 to 12a51bb Compare October 23, 2026 11:03
@johnpaulalex

Copy link
Copy Markdown
Contributor Author

Hi @slevis-lmwg, so sorry, miscommunication. I had created the other branch for easier review (6 commits), but I wasn't sure if you wanted me to stack the equivalent delta on top of this PR's branch, so I hadn't touched it.  So no surprise this PR's branch failed again.

Now: I've replaced this PR's branch with those 6 commits, including a b4b-dev sync and uncommenting all the history fields. It passed the newer mt233 test [2].   

I also fixed the system path in my prior comment (which was recording a passing run , on the older mt232, against the 6-commit branch minus the b4b-dev sync) 

If for whatever reason you wanted to look at the original 12 commits for this PR (plus a newly added commit to make them exactly match the 6-commit branch), they are stowed in my backup/4124-3661-correct-restart-inits branch.

John

[2] /glade/derecho/scratch/jpalex/ERP_P64x2_D_Ld3.f10_f10_mt233.IHistClm60BgcCropCrujra.derecho_gnu.clm-default--clm-all_outputs.t4124_mt233/

@slevis-lmwg

slevis-lmwg commented Oct 23, 2026 •

Copy link
Copy Markdown
Contributor

@johnpaulalex thanks and sorry for not catching on to your earlier comment that I needed to test on the alternate branch. In any case, now the test passed, as you expected :-)

Next:

@slevis-lmwg

Copy link
Copy Markdown
Contributor

I have now also looked at the PREV_DAYL updates that you discussed here. If aux_clm passes, I will consider these updates good for b4b-dev.

@slevis-lmwg

Copy link
Copy Markdown
Contributor

I answered my own question about the "missing" test from cime_config/testdefs/ExpectedTestFails.xml:
I removed it myself in #4136 :-))

@slevis-lmwg slevis-lmwg moved this from Stalled to In Progress in LMWG: Sprint Planning Board Oct 23, 2026
@johnpaulalex

Copy link
Copy Markdown
Contributor Author

Sounds great! Thanks for your patience @slevis-lmwg .

To be sure we're on the same page, this is my understanding:

  1. This PR makes an already-passing system test (...IHistClm60BgcCropCrujra...) pass even better :)
  2. Once CTSM can see fix(restart): read, write, and register fates_c13disc_acc in FatesRestartInterfaceMod NGEET/fates#1596, then (combined with this PR), it should make FatesColdAllVars test pass within aux_clm.
  3. So next up is test(ci): Re-enable FatesColdAllVars exact restart tests #4171 (Note currently 4171 is marked as a draft, to prevent early submission, but I think it's ready after this PR)

In general should I be running izumi system tests in parallel with derecho? (same aux_clm suite if so?) Or is that something the approver is going to do anyway?

@slevis-lmwg
slevis-lmwg dismissed ekluzek’s stale review October 23, 2026 20:38

Erik's review was addressed and the comments marked Resolved.
I consider this ready to merge.

@slevis-lmwg

Copy link
Copy Markdown
Contributor

In general should I be running izumi system tests in parallel with derecho? (same aux_clm suite if so?) Or is that something the approver is going to do anyway?

If you have access to izumi, then you could run aux_clm on izumi at the same time as on derecho. Once in a while izumi will catch something that derecho did not. The PR merger runs those tests unless the PR author runs them and indicates that things worked.

@slevis-lmwg
slevis-lmwg merged commit 198a423 into ESCOMP:b4b-dev Oct 23, 2026
4 checks passed
@github-project-automation github-project-automation Bot moved this from In Progress to Done in LMWG: Sprint Planning Board Oct 23, 2026
@github-project-automation github-project-automation Bot moved this from In progress - b4b-dev to Done (non release/external) in CTSM: Upcoming tags Oct 23, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

blocked: dependency Wait to work on this until dependency is resolved blocker another issue/PR depends on this one

Projects

Status: Done (non release/external)
Status: Done

Development

Successfully merging this pull request may close these issues.

FAIL ERP_P64x2_D_Ld3...clm-default--clm-all_outputs COMPARE_base_rest

5 participants