Skip to content

Fix the IBSEB build break and legacy surface-layer input detection - #4025

Merged
asalmgren merged 2 commits into
erf-model:developmentfrom
asalmgren:fix_surface_layer_legacy_prefix_detection
Sep 15, 2026
Merged

asalmgren merged 2 commits into
erf-model:developmentfrom
asalmgren:fix_surface_layer_legacy_prefix_detection

Conversation

@asalmgren

Copy link
Copy Markdown
Collaborator

Two fixes on top of the #3860 merge.

1. development did not compile at HEAD

#3860 changed ERF::m_SurfaceLayer from a single unique_ptr<SurfaceLayer> to one
entry per domain face. #3960 added ibseb_advance, which reads the surface layer for
the wall function beyond neutral. The two merged independently, git merged them
cleanly, and the result does not build — ERF_IBSEB.cpp:143-146 still dereferenced
m_SurfaceLayer as a pointer.

Indexed to zlo: what the wall function needs is the ground beneath the buildings, and
that is the face the other zlo-only consumers already select (ERF.cpp:876).

2. Legacy surface-layer input detection was reading the wrong thing

With more than one surface-layer face, ERF::InitData_post decides whether to keep
reading the historical unqualified erf.most.* / erf.surface_layer.* keys for zlo by
asking whether any such key is present. It asked the live ParmParse table, which cannot
answer that question: ERF_InputSoundingData.H:150,165 queries most.surf_temp and
most.surf_moist with queryAdd while reading the sounding, and queryAdd inserts
the default it was handed when the key is absent. By the time InitData_post runs, both
keys sit in the table holding their negative sentinels whether or not the user typed them.

So every init_type = input_sounding run with more than one surface-layer face looked as
though it carried legacy inputs:

  • a deck that also used the face-qualified spelling hit the "both spellings present"
    abort and could not start — including Tests/test_files/ABL_MOST_Cloudchamber, the
    regression case added by Extend SurfaceLayer for all boundary sides #3860;
  • a deck without it silently took the legacy path.

The fix snapshots the inputs table at the top of ReadParameters() — the last point at
which presence in the table still means the user wrote it — and the detector scans the
snapshot. This is prefix-agnostic, so it stays correct if another erf.most.* queryAdd
appears elsewhere later, which a fix targeting just the two known keys would not.

Verification

ABL_MOST_Cloudchamber is its own regression test. Run locally with the anelastic solve
skipped (this build has no FFT support, so the unmodified deck dies at
project_initial_velocity before reaching the surface-layer code):

Case Result
Cloudchamber deck, pre-fix binary aborts: "Both legacy unqualified and zlo-qualified surface-layer inputs are present"
Same deck, fixed binary initializes cleanly, no warning, no abort
Legacy unqualified erf.most.* + a second face warning, run continues
Both spellings present aborts with the intended message

erf_unit_tests: 585 passed, 3 skipped, 0 failed.

Docs

Docs/sphinx_doc/SurfaceLayer.rst now states the prefixing rule — unqualified for a lone
zlo face, face-qualified once a second face is declared, unqualified applied to zlo with a
warning, both spellings an error — and lists what actually aborts away from zlo: the
bulk_coeff / custom / rico flux types, prescribed surface heat flux, the adiabatic
case, variable sea roughness, and most.pblh_calc / most.include_wstar. The page
previously covered this only as "not all existing options are supported".

Note, not fixed here

In the legacy-warning case, erf.most.surf_temp / surf_moist are also read by the
input-sounding consistency check, which compares with exact floating-point equality — a
sounding line of 12.13 g/kg will not match most.surf_moist = 0.01213. Pre-existing and
orthogonal, but it makes the legacy path harder to exercise than it looks.

🤖 Generated with Claude Code

asalmgren and others added 2 commits September 15, 2026 14:15
…IBSEB

PR erf-model#3860 turned ERF::m_SurfaceLayer from a single unique_ptr into one entry
per domain face, and PR erf-model#3960 added ibseb_advance, which reads the surface
layer for the wall function beyond neutral. The two merged independently and
git merged them cleanly, so development did not compile: ERF_IBSEB.cpp still
dereferenced m_SurfaceLayer as a pointer.

Index zlo. What the wall function needs is the ground beneath the buildings;
a surface layer on a lateral or upper wall says nothing about the stability
of the column, and this is the same face that the other zlo-only consumers
select (see ERF.cpp:876).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…live table

With more than one surface-layer face, ERF decides whether to keep reading the
historical unqualified erf.most.* / erf.surface_layer.* keys for zlo by asking
whether any such key exists. It asked ParmParse directly, which cannot answer
the question: ERF_InputSoundingData.H queries most.surf_temp and most.surf_moist
with queryAdd while reading the sounding, and queryAdd inserts the default it
was handed whenever the key is absent. By the time InitData_post runs, both keys
are in the table holding their negative sentinels whether or not the user ever
typed them.

Every input_sounding run with more than one surface-layer face therefore looked
as though it carried legacy inputs. A deck that also wrote the face-qualified
spelling -- Tests/test_files/ABL_MOST_Cloudchamber, for one -- hit the "both
spellings present" abort and could not start at all; a deck without it silently
took the legacy path.

Snapshot the inputs table at the top of ReadParameters, which is the last point
at which presence in the table still means the user wrote it, and scan the
snapshot. This stays correct if another erf.most.* queryAdd is added elsewhere
later, which a fix targeting the two known keys would not.

ABL_MOST_Cloudchamber is the regression test: it aborts before this change and
initializes cleanly after it.

Also document the prefixing rule in SurfaceLayer.rst -- unqualified for a lone
zlo face, face-qualified once a second face is declared, unqualified applied to
zlo with a warning, both spellings an error -- along with the configurations
that now abort away from zlo, which the page previously covered only as "not
all existing options are supported".

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant