physioblocks 1.0.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (93) hide show
  1. physioblocks/__init__.py +37 -0
  2. physioblocks/base/__init__.py +27 -0
  3. physioblocks/base/operators.py +176 -0
  4. physioblocks/base/registers.py +108 -0
  5. physioblocks/computing/__init__.py +47 -0
  6. physioblocks/computing/assembling.py +291 -0
  7. physioblocks/computing/models.py +811 -0
  8. physioblocks/computing/quantities.py +354 -0
  9. physioblocks/configuration/__init__.py +38 -0
  10. physioblocks/configuration/aliases.py +203 -0
  11. physioblocks/configuration/base.py +123 -0
  12. physioblocks/configuration/computing/__init__.py +27 -0
  13. physioblocks/configuration/computing/quantities.py +56 -0
  14. physioblocks/configuration/constants.py +121 -0
  15. physioblocks/configuration/description/__init__.py +33 -0
  16. physioblocks/configuration/description/blocks.py +239 -0
  17. physioblocks/configuration/description/nets.py +155 -0
  18. physioblocks/configuration/functions.py +695 -0
  19. physioblocks/configuration/simulation/__init__.py +32 -0
  20. physioblocks/configuration/simulation/simulations.py +280 -0
  21. physioblocks/description/__init__.py +34 -0
  22. physioblocks/description/blocks.py +418 -0
  23. physioblocks/description/flux.py +157 -0
  24. physioblocks/description/nets.py +746 -0
  25. physioblocks/io/__init__.py +29 -0
  26. physioblocks/io/aliases.py +73 -0
  27. physioblocks/io/configuration.py +125 -0
  28. physioblocks/launcher/__main__.py +285 -0
  29. physioblocks/launcher/configuration.py +231 -0
  30. physioblocks/launcher/configure/__main__.py +99 -0
  31. physioblocks/launcher/constants.py +105 -0
  32. physioblocks/launcher/files.py +150 -0
  33. physioblocks/launcher/series.py +165 -0
  34. physioblocks/library/__init__.py +27 -0
  35. physioblocks/library/aliases/blocks/c_block.json +5 -0
  36. physioblocks/library/aliases/blocks/rc_block.json +5 -0
  37. physioblocks/library/aliases/blocks/rcr_block.json +5 -0
  38. physioblocks/library/aliases/blocks/spherical_cavity_block.json +5 -0
  39. physioblocks/library/aliases/blocks/valve_rl_block.json +5 -0
  40. physioblocks/library/aliases/flux/heart_flux_dof_couples.jsonc +4 -0
  41. physioblocks/library/aliases/model_components/active_law_macro_huxley_two_moments.json +5 -0
  42. physioblocks/library/aliases/model_components/rheology_fiber_additive.json +5 -0
  43. physioblocks/library/aliases/model_components/spherical_dynamics.json +5 -0
  44. physioblocks/library/aliases/model_components/velocity_law_hht.json +5 -0
  45. physioblocks/library/aliases/nets/circulation_alone_net.json +31 -0
  46. physioblocks/library/aliases/nets/spherical_heart_net.json +93 -0
  47. physioblocks/library/aliases/simulations/circulation_alone_forward_simulation.jsonc +55 -0
  48. physioblocks/library/aliases/simulations/default_forward_simulation.jsonc +7 -0
  49. physioblocks/library/aliases/simulations/default_time.jsonc +8 -0
  50. physioblocks/library/aliases/simulations/newton_method_solver.json +5 -0
  51. physioblocks/library/aliases/simulations/spherical_heart_forward_simulation.jsonc +157 -0
  52. physioblocks/library/aliases/simulations/spherical_heart_with_respiration_forward_simulation.jsonc +45 -0
  53. physioblocks/library/blocks/__init__.py +27 -0
  54. physioblocks/library/blocks/capacitances.py +516 -0
  55. physioblocks/library/blocks/cavity.py +192 -0
  56. physioblocks/library/blocks/valves.py +281 -0
  57. physioblocks/library/functions/__init__.py +27 -0
  58. physioblocks/library/functions/base_operations.py +129 -0
  59. physioblocks/library/functions/first_order.py +113 -0
  60. physioblocks/library/functions/piecewise.py +271 -0
  61. physioblocks/library/functions/trigonometric.py +78 -0
  62. physioblocks/library/functions/watchers.py +113 -0
  63. physioblocks/library/model_components/__init__.py +27 -0
  64. physioblocks/library/model_components/active_law.py +345 -0
  65. physioblocks/library/model_components/dynamics.py +986 -0
  66. physioblocks/library/model_components/rheology.py +160 -0
  67. physioblocks/library/model_components/velocity_law.py +169 -0
  68. physioblocks/references/circulation_alone_sim.jsonc +24 -0
  69. physioblocks/references/spherical_heart_respiration_sim.jsonc +33 -0
  70. physioblocks/references/spherical_heart_sim.jsonc +29 -0
  71. physioblocks/registers/__init__.py +32 -0
  72. physioblocks/registers/load_function_register.py +93 -0
  73. physioblocks/registers/save_function_register.py +106 -0
  74. physioblocks/registers/type_register.py +97 -0
  75. physioblocks/simulation/__init__.py +48 -0
  76. physioblocks/simulation/constants.py +30 -0
  77. physioblocks/simulation/functions.py +71 -0
  78. physioblocks/simulation/runtime.py +484 -0
  79. physioblocks/simulation/saved_quantities.py +129 -0
  80. physioblocks/simulation/setup.py +576 -0
  81. physioblocks/simulation/solvers.py +235 -0
  82. physioblocks/simulation/state.py +340 -0
  83. physioblocks/simulation/time_manager.py +354 -0
  84. physioblocks/utils/__init__.py +27 -0
  85. physioblocks/utils/dynamic_import_utils.py +150 -0
  86. physioblocks/utils/exceptions_utils.py +115 -0
  87. physioblocks/utils/gradient_test_utils.py +337 -0
  88. physioblocks/utils/math_utils.py +109 -0
  89. physioblocks-1.0.0.dist-info/METADATA +127 -0
  90. physioblocks-1.0.0.dist-info/RECORD +93 -0
  91. physioblocks-1.0.0.dist-info/WHEEL +4 -0
  92. physioblocks-1.0.0.dist-info/licenses/licenses/GPL-3.0-only.txt +674 -0
  93. physioblocks-1.0.0.dist-info/licenses/licenses/LGPL-3.0-only.txt +165 -0
@@ -0,0 +1,231 @@
1
+ # SPDX-FileCopyrightText: Copyright INRIA
2
+ #
3
+ # SPDX-License-Identifier: LGPL-3.0-only
4
+ #
5
+ # Copyright INRIA
6
+ #
7
+ # This file is part of PhysioBlocks, a library mostly developed by the
8
+ # [Ananke project-team](https://team.inria.fr/ananke) at INRIA.
9
+ #
10
+ # Authors:
11
+ # - Colin Drieu
12
+ # - Dominique Chapelle
13
+ # - François Kimmig
14
+ # - Philippe Moireau
15
+ #
16
+ # PhysioBlocks is free software: you can redistribute it and/or modify it under the
17
+ # terms of the GNU Lesser General Public License as published by the Free Software
18
+ # Foundation, version 3 of the License.
19
+ #
20
+ # PhysioBlocks is distributed in the hope that it will be useful, but WITHOUT ANY
21
+ # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
22
+ # PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
23
+ #
24
+ # You should have received a copy of the GNU Lesser General Public License along with
25
+ # PhysioBlocks. If not, see <https://www.gnu.org/licenses/>.
26
+
27
+ import json
28
+ import logging
29
+ import shutil
30
+ from pathlib import Path
31
+ from typing import Any
32
+
33
+ import physioblocks.library
34
+ from physioblocks.io.aliases import load_aliases
35
+ from physioblocks.launcher.constants import (
36
+ LAUNCHER_CONFIGURATION_ALIAS_ID,
37
+ LAUNCHER_CONFIGURATION_FILE_NAME,
38
+ LAUNCHER_CONFIGURATION_LIBRARIES_ID,
39
+ LAUNCHER_GITIGNORE_FILE_NAME,
40
+ LAUNCHER_LOG_FILE_NAME,
41
+ LAUNCHER_MODULE_INIT_FILE_NAME,
42
+ LAUNCHER_SERIES_DIR_NAME,
43
+ LAUNCHER_USER_ALIASES_DIR_NAME,
44
+ LAUNCHER_USER_LIBRARY_DIR_NAME,
45
+ PHYSIOBLOCKS_REFERENCES_PATH,
46
+ )
47
+ from physioblocks.launcher.series import SimulationInfo
48
+ from physioblocks.utils.dynamic_import_utils import import_libraries
49
+
50
+ _logger = logging.getLogger(__name__)
51
+
52
+
53
+ def create_simulation_folder_path(series_path: Path, info: SimulationInfo) -> Path:
54
+ sim_folder_name = info.reference
55
+ simulation_folder_path = series_path / sim_folder_name
56
+ simulation_folder_path.mkdir()
57
+
58
+ return simulation_folder_path
59
+
60
+
61
+ def check_target_launcher_directory(launcher_dir_path: Path) -> bool:
62
+ """
63
+ Check that the given directory is suitable to welcome a new launcher directory.
64
+
65
+ It should either not exist (and therefore it will be created) or
66
+ be an empty dir (to avoid erasing existing files)
67
+
68
+ :param launcher_dir_path: the directory path
69
+ :type launcher_dir_path: Path
70
+
71
+ :return: True if the launcher dir can be created at this location,
72
+ False otherwise
73
+ :rtype: boolean
74
+ """
75
+ return launcher_dir_path.exists() is False or (
76
+ launcher_dir_path.is_dir() and not any(launcher_dir_path.iterdir())
77
+ )
78
+
79
+
80
+ def check_launcher_directory(launcher_dir_path: Path) -> bool:
81
+ """
82
+ Check that the given directory can launch a simulation.
83
+
84
+ :param launcher_dir_path: the directory path
85
+ :type launcher_dir_path: Path
86
+
87
+ :return: True if a simulation can be launched at this location, False otherwise
88
+ :rtype: boolean
89
+ """
90
+ return (
91
+ launcher_dir_path.exists() is True
92
+ and (
93
+ (launcher_dir_path / LAUNCHER_SERIES_DIR_NAME).exists() is True
94
+ and (launcher_dir_path / LAUNCHER_SERIES_DIR_NAME).is_dir()
95
+ )
96
+ and (
97
+ (launcher_dir_path / LAUNCHER_CONFIGURATION_FILE_NAME).exists() is True
98
+ and (launcher_dir_path / LAUNCHER_CONFIGURATION_FILE_NAME).is_file()
99
+ )
100
+ and (
101
+ (launcher_dir_path / LAUNCHER_LOG_FILE_NAME).exists() is True
102
+ and (launcher_dir_path / LAUNCHER_LOG_FILE_NAME).is_file()
103
+ )
104
+ )
105
+
106
+
107
+ def setup_launcher_directory(launcher_dir_path: Path) -> None:
108
+ """
109
+ Configure the launcher directory at the given path.
110
+
111
+ :param launcher_dir_path:
112
+ :type launcher_dir_path: Path
113
+ """
114
+ # Create the root simulation directory if it doesn't exist
115
+ if launcher_dir_path.exists() is False:
116
+ launcher_dir_path.mkdir()
117
+
118
+ # Create the series directory if it doesn't exist
119
+ series_path = launcher_dir_path / LAUNCHER_SERIES_DIR_NAME
120
+ if series_path.exists() is False:
121
+ series_path.mkdir()
122
+
123
+ # create a empty log file
124
+ launcher_log_file_path = launcher_dir_path / LAUNCHER_LOG_FILE_NAME
125
+ launcher_log_file_path.touch()
126
+
127
+ # copy simulations references from python package.
128
+ physioblocks_references_folder_path = (
129
+ Path(physioblocks.__file__).parent / PHYSIOBLOCKS_REFERENCES_PATH
130
+ )
131
+ launcher_references_folder_path = launcher_dir_path / PHYSIOBLOCKS_REFERENCES_PATH
132
+ if physioblocks_references_folder_path.exists() is True:
133
+ shutil.copytree(
134
+ physioblocks_references_folder_path,
135
+ launcher_references_folder_path,
136
+ dirs_exist_ok=False,
137
+ )
138
+ else:
139
+ _logger.warning(
140
+ str.format(
141
+ "No references configuration folder at: {0}",
142
+ str(physioblocks_references_folder_path),
143
+ )
144
+ )
145
+
146
+ # create the user library directory
147
+ launcher_user_library_dir_path = launcher_dir_path / LAUNCHER_USER_LIBRARY_DIR_NAME
148
+ if launcher_user_library_dir_path.exists() is False:
149
+ launcher_user_library_dir_path.mkdir()
150
+
151
+ # put an empty __init__.py file in the user library
152
+ launcher_user_library_init_path = (
153
+ launcher_user_library_dir_path / LAUNCHER_MODULE_INIT_FILE_NAME
154
+ )
155
+ launcher_user_library_init_path.touch()
156
+
157
+ # create the user aliases directory
158
+ launcher_user_aliases_dir_path = launcher_dir_path / LAUNCHER_USER_ALIASES_DIR_NAME
159
+ if launcher_user_aliases_dir_path.exists() is False:
160
+ launcher_user_aliases_dir_path.mkdir()
161
+
162
+ # create a empty log file
163
+ launcher_log_file_path = launcher_dir_path / LAUNCHER_LOG_FILE_NAME
164
+ launcher_log_file_path.touch()
165
+
166
+ # create the launcher configuration file
167
+ base_library_dir_path = Path(physioblocks.library.__file__).parent
168
+ base_aliases_dir_path = (
169
+ Path(physioblocks.library.__file__).parent / LAUNCHER_CONFIGURATION_ALIAS_ID
170
+ )
171
+ base_configuration = {
172
+ LAUNCHER_CONFIGURATION_LIBRARIES_ID: [
173
+ str(base_library_dir_path.absolute()),
174
+ str(launcher_user_library_dir_path.absolute()),
175
+ ],
176
+ LAUNCHER_CONFIGURATION_ALIAS_ID: [
177
+ str(base_aliases_dir_path.absolute()),
178
+ str(launcher_user_aliases_dir_path.absolute()),
179
+ ],
180
+ }
181
+ launcher_configuration_file_path = (
182
+ launcher_dir_path / LAUNCHER_CONFIGURATION_FILE_NAME
183
+ )
184
+ launcher_configuration_file_path.write_text(
185
+ json.dumps(base_configuration, indent=4)
186
+ )
187
+
188
+ # create a gitignore file
189
+ launcher_gitignore_path = Path(launcher_dir_path / LAUNCHER_GITIGNORE_FILE_NAME)
190
+ launcher_gitignore_path.write_text("*")
191
+
192
+
193
+ def get_launcher_configuration(root_sim_directory: Path) -> Any:
194
+ """
195
+ Get the launcher configuration for the given simulation directory.
196
+
197
+ :param root_sim_directory: the path to the launcher directory.
198
+ :type root_sim_directory: Path
199
+
200
+ :return: the configuration
201
+ :rtype: dict
202
+ """
203
+ launcher_config_path = root_sim_directory / LAUNCHER_CONFIGURATION_FILE_NAME
204
+ launcher_configuration = json.load(launcher_config_path.open("r"))
205
+ return launcher_configuration
206
+
207
+
208
+ def import_configured_libraries(launcher_configuration: dict[str, Any]) -> None:
209
+ """
210
+ Dynamicaly import libraries in the given configuration
211
+
212
+ :param launcher_configuration: the layncher configuration
213
+ :type launcher_configuration: dict[str, Any]
214
+ """
215
+ libraries_paths = [
216
+ Path(lib_path)
217
+ for lib_path in launcher_configuration[LAUNCHER_CONFIGURATION_LIBRARIES_ID]
218
+ ]
219
+
220
+ import_libraries(libraries_paths)
221
+
222
+
223
+ def import_configured_aliases(launcher_configuration: dict[str, Any]) -> None:
224
+ """
225
+ Dynamicaly import all alias at the given paths in configuration
226
+
227
+ :param launcher_configuration: the launcher configuration
228
+ :type launcher_configuration: dict[str, Any]
229
+ """
230
+ for alias_path in launcher_configuration[LAUNCHER_CONFIGURATION_ALIAS_ID]:
231
+ load_aliases(alias_path)
@@ -0,0 +1,99 @@
1
+ # SPDX-FileCopyrightText: Copyright INRIA
2
+ #
3
+ # SPDX-License-Identifier: LGPL-3.0-only
4
+ #
5
+ # Copyright INRIA
6
+ #
7
+ # This file is part of PhysioBlocks, a library mostly developed by the
8
+ # [Ananke project-team](https://team.inria.fr/ananke) at INRIA.
9
+ #
10
+ # Authors:
11
+ # - Colin Drieu
12
+ # - Dominique Chapelle
13
+ # - François Kimmig
14
+ # - Philippe Moireau
15
+ #
16
+ # PhysioBlocks is free software: you can redistribute it and/or modify it under the
17
+ # terms of the GNU Lesser General Public License as published by the Free Software
18
+ # Foundation, version 3 of the License.
19
+ #
20
+ # PhysioBlocks is distributed in the hope that it will be useful, but WITHOUT ANY
21
+ # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
22
+ # PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
23
+ #
24
+ # You should have received a copy of the GNU Lesser General Public License along with
25
+ # PhysioBlocks. If not, see <https://www.gnu.org/licenses/>.
26
+
27
+ """
28
+ Configure a directory to be used as a root directory for the PhysioBlocks launcher.
29
+ """
30
+
31
+ import argparse
32
+ import logging
33
+ import sys
34
+ from pathlib import Path
35
+
36
+ from physioblocks.launcher.configuration import (
37
+ check_target_launcher_directory,
38
+ setup_launcher_directory,
39
+ )
40
+ from physioblocks.utils import exceptions_utils
41
+
42
+ _logger = logging.getLogger()
43
+ _logger.setLevel(logging.DEBUG)
44
+ sys.excepthook = exceptions_utils.create_uncaught_exception_logger_handler(_logger)
45
+
46
+
47
+ def main(target_dir: Path) -> int:
48
+ if check_target_launcher_directory(target_dir) is False:
49
+ raise OSError(
50
+ str.format(
51
+ "Launcher directory must either not exist already or be empty. "
52
+ "Provided directory: {0}",
53
+ str(target_dir.absolute()),
54
+ )
55
+ )
56
+
57
+ setup_launcher_directory(target_dir)
58
+ _logger.info(
59
+ str.format("Launcher directory created at {0}.", str(target_dir.absolute()))
60
+ )
61
+
62
+ return 0
63
+
64
+
65
+ if __name__ == "__main__":
66
+ parser = argparse.ArgumentParser(
67
+ description=__doc__, formatter_class=argparse.HelpFormatter
68
+ )
69
+ parser.add_argument(
70
+ "-d",
71
+ "--launcher_directory",
72
+ dest="launcher_directory",
73
+ default="./",
74
+ required=False,
75
+ help="The directory where to setup the launcher.",
76
+ )
77
+
78
+ parser.add_argument(
79
+ "-v",
80
+ "--verbose",
81
+ dest="verbose",
82
+ action="store_true",
83
+ default=False,
84
+ required=False,
85
+ help="Display logs in console",
86
+ )
87
+
88
+ args = parser.parse_args()
89
+
90
+ # Direct the logs to stdout
91
+ if args.verbose is True:
92
+ stdout_handler = logging.StreamHandler(sys.stdout)
93
+ stdout_handler.setFormatter(logging.Formatter(logging.BASIC_FORMAT))
94
+ stdout_handler.setLevel(logging.DEBUG)
95
+ _logger.addHandler(stdout_handler)
96
+
97
+ # Convert the str path to a Pathlib Path
98
+ launcher_directory_path = Path(args.launcher_directory)
99
+ sys.exit(main(launcher_directory_path))
@@ -0,0 +1,105 @@
1
+ # SPDX-FileCopyrightText: Copyright INRIA
2
+ #
3
+ # SPDX-License-Identifier: LGPL-3.0-only
4
+ #
5
+ # Copyright INRIA
6
+ #
7
+ # This file is part of PhysioBlocks, a library mostly developed by the
8
+ # [Ananke project-team](https://team.inria.fr/ananke) at INRIA.
9
+ #
10
+ # Authors:
11
+ # - Colin Drieu
12
+ # - Dominique Chapelle
13
+ # - François Kimmig
14
+ # - Philippe Moireau
15
+ #
16
+ # PhysioBlocks is free software: you can redistribute it and/or modify it under the
17
+ # terms of the GNU Lesser General Public License as published by the Free Software
18
+ # Foundation, version 3 of the License.
19
+ #
20
+ # PhysioBlocks is distributed in the hope that it will be useful, but WITHOUT ANY
21
+ # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
22
+ # PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
23
+ #
24
+ # You should have received a copy of the GNU Lesser General Public License along with
25
+ # PhysioBlocks. If not, see <https://www.gnu.org/licenses/>.
26
+
27
+ """Define constants for the launcher scripts"""
28
+
29
+ # SIMULATION INFO
30
+
31
+ LAUNCHER_DATE_TIME_FORMAT = "%Y-%m-%d %H:%M:%S"
32
+ """Launcher format the represent date and time"""
33
+
34
+ LAUNCHER_HOSTNAME_SEPARATOR = "."
35
+ """Launcher format the represent date and time"""
36
+
37
+ LAUNCHER_REFERENCE_SEPARATOR = "_"
38
+ """Separator for SimulationInfo reference"""
39
+
40
+ LAUNCHER_REFERENCE_COLUMN = "reference"
41
+ """SimulationInfo reference column id"""
42
+
43
+ LAUNCHER_DATE_COLUMN = "date_time"
44
+ """SimulationInfo date column id"""
45
+
46
+ LAUNCHER_MACHINE_COLUMN = "machine"
47
+ """SimulationInfo machine column id"""
48
+
49
+ LAUNCHER_VERSION_COLUMN = "version"
50
+ """SimulationInfo version column id"""
51
+
52
+ LAUNCHER_NOTES_COLUMN = "notes"
53
+ """SimulationInfo notes column id"""
54
+
55
+ LAUNCHER_LOG_COLUMNS = [
56
+ LAUNCHER_DATE_COLUMN,
57
+ LAUNCHER_MACHINE_COLUMN,
58
+ LAUNCHER_REFERENCE_COLUMN,
59
+ LAUNCHER_VERSION_COLUMN,
60
+ LAUNCHER_NOTES_COLUMN,
61
+ ]
62
+ """The logged simulation info informations"""
63
+
64
+ LAUNCHER_LOG_FILE_SEPARATOR = ";"
65
+ """the launcher log separator"""
66
+
67
+ # FILES
68
+
69
+ LAUNCHER_SERIES_DIR_NAME = "simulations"
70
+ """name of the series directory."""
71
+
72
+ LAUNCHER_LOG_FILE_NAME = "launcher.log"
73
+ """name of the log file."""
74
+
75
+ LAUNCHER_CONFIGURATION_FILE_NAME = "launcher.json"
76
+ """name of the configuration file."""
77
+
78
+ LAUNCHER_GITIGNORE_FILE_NAME = ".gitignore"
79
+ """name of the gitignore file."""
80
+
81
+ LAUNCHER_USER_LIBRARY_DIR_NAME = "user_library"
82
+ """name of the user library directory."""
83
+
84
+ LAUNCHER_USER_ALIASES_DIR_NAME = "user_aliases"
85
+ """name of the user aliases directory."""
86
+
87
+ LAUNCHER_CONFIGURATION_FILE_NAME = "launcher.json"
88
+ """name of the configuration file."""
89
+
90
+ LAUNCHER_MODULE_INIT_FILE_NAME = "__init__.py"
91
+ """Init file name"""
92
+
93
+ LAUNCHER_COMPARE_TRACE_FILE_NAME = "reference_compare.html"
94
+ """Name of an error file generated with the compare module"""
95
+
96
+ # CONFIGURATION
97
+
98
+ LAUNCHER_CONFIGURATION_LIBRARIES_ID = "libraries"
99
+ """name of the libraries item in the configuration"""
100
+
101
+ LAUNCHER_CONFIGURATION_ALIAS_ID = "aliases"
102
+ """name of the aliases item in the configuration"""
103
+
104
+ PHYSIOBLOCKS_REFERENCES_PATH = "references"
105
+ """Relative path to the references folder"""
@@ -0,0 +1,150 @@
1
+ # SPDX-FileCopyrightText: Copyright INRIA
2
+ #
3
+ # SPDX-License-Identifier: LGPL-3.0-only
4
+ #
5
+ # Copyright INRIA
6
+ #
7
+ # This file is part of PhysioBlocks, a library mostly developed by the
8
+ # [Ananke project-team](https://team.inria.fr/ananke) at INRIA.
9
+ #
10
+ # Authors:
11
+ # - Colin Drieu
12
+ # - Dominique Chapelle
13
+ # - François Kimmig
14
+ # - Philippe Moireau
15
+ #
16
+ # PhysioBlocks is free software: you can redistribute it and/or modify it under the
17
+ # terms of the GNU Lesser General Public License as published by the Free Software
18
+ # Foundation, version 3 of the License.
19
+ #
20
+ # PhysioBlocks is distributed in the hope that it will be useful, but WITHOUT ANY
21
+ # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
22
+ # PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
23
+ #
24
+ # You should have received a copy of the GNU Lesser General Public License along with
25
+ # PhysioBlocks. If not, see <https://www.gnu.org/licenses/>.
26
+
27
+ """
28
+ Describe function to handles the various launcher directory and files
29
+ """
30
+
31
+ from pathlib import Path
32
+
33
+ import pandas as pd
34
+ import plotly
35
+ import plotly.graph_objects as go
36
+ import plotly.subplots
37
+
38
+ from physioblocks.launcher.constants import (
39
+ LAUNCHER_DATE_COLUMN,
40
+ LAUNCHER_LOG_FILE_NAME,
41
+ LAUNCHER_REFERENCE_COLUMN,
42
+ LAUNCHER_SERIES_DIR_NAME,
43
+ )
44
+ from physioblocks.launcher.series import SimulationInfo, is_valid_reference_name
45
+
46
+
47
+ def write_simulation_log_entry(root_folder_path: Path, info: SimulationInfo) -> None:
48
+ launcher_log_path = root_folder_path / LAUNCHER_LOG_FILE_NAME
49
+ if launcher_log_path.exists() and launcher_log_path.is_file():
50
+ try:
51
+ data = pd.read_csv(launcher_log_path)
52
+ data = update_simulations_log(root_folder_path, data)
53
+ # add the current simulation informations to the log
54
+ data = pd.concat(
55
+ [
56
+ data.set_index(LAUNCHER_DATE_COLUMN),
57
+ info.data_frame.set_index(LAUNCHER_DATE_COLUMN),
58
+ ]
59
+ )
60
+ data.to_csv(launcher_log_path)
61
+
62
+ except pd.errors.EmptyDataError:
63
+ # Only log the current info
64
+ info.data_frame.set_index(LAUNCHER_DATE_COLUMN).to_csv(launcher_log_path)
65
+ else:
66
+ raise FileNotFoundError(
67
+ str.format(
68
+ "There is no log for the launcher at {0}",
69
+ str(launcher_log_path.absolute()),
70
+ )
71
+ )
72
+
73
+
74
+ def update_simulations_log(
75
+ root_folder_path: Path, sim_log: pd.DataFrame
76
+ ) -> pd.DataFrame:
77
+ launcher_log_path = root_folder_path / LAUNCHER_LOG_FILE_NAME
78
+ series_dir_path = root_folder_path / LAUNCHER_SERIES_DIR_NAME
79
+
80
+ if launcher_log_path.exists() is True:
81
+ # remove previous reference from log if it has been deleted
82
+ all_references_name = [
83
+ reference_dir_path.name
84
+ for serie_dir_path in series_dir_path.iterdir()
85
+ if serie_dir_path.is_dir()
86
+ for reference_dir_path in serie_dir_path.iterdir()
87
+ if is_valid_reference_name(reference_dir_path.name)
88
+ ]
89
+ following_references_mask = [
90
+ sim_log.loc[i][LAUNCHER_REFERENCE_COLUMN] not in all_references_name
91
+ for i in sim_log.index
92
+ ]
93
+ following_references_index = sim_log.iloc[following_references_mask].index
94
+ new_sim_log = sim_log.drop(following_references_index)
95
+ return new_sim_log
96
+
97
+ raise FileNotFoundError(
98
+ "There is no log for the launcher at {0}", str(launcher_log_path.absolute())
99
+ )
100
+
101
+
102
+ def write_simulation_results(
103
+ simulation_folder: Path, info: SimulationInfo, data: pd.DataFrame, extension: str
104
+ ) -> None:
105
+ file_path = simulation_folder / str.join(".", [info.reference, extension])
106
+ match extension:
107
+ case "csv":
108
+ data.to_csv(file_path, index=False, sep=";")
109
+ case "parquet":
110
+ data.to_parquet(file_path, index=False)
111
+ case _:
112
+ raise OSError(str.format("Invalid file extension: {0}", extension))
113
+
114
+
115
+ def write_figure(
116
+ data_frame: pd.DataFrame,
117
+ folder_path: Path,
118
+ file_name: str,
119
+ rows_height: float = 200.0,
120
+ ) -> None:
121
+ init_figure = go.Figure(
122
+ layout=go.Layout(height=rows_height * len(data_frame.columns))
123
+ )
124
+ figure = plotly.subplots.make_subplots(
125
+ len(data_frame.columns), 1, figure=init_figure, shared_xaxes=True
126
+ )
127
+
128
+ plot_index = 1
129
+ for data_id in data_frame.columns:
130
+ # Trace results
131
+ figure.add_trace(
132
+ go.Scatter(
133
+ x=data_frame.index,
134
+ y=data_frame[data_id],
135
+ mode="lines",
136
+ name=data_id,
137
+ ),
138
+ plot_index,
139
+ 1,
140
+ )
141
+
142
+ figure.update_xaxes(title="time", row=plot_index, col=1)
143
+ figure.update_yaxes(
144
+ title=data_id,
145
+ row=plot_index,
146
+ col=1,
147
+ )
148
+ plot_index += 1
149
+
150
+ figure.write_html(folder_path / file_name)