Skip to content

Commit 8229726

Browse files
Merge pull request #38 from ConnectedSystems/txc-caps
Add cap to bias correction offset
2 parents 1586564 + f48ae8e commit 8229726

File tree

8 files changed

+30
-17
lines changed

8 files changed

+30
-17
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -181,7 +181,7 @@ Area: 1985.73
181181
│ f │ 2.579280.013.0 │ Plant stress threshold, controls at what moisture deficit plants begin to experience stress. │
182182
│ a │ 5.923380.110.0 │ Quickflow storage coefficient, where higher values lead to faster quickflow response. │
183183
│ b │ 0.09899260.0010.1 │ Slowflow storage coefficient, lower values lead to slower baseflow recession. │
184-
│ storage_coef │ 1.861341.0e-1010.0 Groundwater interaction factor, controling how water is exchanged with deeper groundwater. │
184+
│ storage_coef │ 1.861341.0e-1010.0 │ Groundwater interaction factor, controlling how water is exchanged with deeper groundwater. │
185185
│ alpha │ 0.7279051.0e-51.0 │ Effective rainfall scaling factor, partitions rainfall into runoff. │
186186
└──────────────┴───────────┴─────────────┴─────────────┴──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
187187
```
9.98 KB
Loading
-3.1 KB
Loading
-87 Bytes
Loading

docs/src/examples/ensembles/weighted_ensembles.md

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,9 @@ as the ensemble constituents. The default ensemble is a normalized weighted sum.
66
The usual setup process is shown here, detailed in previous sections of this guide.
77

88
```julia
9-
using StatsPlots
10-
using CSV, DataFrames
119
using Statistics
10+
using CSV, DataFrames
11+
using StatsPlots
1212
using Streamfall
1313

1414
data_dir = joinpath(
@@ -86,7 +86,7 @@ low flows as with GR4J.
8686

8787
![](../../assets/ensemble_model_comparison_quickplots.png)
8888

89-
Comparing the temporal cross section:
89+
Comparing the temporal cross section to get an idea of seasonality:
9090

9191
```julia
9292
ihacres_xs = temporal_cross_section(burn_dates, burn_obs, ihacres_node.outflow[burn_in:end]; title="IHACRES", yscale=:log10)
@@ -101,10 +101,11 @@ A reduction in the median error can be seen with extreme errors reduced somewhat
101101
![](../../assets/ensemble_xsection.png)
102102

103103
The median error can then be applied to modelled streamflow (on a month-day basis) as a
104-
form of bias correction.
104+
form of bias correction. Here, the correction factor is capped to -80% and +40% of predicted
105+
outflows.
105106

106107
```julia
107-
q_star = Streamfall.apply_temporal_correction(ensemble, climate, Qo[:, "410730"])
108+
q_star = Streamfall.apply_temporal_correction(ensemble, climate, Qo[:, "410730"]; low_cap=0.8, high_cap=0.4)
108109

109110
bc_ensemble_qp = quickplot(burn_obs, q_star[burn_in:end], climate; label="Bias Corrected Ensemble", log=true)
110111

@@ -116,16 +117,17 @@ bias_corrected_xs = temporal_cross_section(
116117
yscale=:log10
117118
)
118119

119-
plot(bc_ensemble_qp, bias_corrected_xs; layout=(2,1), size=(800, 800))
120+
ens_qp = plot(bc_ensemble_qp, bias_corrected_xs; layout=(2,1), size=(800, 800))
120121
```
121122

122-
While the median error has increased, its variance has reduced significantly. At the same
123-
time, performance at the 75 and 95% CI remain steady relative to the original weighted
124-
ensemble results.
123+
It can be seen here that low flows are better represented, with a commensurate decrease
124+
in median error (and its variance). At the same time, performance at the 75 and 95% CI
125+
remain steady relative to the original weighted ensemble results.
125126

126127
![](../../assets/ensemble_bias_corrected.png)
127128

128129
This ensemble approach may be improved further by:
129130

130131
- Using a rolling window to smooth ensemble predictions
131132
- Defining a custom objective function to target specific conditions
133+
- Using more advanced ensemble approaches other than the simple weighted mean approach

docs/src/primer.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@ Area: 130.0
8585
│ f │ 0.8 │ 0.01 │ 3.0 │ Plant stress threshold, controls at what moisture deficit plants begin to experience stress. │
8686
│ a │ 0.9 │ 0.1 │ 10.0 │ Quickflow storage coefficient, where higher values lead to faster quickflow response. │
8787
│ b │ 0.1 │ 0.001 │ 0.1 │ Slowflow storage coefficient, lower values lead to slower baseflow recession. │
88-
│ storage_coef │ 2.9 │ 1.0e-10 │ 10.0 │ Groundwater interaction factor, controling how water is exchanged with deeper groundwater. │
88+
│ storage_coef │ 2.9 │ 1.0e-10 │ 10.0 │ Groundwater interaction factor, controlling how water is exchanged with deeper groundwater. │
8989
│ alpha │ 0.95 │ 1.0e-5 │ 1.0 │ Effective rainfall scaling factor, partitions rainfall into runoff. │
9090
└──────────────┴───────┴─────────────┴─────────────┴──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
9191
```

src/Nodes/Ensembles/WeightedEnsembleNode.jl

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import ..Analysis: TemporalCrossSection
1+
import ..Analysis: TemporalCrossSection, offsets
22

33
Base.@kwdef mutable struct WeightedEnsembleNode{N<:NetworkNode,P,A<:Real} <: EnsembleNode
44
name::String
@@ -177,20 +177,31 @@ end
177177
apply_temporal_correction(
178178
ensemble::WeightedEnsembleNode,
179179
climate::Climate,
180-
obs::Vector{T}
180+
obs::Vector{T};
181+
low_cap::Float64=1.0,
182+
high_cap::Float64=1.0
181183
) where {T<:Real}
182184
183185
Correct for model bias using median error.
186+
By default, the correction is capped to ± 100% of model prediction modified by
187+
`low_cap` and `high_cap`
184188
"""
185189
function apply_temporal_correction(
186190
ensemble::WeightedEnsembleNode,
187191
climate::Climate,
188-
obs::Vector{T}
189-
) where {T<:Real}
192+
obs::Vector{Float64};
193+
low_cap::Float64=1.0,
194+
high_cap::Float64=1.0
195+
)
196+
low_cap = clamp(low_cap, 0.0, 1.0)
197+
high_cap = clamp(high_cap, 0.0, 1.0)
198+
190199
dates = timesteps(climate)
191200
tcs = TemporalCrossSection(dates, obs, ensemble.outflow)
192201

193-
return max.(ensemble.outflow .+ Streamfall.Analysis.offsets(tcs), 0.0)
202+
x = min.(ensemble.outflow .+ offsets(tcs), ensemble.outflow .* (1.0 + high_cap))
203+
204+
return max.(x, ensemble.outflow .* (1.0 - low_cap))
194205
end
195206

196207
"""

src/Nodes/IHACRES/IHACRESNode.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ Base.@kwdef mutable struct IHACRESBilinearNode{P,A<:AbstractFloat} <: IHACRESNod
2020
a::P = Param(0.9, bounds=(0.1, 10.0), desc="Quickflow storage coefficient, where higher values lead to faster quickflow response.") # quickflow storage coefficient == (1/tau_q)
2121
b::P = Param(0.1, bounds=(1e-3, 0.1), desc="Slowflow storage coefficient, lower values lead to slower baseflow recession.") # slowflow storage coefficent == (1/tau_s)
2222

23-
storage_coef::P = Param(2.9, bounds=(1e-10, 10.0), desc="Groundwater interaction factor, controling how water is exchanged with deeper groundwater.")
23+
storage_coef::P = Param(2.9, bounds=(1e-10, 10.0), desc="Groundwater interaction factor, controlling how water is exchanged with deeper groundwater.")
2424
alpha::P = Param(0.1, bounds=(1e-5, 1 - 1 / 10^9), desc="Effective rainfall scaling factor, partitions rainfall into runoff.")
2525

2626
# const level_params::Array{P, 1} = [

0 commit comments

Comments
 (0)