Nie możesz wybrać więcej, niż 25 tematów Tematy muszą się zaczynać od litery lub cyfry, mogą zawierać myślniki ('-') i mogą mieć do 35 znaków.

1203 wiersze
33KB

  1. /*
  2. sensBrute: A program that calculates the sensitivity of ignition delay time to
  3. kinetic rate parameters using a brute force method.
  4. Copyright (C) 2019 Vyaas Gururajan
  5. This program is free software; you can redistribute it and/or modify it under
  6. the terms of the GNU General Public License as published by the Free Software
  7. Foundation; either version 2 of the License, or (at your option) any later
  8. version.
  9. This program is distributed in the hope that it will be useful, but WITHOUT ANY
  10. WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
  11. PARTICULAR PURPOSE. See the GNU General Public License for more details.
  12. You should have received a copy of the GNU General Public License along with
  13. this program; if not, write to the Free Software Foundation, Inc., 51 Franklin
  14. Street, Fifth Floor, Boston, MA 02110-1301, USA.
  15. */
  16. #include <stdbool.h>
  17. #include <math.h>
  18. #include <stdio.h>
  19. #include <unistd.h>
  20. #include <string.h>
  21. #include <time.h>
  22. /*Cantera include files*/
  23. #include "IdealGasMix.h"
  24. /*Sundials include files*/
  25. #include <cvode/cvode.h> /* prototypes for CVODE fcts., consts. */
  26. #include <nvector/nvector_serial.h> /* access to serial N_Vector */
  27. #include <sunmatrix/sunmatrix_dense.h> /* access to dense SUNMatrix */
  28. #include <sunlinsol/sunlinsol_dense.h> /* access to dense SUNLinearSolver */
  29. #include <cvode/cvode_direct.h> /* access to CVDls interface */
  30. #include <sundials/sundials_types.h> /* defs. of realtype, sunindextype */
  31. #include <sundials/sundials_math.h>
  32. #include <gsl/gsl_math.h>
  33. #include <gsl/gsl_spline.h>
  34. static int imaxarg1,imaxarg2;
  35. #define IMAX(a,b) (imaxarg1=(a),imaxarg2=(b),(imaxarg1) > (imaxarg2) ?\
  36. (imaxarg1) : (imaxarg2))
  37. static int iminarg1,iminarg2;
  38. #define IMIN(a,b) (iminarg1=(a),iminarg2=(b),(iminarg1) < (iminarg2) ?\
  39. (iminarg1) : (iminarg2))
  40. /*quick-sort related routines*/
  41. static unsigned long *lvector(long nl, long nh);
  42. static void free_lvector(unsigned long *v, long nl, long nh);
  43. static void sort(unsigned long n, double arr[], int ind[]);
  44. #define ZERO RCONST(0.0)
  45. #define HALF RCONST(0.5)
  46. #define ONE RCONST(1.0)
  47. #define TWO RCONST(2.0)
  48. #define THREE RCONST(3.0)
  49. #define TEN RCONST(10.0)
  50. #define BUFSIZE 1000
  51. #define EPSILON RCONST(0.1)
  52. /* In order to keep begin the index numbers from 1 instead of 0, we define
  53. * macros here. Also, we define macros to ease referencing various variables in
  54. * the sundials nvector.
  55. */
  56. #define Y0(k) NV_Ith_S(data->Y0,k-1)
  57. #define x(i) NV_Ith_S(data->x,i-1)
  58. #define t(i) NV_Ith_S(data->tArray,i)
  59. #define P(i) NV_Ith_S(data->PArray,i)
  60. #define dPdt(i) NV_Ith_S(data->dPdtArray,i)
  61. #define T(i) NV_Ith_S(y,((i-1)*data->nvar)+data->nt)
  62. #define Y(i,k) NV_Ith_S(y,((i-1)*data->nvar)+data->ny+k-1)
  63. #define Tdot(i) NV_Ith_S(ydot,((i-1)*data->nvar)+data->nt)
  64. #define Ydot(i,k) NV_Ith_S(ydot,((i-1)*data->nvar)+data->ny+k-1)
  65. #define Pp(i) NV_Ith_S(data->Pp,i-1)
  66. #define dPdtp(i) NV_Ith_S(data->dPdtp,i-1)
  67. #define Tres(i) NV_Ith_S(res,((i-1)*(data->nvar+1))+data->nt)
  68. #define Yres(i,k) NV_Ith_S(res,((i-1)*(data->nvar+1))+data->ny+k-1)
  69. #define taures(i) NV_Ith_S(res,((i-1)*(data->nvar+1))+data->ntau)
  70. #define wdot(i) wdot[i-1]
  71. #define enthalpy(i) enthalpy[i-1]
  72. typedef struct {
  73. /* This structure contains all information relevant to evaluating the
  74. * residue.
  75. */
  76. Cantera::IdealGasMix* gas;//Ideal gas object containing thermodynamic and
  77. //kinetic data
  78. realtype P; //Pressure (in Pascals)
  79. realtype T0; //Initial Temperature (in K)
  80. N_Vector Y0; //Initial Mass Fractions
  81. realtype rho0; //initial density
  82. realtype TIgn; //Ignition Temperature (in K)
  83. realtype tauIgn; //Ignition delay time (in s)
  84. N_Vector x; //The grid
  85. N_Vector tArray;
  86. N_Vector PArray;
  87. N_Vector dPdtArray;
  88. N_Vector Pp;
  89. N_Vector dPdtp;
  90. realtype dPdt;
  91. int tArraySize;
  92. bool constantVolume; //boolean to enable/disable constant volume
  93. //version of the ignition delay problem
  94. bool imposedPdt; //boolean to enable/disable manual entry for P and dPdt
  95. int jguess; //saved index to accelerate lookup via "hunt"
  96. bool writeSpeciesSensitivities; //boolean to enable/disable writing
  97. //of species sensitivities
  98. bool IVPSuccess;
  99. int nsp,neq,npts,nvar; //key quantities required in for loops:
  100. //nsp-> no: of chemical species
  101. //neq-> number of governing equations
  102. //npts-> no: of grid points
  103. //nvar->no: of variables
  104. int nt,ny,ntau; //array indices for various variables:
  105. //nt-> temperature (0)
  106. //ny-> species mass fractions
  107. //ntau-> ignition delay time
  108. int KSneq; //No: of equations in the BVP formulation of
  109. //the ignition delay problem
  110. realtype rel; //the relative perturbation factor used in
  111. //computing difference quotients
  112. realtype atol;
  113. realtype rtol;
  114. realtype t1; //Maximum time to run simulation
  115. int nreac;
  116. int pert_index; //index of reaction whose rate is perturbed
  117. bool sensitivityAnalysis;// boolean to activate perturbations for
  118. //sensitivity analysis
  119. bool sens; //if true, perform sensitivity analysis
  120. bool secondOrder; //if true, use second order finite differences
  121. FILE *output; //output file for temperature and species profiles
  122. FILE *ignSensOutput; //output file for ignition sensitivities
  123. FILE *speSensOutput; //output file for species sensitivities
  124. FILE *dPdtInput; //output file for species sensitivities
  125. gsl_interp_accel* acc;
  126. gsl_interp_accel* accdot;
  127. gsl_spline* spline;
  128. gsl_spline* splinedot;
  129. } *UserData;
  130. /* Set the initial conditions using this subroutine: */
  131. static int setInitialProfile(UserData data, N_Vector y);
  132. /* Evaluate the residue for the IVP using this subroutine: */
  133. static int residue(realtype t, N_Vector y, N_Vector ydot, void *user_data);
  134. static int parseInput(UserData data, int argc, char *argv[]);
  135. static int hunt(realtype x, realtype xx[], int n, int jguess);
  136. static void polint(realtype *xdata, realtype *f, int n, realtype x,realtype *y, realtype *dy);
  137. static void lookupDpdt(UserData data, realtype t, realtype *P, realtype *dPdt);
  138. /* Subroutine that prints the column titles in the output file: */
  139. static void printInstructions();
  140. /* Subroutine that prints the column titles in the output file: */
  141. static void printHeader(UserData data);
  142. static void printSensitivitiesHeader(UserData data);
  143. /* Subroutine that prints the output of the IVP into the output file contained
  144. * in the UserData structure: */
  145. static void printOutput(realtype t, N_Vector y, UserData data);
  146. /* Subroutine that prints the sensitivities into the output file (ignSensOutput)
  147. * contained in the UserData structure: */
  148. static void printIgnSensitivities(realtype sensCoeffs[], int indices[], UserData data);
  149. static int parsedPdt(UserData data);
  150. /* Print solver statistics for the IVP: */
  151. static void PrintFinalStats(void *cvode_mem);
  152. /* Subroutine that reports failure if memory hasn't been successfully allocated
  153. * to various objects: */
  154. static int check_flag(void *flagvalue, const char *funcname, int opt);
  155. int main(int argc, char *argv[])
  156. {
  157. clock_t start, end;
  158. double cpu_time_used;
  159. start = clock();
  160. int ier; //error flag
  161. UserData data; //User defined data
  162. data = NULL;
  163. /* Create and load problem data block. */
  164. data = (UserData) malloc(sizeof *data);
  165. ier=parseInput(data,argc,argv);
  166. if(ier==-1)return(-1);
  167. {
  168. /* Solver Memory:*/
  169. void *mem;
  170. mem=NULL;
  171. /* Save the initial mass fractions: */
  172. data->Y0=N_VNew_Serial(data->nsp);
  173. for(int k=1;k<=data->nsp;k++){
  174. Y0(k)=data->gas->massFraction(k-1);
  175. }
  176. /*Assign variable indices:*/
  177. data->nt=0;
  178. data->ny=data->nt+1;
  179. data->ntau=data->ny+data->nsp;
  180. /*Get and save the no: of reactions:*/
  181. data->nreac=data->gas->nReactions();
  182. /* Solution vector of the IVP: */
  183. N_Vector y;
  184. y = NULL;
  185. y = N_VNew_Serial(data->neq);
  186. ier=check_flag((void *)y, "N_VNew_Serial", 0);
  187. realtype t0, tret; //times
  188. t0=tret=ZERO;
  189. /*Set the initial values:*/
  190. setInitialProfile(data, y);
  191. /*Print out the column names and the initial values:*/
  192. printHeader(data);
  193. printOutput(0.0e0,y,data);
  194. /* Create a CVode solver object for solution of the IVP: */
  195. //mem = CVodeCreate(CV_BDF,CV_NEWTON);
  196. mem = CVodeCreate(CV_BDF);
  197. ier=check_flag((void *)mem, "CVodeCreate", 0);
  198. /*Associate the user data with the solver object: */
  199. ier = CVodeSetUserData(mem, data);
  200. ier=check_flag(&ier, "CVodeSetUserData", 1);
  201. /* Initialize the solver object by connecting it to the solution vector
  202. * y: */
  203. ier = CVodeInit(mem, residue, t0, y);
  204. ier=check_flag(&ier, "CVodeInit", 1);
  205. /*Set the tolerances: */
  206. ier = CVodeSStolerances(mem, data->rtol, data->atol);
  207. ier=check_flag(&ier, "IDASStolerances", 1);
  208. /* Create dense SUNMatrix for use in linear solves: */
  209. SUNMatrix A;
  210. A = SUNDenseMatrix(data->neq, data->neq);
  211. ier=check_flag((void *)A, "SUNDenseMatrix", 0);
  212. /* Create dense SUNLinearSolver object for use by CVode: */
  213. SUNLinearSolver LS;
  214. LS = SUNDenseLinearSolver(y, A);
  215. ier=check_flag((void *)LS, "SUNDenseLinearSolver", 0);
  216. /* Call CVDlsSetLinearSolver to attach the matrix and linear solver to
  217. * CVode: */
  218. ier = CVDlsSetLinearSolver(mem, LS, A);
  219. ier=check_flag(&ier, "CVDlsSetLinearSolver", 1);
  220. /* Use a difference quotient Jacobian: */
  221. ier = CVDlsSetJacFn(mem, NULL);
  222. ier=check_flag(&ier, "CVDlsSetJacFn", 1);
  223. /*Begin IVP solution; Save solutions in the temporary solution vector;
  224. * stop problem when the temperature reaches a certain value (like 400
  225. * K) corresponding to the time for ignition :*/
  226. int i=1;
  227. bool delayFound=false;
  228. while (tret<=data->t1) {
  229. if(T(1)>=data->TIgn && !delayFound){
  230. printf("Ignition Delay: %15.6es\n", tret);
  231. data->tauIgn=tret; //Save the ignition delay time
  232. delayFound=true;
  233. }
  234. ier = CVode(mem, data->t1, y, &tret, CV_ONE_STEP);
  235. if(check_flag(&ier, "CVode", 1)) {
  236. data->IVPSuccess=false;
  237. break;
  238. }
  239. else{
  240. data->IVPSuccess=true;
  241. }
  242. printOutput(tret,y,data);
  243. }
  244. if(data->IVPSuccess && data->sens){
  245. /*Enable sensitivity analysis:*/
  246. data->sensitivityAnalysis=true;
  247. data->rel=EPSILON;
  248. realtype oneOverRel=ONE/(data->rel);
  249. /*Create an array to store the logarithmic sensitivity
  250. * coefficients:*/
  251. realtype sensCoeffs[data->nreac];
  252. /*Create an array to store the indices of the reactions (needed
  253. * for sorting the sensitivity coefficients):*/
  254. int indices[data->nreac];
  255. double tauIgnPerturbedForward=0.0e0;
  256. double tauIgnPerturbedBackward=0.0e0;
  257. for(int j=0;j<data->nreac;j++){
  258. /*Save the index of the reaction whose collision
  259. * frequency (A in the Arrhenius law k=A*exp(-Eₐ/RT))
  260. * is to be perturbed:*/
  261. data->pert_index=j;
  262. /*Perturb forward:*/
  263. data->rel=EPSILON;
  264. /*Set the initial values:*/
  265. setInitialProfile(data, y);
  266. /* Initialize the solver object by connecting it to the solution vector
  267. * y: */
  268. ier = CVodeReInit(mem, t0, y);
  269. ier=check_flag(&ier, "CVodeReInit", 1);
  270. /*Rerun the ignition delay problem!*/
  271. printf("Solving forward perturbed problem %d:\n",j);
  272. tret=0.0e0;
  273. delayFound=false;
  274. while (tret<=data->t1) {
  275. if(T(1)>=data->TIgn && !delayFound){
  276. printf("\tIgnition Delay(forward): %15.6es\n", tret);
  277. tauIgnPerturbedForward=tret; //Save the ignition delay time
  278. delayFound=true;
  279. /*No point continuing solution*/
  280. break;
  281. }
  282. ier = CVode(mem, data->t1, y, &tret, CV_ONE_STEP);
  283. if(check_flag(&ier, "CVode", 1)) {
  284. data->IVPSuccess=false;
  285. break;
  286. }
  287. else{
  288. data->IVPSuccess=true;
  289. }
  290. //printOutput(tret,y,data);
  291. }
  292. if(!delayFound)tauIgnPerturbedForward=1e+99;
  293. if(data->secondOrder){
  294. /*Perturb backward:*/
  295. data->rel=-EPSILON;
  296. /*Set the initial values:*/
  297. setInitialProfile(data, y);
  298. /* Initialize the solver object by connecting it to the solution vector
  299. * y: */
  300. ier = CVodeReInit(mem, t0, y);
  301. ier=check_flag(&ier, "CVodeReInit", 1);
  302. /*Rerun the ignition delay problem!*/
  303. printf("Solving backward perturbed problem %d:\n",j);
  304. tret=0.0e0;
  305. delayFound=false;
  306. while (tret<=data->t1) {
  307. if(T(1)>=data->TIgn && !delayFound){
  308. printf("\tIgnition Delay(backward): %15.6es\n", tret);
  309. tauIgnPerturbedBackward=tret; //Save the ignition delay time
  310. delayFound=true;
  311. /*No point continuing solution*/
  312. break;
  313. }
  314. ier = CVode(mem, data->t1, y, &tret, CV_ONE_STEP);
  315. if(check_flag(&ier, "CVode", 1)) {
  316. data->IVPSuccess=false;
  317. break;
  318. }
  319. else{
  320. data->IVPSuccess=true;
  321. }
  322. //printOutput(tret,y,data);
  323. }
  324. if(!delayFound)tauIgnPerturbedBackward=1e+99;
  325. /*Take the finite difference quotient as an
  326. * approximation to the logarithmic sensitivity
  327. * coefficient:*/
  328. sensCoeffs[j]=oneOverRel*(tauIgnPerturbedForward-tauIgnPerturbedBackward)/(2.0e0*data->tauIgn);
  329. }else{
  330. sensCoeffs[j]=oneOverRel*(tauIgnPerturbedForward-data->tauIgn)/(data->tauIgn);
  331. }
  332. indices[j]=j;
  333. printf("\n");
  334. }
  335. /*Sort the sensitivities in ascending order. Note the advancing
  336. * of the beginning indices of the sensCoeffs and indices
  337. * arrays. This is due to the Numerical recipes convention for
  338. * array indexing used in sort subroutine:*/
  339. //sort(data->nreac,sensCoeffs-1,indices-1);
  340. /*Print out the sensitivities:*/
  341. printIgnSensitivities(sensCoeffs,indices,data);
  342. }
  343. /* Print remaining counters and free memory. */
  344. PrintFinalStats(mem);
  345. CVodeFree(&mem);
  346. N_VDestroy_Serial(y);
  347. }
  348. /*Free memory and delete all the vectors and user data:*/
  349. if(data->Y0!=NULL){
  350. N_VDestroy_Serial(data->Y0);
  351. printf("Initial Mass fraction Vector Deleted!\n");
  352. }
  353. if(data->x!=NULL){
  354. N_VDestroy_Serial(data->x);
  355. printf("Grid for BVP deleted!\n");
  356. }
  357. if(data->Pp!=NULL){
  358. N_VDestroy_Serial(data->Pp);
  359. printf("P array deleted!\n");
  360. }
  361. if(data->dPdtp!=NULL){
  362. N_VDestroy_Serial(data->dPdtp);
  363. printf("dPdt array deleted!\n");
  364. }
  365. if(data->gas!=NULL){
  366. delete data->gas;
  367. printf("Gas Deleted!\n");
  368. }
  369. if(data->imposedPdt){
  370. if(data->acc!=NULL){
  371. gsl_interp_accel_free(data->acc);
  372. }
  373. if(data->accdot!=NULL){
  374. gsl_interp_accel_free(data->accdot);
  375. }
  376. if(data->spline!=NULL){
  377. gsl_spline_free(data->spline);
  378. }
  379. if(data->splinedot!=NULL){
  380. gsl_spline_free(data->splinedot);
  381. }
  382. }
  383. if(data!=NULL){
  384. /* Free the user data */
  385. free(data);
  386. printf("User data structure deleted!\n");
  387. }
  388. end = clock();
  389. cpu_time_used = ((double) (end - start)) / CLOCKS_PER_SEC;
  390. printf("Elapsed cpu time: %15.6e s\n", cpu_time_used);
  391. return(0);
  392. }
  393. static int parseInput(UserData data, int argc, char *argv[]){
  394. /*Set defaults here:*/
  395. /*****************************************************/
  396. /*Relative tolerance for IVP:*/
  397. data->rtol = RCONST(1e-14);
  398. /*Absolute tolerance for IVP:*/
  399. data->atol = RCONST(1e-14);
  400. /*Final Time:*/
  401. data->t1 = RCONST(10.0);
  402. /*Solve constant pressure problem:*/
  403. data->constantVolume=false;
  404. /*Do not impose a P vs t curve:*/
  405. data->imposedPdt=false;
  406. /*Disable writing species sensitivities:*/
  407. data->writeSpeciesSensitivities=false;
  408. /*Disable sensitivity analysis:*/
  409. data->sens=false;
  410. /*Index to start with for pressure lookup (see hunt):*/
  411. data->jguess=0;
  412. /*Set rate of change of pressure to zero:*/
  413. data->dPdt=ZERO;
  414. /*Disable sensitivity analysis:*/
  415. data->sensitivityAnalysis=false;
  416. /*Use first order finite differences:*/
  417. data->secondOrder=false;
  418. /*Find the relative perturbation constant:*/
  419. //data->rel=SUNRsqrt(UNIT_ROUNDOFF);
  420. data->rel=0.1;
  421. /*Set flags that indicate success of various stages:*/
  422. data->IVPSuccess=false;
  423. /*****************************************************/
  424. int ier;
  425. int opt;
  426. char mech[BUFSIZE+1];
  427. char comp[BUFSIZE+1];
  428. bool enteredT0, enteredP, enteredMech, enteredComp;
  429. enteredT0=enteredP=enteredMech=enteredComp=false;
  430. while((opt=getopt(argc,argv,"a:r:T:P:m:c:t:vsd2")) != -1){
  431. switch(opt){
  432. case 'a':
  433. data->atol=RCONST(atof(optarg));
  434. break;
  435. case 'r':
  436. data->rtol=RCONST(atof(optarg));
  437. break;
  438. case 'T':
  439. data->T0=RCONST(atof(optarg));
  440. enteredT0=true;
  441. break;
  442. case 'P':
  443. data->P=RCONST(atof(optarg))*Cantera::OneAtm;
  444. enteredP=true;
  445. break;
  446. case 'm':
  447. snprintf(mech,BUFSIZE,"%s",optarg);
  448. enteredMech=true;
  449. break;
  450. case 'c':
  451. snprintf(comp,BUFSIZE,"%s",optarg);
  452. enteredComp=true;
  453. break;
  454. case 't':
  455. data->t1=RCONST(atof(optarg));
  456. enteredT0=true;
  457. break;
  458. case 'v':
  459. data->constantVolume=true;
  460. break;
  461. case 'S':
  462. data->writeSpeciesSensitivities=true;
  463. break;
  464. case 's':
  465. data->sens=true;
  466. break;
  467. case 'd':
  468. data->imposedPdt=true;
  469. break;
  470. case '2':
  471. data->secondOrder=true;
  472. break;
  473. default:
  474. printInstructions();
  475. return(-1);
  476. }
  477. }
  478. if(!enteredT0){
  479. printf("Not specified initial Temperature! Exiting...\n");
  480. printInstructions();
  481. return(-1);
  482. }
  483. else if(!enteredP){
  484. printf("Not specified Pressure! Exiting...\n");
  485. printInstructions();
  486. return(-1);
  487. }
  488. else if(!enteredMech){
  489. printf("Not specified Mechanism! Exiting...\n");
  490. printInstructions();
  491. return(-1);
  492. }
  493. else if(!enteredComp){
  494. printf("Not specified Composition! Exiting...\n");
  495. printInstructions();
  496. return(-1);
  497. }
  498. else{
  499. if(data->imposedPdt){
  500. ier=parsedPdt(data);
  501. if(ier==-1){
  502. return(-1);
  503. }
  504. }
  505. printf("Required inputs provided:\n");
  506. printf("\natol: %15.6e\n", data->atol);
  507. printf("\nrtol: %15.6e\n", data->rtol);
  508. printf("\nT0 : %15.6e K\n", data->T0);
  509. printf("\nP : %15.6e Pa\n", data->P);
  510. printf("\nmech: %s\n", mech);
  511. printf("\ncomp: %s\n", comp);
  512. printf("\nt1 : %15.6e s\n", data->t1);
  513. printf("\nconstantVolume : %d\n", data->constantVolume);
  514. printf("\nimposedPdt : %d\n", data->imposedPdt);
  515. printf("\nwriteSpeciesSensitivities : %d\n",
  516. data->writeSpeciesSensitivities);
  517. printf("Proceeding...\n\n");
  518. }
  519. /*Define Gas here:*/
  520. data->gas = new Cantera::IdealGasMix(mech);
  521. /* Set the initial state of the gas: */
  522. data->gas->setState_TPX(data->T0,
  523. data->P,
  524. comp);
  525. /* Create output file for the solution of the IVP: */
  526. data->output=fopen("output.dat","w");
  527. /* Create output file for the ignition sensitivities (post BVP): */
  528. data->ignSensOutput=fopen("ignitionSensitivities.dat","w");
  529. /* Create output file for the species sensitivities (post BVP): */
  530. data->speSensOutput=fopen("speciesSensitivities.dat","w");
  531. data->rho0=data->gas->density();
  532. if(data->constantVolume){
  533. data->gas->equilibrate("UV");
  534. }
  535. else{
  536. data->gas->equilibrate("HP");
  537. }
  538. data->TIgn=data->T0+(data->gas->temperature()
  539. -data->T0)*RCONST(0.20);
  540. printf("Ignition Temperature: %15.6e K\n",data->TIgn);
  541. data->gas->setState_TPX(data->T0,
  542. data->P,
  543. comp);
  544. /*Get and save the no: of species:*/
  545. data->nsp=data->gas->nSpecies();
  546. /*set the no: of variables for the IVP:*/
  547. data->nvar=data->nsp+1;
  548. /*set the no: of equations for the IVP:*/
  549. data->neq=data->nvar;
  550. return(0);
  551. }
  552. static int setInitialProfile(UserData data, N_Vector y)
  553. {
  554. /*This routine sets the initial temperature and mass fractions for the
  555. * solution of the initial value problem:*/
  556. N_VConst(ZERO, y);
  557. T(1)=data->T0;
  558. for (int k = 1; k <=data->nsp; k++) {
  559. Y(1,k)=Y0(k);
  560. }
  561. return(0);
  562. }
  563. static int residue(realtype t, N_Vector y, N_Vector ydot, void *user_data)
  564. {
  565. /*This is the sub-routine that computes the right hand side F(y) in the
  566. * problem ∂y/∂t = F(y).
  567. *
  568. * The energy conservation equation is
  569. * ∂T/∂t = (1/ρcₚ)(dP/dt -∑ ώᵢhᵢ)
  570. *
  571. * T has units of K
  572. * hᵢ has units J/kmol, so we must multiply the enthalpy
  573. * defined above (getEnthalpy_RT) by T (K) and the gas constant
  574. * (J/kmol/K) to get the right units.
  575. * ρ has units of kg/m³
  576. * cₚ has units J/kg/K
  577. * ώᵢ has units of kmol/m³/s
  578. * dP/dt has units of Pa/s
  579. *
  580. * In the case of a constant volume problem, the energy conservation
  581. * equation can be re-written as
  582. * ∂T/∂t = -(1/ρcᵥ)∑ ώᵢeᵢ
  583. * eᵢ has units J/kmol
  584. * cᵥ has units J/kg/K
  585. *
  586. *
  587. * The species conservation equation is
  588. * ∂Yᵢ/∂t = ώᵢWᵢ/ρ
  589. * Wᵢ has units of kg/kmol
  590. */
  591. /*Assign data structure type to user_data:*/
  592. UserData data;
  593. data=(UserData)user_data;
  594. /*Get no: of species:*/
  595. int nsp=data->nsp;
  596. /*Create temporary arrays to store enthalpy (non-dimensional) and ώᵢ:*/
  597. realtype wdot[nsp];
  598. realtype enthalpy[nsp];
  599. try {
  600. /*If sensitivity analysis has been enabled, perturb the collision
  601. * frequency by using cantera's setMultiplier function:*/
  602. if(data->sensitivityAnalysis){
  603. data->gas->setMultiplier(data->pert_index,ONE+data->rel);
  604. //printf("%d\t%15.9e\n",data->pert_index,ONE+data->rel);
  605. }
  606. /*Set the gas conditions:*/
  607. if(data->constantVolume){
  608. data->gas->setMassFractions_NoNorm(&Y(1,1));
  609. data->gas->setTemperature(T(1));
  610. data->gas->setDensity(data->rho0);
  611. data->P=data->gas->pressure();
  612. }
  613. else if(data->imposedPdt){
  614. data->gas->setMassFractions_NoNorm(&Y(1,1));
  615. data->gas->setTemperature(T(1));
  616. realtype P,dPdt;
  617. P=dPdt=0.0e0;
  618. lookupDpdt(data, t, &P, &dPdt);
  619. data->P=P*Cantera::OneAtm;
  620. data->dPdt=dPdt*Cantera::OneAtm;
  621. data->gas->setPressure(data->P);
  622. }
  623. else{
  624. data->gas->setMassFractions_NoNorm(&Y(1,1));
  625. data->gas->setTemperature(T(1));
  626. data->gas->setPressure(data->P);
  627. }
  628. realtype rho=data->gas->density(); //get ρ
  629. realtype cp=data->gas->cp_mass(); //get cₚ
  630. data->gas->getNetProductionRates(wdot); //get ώᵢ
  631. data->gas->getEnthalpy_RT(enthalpy); //get hᵢ/RT
  632. if(data->constantVolume){
  633. for(int k=1;k<=nsp;k++){
  634. enthalpy(k)=enthalpy(k)-ONE;
  635. }
  636. cp=data->gas->cv_mass();
  637. }
  638. realtype sum=ZERO;
  639. for(int k=1;k<=nsp;k++){
  640. Ydot(1,k)=wdot(k)*(data->gas->molecularWeight(k-1))/rho;
  641. sum=sum+wdot(k)*enthalpy(k)*T(1);
  642. }
  643. sum=sum*Cantera::GasConstant;
  644. Tdot(1)=(data->dPdt-sum)/(rho*cp);
  645. /*If sensitivity analysis has been enabled, *un*perturb the collision
  646. * frequency:*/
  647. if(data->sensitivityAnalysis){
  648. data->gas->setMultiplier(data->pert_index,ONE);
  649. }
  650. } catch (Cantera::CanteraError& err) {
  651. printf("Error:\n");
  652. printf("%s\n",err.what());
  653. return(-1);
  654. }
  655. return(0);
  656. }
  657. static void lookupDpdt(UserData data, realtype t, realtype *P, realtype *dPdt)
  658. {
  659. realtype *tData, *PData, *dPdtData;
  660. tData = N_VGetArrayPointer_Serial(data->tArray);
  661. PData = N_VGetArrayPointer_Serial(data->PArray);
  662. dPdtData = N_VGetArrayPointer_Serial(data->dPdtArray);
  663. int jguess=data->jguess;
  664. int k=0;
  665. int safel=0;
  666. int nOrder=4;
  667. realtype dy;
  668. int npts=data->tArraySize;
  669. if(t<=data->t1){
  670. *P=gsl_spline_eval(data->spline,t,data->acc);
  671. *dPdt=gsl_spline_eval(data->splinedot,t,data->accdot);
  672. }
  673. else{
  674. *P=P(npts-1);
  675. *dPdt=dPdt(npts-1);
  676. }
  677. }
  678. static int hunt(realtype x, realtype xx[], int n, int jguess)
  679. {
  680. int jlo=jguess;
  681. int jm,jhi,inc;
  682. int ascnd;
  683. ascnd=(xx[n-1] >= xx[0]);
  684. if (jlo <= 0 || jlo > n-1) {
  685. jlo=0;
  686. jhi=n;
  687. } else {
  688. inc=1;
  689. if ((x >= xx[jlo]) == ascnd) {
  690. if (jlo == n-1) return(jlo);
  691. jhi=(jlo)+1;
  692. while ((x >= xx[jhi]) == ascnd) {
  693. jlo=jhi;
  694. inc += inc;
  695. jhi=(jlo)+inc;
  696. if (jhi > n-1) {
  697. jhi=n;
  698. break;
  699. }
  700. }
  701. } else {
  702. if (jlo == 1) {
  703. jlo=0;
  704. return (jlo);
  705. }
  706. jhi=(jlo)--;
  707. while ((x < xx[jlo]) == ascnd) {
  708. jhi=(jlo);
  709. inc <<= 1;
  710. if (inc >= jhi) {
  711. jlo=0;
  712. break;
  713. }
  714. else jlo=jhi-inc;
  715. }
  716. }
  717. }
  718. while (jhi-(jlo) != 1) {
  719. jm=(jhi+(jlo)) >> 1;
  720. if ((x >= xx[jm]) == ascnd)
  721. jlo=jm;
  722. else
  723. jhi=jm;
  724. }
  725. if (x == xx[n-1]) jlo=n-2;
  726. if (x == xx[0]) jlo=1;
  727. return(jlo);
  728. }
  729. static void polint(realtype *xdata, realtype *f, int n, realtype x, realtype *y, realtype *dy){
  730. int i,m,ns=1;
  731. realtype den,dif,dift,ho,hp,w;
  732. realtype c[n+1],d[n+1];
  733. dif=fabs(x-xdata[1]);
  734. for (i=1;i<=n;i++) {
  735. if ( (dift=fabs(x-xdata[i])) < dif) {
  736. ns=i;
  737. dif=dift;
  738. }
  739. c[i]=f[i];
  740. d[i]=f[i];
  741. }
  742. *y=f[ns--];
  743. for (m=1;m<n;m++) {
  744. for (i=1;i<=n-m;i++) {
  745. ho=xdata[i]-x;
  746. hp=xdata[i+m]-x;
  747. w=c[i+1]-d[i];
  748. if ( (den=ho-hp) == 0.0) printf("Error in routine polint!\n");
  749. den=w/den;
  750. d[i]=hp*den;
  751. c[i]=ho*den;
  752. }
  753. *y += (*dy=(2*ns < (n-m) ? c[ns+1] : d[ns--]));
  754. }
  755. }
  756. #define SWAP(a,b) temp=(a);(a)=(b);(b)=temp;
  757. #define M 7
  758. #define NSTACK 50
  759. #define NR_END 1
  760. #define FREE_ARG char*
  761. static unsigned long *lvector(long nl, long nh)
  762. /* allocate an unsigned long vector with subscript range v[nl..nh] */
  763. {
  764. unsigned long *v;
  765. v=(unsigned long *)malloc((size_t) ((nh-nl+1+NR_END)*sizeof(long)));
  766. if (!v) printf("allocation failure in lvector()");
  767. return v-nl+NR_END;
  768. }
  769. static void free_lvector(unsigned long *v, long nl, long nh)
  770. /* free an unsigned long vector allocated with lvector() */
  771. {
  772. free((FREE_ARG) (v+nl-NR_END));
  773. }
  774. static void sort(unsigned long n, double arr[], int ind[])
  775. {
  776. unsigned long i,ir=n,j,k,l=1,*istack;
  777. int jstack=0;
  778. double a,temp;
  779. int b;
  780. istack=lvector(1,NSTACK);
  781. for (;;) {
  782. if (ir-l < M) {
  783. for (j=l+1;j<=ir;j++) {
  784. a=arr[j];
  785. b=ind[j];
  786. for (i=j-1;i>=l;i--) {
  787. if (arr[i] <= a) break;
  788. arr[i+1]=arr[i];
  789. ind[i+1]=ind[i];
  790. }
  791. arr[i+1]=a;
  792. ind[i+1]=b;
  793. }
  794. if (jstack == 0) break;
  795. ir=istack[jstack--];
  796. l=istack[jstack--];
  797. } else {
  798. k=(l+ir) >> 1;
  799. SWAP(arr[k],arr[l+1])
  800. SWAP(ind[k],ind[l+1])
  801. if (arr[l] > arr[ir]) {
  802. SWAP(arr[l],arr[ir])
  803. SWAP(ind[l],ind[ir])
  804. }
  805. if (arr[l+1] > arr[ir]) {
  806. SWAP(arr[l+1],arr[ir])
  807. SWAP(ind[l+1],ind[ir])
  808. }
  809. if (arr[l] > arr[l+1]) {
  810. SWAP(arr[l],arr[l+1])
  811. SWAP(ind[l],ind[l+1])
  812. }
  813. i=l+1;
  814. j=ir;
  815. a=arr[l+1];
  816. b=ind[l+1];
  817. for (;;) {
  818. do i++; while (arr[i] < a);
  819. do j--; while (arr[j] > a);
  820. if (j < i) break;
  821. SWAP(arr[i],arr[j]);
  822. SWAP(ind[i],ind[j]);
  823. }
  824. arr[l+1]=arr[j];
  825. ind[l+1]=ind[j];
  826. arr[j]=a;
  827. ind[j]=b;
  828. jstack += 2;
  829. if (jstack > NSTACK) printf("NSTACK too small in sort.");
  830. if (ir-i+1 >= j-l) {
  831. istack[jstack]=ir;
  832. istack[jstack-1]=i;
  833. ir=j-1;
  834. } else {
  835. istack[jstack]=j-1;
  836. istack[jstack-1]=l;
  837. l=i;
  838. }
  839. }
  840. }
  841. free_lvector(istack,1,NSTACK);
  842. }
  843. #undef M
  844. #undef NSTACK
  845. #undef SWAP
  846. static void printInstructions(){
  847. printf("\nInputs either incomplete or wrong!\n");
  848. printf("\n-a <absolute tolerance for IVP>\n");
  849. printf("\n-r <relative tolerance for IVP>\n");
  850. printf("\n-T <Initial Temperature in Kelvin>\n");
  851. printf("\n-P <Initial Pressure in atm>\n");
  852. printf("\n-m <mechanism file (cti or xml)>\n");
  853. printf("\n-c <composition in mole fractions>\n");
  854. printf("\n-t <Total simulation time in seconds>\n");
  855. printf("\n-v :Enables constant volume simulation\n");
  856. printf("\n-s :Enables ignition delay sensitivity output\n");
  857. printf("\n-S :Enables species mass fraction sensitivity output (works only with -s)\n");
  858. printf("\n-d :Enables manual dPdt entryspecies sensitivity output\n");
  859. printf("\n-2 :Enables 2nd order accurate finite differences\n");
  860. printf("\nexample: <executable> ");
  861. printf("-T 1200.0 -P 1.0 -m gri30.cti");
  862. printf(" -c H2:1.0,N2:3.76,O2:1.0");
  863. printf(" -t 1e-03\n");
  864. }
  865. static void printHeader(UserData data)
  866. {
  867. fprintf((data->output), "%15s\t","#1");
  868. for (int k = 1; k <=data->nvar; k++) {
  869. fprintf((data->output), "%15d\t",k+1);
  870. }
  871. fprintf((data->output), "\n");
  872. fprintf((data->output), "%15s\t%15s\t%15s\t",
  873. "#time(s)","Temp(K)","Pressure(Pa)");
  874. for (int k = 1; k <=data->nsp; k++) {
  875. fprintf((data->output), "%15s\t",
  876. data->gas->speciesName(k-1).c_str());
  877. }
  878. fprintf((data->output), "\n");
  879. }
  880. static void printSensitivitiesHeader(UserData data)
  881. {
  882. fprintf((data->speSensOutput), "%15s\t","#1");
  883. for (int k = 1; k <=data->nvar; k++) {
  884. fprintf((data->speSensOutput), "%15d\t",k+1);
  885. }
  886. fprintf((data->speSensOutput), "\n");
  887. fprintf((data->speSensOutput), "%15s\t%15s\t",
  888. "#time(s)","Temp(K)");
  889. for (int k = 1; k <=data->nsp; k++) {
  890. fprintf((data->speSensOutput), "%15s\t",
  891. data->gas->speciesName(k-1).c_str());
  892. }
  893. fprintf((data->speSensOutput), "\n");
  894. }
  895. static void printOutput(realtype t, N_Vector y, UserData data)
  896. {
  897. fprintf((data->output), "%15.6e\t%15.6e\t%15.6e\t",t,T(1),data->P);
  898. for (int k = 1; k <=data->nsp; k++) {
  899. fprintf((data->output), "%15.6e\t",Y(1,k));
  900. }
  901. fprintf((data->output), "\n");
  902. }
  903. static void printIgnSensitivities(realtype sensCoeffs[], int indices[], UserData data)
  904. {
  905. char buf[100];
  906. std::string line;
  907. for(int j=0;j<data->nreac;j++){
  908. line=data->gas->reactionString(indices[j]);
  909. sprintf(buf,"%s",line.c_str());
  910. fprintf((data->ignSensOutput), "%d\t\"%35s\"\t%15.6e\n",indices[j],buf,sensCoeffs[j]);
  911. }
  912. //for(int j=0;j<data->nreac;j++){
  913. // fprintf((data->ignSensOutput), "%d\t%15.6e\n",indices[j],sensCoeffs[j]);
  914. //}
  915. }
  916. static int parsedPdt(UserData data){
  917. /* Open file containing P and dPdt as functions of time: */
  918. data->dPdtInput=fopen("dPdt.dat","r");
  919. if(data->dPdtInput==NULL){
  920. printf("The dPdt.dat file wasn't found!\n");
  921. return(-1);
  922. }
  923. char buf[1000];
  924. char comment[1];
  925. char *ret;
  926. int i=0;
  927. int j=0;
  928. while (fgets(buf,100, data->dPdtInput)!=NULL){
  929. comment[0]=buf[0];
  930. if(strncmp(comment,"#",1)!=0 &&
  931. strncmp(comment,"\n",1)!=0){
  932. i++;
  933. }
  934. }
  935. printf("No: of rows with numbers: %d\n",i);
  936. rewind(data->dPdtInput);
  937. int nPts=i;
  938. data->tArraySize=nPts;
  939. i=0;
  940. data->tArray = N_VNew_Serial(nPts);
  941. data->PArray = N_VNew_Serial(nPts);
  942. data->dPdtArray = N_VNew_Serial(nPts);
  943. while (fgets(buf,100, data->dPdtInput)!=NULL){
  944. comment[0]=buf[0];
  945. if(strncmp(comment,"#",1)==0 ||
  946. strncmp(comment,"\n",1)==0){
  947. printf("Comment! Skip Line!\n");
  948. }
  949. else{
  950. j=0;
  951. ret=strtok(buf,", \t");
  952. t(i)=atof(ret);
  953. j++;
  954. while(ret!=NULL){
  955. ret=strtok(NULL,", \t");
  956. if(j==1){
  957. P(i)=atof(ret);
  958. }
  959. else if(j==2){
  960. dPdt(i)=atof(ret);
  961. }
  962. j++;
  963. }
  964. i++;
  965. }
  966. }
  967. fclose(data->dPdtInput);
  968. //for(int k=0;k<nPts;k++){
  969. // printf("%15.6e\t%15.6e\t%15.6e\n",t(k),P(k),dPdt(k));
  970. //}
  971. data->t1=t(nPts-1);
  972. data->P=P(0)*Cantera::OneAtm;
  973. /*check the polynomial interpolation (testing only)*/
  974. //realtype t,P,dPdt;
  975. //t=1e-03;
  976. //P=dPdt=0.0e0;
  977. //lookupDpdt(data, t, &P, &dPdt);
  978. //printf("Interpolated P value %15.6e. \n",P);
  979. //printf("Interpolated dPdt value %15.6e. \n",dPdt);
  980. //
  981. //GSL additions here:
  982. data->acc=gsl_interp_accel_alloc();
  983. data->spline=gsl_spline_alloc(gsl_interp_steffen,data->tArraySize);
  984. data->accdot=gsl_interp_accel_alloc();
  985. data->splinedot=gsl_spline_alloc(gsl_interp_steffen,data->tArraySize);
  986. double* Pdata;
  987. double* dPdtdata;
  988. double* tdata;
  989. tdata=N_VGetArrayPointer_Serial(data->tArray);
  990. Pdata=N_VGetArrayPointer_Serial(data->PArray);
  991. dPdtdata=N_VGetArrayPointer_Serial(data->dPdtArray);
  992. gsl_spline_init(data->spline,tdata,Pdata,data->tArraySize);
  993. gsl_spline_init(data->splinedot,tdata,dPdtdata,data->tArraySize);
  994. return(0);
  995. }
  996. static void PrintFinalStats(void *cvode_mem)
  997. {
  998. long int nst, nfe, nsetups, nje, nfeLS, nni, ncfn, netf, nge;
  999. int flag;
  1000. flag = CVodeGetNumSteps(cvode_mem, &nst);
  1001. check_flag(&flag, "CVodeGetNumSteps", 1);
  1002. flag = CVodeGetNumRhsEvals(cvode_mem, &nfe);
  1003. check_flag(&flag, "CVodeGetNumRhsEvals", 1);
  1004. flag = CVodeGetNumLinSolvSetups(cvode_mem, &nsetups);
  1005. check_flag(&flag, "CVodeGetNumLinSolvSetups", 1);
  1006. flag = CVodeGetNumErrTestFails(cvode_mem, &netf);
  1007. check_flag(&flag, "CVodeGetNumErrTestFails", 1);
  1008. flag = CVodeGetNumNonlinSolvIters(cvode_mem, &nni);
  1009. check_flag(&flag, "CVodeGetNumNonlinSolvIters", 1);
  1010. flag = CVodeGetNumNonlinSolvConvFails(cvode_mem, &ncfn);
  1011. check_flag(&flag, "CVodeGetNumNonlinSolvConvFails", 1);
  1012. flag = CVDlsGetNumJacEvals(cvode_mem, &nje);
  1013. check_flag(&flag, "CVDlsGetNumJacEvals", 1);
  1014. flag = CVDlsGetNumRhsEvals(cvode_mem, &nfeLS);
  1015. check_flag(&flag, "CVDlsGetNumRhsEvals", 1);
  1016. flag = CVodeGetNumGEvals(cvode_mem, &nge);
  1017. check_flag(&flag, "CVodeGetNumGEvals", 1);
  1018. printf("\nFinal CVode Statistics:\n");
  1019. printf("nst = %-6ld nfe = %-6ld nsetups = %-6ld nfeLS = %-6ld nje = %ld\n",
  1020. nst, nfe, nsetups, nfeLS, nje);
  1021. printf("nni = %-6ld ncfn = %-6ld netf = %-6ld nge = %ld\n \n",
  1022. nni, ncfn, netf, nge);
  1023. }
  1024. static int check_flag(void *flagvalue, const char *funcname, int opt)
  1025. {
  1026. int *errflag;
  1027. /* Check if SUNDIALS function returned NULL pointer - no memory allocated */
  1028. if (opt == 0 && flagvalue == NULL) {
  1029. fprintf(stderr,
  1030. "\nSUNDIALS_ERROR: %s() failed - returned NULL pointer\n\n",
  1031. funcname);
  1032. return(1);
  1033. }
  1034. /* Check if flag < 0 */
  1035. else if (opt == 1) {
  1036. errflag = (int *) flagvalue;
  1037. if (*errflag < 0) {
  1038. fprintf(stderr,
  1039. "\nSUNDIALS_ERROR: %s() failed with flag = %d\n\n",
  1040. funcname, *errflag);
  1041. return(1);
  1042. }
  1043. }
  1044. /* Check if function returned NULL pointer - no memory allocated */
  1045. else if (opt == 2 && flagvalue == NULL) {
  1046. fprintf(stderr,
  1047. "\nMEMORY_ERROR: %s() failed - returned NULL pointer\n\n",
  1048. funcname);
  1049. return(1);
  1050. }
  1051. return(0);
  1052. }