flowfreq 0.8.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- flowfreq-0.8.0/CHANGELOG.md +365 -0
- flowfreq-0.8.0/LICENSE +21 -0
- flowfreq-0.8.0/MANIFEST.in +11 -0
- flowfreq-0.8.0/PKG-INFO +344 -0
- flowfreq-0.8.0/README.md +303 -0
- flowfreq-0.8.0/flowfreq/__init__.py +272 -0
- flowfreq-0.8.0/flowfreq/_detrat.py +202 -0
- flowfreq-0.8.0/flowfreq/_mse_ema.py +273 -0
- flowfreq-0.8.0/flowfreq/_p3_moments.py +399 -0
- flowfreq-0.8.0/flowfreq/_var_emab.py +276 -0
- flowfreq-0.8.0/flowfreq/_var_mom.py +309 -0
- flowfreq-0.8.0/flowfreq/batch.py +129 -0
- flowfreq-0.8.0/flowfreq/bulletin17c.py +2076 -0
- flowfreq-0.8.0/flowfreq/cli.py +157 -0
- flowfreq-0.8.0/flowfreq/core.py +474 -0
- flowfreq-0.8.0/flowfreq/data/gage_attributes.csv +4 -0
- flowfreq-0.8.0/flowfreq/engine.py +231 -0
- flowfreq-0.8.0/flowfreq/flowio.py +133 -0
- flowfreq-0.8.0/flowfreq/fortran_engine.py +597 -0
- flowfreq-0.8.0/flowfreq/freq_plot.py +669 -0
- flowfreq-0.8.0/flowfreq/hydrograph.py +380 -0
- flowfreq-0.8.0/flowfreq/lowflow.py +721 -0
- flowfreq-0.8.0/flowfreq/peakfqr/__init__.py +41 -0
- flowfreq-0.8.0/flowfreq/plots.py +255 -0
- flowfreq-0.8.0/flowfreq/py.typed +0 -0
- flowfreq-0.8.0/flowfreq/qppq.py +937 -0
- flowfreq-0.8.0/flowfreq/regime.py +1266 -0
- flowfreq-0.8.0/flowfreq/report.py +253 -0
- flowfreq-0.8.0/flowfreq/streamstats.py +1314 -0
- flowfreq-0.8.0/flowfreq/transpose.py +1319 -0
- flowfreq-0.8.0/flowfreq/usgs.py +1085 -0
- flowfreq-0.8.0/flowfreq/validation/__init__.py +22 -0
- flowfreq-0.8.0/flowfreq/validation/benchmarks.py +300 -0
- flowfreq-0.8.0/flowfreq/validation/comparisons.py +317 -0
- flowfreq-0.8.0/flowfreq/validation/data/big_sandy_03606500.json +157 -0
- flowfreq-0.8.0/flowfreq/validation/reference.py +352 -0
- flowfreq-0.8.0/flowfreq/validation/reports.py +88 -0
- flowfreq-0.8.0/flowfreq/workflow.py +574 -0
- flowfreq-0.8.0/flowfreq.egg-info/PKG-INFO +344 -0
- flowfreq-0.8.0/flowfreq.egg-info/SOURCES.txt +44 -0
- flowfreq-0.8.0/flowfreq.egg-info/dependency_links.txt +1 -0
- flowfreq-0.8.0/flowfreq.egg-info/entry_points.txt +2 -0
- flowfreq-0.8.0/flowfreq.egg-info/requires.txt +18 -0
- flowfreq-0.8.0/flowfreq.egg-info/top_level.txt +1 -0
- flowfreq-0.8.0/pyproject.toml +254 -0
- flowfreq-0.8.0/setup.cfg +4 -0
|
@@ -0,0 +1,365 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to FlowFreq (formerly HydroLib) are documented here.
|
|
4
|
+
|
|
5
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
|
+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
|
+
|
|
8
|
+
## [Unreleased]
|
|
9
|
+
|
|
10
|
+
## [0.8.0]
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
- **`flowfreq.streamstats`** (Phase 1) -- USGS StreamStats watershed delineation and
|
|
14
|
+
basin-characteristics retrieval for a pour point: `pourpoint` snap, `ss-delineate`
|
|
15
|
+
`delineate/sshydro`, `ss-hydro`, per `docs/STREAMSTATS_MODULE_DESIGN.md`. Validates
|
|
16
|
+
every response against what it's supposed to contain rather than trusting a 200 (an
|
|
17
|
+
unsnappable point returns HTTP 200 with a `WarningMsg` string rather than an error);
|
|
18
|
+
typed/indexable characteristics with provenance; an offline-capable cache; a batch
|
|
19
|
+
entry point that returns results and failures side by side rather than aborting on
|
|
20
|
+
one bad point. Confirmed against the live service, reproducing the design doc's
|
|
21
|
+
published values exactly. Flow-statistics regression estimation (NSS) is deliberately
|
|
22
|
+
out of scope for this phase, and so is the watershed polygon itself --
|
|
23
|
+
`WatershedCharacteristics.polygon_geojson` is always `None`; no call in the verified
|
|
24
|
+
protocol returns it, and getting it is an open question for later.
|
|
25
|
+
- **`flowfreq.streamstats`** (Phase 2) -- NSS (National Streamflow Statistics)
|
|
26
|
+
flow-statistic estimation from real basin characteristics, per
|
|
27
|
+
`docs/STREAMSTATS_NSS_ADDENDUM.md`: `list_statistic_groups()`,
|
|
28
|
+
`estimate_flow_statistics()`, `batch_estimate_flow_statistics()`. Every estimate
|
|
29
|
+
carries NSS's own regression-equation string and a resolved citation
|
|
30
|
+
(title/author/DOI). Validates every parameter against the region's own valid range
|
|
31
|
+
*before* submission -- confirmed live that NSS returns a plausible, silently-wrong
|
|
32
|
+
extrapolated value for an out-of-range input with no warning at all. Region
|
|
33
|
+
selection across NSS's several regressionRegions per state is not automatic (no
|
|
34
|
+
available call filters by location without a watershed polygon, which Phase 1 does
|
|
35
|
+
not produce) -- every geographically-plausible region is returned, unlabelled as to
|
|
36
|
+
which is geographically correct; picking it is the caller's responsibility.
|
|
37
|
+
|
|
38
|
+
### Fixed
|
|
39
|
+
- **`USGSgage.download_daily_flow`**: the timeout was hardcoded at 30s even after an
|
|
40
|
+
earlier fix made a full period-of-record request (tens of thousands of rows for a
|
|
41
|
+
long-running site) the default -- now configurable, default 60s. The default end date
|
|
42
|
+
used local wall-clock time rather than UTC, so a host clock behind UTC could silently
|
|
43
|
+
request a narrower range than intended; now computed in UTC. `start_date`/`end_date`
|
|
44
|
+
are now validated (parseable, `start_date` not after `end_date`) before any request is
|
|
45
|
+
sent, rather than reaching NWIS unvalidated.
|
|
46
|
+
|
|
47
|
+
## [0.7.0]
|
|
48
|
+
|
|
49
|
+
### Added
|
|
50
|
+
- **`flowfreq.transpose`** -- moving a computed statistic from a gaged donor basin to a
|
|
51
|
+
nearby ungaged target by drainage-area ratio, `Q_t(p) = Q_d(p) * (A_t/A_d)**b(p)`, with
|
|
52
|
+
`b(p)` from the applicable published regression rather than assumed to be 1. Three
|
|
53
|
+
functions, deliberately separate: `transpose_frequency` (floods),
|
|
54
|
+
`transpose_duration` (flow-duration statistics) and `transpose_low_flow`.
|
|
55
|
+
|
|
56
|
+
The failure mode this is built around is silent: a wrong exponent does not raise, it
|
|
57
|
+
returns a plausible, monotone, right-order-of-magnitude discharge that is wrong by
|
|
58
|
+
10-30%. So `RegressionExponents` will not construct without a citation, every quantile
|
|
59
|
+
records whether its exponent was published, interpolated or extrapolated, and
|
|
60
|
+
`TransposedResults` deliberately carries no LP3 moments -- no distribution was fitted at
|
|
61
|
+
the target, and reporting the donor's would invent a fit nobody performed.
|
|
62
|
+
|
|
63
|
+
`probability_kind` ("aep" / "exceedance" / "non_exceedance") makes the category error
|
|
64
|
+
unreachable rather than merely documented: a flood regression's exponent describes flood
|
|
65
|
+
response and says nothing about 7Q10, so handing one to `transpose_low_flow` raises.
|
|
66
|
+
Separate functions were not enough on their own, because the *arguments* were still
|
|
67
|
+
interchangeable.
|
|
68
|
+
|
|
69
|
+
Transposed curves are checked for monotonicity in both directions and raise if they
|
|
70
|
+
invert. That was a real defect found during development: with a groundwater-dominated
|
|
71
|
+
donor (flat dry end) and an exponent set falling toward the dry end -- the combination
|
|
72
|
+
the duration literature actually describes -- `transpose_duration` could return
|
|
73
|
+
`Q99 > Q95` at area ratios inside the supported band.
|
|
74
|
+
|
|
75
|
+
- **`flowfreq.qppq`** -- QPPQ daily-series transfer through two flow-duration curves, with
|
|
76
|
+
an invertible empirical `FlowDurationCurve`, seasonal curve construction, donor ranking,
|
|
77
|
+
goodness-of-fit and a leave-one-out harness.
|
|
78
|
+
|
|
79
|
+
Includes a measured result that contradicts the obvious fix. QPPQ assumes donor and
|
|
80
|
+
target sit at the same position in their own curves on the same day; in a snowmelt basin
|
|
81
|
+
a higher target melts later, breaking that systematically. Grouping the curves by season
|
|
82
|
+
is the intuitive remedy and *makes it worse*: on a 25-day imposed offset, log-NSE was
|
|
83
|
+
0.273 annual, 0.040 for three seasons, 0.605 monthly, and 0.895 once the donor series was
|
|
84
|
+
lagged. A coarse season is longer than the offset being corrected. `estimate_donor_lag`
|
|
85
|
+
recovers the offset from rank correlation; `center_of_timing` gives the route for a
|
|
86
|
+
target with no record, since melt timing regresses on basin elevation.
|
|
87
|
+
|
|
88
|
+
`performance()` reports NSE, log-NSE, KGE and dry-end bias together, because an estimate
|
|
89
|
+
that is exact through the freshet and wrong by 10x in September still scores NSE > 0.99.
|
|
90
|
+
|
|
91
|
+
- **`regime.flow_duration_curve`** -- duration statistics were previously reachable only as
|
|
92
|
+
a by-product of drawing a figure, at nine hardcoded percentiles.
|
|
93
|
+
`Hydrograph.plot_flow_duration_curve` now delegates to it, so the table it has always
|
|
94
|
+
returned is one computation rather than two that can drift.
|
|
95
|
+
|
|
96
|
+
### Fixed
|
|
97
|
+
- `fortran_engine.build_emafit_arrays` reused the scalar loop-local names `ql`/`qu`/`tl`
|
|
98
|
+
for the arrays of the same name later in the function -- harmless at runtime, a real
|
|
99
|
+
mypy error.
|
|
100
|
+
|
|
101
|
+
## [0.6.1]
|
|
102
|
+
|
|
103
|
+
### Added
|
|
104
|
+
- `plot_peak_flows_with_thresholds` gained a `yscale: str = "log"` parameter (same convention
|
|
105
|
+
`plot_frequency_curve` already uses), the last gap against `flowfreq-app`'s
|
|
106
|
+
`plot_peak_timeseries` -- a per-plot linear/log toggle the app exposes as a real sidebar
|
|
107
|
+
control. Verified live that switching the app over loses nothing numerically: the
|
|
108
|
+
quantile-line values this function computes analytically from `lp3_params` match
|
|
109
|
+
`run_ffa`'s actual `quantile_df["Flow (cfs)"]` to 0.0% (uncensored and censored sites both
|
|
110
|
+
checked, including the app's full return-period list), and `core.log_pearson3_cdf` matches
|
|
111
|
+
the app's own max-peak-recurrence formula to ~1e-15.
|
|
112
|
+
|
|
113
|
+
## [0.6.0]
|
|
114
|
+
|
|
115
|
+
### Added
|
|
116
|
+
- `plot_peak_flows_with_thresholds`'s `mgbt_threshold` now also draws peaks below the cut as
|
|
117
|
+
hollow outline bars, matching what `flowfreq-app`'s own `plot_peak_timeseries` did that this
|
|
118
|
+
function previously didn't -- the third and last of the three features the app carried
|
|
119
|
+
(0.5.0 moved the other two, return-period lines and the max-peak annotation). A new
|
|
120
|
+
`mgbt_threshold_source` argument labels the threshold line's legend entry (`"override"` vs.
|
|
121
|
+
the default `"MGBT"`), matching the app's own PILF-source distinction. This closes the plot
|
|
122
|
+
dedupe: `flowfreq-app` can now switch to this function and delete its own copy entirely.
|
|
123
|
+
|
|
124
|
+
### Fixed
|
|
125
|
+
- `fortran_engine.build_emafit_arrays` reused the scalar loop-local names `ql`/`qu`/`tl` for
|
|
126
|
+
the final `ql`/`qu`/`tl` arrays later in the same function -- harmless at runtime (Python
|
|
127
|
+
doesn't care), but a real mypy violation (`error: Incompatible types in assignment`).
|
|
128
|
+
Renamed the loop-locals to `row_ql`/`row_qu`/`row_tl`.
|
|
129
|
+
- **`make clean` never wiped `.mypy_cache`**, which is how the error above shipped in 0.5.0
|
|
130
|
+
despite `clean-verify` passing repeatedly: mypy's incremental cache can mask a real error on
|
|
131
|
+
a file it already has a (stale, error-free) entry for, the exact failure mode
|
|
132
|
+
`clean-verify` exists to rule out for the rest of the tree. `clean` now removes it too.
|
|
133
|
+
|
|
134
|
+
## [0.5.0]
|
|
135
|
+
|
|
136
|
+
### Added
|
|
137
|
+
- **The vendored Fortran as a selectable analysis engine.** `Bulletin17C.run_analysis(engine=)`
|
|
138
|
+
accepts `"fortran"` alongside the default `"native"` (which stays the default forever); the
|
|
139
|
+
real feature is `flowfreq.workflow.compare_engines`, which runs both and returns an
|
|
140
|
+
`EngineComparisonReport` with `.max_quantile_deviation_pct` and `.to_markdown()`, and the
|
|
141
|
+
`flowfreq compare` CLI subcommand built on it. Both require the built f2py extension and
|
|
142
|
+
raise the library's existing, actionable `ImportError` rather than falling back to committed
|
|
143
|
+
golden files -- a comparison meant for a LOMR/CLOMR submittal has to mean "measured against
|
|
144
|
+
PeakFQ 8.1.0 just now," not "replayed from a file in this repository" (`docs/
|
|
145
|
+
FORTRAN_ENGINE_DESIGN.md` section 9). New module `flowfreq/fortran_engine.py`: a library-side
|
|
146
|
+
interval builder translating a `Bulletin17C` input set into `emafitpr`'s `ql/qu/tl/tu/dtype`
|
|
147
|
+
arrays, following `siteQT` (`vendor/peakfqr/R/readInputs.R`) rather than the parity suite's
|
|
148
|
+
test-only builder. Handles gap years with and without a declared perception threshold (the
|
|
149
|
+
worked example in the design doc: site 12363000's at-site skew is +0.435 omitting the four
|
|
150
|
+
unmeasured years, +0.250 censoring them at the lower threshold -- both reproduced exactly
|
|
151
|
+
through the new builder), historic-peak flagging restricted to the USGS historic flag rather
|
|
152
|
+
than the whole historical period, zero flows floored at `Qmin = 1e-20` (peakfq has no special
|
|
153
|
+
zero-flow case), a user-supplied PILF threshold, and overlapping perception-threshold periods
|
|
154
|
+
(last-declared wins, per `siteQT`'s own documented priority rule). The adapter from
|
|
155
|
+
`ReferenceResult` to `FrequencyResults` never synthesizes a field the Fortran did not report --
|
|
156
|
+
`ema_iterations`/`ema_converged` are `None`, not a guess. Verified live against the built
|
|
157
|
+
extension on all four parity sites: Big Sandy 0.0587%, Powder River 0.1003%, site 12363000
|
|
158
|
+
0.1057% (matching the design doc's own worked example), all under tolerance; Cains Coulee
|
|
159
|
+
9.741%, correctly surfaced as a failure -- it is the site carrying the pre-existing, still-open
|
|
160
|
+
`skew_weighted` residual (see P3 below), not a defect in this feature.
|
|
161
|
+
- Tests for the four modules that had none: `hydrograph.py`, `plots.py`, `batch.py`, and
|
|
162
|
+
`cli.py` (the last covering `validate`/`benchmark` and `compare`'s extension-agnostic paths;
|
|
163
|
+
`compare`'s Fortran-backed happy path lives in `tests/fortran_parity/test_live_cli_compare.py`
|
|
164
|
+
instead). Writing the `batch.py` tests surfaced a real, pre-existing defect, not introduced
|
|
165
|
+
here and not fixed by this release: `batch.run_multi_site` passes NWIS records straight from
|
|
166
|
+
`usgs.fetch_nwis_batch` (plain dicts) into `B17CEngine.fit`, which reads `.flow` as an
|
|
167
|
+
attribute -- every real multi-site call has therefore been failing per-site, silently, caught
|
|
168
|
+
by a broad `except Exception` and turned into `{"error": ...}`. Pinned as
|
|
169
|
+
`tests/test_batch.py::TestAnalyzeSites::test_real_fetch_output_shape_is_analyzable`,
|
|
170
|
+
`xfail(strict=True)`, so it stops the build the moment someone's fix makes it pass.
|
|
171
|
+
- `plot_peak_flows_with_thresholds` gained opt-in return-period reference lines and a max-peak
|
|
172
|
+
recurrence annotation via a new `lp3_params=(mean_log, std_log, skew)` argument -- the
|
|
173
|
+
library half of retiring `flowfreq-app`'s own duplicate plotting code. The app side (deleting
|
|
174
|
+
its local copy) is a separate, later change gated on a release and a pin bump.
|
|
175
|
+
|
|
176
|
+
## [0.4.0]
|
|
177
|
+
|
|
178
|
+
### Added
|
|
179
|
+
- mypy runs in CI, enforced on the modules that already pass; the rest are
|
|
180
|
+
exempted individually in `pyproject.toml` so the debt is countable and shrinks
|
|
181
|
+
by deleting a stanza. The library ships `py.typed`, so downstream checkers
|
|
182
|
+
trust these annotations -- nothing verified them before.
|
|
183
|
+
- `make cov` and a CI coverage step. `pytest-cov` had been a declared dev
|
|
184
|
+
dependency that nothing invoked.
|
|
185
|
+
- `make clean-verify`, which wipes build artifacts and every `__pycache__`
|
|
186
|
+
before running the gate, so a green result reflects the tree rather than
|
|
187
|
+
whatever was left lying around.
|
|
188
|
+
|
|
189
|
+
### Changed
|
|
190
|
+
- **`B17CEngine.fit` now uses the Bulletin 17C Eq. 7-2 station skew. The
|
|
191
|
+
numbers this public API returns have moved.** It computed
|
|
192
|
+
`((x - mean)**3).mean() / std**3`, the biased population coefficient, while
|
|
193
|
+
`Bulletin17C.run_analysis` in this same library used the unbiased sample
|
|
194
|
+
estimator `n * sum((x - mean)**3) / ((n-1)(n-2) * std**3)`. The two differ by
|
|
195
|
+
`n**2 / ((n-1)(n-2))` -- 7.2% at n=44, 39% at n=10, and short records are
|
|
196
|
+
ordinary in flood frequency work.
|
|
197
|
+
|
|
198
|
+
Measured on Big Sandy (n=44): station skew moves from -0.1748 to -0.1874,
|
|
199
|
+
which is now exactly what the Bulletin 17C path reports for the same record.
|
|
200
|
+
Quantiles move **Q2 +0.13%, Q10 -0.10%, Q100 -0.57%, Q500 -0.93%**. Anything
|
|
201
|
+
derived from `B17CEngine` moves with it, including `batch.batch_summary_table`
|
|
202
|
+
and `plots`. If you have reported a discharge from this class, it will not
|
|
203
|
+
reproduce under 0.4.0 -- pin `v0.3.0` if you need the old figures, and expect
|
|
204
|
+
the 0.4.0 value to be the defensible one.
|
|
205
|
+
|
|
206
|
+
`Bulletin17C` is unaffected: it was always correct, and the release exists to
|
|
207
|
+
make the two agree. Recorded in 0.3.0's test suite as a strict xfail; the
|
|
208
|
+
three tests that replace it in `tests/test_engine.py` now guard against a
|
|
209
|
+
revert, one of them naming the old estimator explicitly.
|
|
210
|
+
|
|
211
|
+
One deliberate difference remains: `Bulletin17C` clips the station skew to
|
|
212
|
+
±3.0 (`MAX_ABS_SKEW`) and `B17CEngine` does not, so the two can still diverge
|
|
213
|
+
on a record with extreme skew. That is a separate question from the estimator
|
|
214
|
+
and was left alone.
|
|
215
|
+
|
|
216
|
+
- `flowfreq.freq_plot.plot_frequency_curve_streamlit` is now
|
|
217
|
+
`plot_frequency_curve`. The old name remains as an alias, so pinned consumers
|
|
218
|
+
keep working; it can go once none use it. The module imports matplotlib and
|
|
219
|
+
returns a `Figure` -- the suffix was always a misnomer and became misleading
|
|
220
|
+
once the app moved to its own repository.
|
|
221
|
+
|
|
222
|
+
### Fixed
|
|
223
|
+
- Four public signatures annotated names their modules never imported, so
|
|
224
|
+
`typing.get_type_hints()` raised `NameError` on `engine.B17CEngine
|
|
225
|
+
.frequency_table`, `batch.batch_summary_table`,
|
|
226
|
+
`freq_plot.plot_peak_flows_with_thresholds` and `Bulletin17C.validate`.
|
|
227
|
+
`from __future__ import annotations` kept it from raising at import, which is
|
|
228
|
+
why it went unnoticed. The first three now resolve; `validate` keeps a
|
|
229
|
+
`TYPE_CHECKING` import to avoid inverting the package's layering, and says so.
|
|
230
|
+
|
|
231
|
+
- `import flowfreq.peakfqr` without the f2py extension built raised a bare
|
|
232
|
+
`ModuleNotFoundError` naming a private submodule. It now explains that the
|
|
233
|
+
extension is built on demand, gives the command and the toolchain, and says
|
|
234
|
+
that nothing else in the library depends on it -- the native EMA is the
|
|
235
|
+
default path. Its docstring also cited `_shared/peakfqr/src/emafit.f`, a path
|
|
236
|
+
that does not exist here; the sources are under `vendor/peakfqr/`.
|
|
237
|
+
|
|
238
|
+
## [0.3.0]
|
|
239
|
+
|
|
240
|
+
### Changed
|
|
241
|
+
- **Split into two repositories.** This repo is the analysis library; the Streamlit
|
|
242
|
+
application moved to [pinhead001/flowfreq-app](https://github.com/pinhead001/flowfreq-app),
|
|
243
|
+
which installs this library as a pinned dependency. `app/` and its three test
|
|
244
|
+
modules are gone from here, along with the `smoke` make target and the CI job
|
|
245
|
+
that ran it.
|
|
246
|
+
- `flowfreq.workflow` — new module holding the high-level entry points that used to
|
|
247
|
+
live in the app: `run_ffa`, `compute_skew_tables`, `build_skew_curves_dict`. The
|
|
248
|
+
display formatters stayed with the app.
|
|
249
|
+
- The gage attributes table moved into the package at `flowfreq/data/`, replacing a
|
|
250
|
+
`package-data` entry that reached outside the package and only ever resolved in a
|
|
251
|
+
source checkout.
|
|
252
|
+
- **Renamed to `flowfreq`** — package, import name, distribution and display name.
|
|
253
|
+
`from hydrolib.core import kfactor` is now `from flowfreq.core import kfactor`;
|
|
254
|
+
the console script is `flowfreq`. Historical entries below keep the old name,
|
|
255
|
+
since they describe what shipped at the time.
|
|
256
|
+
|
|
257
|
+
The old name was unusable. Deltares publishes `hydrolib` and `hydrolib-core` on
|
|
258
|
+
PyPI, both installing a top-level `hydrolib/` package, and `hydrolib-core` ships
|
|
259
|
+
`hydrolib/core/` against this project's `hydrolib/core.py`. Installed together
|
|
260
|
+
one silently destroys the other -- in one order this library's entire API
|
|
261
|
+
disappears, in the other neither package imports at all -- and `pip check`
|
|
262
|
+
reports nothing wrong. `flowfreq` names what the library does (flood *and*
|
|
263
|
+
low-flow frequency) and cannot be mistaken for HYDROLIB.
|
|
264
|
+
|
|
265
|
+
### Added
|
|
266
|
+
- Native Python port of `var_mom` and its dependency tree (`mn2mvarb`/`mse_ema`, `detrat`,
|
|
267
|
+
`VAR_EMAB`/`regmoms`/`ci_ema_m3b`), verified routine-by-routine against the vendored
|
|
268
|
+
peakfq 8.1.0 Fortran. See TODO.md's P3 section for the full account.
|
|
269
|
+
- `MethodOfMoments` now applies the Bulletin 17B conditional-probability adjustment: a
|
|
270
|
+
low-outlier (PILF) threshold — Grubbs-Beck or user-supplied — censors the fit instead of
|
|
271
|
+
only being reported.
|
|
272
|
+
|
|
273
|
+
### Changed
|
|
274
|
+
- `ExpectedMomentsAlgorithm` confidence intervals are now Cohn's asymmetric bounds
|
|
275
|
+
(`hydrolib._var_emab.var_emab`) instead of the symmetric `log_Q ± z*se` approximation.
|
|
276
|
+
- `ExpectedMomentsAlgorithm`'s at-site EMA moment iteration on censored intervals now uses
|
|
277
|
+
the Fortran-verified truncated-moment code (`hydrolib._p3_moments.m_p3`) and the correct
|
|
278
|
+
bias-correction sample size, closing a real accuracy gap on any record with censored
|
|
279
|
+
intervals (Big Sandy's historical gap years included, not just MGBT-flagged PILFs).
|
|
280
|
+
- Regional skew weighting now includes ADJE's censoring bias adjustment and the Halloween
|
|
281
|
+
determinant ratio (`detrat`), matching peakfq's default `at_site_option`.
|
|
282
|
+
|
|
283
|
+
### Fixed
|
|
284
|
+
- `hydrolib/peakfqsa/` (a subprocess wrapper around a PeakfqSA binary that does not exist)
|
|
285
|
+
removed; it was mock-tested only. `hydrolib/validation/reference.py` covers what it
|
|
286
|
+
contributed, pointed at references that actually exist.
|
|
287
|
+
- Bare `except:` in `usgs.py` narrowed to the actual failure modes.
|
|
288
|
+
- `analyze_gage()` no longer prints unconditionally to stdout; uses `logging` like the rest
|
|
289
|
+
of the library.
|
|
290
|
+
|
|
291
|
+
### Removed
|
|
292
|
+
- `hydrolib/peakfqsa/` and its mock-only test suite (see Fixed, above).
|
|
293
|
+
|
|
294
|
+
---
|
|
295
|
+
|
|
296
|
+
## [0.2.0] - 2026-08-31
|
|
297
|
+
|
|
298
|
+
### Added
|
|
299
|
+
- Instantaneous (unit-value) flow retrieval from USGS NWIS
|
|
300
|
+
- Low-flow frequency analysis module (`hydrolib.lowflow`)
|
|
301
|
+
- Annual n-day low-flow frequency with LP3 or lognormal distribution
|
|
302
|
+
- Climatic/water/calendar year definitions
|
|
303
|
+
- Zero-flow-year handling
|
|
304
|
+
- Analytic and bootstrap confidence intervals
|
|
305
|
+
- Flow regime metrics module (`hydrolib.regime`)
|
|
306
|
+
- Richards-Baker flashiness index
|
|
307
|
+
- TQmean metric
|
|
308
|
+
- Baseflow separation (UKIH, Lyne-Hollick, HYSEP variants)
|
|
309
|
+
- Monthly and seasonal flow summaries
|
|
310
|
+
- Diel (sub-daily) variation analysis
|
|
311
|
+
- Within-day flow range and coefficient of variation
|
|
312
|
+
- Timezone-correct local-day grouping
|
|
313
|
+
- Flow series I/O with Parquet backend (`hydrolib.flowio`)
|
|
314
|
+
- Save/load for daily and instantaneous flow data
|
|
315
|
+
|
|
316
|
+
### Changed
|
|
317
|
+
- Improved EMA algorithm convergence handling for edge cases
|
|
318
|
+
- Documentation vignettes reorganized (Low-Flow & Flow Regime guide)
|
|
319
|
+
|
|
320
|
+
### Fixed
|
|
321
|
+
- MGBT outlier detection edge case with small sample sizes
|
|
322
|
+
- Flow duration curve calculation precision
|
|
323
|
+
|
|
324
|
+
---
|
|
325
|
+
|
|
326
|
+
## [0.1.0] - 2026-01-28
|
|
327
|
+
|
|
328
|
+
### Added
|
|
329
|
+
- **USGS Data Retrieval** — Download mean daily, annual peak, and instantaneous flow from NWIS
|
|
330
|
+
- **Bulletin 17C Analysis**
|
|
331
|
+
- Expected Moments Algorithm (EMA) — USGS standard method
|
|
332
|
+
- Method of Moments (MOM) fallback
|
|
333
|
+
- Weighted regional skew (MSE weighting per B17C Appendix 6)
|
|
334
|
+
- Multiple Grubbs-Beck test (MGBT) for low outlier detection
|
|
335
|
+
- 90% confidence intervals (5%/95% limits)
|
|
336
|
+
- **Hydrograph Plotting**
|
|
337
|
+
- Daily time series plots
|
|
338
|
+
- Summary hydrographs (day of water year with percentile bands)
|
|
339
|
+
- Flow duration curves
|
|
340
|
+
- **Frequency Curve Plotting**
|
|
341
|
+
- Log-probability axis
|
|
342
|
+
- LP3 fitted curve with confidence interval band
|
|
343
|
+
- Multi-skew overlay (station / weighted / regional)
|
|
344
|
+
- **Streamlit Web Application**
|
|
345
|
+
- Interactive single/multi-gage analysis
|
|
346
|
+
- Regional skew input controls
|
|
347
|
+
- ZIP export (PNG plots, CSV data, LP3 parameters)
|
|
348
|
+
- Multi-gage comparison tables
|
|
349
|
+
- **CLI Tools**
|
|
350
|
+
- `hydrolib validate` — EMA validation against reference fixtures
|
|
351
|
+
- `hydrolib benchmark` — Numerical benchmarking (text/JSON output)
|
|
352
|
+
- **Technical Reports** — Automated Markdown report generation
|
|
353
|
+
- **Validation Framework** — Parity testing against USGS Fortran reference implementation
|
|
354
|
+
|
|
355
|
+
### Fixed
|
|
356
|
+
- Initial release
|
|
357
|
+
|
|
358
|
+
---
|
|
359
|
+
|
|
360
|
+
## Notes on Versioning
|
|
361
|
+
|
|
362
|
+
- **0.x.x** — Pre-release. API may change without warning.
|
|
363
|
+
- **1.0.0** — Stable API. Breaking changes require major version bump.
|
|
364
|
+
|
|
365
|
+
For upgrade guidance, see the [migration guides](docs/) directory.
|
flowfreq-0.8.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Chris Nelson
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
# Without this file, setuptools' default sdist file-finder pulls in
|
|
2
|
+
# tests/test_*.py individually (matching its built-in test-file heuristic)
|
|
3
|
+
# but not tests/__init__.py, tests/fixtures/, or the tests/fortran_parity/,
|
|
4
|
+
# tests/integration/, tests/validation/ subpackages -- an sdist a user
|
|
5
|
+
# actually tried to test from would fail on the first import. None of it is
|
|
6
|
+
# runnable from an sdist anyway (vendor/ and the fixture data it needs
|
|
7
|
+
# aren't shipped either), so exclude the whole tree rather than ship a
|
|
8
|
+
# partial, broken copy.
|
|
9
|
+
prune tests
|
|
10
|
+
|
|
11
|
+
include CHANGELOG.md
|