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.
Files changed (229) hide show
  1. myokit/__init__.py +9 -36
  2. myokit/__main__.py +76 -142
  3. myokit/_aux.py +62 -16
  4. myokit/_bin/example.mmt +1 -2
  5. myokit/_bin/install-win/menu.json +7 -7
  6. myokit/_config.py +22 -31
  7. myokit/_datablock.py +30 -74
  8. myokit/_datalog.py +49 -72
  9. myokit/_err.py +25 -24
  10. myokit/_expressions.py +50 -68
  11. myokit/_io.py +15 -27
  12. myokit/_model_api.py +453 -249
  13. myokit/_myokit_version.py +1 -5
  14. myokit/_parsing.py +38 -44
  15. myokit/_progress.py +5 -8
  16. myokit/_protocol.py +99 -9
  17. myokit/_sim/__init__.py +7 -24
  18. myokit/_sim/cable.c +6 -8
  19. myokit/_sim/cable.py +6 -8
  20. myokit/_sim/cmodel.h +125 -70
  21. myokit/_sim/cmodel.py +12 -14
  22. myokit/_sim/compiler.py +1 -4
  23. myokit/_sim/cvodessim.c +196 -118
  24. myokit/_sim/cvodessim.py +130 -103
  25. myokit/_sim/differential.hpp +4 -4
  26. myokit/_sim/fiber_tissue.c +4 -8
  27. myokit/_sim/fiber_tissue.py +11 -13
  28. myokit/_sim/jacobian.cpp +2 -2
  29. myokit/_sim/jacobian.py +11 -8
  30. myokit/_sim/mcl.h +53 -55
  31. myokit/_sim/opencl.py +21 -27
  32. myokit/_sim/openclsim.c +3 -7
  33. myokit/_sim/openclsim.cl +3 -3
  34. myokit/_sim/openclsim.py +49 -40
  35. myokit/_sim/pacing.h +36 -16
  36. myokit/_sim/rhs.c +6 -13
  37. myokit/_sim/rhs.py +5 -14
  38. myokit/_sim/sundials.py +1 -4
  39. myokit/_system.py +10 -16
  40. myokit/_unit.py +4 -13
  41. myokit/float.py +0 -3
  42. myokit/formats/__init__.py +8 -10
  43. myokit/formats/ansic/__init__.py +0 -3
  44. myokit/formats/ansic/_ewriter.py +2 -4
  45. myokit/formats/ansic/_exporter.py +1 -4
  46. myokit/formats/ansic/template/cable.c +4 -4
  47. myokit/formats/ansic/template/euler.c +5 -5
  48. myokit/formats/ansic/template/sim.c +6 -6
  49. myokit/formats/axon/__init__.py +1 -3
  50. myokit/formats/axon/_abf.py +12 -17
  51. myokit/formats/axon/_atf.py +5 -6
  52. myokit/formats/axon/_importer.py +0 -3
  53. myokit/formats/cellml/__init__.py +0 -3
  54. myokit/formats/cellml/_ewriter.py +3 -6
  55. myokit/formats/cellml/_exporter.py +3 -6
  56. myokit/formats/cellml/_importer.py +1 -4
  57. myokit/formats/cellml/v1/__init__.py +0 -4
  58. myokit/formats/cellml/v1/_api.py +8 -11
  59. myokit/formats/cellml/v1/_parser.py +2 -5
  60. myokit/formats/cellml/v1/_writer.py +2 -11
  61. myokit/formats/cellml/v2/__init__.py +0 -3
  62. myokit/formats/cellml/v2/_api.py +8 -17
  63. myokit/formats/cellml/v2/_parser.py +2 -5
  64. myokit/formats/cellml/v2/_writer.py +1 -4
  65. myokit/formats/channelml/__init__.py +0 -3
  66. myokit/formats/channelml/_importer.py +11 -21
  67. myokit/formats/cpp/__init__.py +1 -3
  68. myokit/formats/cpp/_ewriter.py +0 -3
  69. myokit/formats/cuda/__init__.py +0 -3
  70. myokit/formats/cuda/_ewriter.py +2 -4
  71. myokit/formats/cuda/_exporter.py +0 -3
  72. myokit/formats/cuda/template/kernel.cu +8 -5
  73. myokit/formats/easyml/__init__.py +0 -3
  74. myokit/formats/easyml/_ewriter.py +9 -11
  75. myokit/formats/easyml/_exporter.py +2 -5
  76. myokit/formats/html/__init__.py +0 -3
  77. myokit/formats/html/_exporter.py +0 -3
  78. myokit/formats/html/_flatten.py +5 -21
  79. myokit/formats/latex/__init__.py +0 -3
  80. myokit/formats/latex/_ewriter.py +1 -4
  81. myokit/formats/latex/_exporter.py +4 -6
  82. myokit/formats/mathml/__init__.py +0 -3
  83. myokit/formats/mathml/_ewriter.py +2 -11
  84. myokit/formats/mathml/_parser.py +4 -6
  85. myokit/formats/matlab/__init__.py +0 -3
  86. myokit/formats/matlab/_ewriter.py +1 -4
  87. myokit/formats/matlab/_exporter.py +2 -5
  88. myokit/formats/matlab/template/main.m +3 -2
  89. myokit/formats/opencl/__init__.py +0 -3
  90. myokit/formats/opencl/_ewriter.py +2 -4
  91. myokit/formats/opencl/_exporter.py +2 -5
  92. myokit/formats/opencl/template/cable.c +10 -10
  93. myokit/formats/opencl/template/kernel.cl +1 -1
  94. myokit/formats/opencl/template/minilog.py +1 -1
  95. myokit/formats/python/__init__.py +0 -3
  96. myokit/formats/python/_ewriter.py +2 -5
  97. myokit/formats/python/_exporter.py +0 -3
  98. myokit/formats/python/template/sim.py +14 -14
  99. myokit/formats/sbml/__init__.py +0 -3
  100. myokit/formats/sbml/_api.py +50 -44
  101. myokit/formats/sbml/_importer.py +1 -4
  102. myokit/formats/sbml/_parser.py +2 -5
  103. myokit/formats/stan/__init__.py +0 -3
  104. myokit/formats/stan/_ewriter.py +2 -4
  105. myokit/formats/stan/_exporter.py +2 -5
  106. myokit/formats/stan/template/cell.stan +3 -3
  107. myokit/formats/sympy/__init__.py +0 -3
  108. myokit/formats/sympy/_ereader.py +1 -4
  109. myokit/formats/sympy/_ewriter.py +2 -5
  110. myokit/formats/wcp/__init__.py +0 -3
  111. myokit/formats/wcp/_wcp.py +2 -8
  112. myokit/formats/xml/__init__.py +0 -3
  113. myokit/formats/xml/_exporter.py +0 -3
  114. myokit/formats/xml/_split.py +0 -3
  115. myokit/gui/__init__.py +80 -246
  116. myokit/gui/datablock_viewer.py +103 -86
  117. myokit/gui/datalog_viewer.py +214 -66
  118. myokit/gui/explorer.py +15 -21
  119. myokit/gui/ide.py +171 -144
  120. myokit/gui/progress.py +9 -9
  121. myokit/gui/source.py +406 -375
  122. myokit/gui/vargrapher.py +2 -12
  123. myokit/lib/deps.py +12 -13
  124. myokit/lib/guess.py +3 -4
  125. myokit/lib/hh.py +20 -18
  126. myokit/lib/markov.py +21 -20
  127. myokit/lib/multi.py +1 -3
  128. myokit/lib/plots.py +20 -9
  129. myokit/pacing.py +0 -3
  130. myokit/pype.py +7 -18
  131. myokit/tests/__init__.py +3 -6
  132. myokit/tests/ansic_event_based_pacing.py +1 -4
  133. myokit/tests/ansic_fixed_form_pacing.py +3 -6
  134. myokit/tests/data/beeler-1977-model-compare-b.mmt +2 -2
  135. myokit/tests/data/clancy-1999-fitting.mmt +1 -0
  136. myokit/tests/test_aux.py +13 -28
  137. myokit/tests/test_cellml_v1_api.py +4 -19
  138. myokit/tests/test_cellml_v1_parser.py +0 -15
  139. myokit/tests/test_cellml_v1_writer.py +0 -9
  140. myokit/tests/test_cellml_v2_api.py +4 -19
  141. myokit/tests/test_cellml_v2_parser.py +0 -15
  142. myokit/tests/test_cellml_v2_writer.py +0 -9
  143. myokit/tests/test_cmodel.py +16 -22
  144. myokit/tests/test_compiler_detection.py +1 -11
  145. myokit/tests/test_component.py +108 -56
  146. myokit/tests/test_config.py +34 -67
  147. myokit/tests/test_datablock.py +1 -9
  148. myokit/tests/test_datalog.py +19 -24
  149. myokit/tests/test_dependency_checking.py +8 -23
  150. myokit/tests/test_expressions.py +0 -9
  151. myokit/tests/test_float.py +1 -5
  152. myokit/tests/test_formats.py +0 -9
  153. myokit/tests/test_formats_axon.py +1 -9
  154. myokit/tests/test_formats_cellml.py +0 -15
  155. myokit/tests/test_formats_channelml.py +0 -15
  156. myokit/tests/test_formats_easyml.py +0 -14
  157. myokit/tests/test_formats_exporters.py +1 -16
  158. myokit/tests/test_formats_expression_writers.py +1 -17
  159. myokit/tests/test_formats_html.py +0 -3
  160. myokit/tests/test_formats_importers.py +1 -16
  161. myokit/tests/test_formats_mathml_content.py +0 -9
  162. myokit/tests/test_formats_mathml_presentation.py +0 -9
  163. myokit/tests/test_formats_opencl.py +0 -10
  164. myokit/tests/test_formats_sbml.py +0 -15
  165. myokit/tests/test_formats_sympy.py +0 -9
  166. myokit/tests/test_formats_wcp.py +1 -3
  167. myokit/tests/test_io.py +27 -27
  168. myokit/tests/test_jacobian_calculator.py +6 -14
  169. myokit/tests/test_jacobian_tracer.py +0 -9
  170. myokit/tests/test_lib_deps.py +0 -9
  171. myokit/tests/test_lib_guess.py +0 -9
  172. myokit/tests/test_lib_hh.py +18 -12
  173. myokit/tests/test_lib_markov.py +21 -13
  174. myokit/tests/test_lib_multi.py +0 -9
  175. myokit/tests/test_lib_plots.py +13 -8
  176. myokit/tests/test_meta.py +0 -3
  177. myokit/tests/test_model.py +390 -96
  178. myokit/tests/test_model_building.py +44 -96
  179. myokit/tests/test_opencl_info.py +5 -14
  180. myokit/tests/test_pacing_factory.py +0 -3
  181. myokit/tests/test_pacing_system_c.py +1 -23
  182. myokit/tests/test_pacing_system_py.py +0 -9
  183. myokit/tests/test_parsing.py +139 -56
  184. myokit/tests/test_progress_reporters.py +0 -3
  185. myokit/tests/test_protocol.py +0 -9
  186. myokit/tests/test_protocol_floating_point.py +1 -10
  187. myokit/tests/test_protocol_time_series.py +82 -0
  188. myokit/tests/test_pype.py +0 -9
  189. myokit/tests/test_quantity.py +0 -9
  190. myokit/tests/test_rhs_benchmarker.py +1 -9
  191. myokit/tests/test_sbml_api.py +27 -42
  192. myokit/tests/test_sbml_parser.py +4 -19
  193. myokit/tests/test_simulation_1d.py +45 -25
  194. myokit/tests/test_simulation_cvodes.py +321 -55
  195. myokit/tests/test_simulation_cvodes_from_disk.py +0 -3
  196. myokit/tests/test_simulation_fiber_tissue.py +39 -12
  197. myokit/tests/test_simulation_log_interval.py +1 -431
  198. myokit/tests/test_simulation_opencl.py +69 -48
  199. myokit/tests/test_simulation_opencl_log_interval.py +1 -3
  200. myokit/tests/test_simulation_opencl_vs_cvode.py +1 -10
  201. myokit/tests/test_simulation_opencl_vs_sim1d.py +1 -10
  202. myokit/tests/test_system_info.py +1 -11
  203. myokit/tests/test_tools.py +0 -9
  204. myokit/tests/test_unit.py +1 -10
  205. myokit/tests/test_user_functions.py +0 -10
  206. myokit/tests/test_variable.py +231 -27
  207. myokit/tools.py +5 -21
  208. myokit/units.py +5 -3
  209. {myokit-1.33.9.dist-info → myokit-1.35.0.dist-info}/METADATA +12 -15
  210. myokit-1.35.0.dist-info/RECORD +391 -0
  211. {myokit-1.33.9.dist-info → myokit-1.35.0.dist-info}/WHEEL +1 -1
  212. {myokit-1.33.9.dist-info → myokit-1.35.0.dist-info}/entry_points.txt +0 -1
  213. myokit/_exec_new.py +0 -15
  214. myokit/_exec_old.py +0 -15
  215. myokit/_sim/cvodesim.c +0 -1551
  216. myokit/_sim/cvodesim.py +0 -674
  217. myokit/_sim/icsim.cpp +0 -563
  218. myokit/_sim/icsim.py +0 -363
  219. myokit/_sim/psim.cpp +0 -656
  220. myokit/_sim/psim.py +0 -493
  221. myokit/lib/common.py +0 -1094
  222. myokit/tests/test_lib_common.py +0 -130
  223. myokit/tests/test_simulation_cvode.py +0 -612
  224. myokit/tests/test_simulation_ic.py +0 -108
  225. myokit/tests/test_simulation_p.py +0 -223
  226. myokit-1.33.9.dist-info/RECORD +0 -403
  227. /myokit/formats/opencl/template/{test → test.sh} +0 -0
  228. {myokit-1.33.9.dist-info → myokit-1.35.0.dist-info}/LICENSE.txt +0 -0
  229. {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