Date: 2026-07-30T21:14:31+00:00

Actual source code: ex42.c

      1: static char help[] = "Meinhard't activator-inhibitor model to test TS domain error feature.\n";

      3: /*
      4:    The activator-inhibitor on a line is described by the PDE:

      6:    da/dt = \alpha a^2 / (1 + \beta h) + \rho_a - \mu_a a + D_a d^2 a/ dx^2
      7:    dh/dt = \alpha a^2 + \rho_h - \mu_h h + D_h d^2 h/ dx^2

      9:    The PDE part will be solve by finite-difference on the line of cells.
     10:  */

     12: #include <petscts.h>

     14: typedef struct {
     15:   PetscInt  nb_cells;
     16:   PetscReal alpha;
     17:   PetscReal beta;
     18:   PetscReal rho_a;
     19:   PetscReal rho_h;
     20:   PetscReal mu_a;
     21:   PetscReal mu_h;
     22:   PetscReal D_a;
     23:   PetscReal D_h;
     24: } AppCtx;

     26: PetscErrorCode RHSFunction(TS ts, PetscReal t, Vec X, Vec DXDT, void *ptr)
     27: {
     28:   AppCtx            *user = (AppCtx *)ptr;
     29:   PetscInt           nb_cells, i;
     30:   PetscReal          alpha, beta;
     31:   PetscReal          rho_a, mu_a, D_a;
     32:   PetscReal          rho_h, mu_h, D_h;
     33:   PetscReal          a, h, da, dh, d2a, d2h;
     34:   PetscScalar       *dxdt;
     35:   const PetscScalar *x;

     37:   PetscFunctionBeginUser;
     38:   nb_cells = user->nb_cells;
     39:   alpha    = user->alpha;
     40:   beta     = user->beta;
     41:   rho_a    = user->rho_a;
     42:   mu_a     = user->mu_a;
     43:   D_a      = user->D_a;
     44:   rho_h    = user->rho_h;
     45:   mu_h     = user->mu_h;
     46:   D_h      = user->D_h;

     48:   PetscCall(VecGetArrayRead(X, &x));
     49:   PetscCall(VecGetArray(DXDT, &dxdt));

     51:   for (i = 0; i < nb_cells; i++) {
     52:     a = x[2 * i];
     53:     h = x[2 * i + 1];
     54:     // Reaction:
     55:     da = alpha * a * a / (1. + beta * h) + rho_a - mu_a * a;
     56:     dh = alpha * a * a + rho_h - mu_h * h;
     57:     // Diffusion:
     58:     d2a = d2h = 0.;
     59:     if (i > 0) {
     60:       d2a += (x[2 * (i - 1)] - a);
     61:       d2h += (x[2 * (i - 1) + 1] - h);
     62:     }
     63:     if (i < nb_cells - 1) {
     64:       d2a += (x[2 * (i + 1)] - a);
     65:       d2h += (x[2 * (i + 1) + 1] - h);
     66:     }
     67:     dxdt[2 * i]     = da + D_a * d2a;
     68:     dxdt[2 * i + 1] = dh + D_h * d2h;
     69:   }
     70:   PetscCall(VecRestoreArray(DXDT, &dxdt));
     71:   PetscCall(VecRestoreArrayRead(X, &x));
     72:   PetscFunctionReturn(PETSC_SUCCESS);
     73: }

     75: PetscErrorCode RHSJacobian(TS ts, PetscReal t, Vec X, Mat J, Mat B, void *ptr)
     76: {
     77:   AppCtx            *user = (AppCtx *)ptr;
     78:   PetscInt           nb_cells, i, idx;
     79:   PetscReal          alpha, beta;
     80:   PetscReal          mu_a, D_a;
     81:   PetscReal          mu_h, D_h;
     82:   PetscReal          a, h;
     83:   const PetscScalar *x;
     84:   PetscScalar        va[4], vh[4];
     85:   PetscInt           ca[4], ch[4], rowa, rowh;

     87:   PetscFunctionBeginUser;
     88:   nb_cells = user->nb_cells;
     89:   alpha    = user->alpha;
     90:   beta     = user->beta;
     91:   mu_a     = user->mu_a;
     92:   D_a      = user->D_a;
     93:   mu_h     = user->mu_h;
     94:   D_h      = user->D_h;

     96:   PetscCall(VecGetArrayRead(X, &x));
     97:   for (i = 0; i < nb_cells; ++i) {
     98:     rowa  = 2 * i;
     99:     rowh  = 2 * i + 1;
    100:     a     = x[2 * i];
    101:     h     = x[2 * i + 1];
    102:     ca[0] = ch[1] = 2 * i;
    103:     va[0]         = 2 * alpha * a / (1. + beta * h) - mu_a;
    104:     vh[1]         = 2 * alpha * a;
    105:     ca[1] = ch[0] = 2 * i + 1;
    106:     va[1]         = -beta * alpha * a * a / ((1. + beta * h) * (1. + beta * h));
    107:     vh[0]         = -mu_h;
    108:     idx           = 2;
    109:     if (i > 0) {
    110:       ca[idx] = 2 * (i - 1);
    111:       ch[idx] = 2 * (i - 1) + 1;
    112:       va[idx] = D_a;
    113:       vh[idx] = D_h;
    114:       va[0] -= D_a;
    115:       vh[0] -= D_h;
    116:       idx++;
    117:     }
    118:     if (i < nb_cells - 1) {
    119:       ca[idx] = 2 * (i + 1);
    120:       ch[idx] = 2 * (i + 1) + 1;
    121:       va[idx] = D_a;
    122:       vh[idx] = D_h;
    123:       va[0] -= D_a;
    124:       vh[0] -= D_h;
    125:       idx++;
    126:     }
    127:     PetscCall(MatSetValues(B, 1, &rowa, idx, ca, va, INSERT_VALUES));
    128:     PetscCall(MatSetValues(B, 1, &rowh, idx, ch, vh, INSERT_VALUES));
    129:   }
    130:   PetscCall(VecRestoreArrayRead(X, &x));
    131:   PetscCall(MatAssemblyBegin(B, MAT_FINAL_ASSEMBLY));
    132:   PetscCall(MatAssemblyEnd(B, MAT_FINAL_ASSEMBLY));
    133:   if (J != B) {
    134:     PetscCall(MatAssemblyBegin(J, MAT_FINAL_ASSEMBLY));
    135:     PetscCall(MatAssemblyEnd(J, MAT_FINAL_ASSEMBLY));
    136:   }
    137:   PetscFunctionReturn(PETSC_SUCCESS);
    138: }

    140: PetscErrorCode DomainErrorFunction(TS ts, PetscReal t, Vec Y, PetscBool *accept)
    141: {
    142:   AppCtx            *user;
    143:   PetscReal          dt;
    144:   const PetscScalar *x;
    145:   PetscInt           nb_cells, i;

    147:   PetscFunctionBeginUser;
    148:   PetscCall(TSGetApplicationContext(ts, &user));
    149:   nb_cells = user->nb_cells;
    150:   PetscCall(VecGetArrayRead(Y, &x));
    151:   for (i = 0; i < 2 * nb_cells; ++i) {
    152:     if (PetscRealPart(x[i]) < 0) {
    153:       PetscCall(TSGetTimeStep(ts, &dt));
    154:       PetscCall(PetscPrintf(PETSC_COMM_WORLD, " ** Domain Error at time %g\n", (double)t));
    155:       *accept = PETSC_FALSE;
    156:       break;
    157:     }
    158:   }
    159:   PetscCall(VecRestoreArrayRead(Y, &x));
    160:   PetscFunctionReturn(PETSC_SUCCESS);
    161: }

    163: PetscErrorCode FormInitialState(Vec X, AppCtx *user)
    164: {
    165:   PetscRandom R;

    167:   PetscFunctionBeginUser;
    168:   PetscCall(PetscRandomCreate(PETSC_COMM_WORLD, &R));
    169:   PetscCall(PetscRandomSetFromOptions(R));
    170:   PetscCall(PetscRandomSetInterval(R, 0., 10.));

    172:   /*
    173:    * Initialize the state vector
    174:    */
    175:   PetscCall(VecSetRandom(X, R));
    176:   PetscCall(PetscRandomDestroy(&R));
    177:   PetscFunctionReturn(PETSC_SUCCESS);
    178: }

    180: PetscErrorCode PrintSolution(Vec X, AppCtx *user)
    181: {
    182:   const PetscScalar *x;
    183:   PetscInt           i;
    184:   PetscInt           nb_cells = user->nb_cells;

    186:   PetscFunctionBeginUser;
    187:   PetscCall(VecGetArrayRead(X, &x));
    188:   PetscCall(PetscPrintf(PETSC_COMM_WORLD, "Activator,Inhibitor\n"));
    189:   for (i = 0; i < nb_cells; i++) PetscCall(PetscPrintf(PETSC_COMM_WORLD, "%5.6e,%5.6e\n", (double)x[2 * i], (double)x[2 * i + 1]));
    190:   PetscCall(VecRestoreArrayRead(X, &x));
    191:   PetscFunctionReturn(PETSC_SUCCESS);
    192: }

    194: int main(int argc, char **argv)
    195: {
    196:   TS          ts;   /* time-stepping context */
    197:   Vec         x;    /* State vector */
    198:   Mat         J;    /* Jacobian matrix */
    199:   AppCtx      user; /* user-defined context */
    200:   PetscReal   ftime;
    201:   PetscInt    its;
    202:   PetscMPIInt size;

    204:   PetscFunctionBeginUser;
    205:   PetscCall(PetscInitialize(&argc, &argv, NULL, help));
    206:   PetscCallMPI(MPI_Comm_size(PETSC_COMM_WORLD, &size));
    207:   PetscCheck(size == 1, PETSC_COMM_WORLD, PETSC_ERR_WRONG_MPI_SIZE, "This is a uniprocessor example only");

    209:   /*
    210:    * Allow user to set the grid dimensions and the equations parameters
    211:    */

    213:   user.nb_cells = 50;
    214:   user.alpha    = 10.;
    215:   user.beta     = 1.;
    216:   user.rho_a    = 1.;
    217:   user.rho_h    = 2.;
    218:   user.mu_a     = 2.;
    219:   user.mu_h     = 3.;
    220:   user.D_a      = 0.;
    221:   user.D_h      = 30.;

    223:   PetscOptionsBegin(PETSC_COMM_WORLD, "", "Problem settings", "PROBLEM");
    224:   PetscCall(PetscOptionsInt("-nb_cells", "Number of cells", "ex42.c", user.nb_cells, &user.nb_cells, NULL));
    225:   PetscCall(PetscOptionsReal("-alpha", "Autocatalysis factor", "ex42.c", user.alpha, &user.alpha, NULL));
    226:   PetscCall(PetscOptionsReal("-beta", "Inhibition factor", "ex42.c", user.beta, &user.beta, NULL));
    227:   PetscCall(PetscOptionsReal("-rho_a", "Default production of the activator", "ex42.c", user.rho_a, &user.rho_a, NULL));
    228:   PetscCall(PetscOptionsReal("-mu_a", "Degradation rate of the activator", "ex42.c", user.mu_a, &user.mu_a, NULL));
    229:   PetscCall(PetscOptionsReal("-D_a", "Diffusion rate of the activator", "ex42.c", user.D_a, &user.D_a, NULL));
    230:   PetscCall(PetscOptionsReal("-rho_h", "Default production of the inhibitor", "ex42.c", user.rho_h, &user.rho_h, NULL));
    231:   PetscCall(PetscOptionsReal("-mu_h", "Degradation rate of the inhibitor", "ex42.c", user.mu_h, &user.mu_h, NULL));
    232:   PetscCall(PetscOptionsReal("-D_h", "Diffusion rate of the inhibitor", "ex42.c", user.D_h, &user.D_h, NULL));
    233:   PetscOptionsEnd();

    235:   PetscCall(PetscPrintf(PETSC_COMM_WORLD, "nb_cells: %" PetscInt_FMT "\n", user.nb_cells));
    236:   PetscCall(PetscPrintf(PETSC_COMM_WORLD, "alpha: %5.5g\n", (double)user.alpha));
    237:   PetscCall(PetscPrintf(PETSC_COMM_WORLD, "beta:  %5.5g\n", (double)user.beta));
    238:   PetscCall(PetscPrintf(PETSC_COMM_WORLD, "rho_a: %5.5g\n", (double)user.rho_a));
    239:   PetscCall(PetscPrintf(PETSC_COMM_WORLD, "mu_a:  %5.5g\n", (double)user.mu_a));
    240:   PetscCall(PetscPrintf(PETSC_COMM_WORLD, "D_a:   %5.5g\n", (double)user.D_a));
    241:   PetscCall(PetscPrintf(PETSC_COMM_WORLD, "rho_h: %5.5g\n", (double)user.rho_h));
    242:   PetscCall(PetscPrintf(PETSC_COMM_WORLD, "mu_h:  %5.5g\n", (double)user.mu_h));
    243:   PetscCall(PetscPrintf(PETSC_COMM_WORLD, "D_h:   %5.5g\n", (double)user.D_h));

    245:   /*
    246:    * Create vector to hold the solution
    247:    */
    248:   PetscCall(VecCreateSeq(PETSC_COMM_WORLD, 2 * user.nb_cells, &x));

    250:   /*
    251:    * Create time-stepper context
    252:    */
    253:   PetscCall(TSCreate(PETSC_COMM_WORLD, &ts));
    254:   PetscCall(TSSetProblemType(ts, TS_NONLINEAR));

    256:   /*
    257:    * Tell the time-stepper context where to compute the solution
    258:    */
    259:   PetscCall(TSSetSolution(ts, x));

    261:   /*
    262:    * Allocate the jacobian matrix
    263:    */
    264:   PetscCall(MatCreateSeqAIJ(PETSC_COMM_WORLD, 2 * user.nb_cells, 2 * user.nb_cells, 4, 0, &J));

    266:   /*
    267:    * Provide the call-back for the non-linear function we are evaluating.
    268:    */
    269:   PetscCall(TSSetRHSFunction(ts, NULL, RHSFunction, &user));

    271:   /*
    272:    * Set the Jacobian matrix and the function user to compute Jacobians
    273:    */
    274:   PetscCall(TSSetRHSJacobian(ts, J, J, RHSJacobian, &user));

    276:   /*
    277:    * Set the function checking the domain
    278:    */
    279:   PetscCall(TSSetFunctionDomainError(ts, DomainErrorFunction));

    281:   /*
    282:    * Initialize the problem with random values
    283:    */
    284:   PetscCall(FormInitialState(x, &user));

    286:   /*
    287:    * Read the solver type from options
    288:    */
    289:   PetscCall(TSSetType(ts, TSPSEUDO));

    291:   /*
    292:    * Set a large number of timesteps and final duration time to insure
    293:    * convergenge to steady state
    294:    */
    295:   PetscCall(TSSetMaxSteps(ts, 2147483647));
    296:   PetscCall(TSSetMaxTime(ts, 1.e12));
    297:   PetscCall(TSSetExactFinalTime(ts, TS_EXACTFINALTIME_STEPOVER));

    299:   /*
    300:    * Set a larger number of potential errors
    301:    */
    302:   PetscCall(TSSetMaxStepRejections(ts, 50));

    304:   /*
    305:    * Also start with a very small dt
    306:    */
    307:   PetscCall(TSSetTimeStep(ts, 0.05));

    309:   /*
    310:    * Set a larger time step increment
    311:    */
    312:   PetscCall(TSPseudoSetTimeStepIncrement(ts, 1.5));

    314:   /*
    315:    * Let the user personalise TS
    316:    */
    317:   PetscCall(TSSetFromOptions(ts));

    319:   /*
    320:    * Set the context for the time stepper
    321:    */
    322:   PetscCall(TSSetApplicationContext(ts, &user));

    324:   /*
    325:    * Setup the time stepper, ready for evaluation
    326:    */
    327:   PetscCall(TSSetUp(ts));

    329:   /*
    330:    * Perform the solve.
    331:    */
    332:   PetscCall(TSSolve(ts, x));
    333:   PetscCall(TSGetSolveTime(ts, &ftime));
    334:   PetscCall(TSGetStepNumber(ts, &its));
    335:   PetscCall(PetscPrintf(PETSC_COMM_WORLD, "Number of time steps = %" PetscInt_FMT ", final time: %4.2e\nResult:\n\n", its, (double)ftime));
    336:   PetscCall(PrintSolution(x, &user));

    338:   /*
    339:    * Free the data structures
    340:    */
    341:   PetscCall(VecDestroy(&x));
    342:   PetscCall(MatDestroy(&J));
    343:   PetscCall(TSDestroy(&ts));
    344:   PetscCall(PetscFinalize());
    345:   return 0;
    346: }

    348: /*TEST
    349:     build:
    350:       requires: !single !complex

    352:     test:
    353:       args: -ts_max_steps 20
    354:       output_file: output/ex42.out

    356: TEST*/