xref: /petsc/src/ts/tutorials/extchem.c (revision 4dfa11a44d5adf2389f1d3acbc8f3c1116dc6c3a)
1 static const char help[] = "Integrate chemistry using TChem.\n";
2 
3 #include <petscts.h>
4 
5 #if defined(PETSC_HAVE_TCHEM)
6 #if defined(MAX)
7 #undef MAX
8 #endif
9 #if defined(MIN)
10 #undef MIN
11 #endif
12 #include <TC_params.h>
13 #include <TC_interface.h>
14 #else
15 #error TChem is required for this example.  Reconfigure PETSc using --download-tchem.
16 #endif
17 /*
18     See extchem.example.1 for how to run an example
19 
20     See also h2_10sp.inp for another example
21 
22     Determine sensitivity of final temperature on each variables initial conditions
23     -ts_dt 1.e-5 -ts_type cn -ts_adjoint_solve -ts_adjoint_view_solution draw
24 
25     The solution for component i = 0 is the temperature.
26 
27     The solution, i > 0, is the mass fraction, massf[i], of species i, i.e. mass of species i/ total mass of all species
28 
29     The mole fraction molef[i], i > 0, is the number of moles of a species/ total number of moles of all species
30         Define M[i] = mass per mole of species i then
31         molef[i] = massf[i]/(M[i]*(sum_j massf[j]/M[j]))
32 
33     FormMoleFraction(User,massf,molef) converts the mass fraction solution of each species to the mole fraction of each species.
34 
35     These are other data sets for other possible runs
36        https://www-pls.llnl.gov/data/docs/science_and_technology/chemistry/combustion/n_heptane_v3.1_therm.dat
37        https://www-pls.llnl.gov/data/docs/science_and_technology/chemistry/combustion/nc7_ver3.1_mech.txt
38 
39 */
40 typedef struct _User *User;
41 struct _User {
42   PetscReal pressure;
43   int       Nspec;
44   int       Nreac;
45   PetscReal Tini;
46   double   *tchemwork;
47   double   *Jdense; /* Dense array workspace where Tchem computes the Jacobian */
48   PetscInt *rows;
49   char    **snames;
50 };
51 
52 static PetscErrorCode PrintSpecies(User, Vec);
53 static PetscErrorCode MassFractionToMoleFraction(User, Vec, Vec *);
54 static PetscErrorCode MoleFractionToMassFraction(User, Vec, Vec *);
55 static PetscErrorCode FormRHSFunction(TS, PetscReal, Vec, Vec, void *);
56 static PetscErrorCode FormRHSJacobian(TS, PetscReal, Vec, Mat, Mat, void *);
57 static PetscErrorCode FormInitialSolution(TS, Vec, void *);
58 static PetscErrorCode ComputeMassConservation(Vec, PetscReal *, void *);
59 static PetscErrorCode MonitorMassConservation(TS, PetscInt, PetscReal, Vec, void *);
60 static PetscErrorCode MonitorTempature(TS, PetscInt, PetscReal, Vec, void *);
61 
62 #define PetscCallTC(ierr) \
63   do { PetscCheck(!ierr, PETSC_COMM_SELF, PETSC_ERR_LIB, "Error in TChem library, return code %d", ierr); } while (0)
64 
65 int main(int argc, char **argv) {
66   TS                ts; /* time integrator */
67   TSAdapt           adapt;
68   Vec               X, lambda; /* solution vector */
69   Mat               J;         /* Jacobian matrix */
70   PetscInt          steps;
71   PetscReal         ftime, dt;
72   char              chemfile[PETSC_MAX_PATH_LEN], thermofile[PETSC_MAX_PATH_LEN], lchemfile[PETSC_MAX_PATH_LEN], lthermofile[PETSC_MAX_PATH_LEN], lperiodic[PETSC_MAX_PATH_LEN];
73   const char       *periodic = "file://${PETSC_DIR}/${PETSC_ARCH}/share/periodictable.dat";
74   struct _User      user; /* user-defined work context */
75   TSConvergedReason reason;
76   char            **snames, *names;
77   PetscInt          i;
78   TSTrajectory      tj;
79   PetscBool         flg = PETSC_FALSE, tflg = PETSC_FALSE, found;
80 
81   PetscFunctionBeginUser;
82   PetscCall(PetscInitialize(&argc, &argv, (char *)0, help));
83   PetscOptionsBegin(PETSC_COMM_WORLD, NULL, "Chemistry solver options", "");
84   PetscCall(PetscOptionsString("-chem", "CHEMKIN input file", "", chemfile, chemfile, sizeof(chemfile), NULL));
85   PetscCall(PetscFileRetrieve(PETSC_COMM_WORLD, chemfile, lchemfile, PETSC_MAX_PATH_LEN, &found));
86   PetscCheck(found, PETSC_COMM_WORLD, PETSC_ERR_FILE_OPEN, "Cannot download %s and no local version %s", chemfile, lchemfile);
87   PetscCall(PetscOptionsString("-thermo", "NASA thermo input file", "", thermofile, thermofile, sizeof(thermofile), NULL));
88   PetscCall(PetscFileRetrieve(PETSC_COMM_WORLD, thermofile, lthermofile, PETSC_MAX_PATH_LEN, &found));
89   PetscCheck(found, PETSC_COMM_WORLD, PETSC_ERR_FILE_OPEN, "Cannot download %s and no local version %s", thermofile, lthermofile);
90   user.pressure = 1.01325e5; /* Pascal */
91   PetscCall(PetscOptionsReal("-pressure", "Pressure of reaction [Pa]", "", user.pressure, &user.pressure, NULL));
92   user.Tini = 1000; /* Kelvin */
93   PetscCall(PetscOptionsReal("-Tini", "Initial temperature [K]", "", user.Tini, &user.Tini, NULL));
94   PetscCall(PetscOptionsBool("-monitor_mass", "Monitor the total mass at each timestep", "", flg, &flg, NULL));
95   PetscCall(PetscOptionsBool("-monitor_temp", "Monitor the temperature each timestep", "", tflg, &tflg, NULL));
96   PetscOptionsEnd();
97 
98   /* tchem requires periodic table in current directory */
99   PetscCall(PetscFileRetrieve(PETSC_COMM_WORLD, periodic, lperiodic, PETSC_MAX_PATH_LEN, &found));
100   PetscCheck(found, PETSC_COMM_WORLD, PETSC_ERR_FILE_OPEN, "Cannot located required periodic table %s or local version %s", periodic, lperiodic);
101 
102   PetscCallTC(TC_initChem(lchemfile, lthermofile, 0, 1.0));
103   TC_setThermoPres(user.pressure);
104   user.Nspec = TC_getNspec();
105   user.Nreac = TC_getNreac();
106   /*
107       Get names of all species in easy to use array
108   */
109   PetscCall(PetscMalloc1((user.Nspec + 1) * LENGTHOFSPECNAME, &names));
110   PetscCall(PetscStrcpy(names, "Temp"));
111   TC_getSnames(user.Nspec, names + LENGTHOFSPECNAME);
112   PetscCall(PetscMalloc1((user.Nspec + 2), &snames));
113   for (i = 0; i < user.Nspec + 1; i++) snames[i] = names + i * LENGTHOFSPECNAME;
114   snames[user.Nspec + 1] = NULL;
115   PetscCall(PetscStrArrayallocpy((const char *const *)snames, &user.snames));
116   PetscCall(PetscFree(snames));
117   PetscCall(PetscFree(names));
118 
119   PetscCall(PetscMalloc3(user.Nspec + 1, &user.tchemwork, PetscSqr(user.Nspec + 1), &user.Jdense, user.Nspec + 1, &user.rows));
120   PetscCall(VecCreateSeq(PETSC_COMM_SELF, user.Nspec + 1, &X));
121 
122   /* PetscCall(MatCreateSeqAIJ(PETSC_COMM_SELF,user.Nspec+1,user.Nspec+1,PETSC_DECIDE,NULL,&J)); */
123   PetscCall(MatCreateSeqDense(PETSC_COMM_SELF, user.Nspec + 1, user.Nspec + 1, NULL, &J));
124   PetscCall(MatSetFromOptions(J));
125 
126   /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
127      Create timestepping solver context
128      - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
129   PetscCall(TSCreate(PETSC_COMM_WORLD, &ts));
130   PetscCall(TSSetType(ts, TSARKIMEX));
131   PetscCall(TSARKIMEXSetFullyImplicit(ts, PETSC_TRUE));
132   PetscCall(TSARKIMEXSetType(ts, TSARKIMEX4));
133   PetscCall(TSSetRHSFunction(ts, NULL, FormRHSFunction, &user));
134   PetscCall(TSSetRHSJacobian(ts, J, J, FormRHSJacobian, &user));
135 
136   if (flg) PetscCall(TSMonitorSet(ts, MonitorMassConservation, NULL, NULL));
137   if (tflg) PetscCall(TSMonitorSet(ts, MonitorTempature, &user, NULL));
138 
139   ftime = 1.0;
140   PetscCall(TSSetMaxTime(ts, ftime));
141   PetscCall(TSSetExactFinalTime(ts, TS_EXACTFINALTIME_STEPOVER));
142 
143   /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
144      Set initial conditions
145    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
146   PetscCall(FormInitialSolution(ts, X, &user));
147   PetscCall(TSSetSolution(ts, X));
148   dt = 1e-10; /* Initial time step */
149   PetscCall(TSSetTimeStep(ts, dt));
150   PetscCall(TSGetAdapt(ts, &adapt));
151   PetscCall(TSAdaptSetStepLimits(adapt, 1e-12, 1e-4)); /* Also available with -ts_adapt_dt_min/-ts_adapt_dt_max */
152   PetscCall(TSSetMaxSNESFailures(ts, -1));             /* Retry step an unlimited number of times */
153 
154   /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
155      Set runtime options
156    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
157   PetscCall(TSSetFromOptions(ts));
158 
159   /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
160      Set final conditions for sensitivities
161    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
162   PetscCall(VecDuplicate(X, &lambda));
163   PetscCall(TSSetCostGradients(ts, 1, &lambda, NULL));
164   PetscCall(VecSetValue(lambda, 0, 1.0, INSERT_VALUES));
165   PetscCall(VecAssemblyBegin(lambda));
166   PetscCall(VecAssemblyEnd(lambda));
167 
168   PetscCall(TSGetTrajectory(ts, &tj));
169   if (tj) {
170     PetscCall(TSTrajectorySetVariableNames(tj, (const char *const *)user.snames));
171     PetscCall(TSTrajectorySetTransform(tj, (PetscErrorCode(*)(void *, Vec, Vec *))MassFractionToMoleFraction, NULL, &user));
172   }
173 
174   /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
175      Pass information to graphical monitoring routine
176    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
177   PetscCall(TSMonitorLGSetVariableNames(ts, (const char *const *)user.snames));
178   PetscCall(TSMonitorLGSetTransform(ts, (PetscErrorCode(*)(void *, Vec, Vec *))MassFractionToMoleFraction, NULL, &user));
179 
180   /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
181      Solve ODE
182      - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
183   PetscCall(TSSolve(ts, X));
184   PetscCall(TSGetSolveTime(ts, &ftime));
185   PetscCall(TSGetStepNumber(ts, &steps));
186   PetscCall(TSGetConvergedReason(ts, &reason));
187   PetscCall(PetscPrintf(PETSC_COMM_WORLD, "%s at time %g after %" PetscInt_FMT " steps\n", TSConvergedReasons[reason], (double)ftime, steps));
188 
189   /* {
190     Vec                max;
191     PetscInt           i;
192     const PetscReal    *bmax;
193 
194     PetscCall(TSMonitorEnvelopeGetBounds(ts,&max,NULL));
195     if (max) {
196       PetscCall(VecGetArrayRead(max,&bmax));
197       PetscCall(PetscPrintf(PETSC_COMM_SELF,"Species - maximum mass fraction\n"));
198       for (i=1; i<user.Nspec; i++) {
199         if (bmax[i] > .01) PetscCall(PetscPrintf(PETSC_COMM_SELF,"%s %g\n",user.snames[i],(double)bmax[i]));
200       }
201       PetscCall(VecRestoreArrayRead(max,&bmax));
202     }
203   }
204 
205   Vec y;
206   PetscCall(MassFractionToMoleFraction(&user,X,&y));
207   PetscCall(PrintSpecies(&user,y));
208   PetscCall(VecDestroy(&y)); */
209 
210   /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
211      Free work space.
212    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
213   TC_reset();
214   PetscCall(PetscStrArrayDestroy(&user.snames));
215   PetscCall(MatDestroy(&J));
216   PetscCall(VecDestroy(&X));
217   PetscCall(VecDestroy(&lambda));
218   PetscCall(TSDestroy(&ts));
219   PetscCall(PetscFree3(user.tchemwork, user.Jdense, user.rows));
220   PetscCall(PetscFinalize());
221   return 0;
222 }
223 
224 static PetscErrorCode FormRHSFunction(TS ts, PetscReal t, Vec X, Vec F, void *ptr) {
225   User               user = (User)ptr;
226   PetscScalar       *f;
227   const PetscScalar *x;
228 
229   PetscFunctionBeginUser;
230   PetscCall(VecGetArrayRead(X, &x));
231   PetscCall(VecGetArray(F, &f));
232 
233   PetscCall(PetscArraycpy(user->tchemwork, x, user->Nspec + 1));
234   user->tchemwork[0] *= user->Tini; /* Dimensionalize */
235   PetscCallTC(TC_getSrc(user->tchemwork, user->Nspec + 1, f));
236   f[0] /= user->Tini; /* Non-dimensionalize */
237 
238   PetscCall(VecRestoreArrayRead(X, &x));
239   PetscCall(VecRestoreArray(F, &f));
240   PetscFunctionReturn(0);
241 }
242 
243 static PetscErrorCode FormRHSJacobian(TS ts, PetscReal t, Vec X, Mat Amat, Mat Pmat, void *ptr) {
244   User               user = (User)ptr;
245   const PetscScalar *x;
246   PetscInt           M = user->Nspec + 1, i;
247 
248   PetscFunctionBeginUser;
249   PetscCall(VecGetArrayRead(X, &x));
250   PetscCall(PetscArraycpy(user->tchemwork, x, user->Nspec + 1));
251   PetscCall(VecRestoreArrayRead(X, &x));
252   user->tchemwork[0] *= user->Tini; /* Dimensionalize temperature (first row) because that is what Tchem wants */
253   PetscCall(TC_getJacTYN(user->tchemwork, user->Nspec, user->Jdense, 1));
254 
255   for (i = 0; i < M; i++) user->Jdense[i + 0 * M] /= user->Tini; /* Non-dimensionalize first column */
256   for (i = 0; i < M; i++) user->Jdense[0 + i * M] /= user->Tini; /* Non-dimensionalize first row */
257   for (i = 0; i < M; i++) user->rows[i] = i;
258   PetscCall(MatSetOption(Pmat, MAT_ROW_ORIENTED, PETSC_FALSE));
259   PetscCall(MatSetOption(Pmat, MAT_IGNORE_ZERO_ENTRIES, PETSC_TRUE));
260   PetscCall(MatZeroEntries(Pmat));
261   PetscCall(MatSetValues(Pmat, M, user->rows, M, user->rows, user->Jdense, INSERT_VALUES));
262 
263   PetscCall(MatAssemblyBegin(Pmat, MAT_FINAL_ASSEMBLY));
264   PetscCall(MatAssemblyEnd(Pmat, MAT_FINAL_ASSEMBLY));
265   if (Amat != Pmat) {
266     PetscCall(MatAssemblyBegin(Amat, MAT_FINAL_ASSEMBLY));
267     PetscCall(MatAssemblyEnd(Amat, MAT_FINAL_ASSEMBLY));
268   }
269   PetscFunctionReturn(0);
270 }
271 
272 PetscErrorCode FormInitialSolution(TS ts, Vec X, void *ctx) {
273   PetscScalar   *x;
274   PetscInt       i;
275   Vec            y;
276   const PetscInt maxspecies = 10;
277   PetscInt       smax = maxspecies, mmax = maxspecies;
278   char          *names[maxspecies];
279   PetscReal      molefracs[maxspecies], sum;
280   PetscBool      flg;
281 
282   PetscFunctionBeginUser;
283   PetscCall(VecZeroEntries(X));
284   PetscCall(VecGetArray(X, &x));
285   x[0] = 1.0; /* Non-dimensionalized by user->Tini */
286 
287   PetscCall(PetscOptionsGetStringArray(NULL, NULL, "-initial_species", names, &smax, &flg));
288   PetscCheck(smax >= 2, PETSC_COMM_SELF, PETSC_ERR_USER, "Must provide at least two initial species");
289   PetscCall(PetscOptionsGetRealArray(NULL, NULL, "-initial_mole", molefracs, &mmax, &flg));
290   PetscCheck(smax == mmax, PETSC_COMM_SELF, PETSC_ERR_USER, "Must provide same number of initial species %" PetscInt_FMT " as initial moles %" PetscInt_FMT, smax, mmax);
291   sum = 0;
292   for (i = 0; i < smax; i++) sum += molefracs[i];
293   for (i = 0; i < smax; i++) molefracs[i] = molefracs[i] / sum;
294   for (i = 0; i < smax; i++) {
295     int ispec = TC_getSpos(names[i], strlen(names[i]));
296     PetscCheck(ispec >= 0, PETSC_COMM_SELF, PETSC_ERR_USER, "Could not find species %s", names[i]);
297     PetscCall(PetscPrintf(PETSC_COMM_SELF, "Species %" PetscInt_FMT ": %s %g\n", i, names[i], (double)molefracs[i]));
298     x[1 + ispec] = molefracs[i];
299   }
300   for (i = 0; i < smax; i++) PetscCall(PetscFree(names[i]));
301   PetscCall(VecRestoreArray(X, &x));
302   /* PetscCall(PrintSpecies((User)ctx,X)); */
303   PetscCall(MoleFractionToMassFraction((User)ctx, X, &y));
304   PetscCall(VecCopy(y, X));
305   PetscCall(VecDestroy(&y));
306   PetscFunctionReturn(0);
307 }
308 
309 /*
310    Converts the input vector which is in mass fractions (used by tchem) to mole fractions
311 */
312 PetscErrorCode MassFractionToMoleFraction(User user, Vec massf, Vec *molef) {
313   PetscScalar       *mof;
314   const PetscScalar *maf;
315 
316   PetscFunctionBeginUser;
317   PetscCall(VecDuplicate(massf, molef));
318   PetscCall(VecGetArrayRead(massf, &maf));
319   PetscCall(VecGetArray(*molef, &mof));
320   mof[0] = maf[0]; /* copy over temperature */
321   TC_getMs2Ml((double *)maf + 1, user->Nspec, mof + 1);
322   PetscCall(VecRestoreArray(*molef, &mof));
323   PetscCall(VecRestoreArrayRead(massf, &maf));
324   PetscFunctionReturn(0);
325 }
326 
327 /*
328    Converts the input vector which is in mole fractions to mass fractions (used by tchem)
329 */
330 PetscErrorCode MoleFractionToMassFraction(User user, Vec molef, Vec *massf) {
331   const PetscScalar *mof;
332   PetscScalar       *maf;
333 
334   PetscFunctionBeginUser;
335   PetscCall(VecDuplicate(molef, massf));
336   PetscCall(VecGetArrayRead(molef, &mof));
337   PetscCall(VecGetArray(*massf, &maf));
338   maf[0] = mof[0]; /* copy over temperature */
339   TC_getMl2Ms((double *)mof + 1, user->Nspec, maf + 1);
340   PetscCall(VecRestoreArrayRead(molef, &mof));
341   PetscCall(VecRestoreArray(*massf, &maf));
342   PetscFunctionReturn(0);
343 }
344 
345 PetscErrorCode ComputeMassConservation(Vec x, PetscReal *mass, void *ctx) {
346   PetscFunctionBeginUser;
347   PetscCall(VecSum(x, mass));
348   PetscFunctionReturn(0);
349 }
350 
351 PetscErrorCode MonitorMassConservation(TS ts, PetscInt step, PetscReal time, Vec x, void *ctx) {
352   const PetscScalar *T;
353   PetscReal          mass;
354 
355   PetscFunctionBeginUser;
356   PetscCall(ComputeMassConservation(x, &mass, ctx));
357   PetscCall(VecGetArrayRead(x, &T));
358   mass -= PetscAbsScalar(T[0]);
359   PetscCall(VecRestoreArrayRead(x, &T));
360   PetscCall(PetscPrintf(PETSC_COMM_WORLD, "Timestep %" PetscInt_FMT " time %g percent mass lost or gained %g\n", step, (double)time, (double)(100. * (1.0 - mass))));
361   PetscFunctionReturn(0);
362 }
363 
364 PetscErrorCode MonitorTempature(TS ts, PetscInt step, PetscReal time, Vec x, void *ctx) {
365   User               user = (User)ctx;
366   const PetscScalar *T;
367 
368   PetscFunctionBeginUser;
369   PetscCall(VecGetArrayRead(x, &T));
370   PetscCall(PetscPrintf(PETSC_COMM_WORLD, "Timestep %" PetscInt_FMT " time %g temperature %g\n", step, (double)time, (double)(T[0] * user->Tini)));
371   PetscCall(VecRestoreArrayRead(x, &T));
372   PetscFunctionReturn(0);
373 }
374 
375 /*
376    Prints out each species with its name
377 */
378 PETSC_UNUSED PetscErrorCode PrintSpecies(User user, Vec molef) {
379   const PetscScalar *mof;
380   PetscInt           i, *idx, n = user->Nspec + 1;
381 
382   PetscFunctionBeginUser;
383   PetscCall(PetscMalloc1(n, &idx));
384   for (i = 0; i < n; i++) idx[i] = i;
385   PetscCall(VecGetArrayRead(molef, &mof));
386   PetscCall(PetscSortRealWithPermutation(n, mof, idx));
387   for (i = 0; i < n; i++) PetscCall(PetscPrintf(PETSC_COMM_SELF, "%6s %g\n", user->snames[idx[n - i - 1]], (double)PetscRealPart(mof[idx[n - i - 1]])));
388   PetscCall(PetscFree(idx));
389   PetscCall(VecRestoreArrayRead(molef, &mof));
390   PetscFunctionReturn(0);
391 }
392 
393 /*TEST
394     build:
395       requires: tchem
396 
397     test:
398       args: -chem http://combustion.berkeley.edu/gri_mech/version30/files30/grimech30.dat -thermo http://combustion.berkeley.edu/gri_mech/version30/files30/thermo30.dat -initial_species CH4,O2,N2,AR -initial_mole 0.0948178320887,0.189635664177,0.706766236705,0.00878026702874 -Tini 1500 -Tini 1500 -ts_arkimex_fully_implicit -ts_max_snes_failures -1  -ts_adapt_dt_max 1e-4 -ts_arkimex_type 4 -ts_max_time .005
399       requires: !single
400       filter: grep -v iterations
401 
402 TEST*/
403