myokit 1.36.0__py3-none-any.whl → 1.37.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 +6 -19
- myokit/_datablock.py +45 -55
- myokit/_datalog.py +2 -2
- myokit/_err.py +26 -3
- myokit/_expressions.py +241 -127
- myokit/_model_api.py +19 -13
- myokit/_myokit_version.py +1 -1
- myokit/_sim/cvodessim.c +221 -149
- myokit/_sim/jacobian.py +3 -3
- myokit/_sim/mcl.h +54 -0
- myokit/_sim/openclsim.py +5 -5
- myokit/_sim/rhs.py +1 -1
- myokit/formats/__init__.py +4 -9
- myokit/formats/ansic/_ewriter.py +4 -20
- myokit/formats/heka/_patchmaster.py +16 -10
- myokit/formats/opencl/_ewriter.py +3 -42
- myokit/formats/opencl/template/minilog.py +1 -1
- myokit/formats/sympy/_ereader.py +2 -1
- myokit/formats/wcp/_wcp.py +3 -3
- myokit/gui/datalog_viewer.py +12 -7
- myokit/lib/hh.py +3 -0
- myokit/lib/markov.py +2 -2
- myokit/lib/plots.py +4 -4
- myokit/tests/data/formats/wcp-file-empty.wcp +0 -0
- myokit/tests/test_datablock.py +10 -10
- myokit/tests/test_datalog.py +4 -1
- myokit/tests/test_expressions.py +532 -251
- myokit/tests/test_formats_ansic.py +6 -18
- myokit/tests/test_formats_cpp.py +0 -5
- myokit/tests/test_formats_cuda.py +7 -15
- myokit/tests/test_formats_easyml.py +4 -9
- myokit/tests/test_formats_latex.py +10 -11
- myokit/tests/test_formats_matlab.py +0 -8
- myokit/tests/test_formats_opencl.py +0 -29
- myokit/tests/test_formats_python.py +2 -19
- myokit/tests/test_formats_stan.py +0 -13
- myokit/tests/test_formats_sympy.py +3 -3
- myokit/tests/test_formats_wcp.py +15 -0
- myokit/tests/test_lib_hh.py +36 -0
- myokit/tests/test_model.py +20 -20
- myokit/tests/test_parsing.py +19 -0
- {myokit-1.36.0.dist-info → myokit-1.37.0.dist-info}/METADATA +1 -1
- {myokit-1.36.0.dist-info → myokit-1.37.0.dist-info}/RECORD +47 -46
- {myokit-1.36.0.dist-info → myokit-1.37.0.dist-info}/LICENSE.txt +0 -0
- {myokit-1.36.0.dist-info → myokit-1.37.0.dist-info}/WHEEL +0 -0
- {myokit-1.36.0.dist-info → myokit-1.37.0.dist-info}/entry_points.txt +0 -0
- {myokit-1.36.0.dist-info → myokit-1.37.0.dist-info}/top_level.txt +0 -0
myokit/_sim/mcl.h
CHANGED
|
@@ -651,6 +651,9 @@ PyObject* mcl_info_device_dict(cl_device_id device_id, size_t bufsize, char* buf
|
|
|
651
651
|
}
|
|
652
652
|
|
|
653
653
|
// Name
|
|
654
|
+
#ifdef MYOKIT_DEBUG_MESSAGES
|
|
655
|
+
printf(" Querying name\n");
|
|
656
|
+
#endif
|
|
654
657
|
flag = clGetDeviceInfo(device_id, CL_DEVICE_NAME, bufsize, buffer, NULL);
|
|
655
658
|
if(mcl_flag(flag)) { Py_DECREF(device); return NULL; }
|
|
656
659
|
val = PyUnicode_FromString(buffer);
|
|
@@ -658,6 +661,9 @@ PyObject* mcl_info_device_dict(cl_device_id device_id, size_t bufsize, char* buf
|
|
|
658
661
|
Py_CLEAR(val);
|
|
659
662
|
|
|
660
663
|
// Vendor
|
|
664
|
+
#ifdef MYOKIT_DEBUG_MESSAGES
|
|
665
|
+
printf(" Querying vendor\n");
|
|
666
|
+
#endif
|
|
661
667
|
flag = clGetDeviceInfo(device_id, CL_DEVICE_VENDOR, bufsize, buffer, NULL);
|
|
662
668
|
if(mcl_flag(flag)) { Py_DECREF(device); return NULL; }
|
|
663
669
|
val = PyUnicode_FromString(buffer);
|
|
@@ -665,6 +671,9 @@ PyObject* mcl_info_device_dict(cl_device_id device_id, size_t bufsize, char* buf
|
|
|
665
671
|
Py_CLEAR(val);
|
|
666
672
|
|
|
667
673
|
// Device version
|
|
674
|
+
#ifdef MYOKIT_DEBUG_MESSAGES
|
|
675
|
+
printf(" Querying device version\n");
|
|
676
|
+
#endif
|
|
668
677
|
flag = clGetDeviceInfo(device_id, CL_DEVICE_VERSION, bufsize, buffer, NULL);
|
|
669
678
|
if(mcl_flag(flag)) { Py_DECREF(device); return NULL; }
|
|
670
679
|
val = PyUnicode_FromString(buffer);
|
|
@@ -672,6 +681,9 @@ PyObject* mcl_info_device_dict(cl_device_id device_id, size_t bufsize, char* buf
|
|
|
672
681
|
Py_CLEAR(val);
|
|
673
682
|
|
|
674
683
|
// Driver version
|
|
684
|
+
#ifdef MYOKIT_DEBUG_MESSAGES
|
|
685
|
+
printf(" Querying driver version\n");
|
|
686
|
+
#endif
|
|
675
687
|
flag = clGetDeviceInfo(device_id, CL_DRIVER_VERSION, bufsize, buffer, NULL);
|
|
676
688
|
if(mcl_flag(flag)) { Py_DECREF(device); return NULL; }
|
|
677
689
|
val = PyUnicode_FromString(buffer);
|
|
@@ -679,6 +691,9 @@ PyObject* mcl_info_device_dict(cl_device_id device_id, size_t bufsize, char* buf
|
|
|
679
691
|
Py_CLEAR(val);
|
|
680
692
|
|
|
681
693
|
// Clock speed (MHz)
|
|
694
|
+
#ifdef MYOKIT_DEBUG_MESSAGES
|
|
695
|
+
printf(" Querying clock speed\n");
|
|
696
|
+
#endif
|
|
682
697
|
flag = clGetDeviceInfo(device_id, CL_DEVICE_MAX_CLOCK_FREQUENCY, sizeof(buf_uint), &buf_uint, NULL);
|
|
683
698
|
if(mcl_flag(flag)) { Py_DECREF(device); return NULL; }
|
|
684
699
|
val = PyLong_FromUnsignedLong(buf_uint);
|
|
@@ -686,6 +701,9 @@ PyObject* mcl_info_device_dict(cl_device_id device_id, size_t bufsize, char* buf
|
|
|
686
701
|
Py_CLEAR(val);
|
|
687
702
|
|
|
688
703
|
// Global memory (bytes)
|
|
704
|
+
#ifdef MYOKIT_DEBUG_MESSAGES
|
|
705
|
+
printf(" Querying global memory size\n");
|
|
706
|
+
#endif
|
|
689
707
|
flag = clGetDeviceInfo(device_id, CL_DEVICE_GLOBAL_MEM_SIZE, sizeof(buf_ulong), &buf_ulong, NULL);
|
|
690
708
|
if(mcl_flag(flag)) { Py_DECREF(device); return NULL; }
|
|
691
709
|
val = PyLong_FromUnsignedLongLong(buf_ulong);
|
|
@@ -693,6 +711,9 @@ PyObject* mcl_info_device_dict(cl_device_id device_id, size_t bufsize, char* buf
|
|
|
693
711
|
Py_CLEAR(val);
|
|
694
712
|
|
|
695
713
|
// Local memory (bytes)
|
|
714
|
+
#ifdef MYOKIT_DEBUG_MESSAGES
|
|
715
|
+
printf(" Querying local memory size\n");
|
|
716
|
+
#endif
|
|
696
717
|
flag = clGetDeviceInfo(device_id, CL_DEVICE_LOCAL_MEM_SIZE, sizeof(buf_ulong), &buf_ulong, NULL);
|
|
697
718
|
if(mcl_flag(flag)) { Py_DECREF(device); return NULL; }
|
|
698
719
|
val = PyLong_FromUnsignedLongLong(buf_ulong);
|
|
@@ -700,6 +721,9 @@ PyObject* mcl_info_device_dict(cl_device_id device_id, size_t bufsize, char* buf
|
|
|
700
721
|
Py_CLEAR(val);
|
|
701
722
|
|
|
702
723
|
// Const memory (bytes)
|
|
724
|
+
#ifdef MYOKIT_DEBUG_MESSAGES
|
|
725
|
+
printf(" Querying max buffer size\n");
|
|
726
|
+
#endif
|
|
703
727
|
flag = clGetDeviceInfo(device_id, CL_DEVICE_MAX_CONSTANT_BUFFER_SIZE, sizeof(buf_ulong), &buf_ulong, NULL);
|
|
704
728
|
if(mcl_flag(flag)) { Py_DECREF(device); return NULL; }
|
|
705
729
|
val = PyLong_FromUnsignedLongLong(buf_ulong);
|
|
@@ -707,6 +731,9 @@ PyObject* mcl_info_device_dict(cl_device_id device_id, size_t bufsize, char* buf
|
|
|
707
731
|
Py_CLEAR(val);
|
|
708
732
|
|
|
709
733
|
// Computing units
|
|
734
|
+
#ifdef MYOKIT_DEBUG_MESSAGES
|
|
735
|
+
printf(" Querying max computing units\n");
|
|
736
|
+
#endif
|
|
710
737
|
flag = clGetDeviceInfo(device_id, CL_DEVICE_MAX_COMPUTE_UNITS, sizeof(buf_uint), &buf_uint, NULL);
|
|
711
738
|
if(mcl_flag(flag)) { Py_DECREF(device); return NULL; }
|
|
712
739
|
val = PyLong_FromUnsignedLong(buf_uint);
|
|
@@ -714,6 +741,9 @@ PyObject* mcl_info_device_dict(cl_device_id device_id, size_t bufsize, char* buf
|
|
|
714
741
|
Py_CLEAR(val);
|
|
715
742
|
|
|
716
743
|
// Max workgroup size
|
|
744
|
+
#ifdef MYOKIT_DEBUG_MESSAGES
|
|
745
|
+
printf(" Querying max work group size\n");
|
|
746
|
+
#endif
|
|
717
747
|
flag = clGetDeviceInfo(device_id, CL_DEVICE_MAX_WORK_GROUP_SIZE, sizeof(buf_size_t), &buf_size_t, NULL);
|
|
718
748
|
if(mcl_flag(flag)) { Py_DECREF(device); return NULL; }
|
|
719
749
|
val = PyLong_FromSize_t(buf_size_t);
|
|
@@ -721,6 +751,9 @@ PyObject* mcl_info_device_dict(cl_device_id device_id, size_t bufsize, char* buf
|
|
|
721
751
|
Py_CLEAR(val);
|
|
722
752
|
|
|
723
753
|
// Max workitem sizes
|
|
754
|
+
#ifdef MYOKIT_DEBUG_MESSAGES
|
|
755
|
+
printf(" Querying max work item sizes\n");
|
|
756
|
+
#endif
|
|
724
757
|
flag = clGetDeviceInfo(device_id, CL_DEVICE_MAX_WORK_ITEM_DIMENSIONS, sizeof(buf_uint), &buf_uint, NULL);
|
|
725
758
|
if(mcl_flag(flag)) { Py_DECREF(device); return NULL; }
|
|
726
759
|
val = PyLong_FromUnsignedLong(buf_uint);
|
|
@@ -739,6 +772,9 @@ PyObject* mcl_info_device_dict(cl_device_id device_id, size_t bufsize, char* buf
|
|
|
739
772
|
Py_CLEAR(items_sizes_tuple);
|
|
740
773
|
|
|
741
774
|
// Maximum size of a kernel parameter
|
|
775
|
+
#ifdef MYOKIT_DEBUG_MESSAGES
|
|
776
|
+
printf(" Querying maximum kernel parameter size\n");
|
|
777
|
+
#endif
|
|
742
778
|
flag = clGetDeviceInfo(device_id, CL_DEVICE_MAX_PARAMETER_SIZE, sizeof(buf_size_t), &buf_size_t, NULL);
|
|
743
779
|
if(mcl_flag(flag)) { Py_DECREF(device); return NULL; }
|
|
744
780
|
val = PyLong_FromSize_t(buf_size_t);
|
|
@@ -834,29 +870,47 @@ mcl_info(void)
|
|
|
834
870
|
|
|
835
871
|
// Check all platforms
|
|
836
872
|
for (i=0; i<n_platforms; i++) {
|
|
873
|
+
#ifdef MYOKIT_DEBUG_MESSAGES
|
|
874
|
+
printf("Checking platform %u\n", (unsigned int)i);
|
|
875
|
+
#endif
|
|
837
876
|
|
|
838
877
|
// Create platform dict
|
|
839
878
|
platform = mcl_info_platform_dict(platform_ids[i], sizeof(buffer), buffer);
|
|
840
879
|
if (platform == NULL) { Py_DECREF(platforms); return NULL; }
|
|
880
|
+
#ifdef MYOKIT_DEBUG_MESSAGES
|
|
881
|
+
printf(" Obtained platform info dict.\n");
|
|
882
|
+
#endif
|
|
841
883
|
|
|
842
884
|
// Count devices
|
|
843
885
|
flag = clGetDeviceIDs(platform_ids[i], CL_DEVICE_TYPE_ALL, MCL_MAX_DEVICES, device_ids, &n_devices);
|
|
844
886
|
if (flag == CL_DEVICE_NOT_FOUND) {
|
|
845
887
|
n_devices = 0;
|
|
888
|
+
} else if (flag == CL_INVALID_VALUE) {
|
|
889
|
+
// This seems to happen on Mac OS 14.4.1
|
|
890
|
+
n_devices = 0;
|
|
846
891
|
} else if (mcl_flag(flag)) {
|
|
847
892
|
Py_DECREF(platforms);
|
|
848
893
|
return NULL;
|
|
849
894
|
}
|
|
895
|
+
#ifdef MYOKIT_DEBUG_MESSAGES
|
|
896
|
+
printf(" Found %u devices.\n", (unsigned int)n_devices);
|
|
897
|
+
#endif
|
|
850
898
|
|
|
851
899
|
// Create devices tuple, must decref on exception
|
|
852
900
|
devices = PyTuple_New((size_t)n_devices);
|
|
853
901
|
|
|
854
902
|
// Add devices
|
|
855
903
|
for (j=0; j<n_devices; j++) {
|
|
904
|
+
#ifdef MYOKIT_DEBUG_MESSAGES
|
|
905
|
+
printf(" Checking device %u\n", (unsigned int)j);
|
|
906
|
+
#endif
|
|
856
907
|
|
|
857
908
|
// Create device dict, must decref on exception
|
|
858
909
|
device = mcl_info_device_dict(device_ids[j], sizeof(buffer), buffer);
|
|
859
910
|
if (device == NULL) { Py_DECREF(platforms); Py_DECREF(devices); return NULL; }
|
|
911
|
+
#ifdef MYOKIT_DEBUG_MESSAGES
|
|
912
|
+
printf(" Obtained device info dict.\n");
|
|
913
|
+
#endif
|
|
860
914
|
|
|
861
915
|
// Add device to devices tuple (steals reference)
|
|
862
916
|
PyTuple_SetItem(devices, j, device);
|
myokit/_sim/openclsim.py
CHANGED
|
@@ -534,7 +534,7 @@ class SimulationOpenCL(myokit.CModule):
|
|
|
534
534
|
lower, upper = safe_range
|
|
535
535
|
for dims in myokit._dimco(*self._dims):
|
|
536
536
|
key = '.'.join([str(x) for x in dims]) + post
|
|
537
|
-
ar = np.
|
|
537
|
+
ar = np.asarray(_log[key])
|
|
538
538
|
i = np.where(
|
|
539
539
|
(ar < lower)
|
|
540
540
|
| (ar > upper)
|
|
@@ -1080,7 +1080,7 @@ class SimulationOpenCL(myokit.CModule):
|
|
|
1080
1080
|
n = len(self._fields) * self._nx * self._ny
|
|
1081
1081
|
if n:
|
|
1082
1082
|
field_data = self._fields.values()
|
|
1083
|
-
field_data = [np.
|
|
1083
|
+
field_data = [np.asarray(x) for x in field_data]
|
|
1084
1084
|
field_data = np.vstack(field_data)
|
|
1085
1085
|
field_data = list(field_data.reshape(n, order='F'))
|
|
1086
1086
|
else:
|
|
@@ -1342,7 +1342,7 @@ class SimulationOpenCL(myokit.CModule):
|
|
|
1342
1342
|
'This method is unavailable when diffusion is disabled.')
|
|
1343
1343
|
|
|
1344
1344
|
# Check the field's size
|
|
1345
|
-
gx = np.
|
|
1345
|
+
gx = np.asarray(gx, dtype=float)
|
|
1346
1346
|
if len(self._dims) == 1:
|
|
1347
1347
|
s = self._nx - 1
|
|
1348
1348
|
if gx.shape != (s, ):
|
|
@@ -1360,7 +1360,7 @@ class SimulationOpenCL(myokit.CModule):
|
|
|
1360
1360
|
if gy is None:
|
|
1361
1361
|
raise ValueError(
|
|
1362
1362
|
'The argument `gy` must be set for 2-d simulations.')
|
|
1363
|
-
gy = np.
|
|
1363
|
+
gy = np.asarray(gy, dtype=float)
|
|
1364
1364
|
s = (self._ny - 1, self._nx)
|
|
1365
1365
|
if gy.shape != s:
|
|
1366
1366
|
raise ValueError(
|
|
@@ -1514,7 +1514,7 @@ class SimulationOpenCL(myokit.CModule):
|
|
|
1514
1514
|
if not var.is_constant():
|
|
1515
1515
|
raise ValueError('Only constants can be used for fields.')
|
|
1516
1516
|
# Check values
|
|
1517
|
-
values = np.
|
|
1517
|
+
values = np.asarray(values, dtype=float)
|
|
1518
1518
|
if len(self._dims) == 1:
|
|
1519
1519
|
if values.shape != (self._nx, ):
|
|
1520
1520
|
raise ValueError(
|
myokit/_sim/rhs.py
CHANGED
myokit/formats/__init__.py
CHANGED
|
@@ -128,15 +128,7 @@ class ExpressionWriter:
|
|
|
128
128
|
``a**b**c`` is interpreted as ``a**(b**c)``, necessitating a different
|
|
129
129
|
bracket-adding logic than used in Myokit.
|
|
130
130
|
|
|
131
|
-
3.
|
|
132
|
-
Myokit, ``0 == 0 == 0`` is a sequence of two binary operators,
|
|
133
|
-
interpreted as ``(0 == 0) == 0``. Because ``(0 == 0)`` evaluates to
|
|
134
|
-
``1``, this expression returns ``0`` (1 does not equal 0). In Python,
|
|
135
|
-
the expression ``0 == 0 == 0`` is a ternary (n-ary) operator,
|
|
136
|
-
interpreted as ``all_equal(0, 0, 0)``, which evaluates to ``1``. For
|
|
137
|
-
languages that use this convention, extra brackets must be added.
|
|
138
|
-
|
|
139
|
-
4. Myokit does not have increment or decrement operators ``--`` and ``++``,
|
|
131
|
+
3. Myokit does not have increment or decrement operators ``--`` and ``++``,
|
|
140
132
|
so the expression ``--x`` is interpreted as ``-(-x)``. This is the same
|
|
141
133
|
in Python. But in C-based languages, this is interpreted as a decrement
|
|
142
134
|
operator so care must be taken to add extra brackets.
|
|
@@ -886,6 +878,9 @@ class SweepSource:
|
|
|
886
878
|
|
|
887
879
|
Note that a source with zero recorded channels may still report a
|
|
888
880
|
non-zero number of sweeps if it can provide D/A outputs.
|
|
881
|
+
|
|
882
|
+
Similarly, formats like WCP can report zero sweeps but have a non-zero
|
|
883
|
+
channel count (if no data was recorded).
|
|
889
884
|
"""
|
|
890
885
|
raise NotImplementedError
|
|
891
886
|
|
myokit/formats/ansic/_ewriter.py
CHANGED
|
@@ -102,23 +102,14 @@ class CBasedExpressionWriter(PythonExpressionWriter):
|
|
|
102
102
|
|
|
103
103
|
def _ex_not(self, e):
|
|
104
104
|
# C conditions all have brackets, so don't add more
|
|
105
|
-
|
|
106
|
-
return f'(!{self.ex(e[0])})'
|
|
107
|
-
# But do add more if the user's being silly
|
|
108
|
-
return f'(!({self.ex(e[0])}))'
|
|
105
|
+
return f'(!{self.ex(e[0])})'
|
|
109
106
|
|
|
110
107
|
def _ex_if(self, e):
|
|
111
|
-
|
|
112
|
-
# If i is not a condtion (which always gets brackets from this writer)
|
|
113
|
-
# then add brackets
|
|
114
|
-
if not isinstance(e._i, myokit.Condition):
|
|
115
|
-
_if = f'({_if})'
|
|
116
|
-
return f'({_if} ? {_then} : {_else})'
|
|
108
|
+
return f'({self.ex(e._i)} ? {self.ex(e._t)} : {self.ex(e._e)})'
|
|
117
109
|
|
|
118
110
|
def _ex_piecewise(self, e):
|
|
119
111
|
# Render ifs; add extra bracket if not a condition (see _ex_if)
|
|
120
|
-
_ifs = [self.ex(x)
|
|
121
|
-
else f'({self.ex(x)})' for x in e._i]
|
|
112
|
+
_ifs = [self.ex(x) for x in e._i]
|
|
122
113
|
_thens = [self.ex(x) for x in e._e]
|
|
123
114
|
|
|
124
115
|
s = []
|
|
@@ -200,13 +191,7 @@ class AnsiCExpressionWriter(CBasedExpressionWriter):
|
|
|
200
191
|
#def _ex_not(self, e):
|
|
201
192
|
|
|
202
193
|
def _ex_if(self, e):
|
|
203
|
-
# Allow _fcond
|
|
204
|
-
|
|
205
194
|
_if, _then, _else = self.ex(e._i), self.ex(e._t), self.ex(e._e)
|
|
206
|
-
# If i is not a condtion (which always gets brackets from this writer)
|
|
207
|
-
# then add brackets
|
|
208
|
-
if not isinstance(e._i, myokit.Condition):
|
|
209
|
-
_if = f'({_if})'
|
|
210
195
|
|
|
211
196
|
# Use if-then-else function?
|
|
212
197
|
if self._fcond is not None:
|
|
@@ -219,8 +204,7 @@ class AnsiCExpressionWriter(CBasedExpressionWriter):
|
|
|
219
204
|
# Allow _fcond
|
|
220
205
|
|
|
221
206
|
# Render ifs; add extra bracket if not a condition (see _ex_if)
|
|
222
|
-
_ifs = [self.ex(x)
|
|
223
|
-
else f'({self.ex(x)})' for x in e._i]
|
|
207
|
+
_ifs = [self.ex(x) for x in e._i]
|
|
224
208
|
_thens = [self.ex(x) for x in e._e]
|
|
225
209
|
|
|
226
210
|
s = []
|
|
@@ -928,6 +928,7 @@ class Series(TreeNode, myokit.formats.SweepSource):
|
|
|
928
928
|
include_da = False
|
|
929
929
|
|
|
930
930
|
# Populate log
|
|
931
|
+
log.set_time_key('time')
|
|
931
932
|
if join_sweeps:
|
|
932
933
|
# Join sweeps
|
|
933
934
|
offsets = (self._sweep_starts_r if use_real_start_times
|
|
@@ -964,31 +965,34 @@ class Series(TreeNode, myokit.formats.SweepSource):
|
|
|
964
965
|
log.cmeta[name]['unit'] = self._da_units[0]
|
|
965
966
|
|
|
966
967
|
# Add meta data
|
|
967
|
-
log.
|
|
968
|
+
log.meta['time'] = self._time.strftime(myokit.DATE_FORMAT)
|
|
968
969
|
a = self.amplifier_state()
|
|
970
|
+
t = self[0][0] if len(self) and len(self[0]) else None
|
|
969
971
|
log.meta['current_gain_mV_per_pA'] = a.current_gain()
|
|
972
|
+
log.meta['filter1'] = a.filter1()
|
|
973
|
+
if t is not None:
|
|
974
|
+
log.meta['r_pipette_MOhm'] = t.r_pipette()
|
|
975
|
+
log.meta['r_seal_MOhm'] = t.r_seal()
|
|
970
976
|
log.meta['ljp_correction_mV'] = a.ljp()
|
|
971
|
-
log.meta['
|
|
977
|
+
log.meta['voltage_offset_mV'] = a.v_off()
|
|
972
978
|
if a.c_fast_enabled():
|
|
973
979
|
log.meta['c_fast_compensation_enabled'] = 'true'
|
|
974
980
|
log.meta['c_fast_pF'] = a.c_fast()
|
|
975
|
-
log.meta['
|
|
981
|
+
log.meta['c_fast_tau_us'] = a.c_fast_tau()
|
|
976
982
|
else:
|
|
977
983
|
log.meta['c_fast_compensation_enabled'] = 'false'
|
|
984
|
+
log.meta['c_slow_pF'] = a.c_slow()
|
|
978
985
|
log.meta['r_series_MOhm'] = a.r_series()
|
|
979
986
|
if a.r_series_enabled():
|
|
980
987
|
log.meta['r_series_compensation_enabled'] = 'true'
|
|
981
988
|
log.meta['r_series_compensation_percent'] = round(
|
|
982
989
|
a.r_series_fraction() * 100, 1)
|
|
990
|
+
log.meta['r_series_compensation_tau_us'] = a.r_series_tau()
|
|
991
|
+
if t is not None:
|
|
992
|
+
log.meta['r_series_post_compensation_MOhm'] = \
|
|
993
|
+
t.r_series_remaining()
|
|
983
994
|
else:
|
|
984
995
|
log.meta['r_series_compensation_enabled'] = 'false'
|
|
985
|
-
if len(self) and len(self[0]):
|
|
986
|
-
t = self[0][0]
|
|
987
|
-
log.meta['r_pipette_MOhm'] = t.r_pipette()
|
|
988
|
-
log.meta['r_seal_MOhm'] = t.r_series()
|
|
989
|
-
log.meta['r_series_post_compensation_MOhm'] = \
|
|
990
|
-
t.r_series_remaining()
|
|
991
|
-
log.meta['c_slow_pF'] = t.c_slow()
|
|
992
996
|
|
|
993
997
|
return log
|
|
994
998
|
|
|
@@ -1035,6 +1039,8 @@ class Series(TreeNode, myokit.formats.SweepSource):
|
|
|
1035
1039
|
if a.r_series_enabled():
|
|
1036
1040
|
p = round(a.r_series_fraction() * 100, 1)
|
|
1037
1041
|
out.append(f' R series compensation: {p} %.')
|
|
1042
|
+
p = round(a.r_series_tau(), 1)
|
|
1043
|
+
out.append(f' R series compensation tau: {p} us')
|
|
1038
1044
|
else:
|
|
1039
1045
|
out.append(' R series compensation: not enabled')
|
|
1040
1046
|
if len(self) and len(self[0]):
|
|
@@ -31,27 +31,6 @@ class OpenCLExpressionWriter(CBasedExpressionWriter):
|
|
|
31
31
|
self._sp = (precision == myokit.SINGLE_PRECISION)
|
|
32
32
|
self._nm = bool(native_math)
|
|
33
33
|
|
|
34
|
-
def _exc(self, e):
|
|
35
|
-
"""Returns ``ex(e)`` if ``e`` is a Condition, else ``ex(e != 0)``."""
|
|
36
|
-
# Can be removed after https://github.com/myokit/myokit/issues/1056
|
|
37
|
-
if isinstance(e, myokit.Condition):
|
|
38
|
-
return self.ex(e)
|
|
39
|
-
return self.ex(myokit.NotEqual(e, myokit.Number(0)))
|
|
40
|
-
|
|
41
|
-
def _ex_infix_comparison(self, e, op):
|
|
42
|
-
"""Handles ex() for infix condition operators (==, !=, > etc.)."""
|
|
43
|
-
# Can be removed after https://github.com/myokit/myokit/issues/1056
|
|
44
|
-
c1 = isinstance(e[0], myokit.Condition)
|
|
45
|
-
c2 = isinstance(e[1], myokit.Condition)
|
|
46
|
-
if (c1 and c2) or not (c1 or c2):
|
|
47
|
-
return f'({self.ex(e[0])} {op} {self.ex(e[1])})'
|
|
48
|
-
else:
|
|
49
|
-
return f'({self._exc(e[0])} {op} {self._exc(e[1])})'
|
|
50
|
-
|
|
51
|
-
def _ex_infix_logical(self, e, op):
|
|
52
|
-
# Can be removed after https://github.com/myokit/myokit/issues/1056
|
|
53
|
-
return f'({self._exc(e[0])} {op} {self._exc(e[1])})'
|
|
54
|
-
|
|
55
34
|
#def _ex_name(self, e):
|
|
56
35
|
#def _ex_derivative(self, e):
|
|
57
36
|
#def _ex_initial_value(self, e):
|
|
@@ -126,25 +105,7 @@ class OpenCLExpressionWriter(CBasedExpressionWriter):
|
|
|
126
105
|
|
|
127
106
|
#def _ex_and(self, e):
|
|
128
107
|
#def _ex_or(self, e):
|
|
129
|
-
|
|
130
|
-
def
|
|
131
|
-
|
|
132
|
-
return f'(!{self._exc(e[0])})'
|
|
133
|
-
|
|
134
|
-
def _ex_if(self, e):
|
|
135
|
-
# Can be removed after https://github.com/myokit/myokit/issues/1056
|
|
136
|
-
_if, _then, _else = self._exc(e._i), self.ex(e._t), self.ex(e._e)
|
|
137
|
-
return f'({_if} ? {_then} : {_else})'
|
|
138
|
-
|
|
139
|
-
def _ex_piecewise(self, e):
|
|
140
|
-
# Can be removed after https://github.com/myokit/myokit/issues/1056
|
|
141
|
-
_ifs = [self._exc(x) for x in e._i]
|
|
142
|
-
_thens = [self.ex(x) for x in e._e]
|
|
143
|
-
s = []
|
|
144
|
-
n = len(_ifs)
|
|
145
|
-
for _if, _then in zip(_ifs, _thens):
|
|
146
|
-
s.append(f'({_if} ? {_then} : ')
|
|
147
|
-
s.append(_thens[-1])
|
|
148
|
-
s.append(')' * len(_ifs))
|
|
149
|
-
return ''.join(s)
|
|
108
|
+
#def _ex_not(self, e):
|
|
109
|
+
#def _ex_if(self, e):
|
|
110
|
+
#def _ex_piecewise(self, e):
|
|
150
111
|
|
myokit/formats/sympy/_ereader.py
CHANGED
|
@@ -156,7 +156,8 @@ class SymPyExpressionReader:
|
|
|
156
156
|
def _ex_abs(self, e):
|
|
157
157
|
return myokit.Abs(self.ex(e.args[0]))
|
|
158
158
|
|
|
159
|
-
def _ex_not(self, e):
|
|
159
|
+
def _ex_not(self, e): # pragma: no cover
|
|
160
|
+
# Sympy turns not(a == b) into a != b etc., so can't test!
|
|
160
161
|
return myokit.Not(self.ex(e.args[0]))
|
|
161
162
|
|
|
162
163
|
def _ex_equal(self, e):
|
myokit/formats/wcp/_wcp.py
CHANGED
|
@@ -241,7 +241,7 @@ class WcpFile(myokit.formats.SweepSource):
|
|
|
241
241
|
|
|
242
242
|
def _channel_id(self, channel_id):
|
|
243
243
|
""" Checks an int or str channel id and returns a valid int. """
|
|
244
|
-
if self._nr == 0:
|
|
244
|
+
if self._nr == 0:
|
|
245
245
|
raise KeyError(f'Channel {channel_id} not found (empty file).')
|
|
246
246
|
|
|
247
247
|
# Handle string
|
|
@@ -314,7 +314,7 @@ class WcpFile(myokit.formats.SweepSource):
|
|
|
314
314
|
|
|
315
315
|
# Create log
|
|
316
316
|
log = myokit.DataLog()
|
|
317
|
-
if self._nr == 0:
|
|
317
|
+
if self._nr == 0:
|
|
318
318
|
return log
|
|
319
319
|
|
|
320
320
|
# Get channel names
|
|
@@ -433,7 +433,7 @@ class WcpFile(myokit.formats.SweepSource):
|
|
|
433
433
|
return self._nr
|
|
434
434
|
|
|
435
435
|
def records(self):
|
|
436
|
-
""" Deprecated alias of :meth:`
|
|
436
|
+
""" Deprecated alias of :meth:`record_count`. """
|
|
437
437
|
# Deprecated since 2023-06-22
|
|
438
438
|
import warnings
|
|
439
439
|
warnings.warn(
|
myokit/gui/datalog_viewer.py
CHANGED
|
@@ -641,13 +641,18 @@ class SweepSourceTab(GraphTabWidget):
|
|
|
641
641
|
def __init__(self, parent, source):
|
|
642
642
|
super().__init__(parent)
|
|
643
643
|
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
644
|
+
if source.sweep_count() > 0:
|
|
645
|
+
# Must have this condition to avoid getting exceptions with e.g.
|
|
646
|
+
# the empty WCP file in the test data set.
|
|
647
|
+
# myokit log myokit/tests/data/formats/wcp-file-empty.wcp
|
|
648
|
+
|
|
649
|
+
# Add A/D
|
|
650
|
+
for i in range(source.channel_count()):
|
|
651
|
+
self._add_graph_tab(source, i)
|
|
652
|
+
|
|
653
|
+
# Add D/A
|
|
654
|
+
for i in range(source.da_count()):
|
|
655
|
+
self._add_graph_tab(source, i, True)
|
|
651
656
|
|
|
652
657
|
# Add meta data
|
|
653
658
|
self._add_meta_tab(source)
|
myokit/lib/hh.py
CHANGED
|
@@ -298,6 +298,9 @@ class HHModel:
|
|
|
298
298
|
'_y[' + k + '] = ' + state.uname() + ' = '
|
|
299
299
|
+ inf + ' + (_y0[' + k + '] - ' + inf
|
|
300
300
|
+ ') * numpy.exp(-_t / ' + tau + ')')
|
|
301
|
+
f.append(
|
|
302
|
+
'_y[' + k + '][_t == 0] = ' + state.uname() + '[_t == 0] = '
|
|
303
|
+
+ '_y0[' + k + ']')
|
|
301
304
|
|
|
302
305
|
# Add current calculation
|
|
303
306
|
if self._current is not None:
|
myokit/lib/markov.py
CHANGED
|
@@ -1077,7 +1077,7 @@ class AnalyticalSimulation:
|
|
|
1077
1077
|
y0 = PI.dot(self._state.reshape((n, 1)))
|
|
1078
1078
|
|
|
1079
1079
|
# Reshape times array
|
|
1080
|
-
times = np.
|
|
1080
|
+
times = np.asarray(times).reshape((len(times),))
|
|
1081
1081
|
|
|
1082
1082
|
# Calculate state
|
|
1083
1083
|
x = P.dot(y0 * np.exp(times * E))
|
|
@@ -1223,7 +1223,7 @@ class DiscreteSimulation:
|
|
|
1223
1223
|
|
|
1224
1224
|
Returns a discretized state ``y`` where ``sum(y) = nchannels``.
|
|
1225
1225
|
"""
|
|
1226
|
-
x = np.
|
|
1226
|
+
x = np.asarray(x, dtype=float)
|
|
1227
1227
|
if (np.abs(1 - np.sum(x))) > 1e-6:
|
|
1228
1228
|
raise ValueError(
|
|
1229
1229
|
'The sum of fractions in the state to be discretized must'
|
myokit/lib/plots.py
CHANGED
|
@@ -62,28 +62,28 @@ def simulation_times(
|
|
|
62
62
|
def stair(ax, time, realtime, evaluations):
|
|
63
63
|
if time is None:
|
|
64
64
|
raise ValueError('This plotting mode requires "time" to be set.')
|
|
65
|
-
time = np.
|
|
65
|
+
time = np.asarray(time)
|
|
66
66
|
step = np.arange(0, len(time))
|
|
67
67
|
ax.step(time, step, label=label)
|
|
68
68
|
|
|
69
69
|
def stair_inverse(ax, time, realtime, evaluations):
|
|
70
70
|
if time is None:
|
|
71
71
|
raise ValueError('This plotting mode requires "time" to be set.')
|
|
72
|
-
time = np.
|
|
72
|
+
time = np.asarray(time)
|
|
73
73
|
step = np.arange(0, len(time))
|
|
74
74
|
ax.step(step, time, label=label)
|
|
75
75
|
|
|
76
76
|
def load(ax, time, realtime, evaluations):
|
|
77
77
|
if time is None:
|
|
78
78
|
raise ValueError('This plotting mode requires "time" to be set.')
|
|
79
|
-
time = np.
|
|
79
|
+
time = np.asarray(time)
|
|
80
80
|
size = np.log(1 / (time[1:] - time[:-1]))
|
|
81
81
|
ax.step(time[1:], size, label=label)
|
|
82
82
|
|
|
83
83
|
def histo(ax, time, realtime, evaluations):
|
|
84
84
|
if time is None:
|
|
85
85
|
raise ValueError('This plotting mode requires "time" to be set.')
|
|
86
|
-
time = np.
|
|
86
|
+
time = np.asarray(time)
|
|
87
87
|
zero = float(time[0])
|
|
88
88
|
bucket_w = (time[-1] - zero) / nbuckets
|
|
89
89
|
bucket_x = np.zeros(nbuckets)
|
|
Binary file
|
myokit/tests/test_datablock.py
CHANGED
|
@@ -167,11 +167,11 @@ class DataBlock1dTest(unittest.TestCase):
|
|
|
167
167
|
d = myokit.DataLog()
|
|
168
168
|
d.set_time_key('time')
|
|
169
169
|
d['time'] = time
|
|
170
|
-
d['x', 0] = np.
|
|
171
|
-
d['x', 1] = np.
|
|
172
|
-
d['x', 2] = np.
|
|
173
|
-
d['x', 3] = np.
|
|
174
|
-
d['x', 4] = np.
|
|
170
|
+
d['x', 0] = np.copy(down)
|
|
171
|
+
d['x', 1] = np.copy(down)
|
|
172
|
+
d['x', 2] = np.copy(down)
|
|
173
|
+
d['x', 3] = np.copy(down)
|
|
174
|
+
d['x', 4] = np.copy(down)
|
|
175
175
|
d['x', 1][10:] = 40
|
|
176
176
|
b = myokit.DataBlock1d.from_log(d)
|
|
177
177
|
self.assertEqual(b.cv('x'), 0)
|
|
@@ -180,11 +180,11 @@ class DataBlock1dTest(unittest.TestCase):
|
|
|
180
180
|
d = myokit.DataLog()
|
|
181
181
|
d.set_time_key('time')
|
|
182
182
|
d['time'] = time
|
|
183
|
-
d['x', 0] = np.
|
|
184
|
-
d['x', 1] = np.
|
|
185
|
-
d['x', 2] = np.
|
|
186
|
-
d['x', 3] = np.
|
|
187
|
-
d['x', 4] = np.
|
|
183
|
+
d['x', 0] = np.copy(down)
|
|
184
|
+
d['x', 1] = np.copy(down)
|
|
185
|
+
d['x', 2] = np.copy(down)
|
|
186
|
+
d['x', 3] = np.copy(down)
|
|
187
|
+
d['x', 4] = np.copy(down)
|
|
188
188
|
d['x', 1][10:] = 40
|
|
189
189
|
d['x', 2][10:] = 40
|
|
190
190
|
d['x', 3][10:] = 40
|
myokit/tests/test_datalog.py
CHANGED
|
@@ -2109,9 +2109,12 @@ class DataLogTest(unittest.TestCase):
|
|
|
2109
2109
|
self.assertRaises(ValueError, d.split_periodic, 0)
|
|
2110
2110
|
self.assertRaises(ValueError, d.split_periodic, -1)
|
|
2111
2111
|
|
|
2112
|
-
# Test larger period than log data
|
|
2112
|
+
# Test larger period than log data: Should return length-1 list
|
|
2113
2113
|
d['x'] = [4, 5, 6, 7]
|
|
2114
2114
|
e = d.split_periodic(100)
|
|
2115
|
+
self.assertIsInstance(e, list)
|
|
2116
|
+
self.assertEqual(len(e), 1)
|
|
2117
|
+
e = e[0]
|
|
2115
2118
|
self.assertEqual(set(d.keys()), set(e.keys()))
|
|
2116
2119
|
self.assertFalse(d is e)
|
|
2117
2120
|
|