myokit 1.35.0__py3-none-any.whl → 1.35.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 +11 -14
- myokit/__main__.py +0 -3
- myokit/_config.py +1 -3
- myokit/_datablock.py +914 -12
- myokit/_model_api.py +1 -3
- myokit/_myokit_version.py +1 -1
- myokit/_protocol.py +14 -28
- myokit/_sim/cable.c +1 -1
- myokit/_sim/cable.py +3 -2
- myokit/_sim/cmodel.h +1 -0
- myokit/_sim/cvodessim.c +79 -42
- myokit/_sim/cvodessim.py +20 -8
- myokit/_sim/fiber_tissue.c +1 -1
- myokit/_sim/fiber_tissue.py +3 -2
- myokit/_sim/openclsim.c +1 -1
- myokit/_sim/openclsim.py +8 -11
- myokit/_sim/pacing.h +121 -106
- myokit/_unit.py +1 -1
- myokit/formats/__init__.py +178 -0
- myokit/formats/axon/_abf.py +911 -841
- myokit/formats/axon/_atf.py +62 -59
- myokit/formats/axon/_importer.py +2 -2
- myokit/formats/heka/__init__.py +38 -0
- myokit/formats/heka/_importer.py +39 -0
- myokit/formats/heka/_patchmaster.py +2512 -0
- myokit/formats/wcp/_wcp.py +318 -133
- myokit/gui/datablock_viewer.py +144 -77
- myokit/gui/datalog_viewer.py +212 -231
- myokit/tests/ansic_event_based_pacing.py +3 -3
- myokit/tests/{ansic_fixed_form_pacing.py → ansic_time_series_pacing.py} +6 -6
- myokit/tests/data/formats/abf-v2.abf +0 -0
- myokit/tests/test_datablock.py +84 -0
- myokit/tests/test_datalog.py +2 -1
- myokit/tests/test_formats_axon.py +589 -136
- myokit/tests/test_formats_wcp.py +191 -22
- myokit/tests/test_pacing_system_c.py +51 -23
- myokit/tests/test_pacing_system_py.py +18 -0
- myokit/tests/test_simulation_1d.py +62 -22
- myokit/tests/test_simulation_cvodes.py +52 -3
- myokit/tests/test_simulation_fiber_tissue.py +35 -4
- myokit/tests/test_simulation_opencl.py +28 -4
- {myokit-1.35.0.dist-info → myokit-1.35.2.dist-info}/LICENSE.txt +1 -1
- {myokit-1.35.0.dist-info → myokit-1.35.2.dist-info}/METADATA +1 -1
- {myokit-1.35.0.dist-info → myokit-1.35.2.dist-info}/RECORD +47 -44
- {myokit-1.35.0.dist-info → myokit-1.35.2.dist-info}/WHEEL +0 -0
- {myokit-1.35.0.dist-info → myokit-1.35.2.dist-info}/entry_points.txt +0 -0
- {myokit-1.35.0.dist-info → myokit-1.35.2.dist-info}/top_level.txt +0 -0
myokit/formats/axon/_atf.py
CHANGED
|
@@ -11,6 +11,7 @@ import numpy as np
|
|
|
11
11
|
|
|
12
12
|
from collections import OrderedDict
|
|
13
13
|
|
|
14
|
+
import myokit
|
|
14
15
|
|
|
15
16
|
# Format used for any text
|
|
16
17
|
_ENC = 'ascii'
|
|
@@ -30,11 +31,11 @@ class AtfFile:
|
|
|
30
31
|
"""
|
|
31
32
|
def __init__(self, filename):
|
|
32
33
|
# The path to the file and its basename
|
|
33
|
-
self.
|
|
34
|
+
self._path = os.path.abspath(filename)
|
|
34
35
|
self._filename = os.path.basename(filename)
|
|
35
36
|
|
|
36
37
|
# A version string
|
|
37
|
-
self.
|
|
38
|
+
self._version_str = None
|
|
38
39
|
|
|
39
40
|
# A (multi-line) string containing meta-data found in this file
|
|
40
41
|
self._meta = None
|
|
@@ -45,56 +46,39 @@ class AtfFile:
|
|
|
45
46
|
# Read data
|
|
46
47
|
self._read()
|
|
47
48
|
|
|
48
|
-
def filename(self):
|
|
49
|
-
"""
|
|
50
|
-
Returns this ATF's filename.
|
|
51
|
-
"""
|
|
52
|
-
return self._file
|
|
53
|
-
|
|
54
49
|
def __getitem__(self, key):
|
|
55
50
|
return self._data.__getitem__(key)
|
|
56
51
|
|
|
57
52
|
def __iter__(self):
|
|
58
|
-
"""
|
|
59
|
-
Iterates over all data arrays in this ATF file.
|
|
60
|
-
"""
|
|
61
53
|
return iter(self._data)
|
|
62
54
|
|
|
55
|
+
def __len__(self):
|
|
56
|
+
return len(self._data)
|
|
57
|
+
|
|
58
|
+
def filename(self):
|
|
59
|
+
""" Returns this ATF's filename. """
|
|
60
|
+
return self._filename
|
|
61
|
+
|
|
62
|
+
def info(self):
|
|
63
|
+
""" Returns this ATF's header/meta data. """
|
|
64
|
+
# Deprecated since 2023-07-12
|
|
65
|
+
import warnings
|
|
66
|
+
warnings.warn(
|
|
67
|
+
'The method `info` is deprecated. Please use `meta_str` instead.')
|
|
68
|
+
return self.meta_str()
|
|
69
|
+
|
|
63
70
|
def items(self):
|
|
64
|
-
"""
|
|
65
|
-
Iterates over all key-value pairs in this ATF file's data.
|
|
66
|
-
"""
|
|
71
|
+
""" Returns an iterator over all ``(key, value)`` pairs. """
|
|
67
72
|
return iter(self._data.items())
|
|
68
73
|
|
|
69
74
|
def keys(self):
|
|
70
|
-
"""
|
|
71
|
-
Iterates over all keys in this ATF file's data.
|
|
72
|
-
"""
|
|
75
|
+
""" Returns an iterator over all keys in this ATF. """
|
|
73
76
|
return iter(self._data.keys())
|
|
74
77
|
|
|
75
|
-
def
|
|
76
|
-
"""
|
|
77
|
-
Iterates over all values in this ATF file's data.
|
|
78
|
-
"""
|
|
79
|
-
return iter(self._data.values())
|
|
80
|
-
|
|
81
|
-
def __len__(self):
|
|
82
|
-
"""
|
|
83
|
-
Returns the number of records in this file.
|
|
84
|
-
"""
|
|
85
|
-
return len(self._data)
|
|
86
|
-
|
|
87
|
-
def info(self):
|
|
88
|
-
"""
|
|
89
|
-
Returns the header/meta data found in this file.
|
|
90
|
-
"""
|
|
91
|
-
return self._meta
|
|
92
|
-
|
|
93
|
-
def myokit_log(self):
|
|
78
|
+
def log(self):
|
|
94
79
|
"""
|
|
95
80
|
Returns this file's time series data as a :class:`myokit.DataLog`.
|
|
96
81
|
"""
|
|
97
|
-
import myokit
|
|
98
82
|
log = myokit.DataLog()
|
|
99
83
|
if len(self._data) > 0:
|
|
100
84
|
log.set_time_key(next(iter(self._data.keys())))
|
|
@@ -102,17 +86,36 @@ class AtfFile:
|
|
|
102
86
|
log[k] = v
|
|
103
87
|
return log
|
|
104
88
|
|
|
105
|
-
def
|
|
89
|
+
def meta_str(self, verbose=True):
|
|
106
90
|
"""
|
|
107
|
-
|
|
91
|
+
Returns this ATF's header data as an unstructured multi-line string.
|
|
92
|
+
|
|
93
|
+
Note that the ``verbose`` argument doesn't do anything, but provides
|
|
94
|
+
compatibility with similar methods in other files.
|
|
108
95
|
"""
|
|
109
|
-
|
|
96
|
+
return self._meta
|
|
97
|
+
|
|
98
|
+
def myokit_log(self):
|
|
99
|
+
""" Deprecated alias of :meth:`log`. """
|
|
100
|
+
# Deprecated since 2023-06-22
|
|
101
|
+
import warnings
|
|
102
|
+
warnings.warn(
|
|
103
|
+
'The method `myokit_log` is deprecated. Please use `log` instead.')
|
|
104
|
+
return self.log()
|
|
105
|
+
|
|
106
|
+
def path(self):
|
|
107
|
+
""" Returns the path to this ATF file. """
|
|
108
|
+
return self._path
|
|
109
|
+
|
|
110
|
+
def _read(self):
|
|
111
|
+
""" Reads the data in this file. """
|
|
112
|
+
with open(self._path, 'rb') as f:
|
|
110
113
|
# Check version
|
|
111
114
|
line = f.readline().decode(_ENC)
|
|
112
115
|
line_index = 1
|
|
113
116
|
if line[:3] != 'ATF':
|
|
114
117
|
raise Exception('Unrecognised file type.')
|
|
115
|
-
self.
|
|
118
|
+
self._version_str = line[3:].strip()
|
|
116
119
|
|
|
117
120
|
# Read number of header lines, number of fields
|
|
118
121
|
line = f.readline().decode(_ENC)
|
|
@@ -131,9 +134,8 @@ class AtfFile:
|
|
|
131
134
|
line_index += 1
|
|
132
135
|
if line[0] != '"' or line[-1] != '"':
|
|
133
136
|
raise Exception(
|
|
134
|
-
'Invalid header on line
|
|
135
|
-
|
|
136
|
-
' marks "like this".')
|
|
137
|
+
f'Invalid header on line f{line_index}: expecting '
|
|
138
|
+
f' lines wrapped in double quotes ("like this").')
|
|
137
139
|
line = line[1:-1].strip()
|
|
138
140
|
raw.append(line)
|
|
139
141
|
if key_value_pairs:
|
|
@@ -170,8 +172,8 @@ class AtfFile:
|
|
|
170
172
|
delims = delims[1:-1]
|
|
171
173
|
if len(delims) + 1 != nf:
|
|
172
174
|
raise Exception(
|
|
173
|
-
'Unable to parse column headers: Expected '
|
|
174
|
-
|
|
175
|
+
f'Unable to parse column headers: Expected {nf} headers,'
|
|
176
|
+
f' found {len(delims) + 1}.')
|
|
175
177
|
commas = ',' in delims[0]
|
|
176
178
|
for delim in delims:
|
|
177
179
|
if commas != (',' in delim):
|
|
@@ -193,8 +195,8 @@ class AtfFile:
|
|
|
193
195
|
if len(keys) != nf: # pragma: no cover
|
|
194
196
|
# This should have been picked up above
|
|
195
197
|
raise Exception(
|
|
196
|
-
'Unable to parse column headers: Expected '
|
|
197
|
-
|
|
198
|
+
f'Unable to parse column headers: Expected {nf} headers,'
|
|
199
|
+
f' found {len(keys)}.')
|
|
198
200
|
|
|
199
201
|
# Read data
|
|
200
202
|
data = []
|
|
@@ -209,18 +211,19 @@ class AtfFile:
|
|
|
209
211
|
vals = line.split(sep)
|
|
210
212
|
if len(vals) != nf:
|
|
211
213
|
raise Exception(
|
|
212
|
-
'Invalid data on line
|
|
213
|
-
|
|
214
|
-
+ ' ' + str(len(vals)) + '.')
|
|
214
|
+
f'Invalid data on line f{line_index}: expecting {nf}'
|
|
215
|
+
f' fields, found {len(vals)}.')
|
|
215
216
|
vals = [float(x) for x in vals]
|
|
216
217
|
for k, d in enumerate(vals):
|
|
217
218
|
data[k].append(d)
|
|
218
219
|
|
|
220
|
+
def values(self):
|
|
221
|
+
""" Returns an iterator over all values in this ATF. """
|
|
222
|
+
return iter(self._data.values())
|
|
223
|
+
|
|
219
224
|
def version(self):
|
|
220
|
-
"""
|
|
221
|
-
|
|
222
|
-
"""
|
|
223
|
-
return self._version
|
|
225
|
+
""" Returns a string representation of this file's version number. """
|
|
226
|
+
return self._version_str
|
|
224
227
|
|
|
225
228
|
|
|
226
229
|
def load_atf(filename):
|
|
@@ -228,7 +231,7 @@ def load_atf(filename):
|
|
|
228
231
|
Reads an ATF file and returns its data as a :class:`myokit.DataLog`.
|
|
229
232
|
"""
|
|
230
233
|
filename = os.path.expanduser(filename)
|
|
231
|
-
return AtfFile(filename).
|
|
234
|
+
return AtfFile(filename).log()
|
|
232
235
|
|
|
233
236
|
|
|
234
237
|
def save_atf(log, filename, fields=None):
|
|
@@ -306,12 +309,12 @@ def save_atf(log, filename, fields=None):
|
|
|
306
309
|
f.write(('ATF 1.0' + eol).encode(_ENC))
|
|
307
310
|
|
|
308
311
|
# Write number of header lines, number of fields
|
|
309
|
-
f.write(
|
|
312
|
+
f.write(f'{nh}{delim}{nf}{eol}'.encode(_ENC))
|
|
310
313
|
for k, v in header:
|
|
311
|
-
f.write(
|
|
314
|
+
f.write(f'"{k}={v}"{eol}'.encode(_ENC))
|
|
312
315
|
|
|
313
316
|
# Write field names
|
|
314
|
-
f.write((delim.join(['"
|
|
317
|
+
f.write((delim.join([f'"{k}"' for k in keys]) + eol).encode(_ENC))
|
|
315
318
|
|
|
316
319
|
# Write data
|
|
317
320
|
for i in range(nd):
|
myokit/formats/axon/_importer.py
CHANGED
|
@@ -16,7 +16,7 @@ class AbfImporter(myokit.formats.Importer):
|
|
|
16
16
|
def supports_protocol(self):
|
|
17
17
|
return True
|
|
18
18
|
|
|
19
|
-
def protocol(self, filename, channel=
|
|
19
|
+
def protocol(self, filename, channel=0):
|
|
20
20
|
"""
|
|
21
21
|
Attempts to load the protocol from the file at ``filename``.
|
|
22
22
|
|
|
@@ -25,4 +25,4 @@ class AbfImporter(myokit.formats.Importer):
|
|
|
25
25
|
"""
|
|
26
26
|
from myokit.formats.axon import AbfFile
|
|
27
27
|
abf = AbfFile(filename)
|
|
28
|
-
return abf.
|
|
28
|
+
return abf.da_protocol(channel)
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
#
|
|
2
|
+
# Provides support for working with data in formats used by HEKA.
|
|
3
|
+
#
|
|
4
|
+
# This file is part of Myokit.
|
|
5
|
+
# See http://myokit.org for copyright, sharing, and licensing details.
|
|
6
|
+
from ._patchmaster import ( # noqa
|
|
7
|
+
AmplifierMode,
|
|
8
|
+
EndianAwareReader,
|
|
9
|
+
Group,
|
|
10
|
+
NoSupportedDAChannelError,
|
|
11
|
+
PatchMasterFile,
|
|
12
|
+
PulsedFile,
|
|
13
|
+
Segment,
|
|
14
|
+
SegmentClass,
|
|
15
|
+
SegmentIncrement,
|
|
16
|
+
SegmentStorage,
|
|
17
|
+
Series,
|
|
18
|
+
Stimulus,
|
|
19
|
+
StimulusChannel,
|
|
20
|
+
StimulusFile,
|
|
21
|
+
Sweep,
|
|
22
|
+
Trace,
|
|
23
|
+
TreeNode,
|
|
24
|
+
)
|
|
25
|
+
from ._importer import PatchMasterImporter
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
# Importers
|
|
29
|
+
_importers = {
|
|
30
|
+
'heka': PatchMasterImporter,
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def importers():
|
|
35
|
+
""" Returns a dict of all importers available in this module. """
|
|
36
|
+
return dict(_importers)
|
|
37
|
+
# Exporters
|
|
38
|
+
# Expression writers
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
#
|
|
2
|
+
# Imports step protocols from HEKA PatchMaster files.
|
|
3
|
+
#
|
|
4
|
+
# This file is part of Myokit.
|
|
5
|
+
# See http://myokit.org for copyright, sharing, and licensing details.
|
|
6
|
+
#
|
|
7
|
+
import myokit.formats
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class PatchMasterImporter(myokit.formats.Importer):
|
|
11
|
+
"""
|
|
12
|
+
This :class:`Importer <myokit.formats.Importer>` can import (step)
|
|
13
|
+
protocols from "series" inside HEKA PatchMaster files.
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
def supports_protocol(self):
|
|
17
|
+
return True
|
|
18
|
+
|
|
19
|
+
def protocol(self, filename, group=None, series=None):
|
|
20
|
+
"""
|
|
21
|
+
Attempts to load the protocol from the file at ``filename``.
|
|
22
|
+
|
|
23
|
+
Because PatchMaster files can contain several experiments, a further
|
|
24
|
+
selection can be made with the arguments:
|
|
25
|
+
|
|
26
|
+
``filename``
|
|
27
|
+
The file to read
|
|
28
|
+
``group``
|
|
29
|
+
The group to read from (as a string).
|
|
30
|
+
``series``
|
|
31
|
+
The integer index of the desired series in the specified group.
|
|
32
|
+
|
|
33
|
+
"""
|
|
34
|
+
from myokit.formats.heka import PatchMasterFile
|
|
35
|
+
with PatchMasterFile(filename) as f:
|
|
36
|
+
group = f.group(group)
|
|
37
|
+
series = group[series]
|
|
38
|
+
return series.stimulus().protocol()
|
|
39
|
+
|