myokit 1.33.9__py3-none-any.whl → 1.35.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.
Files changed (229) hide show
  1. myokit/__init__.py +9 -36
  2. myokit/__main__.py +76 -142
  3. myokit/_aux.py +62 -16
  4. myokit/_bin/example.mmt +1 -2
  5. myokit/_bin/install-win/menu.json +7 -7
  6. myokit/_config.py +22 -31
  7. myokit/_datablock.py +30 -74
  8. myokit/_datalog.py +49 -72
  9. myokit/_err.py +25 -24
  10. myokit/_expressions.py +50 -68
  11. myokit/_io.py +15 -27
  12. myokit/_model_api.py +453 -249
  13. myokit/_myokit_version.py +1 -5
  14. myokit/_parsing.py +38 -44
  15. myokit/_progress.py +5 -8
  16. myokit/_protocol.py +99 -9
  17. myokit/_sim/__init__.py +7 -24
  18. myokit/_sim/cable.c +6 -8
  19. myokit/_sim/cable.py +6 -8
  20. myokit/_sim/cmodel.h +125 -70
  21. myokit/_sim/cmodel.py +12 -14
  22. myokit/_sim/compiler.py +1 -4
  23. myokit/_sim/cvodessim.c +196 -118
  24. myokit/_sim/cvodessim.py +130 -103
  25. myokit/_sim/differential.hpp +4 -4
  26. myokit/_sim/fiber_tissue.c +4 -8
  27. myokit/_sim/fiber_tissue.py +11 -13
  28. myokit/_sim/jacobian.cpp +2 -2
  29. myokit/_sim/jacobian.py +11 -8
  30. myokit/_sim/mcl.h +53 -55
  31. myokit/_sim/opencl.py +21 -27
  32. myokit/_sim/openclsim.c +3 -7
  33. myokit/_sim/openclsim.cl +3 -3
  34. myokit/_sim/openclsim.py +49 -40
  35. myokit/_sim/pacing.h +36 -16
  36. myokit/_sim/rhs.c +6 -13
  37. myokit/_sim/rhs.py +5 -14
  38. myokit/_sim/sundials.py +1 -4
  39. myokit/_system.py +10 -16
  40. myokit/_unit.py +4 -13
  41. myokit/float.py +0 -3
  42. myokit/formats/__init__.py +8 -10
  43. myokit/formats/ansic/__init__.py +0 -3
  44. myokit/formats/ansic/_ewriter.py +2 -4
  45. myokit/formats/ansic/_exporter.py +1 -4
  46. myokit/formats/ansic/template/cable.c +4 -4
  47. myokit/formats/ansic/template/euler.c +5 -5
  48. myokit/formats/ansic/template/sim.c +6 -6
  49. myokit/formats/axon/__init__.py +1 -3
  50. myokit/formats/axon/_abf.py +12 -17
  51. myokit/formats/axon/_atf.py +5 -6
  52. myokit/formats/axon/_importer.py +0 -3
  53. myokit/formats/cellml/__init__.py +0 -3
  54. myokit/formats/cellml/_ewriter.py +3 -6
  55. myokit/formats/cellml/_exporter.py +3 -6
  56. myokit/formats/cellml/_importer.py +1 -4
  57. myokit/formats/cellml/v1/__init__.py +0 -4
  58. myokit/formats/cellml/v1/_api.py +8 -11
  59. myokit/formats/cellml/v1/_parser.py +2 -5
  60. myokit/formats/cellml/v1/_writer.py +2 -11
  61. myokit/formats/cellml/v2/__init__.py +0 -3
  62. myokit/formats/cellml/v2/_api.py +8 -17
  63. myokit/formats/cellml/v2/_parser.py +2 -5
  64. myokit/formats/cellml/v2/_writer.py +1 -4
  65. myokit/formats/channelml/__init__.py +0 -3
  66. myokit/formats/channelml/_importer.py +11 -21
  67. myokit/formats/cpp/__init__.py +1 -3
  68. myokit/formats/cpp/_ewriter.py +0 -3
  69. myokit/formats/cuda/__init__.py +0 -3
  70. myokit/formats/cuda/_ewriter.py +2 -4
  71. myokit/formats/cuda/_exporter.py +0 -3
  72. myokit/formats/cuda/template/kernel.cu +8 -5
  73. myokit/formats/easyml/__init__.py +0 -3
  74. myokit/formats/easyml/_ewriter.py +9 -11
  75. myokit/formats/easyml/_exporter.py +2 -5
  76. myokit/formats/html/__init__.py +0 -3
  77. myokit/formats/html/_exporter.py +0 -3
  78. myokit/formats/html/_flatten.py +5 -21
  79. myokit/formats/latex/__init__.py +0 -3
  80. myokit/formats/latex/_ewriter.py +1 -4
  81. myokit/formats/latex/_exporter.py +4 -6
  82. myokit/formats/mathml/__init__.py +0 -3
  83. myokit/formats/mathml/_ewriter.py +2 -11
  84. myokit/formats/mathml/_parser.py +4 -6
  85. myokit/formats/matlab/__init__.py +0 -3
  86. myokit/formats/matlab/_ewriter.py +1 -4
  87. myokit/formats/matlab/_exporter.py +2 -5
  88. myokit/formats/matlab/template/main.m +3 -2
  89. myokit/formats/opencl/__init__.py +0 -3
  90. myokit/formats/opencl/_ewriter.py +2 -4
  91. myokit/formats/opencl/_exporter.py +2 -5
  92. myokit/formats/opencl/template/cable.c +10 -10
  93. myokit/formats/opencl/template/kernel.cl +1 -1
  94. myokit/formats/opencl/template/minilog.py +1 -1
  95. myokit/formats/python/__init__.py +0 -3
  96. myokit/formats/python/_ewriter.py +2 -5
  97. myokit/formats/python/_exporter.py +0 -3
  98. myokit/formats/python/template/sim.py +14 -14
  99. myokit/formats/sbml/__init__.py +0 -3
  100. myokit/formats/sbml/_api.py +50 -44
  101. myokit/formats/sbml/_importer.py +1 -4
  102. myokit/formats/sbml/_parser.py +2 -5
  103. myokit/formats/stan/__init__.py +0 -3
  104. myokit/formats/stan/_ewriter.py +2 -4
  105. myokit/formats/stan/_exporter.py +2 -5
  106. myokit/formats/stan/template/cell.stan +3 -3
  107. myokit/formats/sympy/__init__.py +0 -3
  108. myokit/formats/sympy/_ereader.py +1 -4
  109. myokit/formats/sympy/_ewriter.py +2 -5
  110. myokit/formats/wcp/__init__.py +0 -3
  111. myokit/formats/wcp/_wcp.py +2 -8
  112. myokit/formats/xml/__init__.py +0 -3
  113. myokit/formats/xml/_exporter.py +0 -3
  114. myokit/formats/xml/_split.py +0 -3
  115. myokit/gui/__init__.py +80 -246
  116. myokit/gui/datablock_viewer.py +103 -86
  117. myokit/gui/datalog_viewer.py +214 -66
  118. myokit/gui/explorer.py +15 -21
  119. myokit/gui/ide.py +171 -144
  120. myokit/gui/progress.py +9 -9
  121. myokit/gui/source.py +406 -375
  122. myokit/gui/vargrapher.py +2 -12
  123. myokit/lib/deps.py +12 -13
  124. myokit/lib/guess.py +3 -4
  125. myokit/lib/hh.py +20 -18
  126. myokit/lib/markov.py +21 -20
  127. myokit/lib/multi.py +1 -3
  128. myokit/lib/plots.py +20 -9
  129. myokit/pacing.py +0 -3
  130. myokit/pype.py +7 -18
  131. myokit/tests/__init__.py +3 -6
  132. myokit/tests/ansic_event_based_pacing.py +1 -4
  133. myokit/tests/ansic_fixed_form_pacing.py +3 -6
  134. myokit/tests/data/beeler-1977-model-compare-b.mmt +2 -2
  135. myokit/tests/data/clancy-1999-fitting.mmt +1 -0
  136. myokit/tests/test_aux.py +13 -28
  137. myokit/tests/test_cellml_v1_api.py +4 -19
  138. myokit/tests/test_cellml_v1_parser.py +0 -15
  139. myokit/tests/test_cellml_v1_writer.py +0 -9
  140. myokit/tests/test_cellml_v2_api.py +4 -19
  141. myokit/tests/test_cellml_v2_parser.py +0 -15
  142. myokit/tests/test_cellml_v2_writer.py +0 -9
  143. myokit/tests/test_cmodel.py +16 -22
  144. myokit/tests/test_compiler_detection.py +1 -11
  145. myokit/tests/test_component.py +108 -56
  146. myokit/tests/test_config.py +34 -67
  147. myokit/tests/test_datablock.py +1 -9
  148. myokit/tests/test_datalog.py +19 -24
  149. myokit/tests/test_dependency_checking.py +8 -23
  150. myokit/tests/test_expressions.py +0 -9
  151. myokit/tests/test_float.py +1 -5
  152. myokit/tests/test_formats.py +0 -9
  153. myokit/tests/test_formats_axon.py +1 -9
  154. myokit/tests/test_formats_cellml.py +0 -15
  155. myokit/tests/test_formats_channelml.py +0 -15
  156. myokit/tests/test_formats_easyml.py +0 -14
  157. myokit/tests/test_formats_exporters.py +1 -16
  158. myokit/tests/test_formats_expression_writers.py +1 -17
  159. myokit/tests/test_formats_html.py +0 -3
  160. myokit/tests/test_formats_importers.py +1 -16
  161. myokit/tests/test_formats_mathml_content.py +0 -9
  162. myokit/tests/test_formats_mathml_presentation.py +0 -9
  163. myokit/tests/test_formats_opencl.py +0 -10
  164. myokit/tests/test_formats_sbml.py +0 -15
  165. myokit/tests/test_formats_sympy.py +0 -9
  166. myokit/tests/test_formats_wcp.py +1 -3
  167. myokit/tests/test_io.py +27 -27
  168. myokit/tests/test_jacobian_calculator.py +6 -14
  169. myokit/tests/test_jacobian_tracer.py +0 -9
  170. myokit/tests/test_lib_deps.py +0 -9
  171. myokit/tests/test_lib_guess.py +0 -9
  172. myokit/tests/test_lib_hh.py +18 -12
  173. myokit/tests/test_lib_markov.py +21 -13
  174. myokit/tests/test_lib_multi.py +0 -9
  175. myokit/tests/test_lib_plots.py +13 -8
  176. myokit/tests/test_meta.py +0 -3
  177. myokit/tests/test_model.py +390 -96
  178. myokit/tests/test_model_building.py +44 -96
  179. myokit/tests/test_opencl_info.py +5 -14
  180. myokit/tests/test_pacing_factory.py +0 -3
  181. myokit/tests/test_pacing_system_c.py +1 -23
  182. myokit/tests/test_pacing_system_py.py +0 -9
  183. myokit/tests/test_parsing.py +139 -56
  184. myokit/tests/test_progress_reporters.py +0 -3
  185. myokit/tests/test_protocol.py +0 -9
  186. myokit/tests/test_protocol_floating_point.py +1 -10
  187. myokit/tests/test_protocol_time_series.py +82 -0
  188. myokit/tests/test_pype.py +0 -9
  189. myokit/tests/test_quantity.py +0 -9
  190. myokit/tests/test_rhs_benchmarker.py +1 -9
  191. myokit/tests/test_sbml_api.py +27 -42
  192. myokit/tests/test_sbml_parser.py +4 -19
  193. myokit/tests/test_simulation_1d.py +45 -25
  194. myokit/tests/test_simulation_cvodes.py +321 -55
  195. myokit/tests/test_simulation_cvodes_from_disk.py +0 -3
  196. myokit/tests/test_simulation_fiber_tissue.py +39 -12
  197. myokit/tests/test_simulation_log_interval.py +1 -431
  198. myokit/tests/test_simulation_opencl.py +69 -48
  199. myokit/tests/test_simulation_opencl_log_interval.py +1 -3
  200. myokit/tests/test_simulation_opencl_vs_cvode.py +1 -10
  201. myokit/tests/test_simulation_opencl_vs_sim1d.py +1 -10
  202. myokit/tests/test_system_info.py +1 -11
  203. myokit/tests/test_tools.py +0 -9
  204. myokit/tests/test_unit.py +1 -10
  205. myokit/tests/test_user_functions.py +0 -10
  206. myokit/tests/test_variable.py +231 -27
  207. myokit/tools.py +5 -21
  208. myokit/units.py +5 -3
  209. {myokit-1.33.9.dist-info → myokit-1.35.0.dist-info}/METADATA +12 -15
  210. myokit-1.35.0.dist-info/RECORD +391 -0
  211. {myokit-1.33.9.dist-info → myokit-1.35.0.dist-info}/WHEEL +1 -1
  212. {myokit-1.33.9.dist-info → myokit-1.35.0.dist-info}/entry_points.txt +0 -1
  213. myokit/_exec_new.py +0 -15
  214. myokit/_exec_old.py +0 -15
  215. myokit/_sim/cvodesim.c +0 -1551
  216. myokit/_sim/cvodesim.py +0 -674
  217. myokit/_sim/icsim.cpp +0 -563
  218. myokit/_sim/icsim.py +0 -363
  219. myokit/_sim/psim.cpp +0 -656
  220. myokit/_sim/psim.py +0 -493
  221. myokit/lib/common.py +0 -1094
  222. myokit/tests/test_lib_common.py +0 -130
  223. myokit/tests/test_simulation_cvode.py +0 -612
  224. myokit/tests/test_simulation_ic.py +0 -108
  225. myokit/tests/test_simulation_p.py +0 -223
  226. myokit-1.33.9.dist-info/RECORD +0 -403
  227. /myokit/formats/opencl/template/{test → test.sh} +0 -0
  228. {myokit-1.33.9.dist-info → myokit-1.35.0.dist-info}/LICENSE.txt +0 -0
  229. {myokit-1.33.9.dist-info → myokit-1.35.0.dist-info}/top_level.txt +0 -0
myokit/_sim/cvodesim.c DELETED
@@ -1,1551 +0,0 @@
1
- <?
2
- # cvodesim.c
3
- #
4
- # A pype template for a single cell CVODE-based simulation.
5
- #
6
- # Required variables
7
- # -----------------------------------------------------------------------------
8
- # module_name A module name
9
- # model A myokit model
10
- # potential A variable from the model used to track threshold crossings
11
- # -----------------------------------------------------------------------------
12
- #
13
- # This file is part of Myokit.
14
- # See http://myokit.org for copyright, sharing, and licensing details.
15
- #
16
- import myokit
17
- import myokit.formats.ansic as ansic
18
-
19
- # Get model
20
- model.reserve_unique_names(*ansic.keywords)
21
- model.create_unique_names()
22
-
23
- # Get expression writer
24
- w = ansic.AnsiCExpressionWriter()
25
-
26
- # Define lhs function
27
- def v(var):
28
- # Explicitly asking for derivative?
29
- if isinstance(var, myokit.Derivative):
30
- return 'NV_Ith_S(ydot, ' + str(var.var().indice()) + ')'
31
- # Name given? get variable object from name
32
- if isinstance(var, myokit.Name):
33
- var = var.var()
34
- # Handle states
35
- if var.is_state():
36
- return 'NV_Ith_S(y, ' + str(var.indice()) + ')'
37
- # Handle constants and intermediary variables
38
- if var.is_constant():
39
- return 'AC_' + var.uname()
40
- else:
41
- return 'AV_' + var.uname()
42
- w.set_lhs_function(v)
43
-
44
- # Tab
45
- tab = ' '
46
-
47
- # Get mapping of bound variables to internal refs, set the RHS of unbound variables
48
- # to zero and remove any unsupported bindings.
49
- bound_variables = model.prepare_bindings({
50
- 'time' : 't',
51
- 'pace' : 'engine_pace',
52
- 'realtime' : 'engine_realtime',
53
- 'evaluations' : 'engine_evaluations',
54
- })
55
- #
56
- # About the bindings:
57
- #
58
- # Time is bound to "t", the time variable used in the function. This is
59
- # required for periodic logging and point-list logging, when "tlog" increases
60
- # while engine_time stays fixed at the current solver time.
61
- # Pace is bound to engine_pace, since the solver always visits the points where
62
- # its value changes the same problem as with logging "engine_time" doesn't
63
- # occur.
64
- # Realtime is only useful without variable logging, so again binding to global
65
- # is ok.
66
- # Evaluations may increase during interpolation, but this evaluation will be
67
- # taken into account in the global variable "engine_evaluations", so this is
68
- # fine.
69
- #
70
-
71
- # Get equations
72
- equations = model.solvable_order()
73
- ?>
74
- #define PY_SSIZE_T_CLEAN
75
- #include <Python.h>
76
- #include <stdio.h>
77
- #include <math.h>
78
- #include <string.h>
79
-
80
- #include <cvode/cvode.h>
81
- #include <nvector/nvector_serial.h>
82
- #include <sundials/sundials_types.h>
83
- #include <sundials/sundials_config.h>
84
- #ifndef SUNDIALS_VERSION_MAJOR
85
- #define SUNDIALS_VERSION_MAJOR 2
86
- #endif
87
- #if SUNDIALS_VERSION_MAJOR >= 3
88
- #include <sunmatrix/sunmatrix_dense.h>
89
- #include <sunlinsol/sunlinsol_dense.h>
90
- #include <cvode/cvode_direct.h>
91
- #else
92
- #include <cvode/cvode_dense.h>
93
- #endif
94
-
95
- #include "pacing.h"
96
-
97
- #define N_STATE <?= model.count_states() ?>
98
- #define USE_CVODE <?= 1 if model.count_states() > 0 else 0 ?>
99
-
100
- /* Pacing */
101
- ESys epacing; /* Event-based pacing system */
102
- FSys fpacing; /* Fixed-form pacing system */
103
-
104
- /*
105
- * Check sundials flags, set python error
106
- * flagvalue : The value to check
107
- * funcname : The name of the function that returned the flag
108
- * opt : Mode selector
109
- * 0 : Error if the flag is null
110
- * 1 : Error if the flag is < 0
111
- * 2 : Errir
112
- */
113
- static int
114
- check_cvode_flag(void *flagvalue, char *funcname, int opt)
115
- {
116
- if (opt == 0 && flagvalue == NULL) {
117
- /* Check if sundials function returned null pointer */
118
- char str[200];
119
- sprintf(str, "%s() failed - returned NULL pointer", funcname);
120
- PyErr_SetString(PyExc_Exception, str);
121
- return 1;
122
- } else if (opt == 1) {
123
- /* Check if flag < 0 */
124
- int flag = *((int*)flagvalue);
125
- if (flag < 0) {
126
- if (strcmp(funcname, "CVode") == 0) {
127
- switch (flag) {
128
- case -1:
129
- 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.");
130
- break;
131
- case -2:
132
- 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.");
133
- break;
134
- case -3:
135
- 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.");
136
- break;
137
- case -4:
138
- 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.");
139
- break;
140
- case -5:
141
- PyErr_SetString(PyExc_ArithmeticError, "Function CVode() failed with flag -5 CV_LINIT_FAIL: The linear solver's initialization function failed.");
142
- break;
143
- case -6:
144
- PyErr_SetString(PyExc_ArithmeticError, "Function CVode() failed with flag -6 CV_LSETUP_FAIL: The linear solver's setup function failed in an unrecoverable manner.");
145
- break;
146
- case -7:
147
- PyErr_SetString(PyExc_ArithmeticError, "Function CVode() failed with flag -7 CV_LSOLVE_FAIL: The linear solver's solve function failed in an unrecoverable manner.");
148
- break;
149
- case -8:
150
- PyErr_SetString(PyExc_ArithmeticError, "Function CVode() failed with flag -8 CV_RHSFUNC_FAIL: The right-hand side function failed in an unrecoverable manner.");
151
- break;
152
- case -9:
153
- PyErr_SetString(PyExc_ArithmeticError, "Function CVode() failed with flag -9 CV_FIRST_RHSFUNC_ERR: The right-hand side function failed at the first call.");
154
- break;
155
- case -10:
156
- PyErr_SetString(PyExc_ArithmeticError, "Function CVode() failed with flag -10 CV_REPTD_RHSFUNC_ERR: The right-hand side function had repeated recoverable errors.");
157
- break;
158
- case -11:
159
- 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.");
160
- break;
161
- case -12:
162
- PyErr_SetString(PyExc_ArithmeticError, "Function CVode() failed with flag -12 CV_RTFUNC_FAIL: The root finding function failed in an unrecoverable manner.");
163
- break;
164
- case -20:
165
- PyErr_SetString(PyExc_Exception, "Function CVode() failed with flag -20 CV_MEM_FAIL: A memory allocation failed.");
166
- break;
167
- case -21:
168
- PyErr_SetString(PyExc_Exception, "Function CVode() failed with flag -21 CV_MEM_NULL: The cvode mem argument was NULL.");
169
- break;
170
- case -22:
171
- PyErr_SetString(PyExc_Exception, "Function CVode() failed with flag -22 CV_ILL_INPUT: One of the function inputs is illegal.");
172
- break;
173
- case -23:
174
- 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.");
175
- break;
176
- case -24:
177
- PyErr_SetString(PyExc_Exception, "Function CVode() failed with flag -24 CV_BAD_K: The derivative order k is larger than the order used.");
178
- break;
179
- case -25:
180
- PyErr_SetString(PyExc_Exception, "Function CVode() failed with flag -25 CV_BAD_T: The time t is outside the last step taken.");
181
- break;
182
- case -26:
183
- PyErr_SetString(PyExc_Exception, "Function CVode() failed with flag -26 CV_BAD_DKY: The output derivative vector is NULL.");
184
- break;
185
- case -27:
186
- PyErr_SetString(PyExc_Exception, "Function CVode() failed with flag -27 CV_TOO_CLOSE: The output and initial times are too close to each other.");
187
- break;
188
- default: {
189
- /* Note: Brackets are required here, default: should be followed by
190
- a _statement_ and char str[200]; is technically not a statement... */
191
- char str[200];
192
- sprintf(str, "Function CVode() failed with unknown flag = %d", flag);
193
- PyErr_SetString(PyExc_Exception, str);
194
- }}
195
- } else {
196
- char str[200];
197
- sprintf(str, "%s() failed with flag = %d", funcname, flag);
198
- PyErr_SetString(PyExc_Exception, str);
199
- }
200
- return 1;
201
- }
202
- }
203
- return 0;
204
- }
205
-
206
- /*
207
- * Declare intermediary, temporary and system variables
208
- */
209
- static realtype engine_time = 0; /* Engine time */
210
- static realtype engine_time_last = 0; /* Previous engine time */
211
- static realtype engine_pace = 0;
212
- static realtype engine_realtime = 0;
213
- static realtype engine_starttime = 0;
214
- static realtype rootfinding_threshold = 0;
215
- static long engine_evaluations = 0;
216
- static long engine_steps = 0;
217
- <?
218
- for var in model.variables(state=False, deep=True):
219
- if var.is_literal():
220
- print('static realtype ' + v(var) + ' = ' + myokit.float.str(var.rhs().eval()) + ';')
221
- else:
222
- print('static realtype ' + v(var) + ';')
223
- ?>
224
- /*
225
- * Set values of calculated constants
226
- */
227
- static void
228
- updateConstants(void)
229
- {
230
- <?
231
- for label, eqs in equations.items():
232
- for eq in eqs.equations(const=True):
233
- if not eq.rhs.is_literal():
234
- print(tab + w.eq(eq) + ';')
235
- ?>}
236
-
237
- /*
238
- * Right-hand-side function of the model ODE
239
- */
240
- static int
241
- rhs(realtype t, N_Vector y, N_Vector ydot, void *f_data)
242
- {
243
- /* Fixed-form pacing? Then look-up correct value of pacing variable! */
244
- FSys_Flag flag_fpacing;
245
- if (fpacing != NULL) {
246
- engine_pace = FSys_GetLevel(fpacing, t, &flag_fpacing);
247
- if (flag_fpacing != FSys_OK) { /* This should never happen */
248
- FSys_SetPyErr(flag_fpacing);
249
- return -1; /* Negative value signals irrecoverable error to CVODE */
250
- }
251
- }
252
- <?
253
- for label, eqs in equations.items():
254
- if eqs.has_equations(const=False):
255
- print(tab + '/* ' + label + ' */')
256
- for eq in eqs.equations(const=False):
257
- var = eq.lhs.var()
258
- try:
259
- print(tab + v(var) + ' = ' + bound_variables[var] + ';')
260
- except KeyError:
261
- print(tab + w.eq(eq) + ';')
262
- print(tab)
263
- ?>
264
- engine_evaluations++;
265
- return 0;
266
- }
267
-
268
- /*
269
- * Right-hand-side function, bound variables only
270
- */
271
- static int
272
- update_bindings(realtype t, N_Vector y, N_Vector ydot, void *f_data)
273
- {
274
- <?
275
- for var, internal in bound_variables.items():
276
- print(tab + v(var) + ' = ' + internal + ';')
277
- ?>
278
- return 0;
279
- }
280
-
281
- /*
282
- * Update variables bound to engine.realtime
283
- */
284
- static int
285
- update_realtime_bindings(realtype t, N_Vector y, N_Vector ydot, void *f_data)
286
- {
287
- <?
288
- var = model.binding('realtime')
289
- if var is not None:
290
- print(tab + v(var) + ' = engine_realtime;')
291
- ?>
292
- return 0;
293
- }
294
-
295
- /*
296
- * Root finding function
297
- */<?
298
- root_finding_indice = potential.indice() if potential is not None else 0
299
- ?>
300
- static int
301
- root_finding(realtype t, N_Vector y, realtype *gout, void *f_data)
302
- {
303
- gout[0] = NV_Ith_S(y, <?=root_finding_indice?>) - rootfinding_threshold;
304
- return 0;
305
- }
306
-
307
- /*
308
- * Settings
309
- */
310
- static double abs_tol = 1e-6; /* The absolute tolerance */
311
- static double rel_tol = 1e-4; /* The relative tolerance */
312
- static double dt_max = 0; /* The maximum step size (0.0 for none) */
313
- static double dt_min = 0; /* The minimum step size (0.0 for none) */
314
-
315
- /*
316
- * Change the tolerance settings
317
- */
318
- static PyObject*
319
- sim_set_tolerance(PyObject *self, PyObject *args)
320
- {
321
- /* Check input arguments */
322
- double tabs, trel;
323
- if (!PyArg_ParseTuple(args, "dd", &tabs, &trel)) {
324
- PyErr_SetString(PyExc_Exception, "Expected input arguments: abs_tol(float), rel_tol(float).");
325
- return 0;
326
- }
327
- abs_tol = tabs;
328
- rel_tol = trel;
329
- Py_RETURN_NONE;
330
- }
331
-
332
- /*
333
- * Change the maximum step size (0 for none)
334
- */
335
- static PyObject*
336
- sim_set_max_step_size(PyObject *self, PyObject *args)
337
- {
338
- /* Check input arguments */
339
- double tmax;
340
- if (!PyArg_ParseTuple(args, "d", &tmax)) {
341
- PyErr_SetString(PyExc_Exception, "Expected input argument: tmax(float).");
342
- return 0;
343
- }
344
- dt_max = tmax;
345
- Py_RETURN_NONE;
346
- }
347
-
348
- /*
349
- * Change the minimum step size (0 for none)
350
- */
351
- static PyObject*
352
- sim_set_min_step_size(PyObject *self, PyObject *args)
353
- {
354
- /* Check input arguments */
355
- double tmin;
356
- if (!PyArg_ParseTuple(args, "d", &tmin)) {
357
- PyErr_SetString(PyExc_Exception, "Expected input argument: tmin(float).");
358
- return 0;
359
- }
360
- dt_min = tmin;
361
- Py_RETURN_NONE;
362
- }
363
-
364
- /*
365
- * Add a variable to the logging lists. Returns 1 if successful
366
- */
367
- static int
368
- log_add(PyObject* log_dict, PyObject** logs, realtype** vars, int i, const char* name, const realtype* var)
369
- {
370
- /* See first use of log_add for notes on unicode */
371
- int added = 0;
372
- PyObject* key = PyUnicode_FromString(name);
373
- if (PyDict_Contains(log_dict, key)) {
374
- logs[i] = PyDict_GetItem(log_dict, key);
375
- vars[i] = (realtype*)var;
376
- added = 1;
377
- }
378
- Py_DECREF(key);
379
- return added;
380
- }
381
-
382
- /*
383
- * Error and warning message handler for CVODE.
384
- * Error messages are already set via check_cvode_flag, so this method
385
- * suppresses error messages.
386
- * Warnings are passed to Python's warning system, where they can be
387
- * caught or suppressed using the warnings module.
388
- */
389
- void
390
- ErrorHandler(int error_code, const char *module, const char *function,
391
- char *msg, void *eh_data)
392
- {
393
- char errstr[1024];
394
- if (error_code > 0) {
395
- sprintf(errstr, "CVODE: %s", msg);
396
- PyErr_WarnEx(PyExc_RuntimeWarning, errstr, 1);
397
- }
398
- }
399
-
400
- /*
401
- * Simulation variables
402
- */
403
-
404
- int running = 0; /* Running yes or no */
405
-
406
- /* Input arguments */
407
- double tmin; /* The initial simulation time */
408
- double tmax; /* The final simulation time */
409
- PyObject* state_in; /* The initial state */
410
- PyObject* state_out; /* The final state */
411
- PyObject* inputs; /* A vector used to return the binding inputs` values */
412
- PyObject* eprotocol; /* An event-based pacing protocol */
413
- PyObject* fprotocol; /* A fixed-form pacing protocol */
414
- PyObject* log_dict; /* The log dict */
415
- double log_interval; /* Periodic logging: The log interval (0 to disable) */
416
- PyObject* log_times; /* Point-list logging: List of points (None to disable) */
417
- PyObject* root_list; /* Empty list if root finding should be used */
418
- double root_threshold; /* Threshold to use for root finding */
419
- PyObject* benchtime; /* Callable time() function or None */
420
-
421
- /* Next simulation halting point */
422
- double tnext;
423
-
424
- /* Checking for repeated zero size steps */
425
- int zero_step_count;
426
- int max_zero_step_count = 500; /* Increased this from 50 */
427
-
428
- /* CVode objects */
429
- void *cvode_mem; /* The memory used by the solver */
430
- N_Vector y; /* Stores the current position y */
431
- N_Vector y_log; /* Used to store y when logging */
432
- N_Vector dy_log; /* Used to store dy when logging */
433
- N_Vector y_last; /* Used to store previous value of y for error handling */
434
- #if SUNDIALS_VERSION_MAJOR >= 3
435
- SUNMatrix sundense_matrix; /* Dense matrix for linear solves */
436
- SUNLinearSolver sundense_solver; /* Linear solver object */
437
- #endif
438
- #if SUNDIALS_VERSION_MAJOR >= 6
439
- SUNContext sundials_context; /* A sundials context to run in (for profiling etc.) */
440
- #endif
441
-
442
- /* Root finding */
443
- int* rootsfound; /* Used to store found roots */
444
-
445
- /* Logging */
446
- PyObject** logs; /* An array of pointers to a PyObject */
447
- realtype** vars; /* An array of pointers to realtype */
448
- int n_vars; /* Number of logging variables */
449
- int log_bound; /* True if logging bound variables */
450
- int log_inter; /* True if logging intermediary variables */
451
- int log_deriv; /* True if logging derivatives */
452
- PyObject* list_update_str; /* PyUnicode, used to call "append" method */
453
- Py_ssize_t ilog; /* Periodic/point-list logging: Index of next point */
454
- double tlog; /* Periodic/point-list logging: Next point */
455
- int dynamic_logging; /* True if logging every point. */
456
-
457
- /*
458
- * Cleans up after a simulation
459
- */
460
- static PyObject*
461
- sim_clean()
462
- {
463
- if (running != 0) {
464
- /* Done with str="append", decref it */
465
- Py_XDECREF(list_update_str); list_update_str = NULL;
466
-
467
- /* Free allocated space */
468
- free(vars); vars = NULL;
469
- free(logs); logs = NULL;
470
- free(rootsfound); rootsfound = NULL;
471
-
472
- /* Free CVode space */
473
- N_VDestroy_Serial(y); y = NULL;
474
- N_VDestroy_Serial(dy_log); dy_log = NULL;
475
- if (USE_CVODE && !dynamic_logging) {
476
- N_VDestroy_Serial(y_log);
477
- y_log = NULL;
478
- }
479
- CVodeFree(&cvode_mem); cvode_mem = NULL;
480
- #if SUNDIALS_VERSION_MAJOR >= 3
481
- SUNLinSolFree(sundense_solver); sundense_solver = NULL;
482
- SUNMatDestroy(sundense_matrix); sundense_matrix = NULL;
483
- #endif
484
- #if SUNDIALS_VERSION_MAJOR >= 6
485
- SUNContext_Free(&sundials_context); sundials_context = NULL;
486
- #endif
487
-
488
- /* Free pacing system space */
489
- ESys_Destroy(epacing); epacing = NULL;
490
- FSys_Destroy(fpacing); fpacing = NULL;
491
-
492
- /* No longer running */
493
- running = 0;
494
- }
495
-
496
- /* Return 0, allowing the construct
497
- PyErr_SetString(PyExc_Exception, "Oh noes!");
498
- return sim_clean()
499
- to terminate a python function. */
500
- return 0;
501
- }
502
- static PyObject*
503
- py_sim_clean(PyObject *self, PyObject *args)
504
- {
505
- sim_clean();
506
- Py_RETURN_NONE;
507
- }
508
-
509
- /*
510
- * Initialise a run
511
- */
512
- static PyObject*
513
- sim_init(PyObject *self, PyObject *args)
514
- {
515
- int i, j;
516
- int flag_cvode;
517
- int log_first_point;
518
- ESys_Flag flag_epacing;
519
- FSys_Flag flag_fpacing;
520
- Py_ssize_t pos;
521
- PyObject *flt;
522
- PyObject *key;
523
- PyObject* ret;
524
- PyObject *value;
525
-
526
- #ifndef SUNDIALS_DOUBLE_PRECISION
527
- PyErr_SetString(PyExc_Exception, "Sundials must be compiled with double precision.");
528
- /* No memory freeing is needed here, return directly */
529
- return 0;
530
- #endif
531
-
532
- /* Check if already running */
533
- if (running != 0) {
534
- PyErr_SetString(PyExc_Exception, "Simulation already initialized.");
535
- return 0;
536
- }
537
-
538
- /* Set all pointers used in sim_clean to null */
539
- list_update_str = NULL;
540
- vars = NULL;
541
- logs = NULL;
542
- rootsfound = NULL;
543
- y = NULL;
544
- dy_log = NULL;
545
- y_log = NULL;
546
- cvode_mem = NULL;
547
- epacing = NULL;
548
- fpacing = NULL;
549
- log_times = NULL;
550
- #if SUNDIALS_VERSION_MAJOR >= 3
551
- sundense_matrix = NULL;
552
- sundense_solver = NULL;
553
- #endif
554
- #if SUNDIALS_VERSION_MAJOR >= 6
555
- sundials_context = NULL;
556
- #endif
557
-
558
- /* Check input arguments */
559
- if (!PyArg_ParseTuple(args, "ddOOOOOOdOOdO",
560
- &tmin,
561
- &tmax,
562
- &state_in,
563
- &state_out,
564
- &inputs,
565
- &eprotocol,
566
- &fprotocol,
567
- &log_dict,
568
- &log_interval,
569
- &log_times,
570
- &root_list,
571
- &root_threshold,
572
- &benchtime)) {
573
- PyErr_SetString(PyExc_Exception, "Incorrect input arguments.");
574
- /* Nothing allocated yet, no pyobjects _created_, return directly */
575
- return 0;
576
- }
577
-
578
- /* Now officialy running :) */
579
- running = 1;
580
-
581
- /*************************************************************************
582
- From this point on, no more direct returning! Use sim_clean()
583
-
584
- To check if this list is still up to date manually search for cvode
585
- and python stuff. To find what to free() search for "alloc("
586
- Initialize all to NULL so that free() will work without errors.
587
-
588
- Notes:
589
- 1. Functions like PyList_New and PyDict_New create a new object with a
590
- refcount of 1. They pass on the ownership of this reference to the
591
- caller, IE they return the reference and it becomes the caller's
592
- responsibility to call PyDECREF
593
- 2. Functions like PyList_Append and PyDict_SetItem create a new reference
594
- to the items you pass them, IE they increase the ref count and will
595
- decrease it when they're done with it. This means that you retain
596
- ownership of your own reference to this items and will also need to
597
- call decref when you're done with them.
598
- 3. PyList_SetItem and PyTuple_SetItem are exceptions to the rule: they
599
- "steal" a reference to the item you pass into them. This means they do
600
- not increase the refcount of the item, but _do_ decrease it when they
601
- themselves are destructed.
602
- This _only_ holds for the SetItem functions, and _only_ for list and
603
- tuple.
604
- The reasonining behind this is that it's a very common scenario for
605
- populating lists and tuples.
606
- 4. PyList_GetItem and PyTuple_GetItem are exceptions to the rule: they
607
- return a "borrowed" reference to an item. This means you should never
608
- decref them!
609
- This _only_ holds for list and tuple.
610
- 5. When you return a newly created reference from a function, you pass on
611
- the ownership of that reference to the calling function. This means you
612
- don't have to call DECREF on the return value of a function.
613
- 6. References passed _into_ your function as arguments are _borrowed_:
614
- Their refcount doesn't change and you don't have to increase or decrease
615
- it. The object they point to is guaranteed to exist for as long as your
616
- function runs.
617
-
618
- Result:
619
- A. The log and protocol objects passed to this function are borrowed
620
- references: no need to change the reference count.
621
- B. The PyFloat objects that are created have refcount 1. They're added to
622
- the lists using append, which increases their refcount. So they should
623
- be decref'd after appending.
624
- C. The time float that is created has refcount 1. It's ownership is passed
625
- on to the calling function. No need to decref.
626
- D. The PyFloat objects in this list are added using PyList_SetItem which
627
- steals ownership: No need to decref.
628
- */
629
-
630
- /*
631
- * Create sundials context
632
- */
633
- #if SUNDIALS_VERSION_MAJOR >= 6
634
- flag_cvode = SUNContext_Create(NULL, &sundials_context);
635
- if (check_cvode_flag(&flag_cvode, "SUNContext_Create", 1)) {
636
- PyErr_SetString(PyExc_Exception, "Failed to create Sundials context.");
637
- return sim_clean();
638
- }
639
- #endif
640
-
641
- /*
642
- * Create state vectors
643
- */
644
-
645
- /* Create state vector */
646
- #if SUNDIALS_VERSION_MAJOR >= 6
647
- y = N_VNew_Serial(N_STATE, sundials_context);
648
- #else
649
- y = N_VNew_Serial(N_STATE);
650
- #endif
651
- if (check_cvode_flag((void*)y, "N_VNew_Serial", 0)) {
652
- PyErr_SetString(PyExc_Exception, "Failed to create state vector.");
653
- return sim_clean();
654
- }
655
-
656
- /* Create state vector copy for error handling */
657
- #if SUNDIALS_VERSION_MAJOR >= 6
658
- y_last = N_VNew_Serial(N_STATE, sundials_context);
659
- #else
660
- y_last = N_VNew_Serial(N_STATE);
661
- #endif
662
- if (check_cvode_flag((void*)y_last, "N_VNew_Serial", 0)) {
663
- PyErr_SetString(PyExc_Exception, "Failed to create last-state vector.");
664
- return sim_clean();
665
- }
666
-
667
- /* Determine if dynamic logging is being used (or if it's periodic/point-list logging) */
668
- dynamic_logging = (log_interval <= 0 && log_times == Py_None);
669
-
670
- /* Create state vector for logging */
671
- if (dynamic_logging || !USE_CVODE) {
672
- /* Dynamic logging or cvode-free mode: don't interpolate,
673
- so let y_log point to y */
674
- y_log = y;
675
- } else {
676
- /* Logging at fixed points:
677
- Keep y_log as a separate N_Vector for cvode interpolation */
678
- #if SUNDIALS_VERSION_MAJOR >= 6
679
- y_log = N_VNew_Serial(N_STATE, sundials_context);
680
- #else
681
- y_log = N_VNew_Serial(N_STATE);
682
- #endif
683
- if (check_cvode_flag((void*)y_log, "N_VNew_Serial", 0)) {
684
- PyErr_SetString(PyExc_Exception, "Failed to create logging state vector.");
685
- return sim_clean();
686
- }
687
- }
688
-
689
- /* Create derivative vector for logging */
690
- #if SUNDIALS_VERSION_MAJOR >= 6
691
- dy_log = N_VNew_Serial(N_STATE, sundials_context);
692
- #else
693
- dy_log = N_VNew_Serial(N_STATE);
694
- #endif
695
- if (check_cvode_flag((void*)dy_log, "N_VNew_Serial", 0)) {
696
- PyErr_SetString(PyExc_Exception, "Failed to create logging state derivatives vector.");
697
- return sim_clean();
698
- }
699
-
700
- /* Set calculated constants */
701
- updateConstants();
702
-
703
- /* Set initial values */
704
- if (!PyList_Check(state_in)) {
705
- PyErr_SetString(PyExc_Exception, "'state_in' must be a list.");
706
- return sim_clean();
707
- }
708
- for(i=0; i<N_STATE; i++) {
709
- flt = PyList_GetItem(state_in, i); /* Don't decref! */
710
- if (!PyFloat_Check(flt)) {
711
- char errstr[200];
712
- sprintf(errstr, "Item %d in state vector is not a float.", i);
713
- PyErr_SetString(PyExc_Exception, errstr);
714
- return sim_clean();
715
- }
716
- NV_Ith_S(y, i) = PyFloat_AsDouble(flt);
717
- NV_Ith_S(y_last, i) = NV_Ith_S(y, i);
718
- }
719
-
720
- /* Periodic or point-list logging? Then set init state in y_log as well */
721
- #if USE_CVODE
722
- if (!dynamic_logging) {
723
- for(i=0; i<N_STATE; i++) {
724
- NV_Ith_S(y_log, i) = NV_Ith_S(y, i);
725
- }
726
- }
727
- #endif
728
- /* In cvode-free mode, y_log points to y, so no need */
729
-
730
- /* Root finding list of integers (only contains 1 int...) */
731
- rootsfound = (int*)malloc(sizeof(int)*1);
732
-
733
- /* Reset evaluation count */
734
- engine_evaluations = 0;
735
-
736
- /* Reset step count */
737
- engine_steps = 0;
738
-
739
- /* Zero step tracking */
740
- zero_step_count = 0;
741
-
742
- /* Check output list */
743
- if (!PyList_Check(state_out)) {
744
- PyErr_SetString(PyExc_Exception, "'state_out' must be a list.");
745
- return sim_clean();
746
- }
747
-
748
- /* Check for loss-of-precision issue in periodic logging */
749
- if (log_interval > 0) {
750
- if (tmax + log_interval == tmax) {
751
- PyErr_SetString(PyExc_Exception, "Log interval is too small compared to tmax; issue with numerical precision: float(tmax + log_interval) = float(tmax).");
752
- return sim_clean();
753
- }
754
- }
755
-
756
- /* Set up logging */
757
- log_inter = 0;
758
- log_bound = 0;
759
- n_vars = PyDict_Size(log_dict);
760
- logs = (PyObject**)malloc(sizeof(PyObject*)*n_vars);
761
- vars = (realtype**)malloc(sizeof(realtype*)*n_vars);
762
- i = 0;
763
-
764
- /* Note: The variable names are all ascii compatible
765
- In Python2, they are stored in logs as either unicode or bytes
766
- In Python3, they are stored exclusively as unicode
767
- However, in Python2 b'name' matches u'name' so this is ok (in Python it
768
- does not, but we always use unicode so it's ok).
769
- The strategy here will be to convert these C-strings to unicode
770
- inside log_add, before the comparison. */
771
-
772
- /* Check states */
773
- <?
774
- for var in model.states():
775
- print(tab + 'i += log_add(log_dict, logs, vars, i, "' + var.qname() + '", &NV_Ith_S(y_log, ' + str(var.indice()) + '));')
776
- ?>
777
-
778
- /* Check derivatives */
779
- j = i;
780
- <?
781
- for var in model.states():
782
- print(tab + 'i += log_add(log_dict, logs, vars, i, "dot(' + var.qname() + ')", &NV_Ith_S(dy_log, ' + str(var.indice()) + '));')
783
- ?>
784
- log_deriv = (i > j);
785
-
786
- /* Check bound variables */
787
- j = i;
788
- <?
789
- for var, internal in bound_variables.items():
790
- print(tab + 'i += log_add(log_dict, logs, vars, i, "' + var.qname() + '", &' + v(var) + ');')
791
- ?>
792
- log_bound = (i > j);
793
-
794
- /* Remaining variables will require an extra rhs() call to evaluate their
795
- values at every log point */
796
- j = i;
797
- <?
798
- for var in model.variables(deep=True, state=False, bound=False, const=False):
799
- print(tab + 'i += log_add(log_dict, logs, vars, i, "' + var.qname() + '", &' + v(var) + ');')
800
- ?>
801
- log_inter = (i > j);
802
-
803
- /* Check if log contained extra variables */
804
- if (i != n_vars) {
805
- PyErr_SetString(PyExc_Exception, "Unknown variables found in logging dictionary.");
806
- return sim_clean();
807
- }
808
-
809
- /* Set up event-based pacing */
810
- if (eprotocol != Py_None) {
811
- epacing = ESys_Create(&flag_epacing);
812
- if (flag_epacing != ESys_OK) { ESys_SetPyErr(flag_epacing); return sim_clean(); }
813
- flag_epacing = ESys_Populate(epacing, eprotocol);
814
- if (flag_epacing != ESys_OK) { ESys_SetPyErr(flag_epacing); return sim_clean(); }
815
- flag_epacing = ESys_AdvanceTime(epacing, tmin);
816
- if (flag_epacing != ESys_OK) { ESys_SetPyErr(flag_epacing); return sim_clean(); }
817
- tnext = ESys_GetNextTime(epacing, &flag_epacing);
818
- engine_pace = ESys_GetLevel(epacing, &flag_epacing);
819
- tnext = (tnext < tmax) ? tnext : tmax;
820
- } else {
821
- tnext = tmax;
822
- }
823
-
824
- /* Set up fixed-form pacing */
825
- if (eprotocol == Py_None && fprotocol != Py_None) {
826
- /* Check 'protocol' is tuple (times, values) */
827
- if (!PyTuple_Check(fprotocol)) {
828
- PyErr_SetString(PyExc_Exception, "Fixed-form pacing protocol should be tuple or None.");
829
- return sim_clean();
830
- }
831
- if (PyTuple_Size(fprotocol) != 2) {
832
- PyErr_SetString(PyExc_Exception, "Fixed-form pacing protocol tuple should have size 2.");
833
- return sim_clean();
834
- }
835
- /* Create fixed-form pacing object and populate */
836
- fpacing = FSys_Create(&flag_fpacing);
837
- if (flag_fpacing != FSys_OK) { FSys_SetPyErr(flag_fpacing); return sim_clean(); }
838
- flag_fpacing = FSys_Populate(fpacing,
839
- PyTuple_GetItem(fprotocol, 0), /* Borrowed, no decref */
840
- PyTuple_GetItem(fprotocol, 1));
841
- if (flag_fpacing != FSys_OK) { FSys_SetPyErr(flag_fpacing); return sim_clean(); }
842
- }
843
-
844
- /* Set simulation starting time */
845
- engine_time = tmin;
846
-
847
- /* Check dt_max and dt_min */
848
- if (dt_max < 0) dt_max = 0.0;
849
- if (dt_min < 0) dt_min = 0.0;
850
-
851
- /* Create solver
852
- * Using Backward differentiation and Newton iteration */
853
- #if USE_CVODE > 0
854
- #if SUNDIALS_VERSION_MAJOR >= 6
855
- cvode_mem = CVodeCreate(CV_BDF, sundials_context);
856
- #elif SUNDIALS_VERSION_MAJOR >= 4
857
- cvode_mem = CVodeCreate(CV_BDF);
858
- #else
859
- cvode_mem = CVodeCreate(CV_BDF, CV_NEWTON);
860
- #endif
861
- if (check_cvode_flag((void*)cvode_mem, "CVodeCreate", 0)) return sim_clean();
862
-
863
- /* Set error and warning-message handler */
864
- flag_cvode = CVodeSetErrHandlerFn(cvode_mem, ErrorHandler, NULL);
865
- if (check_cvode_flag(&flag_cvode, "CVodeInit", 1)) return sim_clean();
866
-
867
- /* Initialise solver memory, specify the rhs */
868
- flag_cvode = CVodeInit(cvode_mem, rhs, engine_time, y);
869
- if (check_cvode_flag(&flag_cvode, "CVodeInit", 1)) return sim_clean();
870
-
871
- /* Set absolute and relative tolerances */
872
- flag_cvode = CVodeSStolerances(cvode_mem, RCONST(rel_tol), RCONST(abs_tol));
873
- if (check_cvode_flag(&flag_cvode, "CVodeSStolerances", 1)) return sim_clean();
874
-
875
- /* Set a maximum step size (or 0.0 for none) */
876
-
877
- flag_cvode = CVodeSetMaxStep(cvode_mem, dt_max);
878
- if (check_cvode_flag(&flag_cvode, "CVodeSetmaxStep", 1)) return sim_clean();
879
-
880
- /* Set a minimum step size (or 0.0 for none) */
881
- flag_cvode = CVodeSetMinStep(cvode_mem, dt_min);
882
- if (check_cvode_flag(&flag_cvode, "CVodeSetminStep", 1)) return sim_clean();
883
-
884
- #if SUNDIALS_VERSION_MAJOR >= 6
885
- /* Create dense matrix for use in linear solves */
886
- sundense_matrix = SUNDenseMatrix(N_STATE, N_STATE, sundials_context);
887
- if (check_cvode_flag((void *)sundense_matrix, "SUNDenseMatrix", 0)) return sim_clean();
888
-
889
- /* Create dense linear solver object with matrix */
890
- sundense_solver = SUNLinSol_Dense(y, sundense_matrix, sundials_context);
891
- if (check_cvode_flag((void *)sundense_solver, "SUNLinSol_Dense", 0)) return sim_clean();
892
-
893
- /* Attach the matrix and solver to cvode */
894
- flag_cvode = CVodeSetLinearSolver(cvode_mem, sundense_solver, sundense_matrix);
895
- if (check_cvode_flag(&flag_cvode, "CVodeSetLinearSolver", 1)) return sim_clean();
896
- #elif SUNDIALS_VERSION_MAJOR >= 4
897
- /* Create dense matrix for use in linear solves */
898
- sundense_matrix = SUNDenseMatrix(N_STATE, N_STATE);
899
- if (check_cvode_flag((void *)sundense_matrix, "SUNDenseMatrix", 0)) return sim_clean();
900
-
901
- /* Create dense linear solver object with matrix */
902
- sundense_solver = SUNLinSol_Dense(y, sundense_matrix);
903
- if (check_cvode_flag((void *)sundense_solver, "SUNLinSol_Dense", 0)) return sim_clean();
904
-
905
- /* Attach the matrix and solver to cvode */
906
- flag_cvode = CVodeSetLinearSolver(cvode_mem, sundense_solver, sundense_matrix);
907
- if (check_cvode_flag(&flag_cvode, "CVodeSetLinearSolver", 1)) return sim_clean();
908
- #elif SUNDIALS_VERSION_MAJOR >= 3
909
- /* Create dense matrix for use in linear solves */
910
- sundense_matrix = SUNDenseMatrix(N_STATE, N_STATE);
911
- if(check_cvode_flag((void *)sundense_matrix, "SUNDenseMatrix", 0)) return sim_clean();
912
-
913
- /* Create dense linear solver object with matrix */
914
- sundense_solver = SUNDenseLinearSolver(y, sundense_matrix);
915
- if(check_cvode_flag((void *)sundense_solver, "SUNDenseLinearSolver", 0)) return sim_clean();
916
-
917
- /* Attach the matrix and solver to cvode */
918
- flag_cvode = CVDlsSetLinearSolver(cvode_mem, sundense_solver, sundense_matrix);
919
- if(check_cvode_flag(&flag_cvode, "CVDlsSetLinearSolver", 1)) return sim_clean();
920
- #else
921
- /* Create dense matrix for use in linear solves */
922
- flag_cvode = CVDense(cvode_mem, N_STATE);
923
- if (check_cvode_flag(&flag_cvode, "CVDense", 1)) return sim_clean();
924
- #endif
925
- #endif
926
-
927
- /* Benchmarking? Then set engine_realtime to 0.0 */
928
- if (benchtime != Py_None) {
929
- /* Store initial time as 0 */
930
- engine_realtime = 0.0;
931
- /* Tell sim_step to set engine_starttime */
932
- engine_starttime = -1;
933
- }
934
-
935
- /* Set string for updating lists/arrays using Python interface. */
936
- list_update_str = PyUnicode_FromString("append");
937
-
938
- /* Set logging points */
939
- if (log_interval > 0) {
940
-
941
- /* Periodic logging */
942
- ilog = 0;
943
- tlog = tmin;
944
-
945
- } else if (log_times != Py_None) {
946
-
947
- /* Point-list logging */
948
-
949
- /* Check the log_times list */
950
- if (!PyList_Check(log_times)) {
951
- PyErr_SetString(PyExc_Exception, "'log_times' must be a list.");
952
- return sim_clean();
953
- }
954
-
955
- /* Read next log point off the list */
956
- ilog = 0;
957
- tlog = engine_time - 1;
958
- while(ilog < PyList_Size(log_times) && tlog < engine_time) {
959
- flt = PyList_GetItem(log_times, ilog); /* Borrowed */
960
- if (!PyFloat_Check(flt)) {
961
- PyErr_SetString(PyExc_Exception, "Entries in 'log_times' must be floats.");
962
- return sim_clean();
963
- }
964
- tlog = PyFloat_AsDouble(flt);
965
- ilog++;
966
- flt = NULL;
967
- }
968
-
969
- /* No points beyond engine_time? Then don't log any future points. */
970
- if(tlog < engine_time) {
971
- tlog = tmax + 1;
972
- }
973
-
974
- } else {
975
-
976
- /*
977
- * Dynamic logging
978
- *
979
- * Log the first entry, but only if not appending to an existing log.
980
- * This prevents points from appearing twice when a simulation with
981
- * dynamic logging is stopped and started.
982
- */
983
-
984
- /* Check if the log is empty */
985
- log_first_point = 1;
986
- pos = 0;
987
- if(PyDict_Next(log_dict, &pos, &key, &value)) {
988
- /* Items found in dict, randomly selected list now in "value" */
989
- /* Both key and value are borrowed references, no need to decref */
990
- log_first_point = (PyObject_Size(value) <= 0);
991
- }
992
-
993
- /* If so, log the first point! */
994
- if (log_first_point) {
995
- rhs(engine_time, y, dy_log, 0);
996
- /* At this point, we have y(t), inter(t) and dy(t) */
997
- /* We've also loaded time(t) and pace(t) */
998
- for(i=0; i<n_vars; i++) {
999
- flt = PyFloat_FromDouble(*vars[i]);
1000
- ret = PyObject_CallMethodObjArgs(logs[i], list_update_str, flt, NULL);
1001
- Py_DECREF(flt);
1002
- Py_XDECREF(ret);
1003
- if (ret == NULL) {
1004
- flt = NULL;
1005
- PyErr_SetString(PyExc_Exception, "Call to append() failed on logging list.");
1006
- return sim_clean();
1007
- }
1008
- }
1009
- flt = NULL;
1010
- ret = NULL;
1011
- }
1012
- }
1013
-
1014
- /* Root finding enabled? (cvode-mode only) */
1015
- #if USE_CVODE
1016
- if (PySequence_Check(root_list)) {
1017
- /* Set threshold */
1018
- rootfinding_threshold = root_threshold;
1019
- /* Initialize root function with 1 component */
1020
- flag_cvode = CVodeRootInit(cvode_mem, 1, root_finding);
1021
- if (check_cvode_flag(&flag_cvode, "CVodeRootInit", 1)) return sim_clean();
1022
- }
1023
- #endif
1024
-
1025
- /* Done! */
1026
- Py_RETURN_NONE;
1027
- }
1028
-
1029
- /*
1030
- * Takes the next steps in a simulation run
1031
- */
1032
- static PyObject*
1033
- sim_step(PyObject *self, PyObject *args)
1034
- {
1035
- ESys_Flag flag_epacing;
1036
- int i;
1037
- int steps_taken = 0; /* Number of integration steps taken in this call */
1038
- int flag_cvode; /* CVode flag */
1039
- int flag_root; /* Root finding flag */
1040
- int flag_reinit = 0; /* Set if CVODE needs to be reset during a simulation step */
1041
- PyObject *flt, *ret;
1042
-
1043
- /*
1044
- * Benchmarking? Then make sure start time is set.
1045
- * This is handled here instead of in sim_init so it only includes time
1046
- * taken performing steps, not time initialising memory etc.
1047
- */
1048
- if (benchtime != Py_None && engine_starttime < 0) {
1049
- flt = PyObject_CallFunction(benchtime, "");
1050
- if (!PyFloat_Check(flt)) {
1051
- Py_XDECREF(flt); flt = NULL;
1052
- PyErr_SetString(PyExc_Exception, "Call to benchmark time function didn't return float.");
1053
- return sim_clean();
1054
- }
1055
- engine_starttime = PyFloat_AsDouble(flt);
1056
- Py_DECREF(flt); flt = NULL;
1057
- }
1058
-
1059
- /* Go! */
1060
- while(1) {
1061
-
1062
- /* Back-up current y (no allocation, this is fast) */
1063
- for(i=0; i<N_STATE; i++) {
1064
- NV_Ith_S(y_last, i) = NV_Ith_S(y, i);
1065
- }
1066
-
1067
- /* Store engine time before step */
1068
- engine_time_last = engine_time;
1069
-
1070
- #if USE_CVODE
1071
-
1072
- /* Take a single ODE step */
1073
- flag_cvode = CVode(cvode_mem, tnext, y, &engine_time, CV_ONE_STEP);
1074
-
1075
- /* Check for errors */
1076
- if (check_cvode_flag(&flag_cvode, "CVode", 1)) {
1077
- /* Something went wrong... Set outputs and return */
1078
- for(i=0; i<N_STATE; i++) {
1079
- PyList_SetItem(state_out, i, PyFloat_FromDouble(NV_Ith_S(y_last, i)));
1080
- /* PyList_SetItem steals a reference: no need to decref the double! */
1081
- }
1082
- PyList_SetItem(inputs, 0, PyFloat_FromDouble(engine_time));
1083
- PyList_SetItem(inputs, 1, PyFloat_FromDouble(engine_pace));
1084
- PyList_SetItem(inputs, 2, PyFloat_FromDouble(engine_realtime));
1085
- PyList_SetItem(inputs, 3, PyFloat_FromDouble(engine_evaluations));
1086
- return sim_clean();
1087
- }
1088
-
1089
- #else
1090
-
1091
- /* Just jump to next event */
1092
- /* Note 1: To stay compatible with cvode-mode, don't jump to the
1093
- next log time (if tlog < tnext) */
1094
- /* Note 2: tnext can be infinity, so don't always jump there. */
1095
- engine_time = (tmax > tnext) ? tnext : tmax;
1096
- flag_cvode = CV_SUCCESS;
1097
-
1098
- #endif
1099
-
1100
- /* Check if progress is being made */
1101
- if(engine_time == engine_time_last) {
1102
- if(++zero_step_count >= max_zero_step_count) {
1103
- char errstr[200];
1104
- sprintf(errstr, "ZERO_STEP %f", engine_time);
1105
- PyErr_SetString(PyExc_Exception, errstr);
1106
- return sim_clean();
1107
- }
1108
- } else {
1109
- /* Only count consecutive zero steps! */
1110
- zero_step_count = 0;
1111
- }
1112
-
1113
- /* Update step count */
1114
- engine_steps++;
1115
-
1116
- /* If we got to this point without errors... */
1117
- if ((flag_cvode == CV_SUCCESS) || (flag_cvode == CV_ROOT_RETURN)) {
1118
-
1119
- /* Next event time exceeded? (Can't happen in cvode-free mode) */
1120
- #if USE_CVODE
1121
- if (engine_time > tnext) {
1122
-
1123
- /* Go back to engine_time=tnext */
1124
- flag_cvode = CVodeGetDky(cvode_mem, tnext, 0, y);
1125
- if (check_cvode_flag(&flag_cvode, "CVodeGetDky", 1)) return sim_clean();
1126
- engine_time = tnext;
1127
- /* Require reinit (after logging) */
1128
- flag_reinit = 1;
1129
-
1130
- } else if (flag_cvode == CV_ROOT_RETURN) {
1131
-
1132
- /* Store found roots */
1133
- flag_root = CVodeGetRootInfo(cvode_mem, rootsfound);
1134
- if (check_cvode_flag(&flag_root, "CVodeGetRootInfo", 1)) return sim_clean();
1135
- flt = PyTuple_New(2);
1136
- PyTuple_SetItem(flt, 0, PyFloat_FromDouble(engine_time)); /* Steals reference, so this is ok */
1137
- PyTuple_SetItem(flt, 1, PyLong_FromLong(rootsfound[0]));
1138
- ret = PyObject_CallMethodObjArgs(root_list, list_update_str, flt, NULL);
1139
- Py_DECREF(flt); flt = NULL;
1140
- Py_XDECREF(ret);
1141
- if (ret == NULL) {
1142
- PyErr_SetString(PyExc_Exception, "Call to append() failed on root finding list.");
1143
- return sim_clean();
1144
- }
1145
- ret = NULL;
1146
- }
1147
- #endif
1148
-
1149
- /* Periodic logging or point-list logging */
1150
- if (!dynamic_logging && engine_time > tlog) {
1151
- /* Note: For periodic logging, the condition should be
1152
- `time > tlog` so that we log half-open intervals (i.e. the
1153
- final point should never be included). */
1154
-
1155
- /* Benchmarking? Then set engine_realtime */
1156
- if (benchtime != Py_None) {
1157
- flt = PyObject_CallFunction(benchtime, "");
1158
- if (!PyFloat_Check(flt)) {
1159
- Py_XDECREF(flt); flt = NULL;
1160
- PyErr_SetString(PyExc_Exception, "Call to benchmark time function didn't return float.");
1161
- return sim_clean();
1162
- }
1163
- engine_realtime = PyFloat_AsDouble(flt) - engine_starttime;
1164
- Py_DECREF(flt); flt = NULL;
1165
- /* Update any variables bound to realtime */
1166
- update_realtime_bindings(engine_time, y, dy_log, 0);
1167
- }
1168
-
1169
- /* Log points */
1170
- while (engine_time > tlog) {
1171
-
1172
- /* Get interpolated y(tlog) */
1173
- #if USE_CVODE
1174
- flag_cvode = CVodeGetDky(cvode_mem, tlog, 0, y_log);
1175
- if (check_cvode_flag(&flag_cvode, "CVodeGetDky", 1)) return sim_clean();
1176
- #endif
1177
- /* If cvode-free mode, the state can't change so we don't
1178
- need to do anything here */
1179
-
1180
- /* Calculate intermediate variables & derivatives */
1181
- rhs(tlog, y_log, dy_log, 0);
1182
-
1183
- /* Write to log */
1184
- for(i=0; i<n_vars; i++) {
1185
- flt = PyFloat_FromDouble(*vars[i]);
1186
- ret = PyObject_CallMethodObjArgs(logs[i], list_update_str, flt, NULL);
1187
- Py_DECREF(flt);
1188
- Py_XDECREF(ret);
1189
- if (ret == NULL) {
1190
- flt = NULL;
1191
- PyErr_SetString(PyExc_Exception, "Call to append() failed on logging list.");
1192
- return sim_clean();
1193
- }
1194
- }
1195
- ret = flt = NULL;
1196
-
1197
- /* Get next logging point */
1198
- if (log_interval > 0) {
1199
- /* Periodic logging */
1200
- ilog++;
1201
- tlog = tmin + (double)ilog * log_interval;
1202
- if (ilog == 0) {
1203
- /* Unsigned int wraps around instead of overflowing, becomes zero again */
1204
- PyErr_SetString(PyExc_Exception, "Overflow in logged step count: Simulation too long!");
1205
- return sim_clean();
1206
- }
1207
- } else {
1208
- /* Point-list logging */
1209
- /* Read next log point off the list */
1210
- if (ilog < PyList_Size(log_times)) {
1211
- flt = PyList_GetItem(log_times, ilog); /* Borrowed */
1212
- if (!PyFloat_Check(flt)) {
1213
- PyErr_SetString(PyExc_Exception, "Entries in 'log_times' must be floats.");
1214
- return sim_clean();
1215
- }
1216
- tlog = PyFloat_AsDouble(flt);
1217
- ilog++;
1218
- flt = NULL;
1219
- } else {
1220
- tlog = tmax + 1;
1221
- }
1222
- }
1223
- }
1224
- }
1225
-
1226
- /* Event-based pacing */
1227
-
1228
- /* At this point we have logged everything _before_ engine_time, so
1229
- it's safe to update the pacing mechanism. */
1230
- if (epacing != NULL) {
1231
- flag_epacing = ESys_AdvanceTime(epacing, engine_time);
1232
- if (flag_epacing != ESys_OK) { ESys_SetPyErr(flag_epacing); return sim_clean(); }
1233
- tnext = ESys_GetNextTime(epacing, NULL);
1234
- engine_pace = ESys_GetLevel(epacing, NULL);
1235
- tnext = (tnext < tmax) ? tnext : tmax;
1236
- }
1237
-
1238
- /* Dynamic logging: Log every visited point */
1239
- if (dynamic_logging) {
1240
-
1241
- /* Ensure the logged values are correct for the new time t */
1242
- if (log_deriv || log_inter) {
1243
- /* If logging derivatives or intermediaries, calculate the
1244
- values for the current time. */
1245
- rhs(engine_time, y, dy_log, 0);
1246
- } else if (log_bound) {
1247
- /* Logging bounds but not derivs or inters: No need to run
1248
- full rhs, just update bound variables */
1249
- update_bindings(engine_time, y, dy_log, 0);
1250
- }
1251
-
1252
- /* Benchmarking? Then set engine_realtime */
1253
- if (benchtime != Py_None) {
1254
- flt = PyObject_CallFunction(benchtime, "");
1255
- if (!PyFloat_Check(flt)) {
1256
- Py_XDECREF(flt); flt = NULL;
1257
- PyErr_SetString(PyExc_Exception, "Call to benchmark time function didn't return float.");
1258
- return sim_clean();
1259
- }
1260
- engine_realtime = PyFloat_AsDouble(flt) - engine_starttime;
1261
- Py_DECREF(flt); flt = NULL;
1262
- /* Update any variables bound to realtime */
1263
- update_realtime_bindings(engine_time, y, dy_log, 0);
1264
- }
1265
-
1266
- /* Write to log */
1267
- for(i=0; i<n_vars; i++) {
1268
- flt = PyFloat_FromDouble(*vars[i]);
1269
- ret = PyObject_CallMethodObjArgs(logs[i], list_update_str, flt, NULL);
1270
- Py_DECREF(flt); flt = NULL;
1271
- Py_XDECREF(ret);
1272
- if (ret == NULL) {
1273
- PyErr_SetString(PyExc_Exception, "Call to append() failed on logging list.");
1274
- return sim_clean();
1275
- }
1276
- ret = NULL;
1277
- }
1278
-
1279
- }
1280
-
1281
- /* Reinitialize if needed (cvode-mode only) */
1282
- #if USE_CVODE
1283
- if (flag_reinit) {
1284
- flag_reinit = 0;
1285
- /* Re-init */
1286
- flag_cvode = CVodeReInit(cvode_mem, engine_time, y);
1287
- if (check_cvode_flag(&flag_cvode, "CVodeReInit", 1)) return sim_clean();
1288
- }
1289
- #endif
1290
- }
1291
-
1292
- /* Check if we're finished */
1293
- if (ESys_eq(engine_time, tmax)) engine_time = tmax;
1294
- if (engine_time >= tmax) break;
1295
-
1296
- /* Perform any Python signal handling */
1297
- if (PyErr_CheckSignals() != 0) {
1298
- /* Exception (e.g. timeout or keyboard interrupt) occurred?
1299
- Then cancel everything! */
1300
- return sim_clean();
1301
- }
1302
-
1303
- /* Report back to python after every x steps */
1304
- steps_taken++;
1305
- if (steps_taken >= 100) {
1306
- return PyFloat_FromDouble(engine_time);
1307
- }
1308
- }
1309
-
1310
- /* Set final state */
1311
- for(i=0; i<N_STATE; i++) {
1312
- PyList_SetItem(state_out, i, PyFloat_FromDouble(NV_Ith_S(y, i)));
1313
- /* PyList_SetItem steals a reference: no need to decref the double! */
1314
- }
1315
-
1316
- /* Set state of inputs */
1317
- PyList_SetItem(inputs, 0, PyFloat_FromDouble(engine_time));
1318
- PyList_SetItem(inputs, 1, PyFloat_FromDouble(engine_pace));
1319
- PyList_SetItem(inputs, 2, PyFloat_FromDouble(engine_realtime));
1320
- PyList_SetItem(inputs, 3, PyFloat_FromDouble(engine_evaluations));
1321
-
1322
- sim_clean(); /* Ignore return value */
1323
- return PyFloat_FromDouble(engine_time);
1324
- }
1325
-
1326
- /*
1327
- * Evaluates the state derivatives at the given state
1328
- */
1329
- static PyObject*
1330
- sim_eval_derivatives(PyObject *self, PyObject *args)
1331
- {
1332
- /* Declare variables here for C89 compatibility */
1333
- int i;
1334
- int success;
1335
- int iState;
1336
- int flag_cvode;
1337
- double time_in;
1338
- double pace_in;
1339
- char errstr[200];
1340
- PyObject *state;
1341
- PyObject *deriv;
1342
- PyObject *flt;
1343
- N_Vector y;
1344
- N_Vector dy;
1345
- #if SUNDIALS_VERSION_MAJOR >= 6
1346
- SUNContext sundials_context;
1347
- #endif
1348
-
1349
- /* Start */
1350
- success = 0;
1351
-
1352
- /* Check input arguments */
1353
- if (!PyArg_ParseTuple(args, "OOdd", &state, &deriv, &time_in, &pace_in)) {
1354
- PyErr_SetString(PyExc_Exception, "Expecting sequence arguments 'y' and 'dy' followed by floats 'time' and 'pace'.");
1355
- /* Nothing allocated yet, no pyobjects _created_, return directly */
1356
- return 0;
1357
- }
1358
- if (!PySequence_Check(state)) {
1359
- PyErr_SetString(PyExc_Exception, "First argument must support the sequence interface.");
1360
- return 0;
1361
- }
1362
- if (!PySequence_Check(deriv)) {
1363
- PyErr_SetString(PyExc_Exception, "Second argument must support the sequence interface.");
1364
- return 0;
1365
- }
1366
-
1367
- /* From this point on, no more direct returning: use goto error */
1368
- y = NULL; /* A cvode SERIAL vector */
1369
- dy = NULL; /* A cvode SERIAL vector */
1370
-
1371
- /* Create sundials context */
1372
- #if SUNDIALS_VERSION_MAJOR >= 6
1373
- flag_cvode = SUNContext_Create(NULL, &sundials_context);
1374
- if (check_cvode_flag(&flag_cvode, "SUNContext_Create", 1)) {
1375
- PyErr_SetString(PyExc_Exception, "Failed to create Sundials context.");
1376
- goto error;
1377
- }
1378
- #endif
1379
-
1380
- /* Temporary object: decref before re-using for another var :) */
1381
- /* (Unless you get them using PyList_GetItem...) */
1382
- flt = NULL; /* PyFloat */
1383
-
1384
- /* Create state vectors */
1385
- #if SUNDIALS_VERSION_MAJOR >= 6
1386
- y = N_VNew_Serial(N_STATE, sundials_context);
1387
- #else
1388
- y = N_VNew_Serial(N_STATE);
1389
- #endif
1390
- if (check_cvode_flag((void*)y, "N_VNew_Serial", 0)) {
1391
- PyErr_SetString(PyExc_Exception, "Failed to create state vector.");
1392
- goto error;
1393
- }
1394
- #if SUNDIALS_VERSION_MAJOR >= 6
1395
- dy = N_VNew_Serial(N_STATE, sundials_context);
1396
- #else
1397
- dy = N_VNew_Serial(N_STATE);
1398
- #endif
1399
- if (check_cvode_flag((void*)dy, "N_VNew_Serial", 0)) {
1400
- PyErr_SetString(PyExc_Exception, "Failed to create state derivatives vector.");
1401
- goto error;
1402
- }
1403
-
1404
- /* Set calculated constants */
1405
- updateConstants();
1406
-
1407
- /* Set initial values */
1408
- for (iState = 0; iState < N_STATE; iState++) {
1409
- flt = PySequence_GetItem(state, iState); /* Remember to decref! */
1410
- if (!PyFloat_Check(flt)) {
1411
- Py_XDECREF(flt); flt = NULL;
1412
- sprintf(errstr, "Item %d in state vector is not a float.", iState);
1413
- PyErr_SetString(PyExc_Exception, errstr);
1414
- goto error;
1415
- }
1416
- NV_Ith_S(y, iState) = PyFloat_AsDouble(flt);
1417
- Py_DECREF(flt);
1418
- }
1419
- flt = NULL;
1420
-
1421
- /* Set simulation time and pacing variable */
1422
- engine_time = time_in;
1423
- engine_pace = pace_in;
1424
-
1425
- /* Evaluate derivatives */
1426
- rhs(engine_time, y, dy, 0);
1427
-
1428
- /* Set output values */
1429
- for(i=0; i<N_STATE; i++) {
1430
- flt = PyFloat_FromDouble(NV_Ith_S(dy, i));
1431
- if (flt == NULL) {
1432
- PyErr_SetString(PyExc_Exception, "Unable to create float.");
1433
- goto error;
1434
- }
1435
- PySequence_SetItem(deriv, i, flt);
1436
- Py_DECREF(flt);
1437
- }
1438
- flt = NULL;
1439
-
1440
- /* Finished succesfully, free memory and return */
1441
- success = 1;
1442
- error:
1443
- /* Free CVODE space */
1444
- N_VDestroy_Serial(y);
1445
- N_VDestroy_Serial(dy);
1446
- #if SUNDIALS_VERSION_MAJOR >= 6
1447
- SUNContext_Free(&sundials_context);
1448
- #endif
1449
-
1450
- /* Return */
1451
- if (success) {
1452
- Py_RETURN_NONE;
1453
- } else {
1454
- return 0;
1455
- }
1456
- }
1457
-
1458
- /*
1459
- * Alters the value of a (literal) constant
1460
- */
1461
- static PyObject*
1462
- sim_set_constant(PyObject *self, PyObject *args)
1463
- {
1464
- double value;
1465
- char* name;
1466
- char errstr[200];
1467
-
1468
- /* Check input arguments */
1469
- if (!PyArg_ParseTuple(args, "sd", &name, &value)) {
1470
- PyErr_SetString(PyExc_Exception, "Expected input arguments: name (str), value (Float).");
1471
- /* Nothing allocated yet, no pyobjects _created_, return directly */
1472
- return 0;
1473
- }
1474
-
1475
- <?
1476
- for var in model.variables(const=True, deep=True):
1477
- if var.is_literal():
1478
- print(tab + 'if(strcmp("' + var.qname() + '", name) == 0) {')
1479
- print(tab + tab + v(var) + ' = value;')
1480
- print(tab + tab + 'Py_RETURN_NONE;')
1481
- print(tab + '}')
1482
- ?>
1483
- sprintf(errstr, "Constant not found: <%s>", name);
1484
- PyErr_SetString(PyExc_Exception, errstr);
1485
- return 0;
1486
- }
1487
-
1488
- /*
1489
- * Returns the number of steps taken in the last simulation
1490
- */
1491
- static PyObject*
1492
- sim_steps(PyObject *self, PyObject *args)
1493
- {
1494
- return PyLong_FromLong(engine_steps);
1495
- }
1496
-
1497
- /*
1498
- * Returns the number of rhs evaluations performed during the last simulation
1499
- */
1500
- static PyObject*
1501
- sim_evals(PyObject *self, PyObject *args)
1502
- {
1503
- return PyLong_FromLong(engine_evaluations);
1504
- }
1505
-
1506
- /*
1507
- * Methods in this module
1508
- */
1509
- static PyMethodDef SimMethods[] = {
1510
- {"sim_init", sim_init, METH_VARARGS, "Initialize the simulation."},
1511
- {"sim_step", sim_step, METH_VARARGS, "Perform the next step in the simulation."},
1512
- {"sim_clean", py_sim_clean, METH_VARARGS, "Clean up after an aborted simulation."},
1513
- {"eval_derivatives", sim_eval_derivatives, METH_VARARGS, "Evaluate the state derivatives."},
1514
- {"set_constant", sim_set_constant, METH_VARARGS, "Change a (literal) constant."},
1515
- {"set_tolerance", sim_set_tolerance, METH_VARARGS, "Set the absolute and relative solver tolerance."},
1516
- {"set_max_step_size", sim_set_max_step_size, METH_VARARGS, "Set the maximum solver step size (0 for none)."},
1517
- {"set_min_step_size", sim_set_min_step_size, METH_VARARGS, "Set the minimum solver step size (0 for none)."},
1518
- {"number_of_steps", sim_steps, METH_VARARGS, "Returns the number of steps taken in the last simulation."},
1519
- {"number_of_evaluations", sim_evals, METH_VARARGS, "Returns the number of rhs evaluations performed during the last simulation."},
1520
- {NULL},
1521
- };
1522
-
1523
- /*
1524
- * Module definition
1525
- */
1526
- #if PY_MAJOR_VERSION >= 3
1527
-
1528
- static struct PyModuleDef moduledef = {
1529
- PyModuleDef_HEAD_INIT,
1530
- "<?= module_name ?>", /* m_name */
1531
- "Generated CVODESim module",/* m_doc */
1532
- -1, /* m_size */
1533
- SimMethods, /* m_methods */
1534
- NULL, /* m_reload */
1535
- NULL, /* m_traverse */
1536
- NULL, /* m_clear */
1537
- NULL, /* m_free */
1538
- };
1539
-
1540
- PyMODINIT_FUNC PyInit_<?=module_name?>(void) {
1541
- return PyModule_Create(&moduledef);
1542
- }
1543
-
1544
- #else
1545
-
1546
- PyMODINIT_FUNC
1547
- init<?=module_name?>(void) {
1548
- (void) Py_InitModule("<?= module_name ?>", SimMethods);
1549
- }
1550
-
1551
- #endif