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/_model_api.py CHANGED
@@ -4,25 +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
- from collections import OrderedDict
7
+ import io
11
8
  import math
12
9
  import re
13
- import myokit
14
10
 
15
- # StringIO in Python 2 and 3
16
- try:
17
- from cStringIO import StringIO
18
- except ImportError:
19
- from io import StringIO
11
+ from collections import OrderedDict
12
+
13
+ import myokit
20
14
 
21
- # Strings in Python 2 and 3
22
- try:
23
- basestring
24
- except NameError: # pragma: no cover
25
- basestring = str
26
15
 
27
16
  TAB = ' ' * 4
28
17
  NAME = re.compile(r'^[a-zA-Z]\w*$')
@@ -34,8 +23,6 @@ def check_name(name):
34
23
  Tests if the given name is a valid myokit name and raises a
35
24
  :class:`myokit.InvalidNameError` if it isn't.
36
25
  """
37
- # Note: Names are stored as str (so unicode in Python3)
38
- # But the regex restriction means their format is compatible with ascii.
39
26
  # Check str compatibility
40
27
  name = str(name)
41
28
 
@@ -65,7 +52,7 @@ class MetaDataContainer(dict):
65
52
  raise myokit.InvalidMetaDataNameError(
66
53
  'The key <' + str(key) + '>'
67
54
  ' is not a valid meta-data property identifier.')
68
- return super(MetaDataContainer, self).__getitem__(key)
55
+ return super().__getitem__(key)
69
56
 
70
57
  def __setitem__(self, key, item):
71
58
  # Check item
@@ -75,10 +62,10 @@ class MetaDataContainer(dict):
75
62
  raise myokit.InvalidMetaDataNameError(
76
63
  'The key <' + str(key) + '>'
77
64
  ' is not a valid meta-data property identifier.')
78
- super(MetaDataContainer, self).__setitem__(key, item)
65
+ super().__setitem__(key, item)
79
66
 
80
67
 
81
- class ObjectWithMeta(object):
68
+ class ObjectWithMeta:
82
69
  """
83
70
  Base class for objects with meta data.
84
71
 
@@ -87,7 +74,7 @@ class ObjectWithMeta(object):
87
74
  """
88
75
 
89
76
  def __init__(self):
90
- super(ObjectWithMeta, self).__init__()
77
+ super().__init__()
91
78
  self.meta = MetaDataContainer()
92
79
 
93
80
  def _clone_metadata(self, clone):
@@ -135,7 +122,7 @@ class ModelPart(ObjectWithMeta):
135
122
  The given parent should be a ModelPart or None. The name should be
136
123
  unique within the set of children for the given parent.
137
124
  """
138
- super(ModelPart, self).__init__()
125
+ super().__init__()
139
126
  self._parent = parent # This object's parent
140
127
  self._model = None # The model this object belongs to
141
128
  self._name = str(name) # Local name
@@ -153,7 +140,7 @@ class ModelPart(ObjectWithMeta):
153
140
  """
154
141
  Returns this object in ``mmt`` syntax.
155
142
  """
156
- b = StringIO()
143
+ b = io.StringIO()
157
144
  self._code(b, 0)
158
145
  return b.getvalue()
159
146
 
@@ -162,7 +149,7 @@ class ModelPart(ObjectWithMeta):
162
149
  Internal version of _code(), to be implemented by all subclasses.
163
150
 
164
151
  The argument ``t`` specifies the number of tabs to indent the code
165
- with. The argument ``b`` is a cStringIO buffer.
152
+ with. The argument ``b`` is a StringIO buffer.
166
153
  """
167
154
  raise NotImplementedError
168
155
 
@@ -249,7 +236,7 @@ class ModelPart(ObjectWithMeta):
249
236
  return self._uname
250
237
 
251
238
 
252
- class VarProvider(object):
239
+ class VarProvider:
253
240
  """
254
241
  *Abstract class*
255
242
 
@@ -463,7 +450,7 @@ class VarOwner(ModelPart, VarProvider):
463
450
  """
464
451
 
465
452
  def __init__(self, parent, name):
466
- super(VarOwner, self).__init__(parent, name)
453
+ super().__init__(parent, name)
467
454
  self._variables = {}
468
455
  # Set component
469
456
  self._component = self
@@ -720,9 +707,7 @@ class VarOwner(ModelPart, VarProvider):
720
707
  """ See :meth:`VarProvider._resolve(). """
721
708
  def sa(name):
722
709
  # Suggest alternative
723
- m = self.model()
724
- (var, sug, msg) = m.suggest_variable(name)
725
- return msg
710
+ return self.model().suggest_variable(name)[2]
726
711
 
727
712
  # Try resolving as an alias
728
713
  try:
@@ -772,7 +757,7 @@ class Model(ObjectWithMeta, VarProvider):
772
757
  Variables stored inside components can be accessed using :meth:`get()` or
773
758
  :meth:`values()`. Values defined through their derivative make up the
774
759
  model state and can be accessed using :meth:`states()`. States have
775
- initial values accessible through :meth:`inits()`.
760
+ initial values accessible through :meth:`initial_values()`.
776
761
 
777
762
  A model's validity can be checked using :meth:`is_valid()`, which returns
778
763
  the latest validation status and :meth:`validate()`, which (re)validates
@@ -787,20 +772,20 @@ class Model(ObjectWithMeta, VarProvider):
787
772
  For consistency with components, variables, and expressions, models cannot
788
773
  be compared with ``==`` (which will only return ``True`` if both operands
789
774
  are the same object). Checking if models are the same in other senses can
790
- be done with :meth:`is_similar`. Models can be serialised with ``pickle``.
775
+ be done with :meth:`is_similar`. Models can be serialized with ``pickle``.
791
776
  """
792
777
 
793
778
  def __init__(self, name=None):
794
- super(Model, self).__init__()
779
+ super().__init__()
795
780
 
796
781
  # A dictionary of components
797
782
  self._components = {}
798
783
 
799
784
  # The model's state variables
800
- self._state = []
785
+ self._state_vars = []
801
786
 
802
- # The model's current state (list of floats)
803
- self._current_state = []
787
+ # The model's initial state, as a list of Expressions
788
+ self._state_init = []
804
789
 
805
790
  # A dict mapping binding names to variables
806
791
  self._bindings = {}
@@ -1087,9 +1072,11 @@ class Model(ObjectWithMeta, VarProvider):
1087
1072
  for c in self._components.values():
1088
1073
  c._clone1(clone)
1089
1074
 
1090
- # Clone state
1091
- for k, v in enumerate(self._state):
1092
- clone.get(v.qname()).promote(self._current_state[k])
1075
+ # Create states
1076
+ # Note that the order in which promote() is called determines the
1077
+ # state ordering, so this happens here and not in the Variable class.
1078
+ for k, v in enumerate(self._state_vars):
1079
+ clone.get(v.qname()).promote()
1093
1080
 
1094
1081
  # Create mapping of old var references to new references
1095
1082
  var_map = {}
@@ -1110,6 +1097,11 @@ class Model(ObjectWithMeta, VarProvider):
1110
1097
  for prefix, prepend in self._reserved_uname_prefixes.items():
1111
1098
  clone.reserve_unique_name_prefix(prefix, prepend)
1112
1099
 
1100
+ # Copy initial state expressions
1101
+ for k, v in enumerate(self._state_vars):
1102
+ clone.get(v.qname()).set_initial_value(
1103
+ self._state_init[k].clone(subst=lhs_map))
1104
+
1113
1105
  # Return
1114
1106
  return clone
1115
1107
 
@@ -1119,7 +1111,7 @@ class Model(ObjectWithMeta, VarProvider):
1119
1111
 
1120
1112
  Line numbers can be added by setting ``line_numbers=True``.
1121
1113
  """
1122
- b = StringIO()
1114
+ b = io.StringIO()
1123
1115
  b.write('[[model]]\n')
1124
1116
  self._code(b, 0)
1125
1117
  if line_numbers:
@@ -1141,17 +1133,15 @@ class Model(ObjectWithMeta, VarProvider):
1141
1133
  self._code_meta(b, 0)
1142
1134
 
1143
1135
  # Initial state
1144
- if self._state:
1136
+ if self._state_vars:
1145
1137
  pre = t * TAB
1146
1138
  b.write(pre + '# Initial values\n')
1147
- names = [eq.lhs.code() for eq in self.inits()]
1139
+ names = [v.qname() for v in self._state_vars]
1140
+ values = [e.code() for e in self._state_init]
1148
1141
  n = max([len(name) for name in names])
1149
- names = iter(names)
1150
- for eq in self.inits():
1151
- name = next(names)
1152
- b.write(
1153
- pre + name + ' ' * (n - len(name)) + ' = ' + eq.rhs.code()
1154
- + '\n')
1142
+ for name, value in zip(names, values):
1143
+ b.write(pre + name + ' ' * (n - len(name)) + ' = '
1144
+ + value + '\n')
1155
1145
  b.write(pre + '\n')
1156
1146
  else:
1157
1147
  # No initial state? Then add newline
@@ -1243,7 +1233,7 @@ class Model(ObjectWithMeta, VarProvider):
1243
1233
  """
1244
1234
  Returns the number of state variables in this model.
1245
1235
  """
1246
- return len(self._state)
1236
+ return len(self._state_vars)
1247
1237
 
1248
1238
  def create_unique_names(self):
1249
1239
  """
@@ -1357,7 +1347,7 @@ class Model(ObjectWithMeta, VarProvider):
1357
1347
  # Insert new state (if required)
1358
1348
  if state is not None:
1359
1349
  new_state = self.map_to_state(state)
1360
- for state, value in zip(self._state, new_state):
1350
+ for state, value in zip(self._state_vars, new_state):
1361
1351
  values[myokit.Name(state)] = value
1362
1352
  state = None
1363
1353
 
@@ -1390,7 +1380,7 @@ class Model(ObjectWithMeta, VarProvider):
1390
1380
  values[eq.lhs] = eq.rhs.eval(values, precision=precision)
1391
1381
 
1392
1382
  # Return calculated state
1393
- return [values[state.lhs()] for state in self._state]
1383
+ return [values[state.lhs()] for state in self._state_vars]
1394
1384
 
1395
1385
  def eval_state_derivatives(
1396
1386
  self, state=None, inputs=None, precision=myokit.DOUBLE_PRECISION,
@@ -1489,14 +1479,14 @@ class Model(ObjectWithMeta, VarProvider):
1489
1479
  def format_state(self, state=None, state2=None,
1490
1480
  precision=myokit.DOUBLE_PRECISION):
1491
1481
  """
1492
- Converts the given list of floating point numbers to a string where
1493
- each line has the format ``<full_qualified_name> = <float_value>``.
1482
+ Converts a sequence of floating point numbers to a string where each
1483
+ line has the format ``<full_qualified_name> = <float_value>``.
1494
1484
 
1495
1485
  Arguments:
1496
1486
 
1497
1487
  ``state=None``
1498
- The state to show derivatives for. If no state is given the state
1499
- returned by :meth:`state` is used.
1488
+ The state to display. If no state is given this model's (evaluated)
1489
+ :meth:`<initial_values()>initial values` are used.
1500
1490
  ``state2=None``
1501
1491
  An optional second state, to be shown next to ``state`` for
1502
1492
  comparison.
@@ -1505,19 +1495,19 @@ class Model(ObjectWithMeta, VarProvider):
1505
1495
  :meth:`myokit.float.str` when formatting the state values.
1506
1496
 
1507
1497
  """
1508
- n = len(self._state)
1509
- if state is not None:
1498
+ n = len(self._state_vars)
1499
+ if state is None:
1500
+ state = self.initial_values(as_floats=True)
1501
+ else:
1510
1502
  if len(state) != n:
1511
1503
  raise ValueError(
1512
- 'Argument `state` must be a list of (' + str(n)
1504
+ 'Argument `state` must be a sequence of (' + str(n)
1513
1505
  + ') floating point numbers.')
1514
- else:
1515
- state = self.state()
1516
1506
 
1517
1507
  if state2 is not None:
1518
1508
  if len(state2) != n:
1519
1509
  raise ValueError(
1520
- 'Argument `state2` must be a list of (' + str(n)
1510
+ 'Argument `state2` must be a sequence of (' + str(n)
1521
1511
  + ') floating point numbers.')
1522
1512
 
1523
1513
  out = []
@@ -1541,35 +1531,35 @@ class Model(ObjectWithMeta, VarProvider):
1541
1531
  Like :meth:`format_state` but displays the derivatives along with
1542
1532
  each state's value.
1543
1533
 
1544
-
1545
1534
  Arguments:
1546
1535
 
1547
1536
  ``state=None``
1548
- The state to display. If no state is given the state returned by
1549
- :meth:`state` is used.
1537
+ The state to show derivatives for. If no state is given this
1538
+ model's (evaluated) :meth:`<initial_values()>initial values` are
1539
+ used.
1550
1540
  ``derivatives=None``
1551
- An optional list of evaluated derivatives. If not given, the values
1552
- will be calculed from ``state`` using :meth:`eval_derivatives()`.
1541
+ An optional list or other sequence of evaluated derivatives. If not
1542
+ given, the values will be calculed from ``state`` using
1543
+ :meth:`eval_derivatives()`.
1553
1544
  ``precision=myokit.DOUBLE_PRECISION``
1554
1545
  An optional precision argument to use when evaluating the state
1555
1546
  derivatives, and to pass into :meth:`myokit.float.str` when
1556
1547
  formatting the state values and derivatives.
1557
1548
 
1558
1549
  """
1559
- n = len(self._state)
1550
+ n = len(self._state_vars)
1560
1551
  if state is None:
1561
- state = self.state()
1552
+ state = self.initial_values(as_floats=True)
1562
1553
  elif len(state) != n:
1563
1554
  raise ValueError(
1564
- 'Argument `state` must be a list of (' + str(n)
1555
+ 'Argument `state` must be a sequence of (' + str(n)
1565
1556
  + ') floating point numbers.')
1566
1557
 
1567
1558
  if derivatives is None:
1568
- derivatives = self.evaluate_derivatives(
1569
- state, precision=precision)
1559
+ derivatives = self.evaluate_derivatives(state, precision=precision)
1570
1560
  elif len(derivatives) != n:
1571
1561
  raise ValueError(
1572
- 'Argument `deriv` must be a list of (' + str(n)
1562
+ 'Argument `derivatives` must be a sequence of (' + str(n)
1573
1563
  + ') floating point numbers.')
1574
1564
 
1575
1565
  out = []
@@ -1746,7 +1736,7 @@ class Model(ObjectWithMeta, VarProvider):
1746
1736
  'as external_component, or a string if only one '
1747
1737
  'component is provided'
1748
1738
  )
1749
- if isinstance(new_name, basestring):
1739
+ if isinstance(new_name, str):
1750
1740
  if len(external_component) != 1:
1751
1741
  raise TypeError(new_name_error_str)
1752
1742
  new_name = [new_name]
@@ -1754,7 +1744,7 @@ class Model(ObjectWithMeta, VarProvider):
1754
1744
  try:
1755
1745
  ok = (
1756
1746
  len(new_name) == len(external_component) and
1757
- all(isinstance(name, basestring) for name in new_name)
1747
+ all(isinstance(name, str) for name in new_name)
1758
1748
  )
1759
1749
  if not ok:
1760
1750
  raise TypeError(new_name_error_str)
@@ -1802,6 +1792,9 @@ class Model(ObjectWithMeta, VarProvider):
1802
1792
  for var in comp.variables():
1803
1793
  vars_ref.update(var.refs_to(state_refs=False))
1804
1794
  vars_ref.update(var.refs_to(state_refs=True))
1795
+ if var.is_state():
1796
+ vars_ref.update(
1797
+ [e.var() for e in var.initial_value().references()])
1805
1798
  vars_ref.update(comp._alias_map.values())
1806
1799
  vars_ref -= set(comp.variables())
1807
1800
  vars_ref = [x for x in vars_ref if not x.is_nested()]
@@ -1830,7 +1823,7 @@ class Model(ObjectWithMeta, VarProvider):
1830
1823
  'The variable <' + self_var.qname() + '> in the'
1831
1824
  ' given var_map\'s values is not part of this'
1832
1825
  ' model.')
1833
- elif isinstance(self_var, basestring):
1826
+ elif isinstance(self_var, str):
1834
1827
  try:
1835
1828
  self_var = self.var(self_var)
1836
1829
  except KeyError:
@@ -1852,7 +1845,7 @@ class Model(ObjectWithMeta, VarProvider):
1852
1845
  'The variable <' + ext_var.qname() + '> in the'
1853
1846
  ' given var_map\'s keys but is not part of the'
1854
1847
  ' source model.')
1855
- elif isinstance(ext_var, basestring):
1848
+ elif isinstance(ext_var, str):
1856
1849
  try:
1857
1850
  ext_var = ext_model.var(ext_var)
1858
1851
  except KeyError:
@@ -1877,8 +1870,8 @@ class Model(ObjectWithMeta, VarProvider):
1877
1870
  if ext_var in vars_to_map:
1878
1871
  var_map[ext_var] = self_var
1879
1872
 
1880
- # add variables to var_map that map to other imported components
1881
- # but will be reassigned to the clone later
1873
+ # Add variables to var_map that map to other imported components but
1874
+ # will be reassigned to the clone later
1882
1875
  for l in map_to_clone:
1883
1876
  for ext_var in l:
1884
1877
  if ext_var not in var_map:
@@ -1963,19 +1956,29 @@ class Model(ObjectWithMeta, VarProvider):
1963
1956
  ' mismatch: ' + str(e))
1964
1957
 
1965
1958
  # Clone component pt 1: create, meta data, empty variables
1966
- new_component = []
1959
+ new_component = [] # List of components
1967
1960
  for i, comp in enumerate(external_component):
1968
1961
  new_component.append(comp._clone1(self, new_name[i]))
1969
1962
 
1970
- for var in comp.variables(state=True):
1971
- # Clone states
1972
- # TODO: Not sure why clone() code doesn't do this?
1973
- new_component[i].get(var.qname(comp)).promote(
1974
- var.state_value())
1975
1963
  # Now we can add variable to var_map if needed
1976
1964
  for var in map_to_clone[i]:
1977
1965
  var_map[var] = new_component[i].get(var.qname(comp))
1978
1966
 
1967
+ # Clone states, preserving the state order
1968
+ # Note: The order in which promote() is called determines the order
1969
+ # of the states in the new component. (This is one of the reasons that
1970
+ # the component._clone1 method called above doesn't call promote.)
1971
+ new_states = [] # New states, from all components
1972
+ state_map = {} # New-state to old state
1973
+ for old_comp, comp in zip(external_component, new_component):
1974
+ for ext_var in old_comp.variables(state=True):
1975
+ var = comp.get(ext_var.name())
1976
+ state_map[var] = ext_var
1977
+ new_states.append(var)
1978
+ new_states.sort(key=lambda var: state_map[var].index())
1979
+ for var in new_states:
1980
+ var.promote() # Initial value is set later
1981
+
1979
1982
  # Create mapping of old var references to new references
1980
1983
  # This is a mapping from Name(var) and Derivative(Name(var)) objects
1981
1984
  # to new myokit.Expressions referencing the target model's variables
@@ -1991,9 +1994,10 @@ class Model(ObjectWithMeta, VarProvider):
1991
1994
  lhs_map[myokit.Derivative(myokit.Name(ext_var))] = \
1992
1995
  myokit.Derivative(myokit.Name(self_var))
1993
1996
 
1994
- # Next, add all entries in the var_map. If unit conversion is enabled,
1995
- # this may include the addition of unit conversion factors
1996
-
1997
+ # Next, add all entries in the var_map to the lhs_map. If unit
1998
+ # conversion is enabled, this may include the addition of unit
1999
+ # conversion factors (so some Names in lhs_map will be mapped onto
2000
+ # Multiply expressions).
1997
2001
  for ext_var, self_var in var_map.items():
1998
2002
  # Substitute in either a reference to self_var, or an expression
1999
2003
  # that converts self_var to the units ext_var's equation expects.
@@ -2022,6 +2026,11 @@ class Model(ObjectWithMeta, VarProvider):
2022
2026
  for i, comp in enumerate(external_component):
2023
2027
  comp._clone2(new_component[i], lhs_map, var_map)
2024
2028
 
2029
+ # Clone initial values
2030
+ for var in new_states:
2031
+ var.set_initial_value(
2032
+ state_map[var].initial_value().clone(subst=lhs_map))
2033
+
2025
2034
  # Time unit conversion? Then update all derivatives.
2026
2035
  if time_factor is not None:
2027
2036
  for comp in new_component:
@@ -2031,15 +2040,32 @@ class Model(ObjectWithMeta, VarProvider):
2031
2040
  var.set_rhs(
2032
2041
  myokit.Multiply(rhs, myokit.Number(time_factor)))
2033
2042
 
2043
+ def initial_values(self, as_floats=False):
2044
+ """
2045
+ Returns a list of the model's initial values.
2046
+
2047
+ By default, expressions are returned, but this can be changed to
2048
+ a list of floats by setting ``as_floats=True``.
2049
+ """
2050
+ if as_floats:
2051
+ if any(not e.is_literal() for e in self._state_init):
2052
+ self.validate() # Check for cycles before evaluating
2053
+ return [float(y) for y in self._state_init]
2054
+ return list(self._state_init)
2055
+
2034
2056
  def inits(self):
2035
2057
  """
2036
- Returns an iterator over the ``Equation`` objects defining this model's
2037
- current state.
2058
+ Deprecated method: Returns an iterator over the ``Equation`` objects
2059
+ defining this model's initial values.
2038
2060
  """
2061
+ # Deprecated since 2023-02-22
2062
+ import warnings
2063
+ warnings.warn('The method `inits` is deprecated.')
2064
+
2039
2065
  def StateDefIterator(model):
2040
- for k, var in enumerate(model._state):
2041
- yield Equation(
2042
- myokit.Name(var), myokit.Number(self._current_state[k]))
2066
+ for var, value in zip(model._state_vars, model._state_init):
2067
+ yield Equation(myokit.Name(var), value)
2068
+
2043
2069
  return StateDefIterator(self)
2044
2070
 
2045
2071
  def is_similar(self, other, check_unames=False):
@@ -2134,10 +2160,13 @@ class Model(ObjectWithMeta, VarProvider):
2134
2160
 
2135
2161
  def load_state(self, filename):
2136
2162
  """
2137
- Sets the model state using data from a file formatted in any style
2138
- accepted by :func:`myokit.parse_state`.
2163
+ Deprecated method: Sets the model's initial values using data from a
2164
+ file formatted in any style accepted by :func:`myokit.map_to_state`.
2139
2165
  """
2140
- self.set_state(myokit.load_state(filename, self))
2166
+ # Deprecated since 2023-02-22
2167
+ import warnings
2168
+ warnings.warn('The method `load_state` is deprecated.')
2169
+ self.set_initial_values(myokit.load_state(filename, self))
2141
2170
 
2142
2171
  def map_component_dependencies(
2143
2172
  self, omit_states=True, omit_constants=False):
@@ -2160,7 +2189,7 @@ class Model(ObjectWithMeta, VarProvider):
2160
2189
  not ``[B,C]``.
2161
2190
 
2162
2191
  By default, dependencies on state variables' current values are
2163
- omitted. This behaviour can be changed by setting ``omit_states`` to
2192
+ omitted. This behavior can be changed by setting ``omit_states`` to
2164
2193
  ``False``.
2165
2194
 
2166
2195
  To omit all dependencies on constants, set ``omit_constants`` to
@@ -2340,7 +2369,7 @@ class Model(ObjectWithMeta, VarProvider):
2340
2369
  dependencies will be added to the dependency lists of their parents.
2341
2370
 
2342
2371
  By default, dependencies on state variables' current values are
2343
- omitted. This behaviour can be changed by setting ``omit_states`` to
2372
+ omitted. This behavior can be changed by setting ``omit_states`` to
2344
2373
  ``False``.
2345
2374
 
2346
2375
  In case of a dependency such as::
@@ -2484,7 +2513,7 @@ class Model(ObjectWithMeta, VarProvider):
2484
2513
  dependencies will be added to the dependency lists of their parents.
2485
2514
 
2486
2515
  By default, dependencies on state variables' current values are
2487
- omitted. This behaviour can be changed by setting ``omit_states`` to
2516
+ omitted. This behavior can be changed by setting ``omit_states`` to
2488
2517
  ``False``. Dependencies on constants are included by default, but this
2489
2518
  can be changed by setting ``omit_constants`` to ``True``.
2490
2519
  """
@@ -2555,7 +2584,7 @@ class Model(ObjectWithMeta, VarProvider):
2555
2584
 
2556
2585
  """
2557
2586
  n = self.count_states()
2558
- if isinstance(state, basestring):
2587
+ if isinstance(state, str):
2559
2588
  # String given. Parse into name:float map or list
2560
2589
  state = myokit.parse_state(state)
2561
2590
  if isinstance(state, dict):
@@ -2592,45 +2621,6 @@ class Model(ObjectWithMeta, VarProvider):
2592
2621
  except KeyError:
2593
2622
  return None
2594
2623
 
2595
- def prepare_bindings(self, labels):
2596
- """
2597
- Takes a mapping of binding labels to internal references as input and
2598
- returns a mapping of variables to internal references. All variables
2599
- appearing in the map will have their right hand side set to zero. All
2600
- bindings not mapped to any internal reference will be deleted.
2601
-
2602
- The argument ``mapping`` should take the form::
2603
-
2604
- labels = {
2605
- 'binding_label_1' : internal_name_1,
2606
- 'binding_label_2' : internal_name_2,
2607
- ...
2608
- }
2609
-
2610
- The returned dictionary will have the form::
2611
-
2612
- variables = {
2613
- variable_x : internal_name_1,
2614
- variable_y : internal_name_2,
2615
- ...
2616
- }
2617
-
2618
- Unsupported bindings (i.e. bindings not appearing in ``labels``) will
2619
- be ignored.
2620
- """
2621
- unused = []
2622
- variables = {}
2623
- for label, var in self._bindings.items():
2624
- try:
2625
- variables[var] = labels[label]
2626
- except KeyError:
2627
- unused.append(var)
2628
- continue
2629
- var.set_rhs(0)
2630
- for var in unused:
2631
- var.set_binding(None)
2632
- return variables
2633
-
2634
2624
  def __reduce__(self):
2635
2625
  """
2636
2626
  Pickles the model.
@@ -2701,7 +2691,7 @@ class Model(ObjectWithMeta, VarProvider):
2701
2691
 
2702
2692
  This method does not affect the model's validation status.
2703
2693
  """
2704
- n = len(self._state)
2694
+ n = len(self._state_vars)
2705
2695
  if len(order) != n:
2706
2696
  raise ValueError(
2707
2697
  'The given list must contain the same number of entries as'
@@ -2719,11 +2709,11 @@ class Model(ObjectWithMeta, VarProvider):
2719
2709
  'Duplicate entry in order specification: "'
2720
2710
  + str(v.qname()) + '".')
2721
2711
  state.append(v)
2722
- current.append(self._current_state[v._indice])
2723
- self._state = state
2724
- self._current_state = current
2712
+ current.append(self._state_init[v._index])
2713
+ self._state_vars = state
2714
+ self._state_init = current
2725
2715
  for k, v in enumerate(state):
2726
- v._indice = k
2716
+ v._index = k
2727
2717
 
2728
2718
  def remove_component(self, component):
2729
2719
  """
@@ -2771,7 +2761,7 @@ class Model(ObjectWithMeta, VarProvider):
2771
2761
  time_unit = time.unit()
2772
2762
 
2773
2763
  # Scan all states
2774
- for state in self._state:
2764
+ for state in self._state_vars:
2775
2765
 
2776
2766
  # Search for references to dot(state)
2777
2767
  refs = list(state.refs_by())
@@ -2842,8 +2832,8 @@ class Model(ObjectWithMeta, VarProvider):
2842
2832
  """
2843
2833
  Resets the indices of this model's state variables.
2844
2834
  """
2845
- for k, v in enumerate(self._state):
2846
- v._indice = k
2835
+ for k, v in enumerate(self._state_vars):
2836
+ v._index = k
2847
2837
 
2848
2838
  def _reset_validation(self):
2849
2839
  """
@@ -2853,7 +2843,11 @@ class Model(ObjectWithMeta, VarProvider):
2853
2843
 
2854
2844
  def _resolve(self, name):
2855
2845
  """ See :meth:`VarProvider._resolve(). """
2856
- return self.get(name)
2846
+ try:
2847
+ return self.get(name)
2848
+ except KeyError:
2849
+ raise myokit.UnresolvedReferenceError(
2850
+ name, self.suggest_variable(name)[2])
2857
2851
 
2858
2852
  def resolve_interdependent_components(self):
2859
2853
  """
@@ -2887,9 +2881,39 @@ class Model(ObjectWithMeta, VarProvider):
2887
2881
 
2888
2882
  def save_state(self, filename):
2889
2883
  """
2890
- Saves the model state to a file.
2884
+ Deprecated method: Saves the model state to a file (as floats).
2891
2885
  """
2892
- return myokit.save_state(filename, self.state(), self)
2886
+ # Deprecated since 2023-02-22
2887
+ import warnings
2888
+ warnings.warn('The method `save_state` is deprecated.')
2889
+
2890
+ return myokit.save_state(
2891
+ filename, self.initial_values(as_floats=True), self)
2892
+
2893
+ def set_initial_values(self, values):
2894
+ """
2895
+ Sets this model's initial values.
2896
+
2897
+ The ``values`` must be specified as either a list of floats,
2898
+ expressions, and/or strings; or as a dict or string in a format
2899
+ accepted by :meth:`map_to_state`.
2900
+ """
2901
+ # Use map to state?
2902
+ if isinstance(values, str) or isinstance(values, dict):
2903
+ self._state_init = [
2904
+ myokit.Number(x) for x in self.map_to_state(values)]
2905
+ elif len(values) != len(self._state_vars):
2906
+ raise ValueError('Wrong number of initial values, expecting '
2907
+ + str(len(self._state_vars)) + '.')
2908
+ else:
2909
+ # Parsing of arguments without making changes, in case it fails.
2910
+ expr = []
2911
+ for var, value in zip(self._state_vars, values):
2912
+ expr.append(var._set_initial_value(value, False))
2913
+
2914
+ # Set all at once, and reset validation status
2915
+ self._state_init = expr
2916
+ self._valid = None
2893
2917
 
2894
2918
  def set_name(self, name=None):
2895
2919
  """
@@ -2914,10 +2938,14 @@ class Model(ObjectWithMeta, VarProvider):
2914
2938
 
2915
2939
  def set_state(self, state):
2916
2940
  """
2917
- Changes this model's state. Accepts any type of input handled by
2918
- :meth:`map_to_state`.
2941
+ Deprecated method: use :meth:`set_initial_values` instead.
2919
2942
  """
2920
- self._current_state = self.map_to_state(state)
2943
+ # Deprecated since 2023-02-22
2944
+ import warnings
2945
+ warnings.warn(
2946
+ 'The method `set_state` is deprecated. Please use'
2947
+ ' `set_initial_values` instead.')
2948
+ self.set_initial_values(state)
2921
2949
 
2922
2950
  def set_value(self, qname, value):
2923
2951
  """
@@ -2938,6 +2966,9 @@ class Model(ObjectWithMeta, VarProvider):
2938
2966
  The variable's equation and value are displayed, along with the value
2939
2967
  and formula of any nested variables and the values of all dependencies.
2940
2968
  """
2969
+ # Model must be valid, or cycles can occur
2970
+ self.validate()
2971
+
2941
2972
  def format_float(number):
2942
2973
  s = str(number)
2943
2974
  if len(s) < 10:
@@ -2951,7 +2982,10 @@ class Model(ObjectWithMeta, VarProvider):
2951
2982
  # Add initial value
2952
2983
  rhs = var.rhs()
2953
2984
  if var.is_state():
2954
- out.append('Initial value = ' + str(var.state_value()))
2985
+ value = var.initial_value()
2986
+ out.append('Initial value = ' + value.code())
2987
+ if not isinstance(value, myokit.Number):
2988
+ out.append(' = ' + format_float(value))
2955
2989
  out.append(spacer)
2956
2990
  varname = var.lhs().code()
2957
2991
 
@@ -3237,17 +3271,22 @@ class Model(ObjectWithMeta, VarProvider):
3237
3271
 
3238
3272
  def state(self):
3239
3273
  """
3240
- Returns the current state of the model as a list of floating point
3241
- numbers.
3274
+ Deprecated method, use
3275
+ :meth:`initial_values(as_floats=True)<initial_values>` instead.
3242
3276
  """
3243
- return list(self._current_state)
3277
+ # Deprecated since 2023-02-22
3278
+ import warnings
3279
+ warnings.warn(
3280
+ 'The method `state` is deprecated. Please use'
3281
+ ' `initial_values(as_floats=True)` instead.')
3282
+ return self.initial_values(as_floats=True)
3244
3283
 
3245
3284
  def states(self):
3246
3285
  """
3247
3286
  Returns an iterator over this model's state :class:`variable
3248
3287
  <myokit.Variable>` objects.
3249
3288
  """
3250
- return iter(self._state)
3289
+ return iter(self._state_vars)
3251
3290
 
3252
3291
  def suggest_variable(self, name):
3253
3292
  """
@@ -3328,10 +3367,7 @@ class Model(ObjectWithMeta, VarProvider):
3328
3367
  "time". For a valid model, this method always returns a unique
3329
3368
  variable. If no time variable has been declared ``None`` is returned.
3330
3369
  """
3331
- try:
3332
- return self._bindings['time']
3333
- except KeyError:
3334
- return None
3370
+ return self._bindings.get('time')
3335
3371
 
3336
3372
  def timex(self):
3337
3373
  """
@@ -3373,7 +3409,7 @@ class Model(ObjectWithMeta, VarProvider):
3373
3409
 
3374
3410
  def validate(self, remove_unused_variables=False):
3375
3411
  """
3376
- Attempts to check model validity, raises errors if it isn't.
3412
+ Validates this model and raises errors if any issues are found.
3377
3413
 
3378
3414
  Small issues (e.g. unused variables) will generate warnings, which
3379
3415
  can be retrieved using :meth:`Model.warnings()` or
@@ -3399,6 +3435,15 @@ class Model(ObjectWithMeta, VarProvider):
3399
3435
  'Invalid time variable set. Time variable must be bound to'
3400
3436
  ' external value "time".')
3401
3437
 
3438
+ # Test initial value expresions
3439
+ n = len(self._state_vars)
3440
+ if n != len(self._state_init): # pragma: no cover
3441
+ # Cover pragma: This can only happen if there's an API bug
3442
+ self._valid = False
3443
+ raise myokit.IntegrityError(
3444
+ 'Initial values list must have same size as state variables'
3445
+ ' list.')
3446
+
3402
3447
  # Validation of components, variables
3403
3448
  for c in self.components():
3404
3449
  if c._parent != self: # pragma: no cover
@@ -3419,15 +3464,6 @@ class Model(ObjectWithMeta, VarProvider):
3419
3464
  'Component called <' + c.qname() + '> found at index <'
3420
3465
  + n + '>.')
3421
3466
 
3422
- # Test current state values
3423
- n = len(self._state)
3424
- if n != len(self._current_state): # pragma: no cover
3425
- # Cover pragma: This can only happen if there's an API bug
3426
- self._valid = False
3427
- raise myokit.IntegrityError(
3428
- 'Current state values list must have same size as state'
3429
- ' variables list.')
3430
-
3431
3467
  # Find cycles, warn of unused variables
3432
3468
  self._validate_solvability(remove_unused_variables)
3433
3469
 
@@ -3481,7 +3517,7 @@ class Model(ObjectWithMeta, VarProvider):
3481
3517
 
3482
3518
  # Follow all state variables (unless already visited), all bound
3483
3519
  # variables and all used variables.
3484
- used = [x for x in self._state]
3520
+ used = [x for x in self._state_vars]
3485
3521
  used += [x for x in self._bindings.values()]
3486
3522
  used += [x for x in self._labels.values()]
3487
3523
 
@@ -3547,7 +3583,7 @@ class Model(ObjectWithMeta, VarProvider):
3547
3583
  if not isinstance(var, ModelPart):
3548
3584
  var = self.suggest_variable(var)
3549
3585
  if var[0] is None:
3550
- if var[1] is None:
3586
+ if var[1] is None: # pragma: no cover
3551
3587
  raise Exception(var[2])
3552
3588
  var = var[1]
3553
3589
  out.append(
@@ -3606,7 +3642,7 @@ class Component(VarOwner):
3606
3642
  """
3607
3643
 
3608
3644
  def __init__(self, model, name):
3609
- super(Component, self).__init__(model, name)
3645
+ super().__init__(model, name)
3610
3646
  self._alias_map = {} # Maps variable names to other variables names
3611
3647
 
3612
3648
  def _clone1(self, model, new_name=None):
@@ -3692,7 +3728,7 @@ class Component(VarOwner):
3692
3728
  var._delete(recursive=True, ignore_siblings=True)
3693
3729
 
3694
3730
  # Delete links to parent
3695
- super(Component, self)._delete()
3731
+ super()._delete()
3696
3732
 
3697
3733
  def alias(self, name):
3698
3734
  """
@@ -3824,10 +3860,10 @@ class Variable(VarOwner):
3824
3860
  """
3825
3861
 
3826
3862
  def __init__(self, parent, name):
3827
- super(Variable, self).__init__(parent, name)
3863
+ super().__init__(parent, name)
3828
3864
 
3829
- # Indice, only set if this is a state variable
3830
- self._indice = None
3865
+ # Index, only set if this is a state variable
3866
+ self._index = None
3831
3867
 
3832
3868
  # This variable's unit, if given, else dimensionless
3833
3869
  self._unit = None
@@ -3885,7 +3921,11 @@ class Variable(VarOwner):
3885
3921
  """
3886
3922
  # Set value
3887
3923
  if value is None:
3888
- value = self.state_value() if self._is_state else self._rhs.eval()
3924
+ self.model().validate() # Model must be valid before evaluations
3925
+ if self._is_state:
3926
+ value = self.initial_value(True)
3927
+ else:
3928
+ value = self._rhs.eval()
3889
3929
  else:
3890
3930
  value = float(value)
3891
3931
 
@@ -3920,7 +3960,7 @@ class Variable(VarOwner):
3920
3960
  The argument ``lhs_map`` should be a dictionary mapping old
3921
3961
  :class:`LhsExpression` objects their equivalents in the new model.
3922
3962
  """
3923
- # _indice is set by promoting (done by model)
3963
+ # _index is set by promoting (done by model)
3924
3964
  # _binding
3925
3965
  if self._binding:
3926
3966
  v.set_binding(self._binding)
@@ -3942,6 +3982,10 @@ class Variable(VarOwner):
3942
3982
  for k in self.variables():
3943
3983
  k._clone2(v[k.name()], lhs_map)
3944
3984
 
3985
+ # Note: initial values are stored inside the model, and the state order
3986
+ # depends on the order in which promoting occurs, so states are only
3987
+ # created by Model.clone()
3988
+
3945
3989
  def _code(self, b, t):
3946
3990
  """
3947
3991
  Create the code for this variable and any child variables.
@@ -4081,7 +4125,12 @@ class Variable(VarOwner):
4081
4125
 
4082
4126
  # For states, update the current/initial value
4083
4127
  if self._is_state:
4084
- self.set_state_value(self.state_value() * float(fw))
4128
+ # Number? Then just multiply. Else use expression.
4129
+ value = self.initial_value()
4130
+ if isinstance(value, myokit.Number):
4131
+ self.set_initial_value(float(value) * float(fw))
4132
+ else:
4133
+ self.set_initial_value(myokit.Multiply(value, fw))
4085
4134
 
4086
4135
  # Update all references to the variable
4087
4136
  old_ref = myokit.Name(self)
@@ -4159,6 +4208,29 @@ class Variable(VarOwner):
4159
4208
  ' can not be removed: it is used by ' + ' and '.join(
4160
4209
  ['<' + v.qname() + '>' for v in refs]) + '.')
4161
4210
 
4211
+ # Third check: Do initial values depend on this variable?
4212
+ # Note that, instead of using a cached set in every variable, this
4213
+ # reference is just checked by scanning all init expressions (which
4214
+ # contain a cached set of references).
4215
+ # Note that we don't optimise by checking if this variable is constant,
4216
+ # as it's possible to create (invalid) models where non-constants are
4217
+ # referenced in initial values (but validate() will pick this up!).
4218
+ refs = set()
4219
+ m = self.model()
4220
+ n = myokit.Name(self)
4221
+ for v, e in zip(m._state_vars, m._state_init):
4222
+ if n in e.references():
4223
+ refs.add(v)
4224
+ if ignore_siblings:
4225
+ # Refs from sibling variables are allowed
4226
+ refs = refs.difference(
4227
+ set([x for x in refs if x.has_ancestor(self._parent)]))
4228
+ if refs:
4229
+ raise myokit.IntegrityError(
4230
+ 'Variable <' + self.qname() + '> can not be removed: it is'
4231
+ ' used in the inital value(s) for ' + ' and '.join(
4232
+ ['<' + v.qname() + '>' for v in refs]) + '.')
4233
+
4162
4234
  # At this point it's OK to delete. Rest of the code makes changes,
4163
4235
  # shouldn't raise errors.
4164
4236
 
@@ -4183,7 +4255,6 @@ class Variable(VarOwner):
4183
4255
  self.set_label(None)
4184
4256
 
4185
4257
  # Remove any aliases
4186
- m = self.parent(Model)
4187
4258
  for c in m.components():
4188
4259
  c.remove_aliases_for(self)
4189
4260
 
@@ -4196,7 +4267,7 @@ class Variable(VarOwner):
4196
4267
  self._remove_variable_internal(kid)
4197
4268
 
4198
4269
  # Remove parent links
4199
- super(Variable, self)._delete()
4270
+ super()._delete()
4200
4271
 
4201
4272
  def demote(self):
4202
4273
  """
@@ -4205,7 +4276,7 @@ class Variable(VarOwner):
4205
4276
  This will reset the validation status of the model this variable
4206
4277
  belongs to.
4207
4278
  """
4208
- if self._indice is None:
4279
+ if self._index is None:
4209
4280
  raise Exception('Variable is not a state variable.')
4210
4281
 
4211
4282
  # Check that nobody has references to this var's derivative
@@ -4218,16 +4289,16 @@ class Variable(VarOwner):
4218
4289
  model = self.model()
4219
4290
  try:
4220
4291
  # Remove initial value
4221
- del model._current_state[self._indice]
4292
+ del model._state_init[self._index]
4222
4293
 
4223
4294
  # Remove this variable from the state
4224
- del model._state[self._indice]
4295
+ del model._state_vars[self._index]
4225
4296
 
4226
4297
  # Set lhs to name expression
4227
4298
  self._lhs = myokit.Name(self)
4228
4299
 
4229
- # Remove this variable's indice
4230
- self._indice = None
4300
+ # Remove this variable's index
4301
+ self._index = None
4231
4302
 
4232
4303
  # Reset other states' indices
4233
4304
  model._reset_indices()
@@ -4260,14 +4331,43 @@ class Variable(VarOwner):
4260
4331
  """
4261
4332
  return self._rhs.eval()
4262
4333
 
4263
- def indice(self):
4334
+ def index(self):
4264
4335
  """
4265
4336
  For state variables, this will return their index in the state vector.
4337
+
4266
4338
  For all other variables, this will raise an exception.
4267
4339
  """
4268
- if self._indice is None:
4340
+ if self._index is None:
4341
+ raise Exception('Only state variables have initial values.')
4342
+ return self._index
4343
+
4344
+ def indice(self):
4345
+ """ Deprecated alias of :meth:`index`. """
4346
+
4347
+ # Deprecated on 2023-06-07
4348
+ import warnings
4349
+ warnings.warn(
4350
+ 'The method `indice` is deprecated. Please use `index()` instead.')
4351
+ return self.index()
4352
+
4353
+ def initial_value(self, as_float=False):
4354
+ """
4355
+ Returns a state variable's initial value, or raises an exception when
4356
+ called on a non-state variable.
4357
+
4358
+ By default, a :class:`myokit.Expression` is returned. To evaluate and
4359
+ return a float set ``as_float=True``.
4360
+ """
4361
+ if not self._is_state:
4269
4362
  raise Exception('Only state variables have initial values.')
4270
- return self._indice
4363
+
4364
+ model = self.model()
4365
+ expr = model._state_init[self._index]
4366
+ if not as_float:
4367
+ return expr
4368
+ if not expr.is_literal():
4369
+ model.validate()
4370
+ return expr.eval()
4271
4371
 
4272
4372
  def is_bound(self):
4273
4373
  """
@@ -4277,19 +4377,21 @@ class Variable(VarOwner):
4277
4377
 
4278
4378
  def is_constant(self):
4279
4379
  """
4280
- Returns ``True`` if this variable is constant.
4380
+ Returns ``True`` if this variable has a constant value (even if that
4381
+ value is defined in terms of other constants).
4281
4382
 
4282
4383
  Myokit doesn't discern between mathematical and physical constants,
4283
4384
  parameters etc. Anything that doesn't change during a simulation is
4284
- termed a constant. Note that this specifically excludes variables bound
4285
- to external inputs.
4385
+ termed a constant. Note that this specifically excludes variables that
4386
+ define a _binding_ to an external input.
4286
4387
  """
4287
4388
  return self._is_constant
4288
4389
 
4289
4390
  def is_intermediary(self):
4290
4391
  """
4291
4392
  Returns ``True`` if this variable is an intermediary variable, i.e. not
4292
- a constant or a state variable (and not bound to an external variable).
4393
+ a constant, not a state variable, and not bound to an external input
4394
+ such as time.
4293
4395
  """
4294
4396
  return self._is_intermediary
4295
4397
 
@@ -4301,8 +4403,7 @@ class Variable(VarOwner):
4301
4403
 
4302
4404
  def is_literal(self):
4303
4405
  """
4304
- Returns ``True`` if this variable's expression contains only literal
4305
- values.
4406
+ Returns ``True`` if this variable does not depend on other variables.
4306
4407
  """
4307
4408
  return self._is_literal
4308
4409
 
@@ -4341,15 +4442,27 @@ class Variable(VarOwner):
4341
4442
  """
4342
4443
  return self._lhs
4343
4444
 
4344
- def promote(self, state_value=0):
4445
+ def promote(self, initial_value=0, state_value=None):
4345
4446
  """
4346
- Turns this variable into a state variable with a current state value
4347
- given by ``state_value``.
4447
+ Turns this variable into a state variable with an initial value given
4448
+ by ``initial_value``.
4348
4449
 
4349
- This will reset the validation status of the model this variable
4350
- belongs to.
4450
+ The new ``initial_value`` should be:
4451
+
4452
+ 1. A numerical value.
4453
+ 2. A :class:`myokit.Expression`.
4454
+ 3. A string which can be parsed to a :class:`myokit.Expression`. Any
4455
+ references to variables must be made using their fully qualified
4456
+ names.
4457
+
4458
+ Note that expressions can contain references to non-nested and
4459
+ constant-valued variables (i.e. their right-hand side is either a
4460
+ literal expression or refers only to constants).
4461
+
4462
+ Calling ``promote`` will reset the validation status of the model this
4463
+ variable belongs to.
4351
4464
  """
4352
- if self._indice is not None:
4465
+ if self._index is not None:
4353
4466
  raise Exception('Variable is already a state variable')
4354
4467
  if not isinstance(self._parent, Component):
4355
4468
  raise Exception('State variables can only be added to Components.')
@@ -4357,26 +4470,40 @@ class Variable(VarOwner):
4357
4470
  raise Exception(
4358
4471
  'State variables cannot be bound to an external value.')
4359
4472
 
4360
- # Check state value argument
4361
- if isinstance(state_value, myokit.Expression):
4362
- if not state_value.is_literal():
4363
- raise myokit.NonLiteralValueError(
4364
- 'Expressions for state values can not contain references'
4365
- ' to other variables.')
4473
+ # Deprecated on 2023-02-22
4474
+ if state_value is not None:
4475
+ if initial_value != 0:
4476
+ raise Exception('Deprecated keyword argument `state_value` can'
4477
+ ' not be used at the same time as its'
4478
+ ' replacement `initial_value`.')
4479
+ initial_value = state_value
4366
4480
 
4481
+ import warnings
4482
+ warnings.warn('The keyword argument `state_value` is deprecated.'
4483
+ ' Please use `initial_value` instead.')
4484
+
4485
+ # Handle string and number rhs's
4367
4486
  model = self.model()
4487
+ if not isinstance(initial_value, myokit.Expression):
4488
+ if isinstance(initial_value, str):
4489
+ # Expressions are evaluated in model context
4490
+ initial_value = myokit.parse_expression(
4491
+ initial_value, context=model)
4492
+ elif initial_value is not None:
4493
+ initial_value = myokit.Number(initial_value)
4494
+
4368
4495
  try:
4369
4496
  # Set lhs to derivative expression
4370
4497
  self._lhs = myokit.Derivative(myokit.Name(self))
4371
4498
 
4372
- # Get new indice
4373
- self._indice = len(model._state)
4499
+ # Get new index
4500
+ self._index = len(model._state_vars)
4374
4501
 
4375
4502
  # Add to list of states
4376
- model._state.append(self)
4503
+ model._state_vars.append(self)
4377
4504
 
4378
- # Add value to list of current values
4379
- model._current_state.append(float(state_value))
4505
+ # Add initial_value to list of current values
4506
+ model._state_init.append(initial_value)
4380
4507
 
4381
4508
  # All references to this variable are now considered references to
4382
4509
  # its state value
@@ -4448,9 +4575,9 @@ class Variable(VarOwner):
4448
4575
  # Create function
4449
4576
  local = {}
4450
4577
  if use_numpy:
4451
- myokit._exec(func, {'numpy': numpy}, local)
4578
+ exec(func, {'numpy': numpy}, local)
4452
4579
  else:
4453
- myokit._exec(func, {'math': math}, local)
4580
+ exec(func, {'math': math}, local)
4454
4581
  handle = local['var_pyfunc_generated']
4455
4582
 
4456
4583
  # Return
@@ -4524,7 +4651,7 @@ class Variable(VarOwner):
4524
4651
  self._parent.move_variable(self, self._parent, new_name)
4525
4652
 
4526
4653
  def __repr__(self):
4527
- if self._indice is not None:
4654
+ if self._index is not None:
4528
4655
  return '<State(' + self.qname() + ')>'
4529
4656
  else:
4530
4657
  return '<Var(' + self.qname() + ')>'
@@ -4539,7 +4666,7 @@ class Variable(VarOwner):
4539
4666
  s_old = (self._is_bound, self._is_state, self._is_intermediary,
4540
4667
  self._is_literal, self._is_constant, self._is_nested)
4541
4668
  self._is_bound = self._binding is not None
4542
- self._is_state = self._indice is not None
4669
+ self._is_state = self._index is not None
4543
4670
  self._is_nested = isinstance(self._parent, Variable)
4544
4671
  if self._is_state or self._is_bound or self._rhs is None:
4545
4672
  self._is_constant = False
@@ -4586,7 +4713,7 @@ class Variable(VarOwner):
4586
4713
  ' is already bound to "' + self._binding + '".')
4587
4714
 
4588
4715
  # Check if not a state
4589
- if self._indice is not None:
4716
+ if self._index is not None:
4590
4717
  raise myokit.InvalidBindingError(
4591
4718
  'State variables cannot be bound to an external value.')
4592
4719
 
@@ -4606,6 +4733,42 @@ class Variable(VarOwner):
4606
4733
  # Reset model validation
4607
4734
  model._reset_validation()
4608
4735
 
4736
+ def set_initial_value(self, value):
4737
+ """
4738
+ Sets the initial value of a state variable, or raises an exception if
4739
+ called on a non-state variable.
4740
+
4741
+ The new value can be passed in as an expression, number, or a string
4742
+ (in which case it will be parsed as an expression). Expressions can
4743
+ refer to variables as long as they are not nested and are constant in
4744
+ time. Variable references in strings must be made using fully qualified
4745
+ names (``component.variable``).
4746
+ """
4747
+ if not self._is_state:
4748
+ raise Exception('Only state variables have state values.')
4749
+ self._set_initial_value(value, True)
4750
+
4751
+ def _set_initial_value(self, value, make_the_change):
4752
+ """ Internal version of `set_initial_value`. """
4753
+ # Handle strings and floats
4754
+ model = self.model()
4755
+ if not isinstance(value, myokit.Expression):
4756
+ if isinstance(value, str):
4757
+ value = myokit.parse_expression(value, context=model)
4758
+ else:
4759
+ value = myokit.Number(value)
4760
+
4761
+ # Allow internal calls to parse `value` without making a change
4762
+ if not make_the_change:
4763
+ return value
4764
+
4765
+ # Update
4766
+ try:
4767
+ model._state_init[self._index] = value
4768
+ finally:
4769
+ # Reset model validation, but not the variable cache
4770
+ model._reset_validation()
4771
+
4609
4772
  def set_label(self, label=None):
4610
4773
  """
4611
4774
  Adds a unique ``label`` for this variable, indicated that its value can
@@ -4658,7 +4821,7 @@ class Variable(VarOwner):
4658
4821
  """
4659
4822
  # Handle string and number rhs's
4660
4823
  if not isinstance(rhs, myokit.Expression):
4661
- if isinstance(rhs, basestring):
4824
+ if isinstance(rhs, str):
4662
4825
  rhs = myokit.parse_expression(rhs, context=self)
4663
4826
  elif rhs is not None:
4664
4827
  rhs = myokit.Number(rhs)
@@ -4693,19 +4856,14 @@ class Variable(VarOwner):
4693
4856
 
4694
4857
  def set_state_value(self, value):
4695
4858
  """
4696
- If this variable is a state variable, its current value will be
4697
- updated. For all other variables this raises an exception.
4859
+ Deprecated method, use :meth:`set_initial_value` instead.
4698
4860
  """
4699
- if not self._is_state:
4700
- raise Exception('Only state variables have state values.')
4701
- model = self.model()
4702
- if isinstance(value, myokit.Expression):
4703
- if not value.is_literal():
4704
- raise myokit.NonLiteralValueError(
4705
- 'Expressions for state values can not contain references'
4706
- ' to other variables.')
4707
- model._current_state[self._indice] = float(value)
4708
- # No need to reset validation status or cache here.
4861
+ # Deprecated since 2023-02-22
4862
+ import warnings
4863
+ warnings.warn(
4864
+ 'The method `set_state_value` is deprecated. Please use'
4865
+ ' `set_initial_value` instead.')
4866
+ self.set_initial_value(value)
4709
4867
 
4710
4868
  def set_unit(self, unit=None):
4711
4869
  """
@@ -4714,7 +4872,7 @@ class Variable(VarOwner):
4714
4872
  """
4715
4873
  if unit is None or isinstance(unit, myokit.Unit):
4716
4874
  self._unit = unit
4717
- elif isinstance(unit, basestring):
4875
+ elif isinstance(unit, str):
4718
4876
  self._unit = myokit.parse_unit(unit)
4719
4877
  else:
4720
4878
  raise TypeError('Method set_unit() expects a myokit.Unit or None.')
@@ -4723,12 +4881,15 @@ class Variable(VarOwner):
4723
4881
 
4724
4882
  def state_value(self):
4725
4883
  """
4726
- For state variables, this will return their current value.
4727
- For all other variables, this will raise an exception.
4884
+ Deprecated method, use
4885
+ :meth:`initial_value(as_float=True)<initial_value>` instead.
4728
4886
  """
4729
- if not self._is_state:
4730
- raise Exception('Only state variables have initial values.')
4731
- return self.model()._current_state[self._indice]
4887
+ # Deprecated since 2023-02-22
4888
+ import warnings
4889
+ warnings.warn(
4890
+ 'The method `state_value` is deprecated. Please use'
4891
+ ' `initial_value(as_float=True)` instead.')
4892
+ return self.initial_value(as_float=True)
4732
4893
 
4733
4894
  def unit(self, mode=myokit.UNIT_TOLERANT):
4734
4895
  """
@@ -4752,46 +4913,89 @@ class Variable(VarOwner):
4752
4913
  """
4753
4914
  Attempts to check this variable's validity, raises errors if it isn't.
4754
4915
  """
4916
+ #
4755
4917
  # Validate rhs
4918
+ #
4756
4919
  if self._rhs is None:
4757
4920
  raise myokit.MissingRhsError(self)
4758
4921
  self._rhs.validate()
4759
4922
 
4760
- # Partial derivatives are not allowed in an RHS
4923
+ # RHS: No PartialDerivative objects
4761
4924
  if self._rhs.contains_type(myokit.PartialDerivative):
4762
4925
  raise myokit.IntegrityError(
4763
4926
  'Partial derivatives may not appear in expressions set as'
4764
- ' right-hand side of a variable.')
4927
+ ' right-hand side of a variable: <' + self.qname() + '>.')
4765
4928
 
4766
- # Initial values are not allowed in an RHS
4929
+ # RHS: No InitialValue objects
4767
4930
  if self._rhs.contains_type(myokit.InitialValue):
4768
4931
  raise myokit.IntegrityError(
4769
4932
  'Initial value operators may not appear in expressions set as'
4770
- ' right-hand side of a variable.')
4933
+ ' right-hand side of a variable: <' + self.qname() + '>.')
4771
4934
 
4772
- # Conditions are not allowed as an RHS
4935
+ # RHS: Can't evaluate to True or False
4773
4936
  if isinstance(self._rhs, myokit.Condition):
4774
4937
  raise myokit.IntegrityError(
4775
4938
  'The right-hand side expression for a variable can not be a'
4776
- ' condition.')
4939
+ ' condition: <' + self.qname() + '>.')
4777
4940
 
4941
+ #
4778
4942
  # Check state variables
4779
- is_state = self._indice is not None
4943
+ #
4944
+ is_state = self._index is not None
4780
4945
  is_deriv = self.lhs().is_derivative()
4781
4946
  if is_state:
4947
+ # Derivative is set
4782
4948
  if not is_deriv: # pragma: no cover
4783
4949
  raise myokit.IntegrityError(
4784
4950
  'Variable <' + self.qname() + '> is listed as a state'
4785
4951
  ' variable but its lhs is not a derivative.')
4952
+
4953
+ # Not nested
4786
4954
  if self._is_nested: # pragma: no cover
4787
4955
  raise myokit.IntegrityError(
4788
4956
  'State variables should not be nested: <'
4789
4957
  + str(self.qname()) + '>.')
4958
+
4959
+ # Index matches model
4790
4960
  m = self.model()
4791
- if not m._state[self._indice] == self: # pragma: no cover
4961
+ if not m._state_vars[self._index] == self: # pragma: no cover
4792
4962
  raise myokit.IntegrityError(
4793
4963
  'State variable not listed in model state vector at'
4794
- ' correct indice: <' + self.qname() + '>.')
4964
+ ' correct index: <' + self.qname() + '>.')
4965
+
4966
+ # Initial value is an expression
4967
+ i = m._state_init[self._index]
4968
+ if not isinstance(i, myokit.Expression): # pragma: no cover
4969
+ raise myokit.IntegrityError(
4970
+ 'Initial value for <' + self.qname() + '> is not an'
4971
+ ' expression.')
4972
+
4973
+ # Init: No PartialDerivative or InitialValue operators
4974
+ if i.contains_type(myokit.PartialDerivative):
4975
+ raise myokit.IntegrityError(
4976
+ 'Partial derivatives may not appear in model expressions:'
4977
+ ' initial value for <' + self.qname() + '>.')
4978
+ if i.contains_type(myokit.InitialValue):
4979
+ raise myokit.IntegrityError(
4980
+ 'Initial values may not appear in model expressions:'
4981
+ ' initial value for < ' + self.qname() + '>.')
4982
+
4983
+ # Init: Can't evaluate to True or False
4984
+ if isinstance(i, myokit.Condition):
4985
+ raise myokit.IntegrityError(
4986
+ 'The initial value for a variable can not be a'
4987
+ ' condition: <' + self.qname() + '>.')
4988
+
4989
+ # Init: No nested variables or non-constants
4990
+ for ref in i.references():
4991
+ var = ref.var()
4992
+ if var.is_nested():
4993
+ raise myokit.IllegalReferenceInInitialValueError(self, ref)
4994
+ if not var.is_constant():
4995
+ raise myokit.IntegrityError(
4996
+ 'Initial value for variable <' + self.qname() + '> is'
4997
+ ' not constant: ' + i.code() + '.')
4998
+
4795
4999
  elif is_deriv: # pragma: no cover
4796
5000
  raise myokit.IntegrityError(
4797
5001
  'A derivative was set for <' + self.qname() + '> but this is'
@@ -4851,7 +5055,7 @@ class Variable(VarOwner):
4851
5055
  return self._rhs.eval()
4852
5056
 
4853
5057
 
4854
- class Equation(object):
5058
+ class Equation:
4855
5059
  """
4856
5060
  Defines an equation: a statement that a left-hand side (LHS) is equal to a
4857
5061
  right-hand side (RHS) expression.
@@ -4886,7 +5090,7 @@ class Equation(object):
4886
5090
 
4887
5091
  def code(self):
4888
5092
  """ Returns an ``.mmt`` representation of this equation. """
4889
- b = StringIO()
5093
+ b = io.StringIO()
4890
5094
  self._lhs._code(b, None)
4891
5095
  b.write(' = ')
4892
5096
  self._rhs._code(b, None)
@@ -4932,7 +5136,7 @@ class EquationList(list, VarProvider):
4932
5136
  return stream(self)
4933
5137
 
4934
5138
 
4935
- class UserFunction(object):
5139
+ class UserFunction:
4936
5140
  """
4937
5141
  Represents a user function.
4938
5142