static char help[] = "Time-Dependent Reactive Flow example in 2D with Darcy Flow"; /* This example solves the elementary chemical reaction: SP_A + 2SP_B = SP_C Subject to predetermined flow modeled as though it were in a porous media. This flow is modeled as advection and diffusion of the three species as v = porosity*saturation*grad(q) and the time-dependent equation solved for each species as advection + diffusion + reaction: v dot grad SP_i + dSP_i / dt + dispersivity*div*grad(SP_i) + R(SP_i) = 0 The following options are available: -length_x - The length of the domain in the direction of the flow -length_y - The length of the domain in the direction orthogonal to the flow -gradq_inflow - The inflow speed as if the saturation and porosity were 1. -saturation - The saturation of the porous medium. -porosity - The porosity of the medium. -dispersivity - The dispersivity of the flow. -rate_constant - The rate constant for the chemical reaction -stoich - The stoichiometry matrix for the reaction -sp_inflow - The species concentrations at the inflow -sp_0 - The species concentrations at time 0 */ #include #include #include #include #define N_SPECIES 3 #define N_REACTIONS 1 #define DIM 2 #define stoich(i, j) ctx->stoichiometry[N_SPECIES * i + j] typedef struct { PetscScalar sp[N_SPECIES]; } Field; typedef struct { Field x_inflow; Field x_0; PetscReal stoichiometry[N_SPECIES * N_REACTIONS]; PetscReal porosity; PetscReal dispersivity; PetscReal saturation; PetscReal rate_constant[N_REACTIONS]; PetscReal gradq_inflow; PetscReal length[DIM]; } AppCtx; extern PetscErrorCode FormInitialGuess(DM da, AppCtx *ctx, Vec X); extern PetscErrorCode FormIFunctionLocal(DMDALocalInfo *, PetscReal, Field **, Field **, Field **, AppCtx *); extern PetscErrorCode FormIFunction(TS, PetscReal, Vec, Vec, Vec, void *); extern PetscErrorCode ReactingFlowPostCheck(SNESLineSearch, Vec, Vec, Vec, PetscBool *, PetscBool *, void *); PetscErrorCode SetFromOptions(AppCtx *ctx) { PetscInt i, j; PetscFunctionBeginUser; ctx->dispersivity = 0.5; ctx->porosity = 0.25; ctx->saturation = 1.0; ctx->gradq_inflow = 1.0; ctx->rate_constant[0] = 0.5; ctx->length[0] = 100.0; ctx->length[1] = 100.0; ctx->x_0.sp[0] = 0.0; ctx->x_0.sp[1] = 0.0; ctx->x_0.sp[2] = 0.0; ctx->x_inflow.sp[0] = 0.05; ctx->x_inflow.sp[1] = 0.05; ctx->x_inflow.sp[2] = 0.0; for (i = 0; i < N_REACTIONS; i++) { for (j = 0; j < N_SPECIES; j++) stoich(i, j) = 0.; } /* set up a pretty easy example */ stoich(0, 0) = -1.; stoich(0, 1) = -2.; stoich(0, 2) = 1.; PetscInt as = N_SPECIES; PetscCall(PetscOptionsGetReal(NULL, NULL, "-length_x", &ctx->length[0], NULL)); PetscCall(PetscOptionsGetReal(NULL, NULL, "-length_y", &ctx->length[1], NULL)); PetscCall(PetscOptionsGetReal(NULL, NULL, "-porosity", &ctx->porosity, NULL)); PetscCall(PetscOptionsGetReal(NULL, NULL, "-saturation", &ctx->saturation, NULL)); PetscCall(PetscOptionsGetReal(NULL, NULL, "-dispersivity", &ctx->dispersivity, NULL)); PetscCall(PetscOptionsGetReal(NULL, NULL, "-gradq_inflow", &ctx->gradq_inflow, NULL)); PetscCall(PetscOptionsGetReal(NULL, NULL, "-rate_constant", &ctx->rate_constant[0], NULL)); PetscCall(PetscOptionsGetRealArray(NULL, NULL, "-sp_inflow", ctx->x_inflow.sp, &as, NULL)); PetscCall(PetscOptionsGetRealArray(NULL, NULL, "-sp_0", ctx->x_0.sp, &as, NULL)); as = N_SPECIES; PetscCall(PetscOptionsGetRealArray(NULL, NULL, "-stoich", ctx->stoichiometry, &as, NULL)); PetscFunctionReturn(PETSC_SUCCESS); } int main(int argc, char **argv) { TS ts; SNES snes; SNESLineSearch linesearch; Vec x; AppCtx ctx; DM da; PetscFunctionBeginUser; PetscCall(PetscInitialize(&argc, &argv, NULL, help)); PetscCall(SetFromOptions(&ctx)); PetscCall(TSCreate(PETSC_COMM_WORLD, &ts)); PetscCall(TSSetType(ts, TSCN)); PetscCall(TSSetProblemType(ts, TS_NONLINEAR)); PetscCall(TSSetIFunction(ts, NULL, FormIFunction, &ctx)); PetscCall(DMDACreate2d(PETSC_COMM_WORLD, DM_BOUNDARY_NONE, DM_BOUNDARY_NONE, DMDA_STENCIL_STAR, -4, -4, PETSC_DECIDE, PETSC_DECIDE, N_SPECIES, 1, NULL, NULL, &da)); PetscCall(DMSetFromOptions(da)); PetscCall(DMSetUp(da)); PetscCall(DMDASetUniformCoordinates(da, 0.0, 1.0, 0.0, 1.0, 0.0, 1.0)); PetscCall(DMDASetFieldName(da, 0, "species A")); PetscCall(DMDASetFieldName(da, 1, "species B")); PetscCall(DMDASetFieldName(da, 2, "species C")); PetscCall(DMSetApplicationContext(da, &ctx)); PetscCall(DMCreateGlobalVector(da, &x)); PetscCall(FormInitialGuess(da, &ctx, x)); PetscCall(TSSetDM(ts, da)); PetscCall(TSSetMaxTime(ts, 1000.0)); PetscCall(TSSetExactFinalTime(ts, TS_EXACTFINALTIME_STEPOVER)); PetscCall(TSSetTimeStep(ts, 1.0)); PetscCall(TSSetSolution(ts, x)); PetscCall(TSSetFromOptions(ts)); PetscCall(TSGetSNES(ts, &snes)); PetscCall(SNESGetLineSearch(snes, &linesearch)); PetscCall(SNESLineSearchSetPostCheck(linesearch, ReactingFlowPostCheck, (void *)&ctx)); PetscCall(SNESSetFromOptions(snes)); PetscCall(TSSolve(ts, x)); PetscCall(VecDestroy(&x)); PetscCall(TSDestroy(&ts)); PetscCall(DMDestroy(&da)); PetscCall(PetscFinalize()); PetscFunctionReturn(PETSC_SUCCESS); } /* ------------------------------------------------------------------- */ PetscErrorCode FormInitialGuess(DM da, AppCtx *ctx, Vec X) { PetscInt i, j, l, Mx, My, xs, ys, xm, ym; Field **x; PetscFunctionBeginUser; PetscCall(DMDAGetInfo(da, PETSC_IGNORE, &Mx, &My, PETSC_IGNORE, PETSC_IGNORE, PETSC_IGNORE, PETSC_IGNORE, PETSC_IGNORE, PETSC_IGNORE, PETSC_IGNORE, PETSC_IGNORE, PETSC_IGNORE, PETSC_IGNORE)); PetscCall(DMDAVecGetArray(da, X, &x)); PetscCall(DMDAGetCorners(da, &xs, &ys, NULL, &xm, &ym, NULL)); for (j = ys; j < ys + ym; j++) { for (i = xs; i < xs + xm; i++) { for (l = 0; l < N_SPECIES; l++) { if (i == 0) { if (l == 0) x[j][i].sp[l] = (ctx->x_inflow.sp[l] * ((PetscScalar)j) / (My - 1)); else if (l == 1) x[j][i].sp[l] = (ctx->x_inflow.sp[l] * (1. - ((PetscScalar)j) / (My - 1))); else x[j][i].sp[l] = ctx->x_0.sp[l]; } } } } PetscCall(DMDAVecRestoreArray(da, X, &x)); PetscFunctionReturn(PETSC_SUCCESS); } PetscErrorCode FormIFunctionLocal(DMDALocalInfo *info, PetscScalar ptime, Field **x, Field **xt, Field **f, AppCtx *ctx) { PetscInt i, j, l, m; PetscReal hx, hy, dhx, dhy, hxdhy, hydhx, scale; PetscScalar u, uxx, uyy; PetscScalar vx, vy, sxp, syp, sxm, sym, avx, vxp, vxm, avy, vyp, vym, f_advect; PetscScalar rate; PetscFunctionBeginUser; hx = ctx->length[0] / ((PetscReal)(info->mx - 1)); hy = ctx->length[1] / ((PetscReal)(info->my - 1)); dhx = 1. / hx; dhy = 1. / hy; hxdhy = hx / hy; hydhx = hy / hx; scale = hx * hy; for (j = info->ys; j < info->ys + info->ym; j++) { for (i = info->xs; i < info->xs + info->xm; i++) { vx = ctx->gradq_inflow * ctx->porosity * ctx->saturation; vy = 0.0 * dhy; avx = PetscAbsScalar(vx); vxp = .5 * (vx + avx); vxm = .5 * (vx - avx); avy = PetscAbsScalar(vy); vyp = .5 * (vy + avy); vym = .5 * (vy - avy); /* chemical reactions */ for (l = 0; l < N_SPECIES; l++) { /* determine the velocites as the gradients of the pressure */ if (i == 0) { sxp = (x[j][i + 1].sp[l] - x[j][i].sp[l]) * dhx; sxm = sxp; } else if (i == info->mx - 1) { sxp = (x[j][i].sp[l] - x[j][i - 1].sp[l]) * dhx; sxm = sxp; } else { sxm = (x[j][i + 1].sp[l] - x[j][i].sp[l]) * dhx; sxp = (x[j][i].sp[l] - x[j][i - 1].sp[l]) * dhx; } if (j == 0) { syp = (x[j + 1][i].sp[l] - x[j][i].sp[l]) * dhy; sym = syp; } else if (j == info->my - 1) { syp = (x[j][i].sp[l] - x[j - 1][i].sp[l]) * dhy; sym = syp; } else { sym = (x[j + 1][i].sp[l] - x[j][i].sp[l]) * dhy; syp = (x[j][i].sp[l] - x[j - 1][i].sp[l]) * dhy; } /* 4 flops per species*point */ if (i == 0) { if (l == 0) f[j][i].sp[l] = (x[j][i].sp[l] - ctx->x_inflow.sp[l] * ((PetscScalar)j) / (info->my - 1)); else if (l == 1) f[j][i].sp[l] = (x[j][i].sp[l] - ctx->x_inflow.sp[l] * (1. - ((PetscScalar)j) / (info->my - 1))); else f[j][i].sp[l] = x[j][i].sp[l]; } else { f[j][i].sp[l] = xt[j][i].sp[l] * scale; u = x[j][i].sp[l]; if (j == 0) uyy = u - x[j + 1][i].sp[l]; else if (j == info->my - 1) uyy = u - x[j - 1][i].sp[l]; else uyy = (2.0 * u - x[j - 1][i].sp[l] - x[j + 1][i].sp[l]) * hxdhy; if (i != info->mx - 1) uxx = (2.0 * u - x[j][i - 1].sp[l] - x[j][i + 1].sp[l]) * hydhx; else uxx = u - x[j][i - 1].sp[l]; /* 10 flops per species*point */ f_advect = 0.; f_advect += scale * (vxp * sxp + vxm * sxm); f_advect += scale * (vyp * syp + vym * sym); f[j][i].sp[l] += f_advect + ctx->dispersivity * (uxx + uyy); /* 14 flops per species*point */ } } /* reaction */ if (i != 0) { for (m = 0; m < N_REACTIONS; m++) { rate = ctx->rate_constant[m]; for (l = 0; l < N_SPECIES; l++) { if (stoich(m, l) < 0) { /* assume an elementary reaction */ rate *= PetscPowScalar(x[j][i].sp[l], PetscAbsScalar(stoich(m, l))); /* ~10 flops per reaction per species per point */ } } for (l = 0; l < N_SPECIES; l++) { f[j][i].sp[l] += -scale * stoich(m, l) * rate; /* Reaction term */ /* 3 flops per reaction per species per point */ } } } } } PetscCall(PetscLogFlops((N_SPECIES * (28.0 + 13.0 * N_REACTIONS)) * info->ym * info->xm)); PetscFunctionReturn(PETSC_SUCCESS); } PetscErrorCode ReactingFlowPostCheck(SNESLineSearch linesearch, Vec X, Vec Y, Vec W, PetscBool *changed_y, PetscBool *changed_w, void *vctx) { PetscInt i, j, l, Mx, My, xs, ys, xm, ym; Field **x; SNES snes; DM da; PetscScalar min; PetscFunctionBeginUser; *changed_w = PETSC_FALSE; PetscCall(VecMin(X, NULL, &min)); if (min >= 0.) PetscFunctionReturn(PETSC_SUCCESS); *changed_w = PETSC_TRUE; PetscCall(SNESLineSearchGetSNES(linesearch, &snes)); PetscCall(SNESGetDM(snes, &da)); PetscCall(DMDAGetInfo(da, PETSC_IGNORE, &Mx, &My, PETSC_IGNORE, PETSC_IGNORE, PETSC_IGNORE, PETSC_IGNORE, PETSC_IGNORE, PETSC_IGNORE, PETSC_IGNORE, PETSC_IGNORE, PETSC_IGNORE, PETSC_IGNORE)); PetscCall(DMDAVecGetArray(da, W, &x)); PetscCall(DMDAGetCorners(da, &xs, &ys, NULL, &xm, &ym, NULL)); for (j = ys; j < ys + ym; j++) { for (i = xs; i < xs + xm; i++) { for (l = 0; l < N_SPECIES; l++) { if (x[j][i].sp[l] < 0.) x[j][i].sp[l] = 0.; } } } PetscCall(DMDAVecRestoreArray(da, W, &x)); PetscFunctionReturn(PETSC_SUCCESS); } PetscErrorCode FormIFunction(TS ts, PetscReal ptime, Vec X, Vec Xdot, Vec F, void *user) { DMDALocalInfo info; Field **u, **udot, **fu; Vec localX; DM da; PetscFunctionBeginUser; PetscCall(TSGetDM(ts, (DM *)&da)); PetscCall(DMGetLocalVector(da, &localX)); PetscCall(DMGlobalToLocalBegin(da, X, INSERT_VALUES, localX)); PetscCall(DMGlobalToLocalEnd(da, X, INSERT_VALUES, localX)); PetscCall(DMDAGetLocalInfo(da, &info)); PetscCall(DMDAVecGetArrayRead(da, localX, &u)); PetscCall(DMDAVecGetArrayRead(da, Xdot, &udot)); PetscCall(DMDAVecGetArray(da, F, &fu)); PetscCall(FormIFunctionLocal(&info, ptime, u, udot, fu, (AppCtx *)user)); PetscCall(DMDAVecRestoreArrayRead(da, localX, &u)); PetscCall(DMDAVecRestoreArrayRead(da, Xdot, &udot)); PetscCall(DMDAVecRestoreArray(da, F, &fu)); PetscCall(DMRestoreLocalVector(da, &localX)); PetscFunctionReturn(PETSC_SUCCESS); }