Coverage for physioblocks / library / model_components / active_law.py: 100%
70 statements
« prev ^ index » next coverage.py v7.13.1, created at 2026-01-09 16:40 +0100
« prev ^ index » next coverage.py v7.13.1, created at 2026-01-09 16:40 +0100
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/>.
27from dataclasses import dataclass
28from typing import Any
30import numpy as np
31from numpy.typing import NDArray
33from physioblocks.computing import (
34 Expression,
35 ModelComponent,
36 Quantity,
37 diff,
38 mid_point,
39)
40from physioblocks.registers import register_type
41from physioblocks.simulation import Time
43# Constant for the active law type id
44ACTIVE_LAW_MACRO_HUXLEY_TWO_MOMENTS_TYPE_ID = "active_law_macro_huxley_two_moments"
47@register_type(ACTIVE_LAW_MACRO_HUXLEY_TWO_MOMENTS_TYPE_ID)
48@dataclass
49class ActiveLawMacroscopicHuxleyTwoMoment(ModelComponent):
50 r"""
51 Describes the Macroscopic Huxley Two Moment implementation of the ative law.
53 **Internal Equations:**
55 .. math::
57 \dot{K}_c + \bigl(|u|+\alpha|\dot{e}_c|\bigr) \, K_c - n_0(e_c) K_0 |u|_+ = 0
59 .. math::
61 \dot{\lambda}_c + \frac{1}{2} \bigl(|u|+\alpha|\dot{e}_c|\bigr) \, \lambda_c - \sqrt{K_c} \, \dot{e}_c - \frac{n_0(e_c)}{\sqrt{K_c}} \biggl( T_0 - \frac{K_0\,\lambda_c}{2\sqrt{K_c}} \biggr) |u|_+ = 0
64 .. math::
66 \lambda_c - T_c/\sqrt{K_c} = 0
68 **Discretised:**
70 .. math::
72 \frac{K_c^{n+1}-K_c^n}{\Delta t^n} + \Bigl(|u^{n+1}|+\alpha\Bigl|\frac{e_c^{n+1}-e_c^n}{\Delta t^n}\Bigr|\Bigr) \, K_c^{n+1} - n_0(e_c^n) K_0 \, |u^{n+1}|_+ = 0
74 .. math::
76 \frac{\lambda_c^{n+1}-\lambda_c^n}{\Delta t^n} + \frac{1}{2}\Bigl(|u^{n+1}|+\alpha\Bigl|\frac{e_c^{n+1}-e_c^n}{\Delta t^n}\Bigr|\Bigr) \, \lambda_c^{n + \frac{1}{2}} - \sqrt{K_c^{n+1}} \, \frac{e_c^{n+1}-e_c^n}{\Delta t^n} - \frac{n_0(e_c^n)}{\sqrt{K_c^{n+1}}} \biggl( T_0 - \frac{K_0\,\lambda_c^{n + \frac{1}{2}}}{2\sqrt{K_c^{n+1}}} \biggr) |u^{n+1}|_+ = 0
78 .. math::
80 T_c^{n + \frac{1}{2}\sharp} - \lambda_c^{n + \frac{1}{2}} \sqrt{K_c^{n+1}} = 0
81 """ # noqa: E501
83 fib_deform: Quantity[np.float64]
84 """:math:`e_c` the fiber deformation """
86 active_stiffness: Quantity[np.float64]
87 """:math:`K_c` the active stiffness"""
89 active_energy_sqrt: Quantity[np.float64]
90 """:math:`\\lambda_c` the active energy sqrt"""
92 active_tension_discr: Quantity[np.float64]
93 """:math:`T_c^{n + \\frac{1}{2}\\sharp}` the active tension discretisation"""
95 starling_abscissas: Quantity[NDArray[np.float64]]
96 """Abscissas of :math:`n_0` the discretized starling function"""
98 starling_ordinates: Quantity[NDArray[np.float64]]
99 """Ordinates of :math:`n_0` the discretize starling function"""
101 activation: Quantity[np.float64]
102 """:math:`u` the activation function"""
104 crossbridge_stiffness: Quantity[np.float64]
105 """:math:`K_0` the crossbridge stiffness"""
107 contractility: Quantity[np.float64]
108 """:math:`T_0` the contractility"""
110 destruction_rate: Quantity[np.float64]
111 """the destruction rate"""
113 time: Time
114 """:math:`t` the simulation time"""
116 def active_law_residual(self) -> Any:
117 """
118 Compute the residual of the active law.
120 :return: the residual value
121 :rtype: np.array
122 """
123 starling = np.interp(
124 self.fib_deform.current,
125 self.starling_abscissas.current,
126 self.starling_ordinates.current,
127 )
128 abs_plus_starling = np.clip(starling, 0.0, None)
129 abs_plus_activation = np.clip(self.activation.new, 0.0, None)
130 active_stiffness_dynamic_eq = (
131 (self.active_stiffness.new - self.active_stiffness.current)
132 * self.time.inv_dt
133 + self._get_deform_rate() * self.active_stiffness.new
134 - abs_plus_starling
135 * self.crossbridge_stiffness.current
136 * abs_plus_activation
137 )
138 active_energy_sqrt_dynamic_eq = (
139 diff(self.active_energy_sqrt) * self.time.inv_dt
140 + 0.5 * self._get_deform_rate() * mid_point(self.active_energy_sqrt)
141 - (abs_plus_starling / np.sqrt(self.active_stiffness.new))
142 * (
143 self.contractility.current
144 - (
145 0.5
146 * self.crossbridge_stiffness.current
147 * mid_point(self.active_energy_sqrt)
148 / (np.sqrt(self.active_stiffness.new))
149 )
150 )
151 * abs_plus_activation
152 - np.sqrt(self.active_stiffness.new)
153 * diff(self.fib_deform)
154 * self.time.inv_dt
155 )
157 active_tension_discr_relation = self.active_tension_discr.new - np.sqrt(
158 self.active_stiffness.new
159 ) * mid_point(self.active_energy_sqrt)
161 return np.array(
162 [
163 active_stiffness_dynamic_eq,
164 active_energy_sqrt_dynamic_eq,
165 active_tension_discr_relation,
166 ],
167 )
169 def active_law_residual_dactive_stiffness(self) -> Any:
170 """
171 Compute the residual partial derivative for the active stiffness
173 :return: the partial derivative
174 :rtype: np.array
175 """
176 starling = np.interp(
177 self.fib_deform.current,
178 self.starling_abscissas.current,
179 self.starling_ordinates.current,
180 )
181 abs_plus_starling = np.clip(starling, 0.0, None)
182 abs_plus_activation = np.clip(self.activation.new, 0.0, None)
184 active_stiffness_active_law_dactive_stiffness = (
185 self.time.inv_dt + self._get_deform_rate()
186 )
188 active_energy_sqrt_active_law_dactive_stiffness = (
189 -abs_plus_activation
190 * abs_plus_starling
191 * (
192 -0.5
193 * self.contractility.current
194 * np.pow(self.active_stiffness.new, -1.5)
195 + (
196 0.5
197 * self.crossbridge_stiffness.current
198 * mid_point(self.active_energy_sqrt)
199 * np.pow(self.active_stiffness.new, -2)
200 )
201 )
202 - 0.5
203 * diff(self.fib_deform)
204 * self.time.inv_dt
205 / np.sqrt(self.active_stiffness.new)
206 )
208 active_tension_discr_relation_dactive_stiffness = (
209 -0.5
210 * mid_point(self.active_energy_sqrt)
211 / np.sqrt(self.active_stiffness.new)
212 )
214 return np.array(
215 [
216 active_stiffness_active_law_dactive_stiffness,
217 active_energy_sqrt_active_law_dactive_stiffness,
218 active_tension_discr_relation_dactive_stiffness,
219 ],
220 )
222 def active_law_residual_dactive_energy_sqrt(self) -> Any:
223 """
224 Compute the residual partial derivative for the active energy sqrt
226 :return: the partial derivative
227 :rtype: np.array
228 """
229 starling = np.interp(
230 self.fib_deform.current,
231 self.starling_abscissas.current,
232 self.starling_ordinates.current,
233 )
234 abs_plus_starling = np.clip(starling, 0.0, None)
235 abs_plus_activation = np.clip(self.activation.new, 0.0, None)
237 active_stiffness_active_law_dactive_energy_sqrt = 0.0
239 active_energy_sqrt_active_law_dactive_energy_sqrt = (
240 self.time.inv_dt
241 + 0.25
242 * (
243 abs(self.activation.new)
244 + self.destruction_rate.current
245 * (self.fib_deform.new - self.fib_deform.current)
246 * self.time.inv_dt
247 )
248 + 0.25
249 * abs_plus_activation
250 * abs_plus_starling
251 * self.crossbridge_stiffness.current
252 / self.active_stiffness.new
253 )
255 active_tension_discr_relation_dactive_energy_sqrt = -0.5 * np.sqrt(
256 self.active_stiffness.new
257 )
259 return np.array(
260 [
261 active_stiffness_active_law_dactive_energy_sqrt,
262 active_energy_sqrt_active_law_dactive_energy_sqrt,
263 active_tension_discr_relation_dactive_energy_sqrt,
264 ],
265 )
267 def active_law_residual_dfib_deform(self) -> Any:
268 """
269 Compute the residual partial derivative for the fiber deformation
271 :return: the partial derivative
272 :rtype: np.array
273 """
274 active_stiffness_active_law_dfib_deform = (
275 self.destruction_rate.current * self.active_stiffness.new * self.time.inv_dt
276 )
278 active_energy_sqrt_active_law_dfib_deform = -(
279 -0.5
280 * self.destruction_rate.current
281 * mid_point(self.active_energy_sqrt)
282 * self.time.inv_dt
283 + np.sqrt(self.active_stiffness.new) * self.time.inv_dt
284 )
286 active_tension_discr_dfib_deform = 0.0
288 return np.array(
289 [
290 active_stiffness_active_law_dfib_deform,
291 active_energy_sqrt_active_law_dfib_deform,
292 active_tension_discr_dfib_deform,
293 ],
294 )
296 def active_law_residual_dactive_tension(self) -> Any:
297 """
298 Compute the residual partial derivative for the active tension
300 :return: the partial derivative
301 :rtype: np.array
302 """
303 return np.array(
304 [0.0, 0.0, 1.0],
305 )
307 def _get_deform_rate(self) -> Any:
308 return np.fabs(self.activation.new) + (
309 self.destruction_rate.current
310 * np.fabs(diff(self.fib_deform))
311 * self.time.inv_dt
312 )
315# Define the expression for the residual of the ActiveLawMacroscopicHuxleyTwoMoment and
316# its partial derivatives.
317_active_law_macroscopic_huxley_two_moment_residual_expression = Expression(
318 3,
319 ActiveLawMacroscopicHuxleyTwoMoment.active_law_residual,
320 {
321 "active_stiffness": ActiveLawMacroscopicHuxleyTwoMoment.active_law_residual_dactive_stiffness, # noqa: E501
322 "active_energy_sqrt": ActiveLawMacroscopicHuxleyTwoMoment.active_law_residual_dactive_energy_sqrt, # noqa: E501
323 "active_tension_discr": ActiveLawMacroscopicHuxleyTwoMoment.active_law_residual_dactive_tension, # noqa: E501
324 "fib_deform": ActiveLawMacroscopicHuxleyTwoMoment.active_law_residual_dfib_deform, # noqa: E501
325 },
326)
328ActiveLawMacroscopicHuxleyTwoMoment.declares_internal_expression(
329 "active_stiffness",
330 _active_law_macroscopic_huxley_two_moment_residual_expression,
331 1,
332 0,
333)
334ActiveLawMacroscopicHuxleyTwoMoment.declares_internal_expression(
335 "active_energy_sqrt",
336 _active_law_macroscopic_huxley_two_moment_residual_expression,
337 1,
338 1,
339)
340ActiveLawMacroscopicHuxleyTwoMoment.declares_internal_expression(
341 "active_tension_discr",
342 _active_law_macroscopic_huxley_two_moment_residual_expression,
343 1,
344 2,
345)