PRMSCanopy: gate grass rain interception on pkwater_ante (carries #413's ifort retirement) - #414
Merged
jmccreight merged 7 commits intoAug 27, 2026
Conversation
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
commented
Aug 27, 2026
jmccreight
left a comment
Collaborator
Author
There was a problem hiding this comment.
All looking very nice
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
PRMS gates rain interception by grasses on the antecedent snowpack —
intcp.f90:416testsPkwater_equivbeforesnowcompupdates it for thetimestep, and
snowcomp.f90:946publishes exactly that value asPkwater_ante. pywatershed instead reconstructed the pack aspk_ice_prev + freeh2o_prev. PRMS carriesPkwater_equivas its ownaccumulated 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 — flippingwhether a day's rain is intercepted. This was failing
ucb_2yr_nhmon macOS.PRMSCanopynow takespkwater_anteas an input in place ofpk_ice_prevand
freeh2o_prev, andPRMSSnowdeclarespkwater_ante(assnowcompdoes,
snowcomp.f90:346-349) so coupled models supply it. Code that handsPRMSCanopyits inputs individually must be updated; models assembled fromprocess 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 fromprms_src/with gfortran the first time they are needed, bypywatershed.utils.compile_prms(), whichci.yaml,autotest/ci_local.sh,and the test-data fixtures all call. The
prms_srcmakelists gained-std=gnu17, without which PRMS 5.2.1's C sources fail to build under theC23 default of gcc 15. Apple Silicon binaries are tagged
mac_armratherthan
m1, and Intel macOS is no longer detected. GSFLOW binaries staychecked in because their source is not part of this repository.
Also here:
thousands per run.
constants.natis nownp.datetime64("NaT", "ns"),the environment files require
holoviews >=1.23.0, andautotest/pytest.iniignores the
ndarray.shapeassignment netCDF4 <= 1.7.4 makes on everyvariable write (fixed upstream but unreleased; tracked in
MAINTENANCE.md).gfortran<16in both environment files. Windows CI links against aconda-forge mingw sysroot that stopped shipping
crt2.oanddefault-manifest.owhere 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.mdcarries the timeline, the fallback pin, and a draft report for the
feedstock.
MAINTENANCE.mdgains items for the gfortran ceilingand Lint Jupyter notebooks: put
*.ipynbin ruff'sinclude#412's two follow-ups; the PR template gets its own checklist sectionand a strike-through convention for items that don't apply.
Checklist
Closes #xxxxTests added— existingtest_prms_canopy.py,test_prms_above_snow.py, andtest_prms_et_canopy.pycover the changePerformance benchmarks added or runwhats-new.rst(:pull:`XXX`)placeholder inwhats-new.rstis replaced with this PR's numberpywatershed/__init__.pyand listed indoc/api/*.rstDocs
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 onDocumentation Buildand finally looking for thedocumentation-htmlartifact which will download as a zip file.