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/lib/common.py
DELETED
|
@@ -1,1094 +0,0 @@
|
|
|
1
|
-
#
|
|
2
|
-
# Common experiments (activation, inactivation, etc.).
|
|
3
|
-
#
|
|
4
|
-
# Some functions in this module require a recent version of scipy (i.e. the
|
|
5
|
-
# method scipy.optimize.curve_fit must exist).
|
|
6
|
-
#
|
|
7
|
-
# This file is part of Myokit.
|
|
8
|
-
# See http://myokit.org for copyright, sharing, and licensing details.
|
|
9
|
-
#
|
|
10
|
-
from __future__ import absolute_import, division
|
|
11
|
-
from __future__ import print_function, unicode_literals
|
|
12
|
-
|
|
13
|
-
import numpy as np
|
|
14
|
-
import myokit
|
|
15
|
-
|
|
16
|
-
# Strings in Python 2 and 3
|
|
17
|
-
try:
|
|
18
|
-
basestring
|
|
19
|
-
except NameError: # pragma: no python 2 cover
|
|
20
|
-
basestring = str
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
#
|
|
24
|
-
# Deprecated since 2018-04-16
|
|
25
|
-
#
|
|
26
|
-
import warnings
|
|
27
|
-
warnings.warn(
|
|
28
|
-
'The module myokit.lib.common is deprecated: it will be removed in future'
|
|
29
|
-
' versions of Myokit.'
|
|
30
|
-
)
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
class StepProtocol(object):
|
|
34
|
-
"""
|
|
35
|
-
An abstract base class for step protocol experiments.
|
|
36
|
-
"""
|
|
37
|
-
def __init__(self, model, var, vvar=None):
|
|
38
|
-
# Clone model
|
|
39
|
-
self._model = model.clone()
|
|
40
|
-
# Get variable names
|
|
41
|
-
self._vars = []
|
|
42
|
-
if isinstance(var, (basestring, myokit.Variable)):
|
|
43
|
-
var = [var]
|
|
44
|
-
for v in var:
|
|
45
|
-
if isinstance(v, myokit.Variable):
|
|
46
|
-
v = v.qname()
|
|
47
|
-
self._vars.append(self._model.get(v).qname())
|
|
48
|
-
# Turn the membrane potential into a constant
|
|
49
|
-
if vvar is None:
|
|
50
|
-
vvar = self._model.label('membrane_potential')
|
|
51
|
-
if vvar is None:
|
|
52
|
-
raise ValueError(
|
|
53
|
-
'Membrane potential variable must be given as the argument'
|
|
54
|
-
' "vvar" or labeled in the model as "membrane_potential".')
|
|
55
|
-
else:
|
|
56
|
-
if isinstance(vvar, myokit.Variable):
|
|
57
|
-
vvar = vvar.qname()
|
|
58
|
-
vvar = self._model.get(vvar)
|
|
59
|
-
if vvar.is_state():
|
|
60
|
-
vvar.demote()
|
|
61
|
-
vvar.set_binding(None)
|
|
62
|
-
vvar.set_rhs(0)
|
|
63
|
-
# Get membrane potential name
|
|
64
|
-
self._vvar = vvar.qname()
|
|
65
|
-
# Get time variable name
|
|
66
|
-
self._tvar = self._model.time().qname()
|
|
67
|
-
# Start without any conversion factor
|
|
68
|
-
self._factor = None
|
|
69
|
-
# Maximum step size during logged simulation or None
|
|
70
|
-
self._max_step_size = None
|
|
71
|
-
# No simulation data yet
|
|
72
|
-
self._logs = None
|
|
73
|
-
# Holding potential & step potentials
|
|
74
|
-
self.set_holding_potential()
|
|
75
|
-
self.set_step_potential()
|
|
76
|
-
|
|
77
|
-
def convert_g2i(self, vrev=60, gmax=1):
|
|
78
|
-
"""
|
|
79
|
-
Converts any data logged during this experiment from conductances to
|
|
80
|
-
currents by multiplying each step's data by::
|
|
81
|
-
|
|
82
|
-
gmax * (v - vrev)
|
|
83
|
-
|
|
84
|
-
This can be useful to obtain the original current traces when a
|
|
85
|
-
conductance value was logged or when creating an IV protocol.
|
|
86
|
-
|
|
87
|
-
Calling this method will override any previously set conversion factor.
|
|
88
|
-
"""
|
|
89
|
-
self._factor = float(gmax) * (self._steps - float(vrev))
|
|
90
|
-
|
|
91
|
-
def convert_i2g(self, vrev=60, gmax=1):
|
|
92
|
-
"""
|
|
93
|
-
Converts any data logged during this experiment from currents to
|
|
94
|
-
conductance by multiplying each step's data by::
|
|
95
|
-
|
|
96
|
-
1 / (gmax * (v - vrev))
|
|
97
|
-
|
|
98
|
-
This can be useful to obtain an activation or inactivation curve from
|
|
99
|
-
logged current data. However, since this leads to numerical issues
|
|
100
|
-
around ``v == vrev`` its better to run these experiments directly on a
|
|
101
|
-
conductance variable.
|
|
102
|
-
|
|
103
|
-
Calling this method will override any previously set conversion factor.
|
|
104
|
-
"""
|
|
105
|
-
self._factor = 1.0 / (float(gmax) * (self._steps - float(vrev)))
|
|
106
|
-
|
|
107
|
-
def disable_conversion(self):
|
|
108
|
-
"""
|
|
109
|
-
Disables any previously set conversion factor.
|
|
110
|
-
"""
|
|
111
|
-
self._factor = None
|
|
112
|
-
|
|
113
|
-
def fit_boltzmann(self, var=None):
|
|
114
|
-
"""
|
|
115
|
-
Attempts to fit a Boltzmann curve to a voltage-peaks dataset.
|
|
116
|
-
|
|
117
|
-
The variable whose peaks to use can be specified as ``var``. If no
|
|
118
|
-
variable is given the first value specified in the constructor is used.
|
|
119
|
-
The fitted curve is given by::
|
|
120
|
-
|
|
121
|
-
g = gmin + (gmax - gmin) / (1 + np.exp((v - v_half) / k))
|
|
122
|
-
|
|
123
|
-
Note: This method requires a scipy installation providing the method
|
|
124
|
-
scipy.optimize.curve_fit
|
|
125
|
-
"""
|
|
126
|
-
from scipy.optimize import curve_fit
|
|
127
|
-
d = self.peaks(normalize=True).npview()
|
|
128
|
-
v = d[self._vvar]
|
|
129
|
-
g = d[var] if var else d[self._vars[0]]
|
|
130
|
-
o = np.ones(v.shape)
|
|
131
|
-
gmin = np.min(g)
|
|
132
|
-
gmax = np.max(g)
|
|
133
|
-
|
|
134
|
-
def f(v, v_half, k):
|
|
135
|
-
return np.select(
|
|
136
|
-
[v == v_half],
|
|
137
|
-
[o * k],
|
|
138
|
-
gmin + (gmax - gmin) / (1.0 + np.exp((v - v_half) / k)))
|
|
139
|
-
|
|
140
|
-
vmid = v[-1] - v[0]
|
|
141
|
-
slope = -5 if g[0] < g[-1] else 5
|
|
142
|
-
p = curve_fit(f, v, g, [vmid, slope])
|
|
143
|
-
return p[0]
|
|
144
|
-
|
|
145
|
-
def peaks(self, normalize=False):
|
|
146
|
-
"""
|
|
147
|
-
Returns a :class:`myokit.DataLog` containing the tested step
|
|
148
|
-
voltages and the peak values of the logged variable(s).
|
|
149
|
-
|
|
150
|
-
The names used in the simulation log correspond to those used in the
|
|
151
|
-
model. For example, when doing an experiment on a Sodium channel the
|
|
152
|
-
simulation log might have entries ``membrane.v`` and ``ina.g`` where
|
|
153
|
-
``membrane.v`` contains the used voltage steps while ``ina.g`` contains
|
|
154
|
-
the peak values measured at those voltages.
|
|
155
|
-
|
|
156
|
-
If ``normalize`` is set to ``True``, the peak data returned will be
|
|
157
|
-
normalized by dividing all values by the largest (most positive) peak
|
|
158
|
-
in the list. If no positive, non-zero values are found no normalization
|
|
159
|
-
will be applied.
|
|
160
|
-
|
|
161
|
-
If any conversion factor was specified the data will be converted
|
|
162
|
-
before normalization.
|
|
163
|
-
"""
|
|
164
|
-
# Run simulation if needed
|
|
165
|
-
if self._logs is None:
|
|
166
|
-
self._run()
|
|
167
|
-
# Create a copy of the voltage steps
|
|
168
|
-
v = np.array(self._steps, copy=True)
|
|
169
|
-
# Create a simulation log
|
|
170
|
-
d = myokit.DataLog()
|
|
171
|
-
d[self._vvar] = v
|
|
172
|
-
# Factor
|
|
173
|
-
factor = self._factor if self._factor is not None else 1
|
|
174
|
-
# Find the peaks
|
|
175
|
-
for var in self._vars:
|
|
176
|
-
peaks = np.zeros(len(v))
|
|
177
|
-
for k, log in enumerate(self._logs):
|
|
178
|
-
peaks[k] = log[var][np.argmax(np.abs(log[var]))]
|
|
179
|
-
d[var] = peaks * factor
|
|
180
|
-
# Normalize (only if log contains positive values)
|
|
181
|
-
if normalize:
|
|
182
|
-
for var in self._vars:
|
|
183
|
-
x = d[var]
|
|
184
|
-
m = np.max(x)
|
|
185
|
-
if m > 0:
|
|
186
|
-
d[var] = x / m
|
|
187
|
-
return d
|
|
188
|
-
|
|
189
|
-
def _run(self):
|
|
190
|
-
"""
|
|
191
|
-
Should run the simulation and save the current traces in self._logs
|
|
192
|
-
"""
|
|
193
|
-
raise NotImplementedError
|
|
194
|
-
|
|
195
|
-
def set_constant(self, var, value):
|
|
196
|
-
"""
|
|
197
|
-
Changes the value of a constant in the used model.
|
|
198
|
-
"""
|
|
199
|
-
if isinstance(var, myokit.Variable):
|
|
200
|
-
var = var.qname()
|
|
201
|
-
var = self._model.get(var)
|
|
202
|
-
self._model.set_value(var, value)
|
|
203
|
-
self._logs = None
|
|
204
|
-
|
|
205
|
-
def set_holding_potential(self, vhold=-140, thold=1800):
|
|
206
|
-
"""
|
|
207
|
-
Sets the holding potential and the time to hold. During the experiment,
|
|
208
|
-
the cell will be held at ``vhold`` for ``thold`` time units before
|
|
209
|
-
every voltage step.
|
|
210
|
-
"""
|
|
211
|
-
vhold = float(vhold)
|
|
212
|
-
thold = float(thold)
|
|
213
|
-
if thold < 0:
|
|
214
|
-
raise ValueError('Time to hold cannot be negative.')
|
|
215
|
-
self._vhold = vhold
|
|
216
|
-
self._thold = thold
|
|
217
|
-
self._logs = None
|
|
218
|
-
|
|
219
|
-
def set_max_step_size(self, dtmax=None):
|
|
220
|
-
"""
|
|
221
|
-
Can be used to set a maximum step size to use in the logged parts of
|
|
222
|
-
the simulation. Use ``dtmax==None`` to let the solver chose any size it
|
|
223
|
-
likes.
|
|
224
|
-
"""
|
|
225
|
-
self._max_step_size = None if dtmax is None else float(dtmax)
|
|
226
|
-
|
|
227
|
-
def set_step_potential(self, vmin=-100, vmax=50, dv=1, tstep=200):
|
|
228
|
-
"""
|
|
229
|
-
Sets the step potentials and the step duration. Each experiment will
|
|
230
|
-
step linearly from ``vmin`` to ``vmax`` with steps of size ``dv``. The
|
|
231
|
-
cell is held at each step for ``tstep`` time units.
|
|
232
|
-
"""
|
|
233
|
-
vmin = float(vmin)
|
|
234
|
-
vmax = float(vmax)
|
|
235
|
-
dv = float(dv)
|
|
236
|
-
tstep = float(tstep)
|
|
237
|
-
if vmax <= vmin:
|
|
238
|
-
raise ValueError(
|
|
239
|
-
'Maximum voltage must be greater than minimum voltage.')
|
|
240
|
-
if dv <= 0:
|
|
241
|
-
raise ValueError(
|
|
242
|
-
'The voltage increment dv must be greater than zero.')
|
|
243
|
-
if tstep < 0:
|
|
244
|
-
raise ValueError('Step duration cannot be negative.')
|
|
245
|
-
self._vmin = vmin
|
|
246
|
-
self._vmax = vmax
|
|
247
|
-
self._dv = dv
|
|
248
|
-
self._tstep = tstep
|
|
249
|
-
self._steps = np.arange(self._vmin, self._vmax + self._dv, self._dv)
|
|
250
|
-
self._logs = None
|
|
251
|
-
|
|
252
|
-
def steps(self):
|
|
253
|
-
"""
|
|
254
|
-
Returns the list of steps this protocol will use.
|
|
255
|
-
"""
|
|
256
|
-
return list(self._steps)
|
|
257
|
-
|
|
258
|
-
def times(self):
|
|
259
|
-
"""
|
|
260
|
-
Returns a :class:`myokit.DataLog` containing the time-to-peak for
|
|
261
|
-
each logged variable at each voltage step.
|
|
262
|
-
"""
|
|
263
|
-
# Run simulation if needed
|
|
264
|
-
if self._logs is None:
|
|
265
|
-
self._run()
|
|
266
|
-
# Create a copy of the voltage steps
|
|
267
|
-
v = np.array(self._steps, copy=True)
|
|
268
|
-
# Create a simulation log
|
|
269
|
-
d = myokit.DataLog()
|
|
270
|
-
d[self._vvar] = v
|
|
271
|
-
# Find the peaks
|
|
272
|
-
for var in self._vars:
|
|
273
|
-
times = np.zeros(len(v))
|
|
274
|
-
for k, log in enumerate(self._logs):
|
|
275
|
-
times[k] = log[self._tvar][np.argmax(np.abs(log[var]))]
|
|
276
|
-
d[var] = times
|
|
277
|
-
return d
|
|
278
|
-
|
|
279
|
-
def traces(self):
|
|
280
|
-
"""
|
|
281
|
-
Returns the logged traces for each variable as an ordered list of
|
|
282
|
-
tuples ``(v, DataLog)``.
|
|
283
|
-
|
|
284
|
-
If any conversion factor was specified the data will be converted
|
|
285
|
-
before returning.
|
|
286
|
-
"""
|
|
287
|
-
if self._logs is None:
|
|
288
|
-
self._run()
|
|
289
|
-
data = []
|
|
290
|
-
steps = iter(self._steps)
|
|
291
|
-
factor = self._factor
|
|
292
|
-
if factor is None:
|
|
293
|
-
factor = np.ones(len(self._steps))
|
|
294
|
-
for k, log in enumerate(self._logs):
|
|
295
|
-
v = next(steps)
|
|
296
|
-
d = myokit.DataLog()
|
|
297
|
-
for var in self._vars:
|
|
298
|
-
d[var] = np.array(log[var]) * factor[k]
|
|
299
|
-
d[self._tvar] = log[self._tvar]
|
|
300
|
-
data.append((v, d))
|
|
301
|
-
return data
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
class Activation(StepProtocol):
|
|
305
|
-
"""
|
|
306
|
-
Runs a step protocol and measures during the step. Can be used to create
|
|
307
|
-
activation curves and I-V curves.
|
|
308
|
-
|
|
309
|
-
The protocol is defined as follows: Initially, the membrane potential is
|
|
310
|
-
held at a holding potential ``vhold`` for the duration of ``thold`` time
|
|
311
|
-
units. Then, the voltage is changed to ``vstep`` and held there for
|
|
312
|
-
``vstep`` time units. During this step, the cell's response is logged. The
|
|
313
|
-
experiment is repeated for ``vstep`` values ranging linearly from ``vmin``
|
|
314
|
-
to ``vmax`` with an increment of ``dv``.
|
|
315
|
-
|
|
316
|
-
::
|
|
317
|
-
|
|
318
|
-
. +--- vstep ---+
|
|
319
|
-
. +-------------+
|
|
320
|
-
. +-------------+
|
|
321
|
-
. | |
|
|
322
|
-
. vhold -------+ +-
|
|
323
|
-
. t=0 t=thold t=thold+tstep
|
|
324
|
-
. no current current!
|
|
325
|
-
|
|
326
|
-
Accepts the following input arguments:
|
|
327
|
-
|
|
328
|
-
``model``
|
|
329
|
-
The model for which to run the simulations
|
|
330
|
-
``var``
|
|
331
|
-
A variable or a list of variables to log and use in calculations
|
|
332
|
-
``vvar``
|
|
333
|
-
The membrane potential variable or its qname. If not given, the method
|
|
334
|
-
will search for a variable labelled ``membrane_potential``.
|
|
335
|
-
|
|
336
|
-
Depending on the experiment being run, ``var`` could be a conductance or a
|
|
337
|
-
current variable (or a list of conductances or currents).
|
|
338
|
-
|
|
339
|
-
The experiment is not performed until a call to one of the post-processing
|
|
340
|
-
methods is made. After this, the raw data will be cached. Any change to the
|
|
341
|
-
protocol variables after this point will delete the cached data.
|
|
342
|
-
"""
|
|
343
|
-
def __init__(self, model, var, vvar=None):
|
|
344
|
-
super(Activation, self).__init__(model, var, vvar)
|
|
345
|
-
|
|
346
|
-
def _run(self):
|
|
347
|
-
"""
|
|
348
|
-
Runs the experiment, logs during the voltage steps.
|
|
349
|
-
"""
|
|
350
|
-
log = self._vars + [self._tvar]
|
|
351
|
-
s = myokit.Simulation(self._model)
|
|
352
|
-
d = []
|
|
353
|
-
for v in self._steps:
|
|
354
|
-
s.reset()
|
|
355
|
-
s.set_constant(self._vvar, self._vhold)
|
|
356
|
-
s.set_max_step_size(None)
|
|
357
|
-
s.pre(self._thold)
|
|
358
|
-
s.set_constant(self._vvar, v)
|
|
359
|
-
s.set_max_step_size(self._max_step_size)
|
|
360
|
-
d.append(s.run(self._tstep, log=log))
|
|
361
|
-
self._logs = d
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
class Inactivation(StepProtocol):
|
|
365
|
-
"""
|
|
366
|
-
Can run an inactivation step protocol on a model and calculate various
|
|
367
|
-
entities using the results.
|
|
368
|
-
|
|
369
|
-
The protocol starts by holding the membrane potential at a high value,
|
|
370
|
-
causing the channels to fully activate and then inactivate. Next, the
|
|
371
|
-
potential is stepped to a lower value, causing the inactivation to go away
|
|
372
|
-
while the activation stays close to full. Current is measured after the
|
|
373
|
-
step, when the cell is at the holding potential.
|
|
374
|
-
|
|
375
|
-
::
|
|
376
|
-
|
|
377
|
-
. --- vstep ---+
|
|
378
|
-
. -------------+
|
|
379
|
-
. -------------+
|
|
380
|
-
. |
|
|
381
|
-
. +--- vhold ---+-
|
|
382
|
-
. t=0 t=tstep t=tstep+thold
|
|
383
|
-
|
|
384
|
-
Accepts the following input arguments:
|
|
385
|
-
|
|
386
|
-
``model``
|
|
387
|
-
The model for which to run the simulations
|
|
388
|
-
``var``
|
|
389
|
-
A variable or a list of variables to log and use in calculations
|
|
390
|
-
``vvar``
|
|
391
|
-
The membrane potential variable or its qname. If not given, the method
|
|
392
|
-
will search for a variable labelled ``membrane_potential``.
|
|
393
|
-
|
|
394
|
-
Depending on the experiment being run, ``var`` could be a conductance or a
|
|
395
|
-
current variable (or a list of conductances or currents).
|
|
396
|
-
|
|
397
|
-
The experiment is not performed until a call to one of the post-processing
|
|
398
|
-
methods is made. After this, the raw data will be cached. Any change to the
|
|
399
|
-
protocol variables after this point will delete the cached data.
|
|
400
|
-
"""
|
|
401
|
-
def __init__(self, model, var, vvar=None):
|
|
402
|
-
super(Inactivation, self).__init__(model, var, vvar)
|
|
403
|
-
|
|
404
|
-
def _run(self):
|
|
405
|
-
"""
|
|
406
|
-
Runs the simulation, saves the current traces.
|
|
407
|
-
"""
|
|
408
|
-
log = self._vars + [self._tvar]
|
|
409
|
-
d = []
|
|
410
|
-
s = myokit.Simulation(self._model)
|
|
411
|
-
for v in self._steps:
|
|
412
|
-
s.reset()
|
|
413
|
-
s.set_constant(self._vvar, v)
|
|
414
|
-
s.pre(self._tstep)
|
|
415
|
-
s.set_constant(self._vvar, self._vhold)
|
|
416
|
-
d.append(s.run(self._thold, log=log))
|
|
417
|
-
self._logs = d
|
|
418
|
-
|
|
419
|
-
def set_holding_potential(self, vhold=-20, thold=50):
|
|
420
|
-
"""
|
|
421
|
-
Sets the holding potential and the time to hold. During the experiment,
|
|
422
|
-
the cell will be held at ``vhold`` for ``thold`` time units before
|
|
423
|
-
every voltage step.
|
|
424
|
-
"""
|
|
425
|
-
super(Inactivation, self).set_holding_potential(vhold, thold)
|
|
426
|
-
|
|
427
|
-
def set_step_potential(self, vmin=-100, vmax=-40, dv=1, tstep=1000):
|
|
428
|
-
"""
|
|
429
|
-
Sets the step potentials and the step duration. Each experiment will
|
|
430
|
-
step linearly from ``vmin`` to ``vmax`` with steps of size ``dv``. The
|
|
431
|
-
cell is held at each step for ``tstep`` time units.
|
|
432
|
-
"""
|
|
433
|
-
super(Inactivation, self).set_step_potential(vmin, vmax, dv, tstep)
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
class Recovery(object):
|
|
437
|
-
"""
|
|
438
|
-
Can run a two-pulse recovery from inactivation experiment and process the
|
|
439
|
-
results.
|
|
440
|
-
|
|
441
|
-
The experiment proceeds as follows: first, the cell is held at a low
|
|
442
|
-
holding potential ``vhold`` for ``thold`` time units. This causes the
|
|
443
|
-
channels to fully deactivate (activation=0) and fully recover from
|
|
444
|
-
inactivation (inactivation=1). Next, the membrane is stepped up to a
|
|
445
|
-
voltage ``vstep`` and kept there for ``tstep1`` time units. This causes the
|
|
446
|
-
channels to activate (activation=1) and then inactivate (inactivation=0).
|
|
447
|
-
The model then steps back down to ``vhold`` for ``twait`` time units,
|
|
448
|
-
during which partial recovery from inactivation is expected. After this
|
|
449
|
-
short recovery period, the cell is stepped back to ``vstep`` for another
|
|
450
|
-
``tstep2`` time units. The recovery from inactivation can then be judged
|
|
451
|
-
by comparing the peak current during step2 with the peak current during
|
|
452
|
-
step1. By varying the time between steps ``twait`` a plot of recovery
|
|
453
|
-
characteristics can be made.
|
|
454
|
-
|
|
455
|
-
::
|
|
456
|
-
|
|
457
|
-
. +--- vstep ---+ +- vstep -+
|
|
458
|
-
. | | twait | |
|
|
459
|
-
. | | <-----> | |
|
|
460
|
-
. | | | |
|
|
461
|
-
. +--- vhold ---+ +- vhold -+ +---
|
|
462
|
-
. t=0 t=thold t+=tstep1 t+=twait t+=tstep2
|
|
463
|
-
|
|
464
|
-
Accepts the following input arguments:
|
|
465
|
-
|
|
466
|
-
``model``
|
|
467
|
-
The model for which to run the simulations
|
|
468
|
-
``var``
|
|
469
|
-
A conductance variable (or a list of variables) to record during the
|
|
470
|
-
experiment. Variables can be specified using Variable objects or
|
|
471
|
-
through their fully qualified names.
|
|
472
|
-
``vvar``
|
|
473
|
-
The membrane potential variable or its qname. If not given, the method
|
|
474
|
-
will search for a variable labelled ``membrane_potential``.
|
|
475
|
-
|
|
476
|
-
The argument ``var`` expects conduction variables, not currents. In other
|
|
477
|
-
words, it expects the name of a variable ``g`` such that
|
|
478
|
-
``I = Gmax * g * (V - E)``. Using conductance in this test rather than
|
|
479
|
-
current avoids the numerical problems incurred by dividing ``I`` through
|
|
480
|
-
``(V-E)``.
|
|
481
|
-
"""
|
|
482
|
-
def __init__(self, model, var, vvar=None):
|
|
483
|
-
if not model.is_valid():
|
|
484
|
-
raise ValueError('This method requires a valid model.')
|
|
485
|
-
# Clone model & variables
|
|
486
|
-
self._model = model.clone()
|
|
487
|
-
|
|
488
|
-
# Get time variable
|
|
489
|
-
self._tvar = self._model.time()
|
|
490
|
-
|
|
491
|
-
# Check conductance variables
|
|
492
|
-
self._vars = []
|
|
493
|
-
if isinstance(var, (basestring, myokit.Variable)):
|
|
494
|
-
var = [var]
|
|
495
|
-
for v in var:
|
|
496
|
-
if isinstance(v, myokit.Variable):
|
|
497
|
-
v = v.qname()
|
|
498
|
-
elif '.' not in v:
|
|
499
|
-
raise ValueError(
|
|
500
|
-
'The variable name(s) given as var must be given as fully'
|
|
501
|
-
' qualified names <component.variable>.')
|
|
502
|
-
self._vars.append(self._model.get(v))
|
|
503
|
-
|
|
504
|
-
# Check membrane potential
|
|
505
|
-
if vvar is None:
|
|
506
|
-
self._vvar = self._model.label('membrane_potential')
|
|
507
|
-
if self._vvar is None:
|
|
508
|
-
raise ValueError(
|
|
509
|
-
'Membrane potential variable must be given by vvar or'
|
|
510
|
-
' specified using the label "membrane_potential".')
|
|
511
|
-
else:
|
|
512
|
-
if isinstance(vvar, myokit.Variable):
|
|
513
|
-
vvar = vvar.qname()
|
|
514
|
-
elif '.' not in vvar:
|
|
515
|
-
raise ValueError(
|
|
516
|
-
'The variable name vvar must be given as a fully qualified'
|
|
517
|
-
' variable name <component.var>.')
|
|
518
|
-
self._vvar = self._model.get(vvar)
|
|
519
|
-
|
|
520
|
-
# Update membrane potential variable
|
|
521
|
-
if self._vvar.is_state():
|
|
522
|
-
self._vvar.demote()
|
|
523
|
-
self._vvar.set_binding(None)
|
|
524
|
-
|
|
525
|
-
# Set voltages
|
|
526
|
-
self.set_holding_potential()
|
|
527
|
-
self.set_step_potential()
|
|
528
|
-
self.set_pause_duration()
|
|
529
|
-
|
|
530
|
-
def ratio(self):
|
|
531
|
-
"""
|
|
532
|
-
Returns the ratios of the peak conductances (p1 / p2) for step 1 and
|
|
533
|
-
step 2.
|
|
534
|
-
|
|
535
|
-
The returned value is a :class:`myokit.DataLog` with entries
|
|
536
|
-
corresponding to the given variable names.
|
|
537
|
-
"""
|
|
538
|
-
# Times to wait
|
|
539
|
-
twaits = np.exp(
|
|
540
|
-
np.linspace(np.log(self._tmin), np.log(self._tmax), self._nt))
|
|
541
|
-
|
|
542
|
-
# Variables to log
|
|
543
|
-
log_vars = [x.qname() for x in self._vars]
|
|
544
|
-
|
|
545
|
-
# Run simulations
|
|
546
|
-
self._vvar.set_rhs(self._vhold) # Make V a constant
|
|
547
|
-
s = myokit.Simulation(self._model)
|
|
548
|
-
log = myokit.DataLog()
|
|
549
|
-
gvars = [x.qname() for x in self._vars]
|
|
550
|
-
for g in gvars:
|
|
551
|
-
log[g] = []
|
|
552
|
-
log[self._tvar.qname()] = list(twaits)
|
|
553
|
-
for twait in twaits:
|
|
554
|
-
s.set_constant(self._vvar, self._vhold)
|
|
555
|
-
s.run(self._thold, log=myokit.LOG_NONE)
|
|
556
|
-
s.set_constant(self._vvar, self._vstep)
|
|
557
|
-
d1 = s.run(self._tstep1, log=log_vars)
|
|
558
|
-
s.set_constant(self._vvar, self._vhold)
|
|
559
|
-
s.run(twait, log=myokit.LOG_NONE)
|
|
560
|
-
s.set_constant(self._vvar, self._vstep)
|
|
561
|
-
d2 = s.run(self._tstep2, log=log_vars)
|
|
562
|
-
for g in gvars:
|
|
563
|
-
ratio = np.max(d1[g])
|
|
564
|
-
ratio = np.nan if ratio == 0 else np.max(d2[g]) / ratio
|
|
565
|
-
log[g].append(ratio)
|
|
566
|
-
return log
|
|
567
|
-
|
|
568
|
-
def set_holding_potential(self, vhold=-120, thold=2000):
|
|
569
|
-
"""
|
|
570
|
-
Sets the holding potential and the time to hold. During the experiment,
|
|
571
|
-
the cell will be held at ``vhold`` for ``thold`` time units before
|
|
572
|
-
every voltage step.
|
|
573
|
-
"""
|
|
574
|
-
vhold = float(vhold)
|
|
575
|
-
thold = float(thold)
|
|
576
|
-
if thold < 0:
|
|
577
|
-
raise ValueError('Time to hold cannot be negative.')
|
|
578
|
-
self._vhold = vhold
|
|
579
|
-
self._thold = thold
|
|
580
|
-
|
|
581
|
-
def set_pause_duration(self, tmin=0.5, tmax=1000, nt=50):
|
|
582
|
-
"""
|
|
583
|
-
Sets the duration of the pauses between the steps.
|
|
584
|
-
|
|
585
|
-
``tmin``
|
|
586
|
-
The shortest time between steps
|
|
587
|
-
``tmax``
|
|
588
|
-
The longest time between steps
|
|
589
|
-
``nt``
|
|
590
|
-
The number of times to test
|
|
591
|
-
|
|
592
|
-
"""
|
|
593
|
-
tmin = float(tmin)
|
|
594
|
-
tmax = float(tmax)
|
|
595
|
-
nt = int(nt)
|
|
596
|
-
if tmin < 0:
|
|
597
|
-
raise ValueError('Minimum time cannot be negative.')
|
|
598
|
-
if tmax < 0:
|
|
599
|
-
raise ValueError('Maximum time cannot be positive.')
|
|
600
|
-
if tmin >= tmax:
|
|
601
|
-
raise ValueError('Maximum time must be grater than minimum.')
|
|
602
|
-
if nt < 2:
|
|
603
|
-
raise ValueError(
|
|
604
|
-
'The number of times to test must be greater than one.')
|
|
605
|
-
self._tmin = tmin
|
|
606
|
-
self._tmax = tmax
|
|
607
|
-
self._nt = nt
|
|
608
|
-
|
|
609
|
-
def set_step_potential(self, vstep=-20, tstep1=500, tstep2=25):
|
|
610
|
-
"""
|
|
611
|
-
Sets the step potential and the step durations.
|
|
612
|
-
|
|
613
|
-
``vstep``
|
|
614
|
-
The potential used during the steps
|
|
615
|
-
``tstep1``
|
|
616
|
-
The duration of the first pulse
|
|
617
|
-
``tstep2``
|
|
618
|
-
The duration of the second pulse
|
|
619
|
-
|
|
620
|
-
"""
|
|
621
|
-
vstep = float(vstep)
|
|
622
|
-
tstep1 = float(tstep1)
|
|
623
|
-
tstep2 = float(tstep2)
|
|
624
|
-
if tstep1 < 0:
|
|
625
|
-
raise ValueError('Time of first step cannot be negative.')
|
|
626
|
-
if tstep2 < 0:
|
|
627
|
-
raise ValueError('Time of second step cannot be negative.')
|
|
628
|
-
self._vstep = vstep
|
|
629
|
-
self._tstep1 = tstep1
|
|
630
|
-
self._tstep2 = tstep2
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
class Restitution(object):
|
|
634
|
-
"""
|
|
635
|
-
Can run a restitution experiment and return the values needed to make a
|
|
636
|
-
plot.
|
|
637
|
-
|
|
638
|
-
Accepts the following input arguments:
|
|
639
|
-
|
|
640
|
-
``model``
|
|
641
|
-
The model for which to run the simulations
|
|
642
|
-
``vvar``
|
|
643
|
-
The variable or variable name representing membrane potential. If not
|
|
644
|
-
given, the method will look for the label ``membrane_potential``, if
|
|
645
|
-
that's not found an exception is raised.
|
|
646
|
-
|
|
647
|
-
"""
|
|
648
|
-
def __init__(self, model, vvar=None):
|
|
649
|
-
# Check model
|
|
650
|
-
self._model = model.clone()
|
|
651
|
-
|
|
652
|
-
# Check membrane potential
|
|
653
|
-
if vvar is None:
|
|
654
|
-
self._vvar = self._model.label('membrane_potential')
|
|
655
|
-
if self._vvar is None:
|
|
656
|
-
raise ValueError(
|
|
657
|
-
'Membrane potential variable must be given by vvar or'
|
|
658
|
-
' specified using the label "membrane_potential".')
|
|
659
|
-
else:
|
|
660
|
-
if isinstance(vvar, myokit.Variable):
|
|
661
|
-
vvar = vvar.qname()
|
|
662
|
-
elif '.' not in vvar:
|
|
663
|
-
raise ValueError(
|
|
664
|
-
'The variable name vvar must be given as a fully qualified'
|
|
665
|
-
' variable name <component.var>.')
|
|
666
|
-
self._vvar = self._model.get(vvar)
|
|
667
|
-
|
|
668
|
-
# Set default arguments
|
|
669
|
-
self.set_max_step_size()
|
|
670
|
-
self.set_times()
|
|
671
|
-
self.set_beats()
|
|
672
|
-
self.set_stimulus()
|
|
673
|
-
self.set_threshold()
|
|
674
|
-
|
|
675
|
-
# No data yet!
|
|
676
|
-
self._data = None
|
|
677
|
-
|
|
678
|
-
def _run(self):
|
|
679
|
-
"""
|
|
680
|
-
Runs the simulations, saves the data.
|
|
681
|
-
"""
|
|
682
|
-
# Create protocol
|
|
683
|
-
e = {
|
|
684
|
-
'level': self._stim_level,
|
|
685
|
-
'start': 0,
|
|
686
|
-
'duration': self._stim_duration,
|
|
687
|
-
'period': self._clmin,
|
|
688
|
-
'multiplier': 0,
|
|
689
|
-
}
|
|
690
|
-
|
|
691
|
-
# Create simulation
|
|
692
|
-
s = myokit.Simulation(self._model)
|
|
693
|
-
s.set_max_step_size(self._max_step_size)
|
|
694
|
-
|
|
695
|
-
# Start testing
|
|
696
|
-
i = 0
|
|
697
|
-
pcls = []
|
|
698
|
-
apds = []
|
|
699
|
-
c = self._clmax
|
|
700
|
-
while c >= self._clmin:
|
|
701
|
-
# Update cycle length
|
|
702
|
-
c = self._clmax - i * self._dcl
|
|
703
|
-
i += 1
|
|
704
|
-
|
|
705
|
-
# Create and set new protocol
|
|
706
|
-
p = myokit.Protocol()
|
|
707
|
-
e['period'] = c
|
|
708
|
-
p.schedule(**e)
|
|
709
|
-
s.set_protocol(p)
|
|
710
|
-
|
|
711
|
-
# Run simulation
|
|
712
|
-
s.reset()
|
|
713
|
-
s.pre(c * self._pre_beats)
|
|
714
|
-
d, a = s.run(
|
|
715
|
-
c * self._beats,
|
|
716
|
-
log=myokit.LOG_NONE,
|
|
717
|
-
apd_variable=self._vvar,
|
|
718
|
-
apd_threshold=self._apd_threshold
|
|
719
|
-
)
|
|
720
|
-
|
|
721
|
-
# Save apds
|
|
722
|
-
for apd in a['duration']:
|
|
723
|
-
pcls.append(c)
|
|
724
|
-
apds.append(apd)
|
|
725
|
-
|
|
726
|
-
# Store data
|
|
727
|
-
self._data = pcls, apds
|
|
728
|
-
|
|
729
|
-
def run(self):
|
|
730
|
-
"""
|
|
731
|
-
Returns a :class:`DataLog` containing the tested cycle lengths as
|
|
732
|
-
``cl`` and the measured action potential durations as ``apd``. The
|
|
733
|
-
diastolic intervals are given as ``di``.
|
|
734
|
-
|
|
735
|
-
Each cycle length is repeated ``beats`` number of times, where
|
|
736
|
-
``beats`` is the number of beats specified in the constructor.
|
|
737
|
-
"""
|
|
738
|
-
# Run
|
|
739
|
-
if self._data is None:
|
|
740
|
-
self._run()
|
|
741
|
-
# Get data
|
|
742
|
-
cl, apd = self._data
|
|
743
|
-
d = myokit.DataLog()
|
|
744
|
-
d['cl'] = list(cl)
|
|
745
|
-
d['apd'] = list(apd)
|
|
746
|
-
d['di'] = list(np.array(cl, copy=False) - np.array(apd, copy=False))
|
|
747
|
-
return d
|
|
748
|
-
|
|
749
|
-
def set_beats(self, beats=2, pre=50):
|
|
750
|
-
"""
|
|
751
|
-
Sets the number of beats each cycle length is tested for.
|
|
752
|
-
|
|
753
|
-
``beats``
|
|
754
|
-
The number of beats during which apd is measured
|
|
755
|
-
``pre``
|
|
756
|
-
The number of pre-pacing beats done at each cycle length before the
|
|
757
|
-
measurement.
|
|
758
|
-
"""
|
|
759
|
-
beats = int(beats)
|
|
760
|
-
pre = int(pre)
|
|
761
|
-
if beats < 1:
|
|
762
|
-
raise ValueError(
|
|
763
|
-
'The number of beats must be an integer greater than zero.')
|
|
764
|
-
if pre < 0:
|
|
765
|
-
raise ValueError(
|
|
766
|
-
'The number of pre-pacing beats must be a positive integer.')
|
|
767
|
-
self._beats = beats
|
|
768
|
-
self._pre_beats = pre
|
|
769
|
-
self._data = None
|
|
770
|
-
|
|
771
|
-
def set_max_step_size(self, dtmax=None):
|
|
772
|
-
"""
|
|
773
|
-
Sets an (optional) maximum step size for the solver. To let the solver
|
|
774
|
-
pick any step size it likes, use ``dtmax=None``.
|
|
775
|
-
|
|
776
|
-
This method can be useful to avoid "CVODES flag 22" errors.
|
|
777
|
-
"""
|
|
778
|
-
if dtmax is None:
|
|
779
|
-
self._max_step_size = None
|
|
780
|
-
else:
|
|
781
|
-
dtmax = float(dtmax)
|
|
782
|
-
if dtmax <= 0:
|
|
783
|
-
raise ValueError(
|
|
784
|
-
'Maximum step size must be greater than zero.')
|
|
785
|
-
self._max_step_size = dtmax
|
|
786
|
-
self._data = None
|
|
787
|
-
|
|
788
|
-
def set_stimulus(self, duration=2.0, level=1):
|
|
789
|
-
"""
|
|
790
|
-
Sets the stimulus used to pace the model.
|
|
791
|
-
|
|
792
|
-
``stim_duration``
|
|
793
|
-
The duration of the pacing stimulus.
|
|
794
|
-
``stim_level``
|
|
795
|
-
The level of the dimensionless pacing stimulus.
|
|
796
|
-
|
|
797
|
-
"""
|
|
798
|
-
duration = float(duration)
|
|
799
|
-
level = float(level)
|
|
800
|
-
if duration < 0:
|
|
801
|
-
raise ValueError('The duratio cannot be negative.')
|
|
802
|
-
self._stim_duration = duration
|
|
803
|
-
self._stim_level = level
|
|
804
|
-
self._data = None
|
|
805
|
-
|
|
806
|
-
def set_threshold(self, threshold=-70):
|
|
807
|
-
"""
|
|
808
|
-
Sets the APD threshold, specified as a fixed membrane potential.
|
|
809
|
-
"""
|
|
810
|
-
self._apd_threshold = float(threshold)
|
|
811
|
-
self._data = None
|
|
812
|
-
|
|
813
|
-
def set_times(self, clmin=300, clmax=1200, dcl=20):
|
|
814
|
-
"""
|
|
815
|
-
Sets the pacing cycle lengths tested in this experiment.
|
|
816
|
-
|
|
817
|
-
``clmin``
|
|
818
|
-
The shortest cycle length tested.
|
|
819
|
-
``clmax``
|
|
820
|
-
The longest cycle length tested.
|
|
821
|
-
``dcl``
|
|
822
|
-
The size of the steps from ``clmin`` to ``clmax``
|
|
823
|
-
|
|
824
|
-
"""
|
|
825
|
-
clmin = float(clmin)
|
|
826
|
-
clmax = float(clmax)
|
|
827
|
-
dcl = float(dcl)
|
|
828
|
-
if clmin < 0:
|
|
829
|
-
raise ValueError('Minimum time cannot be negative.')
|
|
830
|
-
if clmin >= clmax:
|
|
831
|
-
raise ValueError('Minimum time must be smaller than maximum time.')
|
|
832
|
-
if dcl <= 0:
|
|
833
|
-
raise ValueError('Step size must be greater than zero')
|
|
834
|
-
self._clmin = clmin
|
|
835
|
-
self._clmax = clmax
|
|
836
|
-
self._dcl = dcl
|
|
837
|
-
self._data = None
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
class StrengthDuration(object):
|
|
841
|
-
"""
|
|
842
|
-
For a range of durations, this experiment checks the stimulus size needed
|
|
843
|
-
to excite a cell.
|
|
844
|
-
|
|
845
|
-
Accepts the following input arguments:
|
|
846
|
-
|
|
847
|
-
``model``
|
|
848
|
-
The model to run the experiment with.
|
|
849
|
-
``ivar``
|
|
850
|
-
A variable that can be used as the stimulus current. It's value will
|
|
851
|
-
be set as ``pace * amplitude`` where pace is a variable bound to the
|
|
852
|
-
pacing signal and amplitude is varied by the method.
|
|
853
|
-
``vvar``
|
|
854
|
-
A variable that indicates the membrane potential. If not specified (or
|
|
855
|
-
given as ``None``), the method will search for a variable labeled as
|
|
856
|
-
``membrane_potential``.
|
|
857
|
-
|
|
858
|
-
"""
|
|
859
|
-
def __init__(self, model, ivar, vvar=None):
|
|
860
|
-
# Clone model
|
|
861
|
-
self._model = model.clone()
|
|
862
|
-
del model
|
|
863
|
-
|
|
864
|
-
# Get stimulus current variable
|
|
865
|
-
if isinstance(ivar, myokit.Variable):
|
|
866
|
-
ivar = ivar.qname()
|
|
867
|
-
self._ivar = self._model.get(ivar)
|
|
868
|
-
del ivar
|
|
869
|
-
|
|
870
|
-
# Get membrane potential variable
|
|
871
|
-
if vvar is None:
|
|
872
|
-
self._vvar = self._model.label('membrane_potential')
|
|
873
|
-
if self._vvar is None:
|
|
874
|
-
raise ValueError(
|
|
875
|
-
'This method requires the membrane potential variable to'
|
|
876
|
-
' be passed in as `vvar` or indicated in the model using'
|
|
877
|
-
' the label `membrane_potential`.')
|
|
878
|
-
else:
|
|
879
|
-
if isinstance(vvar, myokit.Variable):
|
|
880
|
-
vvar = vvar.qname()
|
|
881
|
-
self._vvar = self._model.get(vvar)
|
|
882
|
-
del vvar
|
|
883
|
-
|
|
884
|
-
# Get time variable
|
|
885
|
-
self._tvar = self._model.time()
|
|
886
|
-
|
|
887
|
-
# Unbind any existing pace variable
|
|
888
|
-
var = self._model.binding('pace')
|
|
889
|
-
if var is not None:
|
|
890
|
-
var.set_binding(None)
|
|
891
|
-
|
|
892
|
-
# Create new pacing variable
|
|
893
|
-
c = self._ivar.parent(myokit.Component)
|
|
894
|
-
|
|
895
|
-
def add_variable(c, name):
|
|
896
|
-
try:
|
|
897
|
-
return c.add_variable(name)
|
|
898
|
-
except myokit.DuplicateName:
|
|
899
|
-
i = 2
|
|
900
|
-
n = name + str(i)
|
|
901
|
-
while True:
|
|
902
|
-
try:
|
|
903
|
-
return c.add_variable(n)
|
|
904
|
-
except myokit.DuplicateName:
|
|
905
|
-
i += 1
|
|
906
|
-
n = name + str(i)
|
|
907
|
-
self._pvar = add_variable(c, 'pace')
|
|
908
|
-
self._pvar.set_binding('pace')
|
|
909
|
-
self._pvar.set_rhs(0)
|
|
910
|
-
|
|
911
|
-
# Create new amplitude variable
|
|
912
|
-
self._avar = add_variable(c, 'amplitude')
|
|
913
|
-
self._avar.set_rhs(0)
|
|
914
|
-
|
|
915
|
-
# Set rhs of current variable
|
|
916
|
-
if self._ivar.is_state():
|
|
917
|
-
self._ivar.demote()
|
|
918
|
-
self._ivar.set_rhs(myokit.Multiply(self._pvar.lhs(), self._avar.lhs()))
|
|
919
|
-
|
|
920
|
-
# Set default parameters
|
|
921
|
-
self.set_currents()
|
|
922
|
-
self.set_precision()
|
|
923
|
-
self.set_threshold()
|
|
924
|
-
self.set_times()
|
|
925
|
-
|
|
926
|
-
# No data yet!
|
|
927
|
-
self._data = None
|
|
928
|
-
|
|
929
|
-
def run(self, debug=False):
|
|
930
|
-
"""
|
|
931
|
-
Runs the experiment, returning a :class:`myokit.DataLog` with the
|
|
932
|
-
entries ``duration`` and ``strength``, where each strenght is the
|
|
933
|
-
minimum required to create a depolarisation at the corresponding
|
|
934
|
-
duration.
|
|
935
|
-
"""
|
|
936
|
-
if self._data is None:
|
|
937
|
-
self._run(debug)
|
|
938
|
-
return self._data
|
|
939
|
-
|
|
940
|
-
def _run(self, debug=False):
|
|
941
|
-
"""
|
|
942
|
-
Inner version of run()
|
|
943
|
-
"""
|
|
944
|
-
if debug:
|
|
945
|
-
import traceback
|
|
946
|
-
|
|
947
|
-
# Create simulation
|
|
948
|
-
s = myokit.Simulation(self._model)
|
|
949
|
-
|
|
950
|
-
# Variables to log
|
|
951
|
-
vvar = self._vvar.qname()
|
|
952
|
-
|
|
953
|
-
# Output data
|
|
954
|
-
durations = np.array(self._durations, copy=True)
|
|
955
|
-
amplitudes = np.zeros(durations.shape)
|
|
956
|
-
|
|
957
|
-
# Test every duration
|
|
958
|
-
for k, duration in enumerate(durations):
|
|
959
|
-
if debug:
|
|
960
|
-
print('Testing duration: ' + str(duration))
|
|
961
|
-
s.set_protocol(myokit.pacing.blocktrain(self._time + 1, duration))
|
|
962
|
-
a1 = self._amin
|
|
963
|
-
a2 = self._amax
|
|
964
|
-
|
|
965
|
-
# Test minimum amplitude
|
|
966
|
-
s.reset()
|
|
967
|
-
s.set_constant(self._avar, a1)
|
|
968
|
-
try:
|
|
969
|
-
d = s.run(self._time, log=[vvar]).npview()
|
|
970
|
-
t1 = (np.max(d[vvar]) > self._threshold)
|
|
971
|
-
except Exception:
|
|
972
|
-
if debug:
|
|
973
|
-
traceback.print_exc()
|
|
974
|
-
t1 = False
|
|
975
|
-
if debug:
|
|
976
|
-
print(t1)
|
|
977
|
-
|
|
978
|
-
# Test maximum amplitude
|
|
979
|
-
s.reset()
|
|
980
|
-
s.set_constant(self._avar, a2)
|
|
981
|
-
try:
|
|
982
|
-
d = s.run(self._time, log=[vvar]).npview()
|
|
983
|
-
t2 = (np.max(d[vvar]) > self._threshold)
|
|
984
|
-
except Exception:
|
|
985
|
-
if debug:
|
|
986
|
-
traceback.print_exc()
|
|
987
|
-
t2 = False
|
|
988
|
-
if debug:
|
|
989
|
-
print(t2)
|
|
990
|
-
if t1 == t2:
|
|
991
|
-
# No zero crossing found
|
|
992
|
-
amplitudes[k] = np.nan
|
|
993
|
-
if debug:
|
|
994
|
-
print('> no zero crossing')
|
|
995
|
-
continue
|
|
996
|
-
|
|
997
|
-
# Zero must lie in between. Start bisection search
|
|
998
|
-
a = 0.5 * a1 + 0.5 * a2
|
|
999
|
-
for j in range(0, self._precision):
|
|
1000
|
-
s.reset()
|
|
1001
|
-
s.set_constant(self._avar, a)
|
|
1002
|
-
try:
|
|
1003
|
-
d = s.run(self._time, log=[vvar]).npview()
|
|
1004
|
-
except Exception:
|
|
1005
|
-
if debug:
|
|
1006
|
-
traceback.print_exc()
|
|
1007
|
-
break
|
|
1008
|
-
t = (np.max(d[vvar]) > self._threshold)
|
|
1009
|
-
if t1 == t:
|
|
1010
|
-
a1 = a
|
|
1011
|
-
else:
|
|
1012
|
-
a2 = a
|
|
1013
|
-
a = 0.5 * a1 + 0.5 * a2
|
|
1014
|
-
amplitudes[k] = a
|
|
1015
|
-
if debug:
|
|
1016
|
-
print('> ' + str(a))
|
|
1017
|
-
|
|
1018
|
-
# Set output data
|
|
1019
|
-
self._data = myokit.DataLog()
|
|
1020
|
-
self._data['duration'] = durations
|
|
1021
|
-
self._data['strength'] = amplitudes
|
|
1022
|
-
|
|
1023
|
-
def set_currents(self, imin=-250, imax=0):
|
|
1024
|
-
"""
|
|
1025
|
-
Sets the range of current levels tested.
|
|
1026
|
-
|
|
1027
|
-
``imin``
|
|
1028
|
-
The lowest (or most negative) current amplitude to test.
|
|
1029
|
-
``imax``
|
|
1030
|
-
The greatest (or least negative) current amplitude to test.
|
|
1031
|
-
"""
|
|
1032
|
-
amin = float(imin)
|
|
1033
|
-
amax = float(imax)
|
|
1034
|
-
if amin >= amax:
|
|
1035
|
-
raise ValueError(
|
|
1036
|
-
'Minimum current amplitude must be smaller than maximum'
|
|
1037
|
-
' value.')
|
|
1038
|
-
self._amin = amin
|
|
1039
|
-
self._amax = amax
|
|
1040
|
-
self._data = None
|
|
1041
|
-
|
|
1042
|
-
def set_precision(self, precision=10):
|
|
1043
|
-
"""
|
|
1044
|
-
Sets the number of different amplitudes tried. This is done using a
|
|
1045
|
-
bisection algorithm, so low values of ``precision`` can still produce a
|
|
1046
|
-
good result.
|
|
1047
|
-
"""
|
|
1048
|
-
precision = int(precision)
|
|
1049
|
-
if precision < 1:
|
|
1050
|
-
raise ValueError('The number of tries must be greater than zero.')
|
|
1051
|
-
self._precision = precision
|
|
1052
|
-
self._data = None
|
|
1053
|
-
|
|
1054
|
-
def set_threshold(self, threshold=10):
|
|
1055
|
-
"""
|
|
1056
|
-
Sets the level above which the membrane potential must rise to count as
|
|
1057
|
-
a depolarization.
|
|
1058
|
-
"""
|
|
1059
|
-
self._threshold = float(threshold)
|
|
1060
|
-
self._data = None
|
|
1061
|
-
|
|
1062
|
-
def set_times(self, tmin=0.2, tmax=2.0, dt=0.1, twait=50):
|
|
1063
|
-
"""
|
|
1064
|
-
Sets the tested stimulus durations.
|
|
1065
|
-
|
|
1066
|
-
``tmin``
|
|
1067
|
-
The smallest stimulus time tested.
|
|
1068
|
-
``tmax``
|
|
1069
|
-
The largest stimulus time tested.
|
|
1070
|
-
``dt``
|
|
1071
|
-
The step size when going from tmin to tmax.
|
|
1072
|
-
``twait``
|
|
1073
|
-
The duration of the experiment that tries to measure a
|
|
1074
|
-
depolarization.
|
|
1075
|
-
|
|
1076
|
-
"""
|
|
1077
|
-
tmin = float(tmin)
|
|
1078
|
-
tmax = float(tmax)
|
|
1079
|
-
if tmin < 0:
|
|
1080
|
-
raise ValueError('Minimum time cannot be negative.')
|
|
1081
|
-
if tmax < 0:
|
|
1082
|
-
raise ValueError('Maximum time cannot be negative.')
|
|
1083
|
-
if tmin >= tmax:
|
|
1084
|
-
raise ValueError(
|
|
1085
|
-
'The maximum time must be greater than the minimum time.')
|
|
1086
|
-
dt = float(dt)
|
|
1087
|
-
if dt <= 0:
|
|
1088
|
-
raise ValueError('The step size must be greater than zero.')
|
|
1089
|
-
twait = float(twait)
|
|
1090
|
-
if twait < 0:
|
|
1091
|
-
raise ValueError('The time "twait" must be greater than zero.')
|
|
1092
|
-
self._durations = np.arange(tmin, tmax, dt)
|
|
1093
|
-
self._time = twait
|
|
1094
|
-
self._data = None
|