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.
- physioblocks/__init__.py +37 -0
- physioblocks/base/__init__.py +27 -0
- physioblocks/base/operators.py +176 -0
- physioblocks/base/registers.py +108 -0
- physioblocks/computing/__init__.py +47 -0
- physioblocks/computing/assembling.py +291 -0
- physioblocks/computing/models.py +811 -0
- physioblocks/computing/quantities.py +354 -0
- physioblocks/configuration/__init__.py +38 -0
- physioblocks/configuration/aliases.py +203 -0
- physioblocks/configuration/base.py +123 -0
- physioblocks/configuration/computing/__init__.py +27 -0
- physioblocks/configuration/computing/quantities.py +56 -0
- physioblocks/configuration/constants.py +121 -0
- physioblocks/configuration/description/__init__.py +33 -0
- physioblocks/configuration/description/blocks.py +239 -0
- physioblocks/configuration/description/nets.py +155 -0
- physioblocks/configuration/functions.py +695 -0
- physioblocks/configuration/simulation/__init__.py +32 -0
- physioblocks/configuration/simulation/simulations.py +280 -0
- physioblocks/description/__init__.py +34 -0
- physioblocks/description/blocks.py +418 -0
- physioblocks/description/flux.py +157 -0
- physioblocks/description/nets.py +746 -0
- physioblocks/io/__init__.py +29 -0
- physioblocks/io/aliases.py +73 -0
- physioblocks/io/configuration.py +125 -0
- physioblocks/launcher/__main__.py +285 -0
- physioblocks/launcher/configuration.py +231 -0
- physioblocks/launcher/configure/__main__.py +99 -0
- physioblocks/launcher/constants.py +105 -0
- physioblocks/launcher/files.py +150 -0
- physioblocks/launcher/series.py +165 -0
- physioblocks/library/__init__.py +27 -0
- physioblocks/library/aliases/blocks/c_block.json +5 -0
- physioblocks/library/aliases/blocks/rc_block.json +5 -0
- physioblocks/library/aliases/blocks/rcr_block.json +5 -0
- physioblocks/library/aliases/blocks/spherical_cavity_block.json +5 -0
- physioblocks/library/aliases/blocks/valve_rl_block.json +5 -0
- physioblocks/library/aliases/flux/heart_flux_dof_couples.jsonc +4 -0
- physioblocks/library/aliases/model_components/active_law_macro_huxley_two_moments.json +5 -0
- physioblocks/library/aliases/model_components/rheology_fiber_additive.json +5 -0
- physioblocks/library/aliases/model_components/spherical_dynamics.json +5 -0
- physioblocks/library/aliases/model_components/velocity_law_hht.json +5 -0
- physioblocks/library/aliases/nets/circulation_alone_net.json +31 -0
- physioblocks/library/aliases/nets/spherical_heart_net.json +93 -0
- physioblocks/library/aliases/simulations/circulation_alone_forward_simulation.jsonc +55 -0
- physioblocks/library/aliases/simulations/default_forward_simulation.jsonc +7 -0
- physioblocks/library/aliases/simulations/default_time.jsonc +8 -0
- physioblocks/library/aliases/simulations/newton_method_solver.json +5 -0
- physioblocks/library/aliases/simulations/spherical_heart_forward_simulation.jsonc +157 -0
- physioblocks/library/aliases/simulations/spherical_heart_with_respiration_forward_simulation.jsonc +45 -0
- physioblocks/library/blocks/__init__.py +27 -0
- physioblocks/library/blocks/capacitances.py +516 -0
- physioblocks/library/blocks/cavity.py +192 -0
- physioblocks/library/blocks/valves.py +281 -0
- physioblocks/library/functions/__init__.py +27 -0
- physioblocks/library/functions/base_operations.py +129 -0
- physioblocks/library/functions/first_order.py +113 -0
- physioblocks/library/functions/piecewise.py +271 -0
- physioblocks/library/functions/trigonometric.py +78 -0
- physioblocks/library/functions/watchers.py +113 -0
- physioblocks/library/model_components/__init__.py +27 -0
- physioblocks/library/model_components/active_law.py +345 -0
- physioblocks/library/model_components/dynamics.py +986 -0
- physioblocks/library/model_components/rheology.py +160 -0
- physioblocks/library/model_components/velocity_law.py +169 -0
- physioblocks/references/circulation_alone_sim.jsonc +24 -0
- physioblocks/references/spherical_heart_respiration_sim.jsonc +33 -0
- physioblocks/references/spherical_heart_sim.jsonc +29 -0
- physioblocks/registers/__init__.py +32 -0
- physioblocks/registers/load_function_register.py +93 -0
- physioblocks/registers/save_function_register.py +106 -0
- physioblocks/registers/type_register.py +97 -0
- physioblocks/simulation/__init__.py +48 -0
- physioblocks/simulation/constants.py +30 -0
- physioblocks/simulation/functions.py +71 -0
- physioblocks/simulation/runtime.py +484 -0
- physioblocks/simulation/saved_quantities.py +129 -0
- physioblocks/simulation/setup.py +576 -0
- physioblocks/simulation/solvers.py +235 -0
- physioblocks/simulation/state.py +340 -0
- physioblocks/simulation/time_manager.py +354 -0
- physioblocks/utils/__init__.py +27 -0
- physioblocks/utils/dynamic_import_utils.py +150 -0
- physioblocks/utils/exceptions_utils.py +115 -0
- physioblocks/utils/gradient_test_utils.py +337 -0
- physioblocks/utils/math_utils.py +109 -0
- physioblocks-1.0.0.dist-info/METADATA +127 -0
- physioblocks-1.0.0.dist-info/RECORD +93 -0
- physioblocks-1.0.0.dist-info/WHEEL +4 -0
- physioblocks-1.0.0.dist-info/licenses/licenses/GPL-3.0-only.txt +674 -0
- 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)
|