From 0244f9a0b41a4112e5ed399b42d345e5aa0885e5 Mon Sep 17 00:00:00 2001 From: sniffo Date: Mon, 2 Dec 2024 12:22:51 -0500 Subject: [PATCH 1/3] edit record method to make simulations faster Changes the `record` class method in the History class such that it records the datasets dataframes as entries in a dictionary with the `time_stamp` as keys that map to corresponding dataframe for each element. The `self.dicts` property is introduced to account for this - this is a nested dictionary with the `eptm.elements` as keys and the above dictionaries as the values. This allows for appending the dataframes at each time step without having to load onto memory the increasingly large `self.datasets[element]` dataframes. The `update_datasets` class method is introduced in order to concatenate all the dataframes within `self.datasets[element]` at the end of the simulation. This will be accompanied by changes to the `EulerSolver` class method `solve` to run `History.update_datasets` athe the final time point. --- src/tyssue/core/history.py | 27 ++++++++++++++++----------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/src/tyssue/core/history.py b/src/tyssue/core/history.py index 7a4c1879..4bacfbe8 100644 --- a/src/tyssue/core/history.py +++ b/src/tyssue/core/history.py @@ -81,6 +81,7 @@ def __init__( self.datasets = {} self.columns = {} + self.columns = {} vcols = sheet.coords + extra_cols["vert"] vcols = list(set(vcols)) self.vcols = _filter_columns(vcols, sheet.vert_df.columns, "vertex") @@ -88,6 +89,7 @@ def __init__( if "time" not in self.vcols: _vert_h["time"] = 0 self.datasets["vert"] = _vert_h + self.dicts["vert"] = {} self.columns["vert"] = self.vcols fcols = extra_cols["face"] @@ -96,6 +98,7 @@ def __init__( if "time" not in self.fcols: _face_h["time"] = 0 self.datasets["face"] = _face_h + self.dicts["face"] = {} self.columns["face"] = self.fcols if sheet.cell_df is not None: @@ -105,6 +108,7 @@ def __init__( if "time" not in self.ccols: _cell_h["time"] = 0 self.datasets["cell"] = _cell_h + self.dicts["cell"] = {} self.columns["cell"] = self.ccols extra_cols["edge"].append("cell") @@ -115,6 +119,7 @@ def __init__( if "time" not in self.ecols: _edge_h["time"] = 0 self.datasets["edge"] = _edge_h + self.dicts["edge"] = {} self.columns["edge"] = self.ecols def __len__(self): @@ -156,7 +161,8 @@ def cell_h(self): def record(self, time_stamp=None): """Appends a copy of the sheet datasets to the history instance. - + Appends to the corresponding dict. Use update_datasets() method + to create the self.datasets dataframes as in the previous implementation Parameters ---------- time_stamp : float, save specific timestamp @@ -171,25 +177,24 @@ def record(self, time_stamp=None): self.index % (int(self.save_every / self.dt)) == 0 ): for element in self.datasets: - hist = self.datasets[element] cols = self.columns[element] df = self.sheet.datasets[element][cols].reset_index(drop=False) if "time" not in cols: - times = pd.Series(np.ones((df.shape[0],)) * self.time, name="time") - df = pd.concat([df, times], ignore_index=False, axis=1, sort=False) - else: df["time"] = self.time - - if self.time in hist["time"]: + if f"{self.time}" in self.dicts[element].keys(): # erase previously recorded time point - hist = hist[hist["time"] != self.time] + self.dicts[element].pop(f"{self.time}") - hist = pd.concat([hist, df], ignore_index=True, axis=0, sort=False) - - self.datasets[element] = hist + self.dicts[element].update({f"{self.time}": df}) self.index += 1 + def update_datasets(self): + """Concatenate all datasets in self.datasets into self.datasets as pd.DataFrame objects + """ + for element in self.sheet.datasets: + self.datasets[element] = pd.concat(self.dicts[element].values(), ignore_index=True) + def retrieve(self, time): """Return datasets at time `time`. From 219e35d7b2de7d9a39cd38fa625f8344c699f40f Mon Sep 17 00:00:00 2001 From: sniffo Date: Mon, 2 Dec 2024 12:31:54 -0500 Subject: [PATCH 2/3] introduce History.update_datasets to solve method Includes the `History.update_datasets` class method so that it runs at the last iteration of the `EulerSolver.solve` class method to generate the dataframes for History.datasets --- src/tyssue/solvers/viscous.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/tyssue/solvers/viscous.py b/src/tyssue/solvers/viscous.py index fcb4d351..84261ae7 100644 --- a/src/tyssue/solvers/viscous.py +++ b/src/tyssue/solvers/viscous.py @@ -98,6 +98,9 @@ def set_pos(self, pos): def record(self, t): self.history.record(time_stamp=t) + def update_datasets(self): + self.history.update_datasets() + def solve(self, tf, dt, on_topo_change=None, topo_change_args=()): """Solves the system of differential equations from the current time to tf with steps of dt with a forward Euler method. @@ -132,6 +135,9 @@ def solve(self, tf, dt, on_topo_change=None, topo_change_args=()): self.eptm.topo_changed = False self.record(t) + if t == tf: + self.update_datasets() + def ode_func(self, t, pos): """Computes the models' gradient. From 3d87f768661f09ad1a59d261675ca2bc68254acb Mon Sep 17 00:00:00 2001 From: sniffo Date: Mon, 2 Dec 2024 15:14:19 -0500 Subject: [PATCH 3/3] Update history.py --- src/tyssue/core/history.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tyssue/core/history.py b/src/tyssue/core/history.py index 4bacfbe8..4adef9ac 100644 --- a/src/tyssue/core/history.py +++ b/src/tyssue/core/history.py @@ -81,7 +81,7 @@ def __init__( self.datasets = {} self.columns = {} - self.columns = {} + self.dicts = {} vcols = sheet.coords + extra_cols["vert"] vcols = list(set(vcols)) self.vcols = _filter_columns(vcols, sheet.vert_df.columns, "vertex")