pyfloodrisk 0.1.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.
Files changed (43) hide show
  1. pyfloodrisk-0.1.0/LICENSE +21 -0
  2. pyfloodrisk-0.1.0/PKG-INFO +75 -0
  3. pyfloodrisk-0.1.0/README.md +54 -0
  4. pyfloodrisk-0.1.0/pyproject.toml +42 -0
  5. pyfloodrisk-0.1.0/setup.cfg +4 -0
  6. pyfloodrisk-0.1.0/src/pyfloodrisk/__init__.py +28 -0
  7. pyfloodrisk-0.1.0/src/pyfloodrisk/calibration_robust.py +191 -0
  8. pyfloodrisk-0.1.0/src/pyfloodrisk/data/CS_418005/Areal_CS_AllStats.csv +892 -0
  9. pyfloodrisk-0.1.0/src/pyfloodrisk/data/CS_418005/Areal_CS_Increments.csv +892 -0
  10. pyfloodrisk-0.1.0/src/pyfloodrisk/data/CS_418005/CS_AllStats.csv +721 -0
  11. pyfloodrisk-0.1.0/src/pyfloodrisk/data/CS_418005/CS_Increments.csv +721 -0
  12. pyfloodrisk-0.1.0/src/pyfloodrisk/data/CS_421026/Areal_ECsouth_AllStats.csv +892 -0
  13. pyfloodrisk-0.1.0/src/pyfloodrisk/data/CS_421026/Areal_ECsouth_Increments.csv +892 -0
  14. pyfloodrisk-0.1.0/src/pyfloodrisk/data/CS_421026/ECsouth_AllStats.csv +745 -0
  15. pyfloodrisk-0.1.0/src/pyfloodrisk/data/CS_421026/ECsouth_Increments.csv +721 -0
  16. pyfloodrisk-0.1.0/src/pyfloodrisk/data/GR4H_climatedata/GR4H_climatedata_418005_hr.csv +52585 -0
  17. pyfloodrisk-0.1.0/src/pyfloodrisk/data/GR4H_climatedata/GR4H_climatedata_421026_hr.csv +57635 -0
  18. pyfloodrisk-0.1.0/src/pyfloodrisk/data/__init__.py +0 -0
  19. pyfloodrisk-0.1.0/src/pyfloodrisk/data/hrs_station_details.csv +479 -0
  20. pyfloodrisk-0.1.0/src/pyfloodrisk/data/storms/421026_1in2000_12hr_tp01.csv +121 -0
  21. pyfloodrisk-0.1.0/src/pyfloodrisk/data/storms/421026_1in2000_12hr_tp02.csv +121 -0
  22. pyfloodrisk-0.1.0/src/pyfloodrisk/data/storms/421026_1in2000_12hr_tp03.csv +121 -0
  23. pyfloodrisk-0.1.0/src/pyfloodrisk/data/storms/421026_1in2000_12hr_tp04.csv +121 -0
  24. pyfloodrisk-0.1.0/src/pyfloodrisk/data/storms/421026_1in2000_12hr_tp05.csv +121 -0
  25. pyfloodrisk-0.1.0/src/pyfloodrisk/data/storms/421026_1in2000_12hr_tp06.csv +121 -0
  26. pyfloodrisk-0.1.0/src/pyfloodrisk/data/storms/421026_1in2000_12hr_tp07.csv +121 -0
  27. pyfloodrisk-0.1.0/src/pyfloodrisk/data/storms/421026_1in2000_12hr_tp08.csv +121 -0
  28. pyfloodrisk-0.1.0/src/pyfloodrisk/data/storms/421026_1in2000_12hr_tp09.csv +121 -0
  29. pyfloodrisk-0.1.0/src/pyfloodrisk/data/storms/421026_1in2000_12hr_tp10.csv +121 -0
  30. pyfloodrisk-0.1.0/src/pyfloodrisk/demo_data.py +88 -0
  31. pyfloodrisk-0.1.0/src/pyfloodrisk/design_flood.py +187 -0
  32. pyfloodrisk-0.1.0/src/pyfloodrisk/design_storm.py +231 -0
  33. pyfloodrisk-0.1.0/src/pyfloodrisk/gr4h/GR4H_calibrate.py +128 -0
  34. pyfloodrisk-0.1.0/src/pyfloodrisk/gr4h/GR4H_model.py +416 -0
  35. pyfloodrisk-0.1.0/src/pyfloodrisk/gr4h/__init__.py +10 -0
  36. pyfloodrisk-0.1.0/src/pyfloodrisk/hydroevents.py +401 -0
  37. pyfloodrisk-0.1.0/src/pyfloodrisk.egg-info/PKG-INFO +75 -0
  38. pyfloodrisk-0.1.0/src/pyfloodrisk.egg-info/SOURCES.txt +41 -0
  39. pyfloodrisk-0.1.0/src/pyfloodrisk.egg-info/dependency_links.txt +1 -0
  40. pyfloodrisk-0.1.0/src/pyfloodrisk.egg-info/requires.txt +10 -0
  41. pyfloodrisk-0.1.0/src/pyfloodrisk.egg-info/top_level.txt +1 -0
  42. pyfloodrisk-0.1.0/tests/test_demo_data.py +42 -0
  43. pyfloodrisk-0.1.0/tests/test_design_storm.py +42 -0
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Ze Jiang, Caleb Dykman
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,75 @@
1
+ Metadata-Version: 2.4
2
+ Name: pyfloodrisk
3
+ Version: 0.1.0
4
+ Summary: Flood Risk Assessment Workflow for GR4H-Based Design Flood Analysis
5
+ Author-email: Ze Jiang <ze.jiang@unsw.edu.au>, Caleb Dykman <caleb.dykman@unsw.edu.au>
6
+ License: MIT
7
+ Project-URL: Repository, https://github.com/crdykman/pyfloodrisk
8
+ Requires-Python: >=3.10
9
+ Description-Content-Type: text/markdown
10
+ License-File: LICENSE
11
+ Requires-Dist: numpy>=1.24
12
+ Requires-Dist: pandas>=2.0
13
+ Requires-Dist: scipy>=1.10
14
+ Requires-Dist: numba>=0.57
15
+ Requires-Dist: spotpy>=1.5
16
+ Requires-Dist: seaborn>=0.12
17
+ Provides-Extra: dev
18
+ Requires-Dist: pytest>=7; extra == "dev"
19
+ Requires-Dist: pytest-cov; extra == "dev"
20
+ Dynamic: license-file
21
+
22
+ # pyfloodrisk
23
+
24
+ Flood Risk Assessment Workflow for GR4H-Based Design Flood Analysis.
25
+
26
+ `pyfloodrisk` is a Python port of an R-based design flood workflow built
27
+ around the GR4H hourly rainfall-runoff model. It covers the full pipeline
28
+ from streamflow calibration through to design flood simulation:
29
+
30
+ - **GR4H** — hourly production/routing rainfall-runoff model
31
+ (`pyfloodrisk.gr4h`).
32
+ - **Calibration** — DREAM-based calibration against observed streamflow,
33
+ plus an optional robust re-calibration step that re-ranks the
34
+ behavioural posterior against design storm events
35
+ (`calibration`, `behavioural_posterior`, `robust_calibration`).
36
+ - **Event delineation** — baseflow separation and hydrologic event
37
+ extraction (peaks-over-threshold or local maxima), used to derive
38
+ antecedent model states ahead of each event (`hydro_event_pipeline`,
39
+ `extract_initial_states`).
40
+ - **Design storms** — builds design storms from temporal-pattern
41
+ increment files (`build_design_storm`).
42
+ - **Design flood simulation** — runs GR4H forward across design storm
43
+ patterns and antecedent states to produce a design flood ensemble
44
+ (`simulate_design_flood`, `run_demo_workflow`).
45
+
46
+ A small bundled demo dataset (climate data and design storm increments
47
+ for two stations) lets the whole workflow run end-to-end without any
48
+ external data.
49
+
50
+ ## Installation
51
+
52
+ ```bash
53
+ pip install -e .
54
+ ```
55
+
56
+ For running the test suite:
57
+
58
+ ```bash
59
+ pip install -e ".[dev]"
60
+ ```
61
+
62
+ ## Quickstart
63
+
64
+ ```python
65
+ from pyfloodrisk import run_demo_workflow
66
+
67
+ results = run_demo_workflow(station="421026")
68
+ ```
69
+
70
+ ## Development
71
+
72
+
73
+ ## License
74
+
75
+ MIT — see [LICENSE](LICENSE).
@@ -0,0 +1,54 @@
1
+ # pyfloodrisk
2
+
3
+ Flood Risk Assessment Workflow for GR4H-Based Design Flood Analysis.
4
+
5
+ `pyfloodrisk` is a Python port of an R-based design flood workflow built
6
+ around the GR4H hourly rainfall-runoff model. It covers the full pipeline
7
+ from streamflow calibration through to design flood simulation:
8
+
9
+ - **GR4H** — hourly production/routing rainfall-runoff model
10
+ (`pyfloodrisk.gr4h`).
11
+ - **Calibration** — DREAM-based calibration against observed streamflow,
12
+ plus an optional robust re-calibration step that re-ranks the
13
+ behavioural posterior against design storm events
14
+ (`calibration`, `behavioural_posterior`, `robust_calibration`).
15
+ - **Event delineation** — baseflow separation and hydrologic event
16
+ extraction (peaks-over-threshold or local maxima), used to derive
17
+ antecedent model states ahead of each event (`hydro_event_pipeline`,
18
+ `extract_initial_states`).
19
+ - **Design storms** — builds design storms from temporal-pattern
20
+ increment files (`build_design_storm`).
21
+ - **Design flood simulation** — runs GR4H forward across design storm
22
+ patterns and antecedent states to produce a design flood ensemble
23
+ (`simulate_design_flood`, `run_demo_workflow`).
24
+
25
+ A small bundled demo dataset (climate data and design storm increments
26
+ for two stations) lets the whole workflow run end-to-end without any
27
+ external data.
28
+
29
+ ## Installation
30
+
31
+ ```bash
32
+ pip install -e .
33
+ ```
34
+
35
+ For running the test suite:
36
+
37
+ ```bash
38
+ pip install -e ".[dev]"
39
+ ```
40
+
41
+ ## Quickstart
42
+
43
+ ```python
44
+ from pyfloodrisk import run_demo_workflow
45
+
46
+ results = run_demo_workflow(station="421026")
47
+ ```
48
+
49
+ ## Development
50
+
51
+
52
+ ## License
53
+
54
+ MIT — see [LICENSE](LICENSE).
@@ -0,0 +1,42 @@
1
+ [build-system]
2
+ requires = ["setuptools>=68", "wheel"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "pyfloodrisk"
7
+ version = "0.1.0"
8
+ description = "Flood Risk Assessment Workflow for GR4H-Based Design Flood Analysis"
9
+ readme = "README.md"
10
+ license = { text = "MIT" }
11
+ authors = [
12
+ { name = "Ze Jiang", email = "ze.jiang@unsw.edu.au" },
13
+ { name = "Caleb Dykman", email = "caleb.dykman@unsw.edu.au" },
14
+ ]
15
+ requires-python = ">=3.10"
16
+ dependencies = [
17
+ "numpy>=1.24",
18
+ "pandas>=2.0",
19
+ "scipy>=1.10",
20
+ "numba>=0.57",
21
+ "spotpy>=1.5",
22
+ "seaborn>=0.12",
23
+ ]
24
+
25
+ [project.optional-dependencies]
26
+ dev = ["pytest>=7", "pytest-cov"]
27
+
28
+ [project.urls]
29
+ Repository = "https://github.com/crdykman/pyfloodrisk"
30
+
31
+ [tool.setuptools.packages.find]
32
+ where = ["src"]
33
+
34
+ [tool.setuptools.package-dir]
35
+ "" = "src"
36
+
37
+ [tool.setuptools.package-data]
38
+ "pyfloodrisk.data" = ["**/*"]
39
+
40
+ [tool.pytest.ini_options]
41
+ testpaths = ["tests"]
42
+ pythonpath = ["src"]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,28 @@
1
+ """Flood Risk Assessment: GR4H-Based Design Flood Analysis."""
2
+
3
+ from .calibration_robust import behavioural_posterior, calibration, robust_calibration
4
+ from .demo_data import (
5
+ catchment_data,
6
+ demo_paths,
7
+ list_demo_stations,
8
+ load_demo_station_data,
9
+ )
10
+ from .design_flood import run_demo_workflow, simulate_design_flood
11
+ from .design_storm import build_design_storm
12
+ from .hydroevents import extract_initial_states, hydro_event_pipeline
13
+
14
+ __version__ = "0.1.0"
15
+ __all__ = [
16
+ "behavioural_posterior",
17
+ "build_design_storm",
18
+ "calibration",
19
+ "catchment_data",
20
+ "demo_paths",
21
+ "extract_initial_states",
22
+ "hydro_event_pipeline",
23
+ "list_demo_stations",
24
+ "load_demo_station_data",
25
+ "robust_calibration",
26
+ "run_demo_workflow",
27
+ "simulate_design_flood",
28
+ ]
@@ -0,0 +1,191 @@
1
+ """DREAM-based calibration and robust (multi-event) re-calibration of GR4H."""
2
+
3
+ import numpy as np
4
+ import pandas as pd
5
+ import seaborn as sns
6
+ import spotpy
7
+
8
+ from .gr4h.GR4H_model import GR4H
9
+ from .gr4h.GR4H_calibrate import spot_setup
10
+
11
+
12
+ def calibration(area, data, nsamples, eventsidx=None, save_output=True):
13
+ """Calibrate GR4H against observed streamflow using DREAM.
14
+
15
+ Parameters
16
+ ----------
17
+ area : float
18
+ Catchment area in km2.
19
+ data : str or Path
20
+ Path to a climate CSV with ``prec``, ``pet``, and ``qt`` columns.
21
+ nsamples : int
22
+ Maximum number of DREAM function evaluations (repetitions).
23
+ eventsidx : array_like, optional
24
+ Indices to restrict the objective function to specific events.
25
+ If None, all data after the warm-up period is used.
26
+ save_output : bool, optional
27
+ If True (default), save the DREAM chain history to
28
+ ``dream_calibration_results.npy``.
29
+
30
+ Returns
31
+ -------
32
+ Structured array of DREAM chain results (parameters, likelihood,
33
+ and chain id per sample), as returned by ``spotpy``'s
34
+ ``sampler.getdata()``.
35
+ """
36
+ spotsetup = spot_setup(
37
+ data,
38
+ area,
39
+ obj_func=spotpy.objectivefunctions.nashsutcliffe,
40
+ ps0=1.0,
41
+ rs0=0.5,
42
+ warmup=365,
43
+ events=eventsidx,
44
+ )
45
+
46
+ sampler = spotpy.algorithms.dream(
47
+ spotsetup,
48
+ dbname="DREAM_GR4H",
49
+ dbformat="ram",
50
+ save_threshold=0.0,
51
+ save_sim=False,
52
+ )
53
+
54
+ # DREAM sampling settings
55
+ rep = nsamples # maximum number of function evaluations allowed
56
+ nChains = max(nsamples // 1000, 10)
57
+ nCr = 3
58
+ delta = 3
59
+ c = 0.1
60
+ eps = 10e-6
61
+ convergence_limit = 1.01
62
+ runs_after_convergence = 100
63
+ acceptance_test_option = 1
64
+
65
+ sampler.sample(
66
+ rep,
67
+ nChains,
68
+ nCr,
69
+ delta,
70
+ c,
71
+ eps,
72
+ convergence_limit,
73
+ runs_after_convergence,
74
+ acceptance_test_option,
75
+ )
76
+ results = sampler.getdata()
77
+ if save_output:
78
+ np.save("dream_calibration_results.npy", results)
79
+
80
+ return results
81
+
82
+
83
+ def behavioural_posterior(results, Cb=0.6, n=100):
84
+ """Select the behavioural posterior from a DREAM calibration run.
85
+
86
+ Parameters
87
+ ----------
88
+ results : structured ndarray
89
+ Output of :func:`calibration`.
90
+ Cb : float, optional
91
+ Minimum likelihood (``like1``) threshold for a sample to be
92
+ considered behavioural. Default 0.6.
93
+ n : int, optional
94
+ Number of top-likelihood samples to consider before applying the
95
+ ``Cb`` threshold. Default 100.
96
+
97
+ Returns
98
+ -------
99
+ Structured ndarray subset of ``results``, sorted by descending
100
+ likelihood and filtered to ``like1 >= Cb``.
101
+ """
102
+ topn = np.sort(results, order="like1")[-n:][::-1]
103
+ bpost = topn[topn["like1"] >= Cb]
104
+ return bpost
105
+
106
+
107
+ def robust_calibration(
108
+ posterior,
109
+ area,
110
+ events,
111
+ ps=[0.5, 0.6, 0.7, 0.8, 0.9],
112
+ rs=[0.5, 0.6, 0.7, 0.8, 0.9],
113
+ topn=20,
114
+ save_output=False,
115
+ ):
116
+ """Re-rank a behavioural posterior by robustness against design events.
117
+
118
+ For every posterior parameter set and every combination of initial
119
+ production/routing state (``ps``/``rs``), simulates each design event
120
+ and scores parameter sets by how consistently they reproduce a
121
+ plausible peak-flow distribution (estimated via KDE) across events of
122
+ the same frequency.
123
+
124
+ Parameters
125
+ ----------
126
+ posterior : structured ndarray
127
+ Behavioural posterior from :func:`behavioural_posterior`.
128
+ area : float
129
+ Catchment area in km2.
130
+ events : dict
131
+ Nested mapping ``{duration: {frequency: [forcings, ...]}}``, where
132
+ each ``forcings`` is a DataFrame accepted by ``GR4H.run``.
133
+ ps, rs : list of float, optional
134
+ Candidate initial production/routing storage fractions to test.
135
+ topn : int, optional
136
+ Number of most robust parameter sets to keep. Default 20.
137
+ save_output : bool, optional
138
+ If True, save the retained parameter sets to
139
+ ``robust_models_{duration}hr.npy``.
140
+
141
+ Returns
142
+ -------
143
+ Structured ndarray subset of ``posterior`` containing the ``topn``
144
+ most robust parameter sets for the (first) duration in ``events``.
145
+ """
146
+ durs = events.keys()
147
+ for d in durs:
148
+ df_likelihood = pd.DataFrame()
149
+ frqs = events[d].keys()
150
+ for f in frqs:
151
+ sims_event_max = pd.DataFrame()
152
+ nevents = len(events[d][f])
153
+ nsimspfd = nevents * len(ps) * len(rs)
154
+ for j in range(len(posterior)):
155
+ for m in ps:
156
+ for n in rs:
157
+ parameters = {
158
+ "ps0": m, # Initial production storage (ps/X1)
159
+ "rs0": n, # Initial routing storage (rs/X3)
160
+ "x1": posterior[j][1], # Max production capacity (mm)
161
+ "x2": posterior[j][2], # Water exchange coeff. (mm)
162
+ "x3": posterior[j][3], # Routing max capacity (mm)
163
+ "x4": posterior[j][4], # Unit hydrograph base (hrs)
164
+ }
165
+ # Create a model
166
+ model = GR4H(area=area, params=parameters)
167
+ for i in range(nevents):
168
+ # Run model
169
+ sim = model.run(events[d][f][i])
170
+ col = f"Param-{i}_ps0-{m}_rs0-{n}_event-{i}_1in{f}_dur-{d}"
171
+ sims_event_max[col] = [sim.qt.max()]
172
+
173
+ # Robustness: score peak flows against a KDE of their distribution
174
+ x, y = (
175
+ sns.kdeplot(sims_event_max.values[0], cut=10, clip=(0, None))
176
+ .get_lines()[0]
177
+ .get_data()
178
+ )
179
+ likelihood = np.zeros(len(posterior))
180
+ for i in range(len(posterior)):
181
+ window = sims_event_max.values[0, i * nsimspfd:(i + 1) * nsimspfd]
182
+ likelihood[i] = np.sum(np.interp(window, x, np.log10(y)))
183
+ df_likelihood[f"Likelihood_1in{f}"] = likelihood
184
+
185
+ df_likelihood["Likelihood_total"] = df_likelihood.sum(axis=1)
186
+ robust_models_idx = df_likelihood.nlargest(topn, "Likelihood_total").index
187
+ robust_models = posterior[robust_models_idx]
188
+ if save_output:
189
+ np.save(f"robust_models_{d}hr.npy", robust_models)
190
+
191
+ return robust_models