-
Notifications
You must be signed in to change notification settings - Fork 23
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Refactor SBM
concept
#487
Refactor SBM
concept
#487
Conversation
* Remove vertical concept `FLEXTopo` * Remove vertical concept `HBV` * Update download test data for build * Cleanup docs Removed `HBV` and `FLEXTopo` concepts. * Removed code related to `FLEXTopo` Use of extra dim `classes`. * Remove and change river and land `inwater` functions As a result of removing `HBV` and `FLEXTopo` concepts. * Update changelog * Fix typo in docs Co-authored-by: JoostBuitink <[email protected]> --------- Co-authored-by: JoostBuitink <[email protected]>
* Cleanup metadata structs Remove `exchange`, `grid_type` and `grid_location` from metadata structs. `grid_type` is not required, it is already implemented as part of BMI. `exhange` and `grid_location` are now implemented without the use of the FieldMetadata package. * Update changelog * Fix `exchange` function for `ShallowWaterLand` * Add tests * Move `exchange` and `grid_location` functions To file bmi.jl as these functions are quite specific to BMI functionality. * Add `v1` branch to CI
* Add configuration file for Julia Formatter * Add format on save in vscode settings file * Remove .vscode from gitignore * Format all Julia files * Sync wflow's style guide with ribasim's style guide * Update formatting * Update .vscode/settings.json * Remove outdated comments * Add workaround for VS Code bugs
* Stop using local JULIAUP_DEPOT_PATH * Avoid error when override is already set Co-authored-by: Martijn Visser <[email protected]>
In `update` functions use `model` for interception, snow and glacier models. Add wrapper methods for snow runoff and glacier melt.
This is with kinematic wave routing. Renamed the model for `SBM` from `soil` to `bucket` as it is more related to this type of model (Simple Bucket Model) than a soil model that for example solves the Richards equation for the unsaturated zone and upper part of the saturated zone.
Also removed the vertical_process.jl file.
Mostly water demand and allocation functionality. All tests pass.
Add water demand and allocation, and snow transport computations.
Add separate functions to update the boundary conditions of the snow and `SBM` models.
In addition: - add a parameter set for LandParameters (to share accross model components). - make use of type NamedTuple (for external models) in the update of models and boundary conditions, these external models provide input to the models and boundary conditions. - move shared parameters (vegetation and land) to file Parameters.jl.
In update of models and boundaries of `LandHydrologySBM`
It only contains two parameters that are not shared that much across different models of `LandHydrologySBM`.
Use the struct name for outer constructor functions.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I cannot comment on functionality yet, but I made some code quality suggestions
src/utils.jl
Outdated
for i in eachindex(ssf) | ||
ssfmax[i] = ((kh_0[i] * slope[i]) / f[i]) * (1.0 - exp(-f[i] * soilthickness[i])) | ||
ssf[i] = | ||
((kh_0[i] * slope[i]) / f[i]) * | ||
(exp(-f[i] * zi[i]) - exp(-f[i] * soilthickness[i])) * | ||
dw[i] | ||
end |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks like this can be written without an explicit loop
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, good catch!
src/vegetation/canopy.jl
Outdated
"Initialize interception model variables" | ||
function InterceptionVariables( | ||
n; | ||
canopy_potevap::Vector{T} = fill(mv, n), | ||
interception_rate::Vector{T} = fill(mv, n), | ||
canopy_storage::Vector{T} = fill(0.0, n), | ||
stemflow::Vector{T} = fill(mv, n), | ||
throughfall::Vector{T} = fill(mv, n), | ||
) where {T} | ||
return InterceptionVariables(; | ||
canopy_potevap = canopy_potevap, | ||
interception_rate = interception_rate, | ||
canopy_storage = canopy_storage, | ||
stemflow = stemflow, | ||
throughfall = throughfall, | ||
) | ||
end |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can be done with default values as mentioned before
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Make now use of a simple constructor as in water_demand,jl.
Co-authored-by: Bart de Koning <[email protected]>
Co-authored-by: Bart de Koning <[email protected]>
Simplify external constructors.
Rewrite without explicit loop.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Very nice work, makes the code a lot more futureproof! I have a couple of improvements/questions, see the comments for those!
src/parameters.jl
Outdated
sl = ncread( | ||
nc, | ||
config, | ||
"vertical.vegetation_parameter_set.specific_leaf"; | ||
optional = false, | ||
sel = inds, | ||
type = Float, | ||
) | ||
swood = ncread( | ||
nc, | ||
config, | ||
"vertical.vegetation_parameter_set.storage_wood"; | ||
optional = false, | ||
sel = inds, | ||
type = Float, | ||
) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would it be an idea to actually match the names of these parameter to what we set in the toml now as well? So sl
would become specific_leaf
(or maybe even storage_specific_leaf
to be in line with storage_wood
?), and swood
would become storage_wood
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree, was planning to actually look into this for all variables in Wflow as part of another PR. Anyway, good to already change these parameter names!
src/sbm.jl
Outdated
if modelsnow && modelglacier | ||
glacier_bc = SnowStateBC{Float}(; snow_storage = snow_model.variables.snow_storage) | ||
glacier_model = GlacierHbvModel(nc, config, inds, dt, glacier_bc) | ||
else |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe good to throw a warning when modelglacier=true && modelsnow=false
. It does not make sense to use this combination, but at least there will be some feedback to the user that glaciers will not be accounted for.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree and added a warning.
src/sbm_model.jl
Outdated
cellength = abs(mean(diff(x_nc))) | ||
|
||
sizeinmetres = get(config.model, "sizeinmetres", false)::Bool | ||
xl, yl = cell_lengths(y, cellength, sizeinmetres) | ||
riverfrac = river_fraction(river, riverlength, riverwidth, xl, yl) | ||
|
||
lsm = LandHydrologySBM(nc, config, riverfrac, inds) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
in sbm.jl
this is called land_hydrology_model
, so to be consistent, maybe change this to lhm
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fair point, changed to lhm
.
src/soil/soil.jl
Outdated
if haskey(config.input.vertical.soil.parameters, "et_reftopot") | ||
@warn string( | ||
"The `et_reftopot` key in `[input.vertical.soil.parameters]` is now called ", | ||
"`kc`. Please update your TOML file.", | ||
) | ||
end |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For v1.0, I think we can get rid of this warning, right?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, removed the warning.
src/vegetation/canopy.jl
Outdated
e_r = ncread( | ||
nc, | ||
config, | ||
"vertical.interception.parameters.eoverr"; | ||
sel = inds, | ||
defaults = 0.1, | ||
type = Float, | ||
) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Best to match the names of the parameters here (either e_r
or eoverr
)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good point, changed it to e_r
.
-Use consistently `!` for functions that are mutating, and return always `nothing` for these functions. - Fix missing `canonicalize` from `Dates`
Co-authored-by: JoostBuitink <[email protected]>
…low.jl into refactor/sbm_concept
As of Julia 1.7 there is no difference and `isnothing` is also used in other code sections.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As far as I can tell these changes look good. In Ribasim we don't have the mandatory return
at the end of each function, but that is a matter of taste.
As briefly disscussed with @verseve, I am in favor of splitting the parameters and the states (i.e. the unknowns you are solving for) in your data structure, but let's leave that as a follow up. The main reason for splitting those is that it is more in alignment with the SciML standard.
"Struct to store water demand model variables" | ||
@get_units @grid_loc @with_kw struct DemandVariables{T} | ||
irri_demand_gross::Vector{T} # irrigation gross demand [mm Δt⁻¹] | ||
nonirri_demand_gross::Vector{T} # non-irrigation gross demand [mm Δt⁻¹] | ||
total_gross_demand::Vector{T} # total gross demand [mm Δt⁻¹] | ||
surfacewater_demand::Vector{T} # demand from surface water [mm Δt⁻¹] | ||
groundwater_demand::Vector{T} # demand from groundwater [mm Δt⁻¹] | ||
end | ||
|
||
"Initialize water demand variables" | ||
function DemandVariables(T::Type{<:AbstractFloat}, n::Int) | ||
return DemandVariables{T}(; | ||
irri_demand_gross = zeros(T, n), | ||
nonirri_demand_gross = zeros(T, n), | ||
total_gross_demand = zeros(T, n), | ||
surfacewater_demand = zeros(T, n), | ||
groundwater_demand = zeros(T, n), | ||
) | ||
end |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I still think things like this can be squashed into one by using default field values allowed by @with_kw
, or is there a specific reason you're not doing that?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, not a specific reason, I would say it is a matter of taste.
inds_river = network.river.indices_allocation_areas | ||
inds_land = network.land.indices_allocation_areas | ||
res_index = network.river.reservoir_index | ||
lake_index = network.river.lake_index |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
inds_river = network.river.indices_allocation_areas | |
inds_land = network.land.indices_allocation_areas | |
res_index = network.river.reservoir_index | |
lake_index = network.river.lake_index | |
(; indices_allocation_area, resevoir_index, lake_index) = network.river | |
inds_land = network.land.indices_allocation_areas |
The only disadvantage of this is that you have to use the exact name of the field
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, and although a small disadvantage: the naming is quite different now between inds_land
and indices_allocation_area
. Suggest to keep the original naming.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM!
Yes, note that this PR made a start with this by using the fields |
Issue addressed
Fixes #486
Explanation
See issue #486. Additionally, the vertical concepts
HBV
andFLEXTopo
have been removed from the code, see also Discussion #342.Checklist
master