myokit 1.37.1__py3-none-any.whl → 1.37.2__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 +2 -2
- myokit/_datalog.py +15 -6
- myokit/_myokit_version.py +1 -1
- myokit/_sim/cvodessim.py +3 -3
- myokit/formats/heka/__init__.py +4 -0
- myokit/formats/heka/_patchmaster.py +128 -100
- myokit/formats/sbml/__init__.py +21 -1
- myokit/formats/sbml/_api.py +160 -6
- myokit/formats/sbml/_exporter.py +53 -0
- myokit/formats/sbml/_writer.py +355 -0
- myokit/tests/test_formats_exporters_run.py +3 -0
- myokit/tests/test_formats_sbml.py +57 -1
- myokit/tests/test_sbml_api.py +90 -0
- myokit/tests/test_sbml_export.py +327 -0
- {myokit-1.37.1.dist-info → myokit-1.37.2.dist-info}/LICENSE.txt +1 -1
- {myokit-1.37.1.dist-info → myokit-1.37.2.dist-info}/METADATA +4 -4
- {myokit-1.37.1.dist-info → myokit-1.37.2.dist-info}/RECORD +20 -17
- {myokit-1.37.1.dist-info → myokit-1.37.2.dist-info}/WHEEL +1 -1
- {myokit-1.37.1.dist-info → myokit-1.37.2.dist-info}/entry_points.txt +0 -0
- {myokit-1.37.1.dist-info → myokit-1.37.2.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,355 @@
|
|
|
1
|
+
#
|
|
2
|
+
# SBML Writer: Writes an SBML Model to disk
|
|
3
|
+
#
|
|
4
|
+
# This file is part of Myokit.
|
|
5
|
+
# See http://myokit.org for copyright, sharing, and licensing details.
|
|
6
|
+
#
|
|
7
|
+
from typing import Tuple
|
|
8
|
+
from lxml import etree
|
|
9
|
+
|
|
10
|
+
import myokit
|
|
11
|
+
from myokit._unit import Quantity
|
|
12
|
+
from myokit.formats.mathml._ewriter import MathMLExpressionWriter
|
|
13
|
+
from myokit.formats.sbml._api import (
|
|
14
|
+
Model, Compartment,
|
|
15
|
+
Parameter, Species,
|
|
16
|
+
Reaction, SpeciesReference
|
|
17
|
+
)
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def write_file(path: str, model: Model):
|
|
21
|
+
"""
|
|
22
|
+
Writes an SBML model to the given path.
|
|
23
|
+
"""
|
|
24
|
+
return SBMLWriter.write_file(path, model)
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def write_string(model: Model) -> str:
|
|
28
|
+
"""
|
|
29
|
+
Writes an SBML model to a string and returns it.
|
|
30
|
+
"""
|
|
31
|
+
return SBMLWriter.write_string(model)
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
class SBMLWriter:
|
|
35
|
+
"""
|
|
36
|
+
Writes SBML documents
|
|
37
|
+
"""
|
|
38
|
+
|
|
39
|
+
@staticmethod
|
|
40
|
+
def write_file(path: str, model: Model):
|
|
41
|
+
tree = SBMLWriter._model(model)
|
|
42
|
+
# Write to disk
|
|
43
|
+
tree.write(
|
|
44
|
+
path,
|
|
45
|
+
encoding='utf-8',
|
|
46
|
+
method='xml',
|
|
47
|
+
xml_declaration=True,
|
|
48
|
+
pretty_print=True,
|
|
49
|
+
)
|
|
50
|
+
|
|
51
|
+
@staticmethod
|
|
52
|
+
def write_string(model: Model) -> str:
|
|
53
|
+
tree = SBMLWriter._model(model)
|
|
54
|
+
return etree.tostring(
|
|
55
|
+
tree,
|
|
56
|
+
encoding='utf-8',
|
|
57
|
+
method='xml',
|
|
58
|
+
pretty_print=True,
|
|
59
|
+
)
|
|
60
|
+
|
|
61
|
+
@staticmethod
|
|
62
|
+
def _compartment(
|
|
63
|
+
compartment: Compartment,
|
|
64
|
+
unit_to_str: dict
|
|
65
|
+
) -> etree.Element:
|
|
66
|
+
node = etree.Element('compartment', id=compartment.sid())
|
|
67
|
+
if compartment.size_units() != myokit.units.dimensionless:
|
|
68
|
+
node.attrib['units'] = unit_to_str[compartment.size_units()]
|
|
69
|
+
if compartment.spatial_dimensions() is not None:
|
|
70
|
+
node.attrib['spatialDimensions'] = str(
|
|
71
|
+
compartment.spatial_dimensions()
|
|
72
|
+
)
|
|
73
|
+
return node
|
|
74
|
+
|
|
75
|
+
@staticmethod
|
|
76
|
+
def _unit(sid: str, unit: myokit.Unit) -> etree.Element:
|
|
77
|
+
node = etree.Element('unitDefinition', id=sid)
|
|
78
|
+
kinds = [
|
|
79
|
+
'gram', 'metre', 'second',
|
|
80
|
+
'ampere', 'kelvin', 'candela', 'mole'
|
|
81
|
+
]
|
|
82
|
+
multiplier = unit.multiplier()
|
|
83
|
+
for exponent, kind in zip(unit.exponents(), kinds):
|
|
84
|
+
if exponent != 0:
|
|
85
|
+
child = etree.Element('unit')
|
|
86
|
+
child.attrib['kind'] = kind
|
|
87
|
+
child.attrib['exponent'] = str(exponent)
|
|
88
|
+
if multiplier is not None:
|
|
89
|
+
child.attrib['multiplier'] = str(multiplier)
|
|
90
|
+
multiplier = None
|
|
91
|
+
node.append(child)
|
|
92
|
+
# might also have a dimensionless unit and a multiplier
|
|
93
|
+
if multiplier is not None:
|
|
94
|
+
child = etree.Element('unit')
|
|
95
|
+
child.attrib['kind'] = 'dimensionless'
|
|
96
|
+
child.attrib['multiplier'] = str(multiplier)
|
|
97
|
+
node.append(child)
|
|
98
|
+
return node
|
|
99
|
+
|
|
100
|
+
@staticmethod
|
|
101
|
+
def _parameter(
|
|
102
|
+
parameter: Parameter,
|
|
103
|
+
unit_to_str_map: dict
|
|
104
|
+
) -> Tuple[etree.Element, etree.Element, etree.Element]:
|
|
105
|
+
"""
|
|
106
|
+
returns the XML representation of this parameter as a tuple of
|
|
107
|
+
(parameter, initial_assignment, rule).
|
|
108
|
+
"""
|
|
109
|
+
parameter_xml = etree.Element('parameter', id=parameter.sid())
|
|
110
|
+
if (
|
|
111
|
+
parameter.units() is not None and
|
|
112
|
+
parameter.units() != myokit.units.dimensionless
|
|
113
|
+
):
|
|
114
|
+
parameter_xml.attrib['units'] = unit_to_str_map[parameter.units()]
|
|
115
|
+
|
|
116
|
+
if parameter.is_constant():
|
|
117
|
+
parameter_xml.attrib['constant'] = 'true'
|
|
118
|
+
|
|
119
|
+
if parameter.is_literal():
|
|
120
|
+
value = parameter.value().eval()
|
|
121
|
+
parameter_xml.attrib['value'] = str(value)
|
|
122
|
+
return parameter_xml, None, None
|
|
123
|
+
else:
|
|
124
|
+
initial_assignment, rule = SBMLWriter._quantity(parameter)
|
|
125
|
+
return parameter_xml, initial_assignment, rule
|
|
126
|
+
|
|
127
|
+
@staticmethod
|
|
128
|
+
def _math(expression: myokit.Expression) -> etree.Element:
|
|
129
|
+
math = etree.Element(
|
|
130
|
+
'math',
|
|
131
|
+
xmlns='http://www.w3.org/1998/Math/MathML'
|
|
132
|
+
)
|
|
133
|
+
|
|
134
|
+
def flhs(lhs):
|
|
135
|
+
var = lhs.var()
|
|
136
|
+
if isinstance(var, str):
|
|
137
|
+
return var
|
|
138
|
+
if var.binding() == 'time':
|
|
139
|
+
return "http://www.sbml.org/sbml/symbols/time"
|
|
140
|
+
return var.uname()
|
|
141
|
+
|
|
142
|
+
mathml_writer = MathMLExpressionWriter()
|
|
143
|
+
mathml_writer.set_lhs_function(flhs)
|
|
144
|
+
mathml_writer.ex(expression, math)
|
|
145
|
+
return math
|
|
146
|
+
|
|
147
|
+
@staticmethod
|
|
148
|
+
def _quantity(quantity: Quantity) -> etree.Element:
|
|
149
|
+
initial_value = quantity.initial_value()
|
|
150
|
+
initial_assignment = None
|
|
151
|
+
if initial_value is not None:
|
|
152
|
+
initial_assignment = etree.Element(
|
|
153
|
+
'initialAssignment', symbol=quantity.sid()
|
|
154
|
+
)
|
|
155
|
+
math = SBMLWriter._math(initial_value)
|
|
156
|
+
initial_assignment.append(math)
|
|
157
|
+
|
|
158
|
+
value = quantity.value()
|
|
159
|
+
rule = None
|
|
160
|
+
if value is not None:
|
|
161
|
+
if quantity.is_rate():
|
|
162
|
+
rule_type = 'rateRule'
|
|
163
|
+
else:
|
|
164
|
+
rule_type = 'assignmentRule'
|
|
165
|
+
rule = etree.Element(rule_type, variable=quantity.sid())
|
|
166
|
+
math = SBMLWriter._math(quantity.value())
|
|
167
|
+
rule.append(math)
|
|
168
|
+
return initial_assignment, rule
|
|
169
|
+
|
|
170
|
+
@staticmethod
|
|
171
|
+
def _reaction(reaction: Reaction) -> etree.Element:
|
|
172
|
+
reaction_xml = etree.Element('reaction', id=reaction.sid())
|
|
173
|
+
list_of_reactants = etree.Element('listOfReactants')
|
|
174
|
+
for reactant in reaction.reactants():
|
|
175
|
+
node = SBMLWriter._species_reference(reactant)
|
|
176
|
+
list_of_reactants.append(node)
|
|
177
|
+
reaction_xml.append(list_of_reactants)
|
|
178
|
+
list_of_products = etree.Element('listOfProducts')
|
|
179
|
+
for product in reaction.products():
|
|
180
|
+
node = SBMLWriter._species_reference(product)
|
|
181
|
+
list_of_products.append(node)
|
|
182
|
+
reaction_xml.append(list_of_products)
|
|
183
|
+
list_of_modifiers = etree.Element('listOfModifiers')
|
|
184
|
+
for modifier in reaction.modifiers():
|
|
185
|
+
node = SBMLWriter._modifier_species_reference(modifier)
|
|
186
|
+
list_of_modifiers.append(node)
|
|
187
|
+
reaction_xml.append(list_of_modifiers)
|
|
188
|
+
if reaction.kinetic_law() is not None:
|
|
189
|
+
kinetic_law = etree.Element('kineticLaw')
|
|
190
|
+
math = SBMLWriter._math(reaction.kinetic_law())
|
|
191
|
+
kinetic_law.append(math)
|
|
192
|
+
reaction_xml.append(kinetic_law)
|
|
193
|
+
return reaction_xml
|
|
194
|
+
|
|
195
|
+
@staticmethod
|
|
196
|
+
def _species(species: Species, unit_to_str_map: dict) -> Tuple[
|
|
197
|
+
etree.Element, etree.Element
|
|
198
|
+
]:
|
|
199
|
+
"""
|
|
200
|
+
Returns the XML representation of this species as a tuple of
|
|
201
|
+
(species, rule).
|
|
202
|
+
"""
|
|
203
|
+
species_xml = etree.Element('species', id=species.sid())
|
|
204
|
+
species_xml.attrib['compartment'] = species.compartment().sid()
|
|
205
|
+
initial_value, initial_value_in_amount = species.initial_value()
|
|
206
|
+
if initial_value_in_amount is None:
|
|
207
|
+
if species.is_amount():
|
|
208
|
+
attrib_name = 'initialAmount'
|
|
209
|
+
else:
|
|
210
|
+
attrib_name = 'initialConcentration'
|
|
211
|
+
else:
|
|
212
|
+
if initial_value_in_amount:
|
|
213
|
+
attrib_name = 'initialAmount'
|
|
214
|
+
else:
|
|
215
|
+
attrib_name = 'initialConcentration'
|
|
216
|
+
if initial_value is not None:
|
|
217
|
+
initial_value_eval = initial_value.eval()
|
|
218
|
+
species_xml.attrib[attrib_name] = str(initial_value_eval)
|
|
219
|
+
species_xml.attrib['constant'] = str(species.is_constant())
|
|
220
|
+
if species.substance_units() != myokit.units.dimensionless:
|
|
221
|
+
species_xml.attrib['units'] = unit_to_str_map[
|
|
222
|
+
species.substance_units()
|
|
223
|
+
]
|
|
224
|
+
species_xml.attrib['boundaryCondition'] = str(species.is_boundary())
|
|
225
|
+
|
|
226
|
+
if species.value() is None:
|
|
227
|
+
return species_xml, None
|
|
228
|
+
|
|
229
|
+
if species.is_rate():
|
|
230
|
+
rule_type = 'rateRule'
|
|
231
|
+
else:
|
|
232
|
+
rule_type = 'assignmentRule'
|
|
233
|
+
rule = etree.Element(rule_type, variable=species.sid())
|
|
234
|
+
math = SBMLWriter._math(species.value())
|
|
235
|
+
rule.append(math)
|
|
236
|
+
return species_xml, rule
|
|
237
|
+
|
|
238
|
+
@staticmethod
|
|
239
|
+
def _species_reference(ref: SpeciesReference) -> etree.Element:
|
|
240
|
+
species_reference = etree.Element(
|
|
241
|
+
'speciesReference',
|
|
242
|
+
species=ref.species().sid()
|
|
243
|
+
)
|
|
244
|
+
if ref.sid() is not None:
|
|
245
|
+
species_reference.attrib['id'] = ref.sid()
|
|
246
|
+
value = ref.value()
|
|
247
|
+
if value is not None:
|
|
248
|
+
value_eval = value.eval()
|
|
249
|
+
species_reference.attrib['stoichiometry'] = str(value_eval)
|
|
250
|
+
return species_reference
|
|
251
|
+
|
|
252
|
+
@staticmethod
|
|
253
|
+
def _modifier_species_reference(ref: SpeciesReference) -> etree.Element:
|
|
254
|
+
species_reference = etree.Element(
|
|
255
|
+
'modifierSpeciesReference',
|
|
256
|
+
species=ref.species().sid()
|
|
257
|
+
)
|
|
258
|
+
if ref.sid() is not None:
|
|
259
|
+
species_reference.attrib['id'] = ref.sid()
|
|
260
|
+
return species_reference
|
|
261
|
+
|
|
262
|
+
@staticmethod
|
|
263
|
+
def _model(model: Model) -> etree.ElementTree:
|
|
264
|
+
root = etree.Element(
|
|
265
|
+
'sbml',
|
|
266
|
+
xmlns='http://www.sbml.org/sbml/level3/version2/core',
|
|
267
|
+
level='3',
|
|
268
|
+
version='2'
|
|
269
|
+
)
|
|
270
|
+
|
|
271
|
+
# setup a map from unit to string
|
|
272
|
+
unit_map_to_str = {
|
|
273
|
+
unit: string for string, unit in Model.base_units.items()
|
|
274
|
+
}
|
|
275
|
+
for unitid, unit in model.units().items():
|
|
276
|
+
unit_map_to_str[unit] = unitid
|
|
277
|
+
|
|
278
|
+
name = model.name() if model.name() else 'unnamed_model'
|
|
279
|
+
model_root = etree.Element('model', id=name)
|
|
280
|
+
if model.time_units() != myokit.units.dimensionless:
|
|
281
|
+
model_root.attrib['timeUnits'] = unit_map_to_str[
|
|
282
|
+
model.time_units()
|
|
283
|
+
]
|
|
284
|
+
if model.area_units() != myokit.units.dimensionless:
|
|
285
|
+
model_root.attrib['areaUnits'] = unit_map_to_str[
|
|
286
|
+
model.area_units()
|
|
287
|
+
]
|
|
288
|
+
if model.length_units() != myokit.units.dimensionless:
|
|
289
|
+
model_root.attrib['lengthUnits'] = unit_map_to_str[
|
|
290
|
+
model.length_units()
|
|
291
|
+
]
|
|
292
|
+
if model.substance_units() != myokit.units.dimensionless:
|
|
293
|
+
model_root.attrib['substanceUnits'] = unit_map_to_str[
|
|
294
|
+
model.substance_units()
|
|
295
|
+
]
|
|
296
|
+
if model.extent_units() != myokit.units.dimensionless:
|
|
297
|
+
model_root.attrib['extentUnits'] = unit_map_to_str[
|
|
298
|
+
model.extent_units()
|
|
299
|
+
]
|
|
300
|
+
if model.volume_units() != myokit.units.dimensionless:
|
|
301
|
+
model_root.attrib['volumeUnits'] = unit_map_to_str[
|
|
302
|
+
model.volume_units()
|
|
303
|
+
]
|
|
304
|
+
|
|
305
|
+
if model.has_units():
|
|
306
|
+
list_of_units = etree.Element('listOfUnitDefinitions')
|
|
307
|
+
for sid, unit in model.units().items():
|
|
308
|
+
node = SBMLWriter._unit(sid, unit)
|
|
309
|
+
list_of_units.append(node)
|
|
310
|
+
model_root.append(list_of_units)
|
|
311
|
+
if model.compartments():
|
|
312
|
+
list_of_compartments = etree.Element('listOfCompartments')
|
|
313
|
+
for compartment in model.compartments():
|
|
314
|
+
node = SBMLWriter._compartment(compartment, unit_map_to_str)
|
|
315
|
+
list_of_compartments.append(node)
|
|
316
|
+
model_root.append(list_of_compartments)
|
|
317
|
+
list_of_rules = etree.Element('listOfRules')
|
|
318
|
+
list_of_initial_assignments = etree.Element('listOfInitialAssignments')
|
|
319
|
+
if model.parameters():
|
|
320
|
+
list_of_parameters = etree.Element('listOfParameters')
|
|
321
|
+
for parameter in model.parameters():
|
|
322
|
+
param_node, initial_value_node, rule_node = \
|
|
323
|
+
SBMLWriter._parameter(parameter, unit_map_to_str)
|
|
324
|
+
list_of_parameters.append(param_node)
|
|
325
|
+
if initial_value_node is not None:
|
|
326
|
+
list_of_initial_assignments.append(initial_value_node)
|
|
327
|
+
if rule_node is not None:
|
|
328
|
+
list_of_rules.append(rule_node)
|
|
329
|
+
model_root.append(list_of_parameters)
|
|
330
|
+
if model.species_list():
|
|
331
|
+
list_of_species = etree.Element('listOfSpecies')
|
|
332
|
+
for species in model.species_list():
|
|
333
|
+
species_node, rule_node = SBMLWriter._species(
|
|
334
|
+
species,
|
|
335
|
+
unit_map_to_str
|
|
336
|
+
)
|
|
337
|
+
if rule_node is not None:
|
|
338
|
+
list_of_rules.append(rule_node)
|
|
339
|
+
list_of_species.append(species_node)
|
|
340
|
+
model_root.append(list_of_species)
|
|
341
|
+
if model.reactions():
|
|
342
|
+
list_of_reactions = etree.Element('listOfReactions')
|
|
343
|
+
for reaction in model.reactions():
|
|
344
|
+
node = SBMLWriter._reaction(reaction)
|
|
345
|
+
list_of_reactions.append(node)
|
|
346
|
+
model_root.append(list_of_reactions)
|
|
347
|
+
|
|
348
|
+
if len(list_of_initial_assignments) > 0:
|
|
349
|
+
model_root.append(list_of_initial_assignments)
|
|
350
|
+
|
|
351
|
+
if len(list_of_rules) > 0:
|
|
352
|
+
model_root.append(list_of_rules)
|
|
353
|
+
|
|
354
|
+
root.append(model_root)
|
|
355
|
+
return etree.ElementTree(root)
|
|
@@ -104,6 +104,9 @@ class ExportTest(unittest.TestCase):
|
|
|
104
104
|
name = 'test_' + name + '_exporter'
|
|
105
105
|
self.assertIn(name, methods)
|
|
106
106
|
|
|
107
|
+
def test_sbml_exporter(self):
|
|
108
|
+
self._test(myokit.formats.exporter('sbml'))
|
|
109
|
+
|
|
107
110
|
def test_ansic_exporter(self):
|
|
108
111
|
self._test(myokit.formats.exporter('ansic'))
|
|
109
112
|
|
|
@@ -15,7 +15,63 @@ import myokit.formats
|
|
|
15
15
|
import myokit.formats.sbml
|
|
16
16
|
|
|
17
17
|
# from shared import DIR_FORMATS, WarningCollector
|
|
18
|
-
from myokit.tests import DIR_FORMATS, WarningCollector
|
|
18
|
+
from myokit.tests import DIR_FORMATS, WarningCollector, TemporaryDirectory
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class SBMLExporterTest(unittest.TestCase):
|
|
22
|
+
"""
|
|
23
|
+
Tests for :class:`myokit.formats.sbml.SBMLExporter`.
|
|
24
|
+
"""
|
|
25
|
+
|
|
26
|
+
def test_capability_reporting(self):
|
|
27
|
+
# Test if the right capabilities are reported.
|
|
28
|
+
e = myokit.formats.exporter('sbml')
|
|
29
|
+
self.assertTrue(e.supports_model())
|
|
30
|
+
self.assertFalse(e.supports_runnable())
|
|
31
|
+
|
|
32
|
+
def test_stimulus_generation(self):
|
|
33
|
+
# Tests if protocols allow a stimulus current to be added
|
|
34
|
+
|
|
35
|
+
e = myokit.formats.exporter('sbml')
|
|
36
|
+
i = myokit.formats.importer('sbml')
|
|
37
|
+
|
|
38
|
+
# Load input model
|
|
39
|
+
m1, p1, _ = myokit.load('example')
|
|
40
|
+
org_code = m1.code()
|
|
41
|
+
|
|
42
|
+
# 1. Export without a protocol
|
|
43
|
+
with TemporaryDirectory() as d:
|
|
44
|
+
path = d.path('model.sbml')
|
|
45
|
+
with WarningCollector() as w:
|
|
46
|
+
e.model(path, m1)
|
|
47
|
+
m2 = i.model(path)
|
|
48
|
+
self.assertFalse(w.has_warnings())
|
|
49
|
+
self.assertTrue(isinstance(m2.get('global.pace').rhs(), myokit.Number))
|
|
50
|
+
|
|
51
|
+
# 2. Export with protocol, but without variable bound to pacing
|
|
52
|
+
m1.get('engine.pace').set_binding(None)
|
|
53
|
+
with TemporaryDirectory() as d:
|
|
54
|
+
path = d.path('model.sbml')
|
|
55
|
+
with WarningCollector() as w:
|
|
56
|
+
e.model(path, m1, p1)
|
|
57
|
+
m2 = i.model(path)
|
|
58
|
+
self.assertTrue(w.has_warnings())
|
|
59
|
+
self.assertTrue(isinstance(m2.get('global.pace').rhs(), myokit.Number))
|
|
60
|
+
|
|
61
|
+
# 3. Export with protocol and variable bound to pacing
|
|
62
|
+
m1.get('engine.pace').set_binding('pace')
|
|
63
|
+
with TemporaryDirectory() as d:
|
|
64
|
+
path = d.path('model.cellml')
|
|
65
|
+
with WarningCollector() as w:
|
|
66
|
+
e.model(path, m1, p1)
|
|
67
|
+
m2 = i.model(path)
|
|
68
|
+
self.assertFalse(w.has_warnings())
|
|
69
|
+
rhs = m2.get('global.i_stim').rhs()
|
|
70
|
+
self.assertTrue(rhs, myokit.Multiply)
|
|
71
|
+
self.assertTrue(isinstance(rhs[0], myokit.Piecewise))
|
|
72
|
+
|
|
73
|
+
# Check original model is unchanged
|
|
74
|
+
self.assertEqual(org_code, m1.code())
|
|
19
75
|
|
|
20
76
|
|
|
21
77
|
class SBMLImporterTest(unittest.TestCase):
|
myokit/tests/test_sbml_api.py
CHANGED
|
@@ -2181,6 +2181,96 @@ class SBMLTestMyokitModel(unittest.TestCase):
|
|
|
2181
2181
|
myokit.Plus(myokit.Number(1), myokit.Name(m.time())))
|
|
2182
2182
|
|
|
2183
2183
|
|
|
2184
|
+
class SBMLTestModelFromMyokit(unittest.TestCase):
|
|
2185
|
+
def test_basic(self):
|
|
2186
|
+
m = myokit.Model()
|
|
2187
|
+
c = m.add_component('comp')
|
|
2188
|
+
v = c.add_variable('var')
|
|
2189
|
+
v.set_rhs(myokit.Number(3))
|
|
2190
|
+
t = c.add_variable('time')
|
|
2191
|
+
t.set_binding('time')
|
|
2192
|
+
t.set_rhs(myokit.Number(0))
|
|
2193
|
+
|
|
2194
|
+
s = sbml.Model.from_myokit_model(m)
|
|
2195
|
+
compartment_names = [c.sid() for c in s.compartments()]
|
|
2196
|
+
self.assertCountEqual(compartment_names, [])
|
|
2197
|
+
parameter_names = [v.sid() for v in s.parameters()]
|
|
2198
|
+
self.assertCountEqual(parameter_names, ['var'])
|
|
2199
|
+
|
|
2200
|
+
def test_vars_with_same_name(self):
|
|
2201
|
+
m = myokit.Model()
|
|
2202
|
+
c = m.add_component('comp')
|
|
2203
|
+
v = c.add_variable('var')
|
|
2204
|
+
v.set_rhs(myokit.Number(3))
|
|
2205
|
+
c = m.add_component('comp2')
|
|
2206
|
+
v = c.add_variable('var')
|
|
2207
|
+
v.set_rhs(myokit.Number(3))
|
|
2208
|
+
t = c.add_variable('time')
|
|
2209
|
+
t.set_binding('time')
|
|
2210
|
+
t.set_rhs(myokit.Number(0))
|
|
2211
|
+
|
|
2212
|
+
s = sbml.Model.from_myokit_model(m)
|
|
2213
|
+
compartment_names = [c.sid() for c in s.compartments()]
|
|
2214
|
+
self.assertCountEqual(compartment_names, [])
|
|
2215
|
+
parameter_names = [v.sid() for v in s.parameters()]
|
|
2216
|
+
self.assertCountEqual(parameter_names, ['comp2_var', 'comp_var'])
|
|
2217
|
+
|
|
2218
|
+
def test_rate_eqn_with_unit(self):
|
|
2219
|
+
m = myokit.Model()
|
|
2220
|
+
c = m.add_component('comp')
|
|
2221
|
+
|
|
2222
|
+
t = c.add_variable('time', rhs=myokit.Number(0))
|
|
2223
|
+
t.set_unit(myokit.units.second)
|
|
2224
|
+
t.set_binding('time')
|
|
2225
|
+
|
|
2226
|
+
p = c.add_variable('param')
|
|
2227
|
+
p.set_rhs(myokit.Number(2))
|
|
2228
|
+
|
|
2229
|
+
v = c.add_variable('var', initial_value=myokit.Number(1))
|
|
2230
|
+
v_unit = 1e3 * myokit.units.meter
|
|
2231
|
+
v.set_unit(v_unit)
|
|
2232
|
+
v.set_rhs(myokit.Multiply(myokit.Number(4), myokit.Name(p)))
|
|
2233
|
+
|
|
2234
|
+
v = c.add_variable('var2', initial_value=myokit.Number(1))
|
|
2235
|
+
v.set_unit(myokit.units.meter)
|
|
2236
|
+
v.set_rhs(myokit.Number(4))
|
|
2237
|
+
|
|
2238
|
+
s = sbml.Model.from_myokit_model(m)
|
|
2239
|
+
parameter_names = [v.sid() for v in s.parameters()]
|
|
2240
|
+
self.assertCountEqual(parameter_names, ['var', 'var2', 'param'])
|
|
2241
|
+
# only non-base unit is kilometer
|
|
2242
|
+
self.assertCountEqual(s.units().values(), [v_unit])
|
|
2243
|
+
self.assertEqual(s.time_units(), myokit.units.second)
|
|
2244
|
+
v = s.parameter('var')
|
|
2245
|
+
self.assertEqual(v.initial_value(), myokit.Number(1))
|
|
2246
|
+
self.assertEqual(
|
|
2247
|
+
v.value(),
|
|
2248
|
+
myokit.Multiply(myokit.Number(4), myokit.Name(p))
|
|
2249
|
+
)
|
|
2250
|
+
|
|
2251
|
+
def test_incompatible_unit(self):
|
|
2252
|
+
m = myokit.Model()
|
|
2253
|
+
c = m.add_component('comp')
|
|
2254
|
+
|
|
2255
|
+
t = c.add_variable('time', rhs=myokit.Number(0))
|
|
2256
|
+
t.set_unit(myokit.units.second)
|
|
2257
|
+
t.set_binding('time')
|
|
2258
|
+
|
|
2259
|
+
p = c.add_variable('param')
|
|
2260
|
+
p.set_rhs(myokit.Number(2))
|
|
2261
|
+
p.set_unit(myokit.units.meter)
|
|
2262
|
+
|
|
2263
|
+
v = c.add_variable('var', initial_value=myokit.Number(1))
|
|
2264
|
+
v.set_rhs(myokit.Plus(myokit.Name(p), myokit.Name(t)))
|
|
2265
|
+
|
|
2266
|
+
s = sbml.Model.from_myokit_model(m)
|
|
2267
|
+
parameter_names = [v.sid() for v in s.parameters()]
|
|
2268
|
+
self.assertCountEqual(parameter_names, ['var', 'param'])
|
|
2269
|
+
|
|
2270
|
+
p = s.parameter('var')
|
|
2271
|
+
self.assertIsNone(p.units())
|
|
2272
|
+
|
|
2273
|
+
|
|
2184
2274
|
if __name__ == '__main__':
|
|
2185
2275
|
import warnings
|
|
2186
2276
|
warnings.simplefilter('always')
|