Per-model scientific versioning#560
Merged
Merged
Conversation
Add a scientific version to each model, separate from the package Version, so
downstream tools can tell scientific changes apart from software changes.
The version is a `static constexpr int scientific_version` on each strategy
class (inst/include/plant/models/*_strategy.h), next to the equations it
versions. It is bumped manually only when equations or default parameters
change simulation output for identical inputs -- not for refactors, performance
or interface changes. Exposed to R via model_version() (integer) and model_id()
("FF16@v1"), reading the constant through the new
src/strategy_version.cpp dispatcher.
A drift-guard test (tests/testthat/test-model-version.R) snapshots each model's
default pars/control and fails when a default changes without a version bump.
FF16, K93, TF24 and TF24f all start at version 1.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
TF24's scientific version starts at 2, since a published result used the
pre-versioning "v1" science.
TF24f is a fast approximation of TF24 and inherits its equations/parameters, so
it now carries a compound version "<TF24 version>.<approximation_revision>"
(e.g. "TF24f@v2.1"): the major component auto-tracks
TF24_Strategy::scientific_version -- so a TF24 scientific change also
invalidates TF24f (the safe direction) -- and approximation_revision (in
tf24f_strategy.h) tracks changes to the fast approximation itself.
model_version() now returns a version string ("1", "2.1") rather than an
integer; model_id() formats it as "Model@v<version>".
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
dfalster
added a commit
to traitecoevo/logpile
that referenced
this pull request
Jul 10, 2026
Resolves #4 ## Context `logpile` content-addresses simulations by the SHA-256 fingerprint of their resolved inputs, and `model_id` (`"FF16@v1"`) is one of those inputs. Until now the `@vN` scientific version was **hand-typed** on the logpile side, and the `plant` package version was recorded only as inert `build_hash` metadata that never triggered a rerun — so a behavioural `plant` change silently produced stale cache hits (the known gotcha in `AGENTS.md`). Depends on traitecoevo/plant#560, which adds `plant::model_version()` / `plant::model_id()`. ## What this changes - `resolve_request()` now **re-derives** the model's scientific version from `plant::model_version()` rather than trusting the request's `@vN`. The authoritative version flows into the fingerprint, so: - a `plant` scientific-version bump changes the fingerprint → reruns happen; - a software-only `plant` release leaves it untouched → cached results are reused. - `parse_model_id()` now accepts a **bare model name** (`"FF16"`), a full id (`"FF16@v1"`), or a **compound** version (`"TF24f@v2.1"`, since TF24f is a fast approximation of TF24). The version is handled as a string. - A **mismatch guard**: a pinned version that disagrees with the installed `plant` is a hard error, not a silent stale hit. - Bare and matching-pinned ids resolve identically, so **existing archives stay valid** (`FF16` and `FF16@v1` produce the same fingerprint). - `AGENTS.md` gotcha updated to reflect the new behaviour. ## Verification - Full suite: **225 pass, 0 fail** (1 pre-existing deferred-driver skip), with `plant` #560 installed. - Confirmed: bare/pinned/default all resolve to `FF16@v1`; bare and pinned fingerprints identical; `FF16@v99` errors; bumping the resolved version changes the fingerprint. 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
dfalster
added a commit
that referenced
this pull request
Jul 10, 2026
Brings the latest develop (incl. #560 per-model scientific versioning, #548 hyperparameterisation harmonisation, #556 scenario framework, #562 Windows fixes) into the TF24 NSC storage work (#517/#554). Conflict resolution & reconciliation: - tests/testthat/test-strategy-tf24.R: * Kept the #517 drought test as an assertion of COMPLETION (the NSC storage pool bounds mortality and fixes the #550 blow-up at the root cause); dropped develop's graceful-failure/skip_on_os("windows") variant since completion is now platform-independent. * Re-pinned offspring_production against the merged science (develop's #548 hyperpar baseline + NSC): single species 25.4 -> 82.09; two-species dominant 16.9 -> 67.54, with the slower species still competitively excluded (< 0.5). Neither pre-merge branch value survived, so both were re-measured. - R/RcppR6.R: regenerated (make RcppR6); hash reconciled. Apply #560 versioning to the #554 science update: - TF24_Strategy::scientific_version 2 -> 3 (NSC storage + reserve-gated growth and reserves-based mortality change output for identical inputs). - TF24f auto-tracks the major component -> TF24f@v3.1 (approximation_revision unchanged); re-blessed the model-version drift-guard snapshot; added a NEWS entry. Invalidates the logpile cache for TF24/TF24f, as intended. Full suite: 2413 pass, 0 fail (4 pre-existing/opt-in skips). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
dfalster
added a commit
that referenced
this pull request
Jul 10, 2026
Brings the latest develop (incl. #554 NSC storage + #549 soil-numerics fixes, #556 scenario framework, #560 versioning) into the layered soil-parameter work. Conflicts in inst/include/plant/models/tf24_environment.h resolved by COMBINING both changes (they touch the same retention/conductivity code): - soil_K_from_soil_theta: keep #558 per-layer parameters (K_sat/soil_moist_sat/ n_psi via soil_parameter_value, scalar fallback) AND apply develop's #549 clamp of theta to [0, soil_moist_sat_layer]. - psi_from_soil_moist: keep #558 per-layer parameters AND apply develop's #549 cap of psi at soil_psi_max_. Both now read the per-layer value where develop used the scalar, so the layered API and the blow-up guards coexist. R/RcppR6.R regenerated (hash reconciled). This also clears the Windows CI failure: the non-portable "blow-up fails gracefully (#550)" test that expected run_scm() to throw is replaced by #554's completion assertion (NSC bounds mortality on all platforms). No scientific_version bump: #558 is an interface addition with scalar-fallback behaviour, so default simulation output is unchanged. Full suite: 2430 pass, 0 fail (4 pre-existing/opt-in skips). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
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.
Resolves #561
Context
planthas a single packageVersion, but its scientific models evolve on a different clock than the code: FF16/K93 are scientifically frozen (only refactors/perf/interface changes), while TF24/TF24f change commit-to-commit. Software version is therefore a poor proxy for "did the science change". Downstream tools likelogpilearchive simulations and need to re-run only when the science changes.What this adds
static constexpr int scientific_versionon each strategy class ininst/include/plant/models/*_strategy.h, co-located with the equations it versions. Bumped manually only when equations or default parameters change the simulation output for identical inputs — never for refactors, performance, interface, or serialisation changes.model_version("FF16")→1andmodel_id("FF16")→"FF16@v1", reading the C++ constant through the newsrc/strategy_version.cppdispatcher (single source of truth; no duplicated number in R).tests/testthat/test-model-version.R) that snapshots each model's defaultpars/controland fails when a default changes without a version bump (verified: it fails on a perturbed default). Pure equation changes are not auto-detected and rely on review — documented in the header comments andagents.md§7.Starting versions:
FF16@v1,K93@v1,TF24@v2(a published result used the pre-versioning "v1" science).TF24fis a fast approximation of TF24 and inherits its code, so it carries a compound versionTF24f@v2.1— the major component auto-tracks TF24 (a TF24 change invalidates TF24f too, the safe direction), the minor is TF24f's own approximation revision.model_version()returns a version string ("1","2.1").Verification
make full_compile roxygenclean;model_version()/model_id()return expected values.🤖 Generated with Claude Code