From 3d50d76b5e7e7f74e229168ae63baa59b3c3569e Mon Sep 17 00:00:00 2001 From: Willem van Verseveld Date: Wed, 18 Dec 2024 15:35:19 +0100 Subject: [PATCH] Update check of state variables Checking list of standard names (`input.state.variables`). --- src/io.jl | 4 +- src/standard_name.jl | 2 + src/states.jl | 207 ++++++++++++++++--------------------------- src/utils.jl | 4 +- test/run_sbm.jl | 2 +- 5 files changed, 79 insertions(+), 140 deletions(-) diff --git a/src/io.jl b/src/io.jl index 8edb2792a..36dc41562 100644 --- a/src/io.jl +++ b/src/io.jl @@ -979,9 +979,7 @@ function prepare_writer( # create a separate state output netCDF that will hold the last timestep of all states # but only if config.state.path_output has been set if haskey(config, "state") && haskey(config.state, "path_output") - # TODO: revert back to state checking - # state_ncnames = check_states(config) - state_ncnames = ncnames(config.state.variables) + state_ncnames = check_states(config) state_map = out_map(state_ncnames, modelmap) nc_state_path = output_path(config, config.state.path_output) @info "Create a state output netCDF file `$nc_state_path`." diff --git a/src/standard_name.jl b/src/standard_name.jl index ee512d6cf..2d10d7a66 100644 --- a/src/standard_name.jl +++ b/src/standard_name.jl @@ -19,6 +19,8 @@ const sbm_standard_name_map = Dict{String, ComposedFunction}( "river_water__volume" => @optic(_.lateral.river.variables.volume), "floodplain_water__volume" => @optic(_.lateral.river.floodplain.variables.volume), "floodplain_water__depth" => @optic(_.lateral.river.floodplain.variables.h), + "floodplain_water__volume_flow_rate" => + @optic(_.lateral.river.floodplain.variables.q), "reservoir_water__volume" => @optic(_.lateral.river.boundary_conditions.reservoir.variables.volume), "soil_water_sat-zone_top__recharge_volume_flux" => diff --git a/src/states.jl b/src/states.jl index f2174cb20..b6241b013 100644 --- a/src/states.jl +++ b/src/states.jl @@ -2,11 +2,11 @@ get_snow_states(model_type::AbstractString) Extract required snow model states, given a certain `model_type`. Returns a tuple with the -required states (internal names as symbols). +required states (standard name). """ function get_snow_states(model_type::AbstractString) if model_type == "sbm" || model_type == "sbm_gwf" - states = (:snow_storage, :snow_water) + states = ("snowpack~dry__leq-depth", "snowpack~liquid__depth") elseif model_type == "sediment" states = () else @@ -19,11 +19,11 @@ end get_glacier_states(model_type::AbstractString) Extract required glacier model states, given a certain `model_type`. Returns a tuple with -the required states (internal names as symbols). +the required states (standard name). """ function get_glacier_states(model_type::AbstractString) if model_type == "sbm" || model_type == "sbm_gwf" - states = (:glacier_store,) + states = ("glacier_ice__leq-volume",) elseif model_type == "sediment" states = () else @@ -36,11 +36,11 @@ end get_interception_states(model_type::AbstractString) Extract required interception model states, given a certain `model_type`. Returns a tuple -with the required states (internal names as symbols). +with the required states (standard name). """ function get_interception_states(model_type::AbstractString) if model_type == "sbm" || model_type == "sbm_gwf" - states = (:canopy_storage,) + states = ("vegetation_canopy_water__storage",) elseif model_type == "sediment" states = () else @@ -58,9 +58,16 @@ modelled. Returns a tuple with the required states (internal names as symbols). function get_soil_states(model_type::AbstractString; snow = false) if model_type == "sbm" || model_type == "sbm_gwf" if snow - states = (:satwaterdepth, :tsoil, :ustorelayerdepth) + states = ( + "soil_water_sat-zone__depth", + "soil_surface__temperature", + "soil_water_unsat-zone__depth-per-soil_layer", + ) else - states = (:satwaterdepth, :ustorelayerdepth) + states = ( + "soil_water_sat-zone__depth", + "soil_water_unsat-zone__depth-per-soil_layer", + ) end elseif model_type == "sediment" states = () @@ -72,51 +79,28 @@ end function get_sediment_states() states = ( - :leftover_clay, - :leftover_silt, - :leftover_sand, - :leftover_sagg, - :leftover_lagg, - :leftover_gravel, - :store_clay, - :store_silt, - :store_sand, - :store_sagg, - :store_lagg, - :store_gravel, - :clay, - :silt, - :sand, - :sagg, - :lagg, - :gravel, + "river_water_clay__mass", + "river_bed_clay__mass", + "river_water_gravel__mass", + "river_bed_gravel__mass", + "river_water_aggregates~large__mass", + "river_bed_aggregates~large__mass", + "river_water_clay__mass_flow_rate", + "river_water_gravel__mass_flow_rate", + "river_water_aggregates~large__mass_flow_rate", + "river_water_aggregates~small__mass_flow_rate", + "river_water_sand__mass_flow_rate", + "river_water_silt__mass_flow_rate", + "river_water_aggregates~small__mass", + "river_bed_aggregates~small__mass", + "river_water_sand__mass", + "river_bed_sand__mass", + "river_water_silt__mass", + "river_bed_silt__mass", ) return states end -""" - add_to_required_states(required_states::Tuple, key_entry::Tuple, states::Tuple) - add_to_required_states(required_states::Tuple, key_entry::Tuple, states::Nothing) - -Function to iterate through the list of `states` and add these to the `required_states` -variable. This is a tuple of tuples, that contains the correct "prefix" to the state -category (provided by the `key_entry` variable). This is added in front of each element in -the list of `states`, and the `required_states` tuple is returned. - -When `nothing` is passed as the `states`, the `required_states` variable is simply returned. -""" -function add_to_required_states(required_states::Tuple, key_entry::Tuple, states::Tuple) - for state in states - required_states = (required_states..., (key_entry..., state)) - end - - return required_states -end - -function add_to_required_states(required_states::Tuple, key_entry::Tuple, states::Nothing) - return required_states -end - """ extract_required_states(config::Config) @@ -156,12 +140,12 @@ function extract_required_states(config::Config) # Subsurface states if model_type == "sbm_gwf" - ssf_states = (:head,) + ssf_states = ("subsurface_water__hydraulic_head",) elseif model_type == "sediment" - ssf_states = nothing + ssf_states = () else do_subsurface_flow = get(config.model, "kinematic-wave_subsurface", true)::Bool - ssf_states = do_subsurface_flow ? (:ssf,) : nothing + ssf_states = do_subsurface_flow ? ("subsurface_water__volume_flow_rate",) : () end # Land states @@ -176,101 +160,58 @@ function extract_required_states(config::Config) "kinematic-wave", )::String if land_routing == "local-inertial" - land_states = (:qx, :qy, :h, :h_av) - elseif land_routing == "kinematic-wave" - if model_type == "sbm" || model_type == "sbm_gwf" - land_states = (:q, :h, :h_av) - else - land_states = (:q, :h) - end + land_states = ( + "land_surface_water__x_component_of_volume_flow_rate", + "land_surface_water__y_component_of_volume_flow_rate", + "land_surface_water__depth", + "land_surface_water__time_average_of_depth", + ) + else + land_states = ( + "land_surface_water__volume_flow_rate", + "land_surface_water__depth", + "land_surface_water__time_average_of_depth", + ) end end # River states if model_type == "sediment" river_states = get_sediment_states() - elseif model_type == "sbm" || model_type == "sbm_gwf" - river_states = (:q, :h, :h_av) else - river_states = (:q, :h) + river_states = ( + "river_water__volume_flow_rate", + "river_water__depth", + "river_water__time_average_of_depth", + ) end # Floodplain states - floodplain_states = do_floodplains ? (:q, :h) : nothing + floodplain_states = + do_floodplains ? ("floodplain_water__volume_flow_rate", "floodplain_water__depth") : + () # Lake and reservoir states - lake_states = do_lakes ? (:waterlevel,) : nothing - reservoir_states = do_reservoirs ? (:volume,) : nothing + lake_states = do_lakes ? ("lake_water_level__elevation",) : () + reservoir_states = do_reservoirs ? ("reservoir_water__volume",) : () # Paddy states - paddy_states = do_paddy ? (:h,) : nothing + paddy_states = do_paddy ? ("land_surface_water~paddy__depth",) : () + + # Add required states to a tuple, similar to the keys in the output of + # `ncnames(config.state.variables)` + required_states = snow_states..., + glacier_states..., + interception_states..., + soil_states..., + ssf_states..., + land_states..., + river_states..., + floodplain_states..., + lake_states..., + reservoir_states..., + paddy_states... - # Build required states in a tuple, similar to the keys in the output of - # `ncnames(config.state)` - required_states = () - # Add snow, glacier, interception and sbm soil states to dict - required_states = - add_to_required_states(required_states, (:vertical, :snow, :variables), snow_states) - required_states = add_to_required_states( - required_states, - (:vertical, :glacier, :variables), - glacier_states, - ) - required_states = add_to_required_states( - required_states, - (:vertical, :interception, :variables), - interception_states, - ) - required_states = - add_to_required_states(required_states, (:vertical, :soil, :variables), soil_states) - # Add subsurface states to dict - if model_type == "sbm_gwf" - key_entry = (:lateral, :subsurface, :flow, :aquifer, :variables) - else - key_entry = (:lateral, :subsurface, :variables) - end - required_states = add_to_required_states(required_states, key_entry, ssf_states) - # Add land states to dict - required_states = - add_to_required_states(required_states, (:lateral, :land, :variables), land_states) - # Add sediment states to dict - if model_type == "sediment" - required_states = add_to_required_states( - required_states, - (:lateral, :river, :sediment_flux, :variables), - river_states, - ) - else - required_states = add_to_required_states( - required_states, - (:lateral, :river, :variables), - river_states, - ) - end - # Add floodplain states to dict - required_states = add_to_required_states( - required_states, - (:lateral, :river, :floodplain, :variables), - floodplain_states, - ) - # Add lake states to dict - required_states = add_to_required_states( - required_states, - (:lateral, :river, :boundary_conditions, :lake, :variables), - lake_states, - ) - # Add reservoir states to dict - required_states = add_to_required_states( - required_states, - (:lateral, :river, :boundary_conditions, :reservoir, :variables), - reservoir_states, - ) - # Add paddy states to dict - required_states = add_to_required_states( - required_states, - (:vertical, :demand, :paddy, :variables), - paddy_states, - ) return required_states end @@ -283,7 +224,7 @@ required states. If not all states are covered, an error is thrown to warn the u provided than required, a warning is logged, and the unnecessary state is removed. """ function check_states(config::Config) - state_ncnames = ncnames(config.state) + state_ncnames = ncnames(config.state.variables) # Get required states required_states = extract_required_states(config) diff --git a/src/utils.jl b/src/utils.jl index ea57d9e36..25f05821c 100644 --- a/src/utils.jl +++ b/src/utils.jl @@ -157,9 +157,7 @@ function set_states!(instate_path, model; type = nothing, dimname = nothing) (; network, vertical, config) = model # Check if required states are covered - # TODO: revert back to state checking - # state_ncnames = check_states(config) - state_ncnames = ncnames(config.state.variables) + state_ncnames = check_states(config) # states in netCDF include dim time (one value) at index 3 or 4, 3 or 4 dims are allowed NCDataset(instate_path) do ds diff --git a/test/run_sbm.jl b/test/run_sbm.jl index e46061760..e62469a5c 100644 --- a/test/run_sbm.jl +++ b/test/run_sbm.jl @@ -313,7 +313,7 @@ config.model.floodplain_1d = true config.model.river_routing = "local-inertial" config.model.land_routing = "kinematic-wave" config.input.parameters["floodplain_water__sum_of_volume-per-depth"] = "floodplain_volume" -config.state.variables.floodplain_water__volume = "q_floodplain" +config.state.variables.floodplain_water__volume_flow_rate = "q_floodplain" config.state.variables.floodplain_water__depth = "h_floodplain" model = Wflow.initialize_sbm_model(config)