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.
Store information for a subnetwork used for allocation.
allocationnetworkid: The ID of this allocation network 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 allocgraphedgeidsuserdemand: AG user node ID => AG user inflow edge 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
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
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
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.
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
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
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
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
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 0.0 if it is not provided explicitly. record: Collected data of allocation optimizations for output file.
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.
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.
Add the flow capacity constraints to the allocation problem. Only finite capacities get a constraint. The constraint indices are the allocation graph edge IDs.
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
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.
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.
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.
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.
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.
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.
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.
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.
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.
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
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
Plot the nodes. Each node type is given a separate marker.
-
-
1.1.1 plot
-
Node.plot(ax=None, zorder=None)
-
Plot the nodes. Each node type is given a separate marker.
+
+
1.1.1 connectivity_from_geometry
+
Node.connectivity_from_geometry(lines)
+
Derive from_node_id and to_node_id for every edge in lines. LineStrings may be used to connect multiple nodes in a sequence, but every linestring vertex must also a node.
1.1.1.1 Parameters
+
+
+
Name
+
Type
+
Description
+
Default
+
+
+
+
+
node
+
Node
+
+
required
+
+
+
lines
+
np.ndarray
+
Array of shapely linestrings.
+
required
+
+
+
+
+
+
1.1.1.2 Returns
+
+
+
+
Type
+
Description
+
+
+
+
+
np.ndarray of int
+
+
+
+
np.ndarray of int
+
+
+
+
+
+
+
+
1.1.2 geometry_from_connectivity
+
Node.geometry_from_connectivity(from_id, to_id)
+
Create edge shapely geometries from connectivities.
+
+
1.1.2.1 Parameters
+
+
+
+
+
+
+
+
+
+
Name
+
Type
+
Description
+
Default
+
+
+
+
+
node
+
Ribasim.Node
+
+
required
+
+
+
from_id
+
Sequence[int]
+
First node of every edge.
+
required
+
+
+
to_id
+
Sequence[int]
+
Second node of every edge.
+
required
+
+
+
+
+
+
1.1.2.2 Returns
+
+
+
+
Type
+
Description
+
+
+
+
+
np.ndarray
+
Array of shapely LineStrings.
+
+
+
+
+
+
+
1.1.3 plot
+
Node.plot(ax=None, zorder=None)
+
Plot the nodes. Each node type is given a separate marker.
Derive from_node_id and to_node_id for every edge in lines. LineStrings may be used to connect multiple nodes in a sequence, but every linestring vertex must also a node.
Create edge shapely geometries from connectivities.
-
-
1.1 Parameters
-
-
-
-
-
-
-
-
-
-
Name
-
Type
-
Description
-
Default
-
-
-
-
-
node
-
Ribasim.Node
-
-
required
-
-
-
from_id
-
Sequence[int]
-
First node of every edge.
-
required
-
-
-
to_id
-
Sequence[int]
-
Second node of every edge.
-
required
-
-
-
-
-
-
1.2 Returns
-
-
-
-
Type
-
Description
-
-
-
-
-
np.ndarray
-
Array of shapely LineStrings.
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/schema/root.schema.json b/schema/root.schema.json
index ad7a55499..751f152f0 100644
--- a/schema/root.schema.json
+++ b/schema/root.schema.json
@@ -1,77 +1,77 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"properties": {
- "BasinProfile": {
- "$ref": "BasinProfile.schema.json"
+ "basinprofile": {
+ "$ref": "basinprofile.schema.json"
},
- "BasinState": {
- "$ref": "BasinState.schema.json"
+ "basinstate": {
+ "$ref": "basinstate.schema.json"
},
- "BasinStatic": {
- "$ref": "BasinStatic.schema.json"
+ "basinstatic": {
+ "$ref": "basinstatic.schema.json"
},
- "BasinTime": {
- "$ref": "BasinTime.schema.json"
+ "basintime": {
+ "$ref": "basintime.schema.json"
},
- "DiscreteControlCondition": {
- "$ref": "DiscreteControlCondition.schema.json"
+ "discretecontrolcondition": {
+ "$ref": "discretecontrolcondition.schema.json"
},
- "DiscreteControlLogic": {
- "$ref": "DiscreteControlLogic.schema.json"
+ "discretecontrollogic": {
+ "$ref": "discretecontrollogic.schema.json"
},
- "Edge": {
- "$ref": "Edge.schema.json"
+ "edge": {
+ "$ref": "edge.schema.json"
},
- "FlowBoundaryStatic": {
- "$ref": "FlowBoundaryStatic.schema.json"
+ "flowboundarystatic": {
+ "$ref": "flowboundarystatic.schema.json"
},
- "FlowBoundaryTime": {
- "$ref": "FlowBoundaryTime.schema.json"
+ "flowboundarytime": {
+ "$ref": "flowboundarytime.schema.json"
},
- "FractionalFlowStatic": {
- "$ref": "FractionalFlowStatic.schema.json"
+ "fractionalflowstatic": {
+ "$ref": "fractionalflowstatic.schema.json"
},
- "LevelBoundaryStatic": {
- "$ref": "LevelBoundaryStatic.schema.json"
+ "levelboundarystatic": {
+ "$ref": "levelboundarystatic.schema.json"
},
- "LevelBoundaryTime": {
- "$ref": "LevelBoundaryTime.schema.json"
+ "levelboundarytime": {
+ "$ref": "levelboundarytime.schema.json"
},
- "LinearResistanceStatic": {
- "$ref": "LinearResistanceStatic.schema.json"
+ "linearresistancestatic": {
+ "$ref": "linearresistancestatic.schema.json"
},
- "ManningResistanceStatic": {
- "$ref": "ManningResistanceStatic.schema.json"
+ "manningresistancestatic": {
+ "$ref": "manningresistancestatic.schema.json"
},
- "Node": {
- "$ref": "Node.schema.json"
+ "node": {
+ "$ref": "node.schema.json"
},
- "OutletStatic": {
- "$ref": "OutletStatic.schema.json"
+ "outletstatic": {
+ "$ref": "outletstatic.schema.json"
},
- "PidControlStatic": {
- "$ref": "PidControlStatic.schema.json"
+ "pidcontrolstatic": {
+ "$ref": "pidcontrolstatic.schema.json"
},
- "PidControlTime": {
- "$ref": "PidControlTime.schema.json"
+ "pidcontroltime": {
+ "$ref": "pidcontroltime.schema.json"
},
- "PumpStatic": {
- "$ref": "PumpStatic.schema.json"
+ "pumpstatic": {
+ "$ref": "pumpstatic.schema.json"
},
- "TabulatedRatingCurveStatic": {
- "$ref": "TabulatedRatingCurveStatic.schema.json"
+ "tabulatedratingcurvestatic": {
+ "$ref": "tabulatedratingcurvestatic.schema.json"
},
- "TabulatedRatingCurveTime": {
- "$ref": "TabulatedRatingCurveTime.schema.json"
+ "tabulatedratingcurvetime": {
+ "$ref": "tabulatedratingcurvetime.schema.json"
},
- "TerminalStatic": {
- "$ref": "TerminalStatic.schema.json"
+ "terminalstatic": {
+ "$ref": "terminalstatic.schema.json"
},
- "UserStatic": {
- "$ref": "UserStatic.schema.json"
+ "userstatic": {
+ "$ref": "userstatic.schema.json"
},
- "UserTime": {
- "$ref": "UserTime.schema.json"
+ "usertime": {
+ "$ref": "usertime.schema.json"
}
},
"$id": "https://deltares.github.io/Ribasim/schema/root.schema.json",
diff --git a/search.json b/search.json
index 584229220..2516c5195 100644
--- a/search.json
+++ b/search.json
@@ -277,91 +277,56 @@
"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.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 df=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 df=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 network=ribasim.Network(\n node=node,\n edge=edge,\n ),\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\nPosixPath('data/basic/ribasim.toml')\n\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\": pd.date_range(model.starttime, model.endtime),\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.df[\"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.df = forcing\nmodel.basin.state.df = state\n\n\nmodel.write(datadir / \"basic_transient\")\n\nPosixPath('data/basic_transient/ribasim.toml')\n\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 0x7fe34307b8f0>\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 df=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 df=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 network=ribasim.Network(\n node=node,\n edge=edge,\n ),\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\nPosixPath('data/level_setpoint_with_minmax/ribasim.toml')\n\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.df.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-08 19:02:21.861000 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 08:56:10.319000 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 06:05:15.592000 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 df=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 df=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 network=ribasim.Network(\n node=node,\n edge=edge,\n ),\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\nPosixPath('data/pid_control/ribasim.toml')\n\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.df.target.to_numpy()[::2]\ntimes = date2num(model.pid_control.time.df.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.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 df=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 = node.geometry_from_connectivity(from_id, to_id)\nedge = ribasim.Edge(\n df=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 network=ribasim.Network(\n node=node,\n edge=edge,\n ),\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\nPosixPath('data/basic/ribasim.toml')\n\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\": pd.date_range(model.starttime, model.endtime),\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.df[\"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.df = forcing\nmodel.basin.state.df = state\n\n\nmodel.write(datadir / \"basic_transient\")\n\nPosixPath('data/basic_transient/ribasim.toml')\n\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 0x7fda1c9f33e0>\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 df=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 = node.geometry_from_connectivity(from_id, to_id)\nedge = ribasim.Edge(\n df=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 network=ribasim.Network(\n node=node,\n edge=edge,\n ),\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\nPosixPath('data/level_setpoint_with_minmax/ribasim.toml')\n\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.df.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-08 19:02:21.861000 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 08:56:10.319000 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 06:05:15.592000 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 df=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 = node.geometry_from_connectivity(from_id, to_id)\nedge = ribasim.Edge(\n df=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 network=ribasim.Network(\n node=node,\n edge=edge,\n ),\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\nPosixPath('data/pid_control/ribasim.toml')\n\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.df.target.to_numpy()[::2]\ntimes = date2num(model.pid_control.time.df.time)[::2]\nax.plot(times, target_levels, color=\"k\", ls=\":\", label=\"target level\")\npass"
},
{
- "objectID": "python/reference/utils.connectivity_from_geometry.html",
- "href": "python/reference/utils.connectivity_from_geometry.html",
- "title": "1 utils.connectivity_from_geometry",
- "section": "",
- "text": "utils.connectivity_from_geometry(node, lines)\nDerive from_node_id and to_node_id for every edge in lines. LineStrings may be used to connect multiple nodes in a sequence, but every linestring vertex must also a node.\n\n\n\n\n\nName\nType\nDescription\nDefault\n\n\n\n\nnode\nNode\n\nrequired\n\n\nlines\nnp.ndarray\nArray of shapely linestrings.\nrequired\n\n\n\n\n\n\n\n\n\nType\nDescription\n\n\n\n\nnp.ndarray of int\n\n\n\nnp.ndarray of int"
- },
- {
- "objectID": "python/reference/utils.connectivity_from_geometry.html#parameters",
- "href": "python/reference/utils.connectivity_from_geometry.html#parameters",
- "title": "1 utils.connectivity_from_geometry",
- "section": "",
- "text": "Name\nType\nDescription\nDefault\n\n\n\n\nnode\nNode\n\nrequired\n\n\nlines\nnp.ndarray\nArray of shapely linestrings.\nrequired"
- },
- {
- "objectID": "python/reference/utils.connectivity_from_geometry.html#returns",
- "href": "python/reference/utils.connectivity_from_geometry.html#returns",
- "title": "1 utils.connectivity_from_geometry",
- "section": "",
- "text": "Type\nDescription\n\n\n\n\nnp.ndarray of int\n\n\n\nnp.ndarray of int"
- },
- {
- "objectID": "python/reference/DiscreteControl.html",
- "href": "python/reference/DiscreteControl.html",
- "title": "1 DiscreteControl",
- "section": "",
- "text": "1 DiscreteControl\nDiscreteControl()"
- },
- {
- "objectID": "python/reference/Node.html",
- "href": "python/reference/Node.html",
- "title": "1 Node",
- "section": "",
- "text": "Node()\nThe Ribasim nodes as Point geometries.\n\n\n\n\n\nName\nDescription\n\n\n\n\nplot\nPlot the nodes. Each node type is given a separate marker.\n\n\n\n\n\nNode.plot(ax=None, zorder=None)\nPlot the nodes. Each node type is given a separate marker.\n\n\n\n\n\n\n\n\n\n\n\nName\nType\nDescription\nDefault\n\n\n\n\nax\nOptional\nThe axis on which the nodes will be plotted.\nNone\n\n\n\n\n\n\n\n\n\nType\nDescription\n\n\n\n\nNone"
- },
- {
- "objectID": "python/reference/Node.html#methods",
- "href": "python/reference/Node.html#methods",
- "title": "1 Node",
+ "objectID": "python/reference/Pump.html",
+ "href": "python/reference/Pump.html",
+ "title": "1 Pump",
"section": "",
- "text": "Name\nDescription\n\n\n\n\nplot\nPlot the nodes. Each node type is given a separate marker.\n\n\n\n\n\nNode.plot(ax=None, zorder=None)\nPlot the nodes. Each node type is given a separate marker.\n\n\n\n\n\n\n\n\n\n\n\nName\nType\nDescription\nDefault\n\n\n\n\nax\nOptional\nThe axis on which the nodes will be plotted.\nNone\n\n\n\n\n\n\n\n\n\nType\nDescription\n\n\n\n\nNone"
+ "text": "1 Pump\nPump()"
},
{
- "objectID": "python/reference/User.html",
- "href": "python/reference/User.html",
- "title": "1 User",
+ "objectID": "python/reference/LinearResistance.html",
+ "href": "python/reference/LinearResistance.html",
+ "title": "1 LinearResistance",
"section": "",
- "text": "1 User\nUser()"
+ "text": "1 LinearResistance\nLinearResistance()"
},
{
- "objectID": "python/reference/Edge.html",
- "href": "python/reference/Edge.html",
- "title": "1 Edge",
+ "objectID": "python/reference/Terminal.html",
+ "href": "python/reference/Terminal.html",
+ "title": "1 Terminal",
"section": "",
- "text": "Edge()\nDefines the connections between nodes.\n\n\n\n\n\n\n\n\n\n\n\nName\nType\nDescription\nDefault\n\n\n\n\nstatic\npandas.DataFrame\nTable describing the flow connections.\nrequired"
+ "text": "1 Terminal\nTerminal()"
},
{
- "objectID": "python/reference/Edge.html#parameters",
- "href": "python/reference/Edge.html#parameters",
- "title": "1 Edge",
+ "objectID": "python/reference/PidControl.html",
+ "href": "python/reference/PidControl.html",
+ "title": "1 PidControl",
"section": "",
- "text": "Name\nType\nDescription\nDefault\n\n\n\n\nstatic\npandas.DataFrame\nTable describing the flow connections.\nrequired"
+ "text": "1 PidControl\nPidControl()"
},
{
- "objectID": "python/reference/utils.geometry_from_connectivity.html",
- "href": "python/reference/utils.geometry_from_connectivity.html",
- "title": "1 utils.geometry_from_connectivity",
+ "objectID": "python/reference/Model.html",
+ "href": "python/reference/Model.html",
+ "title": "1 Model",
"section": "",
- "text": "utils.geometry_from_connectivity(node, from_id, to_id)\nCreate edge shapely geometries from connectivities.\n\n\n\n\n\n\n\n\n\n\n\nName\nType\nDescription\nDefault\n\n\n\n\nnode\nRibasim.Node\n\nrequired\n\n\nfrom_id\nSequence[int]\nFirst node of every edge.\nrequired\n\n\nto_id\nSequence[int]\nSecond node of every edge.\nrequired\n\n\n\n\n\n\n\n\n\nType\nDescription\n\n\n\n\nnp.ndarray\nArray of shapely LineStrings."
+ "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\nstarttime\ndatetime.datetime\nStarting time of the simulation.\nrequired\n\n\nendtime\ndatetime.datetime\nEnd time of the simulation.\nrequired\n\n\nupdate_timestep\n\nThe output time step of the simulation in seconds (default of 1 day)\nrequired\n\n\nrelative_dir\n\nThe relative directory of the input files.\nrequired\n\n\ninput_dir\n\nThe directory of the input files.\nrequired\n\n\nresults_dir\n\nThe directory of the results files.\nrequired\n\n\nnetwork\n\nClass containing the topology (nodes and edges) of the model.\nrequired\n\n\nresults\n\nResults configuration options.\nrequired\n\n\nsolver\n\nSolver configuration options.\nrequired\n\n\nlogging\n\nLogging configuration options.\nrequired\n\n\nallocation\n\nThe allocation configuration.\nrequired\n\n\nbasin\nBasin\nThe waterbodies.\nrequired\n\n\nfractional_flow\nFractionalFlow\nSplit flows into fractions.\nrequired\n\n\nlevel_boundary\nLevelBoundary\nBoundary condition specifying the water level.\nrequired\n\n\nflow_boundary\nFlowBoundary\nBoundary conditions specifying the flow.\nrequired\n\n\nlinear_resistance\n\nLinear flow resistance.\nrequired\n\n\nmanning_resistance\nManningResistance\nFlow resistance based on the Manning formula.\nrequired\n\n\ntabulated_rating_curve\nTabulatedRatingCurve\nTabulated rating curve describing flow based on the upstream water level.\nrequired\n\n\npump\nPump\nPrescribed flow rate from one basin to the other.\nrequired\n\n\noutlet\nOutlet\nPrescribed flow rate from one basin to the other.\nrequired\n\n\nterminal\nTerminal\nWater sink without state or properties.\nrequired\n\n\ndiscrete_control\nDiscreteControl\nDiscrete control logic.\nrequired\n\n\npid_control\nPidControl\nPID controller attempting to set the level of a basin to a desired value using a pump/outlet.\nrequired\n\n\nuser\nUser\nUser node type with demand and priority.\nrequired\n\n\n\n\n\n\n\n\n\nName\nDescription\n\n\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\nvalidate_model\nValidate the model.\n\n\nvalidate_model_node_field_ids\nCheck whether the node IDs of the node_type fields are valid.\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\nwrite\nWrite the contents of the model to a database and a TOML configuration file.\n\n\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.validate_model()\nValidate the model.\nChecks: - 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_field_ids()\nCheck whether the node IDs of the node_type fields are valid.\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.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/utils.geometry_from_connectivity.html#parameters",
- "href": "python/reference/utils.geometry_from_connectivity.html#parameters",
- "title": "1 utils.geometry_from_connectivity",
+ "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\nRibasim.Node\n\nrequired\n\n\nfrom_id\nSequence[int]\nFirst node of every edge.\nrequired\n\n\nto_id\nSequence[int]\nSecond node of every edge.\nrequired"
+ "text": "Name\nType\nDescription\nDefault\n\n\n\n\nstarttime\ndatetime.datetime\nStarting time of the simulation.\nrequired\n\n\nendtime\ndatetime.datetime\nEnd time of the simulation.\nrequired\n\n\nupdate_timestep\n\nThe output time step of the simulation in seconds (default of 1 day)\nrequired\n\n\nrelative_dir\n\nThe relative directory of the input files.\nrequired\n\n\ninput_dir\n\nThe directory of the input files.\nrequired\n\n\nresults_dir\n\nThe directory of the results files.\nrequired\n\n\nnetwork\n\nClass containing the topology (nodes and edges) of the model.\nrequired\n\n\nresults\n\nResults configuration options.\nrequired\n\n\nsolver\n\nSolver configuration options.\nrequired\n\n\nlogging\n\nLogging configuration options.\nrequired\n\n\nallocation\n\nThe allocation configuration.\nrequired\n\n\nbasin\nBasin\nThe waterbodies.\nrequired\n\n\nfractional_flow\nFractionalFlow\nSplit flows into fractions.\nrequired\n\n\nlevel_boundary\nLevelBoundary\nBoundary condition specifying the water level.\nrequired\n\n\nflow_boundary\nFlowBoundary\nBoundary conditions specifying the flow.\nrequired\n\n\nlinear_resistance\n\nLinear flow resistance.\nrequired\n\n\nmanning_resistance\nManningResistance\nFlow resistance based on the Manning formula.\nrequired\n\n\ntabulated_rating_curve\nTabulatedRatingCurve\nTabulated rating curve describing flow based on the upstream water level.\nrequired\n\n\npump\nPump\nPrescribed flow rate from one basin to the other.\nrequired\n\n\noutlet\nOutlet\nPrescribed flow rate from one basin to the other.\nrequired\n\n\nterminal\nTerminal\nWater sink without state or properties.\nrequired\n\n\ndiscrete_control\nDiscreteControl\nDiscrete control logic.\nrequired\n\n\npid_control\nPidControl\nPID controller attempting to set the level of a basin to a desired value using a pump/outlet.\nrequired\n\n\nuser\nUser\nUser node type with demand and priority.\nrequired"
},
{
- "objectID": "python/reference/utils.geometry_from_connectivity.html#returns",
- "href": "python/reference/utils.geometry_from_connectivity.html#returns",
- "title": "1 utils.geometry_from_connectivity",
+ "objectID": "python/reference/Model.html#methods",
+ "href": "python/reference/Model.html#methods",
+ "title": "1 Model",
"section": "",
- "text": "Type\nDescription\n\n\n\n\nnp.ndarray\nArray of shapely LineStrings."
+ "text": "Name\nDescription\n\n\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\nvalidate_model\nValidate the model.\n\n\nvalidate_model_node_field_ids\nCheck whether the node IDs of the node_type fields are valid.\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\nwrite\nWrite the contents of the model to a database and a TOML configuration file.\n\n\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.validate_model()\nValidate the model.\nChecks: - 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_field_ids()\nCheck whether the node IDs of the node_type fields are valid.\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.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/LevelBoundary.html",
@@ -459,7 +424,7 @@
"href": "python/reference/index.html",
"title": "1 API Reference",
"section": "",
- "text": "The Model class represents an entire Ribasim model.\n\n\n\nModel\nA full Ribasim model schematisation with all input.\n\n\n\n\n\n\nThe Node and Edge database layers define the network layout.\n\n\n\nNode\nThe Ribasim nodes as Point geometries.\n\n\nEdge\nDefines the connections between nodes.\n\n\n\n\n\n\nAvailable node types to model different situations.\n\n\n\nBasin\n\n\n\nFractionalFlow\n\n\n\nTabulatedRatingCurve\n\n\n\nPump\n\n\n\nOutlet\n\n\n\nUser\n\n\n\nLevelBoundary\n\n\n\nFlowBoundary\n\n\n\nLinearResistance\n\n\n\nManningResistance\n\n\n\nTerminal\n\n\n\nDiscreteControl\n\n\n\nPidControl\n\n\n\n\n\n\n\nCollection of utility functions.\n\n\n\nutils.geometry_from_connectivity\nCreate edge shapely geometries from connectivities.\n\n\nutils.connectivity_from_geometry\nDerive from_node_id and to_node_id for every edge in lines. LineStrings"
+ "text": "The Model class represents an entire Ribasim model.\n\n\n\nModel\nA full Ribasim model schematisation with all input.\n\n\n\n\n\n\nThe Node and Edge database layers define the network layout.\n\n\n\nNode\nThe Ribasim nodes as Point geometries.\n\n\nEdge\nDefines the connections between nodes.\n\n\n\n\n\n\nAvailable node types to model different situations.\n\n\n\nBasin\n\n\n\nFractionalFlow\n\n\n\nTabulatedRatingCurve\n\n\n\nPump\n\n\n\nOutlet\n\n\n\nUser\n\n\n\nLevelBoundary\n\n\n\nFlowBoundary\n\n\n\nLinearResistance\n\n\n\nManningResistance\n\n\n\nTerminal\n\n\n\nDiscreteControl\n\n\n\nPidControl"
},
{
"objectID": "python/reference/index.html#model",
@@ -483,60 +448,46 @@
"text": "Available node types to model different situations.\n\n\n\nBasin\n\n\n\nFractionalFlow\n\n\n\nTabulatedRatingCurve\n\n\n\nPump\n\n\n\nOutlet\n\n\n\nUser\n\n\n\nLevelBoundary\n\n\n\nFlowBoundary\n\n\n\nLinearResistance\n\n\n\nManningResistance\n\n\n\nTerminal\n\n\n\nDiscreteControl\n\n\n\nPidControl"
},
{
- "objectID": "python/reference/index.html#utility-functions",
- "href": "python/reference/index.html#utility-functions",
- "title": "1 API Reference",
- "section": "",
- "text": "Collection of utility functions.\n\n\n\nutils.geometry_from_connectivity\nCreate edge shapely geometries from connectivities.\n\n\nutils.connectivity_from_geometry\nDerive from_node_id and to_node_id for every edge in lines. LineStrings"
- },
- {
- "objectID": "python/reference/Model.html",
- "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\nstarttime\ndatetime.datetime\nStarting time of the simulation.\nrequired\n\n\nendtime\ndatetime.datetime\nEnd time of the simulation.\nrequired\n\n\nupdate_timestep\n\nThe output time step of the simulation in seconds (default of 1 day)\nrequired\n\n\nrelative_dir\n\nThe relative directory of the input files.\nrequired\n\n\ninput_dir\n\nThe directory of the input files.\nrequired\n\n\nresults_dir\n\nThe directory of the results files.\nrequired\n\n\nnetwork\n\nClass containing the topology (nodes and edges) of the model.\nrequired\n\n\nresults\n\nResults configuration options.\nrequired\n\n\nsolver\n\nSolver configuration options.\nrequired\n\n\nlogging\n\nLogging configuration options.\nrequired\n\n\nallocation\n\nThe allocation configuration.\nrequired\n\n\nbasin\nBasin\nThe waterbodies.\nrequired\n\n\nfractional_flow\nFractionalFlow\nSplit flows into fractions.\nrequired\n\n\nlevel_boundary\nLevelBoundary\nBoundary condition specifying the water level.\nrequired\n\n\nflow_boundary\nFlowBoundary\nBoundary conditions specifying the flow.\nrequired\n\n\nlinear_resistance\n\nLinear flow resistance.\nrequired\n\n\nmanning_resistance\nManningResistance\nFlow resistance based on the Manning formula.\nrequired\n\n\ntabulated_rating_curve\nTabulatedRatingCurve\nTabulated rating curve describing flow based on the upstream water level.\nrequired\n\n\npump\nPump\nPrescribed flow rate from one basin to the other.\nrequired\n\n\noutlet\nOutlet\nPrescribed flow rate from one basin to the other.\nrequired\n\n\nterminal\nTerminal\nWater sink without state or properties.\nrequired\n\n\ndiscrete_control\nDiscreteControl\nDiscrete control logic.\nrequired\n\n\npid_control\nPidControl\nPID controller attempting to set the level of a basin to a desired value using a pump/outlet.\nrequired\n\n\nuser\nUser\nUser node type with demand and priority.\nrequired\n\n\n\n\n\n\n\n\n\nName\nDescription\n\n\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\nvalidate_model\nValidate the model.\n\n\nvalidate_model_node_field_ids\nCheck whether the node IDs of the node_type fields are valid.\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\nwrite\nWrite the contents of the model to a database and a TOML configuration file.\n\n\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.validate_model()\nValidate the model.\nChecks: - 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_field_ids()\nCheck whether the node IDs of the node_type fields are valid.\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.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",
+ "objectID": "python/reference/Edge.html",
+ "href": "python/reference/Edge.html",
+ "title": "1 Edge",
"section": "",
- "text": "Name\nType\nDescription\nDefault\n\n\n\n\nstarttime\ndatetime.datetime\nStarting time of the simulation.\nrequired\n\n\nendtime\ndatetime.datetime\nEnd time of the simulation.\nrequired\n\n\nupdate_timestep\n\nThe output time step of the simulation in seconds (default of 1 day)\nrequired\n\n\nrelative_dir\n\nThe relative directory of the input files.\nrequired\n\n\ninput_dir\n\nThe directory of the input files.\nrequired\n\n\nresults_dir\n\nThe directory of the results files.\nrequired\n\n\nnetwork\n\nClass containing the topology (nodes and edges) of the model.\nrequired\n\n\nresults\n\nResults configuration options.\nrequired\n\n\nsolver\n\nSolver configuration options.\nrequired\n\n\nlogging\n\nLogging configuration options.\nrequired\n\n\nallocation\n\nThe allocation configuration.\nrequired\n\n\nbasin\nBasin\nThe waterbodies.\nrequired\n\n\nfractional_flow\nFractionalFlow\nSplit flows into fractions.\nrequired\n\n\nlevel_boundary\nLevelBoundary\nBoundary condition specifying the water level.\nrequired\n\n\nflow_boundary\nFlowBoundary\nBoundary conditions specifying the flow.\nrequired\n\n\nlinear_resistance\n\nLinear flow resistance.\nrequired\n\n\nmanning_resistance\nManningResistance\nFlow resistance based on the Manning formula.\nrequired\n\n\ntabulated_rating_curve\nTabulatedRatingCurve\nTabulated rating curve describing flow based on the upstream water level.\nrequired\n\n\npump\nPump\nPrescribed flow rate from one basin to the other.\nrequired\n\n\noutlet\nOutlet\nPrescribed flow rate from one basin to the other.\nrequired\n\n\nterminal\nTerminal\nWater sink without state or properties.\nrequired\n\n\ndiscrete_control\nDiscreteControl\nDiscrete control logic.\nrequired\n\n\npid_control\nPidControl\nPID controller attempting to set the level of a basin to a desired value using a pump/outlet.\nrequired\n\n\nuser\nUser\nUser node type with demand and priority.\nrequired"
+ "text": "Edge()\nDefines the connections between nodes.\n\n\n\n\n\n\n\n\n\n\n\nName\nType\nDescription\nDefault\n\n\n\n\nstatic\npandas.DataFrame\nTable describing the flow connections.\nrequired"
},
{
- "objectID": "python/reference/Model.html#methods",
- "href": "python/reference/Model.html#methods",
- "title": "1 Model",
+ "objectID": "python/reference/Edge.html#parameters",
+ "href": "python/reference/Edge.html#parameters",
+ "title": "1 Edge",
"section": "",
- "text": "Name\nDescription\n\n\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\nvalidate_model\nValidate the model.\n\n\nvalidate_model_node_field_ids\nCheck whether the node IDs of the node_type fields are valid.\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\nwrite\nWrite the contents of the model to a database and a TOML configuration file.\n\n\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.validate_model()\nValidate the model.\nChecks: - 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_field_ids()\nCheck whether the node IDs of the node_type fields are valid.\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.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": "Name\nType\nDescription\nDefault\n\n\n\n\nstatic\npandas.DataFrame\nTable describing the flow connections.\nrequired"
},
{
- "objectID": "python/reference/PidControl.html",
- "href": "python/reference/PidControl.html",
- "title": "1 PidControl",
+ "objectID": "python/reference/User.html",
+ "href": "python/reference/User.html",
+ "title": "1 User",
"section": "",
- "text": "1 PidControl\nPidControl()"
+ "text": "1 User\nUser()"
},
{
- "objectID": "python/reference/Terminal.html",
- "href": "python/reference/Terminal.html",
- "title": "1 Terminal",
+ "objectID": "python/reference/Node.html",
+ "href": "python/reference/Node.html",
+ "title": "1 Node",
"section": "",
- "text": "1 Terminal\nTerminal()"
+ "text": "Node()\nThe Ribasim nodes as Point geometries.\n\n\n\n\n\nName\nDescription\n\n\n\n\nconnectivity_from_geometry\nDerive from_node_id and to_node_id for every edge in lines. LineStrings\n\n\ngeometry_from_connectivity\nCreate edge shapely geometries from connectivities.\n\n\nplot\nPlot the nodes. Each node type is given a separate marker.\n\n\n\n\n\nNode.connectivity_from_geometry(lines)\nDerive from_node_id and to_node_id for every edge in lines. LineStrings may be used to connect multiple nodes in a sequence, but every linestring vertex must also a node.\n\n\n\n\n\nName\nType\nDescription\nDefault\n\n\n\n\nnode\nNode\n\nrequired\n\n\nlines\nnp.ndarray\nArray of shapely linestrings.\nrequired\n\n\n\n\n\n\n\n\n\nType\nDescription\n\n\n\n\nnp.ndarray of int\n\n\n\nnp.ndarray of int\n\n\n\n\n\n\n\n\nNode.geometry_from_connectivity(from_id, to_id)\nCreate edge shapely geometries from connectivities.\n\n\n\n\n\n\n\n\n\n\n\nName\nType\nDescription\nDefault\n\n\n\n\nnode\nRibasim.Node\n\nrequired\n\n\nfrom_id\nSequence[int]\nFirst node of every edge.\nrequired\n\n\nto_id\nSequence[int]\nSecond node of every edge.\nrequired\n\n\n\n\n\n\n\n\n\nType\nDescription\n\n\n\n\nnp.ndarray\nArray of shapely LineStrings.\n\n\n\n\n\n\n\nNode.plot(ax=None, zorder=None)\nPlot the nodes. Each node type is given a separate marker.\n\n\n\n\n\n\n\n\n\n\n\nName\nType\nDescription\nDefault\n\n\n\n\nax\nOptional\nThe axis on which the nodes will be plotted.\nNone\n\n\n\n\n\n\n\n\n\nType\nDescription\n\n\n\n\nNone"
},
{
- "objectID": "python/reference/LinearResistance.html",
- "href": "python/reference/LinearResistance.html",
- "title": "1 LinearResistance",
+ "objectID": "python/reference/Node.html#methods",
+ "href": "python/reference/Node.html#methods",
+ "title": "1 Node",
"section": "",
- "text": "1 LinearResistance\nLinearResistance()"
+ "text": "Name\nDescription\n\n\n\n\nconnectivity_from_geometry\nDerive from_node_id and to_node_id for every edge in lines. LineStrings\n\n\ngeometry_from_connectivity\nCreate edge shapely geometries from connectivities.\n\n\nplot\nPlot the nodes. Each node type is given a separate marker.\n\n\n\n\n\nNode.connectivity_from_geometry(lines)\nDerive from_node_id and to_node_id for every edge in lines. LineStrings may be used to connect multiple nodes in a sequence, but every linestring vertex must also a node.\n\n\n\n\n\nName\nType\nDescription\nDefault\n\n\n\n\nnode\nNode\n\nrequired\n\n\nlines\nnp.ndarray\nArray of shapely linestrings.\nrequired\n\n\n\n\n\n\n\n\n\nType\nDescription\n\n\n\n\nnp.ndarray of int\n\n\n\nnp.ndarray of int\n\n\n\n\n\n\n\n\nNode.geometry_from_connectivity(from_id, to_id)\nCreate edge shapely geometries from connectivities.\n\n\n\n\n\n\n\n\n\n\n\nName\nType\nDescription\nDefault\n\n\n\n\nnode\nRibasim.Node\n\nrequired\n\n\nfrom_id\nSequence[int]\nFirst node of every edge.\nrequired\n\n\nto_id\nSequence[int]\nSecond node of every edge.\nrequired\n\n\n\n\n\n\n\n\n\nType\nDescription\n\n\n\n\nnp.ndarray\nArray of shapely LineStrings.\n\n\n\n\n\n\n\nNode.plot(ax=None, zorder=None)\nPlot the nodes. Each node type is given a separate marker.\n\n\n\n\n\n\n\n\n\n\n\nName\nType\nDescription\nDefault\n\n\n\n\nax\nOptional\nThe axis on which the nodes will be plotted.\nNone\n\n\n\n\n\n\n\n\n\nType\nDescription\n\n\n\n\nNone"
},
{
- "objectID": "python/reference/Pump.html",
- "href": "python/reference/Pump.html",
- "title": "1 Pump",
+ "objectID": "python/reference/DiscreteControl.html",
+ "href": "python/reference/DiscreteControl.html",
+ "title": "1 DiscreteControl",
"section": "",
- "text": "1 Pump\nPump()"
+ "text": "1 DiscreteControl\nDiscreteControl()"
},
{
"objectID": "python/index.html",
@@ -725,7 +676,7 @@
"href": "core/equations.html#sec-reduction_factor",
"title": "Equations",
"section": "2.1 The reduction factor",
- "text": "2.1 The reduction factor\nAt several points in the equations below a reduction factor is used. This is a term that makes certain transitions more smooth, for instance when a pump stops providing water when its source basin dries up. The reduction factor is given by\n\\[\\begin{align}\n \\phi(x; p) =\n \\begin{cases}\n 0 &\\text{if}\\quad x < 0 \\\\\n -2 \\left(\\frac{x}{p}\\right)^3 + 3\\left(\\frac{x}{p}\\right)^2 &\\text{if}\\quad 0 \\le x \\le p \\\\\n 1 &\\text{if}\\quad x > p\n \\end{cases}\n\\end{align}\\]\nHere \\(p > 0\\) is the threshold value which determines the interval \\([0,p]\\) of the smooth transition between \\(0\\) and \\(1\\), see the plot below.\n\n\nCode\nimport numpy as np\nimport matplotlib.pyplot as plt\n\ndef f(x, p = 3):\n x_scaled = x / p\n phi = (-2 * x_scaled + 3) * x_scaled**2\n phi = np.where(x < 0, 0, phi)\n phi = np.where(x > p, 1, phi)\n\n return phi\n\nfontsize = 15\np = 3\nN = 100\nx_min = -1\nx_max = 4\nx = np.linspace(x_min,x_max,N)\nphi = f(x,p)\n\nfig,ax = plt.subplots(dpi=80)\nax.plot(x,phi)\n\ny_lim = ax.get_ylim()\n\nax.set_xticks([0,p], [0,\"$p$\"], fontsize=fontsize)\nax.set_yticks([0,1], [0,1], fontsize=fontsize)\nax.hlines([0,1],x_min,x_max, color = \"k\", ls = \":\", zorder=-1)\nax.vlines([0,p], *y_lim, color = \"k\", ls = \":\")\nax.set_xlim(x_min,x_max)\nax.set_xlabel(\"$x$\", fontsize=fontsize)\nax.set_ylabel(\"$\\phi(x;p)$\", fontsize=fontsize)\nax.set_ylim(y_lim)\n\nfig.tight_layout()\nplt.show()\n\n\n<>:31: SyntaxWarning: invalid escape sequence '\\p'\n<>:31: SyntaxWarning: invalid escape sequence '\\p'\n/tmp/ipykernel_4431/665069857.py:31: SyntaxWarning: invalid escape sequence '\\p'\n ax.set_ylabel(\"$\\phi(x;p)$\", fontsize=fontsize)"
+ "text": "2.1 The reduction factor\nAt several points in the equations below a reduction factor is used. This is a term that makes certain transitions more smooth, for instance when a pump stops providing water when its source basin dries up. The reduction factor is given by\n\\[\\begin{align}\n \\phi(x; p) =\n \\begin{cases}\n 0 &\\text{if}\\quad x < 0 \\\\\n -2 \\left(\\frac{x}{p}\\right)^3 + 3\\left(\\frac{x}{p}\\right)^2 &\\text{if}\\quad 0 \\le x \\le p \\\\\n 1 &\\text{if}\\quad x > p\n \\end{cases}\n\\end{align}\\]\nHere \\(p > 0\\) is the threshold value which determines the interval \\([0,p]\\) of the smooth transition between \\(0\\) and \\(1\\), see the plot below.\n\n\nCode\nimport numpy as np\nimport matplotlib.pyplot as plt\n\ndef f(x, p = 3):\n x_scaled = x / p\n phi = (-2 * x_scaled + 3) * x_scaled**2\n phi = np.where(x < 0, 0, phi)\n phi = np.where(x > p, 1, phi)\n\n return phi\n\nfontsize = 15\np = 3\nN = 100\nx_min = -1\nx_max = 4\nx = np.linspace(x_min,x_max,N)\nphi = f(x,p)\n\nfig,ax = plt.subplots(dpi=80)\nax.plot(x,phi)\n\ny_lim = ax.get_ylim()\n\nax.set_xticks([0,p], [0,\"$p$\"], fontsize=fontsize)\nax.set_yticks([0,1], [0,1], fontsize=fontsize)\nax.hlines([0,1],x_min,x_max, color = \"k\", ls = \":\", zorder=-1)\nax.vlines([0,p], *y_lim, color = \"k\", ls = \":\")\nax.set_xlim(x_min,x_max)\nax.set_xlabel(\"$x$\", fontsize=fontsize)\nax.set_ylabel(\"$\\phi(x;p)$\", fontsize=fontsize)\nax.set_ylim(y_lim)\n\nfig.tight_layout()\nplt.show()\n\n\n<>:31: SyntaxWarning: invalid escape sequence '\\p'\n<>:31: SyntaxWarning: invalid escape sequence '\\p'\n/tmp/ipykernel_4422/665069857.py:31: SyntaxWarning: invalid escape sequence '\\p'\n ax.set_ylabel(\"$\\phi(x;p)$\", fontsize=fontsize)"
},
{
"objectID": "core/equations.html#precipitation",