myokit 1.33.9__py3-none-any.whl → 1.35.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.
- myokit/__init__.py +9 -36
- myokit/__main__.py +76 -142
- myokit/_aux.py +62 -16
- myokit/_bin/example.mmt +1 -2
- myokit/_bin/install-win/menu.json +7 -7
- myokit/_config.py +22 -31
- myokit/_datablock.py +30 -74
- myokit/_datalog.py +49 -72
- myokit/_err.py +25 -24
- myokit/_expressions.py +50 -68
- myokit/_io.py +15 -27
- myokit/_model_api.py +453 -249
- myokit/_myokit_version.py +1 -5
- myokit/_parsing.py +38 -44
- myokit/_progress.py +5 -8
- myokit/_protocol.py +99 -9
- myokit/_sim/__init__.py +7 -24
- myokit/_sim/cable.c +6 -8
- myokit/_sim/cable.py +6 -8
- myokit/_sim/cmodel.h +125 -70
- myokit/_sim/cmodel.py +12 -14
- myokit/_sim/compiler.py +1 -4
- myokit/_sim/cvodessim.c +196 -118
- myokit/_sim/cvodessim.py +130 -103
- myokit/_sim/differential.hpp +4 -4
- myokit/_sim/fiber_tissue.c +4 -8
- myokit/_sim/fiber_tissue.py +11 -13
- myokit/_sim/jacobian.cpp +2 -2
- myokit/_sim/jacobian.py +11 -8
- myokit/_sim/mcl.h +53 -55
- myokit/_sim/opencl.py +21 -27
- myokit/_sim/openclsim.c +3 -7
- myokit/_sim/openclsim.cl +3 -3
- myokit/_sim/openclsim.py +49 -40
- myokit/_sim/pacing.h +36 -16
- myokit/_sim/rhs.c +6 -13
- myokit/_sim/rhs.py +5 -14
- myokit/_sim/sundials.py +1 -4
- myokit/_system.py +10 -16
- myokit/_unit.py +4 -13
- myokit/float.py +0 -3
- myokit/formats/__init__.py +8 -10
- myokit/formats/ansic/__init__.py +0 -3
- myokit/formats/ansic/_ewriter.py +2 -4
- myokit/formats/ansic/_exporter.py +1 -4
- myokit/formats/ansic/template/cable.c +4 -4
- myokit/formats/ansic/template/euler.c +5 -5
- myokit/formats/ansic/template/sim.c +6 -6
- myokit/formats/axon/__init__.py +1 -3
- myokit/formats/axon/_abf.py +12 -17
- myokit/formats/axon/_atf.py +5 -6
- myokit/formats/axon/_importer.py +0 -3
- myokit/formats/cellml/__init__.py +0 -3
- myokit/formats/cellml/_ewriter.py +3 -6
- myokit/formats/cellml/_exporter.py +3 -6
- myokit/formats/cellml/_importer.py +1 -4
- myokit/formats/cellml/v1/__init__.py +0 -4
- myokit/formats/cellml/v1/_api.py +8 -11
- myokit/formats/cellml/v1/_parser.py +2 -5
- myokit/formats/cellml/v1/_writer.py +2 -11
- myokit/formats/cellml/v2/__init__.py +0 -3
- myokit/formats/cellml/v2/_api.py +8 -17
- myokit/formats/cellml/v2/_parser.py +2 -5
- myokit/formats/cellml/v2/_writer.py +1 -4
- myokit/formats/channelml/__init__.py +0 -3
- myokit/formats/channelml/_importer.py +11 -21
- myokit/formats/cpp/__init__.py +1 -3
- myokit/formats/cpp/_ewriter.py +0 -3
- myokit/formats/cuda/__init__.py +0 -3
- myokit/formats/cuda/_ewriter.py +2 -4
- myokit/formats/cuda/_exporter.py +0 -3
- myokit/formats/cuda/template/kernel.cu +8 -5
- myokit/formats/easyml/__init__.py +0 -3
- myokit/formats/easyml/_ewriter.py +9 -11
- myokit/formats/easyml/_exporter.py +2 -5
- myokit/formats/html/__init__.py +0 -3
- myokit/formats/html/_exporter.py +0 -3
- myokit/formats/html/_flatten.py +5 -21
- myokit/formats/latex/__init__.py +0 -3
- myokit/formats/latex/_ewriter.py +1 -4
- myokit/formats/latex/_exporter.py +4 -6
- myokit/formats/mathml/__init__.py +0 -3
- myokit/formats/mathml/_ewriter.py +2 -11
- myokit/formats/mathml/_parser.py +4 -6
- myokit/formats/matlab/__init__.py +0 -3
- myokit/formats/matlab/_ewriter.py +1 -4
- myokit/formats/matlab/_exporter.py +2 -5
- myokit/formats/matlab/template/main.m +3 -2
- myokit/formats/opencl/__init__.py +0 -3
- myokit/formats/opencl/_ewriter.py +2 -4
- myokit/formats/opencl/_exporter.py +2 -5
- myokit/formats/opencl/template/cable.c +10 -10
- myokit/formats/opencl/template/kernel.cl +1 -1
- myokit/formats/opencl/template/minilog.py +1 -1
- myokit/formats/python/__init__.py +0 -3
- myokit/formats/python/_ewriter.py +2 -5
- myokit/formats/python/_exporter.py +0 -3
- myokit/formats/python/template/sim.py +14 -14
- myokit/formats/sbml/__init__.py +0 -3
- myokit/formats/sbml/_api.py +50 -44
- myokit/formats/sbml/_importer.py +1 -4
- myokit/formats/sbml/_parser.py +2 -5
- myokit/formats/stan/__init__.py +0 -3
- myokit/formats/stan/_ewriter.py +2 -4
- myokit/formats/stan/_exporter.py +2 -5
- myokit/formats/stan/template/cell.stan +3 -3
- myokit/formats/sympy/__init__.py +0 -3
- myokit/formats/sympy/_ereader.py +1 -4
- myokit/formats/sympy/_ewriter.py +2 -5
- myokit/formats/wcp/__init__.py +0 -3
- myokit/formats/wcp/_wcp.py +2 -8
- myokit/formats/xml/__init__.py +0 -3
- myokit/formats/xml/_exporter.py +0 -3
- myokit/formats/xml/_split.py +0 -3
- myokit/gui/__init__.py +80 -246
- myokit/gui/datablock_viewer.py +103 -86
- myokit/gui/datalog_viewer.py +214 -66
- myokit/gui/explorer.py +15 -21
- myokit/gui/ide.py +171 -144
- myokit/gui/progress.py +9 -9
- myokit/gui/source.py +406 -375
- myokit/gui/vargrapher.py +2 -12
- myokit/lib/deps.py +12 -13
- myokit/lib/guess.py +3 -4
- myokit/lib/hh.py +20 -18
- myokit/lib/markov.py +21 -20
- myokit/lib/multi.py +1 -3
- myokit/lib/plots.py +20 -9
- myokit/pacing.py +0 -3
- myokit/pype.py +7 -18
- myokit/tests/__init__.py +3 -6
- myokit/tests/ansic_event_based_pacing.py +1 -4
- myokit/tests/ansic_fixed_form_pacing.py +3 -6
- myokit/tests/data/beeler-1977-model-compare-b.mmt +2 -2
- myokit/tests/data/clancy-1999-fitting.mmt +1 -0
- myokit/tests/test_aux.py +13 -28
- myokit/tests/test_cellml_v1_api.py +4 -19
- myokit/tests/test_cellml_v1_parser.py +0 -15
- myokit/tests/test_cellml_v1_writer.py +0 -9
- myokit/tests/test_cellml_v2_api.py +4 -19
- myokit/tests/test_cellml_v2_parser.py +0 -15
- myokit/tests/test_cellml_v2_writer.py +0 -9
- myokit/tests/test_cmodel.py +16 -22
- myokit/tests/test_compiler_detection.py +1 -11
- myokit/tests/test_component.py +108 -56
- myokit/tests/test_config.py +34 -67
- myokit/tests/test_datablock.py +1 -9
- myokit/tests/test_datalog.py +19 -24
- myokit/tests/test_dependency_checking.py +8 -23
- myokit/tests/test_expressions.py +0 -9
- myokit/tests/test_float.py +1 -5
- myokit/tests/test_formats.py +0 -9
- myokit/tests/test_formats_axon.py +1 -9
- myokit/tests/test_formats_cellml.py +0 -15
- myokit/tests/test_formats_channelml.py +0 -15
- myokit/tests/test_formats_easyml.py +0 -14
- myokit/tests/test_formats_exporters.py +1 -16
- myokit/tests/test_formats_expression_writers.py +1 -17
- myokit/tests/test_formats_html.py +0 -3
- myokit/tests/test_formats_importers.py +1 -16
- myokit/tests/test_formats_mathml_content.py +0 -9
- myokit/tests/test_formats_mathml_presentation.py +0 -9
- myokit/tests/test_formats_opencl.py +0 -10
- myokit/tests/test_formats_sbml.py +0 -15
- myokit/tests/test_formats_sympy.py +0 -9
- myokit/tests/test_formats_wcp.py +1 -3
- myokit/tests/test_io.py +27 -27
- myokit/tests/test_jacobian_calculator.py +6 -14
- myokit/tests/test_jacobian_tracer.py +0 -9
- myokit/tests/test_lib_deps.py +0 -9
- myokit/tests/test_lib_guess.py +0 -9
- myokit/tests/test_lib_hh.py +18 -12
- myokit/tests/test_lib_markov.py +21 -13
- myokit/tests/test_lib_multi.py +0 -9
- myokit/tests/test_lib_plots.py +13 -8
- myokit/tests/test_meta.py +0 -3
- myokit/tests/test_model.py +390 -96
- myokit/tests/test_model_building.py +44 -96
- myokit/tests/test_opencl_info.py +5 -14
- myokit/tests/test_pacing_factory.py +0 -3
- myokit/tests/test_pacing_system_c.py +1 -23
- myokit/tests/test_pacing_system_py.py +0 -9
- myokit/tests/test_parsing.py +139 -56
- myokit/tests/test_progress_reporters.py +0 -3
- myokit/tests/test_protocol.py +0 -9
- myokit/tests/test_protocol_floating_point.py +1 -10
- myokit/tests/test_protocol_time_series.py +82 -0
- myokit/tests/test_pype.py +0 -9
- myokit/tests/test_quantity.py +0 -9
- myokit/tests/test_rhs_benchmarker.py +1 -9
- myokit/tests/test_sbml_api.py +27 -42
- myokit/tests/test_sbml_parser.py +4 -19
- myokit/tests/test_simulation_1d.py +45 -25
- myokit/tests/test_simulation_cvodes.py +321 -55
- myokit/tests/test_simulation_cvodes_from_disk.py +0 -3
- myokit/tests/test_simulation_fiber_tissue.py +39 -12
- myokit/tests/test_simulation_log_interval.py +1 -431
- myokit/tests/test_simulation_opencl.py +69 -48
- myokit/tests/test_simulation_opencl_log_interval.py +1 -3
- myokit/tests/test_simulation_opencl_vs_cvode.py +1 -10
- myokit/tests/test_simulation_opencl_vs_sim1d.py +1 -10
- myokit/tests/test_system_info.py +1 -11
- myokit/tests/test_tools.py +0 -9
- myokit/tests/test_unit.py +1 -10
- myokit/tests/test_user_functions.py +0 -10
- myokit/tests/test_variable.py +231 -27
- myokit/tools.py +5 -21
- myokit/units.py +5 -3
- {myokit-1.33.9.dist-info → myokit-1.35.0.dist-info}/METADATA +12 -15
- myokit-1.35.0.dist-info/RECORD +391 -0
- {myokit-1.33.9.dist-info → myokit-1.35.0.dist-info}/WHEEL +1 -1
- {myokit-1.33.9.dist-info → myokit-1.35.0.dist-info}/entry_points.txt +0 -1
- myokit/_exec_new.py +0 -15
- myokit/_exec_old.py +0 -15
- myokit/_sim/cvodesim.c +0 -1551
- myokit/_sim/cvodesim.py +0 -674
- myokit/_sim/icsim.cpp +0 -563
- myokit/_sim/icsim.py +0 -363
- myokit/_sim/psim.cpp +0 -656
- myokit/_sim/psim.py +0 -493
- myokit/lib/common.py +0 -1094
- myokit/tests/test_lib_common.py +0 -130
- myokit/tests/test_simulation_cvode.py +0 -612
- myokit/tests/test_simulation_ic.py +0 -108
- myokit/tests/test_simulation_p.py +0 -223
- myokit-1.33.9.dist-info/RECORD +0 -403
- /myokit/formats/opencl/template/{test → test.sh} +0 -0
- {myokit-1.33.9.dist-info → myokit-1.35.0.dist-info}/LICENSE.txt +0 -0
- {myokit-1.33.9.dist-info → myokit-1.35.0.dist-info}/top_level.txt +0 -0
myokit/_sim/cvodesim.py
DELETED
|
@@ -1,674 +0,0 @@
|
|
|
1
|
-
#
|
|
2
|
-
# CVODE-driven single cell simulation
|
|
3
|
-
#
|
|
4
|
-
# This file is part of Myokit.
|
|
5
|
-
# See http://myokit.org for copyright, sharing, and licensing details.
|
|
6
|
-
#
|
|
7
|
-
from __future__ import absolute_import, division
|
|
8
|
-
from __future__ import print_function, unicode_literals
|
|
9
|
-
|
|
10
|
-
import os
|
|
11
|
-
import myokit
|
|
12
|
-
import platform
|
|
13
|
-
|
|
14
|
-
# Location of C template
|
|
15
|
-
SOURCE_FILE = 'cvodesim.c'
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
class Simulation(myokit.CModule):
|
|
19
|
-
"""
|
|
20
|
-
Runs single cell simulations using the CVODE solver (see [1]); CVODE uses
|
|
21
|
-
an implicit multi-step method to achieve high accuracy and stability with
|
|
22
|
-
adaptive step sizes.
|
|
23
|
-
|
|
24
|
-
The model passed to the simulation is cloned and stored internally, so
|
|
25
|
-
changes to the original model object will not affect the simulation. A
|
|
26
|
-
protocol can be passed in as ``protocol`` or set later using
|
|
27
|
-
:meth:`set_protocol`.
|
|
28
|
-
|
|
29
|
-
Simulations maintain an internal state consisting of
|
|
30
|
-
|
|
31
|
-
- the current simulation time
|
|
32
|
-
- the current state
|
|
33
|
-
- the default state
|
|
34
|
-
|
|
35
|
-
When a simulation is created, the simulation time is set to 0 and both the
|
|
36
|
-
current and the default state are copied from the model.
|
|
37
|
-
After each call to :meth:`Simulation.run` the time variable and current
|
|
38
|
-
state are updated, so that each successive call to run continues where the
|
|
39
|
-
previous simulation left off. A :meth:`reset` method is provided that will
|
|
40
|
-
set the time back to 0 and revert the current state to the default state.
|
|
41
|
-
To change the time or state manually, use :meth:`set_time` and
|
|
42
|
-
:meth:`set_state`.
|
|
43
|
-
|
|
44
|
-
A pre-pacing method :meth:`pre` is provided that doesn't affect the
|
|
45
|
-
simulation time but will update the current *and the default state*. This
|
|
46
|
-
allows you to pre-pace, run a simulation, reset to the pre-paced state, run
|
|
47
|
-
another simulation etc.
|
|
48
|
-
|
|
49
|
-
To get action potential duration (APD) measurements, the simulation can be
|
|
50
|
-
run with threshold crossing detection. To enable this, the membrane
|
|
51
|
-
potential variable *must* be specified when the simulation is created using
|
|
52
|
-
the ``apd_var`` argument. This can be either a variable object or a string
|
|
53
|
-
containing the variable's fully qualified name. When running a simulation a
|
|
54
|
-
threshold value can be passed in. In addition to the usual simulation log
|
|
55
|
-
the run method will then return a list of all times at which ``apd_var``
|
|
56
|
-
crossed the threshold. *Please note this is an APD calculated as the time
|
|
57
|
-
between the crossing of a fixed threshold, it does not calculate dynamic
|
|
58
|
-
thresholds like "90% of max(V) - min(V)".*
|
|
59
|
-
|
|
60
|
-
The simulation provides four inputs a model variable can be bound to:
|
|
61
|
-
|
|
62
|
-
``time``
|
|
63
|
-
This input provides the simulation time.
|
|
64
|
-
``pace``
|
|
65
|
-
This input provides the current value of the pacing variable. This is
|
|
66
|
-
determined using the protocol passed into the Simulation.
|
|
67
|
-
``evaluations``
|
|
68
|
-
This input provides the number of rhs evaluations used at each point in
|
|
69
|
-
time and can be used to gain some insight into the solver's behaviour.
|
|
70
|
-
``realtime``
|
|
71
|
-
This input provides the elapsed system time at each logged point.
|
|
72
|
-
|
|
73
|
-
No variable labels are required for this simulation type.
|
|
74
|
-
|
|
75
|
-
[1] SUNDIALS: Suite of nonlinear and differential/algebraic equation
|
|
76
|
-
solvers. Hindmarsh, Brown, Woodward, et al. (2005) ACM Transactions on
|
|
77
|
-
Mathematical Software.
|
|
78
|
-
|
|
79
|
-
"""
|
|
80
|
-
_index = 0 # Simulation id
|
|
81
|
-
|
|
82
|
-
def __init__(self, model, protocol=None, apd_var=None):
|
|
83
|
-
super(Simulation, self).__init__()
|
|
84
|
-
|
|
85
|
-
# Require a valid model
|
|
86
|
-
if not model.is_valid():
|
|
87
|
-
model.validate()
|
|
88
|
-
model = model.clone()
|
|
89
|
-
self._model = model
|
|
90
|
-
|
|
91
|
-
# Set protocol
|
|
92
|
-
self._protocol = None
|
|
93
|
-
self._fixed_form_protocol = None
|
|
94
|
-
self.set_protocol(protocol)
|
|
95
|
-
|
|
96
|
-
# Check potential and threshold values
|
|
97
|
-
if apd_var is None:
|
|
98
|
-
self._apd_var = None
|
|
99
|
-
else:
|
|
100
|
-
if isinstance(apd_var, myokit.Variable):
|
|
101
|
-
apd_var = apd_var.qname()
|
|
102
|
-
self._apd_var = self._model.get(apd_var)
|
|
103
|
-
if not self._apd_var.is_state():
|
|
104
|
-
raise ValueError('The `apd_var` must be a state variable.')
|
|
105
|
-
|
|
106
|
-
# Get state and default state from model
|
|
107
|
-
self._state = self._model.state()
|
|
108
|
-
self._default_state = list(self._state)
|
|
109
|
-
|
|
110
|
-
# Last state reached before error
|
|
111
|
-
self._error_state = None
|
|
112
|
-
|
|
113
|
-
# Starting time
|
|
114
|
-
self._time = 0
|
|
115
|
-
|
|
116
|
-
# Unique simulation id
|
|
117
|
-
Simulation._index += 1
|
|
118
|
-
module_name = 'myokit_leg_sim_' + str(Simulation._index)
|
|
119
|
-
module_name += '_' + str(myokit.pid_hash())
|
|
120
|
-
|
|
121
|
-
# Arguments
|
|
122
|
-
args = {
|
|
123
|
-
'module_name': module_name,
|
|
124
|
-
'model': self._model,
|
|
125
|
-
'potential': self._apd_var,
|
|
126
|
-
}
|
|
127
|
-
fname = os.path.join(myokit.DIR_CFUNC, SOURCE_FILE)
|
|
128
|
-
|
|
129
|
-
# Define libraries
|
|
130
|
-
libs = [
|
|
131
|
-
'sundials_cvode',
|
|
132
|
-
'sundials_nvecserial',
|
|
133
|
-
]
|
|
134
|
-
if platform.system() != 'Windows': # pragma: no windows cover
|
|
135
|
-
libs.append('m')
|
|
136
|
-
|
|
137
|
-
# Define library paths
|
|
138
|
-
# Note: Sundials path on windows already includes local binaries
|
|
139
|
-
libd = list(myokit.SUNDIALS_LIB)
|
|
140
|
-
incd = list(myokit.SUNDIALS_INC)
|
|
141
|
-
incd.append(myokit.DIR_CFUNC)
|
|
142
|
-
|
|
143
|
-
# Create extension
|
|
144
|
-
self._sim = self._compile(module_name, fname, args, libs, libd, incd)
|
|
145
|
-
|
|
146
|
-
# Set default tolerance values
|
|
147
|
-
self._tolerance = None
|
|
148
|
-
self.set_tolerance()
|
|
149
|
-
|
|
150
|
-
# Set default min and max step size
|
|
151
|
-
self._dtmax = self._dtmin = None
|
|
152
|
-
|
|
153
|
-
def default_state(self):
|
|
154
|
-
"""
|
|
155
|
-
Returns the default state.
|
|
156
|
-
"""
|
|
157
|
-
return list(self._default_state)
|
|
158
|
-
|
|
159
|
-
def last_state(self):
|
|
160
|
-
"""
|
|
161
|
-
If the last simulation resulted in an error, this will return the last
|
|
162
|
-
state reached during that simulation. In all other cases, this method
|
|
163
|
-
will return ``None``.
|
|
164
|
-
"""
|
|
165
|
-
return list(self._error_state) if self._error_state else None
|
|
166
|
-
|
|
167
|
-
def eval_derivatives(self, y=None):
|
|
168
|
-
"""
|
|
169
|
-
Evaluates and returns the state derivatives.
|
|
170
|
-
|
|
171
|
-
The state to evaluate for can be given as ``y``. If no state is given
|
|
172
|
-
the current simulation state is used.
|
|
173
|
-
"""
|
|
174
|
-
if y is None:
|
|
175
|
-
y = list(self._state)
|
|
176
|
-
else:
|
|
177
|
-
y = self._model.map_to_state(y)
|
|
178
|
-
dy = list(self._state)
|
|
179
|
-
self._sim.eval_derivatives(y, dy, 0, 0)
|
|
180
|
-
return dy
|
|
181
|
-
|
|
182
|
-
def last_number_of_evaluations(self):
|
|
183
|
-
"""
|
|
184
|
-
Returns the number of rhs evaluations performed by the solver during
|
|
185
|
-
the last simulation.
|
|
186
|
-
"""
|
|
187
|
-
return self._sim.number_of_evaluations()
|
|
188
|
-
|
|
189
|
-
def last_number_of_steps(self):
|
|
190
|
-
"""
|
|
191
|
-
Returns the number of steps taken by the solver during the last
|
|
192
|
-
simulation.
|
|
193
|
-
"""
|
|
194
|
-
return self._sim.number_of_steps()
|
|
195
|
-
|
|
196
|
-
def pre(self, duration, progress=None, msg='Pre-pacing Simulation'):
|
|
197
|
-
"""
|
|
198
|
-
This method can be used to perform an unlogged simulation, typically to
|
|
199
|
-
pre-pace to a (semi-)stable orbit.
|
|
200
|
-
|
|
201
|
-
After running this method
|
|
202
|
-
|
|
203
|
-
- The simulation time is **not** affected
|
|
204
|
-
- The current state and the default state are updated to the final
|
|
205
|
-
state reached in the simulation.
|
|
206
|
-
|
|
207
|
-
Calls to :meth:`reset` after using :meth:`pre` will set the current
|
|
208
|
-
state to this new default state.
|
|
209
|
-
|
|
210
|
-
To obtain feedback on the simulation progress, an object implementing
|
|
211
|
-
the :class:`myokit.ProgressReporter` interface can be passed in.
|
|
212
|
-
passed in as ``progress``. An optional description of the current
|
|
213
|
-
simulation to use in the ProgressReporter can be passed in as `msg`.
|
|
214
|
-
"""
|
|
215
|
-
duration = float(duration)
|
|
216
|
-
self._run(duration, myokit.LOG_NONE, None, None, None, progress, msg)
|
|
217
|
-
self._default_state = self._state
|
|
218
|
-
|
|
219
|
-
def __reduce__(self):
|
|
220
|
-
"""
|
|
221
|
-
Pickles this Simulation.
|
|
222
|
-
|
|
223
|
-
See: https://docs.python.org/3/library/pickle.html#object.__reduce__
|
|
224
|
-
"""
|
|
225
|
-
apd_var = None if self._apd_var is None else self._apd_var.qname()
|
|
226
|
-
return (
|
|
227
|
-
self.__class__,
|
|
228
|
-
(self._model, self._protocol, apd_var),
|
|
229
|
-
(
|
|
230
|
-
self._time,
|
|
231
|
-
self._state,
|
|
232
|
-
self._default_state,
|
|
233
|
-
self._fixed_form_protocol,
|
|
234
|
-
self._tolerance,
|
|
235
|
-
self._dtmin,
|
|
236
|
-
self._dtmax,
|
|
237
|
-
),
|
|
238
|
-
)
|
|
239
|
-
|
|
240
|
-
def reset(self):
|
|
241
|
-
"""
|
|
242
|
-
Resets the simulation:
|
|
243
|
-
|
|
244
|
-
- The time variable is set to 0
|
|
245
|
-
- The state is set to the default state
|
|
246
|
-
|
|
247
|
-
"""
|
|
248
|
-
self._time = 0
|
|
249
|
-
self._state = list(self._default_state)
|
|
250
|
-
|
|
251
|
-
def run(
|
|
252
|
-
self, duration, log=None, log_interval=None, log_times=None,
|
|
253
|
-
apd_threshold=None, progress=None, msg='Running simulation'):
|
|
254
|
-
"""
|
|
255
|
-
Runs a simulation and returns the logged results. Running a simulation
|
|
256
|
-
has the following effects:
|
|
257
|
-
|
|
258
|
-
- The internal state is updated to the last state in the simulation.
|
|
259
|
-
- The simulation's time variable is updated to reflect the time
|
|
260
|
-
elapsed during the simulation.
|
|
261
|
-
|
|
262
|
-
The number of time units to simulate can be set with ``duration``.
|
|
263
|
-
|
|
264
|
-
The method returns a :class:`myokit.DataLog` dictionary that maps
|
|
265
|
-
variable names to lists of logged values. The variables to log can be
|
|
266
|
-
indicated using the ``log`` argument. There are several options for its
|
|
267
|
-
value:
|
|
268
|
-
|
|
269
|
-
- ``None`` (default), to log all states.
|
|
270
|
-
- An integer flag or a combination of flags. Options:
|
|
271
|
-
``myokit.LOG_NONE``, ``myokit.LOG_STATE``, ``myokit.LOG_BOUND``,
|
|
272
|
-
``myokit.LOG_INTER``, ``myokit.LOG_DERIV`` or ``myokit.LOG_ALL``.
|
|
273
|
-
- A sequence of variable names. To log derivatives, use
|
|
274
|
-
"dot(membrane.V)".
|
|
275
|
-
- A :class:`myokit.DataLog` object. In this case, the new data
|
|
276
|
-
will be appended to the existing log.
|
|
277
|
-
|
|
278
|
-
For detailed information about the ``log`` argument, see the function
|
|
279
|
-
:meth:`myokit.prepare_log`.
|
|
280
|
-
|
|
281
|
-
By default, every step the solver takes is logged. This is usually
|
|
282
|
-
advantageous, since more points are added exactly at the times the
|
|
283
|
-
system gets more interesting. However, if equidistant points are
|
|
284
|
-
required a ``log_interval`` can be set. Alternatively, the
|
|
285
|
-
``log_times`` argument can be used to specify logging times directly.
|
|
286
|
-
|
|
287
|
-
To obtain accurate measurements of the action potential (AP) duration,
|
|
288
|
-
the argument ``apd_threshold`` can be set to a fixed threshold level
|
|
289
|
-
used to define the AP. This functionality is only available for
|
|
290
|
-
simulations created with a valid ``apd_var`` argument. If apd
|
|
291
|
-
measurements are enabled, the value returned by this method has the
|
|
292
|
-
form ``(log, apds)``.
|
|
293
|
-
|
|
294
|
-
To obtain feedback on the simulation progress, an object implementing
|
|
295
|
-
the :class:`myokit.ProgressReporter` interface can be passed in.
|
|
296
|
-
passed in as ``progress``. An optional description of the current
|
|
297
|
-
simulation to use in the ProgressReporter can be passed in as ``msg``.
|
|
298
|
-
|
|
299
|
-
The ``duration`` argument cannot be negative, and special care needs to
|
|
300
|
-
be taken when very small (positive) values are used. If ``duration`` is
|
|
301
|
-
zero or so small that
|
|
302
|
-
``simulation.time() + duration == simulation.time()``, then the method
|
|
303
|
-
returns without updating the internal states or time. However, for
|
|
304
|
-
some extremely short durations (approx ``2 epsilon * time``), the
|
|
305
|
-
simulation will try to run but the underlying CVODE engine may return a
|
|
306
|
-
``CV_TOO_CLOSE`` error, causing a :class:`myokit.SimulationError` to be
|
|
307
|
-
raised.
|
|
308
|
-
"""
|
|
309
|
-
duration = float(duration)
|
|
310
|
-
output = self._run(
|
|
311
|
-
duration, log, log_interval, log_times, apd_threshold, progress,
|
|
312
|
-
msg)
|
|
313
|
-
self._time += duration
|
|
314
|
-
return output
|
|
315
|
-
|
|
316
|
-
def _run(
|
|
317
|
-
self, duration, log, log_interval, log_times, apd_threshold,
|
|
318
|
-
progress, msg):
|
|
319
|
-
|
|
320
|
-
# Reset error state
|
|
321
|
-
self._error_state = None
|
|
322
|
-
|
|
323
|
-
# Simulation times
|
|
324
|
-
if duration < 0:
|
|
325
|
-
raise ValueError('Simulation time can\'t be negative.')
|
|
326
|
-
tmin = self._time
|
|
327
|
-
tmax = tmin + duration
|
|
328
|
-
|
|
329
|
-
# Parse log argument
|
|
330
|
-
log = myokit.prepare_log(log, self._model, if_empty=myokit.LOG_ALL)
|
|
331
|
-
|
|
332
|
-
# Logging period (None or 0 = disabled)
|
|
333
|
-
log_interval = 0 if log_interval is None else float(log_interval)
|
|
334
|
-
if log_interval < 0:
|
|
335
|
-
log_interval = 0
|
|
336
|
-
|
|
337
|
-
# Logging points (None or empty list = disabled)
|
|
338
|
-
if log_times is not None:
|
|
339
|
-
log_times = [float(x) for x in log_times]
|
|
340
|
-
if len(log_times) == 0:
|
|
341
|
-
log_times = None
|
|
342
|
-
else:
|
|
343
|
-
# Allow duplicates, but always non-decreasing!
|
|
344
|
-
import numpy as np
|
|
345
|
-
x = np.asarray(log_times)
|
|
346
|
-
if np.any(x[1:] < x[:-1]):
|
|
347
|
-
raise ValueError(
|
|
348
|
-
'Values in log_times must be non-decreasing.')
|
|
349
|
-
del x, np
|
|
350
|
-
if log_times is not None and log_interval > 0:
|
|
351
|
-
raise ValueError(
|
|
352
|
-
'The arguments log_times and log_interval cannot be used'
|
|
353
|
-
' simultaneously.')
|
|
354
|
-
|
|
355
|
-
# Threshold for APD measurement
|
|
356
|
-
root_list = None
|
|
357
|
-
root_threshold = 0
|
|
358
|
-
if apd_threshold is not None:
|
|
359
|
-
if self._apd_var is None:
|
|
360
|
-
raise ValueError(
|
|
361
|
-
'Threshold given but Simulation object was'
|
|
362
|
-
' created without apd_var argument.')
|
|
363
|
-
else:
|
|
364
|
-
root_list = []
|
|
365
|
-
root_threshold = float(apd_threshold)
|
|
366
|
-
|
|
367
|
-
# Get progress indication function (if any)
|
|
368
|
-
if progress is None:
|
|
369
|
-
progress = myokit._Simulation_progress
|
|
370
|
-
if progress:
|
|
371
|
-
if not isinstance(progress, myokit.ProgressReporter):
|
|
372
|
-
raise ValueError(
|
|
373
|
-
'The argument "progress" must be either a'
|
|
374
|
-
' subclass of myokit.ProgressReporter or None.')
|
|
375
|
-
|
|
376
|
-
# Determine benchmarking mode, create time() function if needed
|
|
377
|
-
if self._model.binding('realtime') is not None:
|
|
378
|
-
import timeit
|
|
379
|
-
bench = timeit.default_timer
|
|
380
|
-
else:
|
|
381
|
-
bench = None
|
|
382
|
-
|
|
383
|
-
# Run simulation
|
|
384
|
-
# The simulation is run only if (tmin + duration > tmin). This is a
|
|
385
|
-
# stronger check than (duration == 0), which will return true even for
|
|
386
|
-
# very short durations (and will cause zero iterations of the
|
|
387
|
-
# "while (t < tmax)" loop below).
|
|
388
|
-
istate = list(self._state)
|
|
389
|
-
if tmin + duration > tmin:
|
|
390
|
-
|
|
391
|
-
# Lists to return state in
|
|
392
|
-
rstate = list(istate)
|
|
393
|
-
rbound = [0, 0, 0, 0] # time, pace, realtime, evaluations
|
|
394
|
-
|
|
395
|
-
# Initialize
|
|
396
|
-
self._sim.sim_init(
|
|
397
|
-
tmin,
|
|
398
|
-
tmax,
|
|
399
|
-
istate,
|
|
400
|
-
rstate,
|
|
401
|
-
rbound,
|
|
402
|
-
self._protocol,
|
|
403
|
-
self._fixed_form_protocol,
|
|
404
|
-
log,
|
|
405
|
-
log_interval,
|
|
406
|
-
log_times,
|
|
407
|
-
root_list,
|
|
408
|
-
root_threshold,
|
|
409
|
-
bench,
|
|
410
|
-
)
|
|
411
|
-
t = tmin
|
|
412
|
-
|
|
413
|
-
try:
|
|
414
|
-
if progress:
|
|
415
|
-
# Loop with feedback
|
|
416
|
-
with progress.job(msg):
|
|
417
|
-
r = 1.0 / duration if duration != 0 else 1
|
|
418
|
-
while t < tmax:
|
|
419
|
-
t = self._sim.sim_step()
|
|
420
|
-
if not progress.update(min((t - tmin) * r, 1)):
|
|
421
|
-
raise myokit.SimulationCancelledError()
|
|
422
|
-
else:
|
|
423
|
-
# Loop without feedback
|
|
424
|
-
while t < tmax:
|
|
425
|
-
t = self._sim.sim_step()
|
|
426
|
-
except ArithmeticError as e:
|
|
427
|
-
# Some CVODE errors are set to raise an ArithmeticError,
|
|
428
|
-
# which users may be able to debug.
|
|
429
|
-
self._error_state = list(rstate)
|
|
430
|
-
txt = ['A numerical error occurred during simulation at'
|
|
431
|
-
' t = ' + str(t) + '.', 'Last reached state: ']
|
|
432
|
-
txt.extend([' ' + x for x in
|
|
433
|
-
self._model.format_state(rstate).splitlines()])
|
|
434
|
-
txt.append('Inputs for binding: ')
|
|
435
|
-
txt.append(' time = ' + myokit.float.str(rbound[0]))
|
|
436
|
-
txt.append(' pace = ' + myokit.float.str(rbound[1]))
|
|
437
|
-
txt.append(' realtime = ' + myokit.float.str(rbound[2]))
|
|
438
|
-
txt.append(' evaluations = ' + myokit.float.str(rbound[3]))
|
|
439
|
-
txt.append(str(e))
|
|
440
|
-
try:
|
|
441
|
-
self._model.evaluate_derivatives(rstate)
|
|
442
|
-
except myokit.NumericalError as en:
|
|
443
|
-
txt.append(str(en))
|
|
444
|
-
raise myokit.SimulationError('\n'.join(txt))
|
|
445
|
-
except Exception as e:
|
|
446
|
-
|
|
447
|
-
# Store error state
|
|
448
|
-
self._error_state = list(rstate)
|
|
449
|
-
|
|
450
|
-
# Check for known CVODE errors
|
|
451
|
-
if 'Function CVode()' in str(e):
|
|
452
|
-
raise myokit.SimulationError(str(e))
|
|
453
|
-
|
|
454
|
-
# Check for zero step error
|
|
455
|
-
if str(e)[:10] == 'ZERO_STEP ': # pragma: no cover
|
|
456
|
-
t = float(str(e)[10:])
|
|
457
|
-
raise myokit.SimulationError(
|
|
458
|
-
'Maximum number of zero-size steps made at t='
|
|
459
|
-
+ str(t))
|
|
460
|
-
|
|
461
|
-
# Unknown exception: re-raise!
|
|
462
|
-
raise
|
|
463
|
-
finally:
|
|
464
|
-
# Clean even after KeyboardInterrupt or other Exception
|
|
465
|
-
self._sim.sim_clean()
|
|
466
|
-
|
|
467
|
-
# Update internal state
|
|
468
|
-
self._state = rstate
|
|
469
|
-
|
|
470
|
-
# Return
|
|
471
|
-
if root_list is not None:
|
|
472
|
-
# Calculate apds and return (log, apds)
|
|
473
|
-
st = []
|
|
474
|
-
dr = []
|
|
475
|
-
if root_list:
|
|
476
|
-
roots = iter(root_list)
|
|
477
|
-
time, direction = next(roots)
|
|
478
|
-
tlast = time if direction > 0 else None
|
|
479
|
-
for time, direction in roots:
|
|
480
|
-
if direction > 0:
|
|
481
|
-
tlast = time
|
|
482
|
-
else:
|
|
483
|
-
st.append(tlast)
|
|
484
|
-
dr.append(time - tlast)
|
|
485
|
-
apds = myokit.DataLog()
|
|
486
|
-
apds['start'] = st
|
|
487
|
-
apds['duration'] = dr
|
|
488
|
-
return log, apds
|
|
489
|
-
else:
|
|
490
|
-
# Return log
|
|
491
|
-
return log
|
|
492
|
-
|
|
493
|
-
def set_constant(self, var, value):
|
|
494
|
-
"""
|
|
495
|
-
Changes a model constant. Only literal constants (constants not
|
|
496
|
-
dependent on any other variable) can be changed.
|
|
497
|
-
|
|
498
|
-
The constant ``var`` can be given as a :class:`Variable` or a string
|
|
499
|
-
containing a variable qname. The ``value`` should be given as a float.
|
|
500
|
-
"""
|
|
501
|
-
value = float(value)
|
|
502
|
-
if isinstance(var, myokit.Variable):
|
|
503
|
-
var = var.qname()
|
|
504
|
-
var = self._model.get(var)
|
|
505
|
-
if not var.is_literal():
|
|
506
|
-
raise ValueError(
|
|
507
|
-
'The given variable <' + var.qname() + '> is not a literal.')
|
|
508
|
-
|
|
509
|
-
# Update value in internal model: This is required for error handling
|
|
510
|
-
# (to show the correct values), but also takes care of constants in
|
|
511
|
-
# pickled/unpickled simulations.
|
|
512
|
-
self._model.set_value(var.qname(), value)
|
|
513
|
-
|
|
514
|
-
# Update value in compiled simulation module
|
|
515
|
-
self._sim.set_constant(var.qname(), value)
|
|
516
|
-
|
|
517
|
-
def set_default_state(self, state):
|
|
518
|
-
"""
|
|
519
|
-
Allows you to manually set the default state.
|
|
520
|
-
"""
|
|
521
|
-
self._default_state = self._model.map_to_state(state)
|
|
522
|
-
|
|
523
|
-
def set_max_step_size(self, dtmax=None):
|
|
524
|
-
"""
|
|
525
|
-
Sets a maximum step size. To let the solver pick any step size it likes
|
|
526
|
-
use ``dtmax = None``.
|
|
527
|
-
"""
|
|
528
|
-
dtmax = 0 if dtmax is None else float(dtmax)
|
|
529
|
-
if dtmax < 0:
|
|
530
|
-
dtmax = 0
|
|
531
|
-
|
|
532
|
-
# Store internally
|
|
533
|
-
self._dtmax = dtmax
|
|
534
|
-
|
|
535
|
-
# Set in simulation
|
|
536
|
-
self._sim.set_max_step_size(dtmax)
|
|
537
|
-
|
|
538
|
-
def set_min_step_size(self, dtmin=None):
|
|
539
|
-
"""
|
|
540
|
-
Sets a minimum step size. To let the solver pick any step size it likes
|
|
541
|
-
use ``dtmin = None``.
|
|
542
|
-
"""
|
|
543
|
-
dtmin = 0 if dtmin is None else float(dtmin)
|
|
544
|
-
if dtmin < 0:
|
|
545
|
-
dtmin = 0
|
|
546
|
-
|
|
547
|
-
# Store internally
|
|
548
|
-
self._dtmin = dtmin
|
|
549
|
-
|
|
550
|
-
# Set in simulation
|
|
551
|
-
self._sim.set_min_step_size(dtmin)
|
|
552
|
-
|
|
553
|
-
def set_fixed_form_protocol(self, times=None, values=None):
|
|
554
|
-
"""
|
|
555
|
-
Configures this simulation to run with a predetermined protocol
|
|
556
|
-
instead of the usual event-based mechanism.
|
|
557
|
-
|
|
558
|
-
A 1D time-series should be given as input. During the simulation, the
|
|
559
|
-
value of the pacing variable will be determined by linearly
|
|
560
|
-
interpolating between the two nearest points in the series. If the
|
|
561
|
-
simulation time is outside the bounds of the time-series, the first or
|
|
562
|
-
last value in the series will be used.
|
|
563
|
-
|
|
564
|
-
Setting a predetermined protocol clears any previously set (event-based
|
|
565
|
-
or pre-determined) protocol. To clear all protocols, call this method
|
|
566
|
-
with `times=None`. When a simulation is run without any protocol, the
|
|
567
|
-
value of any variables bound to `pace` will be set to 0.
|
|
568
|
-
|
|
569
|
-
Arguments:
|
|
570
|
-
|
|
571
|
-
``times``
|
|
572
|
-
A non-decreasing array of times. If any times appear more than
|
|
573
|
-
once, only the value at the highest index will be used.
|
|
574
|
-
``values``
|
|
575
|
-
An array of values for the pacing variable. Must have the same size
|
|
576
|
-
as ``times``.
|
|
577
|
-
|
|
578
|
-
"""
|
|
579
|
-
# Check input
|
|
580
|
-
if times is None:
|
|
581
|
-
if values is not None:
|
|
582
|
-
raise ValueError('Values array given, but no times array.')
|
|
583
|
-
else:
|
|
584
|
-
if values is None:
|
|
585
|
-
raise ValueError('Times array given, but no values array.')
|
|
586
|
-
if len(times) != len(values):
|
|
587
|
-
raise ValueError('Times and values array must have same size.')
|
|
588
|
-
|
|
589
|
-
# Clear event-based protocol, if set
|
|
590
|
-
self._protocol = None
|
|
591
|
-
|
|
592
|
-
# Set new protocol
|
|
593
|
-
if times is None:
|
|
594
|
-
# Clear predetermined protocol
|
|
595
|
-
self._fixed_form_protocol = None
|
|
596
|
-
else:
|
|
597
|
-
# Copy data and set
|
|
598
|
-
self._fixed_form_protocol = (list(times), list(values))
|
|
599
|
-
|
|
600
|
-
def set_protocol(self, protocol=None):
|
|
601
|
-
"""
|
|
602
|
-
Sets the pacing :class:`Protocol` used by this simulation.
|
|
603
|
-
|
|
604
|
-
To run without pacing call this method with ``protocol = None``. In
|
|
605
|
-
this case, the value of any variables bound to `pace` will be set to 0.
|
|
606
|
-
"""
|
|
607
|
-
# Clear predetermined protocol, if set
|
|
608
|
-
self._fixed_form_protocol = None
|
|
609
|
-
|
|
610
|
-
# Set new protocol
|
|
611
|
-
if protocol is None:
|
|
612
|
-
self._protocol = None
|
|
613
|
-
else:
|
|
614
|
-
self._protocol = protocol.clone()
|
|
615
|
-
|
|
616
|
-
def __setstate__(self, state):
|
|
617
|
-
"""
|
|
618
|
-
Called after unpickling.
|
|
619
|
-
|
|
620
|
-
See: https://docs.python.org/3/library/pickle.html#object.__setstate__
|
|
621
|
-
"""
|
|
622
|
-
self._time = state[0]
|
|
623
|
-
self._state = state[1]
|
|
624
|
-
self._default_state = state[2]
|
|
625
|
-
self._fixed_form_protocol = state[3]
|
|
626
|
-
|
|
627
|
-
# The following properties need to be set on the internal simulation
|
|
628
|
-
# object
|
|
629
|
-
self.set_tolerance(*state[4])
|
|
630
|
-
self.set_min_step_size(state[5])
|
|
631
|
-
self.set_max_step_size(state[6])
|
|
632
|
-
|
|
633
|
-
def set_state(self, state):
|
|
634
|
-
"""
|
|
635
|
-
Sets the current state.
|
|
636
|
-
"""
|
|
637
|
-
self._state = self._model.map_to_state(state)
|
|
638
|
-
|
|
639
|
-
def set_time(self, time=0):
|
|
640
|
-
"""
|
|
641
|
-
Sets the current simulation time.
|
|
642
|
-
"""
|
|
643
|
-
self._time = float(time)
|
|
644
|
-
|
|
645
|
-
def set_tolerance(self, abs_tol=1e-6, rel_tol=1e-4):
|
|
646
|
-
"""
|
|
647
|
-
Sets the solver tolerances. Absolute tolerance is set using
|
|
648
|
-
``abs_tol``, relative tolerance using ``rel_tol``. For more information
|
|
649
|
-
on these values, see the Sundials CVODE documentation.
|
|
650
|
-
"""
|
|
651
|
-
abs_tol = float(abs_tol)
|
|
652
|
-
if abs_tol <= 0:
|
|
653
|
-
raise ValueError('Absolute tolerance must be positive float.')
|
|
654
|
-
rel_tol = float(rel_tol)
|
|
655
|
-
if rel_tol <= 0:
|
|
656
|
-
raise ValueError('Relative tolerance must be positive float.')
|
|
657
|
-
|
|
658
|
-
# Store tolerance in Python (for pickling)
|
|
659
|
-
self._tolerance = (abs_tol, rel_tol)
|
|
660
|
-
|
|
661
|
-
# Set tolerance in simulation
|
|
662
|
-
self._sim.set_tolerance(abs_tol, rel_tol)
|
|
663
|
-
|
|
664
|
-
def state(self):
|
|
665
|
-
"""
|
|
666
|
-
Returns the current state.
|
|
667
|
-
"""
|
|
668
|
-
return list(self._state)
|
|
669
|
-
|
|
670
|
-
def time(self):
|
|
671
|
-
"""
|
|
672
|
-
Returns the current simulation time.
|
|
673
|
-
"""
|
|
674
|
-
return self._time
|