myokit 1.35.4__py3-none-any.whl → 1.36.1__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 +5 -3
- myokit/__main__.py +9 -159
- myokit/_config.py +2 -2
- myokit/_expressions.py +6 -6
- myokit/_model_api.py +11 -7
- myokit/_myokit_version.py +1 -1
- myokit/_protocol.py +4 -0
- myokit/_sim/__init__.py +1 -0
- myokit/_sim/cvodessim.c +321 -177
- myokit/_sim/cvodessim.py +107 -43
- myokit/_sim/mcl.h +54 -0
- myokit/formats/__init__.py +63 -12
- myokit/formats/ansic/__init__.py +2 -1
- myokit/formats/ansic/_ewriter.py +159 -40
- myokit/formats/cpp/_ewriter.py +12 -1
- myokit/formats/cuda/_ewriter.py +15 -51
- myokit/formats/easyml/_ewriter.py +26 -54
- myokit/formats/heka/_patchmaster.py +15 -3
- myokit/formats/latex/_ewriter.py +103 -88
- myokit/formats/latex/_exporter.py +1 -1
- myokit/formats/mathml/_ewriter.py +2 -2
- myokit/formats/matlab/_ewriter.py +50 -28
- myokit/formats/opencl/_ewriter.py +61 -78
- myokit/formats/python/_ewriter.py +81 -50
- myokit/formats/stan/_ewriter.py +29 -37
- myokit/gui/source.py +1 -1
- myokit/lib/hh.py +3 -0
- myokit/lib/markov.py +6 -0
- myokit/tests/__init__.py +70 -0
- myokit/tests/data/decker.model +59 -59
- myokit/tests/test_formats.py +115 -7
- myokit/tests/test_formats_ansic.py +344 -0
- myokit/tests/test_formats_axon.py +17 -0
- myokit/tests/test_formats_cpp.py +97 -0
- myokit/tests/test_formats_cuda.py +226 -0
- myokit/tests/test_formats_easyml.py +169 -152
- myokit/tests/{test_formats_exporters.py → test_formats_exporters_run.py} +1 -69
- myokit/tests/test_formats_html.py +1 -3
- myokit/tests/test_formats_latex.py +211 -0
- myokit/tests/test_formats_mathml_content.py +13 -0
- myokit/tests/test_formats_mathml_presentation.py +54 -42
- myokit/tests/test_formats_matlab.py +218 -0
- myokit/tests/test_formats_opencl.py +206 -380
- myokit/tests/test_formats_python.py +557 -0
- myokit/tests/test_formats_stan.py +175 -0
- myokit/tests/test_formats_sympy.py +9 -2
- myokit/tests/test_lib_hh.py +36 -0
- myokit/tests/test_lib_plots.py +0 -16
- myokit/tests/test_model.py +21 -1
- myokit/tests/test_simulation_cvodes.py +137 -56
- myokit/tools.py +3 -2
- {myokit-1.35.4.dist-info → myokit-1.36.1.dist-info}/LICENSE.txt +1 -1
- {myokit-1.35.4.dist-info → myokit-1.36.1.dist-info}/METADATA +19 -8
- {myokit-1.35.4.dist-info → myokit-1.36.1.dist-info}/RECORD +57 -52
- {myokit-1.35.4.dist-info → myokit-1.36.1.dist-info}/WHEEL +1 -1
- myokit/tests/test_formats_expression_writers.py +0 -1281
- myokit/tests/test_formats_importers.py +0 -53
- {myokit-1.35.4.dist-info → myokit-1.36.1.dist-info}/entry_points.txt +0 -0
- {myokit-1.35.4.dist-info → myokit-1.36.1.dist-info}/top_level.txt +0 -0
myokit/formats/ansic/_ewriter.py
CHANGED
|
@@ -9,32 +9,38 @@ import myokit
|
|
|
9
9
|
from myokit.formats.python import PythonExpressionWriter
|
|
10
10
|
|
|
11
11
|
|
|
12
|
-
class
|
|
12
|
+
class CBasedExpressionWriter(PythonExpressionWriter):
|
|
13
13
|
"""
|
|
14
|
-
|
|
15
|
-
equations for variables in a C-style syntax.
|
|
14
|
+
Base class for C-style expression writers.
|
|
16
15
|
"""
|
|
17
16
|
def __init__(self):
|
|
18
17
|
super().__init__()
|
|
19
18
|
self._function_prefix = ''
|
|
20
|
-
self._fcond = None
|
|
21
19
|
|
|
22
|
-
def
|
|
23
|
-
|
|
24
|
-
|
|
20
|
+
def _ex_prefix(self, e, op):
|
|
21
|
+
# PrefixPlus and PrefixMinus. No simplifications should be made here
|
|
22
|
+
# for PrefixPlus, see https://github.com/myokit/myokit/issues/1054
|
|
23
|
+
x = self.ex(e[0])
|
|
24
|
+
return f'{op}({x})' if e.bracket(e[0]) or x[0] == op else f'{op}{x}'
|
|
25
25
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
26
|
+
def _ex_infix_comparison(self, e, op):
|
|
27
|
+
# For equals etc
|
|
28
|
+
return f'({self.ex(e[0])} {op} {self.ex(e[1])})'
|
|
29
|
+
|
|
30
|
+
def _ex_infix_logical(self, e, op):
|
|
31
|
+
# For and and or
|
|
32
|
+
return f'({self.ex(e[0])} {op} {self.ex(e[1])})'
|
|
32
33
|
|
|
33
34
|
#def _ex_name(self, e):
|
|
34
35
|
#def _ex_derivative(self, e):
|
|
36
|
+
#def _ex_initial_value(self, e):
|
|
37
|
+
#def _ex_partial_derivative(self, e):
|
|
38
|
+
|
|
35
39
|
#def _ex_number(self, e):
|
|
40
|
+
|
|
36
41
|
#def _ex_prefix_plus(self, e):
|
|
37
42
|
#def _ex_prefix_minus(self, e):
|
|
43
|
+
|
|
38
44
|
#def _ex_plus(self, e):
|
|
39
45
|
#def _ex_minus(self, e):
|
|
40
46
|
#def _ex_multiply(self, e):
|
|
@@ -43,17 +49,20 @@ class AnsiCExpressionWriter(PythonExpressionWriter):
|
|
|
43
49
|
def _ex_quotient(self, e):
|
|
44
50
|
# Note that this _must_ round towards minus infinity!
|
|
45
51
|
# See myokit.Quotient !
|
|
52
|
+
# No extra brackets needed: Function
|
|
46
53
|
return self.ex(myokit.Floor(myokit.Divide(e[0], e[1])))
|
|
47
54
|
|
|
48
55
|
def _ex_remainder(self, e):
|
|
49
56
|
# Note that this _must_ use the same round-to-neg-inf convention as
|
|
50
57
|
# myokit.Quotient! Implementation below is consistent with Python
|
|
51
|
-
# convention
|
|
52
|
-
|
|
53
|
-
|
|
58
|
+
# convention.
|
|
59
|
+
# Extra brackets needed! Minus has lower precedence than division.
|
|
60
|
+
i = myokit.Minus(e[0], myokit.Multiply(
|
|
61
|
+
e[1], myokit.Floor(myokit.Divide(e[0], e[1]))))
|
|
62
|
+
return f'({self.ex(i)})'
|
|
54
63
|
|
|
55
64
|
def _ex_power(self, e):
|
|
56
|
-
return 'pow(
|
|
65
|
+
return f'pow({self.ex(e[0])}, {self.ex(e[1])})'
|
|
57
66
|
|
|
58
67
|
#def _ex_sqrt(self, e):
|
|
59
68
|
#def _ex_sin(self, e):
|
|
@@ -67,7 +76,9 @@ class AnsiCExpressionWriter(PythonExpressionWriter):
|
|
|
67
76
|
def _ex_log(self, e):
|
|
68
77
|
if len(e) == 1:
|
|
69
78
|
return self._ex_function(e, 'log')
|
|
70
|
-
|
|
79
|
+
# Always add brackets: Parent was expecting a function so will never
|
|
80
|
+
# have added them.
|
|
81
|
+
return f'(log({self.ex(e[0])}) / log({self.ex(e[1])}))'
|
|
71
82
|
|
|
72
83
|
#def _ex_log10(self, e):
|
|
73
84
|
#def _ex_floor(self, e):
|
|
@@ -76,9 +87,6 @@ class AnsiCExpressionWriter(PythonExpressionWriter):
|
|
|
76
87
|
def _ex_abs(self, e):
|
|
77
88
|
return self._ex_function(e, 'fabs')
|
|
78
89
|
|
|
79
|
-
def _ex_not(self, e):
|
|
80
|
-
return '!(' + self.ex(e[0]) + ')'
|
|
81
|
-
|
|
82
90
|
#def _ex_equal(self, e):
|
|
83
91
|
#def _ex_not_equal(self, e):
|
|
84
92
|
#def _ex_more(self, e):
|
|
@@ -87,32 +95,143 @@ class AnsiCExpressionWriter(PythonExpressionWriter):
|
|
|
87
95
|
#def _ex_less_equal(self, e):
|
|
88
96
|
|
|
89
97
|
def _ex_and(self, e):
|
|
90
|
-
return self.
|
|
98
|
+
return self._ex_infix_logical(e, '&&')
|
|
91
99
|
|
|
92
100
|
def _ex_or(self, e):
|
|
93
|
-
return self.
|
|
101
|
+
return self._ex_infix_logical(e, '||')
|
|
102
|
+
|
|
103
|
+
def _ex_not(self, e):
|
|
104
|
+
# C conditions all have brackets, so don't add more
|
|
105
|
+
if isinstance(e[0], (myokit.Condition)):
|
|
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])}))'
|
|
94
109
|
|
|
95
110
|
def _ex_if(self, e):
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
111
|
+
_if, _then, _else = self.ex(e._i), self.ex(e._t), self.ex(e._e)
|
|
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})'
|
|
117
|
+
|
|
118
|
+
def _ex_piecewise(self, e):
|
|
119
|
+
# Render ifs; add extra bracket if not a condition (see _ex_if)
|
|
120
|
+
_ifs = [self.ex(x) if isinstance(x, myokit.Condition)
|
|
121
|
+
else f'({self.ex(x)})' for x in e._i]
|
|
122
|
+
_thens = [self.ex(x) for x in e._e]
|
|
123
|
+
|
|
124
|
+
s = []
|
|
125
|
+
n = len(_ifs)
|
|
126
|
+
for _if, _then in zip(_ifs, _thens):
|
|
127
|
+
s.append(f'({_if} ? {_then} : ')
|
|
128
|
+
s.append(_thens[-1])
|
|
129
|
+
s.append(')' * len(_ifs))
|
|
130
|
+
return ''.join(s)
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
class AnsiCExpressionWriter(CBasedExpressionWriter):
|
|
134
|
+
"""
|
|
135
|
+
This :class:`ExpressionWriter <myokit.formats.ExpressionWriter>` writes
|
|
136
|
+
equations for variables in a C-style syntax.
|
|
137
|
+
"""
|
|
138
|
+
def __init__(self):
|
|
139
|
+
super().__init__()
|
|
140
|
+
self._fcond = None
|
|
141
|
+
|
|
142
|
+
def set_condition_function(self, func=None):
|
|
143
|
+
"""
|
|
144
|
+
Sets a function name to use for :class:`myokit.If`; if not set the
|
|
145
|
+
ternary operatur will be used.
|
|
146
|
+
|
|
147
|
+
If given, the function arguments should be ``(condition, value_if_true,
|
|
148
|
+
value_if_false)``. To revert to using the ternary operator, call with
|
|
149
|
+
``func=None``.
|
|
150
|
+
"""
|
|
151
|
+
self._fcond = func
|
|
152
|
+
|
|
153
|
+
#def _ex_name(self, e):
|
|
154
|
+
#def _ex_derivative(self, e):
|
|
155
|
+
|
|
156
|
+
def _ex_initial_value(self, e):
|
|
157
|
+
# These are disabled by default, but enabled here to support CVODES
|
|
158
|
+
# sensitivity calculations.
|
|
159
|
+
return self._flhs(e)
|
|
160
|
+
|
|
161
|
+
def _ex_partial_derivative(self, e):
|
|
162
|
+
# These are disabled by default, but enabled here to support CVODES
|
|
163
|
+
# sensitivity calculations.
|
|
164
|
+
return self._flhs(e)
|
|
165
|
+
|
|
166
|
+
#def _ex_number(self, e):
|
|
167
|
+
|
|
168
|
+
#def _ex_prefix_plus(self, e):
|
|
169
|
+
#def _ex_prefix_minus(self, e):
|
|
170
|
+
|
|
171
|
+
#def _ex_plus(self, e):
|
|
172
|
+
#def _ex_minus(self, e):
|
|
173
|
+
#def _ex_multiply(self, e):
|
|
174
|
+
#def _ex_divide(self, e):
|
|
175
|
+
|
|
176
|
+
#def _ex_quotient(self, e):
|
|
177
|
+
#def _ex_remainder(self, e):
|
|
178
|
+
#def _ex_power(self, e):
|
|
179
|
+
#def _ex_sqrt(self, e):
|
|
180
|
+
#def _ex_sin(self, e):
|
|
181
|
+
#def _ex_cos(self, e):
|
|
182
|
+
#def _ex_tan(self, e):
|
|
183
|
+
#def _ex_asin(self, e):
|
|
184
|
+
#def _ex_acos(self, e):
|
|
185
|
+
#def _ex_atan(self, e):
|
|
186
|
+
#def _ex_exp(self, e):
|
|
187
|
+
#def _ex_log(self, e):
|
|
188
|
+
#def _ex_log10(self, e):
|
|
189
|
+
#def _ex_floor(self, e):
|
|
190
|
+
#def _ex_ceil(self, e):
|
|
191
|
+
#def _ex_abs(self, e):
|
|
192
|
+
#def _ex_equal(self, e):
|
|
193
|
+
#def _ex_not_equal(self, e):
|
|
194
|
+
#def _ex_more(self, e):
|
|
195
|
+
#def _ex_less(self, e):
|
|
196
|
+
#def _ex_more_equal(self, e):
|
|
197
|
+
#def _ex_less_equal(self, e):
|
|
198
|
+
#def _ex_and(self, e):
|
|
199
|
+
#def _ex_or(self, e):
|
|
200
|
+
#def _ex_not(self, e):
|
|
201
|
+
|
|
202
|
+
def _ex_if(self, e):
|
|
203
|
+
# Allow _fcond
|
|
204
|
+
|
|
205
|
+
_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
|
+
|
|
211
|
+
# Use if-then-else function?
|
|
212
|
+
if self._fcond is not None:
|
|
213
|
+
return f'{self._fcond}({_if}, {_then}, {_else})'
|
|
214
|
+
|
|
215
|
+
# Default: use ternary operator
|
|
216
|
+
return f'({_if} ? {_then} : {_else})'
|
|
101
217
|
|
|
102
218
|
def _ex_piecewise(self, e):
|
|
219
|
+
# Allow _fcond
|
|
220
|
+
|
|
221
|
+
# Render ifs; add extra bracket if not a condition (see _ex_if)
|
|
222
|
+
_ifs = [self.ex(x) if isinstance(x, myokit.Condition)
|
|
223
|
+
else f'({self.ex(x)})' for x in e._i]
|
|
224
|
+
_thens = [self.ex(x) for x in e._e]
|
|
225
|
+
|
|
103
226
|
s = []
|
|
104
|
-
n = len(
|
|
105
|
-
if self._fcond is None:
|
|
106
|
-
for
|
|
107
|
-
s.append('
|
|
108
|
-
s.append(self.ex(e._e[n]))
|
|
109
|
-
s.append(')' * n)
|
|
227
|
+
n = len(_ifs)
|
|
228
|
+
if self._fcond is not None:
|
|
229
|
+
for _if, _then in zip(_ifs, _thens):
|
|
230
|
+
s.append(f'{self._fcond}({_if}, {_then}, ')
|
|
110
231
|
else:
|
|
111
|
-
for
|
|
112
|
-
s.append(
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
s.append(self.ex(e._e[n]))
|
|
116
|
-
s.append(')' * n)
|
|
232
|
+
for _if, _then in zip(_ifs, _thens):
|
|
233
|
+
s.append(f'({_if} ? {_then} : ')
|
|
234
|
+
s.append(_thens[-1])
|
|
235
|
+
s.append(')' * len(_ifs))
|
|
117
236
|
return ''.join(s)
|
|
118
237
|
|
myokit/formats/cpp/_ewriter.py
CHANGED
|
@@ -12,4 +12,15 @@ class CppExpressionWriter(AnsiCExpressionWriter):
|
|
|
12
12
|
This :class:`ExpressionWriter <myokit.formats.ExpressionWriter>` translates
|
|
13
13
|
myokit :class:`expressions <myokit.Expression>` to their C++ equivalent.
|
|
14
14
|
"""
|
|
15
|
-
|
|
15
|
+
# Note: The set_condition_function is used by the jacobian calculator
|
|
16
|
+
# Once that has gone, this can base off the CBasedExpressionWriter without
|
|
17
|
+
# any modifications (so two methods below can then go).
|
|
18
|
+
|
|
19
|
+
def _ex_initial_value(self, e):
|
|
20
|
+
raise NotImplementedError(
|
|
21
|
+
'Initial values are not supported by this expression writer.')
|
|
22
|
+
|
|
23
|
+
def _ex_partial_derivative(self, e):
|
|
24
|
+
raise NotImplementedError(
|
|
25
|
+
'Partial derivatives are not supported by this expression writer.')
|
|
26
|
+
|
myokit/formats/cuda/_ewriter.py
CHANGED
|
@@ -6,24 +6,26 @@
|
|
|
6
6
|
#
|
|
7
7
|
import myokit
|
|
8
8
|
|
|
9
|
-
from myokit.formats.
|
|
9
|
+
from myokit.formats.ansic import CBasedExpressionWriter
|
|
10
10
|
|
|
11
11
|
|
|
12
|
-
class CudaExpressionWriter(
|
|
12
|
+
class CudaExpressionWriter(CBasedExpressionWriter):
|
|
13
13
|
"""
|
|
14
14
|
This :class:`ExpressionWriter <myokit.formats.ExpressionWriter>` translates
|
|
15
15
|
Myokit :class:`expressions <myokit.Expression>` to their CUDA equivalents.
|
|
16
16
|
"""
|
|
17
17
|
def __init__(self, precision=myokit.SINGLE_PRECISION):
|
|
18
18
|
super().__init__()
|
|
19
|
-
self._function_prefix = ''
|
|
20
19
|
self._sp = (precision == myokit.SINGLE_PRECISION)
|
|
21
20
|
|
|
22
21
|
#def _ex_name(self, e):
|
|
23
22
|
#def _ex_derivative(self, e):
|
|
23
|
+
#def _ex_initial_value(self, e):
|
|
24
|
+
#def _ex_partial_derivative(self, e):
|
|
24
25
|
|
|
25
26
|
def _ex_number(self, e):
|
|
26
|
-
|
|
27
|
+
x = super()._ex_number(e)
|
|
28
|
+
return x + 'f' if self._sp else x
|
|
27
29
|
|
|
28
30
|
#def _ex_prefix_plus(self, e):
|
|
29
31
|
#def _ex_prefix_minus(self, e):
|
|
@@ -32,30 +34,12 @@ class CudaExpressionWriter(PythonExpressionWriter):
|
|
|
32
34
|
#def _ex_multiply(self, e):
|
|
33
35
|
#def _ex_divide(self, e):
|
|
34
36
|
|
|
35
|
-
def _ex_quotient(self, e):
|
|
36
|
-
|
|
37
|
-
# See myokit.Quotient
|
|
38
|
-
# CUDA docs are unclear on convention, so assuming it follows C and
|
|
39
|
-
# so we need a custom implementation.
|
|
40
|
-
return self.ex(myokit.Floor(myokit.Divide(e[0], e[1])))
|
|
41
|
-
|
|
42
|
-
def _ex_remainder(self, e):
|
|
43
|
-
# Note that this _must_ use the same round-to-neg-inf convention as
|
|
44
|
-
# myokit.Quotient.
|
|
45
|
-
# CUDA docs are unclear on convention, so assuming it follows C and
|
|
46
|
-
# so we need a custom implementation.
|
|
47
|
-
return self.ex(myokit.Minus(
|
|
48
|
-
e[0], myokit.Multiply(e[1], myokit.Quotient(e[0], e[1]))))
|
|
37
|
+
#def _ex_quotient(self, e):
|
|
38
|
+
#def _ex_remainder(self, e):
|
|
49
39
|
|
|
50
40
|
def _ex_power(self, e):
|
|
51
|
-
if
|
|
52
|
-
|
|
53
|
-
return '((' + self.ex(e[0]) + ') * (' + self.ex(e[0]) + '))'
|
|
54
|
-
else:
|
|
55
|
-
return '(' + self.ex(e[0]) + ' * ' + self.ex(e[0]) + ')'
|
|
56
|
-
else:
|
|
57
|
-
f = 'powf' if self._sp else 'pow'
|
|
58
|
-
return f + '(' + self.ex(e[0]) + ', ' + self.ex(e[1]) + ')'
|
|
41
|
+
pow = 'powf' if self._sp else 'pow'
|
|
42
|
+
return f'{pow}({self.ex(e[0])}, {self.ex(e[1])})'
|
|
59
43
|
|
|
60
44
|
def _ex_sqrt(self, e):
|
|
61
45
|
f = 'sqrtf' if self._sp else 'sqrt'
|
|
@@ -112,35 +96,15 @@ class CudaExpressionWriter(PythonExpressionWriter):
|
|
|
112
96
|
f = 'fabsf' if self._sp else 'fabs'
|
|
113
97
|
return self._ex_function(e, f)
|
|
114
98
|
|
|
115
|
-
def _ex_not(self, e):
|
|
116
|
-
return '!(' + self.ex(e[0]) + ')'
|
|
117
|
-
|
|
118
99
|
#def _ex_equal(self, e):
|
|
119
100
|
#def _ex_not_equal(self, e):
|
|
120
101
|
#def _ex_more(self, e):
|
|
121
102
|
#def _ex_less(self, e):
|
|
122
103
|
#def _ex_more_equal(self, e):
|
|
123
104
|
#def _ex_less_equal(self, e):
|
|
124
|
-
|
|
125
|
-
def
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
def
|
|
129
|
-
return self._ex_infix_condition(e, '||')
|
|
130
|
-
|
|
131
|
-
def _ex_if(self, e):
|
|
132
|
-
return '(%s ? %s : %s)' % (self.ex(e._i), self.ex(e._t), self.ex(e._e))
|
|
133
|
-
|
|
134
|
-
def _ex_piecewise(self, e):
|
|
135
|
-
s = []
|
|
136
|
-
n = len(e._i)
|
|
137
|
-
for i in range(0, n):
|
|
138
|
-
s.append('(')
|
|
139
|
-
s.append(self.ex(e._i[i]))
|
|
140
|
-
s.append(' ? ')
|
|
141
|
-
s.append(self.ex(e._e[i]))
|
|
142
|
-
s.append(' : ')
|
|
143
|
-
s.append(self.ex(e._e[n]))
|
|
144
|
-
s.append(')' * n)
|
|
145
|
-
return ''.join(s)
|
|
105
|
+
#def _ex_and(self, e):
|
|
106
|
+
#def _ex_or(self, e):
|
|
107
|
+
#def _ex_not(self, e):
|
|
108
|
+
#def _ex_if(self, e):
|
|
109
|
+
#def _ex_piecewise(self, e):
|
|
146
110
|
|
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
#
|
|
2
2
|
# EasyML expression writer
|
|
3
3
|
#
|
|
4
|
+
# Supported functions:
|
|
5
|
+
# https://opencarp.org/documentation/examples/01_ep_single_cell/05_easyml
|
|
6
|
+
#
|
|
4
7
|
# This file is part of Myokit.
|
|
5
8
|
# See http://myokit.org for copyright, sharing, and licensing details.
|
|
6
9
|
#
|
|
@@ -8,24 +11,22 @@ import warnings
|
|
|
8
11
|
|
|
9
12
|
import myokit
|
|
10
13
|
|
|
11
|
-
from myokit.formats.
|
|
14
|
+
from myokit.formats.ansic import CBasedExpressionWriter
|
|
12
15
|
|
|
13
16
|
|
|
14
|
-
class EasyMLExpressionWriter(
|
|
17
|
+
class EasyMLExpressionWriter(CBasedExpressionWriter):
|
|
15
18
|
"""
|
|
16
19
|
This :class:`ExpressionWriter <myokit.formats.ExpressionWriter>` writes
|
|
17
20
|
equations for variables in EasyML syntax.
|
|
21
|
+
|
|
22
|
+
EasyML has a C-like syntax, and uses C operator precedence.
|
|
18
23
|
"""
|
|
19
24
|
def __init__(self):
|
|
20
25
|
super().__init__()
|
|
21
|
-
self._function_prefix = ''
|
|
22
26
|
|
|
23
27
|
#def _ex_name(self, e):
|
|
24
28
|
#def _ex_derivative(self, e):
|
|
25
|
-
|
|
26
|
-
def _ex_number(self, e):
|
|
27
|
-
return myokit.float.str(e)
|
|
28
|
-
|
|
29
|
+
#def _ex_number(self, e):
|
|
29
30
|
#def _ex_prefix_plus(self, e):
|
|
30
31
|
#def _ex_prefix_minus(self, e):
|
|
31
32
|
#def _ex_plus(self, e):
|
|
@@ -33,69 +34,52 @@ class EasyMLExpressionWriter(PythonExpressionWriter):
|
|
|
33
34
|
def _ex_minus(self, e):
|
|
34
35
|
if isinstance(e[0], myokit.Exp) and isinstance(e[1], myokit.Number):
|
|
35
36
|
if e[1].eval() == 1:
|
|
36
|
-
return 'expm1(
|
|
37
|
+
return f'expm1({self.ex(e[0][0])})'
|
|
37
38
|
if isinstance(e[1], myokit.Exp) and isinstance(e[0], myokit.Number):
|
|
38
39
|
if e[0].eval() == 1:
|
|
39
|
-
return '-expm1(
|
|
40
|
+
return f'-expm1({self.ex(e[1][0])})'
|
|
40
41
|
return super()._ex_minus(e)
|
|
41
42
|
|
|
42
43
|
#def _ex_multiply(self, e):
|
|
43
44
|
#def _ex_divide(self, e):
|
|
44
|
-
|
|
45
|
-
def
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
def _ex_remainder(self, e):
|
|
49
|
-
return self.ex(myokit.Minus(
|
|
50
|
-
e[0], myokit.Multiply(e[1], myokit.Quotient(e[0], e[1]))))
|
|
51
|
-
|
|
52
|
-
def _ex_power(self, e):
|
|
53
|
-
return 'pow(' + self.ex(e[0]) + ', ' + self.ex(e[1]) + ')'
|
|
54
|
-
|
|
45
|
+
#def _ex_quotient(self, e):
|
|
46
|
+
#def _ex_remainder(self, e):
|
|
47
|
+
#def _ex_power(self, e):
|
|
55
48
|
#def _ex_sqrt(self, e):
|
|
56
49
|
|
|
57
50
|
def _ex_sin(self, e):
|
|
58
|
-
warnings.warn('
|
|
51
|
+
warnings.warn('Unsupported function: sin()')
|
|
59
52
|
return super()._ex_sin(e)
|
|
60
53
|
|
|
61
54
|
#def _ex_cos(self, e):
|
|
62
55
|
|
|
63
56
|
def _ex_tan(self, e):
|
|
64
|
-
warnings.warn('
|
|
57
|
+
warnings.warn('Unsupported function: tan()')
|
|
65
58
|
return super()._ex_tan(e)
|
|
66
59
|
|
|
67
60
|
def _ex_asin(self, e):
|
|
68
|
-
warnings.warn('
|
|
61
|
+
warnings.warn('Unsupported function: asin()')
|
|
69
62
|
return super()._ex_asin(e)
|
|
70
63
|
|
|
71
64
|
#def _ex_acos(self, e):
|
|
72
65
|
|
|
73
66
|
def _ex_atan(self, e):
|
|
74
|
-
warnings.warn('
|
|
67
|
+
warnings.warn('Unsupported function: atan()')
|
|
75
68
|
return super()._ex_atan(e)
|
|
76
69
|
|
|
77
70
|
#def _ex_exp(self, e):
|
|
78
|
-
|
|
79
|
-
def _ex_log(self, e):
|
|
80
|
-
if len(e) == 1:
|
|
81
|
-
return self._ex_function(e, 'log')
|
|
82
|
-
return '(log(' + self.ex(e[0]) + ') / log(' + self.ex(e[1]) + '))'
|
|
83
|
-
|
|
71
|
+
#def _ex_log(self, e):
|
|
84
72
|
#def _ex_log10(self, e):
|
|
85
73
|
|
|
86
74
|
def _ex_floor(self, e):
|
|
87
|
-
warnings.warn('
|
|
75
|
+
warnings.warn('Unsupported function: floor()')
|
|
88
76
|
return super()._ex_floor(e)
|
|
89
77
|
|
|
90
78
|
def _ex_ceil(self, e):
|
|
91
|
-
warnings.warn('
|
|
79
|
+
warnings.warn('Unsupported function: ceil()')
|
|
92
80
|
return super()._ex_ceil(e)
|
|
93
81
|
|
|
94
|
-
def _ex_abs(self, e):
|
|
95
|
-
return self._ex_function(e, 'fabs')
|
|
96
|
-
|
|
97
|
-
def _ex_not(self, e):
|
|
98
|
-
return '!(' + self.ex(e[0]) + ')'
|
|
82
|
+
#def _ex_abs(self, e):
|
|
99
83
|
|
|
100
84
|
#def _ex_equal(self, e):
|
|
101
85
|
#def _ex_not_equal(self, e):
|
|
@@ -104,22 +88,10 @@ class EasyMLExpressionWriter(PythonExpressionWriter):
|
|
|
104
88
|
#def _ex_more_equal(self, e):
|
|
105
89
|
#def _ex_less_equal(self, e):
|
|
106
90
|
|
|
107
|
-
def
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
def _ex_or(self, e):
|
|
111
|
-
return self._ex_infix_condition(e, 'or')
|
|
112
|
-
|
|
113
|
-
def _ex_if(self, e):
|
|
114
|
-
ite = (self.ex(e._i), self.ex(e._t), self.ex(e._e))
|
|
115
|
-
return '(%s ? %s : %s)' % ite
|
|
91
|
+
#def _ex_not(self, e):
|
|
92
|
+
#def _ex_and(self, e):
|
|
93
|
+
#def _ex_or(self, e):
|
|
116
94
|
|
|
117
|
-
def
|
|
118
|
-
|
|
119
|
-
n = len(e._i)
|
|
120
|
-
for i in range(0, n):
|
|
121
|
-
s.append('(%s ? %s : ' % (self.ex(e._i[i]), self.ex(e._e[i])))
|
|
122
|
-
s.append(self.ex(e._e[n]))
|
|
123
|
-
s.append(')' * n)
|
|
124
|
-
return ''.join(s)
|
|
95
|
+
#def _ex_if(self, e):
|
|
96
|
+
#def _ex_piecewise(self, e):
|
|
125
97
|
|
|
@@ -1359,8 +1359,17 @@ class Trace(TreeNode):
|
|
|
1359
1359
|
|
|
1360
1360
|
def r_seal(self):
|
|
1361
1361
|
"""
|
|
1362
|
-
Returns the seal resistance (MOhm) determined
|
|
1362
|
+
Returns the "seal resistance" (MOhm) determined in the waiting time
|
|
1363
1363
|
before the trace was acquired.
|
|
1364
|
+
|
|
1365
|
+
This is equal to the value "R-memb" on the display. If a test pulse is
|
|
1366
|
+
being used, it is calculated as dV/dI where dV and dI are the
|
|
1367
|
+
differences in (command) voltage and current before and during the
|
|
1368
|
+
pulse. If no test pulse is used it is simply the ratio between the V
|
|
1369
|
+
and I measurements.
|
|
1370
|
+
|
|
1371
|
+
This is the same measurement as :meth:`r_pipette`, but logged
|
|
1372
|
+
automatically before each trace.
|
|
1364
1373
|
"""
|
|
1365
1374
|
return self._r_seal * 1e-6
|
|
1366
1375
|
|
|
@@ -1384,8 +1393,11 @@ class Trace(TreeNode):
|
|
|
1384
1393
|
Returns the pipette resistance (MOhm) determined from the test pulse
|
|
1385
1394
|
before breaking the seal.
|
|
1386
1395
|
|
|
1387
|
-
This
|
|
1388
|
-
|
|
1396
|
+
This is equal to the value "R-memb" on the display, but logged when a
|
|
1397
|
+
"R-memb to R-pip" button was pressed (or called programmatically). It
|
|
1398
|
+
uses the same measurement as :meth:`r_seal`, but logged at a different
|
|
1399
|
+
time. The intended use is to store the resistance of the pipette tip
|
|
1400
|
+
before touching a cell.
|
|
1389
1401
|
"""
|
|
1390
1402
|
return self._r_pipette * 1e-6
|
|
1391
1403
|
|