Skip to content
Merged
Show file tree
Hide file tree
Changes from 30 commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
e4c74da
Add config.jl + code cleanup
SouthEndMusic Sep 7, 2025
368a320
Don't change julia version in Manifest
SouthEndMusic Sep 8, 2025
7a96e04
Merge branch 'master' into toml_parse
SouthEndMusic Sep 8, 2025
e9a4230
Update server
SouthEndMusic Sep 8, 2025
0f34ea0
Restore server Project.toml
SouthEndMusic Sep 8, 2025
a5caf52
merge fix
SouthEndMusic Sep 8, 2025
777a66b
Another merge fix
SouthEndMusic Sep 8, 2025
6da0858
Update docs env
SouthEndMusic Sep 8, 2025
0f2e1ee
Comments addressed
SouthEndMusic Sep 10, 2025
e626f2f
Improve test coverage
SouthEndMusic Sep 10, 2025
493ab20
Ignore extra TOML fields
SouthEndMusic Sep 10, 2025
8c64e74
Do it right this time
SouthEndMusic Sep 10, 2025
f04db8e
Bring back river_gauge_* for now
SouthEndMusic Sep 10, 2025
1b51a4e
Move flexible part of input section to config.input.flexible
SouthEndMusic Sep 10, 2025
e6f288a
Make cold_start__flag mutable
SouthEndMusic Sep 10, 2025
d092f4c
Move config option validation to config.jl
SouthEndMusic Sep 11, 2025
7059e91
A lot of stuff
SouthEndMusic Sep 16, 2025
5a60c04
Fix Manifest
SouthEndMusic Sep 16, 2025
2a6d305
run CI RUN
SouthEndMusic Sep 16, 2025
d6ef678
trivial sbm.jl change
SouthEndMusic Sep 16, 2025
d103d6a
Merge branch 'master' into toml_parse
SouthEndMusic Sep 16, 2025
9c79ea7
merge fix
SouthEndMusic Sep 16, 2025
3e3c9d8
Another merge fix
SouthEndMusic Sep 16, 2025
a6acf18
Fix writing config back to TOML
SouthEndMusic Sep 16, 2025
2e4ed81
Refactor without Configurations.jl
SouthEndMusic Sep 18, 2025
3ee513b
Comments addressed
SouthEndMusic Sep 18, 2025
b4a4a9d
Merge branch 'master' into toml_parse
SouthEndMusic Sep 18, 2025
2e2eb85
Merge master
SouthEndMusic Sep 18, 2025
683ca47
Merge branch 'master' into toml_parse
SouthEndMusic Sep 24, 2025
ef7343b
Comments addressed
SouthEndMusic Sep 24, 2025
9356f9b
Merge branch 'master' into toml_parse
SouthEndMusic Sep 25, 2025
72d0ecd
Only print scale and offset if they are not default
SouthEndMusic Sep 25, 2025
0206dbe
Add backticks to names and values in info message
SouthEndMusic Sep 25, 2025
433ff5f
Merge branch 'master' into toml_parse
SouthEndMusic Sep 25, 2025
6a277bd
Fix typo toml file
vers-w Sep 25, 2025
8625071
Update test value
vers-w Sep 25, 2025
9e8fec1
Set logging to silent in io.jl test
vers-w Sep 25, 2025
5bcfa41
Log message modifying NetCDF parameter
vers-w Sep 25, 2025
2dbd6a5
Fix default `dir_input` and `dir_output`
vers-w Sep 26, 2025
f73e807
Merge branch 'master' into toml_parse
SouthEndMusic Sep 29, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 11 additions & 1 deletion Wflow/Manifest.toml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 4 additions & 0 deletions Wflow/Project.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,15 @@ CFTime = "179af706-886a-5703-950a-314cd64e0468"
CompositionsBase = "a33af91c-f02d-484b-be07-31d278c5ca2b"
Dates = "ade2ca70-3891-5945-98fb-dc099432e06a"
DelimitedFiles = "8bb1440f-4735-579b-a4ab-409b98df4dab"
EnumX = "4e289a0a-7415-4d19-859d-a7e5c4648b56"
Glob = "c27321d9-0574-5035-807b-f59d2c89b15c"
Graphs = "86223c79-3864-5bf0-83f7-82e725a168b6"
LoggingExtras = "e6f89c97-d47a-5376-807f-9c37f3926c36"
NCDatasets = "85f8d34a-cbdd-5861-8df4-14fed0d494ab"
Parameters = "d96e819e-fc66-5662-9728-84c9c7592b0a"
Polyester = "f517fe37-dbe3-4b94-8317-1923a5111588"
ProgressLogging = "33c8b6b6-d38a-422a-b730-caa89a2f386c"
PropertyDicts = "f8a19df8-e894-5f55-a973-672c1158cbca"
StaticArrays = "90137ffa-7385-5640-81b9-e52037218182"
Statistics = "10745b16-79ce-11e8-11f9-7d13ad32a3b2"
TOML = "fa267f1f-6049-4f14-aa54-33bafae1ed76"
Expand All @@ -31,6 +33,7 @@ CompositionsBase = "0.1.2"
Dates = "1"
DelimitedFiles = "1"
Downloads = "1"
EnumX = "1.0.5"
Glob = "1.3"
Graphs = "1.4"
LoggingExtras = "0.4.6,0.5,1"
Expand All @@ -39,6 +42,7 @@ Parameters = "0.12"
Polyester = "0.7.10"
Polynomials = "3,4"
ProgressLogging = "0.1"
PropertyDicts = "0.2.1"
QuadGK = "2"
Random = "1"
StaticArrays = "0.12,1.0"
Expand Down
69 changes: 37 additions & 32 deletions Wflow/src/Wflow.jl
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ using Dates:
datetime2unix,
canonicalize
using DelimitedFiles: readdlm
using EnumX: @enumx, EnumX
using Glob: glob
using Graphs:
add_edge!,
Expand All @@ -43,11 +44,24 @@ using Graphs:
src,
topological_sort_by_dfs,
vertices
using LoggingExtras
using LoggingExtras:
ConsoleLogger,
Debug,
EarlyFilteredLogger,
Error,
FormatLogger,
Info,
LogLevel,
MinLevelLogger,
NullLogger,
TeeLogger,
Warn,
with_logger
using NCDatasets: NCDatasets, NCDataset, dimnames, dimsize, nomissing, defDim, defVar
using Parameters: @with_kw
using Polyester: @batch
using ProgressLogging: @progress
using PropertyDicts: PropertyDict
using StaticArrays: SVector, pushfirst, setindex
using Statistics: mean, median, quantile!, quantile
using TerminalLoggers
Expand All @@ -57,7 +71,6 @@ const CFDataset = Union{NCDataset, NCDatasets.MFDataset}
const CFVariable_MF = Union{NCDatasets.CFVariable, NCDatasets.MFCFVariable}
const VERSION =
VersionNumber(TOML.parsefile(joinpath(@__DIR__, "..", "Project.toml"))["version"])
const ROUTING_OPTIONS = (("kinematic-wave", "local-inertial"))

mutable struct Clock{T}
time::T
Expand All @@ -68,8 +81,7 @@ end
function Clock(config)
# this constructor is used by reset_clock!, since if the Clock has already
# been constructed before, the config is complete
calendar = get(config.time, "calendar", "standard")::String
starttime = cftime(config.time.starttime, calendar)
starttime = cftime(config.time.starttime, config.time.calendar)
dt = Second(config.time.timestepsecs)
return Clock(starttime, 0, dt)
end
Expand All @@ -78,32 +90,27 @@ function Clock(config, reader)
nctimes = reader.dataset["time"][:]

# if the timestep is not given, use the difference between netCDF time 1 and 2
timestepsecs = get(config.time, "timestepsecs", nothing)
if timestepsecs === nothing
if isnothing(config.time.timestepsecs)
timestepsecs = Dates.value(Second(nctimes[2] - nctimes[1]))
config.time.timestepsecs = timestepsecs
end
dt = Second(timestepsecs)
dt = Second(config.time.timestepsecs)

# if the config file does not have a start or endtime, follow the netCDF times
# and add them to the config
starttime = get(config.time, "starttime", nothing)
if starttime === nothing
if isnothing(config.time.starttime)
starttime = first(nctimes) - dt
config.time.starttime = starttime
end
endtime = get(config.time, "endtime", nothing)
if endtime === nothing
if isnothing(config.time.endtime)
endtime = last(nctimes)
config.time.endtime = endtime
end

calendar = get(config.time, "calendar", "standard")::String
fews_run = get(config, "fews_run__flag", false)::Bool
if fews_run
config.time.starttime = starttime + dt
if config.fews_run__flag
config.time.starttime += dt
end
starttime = cftime(config.time.starttime, calendar)
starttime = cftime(config.time.starttime, config.time.calendar)

return Clock(starttime, 0, dt)
end
Expand All @@ -117,6 +124,9 @@ struct SbmModel <: AbstractModelType end # "sbm" type / sbm_model.jl
struct SbmGwfModel <: AbstractModelType end # "sbm_gwf" type / sbm_gwf_model.jl
struct SedimentModel <: AbstractModelType end # "sediment" type / sediment_model.jl

include("config_structure.jl")
include("config_utils.jl")
include("config_init.jl")
include("io.jl")
include("network.jl")
include("routing/routing.jl")
Expand Down Expand Up @@ -149,16 +159,13 @@ with input, model and output settings).
function Model(config::Config)::Model
model_type = config.model.type

if model_type ∉ ("sbm", "sbm_gwf", "sediment")
error("Unknown model type $model_type.")
end
@info "Initialize model variables for model type `$model_type`."

type = if model_type == "sbm"
type = if model_type == ModelType.sbm
SbmModel()
elseif model_type == "sbm_gwf"
elseif model_type == ModelType.sbm_gwf
SbmGwfModel()
elseif model_type == "sediment"
elseif model_type == ModelType.sediment
SedimentModel()
end

Expand Down Expand Up @@ -235,10 +242,9 @@ This makes it easier to start a run from the command line without having to esca
function run(tomlpath::AbstractString; silent = nothing)
config = Config(tomlpath)
# if the silent kwarg is not set, check if it is set in the TOML
if silent === nothing
silent = get(config.logging, "silent", false)::Bool
if isnothing(silent)
silent = config.logging.silent
end
fews_run = get(config, "fews_run", false)::Bool
logger, logfile = init_logger(config; silent)
with_logger(logger) do
@info "Wflow version `v$VERSION`"
Expand All @@ -248,7 +254,7 @@ function run(tomlpath::AbstractString; silent = nothing)
catch e
# avoid logging backtrace for the single line FEWS log format
# that logger also uses SimpleLogger which doesn't result in a good backtrace
if fews_run
if config.fews_run__flag
@error "Wflow simulation failed" exception = e _id = :wflow_run
else
@error "Wflow simulation failed" exception = (e, catch_backtrace()) _id =
Expand Down Expand Up @@ -282,16 +288,15 @@ end
function run!(model::Model; close_files = true)
(; config, writer, clock) = model

model_type = config.model.type::String
model_type = config.model.type

# determine timesteps to run
calendar = get(config.time, "calendar", "standard")::String
starttime = clock.time
dt = clock.dt
endtime = cftime(config.time.endtime, calendar)
endtime = cftime(config.time.endtime, config.time.calendar)
times = range(starttime + dt, endtime; step = dt)

@info "Run information" model_type starttime dt endtime nthreads()
@info "Run information" model_type = String(Symbol(model_type)) starttime dt endtime nthreads()
runstart_time = now()
@progress for (i, time) in enumerate(times)
@debug "Starting timestep." time i now()
Expand All @@ -314,8 +319,8 @@ function run!(model::Model; close_files = true)
end

# copy TOML to dir_output, to archive what settings were used
if haskey(config, "dir_output")
src = normpath(pathof(config))
if !isnothing(config.dir_output)
src = normpath(config.path)
dst = output_path(config, basename(src))
if src != dst
@debug "Copying TOML file." src dst
Expand Down
43 changes: 19 additions & 24 deletions Wflow/src/bmi.jl
Original file line number Diff line number Diff line change
Expand Up @@ -19,21 +19,17 @@ Initialize the model. Reads the input settings and data as defined in the Config
generated from the configuration file `config_file`. Will return a Model that is ready to
run.
"""

function BMI.initialize(::Type{<:Model}, config_file)
function BMI.initialize(::Type{<:Model}, config_file::AbstractString)
config = Config(config_file)
model_type = config.model.type

if model_type ∉ ("sbm", "sbm_gwf", "sediment")
error("Unknown model type $model_type.")
end
@info "Initialize model variables for model type `$model_type`."

type = if model_type == "sbm"
type = if model_type == ModelType.sbm
SbmModel()
elseif model_type == "sbm_gwf"
elseif model_type == ModelType.sbm_gwf
SbmGwfModel()
elseif model_type == "sediment"
elseif model_type == ModelType.sediment
SedimentModel()
end
model = Model(config, type)
Expand Down Expand Up @@ -84,7 +80,7 @@ function BMI.finalize(model::Model)
end

function BMI.get_component_name(model::Model)
return model.config.model.type
return string(model.config.model.type)
end

function BMI.get_input_item_count(model::Model)
Expand All @@ -104,7 +100,7 @@ exchanged.
"""
function BMI.get_input_var_names(model::Model)
(; config, land) = model
if haskey(config, "API") && haskey(config.API, "variables")
if do_api(config)
var_names = config.API.variables
idx = []
for (i, var) in enumerate(var_names)
Expand Down Expand Up @@ -133,7 +129,7 @@ function BMI.get_output_var_names(model::Model)
return BMI.get_input_var_names(model)
end

function BMI.get_var_grid(model::Model, name::String)
function BMI.get_var_grid(::Model, name::String)
return if occursin("reservoir", name)
0
elseif occursin("drain", name)
Expand Down Expand Up @@ -177,30 +173,29 @@ function BMI.get_var_location(model::Model, name::String)
end

function BMI.get_current_time(model::Model)
(; config) = model
calendar = get(config, "calendar", "standard")::String
starttime = cftime(config.time.starttime, calendar)
return 0.001 * Dates.value(model.clock.time - starttime)
(; config, clock) = model
(; starttime, calendar) = config.time
starttime = cftime(starttime, calendar)
return 0.001 * Dates.value(clock.time - starttime)
end

function BMI.get_start_time(model::Model)
function BMI.get_start_time(::Model)
return 0.0
end

function BMI.get_end_time(model::Model)
(; config) = model
calendar = get(config, "calendar", "standard")::String
starttime = cftime(config.time.starttime, calendar)
endtime = cftime(config.time.endtime, calendar)
return 0.001 * Dates.value(endtime - starttime)
(; starttime, endtime, calendar) = model.config.time
starttime_ = cftime(starttime, calendar)
endtime_ = cftime(endtime, calendar)
return 0.001 * Dates.value(endtime_ - starttime_)
end

function BMI.get_time_units(model::Model)
return "s"
end

function BMI.get_time_step(model::Model)
return Float64(model.config.time.timestepsecs)
return model.config.time.timestepsecs
end

function BMI.get_value(model::Model, name::String, dest::Vector{Float64})
Expand Down Expand Up @@ -369,7 +364,7 @@ function save_state(model::Model)
end

function get_start_unix_time(model::Model)
return datetime2unix(DateTime(model.config.time.starttime))
return datetime2unix(model.config.time.starttime)
Comment thread
SouthEndMusic marked this conversation as resolved.
end

# BMI helper functions.
Expand Down Expand Up @@ -409,7 +404,7 @@ end

grid_element_type(model, var::PropertyLens) = "node"

function grid_element_type(model, lens::ComposedFunction)
function grid_element_type(model::Model, lens::ComposedFunction)
lens_components = decompose(lens)
var = lens_components[1]
element_type = if PropertyLens(:river_flow) in lens_components
Expand Down
Loading
Loading