Observed Controller
Observed NoDb controller for model-fit statistics between observed CSV time series and WEPP outputs. See also:
../../../../AGENTS.mdand../../../../wepppy/weppcloud/controllers_js/README.md#observed-controller-reference-2025-helper-migration
Overview
- Parses observed CSV text (
Date+ measures) into a normalizedobserved.csvwith year/month/day/julian fields. - Loads WEPP interchange outputs to compute daily/yearly model-fit statistics for hillslopes and channels.
- Persists results in
observed.nodb, writes comparison CSVs under<run>/observed/, and exposes report + CSV routes.
Data Flow
parse_textdata(textdata)reads CSV, parses dates, and writes<run>/observed/observed.csv.calc_model_fit()loads hillslope + channel simulations and runs metrics:- Hillslopes:
wepp/output/interchange/totalwatsed3.parquet(reused if present). - Channels:
wepp/output/ebe_pw0.txt+wepp/output/chanwb.out.
- Hillslopes:
- Outputs are written to
<run>/observed/and summarized inobserved.nodb.
Outputs
observed/observed.csv(normalized input withYear,Month,Day,Julian).observed/Hillslopes-<Measure>-Daily.csv,observed/Channels-<Measure>-Yearly.csv, etc.observed.log(timing logs, status stream).observed.nodb(results payload).
Observed Report + Graph (Specification)
- Route:
/runs/<runid>/<config>/report/observed/with optional?selected=<series>. - Default graph selection:
Hillslopes-Streamflow_(mm)-Dailywhen available, otherwise the first CSV in<run>/observed/. - Report content order: summary tables first, graph second.
- Graph data source:
GET /runs/<runid>/<config>/resources/observed/<file>(client-side CSV fetch). - Graph CSV format:
date,Modeled,Observed(date isYYYY-MM-DDfor Daily,YYYYfor Yearly). - Graph behavior: two line series (Simulated/Observed), main plot + brush context, no precipitation bars.
- Error handling: if no comparison files exist, the report shows a message and skips the chart.
UI Wiring
- Control template:
wepppy/weppcloud/templates/controls/observed_pure.htm. - Controller:
wepppy/weppcloud/controllers_js/observed.js.- On page load, the summary pane shows “View Model Fit Results” when
observed.hasResultsis true. - Status stream uses channel
observed(controlBase.attach_status_stream).
- On page load, the summary pane shows “View Model Fit Results” when
- Report:
wepppy/weppcloud/templates/reports/wepp/observed.htm(tables + graph).
Endpoints
| Route | Method | Purpose |
|---|---|---|
/runs/<runid>/<config>/tasks/run_model_fit |
POST | Parse CSV + run model fit (sync) |
/runs/<runid>/<config>/report/observed/ |
GET | Render observed summary report |
/runs/<runid>/<config>/resources/observed/<file> |
GET | Download CSV artifacts |
Profiling Notes (Dec 2025)
Measured on test run /wc1/runs/un/unpresidential-shabbiness with tests/data/observed/CedarRv_WA.csv:
- Before refactor (rebuilding interchange):
parse_textdata ~5s,calc_model_fit ~75s(hillslope load ~72s). - After reuse of
totalwatsed3.parquet+ parallel stats:parse_textdata ~5s,calc_model_fit ~2.2s. Times depend on cache state and machine I/O; useobserved.logfor per-step timings.
Test Data & Reference Run
- Observed CSV fixture:
tests/data/observed/CedarRv_WA.csv. - Reference run:
https://wc.bearhive.duckdns.org/weppcloud/runs/unpresidential-shabbiness/disturbed9002/- Run directory:
/wc1/runs/un/unpresidential-shabbiness - Config:
disturbed9002
- Run directory:
- Note: running the observed regression test overwrites
<run>/observed/outputs and updatesobserved.nodb.
Tests
wctl run-pytest tests/nodb/mods/test_observed_processing.py
wctl run-pytest tests/weppcloud/routes/test_observed_bp.py
wctl run-npm test -- observed
Development Guidance
- Report payload assembly lives in
wepppy/weppcloud/routes/nodb_api/observed_bp.pyand should stay lightweight; chart data is fetched client-side. - Graph rendering lives in
wepppy/weppcloud/templates/reports/wepp/observed.htmand uses the localstatic/js/d3.jsbundle. - If you add new measures, ensure
Observed._write_measureoutputsModeled+Observedcolumns withdateso the graph can render without backend changes. - The
selectedquery param should always map to a CSV in<run>/observed/; guard against missing files and fall back to the first available series.
Implementation Notes
parse_textdatauses pandas with thepyarrowCSV engine and in-reader date parsing.calc_model_fitruns hillslope stats in parallel with channel load + stats.- The observed model fit remains synchronous (not RQ); large runs can still block the request thread.