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.
- myokit/__init__.py +9 -36
- myokit/__main__.py +76 -142
- myokit/_aux.py +62 -16
- myokit/_bin/example.mmt +1 -2
- myokit/_bin/install-win/menu.json +7 -7
- myokit/_config.py +22 -31
- myokit/_datablock.py +30 -74
- myokit/_datalog.py +49 -72
- myokit/_err.py +25 -24
- myokit/_expressions.py +50 -68
- myokit/_io.py +15 -27
- myokit/_model_api.py +453 -249
- myokit/_myokit_version.py +1 -5
- myokit/_parsing.py +38 -44
- myokit/_progress.py +5 -8
- myokit/_protocol.py +99 -9
- myokit/_sim/__init__.py +7 -24
- myokit/_sim/cable.c +6 -8
- myokit/_sim/cable.py +6 -8
- myokit/_sim/cmodel.h +125 -70
- myokit/_sim/cmodel.py +12 -14
- myokit/_sim/compiler.py +1 -4
- myokit/_sim/cvodessim.c +196 -118
- myokit/_sim/cvodessim.py +130 -103
- myokit/_sim/differential.hpp +4 -4
- myokit/_sim/fiber_tissue.c +4 -8
- myokit/_sim/fiber_tissue.py +11 -13
- myokit/_sim/jacobian.cpp +2 -2
- myokit/_sim/jacobian.py +11 -8
- myokit/_sim/mcl.h +53 -55
- myokit/_sim/opencl.py +21 -27
- myokit/_sim/openclsim.c +3 -7
- myokit/_sim/openclsim.cl +3 -3
- myokit/_sim/openclsim.py +49 -40
- myokit/_sim/pacing.h +36 -16
- myokit/_sim/rhs.c +6 -13
- myokit/_sim/rhs.py +5 -14
- myokit/_sim/sundials.py +1 -4
- myokit/_system.py +10 -16
- myokit/_unit.py +4 -13
- myokit/float.py +0 -3
- myokit/formats/__init__.py +8 -10
- myokit/formats/ansic/__init__.py +0 -3
- myokit/formats/ansic/_ewriter.py +2 -4
- myokit/formats/ansic/_exporter.py +1 -4
- myokit/formats/ansic/template/cable.c +4 -4
- myokit/formats/ansic/template/euler.c +5 -5
- myokit/formats/ansic/template/sim.c +6 -6
- myokit/formats/axon/__init__.py +1 -3
- myokit/formats/axon/_abf.py +12 -17
- myokit/formats/axon/_atf.py +5 -6
- myokit/formats/axon/_importer.py +0 -3
- myokit/formats/cellml/__init__.py +0 -3
- myokit/formats/cellml/_ewriter.py +3 -6
- myokit/formats/cellml/_exporter.py +3 -6
- myokit/formats/cellml/_importer.py +1 -4
- myokit/formats/cellml/v1/__init__.py +0 -4
- myokit/formats/cellml/v1/_api.py +8 -11
- myokit/formats/cellml/v1/_parser.py +2 -5
- myokit/formats/cellml/v1/_writer.py +2 -11
- myokit/formats/cellml/v2/__init__.py +0 -3
- myokit/formats/cellml/v2/_api.py +8 -17
- myokit/formats/cellml/v2/_parser.py +2 -5
- myokit/formats/cellml/v2/_writer.py +1 -4
- myokit/formats/channelml/__init__.py +0 -3
- myokit/formats/channelml/_importer.py +11 -21
- myokit/formats/cpp/__init__.py +1 -3
- myokit/formats/cpp/_ewriter.py +0 -3
- myokit/formats/cuda/__init__.py +0 -3
- myokit/formats/cuda/_ewriter.py +2 -4
- myokit/formats/cuda/_exporter.py +0 -3
- myokit/formats/cuda/template/kernel.cu +8 -5
- myokit/formats/easyml/__init__.py +0 -3
- myokit/formats/easyml/_ewriter.py +9 -11
- myokit/formats/easyml/_exporter.py +2 -5
- myokit/formats/html/__init__.py +0 -3
- myokit/formats/html/_exporter.py +0 -3
- myokit/formats/html/_flatten.py +5 -21
- myokit/formats/latex/__init__.py +0 -3
- myokit/formats/latex/_ewriter.py +1 -4
- myokit/formats/latex/_exporter.py +4 -6
- myokit/formats/mathml/__init__.py +0 -3
- myokit/formats/mathml/_ewriter.py +2 -11
- myokit/formats/mathml/_parser.py +4 -6
- myokit/formats/matlab/__init__.py +0 -3
- myokit/formats/matlab/_ewriter.py +1 -4
- myokit/formats/matlab/_exporter.py +2 -5
- myokit/formats/matlab/template/main.m +3 -2
- myokit/formats/opencl/__init__.py +0 -3
- myokit/formats/opencl/_ewriter.py +2 -4
- myokit/formats/opencl/_exporter.py +2 -5
- myokit/formats/opencl/template/cable.c +10 -10
- myokit/formats/opencl/template/kernel.cl +1 -1
- myokit/formats/opencl/template/minilog.py +1 -1
- myokit/formats/python/__init__.py +0 -3
- myokit/formats/python/_ewriter.py +2 -5
- myokit/formats/python/_exporter.py +0 -3
- myokit/formats/python/template/sim.py +14 -14
- myokit/formats/sbml/__init__.py +0 -3
- myokit/formats/sbml/_api.py +50 -44
- myokit/formats/sbml/_importer.py +1 -4
- myokit/formats/sbml/_parser.py +2 -5
- myokit/formats/stan/__init__.py +0 -3
- myokit/formats/stan/_ewriter.py +2 -4
- myokit/formats/stan/_exporter.py +2 -5
- myokit/formats/stan/template/cell.stan +3 -3
- myokit/formats/sympy/__init__.py +0 -3
- myokit/formats/sympy/_ereader.py +1 -4
- myokit/formats/sympy/_ewriter.py +2 -5
- myokit/formats/wcp/__init__.py +0 -3
- myokit/formats/wcp/_wcp.py +2 -8
- myokit/formats/xml/__init__.py +0 -3
- myokit/formats/xml/_exporter.py +0 -3
- myokit/formats/xml/_split.py +0 -3
- myokit/gui/__init__.py +80 -246
- myokit/gui/datablock_viewer.py +103 -86
- myokit/gui/datalog_viewer.py +214 -66
- myokit/gui/explorer.py +15 -21
- myokit/gui/ide.py +171 -144
- myokit/gui/progress.py +9 -9
- myokit/gui/source.py +406 -375
- myokit/gui/vargrapher.py +2 -12
- myokit/lib/deps.py +12 -13
- myokit/lib/guess.py +3 -4
- myokit/lib/hh.py +20 -18
- myokit/lib/markov.py +21 -20
- myokit/lib/multi.py +1 -3
- myokit/lib/plots.py +20 -9
- myokit/pacing.py +0 -3
- myokit/pype.py +7 -18
- myokit/tests/__init__.py +3 -6
- myokit/tests/ansic_event_based_pacing.py +1 -4
- myokit/tests/ansic_fixed_form_pacing.py +3 -6
- myokit/tests/data/beeler-1977-model-compare-b.mmt +2 -2
- myokit/tests/data/clancy-1999-fitting.mmt +1 -0
- myokit/tests/test_aux.py +13 -28
- myokit/tests/test_cellml_v1_api.py +4 -19
- myokit/tests/test_cellml_v1_parser.py +0 -15
- myokit/tests/test_cellml_v1_writer.py +0 -9
- myokit/tests/test_cellml_v2_api.py +4 -19
- myokit/tests/test_cellml_v2_parser.py +0 -15
- myokit/tests/test_cellml_v2_writer.py +0 -9
- myokit/tests/test_cmodel.py +16 -22
- myokit/tests/test_compiler_detection.py +1 -11
- myokit/tests/test_component.py +108 -56
- myokit/tests/test_config.py +34 -67
- myokit/tests/test_datablock.py +1 -9
- myokit/tests/test_datalog.py +19 -24
- myokit/tests/test_dependency_checking.py +8 -23
- myokit/tests/test_expressions.py +0 -9
- myokit/tests/test_float.py +1 -5
- myokit/tests/test_formats.py +0 -9
- myokit/tests/test_formats_axon.py +1 -9
- myokit/tests/test_formats_cellml.py +0 -15
- myokit/tests/test_formats_channelml.py +0 -15
- myokit/tests/test_formats_easyml.py +0 -14
- myokit/tests/test_formats_exporters.py +1 -16
- myokit/tests/test_formats_expression_writers.py +1 -17
- myokit/tests/test_formats_html.py +0 -3
- myokit/tests/test_formats_importers.py +1 -16
- myokit/tests/test_formats_mathml_content.py +0 -9
- myokit/tests/test_formats_mathml_presentation.py +0 -9
- myokit/tests/test_formats_opencl.py +0 -10
- myokit/tests/test_formats_sbml.py +0 -15
- myokit/tests/test_formats_sympy.py +0 -9
- myokit/tests/test_formats_wcp.py +1 -3
- myokit/tests/test_io.py +27 -27
- myokit/tests/test_jacobian_calculator.py +6 -14
- myokit/tests/test_jacobian_tracer.py +0 -9
- myokit/tests/test_lib_deps.py +0 -9
- myokit/tests/test_lib_guess.py +0 -9
- myokit/tests/test_lib_hh.py +18 -12
- myokit/tests/test_lib_markov.py +21 -13
- myokit/tests/test_lib_multi.py +0 -9
- myokit/tests/test_lib_plots.py +13 -8
- myokit/tests/test_meta.py +0 -3
- myokit/tests/test_model.py +390 -96
- myokit/tests/test_model_building.py +44 -96
- myokit/tests/test_opencl_info.py +5 -14
- myokit/tests/test_pacing_factory.py +0 -3
- myokit/tests/test_pacing_system_c.py +1 -23
- myokit/tests/test_pacing_system_py.py +0 -9
- myokit/tests/test_parsing.py +139 -56
- myokit/tests/test_progress_reporters.py +0 -3
- myokit/tests/test_protocol.py +0 -9
- myokit/tests/test_protocol_floating_point.py +1 -10
- myokit/tests/test_protocol_time_series.py +82 -0
- myokit/tests/test_pype.py +0 -9
- myokit/tests/test_quantity.py +0 -9
- myokit/tests/test_rhs_benchmarker.py +1 -9
- myokit/tests/test_sbml_api.py +27 -42
- myokit/tests/test_sbml_parser.py +4 -19
- myokit/tests/test_simulation_1d.py +45 -25
- myokit/tests/test_simulation_cvodes.py +321 -55
- myokit/tests/test_simulation_cvodes_from_disk.py +0 -3
- myokit/tests/test_simulation_fiber_tissue.py +39 -12
- myokit/tests/test_simulation_log_interval.py +1 -431
- myokit/tests/test_simulation_opencl.py +69 -48
- myokit/tests/test_simulation_opencl_log_interval.py +1 -3
- myokit/tests/test_simulation_opencl_vs_cvode.py +1 -10
- myokit/tests/test_simulation_opencl_vs_sim1d.py +1 -10
- myokit/tests/test_system_info.py +1 -11
- myokit/tests/test_tools.py +0 -9
- myokit/tests/test_unit.py +1 -10
- myokit/tests/test_user_functions.py +0 -10
- myokit/tests/test_variable.py +231 -27
- myokit/tools.py +5 -21
- myokit/units.py +5 -3
- {myokit-1.33.9.dist-info → myokit-1.35.0.dist-info}/METADATA +12 -15
- myokit-1.35.0.dist-info/RECORD +391 -0
- {myokit-1.33.9.dist-info → myokit-1.35.0.dist-info}/WHEEL +1 -1
- {myokit-1.33.9.dist-info → myokit-1.35.0.dist-info}/entry_points.txt +0 -1
- myokit/_exec_new.py +0 -15
- myokit/_exec_old.py +0 -15
- myokit/_sim/cvodesim.c +0 -1551
- myokit/_sim/cvodesim.py +0 -674
- myokit/_sim/icsim.cpp +0 -563
- myokit/_sim/icsim.py +0 -363
- myokit/_sim/psim.cpp +0 -656
- myokit/_sim/psim.py +0 -493
- myokit/lib/common.py +0 -1094
- myokit/tests/test_lib_common.py +0 -130
- myokit/tests/test_simulation_cvode.py +0 -612
- myokit/tests/test_simulation_ic.py +0 -108
- myokit/tests/test_simulation_p.py +0 -223
- myokit-1.33.9.dist-info/RECORD +0 -403
- /myokit/formats/opencl/template/{test → test.sh} +0 -0
- {myokit-1.33.9.dist-info → myokit-1.35.0.dist-info}/LICENSE.txt +0 -0
- {myokit-1.33.9.dist-info → myokit-1.35.0.dist-info}/top_level.txt +0 -0
myokit/tests/test_model.py
CHANGED
|
@@ -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
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
self.
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
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'
|
|
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'
|
|
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'
|
|
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'
|
|
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
|
-
#
|
|
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').
|
|
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').
|
|
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').
|
|
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.
|
|
1648
|
+
s1 = m.initial_values(True)
|
|
1458
1649
|
with TemporaryDirectory() as d:
|
|
1459
1650
|
path = d.path('state.csv')
|
|
1460
|
-
|
|
1461
|
-
|
|
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.
|
|
1465
|
-
self.assertNotEqual(m.
|
|
1466
|
-
|
|
1467
|
-
|
|
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
|
|
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),
|
|
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
|
|