From cda369b33b176548d23174deafde7df373145f7a Mon Sep 17 00:00:00 2001 From: Daniel Tollenaar Date: Tue, 10 Dec 2024 21:48:38 +0100 Subject: [PATCH 1/3] run_model --- src/ribasim_nl/ribasim_nl/model.py | 10 ++++++ src/ribasim_nl/ribasim_nl/run_model.py | 44 ++++++++++++++++++++++++++ 2 files changed, 54 insertions(+) create mode 100644 src/ribasim_nl/ribasim_nl/run_model.py diff --git a/src/ribasim_nl/ribasim_nl/model.py b/src/ribasim_nl/ribasim_nl/model.py index 5510fea..d7f3433 100644 --- a/src/ribasim_nl/ribasim_nl/model.py +++ b/src/ribasim_nl/ribasim_nl/model.py @@ -17,6 +17,7 @@ from ribasim_nl.case_conversions import pascal_to_snake_case from ribasim_nl.geometry import split_basin +from ribasim_nl.run_model import run manning_data = manning_resistance.Static(length=[100], manning_n=[0.04], profile_width=[10], profile_slope=[1]) level_data = level_boundary.Static(level=[0]) @@ -97,6 +98,15 @@ def graph(self): def next_node_id(self): return self.node_table().df.index.max() + 1 + def run(self, stream_output=True, returncode=True): + """_summary_ + + Args: + stream_output (bool, optional): stream output in IDE. Defaults to True. + returncode (bool, optional): return returncode after running model. Defaults to True. + """ + return run(self.filepath, stream_output=stream_output, returncode=returncode) + # methods relying on networkx. Discuss making this all in a subclass of Model def _upstream_nodes(self, node_id): # get upstream nodes 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..561c0c0 --- /dev/null +++ b/src/ribasim_nl/ribasim_nl/run_model.py @@ -0,0 +1,44 @@ +# %% +import os +import subprocess +from pathlib import Path + + +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 From d4b362ae032d85f8f70b4226e1de7bc1ba2b375a Mon Sep 17 00:00:00 2001 From: Daniel Tollenaar Date: Tue, 10 Dec 2024 22:24:49 +0100 Subject: [PATCH 2/3] update at samenvoegen --- notebooks/samenvoegen_modellen.py | 8 ++++++++ src/ribasim_nl/ribasim_nl/model.py | 31 ++++++++++++++++++++++++++---- 2 files changed, 35 insertions(+), 4 deletions(-) diff --git a/notebooks/samenvoegen_modellen.py b/notebooks/samenvoegen_modellen.py index 11669fd..27fddd0 100644 --- a/notebooks/samenvoegen_modellen.py +++ b/notebooks/samenvoegen_modellen.py @@ -195,6 +195,14 @@ def get_model_path(model, model_version): # read model ribasim_model = Model.read(model_path) + # run model + if not ribasim_model.basin_outstate.filepath.exists(): + print("run model to update state") + returncode = ribasim_model.run() + if returncode != 0: + raise Exception("model won't run successfully!") + ribasim_model.update_state() + # add meta_waterbeheerder for node_type in ribasim_model.node_table().df.node_type.unique(): ribasim_node = getattr(ribasim_model, pascal_to_snake_case(node_type)) diff --git a/src/ribasim_nl/ribasim_nl/model.py b/src/ribasim_nl/ribasim_nl/model.py index d7f3433..dddc0fd 100644 --- a/src/ribasim_nl/ribasim_nl/model.py +++ b/src/ribasim_nl/ribasim_nl/model.py @@ -60,7 +60,7 @@ def node_properties_to_table(table, node_properties, node_id): table_node_df.loc[node_id, [column]] = value -class BasinResults(BaseModel): +class Results(BaseModel): filepath: Path _df = None @@ -72,16 +72,24 @@ def df(self) -> pd.DataFrame: class Model(Model): - _basin_results: BasinResults | None = None + _basin_results: Results | None = None + _basin_outstate: Results | None = None _graph: nx.Graph | None = None @property def basin_results(self): if self._basin_results is None: filepath = self.filepath.parent.joinpath(self.results_dir, "basin.arrow").absolute().resolve() - self._basin_results = BasinResults(filepath=filepath) + self._basin_results = Results(filepath=filepath) return self._basin_results + @property + def basin_outstate(self): + if self._basin_outstate is None: + filepath = self.filepath.parent.joinpath(self.results_dir, "basin_state.arrow").absolute().resolve() + self._basin_outstate = Results(filepath=filepath) + return self._basin_outstate + @property def graph(self): # create a DiGraph from edge-table @@ -99,7 +107,7 @@ def next_node_id(self): return self.node_table().df.index.max() + 1 def run(self, stream_output=True, returncode=True): - """_summary_ + """Run your Ribasim model Args: stream_output (bool, optional): stream output in IDE. Defaults to True. @@ -107,6 +115,21 @@ def run(self, stream_output=True, returncode=True): """ return run(self.filepath, stream_output=stream_output, returncode=returncode) + def update_state(self, time_stamp: pd.Timestamp | None = None): + """Update basin.state with results or final basin_state (outstate) + + Args: + time_stamp (pd.Timestamp | None, optional): Timestamp in results to update basin.state with . Defaults to None. + """ + if time_stamp is None: + self.basin.state = self.basin_outstate.df + else: + df = self.basin_results.df.loc[time_stamp][["node_id", "level"]] + df.reset_index(inplace=True, drop=True) + df.index += 1 + df.index.name = "fid" + self.basin.state = df + # methods relying on networkx. Discuss making this all in a subclass of Model def _upstream_nodes(self, node_id): # get upstream nodes From a694efc949b444c9a1efb680b31583080fb946fe Mon Sep 17 00:00:00 2001 From: Daniel Tollenaar Date: Tue, 10 Dec 2024 23:25:16 +0100 Subject: [PATCH 3/3] minor_fixes --- notebooks/samenvoegen_modellen.py | 12 ++++++------ src/ribasim_nl/ribasim_nl/model.py | 8 ++++---- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/notebooks/samenvoegen_modellen.py b/notebooks/samenvoegen_modellen.py index 27fddd0..4d3a156 100644 --- a/notebooks/samenvoegen_modellen.py +++ b/notebooks/samenvoegen_modellen.py @@ -195,13 +195,18 @@ def get_model_path(model, model_version): # read model ribasim_model = Model.read(model_path) + # TODO: make sure this isn't needed next round! + if model["authority"] in RESET_TABLES: + ribasim_model.remove_unassigned_basin_area() + ribasim_model = reset_static_tables(ribasim_model) + # run model if not ribasim_model.basin_outstate.filepath.exists(): print("run model to update state") returncode = ribasim_model.run() if returncode != 0: raise Exception("model won't run successfully!") - ribasim_model.update_state() + ribasim_model.update_state() # add meta_waterbeheerder for node_type in ribasim_model.node_table().df.node_type.unique(): @@ -211,11 +216,6 @@ def get_model_path(model, model_version): if idx == 0: lhm_model = ribasim_model else: - # TODO: make sure this isn't needed next round! - if model["authority"] in RESET_TABLES: - ribasim_model.remove_unassigned_basin_area() - ribasim_model = reset_static_tables(ribasim_model) - lhm_model = concat([lhm_model, ribasim_model]) readme += f""" **{model["authority"]}**: {model["model"]} ({model_version.version})""" diff --git a/src/ribasim_nl/ribasim_nl/model.py b/src/ribasim_nl/ribasim_nl/model.py index dddc0fd..0e5ecdc 100644 --- a/src/ribasim_nl/ribasim_nl/model.py +++ b/src/ribasim_nl/ribasim_nl/model.py @@ -122,13 +122,13 @@ def update_state(self, time_stamp: pd.Timestamp | None = None): time_stamp (pd.Timestamp | None, optional): Timestamp in results to update basin.state with . Defaults to None. """ if time_stamp is None: - self.basin.state = self.basin_outstate.df + df = self.basin_outstate.df else: df = self.basin_results.df.loc[time_stamp][["node_id", "level"]] df.reset_index(inplace=True, drop=True) - df.index += 1 - df.index.name = "fid" - self.basin.state = df + df.index += 1 + df.index.name = "fid" + self.basin.state.df = df # methods relying on networkx. Discuss making this all in a subclass of Model def _upstream_nodes(self, node_id):