From 030524bdc0f07362a564867b4ab7ad6ced7eb8b1 Mon Sep 17 00:00:00 2001 From: Daniel Tollenaar Date: Fri, 20 Dec 2024 11:02:47 +0100 Subject: [PATCH] Hea fixes (#208) Co-authored-by: ngoorden <49673619+ngoorden@users.noreply.github.com> --- .../hunze_en_aas/01_fix_model_network.py | 54 ++++++++++++++++++- notebooks/upload_feedback_formulieren.py | 10 ---- src/ribasim_nl/ribasim_nl/model.py | 5 +- src/ribasim_nl/ribasim_nl/run_model.py | 47 ++++++++++++++++ 4 files changed, 103 insertions(+), 13 deletions(-) create mode 100644 src/ribasim_nl/ribasim_nl/run_model.py diff --git a/notebooks/hunze_en_aas/01_fix_model_network.py b/notebooks/hunze_en_aas/01_fix_model_network.py index 39b956b..f13df40 100644 --- a/notebooks/hunze_en_aas/01_fix_model_network.py +++ b/notebooks/hunze_en_aas/01_fix_model_network.py @@ -27,6 +27,10 @@ if not model_edits_path.exists(): cloud.download_file(model_edits_url) +# Load area file to fill basin area holes +ribasim_areas_path = cloud.joinpath(authority, "verwerkt", "4_ribasim", "areas.gpkg") +ribasim_areas_gdf = gpd.read_file(ribasim_areas_path, fid_as_index=True, layer="areas") + # %% some stuff we'll need again manning_data = manning_resistance.Static(length=[100], manning_n=[0.04], profile_width=[10], profile_slope=[1]) @@ -90,18 +94,26 @@ # Reset static tables model = reset_static_tables(model) +# fix unassigned basin area +model.fix_unassigned_basin_area() +model.explode_basin_area() +# fix unassigned basin area +model.fix_unassigned_basin_area() # %% + actions = [ + "remove_basin_area", "remove_node", "add_basin", + "update_node", "add_basin_area", "update_basin_area", - "merge_basins", "reverse_edge", + "redirect_edge", + "merge_basins", "move_node", "connect_basins", - "update_node", "deactivate_node", ] actions = [i for i in actions if i in gpd.list_layers(model_edits_path).name.to_list()] @@ -117,9 +129,47 @@ method(**kwargs) +# %% Assign Ribasim model ID's (dissolved areas) to the model basin areas (original areas with code) by overlapping the Ribasim area file baed on largest overlap +# then assign Ribasim node-ID's to areas with the same area code. Many nodata areas disappear by this method +# Create the overlay of areas +combined_basin_areas_gdf = gpd.overlay(ribasim_areas_gdf, model.basin.area.df, how="union").explode() +combined_basin_areas_gdf["geometry"] = combined_basin_areas_gdf["geometry"].apply(lambda x: x if x.has_z else x) + +# Calculate area for each geometry +combined_basin_areas_gdf["area"] = combined_basin_areas_gdf.geometry.area + +# Separate rows with and without node_id +non_null_basin_areas_gdf = combined_basin_areas_gdf[combined_basin_areas_gdf["node_id"].notna()] + +# Find largest area node_ids for each code +largest_area_node_ids = non_null_basin_areas_gdf.loc[ + non_null_basin_areas_gdf.groupby("code")["area"].idxmax(), ["code", "node_id"] +] + +# Merge largest area node_ids back into the combined DataFrame +combined_basin_areas_gdf = combined_basin_areas_gdf.merge( + largest_area_node_ids, on="code", how="left", suffixes=("", "_largest") +) + +# Fill missing node_id with the largest_area node_id +combined_basin_areas_gdf["node_id"] = combined_basin_areas_gdf["node_id"].fillna( + combined_basin_areas_gdf["node_id_largest"] +) +combined_basin_areas_gdf.drop(columns=["node_id_largest"], inplace=True) +combined_basin_areas_gdf = combined_basin_areas_gdf.drop_duplicates() +combined_basin_areas_gdf = combined_basin_areas_gdf.dissolve(by="node_id").reset_index() +combined_basin_areas_gdf = combined_basin_areas_gdf[["node_id", "geometry"]] +combined_basin_areas_gdf.index.name = "fid" + +model.basin.area.df = combined_basin_areas_gdf + +model.remove_unassigned_basin_area() + # %% write model model.use_validation = True model.write(ribasim_toml) model.invalid_topology_at_node().to_file(ribasim_toml.with_name("invalid_topology_at_connector_nodes.gpkg")) +model.report_basin_area() +model.report_internal_basins() # %% diff --git a/notebooks/upload_feedback_formulieren.py b/notebooks/upload_feedback_formulieren.py index a25af36..2ffe0a9 100644 --- a/notebooks/upload_feedback_formulieren.py +++ b/notebooks/upload_feedback_formulieren.py @@ -8,17 +8,7 @@ cloud = CloudStorage() WATER_AUTHORITIES = [ - "AaenMaas", - "BrabantseDelta", - "DeDommel", "DrentsOverijsselseDelta", - "HunzeenAas", - "Limburg", - "Noorderzijlvest", - "RijnenIJssel", - "StichtseRijnlanden", - "ValleienVeluwe", - "Vechtstromen", ] FEEDBACK_XLS = cloud.joinpath("Basisgegevens", "feedbackformulier", "Feedback Formulier.xlsx") diff --git a/src/ribasim_nl/ribasim_nl/model.py b/src/ribasim_nl/ribasim_nl/model.py index 2a90279..0bb0159 100644 --- a/src/ribasim_nl/ribasim_nl/model.py +++ b/src/ribasim_nl/ribasim_nl/model.py @@ -713,7 +713,7 @@ def remove_unassigned_basin_area(self): if self.basin.area.df.node_id.duplicated().any(): df = df.dissolve(by="node_id").reset_index() df.index.name = "fid" - self.basin.area.df = df + self.basin.area.df = df def explode_basin_area(self, remove_z=True): df = self.basin.area.df.explode().reset_index(drop=True) @@ -891,3 +891,6 @@ def invalid_topology_at_node(self, edge_type: str = "flow") -> gpd.GeoDataFrame: return gpd.GeoDataFrame( [], columns=["node_id", "node_type", "exception"], geometry=gpd.GeoSeries(crs=self.crs) ).set_index("node_id") + + +# %% diff --git a/src/ribasim_nl/ribasim_nl/run_model.py b/src/ribasim_nl/ribasim_nl/run_model.py new file mode 100644 index 0000000..a750e71 --- /dev/null +++ b/src/ribasim_nl/ribasim_nl/run_model.py @@ -0,0 +1,47 @@ +import os +import subprocess +from pathlib import Path + +# TODO: add ribasim_exe so it can be used if ribasim is not part of env path +# TODO: check if ribasim is in path, stop if not and ribasim_exe is not provided +# TODO: raise FileNotFoundError if toml_path does not exist. User + + +def run( + toml_path: Path, + stream_output: bool = True, + returncode: bool = True, +): + """To run a Ribasim model + + Args: + toml_path (Path): path to your ribasim toml-file + stream_output (bool, optional): stream output in IDE. Defaults to False. + returncode (bool, optional): return return code after running model. Defaults to True. + + """ + env = os.environ.copy() + + input = "" + proc = subprocess.Popen( + ["ribasim", toml_path.as_posix()], + cwd=toml_path.parent.as_posix(), + env=env, + stdin=subprocess.PIPE, + stdout=subprocess.PIPE, + encoding="ascii", + ) + if stream_output: + with proc: + proc.stdin.write(input) + proc.stdin.close() + for line in proc.stdout: + print(line, end="") + outs = None + else: + outs, _ = proc.communicate(input) + + if returncode: + return proc.returncode + else: + return outs