From a9e95c19371d218969ab0549fc7623a4f1de0043 Mon Sep 17 00:00:00 2001 From: Quarto GHA Workflow Runner Date: Mon, 30 Oct 2023 13:57:43 +0000 Subject: [PATCH] Built site for gh-pages --- .nojekyll | 2 +- build/index.html | 258 +++++++++++++++++----------------- core/usage.html | 110 ++++++++++----- python/examples.html | 2 +- python/reference/Model.html | 8 +- schema/Allocation.schema.json | 29 ++++ schema/Config.schema.json | 32 +++-- schema/Edge.schema.json | 11 ++ schema/Node.schema.json | 11 ++ search.json | 17 ++- 10 files changed, 293 insertions(+), 187 deletions(-) create mode 100644 schema/Allocation.schema.json diff --git a/.nojekyll b/.nojekyll index 16cfbb3f2..75049b329 100644 --- a/.nojekyll +++ b/.nojekyll @@ -1 +1 @@ -b78f5444 \ No newline at end of file +e86c009a \ No newline at end of file diff --git a/build/index.html b/build/index.html index 2d5f99f0d..5be0dbdfe 100644 --- a/build/index.html +++ b/build/index.html @@ -262,11 +262,11 @@

solve!
  • BMI.finalize
  • -

    source

    +

    source

    # Ribasim.configModule.

    module config

    Ribasim.config is a submodule of Ribasim to handle the configuration of a Ribasim model. It is implemented using the Configurations package. A full configuration is represented by Config, which is the main API. Ribasim.config is a submodule mainly to avoid name clashes between the configuration sections and the rest of Ribasim.

    -

    source

    +

    source

    @@ -275,7 +275,7 @@

    # Ribasim.AllocationModelType.

    Store information for a subnetwork used for allocation.

    nodeid: All the IDs of the nodes that are in this subnetwork nodeidmapping: Mapping Dictionary; modelnodeid => AGnodeid where such a correspondence exists (all AG node ids are in the values) nodeidmappinginverse: The inverse of nodeidmapping, Dictionary; AG node ID => model node ID Source edge mapping: AG source node ID => subnetwork source edge ID graphallocation: The graph used for the allocation problems capacity: The capacity per edge of the allocation graph, as constrained by nodes that have a maxflowrate problem: The JuMP.jl model for solving the allocation problem Δtallocation: The time interval between consecutive allocation solves

    -

    source

    +

    source

    # Ribasim.AllocationModelMethod.

    Construct the JuMP.jl problem for allocation.

    Definitions

    @@ -287,7 +287,7 @@

    source

    +

    source

    # Ribasim.BasinType.

    Requirements:

      @@ -297,24 +297,24 @@

      source

      +

      source

      # Ribasim.ConnectivityType.

      Store the connectivity information

      graphflow, graphcontrol: directed graph with vertices equal to ids flow: store the flow on every flow edge edgeidsflow, edgeidscontrol: get the external edge id from (src, dst) edgeconnectiontypeflow, edgeconnectiontypescontrol: get (srcnodetype, dstnodetype) from edge id

      if autodiff T = DiffCache{SparseArrays.SparseMatrixCSC{Float64, Int64}, Vector{Float64}} else T = SparseMatrixCSC{Float64, Int} end

      -

      source

      +

      source

      # Ribasim.DiscreteControlType.

      nodeid: node ID of the DiscreteControl node; these are not unique but repeated by the amount of conditions of this DiscreteControl node listenfeatureid: the ID of the node/edge being condition on variable: the name of the variable in the condition greaterthan: The threshold value in the condition conditionvalue: The current value of each condition controlstate: Dictionary: node ID => (control state, control state start) logic_mapping: Dictionary: (control node ID, truth state) => control state record: Namedtuple with discrete control information for results

      -

      source

      +

      source

      # Ribasim.FlatVectorType.

      struct FlatVector{T} <: AbstractVector{T}

      A FlatVector is an AbstractVector that iterates the T of a Vector{Vector{T}}.

      Each inner vector is assumed to be of equal length.

      It is similar to Iterators.flatten, though that doesn’t work with the Tables.Column interface, which needs length and getindex support.

      -

      source

      +

      source

      # Ribasim.FlowBoundaryType.

      nodeid: node ID of the FlowBoundary node active: whether this node is active and thus contributes flow flowrate: target flow rate

      -

      source

      +

      source

      # Ribasim.FractionalFlowType.

      Requirements:

        @@ -323,10 +323,10 @@

        source

        +

        source

        # Ribasim.LevelBoundaryType.

        node_id: node ID of the LevelBoundary node active: whether this node is active level: the fixed level of this ‘infinitely big basin’

        -

        source

        +

        source

        # Ribasim.LinearResistanceType.

        Requirements:

          @@ -334,7 +334,7 @@

          source

          +

          source

          # Ribasim.ManningResistanceType.

          This is a simple Manning-Gauckler reach connection.

            @@ -364,39 +364,39 @@

            source

            +

            source

            # Ribasim.ModelType.

            Model(config_path::AbstractString)
             Model(config::Config)

            Initialize a Model.

            The Model struct is an initialized model, combined with the Config used to create it and saved results. The Basic Model Interface (BMI) is implemented on the Model. A Model can be created from the path to a TOML configuration file, or a Config object.

            -

            source

            +

            source

            # Ribasim.OutletType.

            nodeid: node ID of the Outlet node active: whether this node is active and thus contributes flow flowrate: target flow rate minflowrate: The minimal flow rate of the outlet maxflowrate: The maximum flow rate of the outlet controlmapping: dictionary from (nodeid, controlstate) to target flow rate ispid_controlled: whether the flow rate of this outlet is governed by PID control

            -

            source

            +

            source

            # Ribasim.PidControlType.

            PID control currently only supports regulating basin levels.

            nodeid: node ID of the PidControl node active: whether this node is active and thus sets flow rates listennodeid: the id of the basin being controlled pidparams: a vector interpolation for parameters changing over time. The parameters are respectively target, proportional, integral, derivative, where the last three are the coefficients for the PID equation. error: the current error; basintarget - currentlevel

            -

            source

            +

            source

            # Ribasim.PumpType.

            nodeid: node ID of the Pump node active: whether this node is active and thus contributes flow flowrate: target flow rate minflowrate: The minimal flow rate of the pump maxflowrate: The maximum flow rate of the pump controlmapping: dictionary from (nodeid, controlstate) to target flow rate ispid_controlled: whether the flow rate of this pump is governed by PID control

            -

            source

            +

            source

            # Ribasim.TabulatedRatingCurveType.

            struct TabulatedRatingCurve{C}

            Rating curve from level to discharge. The rating curve is a lookup table with linear interpolation in between. Relation can be updated in time, which is done by moving data from the time field into the tables, which is done in the update_tabulated_rating_curve callback.

            Type parameter C indicates the content backing the StructVector, which can be a NamedTuple of Vectors or Arrow Primitives, and is added to avoid type instabilities.

            nodeid: node ID of the TabulatedRatingCurve node active: whether this node is active and thus contributes flows tables: The current Q(h) relationships time: The time table used for updating the tables controlmapping: dictionary from (nodeid, controlstate) to Q(h) and/or active state

            -

            source

            +

            source

            # Ribasim.TerminalType.

            node_id: node ID of the Terminal node

            -

            source

            +

            source

            # Ribasim.UserType.

            demand: water flux demand of user per priority over time active: whether this node is active and thus demands water allocated: water flux currently allocated to user per priority returnfactor: the factor in [0,1] of how much of the abstracted water is given back to the system minlevel: The level of the source basin below which the user does not abstract priorities: All used priority values. Each user has a demand for all these priorities, which is always 0.0 if it is not provided explicitly.

            -

            source

            +

            source

            # Ribasim.config.ConfigMethod.

            Config(config_path::AbstractString; kwargs...)

            Parse a TOML file to a Config. Keys can be overruled using keyword arguments. To overrule keys from a subsection, e.g. dt from the solver section, use underscores: solver_dt.

            -

            source

            +

            source

            @@ -405,35 +405,35 @@

            # BasicModelInterface.finalizeMethod.

            BMI.finalize(model::Model)::Model

            Write all results to the configured files.

            -

            source

            +

            source

            # BasicModelInterface.initializeMethod.

            BMI.initialize(T::Type{Model}, config_path::AbstractString)::Model

            Initialize a Model from the path to the TOML configuration file.

            -

            source

            +

            source

            # BasicModelInterface.initializeMethod.

            BMI.initialize(T::Type{Model}, config::Config)::Model

            Initialize a Model from a Config.

            -

            source

            +

            source

            # CommonSolve.solve!Method.

            solve!(model::Model)::ODESolution

            Solve a Model until the configured endtime.

            -

            source

            +

            source

            # Ribasim.add_constraints_basin_allocation!Method.

            Add the basin allocation constraints to the allocation problem; the allocations to the basins are bounded from above by the basin demand (these are set before each allocation solve). The constraint indices are allocation graph basin node IDs.

            Constraint: allocation to basin <= basin demand

            -

            source

            +

            source

            # Ribasim.add_constraints_capacity!Method.

            Add the flow capacity constraints to the allocation problem. Only finite capacities get a constraint. The constraint indices are the allocation graph edge IDs.

            Constraint: flow over edge <= edge capacity

            -

            source

            +

            source

            # Ribasim.add_constraints_flow_conservation!Method.

            Add the flow conservation constraints to the allocation problem. The constraint indices are allocgraph user node IDs.

            Constraint: sum(flows out of node node) <= flows into node + flow from storage and vertical fluxes

            -

            source

            +

            source

            # Ribasim.add_constraints_source!Method.

            Add the source constraints to the allocation problem. The actual threshold values will be set before each allocation solve. The constraint indices are the allocation graph source node IDs.

            Constraint: flow over source edge <= source flow in subnetwork

            -

            source

            +

            source

            # Ribasim.add_constraints_user_allocation!Method.

            Add the user allocation constraints to the allocation problem:

              @@ -443,81 +443,81 @@

              source

              +

              source

              # Ribasim.add_constraints_user_returnflow!Method.

              Add the user returnflow constraints to the allocation problem. The constraint indices are allocation graph user node IDs.

              Constraint: outflow from user = return factor * inflow to user

              -

              source

              +

              source

              # Ribasim.add_objective_function!Method.

              Add the objective function to the allocation problem. Objective function: linear combination of allocations to the basins and users, where basin allocations get a weight of 1.0 and user allocations get a weight of 2^(-priority index).

              -

              source

              +

              source

              # Ribasim.add_variables_allocation_basin!Method.

              Add the basin allocation variables A_basin to the allocation problem. The variable indices are the allocation graph basin node IDs. Non-negativivity constraints are also immediately added to the basin allocation variables.

              -

              source

              +

              source

              # Ribasim.add_variables_allocation_user!Method.

              Add the user allocation variables Auser{i} to the allocation problem. The variable name indices i are the allocation graph user node IDs, The variable indices are the priorities.

              -

              source

              +

              source

              # Ribasim.add_variables_flow!Method.

              Add the flow variables F to the allocation problem. The variable indices are the allocation graph edge IDs. Non-negativivity constraints are also immediately added to the flow variables.

              -

              source

              +

              source

              # Ribasim.allocate!Method.

              Update the allocation optimization problem for the given subnetwork with the problem state and flows, solve the allocation problem and assign the results to the users.

              -

              source

              +

              source

              # Ribasim.allocation_graphMethod.

              Build the graph used for the allocation problem.

              -

              source

              +

              source

              # Ribasim.allocation_problemMethod.

              Construct the allocation problem for the current subnetwork as a JuMP.jl model.

              -

              source

              +

              source

              # Ribasim.assign_allocations!Method.

              Assign the allocations to the users as determined by the solution of the allocation problem.

              -

              source

              +

              source

              # Ribasim.avoid_using_own_returnflow!Method.

              Remove user return flow edges that are upstream of the user itself, and collect the IDs of the allocation graph node IDs of the users that do not have this problem.

              -

              source

              +

              source

              # Ribasim.basin_bottomMethod.

              Return the bottom elevation of the basin with index i, or nothing if it doesn’t exist

              -

              source

              +

              source

              # Ribasim.basin_bottomsMethod.

              Get the bottom on both ends of a node. If only one has a bottom, use that for both.

              -

              source

              +

              source

              # Ribasim.basin_tableMethod.

              Create the basin result table from the saved data

              -

              source

              -

              # Ribasim.create_callbacksMethod.

              +

              source

              +

              # Ribasim.create_callbacksMethod.

              Create the different callbacks that are used to store results and feed the simulation with new data. The different callbacks are combined to a CallbackSet that goes to the integrator. Returns the CallbackSet and the SavedValues for flow.

              -

              source

              +

              source

              # Ribasim.create_graphMethod.

              Return a directed graph, and a mapping from source and target nodes to edge fid.

              -

              source

              +

              source

              # Ribasim.create_storage_tablesMethod.

              Read the Basin / profile table and return all area and level and computed storage values

              -

              source

              +

              source

              # Ribasim.datetime_sinceMethod.

              datetime_since(t::Real, t0::DateTime)::DateTime

              Convert a Real that represents the seconds passed since the simulation start to the nearest DateTime. This is used to convert between the solver’s inner float time, and the calendar.

              -

              source

              +

              source

              # Ribasim.datetimesMethod.

              Get all saved times as a Vector{DateTime}

              -

              source

              +

              source

              # Ribasim.discrete_control_affect!Method.

              Change parameters based on the control logic.

              -

              source

              +

              source

              # Ribasim.discrete_control_affect_downcrossing!Method.

              An downcrossing means that a condition (always greater than) becomes false.

              -

              source

              +

              source

              # Ribasim.discrete_control_affect_upcrossing!Method.

              An upcrossing means that a condition (always greater than) becomes true.

              -

              source

              +

              source

              # Ribasim.discrete_control_conditionMethod.

              Listens for changes in condition truths.

              -

              source

              +

              source

              # Ribasim.discrete_control_tableMethod.

              Create a discrete control result table from the saved data

              -

              source

              +

              source

              # Ribasim.expand_logic_mappingMethod.

              Replace the truth states in the logic mapping which contain wildcards with all possible explicit truth states.

              -

              source

              +

              source

              # Ribasim.find_allocation_graph_edges!Method.

              This loop finds allocgraph edges in several ways:

                @@ -525,26 +525,26 @@

                source

                +

                source

                # Ribasim.findlastgroupMethod.

                For an element id and a vector of elements ids, get the range of indices of the last consecutive block of id. Returns the empty range 1:0 if id is not in ids.

                #                         1 2 3 4 5 6 7 8 9
                 Ribasim.findlastgroup(2, [5,4,2,2,5,2,2,2,1])
                 # output
                 6:8
                -

                source

                +

                source

                # Ribasim.findsortedMethod.

                Find the index of element x in a sorted collection a. Returns the index of x if it exists, or nothing if it doesn’t. If x occurs more than once, throw an error.

                -

                source

                +

                source

                # Ribasim.flow_tableMethod.

                Create a flow result table from the saved data

                -

                source

                +

                source

                # Ribasim.formulate_basins!Method.

                Smoothly let the evaporation flux go to 0 when at small water depths Currently at less than 0.1 m.

                -

                source

                +

                source

                # Ribasim.formulate_flow!Method.

                Directed graph: outflow is positive!

                -

                source

                +

                source

                # Ribasim.formulate_flow!Method.

                Conservation of energy for two basins, a and b:

                h_a + v_a^2 / (2 * g) = h_b + v_b^2 / (2 * g) + S_f * L + C / 2 * g * (v_b^2 - v_a^2)
                @@ -572,91 +572,91 @@

                source

                +

                source

                # Ribasim.formulate_flow!Method.

                Directed graph: outflow is positive!

                -

                source

                +

                source

                # Ribasim.get_area_and_levelMethod.

                Compute the area and level of a basin given its storage. Also returns darea/dlevel as it is needed for the Jacobian.

                -

                source

                +

                source

                # Ribasim.get_compressorMethod.

                Get the compressor based on the Results section

                -

                source

                +

                source

                # Ribasim.get_fractional_flow_connected_basinsMethod.

                Get the node type specific indices of the fractional flows and basins, that are consecutively connected to a node of given id.

                -

                source

                +

                source

                # Ribasim.get_jac_prototypeMethod.

                Get a sparse matrix whose sparsity matches the sparsity of the Jacobian of the ODE problem. All nodes are taken into consideration, also the ones that are inactive.

                In Ribasim the Jacobian is typically sparse because each state only depends on a small number of other states.

                Note: the name ‘prototype’ does not mean this code is a prototype, it comes from the naming convention of this sparsity structure in the differentialequations.jl docs.

                -

                source

                +

                source

                # Ribasim.get_levelMethod.

                Get the current water level of a node ID. The ID can belong to either a Basin or a LevelBoundary. storage: tells ForwardDiff whether this call is for differentiation or not

                -

                source

                +

                source

                # Ribasim.get_node_id_mappingMethod.

                Get:

                • The mapping from subnetwork node IDs to allocation graph node IDs
                • The mapping from allocation graph source node IDs to subnetwork source edge IDs
                -

                source

                +

                source

                # Ribasim.get_node_in_out_edgesMethod.

                Get two dictionaries, where:

                • The first one gives the IDs of the inedges for each node ID in the graph
                • The second one gives the IDs of the outedges for each node ID in the graph
                -

                source

                +

                source

                # Ribasim.get_scalar_interpolationMethod.

                Linear interpolation of a scalar with constant extrapolation.

                -

                source

                +

                source

                # Ribasim.get_storage_from_levelMethod.

                Get the storage of a basin from its level.

                -

                source

                +

                source

                # Ribasim.get_storages_and_levelsMethod.

                Get the storage and level of all basins as matrices of nbasin × ntime

                -

                source

                +

                source

                # Ribasim.get_storages_from_levelsMethod.

                Compute the storages of the basins based on the water level of the basins.

                -

                source

                +

                source

                # Ribasim.get_tstopsMethod.

                From an iterable of DateTimes, find the times the solver needs to stop

                -

                source

                +

                source

                # Ribasim.get_valueMethod.

                Get a value for a condition. Currently supports getting levels from basins and flows from flow boundaries.

                -

                source

                +

                source

                # Ribasim.id_indexMethod.

                Get the index of an ID in a set of indices.

                -

                source

                +

                source

                # Ribasim.input_pathMethod.

                Construct a path relative to both the TOML directory and the optional input_dir

                -

                source

                +

                source

                # Ribasim.is_flow_constrainingMethod.

                Whether the given node node is flow constraining by having a maximum flow rate.

                -

                source

                +

                source

                # Ribasim.is_flow_direction_constrainingMethod.

                Whether the given node is flow direction constraining (only in direction of edges).

                -

                source

                +

                source

                # Ribasim.load_dataMethod.

                load_data(db::DB, config::Config, nodetype::Symbol, kind::Symbol)::Union{Table, Query, Nothing}

                Load data from Arrow files if available, otherwise the database. Returns either an Arrow.Table, SQLite.Query or nothing if the data is not present.

                -

                source

                +

                source

                # Ribasim.load_structvectorMethod.

                load_structvector(db::DB, config::Config, ::Type{T})::StructVector{T}

                Load data from Arrow files if available, otherwise the database. Always returns a StructVector of the given struct type T, which is empty if the table is not found. This function validates the schema, and enforces the required sort order.

                -

                source

                +

                source

                # Ribasim.nodefieldsMethod.

                Get all node fieldnames of the parameter object.

                -

                source

                +

                source

                # Ribasim.nodetypeMethod.

                From a SchemaVersion(“ribasim.flowboundary.static”, 1) return (:FlowBoundary, :static)

                -

                source

                +

                source

                # Ribasim.parse_static_and_timeMethod.

                Process the data in the static and time tables for a given node type. The ‘defaults’ named tuple dictates how missing data is filled in. ‘time_interpolatables’ is a vector of Symbols of parameter names for which a time interpolation (linear) object must be constructed. The control mapping for DiscreteControl is also constructed in this function. This function currently does not support node states that are defined by more than one row in a table, as is the case for TabulatedRatingCurve.

                -

                source

                +

                source

                # Ribasim.path_exists_in_graphMethod.

                Find out whether a path exists between a start node and end node in the given graph.

                -

                source

                +

                source

                # Ribasim.process_allocation_graph_edges!Method.

                For the composite allocgraph edges:

                  @@ -664,37 +664,37 @@

                  source

                  +

                  source

                  # Ribasim.profile_storageMethod.

                  Calculate a profile storage by integrating the areas over the levels

                  -

                  source

                  +

                  source

                  # Ribasim.qh_interpolationMethod.

                  From a table with columns nodeid, discharge (Q) and level (h), create a LinearInterpolation from level to discharge for a given nodeid.

                  -

                  source

                  +

                  source

                  # Ribasim.reduction_factorMethod.

                  Function that goes smoothly from 0 to 1 in the interval [0,threshold], and is constant outside this interval.

                  -

                  source

                  +

                  source

                  # Ribasim.results_pathMethod.

                  Construct a path relative to both the TOML directory and the optional results_dir

                  -

                  source

                  +

                  source

                  # Ribasim.runMethod.

                  run(config_file::AbstractString)::Model
                   run(config::Config)::Model

                  Run a Model, given a path to a TOML configuration file, or a Config object. Running a model includes initialization, solving to the end with [solve!](@ref) and writing results with BMI.finalize.

                  -

                  source

                  +

                  source

                  # Ribasim.save_flowMethod.

                  Copy the current flow to the SavedValues

                  -

                  source

                  +

                  source

                  # Ribasim.scalar_interpolation_derivativeMethod.

                  Derivative of scalar interpolation.

                  -

                  source

                  +

                  source

                  # Ribasim.seconds_sinceMethod.

                  seconds_since(t::DateTime, t0::DateTime)::Float64

                  Convert a DateTime to a float that is the number of seconds since the start of the simulation. This is used to convert between the solver’s inner float time, and the calendar.

                  -

                  source

                  +

                  source

                  # Ribasim.set_current_value!Method.

                  From a timeseries table time, load the most recent applicable data into table. table must be a NamedTuple of vectors with all variables that must be loaded. The most recent applicable data is non-NaN data for a given ID that is on or before t.

                  -

                  source

                  +

                  source

                  # Ribasim.set_model_state_in_allocation!Method.

                  Update the allocation problem with model data at the current:

                    @@ -702,38 +702,41 @@

                    source

                    +

                    source

                    # Ribasim.set_static_value!Method.

                    Load data from a source table static into a destination table. Data is matched based on the node_id, which is sorted.

                    -

                    source

                    +

                    source

                    # Ribasim.set_table_row!Method.

                    Update table at row index i, with the values of a given row. table must be a NamedTuple of vectors with all variables that must be loaded. The row must contain all the column names that are present in the table. If a value is NaN, it is not set.

                    -

                    source

                    +

                    source

                    # Ribasim.sorted_table!Method.

                    Depending on if a table can be sorted, either sort it or assert that it is sorted.

                    Tables loaded from the database into memory can be sorted. Tables loaded from Arrow files are memory mapped and can therefore not be sorted.

                    -

                    source

                    +

                    source

                    # Ribasim.timestepsMethod.

                    Get all saved times in seconds since start

                    -

                    source

                    +

                    source

                    +

                    # Ribasim.update_allocation!Method.

                    +

                    Solve the allocation problem for all users and assign allocated abstractions to user nodes.

                    +

                    source

                    # Ribasim.update_basinMethod.

                    Load updates from ‘Basin / time’ into the parameters

                    -

                    source

                    +

                    source

                    # Ribasim.update_jac_prototype!Method.

                    Method for nodes that do not contribute to the Jacobian

                    -

                    source

                    +

                    source

                    # Ribasim.update_jac_prototype!Method.

                    The controlled basin affects itself and the basins upstream and downstream of the controlled pump affect eachother if there is a basin upstream of the pump. The state for the integral term and the controlled basin affect eachother, and the same for the integral state and the basin upstream of the pump if it is indeed a basin.

                    -

                    source

                    +

                    source

                    # Ribasim.update_jac_prototype!Method.

                    If both the unique node upstream and the unique node downstream of these nodes are basins, then these directly depend on eachother and affect the Jacobian 2x Basins always depend on themselves.

                    -

                    source

                    +

                    source

                    # Ribasim.update_jac_prototype!Method.

                    If both the unique node upstream and the nodes down stream (or one node further if a fractional flow is in between) are basins, then the downstream basin depends on the upstream basin(s) and affect the Jacobian as many times as there are downstream basins Upstream basins always depend on themselves.

                    -

                    source

                    +

                    source

                    # Ribasim.update_tabulated_rating_curve!Method.

                    Load updates from ‘TabulatedRatingCurve / time’ into the parameters

                    -

                    source

                    +

                    source

                    # Ribasim.valid_discrete_controlMethod.

                    Check:

                      @@ -741,41 +744,41 @@

                      source

                      +

                      source

                      # Ribasim.valid_edge_typesMethod.

                      Check that only supported edge types are declared.

                      -

                      source

                      +

                      source

                      # Ribasim.valid_edgesMethod.

                      Test for each node given its node type whether the nodes that

                      are downstream (‘down-edge’) of this node are of an allowed type

                      -

                      source

                      +

                      source

                      # Ribasim.valid_flow_ratesMethod.

                      Test whether static or discrete controlled flow rates are indeed non-negative.

                      -

                      source

                      +

                      source

                      # Ribasim.valid_fractional_flowMethod.

                      Check that nodes that have fractional flow outneighbors do not have any other type of outneighbor, that the fractions leaving a node add up to ≈1 and that the fractions are non-negative.

                      -

                      source

                      +

                      source

                      # Ribasim.valid_n_neighborsMethod.

                      Test for each node given its node type whether it has an allowed number of flow/control inneighbors and outneighbors

                      -

                      source

                      +

                      source

                      # Ribasim.valid_profilesMethod.

                      Check whether the profile data has no repeats in the levels and the areas start positive.

                      -

                      source

                      +

                      source

                      # Ribasim.valid_sourcesMethod.

                      The source nodes must only have one outneighbor.

                      -

                      source

                      +

                      source

                      # Ribasim.water_balance!Method.

                      The right hand side function of the system of ODEs set up by Ribasim.

                      -

                      source

                      +

                      source

                      # Ribasim.write_arrowMethod.

                      Write a result table to disk as an Arrow file

                      -

                      source

                      +

                      source

                      # Ribasim.config.algorithmMethod.

                      Create an OrdinaryDiffEqAlgorithm from solver config

                      -

                      source

                      +

                      source

                      # Ribasim.config.snake_caseMethod.

                      Convert a string from CamelCase to snake_case.

                      -

                      source

                      +

                      source

                      @@ -796,7 +799,7 @@

                      source

                      +

                      source

                      @@ -804,10 +807,10 @@

                      1.5 Macros

                      # Ribasim.config.@addfieldsMacro.

                      Add fieldnames with Union{String, Nothing} type to struct expression. Requires (option?) use before it.

                      -

                      source

                      +

                      source

                      # Ribasim.config.@addnodetypesMacro.

                      Add all TableOption subtypes as fields to struct expression. Requires (option?) use before it.

                      -

                      source

                      +

                      source

                      @@ -837,8 +840,8 @@

                      Ribasim.User
                    • Ribasim.config.Config
                    • BasicModelInterface.finalize
                    • -
                    • BasicModelInterface.initialize
                    • BasicModelInterface.initialize
                    • +
                    • BasicModelInterface.initialize
                    • CommonSolve.solve!
                    • Ribasim.add_constraints_basin_allocation!
                    • Ribasim.add_constraints_capacity!
                    • @@ -860,7 +863,7 @@

                      Ribasim.basin_table
                    • Ribasim.config.algorithm
                    • Ribasim.config.snake_case
                    • -
                    • Ribasim.create_callbacks
                    • +
                    • Ribasim.create_callbacks
                    • Ribasim.create_graph
                    • Ribasim.create_storage_tables
                    • Ribasim.datetime_since
                    • @@ -876,9 +879,9 @@

                      Ribasim.findsorted
                    • Ribasim.flow_table
                    • Ribasim.formulate_basins!
                    • -
                    • Ribasim.formulate_flow!
                    • -
                    • Ribasim.formulate_flow!
                    • Ribasim.formulate_flow!
                    • +
                    • Ribasim.formulate_flow!
                    • +
                    • Ribasim.formulate_flow!
                    • Ribasim.get_area_and_level
                    • Ribasim.get_compressor
                    • Ribasim.get_fractional_flow_connected_basins
                    • @@ -917,11 +920,12 @@

                      Ribasim.set_table_row!
                    • Ribasim.sorted_table!
                    • Ribasim.timesteps
                    • +
                    • Ribasim.update_allocation!
                    • Ribasim.update_basin
                    • +
                    • Ribasim.update_jac_prototype!
                    • Ribasim.update_jac_prototype!
                    • Ribasim.update_jac_prototype!
                    • Ribasim.update_jac_prototype!
                    • -
                    • Ribasim.update_jac_prototype!
                    • Ribasim.update_tabulated_rating_curve!
                    • Ribasim.valid_discrete_control
                    • Ribasim.valid_edge_types
                    • diff --git a/core/usage.html b/core/usage.html index 69c78169d..e0e637677 100644 --- a/core/usage.html +++ b/core/usage.html @@ -240,6 +240,7 @@

                      On this page

                    • 1 Configuration file
                    • 2 GeoPackage database and Arrow tables
                    • 3 Node
                    • @@ -319,44 +320,45 @@

                      1 Configuration f starttime = 2019-01-01 # required endtime = 2021-01-01 # required -# all timesteps are in seconds -update_timestep = 86400.0 # optional, default 1 day +# input files +database = "database.gpkg" # required -# input files -database = "database.gpkg" # required - -[results] -# These results files are always written -flow = "results/flow.arrow" # optional, default "results/flow.arrow" -basin = "results/basin.arrow" # optional, default "results/basin.arrow" -control = "results/control.arrow" # optional, default "results/control.arrow" -compression = "zstd" # optional, default "zstd", also supports "lz4" -compression_level = 6 # optional, default 6 - -# Specific tables can also go into Arrow files rather than the database. -# For large tables this can benefit from better compressed file sizes. -# This is optional, tables are retrieved from the database if not specified in the TOML. -[basin] -time = "basin/time.arrow" - -[solver] -algorithm = "QNDF" # optional, default "QNDF" -saveat = [] # optional, default [], which saves every timestep -adaptive = true # optional, default true -dt = 0.0 # optional when adaptive = true, default automatically determined -dtmin = 0.0 # optional, default automatically determined -dtmax = 0.0 # optional, default length of simulation -force_dtmin = false # optional, default false -abstol = 1e-6 # optional, default 1e-6 -reltol = 1e-3 # optional, default 1e-3 -maxiters = 1e9 # optional, default 1e9 -sparse = true # optional, default true -autodiff = true # optional, default true - -[logging] -# defines the logging level of Ribasim -verbosity = "info" # optional, default "info", can otherwise be "debug", "warn" or "error" -timing = false # optional, whether to log debug timing statements +# Specific tables can also go into Arrow files rather than the database. +# For large tables this can benefit from better compressed file sizes. +# This is optional, tables are retrieved from the database if not specified in the TOML. +[basin] +time = "basin/time.arrow" + +[allocation] +timestep = 86400 +use_allocation = true + +[solver] +algorithm = "QNDF" # optional, default "QNDF" +saveat = [] # optional, default [], which saves every timestep +adaptive = true # optional, default true +dt = 0.0 # optional when adaptive = true, default automatically determined +dtmin = 0.0 # optional, default automatically determined +dtmax = 0.0 # optional, default length of simulation +force_dtmin = false # optional, default false +abstol = 1e-6 # optional, default 1e-6 +reltol = 1e-3 # optional, default 1e-3 +maxiters = 1e9 # optional, default 1e9 +sparse = true # optional, default true +autodiff = true # optional, default true + +[logging] +# defines the logging level of Ribasim +verbosity = "info" # optional, default "info", can otherwise be "debug", "warn" or "error" +timing = false # optional, whether to log debug timing statements + +[results] +# These results files are always written +flow = "results/flow.arrow" # optional, default "results/flow.arrow" +basin = "results/basin.arrow" # optional, default "results/basin.arrow" +control = "results/control.arrow" # optional, default "results/control.arrow" +compression = "zstd" # optional, default "zstd", also supports "lz4" +compression_level = 6 # optional, default 6

                      1.1 Solver settings

                      The solver section in the configuration file is entirely optional, since we aim to use defaults that will generally work well. Common reasons to modify the solver settings are to adjust the calculation or result stepsizes: adaptive, dt, and saveat. If your model does not converge, or your performance is lower than expected, it can help to adjust other solver settings as well.

                      @@ -368,6 +370,10 @@

                      The total maximum number of iterations maxiters = 1e9, can normally stay as-is unless doing extremely long simulations.

                      The absolute and relative tolerance for adaptive timestepping can be set with abstol and reltol. For more information on these and other solver options, see the DifferentialEquations.jl docs.

                      +
                      +

                      1.2 Allocation settings

                      +

                      Currently there are the following allocation settings: - use_allocation: A boolean which says whether allocation should be used or not; - timestep: a float value in seconds which dictates the update interval for allocations.

                      +

                      2 GeoPackage database and Arrow tables

                      @@ -407,6 +413,11 @@

                      3 Node

                      Node is a table that specifies the ID and type of each node of a model. The ID must be unique among all nodes, and the type must be one of the available node types listed below.

                      Nodes are components that are connected together to form a larger system. The Basin is a central node type that stores water. The other node types influence the flow between Basins in some way. Counter intuitively, even systems you may think of as edges, such as a canal, are nodes in Ribasim. This is because edges only define direct instantaneous couplings between nodes, and never have storage of their own.

                      +++++ @@ -430,6 +441,16 @@

                      3 Node

                      + + + + + + + + + +
                      column geoarrow (optional)
                      nameString(optional, does not have to be unique)
                      allocation_network_idInt(optional)

                      The available node types as of this writing are listed as the top level bullets below. The sub-bullets indicate which tables are associated to the node type. The table name is the name it must have in the database if it is stored there.

                      @@ -506,6 +527,11 @@

                      4 Edge

                    • “control”: The control edges define which nodes are controlled by a particular control node. Control edges should always point away from the control node. The edges between the control node and the nodes it listens to are not present in Edge \ static, these are defined in Control / condition
                    • +++++ @@ -539,6 +565,16 @@

                      4 Edge

                      + + + + + + + + + +
                      column geometry (optional)
                      nameString(optional, does not have to be unique)
                      allocation_network_idInt(optional, denotes source in allocation network)

                      Similarly to the node table, you can use a geometry to visualize the connections between the nodes in QGIS. For instance, you can draw a line connecting the two node coordinates.

                      diff --git a/python/examples.html b/python/examples.html index b58373d52..31f8241a4 100644 --- a/python/examples.html +++ b/python/examples.html @@ -545,7 +545,7 @@

                      2 Update the basi ax = df_flow.pivot_table(index="time", columns="edge", values="flow_m3d").plot() ax.legend(bbox_to_anchor=(1.3, 1), title="Edge")
                      -
                      <matplotlib.legend.Legend at 0x7f68fc3b1490>
                      +
                      <matplotlib.legend.Legend at 0x7f79a7c98790>

                      diff --git a/python/reference/Model.html b/python/reference/Model.html index 56525f61f..1afaf2207 100644 --- a/python/reference/Model.html +++ b/python/reference/Model.html @@ -277,12 +277,18 @@

                      +allocation +Optional[Allocation] +Allocation settings. +required + + solver Optional[Solver] Solver settings. required - + logging Optional[logging] Logging settings. diff --git a/schema/Allocation.schema.json b/schema/Allocation.schema.json new file mode 100644 index 000000000..d81c4423d --- /dev/null +++ b/schema/Allocation.schema.json @@ -0,0 +1,29 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://deltares.github.io/Ribasim/schema/Allocation.schema.json", + "title": "Allocation", + "description": "A Allocation object based on Ribasim.config.Allocation", + "type": "object", + "properties": { + "timestep": { + "format": "default", + "anyOf": [ + { + "type": "null" + }, + { + "type": "number" + } + ], + "default": null + }, + "use_allocation": { + "format": "default", + "type": "boolean", + "default": false + } + }, + "required": [ + "use_allocation" + ] +} diff --git a/schema/Config.schema.json b/schema/Config.schema.json index 58dea9967..446f9c99a 100644 --- a/schema/Config.schema.json +++ b/schema/Config.schema.json @@ -13,11 +13,6 @@ "format": "date-time", "type": "string" }, - "update_timestep": { - "format": "double", - "type": "number", - "default": 86400 - }, "relative_dir": { "format": "default", "type": "string", @@ -37,15 +32,11 @@ "format": "default", "type": "string" }, - "results": { - "$ref": "https://deltares.github.io/Ribasim/schema/Results.schema.json", + "allocation": { + "$ref": "https://deltares.github.io/Ribasim/schema/Allocation.schema.json", "default": { - "basin": "results/basin.arrow", - "flow": "results/flow.arrow", - "control": "results/control.arrow", - "outstate": null, - "compression": "zstd", - "compression_level": 6 + "timestep": null, + "use_allocation": false } }, "solver": { @@ -75,6 +66,17 @@ "timing": false } }, + "results": { + "$ref": "https://deltares.github.io/Ribasim/schema/Results.schema.json", + "default": { + "basin": "results/basin.arrow", + "flow": "results/flow.arrow", + "control": "results/control.arrow", + "outstate": null, + "compression": "zstd", + "compression_level": 6 + } + }, "terminal": { "$ref": "https://deltares.github.io/Ribasim/schema/terminal.schema.json", "default": { @@ -166,14 +168,14 @@ "required": [ "starttime", "endtime", - "update_timestep", "relative_dir", "input_dir", "results_dir", "database", - "results", + "allocation", "solver", "logging", + "results", "terminal", "pid_control", "level_boundary", diff --git a/schema/Edge.schema.json b/schema/Edge.schema.json index 7cea96291..96f434e7e 100644 --- a/schema/Edge.schema.json +++ b/schema/Edge.schema.json @@ -25,6 +25,17 @@ "format": "default", "type": "string" }, + "allocation_network_id": { + "format": "default", + "anyOf": [ + { + "type": "null" + }, + { + "type": "integer" + } + ] + }, "remarks": { "description": "a hack for pandera", "type": "string", diff --git a/schema/Node.schema.json b/schema/Node.schema.json index 18a7dc643..bf3d3d1b8 100644 --- a/schema/Node.schema.json +++ b/schema/Node.schema.json @@ -17,6 +17,17 @@ "format": "default", "type": "string" }, + "allocation_network_id": { + "format": "default", + "anyOf": [ + { + "type": "null" + }, + { + "type": "integer" + } + ] + }, "remarks": { "description": "a hack for pandera", "type": "string", diff --git a/search.json b/search.json index 63dd647c3..d3bfb4e49 100644 --- a/search.json +++ b/search.json @@ -158,7 +158,7 @@ "href": "build/index.html#functions", "title": "1 API Reference", "section": "1.3 Functions", - "text": "1.3 Functions\n# BasicModelInterface.finalize — Method.\nBMI.finalize(model::Model)::Model\nWrite all results to the configured files.\nsource\n# BasicModelInterface.initialize — Method.\nBMI.initialize(T::Type{Model}, config_path::AbstractString)::Model\nInitialize a Model from the path to the TOML configuration file.\nsource\n# BasicModelInterface.initialize — Method.\nBMI.initialize(T::Type{Model}, config::Config)::Model\nInitialize a Model from a Config.\nsource\n# CommonSolve.solve! — Method.\nsolve!(model::Model)::ODESolution\nSolve a Model until the configured endtime.\nsource\n# Ribasim.add_constraints_basin_allocation! — Method.\nAdd the basin allocation constraints to the allocation problem; the allocations to the basins are bounded from above by the basin demand (these are set before each allocation solve). The constraint indices are allocation graph basin node IDs.\nConstraint: allocation to basin <= basin demand\nsource\n# Ribasim.add_constraints_capacity! — Method.\nAdd the flow capacity constraints to the allocation problem. Only finite capacities get a constraint. The constraint indices are the allocation graph edge IDs.\nConstraint: flow over edge <= edge capacity\nsource\n# Ribasim.add_constraints_flow_conservation! — Method.\nAdd the flow conservation constraints to the allocation problem. The constraint indices are allocgraph user node IDs.\nConstraint: sum(flows out of node node) <= flows into node + flow from storage and vertical fluxes\nsource\n# Ribasim.add_constraints_source! — Method.\nAdd the source constraints to the allocation problem. The actual threshold values will be set before each allocation solve. The constraint indices are the allocation graph source node IDs.\nConstraint: flow over source edge <= source flow in subnetwork\nsource\n# Ribasim.add_constraints_user_allocation! — Method.\nAdd the user allocation constraints to the allocation problem:\n\nThe sum of the allocations to a user is equal to the flow to that user;\nThe allocations to the users are non-negative;\nThe allocations to the users are bounded from above by the user demands (these are set before each allocation solve).\n\nThe demand constrains have name demanduser{i} where the i are the allocation graph user node IDs and the constraint indices are the priorities.\nConstraints: sum(allocations to user of all priorities) = flow to user allocation to user at priority >= 0 allocation to user at priority <= demand from user at priority\nsource\n# Ribasim.add_constraints_user_returnflow! — Method.\nAdd the user returnflow constraints to the allocation problem. The constraint indices are allocation graph user node IDs.\nConstraint: outflow from user = return factor * inflow to user\nsource\n# Ribasim.add_objective_function! — Method.\nAdd the objective function to the allocation problem. Objective function: linear combination of allocations to the basins and users, where basin allocations get a weight of 1.0 and user allocations get a weight of 2^(-priority index).\nsource\n# Ribasim.add_variables_allocation_basin! — Method.\nAdd the basin allocation variables A_basin to the allocation problem. The variable indices are the allocation graph basin node IDs. Non-negativivity constraints are also immediately added to the basin allocation variables.\nsource\n# Ribasim.add_variables_allocation_user! — Method.\nAdd the user allocation variables Auser{i} to the allocation problem. The variable name indices i are the allocation graph user node IDs, The variable indices are the priorities.\nsource\n# Ribasim.add_variables_flow! — Method.\nAdd the flow variables F to the allocation problem. The variable indices are the allocation graph edge IDs. Non-negativivity constraints are also immediately added to the flow variables.\nsource\n# Ribasim.allocate! — Method.\nUpdate the allocation optimization problem for the given subnetwork with the problem state and flows, solve the allocation problem and assign the results to the users.\nsource\n# Ribasim.allocation_graph — Method.\nBuild the graph used for the allocation problem.\nsource\n# Ribasim.allocation_problem — Method.\nConstruct the allocation problem for the current subnetwork as a JuMP.jl model.\nsource\n# Ribasim.assign_allocations! — Method.\nAssign the allocations to the users as determined by the solution of the allocation problem.\nsource\n# Ribasim.avoid_using_own_returnflow! — Method.\nRemove user return flow edges that are upstream of the user itself, and collect the IDs of the allocation graph node IDs of the users that do not have this problem.\nsource\n# Ribasim.basin_bottom — Method.\nReturn the bottom elevation of the basin with index i, or nothing if it doesn’t exist\nsource\n# Ribasim.basin_bottoms — Method.\nGet the bottom on both ends of a node. If only one has a bottom, use that for both.\nsource\n# Ribasim.basin_table — Method.\nCreate the basin result table from the saved data\nsource\n# Ribasim.create_callbacks — Method.\nCreate the different callbacks that are used to store results and feed the simulation with new data. The different callbacks are combined to a CallbackSet that goes to the integrator. Returns the CallbackSet and the SavedValues for flow.\nsource\n# Ribasim.create_graph — Method.\nReturn a directed graph, and a mapping from source and target nodes to edge fid.\nsource\n# Ribasim.create_storage_tables — Method.\nRead the Basin / profile table and return all area and level and computed storage values\nsource\n# Ribasim.datetime_since — Method.\ndatetime_since(t::Real, t0::DateTime)::DateTime\nConvert a Real that represents the seconds passed since the simulation start to the nearest DateTime. This is used to convert between the solver’s inner float time, and the calendar.\nsource\n# Ribasim.datetimes — Method.\nGet all saved times as a Vector{DateTime}\nsource\n# Ribasim.discrete_control_affect! — Method.\nChange parameters based on the control logic.\nsource\n# Ribasim.discrete_control_affect_downcrossing! — Method.\nAn downcrossing means that a condition (always greater than) becomes false.\nsource\n# Ribasim.discrete_control_affect_upcrossing! — Method.\nAn upcrossing means that a condition (always greater than) becomes true.\nsource\n# Ribasim.discrete_control_condition — Method.\nListens for changes in condition truths.\nsource\n# Ribasim.discrete_control_table — Method.\nCreate a discrete control result table from the saved data\nsource\n# Ribasim.expand_logic_mapping — Method.\nReplace the truth states in the logic mapping which contain wildcards with all possible explicit truth states.\nsource\n# Ribasim.find_allocation_graph_edges! — Method.\nThis loop finds allocgraph edges in several ways:\n\nBetween allocgraph nodes whose equivalent in the subnetwork are directly connected\nBetween allocgraph nodes whose equivalent in the subnetwork are connected with one or more non-junction nodes in between\n\nHere edges are added to the allocation graph that are given by a single edge in the subnetwork.\nsource\n# Ribasim.findlastgroup — Method.\nFor an element id and a vector of elements ids, get the range of indices of the last consecutive block of id. Returns the empty range 1:0 if id is not in ids.\n# 1 2 3 4 5 6 7 8 9\nRibasim.findlastgroup(2, [5,4,2,2,5,2,2,2,1])\n# output\n6:8\nsource\n# Ribasim.findsorted — Method.\nFind the index of element x in a sorted collection a. Returns the index of x if it exists, or nothing if it doesn’t. If x occurs more than once, throw an error.\nsource\n# Ribasim.flow_table — Method.\nCreate a flow result table from the saved data\nsource\n# Ribasim.formulate_basins! — Method.\nSmoothly let the evaporation flux go to 0 when at small water depths Currently at less than 0.1 m.\nsource\n# Ribasim.formulate_flow! — Method.\nDirected graph: outflow is positive!\nsource\n# Ribasim.formulate_flow! — Method.\nConservation of energy for two basins, a and b:\nh_a + v_a^2 / (2 * g) = h_b + v_b^2 / (2 * g) + S_f * L + C / 2 * g * (v_b^2 - v_a^2)\nWhere:\n\nha, hb are the heads at basin a and b.\nva, vb are the velocities at basin a and b.\ng is the gravitational constant.\nS_f is the friction slope.\nC is an expansion or extraction coefficient.\n\nWe assume velocity differences are negligible (va = vb):\nh_a = h_b + S_f * L\nThe friction losses are approximated by the Gauckler-Manning formula:\nQ = A * (1 / n) * R_h^(2/3) * S_f^(1/2)\nWhere:\n\nWhere A is the cross-sectional area.\nV is the cross-sectional average velocity.\nn is the Gauckler-Manning coefficient.\nR_h is the hydraulic radius.\nS_f is the friction slope.\n\nThe hydraulic radius is defined as:\nR_h = A / P\nWhere P is the wetted perimeter.\nThe average of the upstream and downstream water depth is used to compute cross-sectional area and hydraulic radius. This ensures that a basin can receive water after it has gone dry.\nsource\n# Ribasim.formulate_flow! — Method.\nDirected graph: outflow is positive!\nsource\n# Ribasim.get_area_and_level — Method.\nCompute the area and level of a basin given its storage. Also returns darea/dlevel as it is needed for the Jacobian.\nsource\n# Ribasim.get_compressor — Method.\nGet the compressor based on the Results section\nsource\n# Ribasim.get_fractional_flow_connected_basins — Method.\nGet the node type specific indices of the fractional flows and basins, that are consecutively connected to a node of given id.\nsource\n# Ribasim.get_jac_prototype — Method.\nGet a sparse matrix whose sparsity matches the sparsity of the Jacobian of the ODE problem. All nodes are taken into consideration, also the ones that are inactive.\nIn Ribasim the Jacobian is typically sparse because each state only depends on a small number of other states.\nNote: the name ‘prototype’ does not mean this code is a prototype, it comes from the naming convention of this sparsity structure in the differentialequations.jl docs.\nsource\n# Ribasim.get_level — Method.\nGet the current water level of a node ID. The ID can belong to either a Basin or a LevelBoundary. storage: tells ForwardDiff whether this call is for differentiation or not\nsource\n# Ribasim.get_node_id_mapping — Method.\nGet:\n\nThe mapping from subnetwork node IDs to allocation graph node IDs\nThe mapping from allocation graph source node IDs to subnetwork source edge IDs\n\nsource\n# Ribasim.get_node_in_out_edges — Method.\nGet two dictionaries, where:\n\nThe first one gives the IDs of the inedges for each node ID in the graph\nThe second one gives the IDs of the outedges for each node ID in the graph\n\nsource\n# Ribasim.get_scalar_interpolation — Method.\nLinear interpolation of a scalar with constant extrapolation.\nsource\n# Ribasim.get_storage_from_level — Method.\nGet the storage of a basin from its level.\nsource\n# Ribasim.get_storages_and_levels — Method.\nGet the storage and level of all basins as matrices of nbasin × ntime\nsource\n# Ribasim.get_storages_from_levels — Method.\nCompute the storages of the basins based on the water level of the basins.\nsource\n# Ribasim.get_tstops — Method.\nFrom an iterable of DateTimes, find the times the solver needs to stop\nsource\n# Ribasim.get_value — Method.\nGet a value for a condition. Currently supports getting levels from basins and flows from flow boundaries.\nsource\n# Ribasim.id_index — Method.\nGet the index of an ID in a set of indices.\nsource\n# Ribasim.input_path — Method.\nConstruct a path relative to both the TOML directory and the optional input_dir\nsource\n# Ribasim.is_flow_constraining — Method.\nWhether the given node node is flow constraining by having a maximum flow rate.\nsource\n# Ribasim.is_flow_direction_constraining — Method.\nWhether the given node is flow direction constraining (only in direction of edges).\nsource\n# Ribasim.load_data — Method.\nload_data(db::DB, config::Config, nodetype::Symbol, kind::Symbol)::Union{Table, Query, Nothing}\nLoad data from Arrow files if available, otherwise the database. Returns either an Arrow.Table, SQLite.Query or nothing if the data is not present.\nsource\n# Ribasim.load_structvector — Method.\nload_structvector(db::DB, config::Config, ::Type{T})::StructVector{T}\nLoad data from Arrow files if available, otherwise the database. Always returns a StructVector of the given struct type T, which is empty if the table is not found. This function validates the schema, and enforces the required sort order.\nsource\n# Ribasim.nodefields — Method.\nGet all node fieldnames of the parameter object.\nsource\n# Ribasim.nodetype — Method.\nFrom a SchemaVersion(“ribasim.flowboundary.static”, 1) return (:FlowBoundary, :static)\nsource\n# Ribasim.parse_static_and_time — Method.\nProcess the data in the static and time tables for a given node type. The ‘defaults’ named tuple dictates how missing data is filled in. ‘time_interpolatables’ is a vector of Symbols of parameter names for which a time interpolation (linear) object must be constructed. The control mapping for DiscreteControl is also constructed in this function. This function currently does not support node states that are defined by more than one row in a table, as is the case for TabulatedRatingCurve.\nsource\n# Ribasim.path_exists_in_graph — Method.\nFind out whether a path exists between a start node and end node in the given graph.\nsource\n# Ribasim.process_allocation_graph_edges! — Method.\nFor the composite allocgraph edges:\n\nFind out whether they are connected to allocgraph nodes on both ends\nCompute their capacity\nFind out their allowed flow direction(s)\n\nsource\n# Ribasim.profile_storage — Method.\nCalculate a profile storage by integrating the areas over the levels\nsource\n# Ribasim.qh_interpolation — Method.\nFrom a table with columns nodeid, discharge (Q) and level (h), create a LinearInterpolation from level to discharge for a given nodeid.\nsource\n# Ribasim.reduction_factor — Method.\nFunction that goes smoothly from 0 to 1 in the interval [0,threshold], and is constant outside this interval.\nsource\n# Ribasim.results_path — Method.\nConstruct a path relative to both the TOML directory and the optional results_dir\nsource\n# Ribasim.run — Method.\nrun(config_file::AbstractString)::Model\nrun(config::Config)::Model\nRun a Model, given a path to a TOML configuration file, or a Config object. Running a model includes initialization, solving to the end with [solve!](@ref) and writing results with BMI.finalize.\nsource\n# Ribasim.save_flow — Method.\nCopy the current flow to the SavedValues\nsource\n# Ribasim.scalar_interpolation_derivative — Method.\nDerivative of scalar interpolation.\nsource\n# Ribasim.seconds_since — Method.\nseconds_since(t::DateTime, t0::DateTime)::Float64\nConvert a DateTime to a float that is the number of seconds since the start of the simulation. This is used to convert between the solver’s inner float time, and the calendar.\nsource\n# Ribasim.set_current_value! — Method.\nFrom a timeseries table time, load the most recent applicable data into table. table must be a NamedTuple of vectors with all variables that must be loaded. The most recent applicable data is non-NaN data for a given ID that is on or before t.\nsource\n# Ribasim.set_model_state_in_allocation! — Method.\nUpdate the allocation problem with model data at the current:\n\nDemands of the users\nFlows of the source edges\nDemands of the basins\n\nsource\n# Ribasim.set_static_value! — Method.\nLoad data from a source table static into a destination table. Data is matched based on the node_id, which is sorted.\nsource\n# Ribasim.set_table_row! — Method.\nUpdate table at row index i, with the values of a given row. table must be a NamedTuple of vectors with all variables that must be loaded. The row must contain all the column names that are present in the table. If a value is NaN, it is not set.\nsource\n# Ribasim.sorted_table! — Method.\nDepending on if a table can be sorted, either sort it or assert that it is sorted.\nTables loaded from the database into memory can be sorted. Tables loaded from Arrow files are memory mapped and can therefore not be sorted.\nsource\n# Ribasim.timesteps — Method.\nGet all saved times in seconds since start\nsource\n# Ribasim.update_basin — Method.\nLoad updates from ‘Basin / time’ into the parameters\nsource\n# Ribasim.update_jac_prototype! — Method.\nMethod for nodes that do not contribute to the Jacobian\nsource\n# Ribasim.update_jac_prototype! — Method.\nThe controlled basin affects itself and the basins upstream and downstream of the controlled pump affect eachother if there is a basin upstream of the pump. The state for the integral term and the controlled basin affect eachother, and the same for the integral state and the basin upstream of the pump if it is indeed a basin.\nsource\n# Ribasim.update_jac_prototype! — Method.\nIf both the unique node upstream and the unique node downstream of these nodes are basins, then these directly depend on eachother and affect the Jacobian 2x Basins always depend on themselves.\nsource\n# Ribasim.update_jac_prototype! — Method.\nIf both the unique node upstream and the nodes down stream (or one node further if a fractional flow is in between) are basins, then the downstream basin depends on the upstream basin(s) and affect the Jacobian as many times as there are downstream basins Upstream basins always depend on themselves.\nsource\n# Ribasim.update_tabulated_rating_curve! — Method.\nLoad updates from ‘TabulatedRatingCurve / time’ into the parameters\nsource\n# Ribasim.valid_discrete_control — Method.\nCheck:\n\nwhether control states are defined for discrete controlled nodes;\nWhether the supplied truth states have the proper length;\nWhether look_ahead is only supplied for condition variables given by a time-series.\n\nsource\n# Ribasim.valid_edge_types — Method.\nCheck that only supported edge types are declared.\nsource\n# Ribasim.valid_edges — Method.\nTest for each node given its node type whether the nodes that\nare downstream (‘down-edge’) of this node are of an allowed type\nsource\n# Ribasim.valid_flow_rates — Method.\nTest whether static or discrete controlled flow rates are indeed non-negative.\nsource\n# Ribasim.valid_fractional_flow — Method.\nCheck that nodes that have fractional flow outneighbors do not have any other type of outneighbor, that the fractions leaving a node add up to ≈1 and that the fractions are non-negative.\nsource\n# Ribasim.valid_n_neighbors — Method.\nTest for each node given its node type whether it has an allowed number of flow/control inneighbors and outneighbors\nsource\n# Ribasim.valid_profiles — Method.\nCheck whether the profile data has no repeats in the levels and the areas start positive.\nsource\n# Ribasim.valid_sources — Method.\nThe source nodes must only have one outneighbor.\nsource\n# Ribasim.water_balance! — Method.\nThe right hand side function of the system of ODEs set up by Ribasim.\nsource\n# Ribasim.write_arrow — Method.\nWrite a result table to disk as an Arrow file\nsource\n# Ribasim.config.algorithm — Method.\nCreate an OrdinaryDiffEqAlgorithm from solver config\nsource\n# Ribasim.config.snake_case — Method.\nConvert a string from CamelCase to snake_case.\nsource" + "text": "1.3 Functions\n# BasicModelInterface.finalize — Method.\nBMI.finalize(model::Model)::Model\nWrite all results to the configured files.\nsource\n# BasicModelInterface.initialize — Method.\nBMI.initialize(T::Type{Model}, config_path::AbstractString)::Model\nInitialize a Model from the path to the TOML configuration file.\nsource\n# BasicModelInterface.initialize — Method.\nBMI.initialize(T::Type{Model}, config::Config)::Model\nInitialize a Model from a Config.\nsource\n# CommonSolve.solve! — Method.\nsolve!(model::Model)::ODESolution\nSolve a Model until the configured endtime.\nsource\n# Ribasim.add_constraints_basin_allocation! — Method.\nAdd the basin allocation constraints to the allocation problem; the allocations to the basins are bounded from above by the basin demand (these are set before each allocation solve). The constraint indices are allocation graph basin node IDs.\nConstraint: allocation to basin <= basin demand\nsource\n# Ribasim.add_constraints_capacity! — Method.\nAdd the flow capacity constraints to the allocation problem. Only finite capacities get a constraint. The constraint indices are the allocation graph edge IDs.\nConstraint: flow over edge <= edge capacity\nsource\n# Ribasim.add_constraints_flow_conservation! — Method.\nAdd the flow conservation constraints to the allocation problem. The constraint indices are allocgraph user node IDs.\nConstraint: sum(flows out of node node) <= flows into node + flow from storage and vertical fluxes\nsource\n# Ribasim.add_constraints_source! — Method.\nAdd the source constraints to the allocation problem. The actual threshold values will be set before each allocation solve. The constraint indices are the allocation graph source node IDs.\nConstraint: flow over source edge <= source flow in subnetwork\nsource\n# Ribasim.add_constraints_user_allocation! — Method.\nAdd the user allocation constraints to the allocation problem:\n\nThe sum of the allocations to a user is equal to the flow to that user;\nThe allocations to the users are non-negative;\nThe allocations to the users are bounded from above by the user demands (these are set before each allocation solve).\n\nThe demand constrains have name demanduser{i} where the i are the allocation graph user node IDs and the constraint indices are the priorities.\nConstraints: sum(allocations to user of all priorities) = flow to user allocation to user at priority >= 0 allocation to user at priority <= demand from user at priority\nsource\n# Ribasim.add_constraints_user_returnflow! — Method.\nAdd the user returnflow constraints to the allocation problem. The constraint indices are allocation graph user node IDs.\nConstraint: outflow from user = return factor * inflow to user\nsource\n# Ribasim.add_objective_function! — Method.\nAdd the objective function to the allocation problem. Objective function: linear combination of allocations to the basins and users, where basin allocations get a weight of 1.0 and user allocations get a weight of 2^(-priority index).\nsource\n# Ribasim.add_variables_allocation_basin! — Method.\nAdd the basin allocation variables A_basin to the allocation problem. The variable indices are the allocation graph basin node IDs. Non-negativivity constraints are also immediately added to the basin allocation variables.\nsource\n# Ribasim.add_variables_allocation_user! — Method.\nAdd the user allocation variables Auser{i} to the allocation problem. The variable name indices i are the allocation graph user node IDs, The variable indices are the priorities.\nsource\n# Ribasim.add_variables_flow! — Method.\nAdd the flow variables F to the allocation problem. The variable indices are the allocation graph edge IDs. Non-negativivity constraints are also immediately added to the flow variables.\nsource\n# Ribasim.allocate! — Method.\nUpdate the allocation optimization problem for the given subnetwork with the problem state and flows, solve the allocation problem and assign the results to the users.\nsource\n# Ribasim.allocation_graph — Method.\nBuild the graph used for the allocation problem.\nsource\n# Ribasim.allocation_problem — Method.\nConstruct the allocation problem for the current subnetwork as a JuMP.jl model.\nsource\n# Ribasim.assign_allocations! — Method.\nAssign the allocations to the users as determined by the solution of the allocation problem.\nsource\n# Ribasim.avoid_using_own_returnflow! — Method.\nRemove user return flow edges that are upstream of the user itself, and collect the IDs of the allocation graph node IDs of the users that do not have this problem.\nsource\n# Ribasim.basin_bottom — Method.\nReturn the bottom elevation of the basin with index i, or nothing if it doesn’t exist\nsource\n# Ribasim.basin_bottoms — Method.\nGet the bottom on both ends of a node. If only one has a bottom, use that for both.\nsource\n# Ribasim.basin_table — Method.\nCreate the basin result table from the saved data\nsource\n# Ribasim.create_callbacks — Method.\nCreate the different callbacks that are used to store results and feed the simulation with new data. The different callbacks are combined to a CallbackSet that goes to the integrator. Returns the CallbackSet and the SavedValues for flow.\nsource\n# Ribasim.create_graph — Method.\nReturn a directed graph, and a mapping from source and target nodes to edge fid.\nsource\n# Ribasim.create_storage_tables — Method.\nRead the Basin / profile table and return all area and level and computed storage values\nsource\n# Ribasim.datetime_since — Method.\ndatetime_since(t::Real, t0::DateTime)::DateTime\nConvert a Real that represents the seconds passed since the simulation start to the nearest DateTime. This is used to convert between the solver’s inner float time, and the calendar.\nsource\n# Ribasim.datetimes — Method.\nGet all saved times as a Vector{DateTime}\nsource\n# Ribasim.discrete_control_affect! — Method.\nChange parameters based on the control logic.\nsource\n# Ribasim.discrete_control_affect_downcrossing! — Method.\nAn downcrossing means that a condition (always greater than) becomes false.\nsource\n# Ribasim.discrete_control_affect_upcrossing! — Method.\nAn upcrossing means that a condition (always greater than) becomes true.\nsource\n# Ribasim.discrete_control_condition — Method.\nListens for changes in condition truths.\nsource\n# Ribasim.discrete_control_table — Method.\nCreate a discrete control result table from the saved data\nsource\n# Ribasim.expand_logic_mapping — Method.\nReplace the truth states in the logic mapping which contain wildcards with all possible explicit truth states.\nsource\n# Ribasim.find_allocation_graph_edges! — Method.\nThis loop finds allocgraph edges in several ways:\n\nBetween allocgraph nodes whose equivalent in the subnetwork are directly connected\nBetween allocgraph nodes whose equivalent in the subnetwork are connected with one or more non-junction nodes in between\n\nHere edges are added to the allocation graph that are given by a single edge in the subnetwork.\nsource\n# Ribasim.findlastgroup — Method.\nFor an element id and a vector of elements ids, get the range of indices of the last consecutive block of id. Returns the empty range 1:0 if id is not in ids.\n# 1 2 3 4 5 6 7 8 9\nRibasim.findlastgroup(2, [5,4,2,2,5,2,2,2,1])\n# output\n6:8\nsource\n# Ribasim.findsorted — Method.\nFind the index of element x in a sorted collection a. Returns the index of x if it exists, or nothing if it doesn’t. If x occurs more than once, throw an error.\nsource\n# Ribasim.flow_table — Method.\nCreate a flow result table from the saved data\nsource\n# Ribasim.formulate_basins! — Method.\nSmoothly let the evaporation flux go to 0 when at small water depths Currently at less than 0.1 m.\nsource\n# Ribasim.formulate_flow! — Method.\nDirected graph: outflow is positive!\nsource\n# Ribasim.formulate_flow! — Method.\nConservation of energy for two basins, a and b:\nh_a + v_a^2 / (2 * g) = h_b + v_b^2 / (2 * g) + S_f * L + C / 2 * g * (v_b^2 - v_a^2)\nWhere:\n\nha, hb are the heads at basin a and b.\nva, vb are the velocities at basin a and b.\ng is the gravitational constant.\nS_f is the friction slope.\nC is an expansion or extraction coefficient.\n\nWe assume velocity differences are negligible (va = vb):\nh_a = h_b + S_f * L\nThe friction losses are approximated by the Gauckler-Manning formula:\nQ = A * (1 / n) * R_h^(2/3) * S_f^(1/2)\nWhere:\n\nWhere A is the cross-sectional area.\nV is the cross-sectional average velocity.\nn is the Gauckler-Manning coefficient.\nR_h is the hydraulic radius.\nS_f is the friction slope.\n\nThe hydraulic radius is defined as:\nR_h = A / P\nWhere P is the wetted perimeter.\nThe average of the upstream and downstream water depth is used to compute cross-sectional area and hydraulic radius. This ensures that a basin can receive water after it has gone dry.\nsource\n# Ribasim.formulate_flow! — Method.\nDirected graph: outflow is positive!\nsource\n# Ribasim.get_area_and_level — Method.\nCompute the area and level of a basin given its storage. Also returns darea/dlevel as it is needed for the Jacobian.\nsource\n# Ribasim.get_compressor — Method.\nGet the compressor based on the Results section\nsource\n# Ribasim.get_fractional_flow_connected_basins — Method.\nGet the node type specific indices of the fractional flows and basins, that are consecutively connected to a node of given id.\nsource\n# Ribasim.get_jac_prototype — Method.\nGet a sparse matrix whose sparsity matches the sparsity of the Jacobian of the ODE problem. All nodes are taken into consideration, also the ones that are inactive.\nIn Ribasim the Jacobian is typically sparse because each state only depends on a small number of other states.\nNote: the name ‘prototype’ does not mean this code is a prototype, it comes from the naming convention of this sparsity structure in the differentialequations.jl docs.\nsource\n# Ribasim.get_level — Method.\nGet the current water level of a node ID. The ID can belong to either a Basin or a LevelBoundary. storage: tells ForwardDiff whether this call is for differentiation or not\nsource\n# Ribasim.get_node_id_mapping — Method.\nGet:\n\nThe mapping from subnetwork node IDs to allocation graph node IDs\nThe mapping from allocation graph source node IDs to subnetwork source edge IDs\n\nsource\n# Ribasim.get_node_in_out_edges — Method.\nGet two dictionaries, where:\n\nThe first one gives the IDs of the inedges for each node ID in the graph\nThe second one gives the IDs of the outedges for each node ID in the graph\n\nsource\n# Ribasim.get_scalar_interpolation — Method.\nLinear interpolation of a scalar with constant extrapolation.\nsource\n# Ribasim.get_storage_from_level — Method.\nGet the storage of a basin from its level.\nsource\n# Ribasim.get_storages_and_levels — Method.\nGet the storage and level of all basins as matrices of nbasin × ntime\nsource\n# Ribasim.get_storages_from_levels — Method.\nCompute the storages of the basins based on the water level of the basins.\nsource\n# Ribasim.get_tstops — Method.\nFrom an iterable of DateTimes, find the times the solver needs to stop\nsource\n# Ribasim.get_value — Method.\nGet a value for a condition. Currently supports getting levels from basins and flows from flow boundaries.\nsource\n# Ribasim.id_index — Method.\nGet the index of an ID in a set of indices.\nsource\n# Ribasim.input_path — Method.\nConstruct a path relative to both the TOML directory and the optional input_dir\nsource\n# Ribasim.is_flow_constraining — Method.\nWhether the given node node is flow constraining by having a maximum flow rate.\nsource\n# Ribasim.is_flow_direction_constraining — Method.\nWhether the given node is flow direction constraining (only in direction of edges).\nsource\n# Ribasim.load_data — Method.\nload_data(db::DB, config::Config, nodetype::Symbol, kind::Symbol)::Union{Table, Query, Nothing}\nLoad data from Arrow files if available, otherwise the database. Returns either an Arrow.Table, SQLite.Query or nothing if the data is not present.\nsource\n# Ribasim.load_structvector — Method.\nload_structvector(db::DB, config::Config, ::Type{T})::StructVector{T}\nLoad data from Arrow files if available, otherwise the database. Always returns a StructVector of the given struct type T, which is empty if the table is not found. This function validates the schema, and enforces the required sort order.\nsource\n# Ribasim.nodefields — Method.\nGet all node fieldnames of the parameter object.\nsource\n# Ribasim.nodetype — Method.\nFrom a SchemaVersion(“ribasim.flowboundary.static”, 1) return (:FlowBoundary, :static)\nsource\n# Ribasim.parse_static_and_time — Method.\nProcess the data in the static and time tables for a given node type. The ‘defaults’ named tuple dictates how missing data is filled in. ‘time_interpolatables’ is a vector of Symbols of parameter names for which a time interpolation (linear) object must be constructed. The control mapping for DiscreteControl is also constructed in this function. This function currently does not support node states that are defined by more than one row in a table, as is the case for TabulatedRatingCurve.\nsource\n# Ribasim.path_exists_in_graph — Method.\nFind out whether a path exists between a start node and end node in the given graph.\nsource\n# Ribasim.process_allocation_graph_edges! — Method.\nFor the composite allocgraph edges:\n\nFind out whether they are connected to allocgraph nodes on both ends\nCompute their capacity\nFind out their allowed flow direction(s)\n\nsource\n# Ribasim.profile_storage — Method.\nCalculate a profile storage by integrating the areas over the levels\nsource\n# Ribasim.qh_interpolation — Method.\nFrom a table with columns nodeid, discharge (Q) and level (h), create a LinearInterpolation from level to discharge for a given nodeid.\nsource\n# Ribasim.reduction_factor — Method.\nFunction that goes smoothly from 0 to 1 in the interval [0,threshold], and is constant outside this interval.\nsource\n# Ribasim.results_path — Method.\nConstruct a path relative to both the TOML directory and the optional results_dir\nsource\n# Ribasim.run — Method.\nrun(config_file::AbstractString)::Model\nrun(config::Config)::Model\nRun a Model, given a path to a TOML configuration file, or a Config object. Running a model includes initialization, solving to the end with [solve!](@ref) and writing results with BMI.finalize.\nsource\n# Ribasim.save_flow — Method.\nCopy the current flow to the SavedValues\nsource\n# Ribasim.scalar_interpolation_derivative — Method.\nDerivative of scalar interpolation.\nsource\n# Ribasim.seconds_since — Method.\nseconds_since(t::DateTime, t0::DateTime)::Float64\nConvert a DateTime to a float that is the number of seconds since the start of the simulation. This is used to convert between the solver’s inner float time, and the calendar.\nsource\n# Ribasim.set_current_value! — Method.\nFrom a timeseries table time, load the most recent applicable data into table. table must be a NamedTuple of vectors with all variables that must be loaded. The most recent applicable data is non-NaN data for a given ID that is on or before t.\nsource\n# Ribasim.set_model_state_in_allocation! — Method.\nUpdate the allocation problem with model data at the current:\n\nDemands of the users\nFlows of the source edges\nDemands of the basins\n\nsource\n# Ribasim.set_static_value! — Method.\nLoad data from a source table static into a destination table. Data is matched based on the node_id, which is sorted.\nsource\n# Ribasim.set_table_row! — Method.\nUpdate table at row index i, with the values of a given row. table must be a NamedTuple of vectors with all variables that must be loaded. The row must contain all the column names that are present in the table. If a value is NaN, it is not set.\nsource\n# Ribasim.sorted_table! — Method.\nDepending on if a table can be sorted, either sort it or assert that it is sorted.\nTables loaded from the database into memory can be sorted. Tables loaded from Arrow files are memory mapped and can therefore not be sorted.\nsource\n# Ribasim.timesteps — Method.\nGet all saved times in seconds since start\nsource\n# Ribasim.update_allocation! — Method.\nSolve the allocation problem for all users and assign allocated abstractions to user nodes.\nsource\n# Ribasim.update_basin — Method.\nLoad updates from ‘Basin / time’ into the parameters\nsource\n# Ribasim.update_jac_prototype! — Method.\nMethod for nodes that do not contribute to the Jacobian\nsource\n# Ribasim.update_jac_prototype! — Method.\nThe controlled basin affects itself and the basins upstream and downstream of the controlled pump affect eachother if there is a basin upstream of the pump. The state for the integral term and the controlled basin affect eachother, and the same for the integral state and the basin upstream of the pump if it is indeed a basin.\nsource\n# Ribasim.update_jac_prototype! — Method.\nIf both the unique node upstream and the unique node downstream of these nodes are basins, then these directly depend on eachother and affect the Jacobian 2x Basins always depend on themselves.\nsource\n# Ribasim.update_jac_prototype! — Method.\nIf both the unique node upstream and the nodes down stream (or one node further if a fractional flow is in between) are basins, then the downstream basin depends on the upstream basin(s) and affect the Jacobian as many times as there are downstream basins Upstream basins always depend on themselves.\nsource\n# Ribasim.update_tabulated_rating_curve! — Method.\nLoad updates from ‘TabulatedRatingCurve / time’ into the parameters\nsource\n# Ribasim.valid_discrete_control — Method.\nCheck:\n\nwhether control states are defined for discrete controlled nodes;\nWhether the supplied truth states have the proper length;\nWhether look_ahead is only supplied for condition variables given by a time-series.\n\nsource\n# Ribasim.valid_edge_types — Method.\nCheck that only supported edge types are declared.\nsource\n# Ribasim.valid_edges — Method.\nTest for each node given its node type whether the nodes that\nare downstream (‘down-edge’) of this node are of an allowed type\nsource\n# Ribasim.valid_flow_rates — Method.\nTest whether static or discrete controlled flow rates are indeed non-negative.\nsource\n# Ribasim.valid_fractional_flow — Method.\nCheck that nodes that have fractional flow outneighbors do not have any other type of outneighbor, that the fractions leaving a node add up to ≈1 and that the fractions are non-negative.\nsource\n# Ribasim.valid_n_neighbors — Method.\nTest for each node given its node type whether it has an allowed number of flow/control inneighbors and outneighbors\nsource\n# Ribasim.valid_profiles — Method.\nCheck whether the profile data has no repeats in the levels and the areas start positive.\nsource\n# Ribasim.valid_sources — Method.\nThe source nodes must only have one outneighbor.\nsource\n# Ribasim.water_balance! — Method.\nThe right hand side function of the system of ODEs set up by Ribasim.\nsource\n# Ribasim.write_arrow — Method.\nWrite a result table to disk as an Arrow file\nsource\n# Ribasim.config.algorithm — Method.\nCreate an OrdinaryDiffEqAlgorithm from solver config\nsource\n# Ribasim.config.snake_case — Method.\nConvert a string from CamelCase to snake_case.\nsource" }, { "objectID": "build/index.html#constants", @@ -179,7 +179,7 @@ "href": "build/index.html#index", "title": "1 API Reference", "section": "1.6 Index", - "text": "1.6 Index\n\nRibasim.Ribasim\nRibasim.config\nRibasim.config.algorithms\nRibasim.AllocationModel\nRibasim.AllocationModel\nRibasim.Basin\nRibasim.Connectivity\nRibasim.DiscreteControl\nRibasim.FlatVector\nRibasim.FlowBoundary\nRibasim.FractionalFlow\nRibasim.LevelBoundary\nRibasim.LinearResistance\nRibasim.ManningResistance\nRibasim.Model\nRibasim.Outlet\nRibasim.PidControl\nRibasim.Pump\nRibasim.TabulatedRatingCurve\nRibasim.Terminal\nRibasim.User\nRibasim.config.Config\nBasicModelInterface.finalize\nBasicModelInterface.initialize\nBasicModelInterface.initialize\nCommonSolve.solve!\nRibasim.add_constraints_basin_allocation!\nRibasim.add_constraints_capacity!\nRibasim.add_constraints_flow_conservation!\nRibasim.add_constraints_source!\nRibasim.add_constraints_user_allocation!\nRibasim.add_constraints_user_returnflow!\nRibasim.add_objective_function!\nRibasim.add_variables_allocation_basin!\nRibasim.add_variables_allocation_user!\nRibasim.add_variables_flow!\nRibasim.allocate!\nRibasim.allocation_graph\nRibasim.allocation_problem\nRibasim.assign_allocations!\nRibasim.avoid_using_own_returnflow!\nRibasim.basin_bottom\nRibasim.basin_bottoms\nRibasim.basin_table\nRibasim.config.algorithm\nRibasim.config.snake_case\nRibasim.create_callbacks\nRibasim.create_graph\nRibasim.create_storage_tables\nRibasim.datetime_since\nRibasim.datetimes\nRibasim.discrete_control_affect!\nRibasim.discrete_control_affect_downcrossing!\nRibasim.discrete_control_affect_upcrossing!\nRibasim.discrete_control_condition\nRibasim.discrete_control_table\nRibasim.expand_logic_mapping\nRibasim.find_allocation_graph_edges!\nRibasim.findlastgroup\nRibasim.findsorted\nRibasim.flow_table\nRibasim.formulate_basins!\nRibasim.formulate_flow!\nRibasim.formulate_flow!\nRibasim.formulate_flow!\nRibasim.get_area_and_level\nRibasim.get_compressor\nRibasim.get_fractional_flow_connected_basins\nRibasim.get_jac_prototype\nRibasim.get_level\nRibasim.get_node_id_mapping\nRibasim.get_node_in_out_edges\nRibasim.get_scalar_interpolation\nRibasim.get_storage_from_level\nRibasim.get_storages_and_levels\nRibasim.get_storages_from_levels\nRibasim.get_tstops\nRibasim.get_value\nRibasim.id_index\nRibasim.input_path\nRibasim.is_flow_constraining\nRibasim.is_flow_direction_constraining\nRibasim.load_data\nRibasim.load_structvector\nRibasim.nodefields\nRibasim.nodetype\nRibasim.parse_static_and_time\nRibasim.path_exists_in_graph\nRibasim.process_allocation_graph_edges!\nRibasim.profile_storage\nRibasim.qh_interpolation\nRibasim.reduction_factor\nRibasim.results_path\nRibasim.run\nRibasim.save_flow\nRibasim.scalar_interpolation_derivative\nRibasim.seconds_since\nRibasim.set_current_value!\nRibasim.set_model_state_in_allocation!\nRibasim.set_static_value!\nRibasim.set_table_row!\nRibasim.sorted_table!\nRibasim.timesteps\nRibasim.update_basin\nRibasim.update_jac_prototype!\nRibasim.update_jac_prototype!\nRibasim.update_jac_prototype!\nRibasim.update_jac_prototype!\nRibasim.update_tabulated_rating_curve!\nRibasim.valid_discrete_control\nRibasim.valid_edge_types\nRibasim.valid_edges\nRibasim.valid_flow_rates\nRibasim.valid_fractional_flow\nRibasim.valid_n_neighbors\nRibasim.valid_profiles\nRibasim.valid_sources\nRibasim.water_balance!\nRibasim.write_arrow\nRibasim.config.@addfields\nRibasim.config.@addnodetypes" + "text": "1.6 Index\n\nRibasim.Ribasim\nRibasim.config\nRibasim.config.algorithms\nRibasim.AllocationModel\nRibasim.AllocationModel\nRibasim.Basin\nRibasim.Connectivity\nRibasim.DiscreteControl\nRibasim.FlatVector\nRibasim.FlowBoundary\nRibasim.FractionalFlow\nRibasim.LevelBoundary\nRibasim.LinearResistance\nRibasim.ManningResistance\nRibasim.Model\nRibasim.Outlet\nRibasim.PidControl\nRibasim.Pump\nRibasim.TabulatedRatingCurve\nRibasim.Terminal\nRibasim.User\nRibasim.config.Config\nBasicModelInterface.finalize\nBasicModelInterface.initialize\nBasicModelInterface.initialize\nCommonSolve.solve!\nRibasim.add_constraints_basin_allocation!\nRibasim.add_constraints_capacity!\nRibasim.add_constraints_flow_conservation!\nRibasim.add_constraints_source!\nRibasim.add_constraints_user_allocation!\nRibasim.add_constraints_user_returnflow!\nRibasim.add_objective_function!\nRibasim.add_variables_allocation_basin!\nRibasim.add_variables_allocation_user!\nRibasim.add_variables_flow!\nRibasim.allocate!\nRibasim.allocation_graph\nRibasim.allocation_problem\nRibasim.assign_allocations!\nRibasim.avoid_using_own_returnflow!\nRibasim.basin_bottom\nRibasim.basin_bottoms\nRibasim.basin_table\nRibasim.config.algorithm\nRibasim.config.snake_case\nRibasim.create_callbacks\nRibasim.create_graph\nRibasim.create_storage_tables\nRibasim.datetime_since\nRibasim.datetimes\nRibasim.discrete_control_affect!\nRibasim.discrete_control_affect_downcrossing!\nRibasim.discrete_control_affect_upcrossing!\nRibasim.discrete_control_condition\nRibasim.discrete_control_table\nRibasim.expand_logic_mapping\nRibasim.find_allocation_graph_edges!\nRibasim.findlastgroup\nRibasim.findsorted\nRibasim.flow_table\nRibasim.formulate_basins!\nRibasim.formulate_flow!\nRibasim.formulate_flow!\nRibasim.formulate_flow!\nRibasim.get_area_and_level\nRibasim.get_compressor\nRibasim.get_fractional_flow_connected_basins\nRibasim.get_jac_prototype\nRibasim.get_level\nRibasim.get_node_id_mapping\nRibasim.get_node_in_out_edges\nRibasim.get_scalar_interpolation\nRibasim.get_storage_from_level\nRibasim.get_storages_and_levels\nRibasim.get_storages_from_levels\nRibasim.get_tstops\nRibasim.get_value\nRibasim.id_index\nRibasim.input_path\nRibasim.is_flow_constraining\nRibasim.is_flow_direction_constraining\nRibasim.load_data\nRibasim.load_structvector\nRibasim.nodefields\nRibasim.nodetype\nRibasim.parse_static_and_time\nRibasim.path_exists_in_graph\nRibasim.process_allocation_graph_edges!\nRibasim.profile_storage\nRibasim.qh_interpolation\nRibasim.reduction_factor\nRibasim.results_path\nRibasim.run\nRibasim.save_flow\nRibasim.scalar_interpolation_derivative\nRibasim.seconds_since\nRibasim.set_current_value!\nRibasim.set_model_state_in_allocation!\nRibasim.set_static_value!\nRibasim.set_table_row!\nRibasim.sorted_table!\nRibasim.timesteps\nRibasim.update_allocation!\nRibasim.update_basin\nRibasim.update_jac_prototype!\nRibasim.update_jac_prototype!\nRibasim.update_jac_prototype!\nRibasim.update_jac_prototype!\nRibasim.update_tabulated_rating_curve!\nRibasim.valid_discrete_control\nRibasim.valid_edge_types\nRibasim.valid_edges\nRibasim.valid_flow_rates\nRibasim.valid_fractional_flow\nRibasim.valid_n_neighbors\nRibasim.valid_profiles\nRibasim.valid_sources\nRibasim.water_balance!\nRibasim.write_arrow\nRibasim.config.@addfields\nRibasim.config.@addnodetypes" }, { "objectID": "contribute/release.html", @@ -452,14 +452,14 @@ "href": "python/reference/Model.html", "title": "1 Model", "section": "", - "text": "Model()\nA full Ribasim model schematisation with all input.\nRibasim model containing the location of the nodes, the edges between the nodes, and the node parametrization.\n\n\n\n\n\n\n\n\n\n\n\nName\nType\nDescription\nDefault\n\n\n\n\nnode\nNode\nThe ID, type and geometry of each node.\nrequired\n\n\nedge\nEdge\nHow the nodes are connected.\nrequired\n\n\nbasin\nBasin\nThe waterbodies.\nrequired\n\n\nfractional_flow\nOptional[FractionalFlow]\nSplit flows into fractions.\nrequired\n\n\nlevel_boundary\nOptional[LevelBoundary]\nBoundary condition specifying the water level.\nrequired\n\n\nflow_boundary\nOptional[FlowBoundary]\nBoundary conditions specifying the flow.\nrequired\n\n\nlinear_resistance\n\nLinear flow resistance.\nrequired\n\n\nmanning_resistance\nOptional[ManningResistance]\nFlow resistance based on the Manning formula.\nrequired\n\n\ntabulated_rating_curve\nOptional[TabulatedRatingCurve]\nTabulated rating curve describing flow based on the upstream water level.\nrequired\n\n\npump\nOptional[Pump]\nPrescribed flow rate from one basin to the other.\nrequired\n\n\noutlet\nOptional[Outlet]\nPrescribed flow rate from one basin to the other.\nrequired\n\n\nterminal\nOptional[Terminal]\nWater sink without state or properties.\nrequired\n\n\ndiscrete_control\nOptional[DiscreteControl]\nDiscrete control logic.\nrequired\n\n\npid_control\nOptional[PidControl]\nPID controller attempting to set the level of a basin to a desired value using a pump/outlet.\nrequired\n\n\nuser\nOptional[User]\nUser node type with demand and priority.\nrequired\n\n\nstarttime\nUnion[str, datetime.datetime]\nStarting time of the simulation.\nrequired\n\n\nendtime\nUnion[str, datetime.datetime]\nEnd time of the simulation.\nrequired\n\n\nsolver\nOptional[Solver]\nSolver settings.\nrequired\n\n\nlogging\nOptional[logging]\nLogging settings.\nrequired\n\n\n\n\n\n\n\n\n\nName\nDescription\n\n\n\n\nfields\nReturn the names of the fields contained in the Model.\n\n\nfrom_toml\nInitialize a model from the TOML configuration file.\n\n\nplot\nPlot the nodes and edges of the model.\n\n\nsort\nSort all input tables as required.\n\n\nvalidate_model\nValidate the model.\n\n\nvalidate_model_node_IDs\nCheck whether the node IDs in the node field correspond to the node IDs on the node type fields.\n\n\nvalidate_model_node_field_IDs\nCheck whether the node IDs of the node_type fields are valid.\n\n\nvalidate_model_node_types\nCheck whether all node types in the node field are valid.\n\n\nwrite\nWrite the contents of the model to a database and a TOML configuration file.\n\n\n\n\n\nModel.fields()\nReturn the names of the fields contained in the Model.\n\n\n\nModel.from_toml(path)\nInitialize a model from the TOML configuration file.\n\n\n\n\n\n\n\n\n\n\n\nName\nType\nDescription\nDefault\n\n\n\n\npath\nFilePath\nPath to the configuration TOML file.\nrequired\n\n\n\n\n\n\n\n\n\nType\nDescription\n\n\n\n\nModel\n\n\n\n\n\n\n\n\nModel.plot(ax=None)\nPlot the nodes and edges of the model.\n\n\n\n\n\n\n\n\n\n\n\nName\nType\nDescription\nDefault\n\n\n\n\nax\nmatplotlib.pyplot.Artist\nAxes on which to draw the plot.\nNone\n\n\n\n\n\n\n\n\n\nType\nDescription\n\n\n\n\nmatplotlib.pyplot.Artist\n\n\n\n\n\n\n\n\nModel.sort()\nSort all input tables as required.\nTables are sorted by “node_id”, unless otherwise specified. Sorting is done automatically before writing the table.\n\n\n\nModel.validate_model()\nValidate the model.\nChecks: - Whether all node types in the node field are valid - Whether the node IDs of the node_type fields are valid - Whether the node IDs in the node field correspond to the node IDs on the node type fields\n\n\n\nModel.validate_model_node_IDs()\nCheck whether the node IDs in the node field correspond to the node IDs on the node type fields.\n\n\n\nModel.validate_model_node_field_IDs()\nCheck whether the node IDs of the node_type fields are valid.\n\n\n\nModel.validate_model_node_types()\nCheck whether all node types in the node field are valid.\n\n\n\nModel.write(directory)\nWrite the contents of the model to a database and a TOML configuration file.\nIf directory does not exist, it is created before writing.\n\n\n\n\n\nName\nType\nDescription\nDefault\n\n\n\n\ndirectory\nFilePath\n\nrequired" + "text": "Model()\nA full Ribasim model schematisation with all input.\nRibasim model containing the location of the nodes, the edges between the nodes, and the node parametrization.\n\n\n\n\n\n\n\n\n\n\n\nName\nType\nDescription\nDefault\n\n\n\n\nnode\nNode\nThe ID, type and geometry of each node.\nrequired\n\n\nedge\nEdge\nHow the nodes are connected.\nrequired\n\n\nbasin\nBasin\nThe waterbodies.\nrequired\n\n\nfractional_flow\nOptional[FractionalFlow]\nSplit flows into fractions.\nrequired\n\n\nlevel_boundary\nOptional[LevelBoundary]\nBoundary condition specifying the water level.\nrequired\n\n\nflow_boundary\nOptional[FlowBoundary]\nBoundary conditions specifying the flow.\nrequired\n\n\nlinear_resistance\n\nLinear flow resistance.\nrequired\n\n\nmanning_resistance\nOptional[ManningResistance]\nFlow resistance based on the Manning formula.\nrequired\n\n\ntabulated_rating_curve\nOptional[TabulatedRatingCurve]\nTabulated rating curve describing flow based on the upstream water level.\nrequired\n\n\npump\nOptional[Pump]\nPrescribed flow rate from one basin to the other.\nrequired\n\n\noutlet\nOptional[Outlet]\nPrescribed flow rate from one basin to the other.\nrequired\n\n\nterminal\nOptional[Terminal]\nWater sink without state or properties.\nrequired\n\n\ndiscrete_control\nOptional[DiscreteControl]\nDiscrete control logic.\nrequired\n\n\npid_control\nOptional[PidControl]\nPID controller attempting to set the level of a basin to a desired value using a pump/outlet.\nrequired\n\n\nuser\nOptional[User]\nUser node type with demand and priority.\nrequired\n\n\nstarttime\nUnion[str, datetime.datetime]\nStarting time of the simulation.\nrequired\n\n\nendtime\nUnion[str, datetime.datetime]\nEnd time of the simulation.\nrequired\n\n\nallocation\nOptional[Allocation]\nAllocation settings.\nrequired\n\n\nsolver\nOptional[Solver]\nSolver settings.\nrequired\n\n\nlogging\nOptional[logging]\nLogging settings.\nrequired\n\n\n\n\n\n\n\n\n\nName\nDescription\n\n\n\n\nfields\nReturn the names of the fields contained in the Model.\n\n\nfrom_toml\nInitialize a model from the TOML configuration file.\n\n\nplot\nPlot the nodes and edges of the model.\n\n\nsort\nSort all input tables as required.\n\n\nvalidate_model\nValidate the model.\n\n\nvalidate_model_node_IDs\nCheck whether the node IDs in the node field correspond to the node IDs on the node type fields.\n\n\nvalidate_model_node_field_IDs\nCheck whether the node IDs of the node_type fields are valid.\n\n\nvalidate_model_node_types\nCheck whether all node types in the node field are valid.\n\n\nwrite\nWrite the contents of the model to a database and a TOML configuration file.\n\n\n\n\n\nModel.fields()\nReturn the names of the fields contained in the Model.\n\n\n\nModel.from_toml(path)\nInitialize a model from the TOML configuration file.\n\n\n\n\n\n\n\n\n\n\n\nName\nType\nDescription\nDefault\n\n\n\n\npath\nFilePath\nPath to the configuration TOML file.\nrequired\n\n\n\n\n\n\n\n\n\nType\nDescription\n\n\n\n\nModel\n\n\n\n\n\n\n\n\nModel.plot(ax=None)\nPlot the nodes and edges of the model.\n\n\n\n\n\n\n\n\n\n\n\nName\nType\nDescription\nDefault\n\n\n\n\nax\nmatplotlib.pyplot.Artist\nAxes on which to draw the plot.\nNone\n\n\n\n\n\n\n\n\n\nType\nDescription\n\n\n\n\nmatplotlib.pyplot.Artist\n\n\n\n\n\n\n\n\nModel.sort()\nSort all input tables as required.\nTables are sorted by “node_id”, unless otherwise specified. Sorting is done automatically before writing the table.\n\n\n\nModel.validate_model()\nValidate the model.\nChecks: - Whether all node types in the node field are valid - Whether the node IDs of the node_type fields are valid - Whether the node IDs in the node field correspond to the node IDs on the node type fields\n\n\n\nModel.validate_model_node_IDs()\nCheck whether the node IDs in the node field correspond to the node IDs on the node type fields.\n\n\n\nModel.validate_model_node_field_IDs()\nCheck whether the node IDs of the node_type fields are valid.\n\n\n\nModel.validate_model_node_types()\nCheck whether all node types in the node field are valid.\n\n\n\nModel.write(directory)\nWrite the contents of the model to a database and a TOML configuration file.\nIf directory does not exist, it is created before writing.\n\n\n\n\n\nName\nType\nDescription\nDefault\n\n\n\n\ndirectory\nFilePath\n\nrequired" }, { "objectID": "python/reference/Model.html#parameters", "href": "python/reference/Model.html#parameters", "title": "1 Model", "section": "", - "text": "Name\nType\nDescription\nDefault\n\n\n\n\nnode\nNode\nThe ID, type and geometry of each node.\nrequired\n\n\nedge\nEdge\nHow the nodes are connected.\nrequired\n\n\nbasin\nBasin\nThe waterbodies.\nrequired\n\n\nfractional_flow\nOptional[FractionalFlow]\nSplit flows into fractions.\nrequired\n\n\nlevel_boundary\nOptional[LevelBoundary]\nBoundary condition specifying the water level.\nrequired\n\n\nflow_boundary\nOptional[FlowBoundary]\nBoundary conditions specifying the flow.\nrequired\n\n\nlinear_resistance\n\nLinear flow resistance.\nrequired\n\n\nmanning_resistance\nOptional[ManningResistance]\nFlow resistance based on the Manning formula.\nrequired\n\n\ntabulated_rating_curve\nOptional[TabulatedRatingCurve]\nTabulated rating curve describing flow based on the upstream water level.\nrequired\n\n\npump\nOptional[Pump]\nPrescribed flow rate from one basin to the other.\nrequired\n\n\noutlet\nOptional[Outlet]\nPrescribed flow rate from one basin to the other.\nrequired\n\n\nterminal\nOptional[Terminal]\nWater sink without state or properties.\nrequired\n\n\ndiscrete_control\nOptional[DiscreteControl]\nDiscrete control logic.\nrequired\n\n\npid_control\nOptional[PidControl]\nPID controller attempting to set the level of a basin to a desired value using a pump/outlet.\nrequired\n\n\nuser\nOptional[User]\nUser node type with demand and priority.\nrequired\n\n\nstarttime\nUnion[str, datetime.datetime]\nStarting time of the simulation.\nrequired\n\n\nendtime\nUnion[str, datetime.datetime]\nEnd time of the simulation.\nrequired\n\n\nsolver\nOptional[Solver]\nSolver settings.\nrequired\n\n\nlogging\nOptional[logging]\nLogging settings.\nrequired" + "text": "Name\nType\nDescription\nDefault\n\n\n\n\nnode\nNode\nThe ID, type and geometry of each node.\nrequired\n\n\nedge\nEdge\nHow the nodes are connected.\nrequired\n\n\nbasin\nBasin\nThe waterbodies.\nrequired\n\n\nfractional_flow\nOptional[FractionalFlow]\nSplit flows into fractions.\nrequired\n\n\nlevel_boundary\nOptional[LevelBoundary]\nBoundary condition specifying the water level.\nrequired\n\n\nflow_boundary\nOptional[FlowBoundary]\nBoundary conditions specifying the flow.\nrequired\n\n\nlinear_resistance\n\nLinear flow resistance.\nrequired\n\n\nmanning_resistance\nOptional[ManningResistance]\nFlow resistance based on the Manning formula.\nrequired\n\n\ntabulated_rating_curve\nOptional[TabulatedRatingCurve]\nTabulated rating curve describing flow based on the upstream water level.\nrequired\n\n\npump\nOptional[Pump]\nPrescribed flow rate from one basin to the other.\nrequired\n\n\noutlet\nOptional[Outlet]\nPrescribed flow rate from one basin to the other.\nrequired\n\n\nterminal\nOptional[Terminal]\nWater sink without state or properties.\nrequired\n\n\ndiscrete_control\nOptional[DiscreteControl]\nDiscrete control logic.\nrequired\n\n\npid_control\nOptional[PidControl]\nPID controller attempting to set the level of a basin to a desired value using a pump/outlet.\nrequired\n\n\nuser\nOptional[User]\nUser node type with demand and priority.\nrequired\n\n\nstarttime\nUnion[str, datetime.datetime]\nStarting time of the simulation.\nrequired\n\n\nendtime\nUnion[str, datetime.datetime]\nEnd time of the simulation.\nrequired\n\n\nallocation\nOptional[Allocation]\nAllocation settings.\nrequired\n\n\nsolver\nOptional[Solver]\nSolver settings.\nrequired\n\n\nlogging\nOptional[logging]\nLogging settings.\nrequired" }, { "objectID": "python/reference/Model.html#methods", @@ -515,7 +515,7 @@ "href": "python/examples.html", "title": "Examples", "section": "", - "text": "1 Basic model with static forcing\n\nfrom pathlib import Path\n\nimport geopandas as gpd\nimport matplotlib.pyplot as plt\nimport numpy as np\nimport pandas as pd\nimport ribasim\n\nSetup the basins:\n\nprofile = pd.DataFrame(\n data={\n \"node_id\": [1, 1, 3, 3, 6, 6, 9, 9],\n \"area\": [0.01, 1000.0] * 4,\n \"level\": [0.0, 1.0] * 4,\n }\n)\n\n# Convert steady forcing to m/s\n# 2 mm/d precipitation, 1 mm/d evaporation\nseconds_in_day = 24 * 3600\nprecipitation = 0.002 / seconds_in_day\nevaporation = 0.001 / seconds_in_day\n\nstatic = pd.DataFrame(\n data={\n \"node_id\": [0],\n \"drainage\": [0.0],\n \"potential_evaporation\": [evaporation],\n \"infiltration\": [0.0],\n \"precipitation\": [precipitation],\n \"urban_runoff\": [0.0],\n }\n)\nstatic = static.iloc[[0, 0, 0, 0]]\nstatic[\"node_id\"] = [1, 3, 6, 9]\n\nbasin = ribasim.Basin(profile=profile, static=static)\n\nSetup linear resistance:\n\nlinear_resistance = ribasim.LinearResistance(\n static=pd.DataFrame(\n data={\"node_id\": [10, 12], \"resistance\": [5e3, (3600.0 * 24) / 100.0]}\n )\n)\n\nSetup Manning resistance:\n\nmanning_resistance = ribasim.ManningResistance(\n static=pd.DataFrame(\n data={\n \"node_id\": [2],\n \"length\": [900.0],\n \"manning_n\": [0.04],\n \"profile_width\": [6.0],\n \"profile_slope\": [3.0],\n }\n )\n)\n\nSet up a rating curve node:\n\n# Discharge: lose 1% of storage volume per day at storage = 1000.0.\nq1000 = 1000.0 * 0.01 / seconds_in_day\n\nrating_curve = ribasim.TabulatedRatingCurve(\n static=pd.DataFrame(\n data={\n \"node_id\": [4, 4],\n \"level\": [0.0, 1.0],\n \"discharge\": [0.0, q1000],\n }\n )\n)\n\nSetup fractional flows:\n\nfractional_flow = ribasim.FractionalFlow(\n static=pd.DataFrame(\n data={\n \"node_id\": [5, 8, 13],\n \"fraction\": [0.3, 0.6, 0.1],\n }\n )\n)\n\nSetup pump:\n\npump = ribasim.Pump(\n static=pd.DataFrame(\n data={\n \"node_id\": [7],\n \"flow_rate\": [0.5 / 3600],\n }\n )\n)\n\nSetup level boundary:\n\nlevel_boundary = ribasim.LevelBoundary(\n static=pd.DataFrame(\n data={\n \"node_id\": [11, 17],\n \"level\": [0.5, 1.5],\n }\n )\n)\n\nSetup flow boundary:\n\nflow_boundary = ribasim.FlowBoundary(\n static=pd.DataFrame(\n data={\n \"node_id\": [15, 16],\n \"flow_rate\": [1e-4, 1e-4],\n }\n )\n)\n\nSetup terminal:\n\nterminal = ribasim.Terminal(\n static=pd.DataFrame(\n data={\n \"node_id\": [14],\n }\n )\n)\n\nSet up the nodes:\n\nxy = np.array(\n [\n (0.0, 0.0), # 1: Basin,\n (1.0, 0.0), # 2: ManningResistance\n (2.0, 0.0), # 3: Basin\n (3.0, 0.0), # 4: TabulatedRatingCurve\n (3.0, 1.0), # 5: FractionalFlow\n (3.0, 2.0), # 6: Basin\n (4.0, 1.0), # 7: Pump\n (4.0, 0.0), # 8: FractionalFlow\n (5.0, 0.0), # 9: Basin\n (6.0, 0.0), # 10: LinearResistance\n (2.0, 2.0), # 11: LevelBoundary\n (2.0, 1.0), # 12: LinearResistance\n (3.0, -1.0), # 13: FractionalFlow\n (3.0, -2.0), # 14: Terminal\n (3.0, 3.0), # 15: FlowBoundary\n (0.0, 1.0), # 16: FlowBoundary\n (6.0, 1.0), # 17: LevelBoundary\n ]\n)\nnode_xy = gpd.points_from_xy(x=xy[:, 0], y=xy[:, 1])\n\nnode_id, node_type = ribasim.Node.get_node_ids_and_types(\n basin,\n manning_resistance,\n rating_curve,\n pump,\n fractional_flow,\n linear_resistance,\n level_boundary,\n flow_boundary,\n terminal,\n)\n\n# Make sure the feature id starts at 1: explicitly give an index.\nnode = ribasim.Node(\n static=gpd.GeoDataFrame(\n data={\"type\": node_type},\n index=pd.Index(node_id, name=\"fid\"),\n geometry=node_xy,\n crs=\"EPSG:28992\",\n )\n)\n\nSetup the edges:\n\nfrom_id = np.array(\n [1, 2, 3, 4, 4, 5, 6, 8, 7, 9, 11, 12, 4, 13, 15, 16, 10], dtype=np.int64\n)\nto_id = np.array(\n [2, 3, 4, 5, 8, 6, 7, 9, 9, 10, 12, 3, 13, 14, 6, 1, 17], dtype=np.int64\n)\nlines = ribasim.utils.geometry_from_connectivity(node, from_id, to_id)\nedge = ribasim.Edge(\n static=gpd.GeoDataFrame(\n data={\n \"from_node_id\": from_id,\n \"to_node_id\": to_id,\n \"edge_type\": len(from_id) * [\"flow\"],\n },\n geometry=lines,\n crs=\"EPSG:28992\",\n )\n)\n\nSetup a model:\n\nmodel = ribasim.Model(\n node=node,\n edge=edge,\n basin=basin,\n level_boundary=level_boundary,\n flow_boundary=flow_boundary,\n pump=pump,\n linear_resistance=linear_resistance,\n manning_resistance=manning_resistance,\n tabulated_rating_curve=rating_curve,\n fractional_flow=fractional_flow,\n terminal=terminal,\n starttime=\"2020-01-01 00:00:00\",\n endtime=\"2021-01-01 00:00:00\",\n)\n\nLet’s take a look at the model:\n\nmodel.plot()\n\n<Axes: >\n\n\n\n\n\nWrite the model to a TOML and GeoPackage:\n\ndatadir = Path(\"data\")\nmodel.write(datadir / \"basic\")\n\n\n\n2 Update the basic model with transient forcing\nThis assumes you have already created the basic model with static forcing.\n\nimport numpy as np\nimport pandas as pd\nimport ribasim\nimport xarray as xr\n\n\nmodel = ribasim.Model.from_toml(datadir / \"basic/ribasim.toml\")\n\n\ntime = pd.date_range(model.starttime, model.endtime)\nday_of_year = time.day_of_year.to_numpy()\nseconds_per_day = 24 * 60 * 60\nevaporation = (\n (-1.0 * np.cos(day_of_year / 365.0 * 2 * np.pi) + 1.0) * 0.0025 / seconds_per_day\n)\nrng = np.random.default_rng(seed=0)\nprecipitation = (\n rng.lognormal(mean=-1.0, sigma=1.7, size=time.size) * 0.001 / seconds_per_day\n)\n\nWe’ll use xarray to easily broadcast the values.\n\ntimeseries = (\n pd.DataFrame(\n data={\n \"node_id\": 1,\n \"time\": time,\n \"drainage\": 0.0,\n \"potential_evaporation\": evaporation,\n \"infiltration\": 0.0,\n \"precipitation\": precipitation,\n \"urban_runoff\": 0.0,\n }\n )\n .set_index(\"time\")\n .to_xarray()\n)\n\nbasin_ids = model.basin.static[\"node_id\"].to_numpy()\nbasin_nodes = xr.DataArray(\n np.ones(len(basin_ids)), coords={\"node_id\": basin_ids}, dims=[\"node_id\"]\n)\nforcing = (timeseries * basin_nodes).to_dataframe().reset_index()\n\n\nstate = pd.DataFrame(\n data={\n \"node_id\": basin_ids,\n \"level\": 1.4,\n \"concentration\": 0.0,\n }\n)\n\n\nmodel.basin.time = forcing\nmodel.basin.state = state\n\n\nmodel.write(datadir / \"basic_transient\")\n\nNow run the model with ribasim basic-transient/ribasim.toml. After running the model, read back the results:\n\ndf_basin = pd.read_feather(datadir / \"basic_transient/results/basin.arrow\")\ndf_basin_wide = df_basin.pivot_table(\n index=\"time\", columns=\"node_id\", values=[\"storage\", \"level\"]\n)\ndf_basin_wide[\"level\"].plot()\n\n<Axes: xlabel='time'>\n\n\n\n\n\n\ndf_flow = pd.read_feather(datadir / \"basic_transient/results/flow.arrow\")\ndf_flow[\"edge\"] = list(zip(df_flow.from_node_id, df_flow.to_node_id))\ndf_flow[\"flow_m3d\"] = df_flow.flow * 86400\nax = df_flow.pivot_table(index=\"time\", columns=\"edge\", values=\"flow_m3d\").plot()\nax.legend(bbox_to_anchor=(1.3, 1), title=\"Edge\")\n\n<matplotlib.legend.Legend at 0x7f68fc3b1490>\n\n\n\n\n\n\ntype(df_flow)\n\npandas.core.frame.DataFrame\n\n\n\n\n3 Model with discrete control\nThe model constructed below consists of a single basin which slowly drains trough a TabulatedRatingCurve, but is held within a range around a target level (setpoint) by two connected pumps. These two pumps behave like a reversible pump. When pumping can be done in only one direction, and the other direction is only possible under gravity, use an Outlet for that direction.\nSet up the nodes:\n\nxy = np.array(\n [\n (0.0, 0.0), # 1: Basin\n (1.0, 1.0), # 2: Pump\n (1.0, -1.0), # 3: Pump\n (2.0, 0.0), # 4: LevelBoundary\n (-1.0, 0.0), # 5: TabulatedRatingCurve\n (-2.0, 0.0), # 6: Terminal\n (1.0, 0.0), # 7: DiscreteControl\n ]\n)\n\nnode_xy = gpd.points_from_xy(x=xy[:, 0], y=xy[:, 1])\n\nnode_type = [\n \"Basin\",\n \"Pump\",\n \"Pump\",\n \"LevelBoundary\",\n \"TabulatedRatingCurve\",\n \"Terminal\",\n \"DiscreteControl\",\n]\n\n# Make sure the feature id starts at 1: explicitly give an index.\nnode = ribasim.Node(\n static=gpd.GeoDataFrame(\n data={\"type\": node_type},\n index=pd.Index(np.arange(len(xy)) + 1, name=\"fid\"),\n geometry=node_xy,\n crs=\"EPSG:28992\",\n )\n)\n\nSetup the edges:\n\nfrom_id = np.array([1, 3, 4, 2, 1, 5, 7, 7], dtype=np.int64)\nto_id = np.array([3, 4, 2, 1, 5, 6, 2, 3], dtype=np.int64)\n\nedge_type = 6 * [\"flow\"] + 2 * [\"control\"]\n\nlines = ribasim.utils.geometry_from_connectivity(node, from_id, to_id)\nedge = ribasim.Edge(\n static=gpd.GeoDataFrame(\n data={\"from_node_id\": from_id, \"to_node_id\": to_id, \"edge_type\": edge_type},\n geometry=lines,\n crs=\"EPSG:28992\",\n )\n)\n\nSetup the basins:\n\nprofile = pd.DataFrame(\n data={\n \"node_id\": [1, 1],\n \"area\": [1000.0, 1000.0],\n \"level\": [0.0, 1.0],\n }\n)\n\nstatic = pd.DataFrame(\n data={\n \"node_id\": [1],\n \"drainage\": [0.0],\n \"potential_evaporation\": [0.0],\n \"infiltration\": [0.0],\n \"precipitation\": [0.0],\n \"urban_runoff\": [0.0],\n }\n)\n\nstate = pd.DataFrame(data={\"node_id\": [1], \"level\": [20.0]})\n\nbasin = ribasim.Basin(profile=profile, static=static, state=state)\n\nSetup the discrete control:\n\ncondition = pd.DataFrame(\n data={\n \"node_id\": 3 * [7],\n \"listen_feature_id\": 3 * [1],\n \"variable\": 3 * [\"level\"],\n \"greater_than\": [5.0, 10.0, 15.0], # min, setpoint, max\n }\n)\n\nlogic = pd.DataFrame(\n data={\n \"node_id\": 5 * [7],\n \"truth_state\": [\"FFF\", \"U**\", \"T*F\", \"**D\", \"TTT\"],\n \"control_state\": [\"in\", \"in\", \"none\", \"out\", \"out\"],\n }\n)\n\ndiscrete_control = ribasim.DiscreteControl(condition=condition, logic=logic)\n\nThe above control logic can be summarized as follows: - If the level gets above the maximum, activate the control state “out” until the setpoint is reached; - If the level gets below the minimum, active the control state “in” until the setpoint is reached; - Otherwise activate the control state “none”.\nSetup the pump:\n\npump = ribasim.Pump(\n static=pd.DataFrame(\n data={\n \"node_id\": 3 * [2] + 3 * [3],\n \"control_state\": 2 * [\"none\", \"in\", \"out\"],\n \"flow_rate\": [0.0, 2e-3, 0.0, 0.0, 0.0, 2e-3],\n }\n )\n)\n\nThe pump data defines the following:\n\n\n\nControl state\nPump #2 flow rate (m/s)\nPump #3 flow rate (m/s)\n\n\n\n\n“none”\n0.0\n0.0\n\n\n“in”\n2e-3\n0.0\n\n\n“out”\n0.0\n2e-3\n\n\n\nSetup the level boundary:\n\nlevel_boundary = ribasim.LevelBoundary(\n static=pd.DataFrame(data={\"node_id\": [4], \"level\": [10.0]})\n)\n\nSetup the rating curve:\n\nrating_curve = ribasim.TabulatedRatingCurve(\n static=pd.DataFrame(\n data={\"node_id\": 2 * [5], \"level\": [2.0, 15.0], \"discharge\": [0.0, 1e-3]}\n )\n)\n\nSetup the terminal:\n\nterminal = ribasim.Terminal(static=pd.DataFrame(data={\"node_id\": [6]}))\n\nSetup a model:\n\nmodel = ribasim.Model(\n node=node,\n edge=edge,\n basin=basin,\n pump=pump,\n level_boundary=level_boundary,\n tabulated_rating_curve=rating_curve,\n terminal=terminal,\n discrete_control=discrete_control,\n starttime=\"2020-01-01 00:00:00\",\n endtime=\"2021-01-01 00:00:00\",\n)\n\nLet’s take a look at the model:\n\nmodel.plot()\n\n<Axes: >\n\n\n\n\n\nListen edges are plotted with a dashed line since they are not present in the “Edge / static” schema but only in the “Control / condition” schema.\n\ndatadir = Path(\"data\")\nmodel.write(datadir / \"level_setpoint_with_minmax\")\n\nNow run the model with level_setpoint_with_minmax/ribasim.toml. After running the model, read back the results:\n\nfrom matplotlib.dates import date2num\n\ndf_basin = pd.read_feather(datadir / \"level_setpoint_with_minmax/results/basin.arrow\")\ndf_basin_wide = df_basin.pivot_table(\n index=\"time\", columns=\"node_id\", values=[\"storage\", \"level\"]\n)\n\nax = df_basin_wide[\"level\"].plot()\n\ngreater_than = model.discrete_control.condition.greater_than\n\nax.hlines(\n greater_than,\n df_basin.time[0],\n df_basin.time.max(),\n lw=1,\n ls=\"--\",\n color=\"k\",\n)\n\ndf_control = pd.read_feather(\n datadir / \"level_setpoint_with_minmax/results/control.arrow\"\n)\n\ny_min, y_max = ax.get_ybound()\nax.fill_between(df_control.time[:2], 2 * [y_min], 2 * [y_max], alpha=0.2, color=\"C0\")\nax.fill_between(df_control.time[2:4], 2 * [y_min], 2 * [y_max], alpha=0.2, color=\"C0\")\n\nax.set_xticks(\n date2num(df_control.time).tolist(),\n df_control.control_state.tolist(),\n rotation=50,\n)\n\nax.set_yticks(greater_than, [\"min\", \"setpoint\", \"max\"])\nax.set_ylabel(\"level\")\nplt.show()\n\n\n\n\nThe highlighted regions show where a pump is active.\nLet’s print an overview of what happened with control:\n\nmodel.print_discrete_control_record(\n datadir / \"level_setpoint_with_minmax/results/control.arrow\"\n)\n\n0. At 2020-01-01 00:00:00 the control node with ID 7 reached truth state TTT:\n For node ID 1 (Basin): level > 5.0\n For node ID 1 (Basin): level > 10.0\n For node ID 1 (Basin): level > 15.0\n\n This yielded control state \"out\":\n For node ID 2 (Pump): flow_rate = 0.0\n For node ID 3 (Pump): flow_rate = 0.002\n\n1. At 2020-02-09 01:17:29.324000 the control node with ID 7 reached truth state TFF:\n For node ID 1 (Basin): level > 5.0\n For node ID 1 (Basin): level < 10.0\n For node ID 1 (Basin): level < 15.0\n\n This yielded control state \"none\":\n For node ID 2 (Pump): flow_rate = 0.0\n For node ID 3 (Pump): flow_rate = 0.0\n\n2. At 2020-07-05 13:24:51.165000 the control node with ID 7 reached truth state FFF:\n For node ID 1 (Basin): level < 5.0\n For node ID 1 (Basin): level < 10.0\n For node ID 1 (Basin): level < 15.0\n\n This yielded control state \"in\":\n For node ID 2 (Pump): flow_rate = 0.002\n For node ID 3 (Pump): flow_rate = 0.0\n\n3. At 2020-08-11 11:49:59.015000 the control node with ID 7 reached truth state TTF:\n For node ID 1 (Basin): level > 5.0\n For node ID 1 (Basin): level > 10.0\n For node ID 1 (Basin): level < 15.0\n\n This yielded control state \"none\":\n For node ID 2 (Pump): flow_rate = 0.0\n For node ID 3 (Pump): flow_rate = 0.0\n\n\n\nNote that crossing direction specific truth states (containing “U”, “D”) are not present in this overview even though they are part of the control logic. This is because in the control logic for this model these truth states are only used to sustain control states, while the overview only shows changes in control states.\n\n\n4 Model with PID control\nSet up the nodes:\n\nxy = np.array(\n [\n (0.0, 0.0), # 1: FlowBoundary\n (1.0, 0.0), # 2: Basin\n (2.0, 0.5), # 3: Pump\n (3.0, 0.0), # 4: LevelBoundary\n (1.5, 1.0), # 5: PidControl\n (2.0, -0.5), # 6: outlet\n (1.5, -1.0), # 7: PidControl\n ]\n)\n\nnode_xy = gpd.points_from_xy(x=xy[:, 0], y=xy[:, 1])\n\nnode_type = [\n \"FlowBoundary\",\n \"Basin\",\n \"Pump\",\n \"LevelBoundary\",\n \"PidControl\",\n \"Outlet\",\n \"PidControl\",\n]\n\n# Make sure the feature id starts at 1: explicitly give an index.\nnode = ribasim.Node(\n static=gpd.GeoDataFrame(\n data={\"type\": node_type},\n index=pd.Index(np.arange(len(xy)) + 1, name=\"fid\"),\n geometry=node_xy,\n crs=\"EPSG:28992\",\n )\n)\n\nSetup the edges:\n\nfrom_id = np.array([1, 2, 3, 4, 6, 5, 7], dtype=np.int64)\nto_id = np.array([2, 3, 4, 6, 2, 3, 6], dtype=np.int64)\n\nlines = ribasim.utils.geometry_from_connectivity(node, from_id, to_id)\nedge = ribasim.Edge(\n static=gpd.GeoDataFrame(\n data={\n \"from_node_id\": from_id,\n \"to_node_id\": to_id,\n \"edge_type\": 5 * [\"flow\"] + 2 * [\"control\"],\n },\n geometry=lines,\n crs=\"EPSG:28992\",\n )\n)\n\nSetup the basins:\n\nprofile = pd.DataFrame(\n data={\"node_id\": [2, 2], \"level\": [0.0, 1.0], \"area\": [1000.0, 1000.0]}\n)\n\nstatic = pd.DataFrame(\n data={\n \"node_id\": [2],\n \"drainage\": [0.0],\n \"potential_evaporation\": [0.0],\n \"infiltration\": [0.0],\n \"precipitation\": [0.0],\n \"urban_runoff\": [0.0],\n }\n)\n\nstate = pd.DataFrame(\n data={\n \"node_id\": [2],\n \"level\": [6.0],\n }\n)\n\nbasin = ribasim.Basin(profile=profile, static=static, state=state)\n\nSetup the pump:\n\npump = ribasim.Pump(\n static=pd.DataFrame(\n data={\n \"node_id\": [3],\n \"flow_rate\": [0.0], # Will be overwritten by PID controller\n }\n )\n)\n\nSetup the outlet:\n\noutlet = ribasim.Outlet(\n static=pd.DataFrame(\n data={\n \"node_id\": [6],\n \"flow_rate\": [0.0], # Will be overwritten by PID controller\n }\n )\n)\n\nSetup flow boundary:\n\nflow_boundary = ribasim.FlowBoundary(\n static=pd.DataFrame(data={\"node_id\": [1], \"flow_rate\": [1e-3]})\n)\n\nSetup flow boundary:\n\nlevel_boundary = ribasim.LevelBoundary(\n static=pd.DataFrame(\n data={\n \"node_id\": [4],\n \"level\": [1.0], # Not relevant\n }\n )\n)\n\nSetup PID control:\n\npid_control = ribasim.PidControl(\n time=pd.DataFrame(\n data={\n \"node_id\": 4 * [5, 7],\n \"time\": [\n \"2020-01-01 00:00:00\",\n \"2020-01-01 00:00:00\",\n \"2020-05-01 00:00:00\",\n \"2020-05-01 00:00:00\",\n \"2020-07-01 00:00:00\",\n \"2020-07-01 00:00:00\",\n \"2020-12-01 00:00:00\",\n \"2020-12-01 00:00:00\",\n ],\n \"listen_node_id\": 4 * [2, 2],\n \"target\": [5.0, 5.0, 5.0, 5.0, 7.5, 7.5, 7.5, 7.5],\n \"proportional\": 4 * [-1e-3, 1e-3],\n \"integral\": 4 * [-1e-7, 1e-7],\n \"derivative\": 4 * [0.0, 0.0],\n }\n )\n)\n\nNote that the coefficients for the pump and the outlet are equal in magnitude but opposite in sign. This way the pump and the outlet equally work towards the same goal, while having opposite effects on the controlled basin due to their connectivity to this basin.\nSetup a model:\n\nmodel = ribasim.Model(\n node=node,\n edge=edge,\n basin=basin,\n flow_boundary=flow_boundary,\n level_boundary=level_boundary,\n pump=pump,\n outlet=outlet,\n pid_control=pid_control,\n starttime=\"2020-01-01 00:00:00\",\n endtime=\"2020-12-01 00:00:00\",\n)\n\nLet’s take a look at the model:\n\nmodel.plot()\n\n<Axes: >\n\n\n\n\n\nWrite the model to a TOML and GeoPackage:\n\ndatadir = Path(\"data\")\nmodel.write(datadir / \"pid_control\")\n\nNow run the model with ribasim pid_control/ribasim.toml. After running the model, read back the results:\n\nfrom matplotlib.dates import date2num\n\ndf_basin = pd.read_feather(datadir / \"pid_control/results/basin.arrow\")\ndf_basin_wide = df_basin.pivot_table(\n index=\"time\", columns=\"node_id\", values=[\"storage\", \"level\"]\n)\nax = df_basin_wide[\"level\"].plot()\nax.set_ylabel(\"level [m]\")\n\n# Plot target level\ntarget_levels = model.pid_control.time.target.to_numpy()[::2]\ntimes = date2num(model.pid_control.time.time)[::2]\nax.plot(times, target_levels, color=\"k\", ls=\":\", label=\"target level\")\npass" + "text": "1 Basic model with static forcing\n\nfrom pathlib import Path\n\nimport geopandas as gpd\nimport matplotlib.pyplot as plt\nimport numpy as np\nimport pandas as pd\nimport ribasim\n\nSetup the basins:\n\nprofile = pd.DataFrame(\n data={\n \"node_id\": [1, 1, 3, 3, 6, 6, 9, 9],\n \"area\": [0.01, 1000.0] * 4,\n \"level\": [0.0, 1.0] * 4,\n }\n)\n\n# Convert steady forcing to m/s\n# 2 mm/d precipitation, 1 mm/d evaporation\nseconds_in_day = 24 * 3600\nprecipitation = 0.002 / seconds_in_day\nevaporation = 0.001 / seconds_in_day\n\nstatic = pd.DataFrame(\n data={\n \"node_id\": [0],\n \"drainage\": [0.0],\n \"potential_evaporation\": [evaporation],\n \"infiltration\": [0.0],\n \"precipitation\": [precipitation],\n \"urban_runoff\": [0.0],\n }\n)\nstatic = static.iloc[[0, 0, 0, 0]]\nstatic[\"node_id\"] = [1, 3, 6, 9]\n\nbasin = ribasim.Basin(profile=profile, static=static)\n\nSetup linear resistance:\n\nlinear_resistance = ribasim.LinearResistance(\n static=pd.DataFrame(\n data={\"node_id\": [10, 12], \"resistance\": [5e3, (3600.0 * 24) / 100.0]}\n )\n)\n\nSetup Manning resistance:\n\nmanning_resistance = ribasim.ManningResistance(\n static=pd.DataFrame(\n data={\n \"node_id\": [2],\n \"length\": [900.0],\n \"manning_n\": [0.04],\n \"profile_width\": [6.0],\n \"profile_slope\": [3.0],\n }\n )\n)\n\nSet up a rating curve node:\n\n# Discharge: lose 1% of storage volume per day at storage = 1000.0.\nq1000 = 1000.0 * 0.01 / seconds_in_day\n\nrating_curve = ribasim.TabulatedRatingCurve(\n static=pd.DataFrame(\n data={\n \"node_id\": [4, 4],\n \"level\": [0.0, 1.0],\n \"discharge\": [0.0, q1000],\n }\n )\n)\n\nSetup fractional flows:\n\nfractional_flow = ribasim.FractionalFlow(\n static=pd.DataFrame(\n data={\n \"node_id\": [5, 8, 13],\n \"fraction\": [0.3, 0.6, 0.1],\n }\n )\n)\n\nSetup pump:\n\npump = ribasim.Pump(\n static=pd.DataFrame(\n data={\n \"node_id\": [7],\n \"flow_rate\": [0.5 / 3600],\n }\n )\n)\n\nSetup level boundary:\n\nlevel_boundary = ribasim.LevelBoundary(\n static=pd.DataFrame(\n data={\n \"node_id\": [11, 17],\n \"level\": [0.5, 1.5],\n }\n )\n)\n\nSetup flow boundary:\n\nflow_boundary = ribasim.FlowBoundary(\n static=pd.DataFrame(\n data={\n \"node_id\": [15, 16],\n \"flow_rate\": [1e-4, 1e-4],\n }\n )\n)\n\nSetup terminal:\n\nterminal = ribasim.Terminal(\n static=pd.DataFrame(\n data={\n \"node_id\": [14],\n }\n )\n)\n\nSet up the nodes:\n\nxy = np.array(\n [\n (0.0, 0.0), # 1: Basin,\n (1.0, 0.0), # 2: ManningResistance\n (2.0, 0.0), # 3: Basin\n (3.0, 0.0), # 4: TabulatedRatingCurve\n (3.0, 1.0), # 5: FractionalFlow\n (3.0, 2.0), # 6: Basin\n (4.0, 1.0), # 7: Pump\n (4.0, 0.0), # 8: FractionalFlow\n (5.0, 0.0), # 9: Basin\n (6.0, 0.0), # 10: LinearResistance\n (2.0, 2.0), # 11: LevelBoundary\n (2.0, 1.0), # 12: LinearResistance\n (3.0, -1.0), # 13: FractionalFlow\n (3.0, -2.0), # 14: Terminal\n (3.0, 3.0), # 15: FlowBoundary\n (0.0, 1.0), # 16: FlowBoundary\n (6.0, 1.0), # 17: LevelBoundary\n ]\n)\nnode_xy = gpd.points_from_xy(x=xy[:, 0], y=xy[:, 1])\n\nnode_id, node_type = ribasim.Node.get_node_ids_and_types(\n basin,\n manning_resistance,\n rating_curve,\n pump,\n fractional_flow,\n linear_resistance,\n level_boundary,\n flow_boundary,\n terminal,\n)\n\n# Make sure the feature id starts at 1: explicitly give an index.\nnode = ribasim.Node(\n static=gpd.GeoDataFrame(\n data={\"type\": node_type},\n index=pd.Index(node_id, name=\"fid\"),\n geometry=node_xy,\n crs=\"EPSG:28992\",\n )\n)\n\nSetup the edges:\n\nfrom_id = np.array(\n [1, 2, 3, 4, 4, 5, 6, 8, 7, 9, 11, 12, 4, 13, 15, 16, 10], dtype=np.int64\n)\nto_id = np.array(\n [2, 3, 4, 5, 8, 6, 7, 9, 9, 10, 12, 3, 13, 14, 6, 1, 17], dtype=np.int64\n)\nlines = ribasim.utils.geometry_from_connectivity(node, from_id, to_id)\nedge = ribasim.Edge(\n static=gpd.GeoDataFrame(\n data={\n \"from_node_id\": from_id,\n \"to_node_id\": to_id,\n \"edge_type\": len(from_id) * [\"flow\"],\n },\n geometry=lines,\n crs=\"EPSG:28992\",\n )\n)\n\nSetup a model:\n\nmodel = ribasim.Model(\n node=node,\n edge=edge,\n basin=basin,\n level_boundary=level_boundary,\n flow_boundary=flow_boundary,\n pump=pump,\n linear_resistance=linear_resistance,\n manning_resistance=manning_resistance,\n tabulated_rating_curve=rating_curve,\n fractional_flow=fractional_flow,\n terminal=terminal,\n starttime=\"2020-01-01 00:00:00\",\n endtime=\"2021-01-01 00:00:00\",\n)\n\nLet’s take a look at the model:\n\nmodel.plot()\n\n<Axes: >\n\n\n\n\n\nWrite the model to a TOML and GeoPackage:\n\ndatadir = Path(\"data\")\nmodel.write(datadir / \"basic\")\n\n\n\n2 Update the basic model with transient forcing\nThis assumes you have already created the basic model with static forcing.\n\nimport numpy as np\nimport pandas as pd\nimport ribasim\nimport xarray as xr\n\n\nmodel = ribasim.Model.from_toml(datadir / \"basic/ribasim.toml\")\n\n\ntime = pd.date_range(model.starttime, model.endtime)\nday_of_year = time.day_of_year.to_numpy()\nseconds_per_day = 24 * 60 * 60\nevaporation = (\n (-1.0 * np.cos(day_of_year / 365.0 * 2 * np.pi) + 1.0) * 0.0025 / seconds_per_day\n)\nrng = np.random.default_rng(seed=0)\nprecipitation = (\n rng.lognormal(mean=-1.0, sigma=1.7, size=time.size) * 0.001 / seconds_per_day\n)\n\nWe’ll use xarray to easily broadcast the values.\n\ntimeseries = (\n pd.DataFrame(\n data={\n \"node_id\": 1,\n \"time\": time,\n \"drainage\": 0.0,\n \"potential_evaporation\": evaporation,\n \"infiltration\": 0.0,\n \"precipitation\": precipitation,\n \"urban_runoff\": 0.0,\n }\n )\n .set_index(\"time\")\n .to_xarray()\n)\n\nbasin_ids = model.basin.static[\"node_id\"].to_numpy()\nbasin_nodes = xr.DataArray(\n np.ones(len(basin_ids)), coords={\"node_id\": basin_ids}, dims=[\"node_id\"]\n)\nforcing = (timeseries * basin_nodes).to_dataframe().reset_index()\n\n\nstate = pd.DataFrame(\n data={\n \"node_id\": basin_ids,\n \"level\": 1.4,\n \"concentration\": 0.0,\n }\n)\n\n\nmodel.basin.time = forcing\nmodel.basin.state = state\n\n\nmodel.write(datadir / \"basic_transient\")\n\nNow run the model with ribasim basic-transient/ribasim.toml. After running the model, read back the results:\n\ndf_basin = pd.read_feather(datadir / \"basic_transient/results/basin.arrow\")\ndf_basin_wide = df_basin.pivot_table(\n index=\"time\", columns=\"node_id\", values=[\"storage\", \"level\"]\n)\ndf_basin_wide[\"level\"].plot()\n\n<Axes: xlabel='time'>\n\n\n\n\n\n\ndf_flow = pd.read_feather(datadir / \"basic_transient/results/flow.arrow\")\ndf_flow[\"edge\"] = list(zip(df_flow.from_node_id, df_flow.to_node_id))\ndf_flow[\"flow_m3d\"] = df_flow.flow * 86400\nax = df_flow.pivot_table(index=\"time\", columns=\"edge\", values=\"flow_m3d\").plot()\nax.legend(bbox_to_anchor=(1.3, 1), title=\"Edge\")\n\n<matplotlib.legend.Legend at 0x7f79a7c98790>\n\n\n\n\n\n\ntype(df_flow)\n\npandas.core.frame.DataFrame\n\n\n\n\n3 Model with discrete control\nThe model constructed below consists of a single basin which slowly drains trough a TabulatedRatingCurve, but is held within a range around a target level (setpoint) by two connected pumps. These two pumps behave like a reversible pump. When pumping can be done in only one direction, and the other direction is only possible under gravity, use an Outlet for that direction.\nSet up the nodes:\n\nxy = np.array(\n [\n (0.0, 0.0), # 1: Basin\n (1.0, 1.0), # 2: Pump\n (1.0, -1.0), # 3: Pump\n (2.0, 0.0), # 4: LevelBoundary\n (-1.0, 0.0), # 5: TabulatedRatingCurve\n (-2.0, 0.0), # 6: Terminal\n (1.0, 0.0), # 7: DiscreteControl\n ]\n)\n\nnode_xy = gpd.points_from_xy(x=xy[:, 0], y=xy[:, 1])\n\nnode_type = [\n \"Basin\",\n \"Pump\",\n \"Pump\",\n \"LevelBoundary\",\n \"TabulatedRatingCurve\",\n \"Terminal\",\n \"DiscreteControl\",\n]\n\n# Make sure the feature id starts at 1: explicitly give an index.\nnode = ribasim.Node(\n static=gpd.GeoDataFrame(\n data={\"type\": node_type},\n index=pd.Index(np.arange(len(xy)) + 1, name=\"fid\"),\n geometry=node_xy,\n crs=\"EPSG:28992\",\n )\n)\n\nSetup the edges:\n\nfrom_id = np.array([1, 3, 4, 2, 1, 5, 7, 7], dtype=np.int64)\nto_id = np.array([3, 4, 2, 1, 5, 6, 2, 3], dtype=np.int64)\n\nedge_type = 6 * [\"flow\"] + 2 * [\"control\"]\n\nlines = ribasim.utils.geometry_from_connectivity(node, from_id, to_id)\nedge = ribasim.Edge(\n static=gpd.GeoDataFrame(\n data={\"from_node_id\": from_id, \"to_node_id\": to_id, \"edge_type\": edge_type},\n geometry=lines,\n crs=\"EPSG:28992\",\n )\n)\n\nSetup the basins:\n\nprofile = pd.DataFrame(\n data={\n \"node_id\": [1, 1],\n \"area\": [1000.0, 1000.0],\n \"level\": [0.0, 1.0],\n }\n)\n\nstatic = pd.DataFrame(\n data={\n \"node_id\": [1],\n \"drainage\": [0.0],\n \"potential_evaporation\": [0.0],\n \"infiltration\": [0.0],\n \"precipitation\": [0.0],\n \"urban_runoff\": [0.0],\n }\n)\n\nstate = pd.DataFrame(data={\"node_id\": [1], \"level\": [20.0]})\n\nbasin = ribasim.Basin(profile=profile, static=static, state=state)\n\nSetup the discrete control:\n\ncondition = pd.DataFrame(\n data={\n \"node_id\": 3 * [7],\n \"listen_feature_id\": 3 * [1],\n \"variable\": 3 * [\"level\"],\n \"greater_than\": [5.0, 10.0, 15.0], # min, setpoint, max\n }\n)\n\nlogic = pd.DataFrame(\n data={\n \"node_id\": 5 * [7],\n \"truth_state\": [\"FFF\", \"U**\", \"T*F\", \"**D\", \"TTT\"],\n \"control_state\": [\"in\", \"in\", \"none\", \"out\", \"out\"],\n }\n)\n\ndiscrete_control = ribasim.DiscreteControl(condition=condition, logic=logic)\n\nThe above control logic can be summarized as follows: - If the level gets above the maximum, activate the control state “out” until the setpoint is reached; - If the level gets below the minimum, active the control state “in” until the setpoint is reached; - Otherwise activate the control state “none”.\nSetup the pump:\n\npump = ribasim.Pump(\n static=pd.DataFrame(\n data={\n \"node_id\": 3 * [2] + 3 * [3],\n \"control_state\": 2 * [\"none\", \"in\", \"out\"],\n \"flow_rate\": [0.0, 2e-3, 0.0, 0.0, 0.0, 2e-3],\n }\n )\n)\n\nThe pump data defines the following:\n\n\n\nControl state\nPump #2 flow rate (m/s)\nPump #3 flow rate (m/s)\n\n\n\n\n“none”\n0.0\n0.0\n\n\n“in”\n2e-3\n0.0\n\n\n“out”\n0.0\n2e-3\n\n\n\nSetup the level boundary:\n\nlevel_boundary = ribasim.LevelBoundary(\n static=pd.DataFrame(data={\"node_id\": [4], \"level\": [10.0]})\n)\n\nSetup the rating curve:\n\nrating_curve = ribasim.TabulatedRatingCurve(\n static=pd.DataFrame(\n data={\"node_id\": 2 * [5], \"level\": [2.0, 15.0], \"discharge\": [0.0, 1e-3]}\n )\n)\n\nSetup the terminal:\n\nterminal = ribasim.Terminal(static=pd.DataFrame(data={\"node_id\": [6]}))\n\nSetup a model:\n\nmodel = ribasim.Model(\n node=node,\n edge=edge,\n basin=basin,\n pump=pump,\n level_boundary=level_boundary,\n tabulated_rating_curve=rating_curve,\n terminal=terminal,\n discrete_control=discrete_control,\n starttime=\"2020-01-01 00:00:00\",\n endtime=\"2021-01-01 00:00:00\",\n)\n\nLet’s take a look at the model:\n\nmodel.plot()\n\n<Axes: >\n\n\n\n\n\nListen edges are plotted with a dashed line since they are not present in the “Edge / static” schema but only in the “Control / condition” schema.\n\ndatadir = Path(\"data\")\nmodel.write(datadir / \"level_setpoint_with_minmax\")\n\nNow run the model with level_setpoint_with_minmax/ribasim.toml. After running the model, read back the results:\n\nfrom matplotlib.dates import date2num\n\ndf_basin = pd.read_feather(datadir / \"level_setpoint_with_minmax/results/basin.arrow\")\ndf_basin_wide = df_basin.pivot_table(\n index=\"time\", columns=\"node_id\", values=[\"storage\", \"level\"]\n)\n\nax = df_basin_wide[\"level\"].plot()\n\ngreater_than = model.discrete_control.condition.greater_than\n\nax.hlines(\n greater_than,\n df_basin.time[0],\n df_basin.time.max(),\n lw=1,\n ls=\"--\",\n color=\"k\",\n)\n\ndf_control = pd.read_feather(\n datadir / \"level_setpoint_with_minmax/results/control.arrow\"\n)\n\ny_min, y_max = ax.get_ybound()\nax.fill_between(df_control.time[:2], 2 * [y_min], 2 * [y_max], alpha=0.2, color=\"C0\")\nax.fill_between(df_control.time[2:4], 2 * [y_min], 2 * [y_max], alpha=0.2, color=\"C0\")\n\nax.set_xticks(\n date2num(df_control.time).tolist(),\n df_control.control_state.tolist(),\n rotation=50,\n)\n\nax.set_yticks(greater_than, [\"min\", \"setpoint\", \"max\"])\nax.set_ylabel(\"level\")\nplt.show()\n\n\n\n\nThe highlighted regions show where a pump is active.\nLet’s print an overview of what happened with control:\n\nmodel.print_discrete_control_record(\n datadir / \"level_setpoint_with_minmax/results/control.arrow\"\n)\n\n0. At 2020-01-01 00:00:00 the control node with ID 7 reached truth state TTT:\n For node ID 1 (Basin): level > 5.0\n For node ID 1 (Basin): level > 10.0\n For node ID 1 (Basin): level > 15.0\n\n This yielded control state \"out\":\n For node ID 2 (Pump): flow_rate = 0.0\n For node ID 3 (Pump): flow_rate = 0.002\n\n1. At 2020-02-09 01:17:29.324000 the control node with ID 7 reached truth state TFF:\n For node ID 1 (Basin): level > 5.0\n For node ID 1 (Basin): level < 10.0\n For node ID 1 (Basin): level < 15.0\n\n This yielded control state \"none\":\n For node ID 2 (Pump): flow_rate = 0.0\n For node ID 3 (Pump): flow_rate = 0.0\n\n2. At 2020-07-05 13:24:51.165000 the control node with ID 7 reached truth state FFF:\n For node ID 1 (Basin): level < 5.0\n For node ID 1 (Basin): level < 10.0\n For node ID 1 (Basin): level < 15.0\n\n This yielded control state \"in\":\n For node ID 2 (Pump): flow_rate = 0.002\n For node ID 3 (Pump): flow_rate = 0.0\n\n3. At 2020-08-11 11:49:59.015000 the control node with ID 7 reached truth state TTF:\n For node ID 1 (Basin): level > 5.0\n For node ID 1 (Basin): level > 10.0\n For node ID 1 (Basin): level < 15.0\n\n This yielded control state \"none\":\n For node ID 2 (Pump): flow_rate = 0.0\n For node ID 3 (Pump): flow_rate = 0.0\n\n\n\nNote that crossing direction specific truth states (containing “U”, “D”) are not present in this overview even though they are part of the control logic. This is because in the control logic for this model these truth states are only used to sustain control states, while the overview only shows changes in control states.\n\n\n4 Model with PID control\nSet up the nodes:\n\nxy = np.array(\n [\n (0.0, 0.0), # 1: FlowBoundary\n (1.0, 0.0), # 2: Basin\n (2.0, 0.5), # 3: Pump\n (3.0, 0.0), # 4: LevelBoundary\n (1.5, 1.0), # 5: PidControl\n (2.0, -0.5), # 6: outlet\n (1.5, -1.0), # 7: PidControl\n ]\n)\n\nnode_xy = gpd.points_from_xy(x=xy[:, 0], y=xy[:, 1])\n\nnode_type = [\n \"FlowBoundary\",\n \"Basin\",\n \"Pump\",\n \"LevelBoundary\",\n \"PidControl\",\n \"Outlet\",\n \"PidControl\",\n]\n\n# Make sure the feature id starts at 1: explicitly give an index.\nnode = ribasim.Node(\n static=gpd.GeoDataFrame(\n data={\"type\": node_type},\n index=pd.Index(np.arange(len(xy)) + 1, name=\"fid\"),\n geometry=node_xy,\n crs=\"EPSG:28992\",\n )\n)\n\nSetup the edges:\n\nfrom_id = np.array([1, 2, 3, 4, 6, 5, 7], dtype=np.int64)\nto_id = np.array([2, 3, 4, 6, 2, 3, 6], dtype=np.int64)\n\nlines = ribasim.utils.geometry_from_connectivity(node, from_id, to_id)\nedge = ribasim.Edge(\n static=gpd.GeoDataFrame(\n data={\n \"from_node_id\": from_id,\n \"to_node_id\": to_id,\n \"edge_type\": 5 * [\"flow\"] + 2 * [\"control\"],\n },\n geometry=lines,\n crs=\"EPSG:28992\",\n )\n)\n\nSetup the basins:\n\nprofile = pd.DataFrame(\n data={\"node_id\": [2, 2], \"level\": [0.0, 1.0], \"area\": [1000.0, 1000.0]}\n)\n\nstatic = pd.DataFrame(\n data={\n \"node_id\": [2],\n \"drainage\": [0.0],\n \"potential_evaporation\": [0.0],\n \"infiltration\": [0.0],\n \"precipitation\": [0.0],\n \"urban_runoff\": [0.0],\n }\n)\n\nstate = pd.DataFrame(\n data={\n \"node_id\": [2],\n \"level\": [6.0],\n }\n)\n\nbasin = ribasim.Basin(profile=profile, static=static, state=state)\n\nSetup the pump:\n\npump = ribasim.Pump(\n static=pd.DataFrame(\n data={\n \"node_id\": [3],\n \"flow_rate\": [0.0], # Will be overwritten by PID controller\n }\n )\n)\n\nSetup the outlet:\n\noutlet = ribasim.Outlet(\n static=pd.DataFrame(\n data={\n \"node_id\": [6],\n \"flow_rate\": [0.0], # Will be overwritten by PID controller\n }\n )\n)\n\nSetup flow boundary:\n\nflow_boundary = ribasim.FlowBoundary(\n static=pd.DataFrame(data={\"node_id\": [1], \"flow_rate\": [1e-3]})\n)\n\nSetup flow boundary:\n\nlevel_boundary = ribasim.LevelBoundary(\n static=pd.DataFrame(\n data={\n \"node_id\": [4],\n \"level\": [1.0], # Not relevant\n }\n )\n)\n\nSetup PID control:\n\npid_control = ribasim.PidControl(\n time=pd.DataFrame(\n data={\n \"node_id\": 4 * [5, 7],\n \"time\": [\n \"2020-01-01 00:00:00\",\n \"2020-01-01 00:00:00\",\n \"2020-05-01 00:00:00\",\n \"2020-05-01 00:00:00\",\n \"2020-07-01 00:00:00\",\n \"2020-07-01 00:00:00\",\n \"2020-12-01 00:00:00\",\n \"2020-12-01 00:00:00\",\n ],\n \"listen_node_id\": 4 * [2, 2],\n \"target\": [5.0, 5.0, 5.0, 5.0, 7.5, 7.5, 7.5, 7.5],\n \"proportional\": 4 * [-1e-3, 1e-3],\n \"integral\": 4 * [-1e-7, 1e-7],\n \"derivative\": 4 * [0.0, 0.0],\n }\n )\n)\n\nNote that the coefficients for the pump and the outlet are equal in magnitude but opposite in sign. This way the pump and the outlet equally work towards the same goal, while having opposite effects on the controlled basin due to their connectivity to this basin.\nSetup a model:\n\nmodel = ribasim.Model(\n node=node,\n edge=edge,\n basin=basin,\n flow_boundary=flow_boundary,\n level_boundary=level_boundary,\n pump=pump,\n outlet=outlet,\n pid_control=pid_control,\n starttime=\"2020-01-01 00:00:00\",\n endtime=\"2020-12-01 00:00:00\",\n)\n\nLet’s take a look at the model:\n\nmodel.plot()\n\n<Axes: >\n\n\n\n\n\nWrite the model to a TOML and GeoPackage:\n\ndatadir = Path(\"data\")\nmodel.write(datadir / \"pid_control\")\n\nNow run the model with ribasim pid_control/ribasim.toml. After running the model, read back the results:\n\nfrom matplotlib.dates import date2num\n\ndf_basin = pd.read_feather(datadir / \"pid_control/results/basin.arrow\")\ndf_basin_wide = df_basin.pivot_table(\n index=\"time\", columns=\"node_id\", values=[\"storage\", \"level\"]\n)\nax = df_basin_wide[\"level\"].plot()\nax.set_ylabel(\"level [m]\")\n\n# Plot target level\ntarget_levels = model.pid_control.time.target.to_numpy()[::2]\ntimes = date2num(model.pid_control.time.time)[::2]\nax.plot(times, target_levels, color=\"k\", ls=\":\", label=\"target level\")\npass" }, { "objectID": "python/reference/LinearResistance.html", @@ -937,6 +937,13 @@ "section": "1.1 Solver settings", "text": "1.1 Solver settings\nThe solver section in the configuration file is entirely optional, since we aim to use defaults that will generally work well. Common reasons to modify the solver settings are to adjust the calculation or result stepsizes: adaptive, dt, and saveat. If your model does not converge, or your performance is lower than expected, it can help to adjust other solver settings as well.\nThe default solver algorithm = \"QNDF\", which is a multistep method similar to Matlab’s ode15s (Shampine and Reichelt 1997). It is an implicit method that supports the default adaptive = true timestepping. The full list of available solvers is: QNDF, Rosenbrock23, TRBDF2, Rodas5, KenCarp4, Tsit5, RK4, ImplicitEuler, Euler. Information on the solver algorithms can be found on the ODE solvers page.\nThe dt controls the stepsize. When adaptive = true, dt only applies to the initial stepsize, and by default it is automatically determined. When adaptive = false a suitable dt must always be provided. The value is in seconds, so dt = 3600.0 corresponds to an hourly timestep. When adaptive = true, dtmin and dtmax control the minimum and maximum allowed dt. By default these depend on the problem and algorithm. If a smaller dt than dtmin is needed to meet the set error tolerances, the simulation stops, unless force_dtmin is set to true. force_dtmin is off by default to ensure an accurate solution.\nBy default the calculation and result stepsize are the same, with saveat = [], which will save every timestep. saveat can be a number, which is the saving interval in seconds, or it can be a list of numbers, which are the times in seconds since start that are saved. For instance, saveat = 86400.0 will save results after every day that passed.\nThe Jacobian matrix provides information about the local sensitivity of the model with respect to changes in the states. For implicit solvers it must be calculated often, which can be expensive to do. There are several methods to do this. By default Ribasim uses a Jacobian derived automatically using ForwardDiff.jl with memory management provided by PreallocationTools.jl. If this is not used by setting autodiff = false, the Jacobian is calculated with a finite difference method, which can be less accurate and more expensive.\nBy default the Jacobian matrix is a sparse matrix (sparse = true). Since each state typically only depends on a small number of other states, this is generally more efficient, especially for larger models. The sparsity structure is calculated from the network and provided as a Jacobian prototype to the solver. For small or highly connected models it could be faster to use a dense Jacobian matrix instead by setting sparse = false.\nThe total maximum number of iterations maxiters = 1e9, can normally stay as-is unless doing extremely long simulations.\nThe absolute and relative tolerance for adaptive timestepping can be set with abstol and reltol. For more information on these and other solver options, see the DifferentialEquations.jl docs." }, + { + "objectID": "core/usage.html#allocation-settings", + "href": "core/usage.html#allocation-settings", + "title": "Usage", + "section": "1.2 Allocation settings", + "text": "1.2 Allocation settings\nCurrently there are the following allocation settings: - use_allocation: A boolean which says whether allocation should be used or not; - timestep: a float value in seconds which dictates the update interval for allocations." + }, { "objectID": "core/usage.html#basin-time", "href": "core/usage.html#basin-time",