static const char help[] = "Integrate chemistry using TChem.\n"; #include #include #if defined(PETSC_HAVE_TCHEM) #if defined(MAX) #undef MAX #endif #if defined(MIN) #undef MIN #endif #include #include #else #error TChem is required for this example. Reconfigure PETSc using --download-tchem. #endif /* This is an extension of extchem.c to solve the reaction equations independently in each cell of a one dimensional field Obtain the three files into this directory curl http://combustion.berkeley.edu/gri_mech/version30/files30/grimech30.dat > chem.inp curl http://combustion.berkeley.edu/gri_mech/version30/files30/thermo30.dat > therm.dat cp $PETSC_DIR/$PETSC_ARCH/externalpackages/tchem/data/periodictable.dat . Run with ./extchemfield -ts_arkimex_fully_implicit -ts_max_snes_failures unlimited -ts_adapt_monitor -ts_adapt_dt_max 1e-4 -ts_arkimex_type 4 -ts_max_time .005 Options for visualizing the solution: Watch certain variables in each cell evolve with time -draw_solution 1 -ts_monitor_lg_solution_variables Temp,H2,O2,H2O,CH4,CO,CO2,C2H2,N2 -lg_use_markers false -draw_pause -2 Watch certain variables in all cells evolve with time -da_refine 4 -ts_monitor_draw_solution -draw_fields_by_name Temp,H2 -draw_vec_mark_points -draw_pause -2 Keep the initial temperature distribution as one monitors the current temperature distribution -ts_monitor_draw_solution_initial -draw_bounds .9,1.7 -draw_fields_by_name Temp Save the images in a .gif (movie) file -draw_save -draw_save_single_file Compute the sensitivities of the solution of the first temperature on the initial conditions -ts_adjoint_solve -ts_time_step 1.e-5 -ts_type cn -ts_adjoint_view_solution draw Turn off diffusion -diffusion no Turn off reactions -reactions no The solution for component i = 0 is the temperature. The solution, i > 0, is the mass fraction, massf[i], of species i, i.e. mass of species i/ total mass of all species The mole fraction molef[i], i > 0, is the number of moles of a species/ total number of moles of all species Define M[i] = mass per mole of species i then molef[i] = massf[i]/(M[i]*(sum_j massf[j]/M[j])) FormMoleFraction(User,massf,molef) converts the mass fraction solution of each species to the mole fraction of each species. */ typedef struct _User *User; struct _User { PetscReal pressure; int Nspec; int Nreac; PetscReal Tini, dx; PetscReal diffus; DM dm; PetscBool diffusion, reactions; double *tchemwork; double *Jdense; /* Dense array workspace where Tchem computes the Jacobian */ PetscInt *rows; }; static PetscErrorCode MonitorCell(TS, User, PetscInt); static PetscErrorCode FormRHSFunction(TS, PetscReal, Vec, Vec, void *); static PetscErrorCode FormRHSJacobian(TS, PetscReal, Vec, Mat, Mat, void *); static PetscErrorCode FormInitialSolution(TS, Vec, void *); #define PetscCallTC(ierr) \ do { \ PetscCheck(!ierr, PETSC_COMM_SELF, PETSC_ERR_LIB, "Error in TChem library, return code %d", ierr); \ } while (0) int main(int argc, char **argv) { TS ts; /* time integrator */ TSAdapt adapt; Vec X; /* solution vector */ Mat J; /* Jacobian matrix */ PetscInt steps, ncells, xs, xm, i; PetscReal ftime, dt; char chemfile[PETSC_MAX_PATH_LEN] = "chem.inp", thermofile[PETSC_MAX_PATH_LEN] = "therm.dat"; struct _User user; TSConvergedReason reason; PetscBool showsolutions = PETSC_FALSE; char **snames, *names; Vec lambda; /* used with TSAdjoint for sensitivities */ PetscFunctionBeginUser; PetscCall(PetscInitialize(&argc, &argv, NULL, help)); PetscOptionsBegin(PETSC_COMM_WORLD, NULL, "Chemistry solver options", ""); PetscCall(PetscOptionsString("-chem", "CHEMKIN input file", "", chemfile, chemfile, sizeof(chemfile), NULL)); PetscCall(PetscOptionsString("-thermo", "NASA thermo input file", "", thermofile, thermofile, sizeof(thermofile), NULL)); user.pressure = 1.01325e5; /* Pascal */ PetscCall(PetscOptionsReal("-pressure", "Pressure of reaction [Pa]", "", user.pressure, &user.pressure, NULL)); user.Tini = 1550; PetscCall(PetscOptionsReal("-Tini", "Initial temperature [K]", "", user.Tini, &user.Tini, NULL)); user.diffus = 100; PetscCall(PetscOptionsReal("-diffus", "Diffusion constant", "", user.diffus, &user.diffus, NULL)); PetscCall(PetscOptionsBool("-draw_solution", "Plot the solution for each cell", "", showsolutions, &showsolutions, NULL)); user.diffusion = PETSC_TRUE; PetscCall(PetscOptionsBool("-diffusion", "Have diffusion", "", user.diffusion, &user.diffusion, NULL)); user.reactions = PETSC_TRUE; PetscCall(PetscOptionsBool("-reactions", "Have reactions", "", user.reactions, &user.reactions, NULL)); PetscOptionsEnd(); PetscCallTC(TC_initChem(chemfile, thermofile, 0, 1.0)); user.Nspec = TC_getNspec(); user.Nreac = TC_getNreac(); PetscCall(DMDACreate1d(PETSC_COMM_WORLD, DM_BOUNDARY_PERIODIC, 10, user.Nspec + 1, 1, NULL, &user.dm)); PetscCall(DMSetFromOptions(user.dm)); PetscCall(DMSetUp(user.dm)); PetscCall(DMDAGetInfo(user.dm, NULL, &ncells, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL)); user.dx = 1.0 / ncells; /* Set the coordinates of the cell centers; note final ghost cell is at x coordinate 1.0 */ PetscCall(DMDASetUniformCoordinates(user.dm, 0.0, 1.0, 0.0, 1.0, 0.0, 1.0)); /* set the names of each field in the DMDA based on the species name */ PetscCall(PetscMalloc1((user.Nspec + 1) * LENGTHOFSPECNAME, &names)); PetscCall(PetscStrncpy(names, "Temp", (user.Nspec + 1) * LENGTHOFSPECNAME)); TC_getSnames(user.Nspec, names + LENGTHOFSPECNAME); PetscCall(PetscMalloc1(user.Nspec + 2, &snames)); for (i = 0; i < user.Nspec + 1; i++) snames[i] = names + i * LENGTHOFSPECNAME; snames[user.Nspec + 1] = NULL; PetscCall(DMDASetFieldNames(user.dm, (const char *const *)snames)); PetscCall(PetscFree(snames)); PetscCall(PetscFree(names)); PetscCall(DMCreateMatrix(user.dm, &J)); PetscCall(DMCreateGlobalVector(user.dm, &X)); PetscCall(PetscMalloc3(user.Nspec + 1, &user.tchemwork, PetscSqr(user.Nspec + 1), &user.Jdense, user.Nspec + 1, &user.rows)); /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Create timestepping solver context - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ PetscCall(TSCreate(PETSC_COMM_WORLD, &ts)); PetscCall(TSSetDM(ts, user.dm)); PetscCall(TSSetType(ts, TSARKIMEX)); PetscCall(TSARKIMEXSetFullyImplicit(ts, PETSC_TRUE)); PetscCall(TSARKIMEXSetType(ts, TSARKIMEX4)); PetscCall(TSSetRHSFunction(ts, NULL, FormRHSFunction, &user)); PetscCall(TSSetRHSJacobian(ts, J, J, FormRHSJacobian, &user)); ftime = 1.0; PetscCall(TSSetMaxTime(ts, ftime)); PetscCall(TSSetExactFinalTime(ts, TS_EXACTFINALTIME_STEPOVER)); /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Set initial conditions - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ PetscCall(FormInitialSolution(ts, X, &user)); PetscCall(TSSetSolution(ts, X)); dt = 1e-10; /* Initial time step */ PetscCall(TSSetTimeStep(ts, dt)); PetscCall(TSGetAdapt(ts, &adapt)); PetscCall(TSAdaptSetStepLimits(adapt, 1e-12, 1e-4)); /* Also available with -ts_adapt_dt_min/-ts_adapt_dt_max */ PetscCall(TSSetMaxSNESFailures(ts, -1)); /* Retry step an unlimited number of times */ /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Pass information to graphical monitoring routine - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ if (showsolutions) { PetscCall(DMDAGetCorners(user.dm, &xs, NULL, NULL, &xm, NULL, NULL)); for (i = xs; i < xs + xm; i++) PetscCall(MonitorCell(ts, &user, i)); } /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Set runtime options - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ PetscCall(TSSetFromOptions(ts)); /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Set final conditions for sensitivities - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ PetscCall(DMCreateGlobalVector(user.dm, &lambda)); PetscCall(TSSetCostGradients(ts, 1, &lambda, NULL)); PetscCall(VecSetValue(lambda, 0, 1.0, INSERT_VALUES)); PetscCall(VecAssemblyBegin(lambda)); PetscCall(VecAssemblyEnd(lambda)); /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Solve ODE - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ PetscCall(TSSolve(ts, X)); PetscCall(TSGetSolveTime(ts, &ftime)); PetscCall(TSGetStepNumber(ts, &steps)); PetscCall(TSGetConvergedReason(ts, &reason)); PetscCall(PetscPrintf(PETSC_COMM_WORLD, "%s at time %g after %" PetscInt_FMT " steps\n", TSConvergedReasons[reason], (double)ftime, steps)); { Vec max; const char *const *names; PetscInt i; const PetscReal *bmax; PetscCall(TSMonitorEnvelopeGetBounds(ts, &max, NULL)); if (max) { PetscCall(TSMonitorLGGetVariableNames(ts, &names)); if (names) { PetscCall(VecGetArrayRead(max, &bmax)); PetscCall(PetscPrintf(PETSC_COMM_SELF, "Species - maximum mass fraction\n")); for (i = 1; i < user.Nspec; i++) { if (bmax[i] > .01) PetscCall(PetscPrintf(PETSC_COMM_SELF, "%s %g\n", names[i], (double)bmax[i])); } PetscCall(VecRestoreArrayRead(max, &bmax)); } } } /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Free work space. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ TC_reset(); PetscCall(DMDestroy(&user.dm)); PetscCall(MatDestroy(&J)); PetscCall(VecDestroy(&X)); PetscCall(VecDestroy(&lambda)); PetscCall(TSDestroy(&ts)); PetscCall(PetscFree3(user.tchemwork, user.Jdense, user.rows)); PetscCall(PetscFinalize()); return 0; } /* Applies the second order centered difference diffusion operator on a one dimensional periodic domain */ static PetscErrorCode FormDiffusionFunction(TS ts, PetscReal t, Vec X, Vec F, void *ptr) { User user = (User)ptr; PetscScalar **f; const PetscScalar **x; DM dm; PetscInt i, xs, xm, j, dof; Vec Xlocal; PetscReal idx; PetscFunctionBeginUser; PetscCall(TSGetDM(ts, &dm)); PetscCall(DMDAGetInfo(dm, NULL, NULL, NULL, NULL, NULL, NULL, NULL, &dof, NULL, NULL, NULL, NULL, NULL)); PetscCall(DMGetLocalVector(dm, &Xlocal)); PetscCall(DMGlobalToLocalBegin(dm, X, INSERT_VALUES, Xlocal)); PetscCall(DMGlobalToLocalEnd(dm, X, INSERT_VALUES, Xlocal)); PetscCall(DMDAVecGetArrayDOFRead(dm, Xlocal, &x)); PetscCall(DMDAVecGetArrayDOF(dm, F, &f)); PetscCall(DMDAGetCorners(dm, &xs, NULL, NULL, &xm, NULL, NULL)); idx = 1.0 * user->diffus / user->dx; for (i = xs; i < xs + xm; i++) { for (j = 0; j < dof; j++) f[i][j] += idx * (x[i + 1][j] - 2.0 * x[i][j] + x[i - 1][j]); } PetscCall(DMDAVecRestoreArrayDOFRead(dm, Xlocal, &x)); PetscCall(DMDAVecRestoreArrayDOF(dm, F, &f)); PetscCall(DMRestoreLocalVector(dm, &Xlocal)); PetscFunctionReturn(PETSC_SUCCESS); } /* Produces the second order centered difference diffusion operator on a one dimensional periodic domain */ static PetscErrorCode FormDiffusionJacobian(TS ts, PetscReal t, Vec X, Mat Amat, Mat Pmat, void *ptr) { User user = (User)ptr; DM dm; PetscInt i, xs, xm, j, dof; PetscReal idx, values[3]; MatStencil row, col[3]; PetscFunctionBeginUser; PetscCall(TSGetDM(ts, &dm)); PetscCall(DMDAGetInfo(dm, NULL, NULL, NULL, NULL, NULL, NULL, NULL, &dof, NULL, NULL, NULL, NULL, NULL)); PetscCall(DMDAGetCorners(dm, &xs, NULL, NULL, &xm, NULL, NULL)); idx = 1.0 * user->diffus / user->dx; values[0] = idx; values[1] = -2.0 * idx; values[2] = idx; for (i = xs; i < xs + xm; i++) { for (j = 0; j < dof; j++) { row.i = i; row.c = j; col[0].i = i - 1; col[0].c = j; col[1].i = i; col[1].c = j; col[2].i = i + 1; col[2].c = j; PetscCall(MatSetValuesStencil(Pmat, 1, &row, 3, col, values, ADD_VALUES)); } } PetscCall(MatAssemblyBegin(Pmat, MAT_FINAL_ASSEMBLY)); PetscCall(MatAssemblyEnd(Pmat, MAT_FINAL_ASSEMBLY)); PetscFunctionReturn(PETSC_SUCCESS); } static PetscErrorCode FormRHSFunction(TS ts, PetscReal t, Vec X, Vec F, void *ptr) { User user = (User)ptr; PetscScalar **f; const PetscScalar **x; DM dm; PetscInt i, xs, xm; PetscFunctionBeginUser; if (user->reactions) { PetscCall(TSGetDM(ts, &dm)); PetscCall(DMDAVecGetArrayDOFRead(dm, X, &x)); PetscCall(DMDAVecGetArrayDOF(dm, F, &f)); PetscCall(DMDAGetCorners(dm, &xs, NULL, NULL, &xm, NULL, NULL)); for (i = xs; i < xs + xm; i++) { PetscCall(PetscArraycpy(user->tchemwork, x[i], user->Nspec + 1)); user->tchemwork[0] *= user->Tini; /* Dimensionalize */ PetscCallTC(TC_getSrc(user->tchemwork, user->Nspec + 1, f[i])); f[i][0] /= user->Tini; /* Non-dimensionalize */ } PetscCall(DMDAVecRestoreArrayDOFRead(dm, X, &x)); PetscCall(DMDAVecRestoreArrayDOF(dm, F, &f)); } else { PetscCall(VecZeroEntries(F)); } if (user->diffusion) PetscCall(FormDiffusionFunction(ts, t, X, F, ptr)); PetscFunctionReturn(PETSC_SUCCESS); } static PetscErrorCode FormRHSJacobian(TS ts, PetscReal t, Vec X, Mat Amat, Mat Pmat, void *ptr) { User user = (User)ptr; const PetscScalar **x; PetscInt M = user->Nspec + 1, i, j, xs, xm; DM dm; PetscFunctionBeginUser; if (user->reactions) { PetscCall(TSGetDM(ts, &dm)); PetscCall(MatZeroEntries(Pmat)); PetscCall(MatSetOption(Pmat, MAT_ROW_ORIENTED, PETSC_FALSE)); PetscCall(MatSetOption(Pmat, MAT_IGNORE_ZERO_ENTRIES, PETSC_TRUE)); PetscCall(DMDAVecGetArrayDOFRead(dm, X, &x)); PetscCall(DMDAGetCorners(dm, &xs, NULL, NULL, &xm, NULL, NULL)); for (i = xs; i < xs + xm; i++) { PetscCall(PetscArraycpy(user->tchemwork, x[i], user->Nspec + 1)); user->tchemwork[0] *= user->Tini; /* Dimensionalize temperature (first row) because that is what Tchem wants */ PetscCall(TC_getJacTYN(user->tchemwork, user->Nspec, user->Jdense, 1)); for (j = 0; j < M; j++) user->Jdense[j + 0 * M] /= user->Tini; /* Non-dimensionalize first column */ for (j = 0; j < M; j++) user->Jdense[0 + j * M] /= user->Tini; /* Non-dimensionalize first row */ for (j = 0; j < M; j++) user->rows[j] = i * M + j; PetscCall(MatSetValues(Pmat, M, user->rows, M, user->rows, user->Jdense, INSERT_VALUES)); } PetscCall(DMDAVecRestoreArrayDOFRead(dm, X, &x)); PetscCall(MatAssemblyBegin(Pmat, MAT_FINAL_ASSEMBLY)); PetscCall(MatAssemblyEnd(Pmat, MAT_FINAL_ASSEMBLY)); } else { PetscCall(MatZeroEntries(Pmat)); } if (user->diffusion) PetscCall(FormDiffusionJacobian(ts, t, X, Amat, Pmat, ptr)); if (Amat != Pmat) { PetscCall(MatAssemblyBegin(Amat, MAT_FINAL_ASSEMBLY)); PetscCall(MatAssemblyEnd(Amat, MAT_FINAL_ASSEMBLY)); } PetscFunctionReturn(PETSC_SUCCESS); } PetscErrorCode FormInitialSolution(TS ts, Vec X, PetscCtx ctx) { PetscScalar **x, *xc; struct { const char *name; PetscReal massfrac; } initial[] = { {"CH4", 0.0948178320887 }, {"O2", 0.189635664177 }, {"N2", 0.706766236705 }, {"AR", 0.00878026702874} }; PetscInt i, j, xs, xm; DM dm; PetscFunctionBeginUser; PetscCall(VecZeroEntries(X)); PetscCall(TSGetDM(ts, &dm)); PetscCall(DMDAGetCorners(dm, &xs, NULL, NULL, &xm, NULL, NULL)); PetscCall(DMDAGetCoordinateArray(dm, &xc)); PetscCall(DMDAVecGetArrayDOF(dm, X, &x)); for (i = xs; i < xs + xm; i++) { x[i][0] = 1.0 + .05 * PetscSinScalar(2. * PETSC_PI * xc[i]); /* Non-dimensionalized by user->Tini */ for (j = 0; j < PETSC_STATIC_ARRAY_LENGTH(initial); j++) { int ispec = TC_getSpos(initial[j].name, (int)strlen(initial[j].name)); PetscCheck(ispec >= 0, PETSC_COMM_SELF, PETSC_ERR_USER, "Could not find species %s", initial[j].name); PetscCall(PetscPrintf(PETSC_COMM_SELF, "Species %d: %s %g\n", j, initial[j].name, (double)initial[j].massfrac)); x[i][1 + ispec] = initial[j].massfrac; } } PetscCall(DMDAVecRestoreArrayDOF(dm, X, &x)); PetscCall(DMDARestoreCoordinateArray(dm, &xc)); PetscFunctionReturn(PETSC_SUCCESS); } /* Routines for displaying the solutions */ typedef struct { PetscInt cell; User user; } UserLGCtx; static PetscErrorCode FormMoleFraction(UserLGCtx *ctx, Vec massf, Vec *molef) { User user = ctx->user; PetscReal *M, tM = 0; PetscInt i, n = user->Nspec + 1; PetscScalar *mof; const PetscScalar **maf; PetscFunctionBeginUser; PetscCall(VecCreateSeq(PETSC_COMM_SELF, n, molef)); PetscCall(PetscMalloc1(user->Nspec, &M)); TC_getSmass(user->Nspec, M); PetscCall(DMDAVecGetArrayDOFRead(user->dm, massf, &maf)); PetscCall(VecGetArray(*molef, &mof)); mof[0] = maf[ctx->cell][0]; /* copy over temperature */ for (i = 1; i < n; i++) tM += maf[ctx->cell][i] / M[i - 1]; for (i = 1; i < n; i++) mof[i] = maf[ctx->cell][i] / (M[i - 1] * tM); PetscCall(DMDAVecRestoreArrayDOFRead(user->dm, massf, &maf)); PetscCall(VecRestoreArray(*molef, &mof)); PetscCall(PetscFree(M)); PetscFunctionReturn(PETSC_SUCCESS); } static PetscErrorCode MonitorCellDestroy(UserLGCtx **uctx) { PetscFunctionBeginUser; PetscCall(PetscFree(*uctx)); PetscFunctionReturn(PETSC_SUCCESS); } /* Use TSMonitorLG to monitor the reactions in a particular cell */ static PetscErrorCode MonitorCell(TS ts, User user, PetscInt cell) { TSMonitorLGCtx ctx; char **snames; UserLGCtx *uctx; char label[128]; PetscReal temp, *xc; PetscMPIInt rank; PetscFunctionBeginUser; PetscCall(DMDAGetCoordinateArray(user->dm, &xc)); temp = 1.0 + .05 * PetscSinScalar(2. * PETSC_PI * xc[cell]); /* Non-dimensionalized by user->Tini */ PetscCall(DMDARestoreCoordinateArray(user->dm, &xc)); PetscCallMPI(MPI_Comm_rank(PETSC_COMM_WORLD, &rank)); PetscCall(PetscSNPrintf(label, sizeof(label), "Initial Temperature %g Cell %d Rank %d", (double)user->Tini * temp, (int)cell, rank)); PetscCall(TSMonitorLGCtxCreate(PETSC_COMM_SELF, NULL, label, PETSC_DECIDE, PETSC_DECIDE, 600, 400, 1, &ctx)); PetscCall(DMDAGetFieldNames(user->dm, (const char *const **)&snames)); PetscCall(TSMonitorLGCtxSetVariableNames(ctx, (const char *const *)snames)); PetscCall(PetscNew(&uctx)); uctx->cell = cell; uctx->user = user; PetscCall(TSMonitorLGCtxSetTransform(ctx, (PetscErrorCode (*)(void *, Vec, Vec *))FormMoleFraction, (PetscCtxDestroyFn *)MonitorCellDestroy, uctx)); PetscCall(TSMonitorSet(ts, TSMonitorLGSolution, ctx, (PetscCtxDestroyFn *)TSMonitorLGCtxDestroy)); PetscFunctionReturn(PETSC_SUCCESS); }