1
1
"""
2
- HydroBucket(; funcs::Vector{<:AbstractHydroFlux}, dfuncs::Vector{<:AbstractStateFlux}=StateFlux[], name::Union{Symbol,Nothing}=nothing, sort_funcs::Bool=false)
2
+ HydroBucket(;
3
+ funcs::Vector{<:AbstractHydroFlux},
4
+ dfuncs::Vector{<:AbstractStateFlux}=StateFlux[],
5
+ name::Union{Symbol,Nothing}=nothing,
6
+ sort_funcs::Bool=false
7
+ )
3
8
4
- Represents a hydrological bucket model component.
9
+ Represents a hydrological bucket model component that handles both single-node and multi-node computations .
5
10
6
11
# Arguments
7
- - `funcs::Vector{<:AbstractHydroFlux}`: A vector of flux functions that describe the hydrological processes.
8
- - `dfuncs::Vector{<:AbstractStateFlux}`: A vector of state derivative functions (default is an empty vector of StateFlux).
9
- - `name::Union{Symbol,Nothing}`: Optional name for the bucket. If not provided, a name will be automatically generated from state variable names.
10
- - `sort_funcs::Bool`: Whether to sort the flux functions (default is false).
12
+ - `funcs::Vector{<:AbstractHydroFlux}`: Vector of flux functions describing hydrological processes
13
+ - `dfuncs::Vector{<:AbstractStateFlux}`: Vector of state derivative functions for ODE calculations (default: empty)
14
+ - `name::Union{Symbol,Nothing}`: Optional bucket identifier. Defaults to auto-generated name from state variables
15
+ - `sort_funcs::Bool} `: Whether to topologically sort flux functions based on dependencies (default: false)
11
16
12
17
# Fields
13
- - `funcs::Vector{<:AbstractHydroFlux}`: Vector of flux functions describing hydrological processes.
14
- - `dfuncs::Vector{<:AbstractStateFlux}`: Vector of state derivative functions for ODE calculations.
15
- - `flux_func::Function`: Combined function for calculating all hydrological fluxes.
16
- - `ode_func::Union{Nothing,Function}`: Function for ordinary differential equations (ODE) calculations, or nothing if no ODE calculations are needed.
17
- - `meta::HydroMeta`: Contains metadata about the bucket, including input, output, state, parameter, and neural network names.
18
+ - `fluxes::Vector{<:AbstractHydroFlux}`: Vector of flux functions describing hydrological processes
19
+ - `dfluxes::Vector{<:AbstractStateFlux}`: Vector of state derivative functions for ODE calculations
20
+ - `flux_func::Function`: Generated function for single-node flux calculations
21
+ - `multi_flux_func::Function`: Generated function for multi-node parallel flux calculations
22
+ - `ode_func::Union{Nothing,Function}`: Generated function for single-node ODE calculations
23
+ - `multi_ode_func::Union{Nothing,Function}`: Generated function for multi-node parallel ODE calculations
24
+ - `meta::ComponentVector`: Metadata containing model structure information
18
25
19
26
# Description
20
- HydroBucket is a structure that encapsulates the behavior of a hydrological bucket model.
21
- It combines multiple flux functions and state derivative functions to model water movement
22
- and storage within a hydrological unit.
23
-
24
- The structure automatically extracts relevant information from the provided functions to
25
- populate the metadata, which includes names of:
26
- - Inputs: Variables that drive the model
27
- - Outputs: Variables produced by the model
28
- - States: Internal model states that evolve over time
29
- - Parameters: Model parameters that control behavior
30
- - Neural Networks: Any neural network components (if applicable)
31
-
32
- The `flux_func` and `ode_func` are constructed based on the provided `funcs` and `dfuncs`,
33
- enabling efficient calculation of fluxes and state changes over time.
34
-
35
- This structure is particularly useful for building complex hydrological models by combining
36
- multiple HydroBucket instances to represent different components of a water system.
37
-
27
+ HydroBucket is a type-stable implementation of a hydrological bucket model that supports both
28
+ single-node and distributed (multi-node) computations. It automatically generates optimized
29
+ functions for flux and ODE calculations based on the provided process functions.
30
+
31
+ ## Model Structure
32
+ The bucket model consists of:
33
+ - Process functions (`fluxes`): Define water movement between storages
34
+ - State derivatives (`dfluxes`): Define rate of change for state variables
35
+ - Generated functions: Optimized implementations for both single and multi-node calculations
36
+
37
+ ## Metadata Components
38
+ The `meta` field tracks:
39
+ - `inputs`: External forcing variables (e.g., precipitation, temperature)
40
+ - `outputs`: Model-generated variables (e.g., runoff, evaporation)
41
+ - `states`: Internal storage variables (e.g., soil moisture, groundwater)
42
+ - `params`: Model parameters controlling process behavior
43
+ - `nn_vars`: Neural network components (if any) for hybrid modeling
44
+
45
+ ## Performance Features
46
+ - Type-stable computations for both single and multi-node cases
47
+ - Efficient broadcasting operations for vectorized calculations
48
+ - Automatic function generation with optimized broadcasting
49
+ - Support for ComponentArray parameters for structured data handling
50
+
51
+ ## Usage Notes
52
+ 1. For single-node simulations: Use `flux_func` and `ode_func`
53
+ 2. For multi-node simulations: Use `multi_flux_func` and `multi_ode_func`
54
+ 3. Parameters should be provided as ComponentVector for type stability
55
+ 4. Broadcasting operations are automatically handled for multi-node cases
56
+
57
+ See also: [`AbstractHydroFlux`](@ref), [`AbstractStateFlux`](@ref), [`ComponentVector`](@ref)
38
58
"""
39
59
struct HydroBucket{S} <: AbstractBucket
40
60
" Name of the bucket"
@@ -43,13 +63,13 @@ struct HydroBucket{S} <: AbstractBucket
43
63
fluxes:: Vector{<:AbstractHydroFlux}
44
64
" Vector of state derivative functions for ODE calculations."
45
65
dfluxes:: Vector{<:AbstractStateFlux}
46
- " Generated function for calculating all hydrological fluxes."
66
+ " Generated function for calculating all hydrological fluxes. (Supports single-node data) "
47
67
flux_func:: Function
48
- " Generated function for calculating all hydrological fluxes."
68
+ " Generated function for calculating all hydrological fluxes. (Supports multi-nodes data) "
49
69
multi_flux_func:: Function
50
- " Generated function for ordinary differential equations (ODE) calculations, or nothing if no ODE calculations are needed."
70
+ " Generated function for ordinary differential equations (ODE) calculations, or nothing if no ODE calculations are needed. (Supports single-node data) "
51
71
ode_func:: Union{Nothing,Function}
52
- " Generated function for ordinary differential equations (ODE) calculations, or nothing if no ODE calculations are needed."
72
+ " Generated function for ordinary differential equations (ODE) calculations, or nothing if no ODE calculations are needed. (Supports multi-nodes data) "
53
73
multi_ode_func:: Union{Nothing,Function}
54
74
" Metadata about the bucket, including input, output, state, parameter, and neural network names."
55
75
meta:: ComponentVector
@@ -118,10 +138,8 @@ The input dimensions must match the number of input variables defined in the mod
118
138
Required parameters and initial states must be present in the pas argument.
119
139
"""
120
140
function (ele:: HydroBucket{true} )(
121
- input:: AbstractArray{T,2} ,
122
- pas:: ComponentVector ;
123
- config:: NamedTuple = NamedTuple (),
124
- kwargs... ,
141
+ input:: AbstractArray{T,2} , pas:: ComponentVector ;
142
+ config:: NamedTuple = NamedTuple (), kwargs...
125
143
) where {T}
126
144
# * get kwargs
127
145
solver = get (config, :solver , ManualSolver {true} ())
@@ -139,15 +157,13 @@ function (ele::HydroBucket{true})(
139
157
end
140
158
141
159
(ele:: HydroBucket{false} )(input:: AbstractArray{T,2} , pas:: ComponentVector ; kwargs... ) where {T} = begin
142
- flux_output = ele. flux_func (input, nothing , pas)
160
+ flux_output = ele. flux_func (eachslice ( input, dims = 1 ) , nothing , pas)
143
161
permutedims (reduce (hcat, flux_output))
144
162
end
145
163
146
164
function (ele:: HydroBucket{true} )(
147
- input:: AbstractArray{T,3} ,
148
- pas:: ComponentVector ;
149
- config:: NamedTuple = NamedTuple (),
150
- kwargs... ,
165
+ input:: AbstractArray{T,3} , pas:: ComponentVector ;
166
+ config:: NamedTuple = NamedTuple (), kwargs...
151
167
) where {T}
152
168
input_dims, num_nodes, time_len = size (input)
153
169
@@ -159,15 +175,11 @@ function (ele::HydroBucket{true})(
159
175
timeidx = get (config, :timeidx , collect (1 : size (input, 3 )))
160
176
161
177
# * prepare states parameters and nns
162
- params = view (pas, :params )
163
- nn_params = isempty (get_nn_vars (ele)) ? ones (eltype (pas), num_nodes) : view (pas, :nns )
164
- expand_params = ComponentVector (NamedTuple {Tuple(get_param_names(ele))} ([params[p][ptyidx] for p in get_param_names (ele)]))
165
- new_pas = ComponentVector (params= expand_params, nns= nn_params)
166
- initstates_mat = view (reshape (Vector (view (pas, :initstates )), num_nodes, :)' , :, styidx)
178
+ new_pas = expand_component_params (pas, ptyidx)
179
+ initstates_mat = expand_component_initstates (pas, styidx)
167
180
168
181
# * prepare input function
169
- input_reshape = reshape (input, input_dims * num_nodes, time_len)
170
- itpfuncs = interp (input_reshape, timeidx)
182
+ itpfuncs = interp (reshape (input, input_dims * num_nodes, time_len), timeidx)
171
183
solved_states = solver (
172
184
(u, p, t) -> begin
173
185
tmp_input = reshape (itpfuncs (t), input_dims, num_nodes)
@@ -182,16 +194,11 @@ function (ele::HydroBucket{true})(
182
194
end
183
195
184
196
function (ele:: HydroBucket{false} )(
185
- input:: AbstractArray{T,3} ,
186
- pas:: ComponentVector ;
187
- config:: NamedTuple = NamedTuple (),
188
- kwargs... ,
197
+ input:: AbstractArray{T,3} , pas:: ComponentVector ;
198
+ config:: NamedTuple = NamedTuple (), kwargs... ,
189
199
) where {T}
190
200
ptyidx = get (config, :ptyidx , 1 : size (input, 2 ))
191
- params = view (pas, :params )
192
- nn_params = isempty (get_nn_vars (ele)) ? ones (eltype (pas), size (input, 2 )) : view (pas, :nns )
193
- expand_params = ComponentVector (NamedTuple {Tuple(get_param_names(ele))} ([params[p][ptyidx] for p in get_param_names (ele)]))
194
- new_pas = ComponentVector (params= expand_params, nns= nn_params)
201
+ new_pas = expand_component_params (pas, ptyidx)
195
202
# * run other functions
196
203
output = ele. flux_func (eachslice (input, dims= 1 ), nothing , new_pas)
197
204
permutedims (reduce ((m1, m2) -> cat (m1, m2, dims= 3 ), output), (3 , 1 , 2 ))
0 commit comments