Skip to content

Commit 50cac0c

Browse files
Doc updates
1 parent 72d9bf0 commit 50cac0c

19 files changed

+283
-104
lines changed

README.md

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -204,14 +204,8 @@ using Streamfall
204204
sn = load_network("Example Network", "../test/data/campaspe/campaspe_network.yml")
205205

206206
# Load climate data, in this case from a CSV file with data for all nodes.
207-
climate_data = CSV.read(
208-
"../test/data/campaspe/climate/climate_historic.csv", DataFrame,
209-
comment="#",
210-
dateformat="YYYY-mm-dd"
211-
)
212-
213207
# Indicate which columns are precipitation and evaporation data based on partial identifiers
214-
climate = Climate(climate_data, "_rain", "_evap")
208+
climate = Climate("../test/data/campaspe/climate/climate.csv", "_rain", "_evap")
215209

216210
# This runs an entire stream network
217211
@info "Running an example stream..."

docs/make.jl

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ makedocs(sitename="Streamfall Documentation",
1414
pages = [
1515
"index.md",
1616
"primer.md",
17+
"expected_data_formats.md",
1718
"Examples" => [
1819
"examples/examples.md",
1920
"examples/node_creation.md",
@@ -25,11 +26,11 @@ makedocs(sitename="Streamfall Documentation",
2526
"Model evaluation" => [
2627
"examples/simple_showcase.md",
2728
"examples/model_comparison.md",
28-
# "examples/multisystem_showcase.md",
29+
"examples/simple_multisystem.md",
2930
]
3031
],
31-
"metrics.md",
3232
"API" => [
33+
"metrics.md",
3334
"Nodes" => [
3435
"API/nodes/Node.md",
3536
"API/nodes/IHACRES.md",
@@ -38,6 +39,7 @@ makedocs(sitename="Streamfall Documentation",
3839
"API/nodes/SYMHYD.md",
3940
"API/nodes/Dam.md"
4041
],
42+
"API/plotting.md",
4143
"API/network.md",
4244
"API/use_methods.md"
4345
]

docs/src/examples/calibration.md

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -26,14 +26,8 @@ sn = load_network("Example Network", "../test/data/campaspe/campaspe_network.yml
2626
plot_network(sn)
2727

2828
# Load climate data - in this case from a CSV file with data for all nodes.
29-
climate_data = CSV.read(
30-
"../test/data/campaspe/climate/climate.csv",
31-
DataFrame;
32-
comment="#"
33-
)
34-
3529
# Indicate which columns are precipitation and evaporation data based on partial identifiers
36-
climate = Climate(climate_data, "_rain", "_evap")
30+
climate = Climate("../test/data/campaspe/climate/climate.csv", "_rain", "_evap")
3731

3832
# Historic flows and dam level data
3933
calib_data = CSV.read(
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
# Network Loading
2+
3+
Loading a pre-defined network from a YAML file.
4+
5+
```julia
6+
using YAML
7+
using Streamfall
8+
9+
10+
sn = load_network("Example Network", "../test/data/campaspe/campaspe_network.yml")
11+
12+
# Find all inlets and outlets
13+
inlets, outlets = find_inlets_and_outlets(sn)
14+
```

docs/src/examples/node_creation.md

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
# Node creation
2+
3+
Example of creating a node with default parameters, then updating the parameters with
4+
user-defined values.
5+
6+
```julia
7+
using Streamfall
8+
9+
hymod_node = create_node(SimpleHyModNode, "410730", 129.2)
10+
11+
# Hymod parameters
12+
Sm_max = 250.0
13+
B = 1.0
14+
alpha = 0.2
15+
Kf = 0.5
16+
Ks = 0.05
17+
18+
# Update parameters
19+
update_params!(hymod_node, Sm_max, B, alpha, Kf, Ks)
20+
```
Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
# Simple two-system interaction
2+
3+
Here, a two-node network including a river and a dam is represented. The example is
4+
based on the Lower Campaspe catchment - a small semi-arid basin in North-Central Victoria,
5+
Australia.
6+
7+
![](../assets/two_node_network.svg)
8+
9+
The dam is the primary water store for farmers in the area. In this simplified example, we
10+
investigate the effect of changing water demands and related policies on historic dam levels.
11+
Water demands may shift due to choice in cultivated crops, changing water management
12+
practices, investment into more efficient irrigation systems, choice in crops, or a
13+
systemic change to water policies.
14+
15+
In essence, this is a cursory investigation into "what might have been" if the regional
16+
context were different.
17+
18+
For the purpose of this example, the farm water requirements are defined as a volume of
19+
daily water requirements throughout a growing season. A growing season is the period of time
20+
over which a crop is cultivated, and is assumed to be between *May* and *February*.
21+
In practice, water requirements are provided by another model.
22+
23+
```julia
24+
using CSV, DataFrames, YAML
25+
using Dates
26+
using Plots
27+
using Streamfall
28+
29+
# Load climate data - in this case from a CSV file with data for all nodes.
30+
# Indicate which columns are precipitation and evaporation data based on partial identifiers
31+
climate = Climate("../test/data/campaspe/climate/climate.csv", "_rain", "_evap")
32+
33+
# Historic extractions from the dam
34+
extraction_data = CSV.read("../test/data/campaspe/gauges/dam_extraction.csv", DataFrame; comment="#")
35+
36+
# Load the example network
37+
sn = load_network("Example Network", "../test/data/campaspe/two_node_network.yml")
38+
39+
# Run the model for the basin to obtain baseline values
40+
run_basin!(sn, climate; extraction=extraction_data)
41+
baseline_dam_level = sn[2].level
42+
baseline_dam_outflow = sn[2].outflow
43+
44+
# Get represented dates for simulation
45+
sim_dates = Streamfall.timesteps(climate)
46+
47+
# Create DataFrame to use as a template to store water extractions
48+
extractions = copy(extraction_data)
49+
50+
"""
51+
Convenience function handling interactions with all "external" models.
52+
Note: We are using a burn-in period of a year.
53+
"""
54+
function run_scenario(sn, climate, extractions, increased_demand; burn_in=366)
55+
reset!(sn)
56+
inlets, outlets = find_inlets_and_outlets(sn)
57+
58+
extractions = copy(extractions)
59+
60+
sim_dates = Streamfall.timesteps(climate)
61+
prep_state!(sn, length(sim_dates))
62+
63+
for (ts, date) in enumerate(sim_dates)
64+
if ((month(date) >= 5) || (month(date) <= 2))
65+
# Within growing season (May - Feb)
66+
67+
# Additional agricultural water demand (ML/day) from historic conditions
68+
# This would normally come from another model indicating daily water demands.
69+
extractions[ts, "406000_releases_[ML]"] += increased_demand
70+
end
71+
72+
for outlet in outlets
73+
run_node!(sn, outlet, climate, ts; extraction=extractions)
74+
end
75+
end
76+
77+
# Return dam levels for assessment
78+
return sn[2].level[burn_in:end]
79+
end
80+
81+
"""
82+
A hypothetical Critical Threshold index.
83+
84+
Mean of proportional distance to critical threshold (default 55% of dam capacity).
85+
Values of 1 indicate the dam is always full, negative values indicate the dam levels are
86+
below the critical threshold.
87+
88+
Greater values indicate greater water security but may have trade-offs with regard to
89+
environmental outcomes and farm profitability.
90+
"""
91+
function critical_threshold_index(levels; threshold=0.55)
92+
max_level = 195
93+
min_level = 160 # mAHD
94+
95+
# Essentially proportion of dam capacity
96+
relative_level = ((levels .- min_level) ./ (max_level - min_level))
97+
98+
# Distance
99+
indicator = (relative_level .- threshold) ./ (1.0 - threshold)
100+
101+
return round(mean(indicator), digits=4)
102+
end
103+
104+
105+
ct_index = critical_threshold_index(baseline_dam_level[366:end])
106+
f = plot(sim_dates[366:end], baseline_dam_level[366:end]; label="Historic modelled ($(ct_index))")
107+
results = Vector{Vector{Float64}}(undef, 5)
108+
109+
for (i, daily_demand) in enumerate([-2.0, -0.5, 1.0, 3.0, 5.0])
110+
results[i] = run_scenario(sn, climate, extractions, daily_demand)
111+
112+
ct_index = critical_threshold_index(results[i])
113+
114+
plot!(sim_dates[366:end], results[i]; label = "+$(daily_demand) ML/day ($(ct_index))")
115+
end
116+
display(f)
117+
# savefig("simple_water_demand.png")
118+
119+
Streamfall.temporal_cross_section(sim_dates, calib_data[:, "406000"], sn[2].level)
120+
```
121+
122+
![](../assets/simple_water_demand.png)

docs/src/examples/simple_showcase.md

Lines changed: 22 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -24,14 +24,8 @@ using Plots
2424
using Streamfall
2525

2626
# Load climate data - in this case from a CSV file with data for all nodes.
27-
climate_data = CSV.read(
28-
"../test/data/campaspe/climate/climate.csv",
29-
DataFrame;
30-
comment="#"
31-
)
32-
3327
# Indicate which columns are precipitation and evaporation data based on partial identifiers
34-
climate = Climate(climate_data, "_rain", "_evap")
28+
climate = Climate("../test/data/campaspe/climate/climate.csv", "_rain", "_evap")
3529

3630
calib_data = CSV.read(
3731
"../test/data/campaspe/gauges/outflow_and_level.csv",
@@ -67,8 +61,27 @@ nse = round(nse_score, digits=4)
6761
quickplot(dam_obs, dam_sim, climate, "IHACRES", false; burn_in=366)
6862
```
6963

70-
![](../assets/calibrated_example.png)
71-
7264
The `quickplot()` function creates the figure displayed above which shows dam levels on the
7365
left (observed and modelled) with a [Q-Q plot](https://en.wikipedia.org/wiki/Q%E2%80%93Q_plot)
7466
on the right.
67+
68+
![](../assets/calibrated_example.png)
69+
70+
```julia
71+
sim_dates = Streamfall.timesteps(climate)
72+
Streamfall.temporal_cross_section(sim_dates, calib_data[:, "406000"], dam_sim)
73+
```
74+
75+
The above shows a "cross-section" of model predictions for each month-day across simulation
76+
time. It is useful to gain an understanding on when models may underperform and give a
77+
sense of a models predictive uncertainty. The units of the y-axis are the same as for the
78+
node (in this case, meters).
79+
80+
Ideally, the median error would be a straight line and the confidence intervals would
81+
be as thin and consistent as possible for all month-days.
82+
83+
Here, we see that while performance is generally good (mean of Median Error is near zero),
84+
the model can under-estimate dam levels in late-April to May and displays a tendency to
85+
over-estimate dam levels between January and June, relative to other times.
86+
87+
![](../assets/temporal_xsection_historic_calibrated.png)

docs/src/index.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ more "hands-on" overview.
2222
---
2323

2424
```@raw html
25-
<table align="center">
25+
<table align="center">
2626
<tr>
2727
<th>Model</th>
2828
<th>Full name</th>

docs/src/primer.md

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ Represented Area: 130.0
7474
7575
Node 1
7676
--------
77-
Name: 410730 [BilinearNode]
77+
Name: 410730 [IHACRESBilinearNode]
7878
Area: 130.0
7979
┌──────────────┬───────┬─────────────┬─────────────┐
8080
│ Parameter │ Value │ Lower Bound │ Upper Bound │
@@ -158,14 +158,8 @@ Date, 406214_rain, 406214_evap, 406219_rain, 406219_evap
158158

159159
```julia
160160
# Load data from CSV
161-
climate_data = CSV.read(
162-
joinpath(data_path, "climate/climate_historic.csv"), DataFrame,
163-
comment="#",
164-
dateformat="YYYY-mm-dd"
165-
)
166-
167161
# Create a climate object, specifying which identifiers to use.
168-
climate = Climate(climate_data, "_rain", "_evap")
162+
climate = Climate("../test/data/campaspe/climate/climate.csv", "_rain", "_evap")
169163
```
170164

171165
## Running a network or node

examples/baseline_xsection.png

6.04 KB
Loading

0 commit comments

Comments
 (0)