Skip to content

PRMSCanopy: gate grass rain interception on pkwater_ante (carries #413's ifort retirement) - #414

Merged
jmccreight merged 7 commits into
DOI-USGS:developfrom
jmccreight:feat_canopy_pkwater_ante
Aug 27, 2026
Merged

PRMSCanopy: gate grass rain interception on pkwater_ante (carries #413's ifort retirement)#414
jmccreight merged 7 commits into
DOI-USGS:developfrom
jmccreight:feat_canopy_pkwater_ante

Conversation

@jmccreight

@jmccreight jmccreight commented Aug 26, 2026

Copy link
Copy Markdown
Collaborator

PRMS gates rain interception by grasses on the antecedent snowpack —
intcp.f90:416 tests Pkwater_equiv before snowcomp updates it for the
timestep, and snowcomp.f90:946 publishes exactly that value as
Pkwater_ante. pywatershed instead reconstructed the pack as
pk_ice_prev + freeh2o_prev. PRMS carries Pkwater_equiv as its own
accumulated double rather than recomputing it as that sum, and in the
vanishing tail of a melting pack the two differ by orders of magnitude and
fall on opposite sides of the dnearzero (2.23e-16) threshold — flipping
whether a day's rain is intercepted. This was failing ucb_2yr_nhm on macOS.

PRMSCanopy now takes pkwater_ante as an input in place of pk_ice_prev
and freeh2o_prev, and PRMSSnow declares pkwater_ante (as snowcomp
does, snowcomp.f90:346-349) so coupled models supply it. Code that hands
PRMSCanopy its inputs individually must be updated; models assembled from
process lists or model dictionaries are unaffected.

Supersedes #413, which should be closed unmerged — this branch was cut
from it and carries its ifort-retirement commit verbatim. That work: PRMS
binaries are no longer checked in to bin/; they are compiled from
prms_src/ with gfortran the first time they are needed, by
pywatershed.utils.compile_prms(), which ci.yaml, autotest/ci_local.sh,
and the test-data fixtures all call. The prms_src makelists gained
-std=gnu17, without which PRMS 5.2.1's C sources fail to build under the
C23 default of gcc 15. Apple Silicon binaries are tagged mac_arm rather
than m1, and Intel macOS is no longer detected. GSFLOW binaries stay
checked in because their source is not part of this repository.

Also here:

  • The test suite's NumPy >= 2.5 deprecation warnings are quieted — tens of
    thousands per run. constants.nat is now np.datetime64("NaT", "ns"),
    the environment files require holoviews >=1.23.0, and autotest/pytest.ini
    ignores the ndarray.shape assignment netCDF4 <= 1.7.4 makes on every
    variable write (fixed upstream but unreleased; tracked in MAINTENANCE.md).
  • gfortran<16 in both environment files. Windows CI links against a
    conda-forge mingw sysroot that stopped shipping crt2.o and
    default-manifest.o where the driver looks for them, on 2026-08-20 —
    after the last green Windows compile. develop is unaffected only because
    its micromamba cache key hashes the environment file and so never
    re-solves. The ceiling is a probe, not a diagnosis; MAINTENANCE.md
    carries the timeline, the fallback pin, and a draft report for the
    feedstock.
  • Repo bookkeeping: MAINTENANCE.md gains items for the gfortran ceiling
    and Lint Jupyter notebooks: put *.ipynb in ruff's include #412's two follow-ups; the PR template gets its own checklist section
    and a strike-through convention for items that don't apply.

Checklist

  • Closes #xxxx
  • Tests added — existing test_prms_canopy.py, test_prms_above_snow.py, and test_prms_et_canopy.py cover the change
  • Performance benchmarks added or run
  • User visible changes (including notable bug fixes) are documented in whats-new.rst
  • The (:pull:`XXX`) placeholder in whats-new.rst is replaced with this PR's number
  • New public classes/functions are exported in pywatershed/__init__.py and listed in doc/api/*.rst

Docs

Look for this PR number in our read-the-docs builds where you can browse on-line.

You can alternatively get a CI-build of the docs by entering the PR number for the ### in https://github.com/DOI-USGS/pywatershed/pull/###/checks then clicking on Documentation Build and finally looking for the documentation-html artifact which will download as a zip file.

jmccreight and others added 7 commits August 24, 2026 16:44
The PRMS binaries are no longer checked in to bin/. They are compiled
from prms_src/ with gfortran (supplied by environment.yml) the first
time they are needed, by pywatershed.utils.compile_prms(), which
ci.yaml, autotest/ci_local.sh, and the test-data fixtures all call.
That removes ~16 MB of binaries and the duplicate platform/binary-name
tables that autotest/utils.py and ci_local.sh each carried.

PRMS 5.2.1 did not build with the current conda toolchain. Its
mmf/read_params.c declares

    static char *open_parameter_file();

and then calls it with an argument. Under C23, the default since gcc
15, an empty parameter list means "takes no arguments", so that call is
a hard error; 5.2.1.1 only ever calls it with no argument, which is why
5.2.1 alone failed. The prms_src makelists now pass -std=gnu17 to gcc.
5.2.1 then builds to a native arm64 binary, roughly 4x faster than the
x86_64 one it replaces.

The retired mac binaries were x86_64 despite their "m1"/"apple_silicon"
names, so they ran under Rosetta 2, including on the macos-latest
runners. Apple Silicon binaries are now tagged mac_arm, and Intel MacOS
is no longer detected.

compile_prms() takes a file lock, because generate_test_data.py runs
pytest with -n=auto and workers would otherwise run "make clean" in the
same source tree while another was compiling. filelock is a test-only
dependency, so it is imported lazily.

GSFLOW binaries stay checked in: their source is not part of this
repository, so they cannot be compiled on demand. Retiring the last
ifort artifact among them is tracked in MAINTENANCE.md.

Three notebooks referenced the deleted binary by name and now call
get_or_compile_prms_exe(). Touching them made the pre-commit ruff hook
lint them (it passes .ipynb paths explicitly, which overrides
[tool.ruff] include, so `ruff check .` and CI never saw them), which
surfaced pre-existing errors in cells unrelated to this change: unused
imports, long lines, bare excepts, and a stray paren that made one cell
a syntax error. Those are fixed here too.

Verified on macOS arm64: hru_1 nhm (109 passed), hru_1 transp_frost,
and drb_2yr nhm (137 passed, exercising both 5.2.1 and 5.2.1.1), plus
on-demand compilation under 14 xdist workers.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
intcp.f90:416 gates grass rain interception on Pkwater_equiv, which
intcp reads before snowcomp updates it, i.e. the previous step's pack --
what snowcomp publishes as Pkwater_ante (snowcomp.f90:946). pywatershed
reconstructed it as pk_ice_prev + freeh2o_prev; in the vanishing tail of
a melting pack the two differ by orders of magnitude and straddle
dnearzero, flipping whether a day's rain is intercepted.

PRMSCanopy now takes pkwater_ante in place of pk_ice_prev and
freeh2o_prev, and PRMSSnow declares pkwater_ante (snowcomp.f90:346-349)
so coupled models supply it.

Also quiet the test suite's NumPy 2.5 deprecation warnings: a unit on
constants.nat, a holoviews >=1.23.0 floor, and a pytest filter for
netCDF4's unreleased fix.

ci-all

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
All five Windows domain jobs fail at the link step with
'ld.exe: cannot find crt2.o' and 'cannot find default-manifest.o'.
Both files come from packages reached through m2w64-sysroot_win-64,
which gcc_impl_win-64 depends on unpinned; the sysroot's build 11
(2026-08-20) and win-64 gfortran 16.2.0 (2026-08-25) both landed
after the last green Windows compile.

The ceiling is a probe: if Windows still fails, the sysroot is the
culprit rather than gcc 16. MAINTENANCE.md carries the timeline, the
fallback pin, and a draft report for the sysroot feedstock.
…-ups

whats-new.rst: replace the :pull:`XXX` placeholders with 414.

CLAUDE.md: always carry the PR template's checklist into a PR body,
even when nothing in it applies, rather than dropping the section.

MAINTENANCE.md: park PR DOI-USGS#412's two deferred follow-ups -- the
pre-commit ruff-check hook's types: [python] may skip notebook-only
commits, and pyproject's optional extra lacks the holoviews floor the
environment files carry.
Give the checklist its own heading and keep every item, striking through
the ones that do not apply instead of deleting them. Collapse the two
performance-benchmark items into one -- the machinery is gone, so both
were always struck, and one struck line still stands as a reminder to
revive it. Add an item for replacing the (:pull:`XXX`) placeholder,
which is the easiest step in the convention to forget. Point the api
item at doc/api/*.rst and at pywatershed/__init__.py, which is where
the convention in CLAUDE.md actually lives.
@jmccreight jmccreight changed the title Feat canopy pkwater ante PRMSCanopy: gate grass rain interception on pkwater_ante (carries #413's ifort retirement) Aug 26, 2026
@jmccreight jmccreight mentioned this pull request Aug 26, 2026
6 tasks
@jmccreight jmccreight self-assigned this Aug 27, 2026

@jmccreight jmccreight left a comment

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

All looking very nice

@jmccreight
jmccreight merged commit da95a98 into DOI-USGS:develop Aug 27, 2026
32 checks passed
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