1、安装
cvode是sundials软件包的一部分。在windows+visualstudio下可以用vcpkg安装使用。
git clone https://github.com/microsoft/vcpkg
然后运行目录中的Bootstrap_vcpkg.bat。
然后进入vcpkg目录中,vcpkg install sundials:x64-windows,这样安装64位dll。
然后管理员身份运行cmd,vcpkg integrate install。
2 使用
新建项目后,在项目属性中可以看到vcpkg选项,证明安装好了。
3 示例程序
程序的主要步骤
1、初始化多线程环境(可选)
2、创建sundials上下文对象
SUNContext_Create()
3、设置问题的大小???
4、设置初值向量
如果已有数据存储在ydata中,可以调用y0 = N_VMake_***(..., ydata)
否则,调用N_VNew_***(...)创建新的向量
然后用ydata = N_VGetArrayPointer(y0)设置单独数据
5、创建CVODE对象
CVodeCreate()返回一个内存结构的指针
6、初始化CVODE求解器
CVodeInit(),定义方程组信息,分配内部的内存,初始化。
7、设置积分误差
CVodeSStolerances() or CVodeSVtolerances() 设置相对误差或者绝对误差
8、创建矩阵对象
matrix_var = SUNDenseMatrix(x, x, context_var);
9、创建求解器
ls = SUNxxx_xxx(y, matrix_var, context_var)
10、把矩阵加入求解器
CVodeSetLinearSolver(mem, ls, matrix_var);
11、设置用户定义的雅克比矩阵
CVodeSetJacFn(mem, Jac), jac是用户定义函数
13、时间步长增加
ier = CVode(cvode_mem, tout, yout, tret itask)
14、释放解向量内存
15、释放求解器内存
CVodeFree()
16、释放线性求解器和矩阵的内存
SUNLinSolFree()
SUNMatDestroy()
17、释放SUNContext对象
其他
Ith和IJth是自定义函数,方便函数形式符合数学公式的形式,即下标序号从1开始,而不是c语言的从0开始。
函数f是ode表达式的直接转写,怎么写详细见https://sundials.readthedocs.io/en/latest/cvode/Usage/index.html#user-supplied-functions。函数g定义了两个数学函数g0和g1,用于求特定值时的参数(比如y1=1,此时求其他的y,可选)。Jac为Jacobian矩阵,该矩阵初始值为0,所以可以只设置非0值。
CVodePrintAllStats输出所有统计信息
/* -----------------------------------------------------------------
* Programmer(s): Scott D. Cohen, Alan C. Hindmarsh and
* Radu Serban @ LLNL
* -----------------------------------------------------------------
* SUNDIALS Copyright Start
* Copyright (c) 2002-2022, Lawrence Livermore National Security
* and Southern Methodist University.
* All rights reserved.
*
* See the top-level LICENSE and NOTICE files for details.
*
* SPDX-License-Identifier: BSD-3-Clause
* SUNDIALS Copyright End
* -----------------------------------------------------------------
* Example problem:
*
* The following is a simple example problem, with the coding
* needed for its solution by CVODE. The problem is from
* chemical kinetics, and consists of the following three rate
* equations:
* dy1/dt = -.04*y1 + 1.e4*y2*y3
* dy2/dt = .04*y1 - 1.e4*y2*y3 - 3.e7*(y2)^2
* dy3/dt = 3.e7*(y2)^2
* on the interval from t = 0.0 to t = 4.e10, with initial
* conditions: y1 = 1.0, y2 = y3 = 0. The problem is stiff.
* While integrating the system, we also use the rootfinding
* feature to find the points at which y1 = 1e-4 or at which
* y3 = 0.01. This program solves the problem with the BDF method,
* Newton iteration with the dense linear solver, and a
* user-supplied Jacobian routine.
* It uses a scalar relative tolerance and a vector absolute
* tolerance. Output is printed in decades from t = .4 to t = 4.e10.
* Run statistics (optional outputs) are printed at the end.
* -----------------------------------------------------------------*/
#include <stdio.h>
//#include <cvode/cvode.h>
#include <cvode/cvode.h> /* prototypes for CVODE fcts., consts. */
#include <nvector/nvector_serial.h> /* access to serial N_Vector */
#include <sunmatrix/sunmatrix_dense.h> /* access to dense SUNMatrix */
#include <sunlinsol/sunlinsol_dense.h> /* access to dense SUNLinearSolver */
#if defined(SUNDIALS_EXTENDED_PRECISION)
#define GSYM "Lg"
#define ESYM "Le"
#define FSYM "Lf"
#else
#define GSYM "g"
#define ESYM "e"
#define FSYM "f"
#endif
/* User-defined vector and matrix accessor macros: Ith, IJth */
/* These macros are defined in order to write code which exactly matches
the mathematical problem description given above.
Ith(v,i) references the ith component of the vector v, where i is in
the range [1..NEQ] and NEQ is defined below. The Ith macro is defined
using the N_VIth macro in nvector.h. N_VIth numbers the components of
a vector starting from 0.
IJth(A,i,j) references the (i,j)th element of the dense matrix A, where
i and j are in the range [1..NEQ]. The IJth macro is defined using the
SM_ELEMENT_D macro. SM_ELEMENT_D numbers rows and columns of
a dense matrix starting from 0. */
#define Ith(v,i) NV_Ith_S(v,i-1) /* i-th vector component i=1..NEQ */
#define IJth(A,i,j) SM_ELEMENT_D(A,i-1,j-1) /* (i,j)-th matrix component i,j=1..NEQ */
/* Problem Constants */
#define NEQ 3 /* number of equations */
#define Y1 RCONST(1.0) /* initial y components */
#define Y2 RCONST(0.0)
#define Y3 RCONST(0.0)
#define RTOL RCONST(1.0e-4) /* scalar relative tolerance */
#define ATOL1 RCONST(1.0e-8) /* vector absolute tolerance components */
#define ATOL2 RCONST(1.0e-14)
#define ATOL3 RCONST(1.0e-6)
#define T0 RCONST(0.0) /* initial time */
#define T1 RCONST(0.4) /* first output time */
#define TMULT RCONST(10.0) /* output time factor */
#define NOUT 12 /* number of output times */
#define ZERO RCONST(0.0)
/* Functions Called by the Solver */
static int f(realtype t, N_Vector y, N_Vector ydot, void *user_data);
static int g(realtype t, N_Vector y, realtype *gout, void *user_data);
static int Jac(realtype t, N_Vector y, N_Vector fy, SUNMatrix J,
void *user_data, N_Vector tmp1, N_Vector tmp2, N_Vector tmp3);
/* Private functions to output results */
static void PrintOutput(realtype t, realtype y1, realtype y2, realtype y3);
static void PrintRootInfo(int root_f1, int root_f2);
/* Private function to check function return values */
static int check_retval(void *returnvalue, const char *funcname, int opt);
/* Private function to check computed solution */
static int check_ans(N_Vector y, realtype t, realtype rtol, N_Vector atol);
/*
*-------------------------------
* Main Program
*-------------------------------
*/
int main()
{
SUNContext sunctx;
realtype t, tout;
N_Vector y;
N_Vector abstol;
SUNMatrix A;
SUNLinearSolver LS;
void *cvode_mem;
int retval, iout;
int retvalr;
int rootsfound[2];
FILE* FID;
y = NULL;
abstol = NULL;
A = NULL;
LS = NULL;
cvode_mem = NULL;
/* Create the SUNDIALS context */
retval = SUNContext_Create(NULL, &sunctx);
if (check_retval(&retval, "SUNContext_Create", 1)) return(1);
/* Initial conditions */
y = N_VNew_Serial(NEQ, sunctx);
if (check_retval((void *)y, "N_VNew_Serial", 0)) return(1);
/* Initialize y */
Ith(y,1) = Y1;
Ith(y,2) = Y2;
Ith(y,3) = Y3;
/* Set the vector absolute tolerance */
abstol = N_VNew_Serial(NEQ, sunctx);
if (check_retval((void *)abstol, "N_VNew_Serial", 0)) return(1);
Ith(abstol,1) = ATOL1;
Ith(abstol,2) = ATOL2;
Ith(abstol,3) = ATOL3;
/* Call CVodeCreate to create the solver memory and specify the
* Backward Differentiation Formula */
cvode_mem = CVodeCreate(CV_BDF, sunctx);
if (check_retval((void *)cvode_mem, "CVodeCreate", 0)) return(1);
/* Call CVodeInit to initialize the integrator memory and specify the
* user's right hand side function in y'=f(t,y), the initial time T0, and
* the initial dependent variable vector y. */
retval = CVodeInit(cvode_mem, f, T0, y);
if (check_retval(&retval, "CVodeInit", 1)) return(1);
/* Call CVodeSVtolerances to specify the scalar relative tolerance
* and vector absolute tolerances */
retval = CVodeSVtolerances(cvode_mem, RTOL, abstol);
if (check_retval(&retval, "CVodeSVtolerances", 1)) return(1);
/* Call CVodeRootInit to specify the root function g with 2 components */
//retval = CVodeRootInit(cvode_mem, 2, g);
//if (check_retval(&retval, "CVodeRootInit", 1)) return(1);
/* Create dense SUNMatrix for use in linear solves */
A = SUNDenseMatrix(3, 3, sunctx);
if (check_retval((void *)A, "SUNDenseMatrix", 0)) return(1);
/* Create dense SUNLinearSolver object for use by CVode */
LS = SUNLinSol_Dense(y, A, sunctx);
if (check_retval((void *)LS, "SUNLinSol_Dense", 0)) return(1);
/* Attach the matrix and linear solver */
retval = CVodeSetLinearSolver(cvode_mem, LS, A);
if (check_retval(&retval, "CVodeSetLinearSolver", 1)) return(1);
/* Set the user-supplied Jacobian routine Jac */
retval = CVodeSetJacFn(cvode_mem, Jac);
if (check_retval(&retval, "CVodeSetJacFn", 1)) return(1);
/* In loop, call CVode, print results, and test for error.
Break out of loop when NOUT preset output times have been reached. */
printf(" \n3-species kinetics problem\n\n");
FILE* vstream;
/* Open file for printing statistics */
//FID = fopen_s(vstream, "cvRoberts_dns_stats.csv", "w");
errno_t ee = fopen_s(&vstream, "cvRoberts_dns_stats.csv", "w");
iout = 0; tout = T1;
while(1) {
retval = CVode(cvode_mem, tout, y, &t, CV_NORMAL);
PrintOutput(t, Ith(y,1), Ith(y,2), Ith(y,3));
if (retval == CV_ROOT_RETURN) {
retvalr = CVodeGetRootInfo(cvode_mem, rootsfound);
if (check_retval(&retvalr, "CVodeGetRootInfo", 1)) return(1);
PrintRootInfo(rootsfound[0],rootsfound[1]);
}
if (check_retval(&retval, "CVode", 1)) break;
if (retval == CV_SUCCESS) {
iout++;
tout *= TMULT;
}
retval = CVodePrintAllStats(cvode_mem, vstream, SUN_OUTPUTFORMAT_CSV);
if (iout == NOUT) break;
}
fclose(vstream);
/* Print final statistics to the screen */
printf("\nFinal Statistics:\n");
retval = CVodePrintAllStats(cvode_mem, stdout, SUN_OUTPUTFORMAT_TABLE);
/* check the solution error */
retval = check_ans(y, t, RTOL, abstol);
/* Free memory */
N_VDestroy(y); /* Free y vector */
N_VDestroy(abstol); /* Free abstol vector */
CVodeFree(&cvode_mem); /* Free CVODE memory */
SUNLinSolFree(LS); /* Free the linear solver memory */
SUNMatDestroy(A); /* Free the matrix memory */
SUNContext_Free(&sunctx); /* Free the SUNDIALS context */
return(retval);
}
/*
*-------------------------------
* Functions called by the solver
*-------------------------------
*/
/*
* f routine. Compute function f(t,y).
*/
static int f(realtype t, N_Vector y, N_Vector ydot, void *user_data)
{
realtype y1, y2, y3, yd1, yd3;
y1 = Ith(y,1); y2 = Ith(y,2); y3 = Ith(y,3);
yd1 = Ith(ydot,1) = RCONST(-0.04)*y1 + RCONST(1.0e4)*y2*y3;
yd3 = Ith(ydot,3) = RCONST(3.0e7)*y2*y2;
Ith(ydot,2) = -yd1 - yd3;
return(0);
}
/*
* g routine. Compute functions g_i(t,y) for i = 0,1.
*/
static int g(realtype t, N_Vector y, realtype *gout, void *user_data)
{
realtype y1, y3;
y1 = Ith(y,1); y3 = Ith(y,3);
gout[0] = y1 - RCONST(0.0001);
gout[1] = y3 - RCONST(0.01);
return(0);
}
/*
* Jacobian routine. Compute J(t,y) = df/dy. *
*/
static int Jac(realtype t, N_Vector y, N_Vector fy, SUNMatrix J,
void *user_data, N_Vector tmp1, N_Vector tmp2, N_Vector tmp3)
{
realtype y2, y3;
y2 = Ith(y,2); y3 = Ith(y,3);
IJth(J,1,1) = RCONST(-0.04);
IJth(J,1,2) = RCONST(1.0e4)*y3;
IJth(J,1,3) = RCONST(1.0e4)*y2;
IJth(J,2,1) = RCONST(0.04);
IJth(J,2,2) = RCONST(-1.0e4)*y3-RCONST(6.0e7)*y2;
IJth(J,2,3) = RCONST(-1.0e4)*y2;
IJth(J,3,1) = ZERO;
IJth(J,3,2) = RCONST(6.0e7)*y2;
IJth(J,3,3) = ZERO;
return(0);
}
/*
*-------------------------------
* Private helper functions
*-------------------------------
*/
static void PrintOutput(realtype t, realtype y1, realtype y2, realtype y3)
{
#if defined(SUNDIALS_EXTENDED_PRECISION)
printf("At t = %0.4Le y =%14.6Le %14.6Le %14.6Le\n", t, y1, y2, y3);
#elif defined(SUNDIALS_DOUBLE_PRECISION)
printf("At t = %0.4e y =%14.6e %14.6e %14.6e\n", t, y1, y2, y3);
#else
printf("At t = %0.4e y =%14.6e %14.6e %14.6e\n", t, y1, y2, y3);
#endif
return;
}
static void PrintRootInfo(int root_f1, int root_f2)
{
printf(" rootsfound[] = %3d %3d\n", root_f1, root_f2);
return;
}
/*
* Check function return value...
* opt == 0 means SUNDIALS function allocates memory so check if
* returned NULL pointer
* opt == 1 means SUNDIALS function returns an integer value so check if
* retval < 0
* opt == 2 means function allocates memory so check if returned
* NULL pointer
*/
static int check_retval(void *returnvalue, const char *funcname, int opt)
{
int *retval;
/* Check if SUNDIALS function returned NULL pointer - no memory allocated */
if (opt == 0 && returnvalue == NULL) {
fprintf(stderr, "\nSUNDIALS_ERROR: %s() failed - returned NULL pointer\n\n",
funcname);
return(1); }
/* Check if retval < 0 */
else if (opt == 1) {
retval = (int *) returnvalue;
if (*retval < 0) {
fprintf(stderr, "\nSUNDIALS_ERROR: %s() failed with retval = %d\n\n",
funcname, *retval);
return(1); }}
/* Check if function returned NULL pointer - no memory allocated */
else if (opt == 2 && returnvalue == NULL) {
fprintf(stderr, "\nMEMORY_ERROR: %s() failed - returned NULL pointer\n\n",
funcname);
return(1); }
return(0);
}
/* compare the solution at the final time 4e10s to a reference solution computed
using a relative tolerance of 1e-8 and absoltue tolerance of 1e-14 */
static int check_ans(N_Vector y, realtype t, realtype rtol, N_Vector atol)
{
int passfail=0; /* answer pass (0) or fail (1) flag */
N_Vector ref; /* reference solution vector */
N_Vector ewt; /* error weight vector */
realtype err; /* wrms error */
realtype ONE=RCONST(1.0);
/* create reference solution and error weight vectors */
ref = N_VClone(y);
ewt = N_VClone(y);
/* set the reference solution data */
NV_Ith_S(ref,0) = RCONST(5.2083495894337328e-08);
NV_Ith_S(ref,1) = RCONST(2.0833399429795671e-13);
NV_Ith_S(ref,2) = RCONST(9.9999994791629776e-01);
/* compute the error weight vector, loosen atol */
N_VAbs(ref, ewt);
N_VLinearSum(rtol, ewt, RCONST(10.0), atol, ewt);
if (N_VMin(ewt) <= ZERO) {
fprintf(stderr, "\nSUNDIALS_ERROR: check_ans failed - ewt <= 0\n\n");
return(-1);
}
N_VInv(ewt, ewt);
/* compute the solution error */
N_VLinearSum(ONE, y, -ONE, ref, ref);
err = N_VWrmsNorm(ref, ewt);
/* is the solution within the tolerances? */
passfail = (err < ONE) ? 0 : 1;
if (passfail) {
fprintf(stdout, "\nSUNDIALS_WARNING: check_ans error=%"GSYM"\n\n", err);
}
/* Free vectors */
N_VDestroy(ref);
N_VDestroy(ewt);
return(passfail);
}