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/__init__.py
CHANGED
|
@@ -336,29 +336,12 @@ from ._err import ( # noqa
|
|
|
336
336
|
SimulationCancelledError,
|
|
337
337
|
SimulationError,
|
|
338
338
|
SimultaneousProtocolEventError,
|
|
339
|
+
TypeError,
|
|
339
340
|
UnresolvedReferenceError,
|
|
340
341
|
UnusedVariableError,
|
|
341
342
|
VariableMappingError,
|
|
342
343
|
)
|
|
343
344
|
|
|
344
|
-
# Check if all errors imported
|
|
345
|
-
# Dynamically importing them doesn't seem to be possible, and forgetting to
|
|
346
|
-
# import an error creates a hard to debug bug (something needs to go wrong
|
|
347
|
-
# before the interpreter reaches the code raising the error and notices it's
|
|
348
|
-
# not there).
|
|
349
|
-
if not __release__:
|
|
350
|
-
from . import _err # noqa
|
|
351
|
-
import inspect # noqa
|
|
352
|
-
_globals = globals()
|
|
353
|
-
ex, name, clas = None, None, None
|
|
354
|
-
for ex in inspect.getmembers(_err):
|
|
355
|
-
name, clas = ex
|
|
356
|
-
if type(clas) == type(MyokitError) and issubclass(clas, MyokitError):
|
|
357
|
-
if name not in _globals: # pragma: no cover
|
|
358
|
-
raise Exception('Failed to import exception: ' + name)
|
|
359
|
-
del ex, name, clas, _globals, inspect # Prevent public visibility
|
|
360
|
-
del _err
|
|
361
|
-
|
|
362
345
|
# Tools
|
|
363
346
|
from . import float # noqa
|
|
364
347
|
from . import tools # noqa
|
|
@@ -386,6 +369,7 @@ from ._expressions import ( # noqa
|
|
|
386
369
|
And,
|
|
387
370
|
ASin,
|
|
388
371
|
ATan,
|
|
372
|
+
BinaryComparison,
|
|
389
373
|
Ceil,
|
|
390
374
|
Condition,
|
|
391
375
|
Cos,
|
|
@@ -411,6 +395,8 @@ from ._expressions import ( # noqa
|
|
|
411
395
|
Multiply,
|
|
412
396
|
Name,
|
|
413
397
|
Number,
|
|
398
|
+
NumericalInfixExpression,
|
|
399
|
+
NumericalPrefixExpression,
|
|
414
400
|
Not,
|
|
415
401
|
NotEqual,
|
|
416
402
|
Or,
|
|
@@ -418,7 +404,6 @@ from ._expressions import ( # noqa
|
|
|
418
404
|
Piecewise,
|
|
419
405
|
Plus,
|
|
420
406
|
Power,
|
|
421
|
-
PrefixCondition,
|
|
422
407
|
PrefixExpression,
|
|
423
408
|
PrefixMinus,
|
|
424
409
|
PrefixPlus,
|
|
@@ -427,6 +412,8 @@ from ._expressions import ( # noqa
|
|
|
427
412
|
Sin,
|
|
428
413
|
Sqrt,
|
|
429
414
|
Tan,
|
|
415
|
+
UnaryNumericalFunction,
|
|
416
|
+
UnaryNumericalDimensionlessFunction,
|
|
430
417
|
)
|
|
431
418
|
|
|
432
419
|
# Unit and quantity
|
myokit/_datablock.py
CHANGED
|
@@ -121,16 +121,19 @@ class DataBlock1d:
|
|
|
121
121
|
if w < 1:
|
|
122
122
|
raise ValueError('Minimum w is 1.')
|
|
123
123
|
self._nx = w
|
|
124
|
+
|
|
124
125
|
# Time
|
|
125
|
-
time = np.array(time
|
|
126
|
+
time = np.array(time) if copy else np.asarray(time)
|
|
126
127
|
if len(time.shape) != 1:
|
|
127
128
|
raise ValueError('Time must be a sequence.')
|
|
128
129
|
if np.any(np.diff(time) < 0):
|
|
129
130
|
raise ValueError('Time must be non-decreasing.')
|
|
130
131
|
self._time = time
|
|
131
132
|
self._nt = len(time)
|
|
133
|
+
|
|
132
134
|
# 0d variables
|
|
133
135
|
self._0d = {}
|
|
136
|
+
|
|
134
137
|
# 1d variables
|
|
135
138
|
self._1d = {}
|
|
136
139
|
|
|
@@ -235,7 +238,7 @@ class DataBlock1d:
|
|
|
235
238
|
return 0
|
|
236
239
|
|
|
237
240
|
# Get times in seconds, lengths in cm
|
|
238
|
-
t = np.
|
|
241
|
+
t = np.asarray(t) * time_multiplier
|
|
239
242
|
x = np.arange(i1, 1 + i2, dtype=float) * length
|
|
240
243
|
|
|
241
244
|
# Use linear least squares to find the conduction velocity
|
|
@@ -287,8 +290,7 @@ class DataBlock1d:
|
|
|
287
290
|
if d not in (0, 1):
|
|
288
291
|
raise ValueError(
|
|
289
292
|
'The given simulation log should only contain 0d or 1d'
|
|
290
|
-
' variables. Found <
|
|
291
|
-
+ str(d) + '.')
|
|
293
|
+
f' variables. Found <{name}> with d = {d}.')
|
|
292
294
|
if d == 1:
|
|
293
295
|
if size is None:
|
|
294
296
|
size = info.size()
|
|
@@ -322,7 +324,7 @@ class DataBlock1d:
|
|
|
322
324
|
data = data.reshape((nt, nx), order='F')
|
|
323
325
|
# If this is a view of existing data, make a copy!
|
|
324
326
|
if data.base is not None:
|
|
325
|
-
data = np.
|
|
327
|
+
data = np.copy(data)
|
|
326
328
|
block.set1d(name, data, copy=False)
|
|
327
329
|
|
|
328
330
|
return block
|
|
@@ -426,7 +428,7 @@ class DataBlock1d:
|
|
|
426
428
|
z = np.reshape(z, (self._nt, self._nx), order='C')
|
|
427
429
|
# If z is a view, create a copy
|
|
428
430
|
if z.base is not None:
|
|
429
|
-
z = np.
|
|
431
|
+
z = np.copy(z)
|
|
430
432
|
return x, y, z
|
|
431
433
|
|
|
432
434
|
def keys0d(self):
|
|
@@ -667,12 +669,12 @@ class DataBlock1d:
|
|
|
667
669
|
head_str = []
|
|
668
670
|
head_str.append(str(self._nt))
|
|
669
671
|
head_str.append(str(self._nx))
|
|
670
|
-
head_str.append('"
|
|
672
|
+
head_str.append(f'"{dtype}"')
|
|
671
673
|
for name in self._0d:
|
|
672
|
-
head_str.append('"
|
|
674
|
+
head_str.append(f'"{name}"')
|
|
673
675
|
head_str.append(str(1))
|
|
674
676
|
for name in self._1d:
|
|
675
|
-
head_str.append('"
|
|
677
|
+
head_str.append(f'"{name}"')
|
|
676
678
|
head_str = '\n'.join(head_str)
|
|
677
679
|
|
|
678
680
|
# Create body
|
|
@@ -714,10 +716,9 @@ class DataBlock1d:
|
|
|
714
716
|
name = str(name)
|
|
715
717
|
if not name:
|
|
716
718
|
raise ValueError('Name cannot be empty.')
|
|
717
|
-
data = np.array(data
|
|
719
|
+
data = np.array(data) if copy else np.asarray(data)
|
|
718
720
|
if data.shape != (self._nt,):
|
|
719
|
-
raise ValueError(
|
|
720
|
-
'Data must be sequence of length ' + str(self._nt) + '.')
|
|
721
|
+
raise ValueError(f'Data must be sequence of length {self._nt}.')
|
|
721
722
|
self._0d[name] = data
|
|
722
723
|
|
|
723
724
|
def set1d(self, name, data, copy=True):
|
|
@@ -734,10 +735,10 @@ class DataBlock1d:
|
|
|
734
735
|
name = str(name)
|
|
735
736
|
if not name:
|
|
736
737
|
raise ValueError('Name cannot be empty.')
|
|
737
|
-
data = np.array(data
|
|
738
|
+
data = np.array(data) if copy else np.asarray(data)
|
|
738
739
|
shape = (self._nt, self._nx)
|
|
739
740
|
if data.shape != shape:
|
|
740
|
-
raise ValueError('Data must have shape
|
|
741
|
+
raise ValueError(f'Data must have shape {shape}.')
|
|
741
742
|
self._1d[name] = data
|
|
742
743
|
|
|
743
744
|
def shape(self):
|
|
@@ -761,14 +762,16 @@ class DataBlock1d:
|
|
|
761
762
|
|
|
762
763
|
The data will be copied, unless ``copy`` is set to ``False``.
|
|
763
764
|
"""
|
|
765
|
+
array = np.array if copy else np.asarray
|
|
766
|
+
|
|
764
767
|
d = myokit.DataLog()
|
|
765
768
|
d.set_time_key('time')
|
|
766
|
-
d['time'] =
|
|
769
|
+
d['time'] = array(self._time)
|
|
767
770
|
for k, v in self._0d.items():
|
|
768
|
-
d[k] =
|
|
771
|
+
d[k] = array(v)
|
|
769
772
|
for k, v in self._1d.items():
|
|
770
773
|
for i in range(self._nx):
|
|
771
|
-
d[
|
|
774
|
+
d[f'{i}.{k}'] = array(v[:, i])
|
|
772
775
|
return d
|
|
773
776
|
|
|
774
777
|
def trace(self, variable, x):
|
|
@@ -823,7 +826,7 @@ class DataBlock2d:
|
|
|
823
826
|
self._ny = h
|
|
824
827
|
self._nx = w
|
|
825
828
|
# Time
|
|
826
|
-
time = np.array(time
|
|
829
|
+
time = np.array(time) if copy else np.asarray(time)
|
|
827
830
|
if len(time.shape) != 1:
|
|
828
831
|
raise ValueError('Time must be a sequence.')
|
|
829
832
|
if not np.all(np.diff(time) >= 0):
|
|
@@ -951,8 +954,7 @@ class DataBlock2d:
|
|
|
951
954
|
x1, y1 = [int(i) for i in pos1]
|
|
952
955
|
if x1 < 0 or y1 < 0:
|
|
953
956
|
raise ValueError(
|
|
954
|
-
'Negative indices not supported: pos1=('
|
|
955
|
-
+ str(x1) + ', ' + str(y1) + ').')
|
|
957
|
+
f'Negative indices not supported: pos1=({x1}, {y1}).')
|
|
956
958
|
else:
|
|
957
959
|
x1, y1 = 0, 0
|
|
958
960
|
|
|
@@ -960,8 +962,7 @@ class DataBlock2d:
|
|
|
960
962
|
x2, y2 = [int(i) for i in pos2]
|
|
961
963
|
if x2 < 0 or y2 < 0:
|
|
962
964
|
raise ValueError(
|
|
963
|
-
'Negative indices not supported: pos2=('
|
|
964
|
-
+ str(x2) + ', ' + str(y2) + ').')
|
|
965
|
+
f'Negative indices not supported: pos2=({x2}, {y2}).')
|
|
965
966
|
else:
|
|
966
967
|
x2, y2 = x1 + w1, 0
|
|
967
968
|
|
|
@@ -1091,8 +1092,7 @@ class DataBlock2d:
|
|
|
1091
1092
|
if d not in (0, 2):
|
|
1092
1093
|
raise ValueError(
|
|
1093
1094
|
'The given simulation log should only contain 0d or 2d'
|
|
1094
|
-
' variables. Found <
|
|
1095
|
-
+ str(d) + '.')
|
|
1095
|
+
f' variables. Found <{name}> with d = {d}.')
|
|
1096
1096
|
if d == 2:
|
|
1097
1097
|
if size is None:
|
|
1098
1098
|
size = info.size()
|
|
@@ -1122,7 +1122,7 @@ class DataBlock2d:
|
|
|
1122
1122
|
data = data.reshape((nt, ny, nx), order='F')
|
|
1123
1123
|
# If this is a view of existing data, make a copy!
|
|
1124
1124
|
if data.base is not None:
|
|
1125
|
-
data = np.
|
|
1125
|
+
data = np.copy(data)
|
|
1126
1126
|
block.set2d(name, data, copy=False)
|
|
1127
1127
|
return block
|
|
1128
1128
|
|
|
@@ -1164,9 +1164,7 @@ class DataBlock2d:
|
|
|
1164
1164
|
return frames
|
|
1165
1165
|
|
|
1166
1166
|
def is_square(self):
|
|
1167
|
-
"""
|
|
1168
|
-
Returns True if this data block's grid is square.
|
|
1169
|
-
"""
|
|
1167
|
+
""" Returns True if this data block's grid is square. """
|
|
1170
1168
|
return self._nx == self._ny
|
|
1171
1169
|
|
|
1172
1170
|
def items0d(self):
|
|
@@ -1467,12 +1465,12 @@ class DataBlock2d:
|
|
|
1467
1465
|
head_str.append(str(self._nt))
|
|
1468
1466
|
head_str.append(str(self._ny))
|
|
1469
1467
|
head_str.append(str(self._nx))
|
|
1470
|
-
head_str.append('"
|
|
1468
|
+
head_str.append(f'"{dtype}"')
|
|
1471
1469
|
for name in self._0d:
|
|
1472
|
-
head_str.append('"
|
|
1470
|
+
head_str.append(f'"{name}"')
|
|
1473
1471
|
head_str.append(str(2))
|
|
1474
1472
|
for name in self._2d:
|
|
1475
|
-
head_str.append('"
|
|
1473
|
+
head_str.append(f'"{name}"')
|
|
1476
1474
|
head_str = '\n'.join(head_str)
|
|
1477
1475
|
|
|
1478
1476
|
# Create body
|
|
@@ -1514,7 +1512,7 @@ class DataBlock2d:
|
|
|
1514
1512
|
delimy = '\n'
|
|
1515
1513
|
data = self._2d[name]
|
|
1516
1514
|
data = data[frame]
|
|
1517
|
-
text = [delimx.join('"
|
|
1515
|
+
text = [delimx.join(f'"{x}"' for x in [xname, yname, zname])]
|
|
1518
1516
|
for y, row in enumerate(data):
|
|
1519
1517
|
for x, z in enumerate(row):
|
|
1520
1518
|
text.append(delimx.join([str(x), str(y), myokit.float.str(z)]))
|
|
@@ -1559,10 +1557,9 @@ class DataBlock2d:
|
|
|
1559
1557
|
name = str(name)
|
|
1560
1558
|
if not name:
|
|
1561
1559
|
raise ValueError('Name cannot be empty.')
|
|
1562
|
-
data = np.array(data
|
|
1560
|
+
data = np.array(data) if copy else np.asarray(data)
|
|
1563
1561
|
if data.shape != (self._nt,):
|
|
1564
|
-
raise ValueError(
|
|
1565
|
-
'Data must be sequence of length ' + str(self._nt) + '.')
|
|
1562
|
+
raise ValueError(f'Data must be sequence of length {self._nt}.')
|
|
1566
1563
|
self._0d[name] = data
|
|
1567
1564
|
|
|
1568
1565
|
def set2d(self, name, data, copy=True):
|
|
@@ -1579,10 +1576,10 @@ class DataBlock2d:
|
|
|
1579
1576
|
name = str(name)
|
|
1580
1577
|
if not name:
|
|
1581
1578
|
raise ValueError('Name cannot be empty.')
|
|
1582
|
-
data = np.array(data
|
|
1579
|
+
data = np.array(data) if copy else np.asarray(data)
|
|
1583
1580
|
shape = (self._nt, self._ny, self._nx)
|
|
1584
1581
|
if data.shape != shape:
|
|
1585
|
-
raise ValueError('Data must have shape
|
|
1582
|
+
raise ValueError(f'Data must have shape {shape}.')
|
|
1586
1583
|
self._2d[name] = data
|
|
1587
1584
|
|
|
1588
1585
|
def shape(self):
|
|
@@ -1606,20 +1603,20 @@ class DataBlock2d:
|
|
|
1606
1603
|
|
|
1607
1604
|
The data will be copied, unless ``copy`` is set to ``False``.
|
|
1608
1605
|
"""
|
|
1606
|
+
array = np.array if copy else np.asarray
|
|
1609
1607
|
d = myokit.DataLog()
|
|
1610
1608
|
|
|
1611
1609
|
# Add 0d vectors
|
|
1612
1610
|
d.set_time_key('time')
|
|
1613
|
-
d['time'] =
|
|
1611
|
+
d['time'] = array(self._time)
|
|
1614
1612
|
for k, v in self._0d.items():
|
|
1615
|
-
d[k] =
|
|
1613
|
+
d[k] = array(v)
|
|
1616
1614
|
|
|
1617
1615
|
# Add 2d fields
|
|
1618
1616
|
for k, v in self._2d.items():
|
|
1619
1617
|
for x in range(self._nx):
|
|
1620
|
-
s = str(x) + '.'
|
|
1621
1618
|
for y in range(self._ny):
|
|
1622
|
-
d[
|
|
1619
|
+
d[f'{x}.{y}.{k}'] = array(v[:, y, x])
|
|
1623
1620
|
return d
|
|
1624
1621
|
|
|
1625
1622
|
def trace(self, variable, x, y):
|
|
@@ -1673,20 +1670,16 @@ class ColorMap:
|
|
|
1673
1670
|
|
|
1674
1671
|
@staticmethod
|
|
1675
1672
|
def exists(name):
|
|
1676
|
-
"""
|
|
1677
|
-
Returns True if the given name corresponds to a colormap.
|
|
1678
|
-
"""
|
|
1673
|
+
""" Returns True if the given name corresponds to a colormap. """
|
|
1679
1674
|
return name in ColorMap._colormaps
|
|
1680
1675
|
|
|
1681
1676
|
@staticmethod
|
|
1682
1677
|
def get(name):
|
|
1683
|
-
"""
|
|
1684
|
-
Returns the colormap method indicated by the given name.
|
|
1685
|
-
"""
|
|
1678
|
+
""" Returns the colormap method indicated by the given name. """
|
|
1686
1679
|
try:
|
|
1687
1680
|
return ColorMap._colormaps[name]()
|
|
1688
1681
|
except KeyError:
|
|
1689
|
-
raise KeyError('Non-existent ColorMap "
|
|
1682
|
+
raise KeyError(f'Non-existent ColorMap "{name}".')
|
|
1690
1683
|
|
|
1691
1684
|
@staticmethod
|
|
1692
1685
|
def hsv_to_rgb(h, s, v):
|
|
@@ -1712,12 +1705,9 @@ class ColorMap:
|
|
|
1712
1705
|
r[idx], g[idx], b[idx] = t[idx], p[idx], v[idx]
|
|
1713
1706
|
idx = (i == 5)
|
|
1714
1707
|
r[idx], g[idx], b[idx] = v[idx], p[idx], q[idx]
|
|
1715
|
-
|
|
1716
|
-
|
|
1717
|
-
|
|
1718
|
-
np.array(b * 255, dtype=np.uint8, copy=False),
|
|
1719
|
-
)
|
|
1720
|
-
return out
|
|
1708
|
+
return (np.array(r * 255, dtype=np.uint8),
|
|
1709
|
+
np.array(g * 255, dtype=np.uint8),
|
|
1710
|
+
np.array(b * 255, dtype=np.uint8))
|
|
1721
1711
|
|
|
1722
1712
|
@staticmethod
|
|
1723
1713
|
def image(name, x, y):
|
|
@@ -1746,7 +1736,7 @@ class ColorMap:
|
|
|
1746
1736
|
Normalizes the given float data based on the specified lower and upper
|
|
1747
1737
|
bounds.
|
|
1748
1738
|
"""
|
|
1749
|
-
floats = np.
|
|
1739
|
+
floats = np.copy(floats)
|
|
1750
1740
|
# Enforce lower and upper bounds
|
|
1751
1741
|
floats[floats < lower] = lower
|
|
1752
1742
|
floats[floats > upper] = upper
|
myokit/_datalog.py
CHANGED
|
@@ -829,7 +829,7 @@ class DataLog(OrderedDict):
|
|
|
829
829
|
|
|
830
830
|
# Guess time variable
|
|
831
831
|
for key in keys:
|
|
832
|
-
x = np.
|
|
832
|
+
x = np.asarray(log[key])
|
|
833
833
|
y = x[1:] - x[:-1]
|
|
834
834
|
if np.all(y > 0):
|
|
835
835
|
log.set_time_key(key)
|
|
@@ -1305,7 +1305,7 @@ class DataLog(OrderedDict):
|
|
|
1305
1305
|
|
|
1306
1306
|
# No splitting needed? Return clone!
|
|
1307
1307
|
if nlogs < 2:
|
|
1308
|
-
return self.clone()
|
|
1308
|
+
return [self.clone()]
|
|
1309
1309
|
|
|
1310
1310
|
# Find split points
|
|
1311
1311
|
tstarts = tmin + np.arange(nlogs) * period
|
myokit/_err.py
CHANGED
|
@@ -27,10 +27,19 @@ class MyokitError(Exception):
|
|
|
27
27
|
|
|
28
28
|
class IntegrityError(MyokitError):
|
|
29
29
|
"""
|
|
30
|
-
Raised if an integrity
|
|
30
|
+
Raised if an "integrity" issue is found or created in a model or its
|
|
31
|
+
components and variables, for example missing parents or children, or
|
|
32
|
+
invalid references.
|
|
31
33
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
+
Integrity errors are usually raised by the ``validate`` method, but can
|
|
35
|
+
also arise from certain manipulations, e.g. deleting or moving a variable
|
|
36
|
+
or component. Integrity errors can also be raised if they are detected
|
|
37
|
+
during some other operation.
|
|
38
|
+
|
|
39
|
+
The error message is stored in the property ``message``.
|
|
40
|
+
|
|
41
|
+
Integrity errors detected during parsing may set a token (pointing to the
|
|
42
|
+
position in the parsed text) retrievable with :meth:`token()`.
|
|
34
43
|
|
|
35
44
|
*Extends:* :class:`myokit.MyokitError`
|
|
36
45
|
"""
|
|
@@ -393,6 +402,20 @@ class SimultaneousProtocolEventError(MyokitError):
|
|
|
393
402
|
"""
|
|
394
403
|
|
|
395
404
|
|
|
405
|
+
class TypeError(IntegrityError):
|
|
406
|
+
"""
|
|
407
|
+
Raised by the expression system if expressions of one type are required but
|
|
408
|
+
others are found.
|
|
409
|
+
|
|
410
|
+
For example, when a Derivative is created with an argument that is not a
|
|
411
|
+
Name, when a condition is given as input to a numerical operator (e.g.
|
|
412
|
+
``log(1 == 2)``), or when a conditional operator is applied to a number
|
|
413
|
+
(e.g. ``and(1, 2)``).
|
|
414
|
+
|
|
415
|
+
*Extends:* :class:`myokit.IntegrityError`
|
|
416
|
+
"""
|
|
417
|
+
|
|
418
|
+
|
|
396
419
|
class UnresolvedReferenceError(IntegrityError):
|
|
397
420
|
"""
|
|
398
421
|
Raised when a reference to a variable cannot be resolved.
|