/** * @file CVodesIntegrator.cpp * */ // Copyright 2001 California Institute of Technology #include "cantera/base/config.h" #include "CVodesIntegrator.h" #include "cantera/base/stringUtils.h" #include using namespace std; #ifdef SUNDIALS_VERSION_22 #include #include #include #include #include #include #include #include #else #if defined(SUNDIALS_VERSION_23) || defined (SUNDIALS_VERSION_24) #include #include #include #include #include #include #include #include #include #else unsupported sundials version! #endif #if defined (SUNDIALS_VERSION_24) #define CV_SS 1 #define CV_SV 2 #endif #endif inline static N_Vector nv(void* x) { return reinterpret_cast(x); } namespace Cantera { class FuncData { public: FuncData(FuncEval* f, int npar = 0) { m_pars.resize(npar, 1.0); m_func = f; } virtual ~FuncData() {} vector_fp m_pars; FuncEval* m_func; }; } extern "C" { /** * Function called by cvodes to evaluate ydot given y. The cvode * integrator allows passing in a void* pointer to access * external data. This pointer is cast to a pointer to a instance * of class FuncEval. The equations to be integrated should be * specified by deriving a class from FuncEval that evaluates the * desired equations. * @ingroup odeGroup */ static int cvodes_rhs(realtype t, N_Vector y, N_Vector ydot, void* f_data) { double* ydata = NV_DATA_S(y); //N_VDATA(y); double* ydotdata = NV_DATA_S(ydot); //N_VDATA(ydot); Cantera::FuncData* d = (Cantera::FuncData*)f_data; Cantera::FuncEval* f = d->m_func; //try { if (d->m_pars.size() == 0) { f->eval(t, ydata, ydotdata, NULL); } else { f->eval(t, ydata, ydotdata, DATA_PTR(d->m_pars)); } //} //catch (...) { //Cantera::showErrors(); //Cantera::error("Teminating execution"); //} return 0; } } namespace Cantera { /** * Constructor. Default settings: dense jacobian, no user-supplied * Jacobian function, Newton iteration. */ CVodesIntegrator::CVodesIntegrator() : m_neq(0), m_cvode_mem(0), m_t0(0.0), m_y(0), m_abstol(0), m_type(DENSE+NOJAC), m_itol(CV_SS), m_method(CV_BDF), m_iter(CV_NEWTON), m_maxord(0), m_reltol(1.e-9), m_abstols(1.e-15), m_reltolsens(1.0e-5), m_abstolsens(1.0e-4), m_nabs(0), m_hmax(0.0), m_maxsteps(20000), m_np(0), m_mupper(0), m_mlower(0) { //m_ropt.resize(OPT_SIZE,0.0); //m_iopt = new long[OPT_SIZE]; //fill(m_iopt, m_iopt+OPT_SIZE,0); } /// Destructor. CVodesIntegrator::~CVodesIntegrator() { if (m_cvode_mem) { if (m_np > 0) { CVodeSensFree(m_cvode_mem); } CVodeFree(&m_cvode_mem); } if (m_y) { N_VDestroy_Serial(nv(m_y)); } if (m_abstol) { N_VDestroy_Serial(nv(m_abstol)); } delete m_fdata; //delete[] m_iopt; } double& CVodesIntegrator::solution(int k) { return NV_Ith_S(nv(m_y),k); } double* CVodesIntegrator::solution() { return NV_DATA_S(nv(m_y)); } void CVodesIntegrator::setTolerances(double reltol, size_t n, double* abstol) { m_itol = CV_SV; m_nabs = n; if (n != m_neq) { if (m_abstol) { N_VDestroy_Serial(nv(m_abstol)); } m_abstol = reinterpret_cast(N_VNew_Serial(n)); } for (size_t i=0; i(N_VNew_Serial(m_neq)); // allocate solution vector for (size_t i = 0; i < m_neq; i++) { NV_Ith_S(nv(m_y), i) = 0.0; } // check abs tolerance array size if (m_itol == CV_SV && m_nabs < m_neq) { throw CVodesErr("not enough absolute tolerance values specified."); } func.getInitialConditions(m_t0, m_neq, NV_DATA_S(nv(m_y))); if (m_cvode_mem) { CVodeFree(&m_cvode_mem); } /* * Specify the method and the iteration type: * Cantera Defaults: * CV_BDF - Use BDF methods * CV_NEWTON - use newton's method */ m_cvode_mem = CVodeCreate(m_method, m_iter); if (!m_cvode_mem) { throw CVodesErr("CVodeCreate failed."); } int flag = 0; #if defined(SUNDIALS_VERSION_22) || defined(SUNDIALS_VERSION_23) if (m_itol == CV_SV) { // vector atol flag = CVodeMalloc(m_cvode_mem, cvodes_rhs, m_t0, nv(m_y), m_itol, m_reltol, nv(m_abstol)); } else { // scalar atol flag = CVodeMalloc(m_cvode_mem, cvodes_rhs, m_t0, nv(m_y), m_itol, m_reltol, &m_abstols); } if (flag != CV_SUCCESS) { if (flag == CV_MEM_FAIL) { throw CVodesErr("Memory allocation failed."); } else if (flag == CV_ILL_INPUT) { throw CVodesErr("Illegal value for CVodeMalloc input argument."); } else { throw CVodesErr("CVodeMalloc failed."); } } #elif defined(SUNDIALS_VERSION_24) flag = CVodeInit(m_cvode_mem, cvodes_rhs, m_t0, nv(m_y)); if (flag != CV_SUCCESS) { if (flag == CV_MEM_FAIL) { throw CVodesErr("Memory allocation failed."); } else if (flag == CV_ILL_INPUT) { throw CVodesErr("Illegal value for CVodeInit input argument."); } else { throw CVodesErr("CVodeInit failed."); } } if (m_itol == CV_SV) { flag = CVodeSVtolerances(m_cvode_mem, m_reltol, nv(m_abstol)); } else { flag = CVodeSStolerances(m_cvode_mem, m_reltol, m_abstols); } if (flag != CV_SUCCESS) { if (flag == CV_MEM_FAIL) { throw CVodesErr("Memory allocation failed."); } else if (flag == CV_ILL_INPUT) { throw CVodesErr("Illegal value for CVodeInit input argument."); } else { throw CVodesErr("CVodeInit failed."); } } #else printf("unknown sundials verson\n"); exit(-1); #endif if (m_type == DENSE + NOJAC) { long int N = m_neq; CVDense(m_cvode_mem, N); } else if (m_type == DIAG) { CVDiag(m_cvode_mem); } else if (m_type == GMRES) { CVSpgmr(m_cvode_mem, PREC_NONE, 0); } else if (m_type == BAND + NOJAC) { long int N = m_neq; long int nu = m_mupper; long int nl = m_mlower; CVBand(m_cvode_mem, N, nu, nl); } else { throw CVodesErr("unsupported option"); } // pass a pointer to func in m_data m_fdata = new FuncData(&func, func.nparams()); //m_data = (void*)&func; #if defined(SUNDIALS_VERSION_22) || defined(SUNDIALS_VERSION_23) flag = CVodeSetFdata(m_cvode_mem, (void*)m_fdata); if (flag != CV_SUCCESS) { throw CVodesErr("CVodeSetFdata failed."); } #elif defined(SUNDIALS_VERSION_24) flag = CVodeSetUserData(m_cvode_mem, (void*)m_fdata); if (flag != CV_SUCCESS) { throw CVodesErr("CVodeSetUserData failed."); } #endif if (func.nparams() > 0) { sensInit(t0, func); flag = CVodeSetSensParams(m_cvode_mem, DATA_PTR(m_fdata->m_pars), NULL, NULL); } // set options if (m_maxord > 0) { flag = CVodeSetMaxOrd(m_cvode_mem, m_maxord); } if (m_maxsteps > 0) { flag = CVodeSetMaxNumSteps(m_cvode_mem, m_maxsteps); } if (m_hmax > 0) { flag = CVodeSetMaxStep(m_cvode_mem, m_hmax); } } void CVodesIntegrator::reinitialize(double t0, FuncEval& func) { m_t0 = t0; //try { func.getInitialConditions(m_t0, m_neq, NV_DATA_S(nv(m_y))); //} //catch (CanteraError) { //showErrors(); //error("Teminating execution"); //} int result; #if defined(SUNDIALS_VERSION_22) || defined(SUNDIALS_VERSION_23) if (m_itol == CV_SV) { result = CVodeReInit(m_cvode_mem, cvodes_rhs, m_t0, nv(m_y), m_itol, m_reltol, nv(m_abstol)); } else { result = CVodeReInit(m_cvode_mem, cvodes_rhs, m_t0, nv(m_y), m_itol, m_reltol, &m_abstols); } if (result != CV_SUCCESS) { throw CVodesErr("CVodeReInit failed. result = "+int2str(result)); } #elif defined(SUNDIALS_VERSION_24) result = CVodeReInit(m_cvode_mem, m_t0, nv(m_y)); if (result != CV_SUCCESS) { throw CVodesErr("CVodeReInit failed. result = "+int2str(result)); } #endif if (m_type == DENSE + NOJAC) { long int N = m_neq; CVDense(m_cvode_mem, N); } else if (m_type == DIAG) { CVDiag(m_cvode_mem); } else if (m_type == BAND + NOJAC) { long int N = m_neq; long int nu = m_mupper; long int nl = m_mlower; CVBand(m_cvode_mem, N, nu, nl); } else if (m_type == GMRES) { CVSpgmr(m_cvode_mem, PREC_NONE, 0); } else { throw CVodesErr("unsupported option"); } // set options if (m_maxord > 0) { CVodeSetMaxOrd(m_cvode_mem, m_maxord); } if (m_maxsteps > 0) { CVodeSetMaxNumSteps(m_cvode_mem, m_maxsteps); } if (m_hmax > 0) { CVodeSetMaxStep(m_cvode_mem, m_hmax); } } void CVodesIntegrator::integrate(double tout) { double t; int flag; flag = CVode(m_cvode_mem, tout, nv(m_y), &t, CV_NORMAL); if (flag != CV_SUCCESS) { throw CVodesErr(" CVodes error encountered."); } #if defined(SUNDIALS_VERSION_22) || defined(SUNDIALS_VERSION_23) if (m_np > 0) { CVodeGetSens(m_cvode_mem, tout, m_yS); } #elif defined(SUNDIALS_VERSION_24) double tretn; if (m_np > 0) { CVodeGetSens(m_cvode_mem, &tretn, m_yS); if (fabs(tretn - tout) > 1.0E-5) { throw CVodesErr("Time of Sensitivities different than time of tout"); } } #endif } double CVodesIntegrator::step(double tout) { double t; int flag; flag = CVode(m_cvode_mem, tout, nv(m_y), &t, CV_ONE_STEP); if (flag != CV_SUCCESS) { throw CVodesErr(" CVodes error encountered."); } return t; } int CVodesIntegrator::nEvals() const { long int ne; CVodeGetNumRhsEvals(m_cvode_mem, &ne); return ne; //return m_iopt[NFE]; } double CVodesIntegrator::sensitivity(size_t k, size_t p) { if (k >= m_neq) { throw CVodesErr("sensitivity: k out of range ("+int2str(p)+")"); } if (p >= m_np) { throw CVodesErr("sensitivity: p out of range ("+int2str(p)+")"); } return NV_Ith_S(m_yS[p],k); } }