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
@@ -8,9 +8,6 @@
8
8
  # This file is part of Myokit.
9
9
  # See http://myokit.org for copyright, sharing, and licensing details.
10
10
  #
11
- from __future__ import absolute_import, division
12
- from __future__ import print_function, unicode_literals
13
-
14
11
  import pickle
15
12
  import re
16
13
  import unittest
@@ -20,13 +17,6 @@ import myokit
20
17
  from myokit.tests import TemporaryDirectory, WarningCollector
21
18
 
22
19
 
23
- # Unit testing in Python 2 and 3
24
- try:
25
- unittest.TestCase.assertRaisesRegex
26
- except AttributeError:
27
- unittest.TestCase.assertRaisesRegex = unittest.TestCase.assertRaisesRegexp
28
-
29
-
30
20
  class ModelTest(unittest.TestCase):
31
21
  """
32
22
  Tests parts of :class:`myokit.Model`.
@@ -301,6 +291,26 @@ class ModelTest(unittest.TestCase):
301
291
  self.assertTrue(m1.has_parse_info())
302
292
  self.assertFalse(m2.has_parse_info())
303
293
 
294
+ # Test initial value expressions are maintained
295
+ m1 = myokit.Model('moo')
296
+ component = m1.add_component('c')
297
+ p = component.add_variable('p')
298
+ q = component.add_variable('q')
299
+ r = component.add_variable('r')
300
+ s = component.add_variable('s')
301
+ z = component.add_variable('z')
302
+ z.set_rhs(123)
303
+ p.promote(1)
304
+ q.promote('1 + exp(3)')
305
+ r.promote('c.z')
306
+ s.promote('1 [g] + c.z / 2 [1/g]')
307
+ m2 = m1.clone()
308
+ self.assertEqual(m2.get('c.p').initial_value(), myokit.Number(1))
309
+ self.assertEqual(m2.get('c.q').initial_value().code(), '1 + exp(3)')
310
+ self.assertEqual(m2.get('c.r').initial_value(), m2.get('c.z').lhs())
311
+ self.assertEqual(m2.get('c.s').initial_value().code(),
312
+ '1 [g] + c.z / 2 [1/g]')
313
+
304
314
  def test_code(self):
305
315
  # Test :meth:`Model.code()`.
306
316
 
@@ -355,44 +365,41 @@ class ModelTest(unittest.TestCase):
355
365
  '13 d = comp1.a\n'
356
366
  )
357
367
 
358
- def test_is_similar(self):
359
- # Check that equality takes both code() and unames into account
360
-
361
- # Test without custom reserved names
362
- m1 = myokit.load_model('example')
363
- m2 = m1.clone()
364
- self.assertIsInstance(m2, myokit.Model)
365
- self.assertFalse(m1 is m2)
366
- self.assertNotEqual(m1, m2)
367
- self.assertNotEqual(m2, m1)
368
- self.assertTrue(m1.is_similar(m2, False))
369
- self.assertTrue(m1.is_similar(m2, True))
370
- self.assertTrue(m2.is_similar(m1, False))
371
- self.assertTrue(m2.is_similar(m1, True))
372
- self.assertTrue(m1.is_similar(m1, True))
373
- self.assertTrue(m2.is_similar(m2, False))
374
-
375
- # Test with none-model
376
- self.assertFalse(m1.is_similar(None))
377
- self.assertFalse(m1.is_similar(m1.code()))
378
-
379
- # Add reserved names
380
- m1.reserve_unique_names('bertie')
381
- self.assertFalse(m1.is_similar(m2))
382
- m1.reserve_unique_names('clair')
383
- self.assertFalse(m1.is_similar(m2))
384
- m2.reserve_unique_names('clair', 'bertie')
385
- self.assertTrue(m1.is_similar(m2))
386
-
387
- # Add reserved name prefixes
388
- m1.reserve_unique_name_prefix('aa', 'bb')
389
- m1.reserve_unique_name_prefix('cc', 'dd')
390
- self.assertFalse(m1.is_similar(m2))
391
- m2.reserve_unique_name_prefix('aa', 'bb')
392
- m2.reserve_unique_name_prefix('cc', 'ee')
393
- self.assertFalse(m1.is_similar(m2))
394
- m2.reserve_unique_name_prefix('cc', 'dd')
395
- self.assertTrue(m1.is_similar(m2))
368
+ # Test initial value expressions are maintained
369
+ model = myokit.Model('moo')
370
+ component = model.add_component('c')
371
+ p = component.add_variable('p')
372
+ q = component.add_variable('q')
373
+ r = component.add_variable('r')
374
+ s = component.add_variable('s')
375
+ z = component.add_variable('z')
376
+ z.set_rhs(123)
377
+ p.promote(1)
378
+ q.promote('1 + exp(3)')
379
+ r.promote('c.z')
380
+ s.promote('1 [g] + c.z / 2 [1/g]')
381
+
382
+ p.set_rhs(1)
383
+ q.set_rhs(2)
384
+ r.set_rhs(3)
385
+ s.set_rhs('4 [g/s]')
386
+
387
+ self.assertEqual(model.code(line_numbers=False), '\n'.join([
388
+ '[[model]]',
389
+ 'name: moo',
390
+ '# Initial values',
391
+ 'c.p = 1',
392
+ 'c.q = 1 + exp(3)',
393
+ 'c.r = c.z',
394
+ 'c.s = 1 [g] + c.z / 2 [1/g]',
395
+ '',
396
+ '[c]',
397
+ 'dot(p) = 1',
398
+ 'dot(q) = 2',
399
+ 'dot(r) = 3',
400
+ 'dot(s) = 4 [g/s]',
401
+ 'z = 123\n\n',
402
+ ]))
396
403
 
397
404
  def test_evaluate_derivatives(self):
398
405
  # Test Model.evaluate_derivatives().
@@ -494,7 +501,7 @@ class ModelTest(unittest.TestCase):
494
501
 
495
502
  # Test with invalid state argument
496
503
  self.assertRaisesRegex(
497
- ValueError, r'list of \(8\)', m.format_state, [1, 2, 3])
504
+ ValueError, r'sequence of \(8\)', m.format_state, [1, 2, 3])
498
505
 
499
506
  # Test with precision argument
500
507
  state1 = [1, 2, 3, 4, 5, 6, 7, 8]
@@ -526,9 +533,26 @@ class ModelTest(unittest.TestCase):
526
533
 
527
534
  # Test with invalid second state argument
528
535
  self.assertRaisesRegex(
529
- ValueError, r'list of \(8\)', m.format_state,
536
+ ValueError, r'sequence of \(8\)', m.format_state,
530
537
  [1, 2, 3, 4, 5, 6, 7, 8], [1, 2, 3])
531
538
 
539
+ # Test with model that has expressions in its initial state.
540
+ # These will be evaluated (the method handles states, not initial state
541
+ # expressions).
542
+ m.get('ina.m').set_initial_value('sqrt(4) / 4')
543
+ m.get('ina.h').set_initial_value('ina.gNa / 20')
544
+ self.assertEqual(
545
+ m.format_state(),
546
+ 'membrane.V = -84.5286\n'
547
+ 'ina.m = 0.5\n'
548
+ 'ina.h = 0.8\n'
549
+ 'ina.j = 0.995484\n'
550
+ 'ica.d = 3e-06\n'
551
+ 'ica.f = 1.0\n'
552
+ 'ik.x = 0.0057\n'
553
+ 'ica.Ca_i = 0.0002'
554
+ )
555
+
532
556
  def test_format_state_derivatives(self):
533
557
  # Test Model.format_state_derivatives().
534
558
 
@@ -585,7 +609,7 @@ class ModelTest(unittest.TestCase):
585
609
 
586
610
  # Test with invalid state argument
587
611
  self.assertRaisesRegex(
588
- ValueError, r'list of \(8\)',
612
+ ValueError, r'sequence of \(8\)',
589
613
  m.format_state_derivatives, [1, 2, 3])
590
614
 
591
615
  # Test with state and precision argument
@@ -636,9 +660,28 @@ class ModelTest(unittest.TestCase):
636
660
 
637
661
  # Test with invalid derivs argument
638
662
  self.assertRaisesRegex(
639
- ValueError, r'list of \(8\)', m.format_state_derivatives,
663
+ ValueError, r'sequence of \(8\)', m.format_state_derivatives,
640
664
  [1, 2, 3, 4, 5, 6, 7, 8], [1, 2, 3])
641
665
 
666
+ # Test without expressions in the initial state (these will be
667
+ # evaluated)
668
+ m.get('ina.m').set_initial_value('sqrt(0.25)')
669
+ m.get('ina.h').set_initial_value('ina.gNa / 20')
670
+ x = m.format_state_derivatives().splitlines()
671
+ y = [
672
+ 'membrane.V = -84.5286 dot = 2.46843981754470434e+02', # noqa
673
+ 'ina.m = 0.5 dot = -8.68225658924664856e+01', # noqa
674
+ 'ina.h = 0.8 dot = 4.89677127030544446e-02', # noqa
675
+ 'ina.j = 0.995484 dot = -3.70409866928434243e-04', # noqa
676
+ 'ica.d = 3e-06 dot = 3.68067721821794798e-04', # noqa
677
+ 'ica.f = 1.0 dot = -3.55010150519739432e-07', # noqa
678
+ 'ik.x = 0.0057 dot = -2.04613933160084307e-07', # noqa
679
+ 'ica.Ca_i = 0.0002 dot = -6.99430692442154227e-06' # noqa
680
+ ]
681
+ for a, b in zip(x, y):
682
+ self.assertTrue(almost_equal(a, b))
683
+ self.assertEqual(len(x), len(y))
684
+
642
685
  def test_get(self):
643
686
  # Test Model.get().
644
687
 
@@ -725,10 +768,12 @@ class ModelTest(unittest.TestCase):
725
768
  # Source model, to import stuff from
726
769
  ms = myokit.parse_model('''
727
770
  [[model]]
771
+ n.c = 1 + n.p
772
+ n.b = sqrt(0.25)
728
773
  p.b = 0.2
729
774
  q.e = 0.2
730
- x.a = 0.2
731
775
  y.e = 0.2
776
+ x.a = 0.2
732
777
 
733
778
  [e]
734
779
  t = 0 [s] bind time
@@ -778,14 +823,20 @@ class ModelTest(unittest.TestCase):
778
823
  sub_e = 2 * e
779
824
  in [m]
780
825
 
781
- # another group component but this isn't independant
826
+ # Another group component but this isn't independent
782
827
  [z]
783
828
  use x.d
784
829
  use y.e
785
830
  use e.h
786
-
787
831
  f = d * e * h
788
832
  in [m/s]
833
+
834
+ # A component with expressions in initial states
835
+ [n]
836
+ a = 1
837
+ dot(b) = 1 [1/s]
838
+ dot(c) = 2 [1/s]
839
+ p = 0.1
789
840
  ''')
790
841
  ms.validate()
791
842
  ms.check_units(myokit.UNIT_STRICT)
@@ -916,6 +967,19 @@ class ModelTest(unittest.TestCase):
916
967
  self.assertEqual(m1['x'].code(), ms['x'].code())
917
968
  self.assertEqual(m1['y'].code(), ms['y'].code())
918
969
  self.assertTrue(ms.is_similar(ms_unaltered, True))
970
+ # Check that state order is preserved
971
+ self.assertLess(m1.get('y.e').index(), m1.get('x.a').index())
972
+
973
+ # Import multiple components and rename
974
+ component_list = [ms['x'], ms['y']]
975
+ m1.import_component(component_list, new_name=['xx', 'yy'])
976
+ self.assertTrue(m1.has_component('xx'))
977
+ self.assertTrue(m1.has_component('yy'))
978
+ self.assertFalse(m1['xx'] is ms['x'])
979
+ self.assertFalse(m1['yy'] is ms['y'])
980
+ self.assertTrue(ms.is_similar(ms_unaltered, True))
981
+ # Check that state order is preserved
982
+ self.assertLess(m1.get('yy.e').index(), m1.get('xx.a').index())
919
983
 
920
984
  # Import 1 component in list
921
985
  m1.import_component([ms['p']], new_name='p3')
@@ -927,6 +991,26 @@ class ModelTest(unittest.TestCase):
927
991
  self.assertEqual(cs, c1)
928
992
  self.assertTrue(ms.is_similar(ms_unaltered, True))
929
993
 
994
+ # Import state variables with expressions in their initial values
995
+ m1_unaltered = m1.clone()
996
+ m1.import_component(ms['n'])
997
+ self.assertEqual(m1.get('n.b').initial_value().code(), 'sqrt(0.25)')
998
+ self.assertEqual(
999
+ m1.get('n.c').initial_value(),
1000
+ myokit.Plus(myokit.Number(1), myokit.Name(m1.get('n.p'))))
1001
+ # Check that state order is preserved
1002
+ self.assertLess(m1.get('n.c').index(), m1.get('n.b').index())
1003
+ m1 = m1_unaltered
1004
+
1005
+ # ...including references to parameters in other components
1006
+ ms2 = ms.clone()
1007
+ ms2.get('n.c').set_initial_value('1 + e.h')
1008
+ ms2_unaltered = ms2.clone()
1009
+ self.assertRaises(
1010
+ myokit.VariableMappingError, m1.import_component, ms2['n'])
1011
+ self.assertTrue(m1.is_similar(m1_unaltered, True))
1012
+ self.assertTrue(ms2.is_similar(ms2_unaltered, True))
1013
+
930
1014
  # Try and fail to import r without a mapping
931
1015
  m1_unaltered = m1.clone()
932
1016
  self.assertRaises(
@@ -1154,7 +1238,7 @@ class ModelTest(unittest.TestCase):
1154
1238
  self.assertTrue(m1.get('r.f').is_state())
1155
1239
  self.assertEqual(m1.get('r.f').unit(), ms.get('r.f').unit())
1156
1240
  self.assertEqual(
1157
- m1.get('r.f').state_value(), ms.get('r.f').state_value())
1241
+ m1.get('r.f').initial_value(), ms.get('r.f').initial_value())
1158
1242
  self.assertEqual(
1159
1243
  m1.get('r.f').rhs().code(),
1160
1244
  myokit.Multiply(ms.get('r.f').rhs(), s2ms).code())
@@ -1167,12 +1251,12 @@ class ModelTest(unittest.TestCase):
1167
1251
  self.assertFalse(m1['y'] is ms['y'])
1168
1252
  self.assertEqual(m1.get('x.a').unit(), ms.get('x.a').unit())
1169
1253
  self.assertEqual(
1170
- m1.get('x.a').state_value(), ms.get('x.a').state_value())
1254
+ m1.get('x.a').initial_value(), ms.get('x.a').initial_value())
1171
1255
  self.assertEqual(
1172
1256
  m1.get('x.a').rhs().code(),
1173
1257
  myokit.Multiply(ms.get('x.a').rhs(), s2ms).code())
1174
1258
  self.assertEqual(
1175
- m1.get('y.e').state_value(), ms.get('y.e').state_value())
1259
+ m1.get('y.e').initial_value(), ms.get('y.e').initial_value())
1176
1260
  self.assertEqual(
1177
1261
  m1.get('y.e').rhs().code(),
1178
1262
  myokit.Multiply(ms.get('y.e').rhs(), s2ms).code())
@@ -1316,6 +1400,113 @@ class ModelTest(unittest.TestCase):
1316
1400
  m1.import_component, ms['q'], var_map=vm, convert_units=True)
1317
1401
  self.assertTrue(ms.is_similar(ms_unaltered, True))
1318
1402
 
1403
+ def test_initial_values(self):
1404
+ # Tests :meth:`Model.initial_values`.
1405
+
1406
+ m = myokit.parse_model('''
1407
+ [[model]]
1408
+ c.x = 2
1409
+ c.y = 1 + 2
1410
+ c.z = 2 * c.p
1411
+
1412
+ [c]
1413
+ t = 0 bind time
1414
+ dot(x) = 0
1415
+ dot(y) = 0
1416
+ dot(z) = 0
1417
+ p = 3
1418
+ q = 4
1419
+ ''')
1420
+
1421
+ # Test expression version
1422
+ p = m.get('c.p')
1423
+ x0 = m.initial_values()
1424
+ self.assertEqual(x0[0], myokit.Number(2))
1425
+ self.assertEqual(
1426
+ x0[1], myokit.Plus(myokit.Number(1), myokit.Number(2)))
1427
+ self.assertEqual(x0[2], myokit.Multiply(myokit.Number(2), p.lhs()))
1428
+
1429
+ # Test float version
1430
+ self.assertEqual(m.initial_values(True), [2, 3, 6])
1431
+
1432
+ # Test deprecated alias
1433
+ with WarningCollector() as w:
1434
+ self.assertEqual(m.state(), m.initial_values(True))
1435
+ self.assertIn('deprecated', w.text())
1436
+
1437
+ # Test cycles are detected before evaluation
1438
+ p.set_rhs('1 / q')
1439
+ m.get('c.q').set_rhs('1 + p')
1440
+ m.initial_values() # No evaluation, so no error
1441
+ self.assertRaises(
1442
+ myokit.CyclicalDependencyError, m.initial_values, as_floats=True)
1443
+
1444
+ def test_inits(self):
1445
+ # Tests :meth:`Model.inits`
1446
+
1447
+ m = myokit.parse_model('''
1448
+ [[model]]
1449
+ c.x = 2
1450
+ c.y = 1 + 2
1451
+ c.z = 2 * c.p
1452
+
1453
+ [c]
1454
+ t = 0 bind time
1455
+ dot(x) = 0
1456
+ dot(y) = 0
1457
+ dot(z) = 0
1458
+ p = 3
1459
+ ''')
1460
+ with WarningCollector() as w:
1461
+ eqs = m.inits()
1462
+ self.assertIn('deprecated', w.text())
1463
+
1464
+ E, N, M = myokit.Equation, myokit.Number, myokit.Multiply
1465
+ self.assertEqual(list(eqs), [
1466
+ E(myokit.Name(m.get('c.x')), N(2)),
1467
+ E(myokit.Name(m.get('c.y')), myokit.Plus(N(1), N(2))),
1468
+ E(myokit.Name(m.get('c.z')), M(N(2), m.get('c.p').lhs()))
1469
+ ])
1470
+
1471
+ def test_is_similar(self):
1472
+ # Check that equality takes both code() and unames into account
1473
+
1474
+ # Test without custom reserved names
1475
+ m1 = myokit.load_model('example')
1476
+ m2 = m1.clone()
1477
+ self.assertIsInstance(m2, myokit.Model)
1478
+ self.assertFalse(m1 is m2)
1479
+ self.assertNotEqual(m1, m2)
1480
+ self.assertNotEqual(m2, m1)
1481
+ self.assertTrue(m1.is_similar(m2, False))
1482
+ self.assertTrue(m1.is_similar(m2, True))
1483
+ self.assertTrue(m2.is_similar(m1, False))
1484
+ self.assertTrue(m2.is_similar(m1, True))
1485
+ self.assertTrue(m1.is_similar(m1, True))
1486
+ self.assertTrue(m2.is_similar(m2, False))
1487
+
1488
+ # Test with none-model
1489
+ self.assertFalse(m1.is_similar(None))
1490
+ self.assertFalse(m1.is_similar(m1.code()))
1491
+
1492
+ # Add reserved names
1493
+ m1.reserve_unique_names('bertie')
1494
+ self.assertFalse(m1.is_similar(m2))
1495
+ m1.reserve_unique_names('clair')
1496
+ self.assertFalse(m1.is_similar(m2))
1497
+ m2.reserve_unique_names('clair', 'bertie')
1498
+ self.assertTrue(m1.is_similar(m2))
1499
+
1500
+ # Add reserved name prefixes
1501
+ m1.reserve_unique_name_prefix('aa', 'bb')
1502
+ m1.reserve_unique_name_prefix('cc', 'dd')
1503
+ self.assertFalse(m1.is_similar(m2))
1504
+ m2.reserve_unique_name_prefix('aa', 'bb')
1505
+ m2.reserve_unique_name_prefix('cc', 'ee')
1506
+ self.assertFalse(m1.is_similar(m2))
1507
+ m2.reserve_unique_name_prefix('cc', 'dd')
1508
+ self.assertTrue(m1.is_similar(m2))
1509
+
1319
1510
  def test_item_at_text_position(self):
1320
1511
  # Test :meth:`Model.item_at_text_position()`.
1321
1512
 
@@ -1454,17 +1645,21 @@ class ModelTest(unittest.TestCase):
1454
1645
  # Test :meth:`Model.save_state()` and :meth:`Model.load_state()`.
1455
1646
 
1456
1647
  m = myokit.load_model('example')
1457
- s1 = m.state()
1648
+ s1 = m.initial_values(True)
1458
1649
  with TemporaryDirectory() as d:
1459
1650
  path = d.path('state.csv')
1460
- m.save_state(path)
1461
- self.assertEqual(m.state(), s1)
1651
+ with WarningCollector() as w:
1652
+ m.save_state(path)
1653
+ self.assertIn('deprecated', w.text())
1654
+ self.assertEqual(m.initial_values(True), s1)
1462
1655
  sx = list(s1)
1463
1656
  sx[0] = 10
1464
- m.set_state(sx)
1465
- self.assertNotEqual(m.state(), s1)
1466
- m.load_state(path)
1467
- self.assertEqual(m.state(), s1)
1657
+ m.set_initial_values(sx)
1658
+ self.assertNotEqual(m.initial_values(True), s1)
1659
+ with WarningCollector() as w:
1660
+ m.load_state(path)
1661
+ self.assertIn('deprecated', w.text())
1662
+ self.assertEqual(m.initial_values(True), s1)
1468
1663
 
1469
1664
  def test_map_to_state(self):
1470
1665
  # Test :meth:`Model.map_to_state()`.
@@ -1624,7 +1819,7 @@ class ModelTest(unittest.TestCase):
1624
1819
  def test_remove_derivative_references(self):
1625
1820
  # Test the remove_derivative_references() method.
1626
1821
 
1627
- m0 = myokit.parse_model("""
1822
+ m0 = myokit.parse_model('''
1628
1823
  [[model]]
1629
1824
  c.x = 0
1630
1825
  c.y = 1
@@ -1643,8 +1838,8 @@ class ModelTest(unittest.TestCase):
1643
1838
 
1644
1839
  [d]
1645
1840
  z = 3 * dot(c.x) / dot(c.y)
1646
- """)
1647
- m2 = myokit.parse_model("""
1841
+ ''')
1842
+ m2 = myokit.parse_model('''
1648
1843
  [[model]]
1649
1844
  c.x = 0
1650
1845
  c.y = 1
@@ -1665,7 +1860,7 @@ class ModelTest(unittest.TestCase):
1665
1860
 
1666
1861
  [d]
1667
1862
  z = 3 * c.dot_x_1 / c.dot_y
1668
- """)
1863
+ ''')
1669
1864
 
1670
1865
  # Remove derivatives from m1
1671
1866
  m1 = m0.clone()
@@ -1700,31 +1895,6 @@ class ModelTest(unittest.TestCase):
1700
1895
  m1.remove_derivative_references()
1701
1896
  self.assertEqual(m1.get('c.dot_y').unit(), myokit.parse_unit('mV/ms'))
1702
1897
 
1703
- def test_remove_variable_with_alias(self):
1704
- # Test cloning of a variable with an alias after an add / remove event.
1705
-
1706
- m = myokit.Model('AddRemoveClone')
1707
- c = m.add_component('c')
1708
- p = c.add_variable('p')
1709
- p.set_binding('time')
1710
- p.set_rhs(0)
1711
- q = c.add_variable('q')
1712
- q.set_rhs(12)
1713
- m.validate() # Raises error if not ok
1714
- m.clone() # Raises error if not ok
1715
- d = m.add_component('d')
1716
- d.add_alias('bert', p)
1717
- e = d.add_variable('e')
1718
- e.set_rhs('10 * bert')
1719
- m.validate()
1720
- m.clone()
1721
- d.add_alias('ernie', q)
1722
- m.validate()
1723
- m.clone()
1724
- c.remove_variable(q)
1725
- m.validate()
1726
- m.clone() # Will raise error if alias isn't deleted
1727
-
1728
1898
  def test_reorder_state(self):
1729
1899
  # Test :meth:`Model.reorder_state()`.
1730
1900
 
@@ -1830,6 +2000,70 @@ class ModelTest(unittest.TestCase):
1830
2000
  c = model['membrane']
1831
2001
  self.assertEqual(c.name(), 'membrane')
1832
2002
 
2003
+ def test_set_initial_values(self):
2004
+ # Tests :meth:`Model.set_initial_values()`.
2005
+ m = myokit.load_model('example')
2006
+ states = [v.qname() for v in m.states()]
2007
+ values = m.initial_values(as_floats=True)
2008
+
2009
+ # Set with floats
2010
+ values[0] += 2
2011
+ m.set_initial_values(values)
2012
+ for val, ref in zip(m.initial_values(True), values):
2013
+ self.assertEqual(val, ref)
2014
+
2015
+ # Set with expressions
2016
+ values[1] -= 3
2017
+ m.set_initial_values([myokit.Number(v) for v in values])
2018
+ for val, ref in zip(m.initial_values(True), values):
2019
+ self.assertEqual(val, ref)
2020
+
2021
+ # Set with mix, including strings and expressions
2022
+ values[2] += 10
2023
+ mixed = list(values)
2024
+ mixed[2] = myokit.Number(mixed[2])
2025
+ mixed[3] = str(values[3])
2026
+ m.set_initial_values(mixed)
2027
+ for val, ref in zip(m.initial_values(True), values):
2028
+ self.assertEqual(val, ref)
2029
+
2030
+ # Set with dict of floats
2031
+ values = m.initial_values(as_floats=True)
2032
+ s = dict(zip(states, values))
2033
+ m.set_initial_values(s)
2034
+ for val, ref in zip(m.initial_values(True), values):
2035
+ self.assertEqual(val, ref)
2036
+
2037
+ # Set with one big string
2038
+ values[0] += 5
2039
+ s = dict(zip(states, values))
2040
+ s = '\n'.join([str(a) + '=' + str(b) for a, b in s.items()])
2041
+ m.set_initial_values(s)
2042
+ for val, ref in zip(m.initial_values(True), values):
2043
+ self.assertEqual(val, ref)
2044
+
2045
+ # Test that errors are detected in validation
2046
+ values[0] = myokit.Name(m.get('membrane.V'))
2047
+ m.set_initial_values(values)
2048
+ self.assertRaisesRegex(myokit.IntegrityError, 'not const', m.validate)
2049
+ values[0] = 'sqrt(3)'
2050
+ m.set_initial_values(values)
2051
+ m.validate()
2052
+
2053
+ # Deprecated alias
2054
+ values = m.initial_values(as_floats=True)
2055
+ values[0] += 10
2056
+ with WarningCollector() as w:
2057
+ m.set_state(values)
2058
+ self.assertIn('deprecated', w.text())
2059
+ for val, ref in zip(m.initial_values(True), values):
2060
+ self.assertEqual(val, ref)
2061
+
2062
+ # Wrong number of elements
2063
+ self.assertRaisesRegex(
2064
+ ValueError, 'Wrong number of initial values',
2065
+ m.set_initial_values, [1, 2, 3])
2066
+
1833
2067
  def test_show_evaluation_of(self):
1834
2068
  # Test :meth:`Model.show_evaluation_of(variable)`.
1835
2069
  # Depends mostly on `references()`, and `code()` methods.
@@ -1867,6 +2101,16 @@ class ModelTest(unittest.TestCase):
1867
2101
  self.assertIn('Literal constant', e)
1868
2102
  self.assertEqual(len(e.splitlines()), 7)
1869
2103
 
2104
+ # Test with expressions in initial values
2105
+ m.get('ina.m').set_initial_value('1 / sqrt(4)')
2106
+ e = m.show_evaluation_of('ina.m')
2107
+ self.assertIn('Initial value = 1 / sqrt(4)', e)
2108
+ self.assertEqual(len(e.splitlines()), 16)
2109
+ m.get('ina.m').set_initial_value('1 / sqrt(ina.gNa)')
2110
+ e = m.show_evaluation_of('ina.m')
2111
+ self.assertIn('Initial value = 1 / sqrt(ina.gNa)', e)
2112
+ self.assertIn(' = 2.5000000', e)
2113
+
1870
2114
  # Test with nothing similar
1871
2115
  m = myokit.Model()
1872
2116
  self.assertRaises(Exception, m.show_evaluation_of, 'Hello')
@@ -1886,7 +2130,7 @@ class ModelTest(unittest.TestCase):
1886
2130
  m = myokit.load_model('example')
1887
2131
  v = m.get('ina.INa')
1888
2132
  e = m.show_line_of(v)
1889
- self.assertIn('Defined on line 91', e)
2133
+ self.assertIn('Defined on line 90', e)
1890
2134
  self.assertIn('Intermediary variable', e)
1891
2135
  self.assertEqual(len(e.splitlines()), 4)
1892
2136
 
@@ -1899,7 +2143,7 @@ class ModelTest(unittest.TestCase):
1899
2143
  self.assertEqual(len(e.splitlines()), 3)
1900
2144
 
1901
2145
  # 'raw' version
1902
- self.assertEqual(m.show_line_of(v, raw=True), 91)
2146
+ self.assertEqual(m.show_line_of(v, raw=True), 90)
1903
2147
  self.assertIsNone(m2.show_line_of(v2, raw=True))
1904
2148
 
1905
2149
  def test_str(self):
@@ -2051,6 +2295,56 @@ class ModelTest(unittest.TestCase):
2051
2295
  ValueError, 'prepend cannot start with prefix',
2052
2296
  m.reserve_unique_name_prefix, 'x', 'x')
2053
2297
 
2298
+ def test_unused_and_cycles(self):
2299
+ # Test unused variable and cycle detection.
2300
+
2301
+ m = myokit.Model('LotkaVolterra')
2302
+ c0 = m.add_component('c0')
2303
+ t = c0.add_variable('time')
2304
+ t.set_rhs(myokit.Number(0))
2305
+ t.set_binding('time')
2306
+ c1 = m.add_component('c1')
2307
+ m.add_component('c2')
2308
+ c1_a = c1.add_variable('a')
2309
+ c1_b = c1.add_variable('b')
2310
+ c1_a.promote(1.0)
2311
+ c1_a.set_rhs(myokit.Multiply(myokit.Name(c1_a), myokit.Number(0.5)))
2312
+ c1_b.set_rhs(myokit.Multiply(myokit.Name(c1_a), myokit.Number(1.0)))
2313
+ # b is unused, test if found
2314
+ m.validate()
2315
+ w = m.warnings()
2316
+ self.assertEqual(len(w), 1)
2317
+ self.assertEqual(type(w[0]), myokit.UnusedVariableError)
2318
+ # b is used by c, c is unused, test if found
2319
+ c1_c = c1.add_variable('c')
2320
+ c1_c.set_rhs(myokit.Name(c1_b))
2321
+ m.validate()
2322
+ w = m.warnings()
2323
+ self.assertEqual(len(w), 2)
2324
+ self.assertEqual(type(w[0]), myokit.UnusedVariableError)
2325
+ self.assertEqual(type(w[1]), myokit.UnusedVariableError)
2326
+ # Test 1:1 cycle
2327
+ c1_b.set_rhs(myokit.Name(c1_b))
2328
+ self.assertRaises(myokit.CyclicalDependencyError, m.validate)
2329
+ # Test longer cycles
2330
+ c1_b.set_rhs(myokit.Multiply(myokit.Number(10), myokit.Name(c1_c)))
2331
+ self.assertRaises(myokit.CyclicalDependencyError, m.validate)
2332
+ # Reset
2333
+ c1_b.set_rhs(myokit.Multiply(myokit.Name(c1_a), myokit.Number(1.0)))
2334
+ m.validate()
2335
+ # Test cycle involving state variable
2336
+ c1_a.set_rhs(myokit.Name(c1_b))
2337
+ m.validate()
2338
+ c1_b.set_rhs(myokit.Multiply(myokit.Name(c1_a), myokit.Name(c1_b)))
2339
+ self.assertRaises(myokit.CyclicalDependencyError, m.validate)
2340
+ c1_b.set_rhs(myokit.Multiply(myokit.Name(c1_a), myokit.Name(c1_c)))
2341
+ c1_c.set_rhs(myokit.Multiply(myokit.Name(c1_a), myokit.Number(3)))
2342
+ m.validate()
2343
+ w = m.warnings()
2344
+ self.assertEqual(len(w), 0)
2345
+ c1_c.set_rhs(myokit.Multiply(myokit.Name(c1_a), myokit.Name(c1_b)))
2346
+ self.assertRaises(myokit.CyclicalDependencyError, m.validate)
2347
+
2054
2348
  def test_validate_and_remove_unused_variables(self):
2055
2349
  # Test :class:`Model.validate` with ``remove_unused_variables=True``.
2056
2350