From 78b9d63ab9e4865bfa93bbf5f0f984a7cb261694 Mon Sep 17 00:00:00 2001 From: Maarten Pronk Date: Thu, 7 Sep 2023 17:06:43 +0200 Subject: [PATCH 01/13] JSONSchema of Config. --- core/src/config.jl | 4 +- docs/gen_schema.jl | 67 +++++++--- docs/schema/Config.schema.json | 98 +++++++++++++++ docs/schema/Logging.schema.json | 23 ++++ docs/schema/Output.schema.json | 49 ++++++++ docs/schema/Solver.schema.json | 69 +++++++++++ docs/schema/basin.schema.json | 43 +++++++ docs/schema/discrete_control.schema.json | 27 ++++ docs/schema/flow_boundary.schema.json | 27 ++++ docs/schema/fractional_flow.schema.json | 19 +++ docs/schema/level_boundary.schema.json | 27 ++++ docs/schema/linear_resistance.schema.json | 19 +++ docs/schema/manning_resistance.schema.json | 19 +++ docs/schema/outlet.schema.json | 19 +++ docs/schema/pid_control.schema.json | 27 ++++ docs/schema/pump.schema.json | 19 +++ .../schema/tabulated_rating_curve.schema.json | 27 ++++ docs/schema/terminal.schema.json | 19 +++ python/ribasim/ribasim/__init__.py | 2 + python/ribasim/ribasim/config.py | 117 ++++++++++++++++++ python/ribasim/ribasim/model.py | 28 +---- 21 files changed, 701 insertions(+), 48 deletions(-) create mode 100644 docs/schema/Config.schema.json create mode 100644 docs/schema/Logging.schema.json create mode 100644 docs/schema/Output.schema.json create mode 100644 docs/schema/Solver.schema.json create mode 100644 docs/schema/basin.schema.json create mode 100644 docs/schema/discrete_control.schema.json create mode 100644 docs/schema/flow_boundary.schema.json create mode 100644 docs/schema/fractional_flow.schema.json create mode 100644 docs/schema/level_boundary.schema.json create mode 100644 docs/schema/linear_resistance.schema.json create mode 100644 docs/schema/manning_resistance.schema.json create mode 100644 docs/schema/outlet.schema.json create mode 100644 docs/schema/pid_control.schema.json create mode 100644 docs/schema/pump.schema.json create mode 100644 docs/schema/tabulated_rating_curve.schema.json create mode 100644 docs/schema/terminal.schema.json create mode 100644 python/ribasim/ribasim/config.py diff --git a/core/src/config.jl b/core/src/config.jl index fee738f45..b74dbf67b 100644 --- a/core/src/config.jl +++ b/core/src/config.jl @@ -109,7 +109,7 @@ end timing::Bool = false end -@option @addnodetypes struct Config +@option @addnodetypes struct Config <: TableOption starttime::DateTime endtime::DateTime @@ -117,7 +117,7 @@ end update_timestep::Float64 = 60 * 60 * 24.0 # optional, when Config is created from a TOML file, this is its directory - relative_dir::String = pwd() + relative_dir::String = "." # ignored(!) input_dir::String = "." output_dir::String = "." diff --git a/docs/gen_schema.jl b/docs/gen_schema.jl index 4ad4b2bec..b6d404685 100644 --- a/docs/gen_schema.jl +++ b/docs/gen_schema.jl @@ -11,10 +11,13 @@ using JSON3 using Legolas using InteractiveUtils using Dates +using Configurations # set empty to have local file references for development const prefix = "https://deltares.github.io/Ribasim/schema/" +jsondefault(x) = identity(x) +jsondefault(x::LogLevel) = "info" jsontype(x) = jsontype(typeof(x)) jsonformat(x) = jsonformat(typeof(x)) jsontype(::Type{<:AbstractString}) = "string" @@ -25,6 +28,7 @@ jsonformat(::Type{<:Float32}) = "float" jsontype(::Type{<:Number}) = "number" jsontype(::Type{<:AbstractVector}) = "list" jsontype(::Type{<:Bool}) = "boolean" +jsontype(::Type{LogLevel}) = "string" jsontype(::Type{<:Missing}) = "null" jsontype(::Type{<:DateTime}) = "string" jsonformat(::Type{<:DateTime}) = "date-time" @@ -34,17 +38,17 @@ jsonformat(::Type{<:Any}) = "default" jsontype(T::Union) = unique(filter(!isequal("null"), jsontype.(Base.uniontypes(T)))) function strip_prefix(T::DataType) - (p, v) = rsplit(string(T), 'V'; limit = 2) + n = string(T) + (p, _) = occursin('V', n) ? rsplit(n, 'V'; limit = 2) : (n, "") return string(last(rsplit(p, '.'; limit = 2))) end -function gen_root_schema(TT::Vector, prefix = prefix) - name = "root" +function gen_root_schema(TT::Vector, prefix = prefix, name = "root") schema = Dict( "\$schema" => "https://json-schema.org/draft/2020-12/schema", "properties" => Dict{String, Dict}(), "\$id" => "$(prefix)$name.schema.json", - "title" => "root", + "title" => name, "description" => "All Ribasim Node types", "type" => "object", ) @@ -60,7 +64,7 @@ end os_line_separator() = Sys.iswindows() ? "\r\n" : "\n" -function gen_schema(T::DataType, prefix = prefix) +function gen_schema(T::DataType, prefix = prefix; pandera = true) name = strip_prefix(T) schema = Dict( "\$schema" => "https://json-schema.org/draft/2020-12/schema", @@ -71,24 +75,44 @@ function gen_schema(T::DataType, prefix = prefix) "properties" => Dict{String, Dict}(), "required" => String[], ) - for (fieldname, fieldtype) in zip(fieldnames(T), fieldtypes(T)) - fieldname = string(fieldname) - schema["properties"][fieldname] = Dict( - "description" => "$fieldname", - "type" => jsontype(fieldtype), - "format" => jsonformat(fieldtype), - ) - if !((fieldtype isa Union) && (fieldtype.a === Missing)) + for (fieldnames, fieldtype) in zip(fieldnames(T), fieldtypes(T)) + fieldname = string(fieldnames) + required = true + ref = false + @info fieldtype, fieldtype <: Ribasim.config.TableOption + if fieldtype <: Ribasim.config.TableOption + schema["properties"][fieldname] = + Dict("\$ref" => "$(prefix)$(strip_prefix(fieldtype)).schema.json") + ref = true + else + schema["properties"][fieldname] = Dict{String, Any}( + "description" => "$fieldname", + "type" => jsontype(fieldtype), + "format" => jsonformat(fieldtype), + ) + end + if T <: Ribasim.config.TableOption + d = field_default(T, fieldnames) + if !(d isa Configurations.ExproniconLite.NoDefault) + if !ref + schema["properties"][fieldname]["default"] = jsondefault(d) + end + required = false + end + end + if !((fieldtype isa Union) && (fieldtype.a === Missing)) && required push!(schema["required"], fieldname) end end - # Temporary hack so pandera will keep the Pydantic record types - schema["properties"]["remarks"] = Dict( - "description" => "a hack for pandera", - "type" => "string", - "format" => "default", - "default" => "", - ) + if pandera + # Temporary hack so pandera will keep the Pydantic record types + schema["properties"]["remarks"] = Dict( + "description" => "a hack for pandera", + "type" => "string", + "format" => "default", + "default" => "", + ) + end open(normpath(@__DIR__, "schema", "$(name).schema.json"), "w") do io JSON3.pretty(io, schema) println(io) @@ -106,4 +130,7 @@ end for T in subtypes(Legolas.AbstractRecord) gen_schema(T) end +for T in subtypes(Ribasim.config.TableOption) + gen_schema(T; pandera = false) +end gen_root_schema(subtypes(Legolas.AbstractRecord)) diff --git a/docs/schema/Config.schema.json b/docs/schema/Config.schema.json new file mode 100644 index 000000000..0527b1d3b --- /dev/null +++ b/docs/schema/Config.schema.json @@ -0,0 +1,98 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "properties": { + "output": { + "$ref": "https://deltares.github.io/Ribasim/schema/Output.schema.json" + }, + "starttime": { + "format": "date-time", + "description": "starttime", + "type": "string" + }, + "update_timestep": { + "format": "double", + "default": 86400, + "description": "update_timestep", + "type": "number" + }, + "input_dir": { + "format": "default", + "default": ".", + "description": "input_dir", + "type": "string" + }, + "output_dir": { + "format": "default", + "default": ".", + "description": "output_dir", + "type": "string" + }, + "level_boundary": { + "$ref": "https://deltares.github.io/Ribasim/schema/level_boundary.schema.json" + }, + "pump": { + "$ref": "https://deltares.github.io/Ribasim/schema/pump.schema.json" + }, + "discrete_control": { + "$ref": "https://deltares.github.io/Ribasim/schema/discrete_control.schema.json" + }, + "solver": { + "$ref": "https://deltares.github.io/Ribasim/schema/Solver.schema.json" + }, + "flow_boundary": { + "$ref": "https://deltares.github.io/Ribasim/schema/flow_boundary.schema.json" + }, + "pid_control": { + "$ref": "https://deltares.github.io/Ribasim/schema/pid_control.schema.json" + }, + "fractional_flow": { + "$ref": "https://deltares.github.io/Ribasim/schema/fractional_flow.schema.json" + }, + "relative_dir": { + "format": "default", + "default": ".", + "description": "relative_dir", + "type": "string" + }, + "endtime": { + "format": "date-time", + "description": "endtime", + "type": "string" + }, + "manning_resistance": { + "$ref": "https://deltares.github.io/Ribasim/schema/manning_resistance.schema.json" + }, + "tabulated_rating_curve": { + "$ref": "https://deltares.github.io/Ribasim/schema/tabulated_rating_curve.schema.json" + }, + "logging": { + "$ref": "https://deltares.github.io/Ribasim/schema/Logging.schema.json" + }, + "outlet": { + "$ref": "https://deltares.github.io/Ribasim/schema/outlet.schema.json" + }, + "geopackage": { + "format": "default", + "description": "geopackage", + "type": "string" + }, + "terminal": { + "$ref": "https://deltares.github.io/Ribasim/schema/terminal.schema.json" + }, + "basin": { + "$ref": "https://deltares.github.io/Ribasim/schema/basin.schema.json" + }, + "linear_resistance": { + "$ref": "https://deltares.github.io/Ribasim/schema/linear_resistance.schema.json" + } + }, + "required": [ + "starttime", + "endtime", + "geopackage" + ], + "$id": "https://deltares.github.io/Ribasim/schema/Config.schema.json", + "title": "Config", + "description": "A Config object based on Ribasim.config.Config", + "type": "object" +} diff --git a/docs/schema/Logging.schema.json b/docs/schema/Logging.schema.json new file mode 100644 index 000000000..96ad1dada --- /dev/null +++ b/docs/schema/Logging.schema.json @@ -0,0 +1,23 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "properties": { + "timing": { + "format": "default", + "default": false, + "description": "timing", + "type": "boolean" + }, + "verbosity": { + "format": "default", + "default": "info", + "description": "verbosity", + "type": "string" + } + }, + "required": [ + ], + "$id": "https://deltares.github.io/Ribasim/schema/Logging.schema.json", + "title": "Logging", + "description": "A Logging object based on Ribasim.config.Logging", + "type": "object" +} diff --git a/docs/schema/Output.schema.json b/docs/schema/Output.schema.json new file mode 100644 index 000000000..8316430ed --- /dev/null +++ b/docs/schema/Output.schema.json @@ -0,0 +1,49 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "properties": { + "compression": { + "format": "default", + "default": "zstd", + "description": "compression", + "type": "object" + }, + "basin": { + "format": "default", + "default": "output/basin.arrow", + "description": "basin", + "type": "string" + }, + "flow": { + "format": "default", + "default": "output/flow.arrow", + "description": "flow", + "type": "string" + }, + "control": { + "format": "default", + "default": "output/control.arrow", + "description": "control", + "type": "string" + }, + "outstate": { + "format": "default", + "default": null, + "description": "outstate", + "type": [ + "string" + ] + }, + "compression_level": { + "format": "default", + "default": 6, + "description": "compression_level", + "type": "integer" + } + }, + "required": [ + ], + "$id": "https://deltares.github.io/Ribasim/schema/Output.schema.json", + "title": "Output", + "description": "A Output object based on Ribasim.config.Output", + "type": "object" +} diff --git a/docs/schema/Solver.schema.json b/docs/schema/Solver.schema.json new file mode 100644 index 000000000..90a754646 --- /dev/null +++ b/docs/schema/Solver.schema.json @@ -0,0 +1,69 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "properties": { + "reltol": { + "format": "double", + "default": 0.001, + "description": "reltol", + "type": "number" + }, + "saveat": { + "format": "default", + "default": [ + ], + "description": "saveat", + "type": [ + "number", + "list" + ] + }, + "maxiters": { + "format": "default", + "default": 1000000000, + "description": "maxiters", + "type": "integer" + }, + "autodiff": { + "format": "default", + "default": true, + "description": "autodiff", + "type": "boolean" + }, + "adaptive": { + "format": "default", + "default": true, + "description": "adaptive", + "type": "boolean" + }, + "algorithm": { + "format": "default", + "default": "QNDF", + "description": "algorithm", + "type": "string" + }, + "abstol": { + "format": "double", + "default": 1.0e-6, + "description": "abstol", + "type": "number" + }, + "dt": { + "format": "double", + "default": 0, + "description": "dt", + "type": "number" + }, + "sparse": { + "format": "default", + "default": true, + "description": "sparse", + "type": "boolean" + } + }, + "required": [ + ], + "$id": "https://deltares.github.io/Ribasim/schema/Solver.schema.json", + "title": "Solver", + "description": "A Solver object based on Ribasim.config.Solver", + "type": "object" +} diff --git a/docs/schema/basin.schema.json b/docs/schema/basin.schema.json new file mode 100644 index 000000000..c1c0d6fb8 --- /dev/null +++ b/docs/schema/basin.schema.json @@ -0,0 +1,43 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "properties": { + "profile": { + "format": "default", + "default": null, + "description": "profile", + "type": [ + "string" + ] + }, + "static": { + "format": "default", + "default": null, + "description": "static", + "type": [ + "string" + ] + }, + "forcing": { + "format": "default", + "default": null, + "description": "forcing", + "type": [ + "string" + ] + }, + "state": { + "format": "default", + "default": null, + "description": "state", + "type": [ + "string" + ] + } + }, + "required": [ + ], + "$id": "https://deltares.github.io/Ribasim/schema/basin.schema.json", + "title": "basin", + "description": "A basin object based on Ribasim.config.basin", + "type": "object" +} diff --git a/docs/schema/discrete_control.schema.json b/docs/schema/discrete_control.schema.json new file mode 100644 index 000000000..022129760 --- /dev/null +++ b/docs/schema/discrete_control.schema.json @@ -0,0 +1,27 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "properties": { + "logic": { + "format": "default", + "default": null, + "description": "logic", + "type": [ + "string" + ] + }, + "condition": { + "format": "default", + "default": null, + "description": "condition", + "type": [ + "string" + ] + } + }, + "required": [ + ], + "$id": "https://deltares.github.io/Ribasim/schema/discrete_control.schema.json", + "title": "discrete_control", + "description": "A discrete_control object based on Ribasim.config.discrete_control", + "type": "object" +} diff --git a/docs/schema/flow_boundary.schema.json b/docs/schema/flow_boundary.schema.json new file mode 100644 index 000000000..4d1219555 --- /dev/null +++ b/docs/schema/flow_boundary.schema.json @@ -0,0 +1,27 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "properties": { + "time": { + "format": "default", + "default": null, + "description": "time", + "type": [ + "string" + ] + }, + "static": { + "format": "default", + "default": null, + "description": "static", + "type": [ + "string" + ] + } + }, + "required": [ + ], + "$id": "https://deltares.github.io/Ribasim/schema/flow_boundary.schema.json", + "title": "flow_boundary", + "description": "A flow_boundary object based on Ribasim.config.flow_boundary", + "type": "object" +} diff --git a/docs/schema/fractional_flow.schema.json b/docs/schema/fractional_flow.schema.json new file mode 100644 index 000000000..91574a793 --- /dev/null +++ b/docs/schema/fractional_flow.schema.json @@ -0,0 +1,19 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "properties": { + "static": { + "format": "default", + "default": null, + "description": "static", + "type": [ + "string" + ] + } + }, + "required": [ + ], + "$id": "https://deltares.github.io/Ribasim/schema/fractional_flow.schema.json", + "title": "fractional_flow", + "description": "A fractional_flow object based on Ribasim.config.fractional_flow", + "type": "object" +} diff --git a/docs/schema/level_boundary.schema.json b/docs/schema/level_boundary.schema.json new file mode 100644 index 000000000..6c5523b05 --- /dev/null +++ b/docs/schema/level_boundary.schema.json @@ -0,0 +1,27 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "properties": { + "time": { + "format": "default", + "default": null, + "description": "time", + "type": [ + "string" + ] + }, + "static": { + "format": "default", + "default": null, + "description": "static", + "type": [ + "string" + ] + } + }, + "required": [ + ], + "$id": "https://deltares.github.io/Ribasim/schema/level_boundary.schema.json", + "title": "level_boundary", + "description": "A level_boundary object based on Ribasim.config.level_boundary", + "type": "object" +} diff --git a/docs/schema/linear_resistance.schema.json b/docs/schema/linear_resistance.schema.json new file mode 100644 index 000000000..9805e87b8 --- /dev/null +++ b/docs/schema/linear_resistance.schema.json @@ -0,0 +1,19 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "properties": { + "static": { + "format": "default", + "default": null, + "description": "static", + "type": [ + "string" + ] + } + }, + "required": [ + ], + "$id": "https://deltares.github.io/Ribasim/schema/linear_resistance.schema.json", + "title": "linear_resistance", + "description": "A linear_resistance object based on Ribasim.config.linear_resistance", + "type": "object" +} diff --git a/docs/schema/manning_resistance.schema.json b/docs/schema/manning_resistance.schema.json new file mode 100644 index 000000000..e943a3a94 --- /dev/null +++ b/docs/schema/manning_resistance.schema.json @@ -0,0 +1,19 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "properties": { + "static": { + "format": "default", + "default": null, + "description": "static", + "type": [ + "string" + ] + } + }, + "required": [ + ], + "$id": "https://deltares.github.io/Ribasim/schema/manning_resistance.schema.json", + "title": "manning_resistance", + "description": "A manning_resistance object based on Ribasim.config.manning_resistance", + "type": "object" +} diff --git a/docs/schema/outlet.schema.json b/docs/schema/outlet.schema.json new file mode 100644 index 000000000..581719bab --- /dev/null +++ b/docs/schema/outlet.schema.json @@ -0,0 +1,19 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "properties": { + "static": { + "format": "default", + "default": null, + "description": "static", + "type": [ + "string" + ] + } + }, + "required": [ + ], + "$id": "https://deltares.github.io/Ribasim/schema/outlet.schema.json", + "title": "outlet", + "description": "A outlet object based on Ribasim.config.outlet", + "type": "object" +} diff --git a/docs/schema/pid_control.schema.json b/docs/schema/pid_control.schema.json new file mode 100644 index 000000000..2a1d96799 --- /dev/null +++ b/docs/schema/pid_control.schema.json @@ -0,0 +1,27 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "properties": { + "time": { + "format": "default", + "default": null, + "description": "time", + "type": [ + "string" + ] + }, + "static": { + "format": "default", + "default": null, + "description": "static", + "type": [ + "string" + ] + } + }, + "required": [ + ], + "$id": "https://deltares.github.io/Ribasim/schema/pid_control.schema.json", + "title": "pid_control", + "description": "A pid_control object based on Ribasim.config.pid_control", + "type": "object" +} diff --git a/docs/schema/pump.schema.json b/docs/schema/pump.schema.json new file mode 100644 index 000000000..a965ab0b1 --- /dev/null +++ b/docs/schema/pump.schema.json @@ -0,0 +1,19 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "properties": { + "static": { + "format": "default", + "default": null, + "description": "static", + "type": [ + "string" + ] + } + }, + "required": [ + ], + "$id": "https://deltares.github.io/Ribasim/schema/pump.schema.json", + "title": "pump", + "description": "A pump object based on Ribasim.config.pump", + "type": "object" +} diff --git a/docs/schema/tabulated_rating_curve.schema.json b/docs/schema/tabulated_rating_curve.schema.json new file mode 100644 index 000000000..f92832e0b --- /dev/null +++ b/docs/schema/tabulated_rating_curve.schema.json @@ -0,0 +1,27 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "properties": { + "time": { + "format": "default", + "default": null, + "description": "time", + "type": [ + "string" + ] + }, + "static": { + "format": "default", + "default": null, + "description": "static", + "type": [ + "string" + ] + } + }, + "required": [ + ], + "$id": "https://deltares.github.io/Ribasim/schema/tabulated_rating_curve.schema.json", + "title": "tabulated_rating_curve", + "description": "A tabulated_rating_curve object based on Ribasim.config.tabulated_rating_curve", + "type": "object" +} diff --git a/docs/schema/terminal.schema.json b/docs/schema/terminal.schema.json new file mode 100644 index 000000000..cbbb272bf --- /dev/null +++ b/docs/schema/terminal.schema.json @@ -0,0 +1,19 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "properties": { + "static": { + "format": "default", + "default": null, + "description": "static", + "type": [ + "string" + ] + } + }, + "required": [ + ], + "$id": "https://deltares.github.io/Ribasim/schema/terminal.schema.json", + "title": "terminal", + "description": "A terminal object based on Ribasim.config.terminal", + "type": "object" +} diff --git a/python/ribasim/ribasim/__init__.py b/python/ribasim/ribasim/__init__.py index 0eb79db09..b9757d2b3 100644 --- a/python/ribasim/ribasim/__init__.py +++ b/python/ribasim/ribasim/__init__.py @@ -2,6 +2,7 @@ from ribasim import models, utils +from ribasim.config import Config from ribasim.geometry.edge import Edge from ribasim.geometry.node import Node from ribasim.model import Logging, Model, Solver @@ -21,6 +22,7 @@ __all__ = [ "models", "utils", + "Config", "Basin", "Edge", "FractionalFlow", diff --git a/python/ribasim/ribasim/config.py b/python/ribasim/ribasim/config.py new file mode 100644 index 000000000..3634c290a --- /dev/null +++ b/python/ribasim/ribasim/config.py @@ -0,0 +1,117 @@ +# generated by datamodel-codegen: +# filename: Config.schema.json +# timestamp: 2023-09-07T15:04:09+00:00 + +from __future__ import annotations + +from datetime import datetime +from typing import Any, Dict, Optional, Union + +from pydantic import BaseModel, Field + + +class Output(BaseModel): + compression: Optional[Dict[str, Any]] = Field("zstd", description="compression") + basin: Optional[str] = Field("output/basin.arrow", description="basin") + flow: Optional[str] = Field("output/flow.arrow", description="flow") + control: Optional[str] = Field("output/control.arrow", description="control") + outstate: Optional[str] = Field(None, description="outstate") + compression_level: Optional[int] = Field(6, description="compression_level") + + +class LevelBoundary(BaseModel): + time: Optional[str] = Field(None, description="time") + static: Optional[str] = Field(None, description="static") + + +class Pump(BaseModel): + static: Optional[str] = Field(None, description="static") + + +class DiscreteControl(BaseModel): + logic: Optional[str] = Field(None, description="logic") + condition: Optional[str] = Field(None, description="condition") + + +class Solver(BaseModel): + reltol: Optional[float] = Field(0.001, description="reltol") + saveat: Optional[Union[float, Any]] = Field([], description="saveat") + maxiters: Optional[int] = Field(1000000000, description="maxiters") + autodiff: Optional[bool] = Field(True, description="autodiff") + adaptive: Optional[bool] = Field(True, description="adaptive") + algorithm: Optional[str] = Field("QNDF", description="algorithm") + abstol: Optional[float] = Field(1e-06, description="abstol") + dt: Optional[float] = Field(0, description="dt") + sparse: Optional[bool] = Field(True, description="sparse") + + +class FlowBoundary(BaseModel): + time: Optional[str] = Field(None, description="time") + static: Optional[str] = Field(None, description="static") + + +class PidControl(BaseModel): + time: Optional[str] = Field(None, description="time") + static: Optional[str] = Field(None, description="static") + + +class FractionalFlow(BaseModel): + static: Optional[str] = Field(None, description="static") + + +class ManningResistance(BaseModel): + static: Optional[str] = Field(None, description="static") + + +class TabulatedRatingCurve(BaseModel): + time: Optional[str] = Field(None, description="time") + static: Optional[str] = Field(None, description="static") + + +class Logging(BaseModel): + timing: Optional[bool] = Field(False, description="timing") + verbosity: Optional[str] = Field("info", description="verbosity") + + +class Outlet(BaseModel): + static: Optional[str] = Field(None, description="static") + + +class Terminal(BaseModel): + static: Optional[str] = Field(None, description="static") + + +class Basin(BaseModel): + profile: Optional[str] = Field(None, description="profile") + static: Optional[str] = Field(None, description="static") + forcing: Optional[str] = Field(None, description="forcing") + state: Optional[str] = Field(None, description="state") + + +class LinearResistance(BaseModel): + static: Optional[str] = Field(None, description="static") + + +class Config(BaseModel): + output: Optional[Output] = None + starttime: datetime = Field(..., description="starttime") + update_timestep: Optional[float] = Field(86400, description="update_timestep") + input_dir: Optional[str] = Field(".", description="input_dir") + output_dir: Optional[str] = Field(".", description="output_dir") + level_boundary: Optional[LevelBoundary] = None + pump: Optional[Pump] = None + discrete_control: Optional[DiscreteControl] = None + solver: Optional[Solver] = None + flow_boundary: Optional[FlowBoundary] = None + pid_control: Optional[PidControl] = None + fractional_flow: Optional[FractionalFlow] = None + relative_dir: Optional[str] = Field(".", description="relative_dir") + endtime: datetime = Field(..., description="endtime") + manning_resistance: Optional[ManningResistance] = None + tabulated_rating_curve: Optional[TabulatedRatingCurve] = None + logging: Optional[Logging] = None + outlet: Optional[Outlet] = None + geopackage: str = Field(..., description="geopackage") + terminal: Optional[Terminal] = None + basin: Optional[Basin] = None + linear_resistance: Optional[LinearResistance] = None diff --git a/python/ribasim/ribasim/model.py b/python/ribasim/ribasim/model.py index 66c2e3bdc..95c002d22 100644 --- a/python/ribasim/ribasim/model.py +++ b/python/ribasim/ribasim/model.py @@ -2,10 +2,9 @@ import inspect import shutil from contextlib import closing -from enum import Enum from pathlib import Path from sqlite3 import connect -from typing import Any, List, Optional, Type, Union, cast +from typing import Any, Optional, Type, cast import matplotlib.pyplot as plt import numpy as np @@ -15,6 +14,7 @@ from pydantic import BaseModel from ribasim import geometry, node_types +from ribasim.config import Logging, Solver from ribasim.geometry.edge import Edge from ribasim.geometry.node import Node @@ -36,30 +36,6 @@ from ribasim.types import FilePath -class Solver(BaseModel): - algorithm: Optional[str] - saveat: Optional[Union[float, List[float]]] - adaptive: Optional[bool] - dt: Optional[float] - abstol: Optional[float] - reltol: Optional[float] - maxiters: Optional[int] - sparse: Optional[bool] - autodiff: Optional[bool] - - -class Verbosity(str, Enum): - debug = "debug" - info = "info" - warn = "warn" - error = "error" - - -class Logging(BaseModel): - verbosity: Optional[Verbosity] = Verbosity.info - timing: Optional[bool] = False - - class Model(BaseModel): """ A full Ribasim model schematisation with all input. From 417a38d9f2639d7885e917040cb3222a5e9524ed Mon Sep 17 00:00:00 2001 From: Maarten Pronk Date: Mon, 11 Sep 2023 21:13:20 +0200 Subject: [PATCH 02/13] Derive Schema automatically. --- python/ribasim/ribasim/__init__.py | 4 +- python/ribasim/ribasim/node_types/basin.py | 45 +++++-------------- .../ribasim/node_types/discrete_control.py | 22 ++------- .../ribasim/node_types/flow_boundary.py | 23 ++-------- .../ribasim/node_types/fractional_flow.py | 13 +----- .../ribasim/node_types/level_boundary.py | 22 ++------- .../ribasim/node_types/linear_resistance.py | 13 +----- .../ribasim/node_types/manning_resistance.py | 13 +----- python/ribasim/ribasim/node_types/outlet.py | 13 +----- .../ribasim/ribasim/node_types/pid_control.py | 22 ++------- python/ribasim/ribasim/node_types/pump.py | 13 +----- .../node_types/tabulated_rating_curve.py | 27 +++-------- python/ribasim/ribasim/node_types/terminal.py | 13 +----- python/ribasim/tests/test_model.py | 10 ++--- .../ribasim_testmodels/__init__.py | 4 +- 15 files changed, 47 insertions(+), 210 deletions(-) diff --git a/python/ribasim/ribasim/__init__.py b/python/ribasim/ribasim/__init__.py index b9757d2b3..7bf297f14 100644 --- a/python/ribasim/ribasim/__init__.py +++ b/python/ribasim/ribasim/__init__.py @@ -2,10 +2,10 @@ from ribasim import models, utils -from ribasim.config import Config +from ribasim.config import Config, Logging, Solver from ribasim.geometry.edge import Edge from ribasim.geometry.node import Node -from ribasim.model import Logging, Model, Solver +from ribasim.model import Model from ribasim.node_types.basin import Basin from ribasim.node_types.discrete_control import DiscreteControl from ribasim.node_types.flow_boundary import FlowBoundary diff --git a/python/ribasim/ribasim/node_types/basin.py b/python/ribasim/ribasim/node_types/basin.py index 3f7f00537..36a2f8342 100644 --- a/python/ribasim/ribasim/node_types/basin.py +++ b/python/ribasim/ribasim/node_types/basin.py @@ -1,43 +1,18 @@ from typing import Optional -import pandera as pa -from pandera.engines.pandas_engine import PydanticModel from pandera.typing import DataFrame -from ribasim import models from ribasim.input_base import TableModel +from ribasim.schemas import ( + BasinForcingSchema, + BasinProfileSchema, + BasinStateSchema, + BasinStaticSchema, +) __all__ = ("Basin",) -class StaticSchema(pa.SchemaModel): - class Config: - """Config with dataframe-level data type.""" - - dtype = PydanticModel(models.BasinStatic) - - -class ForcingSchema(pa.SchemaModel): - class Config: - """Config with dataframe-level data type.""" - - dtype = PydanticModel(models.BasinForcing) - - -class ProfileSchema(pa.SchemaModel): - class Config: - """Config with dataframe-level data type.""" - - dtype = PydanticModel(models.BasinProfile) - - -class StateSchema(pa.SchemaModel): - class Config: - """Config with dataframe-level data type.""" - - dtype = PydanticModel(models.BasinState) - - class Basin(TableModel): """ Input for a (sub-)basin: an area of land where all flowing surface water converges to a single point. @@ -54,10 +29,10 @@ class Basin(TableModel): Table describing the initial condition. """ - profile: DataFrame[ProfileSchema] - static: Optional[DataFrame[StaticSchema]] = None - forcing: Optional[DataFrame[ForcingSchema]] = None - state: Optional[DataFrame[StateSchema]] = None + profile: DataFrame[BasinProfileSchema] + static: Optional[DataFrame[BasinStaticSchema]] = None + forcing: Optional[DataFrame[BasinForcingSchema]] = None + state: Optional[DataFrame[BasinStateSchema]] = None def sort(self): self.profile = self.profile.sort_values(["node_id", "level"], ignore_index=True) diff --git a/python/ribasim/ribasim/node_types/discrete_control.py b/python/ribasim/ribasim/node_types/discrete_control.py index 07f1b1772..16695c731 100644 --- a/python/ribasim/ribasim/node_types/discrete_control.py +++ b/python/ribasim/ribasim/node_types/discrete_control.py @@ -1,27 +1,11 @@ -import pandera as pa -from pandera.engines.pandas_engine import PydanticModel from pandera.typing import DataFrame -from ribasim import models from ribasim.input_base import TableModel +from ribasim.schemas import DiscreteControlConditionSchema, DiscreteControlLogicSchema __all__ = ("DiscreteControl",) -class ConditionSchema(pa.SchemaModel): - class Config: - """Config with dataframe-level data type.""" - - dtype = PydanticModel(models.DiscreteControlCondition) - - -class LogicSchema(pa.SchemaModel): - class Config: - """Config with dataframe-level data type.""" - - dtype = PydanticModel(models.DiscreteControlLogic) - - class DiscreteControl(TableModel): """ Defines the control logic. @@ -34,5 +18,5 @@ class DiscreteControl(TableModel): Table with the information of truth state to control state mapping. """ - condition: DataFrame[ConditionSchema] - logic: DataFrame[LogicSchema] + condition: DataFrame[DiscreteControlConditionSchema] + logic: DataFrame[DiscreteControlLogicSchema] diff --git a/python/ribasim/ribasim/node_types/flow_boundary.py b/python/ribasim/ribasim/node_types/flow_boundary.py index 85aad8166..713d6fa03 100644 --- a/python/ribasim/ribasim/node_types/flow_boundary.py +++ b/python/ribasim/ribasim/node_types/flow_boundary.py @@ -1,30 +1,13 @@ from typing import Optional -import pandera as pa -from pandera.engines.pandas_engine import PydanticModel from pandera.typing import DataFrame -from ribasim import models from ribasim.input_base import TableModel +from ribasim.schemas import FlowBoundaryStaticSchema, FlowBoundaryTimeSchema __all__ = ("FlowBoundary",) -class StaticSchema(pa.SchemaModel): - class Config: - """Config with dataframe-level data type.""" - - dtype = PydanticModel(models.FlowBoundaryStatic) - - -class TimeSchema(pa.SchemaModel): - class Config: - """Config with dataframe-level data type.""" - - dtype = PydanticModel(models.FlowBoundaryTime) - coerce = True # this is required, otherwise a SchemaInitError is raised - - class FlowBoundary(TableModel): """ Sets a precribed flow like a one-sided pump. @@ -37,5 +20,5 @@ class FlowBoundary(TableModel): Table with time-varying flow rates. """ - static: Optional[DataFrame[StaticSchema]] = None - time: Optional[DataFrame[TimeSchema]] = None + static: Optional[DataFrame[FlowBoundaryStaticSchema]] = None + time: Optional[DataFrame[FlowBoundaryTimeSchema]] = None diff --git a/python/ribasim/ribasim/node_types/fractional_flow.py b/python/ribasim/ribasim/node_types/fractional_flow.py index d86d5e40e..1ecba37d5 100644 --- a/python/ribasim/ribasim/node_types/fractional_flow.py +++ b/python/ribasim/ribasim/node_types/fractional_flow.py @@ -1,20 +1,11 @@ -import pandera as pa -from pandera.engines.pandas_engine import PydanticModel from pandera.typing import DataFrame -from ribasim import models from ribasim.input_base import TableModel +from ribasim.schemas import FractionalFlowStaticSchema __all__ = ("FractionalFlow",) -class StaticSchema(pa.SchemaModel): - class Config: - """Config with dataframe-level data type.""" - - dtype = PydanticModel(models.FractionalFlowStatic) - - class FractionalFlow(TableModel): """ Receives a fraction of the flow. The fractions must sum to 1.0 for a furcation. @@ -25,7 +16,7 @@ class FractionalFlow(TableModel): Table with the constant flow fractions. """ - static: DataFrame[StaticSchema] + static: DataFrame[FractionalFlowStaticSchema] def sort(self): self.static = self.static.sort_values("node_id", ignore_index=True) diff --git a/python/ribasim/ribasim/node_types/level_boundary.py b/python/ribasim/ribasim/node_types/level_boundary.py index b5c3b7da3..777938cd4 100644 --- a/python/ribasim/ribasim/node_types/level_boundary.py +++ b/python/ribasim/ribasim/node_types/level_boundary.py @@ -1,29 +1,13 @@ from typing import Optional -import pandera as pa -from pandera.engines.pandas_engine import PydanticModel from pandera.typing import DataFrame -from ribasim import models from ribasim.input_base import TableModel +from ribasim.schemas import LevelBoundaryStaticSchema, LevelBoundaryTimeSchema __all__ = ("LevelBoundary",) -class StaticSchema(pa.SchemaModel): - class Config: - """Config with dataframe-level data type.""" - - dtype = PydanticModel(models.LevelBoundaryStatic) - - -class TimeSchema(pa.SchemaModel): - class Config: - """Config with dataframe-level data type.""" - - dtype = PydanticModel(models.LevelBoundaryTime) - - class LevelBoundary(TableModel): """ Stores water at a given level unaffected by flow, like an infinitely large basin. @@ -34,5 +18,5 @@ class LevelBoundary(TableModel): Table with the constant water levels. """ - static: Optional[DataFrame[StaticSchema]] = None - time: Optional[DataFrame[TimeSchema]] = None + static: Optional[DataFrame[LevelBoundaryStaticSchema]] = None + time: Optional[DataFrame[LevelBoundaryTimeSchema]] = None diff --git a/python/ribasim/ribasim/node_types/linear_resistance.py b/python/ribasim/ribasim/node_types/linear_resistance.py index 9f2ad68dd..8844364de 100644 --- a/python/ribasim/ribasim/node_types/linear_resistance.py +++ b/python/ribasim/ribasim/node_types/linear_resistance.py @@ -1,20 +1,11 @@ -import pandera as pa -from pandera.engines.pandas_engine import PydanticModel from pandera.typing import DataFrame -from ribasim import models from ribasim.input_base import TableModel +from ribasim.schemas import LinearResistanceStaticSchema __all__ = ("LinearResistance",) -class StaticSchema(pa.SchemaModel): - class Config: - """Config with dataframe-level data type.""" - - dtype = PydanticModel(models.LinearResistanceStatic) - - class LinearResistance(TableModel): """ Flow through this connection linearly depends on the level difference @@ -26,4 +17,4 @@ class LinearResistance(TableModel): Table with the constant resistances. """ - static: DataFrame[StaticSchema] + static: DataFrame[LinearResistanceStaticSchema] diff --git a/python/ribasim/ribasim/node_types/manning_resistance.py b/python/ribasim/ribasim/node_types/manning_resistance.py index bf7538314..7c691403d 100644 --- a/python/ribasim/ribasim/node_types/manning_resistance.py +++ b/python/ribasim/ribasim/node_types/manning_resistance.py @@ -1,20 +1,11 @@ -import pandera as pa -from pandera.engines.pandas_engine import PydanticModel from pandera.typing import DataFrame -from ribasim import models from ribasim.input_base import TableModel +from ribasim.schemas import ManningResistanceStaticSchema __all__ = ("ManningResistance",) -class StaticSchema(pa.SchemaModel): - class Config: - """Config with dataframe-level data type.""" - - dtype = PydanticModel(models.ManningResistanceStatic) - - class ManningResistance(TableModel): """ Flow through this connection is estimated by conservation of energy and the @@ -26,4 +17,4 @@ class ManningResistance(TableModel): Table with the constant Manning parameters. """ - static: DataFrame[StaticSchema] + static: DataFrame[ManningResistanceStaticSchema] diff --git a/python/ribasim/ribasim/node_types/outlet.py b/python/ribasim/ribasim/node_types/outlet.py index 45f4afe7f..85849e6b4 100644 --- a/python/ribasim/ribasim/node_types/outlet.py +++ b/python/ribasim/ribasim/node_types/outlet.py @@ -1,20 +1,11 @@ -import pandera as pa -from pandera.engines.pandas_engine import PydanticModel from pandera.typing import DataFrame -from ribasim import models from ribasim.input_base import TableModel +from ribasim.schemas import OutletStaticSchema __all__ = ("Outlet",) -class StaticSchema(pa.SchemaModel): - class Config: - """Config with dataframe-level data type.""" - - dtype = PydanticModel(models.OutletStatic) - - class Outlet(TableModel): """ Conducts water from a source node to a destination node. @@ -29,4 +20,4 @@ class Outlet(TableModel): Table with constant flow rates. """ - static: DataFrame[StaticSchema] + static: DataFrame[OutletStaticSchema] diff --git a/python/ribasim/ribasim/node_types/pid_control.py b/python/ribasim/ribasim/node_types/pid_control.py index 4de9a77d1..4869dcdaf 100644 --- a/python/ribasim/ribasim/node_types/pid_control.py +++ b/python/ribasim/ribasim/node_types/pid_control.py @@ -1,29 +1,13 @@ from typing import Optional -import pandera as pa -from pandera.engines.pandas_engine import PydanticModel from pandera.typing import DataFrame -from ribasim import models from ribasim.input_base import TableModel +from ribasim.schemas import PidControlStaticSchema, PidControlTimeSchema __all__ = ("PidControl",) -class StaticSchema(pa.SchemaModel): - class Config: - """Config with dataframe-level data type.""" - - dtype = PydanticModel(models.PidControlStatic) - - -class TimeSchema(pa.SchemaModel): - class Config: - """Config with dataframe-level data type.""" - - dtype = PydanticModel(models.PidControlTime) - - class PidControl(TableModel): """ Controller based on PID (Proportional, integral, derivative) which @@ -37,8 +21,8 @@ class PidControl(TableModel): Table with time-varying data for this node type. """ - static: Optional[DataFrame[StaticSchema]] = None - time: Optional[DataFrame[TimeSchema]] = None + static: Optional[DataFrame[PidControlStaticSchema]] = None + time: Optional[DataFrame[PidControlTimeSchema]] = None class Config: validate_assignment = True diff --git a/python/ribasim/ribasim/node_types/pump.py b/python/ribasim/ribasim/node_types/pump.py index a6ad5ff56..a3eb4a258 100644 --- a/python/ribasim/ribasim/node_types/pump.py +++ b/python/ribasim/ribasim/node_types/pump.py @@ -1,20 +1,11 @@ -import pandera as pa -from pandera.engines.pandas_engine import PydanticModel from pandera.typing import DataFrame -from ribasim import models from ribasim.input_base import TableModel +from ribasim.schemas import PumpStaticSchema __all__ = ("Pump",) -class StaticSchema(pa.SchemaModel): - class Config: - """Config with dataframe-level data type.""" - - dtype = PydanticModel(models.PumpStatic) - - class Pump(TableModel): """ Pump water from a source node to a destination node. @@ -29,4 +20,4 @@ class Pump(TableModel): Table with constant flow rates. """ - static: DataFrame[StaticSchema] + static: DataFrame[PumpStaticSchema] diff --git a/python/ribasim/ribasim/node_types/tabulated_rating_curve.py b/python/ribasim/ribasim/node_types/tabulated_rating_curve.py index cded2f9f1..15a61752e 100644 --- a/python/ribasim/ribasim/node_types/tabulated_rating_curve.py +++ b/python/ribasim/ribasim/node_types/tabulated_rating_curve.py @@ -1,31 +1,16 @@ from typing import Optional -import pandera as pa -from pandera.engines.pandas_engine import PydanticModel from pandera.typing import DataFrame -from ribasim import models from ribasim.input_base import TableModel +from ribasim.schemas import ( + TabulatedRatingCurveStaticSchema, + TabulatedRatingCurveTimeSchema, +) __all__ = ("TabulatedRatingCurve",) -class StaticSchema(pa.SchemaModel): - class Config: - """Config with dataframe-level data type.""" - - dtype = PydanticModel(models.TabulatedRatingCurveStatic) - coerce = True # this is required, otherwise a SchemaInitError is raised - - -class TimeSchema(pa.SchemaModel): - class Config: - """Config with dataframe-level data type.""" - - dtype = PydanticModel(models.TabulatedRatingCurveTime) - coerce = True # this is required, otherwise a SchemaInitError is raised - - class TabulatedRatingCurve(TableModel): """ Linearly interpolates discharge between a tabulation of level and discharge. @@ -38,8 +23,8 @@ class TabulatedRatingCurve(TableModel): Table with time-varying rating curves. """ - static: Optional[DataFrame[StaticSchema]] = None - time: Optional[DataFrame[TimeSchema]] = None + static: Optional[DataFrame[TabulatedRatingCurveStaticSchema]] = None + time: Optional[DataFrame[TabulatedRatingCurveTimeSchema]] = None def sort(self): self.static = self.static.sort_values(["node_id", "level"], ignore_index=True) diff --git a/python/ribasim/ribasim/node_types/terminal.py b/python/ribasim/ribasim/node_types/terminal.py index a04c85c28..bbc57eca3 100644 --- a/python/ribasim/ribasim/node_types/terminal.py +++ b/python/ribasim/ribasim/node_types/terminal.py @@ -1,20 +1,11 @@ -import pandera as pa -from pandera.engines.pandas_engine import PydanticModel from pandera.typing import DataFrame -from ribasim import models from ribasim.input_base import TableModel +from ribasim.schemas import TerminalStaticSchema __all__ = ("Terminal",) -class StaticSchema(pa.SchemaModel): - class Config: - """Config with dataframe-level data type.""" - - dtype = PydanticModel(models.TerminalStatic) - - class Terminal(TableModel): """ Water sink without state or properties. @@ -25,4 +16,4 @@ class Terminal(TableModel): Table with only node IDs of this type. """ - static: DataFrame[StaticSchema] + static: DataFrame[TerminalStaticSchema] diff --git a/python/ribasim/tests/test_model.py b/python/ribasim/tests/test_model.py index 381cd9eb4..454bbe3df 100644 --- a/python/ribasim/tests/test_model.py +++ b/python/ribasim/tests/test_model.py @@ -14,8 +14,8 @@ def test_repr(basic): def test_solver(): solver = Solver() - assert solver.algorithm is None - assert solver.saveat is None + assert solver.algorithm == "QNDF" # default + assert solver.saveat == [] solver = Solver(saveat=3600.0) assert solver.saveat == 3600.0 @@ -28,10 +28,8 @@ def test_solver(): def test_invalid_node_type(basic): - model = basic - # Add entry with invalid node type - model.node.static = model.node.static._append( + basic.node.static = basic.node.static._append( {"type": "InvalidNodeType", "geometry": Point(0, 0)}, ignore_index=True ) @@ -39,7 +37,7 @@ def test_invalid_node_type(basic): TypeError, match=re.escape("Invalid node types detected: [InvalidNodeType].") + ".+", ): - model.validate_model_node_types() + basic.validate_model_node_types() def test_invalid_node_id(basic): diff --git a/python/ribasim_testmodels/ribasim_testmodels/__init__.py b/python/ribasim_testmodels/ribasim_testmodels/__init__.py index 49cad3bdf..ad34a57df 100644 --- a/python/ribasim_testmodels/ribasim_testmodels/__init__.py +++ b/python/ribasim_testmodels/ribasim_testmodels/__init__.py @@ -32,9 +32,7 @@ discrete_control_of_pid_control_model, pid_control_model, ) -from ribasim_testmodels.time import ( - flow_boundary_time_model, -) +from ribasim_testmodels.time import flow_boundary_time_model from ribasim_testmodels.trivial import trivial_model __all__ = [ From d76ae4852f65096a78316e643e9a7d42c4c5189f Mon Sep 17 00:00:00 2001 From: Maarten Pronk Date: Tue, 12 Sep 2023 11:13:08 +0200 Subject: [PATCH 03/13] Improvements to schemas. --- core/src/config.jl | 6 +++- docs/gen_schema.jl | 28 ++++++++++++++++--- .../DiscreteControlCondition.schema.json | 4 +-- docs/schema/FlowBoundaryStatic.schema.json | 4 +-- docs/schema/FractionalFlowStatic.schema.json | 4 +-- docs/schema/LevelBoundaryStatic.schema.json | 4 +-- .../schema/LinearResistanceStatic.schema.json | 8 ++---- .../ManningResistanceStatic.schema.json | 8 ++---- docs/schema/OutletStatic.schema.json | 16 +++-------- docs/schema/Output.schema.json | 6 ++-- docs/schema/PIDControlStatic.schema.json | 8 ++---- docs/schema/PidControlTime.schema.json | 4 +-- docs/schema/PumpStatic.schema.json | 16 +++-------- docs/schema/Solver.schema.json | 17 +++++++---- .../TabulatedRatingCurveStatic.schema.json | 8 ++---- docs/schema/basin.schema.json | 16 +++-------- docs/schema/discrete_control.schema.json | 8 ++---- docs/schema/flow_boundary.schema.json | 8 ++---- docs/schema/fractional_flow.schema.json | 4 +-- docs/schema/level_boundary.schema.json | 8 ++---- docs/schema/linear_resistance.schema.json | 4 +-- docs/schema/manning_resistance.schema.json | 4 +-- docs/schema/outlet.schema.json | 4 +-- docs/schema/pid_control.schema.json | 8 ++---- docs/schema/pump.schema.json | 4 +-- .../schema/tabulated_rating_curve.schema.json | 8 ++---- docs/schema/terminal.schema.json | 4 +-- python/ribasim/ribasim/config.py | 8 +++--- 28 files changed, 88 insertions(+), 141 deletions(-) diff --git a/core/src/config.jl b/core/src/config.jl index b74dbf67b..2fdad1c32 100644 --- a/core/src/config.jl +++ b/core/src/config.jl @@ -65,9 +65,13 @@ for (T, kinds) in pairs(nodekinds) end const nodetypes = collect(keys(nodekinds)) +# A [] in a TOML is parsed as Vector{Union{}} +Base.convert(::Type{Union{Float64, Vector{Float64}}}, x::Vector{Union{}}) = + convert(Vector{Float64}, x) + @option struct Solver <: TableOption algorithm::String = "QNDF" - saveat::Union{Float64, Vector{Float64}, Vector{Union{}}} = Float64[] + saveat::Union{Float64, Vector{Float64}} = Float64[] adaptive::Bool = true dt::Float64 = 0.0 abstol::Float64 = 1e-6 diff --git a/docs/gen_schema.jl b/docs/gen_schema.jl index b6d404685..be80c033b 100644 --- a/docs/gen_schema.jl +++ b/docs/gen_schema.jl @@ -12,6 +12,7 @@ using Legolas using InteractiveUtils using Dates using Configurations +using Logging # set empty to have local file references for development const prefix = "https://deltares.github.io/Ribasim/schema/" @@ -26,16 +27,31 @@ jsontype(::Type{<:AbstractFloat}) = "number" jsonformat(::Type{<:Float64}) = "double" jsonformat(::Type{<:Float32}) = "float" jsontype(::Type{<:Number}) = "number" -jsontype(::Type{<:AbstractVector}) = "list" +jsontype(::Type{<:AbstractVector}) = "array" jsontype(::Type{<:Bool}) = "boolean" jsontype(::Type{LogLevel}) = "string" +jsontype(::Type{<:Enum}) = "string" jsontype(::Type{<:Missing}) = "null" jsontype(::Type{<:DateTime}) = "string" jsonformat(::Type{<:DateTime}) = "date-time" jsontype(::Type{<:Nothing}) = "null" jsontype(::Type{<:Any}) = "object" jsonformat(::Type{<:Any}) = "default" -jsontype(T::Union) = unique(filter(!isequal("null"), jsontype.(Base.uniontypes(T)))) +function jsontype(T::Union) + t = Base.uniontypes(T) + td = Dict(zip(t, jsontype.(t))) + filter!(x -> !isequal(x.second, "null"), td) + length(td) == 1 && return first(values(td)) + types = Dict[] + for (t, jt) in td + nt = Dict{String, Any}("type" => jt) + if t <: AbstractVector + nt["items"] = Dict("type" => jsontype(eltype(t))) + end + push!(types, nt) + end + return Dict("anyOf" => types) +end function strip_prefix(T::DataType) n = string(T) @@ -79,17 +95,21 @@ function gen_schema(T::DataType, prefix = prefix; pandera = true) fieldname = string(fieldnames) required = true ref = false - @info fieldtype, fieldtype <: Ribasim.config.TableOption if fieldtype <: Ribasim.config.TableOption schema["properties"][fieldname] = Dict("\$ref" => "$(prefix)$(strip_prefix(fieldtype)).schema.json") ref = true else + type = jsontype(fieldtype) schema["properties"][fieldname] = Dict{String, Any}( "description" => "$fieldname", - "type" => jsontype(fieldtype), "format" => jsonformat(fieldtype), ) + if type isa AbstractString + schema["properties"][fieldname]["type"] = type + else + merge!(schema["properties"][fieldname], type) + end end if T <: Ribasim.config.TableOption d = field_default(T, fieldnames) diff --git a/docs/schema/DiscreteControlCondition.schema.json b/docs/schema/DiscreteControlCondition.schema.json index 68eda57ca..6493902a6 100644 --- a/docs/schema/DiscreteControlCondition.schema.json +++ b/docs/schema/DiscreteControlCondition.schema.json @@ -30,9 +30,7 @@ "look_ahead": { "format": "default", "description": "look_ahead", - "type": [ - "number" - ] + "type": "number" } }, "required": [ diff --git a/docs/schema/FlowBoundaryStatic.schema.json b/docs/schema/FlowBoundaryStatic.schema.json index 2bb39e060..ad80e989f 100644 --- a/docs/schema/FlowBoundaryStatic.schema.json +++ b/docs/schema/FlowBoundaryStatic.schema.json @@ -10,9 +10,7 @@ "active": { "format": "default", "description": "active", - "type": [ - "boolean" - ] + "type": "boolean" }, "flow_rate": { "format": "double", diff --git a/docs/schema/FractionalFlowStatic.schema.json b/docs/schema/FractionalFlowStatic.schema.json index f98567832..14e98eaae 100644 --- a/docs/schema/FractionalFlowStatic.schema.json +++ b/docs/schema/FractionalFlowStatic.schema.json @@ -20,9 +20,7 @@ "control_state": { "format": "default", "description": "control_state", - "type": [ - "string" - ] + "type": "string" } }, "required": [ diff --git a/docs/schema/LevelBoundaryStatic.schema.json b/docs/schema/LevelBoundaryStatic.schema.json index 82de99862..bf217c23b 100644 --- a/docs/schema/LevelBoundaryStatic.schema.json +++ b/docs/schema/LevelBoundaryStatic.schema.json @@ -10,9 +10,7 @@ "active": { "format": "default", "description": "active", - "type": [ - "boolean" - ] + "type": "boolean" }, "node_id": { "format": "default", diff --git a/docs/schema/LinearResistanceStatic.schema.json b/docs/schema/LinearResistanceStatic.schema.json index a5d6efdb6..2a5c22de2 100644 --- a/docs/schema/LinearResistanceStatic.schema.json +++ b/docs/schema/LinearResistanceStatic.schema.json @@ -10,9 +10,7 @@ "active": { "format": "default", "description": "active", - "type": [ - "boolean" - ] + "type": "boolean" }, "node_id": { "format": "default", @@ -27,9 +25,7 @@ "control_state": { "format": "default", "description": "control_state", - "type": [ - "string" - ] + "type": "string" } }, "required": [ diff --git a/docs/schema/ManningResistanceStatic.schema.json b/docs/schema/ManningResistanceStatic.schema.json index 5094f68df..069ba9eda 100644 --- a/docs/schema/ManningResistanceStatic.schema.json +++ b/docs/schema/ManningResistanceStatic.schema.json @@ -20,9 +20,7 @@ "active": { "format": "default", "description": "active", - "type": [ - "boolean" - ] + "type": "boolean" }, "profile_width": { "format": "double", @@ -42,9 +40,7 @@ "control_state": { "format": "default", "description": "control_state", - "type": [ - "string" - ] + "type": "string" } }, "required": [ diff --git a/docs/schema/OutletStatic.schema.json b/docs/schema/OutletStatic.schema.json index d307169fc..049c5400b 100644 --- a/docs/schema/OutletStatic.schema.json +++ b/docs/schema/OutletStatic.schema.json @@ -4,9 +4,7 @@ "max_flow_rate": { "format": "default", "description": "max_flow_rate", - "type": [ - "number" - ] + "type": "number" }, "remarks": { "format": "default", @@ -17,9 +15,7 @@ "active": { "format": "default", "description": "active", - "type": [ - "boolean" - ] + "type": "boolean" }, "flow_rate": { "format": "double", @@ -34,16 +30,12 @@ "control_state": { "format": "default", "description": "control_state", - "type": [ - "string" - ] + "type": "string" }, "min_flow_rate": { "format": "default", "description": "min_flow_rate", - "type": [ - "number" - ] + "type": "number" } }, "required": [ diff --git a/docs/schema/Output.schema.json b/docs/schema/Output.schema.json index 8316430ed..e942591c5 100644 --- a/docs/schema/Output.schema.json +++ b/docs/schema/Output.schema.json @@ -5,7 +5,7 @@ "format": "default", "default": "zstd", "description": "compression", - "type": "object" + "type": "string" }, "basin": { "format": "default", @@ -29,9 +29,7 @@ "format": "default", "default": null, "description": "outstate", - "type": [ - "string" - ] + "type": "string" }, "compression_level": { "format": "default", diff --git a/docs/schema/PIDControlStatic.schema.json b/docs/schema/PIDControlStatic.schema.json index b7725031f..e935257bc 100644 --- a/docs/schema/PIDControlStatic.schema.json +++ b/docs/schema/PIDControlStatic.schema.json @@ -20,9 +20,7 @@ "active": { "format": "default", "description": "active", - "type": [ - "boolean" - ] + "type": "boolean" }, "proportional": { "format": "double", @@ -47,9 +45,7 @@ "control_state": { "format": "default", "description": "control_state", - "type": [ - "string" - ] + "type": "string" } }, "required": [ diff --git a/docs/schema/PidControlTime.schema.json b/docs/schema/PidControlTime.schema.json index c86236d97..354ef9cc6 100644 --- a/docs/schema/PidControlTime.schema.json +++ b/docs/schema/PidControlTime.schema.json @@ -45,9 +45,7 @@ "control_state": { "format": "default", "description": "control_state", - "type": [ - "string" - ] + "type": "string" } }, "required": [ diff --git a/docs/schema/PumpStatic.schema.json b/docs/schema/PumpStatic.schema.json index fe4a95864..818ac33ac 100644 --- a/docs/schema/PumpStatic.schema.json +++ b/docs/schema/PumpStatic.schema.json @@ -4,9 +4,7 @@ "max_flow_rate": { "format": "default", "description": "max_flow_rate", - "type": [ - "number" - ] + "type": "number" }, "remarks": { "format": "default", @@ -17,9 +15,7 @@ "active": { "format": "default", "description": "active", - "type": [ - "boolean" - ] + "type": "boolean" }, "flow_rate": { "format": "double", @@ -34,16 +30,12 @@ "control_state": { "format": "default", "description": "control_state", - "type": [ - "string" - ] + "type": "string" }, "min_flow_rate": { "format": "default", "description": "min_flow_rate", - "type": [ - "number" - ] + "type": "number" } }, "required": [ diff --git a/docs/schema/Solver.schema.json b/docs/schema/Solver.schema.json index 90a754646..d083edcdf 100644 --- a/docs/schema/Solver.schema.json +++ b/docs/schema/Solver.schema.json @@ -9,13 +9,20 @@ }, "saveat": { "format": "default", + "anyOf": [ + { + "items": { + "type": "number" + }, + "type": "array" + }, + { + "type": "number" + } + ], "default": [ ], - "description": "saveat", - "type": [ - "number", - "list" - ] + "description": "saveat" }, "maxiters": { "format": "default", diff --git a/docs/schema/TabulatedRatingCurveStatic.schema.json b/docs/schema/TabulatedRatingCurveStatic.schema.json index b89ea9ccf..f6d61f0ac 100644 --- a/docs/schema/TabulatedRatingCurveStatic.schema.json +++ b/docs/schema/TabulatedRatingCurveStatic.schema.json @@ -10,9 +10,7 @@ "active": { "format": "default", "description": "active", - "type": [ - "boolean" - ] + "type": "boolean" }, "node_id": { "format": "default", @@ -32,9 +30,7 @@ "control_state": { "format": "default", "description": "control_state", - "type": [ - "string" - ] + "type": "string" } }, "required": [ diff --git a/docs/schema/basin.schema.json b/docs/schema/basin.schema.json index c1c0d6fb8..31596eec5 100644 --- a/docs/schema/basin.schema.json +++ b/docs/schema/basin.schema.json @@ -5,33 +5,25 @@ "format": "default", "default": null, "description": "profile", - "type": [ - "string" - ] + "type": "string" }, "static": { "format": "default", "default": null, "description": "static", - "type": [ - "string" - ] + "type": "string" }, "forcing": { "format": "default", "default": null, "description": "forcing", - "type": [ - "string" - ] + "type": "string" }, "state": { "format": "default", "default": null, "description": "state", - "type": [ - "string" - ] + "type": "string" } }, "required": [ diff --git a/docs/schema/discrete_control.schema.json b/docs/schema/discrete_control.schema.json index 022129760..7a7eb9e73 100644 --- a/docs/schema/discrete_control.schema.json +++ b/docs/schema/discrete_control.schema.json @@ -5,17 +5,13 @@ "format": "default", "default": null, "description": "logic", - "type": [ - "string" - ] + "type": "string" }, "condition": { "format": "default", "default": null, "description": "condition", - "type": [ - "string" - ] + "type": "string" } }, "required": [ diff --git a/docs/schema/flow_boundary.schema.json b/docs/schema/flow_boundary.schema.json index 4d1219555..cf4155546 100644 --- a/docs/schema/flow_boundary.schema.json +++ b/docs/schema/flow_boundary.schema.json @@ -5,17 +5,13 @@ "format": "default", "default": null, "description": "time", - "type": [ - "string" - ] + "type": "string" }, "static": { "format": "default", "default": null, "description": "static", - "type": [ - "string" - ] + "type": "string" } }, "required": [ diff --git a/docs/schema/fractional_flow.schema.json b/docs/schema/fractional_flow.schema.json index 91574a793..c999b37af 100644 --- a/docs/schema/fractional_flow.schema.json +++ b/docs/schema/fractional_flow.schema.json @@ -5,9 +5,7 @@ "format": "default", "default": null, "description": "static", - "type": [ - "string" - ] + "type": "string" } }, "required": [ diff --git a/docs/schema/level_boundary.schema.json b/docs/schema/level_boundary.schema.json index 6c5523b05..ddc9e0d2c 100644 --- a/docs/schema/level_boundary.schema.json +++ b/docs/schema/level_boundary.schema.json @@ -5,17 +5,13 @@ "format": "default", "default": null, "description": "time", - "type": [ - "string" - ] + "type": "string" }, "static": { "format": "default", "default": null, "description": "static", - "type": [ - "string" - ] + "type": "string" } }, "required": [ diff --git a/docs/schema/linear_resistance.schema.json b/docs/schema/linear_resistance.schema.json index 9805e87b8..cba039b7a 100644 --- a/docs/schema/linear_resistance.schema.json +++ b/docs/schema/linear_resistance.schema.json @@ -5,9 +5,7 @@ "format": "default", "default": null, "description": "static", - "type": [ - "string" - ] + "type": "string" } }, "required": [ diff --git a/docs/schema/manning_resistance.schema.json b/docs/schema/manning_resistance.schema.json index e943a3a94..fb81bfd74 100644 --- a/docs/schema/manning_resistance.schema.json +++ b/docs/schema/manning_resistance.schema.json @@ -5,9 +5,7 @@ "format": "default", "default": null, "description": "static", - "type": [ - "string" - ] + "type": "string" } }, "required": [ diff --git a/docs/schema/outlet.schema.json b/docs/schema/outlet.schema.json index 581719bab..1e6f6246d 100644 --- a/docs/schema/outlet.schema.json +++ b/docs/schema/outlet.schema.json @@ -5,9 +5,7 @@ "format": "default", "default": null, "description": "static", - "type": [ - "string" - ] + "type": "string" } }, "required": [ diff --git a/docs/schema/pid_control.schema.json b/docs/schema/pid_control.schema.json index 2a1d96799..fabb00b4d 100644 --- a/docs/schema/pid_control.schema.json +++ b/docs/schema/pid_control.schema.json @@ -5,17 +5,13 @@ "format": "default", "default": null, "description": "time", - "type": [ - "string" - ] + "type": "string" }, "static": { "format": "default", "default": null, "description": "static", - "type": [ - "string" - ] + "type": "string" } }, "required": [ diff --git a/docs/schema/pump.schema.json b/docs/schema/pump.schema.json index a965ab0b1..0ca5dcdf3 100644 --- a/docs/schema/pump.schema.json +++ b/docs/schema/pump.schema.json @@ -5,9 +5,7 @@ "format": "default", "default": null, "description": "static", - "type": [ - "string" - ] + "type": "string" } }, "required": [ diff --git a/docs/schema/tabulated_rating_curve.schema.json b/docs/schema/tabulated_rating_curve.schema.json index f92832e0b..02108a5a1 100644 --- a/docs/schema/tabulated_rating_curve.schema.json +++ b/docs/schema/tabulated_rating_curve.schema.json @@ -5,17 +5,13 @@ "format": "default", "default": null, "description": "time", - "type": [ - "string" - ] + "type": "string" }, "static": { "format": "default", "default": null, "description": "static", - "type": [ - "string" - ] + "type": "string" } }, "required": [ diff --git a/docs/schema/terminal.schema.json b/docs/schema/terminal.schema.json index cbbb272bf..abfc35e09 100644 --- a/docs/schema/terminal.schema.json +++ b/docs/schema/terminal.schema.json @@ -5,9 +5,7 @@ "format": "default", "default": null, "description": "static", - "type": [ - "string" - ] + "type": "string" } }, "required": [ diff --git a/python/ribasim/ribasim/config.py b/python/ribasim/ribasim/config.py index 3634c290a..db6ec3577 100644 --- a/python/ribasim/ribasim/config.py +++ b/python/ribasim/ribasim/config.py @@ -1,17 +1,17 @@ # generated by datamodel-codegen: # filename: Config.schema.json -# timestamp: 2023-09-07T15:04:09+00:00 +# timestamp: 2023-09-12T09:11:45+00:00 from __future__ import annotations from datetime import datetime -from typing import Any, Dict, Optional, Union +from typing import List, Optional, Union from pydantic import BaseModel, Field class Output(BaseModel): - compression: Optional[Dict[str, Any]] = Field("zstd", description="compression") + compression: Optional[str] = Field("zstd", description="compression") basin: Optional[str] = Field("output/basin.arrow", description="basin") flow: Optional[str] = Field("output/flow.arrow", description="flow") control: Optional[str] = Field("output/control.arrow", description="control") @@ -35,7 +35,7 @@ class DiscreteControl(BaseModel): class Solver(BaseModel): reltol: Optional[float] = Field(0.001, description="reltol") - saveat: Optional[Union[float, Any]] = Field([], description="saveat") + saveat: Optional[Union[List[float], float]] = Field([], description="saveat") maxiters: Optional[int] = Field(1000000000, description="maxiters") autodiff: Optional[bool] = Field(True, description="autodiff") adaptive: Optional[bool] = Field(True, description="adaptive") From bcb893f05527959302050b20abe4e2feff15874b Mon Sep 17 00:00:00 2001 From: Maarten Pronk Date: Tue, 12 Sep 2023 11:16:36 +0200 Subject: [PATCH 04/13] Add autogenerated schemas.py. --- python/ribasim/ribasim/schemas.py | 36 +++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) create mode 100644 python/ribasim/ribasim/schemas.py diff --git a/python/ribasim/ribasim/schemas.py b/python/ribasim/ribasim/schemas.py new file mode 100644 index 000000000..e9d6024c1 --- /dev/null +++ b/python/ribasim/ribasim/schemas.py @@ -0,0 +1,36 @@ +""" +Generate Pandera Schemas from all autogenerated Pydantic Models + +These classes have Schema as a postfix, so Ribasim.models.PumpStatic +becomes Ribasim.node_types.PumpStaticSchema. +""" +import inspect +import sys + +import pandera as pa +from pandera.engines.pandas_engine import PydanticModel + +from ribasim import models + + +def gen_schema(name, cls): + classname = f"{name}Schema" + setattr( + sys.modules[__name__], + classname, + type( + classname, + (pa.DataFrameModel,), + { + "Config": type( + f"{classname}.Config", + (), + {"dtype": PydanticModel(cls), "coerce": True}, + ) + }, + ), + ) + + +for name, cls in inspect.getmembers(models, inspect.isclass): + gen_schema(name, cls) From 8ba027408791a56fca5f07f6a5a5fd3c433d71f2 Mon Sep 17 00:00:00 2001 From: Maarten Pronk Date: Tue, 12 Sep 2023 13:10:20 +0200 Subject: [PATCH 05/13] Ignore dynamically created imports in mypy. --- python/ribasim/ribasim/node_types/basin.py | 2 +- .../ribasim/node_types/discrete_control.py | 5 +++- .../ribasim/node_types/flow_boundary.py | 5 +++- .../ribasim/node_types/fractional_flow.py | 2 +- .../ribasim/node_types/level_boundary.py | 5 +++- .../ribasim/node_types/linear_resistance.py | 2 +- .../ribasim/node_types/manning_resistance.py | 2 +- python/ribasim/ribasim/node_types/outlet.py | 2 +- .../ribasim/ribasim/node_types/pid_control.py | 2 +- python/ribasim/ribasim/node_types/pump.py | 2 +- .../node_types/tabulated_rating_curve.py | 2 +- python/ribasim/ribasim/node_types/terminal.py | 2 +- python/ribasim/ribasim/schemas.py | 29 +++++++++---------- 13 files changed, 34 insertions(+), 28 deletions(-) diff --git a/python/ribasim/ribasim/node_types/basin.py b/python/ribasim/ribasim/node_types/basin.py index 36a2f8342..900b407c3 100644 --- a/python/ribasim/ribasim/node_types/basin.py +++ b/python/ribasim/ribasim/node_types/basin.py @@ -3,7 +3,7 @@ from pandera.typing import DataFrame from ribasim.input_base import TableModel -from ribasim.schemas import ( +from ribasim.schemas import ( # type: ignore BasinForcingSchema, BasinProfileSchema, BasinStateSchema, diff --git a/python/ribasim/ribasim/node_types/discrete_control.py b/python/ribasim/ribasim/node_types/discrete_control.py index 16695c731..99c3fe4b6 100644 --- a/python/ribasim/ribasim/node_types/discrete_control.py +++ b/python/ribasim/ribasim/node_types/discrete_control.py @@ -1,7 +1,10 @@ from pandera.typing import DataFrame from ribasim.input_base import TableModel -from ribasim.schemas import DiscreteControlConditionSchema, DiscreteControlLogicSchema +from ribasim.schemas import ( # type: ignore + DiscreteControlConditionSchema, + DiscreteControlLogicSchema, +) __all__ = ("DiscreteControl",) diff --git a/python/ribasim/ribasim/node_types/flow_boundary.py b/python/ribasim/ribasim/node_types/flow_boundary.py index 713d6fa03..f78f913ca 100644 --- a/python/ribasim/ribasim/node_types/flow_boundary.py +++ b/python/ribasim/ribasim/node_types/flow_boundary.py @@ -3,7 +3,10 @@ from pandera.typing import DataFrame from ribasim.input_base import TableModel -from ribasim.schemas import FlowBoundaryStaticSchema, FlowBoundaryTimeSchema +from ribasim.schemas import ( # type: ignore + FlowBoundaryStaticSchema, + FlowBoundaryTimeSchema, +) __all__ = ("FlowBoundary",) diff --git a/python/ribasim/ribasim/node_types/fractional_flow.py b/python/ribasim/ribasim/node_types/fractional_flow.py index 1ecba37d5..8e6648701 100644 --- a/python/ribasim/ribasim/node_types/fractional_flow.py +++ b/python/ribasim/ribasim/node_types/fractional_flow.py @@ -1,7 +1,7 @@ from pandera.typing import DataFrame from ribasim.input_base import TableModel -from ribasim.schemas import FractionalFlowStaticSchema +from ribasim.schemas import FractionalFlowStaticSchema # type: ignore __all__ = ("FractionalFlow",) diff --git a/python/ribasim/ribasim/node_types/level_boundary.py b/python/ribasim/ribasim/node_types/level_boundary.py index 777938cd4..c83b9e064 100644 --- a/python/ribasim/ribasim/node_types/level_boundary.py +++ b/python/ribasim/ribasim/node_types/level_boundary.py @@ -3,7 +3,10 @@ from pandera.typing import DataFrame from ribasim.input_base import TableModel -from ribasim.schemas import LevelBoundaryStaticSchema, LevelBoundaryTimeSchema +from ribasim.schemas import ( # type: ignore + LevelBoundaryStaticSchema, + LevelBoundaryTimeSchema, +) __all__ = ("LevelBoundary",) diff --git a/python/ribasim/ribasim/node_types/linear_resistance.py b/python/ribasim/ribasim/node_types/linear_resistance.py index 8844364de..ec93a53e1 100644 --- a/python/ribasim/ribasim/node_types/linear_resistance.py +++ b/python/ribasim/ribasim/node_types/linear_resistance.py @@ -1,7 +1,7 @@ from pandera.typing import DataFrame from ribasim.input_base import TableModel -from ribasim.schemas import LinearResistanceStaticSchema +from ribasim.schemas import LinearResistanceStaticSchema # type: ignore __all__ = ("LinearResistance",) diff --git a/python/ribasim/ribasim/node_types/manning_resistance.py b/python/ribasim/ribasim/node_types/manning_resistance.py index 7c691403d..50dc43fe3 100644 --- a/python/ribasim/ribasim/node_types/manning_resistance.py +++ b/python/ribasim/ribasim/node_types/manning_resistance.py @@ -1,7 +1,7 @@ from pandera.typing import DataFrame from ribasim.input_base import TableModel -from ribasim.schemas import ManningResistanceStaticSchema +from ribasim.schemas import ManningResistanceStaticSchema # type: ignore __all__ = ("ManningResistance",) diff --git a/python/ribasim/ribasim/node_types/outlet.py b/python/ribasim/ribasim/node_types/outlet.py index 85849e6b4..46fb70c22 100644 --- a/python/ribasim/ribasim/node_types/outlet.py +++ b/python/ribasim/ribasim/node_types/outlet.py @@ -1,7 +1,7 @@ from pandera.typing import DataFrame from ribasim.input_base import TableModel -from ribasim.schemas import OutletStaticSchema +from ribasim.schemas import OutletStaticSchema # type: ignore __all__ = ("Outlet",) diff --git a/python/ribasim/ribasim/node_types/pid_control.py b/python/ribasim/ribasim/node_types/pid_control.py index 4869dcdaf..a75eae42d 100644 --- a/python/ribasim/ribasim/node_types/pid_control.py +++ b/python/ribasim/ribasim/node_types/pid_control.py @@ -3,7 +3,7 @@ from pandera.typing import DataFrame from ribasim.input_base import TableModel -from ribasim.schemas import PidControlStaticSchema, PidControlTimeSchema +from ribasim.schemas import PidControlStaticSchema, PidControlTimeSchema # type: ignore __all__ = ("PidControl",) diff --git a/python/ribasim/ribasim/node_types/pump.py b/python/ribasim/ribasim/node_types/pump.py index a3eb4a258..d2a966a25 100644 --- a/python/ribasim/ribasim/node_types/pump.py +++ b/python/ribasim/ribasim/node_types/pump.py @@ -1,7 +1,7 @@ from pandera.typing import DataFrame from ribasim.input_base import TableModel -from ribasim.schemas import PumpStaticSchema +from ribasim.schemas import PumpStaticSchema # type: ignore __all__ = ("Pump",) diff --git a/python/ribasim/ribasim/node_types/tabulated_rating_curve.py b/python/ribasim/ribasim/node_types/tabulated_rating_curve.py index 15a61752e..765a45965 100644 --- a/python/ribasim/ribasim/node_types/tabulated_rating_curve.py +++ b/python/ribasim/ribasim/node_types/tabulated_rating_curve.py @@ -3,7 +3,7 @@ from pandera.typing import DataFrame from ribasim.input_base import TableModel -from ribasim.schemas import ( +from ribasim.schemas import ( # type: ignore TabulatedRatingCurveStaticSchema, TabulatedRatingCurveTimeSchema, ) diff --git a/python/ribasim/ribasim/node_types/terminal.py b/python/ribasim/ribasim/node_types/terminal.py index bbc57eca3..3eb47e2a2 100644 --- a/python/ribasim/ribasim/node_types/terminal.py +++ b/python/ribasim/ribasim/node_types/terminal.py @@ -1,7 +1,7 @@ from pandera.typing import DataFrame from ribasim.input_base import TableModel -from ribasim.schemas import TerminalStaticSchema +from ribasim.schemas import TerminalStaticSchema # type: ignore __all__ = ("Terminal",) diff --git a/python/ribasim/ribasim/schemas.py b/python/ribasim/ribasim/schemas.py index e9d6024c1..c7c44ab0c 100644 --- a/python/ribasim/ribasim/schemas.py +++ b/python/ribasim/ribasim/schemas.py @@ -2,7 +2,7 @@ Generate Pandera Schemas from all autogenerated Pydantic Models These classes have Schema as a postfix, so Ribasim.models.PumpStatic -becomes Ribasim.node_types.PumpStaticSchema. +becomes Ribasim.schemas.PumpStaticSchema. """ import inspect import sys @@ -14,22 +14,19 @@ def gen_schema(name, cls): - classname = f"{name}Schema" - setattr( - sys.modules[__name__], - classname, - type( - classname, - (pa.DataFrameModel,), - { - "Config": type( - f"{classname}.Config", - (), - {"dtype": PydanticModel(cls), "coerce": True}, - ) - }, - ), + cname = f"{name}Schema" + ctype = type( + cname, + (pa.DataFrameModel,), + { + "Config": type( + f"{cname}.Config", + (), + {"dtype": PydanticModel(cls), "coerce": True}, + ) + }, ) + setattr(sys.modules[__name__], cname, ctype) for name, cls in inspect.getmembers(models, inspect.isclass): From f503acb8674a6c59153298f655d036d82e1d2107 Mon Sep 17 00:00:00 2001 From: Maarten Pronk Date: Tue, 12 Sep 2023 13:56:14 +0200 Subject: [PATCH 06/13] Add documentation for Config generation. --- .github/pull_request_template.md | 2 +- docs/contribute/addnode.qmd | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index e2e198a22..d1149de66 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -29,7 +29,7 @@ Updating other julia files may be required. - update `docs/core/equations.qmd` - update `docs/core/usage.qmd` - update `docs/python/examples.ipynb` # or start a new example model -- update `docs/schema*.json` by running `julia --project=docs docs/gen_schema.jl` and `datamodel-codegen --use-title-as-name --input docs/schema/root.schema.json --output python/ribasim/ribasim/models.py` +- update `docs/schema*.json` by running `julia --project=docs docs/gen_schema.jl` and `datamodel-codegen --use-title-as-name --use-double-quotes --input docs/schema/root.schema.json --output python/ribasim/ribasim/models.py` and `datamodel-codegen --use-title-as-name --use-double-quotes --input docs/schema/Config.schema.json --output python/ribasim/ribasim/config.py` - update the instructions in `docs/contribute/*.qmd` if something changes there, e.g. something changes in how a new node type must be defined. ## QGIS diff --git a/docs/contribute/addnode.qmd b/docs/contribute/addnode.qmd index 3c15e841f..ed2f047ea 100644 --- a/docs/contribute/addnode.qmd +++ b/docs/contribute/addnode.qmd @@ -247,10 +247,11 @@ If you haven't done so before, you first need to instantiate your docs environme Run `julia --project=docs`, followed by running `instantiate` in the Pkg mode (press `]`). ::: -To generate the Python module `models.py` from the JSON Schemas, run: +To generate the Python module `models.py` and `config.py` from the JSON Schemas, run: ``` -datamodel-codegen --use-title-as-name --input docs/schema/root.schema.json --output python/ribasim/ribasim/models.py +datamodel-codegen --use-title-as-name --use-double-quotes --input docs/schema/root.schema.json --output python/ribasim/ribasim/models.py +`datamodel-codegen --use-title-as-name --use-double-quotes --input docs/schema/Config.schema.json --output python/ribasim/ribasim/config.py` ``` Run [Black](python.qmd#sec-black) to format the generated code. From 3ca5bb5ca16861c37e32757cfd6df2275bacc354 Mon Sep 17 00:00:00 2001 From: Maarten Pronk Date: Thu, 14 Sep 2023 13:37:53 +0200 Subject: [PATCH 07/13] Models without a timestamp. Schemas without description, but non-optional and defaults. --- .github/pull_request_template.md | 2 +- docs/contribute/addnode.qmd | 4 +- docs/gen_schema.jl | 18 +- docs/schema/BasinForcing.schema.json | 7 - docs/schema/BasinProfile.schema.json | 3 - docs/schema/BasinState.schema.json | 2 - docs/schema/BasinStatic.schema.json | 6 - docs/schema/Config.schema.json | 98 +++++++++- .../DiscreteControlCondition.schema.json | 5 - docs/schema/DiscreteControlLogic.schema.json | 3 - docs/schema/Edge.schema.json | 4 - docs/schema/FlowBoundaryStatic.schema.json | 3 - docs/schema/FlowBoundaryTime.schema.json | 3 - docs/schema/FractionalFlowStatic.schema.json | 3 - docs/schema/LevelBoundaryStatic.schema.json | 3 - docs/schema/LevelBoundaryTime.schema.json | 3 - .../schema/LinearResistanceStatic.schema.json | 4 - docs/schema/Logging.schema.json | 4 +- .../ManningResistanceStatic.schema.json | 7 - docs/schema/Node.schema.json | 2 - docs/schema/OutletStatic.schema.json | 6 - docs/schema/Output.schema.json | 12 +- docs/schema/PIDControlStatic.schema.json | 8 - docs/schema/PidControlTime.schema.json | 8 - docs/schema/PumpStatic.schema.json | 6 - docs/schema/Solver.schema.json | 20 +- .../TabulatedRatingCurveStatic.schema.json | 5 - .../TabulatedRatingCurveTime.schema.json | 4 - docs/schema/TerminalStatic.schema.json | 1 - docs/schema/basin.schema.json | 8 +- docs/schema/discrete_control.schema.json | 4 +- docs/schema/flow_boundary.schema.json | 4 +- docs/schema/fractional_flow.schema.json | 2 +- docs/schema/level_boundary.schema.json | 4 +- docs/schema/linear_resistance.schema.json | 2 +- docs/schema/manning_resistance.schema.json | 2 +- docs/schema/outlet.schema.json | 2 +- docs/schema/pid_control.schema.json | 4 +- docs/schema/pump.schema.json | 2 +- .../schema/tabulated_rating_curve.schema.json | 4 +- docs/schema/terminal.schema.json | 2 +- python/ribasim/ribasim/config.py | 178 ++++++++++++------ 42 files changed, 258 insertions(+), 214 deletions(-) diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index d1149de66..c960ba153 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -29,7 +29,7 @@ Updating other julia files may be required. - update `docs/core/equations.qmd` - update `docs/core/usage.qmd` - update `docs/python/examples.ipynb` # or start a new example model -- update `docs/schema*.json` by running `julia --project=docs docs/gen_schema.jl` and `datamodel-codegen --use-title-as-name --use-double-quotes --input docs/schema/root.schema.json --output python/ribasim/ribasim/models.py` and `datamodel-codegen --use-title-as-name --use-double-quotes --input docs/schema/Config.schema.json --output python/ribasim/ribasim/config.py` +- update `docs/schema*.json` by running `julia --project=docs docs/gen_schema.jl` and `datamodel-codegen --use-title-as-name --use-double-quotes --disable-timestamp --use-default --input docs/schema/root.schema.json --output python/ribasim/ribasim/models.py` and `datamodel-codegen --use-title-as-name --use-double-quotes --disable-timestamp --use-default --input docs/schema/Config.schema.json --output python/ribasim/ribasim/config.py` - update the instructions in `docs/contribute/*.qmd` if something changes there, e.g. something changes in how a new node type must be defined. ## QGIS diff --git a/docs/contribute/addnode.qmd b/docs/contribute/addnode.qmd index ed2f047ea..c14feb82c 100644 --- a/docs/contribute/addnode.qmd +++ b/docs/contribute/addnode.qmd @@ -250,8 +250,8 @@ Run `julia --project=docs`, followed by running `instantiate` in the Pkg mode (p To generate the Python module `models.py` and `config.py` from the JSON Schemas, run: ``` -datamodel-codegen --use-title-as-name --use-double-quotes --input docs/schema/root.schema.json --output python/ribasim/ribasim/models.py -`datamodel-codegen --use-title-as-name --use-double-quotes --input docs/schema/Config.schema.json --output python/ribasim/ribasim/config.py` +datamodel-codegen --use-title-as-name --use-double-quotes --disable-timestamp --use-default --input docs/schema/root.schema.json --output python/ribasim/ribasim/models.py +datamodel-codegen --use-title-as-name --use-double-quotes --disable-timestamp --use-default --input docs/schema/Config.schema.json --output python/ribasim/ribasim/config.py ``` Run [Black](python.qmd#sec-black) to format the generated code. diff --git a/docs/gen_schema.jl b/docs/gen_schema.jl index be80c033b..aa69b284b 100644 --- a/docs/gen_schema.jl +++ b/docs/gen_schema.jl @@ -93,18 +93,19 @@ function gen_schema(T::DataType, prefix = prefix; pandera = true) ) for (fieldnames, fieldtype) in zip(fieldnames(T), fieldtypes(T)) fieldname = string(fieldnames) - required = true ref = false + required = true if fieldtype <: Ribasim.config.TableOption - schema["properties"][fieldname] = - Dict("\$ref" => "$(prefix)$(strip_prefix(fieldtype)).schema.json") + schema["properties"][fieldname] = Dict( + "\$ref" => "$(prefix)$(strip_prefix(fieldtype)).schema.json", + "default" => fieldtype(), + ) ref = true + required = false else type = jsontype(fieldtype) - schema["properties"][fieldname] = Dict{String, Any}( - "description" => "$fieldname", - "format" => jsonformat(fieldtype), - ) + schema["properties"][fieldname] = + Dict{String, Any}("format" => jsonformat(fieldtype)) if type isa AbstractString schema["properties"][fieldname]["type"] = type else @@ -117,10 +118,9 @@ function gen_schema(T::DataType, prefix = prefix; pandera = true) if !ref schema["properties"][fieldname]["default"] = jsondefault(d) end - required = false end end - if !((fieldtype isa Union) && (fieldtype.a === Missing)) && required + if !((fieldtype isa Union) && (fieldtype.a === Missing)) push!(schema["required"], fieldname) end end diff --git a/docs/schema/BasinForcing.schema.json b/docs/schema/BasinForcing.schema.json index 956cc7e99..0f0ad5eb2 100644 --- a/docs/schema/BasinForcing.schema.json +++ b/docs/schema/BasinForcing.schema.json @@ -9,37 +9,30 @@ }, "time": { "format": "date-time", - "description": "time", "type": "string" }, "precipitation": { "format": "double", - "description": "precipitation", "type": "number" }, "infiltration": { "format": "double", - "description": "infiltration", "type": "number" }, "urban_runoff": { "format": "double", - "description": "urban_runoff", "type": "number" }, "node_id": { "format": "default", - "description": "node_id", "type": "integer" }, "potential_evaporation": { "format": "double", - "description": "potential_evaporation", "type": "number" }, "drainage": { "format": "double", - "description": "drainage", "type": "number" } }, diff --git a/docs/schema/BasinProfile.schema.json b/docs/schema/BasinProfile.schema.json index 9f771318f..98dc78dd0 100644 --- a/docs/schema/BasinProfile.schema.json +++ b/docs/schema/BasinProfile.schema.json @@ -9,17 +9,14 @@ }, "area": { "format": "double", - "description": "area", "type": "number" }, "node_id": { "format": "default", - "description": "node_id", "type": "integer" }, "level": { "format": "double", - "description": "level", "type": "number" } }, diff --git a/docs/schema/BasinState.schema.json b/docs/schema/BasinState.schema.json index a185891c4..906bf5a96 100644 --- a/docs/schema/BasinState.schema.json +++ b/docs/schema/BasinState.schema.json @@ -9,12 +9,10 @@ }, "node_id": { "format": "default", - "description": "node_id", "type": "integer" }, "level": { "format": "double", - "description": "level", "type": "number" } }, diff --git a/docs/schema/BasinStatic.schema.json b/docs/schema/BasinStatic.schema.json index 77e34e79f..a47d2970b 100644 --- a/docs/schema/BasinStatic.schema.json +++ b/docs/schema/BasinStatic.schema.json @@ -9,32 +9,26 @@ }, "precipitation": { "format": "double", - "description": "precipitation", "type": "number" }, "infiltration": { "format": "double", - "description": "infiltration", "type": "number" }, "urban_runoff": { "format": "double", - "description": "urban_runoff", "type": "number" }, "node_id": { "format": "default", - "description": "node_id", "type": "integer" }, "potential_evaporation": { "format": "double", - "description": "potential_evaporation", "type": "number" }, "drainage": { "format": "double", - "description": "drainage", "type": "number" } }, diff --git a/docs/schema/Config.schema.json b/docs/schema/Config.schema.json index 0527b1d3b..f324fc81b 100644 --- a/docs/schema/Config.schema.json +++ b/docs/schema/Config.schema.json @@ -2,94 +2,176 @@ "$schema": "https://json-schema.org/draft/2020-12/schema", "properties": { "output": { + "default": { + "basin": "output/basin.arrow", + "flow": "output/flow.arrow", + "control": "output/control.arrow", + "outstate": null, + "compression": "zstd", + "compression_level": 6 + }, "$ref": "https://deltares.github.io/Ribasim/schema/Output.schema.json" }, "starttime": { "format": "date-time", - "description": "starttime", "type": "string" }, "update_timestep": { "format": "double", "default": 86400, - "description": "update_timestep", "type": "number" }, "input_dir": { "format": "default", "default": ".", - "description": "input_dir", "type": "string" }, "output_dir": { "format": "default", "default": ".", - "description": "output_dir", "type": "string" }, "level_boundary": { + "default": { + "static": null, + "time": null + }, "$ref": "https://deltares.github.io/Ribasim/schema/level_boundary.schema.json" }, "pump": { + "default": { + "static": null + }, "$ref": "https://deltares.github.io/Ribasim/schema/pump.schema.json" }, "discrete_control": { + "default": { + "condition": null, + "logic": null + }, "$ref": "https://deltares.github.io/Ribasim/schema/discrete_control.schema.json" }, "solver": { + "default": { + "algorithm": "QNDF", + "saveat": [ + ], + "adaptive": true, + "dt": 0, + "abstol": 1.0e-6, + "reltol": 0.001, + "maxiters": 1000000000, + "sparse": true, + "autodiff": true + }, "$ref": "https://deltares.github.io/Ribasim/schema/Solver.schema.json" }, "flow_boundary": { + "default": { + "static": null, + "time": null + }, "$ref": "https://deltares.github.io/Ribasim/schema/flow_boundary.schema.json" }, "pid_control": { + "default": { + "static": null, + "time": null + }, "$ref": "https://deltares.github.io/Ribasim/schema/pid_control.schema.json" }, "fractional_flow": { + "default": { + "static": null + }, "$ref": "https://deltares.github.io/Ribasim/schema/fractional_flow.schema.json" }, "relative_dir": { "format": "default", "default": ".", - "description": "relative_dir", "type": "string" }, "endtime": { "format": "date-time", - "description": "endtime", "type": "string" }, "manning_resistance": { + "default": { + "static": null + }, "$ref": "https://deltares.github.io/Ribasim/schema/manning_resistance.schema.json" }, "tabulated_rating_curve": { + "default": { + "static": null, + "time": null + }, "$ref": "https://deltares.github.io/Ribasim/schema/tabulated_rating_curve.schema.json" }, "logging": { + "default": { + "verbosity": { + "level": 0 + }, + "timing": false + }, "$ref": "https://deltares.github.io/Ribasim/schema/Logging.schema.json" }, "outlet": { + "default": { + "static": null + }, "$ref": "https://deltares.github.io/Ribasim/schema/outlet.schema.json" }, "geopackage": { "format": "default", - "description": "geopackage", "type": "string" }, "terminal": { + "default": { + "static": null + }, "$ref": "https://deltares.github.io/Ribasim/schema/terminal.schema.json" }, "basin": { + "default": { + "forcing": null, + "profile": null, + "state": null, + "static": null + }, "$ref": "https://deltares.github.io/Ribasim/schema/basin.schema.json" }, "linear_resistance": { + "default": { + "static": null + }, "$ref": "https://deltares.github.io/Ribasim/schema/linear_resistance.schema.json" } }, "required": [ "starttime", "endtime", - "geopackage" + "update_timestep", + "relative_dir", + "input_dir", + "output_dir", + "geopackage", + "output", + "solver", + "logging", + "terminal", + "pid_control", + "level_boundary", + "pump", + "tabulated_rating_curve", + "flow_boundary", + "basin", + "manning_resistance", + "discrete_control", + "outlet", + "linear_resistance", + "fractional_flow" ], "$id": "https://deltares.github.io/Ribasim/schema/Config.schema.json", "title": "Config", diff --git a/docs/schema/DiscreteControlCondition.schema.json b/docs/schema/DiscreteControlCondition.schema.json index 6493902a6..bcf3fc534 100644 --- a/docs/schema/DiscreteControlCondition.schema.json +++ b/docs/schema/DiscreteControlCondition.schema.json @@ -9,27 +9,22 @@ }, "greater_than": { "format": "double", - "description": "greater_than", "type": "number" }, "listen_feature_id": { "format": "default", - "description": "listen_feature_id", "type": "integer" }, "node_id": { "format": "default", - "description": "node_id", "type": "integer" }, "variable": { "format": "default", - "description": "variable", "type": "string" }, "look_ahead": { "format": "default", - "description": "look_ahead", "type": "number" } }, diff --git a/docs/schema/DiscreteControlLogic.schema.json b/docs/schema/DiscreteControlLogic.schema.json index a0364d83d..76a549b6b 100644 --- a/docs/schema/DiscreteControlLogic.schema.json +++ b/docs/schema/DiscreteControlLogic.schema.json @@ -9,17 +9,14 @@ }, "truth_state": { "format": "default", - "description": "truth_state", "type": "string" }, "node_id": { "format": "default", - "description": "node_id", "type": "integer" }, "control_state": { "format": "default", - "description": "control_state", "type": "string" } }, diff --git a/docs/schema/Edge.schema.json b/docs/schema/Edge.schema.json index 95c8557bd..43626db5f 100644 --- a/docs/schema/Edge.schema.json +++ b/docs/schema/Edge.schema.json @@ -9,22 +9,18 @@ }, "edge_type": { "format": "default", - "description": "edge_type", "type": "string" }, "fid": { "format": "default", - "description": "fid", "type": "integer" }, "to_node_id": { "format": "default", - "description": "to_node_id", "type": "integer" }, "from_node_id": { "format": "default", - "description": "from_node_id", "type": "integer" } }, diff --git a/docs/schema/FlowBoundaryStatic.schema.json b/docs/schema/FlowBoundaryStatic.schema.json index ad80e989f..635bff762 100644 --- a/docs/schema/FlowBoundaryStatic.schema.json +++ b/docs/schema/FlowBoundaryStatic.schema.json @@ -9,17 +9,14 @@ }, "active": { "format": "default", - "description": "active", "type": "boolean" }, "flow_rate": { "format": "double", - "description": "flow_rate", "type": "number" }, "node_id": { "format": "default", - "description": "node_id", "type": "integer" } }, diff --git a/docs/schema/FlowBoundaryTime.schema.json b/docs/schema/FlowBoundaryTime.schema.json index bc65c4633..dc64986ce 100644 --- a/docs/schema/FlowBoundaryTime.schema.json +++ b/docs/schema/FlowBoundaryTime.schema.json @@ -9,17 +9,14 @@ }, "time": { "format": "date-time", - "description": "time", "type": "string" }, "flow_rate": { "format": "double", - "description": "flow_rate", "type": "number" }, "node_id": { "format": "default", - "description": "node_id", "type": "integer" } }, diff --git a/docs/schema/FractionalFlowStatic.schema.json b/docs/schema/FractionalFlowStatic.schema.json index 14e98eaae..f09e4e600 100644 --- a/docs/schema/FractionalFlowStatic.schema.json +++ b/docs/schema/FractionalFlowStatic.schema.json @@ -9,17 +9,14 @@ }, "node_id": { "format": "default", - "description": "node_id", "type": "integer" }, "fraction": { "format": "double", - "description": "fraction", "type": "number" }, "control_state": { "format": "default", - "description": "control_state", "type": "string" } }, diff --git a/docs/schema/LevelBoundaryStatic.schema.json b/docs/schema/LevelBoundaryStatic.schema.json index bf217c23b..42a58db1c 100644 --- a/docs/schema/LevelBoundaryStatic.schema.json +++ b/docs/schema/LevelBoundaryStatic.schema.json @@ -9,17 +9,14 @@ }, "active": { "format": "default", - "description": "active", "type": "boolean" }, "node_id": { "format": "default", - "description": "node_id", "type": "integer" }, "level": { "format": "double", - "description": "level", "type": "number" } }, diff --git a/docs/schema/LevelBoundaryTime.schema.json b/docs/schema/LevelBoundaryTime.schema.json index fb4ef108f..e50696c32 100644 --- a/docs/schema/LevelBoundaryTime.schema.json +++ b/docs/schema/LevelBoundaryTime.schema.json @@ -9,17 +9,14 @@ }, "time": { "format": "date-time", - "description": "time", "type": "string" }, "node_id": { "format": "default", - "description": "node_id", "type": "integer" }, "level": { "format": "double", - "description": "level", "type": "number" } }, diff --git a/docs/schema/LinearResistanceStatic.schema.json b/docs/schema/LinearResistanceStatic.schema.json index 2a5c22de2..997e2f3c2 100644 --- a/docs/schema/LinearResistanceStatic.schema.json +++ b/docs/schema/LinearResistanceStatic.schema.json @@ -9,22 +9,18 @@ }, "active": { "format": "default", - "description": "active", "type": "boolean" }, "node_id": { "format": "default", - "description": "node_id", "type": "integer" }, "resistance": { "format": "double", - "description": "resistance", "type": "number" }, "control_state": { "format": "default", - "description": "control_state", "type": "string" } }, diff --git a/docs/schema/Logging.schema.json b/docs/schema/Logging.schema.json index 96ad1dada..86fb596a7 100644 --- a/docs/schema/Logging.schema.json +++ b/docs/schema/Logging.schema.json @@ -4,17 +4,17 @@ "timing": { "format": "default", "default": false, - "description": "timing", "type": "boolean" }, "verbosity": { "format": "default", "default": "info", - "description": "verbosity", "type": "string" } }, "required": [ + "verbosity", + "timing" ], "$id": "https://deltares.github.io/Ribasim/schema/Logging.schema.json", "title": "Logging", diff --git a/docs/schema/ManningResistanceStatic.schema.json b/docs/schema/ManningResistanceStatic.schema.json index 069ba9eda..8e68c8e04 100644 --- a/docs/schema/ManningResistanceStatic.schema.json +++ b/docs/schema/ManningResistanceStatic.schema.json @@ -3,12 +3,10 @@ "properties": { "length": { "format": "double", - "description": "length", "type": "number" }, "manning_n": { "format": "double", - "description": "manning_n", "type": "number" }, "remarks": { @@ -19,27 +17,22 @@ }, "active": { "format": "default", - "description": "active", "type": "boolean" }, "profile_width": { "format": "double", - "description": "profile_width", "type": "number" }, "node_id": { "format": "default", - "description": "node_id", "type": "integer" }, "profile_slope": { "format": "double", - "description": "profile_slope", "type": "number" }, "control_state": { "format": "default", - "description": "control_state", "type": "string" } }, diff --git a/docs/schema/Node.schema.json b/docs/schema/Node.schema.json index b7b93b2fc..dfbf5f509 100644 --- a/docs/schema/Node.schema.json +++ b/docs/schema/Node.schema.json @@ -9,12 +9,10 @@ }, "fid": { "format": "default", - "description": "fid", "type": "integer" }, "type": { "format": "default", - "description": "type", "type": "string" } }, diff --git a/docs/schema/OutletStatic.schema.json b/docs/schema/OutletStatic.schema.json index 049c5400b..107e469d1 100644 --- a/docs/schema/OutletStatic.schema.json +++ b/docs/schema/OutletStatic.schema.json @@ -3,7 +3,6 @@ "properties": { "max_flow_rate": { "format": "default", - "description": "max_flow_rate", "type": "number" }, "remarks": { @@ -14,27 +13,22 @@ }, "active": { "format": "default", - "description": "active", "type": "boolean" }, "flow_rate": { "format": "double", - "description": "flow_rate", "type": "number" }, "node_id": { "format": "default", - "description": "node_id", "type": "integer" }, "control_state": { "format": "default", - "description": "control_state", "type": "string" }, "min_flow_rate": { "format": "default", - "description": "min_flow_rate", "type": "number" } }, diff --git a/docs/schema/Output.schema.json b/docs/schema/Output.schema.json index e942591c5..0a4d1de2f 100644 --- a/docs/schema/Output.schema.json +++ b/docs/schema/Output.schema.json @@ -4,41 +4,41 @@ "compression": { "format": "default", "default": "zstd", - "description": "compression", "type": "string" }, "basin": { "format": "default", "default": "output/basin.arrow", - "description": "basin", "type": "string" }, "flow": { "format": "default", "default": "output/flow.arrow", - "description": "flow", "type": "string" }, "control": { "format": "default", "default": "output/control.arrow", - "description": "control", "type": "string" }, "outstate": { "format": "default", "default": null, - "description": "outstate", "type": "string" }, "compression_level": { "format": "default", "default": 6, - "description": "compression_level", "type": "integer" } }, "required": [ + "basin", + "flow", + "control", + "outstate", + "compression", + "compression_level" ], "$id": "https://deltares.github.io/Ribasim/schema/Output.schema.json", "title": "Output", diff --git a/docs/schema/PIDControlStatic.schema.json b/docs/schema/PIDControlStatic.schema.json index e935257bc..1f44a8c03 100644 --- a/docs/schema/PIDControlStatic.schema.json +++ b/docs/schema/PIDControlStatic.schema.json @@ -3,7 +3,6 @@ "properties": { "integral": { "format": "double", - "description": "integral", "type": "number" }, "remarks": { @@ -14,37 +13,30 @@ }, "listen_node_id": { "format": "default", - "description": "listen_node_id", "type": "integer" }, "active": { "format": "default", - "description": "active", "type": "boolean" }, "proportional": { "format": "double", - "description": "proportional", "type": "number" }, "node_id": { "format": "default", - "description": "node_id", "type": "integer" }, "target": { "format": "double", - "description": "target", "type": "number" }, "derivative": { "format": "double", - "description": "derivative", "type": "number" }, "control_state": { "format": "default", - "description": "control_state", "type": "string" } }, diff --git a/docs/schema/PidControlTime.schema.json b/docs/schema/PidControlTime.schema.json index 354ef9cc6..ac03d9546 100644 --- a/docs/schema/PidControlTime.schema.json +++ b/docs/schema/PidControlTime.schema.json @@ -3,7 +3,6 @@ "properties": { "integral": { "format": "double", - "description": "integral", "type": "number" }, "remarks": { @@ -14,37 +13,30 @@ }, "listen_node_id": { "format": "default", - "description": "listen_node_id", "type": "integer" }, "time": { "format": "date-time", - "description": "time", "type": "string" }, "proportional": { "format": "double", - "description": "proportional", "type": "number" }, "node_id": { "format": "default", - "description": "node_id", "type": "integer" }, "target": { "format": "double", - "description": "target", "type": "number" }, "derivative": { "format": "double", - "description": "derivative", "type": "number" }, "control_state": { "format": "default", - "description": "control_state", "type": "string" } }, diff --git a/docs/schema/PumpStatic.schema.json b/docs/schema/PumpStatic.schema.json index 818ac33ac..baa7c0b49 100644 --- a/docs/schema/PumpStatic.schema.json +++ b/docs/schema/PumpStatic.schema.json @@ -3,7 +3,6 @@ "properties": { "max_flow_rate": { "format": "default", - "description": "max_flow_rate", "type": "number" }, "remarks": { @@ -14,27 +13,22 @@ }, "active": { "format": "default", - "description": "active", "type": "boolean" }, "flow_rate": { "format": "double", - "description": "flow_rate", "type": "number" }, "node_id": { "format": "default", - "description": "node_id", "type": "integer" }, "control_state": { "format": "default", - "description": "control_state", "type": "string" }, "min_flow_rate": { "format": "default", - "description": "min_flow_rate", "type": "number" } }, diff --git a/docs/schema/Solver.schema.json b/docs/schema/Solver.schema.json index d083edcdf..ca6c57bd9 100644 --- a/docs/schema/Solver.schema.json +++ b/docs/schema/Solver.schema.json @@ -4,7 +4,6 @@ "reltol": { "format": "double", "default": 0.001, - "description": "reltol", "type": "number" }, "saveat": { @@ -21,53 +20,54 @@ } ], "default": [ - ], - "description": "saveat" + ] }, "maxiters": { "format": "default", "default": 1000000000, - "description": "maxiters", "type": "integer" }, "autodiff": { "format": "default", "default": true, - "description": "autodiff", "type": "boolean" }, "adaptive": { "format": "default", "default": true, - "description": "adaptive", "type": "boolean" }, "algorithm": { "format": "default", "default": "QNDF", - "description": "algorithm", "type": "string" }, "abstol": { "format": "double", "default": 1.0e-6, - "description": "abstol", "type": "number" }, "dt": { "format": "double", "default": 0, - "description": "dt", "type": "number" }, "sparse": { "format": "default", "default": true, - "description": "sparse", "type": "boolean" } }, "required": [ + "algorithm", + "saveat", + "adaptive", + "dt", + "abstol", + "reltol", + "maxiters", + "sparse", + "autodiff" ], "$id": "https://deltares.github.io/Ribasim/schema/Solver.schema.json", "title": "Solver", diff --git a/docs/schema/TabulatedRatingCurveStatic.schema.json b/docs/schema/TabulatedRatingCurveStatic.schema.json index f6d61f0ac..b833be821 100644 --- a/docs/schema/TabulatedRatingCurveStatic.schema.json +++ b/docs/schema/TabulatedRatingCurveStatic.schema.json @@ -9,27 +9,22 @@ }, "active": { "format": "default", - "description": "active", "type": "boolean" }, "node_id": { "format": "default", - "description": "node_id", "type": "integer" }, "discharge": { "format": "double", - "description": "discharge", "type": "number" }, "level": { "format": "double", - "description": "level", "type": "number" }, "control_state": { "format": "default", - "description": "control_state", "type": "string" } }, diff --git a/docs/schema/TabulatedRatingCurveTime.schema.json b/docs/schema/TabulatedRatingCurveTime.schema.json index f65ae219c..47c2ddeb7 100644 --- a/docs/schema/TabulatedRatingCurveTime.schema.json +++ b/docs/schema/TabulatedRatingCurveTime.schema.json @@ -9,22 +9,18 @@ }, "time": { "format": "date-time", - "description": "time", "type": "string" }, "node_id": { "format": "default", - "description": "node_id", "type": "integer" }, "discharge": { "format": "double", - "description": "discharge", "type": "number" }, "level": { "format": "double", - "description": "level", "type": "number" } }, diff --git a/docs/schema/TerminalStatic.schema.json b/docs/schema/TerminalStatic.schema.json index 4d4091a68..257433ed7 100644 --- a/docs/schema/TerminalStatic.schema.json +++ b/docs/schema/TerminalStatic.schema.json @@ -9,7 +9,6 @@ }, "node_id": { "format": "default", - "description": "node_id", "type": "integer" } }, diff --git a/docs/schema/basin.schema.json b/docs/schema/basin.schema.json index 31596eec5..1d8f83eb9 100644 --- a/docs/schema/basin.schema.json +++ b/docs/schema/basin.schema.json @@ -4,29 +4,29 @@ "profile": { "format": "default", "default": null, - "description": "profile", "type": "string" }, "static": { "format": "default", "default": null, - "description": "static", "type": "string" }, "forcing": { "format": "default", "default": null, - "description": "forcing", "type": "string" }, "state": { "format": "default", "default": null, - "description": "state", "type": "string" } }, "required": [ + "forcing", + "profile", + "state", + "static" ], "$id": "https://deltares.github.io/Ribasim/schema/basin.schema.json", "title": "basin", diff --git a/docs/schema/discrete_control.schema.json b/docs/schema/discrete_control.schema.json index 7a7eb9e73..cec7a26b5 100644 --- a/docs/schema/discrete_control.schema.json +++ b/docs/schema/discrete_control.schema.json @@ -4,17 +4,17 @@ "logic": { "format": "default", "default": null, - "description": "logic", "type": "string" }, "condition": { "format": "default", "default": null, - "description": "condition", "type": "string" } }, "required": [ + "condition", + "logic" ], "$id": "https://deltares.github.io/Ribasim/schema/discrete_control.schema.json", "title": "discrete_control", diff --git a/docs/schema/flow_boundary.schema.json b/docs/schema/flow_boundary.schema.json index cf4155546..65e556c1d 100644 --- a/docs/schema/flow_boundary.schema.json +++ b/docs/schema/flow_boundary.schema.json @@ -4,17 +4,17 @@ "time": { "format": "default", "default": null, - "description": "time", "type": "string" }, "static": { "format": "default", "default": null, - "description": "static", "type": "string" } }, "required": [ + "static", + "time" ], "$id": "https://deltares.github.io/Ribasim/schema/flow_boundary.schema.json", "title": "flow_boundary", diff --git a/docs/schema/fractional_flow.schema.json b/docs/schema/fractional_flow.schema.json index c999b37af..834b8b9f5 100644 --- a/docs/schema/fractional_flow.schema.json +++ b/docs/schema/fractional_flow.schema.json @@ -4,11 +4,11 @@ "static": { "format": "default", "default": null, - "description": "static", "type": "string" } }, "required": [ + "static" ], "$id": "https://deltares.github.io/Ribasim/schema/fractional_flow.schema.json", "title": "fractional_flow", diff --git a/docs/schema/level_boundary.schema.json b/docs/schema/level_boundary.schema.json index ddc9e0d2c..cf50dc8b3 100644 --- a/docs/schema/level_boundary.schema.json +++ b/docs/schema/level_boundary.schema.json @@ -4,17 +4,17 @@ "time": { "format": "default", "default": null, - "description": "time", "type": "string" }, "static": { "format": "default", "default": null, - "description": "static", "type": "string" } }, "required": [ + "static", + "time" ], "$id": "https://deltares.github.io/Ribasim/schema/level_boundary.schema.json", "title": "level_boundary", diff --git a/docs/schema/linear_resistance.schema.json b/docs/schema/linear_resistance.schema.json index cba039b7a..edf9c1c5f 100644 --- a/docs/schema/linear_resistance.schema.json +++ b/docs/schema/linear_resistance.schema.json @@ -4,11 +4,11 @@ "static": { "format": "default", "default": null, - "description": "static", "type": "string" } }, "required": [ + "static" ], "$id": "https://deltares.github.io/Ribasim/schema/linear_resistance.schema.json", "title": "linear_resistance", diff --git a/docs/schema/manning_resistance.schema.json b/docs/schema/manning_resistance.schema.json index fb81bfd74..6ac1ea948 100644 --- a/docs/schema/manning_resistance.schema.json +++ b/docs/schema/manning_resistance.schema.json @@ -4,11 +4,11 @@ "static": { "format": "default", "default": null, - "description": "static", "type": "string" } }, "required": [ + "static" ], "$id": "https://deltares.github.io/Ribasim/schema/manning_resistance.schema.json", "title": "manning_resistance", diff --git a/docs/schema/outlet.schema.json b/docs/schema/outlet.schema.json index 1e6f6246d..1b9f11bc9 100644 --- a/docs/schema/outlet.schema.json +++ b/docs/schema/outlet.schema.json @@ -4,11 +4,11 @@ "static": { "format": "default", "default": null, - "description": "static", "type": "string" } }, "required": [ + "static" ], "$id": "https://deltares.github.io/Ribasim/schema/outlet.schema.json", "title": "outlet", diff --git a/docs/schema/pid_control.schema.json b/docs/schema/pid_control.schema.json index fabb00b4d..72bb1a341 100644 --- a/docs/schema/pid_control.schema.json +++ b/docs/schema/pid_control.schema.json @@ -4,17 +4,17 @@ "time": { "format": "default", "default": null, - "description": "time", "type": "string" }, "static": { "format": "default", "default": null, - "description": "static", "type": "string" } }, "required": [ + "static", + "time" ], "$id": "https://deltares.github.io/Ribasim/schema/pid_control.schema.json", "title": "pid_control", diff --git a/docs/schema/pump.schema.json b/docs/schema/pump.schema.json index 0ca5dcdf3..8f4783030 100644 --- a/docs/schema/pump.schema.json +++ b/docs/schema/pump.schema.json @@ -4,11 +4,11 @@ "static": { "format": "default", "default": null, - "description": "static", "type": "string" } }, "required": [ + "static" ], "$id": "https://deltares.github.io/Ribasim/schema/pump.schema.json", "title": "pump", diff --git a/docs/schema/tabulated_rating_curve.schema.json b/docs/schema/tabulated_rating_curve.schema.json index 02108a5a1..36fd1a328 100644 --- a/docs/schema/tabulated_rating_curve.schema.json +++ b/docs/schema/tabulated_rating_curve.schema.json @@ -4,17 +4,17 @@ "time": { "format": "default", "default": null, - "description": "time", "type": "string" }, "static": { "format": "default", "default": null, - "description": "static", "type": "string" } }, "required": [ + "static", + "time" ], "$id": "https://deltares.github.io/Ribasim/schema/tabulated_rating_curve.schema.json", "title": "tabulated_rating_curve", diff --git a/docs/schema/terminal.schema.json b/docs/schema/terminal.schema.json index abfc35e09..4d7809f54 100644 --- a/docs/schema/terminal.schema.json +++ b/docs/schema/terminal.schema.json @@ -4,11 +4,11 @@ "static": { "format": "default", "default": null, - "description": "static", "type": "string" } }, "required": [ + "static" ], "$id": "https://deltares.github.io/Ribasim/schema/terminal.schema.json", "title": "terminal", diff --git a/python/ribasim/ribasim/config.py b/python/ribasim/ribasim/config.py index db6ec3577..ad0774ce5 100644 --- a/python/ribasim/ribasim/config.py +++ b/python/ribasim/ribasim/config.py @@ -1,6 +1,5 @@ # generated by datamodel-codegen: # filename: Config.schema.json -# timestamp: 2023-09-12T09:11:45+00:00 from __future__ import annotations @@ -11,107 +10,166 @@ class Output(BaseModel): - compression: Optional[str] = Field("zstd", description="compression") - basin: Optional[str] = Field("output/basin.arrow", description="basin") - flow: Optional[str] = Field("output/flow.arrow", description="flow") - control: Optional[str] = Field("output/control.arrow", description="control") - outstate: Optional[str] = Field(None, description="outstate") - compression_level: Optional[int] = Field(6, description="compression_level") + compression: Optional[str] = "zstd" + basin: Optional[str] = "output/basin.arrow" + flow: Optional[str] = "output/flow.arrow" + control: Optional[str] = "output/control.arrow" + outstate: Optional[str] = None + compression_level: Optional[int] = 6 class LevelBoundary(BaseModel): - time: Optional[str] = Field(None, description="time") - static: Optional[str] = Field(None, description="static") + time: Optional[str] = None + static: Optional[str] = None class Pump(BaseModel): - static: Optional[str] = Field(None, description="static") + static: Optional[str] = None class DiscreteControl(BaseModel): - logic: Optional[str] = Field(None, description="logic") - condition: Optional[str] = Field(None, description="condition") + logic: Optional[str] = None + condition: Optional[str] = None class Solver(BaseModel): - reltol: Optional[float] = Field(0.001, description="reltol") - saveat: Optional[Union[List[float], float]] = Field([], description="saveat") - maxiters: Optional[int] = Field(1000000000, description="maxiters") - autodiff: Optional[bool] = Field(True, description="autodiff") - adaptive: Optional[bool] = Field(True, description="adaptive") - algorithm: Optional[str] = Field("QNDF", description="algorithm") - abstol: Optional[float] = Field(1e-06, description="abstol") - dt: Optional[float] = Field(0, description="dt") - sparse: Optional[bool] = Field(True, description="sparse") + reltol: Optional[float] = 0.001 + saveat: Optional[Union[List[float], float]] = [] + maxiters: Optional[int] = 1000000000 + autodiff: Optional[bool] = True + adaptive: Optional[bool] = True + algorithm: Optional[str] = "QNDF" + abstol: Optional[float] = 1e-06 + dt: Optional[float] = 0 + sparse: Optional[bool] = True class FlowBoundary(BaseModel): - time: Optional[str] = Field(None, description="time") - static: Optional[str] = Field(None, description="static") + time: Optional[str] = None + static: Optional[str] = None class PidControl(BaseModel): - time: Optional[str] = Field(None, description="time") - static: Optional[str] = Field(None, description="static") + time: Optional[str] = None + static: Optional[str] = None class FractionalFlow(BaseModel): - static: Optional[str] = Field(None, description="static") + static: Optional[str] = None class ManningResistance(BaseModel): - static: Optional[str] = Field(None, description="static") + static: Optional[str] = None class TabulatedRatingCurve(BaseModel): - time: Optional[str] = Field(None, description="time") - static: Optional[str] = Field(None, description="static") + time: Optional[str] = None + static: Optional[str] = None class Logging(BaseModel): - timing: Optional[bool] = Field(False, description="timing") - verbosity: Optional[str] = Field("info", description="verbosity") + timing: Optional[bool] = False + verbosity: Optional[str] = "info" class Outlet(BaseModel): - static: Optional[str] = Field(None, description="static") + static: Optional[str] = None class Terminal(BaseModel): - static: Optional[str] = Field(None, description="static") + static: Optional[str] = None class Basin(BaseModel): - profile: Optional[str] = Field(None, description="profile") - static: Optional[str] = Field(None, description="static") - forcing: Optional[str] = Field(None, description="forcing") - state: Optional[str] = Field(None, description="state") + profile: Optional[str] = None + static: Optional[str] = None + forcing: Optional[str] = None + state: Optional[str] = None class LinearResistance(BaseModel): - static: Optional[str] = Field(None, description="static") + static: Optional[str] = None class Config(BaseModel): - output: Optional[Output] = None - starttime: datetime = Field(..., description="starttime") - update_timestep: Optional[float] = Field(86400, description="update_timestep") - input_dir: Optional[str] = Field(".", description="input_dir") - output_dir: Optional[str] = Field(".", description="output_dir") - level_boundary: Optional[LevelBoundary] = None - pump: Optional[Pump] = None - discrete_control: Optional[DiscreteControl] = None - solver: Optional[Solver] = None - flow_boundary: Optional[FlowBoundary] = None - pid_control: Optional[PidControl] = None - fractional_flow: Optional[FractionalFlow] = None - relative_dir: Optional[str] = Field(".", description="relative_dir") - endtime: datetime = Field(..., description="endtime") - manning_resistance: Optional[ManningResistance] = None - tabulated_rating_curve: Optional[TabulatedRatingCurve] = None - logging: Optional[Logging] = None - outlet: Optional[Outlet] = None - geopackage: str = Field(..., description="geopackage") - terminal: Optional[Terminal] = None - basin: Optional[Basin] = None - linear_resistance: Optional[LinearResistance] = None + output: Optional[Output] = Field( + default_factory=lambda: Output.parse_obj( + { + "basin": "output/basin.arrow", + "flow": "output/flow.arrow", + "control": "output/control.arrow", + "outstate": None, + "compression": "zstd", + "compression_level": 6, + } + ) + ) + starttime: datetime + update_timestep: Optional[float] = 86400 + input_dir: Optional[str] = "." + output_dir: Optional[str] = "." + level_boundary: Optional[LevelBoundary] = Field( + default_factory=lambda: LevelBoundary.parse_obj({"static": None, "time": None}) + ) + pump: Optional[Pump] = Field( + default_factory=lambda: Pump.parse_obj({"static": None}) + ) + discrete_control: Optional[DiscreteControl] = Field( + default_factory=lambda: DiscreteControl.parse_obj( + {"condition": None, "logic": None} + ) + ) + solver: Optional[Solver] = Field( + default_factory=lambda: Solver.parse_obj( + { + "algorithm": "QNDF", + "saveat": [], + "adaptive": True, + "dt": 0, + "abstol": 1e-06, + "reltol": 0.001, + "maxiters": 1000000000, + "sparse": True, + "autodiff": True, + } + ) + ) + flow_boundary: Optional[FlowBoundary] = Field( + default_factory=lambda: FlowBoundary.parse_obj({"static": None, "time": None}) + ) + pid_control: Optional[PidControl] = Field( + default_factory=lambda: PidControl.parse_obj({"static": None, "time": None}) + ) + fractional_flow: Optional[FractionalFlow] = Field( + default_factory=lambda: FractionalFlow.parse_obj({"static": None}) + ) + relative_dir: Optional[str] = "." + endtime: datetime + manning_resistance: Optional[ManningResistance] = Field( + default_factory=lambda: ManningResistance.parse_obj({"static": None}) + ) + tabulated_rating_curve: Optional[TabulatedRatingCurve] = Field( + default_factory=lambda: TabulatedRatingCurve.parse_obj( + {"static": None, "time": None} + ) + ) + logging: Optional[Logging] = Field( + default_factory=lambda: Logging.parse_obj( + {"verbosity": {"level": 0}, "timing": False} + ) + ) + outlet: Optional[Outlet] = Field( + default_factory=lambda: Outlet.parse_obj({"static": None}) + ) + geopackage: str + terminal: Optional[Terminal] = Field( + default_factory=lambda: Terminal.parse_obj({"static": None}) + ) + basin: Optional[Basin] = Field( + default_factory=lambda: Basin.parse_obj( + {"forcing": None, "profile": None, "state": None, "static": None} + ) + ) + linear_resistance: Optional[LinearResistance] = Field( + default_factory=lambda: LinearResistance.parse_obj({"static": None}) + ) From 07ae26e47d6db3a5dfaa367d4ee634c2ca9e8629 Mon Sep 17 00:00:00 2001 From: Maarten Pronk Date: Thu, 14 Sep 2023 13:53:59 +0200 Subject: [PATCH 08/13] Fix required. --- docs/gen_schema.jl | 2 +- docs/schema/Config.schema.json | 17 +---------------- 2 files changed, 2 insertions(+), 17 deletions(-) diff --git a/docs/gen_schema.jl b/docs/gen_schema.jl index aa69b284b..e96f81933 100644 --- a/docs/gen_schema.jl +++ b/docs/gen_schema.jl @@ -120,7 +120,7 @@ function gen_schema(T::DataType, prefix = prefix; pandera = true) end end end - if !((fieldtype isa Union) && (fieldtype.a === Missing)) + if !((fieldtype isa Union) && (fieldtype.a === Missing)) && required push!(schema["required"], fieldname) end end diff --git a/docs/schema/Config.schema.json b/docs/schema/Config.schema.json index f324fc81b..1fec4307e 100644 --- a/docs/schema/Config.schema.json +++ b/docs/schema/Config.schema.json @@ -156,22 +156,7 @@ "relative_dir", "input_dir", "output_dir", - "geopackage", - "output", - "solver", - "logging", - "terminal", - "pid_control", - "level_boundary", - "pump", - "tabulated_rating_curve", - "flow_boundary", - "basin", - "manning_resistance", - "discrete_control", - "outlet", - "linear_resistance", - "fractional_flow" + "geopackage" ], "$id": "https://deltares.github.io/Ribasim/schema/Config.schema.json", "title": "Config", From eb1609f3a0089dc1a10d675ce433c57f81b16653 Mon Sep 17 00:00:00 2001 From: Maarten Pronk Date: Thu, 14 Sep 2023 14:57:16 +0200 Subject: [PATCH 09/13] Use strict nullable. --- .github/pull_request_template.md | 2 +- docs/contribute/addnode.qmd | 4 +- docs/gen_schema.jl | 7 +- docs/schema/Config.schema.json | 17 +- docs/schema/Output.schema.json | 1 - docs/schema/basin.schema.json | 4 - docs/schema/discrete_control.schema.json | 2 - docs/schema/flow_boundary.schema.json | 2 - docs/schema/fractional_flow.schema.json | 1 - docs/schema/level_boundary.schema.json | 2 - docs/schema/linear_resistance.schema.json | 1 - docs/schema/manning_resistance.schema.json | 1 - docs/schema/outlet.schema.json | 1 - docs/schema/pid_control.schema.json | 2 - docs/schema/pump.schema.json | 1 - .../schema/tabulated_rating_curve.schema.json | 2 - docs/schema/terminal.schema.json | 1 - python/ribasim/ribasim/config.py | 118 ++++---- python/ribasim/ribasim/models.py | 263 +++++++++--------- 19 files changed, 211 insertions(+), 221 deletions(-) diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index c960ba153..0faf24dc9 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -29,7 +29,7 @@ Updating other julia files may be required. - update `docs/core/equations.qmd` - update `docs/core/usage.qmd` - update `docs/python/examples.ipynb` # or start a new example model -- update `docs/schema*.json` by running `julia --project=docs docs/gen_schema.jl` and `datamodel-codegen --use-title-as-name --use-double-quotes --disable-timestamp --use-default --input docs/schema/root.schema.json --output python/ribasim/ribasim/models.py` and `datamodel-codegen --use-title-as-name --use-double-quotes --disable-timestamp --use-default --input docs/schema/Config.schema.json --output python/ribasim/ribasim/config.py` +- update `docs/schema*.json` by running `julia --project=docs docs/gen_schema.jl` and `datamodel-codegen --use-title-as-name --use-double-quotes --disable-timestamp --use-default --strict-nullable --input docs/schema/root.schema.json --output python/ribasim/ribasim/models.py` and `datamodel-codegen --use-title-as-name --use-double-quotes --disable-timestamp --use-default --strict-nullable --input docs/schema/Config.schema.json --output python/ribasim/ribasim/config.py` - update the instructions in `docs/contribute/*.qmd` if something changes there, e.g. something changes in how a new node type must be defined. ## QGIS diff --git a/docs/contribute/addnode.qmd b/docs/contribute/addnode.qmd index c14feb82c..7aebeffb8 100644 --- a/docs/contribute/addnode.qmd +++ b/docs/contribute/addnode.qmd @@ -250,8 +250,8 @@ Run `julia --project=docs`, followed by running `instantiate` in the Pkg mode (p To generate the Python module `models.py` and `config.py` from the JSON Schemas, run: ``` -datamodel-codegen --use-title-as-name --use-double-quotes --disable-timestamp --use-default --input docs/schema/root.schema.json --output python/ribasim/ribasim/models.py -datamodel-codegen --use-title-as-name --use-double-quotes --disable-timestamp --use-default --input docs/schema/Config.schema.json --output python/ribasim/ribasim/config.py +datamodel-codegen --use-title-as-name --use-double-quotes --disable-timestamp --use-default --strict-nullable --input docs/schema/root.schema.json --output python/ribasim/ribasim/models.py +datamodel-codegen --use-title-as-name --use-double-quotes --disable-timestamp --use-default --strict-nullable --input docs/schema/Config.schema.json --output python/ribasim/ribasim/config.py ``` Run [Black](python.qmd#sec-black) to format the generated code. diff --git a/docs/gen_schema.jl b/docs/gen_schema.jl index e96f81933..d9915928b 100644 --- a/docs/gen_schema.jl +++ b/docs/gen_schema.jl @@ -94,14 +94,12 @@ function gen_schema(T::DataType, prefix = prefix; pandera = true) for (fieldnames, fieldtype) in zip(fieldnames(T), fieldtypes(T)) fieldname = string(fieldnames) ref = false - required = true if fieldtype <: Ribasim.config.TableOption schema["properties"][fieldname] = Dict( "\$ref" => "$(prefix)$(strip_prefix(fieldtype)).schema.json", "default" => fieldtype(), ) ref = true - required = false else type = jsontype(fieldtype) schema["properties"][fieldname] = @@ -120,7 +118,10 @@ function gen_schema(T::DataType, prefix = prefix; pandera = true) end end end - if !((fieldtype isa Union) && (fieldtype.a === Missing)) && required + if !( + (fieldtype isa Union) && + ((fieldtype.a === Missing) || (fieldtype.a === Nothing)) + ) push!(schema["required"], fieldname) end end diff --git a/docs/schema/Config.schema.json b/docs/schema/Config.schema.json index 1fec4307e..f324fc81b 100644 --- a/docs/schema/Config.schema.json +++ b/docs/schema/Config.schema.json @@ -156,7 +156,22 @@ "relative_dir", "input_dir", "output_dir", - "geopackage" + "geopackage", + "output", + "solver", + "logging", + "terminal", + "pid_control", + "level_boundary", + "pump", + "tabulated_rating_curve", + "flow_boundary", + "basin", + "manning_resistance", + "discrete_control", + "outlet", + "linear_resistance", + "fractional_flow" ], "$id": "https://deltares.github.io/Ribasim/schema/Config.schema.json", "title": "Config", diff --git a/docs/schema/Output.schema.json b/docs/schema/Output.schema.json index 0a4d1de2f..e35fb100c 100644 --- a/docs/schema/Output.schema.json +++ b/docs/schema/Output.schema.json @@ -36,7 +36,6 @@ "basin", "flow", "control", - "outstate", "compression", "compression_level" ], diff --git a/docs/schema/basin.schema.json b/docs/schema/basin.schema.json index 1d8f83eb9..2ddaeb7e3 100644 --- a/docs/schema/basin.schema.json +++ b/docs/schema/basin.schema.json @@ -23,10 +23,6 @@ } }, "required": [ - "forcing", - "profile", - "state", - "static" ], "$id": "https://deltares.github.io/Ribasim/schema/basin.schema.json", "title": "basin", diff --git a/docs/schema/discrete_control.schema.json b/docs/schema/discrete_control.schema.json index cec7a26b5..bd7a06ab1 100644 --- a/docs/schema/discrete_control.schema.json +++ b/docs/schema/discrete_control.schema.json @@ -13,8 +13,6 @@ } }, "required": [ - "condition", - "logic" ], "$id": "https://deltares.github.io/Ribasim/schema/discrete_control.schema.json", "title": "discrete_control", diff --git a/docs/schema/flow_boundary.schema.json b/docs/schema/flow_boundary.schema.json index 65e556c1d..5018b506c 100644 --- a/docs/schema/flow_boundary.schema.json +++ b/docs/schema/flow_boundary.schema.json @@ -13,8 +13,6 @@ } }, "required": [ - "static", - "time" ], "$id": "https://deltares.github.io/Ribasim/schema/flow_boundary.schema.json", "title": "flow_boundary", diff --git a/docs/schema/fractional_flow.schema.json b/docs/schema/fractional_flow.schema.json index 834b8b9f5..f836d7a98 100644 --- a/docs/schema/fractional_flow.schema.json +++ b/docs/schema/fractional_flow.schema.json @@ -8,7 +8,6 @@ } }, "required": [ - "static" ], "$id": "https://deltares.github.io/Ribasim/schema/fractional_flow.schema.json", "title": "fractional_flow", diff --git a/docs/schema/level_boundary.schema.json b/docs/schema/level_boundary.schema.json index cf50dc8b3..678a51752 100644 --- a/docs/schema/level_boundary.schema.json +++ b/docs/schema/level_boundary.schema.json @@ -13,8 +13,6 @@ } }, "required": [ - "static", - "time" ], "$id": "https://deltares.github.io/Ribasim/schema/level_boundary.schema.json", "title": "level_boundary", diff --git a/docs/schema/linear_resistance.schema.json b/docs/schema/linear_resistance.schema.json index edf9c1c5f..ef53a67e6 100644 --- a/docs/schema/linear_resistance.schema.json +++ b/docs/schema/linear_resistance.schema.json @@ -8,7 +8,6 @@ } }, "required": [ - "static" ], "$id": "https://deltares.github.io/Ribasim/schema/linear_resistance.schema.json", "title": "linear_resistance", diff --git a/docs/schema/manning_resistance.schema.json b/docs/schema/manning_resistance.schema.json index 6ac1ea948..b4e39594b 100644 --- a/docs/schema/manning_resistance.schema.json +++ b/docs/schema/manning_resistance.schema.json @@ -8,7 +8,6 @@ } }, "required": [ - "static" ], "$id": "https://deltares.github.io/Ribasim/schema/manning_resistance.schema.json", "title": "manning_resistance", diff --git a/docs/schema/outlet.schema.json b/docs/schema/outlet.schema.json index 1b9f11bc9..18f8b4d85 100644 --- a/docs/schema/outlet.schema.json +++ b/docs/schema/outlet.schema.json @@ -8,7 +8,6 @@ } }, "required": [ - "static" ], "$id": "https://deltares.github.io/Ribasim/schema/outlet.schema.json", "title": "outlet", diff --git a/docs/schema/pid_control.schema.json b/docs/schema/pid_control.schema.json index 72bb1a341..cf4ad8dac 100644 --- a/docs/schema/pid_control.schema.json +++ b/docs/schema/pid_control.schema.json @@ -13,8 +13,6 @@ } }, "required": [ - "static", - "time" ], "$id": "https://deltares.github.io/Ribasim/schema/pid_control.schema.json", "title": "pid_control", diff --git a/docs/schema/pump.schema.json b/docs/schema/pump.schema.json index 8f4783030..882d65ae6 100644 --- a/docs/schema/pump.schema.json +++ b/docs/schema/pump.schema.json @@ -8,7 +8,6 @@ } }, "required": [ - "static" ], "$id": "https://deltares.github.io/Ribasim/schema/pump.schema.json", "title": "pump", diff --git a/docs/schema/tabulated_rating_curve.schema.json b/docs/schema/tabulated_rating_curve.schema.json index 36fd1a328..c25c77e86 100644 --- a/docs/schema/tabulated_rating_curve.schema.json +++ b/docs/schema/tabulated_rating_curve.schema.json @@ -13,8 +13,6 @@ } }, "required": [ - "static", - "time" ], "$id": "https://deltares.github.io/Ribasim/schema/tabulated_rating_curve.schema.json", "title": "tabulated_rating_curve", diff --git a/docs/schema/terminal.schema.json b/docs/schema/terminal.schema.json index 4d7809f54..10bc38cfd 100644 --- a/docs/schema/terminal.schema.json +++ b/docs/schema/terminal.schema.json @@ -8,7 +8,6 @@ } }, "required": [ - "static" ], "$id": "https://deltares.github.io/Ribasim/schema/terminal.schema.json", "title": "terminal", diff --git a/python/ribasim/ribasim/config.py b/python/ribasim/ribasim/config.py index ad0774ce5..7df49a03e 100644 --- a/python/ribasim/ribasim/config.py +++ b/python/ribasim/ribasim/config.py @@ -4,95 +4,95 @@ from __future__ import annotations from datetime import datetime -from typing import List, Optional, Union +from typing import List, Union from pydantic import BaseModel, Field class Output(BaseModel): - compression: Optional[str] = "zstd" - basin: Optional[str] = "output/basin.arrow" - flow: Optional[str] = "output/flow.arrow" - control: Optional[str] = "output/control.arrow" - outstate: Optional[str] = None - compression_level: Optional[int] = 6 + compression: str = "zstd" + basin: str = "output/basin.arrow" + flow: str = "output/flow.arrow" + control: str = "output/control.arrow" + outstate: str = None + compression_level: int = 6 class LevelBoundary(BaseModel): - time: Optional[str] = None - static: Optional[str] = None + time: str = None + static: str = None class Pump(BaseModel): - static: Optional[str] = None + static: str = None class DiscreteControl(BaseModel): - logic: Optional[str] = None - condition: Optional[str] = None + logic: str = None + condition: str = None class Solver(BaseModel): - reltol: Optional[float] = 0.001 - saveat: Optional[Union[List[float], float]] = [] - maxiters: Optional[int] = 1000000000 - autodiff: Optional[bool] = True - adaptive: Optional[bool] = True - algorithm: Optional[str] = "QNDF" - abstol: Optional[float] = 1e-06 - dt: Optional[float] = 0 - sparse: Optional[bool] = True + reltol: float = 0.001 + saveat: Union[List[float], float] = [] + maxiters: int = 1000000000 + autodiff: bool = True + adaptive: bool = True + algorithm: str = "QNDF" + abstol: float = 1e-06 + dt: float = 0 + sparse: bool = True class FlowBoundary(BaseModel): - time: Optional[str] = None - static: Optional[str] = None + time: str = None + static: str = None class PidControl(BaseModel): - time: Optional[str] = None - static: Optional[str] = None + time: str = None + static: str = None class FractionalFlow(BaseModel): - static: Optional[str] = None + static: str = None class ManningResistance(BaseModel): - static: Optional[str] = None + static: str = None class TabulatedRatingCurve(BaseModel): - time: Optional[str] = None - static: Optional[str] = None + time: str = None + static: str = None class Logging(BaseModel): - timing: Optional[bool] = False - verbosity: Optional[str] = "info" + timing: bool = False + verbosity: str = "info" class Outlet(BaseModel): - static: Optional[str] = None + static: str = None class Terminal(BaseModel): - static: Optional[str] = None + static: str = None class Basin(BaseModel): - profile: Optional[str] = None - static: Optional[str] = None - forcing: Optional[str] = None - state: Optional[str] = None + profile: str = None + static: str = None + forcing: str = None + state: str = None class LinearResistance(BaseModel): - static: Optional[str] = None + static: str = None class Config(BaseModel): - output: Optional[Output] = Field( + output: Output = Field( default_factory=lambda: Output.parse_obj( { "basin": "output/basin.arrow", @@ -105,21 +105,19 @@ class Config(BaseModel): ) ) starttime: datetime - update_timestep: Optional[float] = 86400 - input_dir: Optional[str] = "." - output_dir: Optional[str] = "." - level_boundary: Optional[LevelBoundary] = Field( + update_timestep: float = 86400 + input_dir: str = "." + output_dir: str = "." + level_boundary: LevelBoundary = Field( default_factory=lambda: LevelBoundary.parse_obj({"static": None, "time": None}) ) - pump: Optional[Pump] = Field( - default_factory=lambda: Pump.parse_obj({"static": None}) - ) - discrete_control: Optional[DiscreteControl] = Field( + pump: Pump = Field(default_factory=lambda: Pump.parse_obj({"static": None})) + discrete_control: DiscreteControl = Field( default_factory=lambda: DiscreteControl.parse_obj( {"condition": None, "logic": None} ) ) - solver: Optional[Solver] = Field( + solver: Solver = Field( default_factory=lambda: Solver.parse_obj( { "algorithm": "QNDF", @@ -134,42 +132,40 @@ class Config(BaseModel): } ) ) - flow_boundary: Optional[FlowBoundary] = Field( + flow_boundary: FlowBoundary = Field( default_factory=lambda: FlowBoundary.parse_obj({"static": None, "time": None}) ) - pid_control: Optional[PidControl] = Field( + pid_control: PidControl = Field( default_factory=lambda: PidControl.parse_obj({"static": None, "time": None}) ) - fractional_flow: Optional[FractionalFlow] = Field( + fractional_flow: FractionalFlow = Field( default_factory=lambda: FractionalFlow.parse_obj({"static": None}) ) - relative_dir: Optional[str] = "." + relative_dir: str = "." endtime: datetime - manning_resistance: Optional[ManningResistance] = Field( + manning_resistance: ManningResistance = Field( default_factory=lambda: ManningResistance.parse_obj({"static": None}) ) - tabulated_rating_curve: Optional[TabulatedRatingCurve] = Field( + tabulated_rating_curve: TabulatedRatingCurve = Field( default_factory=lambda: TabulatedRatingCurve.parse_obj( {"static": None, "time": None} ) ) - logging: Optional[Logging] = Field( + logging: Logging = Field( default_factory=lambda: Logging.parse_obj( {"verbosity": {"level": 0}, "timing": False} ) ) - outlet: Optional[Outlet] = Field( - default_factory=lambda: Outlet.parse_obj({"static": None}) - ) + outlet: Outlet = Field(default_factory=lambda: Outlet.parse_obj({"static": None})) geopackage: str - terminal: Optional[Terminal] = Field( + terminal: Terminal = Field( default_factory=lambda: Terminal.parse_obj({"static": None}) ) - basin: Optional[Basin] = Field( + basin: Basin = Field( default_factory=lambda: Basin.parse_obj( {"forcing": None, "profile": None, "state": None, "static": None} ) ) - linear_resistance: Optional[LinearResistance] = Field( + linear_resistance: LinearResistance = Field( default_factory=lambda: LinearResistance.parse_obj({"static": None}) ) diff --git a/python/ribasim/ribasim/models.py b/python/ribasim/ribasim/models.py index da6f31259..ef0e28182 100644 --- a/python/ribasim/ribasim/models.py +++ b/python/ribasim/ribasim/models.py @@ -1,6 +1,5 @@ # generated by datamodel-codegen: # filename: root.schema.json -# timestamp: 2023-08-21T14:05:19+00:00 from __future__ import annotations @@ -11,209 +10,209 @@ class DiscreteControlLogic(BaseModel): - remarks: Optional[str] = Field("", description="a hack for pandera") - truth_state: str = Field(..., description="truth_state") - node_id: int = Field(..., description="node_id") - control_state: str = Field(..., description="control_state") + remarks: str = Field("", description="a hack for pandera") + truth_state: str + node_id: int + control_state: str class Edge(BaseModel): - remarks: Optional[str] = Field("", description="a hack for pandera") - edge_type: str = Field(..., description="edge_type") - fid: int = Field(..., description="fid") - to_node_id: int = Field(..., description="to_node_id") - from_node_id: int = Field(..., description="from_node_id") + remarks: str = Field("", description="a hack for pandera") + edge_type: str + fid: int + to_node_id: int + from_node_id: int class FlowBoundaryTime(BaseModel): - remarks: Optional[str] = Field("", description="a hack for pandera") - time: datetime = Field(..., description="time") - flow_rate: float = Field(..., description="flow_rate") - node_id: int = Field(..., description="node_id") + remarks: str = Field("", description="a hack for pandera") + time: datetime + flow_rate: float + node_id: int class PumpStatic(BaseModel): - max_flow_rate: Optional[float] = Field(None, description="max_flow_rate") - remarks: Optional[str] = Field("", description="a hack for pandera") - active: Optional[bool] = Field(None, description="active") - flow_rate: float = Field(..., description="flow_rate") - node_id: int = Field(..., description="node_id") - control_state: Optional[str] = Field(None, description="control_state") - min_flow_rate: Optional[float] = Field(None, description="min_flow_rate") + max_flow_rate: Optional[float] = None + remarks: str = Field("", description="a hack for pandera") + active: Optional[bool] = None + flow_rate: float + node_id: int + control_state: Optional[str] = None + min_flow_rate: Optional[float] = None class LevelBoundaryStatic(BaseModel): - remarks: Optional[str] = Field("", description="a hack for pandera") - active: Optional[bool] = Field(None, description="active") - node_id: int = Field(..., description="node_id") - level: float = Field(..., description="level") + remarks: str = Field("", description="a hack for pandera") + active: Optional[bool] = None + node_id: int + level: float class DiscreteControlCondition(BaseModel): - remarks: Optional[str] = Field("", description="a hack for pandera") - greater_than: float = Field(..., description="greater_than") - listen_feature_id: int = Field(..., description="listen_feature_id") - node_id: int = Field(..., description="node_id") - variable: str = Field(..., description="variable") - look_ahead: Optional[float] = Field(None, description="look_ahead") + remarks: str = Field("", description="a hack for pandera") + greater_than: float + listen_feature_id: int + node_id: int + variable: str + look_ahead: Optional[float] = None class BasinForcing(BaseModel): - remarks: Optional[str] = Field("", description="a hack for pandera") - time: datetime = Field(..., description="time") - precipitation: float = Field(..., description="precipitation") - infiltration: float = Field(..., description="infiltration") - urban_runoff: float = Field(..., description="urban_runoff") - node_id: int = Field(..., description="node_id") - potential_evaporation: float = Field(..., description="potential_evaporation") - drainage: float = Field(..., description="drainage") + remarks: str = Field("", description="a hack for pandera") + time: datetime + precipitation: float + infiltration: float + urban_runoff: float + node_id: int + potential_evaporation: float + drainage: float class FractionalFlowStatic(BaseModel): - remarks: Optional[str] = Field("", description="a hack for pandera") - node_id: int = Field(..., description="node_id") - fraction: float = Field(..., description="fraction") - control_state: Optional[str] = Field(None, description="control_state") + remarks: str = Field("", description="a hack for pandera") + node_id: int + fraction: float + control_state: Optional[str] = None class LinearResistanceStatic(BaseModel): - remarks: Optional[str] = Field("", description="a hack for pandera") - active: Optional[bool] = Field(None, description="active") - node_id: int = Field(..., description="node_id") - resistance: float = Field(..., description="resistance") - control_state: Optional[str] = Field(None, description="control_state") + remarks: str = Field("", description="a hack for pandera") + active: Optional[bool] = None + node_id: int + resistance: float + control_state: Optional[str] = None class PidControlStatic(BaseModel): - integral: float = Field(..., description="integral") - remarks: Optional[str] = Field("", description="a hack for pandera") - listen_node_id: int = Field(..., description="listen_node_id") - active: Optional[bool] = Field(None, description="active") - proportional: float = Field(..., description="proportional") - node_id: int = Field(..., description="node_id") - target: float = Field(..., description="target") - derivative: float = Field(..., description="derivative") - control_state: Optional[str] = Field(None, description="control_state") + integral: float + remarks: str = Field("", description="a hack for pandera") + listen_node_id: int + active: Optional[bool] = None + proportional: float + node_id: int + target: float + derivative: float + control_state: Optional[str] = None class PidControlTime(BaseModel): - integral: float = Field(..., description="integral") - remarks: Optional[str] = Field("", description="a hack for pandera") - listen_node_id: int = Field(..., description="listen_node_id") - time: datetime = Field(..., description="time") - proportional: float = Field(..., description="proportional") - node_id: int = Field(..., description="node_id") - target: float = Field(..., description="target") - derivative: float = Field(..., description="derivative") - control_state: Optional[str] = Field(None, description="control_state") + integral: float + remarks: str = Field("", description="a hack for pandera") + listen_node_id: int + time: datetime + proportional: float + node_id: int + target: float + derivative: float + control_state: Optional[str] = None class ManningResistanceStatic(BaseModel): - length: float = Field(..., description="length") - manning_n: float = Field(..., description="manning_n") - remarks: Optional[str] = Field("", description="a hack for pandera") - active: Optional[bool] = Field(None, description="active") - profile_width: float = Field(..., description="profile_width") - node_id: int = Field(..., description="node_id") - profile_slope: float = Field(..., description="profile_slope") - control_state: Optional[str] = Field(None, description="control_state") + length: float + manning_n: float + remarks: str = Field("", description="a hack for pandera") + active: Optional[bool] = None + profile_width: float + node_id: int + profile_slope: float + control_state: Optional[str] = None class FlowBoundaryStatic(BaseModel): - remarks: Optional[str] = Field("", description="a hack for pandera") - active: Optional[bool] = Field(None, description="active") - flow_rate: float = Field(..., description="flow_rate") - node_id: int = Field(..., description="node_id") + remarks: str = Field("", description="a hack for pandera") + active: Optional[bool] = None + flow_rate: float + node_id: int class OutletStatic(BaseModel): - max_flow_rate: Optional[float] = Field(None, description="max_flow_rate") - remarks: Optional[str] = Field("", description="a hack for pandera") - active: Optional[bool] = Field(None, description="active") - flow_rate: float = Field(..., description="flow_rate") - node_id: int = Field(..., description="node_id") - control_state: Optional[str] = Field(None, description="control_state") - min_flow_rate: Optional[float] = Field(None, description="min_flow_rate") + max_flow_rate: Optional[float] = None + remarks: str = Field("", description="a hack for pandera") + active: Optional[bool] = None + flow_rate: float + node_id: int + control_state: Optional[str] = None + min_flow_rate: Optional[float] = None class Node(BaseModel): - remarks: Optional[str] = Field("", description="a hack for pandera") - fid: int = Field(..., description="fid") - type: str = Field(..., description="type") + remarks: str = Field("", description="a hack for pandera") + fid: int + type: str class TabulatedRatingCurveTime(BaseModel): - remarks: Optional[str] = Field("", description="a hack for pandera") - time: datetime = Field(..., description="time") - node_id: int = Field(..., description="node_id") - discharge: float = Field(..., description="discharge") - level: float = Field(..., description="level") + remarks: str = Field("", description="a hack for pandera") + time: datetime + node_id: int + discharge: float + level: float class TabulatedRatingCurveStatic(BaseModel): - remarks: Optional[str] = Field("", description="a hack for pandera") - active: Optional[bool] = Field(None, description="active") - node_id: int = Field(..., description="node_id") - discharge: float = Field(..., description="discharge") - level: float = Field(..., description="level") - control_state: Optional[str] = Field(None, description="control_state") + remarks: str = Field("", description="a hack for pandera") + active: Optional[bool] = None + node_id: int + discharge: float + level: float + control_state: Optional[str] = None class LevelBoundaryTime(BaseModel): - remarks: Optional[str] = Field("", description="a hack for pandera") - time: datetime = Field(..., description="time") - node_id: int = Field(..., description="node_id") - level: float = Field(..., description="level") + remarks: str = Field("", description="a hack for pandera") + time: datetime + node_id: int + level: float class BasinState(BaseModel): - remarks: Optional[str] = Field("", description="a hack for pandera") - node_id: int = Field(..., description="node_id") - level: float = Field(..., description="level") + remarks: str = Field("", description="a hack for pandera") + node_id: int + level: float class BasinProfile(BaseModel): - remarks: Optional[str] = Field("", description="a hack for pandera") - area: float = Field(..., description="area") - node_id: int = Field(..., description="node_id") - level: float = Field(..., description="level") + remarks: str = Field("", description="a hack for pandera") + area: float + node_id: int + level: float class TerminalStatic(BaseModel): - remarks: Optional[str] = Field("", description="a hack for pandera") - node_id: int = Field(..., description="node_id") + remarks: str = Field("", description="a hack for pandera") + node_id: int class BasinStatic(BaseModel): - remarks: Optional[str] = Field("", description="a hack for pandera") - precipitation: float = Field(..., description="precipitation") - infiltration: float = Field(..., description="infiltration") - urban_runoff: float = Field(..., description="urban_runoff") - node_id: int = Field(..., description="node_id") - potential_evaporation: float = Field(..., description="potential_evaporation") - drainage: float = Field(..., description="drainage") + remarks: str = Field("", description="a hack for pandera") + precipitation: float + infiltration: float + urban_runoff: float + node_id: int + potential_evaporation: float + drainage: float class Root(BaseModel): - BasinForcing: Optional[BasinForcing] = None - BasinProfile: Optional[BasinProfile] = None - BasinState: Optional[BasinState] = None - BasinStatic: Optional[BasinStatic] = None - DiscreteControlCondition: Optional[DiscreteControlCondition] = None DiscreteControlLogic: Optional[DiscreteControlLogic] = None Edge: Optional[Edge] = None - FlowBoundaryStatic: Optional[FlowBoundaryStatic] = None FlowBoundaryTime: Optional[FlowBoundaryTime] = None - FractionalFlowStatic: Optional[FractionalFlowStatic] = None + PumpStatic: Optional[PumpStatic] = None LevelBoundaryStatic: Optional[LevelBoundaryStatic] = None - LevelBoundaryTime: Optional[LevelBoundaryTime] = None + DiscreteControlCondition: Optional[DiscreteControlCondition] = None + BasinForcing: Optional[BasinForcing] = None + FractionalFlowStatic: Optional[FractionalFlowStatic] = None LinearResistanceStatic: Optional[LinearResistanceStatic] = None - ManningResistanceStatic: Optional[ManningResistanceStatic] = None - Node: Optional[Node] = None - OutletStatic: Optional[OutletStatic] = None PidControlStatic: Optional[PidControlStatic] = None PidControlTime: Optional[PidControlTime] = None - PumpStatic: Optional[PumpStatic] = None - TabulatedRatingCurveStatic: Optional[TabulatedRatingCurveStatic] = None + ManningResistanceStatic: Optional[ManningResistanceStatic] = None + FlowBoundaryStatic: Optional[FlowBoundaryStatic] = None + OutletStatic: Optional[OutletStatic] = None + Node: Optional[Node] = None TabulatedRatingCurveTime: Optional[TabulatedRatingCurveTime] = None + TabulatedRatingCurveStatic: Optional[TabulatedRatingCurveStatic] = None + LevelBoundaryTime: Optional[LevelBoundaryTime] = None + BasinState: Optional[BasinState] = None + BasinProfile: Optional[BasinProfile] = None TerminalStatic: Optional[TerminalStatic] = None + BasinStatic: Optional[BasinStatic] = None From 8b5a01a7762eeb507e4f648b6e502f3f6d3061cb Mon Sep 17 00:00:00 2001 From: Maarten Pronk Date: Thu, 14 Sep 2023 16:17:31 +0200 Subject: [PATCH 10/13] Use Configurations.from_dict. --- core/src/config.jl | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/core/src/config.jl b/core/src/config.jl index 2fdad1c32..538fd3283 100644 --- a/core/src/config.jl +++ b/core/src/config.jl @@ -148,6 +148,11 @@ function Configurations.from_dict(::Type{Logging}, ::Type{LogLevel}, level::Abst ) end +# [] in TOML is parsed as a Vector{Union{}} +function Configurations.from_dict(::Type{Solver}, t::Type, saveat::Vector{Union{}}) + return Float64[] +end + # TODO Use with proper alignment function Base.show(io::IO, c::Config) println(io, "Ribasim Config") From a0deee86097f61d91c5308d33f7103d64d1a28df Mon Sep 17 00:00:00 2001 From: Maarten Pronk Date: Thu, 14 Sep 2023 16:18:12 +0200 Subject: [PATCH 11/13] And remove Base.convert. --- core/src/config.jl | 4 ---- 1 file changed, 4 deletions(-) diff --git a/core/src/config.jl b/core/src/config.jl index 538fd3283..608fc3546 100644 --- a/core/src/config.jl +++ b/core/src/config.jl @@ -65,10 +65,6 @@ for (T, kinds) in pairs(nodekinds) end const nodetypes = collect(keys(nodekinds)) -# A [] in a TOML is parsed as Vector{Union{}} -Base.convert(::Type{Union{Float64, Vector{Float64}}}, x::Vector{Union{}}) = - convert(Vector{Float64}, x) - @option struct Solver <: TableOption algorithm::String = "QNDF" saveat::Union{Float64, Vector{Float64}} = Float64[] From 3b2138121b92940b3460958294457dedafa6c549 Mon Sep 17 00:00:00 2001 From: Maarten Pronk Date: Thu, 14 Sep 2023 16:48:21 +0200 Subject: [PATCH 12/13] Generate code from latest Ribasim field changes. --- docs/schema/OutletStatic.schema.json | 5 +---- python/ribasim/ribasim/models.py | 1 + 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/docs/schema/OutletStatic.schema.json b/docs/schema/OutletStatic.schema.json index b84e3b80f..8acf1ac04 100644 --- a/docs/schema/OutletStatic.schema.json +++ b/docs/schema/OutletStatic.schema.json @@ -17,10 +17,7 @@ }, "min_crest_level": { "format": "default", - "description": "min_crest_level", - "type": [ - "number" - ] + "type": "number" }, "flow_rate": { "format": "double", diff --git a/python/ribasim/ribasim/models.py b/python/ribasim/ribasim/models.py index ef0e28182..b9aabdfe2 100644 --- a/python/ribasim/ribasim/models.py +++ b/python/ribasim/ribasim/models.py @@ -129,6 +129,7 @@ class OutletStatic(BaseModel): max_flow_rate: Optional[float] = None remarks: str = Field("", description="a hack for pandera") active: Optional[bool] = None + min_crest_level: Optional[float] = None flow_rate: float node_id: int control_state: Optional[str] = None From 186d18ecba3734102dd787556bc2d066400bdd49 Mon Sep 17 00:00:00 2001 From: Maarten Pronk Date: Thu, 14 Sep 2023 17:03:08 +0200 Subject: [PATCH 13/13] Set Optional types with type null instead of only required. --- docs/gen_schema.jl | 1 - .../DiscreteControlCondition.schema.json | 9 +++- docs/schema/FlowBoundaryStatic.schema.json | 9 +++- docs/schema/FractionalFlowStatic.schema.json | 9 +++- docs/schema/LevelBoundaryStatic.schema.json | 9 +++- .../schema/LinearResistanceStatic.schema.json | 18 +++++++- .../ManningResistanceStatic.schema.json | 18 +++++++- docs/schema/OutletStatic.schema.json | 45 ++++++++++++++++--- docs/schema/Output.schema.json | 11 ++++- docs/schema/PIDControlStatic.schema.json | 18 +++++++- docs/schema/PidControlTime.schema.json | 9 +++- docs/schema/PumpStatic.schema.json | 36 +++++++++++++-- .../TabulatedRatingCurveStatic.schema.json | 18 +++++++- docs/schema/basin.schema.json | 44 ++++++++++++++---- docs/schema/discrete_control.schema.json | 22 +++++++-- docs/schema/flow_boundary.schema.json | 22 +++++++-- docs/schema/fractional_flow.schema.json | 11 ++++- docs/schema/level_boundary.schema.json | 22 +++++++-- docs/schema/linear_resistance.schema.json | 11 ++++- docs/schema/manning_resistance.schema.json | 11 ++++- docs/schema/outlet.schema.json | 11 ++++- docs/schema/pid_control.schema.json | 22 +++++++-- docs/schema/pump.schema.json | 11 ++++- .../schema/tabulated_rating_curve.schema.json | 22 +++++++-- docs/schema/terminal.schema.json | 11 ++++- python/ribasim/ribasim/config.py | 44 +++++++++--------- 26 files changed, 387 insertions(+), 87 deletions(-) diff --git a/docs/gen_schema.jl b/docs/gen_schema.jl index d9915928b..eaa0bbf36 100644 --- a/docs/gen_schema.jl +++ b/docs/gen_schema.jl @@ -40,7 +40,6 @@ jsonformat(::Type{<:Any}) = "default" function jsontype(T::Union) t = Base.uniontypes(T) td = Dict(zip(t, jsontype.(t))) - filter!(x -> !isequal(x.second, "null"), td) length(td) == 1 && return first(values(td)) types = Dict[] for (t, jt) in td diff --git a/docs/schema/DiscreteControlCondition.schema.json b/docs/schema/DiscreteControlCondition.schema.json index bcf3fc534..3ce188fe2 100644 --- a/docs/schema/DiscreteControlCondition.schema.json +++ b/docs/schema/DiscreteControlCondition.schema.json @@ -25,7 +25,14 @@ }, "look_ahead": { "format": "default", - "type": "number" + "anyOf": [ + { + "type": "null" + }, + { + "type": "number" + } + ] } }, "required": [ diff --git a/docs/schema/FlowBoundaryStatic.schema.json b/docs/schema/FlowBoundaryStatic.schema.json index 635bff762..8300ea9fd 100644 --- a/docs/schema/FlowBoundaryStatic.schema.json +++ b/docs/schema/FlowBoundaryStatic.schema.json @@ -9,7 +9,14 @@ }, "active": { "format": "default", - "type": "boolean" + "anyOf": [ + { + "type": "null" + }, + { + "type": "boolean" + } + ] }, "flow_rate": { "format": "double", diff --git a/docs/schema/FractionalFlowStatic.schema.json b/docs/schema/FractionalFlowStatic.schema.json index f09e4e600..64bed064f 100644 --- a/docs/schema/FractionalFlowStatic.schema.json +++ b/docs/schema/FractionalFlowStatic.schema.json @@ -17,7 +17,14 @@ }, "control_state": { "format": "default", - "type": "string" + "anyOf": [ + { + "type": "null" + }, + { + "type": "string" + } + ] } }, "required": [ diff --git a/docs/schema/LevelBoundaryStatic.schema.json b/docs/schema/LevelBoundaryStatic.schema.json index 42a58db1c..f635afce0 100644 --- a/docs/schema/LevelBoundaryStatic.schema.json +++ b/docs/schema/LevelBoundaryStatic.schema.json @@ -9,7 +9,14 @@ }, "active": { "format": "default", - "type": "boolean" + "anyOf": [ + { + "type": "null" + }, + { + "type": "boolean" + } + ] }, "node_id": { "format": "default", diff --git a/docs/schema/LinearResistanceStatic.schema.json b/docs/schema/LinearResistanceStatic.schema.json index 997e2f3c2..5a5146ea9 100644 --- a/docs/schema/LinearResistanceStatic.schema.json +++ b/docs/schema/LinearResistanceStatic.schema.json @@ -9,7 +9,14 @@ }, "active": { "format": "default", - "type": "boolean" + "anyOf": [ + { + "type": "null" + }, + { + "type": "boolean" + } + ] }, "node_id": { "format": "default", @@ -21,7 +28,14 @@ }, "control_state": { "format": "default", - "type": "string" + "anyOf": [ + { + "type": "null" + }, + { + "type": "string" + } + ] } }, "required": [ diff --git a/docs/schema/ManningResistanceStatic.schema.json b/docs/schema/ManningResistanceStatic.schema.json index 8e68c8e04..22bad56df 100644 --- a/docs/schema/ManningResistanceStatic.schema.json +++ b/docs/schema/ManningResistanceStatic.schema.json @@ -17,7 +17,14 @@ }, "active": { "format": "default", - "type": "boolean" + "anyOf": [ + { + "type": "null" + }, + { + "type": "boolean" + } + ] }, "profile_width": { "format": "double", @@ -33,7 +40,14 @@ }, "control_state": { "format": "default", - "type": "string" + "anyOf": [ + { + "type": "null" + }, + { + "type": "string" + } + ] } }, "required": [ diff --git a/docs/schema/OutletStatic.schema.json b/docs/schema/OutletStatic.schema.json index 8acf1ac04..d8feae988 100644 --- a/docs/schema/OutletStatic.schema.json +++ b/docs/schema/OutletStatic.schema.json @@ -3,7 +3,14 @@ "properties": { "max_flow_rate": { "format": "default", - "type": "number" + "anyOf": [ + { + "type": "null" + }, + { + "type": "number" + } + ] }, "remarks": { "format": "default", @@ -13,11 +20,25 @@ }, "active": { "format": "default", - "type": "boolean" + "anyOf": [ + { + "type": "null" + }, + { + "type": "boolean" + } + ] }, "min_crest_level": { "format": "default", - "type": "number" + "anyOf": [ + { + "type": "null" + }, + { + "type": "number" + } + ] }, "flow_rate": { "format": "double", @@ -29,11 +50,25 @@ }, "control_state": { "format": "default", - "type": "string" + "anyOf": [ + { + "type": "null" + }, + { + "type": "string" + } + ] }, "min_flow_rate": { "format": "default", - "type": "number" + "anyOf": [ + { + "type": "null" + }, + { + "type": "number" + } + ] } }, "required": [ diff --git a/docs/schema/Output.schema.json b/docs/schema/Output.schema.json index e35fb100c..42fffb7e9 100644 --- a/docs/schema/Output.schema.json +++ b/docs/schema/Output.schema.json @@ -23,8 +23,15 @@ }, "outstate": { "format": "default", - "default": null, - "type": "string" + "anyOf": [ + { + "type": "null" + }, + { + "type": "string" + } + ], + "default": null }, "compression_level": { "format": "default", diff --git a/docs/schema/PIDControlStatic.schema.json b/docs/schema/PIDControlStatic.schema.json index 1f44a8c03..df64576ee 100644 --- a/docs/schema/PIDControlStatic.schema.json +++ b/docs/schema/PIDControlStatic.schema.json @@ -17,7 +17,14 @@ }, "active": { "format": "default", - "type": "boolean" + "anyOf": [ + { + "type": "null" + }, + { + "type": "boolean" + } + ] }, "proportional": { "format": "double", @@ -37,7 +44,14 @@ }, "control_state": { "format": "default", - "type": "string" + "anyOf": [ + { + "type": "null" + }, + { + "type": "string" + } + ] } }, "required": [ diff --git a/docs/schema/PidControlTime.schema.json b/docs/schema/PidControlTime.schema.json index ac03d9546..de34eaef8 100644 --- a/docs/schema/PidControlTime.schema.json +++ b/docs/schema/PidControlTime.schema.json @@ -37,7 +37,14 @@ }, "control_state": { "format": "default", - "type": "string" + "anyOf": [ + { + "type": "null" + }, + { + "type": "string" + } + ] } }, "required": [ diff --git a/docs/schema/PumpStatic.schema.json b/docs/schema/PumpStatic.schema.json index baa7c0b49..afe80e42c 100644 --- a/docs/schema/PumpStatic.schema.json +++ b/docs/schema/PumpStatic.schema.json @@ -3,7 +3,14 @@ "properties": { "max_flow_rate": { "format": "default", - "type": "number" + "anyOf": [ + { + "type": "null" + }, + { + "type": "number" + } + ] }, "remarks": { "format": "default", @@ -13,7 +20,14 @@ }, "active": { "format": "default", - "type": "boolean" + "anyOf": [ + { + "type": "null" + }, + { + "type": "boolean" + } + ] }, "flow_rate": { "format": "double", @@ -25,11 +39,25 @@ }, "control_state": { "format": "default", - "type": "string" + "anyOf": [ + { + "type": "null" + }, + { + "type": "string" + } + ] }, "min_flow_rate": { "format": "default", - "type": "number" + "anyOf": [ + { + "type": "null" + }, + { + "type": "number" + } + ] } }, "required": [ diff --git a/docs/schema/TabulatedRatingCurveStatic.schema.json b/docs/schema/TabulatedRatingCurveStatic.schema.json index b833be821..cb244575b 100644 --- a/docs/schema/TabulatedRatingCurveStatic.schema.json +++ b/docs/schema/TabulatedRatingCurveStatic.schema.json @@ -9,7 +9,14 @@ }, "active": { "format": "default", - "type": "boolean" + "anyOf": [ + { + "type": "null" + }, + { + "type": "boolean" + } + ] }, "node_id": { "format": "default", @@ -25,7 +32,14 @@ }, "control_state": { "format": "default", - "type": "string" + "anyOf": [ + { + "type": "null" + }, + { + "type": "string" + } + ] } }, "required": [ diff --git a/docs/schema/basin.schema.json b/docs/schema/basin.schema.json index 2ddaeb7e3..6113db247 100644 --- a/docs/schema/basin.schema.json +++ b/docs/schema/basin.schema.json @@ -3,23 +3,51 @@ "properties": { "profile": { "format": "default", - "default": null, - "type": "string" + "anyOf": [ + { + "type": "null" + }, + { + "type": "string" + } + ], + "default": null }, "static": { "format": "default", - "default": null, - "type": "string" + "anyOf": [ + { + "type": "null" + }, + { + "type": "string" + } + ], + "default": null }, "forcing": { "format": "default", - "default": null, - "type": "string" + "anyOf": [ + { + "type": "null" + }, + { + "type": "string" + } + ], + "default": null }, "state": { "format": "default", - "default": null, - "type": "string" + "anyOf": [ + { + "type": "null" + }, + { + "type": "string" + } + ], + "default": null } }, "required": [ diff --git a/docs/schema/discrete_control.schema.json b/docs/schema/discrete_control.schema.json index bd7a06ab1..22a244703 100644 --- a/docs/schema/discrete_control.schema.json +++ b/docs/schema/discrete_control.schema.json @@ -3,13 +3,27 @@ "properties": { "logic": { "format": "default", - "default": null, - "type": "string" + "anyOf": [ + { + "type": "null" + }, + { + "type": "string" + } + ], + "default": null }, "condition": { "format": "default", - "default": null, - "type": "string" + "anyOf": [ + { + "type": "null" + }, + { + "type": "string" + } + ], + "default": null } }, "required": [ diff --git a/docs/schema/flow_boundary.schema.json b/docs/schema/flow_boundary.schema.json index 5018b506c..29f922f9a 100644 --- a/docs/schema/flow_boundary.schema.json +++ b/docs/schema/flow_boundary.schema.json @@ -3,13 +3,27 @@ "properties": { "time": { "format": "default", - "default": null, - "type": "string" + "anyOf": [ + { + "type": "null" + }, + { + "type": "string" + } + ], + "default": null }, "static": { "format": "default", - "default": null, - "type": "string" + "anyOf": [ + { + "type": "null" + }, + { + "type": "string" + } + ], + "default": null } }, "required": [ diff --git a/docs/schema/fractional_flow.schema.json b/docs/schema/fractional_flow.schema.json index f836d7a98..940b2977d 100644 --- a/docs/schema/fractional_flow.schema.json +++ b/docs/schema/fractional_flow.schema.json @@ -3,8 +3,15 @@ "properties": { "static": { "format": "default", - "default": null, - "type": "string" + "anyOf": [ + { + "type": "null" + }, + { + "type": "string" + } + ], + "default": null } }, "required": [ diff --git a/docs/schema/level_boundary.schema.json b/docs/schema/level_boundary.schema.json index 678a51752..f4c2d70c8 100644 --- a/docs/schema/level_boundary.schema.json +++ b/docs/schema/level_boundary.schema.json @@ -3,13 +3,27 @@ "properties": { "time": { "format": "default", - "default": null, - "type": "string" + "anyOf": [ + { + "type": "null" + }, + { + "type": "string" + } + ], + "default": null }, "static": { "format": "default", - "default": null, - "type": "string" + "anyOf": [ + { + "type": "null" + }, + { + "type": "string" + } + ], + "default": null } }, "required": [ diff --git a/docs/schema/linear_resistance.schema.json b/docs/schema/linear_resistance.schema.json index ef53a67e6..96565c7b3 100644 --- a/docs/schema/linear_resistance.schema.json +++ b/docs/schema/linear_resistance.schema.json @@ -3,8 +3,15 @@ "properties": { "static": { "format": "default", - "default": null, - "type": "string" + "anyOf": [ + { + "type": "null" + }, + { + "type": "string" + } + ], + "default": null } }, "required": [ diff --git a/docs/schema/manning_resistance.schema.json b/docs/schema/manning_resistance.schema.json index b4e39594b..aef075138 100644 --- a/docs/schema/manning_resistance.schema.json +++ b/docs/schema/manning_resistance.schema.json @@ -3,8 +3,15 @@ "properties": { "static": { "format": "default", - "default": null, - "type": "string" + "anyOf": [ + { + "type": "null" + }, + { + "type": "string" + } + ], + "default": null } }, "required": [ diff --git a/docs/schema/outlet.schema.json b/docs/schema/outlet.schema.json index 18f8b4d85..1e6e2efd5 100644 --- a/docs/schema/outlet.schema.json +++ b/docs/schema/outlet.schema.json @@ -3,8 +3,15 @@ "properties": { "static": { "format": "default", - "default": null, - "type": "string" + "anyOf": [ + { + "type": "null" + }, + { + "type": "string" + } + ], + "default": null } }, "required": [ diff --git a/docs/schema/pid_control.schema.json b/docs/schema/pid_control.schema.json index cf4ad8dac..5e7eb7024 100644 --- a/docs/schema/pid_control.schema.json +++ b/docs/schema/pid_control.schema.json @@ -3,13 +3,27 @@ "properties": { "time": { "format": "default", - "default": null, - "type": "string" + "anyOf": [ + { + "type": "null" + }, + { + "type": "string" + } + ], + "default": null }, "static": { "format": "default", - "default": null, - "type": "string" + "anyOf": [ + { + "type": "null" + }, + { + "type": "string" + } + ], + "default": null } }, "required": [ diff --git a/docs/schema/pump.schema.json b/docs/schema/pump.schema.json index 882d65ae6..2679991b2 100644 --- a/docs/schema/pump.schema.json +++ b/docs/schema/pump.schema.json @@ -3,8 +3,15 @@ "properties": { "static": { "format": "default", - "default": null, - "type": "string" + "anyOf": [ + { + "type": "null" + }, + { + "type": "string" + } + ], + "default": null } }, "required": [ diff --git a/docs/schema/tabulated_rating_curve.schema.json b/docs/schema/tabulated_rating_curve.schema.json index c25c77e86..2d1beff66 100644 --- a/docs/schema/tabulated_rating_curve.schema.json +++ b/docs/schema/tabulated_rating_curve.schema.json @@ -3,13 +3,27 @@ "properties": { "time": { "format": "default", - "default": null, - "type": "string" + "anyOf": [ + { + "type": "null" + }, + { + "type": "string" + } + ], + "default": null }, "static": { "format": "default", - "default": null, - "type": "string" + "anyOf": [ + { + "type": "null" + }, + { + "type": "string" + } + ], + "default": null } }, "required": [ diff --git a/docs/schema/terminal.schema.json b/docs/schema/terminal.schema.json index 10bc38cfd..c6ca4c650 100644 --- a/docs/schema/terminal.schema.json +++ b/docs/schema/terminal.schema.json @@ -3,8 +3,15 @@ "properties": { "static": { "format": "default", - "default": null, - "type": "string" + "anyOf": [ + { + "type": "null" + }, + { + "type": "string" + } + ], + "default": null } }, "required": [ diff --git a/python/ribasim/ribasim/config.py b/python/ribasim/ribasim/config.py index 7df49a03e..1f5bc7662 100644 --- a/python/ribasim/ribasim/config.py +++ b/python/ribasim/ribasim/config.py @@ -4,7 +4,7 @@ from __future__ import annotations from datetime import datetime -from typing import List, Union +from typing import List, Optional, Union from pydantic import BaseModel, Field @@ -14,22 +14,22 @@ class Output(BaseModel): basin: str = "output/basin.arrow" flow: str = "output/flow.arrow" control: str = "output/control.arrow" - outstate: str = None + outstate: Optional[str] = None compression_level: int = 6 class LevelBoundary(BaseModel): - time: str = None - static: str = None + time: Optional[str] = None + static: Optional[str] = None class Pump(BaseModel): - static: str = None + static: Optional[str] = None class DiscreteControl(BaseModel): - logic: str = None - condition: str = None + logic: Optional[str] = None + condition: Optional[str] = None class Solver(BaseModel): @@ -45,26 +45,26 @@ class Solver(BaseModel): class FlowBoundary(BaseModel): - time: str = None - static: str = None + time: Optional[str] = None + static: Optional[str] = None class PidControl(BaseModel): - time: str = None - static: str = None + time: Optional[str] = None + static: Optional[str] = None class FractionalFlow(BaseModel): - static: str = None + static: Optional[str] = None class ManningResistance(BaseModel): - static: str = None + static: Optional[str] = None class TabulatedRatingCurve(BaseModel): - time: str = None - static: str = None + time: Optional[str] = None + static: Optional[str] = None class Logging(BaseModel): @@ -73,22 +73,22 @@ class Logging(BaseModel): class Outlet(BaseModel): - static: str = None + static: Optional[str] = None class Terminal(BaseModel): - static: str = None + static: Optional[str] = None class Basin(BaseModel): - profile: str = None - static: str = None - forcing: str = None - state: str = None + profile: Optional[str] = None + static: Optional[str] = None + forcing: Optional[str] = None + state: Optional[str] = None class LinearResistance(BaseModel): - static: str = None + static: Optional[str] = None class Config(BaseModel):