myokit 1.38.0__py3-none-any.whl → 1.39.1__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/_config.py +18 -19
- 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.38.0.dist-info → myokit-1.39.1.dist-info}/METADATA +22 -7
- {myokit-1.38.0.dist-info → myokit-1.39.1.dist-info}/RECORD +44 -44
- {myokit-1.38.0.dist-info → myokit-1.39.1.dist-info}/WHEEL +1 -1
- {myokit-1.38.0.dist-info → myokit-1.39.1.dist-info}/entry_points.txt +0 -0
- {myokit-1.38.0.dist-info → myokit-1.39.1.dist-info/licenses}/LICENSE.txt +0 -0
- {myokit-1.38.0.dist-info → myokit-1.39.1.dist-info}/top_level.txt +0 -0
|
@@ -52,9 +52,9 @@ class CellMLParsingError(myokit.ImportError):
|
|
|
52
52
|
"""
|
|
53
53
|
def __init__(self, message, element=None):
|
|
54
54
|
if element is not None:
|
|
55
|
-
try:
|
|
55
|
+
try:
|
|
56
56
|
line = str(element.sourceline)
|
|
57
|
-
message = 'Error on line
|
|
57
|
+
message = f'Error on line {line}. {message}'
|
|
58
58
|
except AttributeError:
|
|
59
59
|
pass
|
|
60
60
|
super().__init__(message)
|
|
@@ -91,7 +91,7 @@ class CellMLParser:
|
|
|
91
91
|
if element.text is not None:
|
|
92
92
|
if element.text.strip():
|
|
93
93
|
raise CellMLParsingError(
|
|
94
|
-
'Text found in
|
|
94
|
+
f'Text found in {self._tag(element, name)}.', element)
|
|
95
95
|
|
|
96
96
|
# Not extension namespaces
|
|
97
97
|
not_ext_ns = (
|
|
@@ -108,9 +108,8 @@ class CellMLParser:
|
|
|
108
108
|
# Check for trailing text
|
|
109
109
|
if child.tail is not None and child.tail.strip():
|
|
110
110
|
raise CellMLParsingError(
|
|
111
|
-
'Text found in
|
|
112
|
-
|
|
113
|
-
child)
|
|
111
|
+
f'Text found in {self._tag(element, name)} (after'
|
|
112
|
+
f' {self._tag(child)} element).', child)
|
|
114
113
|
|
|
115
114
|
# Check if allowed
|
|
116
115
|
if str(child.tag) in allowed:
|
|
@@ -120,8 +119,8 @@ class CellMLParser:
|
|
|
120
119
|
ns = split(child.tag)[0]
|
|
121
120
|
if ns in not_ext_ns:
|
|
122
121
|
raise CellMLParsingError(
|
|
123
|
-
'Unexpected content type in
|
|
124
|
-
|
|
122
|
+
f'Unexpected content type in {self._tag(element, name)},'
|
|
123
|
+
f' found element of type {self._tag(child)}.',
|
|
125
124
|
child)
|
|
126
125
|
else:
|
|
127
126
|
# Check if CellML appearing in non-CellML elements
|
|
@@ -145,8 +144,8 @@ class CellMLParser:
|
|
|
145
144
|
if key not in allowed:
|
|
146
145
|
key = self._item(ns, at)
|
|
147
146
|
raise CellMLParsingError(
|
|
148
|
-
'Unexpected attribute
|
|
149
|
-
|
|
147
|
+
f'Unexpected attribute {key} found in'
|
|
148
|
+
f' {self._tag(element, name)}.', element)
|
|
150
149
|
|
|
151
150
|
def _check_for_cellml_in_extensions(self, element):
|
|
152
151
|
"""
|
|
@@ -158,17 +157,15 @@ class CellMLParser:
|
|
|
158
157
|
ns, at = split(key)
|
|
159
158
|
if ns == self._ns:
|
|
160
159
|
raise CellMLParsingError(
|
|
161
|
-
'CellML attribute
|
|
162
|
-
'
|
|
163
|
-
+ ' (2.4.3).', element)
|
|
160
|
+
f'CellML attribute {self._item(ns, at)} found in extension'
|
|
161
|
+
f' element {self._tag(element)} (2.4.3).', element)
|
|
164
162
|
|
|
165
163
|
# Check if this element has CellML children
|
|
166
164
|
for child in element:
|
|
167
165
|
if split(child.tag)[0] == self._ns:
|
|
168
166
|
raise CellMLParsingError(
|
|
169
|
-
'CellML element
|
|
170
|
-
' extension element
|
|
171
|
-
child)
|
|
167
|
+
f'CellML element {self._tag(child)} found inside'
|
|
168
|
+
f' extension element {self._tag(element)} (2.4.3).', child)
|
|
172
169
|
|
|
173
170
|
# Recurse into children
|
|
174
171
|
self._check_for_cellml_in_extensions(child)
|
|
@@ -193,7 +190,7 @@ class CellMLParser:
|
|
|
193
190
|
# Check uniqueness
|
|
194
191
|
if cmeta_id in self._cmeta_ids:
|
|
195
192
|
raise CellMLParsingError(
|
|
196
|
-
'Duplicate cmeta:id "
|
|
193
|
+
f'Duplicate cmeta:id "{cmeta_id}" (8.5.1).', element)
|
|
197
194
|
|
|
198
195
|
# Store and return
|
|
199
196
|
self._cmeta_ids.add(cmeta_id)
|
|
@@ -255,23 +252,21 @@ class CellMLParser:
|
|
|
255
252
|
if ns is None:
|
|
256
253
|
return item
|
|
257
254
|
elif ns == self._ns:
|
|
258
|
-
return 'cellml:'
|
|
255
|
+
return f'cellml:{item}'
|
|
259
256
|
elif ns == cellml.NS_MATHML:
|
|
260
|
-
return 'mathml:'
|
|
257
|
+
return f'mathml:{item}'
|
|
261
258
|
elif ns == cellml.NS_RDF:
|
|
262
|
-
return 'rdf:'
|
|
259
|
+
return f'rdf:{item}'
|
|
263
260
|
elif ns == cellml.NS_CMETA:
|
|
264
|
-
return 'cmeta:'
|
|
261
|
+
return f'cmeta:{item}'
|
|
265
262
|
else:
|
|
266
|
-
return '{
|
|
263
|
+
return f'{{{ns}}}{item}'
|
|
267
264
|
|
|
268
265
|
def _join(self, element, namespace=None):
|
|
269
266
|
"""
|
|
270
267
|
Joins a ``namespace`` and an ``element`` string into a single string.
|
|
271
268
|
"""
|
|
272
|
-
if namespace is None
|
|
273
|
-
namespace = self._ns
|
|
274
|
-
return '{' + namespace + '}' + element
|
|
269
|
+
return f'{{{self._ns if namespace is None else namespace}}}{element}'
|
|
275
270
|
|
|
276
271
|
def parse(self, root):
|
|
277
272
|
"""
|
|
@@ -301,7 +296,7 @@ class CellMLParser:
|
|
|
301
296
|
parser = etree.XMLParser(remove_comments=True)
|
|
302
297
|
tree = etree.parse(path, parser=parser)
|
|
303
298
|
except Exception as e:
|
|
304
|
-
raise CellMLParsingError('Unable to parse XML: '
|
|
299
|
+
raise CellMLParsingError(f'Unable to parse XML: {e}')
|
|
305
300
|
|
|
306
301
|
# Parse content
|
|
307
302
|
return self.parse(tree.getroot())
|
|
@@ -316,7 +311,7 @@ class CellMLParser:
|
|
|
316
311
|
try:
|
|
317
312
|
root = etree.fromstring(text)
|
|
318
313
|
except Exception as e:
|
|
319
|
-
raise CellMLParsingError('Unable to parse XML: '
|
|
314
|
+
raise CellMLParsingError(f'Unable to parse XML: {e}')
|
|
320
315
|
|
|
321
316
|
# Parse content
|
|
322
317
|
return self.parse(root)
|
|
@@ -358,9 +353,19 @@ class CellMLParser:
|
|
|
358
353
|
for child in self._sort_units(element, model):
|
|
359
354
|
self._parse_units(child, component)
|
|
360
355
|
|
|
361
|
-
# Create variables
|
|
356
|
+
# Create variables, set interfaces, prepare to set initial values
|
|
357
|
+
initial_values = []
|
|
362
358
|
for child in element.findall(self._join('variable')):
|
|
363
|
-
self._parse_variable(child, component)
|
|
359
|
+
init = self._parse_variable(child, component)
|
|
360
|
+
if init is not None:
|
|
361
|
+
initial_values.append(init)
|
|
362
|
+
|
|
363
|
+
# Set initial values
|
|
364
|
+
for child, variable, value in initial_values:
|
|
365
|
+
try:
|
|
366
|
+
variable.set_initial_value(value)
|
|
367
|
+
except myokit.formats.cellml.v1.CellMLError as e:
|
|
368
|
+
raise CellMLParsingError(str(e), child)
|
|
364
369
|
|
|
365
370
|
def _parse_connection(self, element, model, connected):
|
|
366
371
|
"""
|
|
@@ -375,7 +380,7 @@ class CellMLParser:
|
|
|
375
380
|
if len(map_components) != 1:
|
|
376
381
|
raise CellMLParsingError(
|
|
377
382
|
'A connection must contain exactly one map_components element,'
|
|
378
|
-
' found
|
|
383
|
+
f' found {len(map_components)} (3.4.4.1).', element)
|
|
379
384
|
|
|
380
385
|
# Check at least one map_variables is present
|
|
381
386
|
map_variables = element.findall(self._join('map_variables'))
|
|
@@ -421,8 +426,8 @@ class CellMLParser:
|
|
|
421
426
|
if c1 == c2:
|
|
422
427
|
raise CellMLParsingError(
|
|
423
428
|
'The component_1 and component_2 attributes in a'
|
|
424
|
-
' map_components element must be different, got "'
|
|
425
|
-
|
|
429
|
+
f' map_components element must be different, got "{c1}"'
|
|
430
|
+
' twice (3.4.5.4).', element)
|
|
426
431
|
|
|
427
432
|
# Get components
|
|
428
433
|
try:
|
|
@@ -430,22 +435,22 @@ class CellMLParser:
|
|
|
430
435
|
except KeyError:
|
|
431
436
|
raise CellMLParsingError(
|
|
432
437
|
'A map_components component_1 attribute must refer to a'
|
|
433
|
-
' component in the current model, got "
|
|
434
|
-
|
|
438
|
+
f' component in the current model, got "{c1}" (3.4.5.2).',
|
|
439
|
+
element)
|
|
435
440
|
try:
|
|
436
441
|
c2 = model.component(c2)
|
|
437
442
|
except KeyError:
|
|
438
443
|
raise CellMLParsingError(
|
|
439
444
|
'A map_components component_2 attribute must refer to a'
|
|
440
|
-
' component in the current model, got "
|
|
441
|
-
|
|
445
|
+
f' component in the current model, got "{c2}" (3.4.5.3).',
|
|
446
|
+
element)
|
|
442
447
|
|
|
443
448
|
# Check components are not yet connected
|
|
444
449
|
if (c1, c2) in connected:
|
|
445
450
|
raise CellMLParsingError(
|
|
446
451
|
'Each connection in a model must connect a unique pair of'
|
|
447
|
-
' components, found multiple for "
|
|
448
|
-
|
|
452
|
+
f' components, found multiple for "{c1.name()}" and'
|
|
453
|
+
f' "{c2.name()}" (3.4.5.4).', element)
|
|
449
454
|
connected.add((c1, c2))
|
|
450
455
|
connected.add((c2, c1))
|
|
451
456
|
|
|
@@ -484,15 +489,13 @@ class CellMLParser:
|
|
|
484
489
|
except KeyError:
|
|
485
490
|
raise CellMLParsingError(
|
|
486
491
|
'A map_variables variable_1 attribute must refer to a'
|
|
487
|
-
' variable in component_1, got "
|
|
488
|
-
element)
|
|
492
|
+
f' variable in component_1, got "{v1}" (3.4.6.2).', element)
|
|
489
493
|
try:
|
|
490
494
|
v2 = c2.variable(v2)
|
|
491
495
|
except KeyError:
|
|
492
496
|
raise CellMLParsingError(
|
|
493
497
|
'A map_variables variable_2 attribute must refer to a'
|
|
494
|
-
' variable in component_2, got "
|
|
495
|
-
element)
|
|
498
|
+
f' variable in component_2, got "{v1}" (3.4.6.3).', element)
|
|
496
499
|
|
|
497
500
|
# Connect variables
|
|
498
501
|
model = c1.model()
|
|
@@ -515,7 +518,7 @@ class CellMLParser:
|
|
|
515
518
|
text = self.flatten(element)
|
|
516
519
|
if text:
|
|
517
520
|
if 'documentation' in model.meta:
|
|
518
|
-
model.meta['documentation'] += '\n\n'
|
|
521
|
+
model.meta['documentation'] += f'\n\n{text}'
|
|
519
522
|
else:
|
|
520
523
|
model.meta['documentation'] = text
|
|
521
524
|
|
|
@@ -577,8 +580,8 @@ class CellMLParser:
|
|
|
577
580
|
except KeyError:
|
|
578
581
|
raise CellMLParsingError(
|
|
579
582
|
'A component_ref\'s component attribute must reference a'
|
|
580
|
-
' component in the same model, got "'
|
|
581
|
-
|
|
583
|
+
f' component in the same model, got "{component}" (6.4.3.3).',
|
|
584
|
+
element)
|
|
582
585
|
|
|
583
586
|
# Check allowed content
|
|
584
587
|
self._check_allowed_content(element, ['component_ref'], ['component'])
|
|
@@ -591,9 +594,8 @@ class CellMLParser:
|
|
|
591
594
|
if component.parent() is not None:
|
|
592
595
|
raise CellMLParsingError(
|
|
593
596
|
'A component can only have a single encapsulation parent:'
|
|
594
|
-
' found
|
|
595
|
-
|
|
596
|
-
+ ' (6.4.3.2).', element)
|
|
597
|
+
f' found {component} with parents {component.parent()} and'
|
|
598
|
+
f' {parent} (6.4.3.2).', element)
|
|
597
599
|
|
|
598
600
|
# Set parent (won't raise CellMLErrors)
|
|
599
601
|
component.set_parent(parent)
|
|
@@ -642,7 +644,7 @@ class CellMLParser:
|
|
|
642
644
|
# Check type (only if in null namespace)
|
|
643
645
|
if ns is None and rel not in ['encapsulation', 'containment']:
|
|
644
646
|
raise CellMLParsingError(
|
|
645
|
-
'Unknown relationship type: "
|
|
647
|
+
f'Unknown relationship type: "{rel}", expecting either'
|
|
646
648
|
' "encapsulation" or "containment" (6.4.2.2).', element)
|
|
647
649
|
|
|
648
650
|
# Check name, if given
|
|
@@ -653,11 +655,11 @@ class CellMLParser:
|
|
|
653
655
|
'Encapsulation relationships may not define a name'
|
|
654
656
|
' attribute (6.4.2.4).', element)
|
|
655
657
|
|
|
656
|
-
if not myokit.formats.cellml.v1.
|
|
658
|
+
if not myokit.formats.cellml.v1.is_identifier(name):
|
|
657
659
|
raise CellMLParsingError(
|
|
658
660
|
'Relationship_ref name must be a valid CellML identifier,'
|
|
659
|
-
' but found "
|
|
660
|
-
rel
|
|
661
|
+
f' but found "{name}" (6.4.2.3).', element)
|
|
662
|
+
rel = f'{rel}, {name}'
|
|
661
663
|
|
|
662
664
|
# Check uniqueness of relationship (only in null namespace)
|
|
663
665
|
if ns is None and rel in relationships:
|
|
@@ -701,7 +703,7 @@ class CellMLParser:
|
|
|
701
703
|
units = element.attrib[attr]
|
|
702
704
|
except KeyError:
|
|
703
705
|
raise CellMLParsingError(
|
|
704
|
-
'Numbers inside
|
|
706
|
+
'Numbers inside mathml:math must define a cellml:units'
|
|
705
707
|
' attribute (4.4.3.1).', element)
|
|
706
708
|
|
|
707
709
|
# Find units in component
|
|
@@ -710,14 +712,14 @@ class CellMLParser:
|
|
|
710
712
|
units = units.myokit_unit()
|
|
711
713
|
except myokit.formats.cellml.v1.UnitsError as e:
|
|
712
714
|
warnings.warn(
|
|
713
|
-
'The units "
|
|
714
|
-
'
|
|
715
|
-
'
|
|
715
|
+
f'The units "{units}" (referenced inside a MathML'
|
|
716
|
+
' equation) are not supported and have been replaced by'
|
|
717
|
+
f' `dimensionless`. ({e})')
|
|
716
718
|
units = myokit.units.dimensionless
|
|
717
719
|
except myokit.formats.cellml.v1.CellMLError:
|
|
718
720
|
raise CellMLParsingError(
|
|
719
|
-
'Unknown
|
|
720
|
-
'
|
|
721
|
+
f'Unknown units "{units}" referenced inside a MathML'
|
|
722
|
+
' equation (4.4.3.2).', element)
|
|
721
723
|
|
|
722
724
|
# Create and return
|
|
723
725
|
return myokit.Number(value, units)
|
|
@@ -737,8 +739,8 @@ class CellMLParser:
|
|
|
737
739
|
if ns != cellml.NS_MATHML:
|
|
738
740
|
raise CellMLParsingError(
|
|
739
741
|
'The contents of a mathml:math element must be in the'
|
|
740
|
-
' mathml namespace, found "
|
|
741
|
-
|
|
742
|
+
f' mathml namespace, found "{child.tag}" inside'
|
|
743
|
+
f' {component}.', child)
|
|
742
744
|
|
|
743
745
|
# Ignore annotations
|
|
744
746
|
if el in ['annotation', 'annotation-xml']:
|
|
@@ -753,22 +755,22 @@ class CellMLParser:
|
|
|
753
755
|
if el != 'apply':
|
|
754
756
|
raise CellMLParsingError(
|
|
755
757
|
'Unexpected contents in mathml:math. Expecting'
|
|
756
|
-
' mathml:apply but found mathml:
|
|
757
|
-
'
|
|
758
|
+
f' mathml:apply but found mathml:{el} in maths for'
|
|
759
|
+
f' {component}.', child)
|
|
758
760
|
|
|
759
761
|
# Parse
|
|
760
762
|
eq = p.parse(child)
|
|
761
763
|
if not isinstance(eq, myokit.Equal):
|
|
762
764
|
raise CellMLParsingError(
|
|
763
765
|
'Unexpected element in MathML, expecting a list of'
|
|
764
|
-
' equations, got
|
|
766
|
+
f' equations, got {self._tag(child)}.', child)
|
|
765
767
|
lhs, rhs = eq
|
|
766
768
|
|
|
767
769
|
# Check lhs
|
|
768
770
|
if not isinstance(lhs, myokit.LhsExpression):
|
|
769
771
|
raise CellMLParsingError(
|
|
770
772
|
'Invalid expression found on the left-hand side of an'
|
|
771
|
-
' equation:
|
|
773
|
+
f' equation: {self._dae_message}', child)
|
|
772
774
|
|
|
773
775
|
# Promote derivative, check non-derivatives have no initial value
|
|
774
776
|
var = lhs.var()
|
|
@@ -777,15 +779,13 @@ class CellMLParser:
|
|
|
777
779
|
elif var.initial_value() is not None:
|
|
778
780
|
raise CellMLParsingError(
|
|
779
781
|
'Initial value and a defining equation found for'
|
|
780
|
-
' non-state
|
|
781
|
-
child)
|
|
782
|
+
f' non-state {var}: {self._dae_message}', child)
|
|
782
783
|
|
|
783
784
|
# Check for double rhs
|
|
784
785
|
if var.rhs() is not None:
|
|
785
786
|
raise CellMLParsingError(
|
|
786
|
-
'Two defining equations found for
|
|
787
|
-
|
|
788
|
-
child)
|
|
787
|
+
f'Two defining equations found for {var}:'
|
|
788
|
+
f' {self._dae_message}', child)
|
|
789
789
|
|
|
790
790
|
# Set rhs
|
|
791
791
|
try:
|
|
@@ -1020,9 +1020,9 @@ class CellMLParser:
|
|
|
1020
1020
|
if not children:
|
|
1021
1021
|
if base == 'yes':
|
|
1022
1022
|
warnings.warn(
|
|
1023
|
-
'Unable to parse definition for units "
|
|
1024
|
-
'
|
|
1025
|
-
'
|
|
1023
|
+
f'Unable to parse definition for units "{name}", using'
|
|
1024
|
+
' `dimensionless` instead. (Defining new base units is not'
|
|
1025
|
+
' supported.)')
|
|
1026
1026
|
else:
|
|
1027
1027
|
raise CellMLParsingError(
|
|
1028
1028
|
'Units element with base_units="no" must contain at least'
|
|
@@ -1035,8 +1035,8 @@ class CellMLParser:
|
|
|
1035
1035
|
myokit_unit *= self._parse_unit(child, owner)
|
|
1036
1036
|
except myokit.formats.cellml.v1.UnitsError as e:
|
|
1037
1037
|
warnings.warn(
|
|
1038
|
-
'Unable to parse definition for units "
|
|
1039
|
-
'
|
|
1038
|
+
f'Unable to parse definition for units "{name}", using'
|
|
1039
|
+
f' `dimensionless` instead. ({e})')
|
|
1040
1040
|
myokit_unit = myokit.units.dimensionless
|
|
1041
1041
|
|
|
1042
1042
|
# Add units to owner
|
|
@@ -1049,6 +1049,10 @@ class CellMLParser:
|
|
|
1049
1049
|
"""
|
|
1050
1050
|
Parses a variable ``element`` and adds a variable to the given
|
|
1051
1051
|
``component``.
|
|
1052
|
+
|
|
1053
|
+
If an initial value needs to be set, returns a tuple
|
|
1054
|
+
``(element, variable, initial_value_string)``. Otherwise returns
|
|
1055
|
+
``None``.
|
|
1052
1056
|
"""
|
|
1053
1057
|
# Check name is present
|
|
1054
1058
|
try:
|
|
@@ -1076,9 +1080,8 @@ class CellMLParser:
|
|
|
1076
1080
|
variable = component.add_variable(name, units, pub, pri)
|
|
1077
1081
|
except myokit.formats.cellml.v1.UnitsError as e:
|
|
1078
1082
|
warnings.warn(
|
|
1079
|
-
'The units "
|
|
1080
|
-
'
|
|
1081
|
-
' `dimensionless`. (' + str(e) + ')')
|
|
1083
|
+
f'The units "{units}" (assigned to a variable) are not'
|
|
1084
|
+
f' supported and were replaced by `dimensionless`. ({e})')
|
|
1082
1085
|
units = 'dimensionless'
|
|
1083
1086
|
variable = component.add_variable(name, units, pub, pri)
|
|
1084
1087
|
|
|
@@ -1097,9 +1100,10 @@ class CellMLParser:
|
|
|
1097
1100
|
]
|
|
1098
1101
|
self._check_allowed_content(element, [], attr, name)
|
|
1099
1102
|
|
|
1100
|
-
#
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
+
# Store initial value, to parse later
|
|
1104
|
+
init = element.attrib.get('initial_value', None)
|
|
1105
|
+
if init is not None:
|
|
1106
|
+
return (element, variable, init)
|
|
1103
1107
|
|
|
1104
1108
|
except myokit.formats.cellml.v1.CellMLError as e:
|
|
1105
1109
|
raise CellMLParsingError(str(e), element)
|
|
@@ -1149,14 +1153,14 @@ class CellMLParser:
|
|
|
1149
1153
|
# Check doesn't shadow an si unit
|
|
1150
1154
|
if name in si_units:
|
|
1151
1155
|
raise CellMLParsingError(
|
|
1152
|
-
'Units name "
|
|
1153
|
-
' in
|
|
1156
|
+
f'Units name "{name}" overlaps with a predefined name'
|
|
1157
|
+
f' in {self._tag(element)} (5.4.1.2).', element)
|
|
1154
1158
|
|
|
1155
1159
|
# Check for duplicates
|
|
1156
1160
|
if name in local_units:
|
|
1157
1161
|
raise CellMLParsingError(
|
|
1158
|
-
'Duplicate units definition "
|
|
1159
|
-
|
|
1162
|
+
f'Duplicate units definition "{name}" in'
|
|
1163
|
+
f' {self._tag(element)}.', element)
|
|
1160
1164
|
local_units[name] = units
|
|
1161
1165
|
|
|
1162
1166
|
# Component units can shadow model units, so if known units are
|
|
@@ -1196,8 +1200,8 @@ class CellMLParser:
|
|
|
1196
1200
|
deps.difference_update(fresh)
|
|
1197
1201
|
else:
|
|
1198
1202
|
raise CellMLParsingError(
|
|
1199
|
-
'Unable to resolve network of units in
|
|
1200
|
-
|
|
1203
|
+
'Unable to resolve network of units in'
|
|
1204
|
+
f' {self._tag(element)} (5.4.2.2).', element)
|
|
1201
1205
|
return ordered
|
|
1202
1206
|
|
|
1203
1207
|
def _tag(self, element, name=None):
|
|
@@ -1214,6 +1218,6 @@ class CellMLParser:
|
|
|
1214
1218
|
ns, el = split(element.tag)
|
|
1215
1219
|
tag = self._item(ns, el)
|
|
1216
1220
|
if ns == self._ns and name is not None:
|
|
1217
|
-
tag += '[@name="
|
|
1221
|
+
tag += f'[@name="{name}"]'
|
|
1218
1222
|
return tag
|
|
1219
1223
|
|
|
@@ -283,7 +283,7 @@ class CellMLWriter:
|
|
|
283
283
|
self._oxmeta_variables.items(), key=lambda x: x[0]):
|
|
284
284
|
description = etree.SubElement(
|
|
285
285
|
rdf, etree.QName(cellml.NS_RDF, 'Description'))
|
|
286
|
-
description.attrib[etree.QName(cellml.NS_RDF, 'about')] = '#'
|
|
286
|
+
description.attrib[etree.QName(cellml.NS_RDF, 'about')] = f'#{cid}'
|
|
287
287
|
iz = etree.SubElement(
|
|
288
288
|
description, etree.QName(cellml.NS_BQBIOL, 'is'))
|
|
289
289
|
iz.attrib[etree.QName(cellml.NS_RDF, 'resource')] = \
|
|
@@ -352,11 +352,18 @@ class CellMLWriter:
|
|
|
352
352
|
element.attrib['private_interface'] = variable.private_interface()
|
|
353
353
|
|
|
354
354
|
# Add initial value
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
if
|
|
358
|
-
value =
|
|
359
|
-
|
|
355
|
+
init = variable.initial_value()
|
|
356
|
+
if init is not None:
|
|
357
|
+
if isinstance(init, myokit.Number):
|
|
358
|
+
value = myokit.float.str(variable.initial_value()).strip()
|
|
359
|
+
if value[-4:] == 'e+00':
|
|
360
|
+
value = value[:-4]
|
|
361
|
+
element.attrib['initial_value'] = value
|
|
362
|
+
elif isinstance(init, myokit.Name):
|
|
363
|
+
element.attrib['initial_value'] = init.var().name()
|
|
364
|
+
else: # pragma: no cover
|
|
365
|
+
raise Exception(
|
|
366
|
+
f'Unexpected type for initial value: {type(init)}.')
|
|
360
367
|
|
|
361
368
|
# Add cmeta id
|
|
362
369
|
cid = variable.cmeta_id()
|
|
@@ -7,28 +7,25 @@
|
|
|
7
7
|
from ._api import ( # noqa
|
|
8
8
|
AnnotatableElement,
|
|
9
9
|
CellMLError,
|
|
10
|
+
Component,
|
|
10
11
|
clean_identifier,
|
|
11
12
|
create_unit_name,
|
|
12
|
-
|
|
13
|
+
is_identifier,
|
|
13
14
|
Model,
|
|
14
15
|
Units,
|
|
15
16
|
Variable,
|
|
16
|
-
is_basic_real_number_string,
|
|
17
|
-
is_identifier,
|
|
18
|
-
is_integer_string,
|
|
19
|
-
is_real_number_string,
|
|
20
17
|
)
|
|
21
18
|
|
|
22
19
|
from ._parser import ( # noqa
|
|
23
|
-
parse_file,
|
|
24
|
-
parse_string,
|
|
25
20
|
CellMLParser,
|
|
26
21
|
CellMLParsingError,
|
|
22
|
+
parse_file,
|
|
23
|
+
parse_string,
|
|
27
24
|
)
|
|
28
25
|
|
|
29
26
|
from ._writer import ( # noqa
|
|
27
|
+
CellMLWriter,
|
|
30
28
|
write_file,
|
|
31
29
|
write_string,
|
|
32
|
-
CellMLWriter,
|
|
33
30
|
)
|
|
34
31
|
|