Skip to content

Fix uninitialized ilnb read on the thin-snow path in the RUC LSM - #2369

Open
FahrenheitResearch wants to merge 1 commit into
wrf-model:release-v4.8.1from
FahrenheitResearch:ruclsm_thin_snow_ilnb_fix
Open

Fix uninitialized ilnb read on the thin-snow path in the RUC LSM#2369
FahrenheitResearch wants to merge 1 commit into
wrf-model:release-v4.8.1from
FahrenheitResearch:ruclsm_thin_snow_ilnb_fix

Conversation

@FahrenheitResearch

Copy link
Copy Markdown

Initialize the RUC LSM snow-layer count (ilnb) so the thin-snow path no longer reads an undefined value into the TSNAV formula selection.

TYPE: bug fix

KEYWORDS: RUC LSM, snow, uninitialized variable, ilnb, TSNAV, thin snow, restart reproducibility

SOURCE: FahrenheitResearch (independent)

DESCRIPTION OF CHANGES:
Problem:
As described in #2368: in SNOWTEMP and SNOWSEAICE, ilnb is assigned only inside if(snhei.ge.snth), but the TSNAV recalculation at the end of each routine (:5716 / :4410) reads it whenever snhei > 0.. On the thin-snow path (0 < snhei < snth) nothing has assigned it — ilnb is intent(out) in SNOWTEMP (undefined on entry under the Fortran standard) and carries stale caller memory in SNOWSEAICE — and when the leftover value is greater than 1 the two-layer formula runs with deltsn > snhei, an extrapolation across a negative layer thickness whose error grows without bound as the pack thins (14 K at 3 cm of snow, 60 K at 1 mm). It also makes the branch selection, and therefore TSNAV in the restart stream, dependent on compiler, optimization level, and the preceding column, so restarted runs need not reproduce continuous ones.

On @weiwangncar's suggestion to set ilnb in LSMRUC before the call to SFCTMP: I tried that form first, but ilnb is a local variable of SFCTMP (declared under !--- local variables at :1385), not an argument passed in from LSMRUC, so there is nothing in LSMRUC to set. And because SNOWTEMP declares it intent(out), an initialization upstream of the call chain would still be formally undefined on entry to SNOWTEMP — it would work with gfortran's by-reference scalar passing, but by the same accident that makes the current code usually work. Setting the default inside each routine that reads it is the smallest change that is well-defined on every path; happy to rework it into the caller-side form plus an intent change if the committee prefers.

Solution:
Default ilnb=1 immediately before the layer-count branch in both SNOWTEMP and SNOWSEAICE. One layer is what the thin-snow block itself assumes (its comment: the snow "is too thin to be treated separately, therefore it is combined with the first soil layer"), and it reproduces exactly the TSNAV that block already computes (:5191 / :4103) before the overwrite. Thick-path behavior is unchanged: both existing assignments (ilnb=1 / ilnb=2) still execute and take precedence.

ISSUE: Fixes #2368

LIST OF MODIFIED FILES:
M phys/module_sf_ruclsm.F

TESTS CONDUCTED:

  1. Yes. A standalone harness compiles the module from this branch exactly as shipped (gfortran 13.3.0, -O0) and drives SFCTMP with a 33-case, 160-column fixture covering land and sea ice, mosaic and non-mosaic, thick, thin, melting, and snow-free packs:
    • Unpatched, driver stack pre-filled 0.0 vs 12345.0: outputs differ in exactly 45 cells, all of them TSNAV, all on thin-pack mosaic cases (one case reports a pack-mean temperature of +20.12 C against the correct -0.875 C) — the uninitialized read firing.
    • Unpatched, -finit-integer=1 vs -finit-integer=2: the same 45 TSNAV cells differ — the reproduction from the issue, confirmed on release-v4.8.0.
    • Patched: both controls go quiet — 0 differing cells under stack pre-fill, 0 under -finit-integer=1/2, so the output no longer depends on what memory or the compiler left in ilnb.
    • Patched vs unpatched with a zeroed stack (where ilnb reads 0 and already selects the one-layer formula): bit-identical across all 33 cases — no output changes on any thick-snow or snow-free path.
  2. Jenkins tests not yet run; this PR should exercise them.

RELEASE NOTE: Fixed an uninitialized read of the snow-layer count in the RUC LSM: on thin snow packs the pack-average snow temperature TSNAV could be computed with the two-layer formula, whose weights are degenerate there, producing errors that grow without bound as the pack thins and making restarts non-reproducible. The layer count now defaults to the single-layer form that the thin-snow path itself computes.

In SNOWTEMP and SNOWSEAICE, ilnb (the snow layer count) is assigned
only inside if(snhei.ge.snth), but the tsnav recalculation at the end
of each routine reads it whenever snhei > 0. On the thin-snow path
(0 < snhei < snth) the read is undefined -- ilnb is intent(out) in
SNOWTEMP and carries stale caller memory in SNOWSEAICE -- and when the
leftover value is greater than 1 the two-layer formula runs with
deltsn > snhei, an extrapolation across a negative layer thickness
whose error grows without bound as the pack thins.

Default ilnb to one layer before the branch in both routines. One
layer is what the thin-snow block itself assumes (the snow 'is
combined with the first soil layer'), and it reproduces exactly the
tsnav that block already computes before the overwrite. Thick-path
behavior is unchanged: both existing assignments still execute.

Fixes wrf-model#2368
@FahrenheitResearch
FahrenheitResearch requested a review from a team as a code owner July 27, 2026 17:22
@weiwangncar
weiwangncar changed the base branch from release-v4.8.0 to develop July 27, 2026 17:30
@weiwangncar

Copy link
Copy Markdown
Collaborator

The regression test results:

Test Type              | Expected  | Received |  Failed
= = = = = = = = = = = = = = = = = = = = = = = =  = = = =
Number of Tests        : 23           24
Number of Builds       : 60           57
Number of Simulations  : 158           150        0
Number of Comparisons  : 95           86        0

Failed Simulations are: 
None
Which comparisons are not bit-for-bit: 
None

@weiwangncar
weiwangncar changed the base branch from develop to release-v4.8.1 July 27, 2026 19:36
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants