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/_myokit_version.py CHANGED
@@ -4,7 +4,6 @@
4
4
  # This file is part of Myokit.
5
5
  # See http://myokit.org for copyright, sharing, and licensing details.
6
6
  #
7
- import sys
8
7
 
9
8
  # True if this is a release, False for a development version
10
9
  __release__ = True
@@ -15,7 +14,7 @@ __release__ = True
15
14
  # incompatibility
16
15
  # - Changes to revision indicate bugfixes, tiny new features
17
16
  # - There is no significance to odd/even numbers
18
- __version_tuple__ = 1, 33, 9
17
+ __version_tuple__ = 1, 35, 0
19
18
 
20
19
  # String version of the version number
21
20
  __version__ = '.'.join([str(x) for x in __version_tuple__])
@@ -23,6 +22,3 @@ if not __release__: # pragma: no cover
23
22
  __version_tuple__ += ('dev', )
24
23
  __version__ += '.dev'
25
24
 
26
- # Don't expose x on Python2
27
- if sys.hexversion < 0x03000000: # pragma: no python 3 cover
28
- del x
myokit/_parsing.py CHANGED
@@ -4,22 +4,14 @@
4
4
  # This file is part of Myokit.
5
5
  # See http://myokit.org for copyright, sharing, and licensing details.
6
6
  #
7
- from __future__ import absolute_import, division
8
- from __future__ import print_function, unicode_literals
9
-
10
7
  import os
11
8
  import re
12
- #import unicodedata
9
+
13
10
  from collections import OrderedDict
14
11
 
15
12
  import myokit
16
- from myokit import ParseError, ProtocolParseError
17
13
 
18
- # Strings in Python2 and Python3
19
- try:
20
- basestring
21
- except NameError: # pragma: no cover
22
- basestring = str
14
+ from myokit import ParseError, ProtocolParseError
23
15
 
24
16
 
25
17
  def parse(source):
@@ -34,7 +26,7 @@ def parse(source):
34
26
  """
35
27
  # Get raw stream
36
28
  raw = source
37
- if isinstance(raw, basestring):
29
+ if isinstance(raw, str):
38
30
  raw = raw.splitlines()
39
31
  raw = iter(raw)
40
32
 
@@ -78,7 +70,7 @@ def parse_model(source):
78
70
  """
79
71
  # Get raw stream
80
72
  raw = source
81
- if isinstance(raw, basestring):
73
+ if isinstance(raw, str):
82
74
  raw = raw.splitlines()
83
75
  raw = iter(raw)
84
76
 
@@ -100,7 +92,7 @@ def parse_protocol(source):
100
92
  """
101
93
  # Get raw stream
102
94
  raw = source
103
- if isinstance(raw, basestring):
95
+ if isinstance(raw, str):
104
96
  raw = raw.splitlines()
105
97
  raw = iter(raw)
106
98
 
@@ -122,7 +114,7 @@ def parse_script(source):
122
114
  """
123
115
  # Get raw stream
124
116
  raw = source
125
- if isinstance(raw, basestring):
117
+ if isinstance(raw, str):
126
118
  raw = raw.splitlines()
127
119
  raw = iter(raw)
128
120
 
@@ -162,7 +154,7 @@ def parse_state(state):
162
154
  """
163
155
  # Get raw stream
164
156
  raw = state
165
- if isinstance(raw, basestring):
157
+ if isinstance(raw, str):
166
158
  raw = raw.splitlines()
167
159
  raw = iter(raw)
168
160
 
@@ -205,7 +197,7 @@ def split(source):
205
197
  """
206
198
  # Get raw stream
207
199
  raw = source
208
- if isinstance(raw, basestring):
200
+ if isinstance(raw, str):
209
201
  raw = raw.splitlines()
210
202
  raw = iter(raw)
211
203
  segments = ['', '', '']
@@ -317,7 +309,7 @@ def unexpected_token(token, expected):
317
309
  # Parse expected token(s) or string
318
310
  if type(expected) == int:
319
311
  expected = [expected]
320
- if not isinstance(expected, basestring):
312
+ if not isinstance(expected, str):
321
313
  if len(expected) > 2:
322
314
  expected = 'one of [' \
323
315
  + ', '.join([token_str[i] for i in expected]) + ']'
@@ -348,10 +340,11 @@ def reg_token(info, token, obj):
348
340
  d[token[3]] = (token, obj)
349
341
 
350
342
 
351
- class ParseInfo(object):
343
+ class ParseInfo:
352
344
  def __init__(self):
353
345
  self.model = None
354
346
  self.initial_values = OrderedDict()
347
+ self.initial_value_tokens = {}
355
348
  self.alias_map = {}
356
349
  self.user_functions = {}
357
350
 
@@ -420,10 +413,10 @@ def parse_model_from_stream(stream, syntax_only=False):
420
413
  'Duplicate initial value', t0[2], t0[3],
421
414
  'A value for <' + name + '> was already specified.')
422
415
  expect(next(stream), EQUAL)
423
- expr = parse_expression_stream(stream)
416
+ expr = parse_proto_expression(stream)
424
417
  expect(next(stream), EOL)
425
418
  info.initial_values[name] = expr
426
- reg_token(info, t0, expr)
419
+ info.initial_value_tokens[name] = t0
427
420
 
428
421
  token = stream.peek()
429
422
 
@@ -439,15 +432,6 @@ def parse_model_from_stream(stream, syntax_only=False):
439
432
  if syntax_only:
440
433
  return True
441
434
 
442
- # All initial variables must have been used
443
- for qname, e in info.initial_values.items():
444
- raise ParseError(
445
- 'Unused initial value', 0, 0,
446
- 'An unused initial value was found for "' + str(qname) + '".')
447
-
448
- # Re-order the model state
449
- model.reorder_state(state_order)
450
-
451
435
  # Order encountered tokens
452
436
  m = model._tokens
453
437
  model._tokens = {}
@@ -468,6 +452,23 @@ def parse_model_from_stream(stream, syntax_only=False):
468
452
  var.set_rhs(convert_proto_expression(var._proto_rhs, var, info))
469
453
  del var._proto_rhs
470
454
 
455
+ # Resolve variable references in initial values
456
+ for i, var in enumerate(model.states()):
457
+ e = info.initial_values[var.qname()]
458
+ expr = convert_proto_expression(e, model, info)
459
+ var.set_initial_value(expr)
460
+ del info.initial_values[var.qname()]
461
+
462
+ # All initial variables must have been used
463
+ for qname, e in info.initial_values.items():
464
+ t = info.initial_value_tokens[qname]
465
+ raise ParseError(
466
+ 'Unused initial value', t[2], t[3],
467
+ 'An unused initial value was found for "' + qname + '".')
468
+
469
+ # Re-order the model state
470
+ model.reorder_state(state_order)
471
+
471
472
  # Check the semantics of the model
472
473
  try:
473
474
  model.validate()
@@ -675,14 +676,7 @@ def parse_variable(stream, info, parent):
675
676
  raise ParseError(
676
677
  'Missing initial value', line, char,
677
678
  'No initial value was found for "' + var.qname() + '"')
678
- state_value = info.initial_values[var.qname()]
679
- try:
680
- var.promote(state_value)
681
- except myokit.NonLiteralValueError as e:
682
- t = state_value._token
683
- raise ParseError(
684
- 'Illegal state value', t[2], t[3], str(e), cause=e)
685
- del info.initial_values[var.qname()]
679
+ var.promote()
686
680
 
687
681
  # Parse definition, quick unit, bind, label and description syntax
688
682
  # These token must occur in a fixed order!
@@ -1069,7 +1063,7 @@ def strip_expression_units(model_text, skip_literals=True):
1069
1063
  This method will raise a :class:`myokit.ParseError` if the given code
1070
1064
  cannot be parsed to a valid model.
1071
1065
  """
1072
- if isinstance(model_text, basestring):
1066
+ if isinstance(model_text, str):
1073
1067
  lines = model_text.splitlines()
1074
1068
  else:
1075
1069
  lines = model_text
@@ -1265,7 +1259,7 @@ _COMPEQ = '=!><'
1265
1259
  _COMPEQ_MAP = [EQEQUAL, NOTEQUAL, MOREEQUAL, LESSEQUAL]
1266
1260
 
1267
1261
 
1268
- class Tokenizer(object):
1262
+ class Tokenizer:
1269
1263
  """
1270
1264
  Takes a stream of lines as input and provides a stream interface returning
1271
1265
  tokens.
@@ -1310,7 +1304,7 @@ class Tokenizer(object):
1310
1304
  self._catcheri = 0
1311
1305
 
1312
1306
  # String given instead of stream of lines? Convert
1313
- if isinstance(stream_of_lines, basestring):
1307
+ if isinstance(stream_of_lines, str):
1314
1308
  stream_of_lines = iter(stream_of_lines.splitlines())
1315
1309
 
1316
1310
  # Create tokenizer
@@ -1963,7 +1957,7 @@ def format_parse_error(ex, source=None):
1963
1957
  line = None
1964
1958
 
1965
1959
  if ex.line > 0 and source is not None:
1966
- if isinstance(source, basestring) and os.path.isfile(source):
1960
+ if isinstance(source, str) and os.path.isfile(source):
1967
1961
  # Re-open file, find line
1968
1962
  with open(source, 'r') as f:
1969
1963
  for i in range(0, ex.line):
@@ -2013,7 +2007,7 @@ def format_parse_error(ex, source=None):
2013
2007
  return '\n'.join(out)
2014
2008
 
2015
2009
 
2016
- class NudParser(object):
2010
+ class NudParser:
2017
2011
  """
2018
2012
  Expression parser for nud operators.
2019
2013
 
@@ -2082,7 +2076,7 @@ class FunctionParser(NudParser):
2082
2076
  Parser for function calls.
2083
2077
  """
2084
2078
  def __init__(self):
2085
- super(FunctionParser, self).__init__()
2079
+ super().__init__()
2086
2080
  self._rbp = myokit.Function._rbp
2087
2081
 
2088
2082
  def parse(self, stream, info):
@@ -2123,7 +2117,7 @@ class FunctionParser(NudParser):
2123
2117
  return (func, ops, (name,))
2124
2118
 
2125
2119
 
2126
- class LedParser(object):
2120
+ class LedParser:
2127
2121
  """
2128
2122
  Expression parser for led operators.
2129
2123
 
myokit/_progress.py CHANGED
@@ -4,15 +4,12 @@
4
4
  # This file is part of Myokit.
5
5
  # See http://myokit.org for copyright, sharing, and licensing details.
6
6
  #
7
- from __future__ import absolute_import, division
8
- from __future__ import print_function, unicode_literals
9
-
10
7
  import timeit
11
8
 
12
9
  import myokit
13
10
 
14
11
 
15
- class ProgressReporter(object):
12
+ class ProgressReporter:
16
13
  """
17
14
  Interface for progress updates in Simulations. Also allows some job types
18
15
  to be cancelled by the user.
@@ -21,7 +18,7 @@ class ProgressReporter(object):
21
18
  pass in an object implementing this interface. The simulation will use this
22
19
  object to report on its progress.
23
20
 
24
- Note that progress reporters should be re-usable, but the behaviour when
21
+ Note that progress reporters should be re-usable, but the behavior when
25
22
  making calls to a reporter from two different processes (either through
26
23
  multi-threading/multi-processing or jobs nested within jobs) is undefined.
27
24
 
@@ -62,7 +59,7 @@ class ProgressReporter(object):
62
59
  """
63
60
  pass
64
61
 
65
- class _Job(object):
62
+ class _Job:
66
63
  def __init__(self, parent, msg):
67
64
  self._parent = parent
68
65
  self._msg = msg
@@ -97,7 +94,7 @@ class ProgressPrinter(ProgressReporter):
97
94
  status every ten percent.
98
95
  """
99
96
  def __init__(self, digits=1):
100
- super(ProgressPrinter, self).__init__()
97
+ super().__init__()
101
98
  self._b = myokit.tools.Benchmarker()
102
99
  self._f = None
103
100
  self._d = int(digits)
@@ -138,7 +135,7 @@ class Timeout(ProgressReporter):
138
135
  Progress reporter that cancels a simulation after ``timeout`` seconds.
139
136
  """
140
137
  def __init__(self, timeout):
141
- super(Timeout, self).__init__()
138
+ super().__init__()
142
139
  self._timeout = float(timeout)
143
140
 
144
141
  def enter(self, msg=None):
myokit/_protocol.py CHANGED
@@ -1,18 +1,17 @@
1
1
  #
2
- # Defines the python classes that represent a pacing protocol.
2
+ # Defines the python classes that represent pacing protocols.
3
3
  #
4
4
  # This file is part of Myokit.
5
5
  # See http://myokit.org for copyright, sharing, and licensing details.
6
6
  #
7
- from __future__ import absolute_import, division
8
- from __future__ import print_function, unicode_literals
9
-
10
7
  import myokit
11
8
 
12
9
  import numpy as np
13
10
 
11
+ from bisect import bisect_right
12
+
14
13
 
15
- class Protocol(object):
14
+ class Protocol:
16
15
  """
17
16
  Represents a pacing protocol as a sequence of :class:`events
18
17
  <ProtocolEvent>`.
@@ -43,11 +42,11 @@ class Protocol(object):
43
42
  is not.
44
43
 
45
44
  Protocols can be compared with ``==``, which will check if the :meth:`code`
46
- for both protocols is the same. Protocols can be serialised with
45
+ for both protocols is the same. Protocols can be serialized with
47
46
  ``pickle``.
48
47
  """
49
48
  def __init__(self):
50
- super(Protocol, self).__init__()
49
+ super().__init__()
51
50
  self._head = None
52
51
 
53
52
  def add(self, e):
@@ -534,7 +533,7 @@ class Protocol(object):
534
533
  return [p.advance(t) for t in times]
535
534
 
536
535
 
537
- class ProtocolEvent(object):
536
+ class ProtocolEvent:
538
537
  """
539
538
  Describes an event occurring as part of a protocol.
540
539
  """
@@ -717,7 +716,7 @@ class ProtocolEvent(object):
717
716
  return self.in_words()
718
717
 
719
718
 
720
- class PacingSystem(object):
719
+ class PacingSystem:
721
720
  """
722
721
  This class uses a :class:`myokit.Protocol` to update the value of a
723
722
  pacing variable over time.
@@ -850,3 +849,94 @@ class NotAnUnbrokenSequenceError(myokit.MyokitError):
850
849
  """ Error raised exclusively by is_unbroken_sequence_exception(). """
851
850
  pass
852
851
 
852
+
853
+ class TimeSeriesProtocol:
854
+ """
855
+ Represents a pacing protocol as a sequence of time value pairs and an
856
+ interpolation method (currently only linear interpolation is supported).
857
+
858
+ A 1D time-series should be given as input. During the simulation, the value
859
+ of the pacing variable will be determined by interpolating between the two
860
+ nearest points in the series. If the simulation time is outside the bounds
861
+ of the time-series, the first or last value in the series will be used.
862
+
863
+ Protocols can be compared with ``==``, which will check if the sequence of
864
+ time value pairs is the same, and the interpolation method is the same.
865
+ Protocols can be serialized with ``pickle``.
866
+
867
+ """
868
+
869
+ def __init__(self, times, values, method=None):
870
+ super().__init__()
871
+
872
+ if len(times) != len(values):
873
+ raise ValueError('Times and values array must have same size.')
874
+ times_tpl, values_tpl = zip(*[
875
+ (float(t), float(v)) for t, v in sorted(zip(times, values))
876
+ ])
877
+ self._times = list(times_tpl)
878
+ self._values = list(values_tpl)
879
+
880
+ if method is None:
881
+ self._method = 'linear'
882
+ else:
883
+ self._method = str(method).lower()
884
+ if self._method not in ('linear', ):
885
+ raise ValueError(
886
+ 'Unknown interpolation method: ' + self._method)
887
+
888
+ def __eq__(self, other):
889
+ if self is other:
890
+ return True
891
+ if not isinstance(other, TimeSeriesProtocol):
892
+ return False
893
+ return (
894
+ self._method == other._method
895
+ and self._values == other._values
896
+ and self._times == other._times
897
+ )
898
+
899
+ def __getstate__(self):
900
+ return {
901
+ 'times': self._times,
902
+ 'values': self._values,
903
+ 'method': self._method,
904
+ }
905
+
906
+ def __setstate__(self, values):
907
+ self._times = values['times']
908
+ self._values = values['values']
909
+ self._method = values['method']
910
+
911
+ def clone(self):
912
+ """
913
+ Returns a clone of this protocol.
914
+ """
915
+ return TimeSeriesProtocol(self._times, self._values, self._method)
916
+
917
+ def pace(self, t):
918
+ """
919
+ Returns the value of the pacing variable at time ``t``.
920
+ """
921
+ if t < self._times[0]:
922
+ return self._values[0]
923
+ if t > self._times[-1]:
924
+ return self._values[-1]
925
+ i = bisect_right(self._times, t) - 1
926
+ if i == len(self._times) - 1:
927
+ return self._values[i]
928
+ return self._values[i] + (t - self._times[i]) * (
929
+ self._values[i + 1] - self._values[i]
930
+ ) / (self._times[i + 1] - self._times[i])
931
+
932
+ def times(self):
933
+ """
934
+ Returns a list of the times in this protocol.
935
+ """
936
+ return self._times
937
+
938
+ def values(self):
939
+ """
940
+ Returns a list of the values in this protocol.
941
+ """
942
+ return self._values
myokit/_sim/__init__.py CHANGED
@@ -5,10 +5,6 @@
5
5
  # This file is part of Myokit.
6
6
  # See http://myokit.org for copyright, sharing, and licensing details.
7
7
  #
8
- from __future__ import absolute_import, division
9
- from __future__ import print_function, unicode_literals
10
-
11
- # Library imports
12
8
  import os
13
9
  import platform
14
10
  import sys
@@ -36,36 +32,23 @@ if platform.system() == 'Windows': # pragma: no linux cover
36
32
  ).stdin
37
33
  os.popen = _ospop
38
34
 
39
-
40
- # Setuptools imports
41
35
  from setuptools import setup, Extension # noqa
42
36
 
43
-
44
- # Myokit imports
45
37
  import myokit # noqa
46
38
  import myokit.pype # noqa
47
39
 
48
40
 
49
- # Dynamic module finding and loading in Python 3.5+ and younger
50
- if sys.hexversion >= 0x03050000:
51
- import importlib.machinery
41
+ def load_module(name, path):
42
+ """ Dynamically find and load a module (Python 3.5+). """
52
43
  import importlib
44
+ import importlib.machinery
53
45
 
54
- def load_module(name, path):
55
- spec = importlib.machinery.PathFinder.find_spec(name, [path])
56
- module = importlib.util.module_from_spec(spec)
57
- return module
58
-
59
- else: # pragma: no python 3 cover
60
- import imp
61
-
62
- def load_module(name, path):
63
- (f, pathname, description) = imp.find_module(name, [path])
64
- f.close()
65
- return imp.load_dynamic(name, pathname)
46
+ spec = importlib.machinery.PathFinder.find_spec(name, [path])
47
+ module = importlib.util.module_from_spec(spec)
48
+ return module
66
49
 
67
50
 
68
- class CModule(object):
51
+ class CModule:
69
52
  """
70
53
  Abstract base class for classes that dynamically create and compile a
71
54
  back-end C-module.
myokit/_sim/cable.c CHANGED
@@ -26,11 +26,11 @@ model.create_unique_names()
26
26
  w = ansic.AnsiCExpressionWriter()
27
27
 
28
28
  # Process bindings, remove unsupported bindings.
29
- bound_variables = model.prepare_bindings({
29
+ bound_variables = myokit._prepare_bindings(model, {
30
30
  'time' : 'engine_time',
31
31
  'pace' : 'engine_pace',
32
32
  'diffusion_current' : 'diffusion_current',
33
- })
33
+ })
34
34
 
35
35
  # Define var/lhs function
36
36
  def v(var, pre='cell->'):
@@ -279,7 +279,7 @@ py_sim_clean()
279
279
  }
280
280
 
281
281
  /*
282
- * Initialise a run
282
+ * Initialize a run
283
283
  */
284
284
  static PyObject*
285
285
  sim_init(PyObject *self, PyObject *args)
@@ -367,9 +367,7 @@ sim_init(PyObject *self, PyObject *args)
367
367
  for(i_state=0; i_state<ncells * N_STATE; i_state++) {
368
368
  flt = PyList_GetItem(state_in, i_state); /* Borrowed reference */
369
369
  if (!PyFloat_Check(flt)) {
370
- char errstr[200];
371
- sprintf(errstr, "Item %d in state vector is not a float.", i_state);
372
- PyErr_SetString(PyExc_Exception, errstr);
370
+ PyErr_Format(PyExc_Exception, "Item %d in state vector is not a float.", i_state);
373
371
  return sim_clean();
374
372
  }
375
373
  }
@@ -441,7 +439,7 @@ for label, eqs in equations.items():
441
439
  /* Initial values */
442
440
  <?
443
441
  for var in model.states():
444
- print(tab*2 + v(var) + ' = PyFloat_AsDouble(PyList_GetItem(state_in, icell * N_STATE + ' + str(var.indice()) + '));')
442
+ print(tab*2 + v(var) + ' = PyFloat_AsDouble(PyList_GetItem(state_in, icell * N_STATE + ' + str(var.index()) + '));')
445
443
  ?>
446
444
  /* Zeros for pacing and diffusion current */
447
445
  <?
@@ -581,7 +579,7 @@ for var in model.states():
581
579
  for(icell=0; icell<ncells; icell++) {
582
580
  <?
583
581
  for var in model.states():
584
- print(tab*2 + 'PyList_SetItem(state_out, icell * N_STATE + ' + str(var.indice()) + ', PyFloat_FromDouble(' + v(var) + '));')
582
+ print(tab*2 + 'PyList_SetItem(state_out, icell * N_STATE + ' + str(var.index()) + ', PyFloat_FromDouble(' + v(var) + '));')
585
583
  ?>
586
584
  cell++;
587
585
  }
myokit/_sim/cable.py CHANGED
@@ -4,13 +4,11 @@
4
4
  # This file is part of Myokit.
5
5
  # See http://myokit.org for copyright, sharing, and licensing details.
6
6
  #
7
- from __future__ import absolute_import, division
8
- from __future__ import print_function, unicode_literals
9
-
10
7
  import os
11
- import myokit
12
8
  import platform
13
9
 
10
+ import myokit
11
+
14
12
  # Location of source file
15
13
  SOURCE_FILE = 'cable.c'
16
14
 
@@ -39,7 +37,7 @@ class Simulation1d(myokit.CModule):
39
37
  ``pace``
40
38
  The pacing level, this is set if a protocol was passed in.
41
39
  ``diffusion_current``
42
- The current flowing from the cell to its neighbours. This will be
40
+ The current flowing from the cell to its neighbors. This will be
43
41
  positive when the cell is acting as a source, negative when it is
44
42
  acting as a sink.
45
43
 
@@ -77,7 +75,7 @@ class Simulation1d(myokit.CModule):
77
75
 
78
76
  i = sum[g * (V - V_j)]
79
77
 
80
- Where the sum is taken over all neighbouring cells j (see [1]).
78
+ Where the sum is taken over all neighboring cells j (see [1]).
81
79
 
82
80
  The resulting ODE system is solved using a forward Euler (FE) method with
83
81
  fixed step sizes. Smaller step sizes lead to more accurate results, and it
@@ -101,7 +99,7 @@ class Simulation1d(myokit.CModule):
101
99
  _index = 0 # Unique id for generated module
102
100
 
103
101
  def __init__(self, model, protocol=None, ncells=50, rl=False):
104
- super(Simulation1d, self).__init__()
102
+ super().__init__()
105
103
 
106
104
  # Require a valid model
107
105
  model.validate()
@@ -176,7 +174,7 @@ class Simulation1d(myokit.CModule):
176
174
  ' next')
177
175
 
178
176
  # Set state and default state
179
- self._state = self._model.state() * ncells
177
+ self._state = self._model.initial_values(True) * ncells
180
178
  self._default_state = list(self._state)
181
179
 
182
180
  # Unique simulation id