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.
- myokit/__init__.py +9 -36
- myokit/__main__.py +76 -142
- myokit/_aux.py +62 -16
- myokit/_bin/example.mmt +1 -2
- myokit/_bin/install-win/menu.json +7 -7
- myokit/_config.py +22 -31
- myokit/_datablock.py +30 -74
- myokit/_datalog.py +49 -72
- myokit/_err.py +25 -24
- myokit/_expressions.py +50 -68
- myokit/_io.py +15 -27
- myokit/_model_api.py +453 -249
- myokit/_myokit_version.py +1 -5
- myokit/_parsing.py +38 -44
- myokit/_progress.py +5 -8
- myokit/_protocol.py +99 -9
- myokit/_sim/__init__.py +7 -24
- myokit/_sim/cable.c +6 -8
- myokit/_sim/cable.py +6 -8
- myokit/_sim/cmodel.h +125 -70
- myokit/_sim/cmodel.py +12 -14
- myokit/_sim/compiler.py +1 -4
- myokit/_sim/cvodessim.c +196 -118
- myokit/_sim/cvodessim.py +130 -103
- myokit/_sim/differential.hpp +4 -4
- myokit/_sim/fiber_tissue.c +4 -8
- myokit/_sim/fiber_tissue.py +11 -13
- myokit/_sim/jacobian.cpp +2 -2
- myokit/_sim/jacobian.py +11 -8
- myokit/_sim/mcl.h +53 -55
- myokit/_sim/opencl.py +21 -27
- myokit/_sim/openclsim.c +3 -7
- myokit/_sim/openclsim.cl +3 -3
- myokit/_sim/openclsim.py +49 -40
- myokit/_sim/pacing.h +36 -16
- myokit/_sim/rhs.c +6 -13
- myokit/_sim/rhs.py +5 -14
- myokit/_sim/sundials.py +1 -4
- myokit/_system.py +10 -16
- myokit/_unit.py +4 -13
- myokit/float.py +0 -3
- myokit/formats/__init__.py +8 -10
- myokit/formats/ansic/__init__.py +0 -3
- myokit/formats/ansic/_ewriter.py +2 -4
- myokit/formats/ansic/_exporter.py +1 -4
- myokit/formats/ansic/template/cable.c +4 -4
- myokit/formats/ansic/template/euler.c +5 -5
- myokit/formats/ansic/template/sim.c +6 -6
- myokit/formats/axon/__init__.py +1 -3
- myokit/formats/axon/_abf.py +12 -17
- myokit/formats/axon/_atf.py +5 -6
- myokit/formats/axon/_importer.py +0 -3
- myokit/formats/cellml/__init__.py +0 -3
- myokit/formats/cellml/_ewriter.py +3 -6
- myokit/formats/cellml/_exporter.py +3 -6
- myokit/formats/cellml/_importer.py +1 -4
- myokit/formats/cellml/v1/__init__.py +0 -4
- myokit/formats/cellml/v1/_api.py +8 -11
- myokit/formats/cellml/v1/_parser.py +2 -5
- myokit/formats/cellml/v1/_writer.py +2 -11
- myokit/formats/cellml/v2/__init__.py +0 -3
- myokit/formats/cellml/v2/_api.py +8 -17
- myokit/formats/cellml/v2/_parser.py +2 -5
- myokit/formats/cellml/v2/_writer.py +1 -4
- myokit/formats/channelml/__init__.py +0 -3
- myokit/formats/channelml/_importer.py +11 -21
- myokit/formats/cpp/__init__.py +1 -3
- myokit/formats/cpp/_ewriter.py +0 -3
- myokit/formats/cuda/__init__.py +0 -3
- myokit/formats/cuda/_ewriter.py +2 -4
- myokit/formats/cuda/_exporter.py +0 -3
- myokit/formats/cuda/template/kernel.cu +8 -5
- myokit/formats/easyml/__init__.py +0 -3
- myokit/formats/easyml/_ewriter.py +9 -11
- myokit/formats/easyml/_exporter.py +2 -5
- myokit/formats/html/__init__.py +0 -3
- myokit/formats/html/_exporter.py +0 -3
- myokit/formats/html/_flatten.py +5 -21
- myokit/formats/latex/__init__.py +0 -3
- myokit/formats/latex/_ewriter.py +1 -4
- myokit/formats/latex/_exporter.py +4 -6
- myokit/formats/mathml/__init__.py +0 -3
- myokit/formats/mathml/_ewriter.py +2 -11
- myokit/formats/mathml/_parser.py +4 -6
- myokit/formats/matlab/__init__.py +0 -3
- myokit/formats/matlab/_ewriter.py +1 -4
- myokit/formats/matlab/_exporter.py +2 -5
- myokit/formats/matlab/template/main.m +3 -2
- myokit/formats/opencl/__init__.py +0 -3
- myokit/formats/opencl/_ewriter.py +2 -4
- myokit/formats/opencl/_exporter.py +2 -5
- myokit/formats/opencl/template/cable.c +10 -10
- myokit/formats/opencl/template/kernel.cl +1 -1
- myokit/formats/opencl/template/minilog.py +1 -1
- myokit/formats/python/__init__.py +0 -3
- myokit/formats/python/_ewriter.py +2 -5
- myokit/formats/python/_exporter.py +0 -3
- myokit/formats/python/template/sim.py +14 -14
- myokit/formats/sbml/__init__.py +0 -3
- myokit/formats/sbml/_api.py +50 -44
- myokit/formats/sbml/_importer.py +1 -4
- myokit/formats/sbml/_parser.py +2 -5
- myokit/formats/stan/__init__.py +0 -3
- myokit/formats/stan/_ewriter.py +2 -4
- myokit/formats/stan/_exporter.py +2 -5
- myokit/formats/stan/template/cell.stan +3 -3
- myokit/formats/sympy/__init__.py +0 -3
- myokit/formats/sympy/_ereader.py +1 -4
- myokit/formats/sympy/_ewriter.py +2 -5
- myokit/formats/wcp/__init__.py +0 -3
- myokit/formats/wcp/_wcp.py +2 -8
- myokit/formats/xml/__init__.py +0 -3
- myokit/formats/xml/_exporter.py +0 -3
- myokit/formats/xml/_split.py +0 -3
- myokit/gui/__init__.py +80 -246
- myokit/gui/datablock_viewer.py +103 -86
- myokit/gui/datalog_viewer.py +214 -66
- myokit/gui/explorer.py +15 -21
- myokit/gui/ide.py +171 -144
- myokit/gui/progress.py +9 -9
- myokit/gui/source.py +406 -375
- myokit/gui/vargrapher.py +2 -12
- myokit/lib/deps.py +12 -13
- myokit/lib/guess.py +3 -4
- myokit/lib/hh.py +20 -18
- myokit/lib/markov.py +21 -20
- myokit/lib/multi.py +1 -3
- myokit/lib/plots.py +20 -9
- myokit/pacing.py +0 -3
- myokit/pype.py +7 -18
- myokit/tests/__init__.py +3 -6
- myokit/tests/ansic_event_based_pacing.py +1 -4
- myokit/tests/ansic_fixed_form_pacing.py +3 -6
- myokit/tests/data/beeler-1977-model-compare-b.mmt +2 -2
- myokit/tests/data/clancy-1999-fitting.mmt +1 -0
- myokit/tests/test_aux.py +13 -28
- myokit/tests/test_cellml_v1_api.py +4 -19
- myokit/tests/test_cellml_v1_parser.py +0 -15
- myokit/tests/test_cellml_v1_writer.py +0 -9
- myokit/tests/test_cellml_v2_api.py +4 -19
- myokit/tests/test_cellml_v2_parser.py +0 -15
- myokit/tests/test_cellml_v2_writer.py +0 -9
- myokit/tests/test_cmodel.py +16 -22
- myokit/tests/test_compiler_detection.py +1 -11
- myokit/tests/test_component.py +108 -56
- myokit/tests/test_config.py +34 -67
- myokit/tests/test_datablock.py +1 -9
- myokit/tests/test_datalog.py +19 -24
- myokit/tests/test_dependency_checking.py +8 -23
- myokit/tests/test_expressions.py +0 -9
- myokit/tests/test_float.py +1 -5
- myokit/tests/test_formats.py +0 -9
- myokit/tests/test_formats_axon.py +1 -9
- myokit/tests/test_formats_cellml.py +0 -15
- myokit/tests/test_formats_channelml.py +0 -15
- myokit/tests/test_formats_easyml.py +0 -14
- myokit/tests/test_formats_exporters.py +1 -16
- myokit/tests/test_formats_expression_writers.py +1 -17
- myokit/tests/test_formats_html.py +0 -3
- myokit/tests/test_formats_importers.py +1 -16
- myokit/tests/test_formats_mathml_content.py +0 -9
- myokit/tests/test_formats_mathml_presentation.py +0 -9
- myokit/tests/test_formats_opencl.py +0 -10
- myokit/tests/test_formats_sbml.py +0 -15
- myokit/tests/test_formats_sympy.py +0 -9
- myokit/tests/test_formats_wcp.py +1 -3
- myokit/tests/test_io.py +27 -27
- myokit/tests/test_jacobian_calculator.py +6 -14
- myokit/tests/test_jacobian_tracer.py +0 -9
- myokit/tests/test_lib_deps.py +0 -9
- myokit/tests/test_lib_guess.py +0 -9
- myokit/tests/test_lib_hh.py +18 -12
- myokit/tests/test_lib_markov.py +21 -13
- myokit/tests/test_lib_multi.py +0 -9
- myokit/tests/test_lib_plots.py +13 -8
- myokit/tests/test_meta.py +0 -3
- myokit/tests/test_model.py +390 -96
- myokit/tests/test_model_building.py +44 -96
- myokit/tests/test_opencl_info.py +5 -14
- myokit/tests/test_pacing_factory.py +0 -3
- myokit/tests/test_pacing_system_c.py +1 -23
- myokit/tests/test_pacing_system_py.py +0 -9
- myokit/tests/test_parsing.py +139 -56
- myokit/tests/test_progress_reporters.py +0 -3
- myokit/tests/test_protocol.py +0 -9
- myokit/tests/test_protocol_floating_point.py +1 -10
- myokit/tests/test_protocol_time_series.py +82 -0
- myokit/tests/test_pype.py +0 -9
- myokit/tests/test_quantity.py +0 -9
- myokit/tests/test_rhs_benchmarker.py +1 -9
- myokit/tests/test_sbml_api.py +27 -42
- myokit/tests/test_sbml_parser.py +4 -19
- myokit/tests/test_simulation_1d.py +45 -25
- myokit/tests/test_simulation_cvodes.py +321 -55
- myokit/tests/test_simulation_cvodes_from_disk.py +0 -3
- myokit/tests/test_simulation_fiber_tissue.py +39 -12
- myokit/tests/test_simulation_log_interval.py +1 -431
- myokit/tests/test_simulation_opencl.py +69 -48
- myokit/tests/test_simulation_opencl_log_interval.py +1 -3
- myokit/tests/test_simulation_opencl_vs_cvode.py +1 -10
- myokit/tests/test_simulation_opencl_vs_sim1d.py +1 -10
- myokit/tests/test_system_info.py +1 -11
- myokit/tests/test_tools.py +0 -9
- myokit/tests/test_unit.py +1 -10
- myokit/tests/test_user_functions.py +0 -10
- myokit/tests/test_variable.py +231 -27
- myokit/tools.py +5 -21
- myokit/units.py +5 -3
- {myokit-1.33.9.dist-info → myokit-1.35.0.dist-info}/METADATA +12 -15
- myokit-1.35.0.dist-info/RECORD +391 -0
- {myokit-1.33.9.dist-info → myokit-1.35.0.dist-info}/WHEEL +1 -1
- {myokit-1.33.9.dist-info → myokit-1.35.0.dist-info}/entry_points.txt +0 -1
- myokit/_exec_new.py +0 -15
- myokit/_exec_old.py +0 -15
- myokit/_sim/cvodesim.c +0 -1551
- myokit/_sim/cvodesim.py +0 -674
- myokit/_sim/icsim.cpp +0 -563
- myokit/_sim/icsim.py +0 -363
- myokit/_sim/psim.cpp +0 -656
- myokit/_sim/psim.py +0 -493
- myokit/lib/common.py +0 -1094
- myokit/tests/test_lib_common.py +0 -130
- myokit/tests/test_simulation_cvode.py +0 -612
- myokit/tests/test_simulation_ic.py +0 -108
- myokit/tests/test_simulation_p.py +0 -223
- myokit-1.33.9.dist-info/RECORD +0 -403
- /myokit/formats/opencl/template/{test → test.sh} +0 -0
- {myokit-1.33.9.dist-info → myokit-1.35.0.dist-info}/LICENSE.txt +0 -0
- {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
|