Skip to content

Two station static model: Stan file, data, data prep, implementation#469

Open
mdodrill-usgs wants to merge 7 commits into
dev/two-stationfrom
fix/vfts-stan-syntax
Open

Two station static model: Stan file, data, data prep, implementation#469
mdodrill-usgs wants to merge 7 commits into
dev/two-stationfrom
fix/vfts-stan-syntax

Conversation

@mdodrill-usgs

Copy link
Copy Markdown
Collaborator

Implements the two-station (VFTS) metabolism model as a new model type
in streamMetabolizer. All work targets the dev/two-station integration
branch.

Changes

  • inst/models/b2_np_oi_tr_plrckm.stan — static Stan model (fixed
    syntax, lognormal K600, GPP/ER priors, package naming conventions)
  • R/mm_data.R — new two-station columns (DO.obs.up, DO.sat.up,
    DO.obs.down, DO.sat.down, travel.time)
  • R/mm_validate_data.R — fix pre-existing travel.time grep bug in
    na_times check
  • R/mm_ts_prep_data.R — data reshape with upstream lag shift
  • R/metab_2station.R — metab_2station() implementation +
    predict_DO.metab_2station()
  • R/mm_name.R, R/mm_valid_names.R — register bayes_2station type
  • R/mm_parse_name.R — extend type map for b2_ prefix
  • R/specs.R — bayes_2station specs case; prior defaults match
    one-station convention (K600: meanlog=2.484907, sdlog=1.0)
  • R/metab.R — dispatch to metab_2station()
  • R/data.R, data/two_station_example.rda — 30-day example dataset
    (Bishop et al. 2026, lom3.70066)
  • DESCRIPTION — add stats, utils, parallel to Imports

Tests

472 passing tests including a real Stan fit against example data.
0 errors | 3 warnings (pre-existing) | 6 notes (5 pre-existing,
1 resolved in this PR)
Test timing: ~192 sec total, metab_2station adds ~20 sec

Known gaps (deferred, noted in code)

  • predict_DO(use_saved=FALSE) not implemented
  • plot_DO_preds.R and helper-rmse_DO.R hard-code one-station column
    names

References

Bishop et al. 2026. A simplified two-station approach for modeling
metabolism in dam tailwaters subject to diel flow variation.
L&O Methods. https://doi.org/10.1002/lom3.70066

Port VFTS Stan model from LOM paper to inst/models/
- Fix deprecated postfix array syntax (10 declarations; Stan 2.33+ requirement)
- Switch K600 prior from hard-coded normal to lognormal with data-block parameters
- Rename variables to package conventions (n_obs, n_days, light, temp_water, travel_time)
- Verified clean parse
- Add DO.obs.up, DO.sat.up, DO.obs.down, DO.sat.down, travel.time to mm_data() registry
- Create metab_2station() constructor stub with mm_validate_data() integration
- Two-station-specific checks: travel.time positivity, units sanity (<1 day), lead-in data
- Fix mm_validate_data() na_times grep incorrectly matching travel.time alongside solar.time
- Add test-metab_2station.R (7 assertions); all pre-existing tests still pass
- Implement mm_ts_prep_data(data, specs=NULL) reshaping long-format data to Stan list
- Upstream DO/DO.sat shifted per-row by lag = round(travel.time / timestep_days)
- Lead-in rows (first max(lag) rows) excluded from output matrices
- Pivots 8 variables to [n_obs x n_days] matrices following prepdata_bayes() pattern
- K600 prior params passed through from specs (defaults: log(3.48) / 0.5)
- Units stripped before return
- Extend test-metab_2station.R to 15 blocks / 39 assertions; no regressions
- Extend mm_parse_name() type map: b2 = 'bayes_2station' (handles two-character prefix correctly)
- Add predict_DO.metab_2station() stub returning DO.obs.down / DO.mod.down column names
- Flag plot_DO_preds.R and helper-rmse_DO.R as needing update
- Extend test-metab_2station.R to 12 assertions; no regressions in test-mm_name.R
- Rename GPP/ER/k600 to GPP_daily/ER_daily/K600_daily to match format_mcmc_mat_nosplit() lookup table
- Add GPP_daily_mu/sigma and ER_daily_mu/sigma to data block
- Add GPP_daily and ER_daily normal priors in model block
- Group all three priors before likelihood statement
- Register bayes_2station in mm_name(), mm_valid_names()
- Add bayes_2station case to specs(): params_in/out, split_dates=FALSE, engine='stan'
- K600 prior defaults match one-station convention (meanlog=2.484907, sdlog=1.0)
- GPP/ER prior defaults match one-station defaults (GPP: mu=3.1 sd=6.0, ER: mu=-7.1 sd=7.1)
- Add stats, utils, parallel to DESCRIPTION Imports (pre-existing gap)
- Add bayes_2station dispatch to metab()
- Full metab_2station() implementation: validation, data prep, rstan::stan() call,
  MCMC formatting via format_mcmc_mat_nosplit(), metab_model S4 construction
- Add metab_2station class
- Implement predict_DO.metab_2station() returning DO.obs.down / DO.mod.down
- Add two_station_example dataset (30 days, VFTS-2, package column conventions)
- Add R/data.R with full citation: Bishop et al. 2026 and ScienceBase link
- passing tests including real Stan fit; no regressions in mm_name suite
- Known gaps: no Stan compile caching (deferred), predict_DO use_saved=FALSE
  not implemented, plot_DO_preds.R/helper-rmse_DO.R one-station column names deferred
- Add utils::globalVariables for ., metab_50pct, DO.mod.down
- Add @importFrom utils modifyList
- Replace require(rstan) with requireNamespace() pattern
@mdodrill-usgs

Copy link
Copy Markdown
Collaborator Author

Demo

Ran the two-station model against the bundled two_station_example dataset to confirm the full fit → predict pipeline works.

Dataset

?two_station_example
two_station_example

dim: 2904 x 9
names: solar.time, DO.obs.up, DO.sat.up, DO.obs.down, DO.sat.down, light, depth, temp.water, travel.time

Model fit

specs <- specs(mm_name('bayes_2station'),
               n_chains = 1, burnin_steps = 250, saved_steps = 250)
fit <- metab(specs, data = two_station_example)

Fast demo settings (1 chain, 250 warmup + 250 saved steps) to confirm the pipeline runs.

predict_metab() — daily estimates (first 5 days)

date GPP GPP.lower GPP.upper ER ER.lower ER.upper K600 K600.lower K600.upper
2011-07-31 16.88 15.28 18.23 -31.63 -38.54 -24.31 23.26 15.93 30.39
2011-08-01 18.31 16.68 20.06 -30.07 -37.53 -24.80 20.72 15.65 28.43
2011-08-02 19.74 18.30 21.15 -33.00 -39.26 -27.05 24.28 17.99 30.87
2011-08-03 18.62 17.31 19.94 -25.62 -30.21 -21.17 17.45 12.38 21.52
2011-08-04 20.12 18.69 21.68 -30.48 -37.29 -23.40 19.84 13.44 26.60

30 days fit total; GPP/ER/K600 all in reasonable ranges.

predict_DO() — downstream DO fit (first 10 rows)

solar.time DO.obs.down DO.mod.down
2011-07-31 00:00:00 6.95 6.90
2011-07-31 00:15:00 6.94 6.88
2011-07-31 00:30:00 6.95 6.86
2011-07-31 00:45:00 6.96 6.88
2011-07-31 01:00:00 6.96 6.85
2011-07-31 01:15:00 6.96 6.85
2011-07-31 01:30:00 6.97 6.86
2011-07-31 01:45:00 6.97 6.85
2011-07-31 02:00:00 6.98 6.87
2011-07-31 02:15:00 6.99 6.88

Modeled DO tracks observed within ~0.05-0.1 mg/L.

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