myokit 1.37.0__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/_aux.py +4 -0
- myokit/_datablock.py +10 -10
- myokit/_datalog.py +55 -11
- myokit/_myokit_version.py +1 -1
- myokit/_sim/cvodessim.py +3 -3
- myokit/formats/axon/_abf.py +11 -4
- myokit/formats/diffsl/__init__.py +60 -0
- myokit/formats/diffsl/_ewriter.py +145 -0
- myokit/formats/diffsl/_exporter.py +435 -0
- myokit/formats/heka/__init__.py +4 -0
- myokit/formats/heka/_patchmaster.py +408 -156
- 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/gui/datalog_viewer.py +17 -3
- myokit/tests/data/io/bad1d-2-no-header.zip +0 -0
- myokit/tests/data/io/bad1d-3-no-data.zip +0 -0
- myokit/tests/data/io/bad1d-4-not-a-zip.zip +1 -105
- myokit/tests/data/io/bad1d-5-bad-data-type.zip +0 -0
- myokit/tests/data/io/bad1d-6-time-too-short.zip +0 -0
- myokit/tests/data/io/bad1d-7-0d-too-short.zip +0 -0
- myokit/tests/data/io/bad1d-8-1d-too-short.zip +0 -0
- myokit/tests/data/io/bad2d-2-no-header.zip +0 -0
- myokit/tests/data/io/bad2d-3-no-data.zip +0 -0
- myokit/tests/data/io/bad2d-4-not-a-zip.zip +1 -105
- myokit/tests/data/io/bad2d-5-bad-data-type.zip +0 -0
- myokit/tests/data/io/bad2d-8-2d-too-short.zip +0 -0
- myokit/tests/data/io/block1d.mmt +187 -0
- myokit/tests/data/io/datalog-18-duplicate-keys.csv +4 -0
- myokit/tests/test_aux.py +4 -0
- myokit/tests/test_datablock.py +6 -6
- myokit/tests/test_datalog.py +20 -0
- myokit/tests/test_formats_diffsl.py +728 -0
- myokit/tests/test_formats_exporters_run.py +6 -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.0.dist-info → myokit-1.37.2.dist-info}/LICENSE.txt +1 -1
- {myokit-1.37.0.dist-info → myokit-1.37.2.dist-info}/METADATA +4 -4
- {myokit-1.37.0.dist-info → myokit-1.37.2.dist-info}/RECORD +45 -36
- {myokit-1.37.0.dist-info → myokit-1.37.2.dist-info}/WHEEL +1 -1
- {myokit-1.37.0.dist-info → myokit-1.37.2.dist-info}/entry_points.txt +0 -0
- {myokit-1.37.0.dist-info → myokit-1.37.2.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,435 @@
|
|
|
1
|
+
#
|
|
2
|
+
# Export as a DiffSL model.
|
|
3
|
+
#
|
|
4
|
+
# This file is part of Myokit.
|
|
5
|
+
# See http://myokit.org for copyright, sharing, and licensing details.
|
|
6
|
+
#
|
|
7
|
+
import collections
|
|
8
|
+
import warnings
|
|
9
|
+
|
|
10
|
+
import myokit
|
|
11
|
+
import myokit.formats
|
|
12
|
+
import myokit.formats.diffsl as diffsl
|
|
13
|
+
import myokit.lib.guess as guess
|
|
14
|
+
import myokit.lib.markov as markov
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class DiffSLExporter(myokit.formats.Exporter):
|
|
18
|
+
"""
|
|
19
|
+
This :class:`Exporter <myokit.formats.Exporter>` generates a DiffSL
|
|
20
|
+
implementation of a Myokit model.
|
|
21
|
+
|
|
22
|
+
Only the model definition is exported. No inputs are provided, there is
|
|
23
|
+
no protocol defined, and only state variables are output.
|
|
24
|
+
|
|
25
|
+
For details of the language, see https://martinjrobins.github.io/diffsl/
|
|
26
|
+
"""
|
|
27
|
+
|
|
28
|
+
def model(self, path, model, protocol=None, convert_units=True):
|
|
29
|
+
"""
|
|
30
|
+
Exports a :class:`myokit.Model` in DiffSL format, writing the result to
|
|
31
|
+
the file indicated by ``path``.
|
|
32
|
+
|
|
33
|
+
A :class:`myokit.ExportError` will be raised if any errors occur.
|
|
34
|
+
Warnings will be generated if unsupported functions (e.g. atan) are
|
|
35
|
+
used in the model. For a full list, see
|
|
36
|
+
:class:`myokit.formats.diffsl.DiffSLExpressionWriter
|
|
37
|
+
<DiffSLExpressionWriter>`.
|
|
38
|
+
|
|
39
|
+
Arguments:
|
|
40
|
+
|
|
41
|
+
``path``
|
|
42
|
+
The path to write the generated model to.
|
|
43
|
+
``model``
|
|
44
|
+
The :class:`myokit.Model` to export.
|
|
45
|
+
``protocol``
|
|
46
|
+
Not implemented!
|
|
47
|
+
``convert_units``
|
|
48
|
+
If set to ``True`` (default), the method will attempt to convert to
|
|
49
|
+
preferred units for voltage (mV), current (A/F), and time (ms).
|
|
50
|
+
"""
|
|
51
|
+
# Raise exception if protocol is set
|
|
52
|
+
if protocol is not None:
|
|
53
|
+
raise ValueError(
|
|
54
|
+
'DiffSL export does not support an input protocol.'
|
|
55
|
+
)
|
|
56
|
+
|
|
57
|
+
# Check model validity
|
|
58
|
+
try:
|
|
59
|
+
model.validate()
|
|
60
|
+
except myokit.MyokitError as e:
|
|
61
|
+
raise myokit.ExportError(
|
|
62
|
+
'DiffSL export requires a valid model.'
|
|
63
|
+
) from e
|
|
64
|
+
|
|
65
|
+
# Prepare model for export
|
|
66
|
+
model = self._prep_model(model, convert_units)
|
|
67
|
+
|
|
68
|
+
# Generate DiffSL model
|
|
69
|
+
diffsl_model = self._generate_diffsl(model)
|
|
70
|
+
|
|
71
|
+
# Write DiffSL model to file
|
|
72
|
+
with open(path, 'w') as f:
|
|
73
|
+
f.write(diffsl_model)
|
|
74
|
+
|
|
75
|
+
def _prep_model(self, model, convert_units):
|
|
76
|
+
"""
|
|
77
|
+
Prepare the model for export to DiffSL.
|
|
78
|
+
"""
|
|
79
|
+
# Rewrite model so that any Markov models have a 1-sum(...) state
|
|
80
|
+
# This also clones the model, so that changes can be made
|
|
81
|
+
model = markov.convert_markov_models_to_compact_form(model)
|
|
82
|
+
|
|
83
|
+
# Remove all model bindings apart from time and pace
|
|
84
|
+
_ = myokit._prepare_bindings(
|
|
85
|
+
model,
|
|
86
|
+
{
|
|
87
|
+
'time': 't',
|
|
88
|
+
'pace': 'pace',
|
|
89
|
+
},
|
|
90
|
+
)
|
|
91
|
+
|
|
92
|
+
# Remove hardcoded stimulus protocol, if any
|
|
93
|
+
guess.remove_embedded_protocol(model)
|
|
94
|
+
|
|
95
|
+
# Get / try to guess some model variables
|
|
96
|
+
time = model.time() # engine.time
|
|
97
|
+
vm = guess.membrane_potential(model) # Vm
|
|
98
|
+
cm = guess.membrane_capacitance(model) # Cm
|
|
99
|
+
currents = self._guess_currents(model)
|
|
100
|
+
|
|
101
|
+
# Check for explicit time dependence
|
|
102
|
+
time_refs = list(time.refs_by())
|
|
103
|
+
if time_refs:
|
|
104
|
+
raise myokit.ExportError(
|
|
105
|
+
'DiffSL export does not support explicit time dependence:\n'
|
|
106
|
+
+ '\n'.join([f'{v} = {v.rhs()}' for v in time_refs])
|
|
107
|
+
)
|
|
108
|
+
|
|
109
|
+
if convert_units:
|
|
110
|
+
# Convert currents to A/F
|
|
111
|
+
helpers = [] if cm is None else [cm.rhs()]
|
|
112
|
+
for var in currents:
|
|
113
|
+
self._convert_current_unit(var, helpers=helpers)
|
|
114
|
+
|
|
115
|
+
# Convert potentials to mV
|
|
116
|
+
self._convert_potential_unit(vm)
|
|
117
|
+
|
|
118
|
+
# Convert time to ms
|
|
119
|
+
if time.unit() != myokit.units.ms:
|
|
120
|
+
self._convert_unit(time, 'ms')
|
|
121
|
+
|
|
122
|
+
# Add intermediary variables for state derivatives with rhs references
|
|
123
|
+
# Before:
|
|
124
|
+
# dot(x) = x / 5
|
|
125
|
+
# y = 1 + dot(x)
|
|
126
|
+
# After:
|
|
127
|
+
# dot_x = x / 5
|
|
128
|
+
# dot(x) = dot_x
|
|
129
|
+
# y = 1 + dot_x
|
|
130
|
+
model.remove_derivative_references()
|
|
131
|
+
|
|
132
|
+
return model
|
|
133
|
+
|
|
134
|
+
def _generate_diffsl(self, model):
|
|
135
|
+
"""
|
|
136
|
+
Generate a DiffSL model from a prepped Myokit model.
|
|
137
|
+
DiffSL inputs will be left empty, and outputs will be set to
|
|
138
|
+
state variables in alphabetical order.
|
|
139
|
+
"""
|
|
140
|
+
|
|
141
|
+
# Create DiffSL-compatible variable names
|
|
142
|
+
var_to_name = self._create_diffsl_variable_names(model)
|
|
143
|
+
|
|
144
|
+
# Create a naming function
|
|
145
|
+
def var_name(e):
|
|
146
|
+
if isinstance(e, myokit.Derivative):
|
|
147
|
+
return var_to_name[e]
|
|
148
|
+
elif isinstance(e, myokit.LhsExpression):
|
|
149
|
+
return var_to_name[e.var()]
|
|
150
|
+
elif isinstance(e, myokit.Variable):
|
|
151
|
+
return var_to_name[e]
|
|
152
|
+
raise ValueError( # pragma: no cover
|
|
153
|
+
'Not a variable or LhsExpression: ' + str(e)
|
|
154
|
+
)
|
|
155
|
+
|
|
156
|
+
# Create an expression writer
|
|
157
|
+
e = diffsl.DiffSLExpressionWriter()
|
|
158
|
+
e.set_lhs_function(var_name)
|
|
159
|
+
|
|
160
|
+
export_lines = [] # DiffSL export lines
|
|
161
|
+
tab = ' ' # Tab character
|
|
162
|
+
|
|
163
|
+
# Sort equations in solvable order (grouped by component)
|
|
164
|
+
sorted_eqs = model.solvable_order()
|
|
165
|
+
|
|
166
|
+
# Variables to be excluded from output or handled separately.
|
|
167
|
+
# State derivatives are handled in dudt_i, F_i and G_i; time is
|
|
168
|
+
# excluded from the output; pace is handled separately.
|
|
169
|
+
time = model.time()
|
|
170
|
+
pace = model.binding('pace')
|
|
171
|
+
special_vars = set(v for v in model.states())
|
|
172
|
+
special_vars.add(time)
|
|
173
|
+
if pace is not None:
|
|
174
|
+
special_vars.add(pace)
|
|
175
|
+
|
|
176
|
+
# Add metadata
|
|
177
|
+
export_lines.append('/*')
|
|
178
|
+
export_lines.append('This file was generated by Myokit.')
|
|
179
|
+
if model.meta:
|
|
180
|
+
export_lines.append('')
|
|
181
|
+
for key, val in sorted(model.meta.items()):
|
|
182
|
+
export_lines.append(f'{key}: {val}')
|
|
183
|
+
export_lines.append('')
|
|
184
|
+
export_lines.append('*/')
|
|
185
|
+
export_lines.append('')
|
|
186
|
+
|
|
187
|
+
# Add empty input parameter list
|
|
188
|
+
export_lines.append('/* Input parameters */')
|
|
189
|
+
export_lines.append('/* E.g. in = [ varZero, varOne, varTwo ] */')
|
|
190
|
+
export_lines.append('in = [ ]')
|
|
191
|
+
export_lines.append('')
|
|
192
|
+
|
|
193
|
+
# Add pace
|
|
194
|
+
if pace is not None:
|
|
195
|
+
export_lines.append('/* Engine: pace */')
|
|
196
|
+
export_lines.append('/* E.g.')
|
|
197
|
+
export_lines.append(' -80 * (1 - sigmoid((t-100)*5000))')
|
|
198
|
+
export_lines.append(
|
|
199
|
+
' -120 * (sigmoid((t-100)*5000) - sigmoid((t-200)*5000))'
|
|
200
|
+
)
|
|
201
|
+
export_lines.append('*/')
|
|
202
|
+
|
|
203
|
+
lhs = var_name(pace)
|
|
204
|
+
rhs = e.ex(pace.rhs())
|
|
205
|
+
qname = pace.qname()
|
|
206
|
+
unit = '' if pace.unit() is None else f' {pace.unit()}'
|
|
207
|
+
export_lines.append(f'{lhs} {{ {rhs} }} /* {qname}{unit} */')
|
|
208
|
+
export_lines.append('')
|
|
209
|
+
|
|
210
|
+
# Add constants
|
|
211
|
+
const_vars = (
|
|
212
|
+
set(model.variables(const=True, deep=True, state=False))
|
|
213
|
+
- special_vars
|
|
214
|
+
)
|
|
215
|
+
for component_label, eq_list in sorted_eqs.items():
|
|
216
|
+
const_eqs = [
|
|
217
|
+
eq for eq in eq_list.equations() if eq.lhs.var() in const_vars
|
|
218
|
+
]
|
|
219
|
+
if const_eqs:
|
|
220
|
+
export_lines.append(f'/* Constants: {component_label} */')
|
|
221
|
+
for eq in const_eqs:
|
|
222
|
+
v = eq.lhs.var()
|
|
223
|
+
lhs = var_name(v)
|
|
224
|
+
rhs = e.ex(eq.rhs)
|
|
225
|
+
qname = v.qname()
|
|
226
|
+
unit = '' if v.unit() is None else f' {v.unit()}'
|
|
227
|
+
export_lines.append(
|
|
228
|
+
f'{lhs} {{ {rhs} }} /* {qname}{unit} */'
|
|
229
|
+
)
|
|
230
|
+
export_lines.append('')
|
|
231
|
+
|
|
232
|
+
# Add initial conditions `u_i`
|
|
233
|
+
export_lines.append('/* Initial conditions */')
|
|
234
|
+
export_lines.append('u_i {')
|
|
235
|
+
for v in model.states():
|
|
236
|
+
lhs = var_name(v)
|
|
237
|
+
rhs = v.initial_value()
|
|
238
|
+
qname = v.qname()
|
|
239
|
+
unit = '' if v.unit() is None else f' {v.unit()}'
|
|
240
|
+
export_lines.append(f'{tab}{lhs} = {rhs}, /* {qname}{unit} */')
|
|
241
|
+
export_lines.append('}')
|
|
242
|
+
export_lines.append('')
|
|
243
|
+
|
|
244
|
+
# Add state derivatives `dudt_i`
|
|
245
|
+
export_lines.append('dudt_i {')
|
|
246
|
+
for v in model.states():
|
|
247
|
+
lhs = var_name(v.lhs())
|
|
248
|
+
export_lines.append(f'{tab}{lhs} = 0,')
|
|
249
|
+
export_lines.append('}')
|
|
250
|
+
export_lines.append('')
|
|
251
|
+
|
|
252
|
+
# Add remaining variables
|
|
253
|
+
todo_vars = (
|
|
254
|
+
set(model.variables(const=False, deep=True, state=False))
|
|
255
|
+
- special_vars
|
|
256
|
+
)
|
|
257
|
+
for component_label, eq_list in sorted_eqs.items():
|
|
258
|
+
todo_eqs = [
|
|
259
|
+
eq for eq in eq_list.equations() if eq.lhs.var() in todo_vars
|
|
260
|
+
]
|
|
261
|
+
if todo_eqs:
|
|
262
|
+
export_lines.append(f'/* Variables: {component_label} */')
|
|
263
|
+
for eq in todo_eqs:
|
|
264
|
+
v = eq.lhs.var()
|
|
265
|
+
lhs = var_name(v)
|
|
266
|
+
rhs = e.ex(eq.rhs)
|
|
267
|
+
qname = v.qname()
|
|
268
|
+
unit = '' if v.unit() is None else f' {v.unit()}'
|
|
269
|
+
export_lines.append(
|
|
270
|
+
f'{lhs} {{ {rhs} }} /* {qname}{unit} */'
|
|
271
|
+
)
|
|
272
|
+
export_lines.append('')
|
|
273
|
+
|
|
274
|
+
# Add `F_i`
|
|
275
|
+
export_lines.append('/* Solve */')
|
|
276
|
+
export_lines.append('F_i {')
|
|
277
|
+
for v in model.states():
|
|
278
|
+
lhs = var_name(v.lhs())
|
|
279
|
+
export_lines.append(f'{tab}{lhs},')
|
|
280
|
+
export_lines.append('}')
|
|
281
|
+
export_lines.append('')
|
|
282
|
+
|
|
283
|
+
# Add `G_i`
|
|
284
|
+
export_lines.append('G_i {')
|
|
285
|
+
for v in model.states():
|
|
286
|
+
rhs = e.ex(v.rhs())
|
|
287
|
+
export_lines.append(f'{tab}{rhs},')
|
|
288
|
+
export_lines.append('}')
|
|
289
|
+
export_lines.append('')
|
|
290
|
+
|
|
291
|
+
# Output state variables in alphabetical order
|
|
292
|
+
export_lines.append('/* Output */')
|
|
293
|
+
export_lines.append('out_i {')
|
|
294
|
+
vars = list(model.states())
|
|
295
|
+
for v in sorted(vars, key=lambda x: var_name(x).swapcase()):
|
|
296
|
+
lhs = var_name(v)
|
|
297
|
+
export_lines.append(f'{tab}{lhs},')
|
|
298
|
+
export_lines.append('}')
|
|
299
|
+
export_lines.append('')
|
|
300
|
+
|
|
301
|
+
return '\n'.join(export_lines)
|
|
302
|
+
|
|
303
|
+
def _convert_current_unit(self, var, helpers=None):
|
|
304
|
+
"""
|
|
305
|
+
Convert a current to A/F if its present unit isn't recommended.
|
|
306
|
+
"""
|
|
307
|
+
recommended_units = [
|
|
308
|
+
myokit.parse_unit('pA/pF'),
|
|
309
|
+
myokit.parse_unit('uA/cm^2'),
|
|
310
|
+
]
|
|
311
|
+
|
|
312
|
+
if var.unit() not in recommended_units:
|
|
313
|
+
self._convert_unit(var, 'A/F', helpers=helpers)
|
|
314
|
+
|
|
315
|
+
def _convert_potential_unit(self, var, helpers=None):
|
|
316
|
+
"""
|
|
317
|
+
Convert a potential to mV if its present unit isn't recommended.
|
|
318
|
+
"""
|
|
319
|
+
recommended_units = [myokit.units.mV]
|
|
320
|
+
|
|
321
|
+
if var.unit() not in recommended_units:
|
|
322
|
+
self._convert_unit(var, 'mV', helpers=helpers)
|
|
323
|
+
|
|
324
|
+
def _convert_unit(self, var, unit, helpers=None):
|
|
325
|
+
"""
|
|
326
|
+
Convert a variable to the given unit if possible. Throws a warning if
|
|
327
|
+
the conversion is not possible.
|
|
328
|
+
"""
|
|
329
|
+
if var.unit() is None:
|
|
330
|
+
return
|
|
331
|
+
|
|
332
|
+
try:
|
|
333
|
+
var.convert_unit(unit, helpers=helpers)
|
|
334
|
+
except myokit.IncompatibleUnitError:
|
|
335
|
+
warnings.warn(
|
|
336
|
+
'Unable to convert ' + var.qname() + ' to recommended'
|
|
337
|
+
' units of ' + str(unit) + '.'
|
|
338
|
+
)
|
|
339
|
+
|
|
340
|
+
def _create_diffsl_variable_names(self, model):
|
|
341
|
+
"""
|
|
342
|
+
Create DiffSL-compatible names for all variables in the model.
|
|
343
|
+
|
|
344
|
+
The following strategy is followed:
|
|
345
|
+
- Fully qualified names are used for all variables.
|
|
346
|
+
- Variables are checked for special names, and changed if necessary.
|
|
347
|
+
- Unsupported characters like '.' and '_' are replaced.
|
|
348
|
+
- Any conflicts are resolved in a final step by appending a number.
|
|
349
|
+
"""
|
|
350
|
+
|
|
351
|
+
# Convert name to a DiffSL-compatible variable name
|
|
352
|
+
def convert_name(name):
|
|
353
|
+
# Remove unsupported chars like '_' and '.', and stagger case.
|
|
354
|
+
# Preserves existing staggered case in names e.g.
|
|
355
|
+
# voltage_clamp.R_seal_MOhm -> voltageClampRSealMOhm
|
|
356
|
+
# voltageClamp.RSealMOhm -> voltageClampRSealMOhm
|
|
357
|
+
name_chars = []
|
|
358
|
+
caps_flag = False
|
|
359
|
+
for ch in name:
|
|
360
|
+
if ch.isalpha():
|
|
361
|
+
if caps_flag:
|
|
362
|
+
name_chars.append(ch.upper())
|
|
363
|
+
caps_flag = False
|
|
364
|
+
else:
|
|
365
|
+
name_chars.append(ch)
|
|
366
|
+
elif ch.isdigit():
|
|
367
|
+
name_chars.append(ch)
|
|
368
|
+
caps_flag = True
|
|
369
|
+
else:
|
|
370
|
+
caps_flag = True
|
|
371
|
+
|
|
372
|
+
return ''.join(name_chars)
|
|
373
|
+
|
|
374
|
+
var_to_name = collections.OrderedDict()
|
|
375
|
+
|
|
376
|
+
# Store initial names for variables
|
|
377
|
+
for var in model.variables(deep=True, sort=True):
|
|
378
|
+
var_to_name[var] = convert_name(var.qname())
|
|
379
|
+
if var.is_state():
|
|
380
|
+
var_to_name[var.lhs()] = convert_name('diff_' + var.qname())
|
|
381
|
+
|
|
382
|
+
# Check for conflicts with known keywords
|
|
383
|
+
from . import keywords
|
|
384
|
+
|
|
385
|
+
needs_renaming = collections.OrderedDict()
|
|
386
|
+
for keyword in keywords:
|
|
387
|
+
needs_renaming[keyword] = []
|
|
388
|
+
|
|
389
|
+
# Find naming conflicts, create inverse mapping
|
|
390
|
+
name_to_var = collections.OrderedDict()
|
|
391
|
+
for var, name in var_to_name.items():
|
|
392
|
+
|
|
393
|
+
# Known conflict?
|
|
394
|
+
if name in needs_renaming:
|
|
395
|
+
needs_renaming[name].append(var)
|
|
396
|
+
continue
|
|
397
|
+
|
|
398
|
+
# Test for new conflicts
|
|
399
|
+
var2 = name_to_var.get(name, None)
|
|
400
|
+
if var2 is not None:
|
|
401
|
+
needs_renaming[name] = [var2, var]
|
|
402
|
+
continue
|
|
403
|
+
|
|
404
|
+
name_to_var[name] = var
|
|
405
|
+
|
|
406
|
+
# Resolve naming conflicts
|
|
407
|
+
for name, variables in needs_renaming.items():
|
|
408
|
+
# Add a number to the end of the name, increasing until unique
|
|
409
|
+
i = 1
|
|
410
|
+
root = name
|
|
411
|
+
for var in variables:
|
|
412
|
+
name = f'{root}{i}'
|
|
413
|
+
while name in name_to_var:
|
|
414
|
+
i += 1
|
|
415
|
+
name = f'{root}{i}'
|
|
416
|
+
var_to_name[var] = name
|
|
417
|
+
name_to_var[name] = var
|
|
418
|
+
|
|
419
|
+
return var_to_name
|
|
420
|
+
|
|
421
|
+
def _guess_currents(self, model):
|
|
422
|
+
"""
|
|
423
|
+
Tries to make a list of membrane currents. Removes potentials
|
|
424
|
+
from the guessed list.
|
|
425
|
+
"""
|
|
426
|
+
unmatch_units = [
|
|
427
|
+
myokit.units.mV,
|
|
428
|
+
myokit.units.V,
|
|
429
|
+
]
|
|
430
|
+
guessed_currents = guess.membrane_currents(model)
|
|
431
|
+
return [x for x in guessed_currents if x.unit() not in unmatch_units]
|
|
432
|
+
|
|
433
|
+
def supports_model(self):
|
|
434
|
+
"""See :meth:`myokit.formats.Exporter.supports_model()`."""
|
|
435
|
+
return True
|
myokit/formats/heka/__init__.py
CHANGED
|
@@ -9,7 +9,10 @@ from ._patchmaster import ( # noqa
|
|
|
9
9
|
AmplifierSeries,
|
|
10
10
|
AmplifierState,
|
|
11
11
|
AmplifierStateRecord,
|
|
12
|
+
CSlowRange,
|
|
12
13
|
EndianAwareReader,
|
|
14
|
+
Filter1Setting,
|
|
15
|
+
Filter2Type,
|
|
13
16
|
Group,
|
|
14
17
|
NoSupportedDAChannelError,
|
|
15
18
|
PatchMasterFile,
|
|
@@ -22,6 +25,7 @@ from ._patchmaster import ( # noqa
|
|
|
22
25
|
Stimulus,
|
|
23
26
|
StimulusChannel,
|
|
24
27
|
StimulusFile,
|
|
28
|
+
StimulusFilterSetting,
|
|
25
29
|
Sweep,
|
|
26
30
|
Trace,
|
|
27
31
|
TreeNode,
|