myokit 1.37.5__py3-none-any.whl → 1.39.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- myokit/__init__.py +5 -0
- myokit/_datablock.py +6 -5
- myokit/_expressions.py +6 -1
- myokit/_model_api.py +44 -18
- myokit/_myokit_version.py +1 -1
- myokit/_parsing.py +8 -2
- myokit/_sim/cvodessim.py +26 -0
- myokit/formats/__init__.py +37 -0
- myokit/formats/ansic/_ewriter.py +1 -1
- myokit/formats/axon/_abf.py +43 -9
- myokit/formats/cellml/v1/__init__.py +5 -5
- myokit/formats/cellml/v1/_api.py +220 -122
- myokit/formats/cellml/v1/_parser.py +91 -87
- myokit/formats/cellml/v1/_writer.py +13 -6
- myokit/formats/cellml/v2/__init__.py +5 -8
- myokit/formats/cellml/v2/_api.py +182 -106
- myokit/formats/cellml/v2/_parser.py +68 -64
- myokit/formats/cellml/v2/_writer.py +7 -3
- myokit/formats/heka/_patchmaster.py +71 -14
- myokit/formats/mathml/_parser.py +106 -67
- myokit/gui/source.py +18 -12
- myokit/lib/hh.py +21 -37
- myokit/tests/test_cellml_v1_api.py +227 -33
- myokit/tests/test_cellml_v1_parser.py +48 -17
- myokit/tests/test_cellml_v1_writer.py +14 -4
- myokit/tests/test_cellml_v2_api.py +132 -114
- myokit/tests/test_cellml_v2_parser.py +31 -1
- myokit/tests/test_cellml_v2_writer.py +8 -1
- myokit/tests/test_datalog.py +17 -0
- myokit/tests/test_expressions.py +61 -0
- myokit/tests/test_formats.py +99 -0
- myokit/tests/test_formats_mathml_content.py +97 -37
- myokit/tests/test_formats_python.py +1 -1
- myokit/tests/test_model_building.py +2 -0
- myokit/tests/test_parsing.py +32 -0
- myokit/tests/test_simulation_cvodes.py +10 -4
- myokit/tests/test_variable.py +10 -7
- {myokit-1.37.5.dist-info → myokit-1.39.0.dist-info}/METADATA +22 -7
- {myokit-1.37.5.dist-info → myokit-1.39.0.dist-info}/RECORD +43 -43
- {myokit-1.37.5.dist-info → myokit-1.39.0.dist-info}/WHEEL +1 -1
- {myokit-1.37.5.dist-info → myokit-1.39.0.dist-info}/entry_points.txt +0 -0
- {myokit-1.37.5.dist-info → myokit-1.39.0.dist-info/licenses}/LICENSE.txt +0 -0
- {myokit-1.37.5.dist-info → myokit-1.39.0.dist-info}/top_level.txt +0 -0
|
@@ -357,27 +357,10 @@ class ContentMathMLParserTest(unittest.TestCase):
|
|
|
357
357
|
)
|
|
358
358
|
|
|
359
359
|
def test_functions(self):
|
|
360
|
-
# Tests parsing
|
|
361
|
-
|
|
362
|
-
# Power
|
|
363
|
-
a = myokit.Name('a')
|
|
364
|
-
b = myokit.Number(1)
|
|
365
|
-
c = myokit.Number(2)
|
|
366
|
-
e = myokit.Power(a, b)
|
|
367
|
-
x = '<apply><power/><ci>a</ci><cn>1.0</cn></apply>'
|
|
368
|
-
self.assertEqual(self.p(x), e)
|
|
369
|
-
e = myokit.Power(myokit.Power(a, b), c)
|
|
370
|
-
x = ('<apply><power/><apply><power/><ci>a</ci><cn>1.0</cn></apply>'
|
|
371
|
-
'<cn>2.0</cn></apply>')
|
|
372
|
-
self.assertEqual(self.p(x), e)
|
|
373
|
-
e = myokit.Power(a, myokit.Power(b, c))
|
|
374
|
-
x = ('<apply><power/><ci>a</ci><apply><power/><cn>1.0</cn><cn>2.0</cn>'
|
|
375
|
-
'</apply></apply>')
|
|
376
|
-
self.assertEqual(self.p(x), e)
|
|
377
|
-
|
|
378
|
-
#TODO: Degree etc.
|
|
360
|
+
# Tests parsing simple functions
|
|
379
361
|
|
|
380
362
|
# Exp
|
|
363
|
+
a = myokit.Name('a')
|
|
381
364
|
e = myokit.Exp(a)
|
|
382
365
|
x = '<apply><exp/><ci>a</ci></apply>'
|
|
383
366
|
self.assertEqual(self.p(x), e)
|
|
@@ -393,10 +376,19 @@ class ContentMathMLParserTest(unittest.TestCase):
|
|
|
393
376
|
mathml.MathMLError, r'Expecting 1 operand\(s\)', self.p, x)
|
|
394
377
|
|
|
395
378
|
# Floor
|
|
379
|
+
b = myokit.Number(1)
|
|
396
380
|
e = myokit.Floor(b)
|
|
397
381
|
x = '<apply><floor/><cn>1.0</cn></apply>'
|
|
398
382
|
self.assertEqual(self.p(x), e)
|
|
399
383
|
|
|
384
|
+
# Test number of arguments to unary operator
|
|
385
|
+
x = '<apply><floor/></apply>'
|
|
386
|
+
self.assertRaisesRegex(
|
|
387
|
+
mathml.MathMLError, r'Expecting 1 operand\(s\), got 0', self.p, x)
|
|
388
|
+
x = '<apply><floor/><cn>1.0</cn><cn>2.0</cn></apply>'
|
|
389
|
+
self.assertRaisesRegex(
|
|
390
|
+
mathml.MathMLError, r'Expecting 1 operand\(s\), got 2', self.p, x)
|
|
391
|
+
|
|
400
392
|
# Ceil
|
|
401
393
|
e = myokit.Ceil(b)
|
|
402
394
|
x = '<apply><ceiling/><cn>1.0</cn></apply>'
|
|
@@ -466,6 +458,50 @@ class ContentMathMLParserTest(unittest.TestCase):
|
|
|
466
458
|
self.assertRaisesRegex(
|
|
467
459
|
mathml.MathMLError, 'Expecting a single', self.p, x)
|
|
468
460
|
|
|
461
|
+
def test_functions_minmax(self):
|
|
462
|
+
# Tests parsing min and max
|
|
463
|
+
|
|
464
|
+
a = myokit.Name('a')
|
|
465
|
+
b = myokit.Number(1)
|
|
466
|
+
x = '<apply><min/><ci>a</ci><cn>1.0</cn></apply>'
|
|
467
|
+
e = myokit.If(myokit.Less(a, b), a, b)
|
|
468
|
+
self.assertEqual(self.p(x), e)
|
|
469
|
+
|
|
470
|
+
c = myokit.Number(7)
|
|
471
|
+
x = '<apply><max/><cn>7.0</cn><ci>a</ci></apply>'
|
|
472
|
+
e = myokit.If(myokit.More(c, a), c, a)
|
|
473
|
+
self.assertEqual(self.p(x), e)
|
|
474
|
+
|
|
475
|
+
x = f'<apply><min/><ci>a</ci>{x}</apply>'
|
|
476
|
+
e = myokit.If(myokit.Less(a, e), a, e)
|
|
477
|
+
self.assertEqual(self.p(x), e)
|
|
478
|
+
|
|
479
|
+
# Unsupported number of operands
|
|
480
|
+
x = '<apply><min/><ci>a</ci></apply>'
|
|
481
|
+
self.assertRaisesRegex(mathml.MathMLError, 'Only binary', self.p, x)
|
|
482
|
+
x = '<apply><min/><ci>a</ci><ci>b</ci><ci>c</ci></apply>'
|
|
483
|
+
self.assertRaisesRegex(mathml.MathMLError, 'Only binary', self.p, x)
|
|
484
|
+
|
|
485
|
+
def test_functions_power(self):
|
|
486
|
+
# Tests parsing powers
|
|
487
|
+
|
|
488
|
+
a = myokit.Name('a')
|
|
489
|
+
b = myokit.Number(1)
|
|
490
|
+
c = myokit.Number(2)
|
|
491
|
+
e = myokit.Power(a, b)
|
|
492
|
+
x = '<apply><power/><ci>a</ci><cn>1.0</cn></apply>'
|
|
493
|
+
self.assertEqual(self.p(x), e)
|
|
494
|
+
|
|
495
|
+
e = myokit.Power(myokit.Power(a, b), c)
|
|
496
|
+
x = ('<apply><power/><apply><power/><ci>a</ci><cn>1.0</cn></apply>'
|
|
497
|
+
'<cn>2.0</cn></apply>')
|
|
498
|
+
self.assertEqual(self.p(x), e)
|
|
499
|
+
|
|
500
|
+
e = myokit.Power(a, myokit.Power(b, c))
|
|
501
|
+
x = ('<apply><power/><ci>a</ci><apply><power/><cn>1.0</cn><cn>2.0</cn>'
|
|
502
|
+
'</apply></apply>')
|
|
503
|
+
self.assertEqual(self.p(x), e)
|
|
504
|
+
|
|
469
505
|
def test_functions_root(self):
|
|
470
506
|
# Tests parsing roots
|
|
471
507
|
|
|
@@ -618,19 +654,28 @@ class ContentMathMLParserTest(unittest.TestCase):
|
|
|
618
654
|
# Real
|
|
619
655
|
x = self.p('<cn>4</cn>')
|
|
620
656
|
self.assertEqual(x, myokit.Number(4))
|
|
621
|
-
x = self.p('<cn> 4 \n </cn>')
|
|
657
|
+
x = self.p('<cn> 4.0 \n </cn>')
|
|
622
658
|
self.assertEqual(x, myokit.Number(4))
|
|
623
659
|
x = self.p('<cn type="real">4</cn>')
|
|
624
660
|
self.assertEqual(x, myokit.Number(4))
|
|
625
661
|
self.assertRaisesRegex(
|
|
626
662
|
mathml.MathMLError, 'Unable to convert contents of <cn>',
|
|
627
663
|
self.p, '<cn>barry</cn>')
|
|
664
|
+
self.assertRaisesRegex(
|
|
665
|
+
mathml.MathMLError, 'Unable to convert contents of <cn>',
|
|
666
|
+
self.p, '<cn>nan</cn>')
|
|
667
|
+
self.assertRaisesRegex(
|
|
668
|
+
mathml.MathMLError, 'Unable to convert contents of <cn>',
|
|
669
|
+
self.p, '<cn>-Infinity</cn>')
|
|
670
|
+
self.assertRaisesRegex(
|
|
671
|
+
mathml.MathMLError, 'Unable to convert contents of <cn>',
|
|
672
|
+
self.p, '<cn>\n+inf</cn>')
|
|
628
673
|
|
|
629
674
|
# Real with base
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
675
|
+
self.assertEqual(
|
|
676
|
+
myokit.Number(4), self.p('<cn type="real" base="10">4</cn>'))
|
|
677
|
+
self.assertEqual(
|
|
678
|
+
myokit.Number(-3), self.p('<cn type="real" base=" 10 ">-3.0</cn>'))
|
|
634
679
|
self.assertRaisesRegex(
|
|
635
680
|
mathml.MathMLError, 'bases other than 10',
|
|
636
681
|
self.p, '<cn type="real" base="9">4</cn>')
|
|
@@ -653,43 +698,58 @@ class ContentMathMLParserTest(unittest.TestCase):
|
|
|
653
698
|
x = self.p('<cn type="integer" base=" 2 ">100</cn>')
|
|
654
699
|
self.assertEqual(x, myokit.Number(4))
|
|
655
700
|
self.assertRaisesRegex(
|
|
656
|
-
mathml.MathMLError, '
|
|
701
|
+
mathml.MathMLError, 'Invalid base specified',
|
|
657
702
|
self.p, '<cn type="integer" base="barry">7</cn>')
|
|
658
703
|
|
|
659
704
|
# Double
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
705
|
+
self.assertEqual(
|
|
706
|
+
myokit.Number(-3), self.p('<cn type="double">-3</cn>'))
|
|
707
|
+
self.assertEqual(
|
|
708
|
+
myokit.Number(4), self.p('<cn type="double">\t4\n</cn>'))
|
|
664
709
|
self.assertRaisesRegex(
|
|
665
710
|
mathml.MathMLError, 'Unable to convert contents of <cn>',
|
|
666
711
|
self.p, '<cn type="double">larry</cn>')
|
|
712
|
+
self.assertRaisesRegex(
|
|
713
|
+
mathml.MathMLError, 'Unable to convert contents of <cn>',
|
|
714
|
+
self.p, '<cn type="double">nan</cn>')
|
|
667
715
|
|
|
668
716
|
# E-notation
|
|
669
717
|
x = self.p('<cn type="e-notation">40<sep/>-1</cn>')
|
|
670
718
|
self.assertEqual(x, myokit.Number(4))
|
|
671
719
|
x = self.p('<cn type="e-notation">\n40<sep/>-1</cn>')
|
|
672
720
|
self.assertEqual(x, myokit.Number(4))
|
|
673
|
-
x = self.p('<cn type="e-notation">40<sep/>\t-
|
|
674
|
-
self.assertEqual(x, myokit.Number(
|
|
721
|
+
x = self.p('<cn type="e-notation"> -40.3<sep/>\t-12</cn>')
|
|
722
|
+
self.assertEqual(x, myokit.Number(-40.3e-12))
|
|
675
723
|
self.assertRaisesRegex(
|
|
676
724
|
mathml.MathMLError, 'e-notation should have the format',
|
|
677
725
|
self.p, '<cn type="e-notation">12</cn>')
|
|
678
726
|
self.assertRaisesRegex(
|
|
679
|
-
mathml.MathMLError, '
|
|
727
|
+
mathml.MathMLError, 'before the separator should be a basic real',
|
|
680
728
|
self.p, '<cn type="e-notation"> <sep/>2</cn>')
|
|
681
729
|
self.assertRaisesRegex(
|
|
682
730
|
mathml.MathMLError, 'missing part before the separator',
|
|
683
731
|
self.p, '<cn type="e-notation"><sep/>2</cn>')
|
|
684
732
|
self.assertRaisesRegex(
|
|
685
|
-
mathml.MathMLError, '
|
|
733
|
+
mathml.MathMLError, 'after the separator should be an integer',
|
|
686
734
|
self.p, '<cn type="e-notation">2<sep/> </cn>')
|
|
687
735
|
self.assertRaisesRegex(
|
|
688
736
|
mathml.MathMLError, 'missing part after the separator',
|
|
689
737
|
self.p, '<cn type="e-notation">2<sep/></cn>')
|
|
690
738
|
self.assertRaisesRegex(
|
|
691
|
-
mathml.MathMLError, '
|
|
739
|
+
mathml.MathMLError, 'before the separator should be a basic real',
|
|
740
|
+
self.p, '<cn type="e-notation">1e3<sep/>1</cn>')
|
|
741
|
+
self.assertRaisesRegex(
|
|
742
|
+
mathml.MathMLError, 'after the separator should be an integer',
|
|
743
|
+
self.p, '<cn type="e-notation">1<sep/>1e3</cn>')
|
|
744
|
+
self.assertRaisesRegex(
|
|
745
|
+
mathml.MathMLError, 'before the separator should be a basic real',
|
|
692
746
|
self.p, '<cn type="e-notation">larry<sep/>2</cn>')
|
|
747
|
+
self.assertRaisesRegex(
|
|
748
|
+
mathml.MathMLError, 'should be a basic real number',
|
|
749
|
+
self.p, '<cn type="e-notation">nan<sep/>2</cn>')
|
|
750
|
+
self.assertRaisesRegex(
|
|
751
|
+
mathml.MathMLError, 'after the separator should be an integer',
|
|
752
|
+
self.p, '<cn type="e-notation">1<sep/>inf</cn>')
|
|
693
753
|
|
|
694
754
|
# Rational
|
|
695
755
|
x = self.p('<cn type="rational">16<sep/>4</cn>')
|
|
@@ -702,7 +762,7 @@ class ContentMathMLParserTest(unittest.TestCase):
|
|
|
702
762
|
mathml.MathMLError, 'Rational number should have the format',
|
|
703
763
|
self.p, '<cn type="rational">12</cn>')
|
|
704
764
|
self.assertRaisesRegex(
|
|
705
|
-
mathml.MathMLError, '
|
|
765
|
+
mathml.MathMLError, 'before the separator should be an integer',
|
|
706
766
|
self.p, '<cn type="rational"> <sep/>2</cn>')
|
|
707
767
|
self.assertRaisesRegex(
|
|
708
768
|
mathml.MathMLError, 'missing part before the separator',
|
|
@@ -711,10 +771,10 @@ class ContentMathMLParserTest(unittest.TestCase):
|
|
|
711
771
|
mathml.MathMLError, 'missing part after the separator',
|
|
712
772
|
self.p, '<cn type="rational">1<sep/></cn>')
|
|
713
773
|
self.assertRaisesRegex(
|
|
714
|
-
mathml.MathMLError, '
|
|
774
|
+
mathml.MathMLError, 'after the separator should be an integer',
|
|
715
775
|
self.p, '<cn type="rational">1<sep/> </cn>')
|
|
716
776
|
self.assertRaisesRegex(
|
|
717
|
-
mathml.MathMLError, '
|
|
777
|
+
mathml.MathMLError, 'before the separator should be an integer',
|
|
718
778
|
self.p, '<cn type="rational">larry<sep/>2</cn>')
|
|
719
779
|
|
|
720
780
|
# Unknown type
|
|
@@ -302,7 +302,7 @@ class NumPyExpressionWriterTest(myokit.tests.ExpressionWriterTestCase):
|
|
|
302
302
|
if arguments:
|
|
303
303
|
arguments = [numpy.array(arg) for arg in arguments]
|
|
304
304
|
values = function(*arguments)
|
|
305
|
-
self.
|
|
305
|
+
self.assertLess(numpy.max(numpy.abs(values - expected)), 1e-12)
|
|
306
306
|
else:
|
|
307
307
|
value = function()
|
|
308
308
|
self.assertEqual(value, expected)
|
|
@@ -400,6 +400,7 @@ class ModelBuildTest(unittest.TestCase):
|
|
|
400
400
|
# Keywords
|
|
401
401
|
self.assertRaises(myokit.InvalidNameError, m.add_component, 'and')
|
|
402
402
|
self.assertRaises(myokit.InvalidNameError, m.add_component, 'bind')
|
|
403
|
+
self.assertRaises(myokit.InvalidNameError, m.add_component, 'infinity')
|
|
403
404
|
# Test adding variable to component
|
|
404
405
|
c1 = m.add_component('c1')
|
|
405
406
|
# Duplicate
|
|
@@ -414,6 +415,7 @@ class ModelBuildTest(unittest.TestCase):
|
|
|
414
415
|
# Keywords
|
|
415
416
|
self.assertRaises(myokit.InvalidNameError, c0.add_variable, 'or')
|
|
416
417
|
self.assertRaises(myokit.InvalidNameError, c0.add_variable, 'label')
|
|
418
|
+
self.assertRaises(myokit.InvalidNameError, c0.add_variable, 'nan')
|
|
417
419
|
|
|
418
420
|
# Test adding variable to variable
|
|
419
421
|
v1 = c0.add_variable('c0')
|
myokit/tests/test_parsing.py
CHANGED
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
# This file is part of Myokit.
|
|
7
7
|
# See http://myokit.org for copyright, sharing, and licensing details.
|
|
8
8
|
#
|
|
9
|
+
import math
|
|
9
10
|
import os
|
|
10
11
|
import unittest
|
|
11
12
|
|
|
@@ -36,6 +37,12 @@ class TokenizerTest(unittest.TestCase):
|
|
|
36
37
|
s = Tokenizer('.30')
|
|
37
38
|
self.assertEqual(next(s), (p.FLOAT, '.30', 1, 0))
|
|
38
39
|
|
|
40
|
+
# Infinity and nan count as numbers
|
|
41
|
+
s = Tokenizer('infinity')
|
|
42
|
+
self.assertEqual(next(s), (p.INF, 'infinity', 1, 0))
|
|
43
|
+
s = Tokenizer('nan')
|
|
44
|
+
self.assertEqual(next(s), (p.NAN, 'nan', 1, 0))
|
|
45
|
+
|
|
39
46
|
# Finished? Then should get a StopIteration
|
|
40
47
|
self.assertEqual(s.peek()[0], p.EOL)
|
|
41
48
|
self.assertEqual(next(s)[0], p.EOL)
|
|
@@ -716,6 +723,31 @@ class PhasedParseTest(unittest.TestCase):
|
|
|
716
723
|
self.assertRaisesRegex(
|
|
717
724
|
myokit.ParseError, 'not be a condition', p, code)
|
|
718
725
|
|
|
726
|
+
def test_parse_nan_inf(self):
|
|
727
|
+
# Nan and inf are recognised as numbers, and can have units
|
|
728
|
+
from myokit._parsing import parse_model as p
|
|
729
|
+
|
|
730
|
+
s = ('[[model]]',
|
|
731
|
+
'[x]',
|
|
732
|
+
't = 0 bind time',
|
|
733
|
+
'a = infinity + nan [mV]',
|
|
734
|
+
'b = -infinity')
|
|
735
|
+
e = p(s).get('x.a').rhs()
|
|
736
|
+
self.assertIsInstance(e, myokit.Plus)
|
|
737
|
+
self.assertIsInstance(e[0], myokit.Number)
|
|
738
|
+
self.assertGreater(e[0].value(), 0)
|
|
739
|
+
self.assertTrue(math.isinf(e[0].value()))
|
|
740
|
+
self.assertEqual(e[0].unit(), None)
|
|
741
|
+
self.assertTrue(math.isnan(e[1].value()))
|
|
742
|
+
self.assertEqual(e[1].unit(), myokit.units.mV)
|
|
743
|
+
e = p(s).get('x.b').rhs()
|
|
744
|
+
self.assertIsInstance(e, myokit.PrefixMinus)
|
|
745
|
+
self.assertLess(e.eval(), 0)
|
|
746
|
+
self.assertTrue(math.isinf(e.eval()))
|
|
747
|
+
self.assertIsInstance(e[0], myokit.Number)
|
|
748
|
+
self.assertGreater(e[0].value(), 0)
|
|
749
|
+
self.assertTrue(math.isinf(e[0].value()))
|
|
750
|
+
|
|
719
751
|
def test_parse_unit(self):
|
|
720
752
|
# Test :meth:`parse_unit` and :meth:`parse_unit_string`.
|
|
721
753
|
from myokit._parsing import parse_unit_string as p
|
|
@@ -522,16 +522,17 @@ class SimulationTest(unittest.TestCase):
|
|
|
522
522
|
ValueError, 'no `apd_variable` specified',
|
|
523
523
|
self.sim.run, 1, apd_threshold=12)
|
|
524
524
|
|
|
525
|
-
def
|
|
526
|
-
# Tests Simulation.crash_state
|
|
525
|
+
def test_crash_state_inputs_log(self):
|
|
526
|
+
# Tests Simulation.crash_state, crash_inputs, and crash_log
|
|
527
527
|
|
|
528
528
|
m = self.model.clone()
|
|
529
529
|
istim = m.get('membrane.i_stim')
|
|
530
530
|
istim.set_rhs('engine.pace / stim_amplitude')
|
|
531
|
-
|
|
531
|
+
|
|
532
532
|
s = myokit.Simulation(m, self.protocol)
|
|
533
533
|
self.assertIsNone(s.crash_state())
|
|
534
534
|
self.assertIsNone(s.crash_inputs())
|
|
535
|
+
self.assertIsNone(s.crash_log())
|
|
535
536
|
s.run(1)
|
|
536
537
|
self.assertIsNone(s.crash_state())
|
|
537
538
|
s.set_constant('membrane.i_stim.stim_amplitude', 0)
|
|
@@ -545,7 +546,8 @@ class SimulationTest(unittest.TestCase):
|
|
|
545
546
|
self.assertEqual(s.crash_inputs()['pace'], 0)
|
|
546
547
|
self.assertGreaterEqual(s.crash_inputs()['realtime'], 0)
|
|
547
548
|
self.assertGreater(s.crash_inputs()['evaluations'], 0)
|
|
548
|
-
|
|
549
|
+
self.assertIsInstance(s.crash_log(), myokit.DataLog)
|
|
550
|
+
self.assertGreater(s.crash_log().length(), 0)
|
|
549
551
|
|
|
550
552
|
# Test crash at later time
|
|
551
553
|
istim.set_rhs('if(engine.time == 5, 1 / (5 - engine.time), 0)')
|
|
@@ -561,6 +563,8 @@ class SimulationTest(unittest.TestCase):
|
|
|
561
563
|
self.assertEqual(s.crash_inputs()['time'], 5)
|
|
562
564
|
self.assertEqual(s.crash_inputs()['pace'], 1)
|
|
563
565
|
self.assertGreater(s.crash_inputs()['realtime'], 0)
|
|
566
|
+
self.assertIsInstance(s.crash_log(), myokit.DataLog)
|
|
567
|
+
self.assertGreater(s.crash_log().length(), 0)
|
|
564
568
|
|
|
565
569
|
# Above should both crash via cvode flag set. Next should be halted by
|
|
566
570
|
# C code for too many zero steps
|
|
@@ -572,6 +576,8 @@ class SimulationTest(unittest.TestCase):
|
|
|
572
576
|
self.assertAlmostEqual(s.crash_inputs()['time'], 5)
|
|
573
577
|
self.assertEqual(s.crash_inputs()['pace'], 1)
|
|
574
578
|
self.assertGreater(s.crash_inputs()['realtime'], 0)
|
|
579
|
+
self.assertIsInstance(s.crash_log(), myokit.DataLog)
|
|
580
|
+
self.assertGreater(s.crash_log().length(), 0)
|
|
575
581
|
|
|
576
582
|
# Test deprecated alias of crash_state
|
|
577
583
|
x = s.crash_state()
|
myokit/tests/test_variable.py
CHANGED
|
@@ -768,14 +768,18 @@ class VariableTest(unittest.TestCase):
|
|
|
768
768
|
v.set_initial_value(myokit.Number(12, myokit.units.g))
|
|
769
769
|
self.assertEqual(v.initial_value(), myokit.Number(12, myokit.units.g))
|
|
770
770
|
|
|
771
|
-
# Test setting expressions
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
771
|
+
# Test setting expressions and strings
|
|
772
|
+
e = myokit.Plus(myokit.Number(1), myokit.Number(11))
|
|
773
|
+
v.set_initial_value(e)
|
|
774
|
+
self.assertEqual(v.initial_value(), e)
|
|
775
775
|
v.set_initial_value('1 + c.w')
|
|
776
776
|
self.assertEqual(v.initial_value(),
|
|
777
777
|
myokit.Plus(myokit.Number(1), myokit.Name(w)))
|
|
778
778
|
|
|
779
|
+
# Test setting using variables
|
|
780
|
+
v.set_initial_value(w)
|
|
781
|
+
self.assertEqual(v.initial_value(), myokit.Name(w))
|
|
782
|
+
|
|
779
783
|
# Strings must use global context
|
|
780
784
|
self.assertRaisesRegex(myokit.ParseError, 'No component specified for',
|
|
781
785
|
v.set_initial_value, '1 / w')
|
|
@@ -802,9 +806,8 @@ class VariableTest(unittest.TestCase):
|
|
|
802
806
|
x = v.add_variable('x')
|
|
803
807
|
x.set_rhs(1)
|
|
804
808
|
v.set_initial_value(x.lhs())
|
|
805
|
-
self.
|
|
806
|
-
myokit.IllegalReferenceInInitialValueError, m.validate)
|
|
807
|
-
v.set_initial_value(x)
|
|
809
|
+
self.assertRaisesRegex(
|
|
810
|
+
myokit.IllegalReferenceInInitialValueError, 'is nest', m.validate)
|
|
808
811
|
|
|
809
812
|
# InitialValue and PartialDerivative can not be used
|
|
810
813
|
v.set_initial_value(myokit.PartialDerivative(v.lhs(), x.lhs()))
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
2
|
Name: myokit
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.39.0
|
|
4
4
|
Summary: A modeling and simulation tool for cardiac cellular electrophysiology
|
|
5
5
|
Home-page: http://myokit.org
|
|
6
6
|
Author: Michael Clerx
|
|
@@ -28,22 +28,35 @@ Requires-Dist: lxml
|
|
|
28
28
|
Requires-Dist: matplotlib>=2.2
|
|
29
29
|
Requires-Dist: numpy
|
|
30
30
|
Requires-Dist: setuptools
|
|
31
|
+
Provides-Extra: docs
|
|
32
|
+
Requires-Dist: sphinx>=1.7.4; extra == "docs"
|
|
31
33
|
Provides-Extra: dev
|
|
32
34
|
Requires-Dist: coverage; extra == "dev"
|
|
33
35
|
Requires-Dist: flake8>=3; extra == "dev"
|
|
34
|
-
Provides-Extra: docs
|
|
35
|
-
Requires-Dist: sphinx>=1.7.4; extra == "docs"
|
|
36
|
-
Provides-Extra: gui
|
|
37
|
-
Requires-Dist: pyqt6; extra == "gui"
|
|
38
|
-
Requires-Dist: sip; extra == "gui"
|
|
39
36
|
Provides-Extra: optional
|
|
40
37
|
Requires-Dist: scipy; extra == "optional"
|
|
41
38
|
Requires-Dist: sympy; extra == "optional"
|
|
39
|
+
Provides-Extra: gui
|
|
40
|
+
Requires-Dist: pyqt6; extra == "gui"
|
|
41
|
+
Requires-Dist: sip; extra == "gui"
|
|
42
42
|
Provides-Extra: pyqt
|
|
43
43
|
Requires-Dist: pyqt6; extra == "pyqt"
|
|
44
44
|
Requires-Dist: sip; extra == "pyqt"
|
|
45
45
|
Provides-Extra: pyside
|
|
46
46
|
Requires-Dist: pyside6; extra == "pyside"
|
|
47
|
+
Dynamic: author
|
|
48
|
+
Dynamic: author-email
|
|
49
|
+
Dynamic: classifier
|
|
50
|
+
Dynamic: description
|
|
51
|
+
Dynamic: description-content-type
|
|
52
|
+
Dynamic: home-page
|
|
53
|
+
Dynamic: license
|
|
54
|
+
Dynamic: license-file
|
|
55
|
+
Dynamic: project-url
|
|
56
|
+
Dynamic: provides-extra
|
|
57
|
+
Dynamic: requires-dist
|
|
58
|
+
Dynamic: requires-python
|
|
59
|
+
Dynamic: summary
|
|
47
60
|
|
|
48
61
|
[](https://github.com/myokit/myokit/actions/workflows/unit-tests-ubuntu.yml)
|
|
49
62
|
[](https://github.com/myokit/myokit/actions/workflows/unit-tests-macos.yml)
|
|
@@ -51,6 +64,8 @@ Requires-Dist: pyside6; extra == "pyside"
|
|
|
51
64
|
[](https://github.com/myokit/myokit/actions/workflows/unit-tests-windows-miniconda.yml)
|
|
52
65
|
[](https://codecov.io/gh/myokit/myokit)
|
|
53
66
|
[](https://myokit.readthedocs.io/?badge=latest)
|
|
67
|
+
[](https://pypi.org/project/myokit/)
|
|
68
|
+
[](https://anaconda.org/conda-forge/myokit)
|
|
54
69
|
|
|
55
70
|

|
|
56
71
|
|