Skip to content

Commit

Permalink
Merge branch 'main' into remove_edge_sources
Browse files Browse the repository at this point in the history
  • Loading branch information
SouthEndMusic committed Dec 3, 2024
2 parents bc9eeae + 9311290 commit 7830fb1
Show file tree
Hide file tree
Showing 31 changed files with 268 additions and 144 deletions.
24 changes: 20 additions & 4 deletions core/src/allocation_optim.jl
Original file line number Diff line number Diff line change
Expand Up @@ -706,11 +706,9 @@ function save_allocation_flows!(
priority::Int32,
optimization_type::OptimizationType.T,
)::Nothing
(; flow, problem, subnetwork_id) = allocation_model
(; flow, subnetwork_id, sources) = allocation_model
(; allocation, graph) = p
(; record_flow) = allocation
F_basin_in = problem[:F_basin_in]
F_basin_out = problem[:F_basin_out]

edges_allocation = keys(flow.data)

Expand Down Expand Up @@ -765,7 +763,7 @@ function save_allocation_flows!(
for node_id in graph[].node_ids[subnetwork_id]
if node_id.type == NodeType.Basin &&
has_external_demand(graph, node_id, :level_demand)[1]
flow_rate = JuMP.value(F_basin_out[node_id]) - JuMP.value(F_basin_in[node_id])
flow_rate = sources[(node_id, node_id)].basin_flow_rate
push!(record_flow.time, t)
push!(record_flow.edge_id, 0)
push!(record_flow.from_node_type, string(NodeType.Basin))
Expand Down Expand Up @@ -885,6 +883,15 @@ function optimize_per_source!(
)::Nothing
(; problem, sources, subnetwork_id, flow) = allocation_model
(; priorities) = allocation
F_basin_in = problem[:F_basin_in]
F_basin_out = problem[:F_basin_out]

# Start the cumulative basin flow rates at 0
for source in values(sources)
if source.type == AllocationSourceType.basin
source.basin_flow_rate = 0.0
end
end

priority = priorities[priority_idx]

Expand Down Expand Up @@ -931,6 +938,15 @@ function optimize_per_source!(
end
end

# Add to the basin cumulative flow rate
for (edge, source) in sources
if source.type == AllocationSourceType.basin
node_id = edge[1]
source.basin_flow_rate +=
JuMP.value(F_basin_out[node_id]) - JuMP.value(F_basin_in[node_id])
end
end

# Adjust allocated flow to basins
increase_allocateds!(p.basin, problem)
end
Expand Down
3 changes: 3 additions & 0 deletions core/src/parameter.jl
Original file line number Diff line number Diff line change
Expand Up @@ -136,12 +136,15 @@ edge: The outflow edge of the source
type: The type of source (edge, basin, main_to_sub, user_return, buffer)
capacity: The initial capacity of the source as determined by the physical layer
capacity_reduced: The capacity adjusted by passed optimizations
basin_flow_rate: The total outflow rate of a basin when optimized over all sources for one priority.
Ignored when the source is not a basin.
"""
@kwdef mutable struct AllocationSource
const edge::Tuple{NodeID, NodeID}
const type::AllocationSourceType.T
capacity::Float64 = 0.0
capacity_reduced::Float64 = 0.0
basin_flow_rate::Float64 = 0.0
end

function Base.show(io::IO, source::AllocationSource)
Expand Down
9 changes: 5 additions & 4 deletions docs/tutorial/natural-flow.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@
"source": [
"# Crystal River Basin\n",
"We will examine a straightforward example of the Crystal river basin, which includes a main river and a single tributary flowing into the sea (see @fig-crystal-basin).\n",
"An average discharge of $44.45 \\text{ m}^3/\\text{s}$ is measured at the confluence.\n",
"Between 2014 and 2023 an average discharge of $44.45 \\text{ m}^3/\\text{s}$ is measured at the confluence.\n",
"In this module, the basin is free of any activities, allowing the model to simulate the natural flow.\n",
"The next step is to include a demand (irrigation) that taps from a canal out of the main river.\n",
"\n",
Expand Down Expand Up @@ -89,7 +89,9 @@
"metadata": {},
"source": [
"### Setup paths and model configuration\n",
"Reference the paths of the Ribasim installation and model directory and define the time period (2022-01-01 until 2023-01-01) for the model simulation.\n",
"Reference the paths of the Ribasim installation and model directory and define the time period.\n",
"The used simulation period is defined by the `starttime` and `endtime` of the model, not by the input timeseries.\n",
"For now we will look into the period from 2022-01-01 until 2023-01-01 for the model simulation.\n",
"The coordinate reference system (CRS) is also required, and set to [EPSG:4326](https://epsg.io/4326), which means all coordinates are interpreted as latitude and longitude values.\n",
"The CRS is important for correctly placing Ribasim models on the map, but since this is a fictional model, it is not important."
]
Expand Down Expand Up @@ -135,8 +137,7 @@
"data[\"total\"] = data[\"minor\"] + data[\"main\"]\n",
"display(data)\n",
"\n",
"# Average and max inflow of the total inflow data timeseries\n",
"# From 2014 - 2023\n",
"# Average and max inflow of the total inflow data over 2022\n",
"print(\"Average inflow [m3/s]:\", data[\"total\"].mean())\n",
"print(\"Maximum inflow [m3/s]:\", data[\"total\"].max())\n",
"\n",
Expand Down
1 change: 1 addition & 0 deletions python/ribasim/ribasim/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ class Results(ChildModel):
class Solver(ChildModel):
"""
Defines the numerical solver options.
For more details see <https://docs.sciml.ai/DiffEqDocs/stable/basics/common_solver_opts/#solver_options>.
Attributes
Expand Down
1 change: 0 additions & 1 deletion python/ribasim/ribasim/delwaq/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
"generate",
"parse",
"run_delwaq",
"plot",
"add_tracer",
"plot_fraction",
"plot_spatial",
Expand Down
3 changes: 1 addition & 2 deletions python/ribasim/ribasim/delwaq/generate.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,8 +57,8 @@ def _quote(value):
def _make_boundary(data, boundary_type):
"""
Create a Delwaq boundary definition with the given data and boundary type.
Pivot our data from long to wide format, and convert the time to a string.
Pivot our data from long to wide format, and convert the time to a string.
Specifically, we go from a table:
`node_id, substance, time, concentration`
to
Expand Down Expand Up @@ -300,7 +300,6 @@ def generate(
output_path: Path = output_path,
) -> tuple[nx.DiGraph, set[str]]:
"""Generate a Delwaq model from a Ribasim model and results."""

# Read in model and results
model = ribasim.Model.read(toml_path)
results_folder = toml_path.parent / model.results_dir
Expand Down
7 changes: 4 additions & 3 deletions python/ribasim/ribasim/geometry/edge.py
Original file line number Diff line number Diff line change
Expand Up @@ -75,8 +75,10 @@ def add(
edge_id: Optional[NonNegativeInt] = None,
**kwargs,
):
"""Add an edge between nodes. The type of the edge (flow or control)
is automatically inferred from the type of the `from_node`.
"""
Add an edge between nodes.
The type of the edge (flow or control) is automatically inferred from the type of the `from_node`.
Parameters
----------
Expand Down Expand Up @@ -181,7 +183,6 @@ def plot(self, **kwargs) -> Axes:
**kwargs : Dict
Supported: 'ax', 'color_flow', 'color_control'
"""

assert self.df is not None
kwargs = kwargs.copy() # Avoid side-effects
ax = kwargs.get("ax", None)
Expand Down
5 changes: 0 additions & 5 deletions python/ribasim/ribasim/model.py
Original file line number Diff line number Diff line change
Expand Up @@ -300,7 +300,6 @@ def write(self, filepath: str | PathLike[str]) -> Path:
filepath : str | PathLike[str]
A file path with .toml extension.
"""

if self.use_validation:
self._validate_model()

Expand Down Expand Up @@ -351,7 +350,6 @@ def _has_valid_neighbor_amount(
nodes,
) -> bool:
"""Check if the neighbor amount of the two nodes connected by the given edge meet the minimum requirements."""

is_valid = True

# filter graph by edge type
Expand Down Expand Up @@ -419,7 +417,6 @@ def _add_source_sink_node(
Specify that their occurrence in from_node table or to_node table is 0.
"""

# loop over nodes, add the one that is not the downstream (from) or upstream (to) of any other nodes
for index, node in enumerate(nodes):
if nodes.index[index] not in node_info[f"{direction}_node_id"].to_numpy():
Expand Down Expand Up @@ -463,7 +460,6 @@ def _reset_contextvar(self) -> "Model":

def plot_control_listen(self, ax):
"""Plot the implicit listen edges of the model."""

df_listen_edge = pd.DataFrame(
data={
"control_node_id": pd.Series([], dtype="int32[pyarrow]"),
Expand Down Expand Up @@ -598,7 +594,6 @@ def to_xugrid(self, add_flow: bool = False, add_allocation: bool = False):
add_allocation : bool
add allocation results (Optional, defaults to False)
"""

if add_flow and add_allocation:
raise ValueError("Cannot add both allocation and flow results.")

Expand Down
3 changes: 2 additions & 1 deletion python/ribasim/ribasim/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ def __getattr__(

def _node_lookup_numpy(node_id) -> Series[Int32]:
"""Create a lookup table from from node_id to the node dimension index.
Used when adding data onto the nodes of an xugrid dataset.
"""
return pd.Series(
Expand All @@ -43,6 +44,7 @@ def _node_lookup_numpy(node_id) -> Series[Int32]:

def _node_lookup(uds) -> Series[Int32]:
"""Create a lookup table from from node_id to the node dimension index.
Used when adding data onto the nodes of an xugrid dataset.
"""
return pd.Series(
Expand All @@ -57,7 +59,6 @@ def _edge_lookup(uds) -> Series[Int32]:
Used when adding data onto the edges of an xugrid dataset.
"""

return pd.Series(
index=uds["edge_id"],
data=uds[uds.grid.edge_dimension],
Expand Down
5 changes: 3 additions & 2 deletions python/ribasim_api/tests/test_bmi.py
Original file line number Diff line number Diff line change
Expand Up @@ -175,8 +175,9 @@ def test_get_value_ptr_allocation(libribasim, user_demand, tmp_path):

def test_err_unknown_var(libribasim, basic, tmp_path):
"""
Unknown or invalid variable address should trigger Python Exception,
print the kernel error, and not crash the library
Unknown or invalid variable address.
Should trigger Python Exception, print the kernel error, and not crash the library.
"""
basic.write(tmp_path / "ribasim.toml")
config_file = str(tmp_path / "ribasim.toml")
Expand Down
2 changes: 2 additions & 0 deletions python/ribasim_testmodels/ribasim_testmodels/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import ribasim_testmodels
from ribasim_testmodels.allocation import (
allocation_example_model,
allocation_training_model,
fair_distribution_model,
flow_demand_model,
level_demand_model,
Expand Down Expand Up @@ -63,6 +64,7 @@
from ribasim_testmodels.two_basin import two_basin_model

__all__ = [
"allocation_training_model",
"allocation_example_model",
"backwater_model",
"basic_arrow_model",
Expand Down
Loading

0 comments on commit 7830fb1

Please sign in to comment.