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
physioblocks/__init__.py
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
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
|
+
"""Physioblocks package definition"""
|
|
28
|
+
|
|
29
|
+
__version__ = "1.0.0"
|
|
30
|
+
__copyright__ = "INRIA"
|
|
31
|
+
__license__ = "LGPL-3.0-only"
|
|
32
|
+
__authors__ = [
|
|
33
|
+
"Colin Drieu",
|
|
34
|
+
"Dominique Chapelle",
|
|
35
|
+
"François Kimmig",
|
|
36
|
+
"Philippe Moireau",
|
|
37
|
+
]
|
|
@@ -0,0 +1,27 @@
|
|
|
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
|
+
"""Defines bases classes and functions."""
|
|
@@ -0,0 +1,176 @@
|
|
|
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 base classe to provide generic base operators"""
|
|
28
|
+
|
|
29
|
+
from abc import ABC, abstractmethod
|
|
30
|
+
from typing import Any
|
|
31
|
+
|
|
32
|
+
import numpy as np
|
|
33
|
+
from numpy.typing import NDArray
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
class AbstractBaseOperators(ABC):
|
|
37
|
+
"""Base class defining behaviors for common operators
|
|
38
|
+
|
|
39
|
+
The derived classes should implement the :attr:`operation_value` property
|
|
40
|
+
to return the attribute of the class that will be used with base
|
|
41
|
+
operators.
|
|
42
|
+
"""
|
|
43
|
+
|
|
44
|
+
@property
|
|
45
|
+
@abstractmethod
|
|
46
|
+
def operation_value(self) -> Any:
|
|
47
|
+
"""
|
|
48
|
+
Overwrite in derived class to return the value used by the
|
|
49
|
+
class for the base operators.
|
|
50
|
+
"""
|
|
51
|
+
raise NotImplementedError(
|
|
52
|
+
str.format(
|
|
53
|
+
"{0} is not implemented in {1} class.",
|
|
54
|
+
AbstractBaseOperators.operation_value.__name__,
|
|
55
|
+
type(self),
|
|
56
|
+
)
|
|
57
|
+
)
|
|
58
|
+
|
|
59
|
+
# Convertion
|
|
60
|
+
|
|
61
|
+
def __float__(self) -> float:
|
|
62
|
+
return float(self.operation_value)
|
|
63
|
+
|
|
64
|
+
def __array__(
|
|
65
|
+
self, dtype: type | None = None, copy: bool | None = None
|
|
66
|
+
) -> NDArray[Any]:
|
|
67
|
+
return np.asarray(self.operation_value, dtype=dtype, copy=copy)
|
|
68
|
+
|
|
69
|
+
# Compare
|
|
70
|
+
|
|
71
|
+
# Equality
|
|
72
|
+
def __eq__(self, other: Any) -> bool:
|
|
73
|
+
compared_value = (
|
|
74
|
+
other.operation_value if isinstance(other, type(self)) else other
|
|
75
|
+
)
|
|
76
|
+
return bool(self.operation_value == compared_value)
|
|
77
|
+
|
|
78
|
+
# Greater
|
|
79
|
+
def __ge__(self, other: Any) -> bool:
|
|
80
|
+
if isinstance(other, type(self)):
|
|
81
|
+
return bool(self.operation_value >= other.operation_value)
|
|
82
|
+
return bool(self.operation_value >= other)
|
|
83
|
+
|
|
84
|
+
def __gt__(self, other: Any) -> bool:
|
|
85
|
+
if isinstance(other, type(self)):
|
|
86
|
+
return bool(self.operation_value > other.operation_value)
|
|
87
|
+
return bool(self.operation_value > other)
|
|
88
|
+
|
|
89
|
+
# Lesser
|
|
90
|
+
def __le__(self, other: Any) -> bool:
|
|
91
|
+
if isinstance(other, type(self)):
|
|
92
|
+
return bool(self.operation_value <= other.operation_value)
|
|
93
|
+
return bool(self.operation_value <= other)
|
|
94
|
+
|
|
95
|
+
def __lt__(self, other: Any) -> bool:
|
|
96
|
+
if isinstance(other, type(self)):
|
|
97
|
+
return bool(self.operation_value < other.operation_value)
|
|
98
|
+
return bool(self.operation_value < other)
|
|
99
|
+
|
|
100
|
+
# Base Operations
|
|
101
|
+
|
|
102
|
+
# Sum
|
|
103
|
+
def __add__(self, other: Any) -> Any:
|
|
104
|
+
if isinstance(other, type(self)):
|
|
105
|
+
return self.operation_value + other.operation_value
|
|
106
|
+
return self.operation_value + other
|
|
107
|
+
|
|
108
|
+
def __radd__(self, other: Any) -> Any:
|
|
109
|
+
return other + self.operation_value
|
|
110
|
+
|
|
111
|
+
# Subtraction
|
|
112
|
+
def __sub__(self, other: Any) -> Any:
|
|
113
|
+
if isinstance(other, type(self)):
|
|
114
|
+
return self.operation_value - other.operation_value
|
|
115
|
+
return self.operation_value - other
|
|
116
|
+
|
|
117
|
+
def __rsub__(self, other: Any) -> Any:
|
|
118
|
+
return other - self.operation_value
|
|
119
|
+
|
|
120
|
+
# Oposite
|
|
121
|
+
def __neg__(self) -> Any:
|
|
122
|
+
return -self.operation_value
|
|
123
|
+
|
|
124
|
+
# Multiplication
|
|
125
|
+
def __mul__(self, other: Any) -> Any:
|
|
126
|
+
if isinstance(other, type(self)):
|
|
127
|
+
return self.operation_value * other.operation_value
|
|
128
|
+
return self.operation_value * other
|
|
129
|
+
|
|
130
|
+
def __rmul__(self, other: Any) -> Any:
|
|
131
|
+
return other * self.operation_value
|
|
132
|
+
|
|
133
|
+
# MatMul
|
|
134
|
+
def __matmul__(self, other: Any) -> Any:
|
|
135
|
+
if isinstance(other, type(self)):
|
|
136
|
+
return self.operation_value @ other.operation_value
|
|
137
|
+
return self.operation_value @ other
|
|
138
|
+
|
|
139
|
+
def __rmatmul__(self, other: Any) -> Any:
|
|
140
|
+
return other @ self.operation_value
|
|
141
|
+
|
|
142
|
+
# Division
|
|
143
|
+
def __truediv__(self, other: Any) -> Any:
|
|
144
|
+
if isinstance(other, type(self)):
|
|
145
|
+
return self.operation_value / other.operation_value
|
|
146
|
+
return self.operation_value / other
|
|
147
|
+
|
|
148
|
+
def __rtruediv__(self, other: Any) -> Any:
|
|
149
|
+
return other / self.operation_value
|
|
150
|
+
|
|
151
|
+
# Floor Division
|
|
152
|
+
def __floordiv__(self, other: Any) -> Any:
|
|
153
|
+
if isinstance(other, type(self)):
|
|
154
|
+
return self.operation_value // other.operation_value
|
|
155
|
+
return self.operation_value // other
|
|
156
|
+
|
|
157
|
+
def __rfloordiv__(self, other: Any) -> Any:
|
|
158
|
+
return other // self.operation_value
|
|
159
|
+
|
|
160
|
+
# Modulo
|
|
161
|
+
def __mod__(self, other: Any) -> Any:
|
|
162
|
+
if isinstance(other, type(self)):
|
|
163
|
+
return self.operation_value % other.operation_value
|
|
164
|
+
return self.operation_value % other
|
|
165
|
+
|
|
166
|
+
def __rmod__(self, other: Any) -> Any:
|
|
167
|
+
return other % self.operation_value
|
|
168
|
+
|
|
169
|
+
# Power
|
|
170
|
+
def __pow__(self, other: Any) -> Any:
|
|
171
|
+
if isinstance(other, type(self)):
|
|
172
|
+
return self.operation_value**other.operation_value
|
|
173
|
+
return self.operation_value**other
|
|
174
|
+
|
|
175
|
+
def __rpow__(self, other: Any) -> Any:
|
|
176
|
+
return other**self.operation_value
|
|
@@ -0,0 +1,108 @@
|
|
|
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 functions to update two-way :type:`dict` using common :type:`dict`"""
|
|
28
|
+
|
|
29
|
+
from typing import Any
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def register(
|
|
33
|
+
register: dict[Any, Any], registered_key: Any, registered_value: Any
|
|
34
|
+
) -> None:
|
|
35
|
+
"""
|
|
36
|
+
Register the key and value to the provided register.
|
|
37
|
+
|
|
38
|
+
:param register: the register to fill.
|
|
39
|
+
:type dict[Any, Any]:
|
|
40
|
+
|
|
41
|
+
:param registered_key: the key to register.
|
|
42
|
+
:type Any:
|
|
43
|
+
|
|
44
|
+
:param registered_value: the value to register.
|
|
45
|
+
:type Any:
|
|
46
|
+
|
|
47
|
+
:raise ValueError: Raises a ValueError if either the key or value
|
|
48
|
+
is already registered in the provided register.
|
|
49
|
+
"""
|
|
50
|
+
if registered_key in register or registered_value in register:
|
|
51
|
+
raise RegisterError(
|
|
52
|
+
str.format(
|
|
53
|
+
"{0}: {1} key or value is already registered.",
|
|
54
|
+
registered_key,
|
|
55
|
+
registered_value,
|
|
56
|
+
)
|
|
57
|
+
)
|
|
58
|
+
|
|
59
|
+
register[registered_key] = registered_value
|
|
60
|
+
register[registered_value] = registered_key
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def check_key_value_type(
|
|
64
|
+
key: Any, key_type: type[Any], value: Any, value_type: type[Any]
|
|
65
|
+
) -> None:
|
|
66
|
+
"""
|
|
67
|
+
Check if key and params are of the expected types.
|
|
68
|
+
|
|
69
|
+
If not, it raises a type error.
|
|
70
|
+
|
|
71
|
+
:param key: the key value
|
|
72
|
+
:type key: Any
|
|
73
|
+
|
|
74
|
+
:param key_type: the expected type for the key
|
|
75
|
+
:type key_type: type
|
|
76
|
+
|
|
77
|
+
:param value: the value
|
|
78
|
+
:type value: Any
|
|
79
|
+
|
|
80
|
+
:param value_type: the expected type for the value
|
|
81
|
+
:type value_type: type
|
|
82
|
+
|
|
83
|
+
:raises TypeError: raise a type error if key or value param is not of the expected
|
|
84
|
+
types
|
|
85
|
+
"""
|
|
86
|
+
if isinstance(key, key_type) is False:
|
|
87
|
+
raise TypeError(
|
|
88
|
+
str.format(
|
|
89
|
+
"Expected type for key is {0} but got {1}.",
|
|
90
|
+
key_type.__name__,
|
|
91
|
+
type(key).__name__,
|
|
92
|
+
)
|
|
93
|
+
)
|
|
94
|
+
|
|
95
|
+
elif isinstance(value, value_type) is False:
|
|
96
|
+
raise TypeError(
|
|
97
|
+
str.format(
|
|
98
|
+
"Expected type for value is {0} but got {1}.",
|
|
99
|
+
value_type.__name__,
|
|
100
|
+
type(value).__name__,
|
|
101
|
+
)
|
|
102
|
+
)
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
class RegisterError(Exception):
|
|
106
|
+
"""Error Raised when a key or value has already been registered."""
|
|
107
|
+
|
|
108
|
+
pass
|
|
@@ -0,0 +1,47 @@
|
|
|
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
|
+
Computes numeric values for the models.
|
|
29
|
+
"""
|
|
30
|
+
|
|
31
|
+
__all__ = [
|
|
32
|
+
"Block",
|
|
33
|
+
"Expression",
|
|
34
|
+
"ModelComponent",
|
|
35
|
+
"Quantity",
|
|
36
|
+
"diff",
|
|
37
|
+
"mid_alpha",
|
|
38
|
+
"mid_point",
|
|
39
|
+
]
|
|
40
|
+
|
|
41
|
+
from physioblocks.computing.models import Block, Expression, ModelComponent
|
|
42
|
+
from physioblocks.computing.quantities import (
|
|
43
|
+
Quantity,
|
|
44
|
+
diff,
|
|
45
|
+
mid_alpha,
|
|
46
|
+
mid_point,
|
|
47
|
+
)
|
|
@@ -0,0 +1,291 @@
|
|
|
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
|
+
Allows to build systems that computes residual and gradient from collections
|
|
29
|
+
of functions.
|
|
30
|
+
"""
|
|
31
|
+
|
|
32
|
+
from __future__ import annotations
|
|
33
|
+
|
|
34
|
+
from dataclasses import dataclass
|
|
35
|
+
from pprint import pformat
|
|
36
|
+
from typing import Any
|
|
37
|
+
|
|
38
|
+
import numpy as np
|
|
39
|
+
from numpy.typing import NDArray
|
|
40
|
+
|
|
41
|
+
from physioblocks.computing.models import ModelComponent, SystemFunction
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
@dataclass
|
|
45
|
+
class _EqSystemPart:
|
|
46
|
+
"""
|
|
47
|
+
Part of the global equation system.
|
|
48
|
+
|
|
49
|
+
It computes a term that can be summed in the global system at the provided index.
|
|
50
|
+
"""
|
|
51
|
+
|
|
52
|
+
line_index: int
|
|
53
|
+
"""Index where to sum the part in the global system"""
|
|
54
|
+
res_part_size: int
|
|
55
|
+
"""Number of lines in the result of the residual part function"""
|
|
56
|
+
model_component: ModelComponent
|
|
57
|
+
"""The model that holds the data use to compute the residual and gradient part"""
|
|
58
|
+
res_func: SystemFunction
|
|
59
|
+
"""The function to compute the residual part"""
|
|
60
|
+
column_index: int
|
|
61
|
+
"""Start column index of the gradient part"""
|
|
62
|
+
grad_part_size: int
|
|
63
|
+
"""Number of colums in the result of the gradient part function"""
|
|
64
|
+
grad_funcs: dict[int, SystemFunction]
|
|
65
|
+
"""A collection of fonctions to compute the gradient part for each variables,
|
|
66
|
+
associated with their column index"""
|
|
67
|
+
|
|
68
|
+
def compute_residual_part(self) -> np.float64 | NDArray[np.float64]:
|
|
69
|
+
"""
|
|
70
|
+
Compute the residual part numerical value.
|
|
71
|
+
|
|
72
|
+
Can be a scalar or vector of size _res_part_size.
|
|
73
|
+
|
|
74
|
+
:return: the residual part value
|
|
75
|
+
:rtype: np.float64 | NDArray[np.float64]
|
|
76
|
+
"""
|
|
77
|
+
return self.res_func(self.model_component)
|
|
78
|
+
|
|
79
|
+
def compute_gradient_part(self) -> NDArray[np.float64]:
|
|
80
|
+
"""
|
|
81
|
+
Compute the gradient part numerical value.
|
|
82
|
+
|
|
83
|
+
It a matrix of size (_res_part_size, _grad_part_size) to sum in the
|
|
84
|
+
global gradient at index (_index, _min_grad_index).
|
|
85
|
+
|
|
86
|
+
The numerical values are evaluated where the gradient functions are provided,
|
|
87
|
+
the remaing values are set to 0.
|
|
88
|
+
|
|
89
|
+
:return: the gradient part numerical value
|
|
90
|
+
:rtype: np.float64 | NDArray[np.float64]
|
|
91
|
+
"""
|
|
92
|
+
grad_part = np.zeros(
|
|
93
|
+
(self.res_part_size, self.grad_part_size),
|
|
94
|
+
)
|
|
95
|
+
|
|
96
|
+
for grad_index in self.grad_funcs:
|
|
97
|
+
gradient_func = self.grad_funcs[grad_index]
|
|
98
|
+
grad_colum_index = grad_index - self.column_index
|
|
99
|
+
grad_part[:, grad_colum_index] += gradient_func(self.model_component)
|
|
100
|
+
|
|
101
|
+
return grad_part
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
def _create_eq_system_part(
|
|
105
|
+
residual_index: int,
|
|
106
|
+
residual_size: int,
|
|
107
|
+
residual_func: SystemFunction,
|
|
108
|
+
gradients_func: dict[int, SystemFunction],
|
|
109
|
+
model_component: ModelComponent,
|
|
110
|
+
) -> _EqSystemPart:
|
|
111
|
+
"""
|
|
112
|
+
Create an equation system part.
|
|
113
|
+
|
|
114
|
+
:param index: the line index where to sum the expression in the global
|
|
115
|
+
system
|
|
116
|
+
:type index: int
|
|
117
|
+
|
|
118
|
+
:param residual_size: the size of the result of the residual_func
|
|
119
|
+
:type residual_size: int
|
|
120
|
+
|
|
121
|
+
:param residual_func: the function to compute the residual part
|
|
122
|
+
:type residual_func: SystemFunction
|
|
123
|
+
|
|
124
|
+
:param gradients_func: the functions to compute the gradients parts associated with
|
|
125
|
+
the index of the variable in the state vector
|
|
126
|
+
|
|
127
|
+
:param model_component: the model holding the data needed to compute the expression
|
|
128
|
+
:type model_component: ModelComponent
|
|
129
|
+
|
|
130
|
+
:return: an equation system part
|
|
131
|
+
:rtype: _EqSystemPart
|
|
132
|
+
"""
|
|
133
|
+
grad_part_size = 0
|
|
134
|
+
min_grad_index = 0
|
|
135
|
+
if len(gradients_func) > 0:
|
|
136
|
+
min_grad_index = min(gradients_func.keys())
|
|
137
|
+
grad_part_size = max(gradients_func) - min_grad_index + 1
|
|
138
|
+
|
|
139
|
+
return _EqSystemPart(
|
|
140
|
+
residual_index,
|
|
141
|
+
residual_size,
|
|
142
|
+
model_component,
|
|
143
|
+
residual_func,
|
|
144
|
+
min_grad_index,
|
|
145
|
+
grad_part_size,
|
|
146
|
+
gradients_func,
|
|
147
|
+
)
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
class EqSystem:
|
|
151
|
+
"""
|
|
152
|
+
Global Equation system
|
|
153
|
+
|
|
154
|
+
Allows to compute the numerical value of the residual and the gradient
|
|
155
|
+
for a :class:`~physioblocks.description.nets.Net`.
|
|
156
|
+
|
|
157
|
+
The system is built from a collection of **System Parts**.
|
|
158
|
+
The parts are summed in a global system to compute the numerical value of
|
|
159
|
+
the residual and the gradient.
|
|
160
|
+
"""
|
|
161
|
+
|
|
162
|
+
_system_size: int
|
|
163
|
+
"""The system size, fixed at system creation"""
|
|
164
|
+
_system_parts: list[_EqSystemPart]
|
|
165
|
+
"""The collection of system parts"""
|
|
166
|
+
|
|
167
|
+
def __init__(self, size: int):
|
|
168
|
+
"""
|
|
169
|
+
EqSystem constructor
|
|
170
|
+
|
|
171
|
+
:param size: system fixed size
|
|
172
|
+
:type size: int
|
|
173
|
+
"""
|
|
174
|
+
self._system_size = size
|
|
175
|
+
self._system_parts = []
|
|
176
|
+
|
|
177
|
+
def __str__(self) -> str:
|
|
178
|
+
"""
|
|
179
|
+
Get the equation system string representation.
|
|
180
|
+
|
|
181
|
+
:return: the equation system string representation
|
|
182
|
+
:rtype: str
|
|
183
|
+
"""
|
|
184
|
+
|
|
185
|
+
eq_system_dict = {
|
|
186
|
+
index: [
|
|
187
|
+
(eq_part.res_func.__qualname__, "size " + str(eq_part.res_part_size))
|
|
188
|
+
for eq_part in self._system_parts
|
|
189
|
+
if eq_part.line_index == index
|
|
190
|
+
]
|
|
191
|
+
for index in range(0, self.system_size)
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
return pformat(eq_system_dict, indent=2, compact=False)
|
|
195
|
+
|
|
196
|
+
@property
|
|
197
|
+
def system_size(self) -> int:
|
|
198
|
+
"""
|
|
199
|
+
Get the system size
|
|
200
|
+
|
|
201
|
+
:return: the system size
|
|
202
|
+
:rtype: int
|
|
203
|
+
"""
|
|
204
|
+
return self._system_size
|
|
205
|
+
|
|
206
|
+
def add_system_part(
|
|
207
|
+
self,
|
|
208
|
+
residual_index: int,
|
|
209
|
+
residual_size: int,
|
|
210
|
+
residual_func: SystemFunction,
|
|
211
|
+
gradients_func: dict[int, SystemFunction],
|
|
212
|
+
parameters: Any,
|
|
213
|
+
) -> None:
|
|
214
|
+
"""add_system_part(self, residual_index: int, residual_size: int, residual_func: SystemFunction, gradients_func: dict[int, SystemFunction], parameters: Any)
|
|
215
|
+
Add an equation system part to the global system.
|
|
216
|
+
|
|
217
|
+
:param residual_index:
|
|
218
|
+
the line index where to sum the function in the global system
|
|
219
|
+
:type residual_index: int
|
|
220
|
+
|
|
221
|
+
:param residual_size: the size of the result of the residual_func
|
|
222
|
+
:type residual_size: int
|
|
223
|
+
|
|
224
|
+
:param residual_func: the function to compute the residual part
|
|
225
|
+
:type residual_func: SystemFunction
|
|
226
|
+
|
|
227
|
+
:param gradients_func:
|
|
228
|
+
the functions to compute the gradients parts associated with
|
|
229
|
+
the index of the variable in the state vector
|
|
230
|
+
|
|
231
|
+
:param parameters: the parameters needed to compute the SystemFunctions
|
|
232
|
+
(both residual and gradient)
|
|
233
|
+
(should be the same type as the argument of the residual_func and the
|
|
234
|
+
gradients_func input type)
|
|
235
|
+
:type parameters: Any
|
|
236
|
+
|
|
237
|
+
:raise ValueError: Raises a ValueError when either the size of the residual
|
|
238
|
+
or the gradient part will exceed the global system size.
|
|
239
|
+
""" # noqa: E501
|
|
240
|
+
|
|
241
|
+
part = _create_eq_system_part(
|
|
242
|
+
residual_index, residual_size, residual_func, gradients_func, parameters
|
|
243
|
+
)
|
|
244
|
+
|
|
245
|
+
if part.line_index + part.res_part_size > self.system_size:
|
|
246
|
+
raise ValueError("The residual part exceed system size")
|
|
247
|
+
|
|
248
|
+
if part.column_index + part.grad_part_size > self.system_size:
|
|
249
|
+
raise ValueError("The gradient part exceed system size")
|
|
250
|
+
|
|
251
|
+
self._system_parts.append(part)
|
|
252
|
+
|
|
253
|
+
def compute_residual(self) -> NDArray[np.float64]:
|
|
254
|
+
"""
|
|
255
|
+
Compute the numerical value of the residual.
|
|
256
|
+
|
|
257
|
+
:return: the residual
|
|
258
|
+
:rtype: NDArray[np.float64]
|
|
259
|
+
"""
|
|
260
|
+
|
|
261
|
+
residual = np.zeros(
|
|
262
|
+
shape=self.system_size,
|
|
263
|
+
)
|
|
264
|
+
|
|
265
|
+
for part in self._system_parts:
|
|
266
|
+
residual_part = part.compute_residual_part()
|
|
267
|
+
residual_column_index = part.line_index + part.res_part_size
|
|
268
|
+
residual[part.line_index : residual_column_index] += residual_part
|
|
269
|
+
|
|
270
|
+
return residual
|
|
271
|
+
|
|
272
|
+
def compute_gradient(self) -> NDArray[np.float64]:
|
|
273
|
+
"""
|
|
274
|
+
Compute the numerical value of the gradient.
|
|
275
|
+
|
|
276
|
+
:return: the gradient
|
|
277
|
+
:rtype: NDArray[np.float64]
|
|
278
|
+
"""
|
|
279
|
+
grad = np.zeros(
|
|
280
|
+
shape=(self.system_size, self.system_size),
|
|
281
|
+
)
|
|
282
|
+
|
|
283
|
+
for part in self._system_parts:
|
|
284
|
+
if part.grad_part_size > 0:
|
|
285
|
+
gradient_part = part.compute_gradient_part()
|
|
286
|
+
grad[
|
|
287
|
+
part.line_index : part.line_index + part.res_part_size,
|
|
288
|
+
part.column_index : part.column_index + part.grad_part_size,
|
|
289
|
+
] += gradient_part
|
|
290
|
+
|
|
291
|
+
return grad
|