Skip to content

Commit 775a575

Browse files
Performance changes for all supported models
1 parent 3c999a3 commit 775a575

File tree

5 files changed

+248
-273
lines changed

5 files changed

+248
-273
lines changed

src/GR4JNode.jl

Lines changed: 56 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
"""
2-
Portions of the GR4J implementation is directly adapted from Python code written by Andrew MacDonald (2014).
2+
Portions of the GR4J implementation is adapted from Python code written by Andrew MacDonald (2014).
33
44
Per license requirements, the full license conditions included below.
55
@@ -65,8 +65,9 @@ A four-parameter model with two stores.
6565
https://github.com/amacd31/gr4j
6666
6767
"""
68-
Base.@kwdef mutable struct GR4JNode{P, A<:Real} <: GRNJNode
69-
@network_node
68+
Base.@kwdef mutable struct GR4JNode{P, A<:AbstractFloat} <: GRNJNode
69+
const name::String
70+
const area::A
7071

7172
# parameters
7273
X1::P = Param(350.0, bounds=(1.0, 1500.0))
@@ -80,21 +81,37 @@ Base.@kwdef mutable struct GR4JNode{P, A<:Real} <: GRNJNode
8081
# x4 : time base of unit hydrograph UH1 (days, > 0.5)
8182

8283
# stores
83-
p_store::Array{A} = [0.0]
84-
r_store::Array{A} = [0.0]
84+
p_store::Vector{A} = [0.0]
85+
r_store::Vector{A} = [0.0]
8586

86-
UH1::Array{Array{A}} = []
87-
UH2::Array{Array{A}} = []
87+
UH1::Vector{Vector{A}} = []
88+
UH2::Vector{Vector{A}} = []
8889

89-
outflow::Array{A} = []
90+
uh1_ordinates::Vector{A} = []
91+
uh2_ordinates::Vector{A} = []
92+
93+
outflow::Vector{A} = []
9094
end
9195

9296

93-
# function prep_state!(node::HyModNode, sim_length::Int64)
94-
# node.UH1 = fill(undef, sim_length)
95-
# node.UH2 = fill(undef, sim_length)
96-
# node.outflow = fill(undef, sim_length)
97-
# end
97+
function prep_state!(node::GR4JNode, timesteps::Int64)
98+
resize!(node.p_store, timesteps+1)
99+
resize!(node.r_store, timesteps+1)
100+
node.p_store[2:end] .= 0.0
101+
node.r_store[2:end] .= 0.0
102+
103+
# Prep cache
104+
X4 = node.X4.val
105+
nUH1 = Int(ceil(X4))
106+
nUH2 = Int(ceil(2.0*X4))
107+
cUH1, cUH2 = fill(0.0, nUH1), fill(0.0, nUH2)
108+
node.UH1 = fill(cUH1, timesteps)
109+
node.UH2 = fill(cUH2, timesteps)
110+
node.uh1_ordinates = Vector{Float64}(undef, nUH1)
111+
node.uh2_ordinates = Vector{Float64}(undef, nUH2)
112+
113+
node.outflow = fill(0.0, timesteps)
114+
end
98115

99116

100117
function GR4JNode(name::String, spec::Dict)
@@ -129,22 +146,6 @@ function GR4JNode(name::String, spec::Dict)
129146
end
130147

131148

132-
"""
133-
run_node!(node::GR4JNode, climate::Climate)
134-
135-
Run GR4J node for all time steps in given climate sequence.
136-
"""
137-
function run_node!(node::GR4JNode, climate::Climate; inflow=nothing, extraction=nothing, exchange=nothing)
138-
timesteps = sim_length(climate)
139-
# prep_state!(node, sim_length)
140-
for ts in 1:timesteps
141-
run_timestep!(node, climate, ts; inflow=inflow, extraction=extraction, exchange=exchange)
142-
end
143-
144-
return node.outflow
145-
end
146-
147-
148149
"""
149150
run_timestep!(node::GR4JNode, climate::Climate, timestep::Int;
150151
inflow::Float64, extraction::Float64, exchange::Float64)
@@ -158,8 +159,10 @@ function run_timestep!(node::GR4JNode, climate::Climate, timestep::Int; inflow=n
158159
end
159160

160161

161-
function run_timestep!(node::GR4JNode, rain, et, ts; inflow=nothing, extraction=nothing, exchange=nothing)
162-
res = run_gr4j(rain, et, node.X1, node.X2, node.X3, node.X4, node.area, node.p_store[end], node.r_store[end])
162+
function run_timestep!(node::GR4JNode, rain::Float64, et::Float64, ts::Int64; inflow=nothing, extraction=nothing, exchange=nothing)
163+
uh1_cache = node.uh1_ordinates
164+
uh2_cache = node.uh2_ordinates
165+
res = run_gr4j(rain, et, node.X1.val, node.X2.val, node.X3.val, node.X4.val, node.area, node.UH1[ts], node.UH2[ts], uh1_cache, uh2_cache, node.p_store[ts], node.r_store[ts])
163166
Q, p_s, r_s, UH1, UH2 = res
164167

165168
node_name = node.name
@@ -171,7 +174,7 @@ function run_timestep!(node::GR4JNode, rain, et, ts; inflow=nothing, extraction=
171174
Q = Q + in_flow + ex - wo
172175
end
173176

174-
update_state!(node, p_s, r_s, Q, UH1, UH2)
177+
update_state!(node, ts, p_s, r_s, Q, UH1, UH2)
175178
end
176179

177180

@@ -182,6 +185,16 @@ function update_state!(node::GR4JNode, ps, rs, q, UH1, UH2)
182185
push!(node.UH1, UH1)
183186
push!(node.UH2, UH2)
184187
end
188+
function update_state!(node::GR4JNode, ts::Int64, ps, rs, q, UH1, UH2)::Nothing
189+
node.p_store[ts+1] = ps
190+
node.r_store[ts+1] = rs
191+
node.outflow[ts] = q
192+
193+
node.UH1[ts] = UH1
194+
node.UH2[ts] = UH2
195+
196+
return nothing
197+
end
185198

186199

187200
function update_params!(node::GR4JNode, X1::Float64, X2::Float64, X3::Float64, X4::Float64)::Nothing
@@ -218,12 +231,12 @@ end
218231
219232
Determine unit hydrograph ordinates.
220233
"""
221-
function s_curve(t, x4; uh2::Bool = false)::Float64
234+
function s_curve(t::F, x4::F; uh2::Bool = false)::F where {F<:Float64}
222235
if t <= 0.0
223236
return 0.0
224237
end
225238

226-
ordinate::Float64 = 0.0
239+
ordinate::F = 0.0
227240
if t < x4
228241
ordinate = (t/x4)^2.5
229242
else
@@ -258,14 +271,10 @@ Generated simulated streamflow for given rainfall and potential evaporation.
258271
# Returns
259272
- tuple of simulated outflow [ML/day], and intermediate states: p_store, r_store, UH1, UH2
260273
"""
261-
function run_gr4j(P, E, X1, X2, X3, X4, area, p_store=0.0, r_store=0.0)::Tuple
262-
nUH1 = Int(ceil(X4))
263-
nUH2 = Int(ceil(2.0*X4))
264-
265-
uh1_ordinates = Array{Float64}(undef, nUH1)
266-
uh2_ordinates = Array{Float64}(undef, nUH2)
267-
268-
UH1, UH2 = fill(0.0, nUH1), fill(0.0, nUH2)
274+
function run_gr4j(P::F, E::F, X1::F, X2::F, X3::F, X4::F, area::F, UH1::Vector{Float64}, UH2::Vector{Float64}, uh1_ordinates::Vector{Float64}, uh2_ordinates::Vector{Float64}, p_store=0.0, r_store=0.0)::Tuple where {F<:Float64}
275+
# Main.@infiltrate
276+
nUH1::Int64 = Int(ceil(X4))
277+
nUH2::Int64 = Int(ceil(2.0*X4))
269278

270279
@inbounds for t in 2:(nUH1+1)
271280
t_f = Float64(t)
@@ -277,7 +286,7 @@ function run_gr4j(P, E, X1, X2, X3, X4, area, p_store=0.0, r_store=0.0)::Tuple
277286
uh2_ordinates[t - 1] = s_curve(t_f, X4, uh2=true) - s_curve(t_f-1.0, X4, uh2=true)
278287
end
279288

280-
Q = 0.0
289+
Q::F = 0.0
281290
if P > E
282291
net_evap = 0.0
283292
scaled_net_precip = min((P - E)/X1, 13.0)
@@ -303,12 +312,11 @@ function run_gr4j(P, E, X1, X2, X3, X4, area, p_store=0.0, r_store=0.0)::Tuple
303312

304313
p_store = p_store - net_evap + reservoir_production
305314

306-
tmp_a = (p_store / 2.25 / X1)^4
307-
tmp_b = (1 + tmp_a)^0.25
315+
tmp_a::F = (p_store / 2.25 / X1)^4
316+
tmp_b::F = (1 + tmp_a)^0.25
308317
percolation = p_store / tmp_b
309318

310319
routed_volume = routed_volume + (p_store-percolation)
311-
p_store = percolation
312320

313321
@inbounds for i in 1:nUH1-1
314322
UH1[i] = UH1[i+1] + uh1_ordinates[i]*routed_volume
@@ -321,7 +329,7 @@ function run_gr4j(P, E, X1, X2, X3, X4, area, p_store=0.0, r_store=0.0)::Tuple
321329
UH2[end] = uh2_ordinates[end] * routed_volume
322330

323331
tmp_a = (r_store / X3)^3.5
324-
groundwater_exchange = X2 * tmp_a
332+
groundwater_exchange::F = X2 * tmp_a
325333
r_store = max(0.0, r_store + UH1[1] * 0.9 + groundwater_exchange)
326334

327335
tmp_a = (r_store / X3)^4
@@ -333,5 +341,5 @@ function run_gr4j(P, E, X1, X2, X3, X4, area, p_store=0.0, r_store=0.0)::Tuple
333341

334342
Q = (QR + QD) * area
335343

336-
return Q, p_store, r_store, UH1, UH2
344+
return Q, percolation, r_store, UH1, UH2
337345
end

src/HyModNode.jl

Lines changed: 16 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,8 @@ Adapted with kind permission from: https://github.com/jdherman/GRA-2020-SALib
1717
https://doi.org/10.5194/hess-17-149-2013
1818
"""
1919
Base.@kwdef mutable struct SimpleHyModNode{P, A<:AbstractFloat} <: HyModNode
20-
name::String
21-
area::A
20+
const name::String
21+
const area::A
2222

2323
# parameters
2424
Sm_max::P = Param(250.0, bounds=(1.0, 500.0))
@@ -68,37 +68,23 @@ function SimpleHyModNode(name::String, area::Float64, sm_max::Float64, B::Float6
6868
end
6969

7070

71-
function prep_state!(node::HyModNode, sim_length::Int64)
72-
node.Sm = fill(0.0, sim_length+1)
73-
node.Sf1 = fill(0.0, sim_length+1)
74-
node.Sf2 = fill(0.0, sim_length+1)
75-
node.Sf3 = fill(0.0, sim_length+1)
76-
node.Ss1 = fill(0.0, sim_length+1)
77-
78-
node.outflow = fill(0.0, sim_length)
79-
end
71+
function prep_state!(node::SimpleHyModNode, sim_length::Int64)::Nothing
72+
resize!(node.Sm, sim_length+1)
73+
resize!(node.Sf1, sim_length+1)
74+
resize!(node.Sf2, sim_length+1)
75+
resize!(node.Sf3, sim_length+1)
76+
resize!(node.Ss1, sim_length+1)
8077

78+
node.Sm[2:end] .= 0.0
79+
node.Sf1[2:end] .= 0.0
80+
node.Sf2[2:end] .= 0.0
81+
node.Sf3[2:end] .= 0.0
82+
node.Ss1[2:end] .= 0.0
8183

82-
"""
83-
run_node!(node::HyModNode, climate::Climate;
84-
inflow=nothing, extraction=nothing, exchange=nothing)
84+
resize!(node.outflow, sim_length)
85+
node.outflow .= 0.0
8586

86-
Run given HyMod node for entire simulation period.
87-
"""
88-
function run_node!(
89-
node::HyModNode, climate::Climate;
90-
inflow=nothing, extraction=nothing, exchange=nothing
91-
)
92-
timesteps = sim_length(climate)
93-
prep_state!(node, timesteps)
94-
95-
# Identify relevant climate data
96-
P_and_ET = Matrix{Float64}(climate_values(node, climate))
97-
98-
for ts in 1:timesteps
99-
run_timestep!(node, P_and_ET[ts, 1], P_and_ET[ts, 2], ts;
100-
inflow=inflow, extraction=extraction, exchange=exchange)
101-
end
87+
return nothing
10288
end
10389

10490
"""

0 commit comments

Comments
 (0)