riverarchitect.flows
Flow analysis: seasonal flow duration curves and annual peak series.
The open-source replacement for the Analyze Flows part of the ArcGIS GetStarted
module (riverpy/cFlows.py) and for the Tools/make_annual_*.py scripts.
Why it matters
riverarchitect.sharc integrates usable habitat area over a flow duration curve to
produce SHArea, the single number a habitat project is judged on. That curve is not
something the hydrodynamic model produces: it comes from a gauged daily flow record, and it
is seasonal - Chinook fry occupy the reach between 1 February and 15 June, so the flows of
the rest of the year say nothing about their habitat. Without this module a project has 2D
results, habitat suitability curves and no way to weigh one discharge against another.
The chain
Read a daily record of dates and mean daily discharge (
FlowSeries).Take every flow that falls inside a lifestage’s season, in every year of the record, and sort them descending. Rank i of n is exceeded
i / n * 100per cent of that season - the seasonal flow duration curve (FlowSeries.duration_curve()).Interpolate that curve at each discharge the 2D model was run for (
FlowSeries.exceedance_of()), because those are the only discharges habitat can be evaluated at.Write
00_Flows/<condition>/flow_duration_<code>.xlsxin the layoutriverarchitect.sharc.read_flow_duration()expects (FlowSeries.write_flow_duration()).
Also here: FlowSeries.annual_peaks(), the annual maximum series that a flood frequency
analysis needs to attach a return period to each modelled discharge - the
input_definitions.inp “Return periods” line that lifespan mapping reads. The original
exported it for HEC-SSP; return_periods() additionally fits a Gumbel distribution so
that a first estimate needs no second program.
Relation to the original
cFlows.SeasonalFlowProcessor built a n_days x n_years matrix by walking the record
day by day, with a hand-rolled leap-day skip and an index that had to stay in step with the
file. The result is a plain “collect the flows inside the season, sort, rank”, which is what
this does. Leap days are kept rather than dropped: excluding 29 February biased the curve by
one day in four years for no reason the original documented.
- class riverarchitect.flows.FlowSeries(series, unit='us', fish=None)[source]
Bases:
objectA daily flow record, and the duration curves derived from it.
- Parameters:
series (dict or str) –
{date: discharge}, or a path to read one from.unit (str) –
"us"or"si"; only used for labels.fish (riverarchitect.sharc.FishDatabase) – the database seasons are read from. Built on demand.
- Variables:
series (dict) – the record, ascending by date.
- property fish
The habitat database, loaded on first use.
- property years
Calendar years the record spans.
- season_flows(start=None, end=None)[source]
Every discharge inside a season, across every year of the record.
- Parameters:
- Returns:
the flows, unsorted.
- Return type:
- duration_curve(start=None, end=None)[source]
Seasonal flow duration curve: discharge against per-cent exceedance.
Rank i of n sorted descending is exceeded
i / n * 100per cent of the season, which is the plotting position the original used. The highest flow therefore carries a small non-zero exceedance rather than zero, and the lowest carries exactly 100.- Returns:
(discharge, exceedance), discharge descending.- Return type:
- exceedance_of(discharges, start=None, end=None)[source]
Per-cent of the season each discharge is exceeded.
Interpolated on the duration curve. A discharge above everything on record is exceeded 0 per cent of the time and one below everything 100 per cent, which is what
cFlows.interpolate_flow_exceedancereturned at those ends.
- annual_peaks(water_year_start=(10, 1))[source]
Largest discharge of each water year.
- Parameters:
water_year_start (tuple) –
(month, day)the water year begins on. The default is 1 October, the U.S. convention. Pass(1, 1)for calendar years.- Returns:
{water year: peak discharge}. The year label is the one the water year ends in, so 1 October 2019 to 30 September 2020 is 2020.- Return type:
- write_flow_duration(path, discharges=None, start=None, end=None, code='', condition='', limit=20000)[source]
Write a
flow_duration_<code>.xlsxin the layout SHArC reads.Two sheets, matching the workbooks the original produced:
Q datathe full duration curve, discharge in column A and per-cent exceedance in column B from row 3, with the species code and the season in columns D and E.
2D datathe modelled discharges in column B and their interpolated exceedance in column C from row 3 - the sheet
riverarchitect.sharc.read_flow_duration()reads.
- Parameters:
path (str) – where to write.
discharges (iterable) – the modelled discharges for the
2D datasheet.start (tuple) – season bounds as
(month, day).end (tuple) – season bounds as
(month, day).code (str) – four-letter species/lifestage code, for the header.
condition (str) – condition name, for the header.
limit (int) – cap on the number of
Q datarows written. A 50-year record has 18 000 of them; beyond a point they are only file size.
- Returns:
the path written.
- Return type:
- riverarchitect.flows.read_flow_series(path, date_column=0, discharge_column=1, sheet=None)[source]
Read a daily flow record from a spreadsheet or CSV.
- Parameters:
- Returns:
{datetime.date: discharge}, ascending by date.- Return type:
- riverarchitect.flows.seasonal_flow_duration(series, condition, discharges=None, pairs=None, unit='us', output_dir=None, fish=None)[source]
Build a flow duration workbook for every species and lifestage.
This is Analyze Flows: the step between a gauge record and a SHArea calculation. One workbook per species and lifestage lands in
00_Flows/<condition>/, named with the four-letter code SHArC looks for, soriverarchitect.sharc.SHArC.run()finds it without being told where it is.- Parameters:
series (FlowSeries or dict or str) – the daily record.
condition (str) – condition name; sets the output folder and the discharges.
discharges (iterable) – the modelled discharges. Defaults to every
h<Q>.tifin the condition.pairs (iterable) –
(species, lifestage)tuples. Defaults to every pair the fish database defines that has a season.unit (str) –
"us"or"si".output_dir (str) – where the workbooks go. Defaults to
00_Flows/<condition>/.fish (riverarchitect.sharc.FishDatabase) – the database to read seasons from.
- Returns:
one summary dict per workbook written.
- Return type:
- riverarchitect.flows.return_periods(peaks, discharges=None)[source]
Flood return periods from an annual maximum series, by a Gumbel fit.
The
Return periodsline ofinput_definitions.inpis what turns a set of modelled discharges into a lifespan axis, and it is the input most often missing. The original exported the annual peaks for HEC-SSP and left the fitting to it; this gives a defensible first estimate in one step, by the method of moments on a Gumbel (EV1) distribution:\[\alpha = \frac{s\sqrt{6}}{\pi}, \quad u = \bar{x} - \gamma\alpha, \quad T = \frac{1}{1 - \exp(-\exp(-(x - u) / \alpha))}\]- Parameters:
peaks (dict or iterable) – annual peak discharges, or
{year: peak}.discharges (iterable) – discharges to evaluate. Defaults to the peaks themselves.
- Returns:
{discharge: return period in years}, never below 1.0.- Return type:
- Raises:
ValueError – with fewer than ten peaks, where a Gumbel fit is not worth quoting.
- riverarchitect.flows.GUMBEL_EULER = 0.5772156649015329
Euler-Mascheroni constant, for the Gumbel moment estimator.