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.
Files changed (59) hide show
  1. myokit/__init__.py +5 -3
  2. myokit/__main__.py +9 -159
  3. myokit/_config.py +2 -2
  4. myokit/_expressions.py +6 -6
  5. myokit/_model_api.py +11 -7
  6. myokit/_myokit_version.py +1 -1
  7. myokit/_protocol.py +4 -0
  8. myokit/_sim/__init__.py +1 -0
  9. myokit/_sim/cvodessim.c +321 -177
  10. myokit/_sim/cvodessim.py +107 -43
  11. myokit/_sim/mcl.h +54 -0
  12. myokit/formats/__init__.py +63 -12
  13. myokit/formats/ansic/__init__.py +2 -1
  14. myokit/formats/ansic/_ewriter.py +159 -40
  15. myokit/formats/cpp/_ewriter.py +12 -1
  16. myokit/formats/cuda/_ewriter.py +15 -51
  17. myokit/formats/easyml/_ewriter.py +26 -54
  18. myokit/formats/heka/_patchmaster.py +15 -3
  19. myokit/formats/latex/_ewriter.py +103 -88
  20. myokit/formats/latex/_exporter.py +1 -1
  21. myokit/formats/mathml/_ewriter.py +2 -2
  22. myokit/formats/matlab/_ewriter.py +50 -28
  23. myokit/formats/opencl/_ewriter.py +61 -78
  24. myokit/formats/python/_ewriter.py +81 -50
  25. myokit/formats/stan/_ewriter.py +29 -37
  26. myokit/gui/source.py +1 -1
  27. myokit/lib/hh.py +3 -0
  28. myokit/lib/markov.py +6 -0
  29. myokit/tests/__init__.py +70 -0
  30. myokit/tests/data/decker.model +59 -59
  31. myokit/tests/test_formats.py +115 -7
  32. myokit/tests/test_formats_ansic.py +344 -0
  33. myokit/tests/test_formats_axon.py +17 -0
  34. myokit/tests/test_formats_cpp.py +97 -0
  35. myokit/tests/test_formats_cuda.py +226 -0
  36. myokit/tests/test_formats_easyml.py +169 -152
  37. myokit/tests/{test_formats_exporters.py → test_formats_exporters_run.py} +1 -69
  38. myokit/tests/test_formats_html.py +1 -3
  39. myokit/tests/test_formats_latex.py +211 -0
  40. myokit/tests/test_formats_mathml_content.py +13 -0
  41. myokit/tests/test_formats_mathml_presentation.py +54 -42
  42. myokit/tests/test_formats_matlab.py +218 -0
  43. myokit/tests/test_formats_opencl.py +206 -380
  44. myokit/tests/test_formats_python.py +557 -0
  45. myokit/tests/test_formats_stan.py +175 -0
  46. myokit/tests/test_formats_sympy.py +9 -2
  47. myokit/tests/test_lib_hh.py +36 -0
  48. myokit/tests/test_lib_plots.py +0 -16
  49. myokit/tests/test_model.py +21 -1
  50. myokit/tests/test_simulation_cvodes.py +137 -56
  51. myokit/tools.py +3 -2
  52. {myokit-1.35.4.dist-info → myokit-1.36.1.dist-info}/LICENSE.txt +1 -1
  53. {myokit-1.35.4.dist-info → myokit-1.36.1.dist-info}/METADATA +19 -8
  54. {myokit-1.35.4.dist-info → myokit-1.36.1.dist-info}/RECORD +57 -52
  55. {myokit-1.35.4.dist-info → myokit-1.36.1.dist-info}/WHEEL +1 -1
  56. myokit/tests/test_formats_expression_writers.py +0 -1281
  57. myokit/tests/test_formats_importers.py +0 -53
  58. {myokit-1.35.4.dist-info → myokit-1.36.1.dist-info}/entry_points.txt +0 -0
  59. {myokit-1.35.4.dist-info → myokit-1.36.1.dist-info}/top_level.txt +0 -0
myokit/_sim/cvodessim.c CHANGED
@@ -25,20 +25,26 @@ import myokit
25
25
  #include <Python.h>
26
26
  #include <stdio.h>
27
27
 
28
- #include <cvodes/cvodes.h>
29
- #include <nvector/nvector_serial.h>
30
- #include <sundials/sundials_types.h>
31
28
  #include <sundials/sundials_config.h>
32
29
  #ifndef SUNDIALS_VERSION_MAJOR
33
30
  #define SUNDIALS_VERSION_MAJOR 2
34
31
  #endif
32
+ #include <sundials/sundials_types.h>
33
+ #if SUNDIALS_VERSION_MAJOR >= 7
34
+ #define realtype sunrealtype
35
+ #define RCONST SUN_RCONST
36
+ #endif
37
+ #include <nvector/nvector_serial.h>
38
+ #include <cvodes/cvodes.h>
35
39
  #if SUNDIALS_VERSION_MAJOR >= 3
36
40
  #include <sunmatrix/sunmatrix_dense.h>
37
41
  #include <sunlinsol/sunlinsol_dense.h>
38
- #include <cvodes/cvodes_direct.h>
39
42
  #else
40
43
  #include <cvodes/cvodes_dense.h>
41
44
  #endif
45
+ #if SUNDIALS_VERSION_MAJOR < 6
46
+ #include <cvodes/cvodes_direct.h>
47
+ #endif
42
48
 
43
49
  <?
44
50
  if myokit.DEBUG_SM:
@@ -54,6 +60,13 @@ if myokit.DEBUG_SP:
54
60
  print('#ifndef MYOKIT_DEBUG_PROFILING')
55
61
  print('#define MYOKIT_DEBUG_PROFILING')
56
62
  print('#endif')
63
+
64
+ if myokit.DEBUG_SS:
65
+ print('// Show simulator stats')
66
+ print('#ifndef MYOKIT_DEBUG_STATS')
67
+ print('#define MYOKIT_DEBUG_STATS')
68
+ print('#endif')
69
+
57
70
  ?>
58
71
 
59
72
  #include "pacing.h"
@@ -68,107 +81,180 @@ typedef struct {
68
81
  realtype *p;
69
82
  } *UserData;
70
83
 
84
+ /*
85
+ * Check flags set by a generic sundials function, set python error.
86
+ * sundials_flag: The value to check
87
+ * funcname: The name of the function that returned the flag
88
+ */
89
+ int
90
+ check_sundials_flag(int flag, const char *funcname)
91
+ {
92
+ /* Check if flag < 0 */
93
+ if (flag < 0) {
94
+ PyErr_Format(PyExc_Exception, "Function %s failed with flag = %d", funcname, flag);
95
+ return 1;
96
+ }
97
+ return 0;
98
+ }
99
+
100
+ /*
101
+ * Check flags set by any cvode-related function except cvode(), set python error.
102
+ * sundials_flag: The value to check
103
+ * funcname: The name of the function that returned the flag
104
+ */
105
+ int
106
+ check_cvode_related_flag(int flag, const char *funcname)
107
+ {
108
+ /* Check if flag < 0 */
109
+ if (flag < 0) {
110
+ switch (flag) {
111
+ case CV_MEM_NULL:
112
+ PyErr_Format(PyExc_Exception, "Function %s failed with flag CV_MEM_NULL: The cvode memory block was not initialized.", funcname);
113
+ break;
114
+ case CV_MEM_FAIL:
115
+ PyErr_Format(PyExc_Exception, "Function %s failed with flag CV_MEM_FAIL: A memory allocation failed.", funcname);
116
+ break;
117
+ case CV_NO_MALLOC:
118
+ PyErr_Format(PyExc_Exception, "Function %s failed with flag CV_NO_MALLOC: A memory allocation function returned NULL.", funcname);
119
+ break;
120
+ case CV_ILL_INPUT:
121
+ PyErr_Format(PyExc_Exception, "Function %s failed with flag CV_ILL_INPUT: Invalid input arguments.", funcname);
122
+ break;
123
+ case CV_NO_SENS:
124
+ PyErr_Format(PyExc_Exception, "Function %s failed with flag CV_NO_SENS: Forward sensitivity analysis was not initialized.", funcname);
125
+ break;
126
+ case CV_BAD_K:
127
+ PyErr_Format(PyExc_Exception, "Function %s failed with flag CV_BAD_K: Argument k is not in range.", funcname);
128
+ break;
129
+ case CV_BAD_T:
130
+ PyErr_Format(PyExc_Exception, "Function %s failed with flag CV_BAD_T: Argument t is not in range.", funcname);
131
+ break;
132
+ case CV_BAD_DKY:
133
+ PyErr_Format(PyExc_Exception, "Function %s failed with flag CV_BAD_DKY: The argument DKY was NULL.", funcname);
134
+ break;
135
+ default:
136
+ PyErr_Format(PyExc_Exception, "Function %s failed with unhandled flag = %d", funcname, flag);
137
+ }
138
+ return 1;
139
+ }
140
+ return 0;
141
+ }
142
+
71
143
  /*
72
144
  * Check sundials flags, set python error.
73
- * flagvalue : The value to check
145
+ * flag: The value to check
146
+ * funcname: The name of the function that returned the flag
147
+ */
148
+ int
149
+ check_cvode_flag(int flag)
150
+ {
151
+ /* Check if flag < 0 */
152
+ if (flag < 0) {
153
+ switch (flag) {
154
+ case CV_TOO_MUCH_WORK:
155
+ PyErr_SetString(PyExc_Exception, "Function CVode() failed with flag CV_TOO_MUCH_WORK: The solver took mxstep internal steps but could not reach tout.");
156
+ break;
157
+ case CV_TOO_MUCH_ACC:
158
+ PyErr_SetString(PyExc_Exception, "Function CVode() failed with flag CV_TOO_MUCH_ACC: The solver could not satisfy the accuracy demanded by the user for some internal step.");
159
+ break;
160
+ case CV_ERR_FAILURE:
161
+ PyErr_SetString(PyExc_ArithmeticError, "Function CVode() failed with flag CV_ERR_FAILURE: Error test failures occurred too many times during one internal time step or minimum step size was reached.");
162
+ break;
163
+ case CV_CONV_FAILURE:
164
+ PyErr_SetString(PyExc_ArithmeticError, "Function CVode() failed with flag CV_CONV_FAILURE: Convergence test failures occurred too many times during one internal time step or minimum step size was reached.");
165
+ break;
166
+ case CV_LINIT_FAIL:
167
+ PyErr_SetString(PyExc_ArithmeticError, "Function CVode() failed with flag CV_LINIT_FAIL: The linear solver's initialization function failed.");
168
+ break;
169
+ case -6:
170
+ PyErr_SetString(PyExc_ArithmeticError, "Function CVode() failed with flag -6 CV_LSETUP_FAIL: The linear solver's setup function failed in an unrecoverable manner.");
171
+ break;
172
+ case -7:
173
+ PyErr_SetString(PyExc_ArithmeticError, "Function CVode() failed with flag -7 CV_LSOLVE_FAIL: The linear solver's solve function failed in an unrecoverable manner.");
174
+ break;
175
+ case -8:
176
+ PyErr_SetString(PyExc_ArithmeticError, "Function CVode() failed with flag -8 CV_RHSFUNC_FAIL: The right-hand side function failed in an unrecoverable manner.");
177
+ break;
178
+ case -9:
179
+ PyErr_SetString(PyExc_ArithmeticError, "Function CVode() failed with flag -9 CV_FIRST_RHSFUNC_ERR: The right-hand side function failed at the first call.");
180
+ break;
181
+ case -10:
182
+ PyErr_SetString(PyExc_ArithmeticError, "Function CVode() failed with flag -10 CV_REPTD_RHSFUNC_ERR: The right-hand side function had repeated recoverable errors.");
183
+ break;
184
+ case -11:
185
+ PyErr_SetString(PyExc_ArithmeticError, "Function CVode() failed with flag -11 CV_UNREC_RHSFUNC_ERR: The right-hand side function had a recoverable error, but no recovery is possible.");
186
+ break;
187
+ case -12:
188
+ PyErr_SetString(PyExc_ArithmeticError, "Function CVode() failed with flag -12 CV_RTFUNC_FAIL: The root finding function failed in an unrecoverable manner.");
189
+ break;
190
+ case -20:
191
+ PyErr_SetString(PyExc_Exception, "Function CVode() failed with flag -20 CV_MEM_FAIL: A memory allocation failed.");
192
+ break;
193
+ case -21:
194
+ PyErr_SetString(PyExc_Exception, "Function CVode() failed with flag -21 CV_MEM_NULL: The cvode mem argument was NULL.");
195
+ break;
196
+ case -22:
197
+ PyErr_SetString(PyExc_Exception, "Function CVode() failed with flag -22 CV_ILL_INPUT: One of the function inputs is illegal.");
198
+ break;
199
+ case -23:
200
+ PyErr_SetString(PyExc_Exception, "Function CVode() failed with flag -23 CV_NO_MALLOC: The cvode memory block was not allocated by a call to CVodeMalloc.");
201
+ break;
202
+ case -24:
203
+ PyErr_SetString(PyExc_Exception, "Function CVode() failed with flag -24 CV_BAD_K: The derivative order k is larger than the order used.");
204
+ break;
205
+ case -25:
206
+ PyErr_SetString(PyExc_Exception, "Function CVode() failed with flag -25 CV_BAD_T: The time t is outside the last step taken.");
207
+ break;
208
+ case -26:
209
+ PyErr_SetString(PyExc_Exception, "Function CVode() failed with flag -26 CV_BAD_DKY: The output derivative vector is NULL.");
210
+ break;
211
+ case -27:
212
+ PyErr_SetString(PyExc_Exception, "Function CVode() failed with flag -27 CV_TOO_CLOSE: The output and initial times are too close to each other.");
213
+ break;
214
+ default:
215
+ PyErr_Format(PyExc_Exception, "Function CVode() failed with unhandled flag = %d", flag);
216
+ }
217
+ return 1;
218
+ }
219
+ return 0;
220
+ }
221
+
222
+ #if SUNDIALS_VERSION_MAJOR >= 7
223
+ /*
224
+ * Check sundials error code (Sundials 7 and above)
225
+ * sunerr : The SunErroCode to check
74
226
  * funcname : The name of the function that returned the flag
75
- * opt : Mode selector
76
- * 0 : Error if the flag is null
77
- * 1 : Error if the flag is < 0
78
- * 2 : Errir
79
227
  */
80
228
  int
81
- check_cvode_flag(void *flagvalue, char *funcname, int opt)
229
+ check_sundials_error(SUNErrCode code, const char *funcname)
82
230
  {
83
- if (opt == 0 && flagvalue == NULL) {
84
- /* Check if sundials function returned null pointer */
85
- PyErr_Format(PyExc_Exception, "%s() failed - returned NULL pointer", funcname);
231
+ const char* msg;
232
+ if (code) {
233
+ msg = SUNGetErrMsg(code);
234
+ PyErr_Format(PyExc_Exception, "%s() failed with message = %s", funcname, msg);
86
235
  return 1;
87
- } else if (opt == 1) {
88
- /* Check if flag < 0 */
89
- int flag = *((int*)flagvalue);
90
- if (flag < 0) {
91
- if (strcmp(funcname, "CVode") == 0) {
92
- switch (flag) {
93
- case -1:
94
- PyErr_SetString(PyExc_Exception, "Function CVode() failed with flag -1 CV_TOO_MUCH_WORK: The solver took mxstep internal steps but could not reach tout.");
95
- break;
96
- case -2:
97
- PyErr_SetString(PyExc_Exception, "Function CVode() failed with flag -2 CV_TOO_MUCH_ACC: The solver could not satisfy the accuracy demanded by the user for some internal step.");
98
- break;
99
- case -3:
100
- PyErr_SetString(PyExc_ArithmeticError, "Function CVode() failed with flag -3 CV_ERR_FAILURE: Error test failures occurred too many times during one internal time step or minimum step size was reached.");
101
- break;
102
- case -4:
103
- PyErr_SetString(PyExc_ArithmeticError, "Function CVode() failed with flag -4 CV_CONV_FAILURE: Convergence test failures occurred too many times during one internal time step or minimum step size was reached.");
104
- break;
105
- case -5:
106
- PyErr_SetString(PyExc_ArithmeticError, "Function CVode() failed with flag -5 CV_LINIT_FAIL: The linear solver's initialization function failed.");
107
- break;
108
- case -6:
109
- PyErr_SetString(PyExc_ArithmeticError, "Function CVode() failed with flag -6 CV_LSETUP_FAIL: The linear solver's setup function failed in an unrecoverable manner.");
110
- break;
111
- case -7:
112
- PyErr_SetString(PyExc_ArithmeticError, "Function CVode() failed with flag -7 CV_LSOLVE_FAIL: The linear solver's solve function failed in an unrecoverable manner.");
113
- break;
114
- case -8:
115
- PyErr_SetString(PyExc_ArithmeticError, "Function CVode() failed with flag -8 CV_RHSFUNC_FAIL: The right-hand side function failed in an unrecoverable manner.");
116
- break;
117
- case -9:
118
- PyErr_SetString(PyExc_ArithmeticError, "Function CVode() failed with flag -9 CV_FIRST_RHSFUNC_ERR: The right-hand side function failed at the first call.");
119
- break;
120
- case -10:
121
- PyErr_SetString(PyExc_ArithmeticError, "Function CVode() failed with flag -10 CV_REPTD_RHSFUNC_ERR: The right-hand side function had repeated recoverable errors.");
122
- break;
123
- case -11:
124
- PyErr_SetString(PyExc_ArithmeticError, "Function CVode() failed with flag -11 CV_UNREC_RHSFUNC_ERR: The right-hand side function had a recoverable error, but no recovery is possible.");
125
- break;
126
- case -12:
127
- PyErr_SetString(PyExc_ArithmeticError, "Function CVode() failed with flag -12 CV_RTFUNC_FAIL: The root finding function failed in an unrecoverable manner.");
128
- break;
129
- case -20:
130
- PyErr_SetString(PyExc_Exception, "Function CVode() failed with flag -20 CV_MEM_FAIL: A memory allocation failed.");
131
- break;
132
- case -21:
133
- PyErr_SetString(PyExc_Exception, "Function CVode() failed with flag -21 CV_MEM_NULL: The cvode mem argument was NULL.");
134
- break;
135
- case -22:
136
- PyErr_SetString(PyExc_Exception, "Function CVode() failed with flag -22 CV_ILL_INPUT: One of the function inputs is illegal.");
137
- break;
138
- case -23:
139
- PyErr_SetString(PyExc_Exception, "Function CVode() failed with flag -23 CV_NO_MALLOC: The cvode memory block was not allocated by a call to CVodeMalloc.");
140
- break;
141
- case -24:
142
- PyErr_SetString(PyExc_Exception, "Function CVode() failed with flag -24 CV_BAD_K: The derivative order k is larger than the order used.");
143
- break;
144
- case -25:
145
- PyErr_SetString(PyExc_Exception, "Function CVode() failed with flag -25 CV_BAD_T: The time t is outside the last step taken.");
146
- break;
147
- case -26:
148
- PyErr_SetString(PyExc_Exception, "Function CVode() failed with flag -26 CV_BAD_DKY: The output derivative vector is NULL.");
149
- break;
150
- case -27:
151
- PyErr_SetString(PyExc_Exception, "Function CVode() failed with flag -27 CV_TOO_CLOSE: The output and initial times are too close to each other.");
152
- break;
153
- default:
154
- PyErr_Format(PyExc_Exception, "Function CVode() failed with unknown flag = %d", flag);
155
- }
156
- } else {
157
- PyErr_Format(PyExc_Exception, "%s() failed with flag = %d", funcname, flag);
158
- }
159
- return 1;
160
- }
161
236
  }
162
237
  return 0;
163
238
  }
239
+ #endif
164
240
 
165
241
  /*
166
242
  * Error and warning message handler for CVODES.
167
- * Error messages are already set via check_cvode_flag, so this method
243
+ * Error messages are already set via check_cvode_flag & co, so this method
168
244
  * suppresses error messages.
169
245
  * Warnings are passed to Python's warning system, where they can be
170
246
  * caught or suppressed using the warnings module.
171
247
  */
248
+ #if SUNDIALS_VERSION_MAJOR >= 7
249
+ void
250
+ ErrorHandler(int line, const char* function, const char* file, const char* msg,
251
+ SUNErrCode error_code, void* err_user_data, SUNContext context)
252
+ {
253
+ if (error_code) {
254
+ PyErr_WarnFormat(PyExc_RuntimeWarning, 1, "CVODES: %s", msg);
255
+ }
256
+ }
257
+ #else
172
258
  void
173
259
  ErrorHandler(int error_code, const char *module, const char *function,
174
260
  char *msg, void *eh_data)
@@ -177,6 +263,7 @@ ErrorHandler(int error_code, const char *module, const char *function,
177
263
  PyErr_WarnFormat(PyExc_RuntimeWarning, 1, "CVODES: %s", msg);
178
264
  }
179
265
  }
266
+ #endif
180
267
 
181
268
  /*
182
269
  * Initialisation status.
@@ -196,7 +283,7 @@ union PSys *pacing_systems; /* Array of pacing systems (event based or time se
196
283
  enum PSysType *pacing_types; /* Array of pacing system types */
197
284
  PyObject *protocols; /* The protocols used to generate the pacing systems */
198
285
  double* pacing; /* Pacing values, same size as pacing_systems and pacing_types */
199
- int n_pace; /* The number of pacing systems */
286
+ int n_pace; /* The number of pacing systems: Must be set with every call from Python that uses it */
200
287
 
201
288
  /*
202
289
  * CVODE Memory
@@ -562,6 +649,10 @@ sim_init(PyObject *self, PyObject *args)
562
649
  Model_Flag flag_model;
563
650
  ESys_Flag flag_epacing;
564
651
  TSys_Flag flag_fpacing;
652
+ /* Error handling in >=7 */
653
+ #if SUNDIALS_VERSION_MAJOR >= 7
654
+ SUNErrCode sunerr;
655
+ #endif
565
656
 
566
657
  /* Pacing systems */
567
658
  ESys epacing;
@@ -740,11 +831,12 @@ sim_init(PyObject *self, PyObject *args)
740
831
  /*
741
832
  * Create sundials context
742
833
  */
743
- #if SUNDIALS_VERSION_MAJOR >= 6
834
+ #if SUNDIALS_VERSION_MAJOR >= 7
835
+ sunerr = SUNContext_Create(SUN_COMM_NULL, &sundials_context);
836
+ if (check_sundials_error(sunerr, "SUNContext_Create")) return sim_clean();
837
+ #elif SUNDIALS_VERSION_MAJOR >= 6
744
838
  flag_cvode = SUNContext_Create(NULL, &sundials_context);
745
- if (check_cvode_flag(&flag_cvode, "SUNContext_Create", 1)) {
746
- return sim_cleanx(PyExc_Exception, "Failed to create Sundials context.");
747
- }
839
+ if (check_sundials_flag(flag_cvode, "SUNContext_Create")) return sim_clean();
748
840
  #ifdef MYOKIT_DEBUG_PROFILING
749
841
  benchmarker_print("CP Created sundials context.");
750
842
  #endif
@@ -760,9 +852,7 @@ sim_init(PyObject *self, PyObject *args)
760
852
  #else
761
853
  y = N_VNew_Serial(model->n_states);
762
854
  #endif
763
- if (check_cvode_flag((void*)y, "N_VNew_Serial", 0)) {
764
- return sim_cleanx(PyExc_Exception, "Failed to create state vector.");
765
- }
855
+ if (y == NULL) return sim_cleanx(PyExc_Exception, "Unable to allocate space for state vector.");
766
856
 
767
857
  /* Create state vector copy for error handling */
768
858
  #if SUNDIALS_VERSION_MAJOR >= 6
@@ -770,16 +860,12 @@ sim_init(PyObject *self, PyObject *args)
770
860
  #else
771
861
  ylast = N_VNew_Serial(model->n_states);
772
862
  #endif
773
- if (check_cvode_flag((void*)ylast, "N_VNew_Serial", 0)) {
774
- return sim_cleanx(PyExc_Exception, "Failed to create last-state vector.");
775
- }
863
+ if (ylast == NULL) return sim_cleanx(PyExc_Exception, "Unable to allocate space for last-state vector.");
776
864
 
777
865
  /* Create sensitivity vector array */
778
866
  if (model->has_sensitivities) {
779
867
  sy = N_VCloneVectorArray(model->ns_independents, y);
780
- if (check_cvode_flag((void*)sy, "N_VCloneVectorArray", 0)) {
781
- return sim_cleanx(PyExc_Exception, "Failed to allocate space to store sensitivities.");
782
- }
868
+ if (sy == NULL) return sim_cleanx(PyExc_Exception, "Unable to allocate space for sensitivity vector array.");
783
869
  }
784
870
 
785
871
  /*
@@ -802,14 +888,10 @@ sim_init(PyObject *self, PyObject *args)
802
888
  #else
803
889
  z = N_VNew_Serial(model->n_states);
804
890
  #endif
805
- if (check_cvode_flag((void*)z, "N_VNew_Serial", 0)) {
806
- return sim_cleanx(PyExc_Exception, "Failed to create state vector for logging.");
807
- }
891
+ if (z == NULL) return sim_cleanx(PyExc_Exception, "Unable to allocate space for state vector for logging.");
808
892
  if (model->has_sensitivities) {
809
893
  sz = N_VCloneVectorArray(model->ns_independents, y);
810
- if (check_cvode_flag((void*)sz, "N_VCloneVectorArray", 0)) {
811
- return sim_cleanx(PyExc_Exception, "Failed to create state sensitivity vector array for logging.");
812
- }
894
+ if (sz == NULL) return sim_cleanx(PyExc_Exception, "Unable to allocate space for sensitivity vector array for logging.");
813
895
  }
814
896
  }
815
897
 
@@ -957,9 +1039,7 @@ sim_init(PyObject *self, PyObject *args)
957
1039
  /* Create parameter scaling vector, for error control */
958
1040
  /* TODO: Get this from the Python code ? */
959
1041
  pbar = (realtype*)malloc((size_t)model->ns_independents * sizeof(realtype));
960
- if (pbar == NULL) {
961
- return sim_cleanx(PyExc_Exception, "Unable to allocate space to store parameter scales.");
962
- }
1042
+ if (pbar == NULL) return sim_cleanx(PyExc_Exception, "Unable to allocate space for parameter scale array.");
963
1043
  for (i=0; i<model->ns_independents; i++) {
964
1044
  pbar[i] = (udata->p[i] == 0.0 ? 1.0 : fabs(udata->p[i]));
965
1045
  }
@@ -983,17 +1063,11 @@ sim_init(PyObject *self, PyObject *args)
983
1063
  n_pace = (int)PyList_Size(protocols);
984
1064
  }
985
1065
  pacing_systems = (union PSys*)malloc((size_t)n_pace * sizeof(union PSys));
986
- if (pacing_systems == NULL) {
987
- return sim_cleanx(PyExc_Exception, "Unable to allocate space to store pacing systems.");
988
- }
1066
+ if (pacing_systems == NULL) return sim_cleanx(PyExc_Exception, "Unable to allocate space for pacing systems.");
989
1067
  pacing_types = (enum PSysType *)malloc((size_t)n_pace * sizeof(enum PSysType));
990
- if (pacing_types == NULL) {
991
- return sim_cleanx(PyExc_Exception, "Unable to allocate space to store pacing types.");
992
- }
1068
+ if (pacing_types == NULL) return sim_cleanx(PyExc_Exception, "Unable to allocate space for pacing types.");
993
1069
  pacing = (realtype*)malloc((size_t)n_pace * sizeof(realtype));
994
- if (pacing == NULL) {
995
- return sim_cleanx(PyExc_Exception, "Unable to allocate space to store pacing values.");
996
- }
1070
+ if (pacing == NULL) return sim_cleanx(PyExc_Exception, "Unable to allocate space for pacing values.");
997
1071
  Model_SetupPacing(model, n_pace);
998
1072
 
999
1073
  /*
@@ -1074,68 +1148,73 @@ sim_init(PyObject *self, PyObject *args)
1074
1148
  #else
1075
1149
  cvode_mem = CVodeCreate(CV_BDF, CV_NEWTON);
1076
1150
  #endif
1077
- if (check_cvode_flag((void*)cvode_mem, "CVodeCreate", 0)) return sim_clean();
1151
+ if (cvode_mem == NULL) return sim_cleanx(PyExc_Exception, "Unable to allocate CVODE memory.");
1078
1152
 
1079
1153
  /* Set error and warning-message handler */
1154
+ #if SUNDIALS_VERSION_MAJOR >= 7
1155
+ sunerr = SUNContext_PushErrHandler(sundials_context, ErrorHandler, NULL);
1156
+ if (check_sundials_error(sunerr, "SUNContext_PushErrHandler")) return sim_clean();
1157
+ #else
1080
1158
  flag_cvode = CVodeSetErrHandlerFn(cvode_mem, ErrorHandler, NULL);
1081
- if (check_cvode_flag(&flag_cvode, "CVodeInit", 1)) return sim_clean();
1159
+ if (check_cvode_related_flag(flag_cvode, "CVodeSetErrHandlerFn")) return sim_clean();
1160
+ #endif
1082
1161
 
1083
1162
  /* Initialize solver memory, specify the rhs */
1084
1163
  flag_cvode = CVodeInit(cvode_mem, rhs, t, y);
1085
- if (check_cvode_flag(&flag_cvode, "CVodeInit", 1)) return sim_clean();
1164
+ if (check_cvode_related_flag(flag_cvode, "CVodeInit")) return sim_clean();
1086
1165
 
1087
1166
  /* Set absolute and relative tolerances */
1088
1167
  flag_cvode = CVodeSStolerances(cvode_mem, RCONST(rel_tol), RCONST(abs_tol));
1089
- if (check_cvode_flag(&flag_cvode, "CVodeSStolerances", 1)) return sim_clean();
1168
+ if (check_cvode_related_flag(flag_cvode, "CVodeSStolerances")) return sim_clean();
1090
1169
 
1091
1170
  /* Set a maximum step size (or 0.0 for none) */
1092
1171
  flag_cvode = CVodeSetMaxStep(cvode_mem, dt_max < 0 ? 0.0 : dt_max);
1093
- if (check_cvode_flag(&flag_cvode, "CVodeSetmaxStep", 1)) return sim_clean();
1172
+ if (check_cvode_related_flag(flag_cvode, "CVodeSetmaxStep")) return sim_clean();
1094
1173
 
1095
1174
  /* Set a minimum step size (or 0.0 for none) */
1096
1175
  flag_cvode = CVodeSetMinStep(cvode_mem, dt_min < 0 ? 0.0 : dt_min);
1097
- if (check_cvode_flag(&flag_cvode, "CVodeSetminStep", 1)) return sim_clean();
1176
+ if (check_cvode_related_flag(flag_cvode, "CVodeSetminStep")) return sim_clean();
1098
1177
 
1099
1178
  #if SUNDIALS_VERSION_MAJOR >= 6
1100
1179
  /* Create dense matrix for use in linear solves */
1101
1180
  sundense_matrix = SUNDenseMatrix(model->n_states, model->n_states, sundials_context);
1102
- if (check_cvode_flag((void *)sundense_matrix, "SUNDenseMatrix", 0)) return sim_clean();
1181
+ if (sundense_matrix == NULL) return sim_cleanx(PyExc_Exception, "Unable to allocate space for dense matrix.");
1103
1182
 
1104
1183
  /* Create dense linear solver object with matrix */
1105
1184
  sundense_solver = SUNLinSol_Dense(y, sundense_matrix, sundials_context);
1106
- if (check_cvode_flag((void *)sundense_solver, "SUNLinSol_Dense", 0)) return sim_clean();
1185
+ if (sundense_solver == NULL) return sim_cleanx(PyExc_Exception, "Unable to allocate space for dense solver.");
1107
1186
 
1108
1187
  /* Attach the matrix and solver to cvode */
1109
1188
  flag_cvode = CVodeSetLinearSolver(cvode_mem, sundense_solver, sundense_matrix);
1110
- if (check_cvode_flag(&flag_cvode, "CVodeSetLinearSolver", 1)) return sim_clean();
1189
+ if (check_sundials_flag(flag_cvode, "CVodeSetLinearSolver")) return sim_clean();
1111
1190
  #elif SUNDIALS_VERSION_MAJOR >= 4
1112
1191
  /* Create dense matrix for use in linear solves */
1113
1192
  sundense_matrix = SUNDenseMatrix(model->n_states, model->n_states);
1114
- if (check_cvode_flag((void *)sundense_matrix, "SUNDenseMatrix", 0)) return sim_clean();
1193
+ if (sundense_matrix == NULL) return sim_cleanx(PyExc_Exception, "Unable to allocate space for dense matrix.");
1115
1194
 
1116
1195
  /* Create dense linear solver object with matrix */
1117
1196
  sundense_solver = SUNLinSol_Dense(y, sundense_matrix);
1118
- if (check_cvode_flag((void *)sundense_solver, "SUNLinSol_Dense", 0)) return sim_clean();
1197
+ if (sundense_solver == NULL) return sim_cleanx(PyExc_Exception, "Unable to allocate space for dense solver.");
1119
1198
 
1120
1199
  /* Attach the matrix and solver to cvode */
1121
1200
  flag_cvode = CVodeSetLinearSolver(cvode_mem, sundense_solver, sundense_matrix);
1122
- if (check_cvode_flag(&flag_cvode, "CVodeSetLinearSolver", 1)) return sim_clean();
1201
+ if (check_sundials_flag(flag_cvode, "CVodeSetLinearSolver")) return sim_clean();
1123
1202
  #elif SUNDIALS_VERSION_MAJOR >= 3
1124
1203
  /* Create dense matrix for use in linear solves */
1125
1204
  sundense_matrix = SUNDenseMatrix(model->n_states, model->n_states);
1126
- if (check_cvode_flag((void *)sundense_matrix, "SUNDenseMatrix", 0)) return sim_clean();
1205
+ if (sundense_matrix == NULL) return sim_cleanx(PyExc_Exception, "Unable to allocate space for dense matrix.");
1127
1206
 
1128
1207
  /* Create dense linear solver object with matrix */
1129
1208
  sundense_solver = SUNDenseLinearSolver(y, sundense_matrix);
1130
- if (check_cvode_flag((void *)sundense_solver, "SUNDenseLinearSolver", 0)) return sim_clean();
1209
+ if (sundense_solver == NULL) return sim_cleanx(PyExc_Exception, "Unable to allocate space for dense solver.");
1131
1210
 
1132
1211
  /* Attach the matrix and solver to cvode */
1133
1212
  flag_cvode = CVDlsSetLinearSolver(cvode_mem, sundense_solver, sundense_matrix);
1134
- if (check_cvode_flag(&flag_cvode, "CVDlsSetLinearSolver", 1)) return sim_clean();
1213
+ if (check_sundials_flag(flag_cvode, "CVDlsSetLinearSolver")) return sim_clean();
1135
1214
  #else
1136
1215
  /* Create dense matrix for use in linear solves */
1137
1216
  flag_cvode = CVDense(cvode_mem, model->n_states);
1138
- if (check_cvode_flag(&flag_cvode, "CVDense", 1)) return sim_clean();
1217
+ if (check_sundials_flag(flag_cvode, "CVDense")) return sim_clean();
1139
1218
  #endif
1140
1219
 
1141
1220
  #ifdef MYOKIT_DEBUG_PROFILING
@@ -1148,19 +1227,19 @@ sim_init(PyObject *self, PyObject *args)
1148
1227
  RHS of the sensitivity ODE */
1149
1228
  /*flag_cvode = CVodeSensInit(cvode_mem, model->ns_independents, CV_SIMULTANEOUS, rhs1, sy);*/
1150
1229
  flag_cvode = CVodeSensInit(cvode_mem, model->ns_independents, CV_SIMULTANEOUS, NULL, sy);
1151
- if (check_cvode_flag(&flag_cvode, "CVodeSensInit", 1)) return sim_clean();
1230
+ if (check_cvode_related_flag(flag_cvode, "CVodeSensInit")) return sim_clean();
1152
1231
 
1153
1232
  /* Attach user data */
1154
1233
  flag_cvode = CVodeSetUserData(cvode_mem, udata);
1155
- if (check_cvode_flag(&flag_cvode, "CVodeSetUserData", 1)) return sim_clean();
1234
+ if (check_cvode_related_flag(flag_cvode, "CVodeSetUserData")) return sim_clean();
1156
1235
 
1157
1236
  /* Set parameter scales used in tolerances */
1158
1237
  flag_cvode = CVodeSetSensParams(cvode_mem, udata->p, pbar, NULL);
1159
- if (check_cvode_flag(&flag_cvode, "CVodeSetSensParams", 1)) return sim_clean();
1238
+ if (check_cvode_related_flag(flag_cvode, "CVodeSetSensParams")) return sim_clean();
1160
1239
 
1161
1240
  /* Set sensitivity tolerances calculating method (using pbar) */
1162
1241
  flag_cvode = CVodeSensEEtolerances(cvode_mem);
1163
- if (check_cvode_flag(&flag_cvode, "CVodeSensEEtolerances", 1)) return sim_clean();
1242
+ if (check_cvode_related_flag(flag_cvode, "CVodeSensEEtolerances")) return sim_clean();
1164
1243
 
1165
1244
  #ifdef MYOKIT_DEBUG_PROFILING
1166
1245
  benchmarker_print("CP CVODES sensitivity methods initialized.");
@@ -1177,7 +1256,7 @@ sim_init(PyObject *self, PyObject *args)
1177
1256
  if (model->is_ode && PyList_Check(rf_list)) {
1178
1257
  /* Initialize root function with 1 component */
1179
1258
  flag_cvode = CVodeRootInit(cvode_mem, 1, rf_function);
1180
- if (check_cvode_flag(&flag_cvode, "CVodeRootInit", 1)) return sim_clean();
1259
+ if (check_cvode_related_flag(flag_cvode, "CVodeRootInit")) return sim_clean();
1181
1260
 
1182
1261
  /* Direction of root crossings, one entry per root function, but we only use 1. */
1183
1262
  rf_direction = (int*)malloc(sizeof(int));
@@ -1302,6 +1381,22 @@ sim_init(PyObject *self, PyObject *args)
1302
1381
  benchmarker_print("CP Logging times and strategy initialized.");
1303
1382
  #endif
1304
1383
 
1384
+ #ifdef MYOKIT_DEBUG_STATS
1385
+ if (model->is_ode) {
1386
+ printf(" 1. number of steps taken by cvodes.\n");
1387
+ printf(" 2. number of calls to the user's f function.\n");
1388
+ printf(" 3. number of calls made to the linear solver setup function.\n");
1389
+ printf(" 4. number of error test failures.\n");
1390
+ printf(" 5. method order used on the last internal step.\n");
1391
+ printf(" 6. method order to be used on the next internal step.\n");
1392
+ printf(" 7. actual value of initial step size.\n");
1393
+ printf(" 8. step size taken on the last internal step.\n");
1394
+ printf(" 9. step size to be attempted on the next internal step.\n");
1395
+ printf("10. current internal time reached.\n");
1396
+ printf("1\t2\t3\t4\t5\t6\t\t7\t\t8\t\t9\t\t10\n");
1397
+ }
1398
+ #endif
1399
+
1305
1400
  /*
1306
1401
  * Done!
1307
1402
  */
@@ -1340,6 +1435,13 @@ sim_step(PyObject *self, PyObject *args)
1340
1435
  PyObject *val;
1341
1436
  PyObject* ret;
1342
1437
 
1438
+ #ifdef MYOKIT_DEBUG_STATS
1439
+ /* CVODE stats */
1440
+ long int cv_nsteps, cv_nfevals, cv_nlinsetups, cv_netfails;
1441
+ int cv_qlast, cv_qcur;
1442
+ realtype cv_hinused, cv_hlast, cv_hcur, cv_tcur;
1443
+ #endif
1444
+
1343
1445
  /*
1344
1446
  * Set start time for logging of realtime.
1345
1447
  * This is handled here instead of in sim_init so it only includes time
@@ -1367,12 +1469,30 @@ sim_step(PyObject *self, PyObject *args)
1367
1469
 
1368
1470
  /* Take a single ODE step */
1369
1471
  #ifdef MYOKIT_DEBUG_MESSAGES
1370
- printf("\nCM Taking CVODE step from time %g to %g.\n", t, tnext);
1472
+ printf("\nCM Taking CVODE step from time %g to %g", t, tnext);
1371
1473
  #endif
1372
1474
  flag_cvode = CVode(cvode_mem, tnext, y, &t, CV_ONE_STEP);
1475
+ #ifdef MYOKIT_DEBUG_MESSAGES
1476
+ printf(" : flag %d\n", flag_cvode);
1477
+ #endif
1478
+
1479
+ /* Show cvodes stats */
1480
+ #ifdef MYOKIT_DEBUG_STATS
1481
+ CVodeGetIntegratorStats(cvode_mem, &cv_nsteps, &cv_nfevals,
1482
+ &cv_nlinsetups, &cv_netfails, &cv_qlast, &cv_qcur,
1483
+ &cv_hinused, &cv_hlast, &cv_hcur, &cv_tcur);
1484
+ printf("%ld,\t%ld,\t%ld,\t%ld,\t%d,\t%d,\t%g,\t%g,\t%g,\t%g\n",
1485
+ cv_nsteps, cv_nfevals, cv_nlinsetups, cv_netfails,
1486
+ cv_qlast, cv_qcur,
1487
+ cv_hinused, cv_hlast, cv_hcur, cv_tcur);
1488
+ #endif
1373
1489
 
1374
1490
  /* Check for errors */
1375
- if (check_cvode_flag(&flag_cvode, "CVode", 1)) {
1491
+ if (check_cvode_flag(flag_cvode)) {
1492
+ #ifdef MYOKIT_DEBUG_MESSAGES
1493
+ printf("\nCM CVODE flag %d. Setting error output and returning.\n", flag_cvode);
1494
+ #endif
1495
+
1376
1496
  /* Something went wrong... Set outputs and return */
1377
1497
  for (i=0; i<model->n_states; i++) {
1378
1498
  PyList_SetItem(state_py, i, PyFloat_FromDouble(NV_Ith_S(ylast, i)));
@@ -1384,6 +1504,8 @@ sim_step(PyObject *self, PyObject *args)
1384
1504
  for (i=0; i<n_pace; i++) {
1385
1505
  PyList_SetItem(bound_py, 3 + i, PyFloat_FromDouble(pacing[i]));
1386
1506
  }
1507
+
1508
+ /* Error state set by check_cvode_flag, so use ordinary return. */
1387
1509
  return sim_clean();
1388
1510
  }
1389
1511
 
@@ -1400,7 +1522,18 @@ sim_step(PyObject *self, PyObject *args)
1400
1522
  /* Check if progress is being made */
1401
1523
  if (t == tlast) {
1402
1524
  if (++zero_step_count >= max_zero_step_count) {
1403
- return sim_cleanx(PyExc_ArithmeticError, "Maximum number of zero-length steps taken at t=%g", t);
1525
+ /* Something went wrong: set outputs and return */
1526
+ for (i=0; i<model->n_states; i++) {
1527
+ PyList_SetItem(state_py, i, PyFloat_FromDouble(NV_Ith_S(ylast, i)));
1528
+ /* PyList_SetItem steals a reference: no need to decref the double! */
1529
+ }
1530
+ PyList_SetItem(bound_py, 0, PyFloat_FromDouble(tlast));
1531
+ PyList_SetItem(bound_py, 1, PyFloat_FromDouble(realtime));
1532
+ PyList_SetItem(bound_py, 2, PyFloat_FromDouble((double)evaluations));
1533
+ for (i=0; i<n_pace; i++) {
1534
+ PyList_SetItem(bound_py, 3 + i, PyFloat_FromDouble(pacing[i]));
1535
+ }
1536
+ return sim_cleanx(PyExc_ArithmeticError, "Maximum number of zero-length steps taken.");
1404
1537
  }
1405
1538
  } else {
1406
1539
  /* Only count consecutive zero steps */
@@ -1426,10 +1559,10 @@ sim_step(PyObject *self, PyObject *args)
1426
1559
 
1427
1560
  /* Go back to time=tnext */
1428
1561
  flag_cvode = CVodeGetDky(cvode_mem, tnext, 0, y);
1429
- if (check_cvode_flag(&flag_cvode, "CVodeGetDky", 1)) return sim_clean();
1562
+ if (check_cvode_related_flag(flag_cvode, "CVodeGetDky")) return sim_clean();
1430
1563
  if (model->has_sensitivities) {
1431
1564
  flag_cvode = CVodeGetSensDky(cvode_mem, tnext, 0, sy);
1432
- if (check_cvode_flag(&flag_cvode, "CVodeGetSensDky", 1)) return sim_clean();
1565
+ if (check_cvode_related_flag(flag_cvode, "CVodeGetSensDky")) return sim_clean();
1433
1566
  }
1434
1567
  t = tnext;
1435
1568
  /* Require reinit (after logging) */
@@ -1440,7 +1573,7 @@ sim_step(PyObject *self, PyObject *args)
1440
1573
  /* Get current sensitivity vector */
1441
1574
  if (model->has_sensitivities) {
1442
1575
  flag_cvode = CVodeGetSens(cvode_mem, &t, sy);
1443
- if (check_cvode_flag(&flag_cvode, "CVodeGetSens", 1)) return sim_clean();
1576
+ if (check_cvode_related_flag(flag_cvode, "CVodeGetSens")) return sim_clean();
1444
1577
  }
1445
1578
 
1446
1579
  /* Root found */
@@ -1448,7 +1581,7 @@ sim_step(PyObject *self, PyObject *args)
1448
1581
 
1449
1582
  /* Get directions of root crossings (1 per root function) */
1450
1583
  flag_root = CVodeGetRootInfo(cvode_mem, rf_direction);
1451
- if (check_cvode_flag(&flag_root, "CVodeGetRootInfo", 1)) return sim_clean();
1584
+ if (check_cvode_related_flag(flag_root, "CVodeGetRootInfo")) return sim_clean();
1452
1585
  /* We only have one root function, so we know that rf_direction[0] is non-zero at this point. */
1453
1586
 
1454
1587
  /* Store tuple (time, direction) for the found root */
@@ -1488,10 +1621,10 @@ sim_step(PyObject *self, PyObject *args)
1488
1621
  /* Get interpolated y(tlog) */
1489
1622
  if (model->is_ode) {
1490
1623
  flag_cvode = CVodeGetDky(cvode_mem, tlog, 0, z);
1491
- if (check_cvode_flag(&flag_cvode, "CVodeGetDky", 1)) return sim_clean();
1624
+ if (check_cvode_related_flag(flag_cvode, "CVodeGetDky")) return sim_clean();
1492
1625
  if (model->has_sensitivities) {
1493
1626
  flag_cvode = CVodeGetSensDky(cvode_mem, tlog, 0, sz);
1494
- if (check_cvode_flag(&flag_cvode, "CVodeGetSensDky", 1)) return sim_clean();
1627
+ if (check_cvode_related_flag(flag_cvode, "CVodeGetSensDky")) return sim_clean();
1495
1628
  }
1496
1629
  }
1497
1630
  /* If cvode-free mode, the states can't change so we don't
@@ -1617,10 +1750,10 @@ sim_step(PyObject *self, PyObject *args)
1617
1750
  */
1618
1751
  if (model->is_ode && flag_reinit) {
1619
1752
  flag_cvode = CVodeReInit(cvode_mem, t, y);
1620
- if (check_cvode_flag(&flag_cvode, "CVodeReInit", 1)) return sim_clean();
1753
+ if (check_cvode_related_flag(flag_cvode, "CVodeReInit")) return sim_clean();
1621
1754
  if (model->has_sensitivities) {
1622
1755
  flag_cvode = CVodeSensReInit(cvode_mem, CV_SIMULTANEOUS, sy);
1623
- if (check_cvode_flag(&flag_cvode, "CVodeSensReInit", 1)) return sim_clean();
1756
+ if (check_cvode_related_flag(flag_cvode, "CVodeSensReInit")) return sim_clean();
1624
1757
  }
1625
1758
  flag_reinit = 0;
1626
1759
  }
@@ -1697,36 +1830,40 @@ sim_step(PyObject *self, PyObject *args)
1697
1830
  * Evaluates the state derivatives at the given state
1698
1831
  */
1699
1832
  PyObject*
1700
- sim_eval_derivatives(PyObject *self, PyObject *args)
1833
+ sim_evaluate_derivatives(PyObject *self, PyObject *args)
1701
1834
  {
1702
1835
  /* Declare variables here for C89 compatibility */
1703
1836
  int i;
1704
1837
  int success;
1705
1838
  double time_in;
1839
+ double realtime_in;
1840
+ double evaluations_in;
1706
1841
  PyObject *pace_in;
1707
- double *pacing_in;
1708
- Model model;
1709
- Model_Flag flag_model;
1710
- PyObject *state;
1711
- PyObject *deriv;
1842
+ double *pacing_values;
1712
1843
  PyObject *literals;
1713
1844
  PyObject *parameters;
1845
+ PyObject *state;
1846
+ PyObject *deriv;
1714
1847
  PyObject *val;
1848
+ Model model;
1849
+ Model_Flag flag_model;
1715
1850
 
1716
1851
  /* Start */
1717
1852
  success = 0;
1718
1853
 
1719
1854
  /* Check input arguments */
1720
1855
  /* Check input arguments 0123456789ABCDEF*/
1721
- if (!PyArg_ParseTuple(args, "dOOOOO",
1856
+ if (!PyArg_ParseTuple(args, "dOddOOOO",
1722
1857
  &time_in, /* 0. Float: time */
1723
- &pace_in, /* 1. List: pace */
1724
- &state, /* 2. List: state */
1725
- &deriv, /* 3. List: store derivatives here */
1858
+ &pace_in, /* 1. List: pacing values */
1859
+ &realtime_in, /* 2. Float: realtime */
1860
+ &evaluations_in, /* 3. Float: evaluations */
1726
1861
  &literals, /* 4. List: literal constant values */
1727
- &parameters /* 5. List: parameter values */
1728
- )) {
1729
- PyErr_SetString(PyExc_Exception, "Incorrect input arguments in sim_eval_derivatives.");
1862
+ &parameters, /* 5. List: parameter values */
1863
+ &state, /* 6. List: state */
1864
+ &deriv /* 7. List: store derivatives here */
1865
+ )) {
1866
+ PyErr_SetString(PyExc_Exception, "Incorrect input arguments in sim_evaluate_derivatives.");
1730
1867
  /* Nothing allocated yet, no pyobjects _created_, return directly */
1731
1868
  return 0;
1732
1869
  }
@@ -1736,14 +1873,6 @@ sim_eval_derivatives(PyObject *self, PyObject *args)
1736
1873
  PyErr_SetString(PyExc_Exception, "Pace argument must be a list.");
1737
1874
  return 0;
1738
1875
  }
1739
- if (!PyList_Check(state)) {
1740
- PyErr_SetString(PyExc_Exception, "State argument must be a list.");
1741
- return 0;
1742
- }
1743
- if (!PyList_Check(deriv)) {
1744
- PyErr_SetString(PyExc_Exception, "Derivatives argument must be a list.");
1745
- return 0;
1746
- }
1747
1876
  if (!PyList_Check(literals)) {
1748
1877
  PyErr_SetString(PyExc_Exception, "Literals argument must be a list.");
1749
1878
  return 0;
@@ -1752,6 +1881,14 @@ sim_eval_derivatives(PyObject *self, PyObject *args)
1752
1881
  PyErr_SetString(PyExc_Exception, "Parameters argument must be a list.");
1753
1882
  return 0;
1754
1883
  }
1884
+ if (!PyList_Check(state)) {
1885
+ PyErr_SetString(PyExc_Exception, "State argument must be a list.");
1886
+ return 0;
1887
+ }
1888
+ if (!PyList_Check(deriv)) {
1889
+ PyErr_SetString(PyExc_Exception, "Derivatives argument must be a list.");
1890
+ return 0;
1891
+ }
1755
1892
 
1756
1893
  /* From this point on, no more direct returning: use goto error */
1757
1894
  model = NULL;
@@ -1767,6 +1904,8 @@ sim_eval_derivatives(PyObject *self, PyObject *args)
1767
1904
  goto error;
1768
1905
  }
1769
1906
 
1907
+ /* Set up pacing (but without protocols) */
1908
+ n_pace = (int)PyList_Size(pace_in);
1770
1909
  flag_model = Model_SetupPacing(model, n_pace);
1771
1910
  if (flag_model != Model_OK) {
1772
1911
  Model_SetPyErr(flag_model);
@@ -1774,18 +1913,23 @@ sim_eval_derivatives(PyObject *self, PyObject *args)
1774
1913
  }
1775
1914
 
1776
1915
  /* Set pacing values */
1777
- pacing_in = (double*)malloc((size_t)n_pace * sizeof(double));
1916
+ pacing_values = (double*)malloc((size_t)n_pace * sizeof(double));
1778
1917
  for (i=0; i<n_pace; i++) {
1779
1918
  val = PyList_GetItem(pace_in, i); /* Don't decref */
1780
1919
  if (!PyFloat_Check(val)) {
1781
1920
  PyErr_Format(PyExc_Exception, "Item %d in pace vector is not a float.", i);
1782
1921
  goto error;
1783
1922
  }
1784
- pacing_in[i] = PyFloat_AsDouble(val);
1923
+ pacing_values[i] = PyFloat_AsDouble(val);
1785
1924
  }
1786
1925
 
1787
1926
  /* Set bound variables */
1788
- Model_SetBoundVariables(model, (realtype)time_in, (realtype*)pacing_in, 0, 0);
1927
+ Model_SetBoundVariables(
1928
+ model,
1929
+ (realtype)time_in,
1930
+ (realtype*)pacing_values,
1931
+ (realtype)realtime_in,
1932
+ (realtype)evaluations_in);
1789
1933
 
1790
1934
  /* Set literal values */
1791
1935
  for (i=0; i<model->n_literals; i++) {
@@ -1925,7 +2069,7 @@ PyMethodDef SimMethods[] = {
1925
2069
  {"sim_init", sim_init, METH_VARARGS, "Initialize the simulation."},
1926
2070
  {"sim_step", sim_step, METH_VARARGS, "Perform the next step in the simulation."},
1927
2071
  {"sim_clean", py_sim_clean, METH_VARARGS, "Clean up after an aborted simulation."},
1928
- {"eval_derivatives", sim_eval_derivatives, METH_VARARGS, "Evaluate the state derivatives."},
2072
+ {"evaluate_derivatives", sim_evaluate_derivatives, METH_VARARGS, "Evaluate the state derivatives."},
1929
2073
  {"set_tolerance", sim_set_tolerance, METH_VARARGS, "Set the absolute and relative solver tolerance."},
1930
2074
  {"set_max_step_size", sim_set_max_step_size, METH_VARARGS, "Set the maximum solver step size (0 for none)."},
1931
2075
  {"set_min_step_size", sim_set_min_step_size, METH_VARARGS, "Set the minimum solver step size (0 for none)."},