Actual source code: ex21.c

  1: static char help[] = "Solves a time-dependent nonlinear PDE with lower and upper bounds on the interior grid points. Uses implicit\n\
  2: timestepping.  Runtime options include:\n\
  3:   -M <xg>, where <xg> = number of grid points\n\
  4:   -debug : Activate debugging printouts\n\
  5:   -nox   : Deactivate x-window graphics\n\
  6:   -ul   : lower bound\n\
  7:   -uh  : upper bound\n\n";

  9: /* ------------------------------------------------------------------------

 11:    This is a variation of ex2.c to solve the PDE

 13:                u * u_xx
 14:          u_t = ---------
 15:                2*(t+1)^2

 17:     with box constraints on the interior grid points
 18:     ul <= u(t,x) <= uh with x != 0,1
 19:     on the domain 0 <= x <= 1, with boundary conditions
 20:          u(t,0) = t + 1,  u(t,1) = 2*t + 2,
 21:     and initial condition
 22:          u(0,x) = 1 + x*x.

 24:     The exact solution is:
 25:          u(t,x) = (1 + x*x) * (1 + t)

 27:     We use by default the backward Euler method.

 29:   ------------------------------------------------------------------------- */

 31: /*
 32:    Include "petscts.h" to use the PETSc timestepping routines. Note that
 33:    this file automatically includes "petscsys.h" and other lower-level
 34:    PETSc include files.

 36:    Include the "petscdmda.h" to allow us to use the distributed array data
 37:    structures to manage the parallel grid.
 38: */
 39: #include <petscts.h>
 40: #include <petscdm.h>
 41: #include <petscdmda.h>
 42: #include <petscdraw.h>

 44: /*
 45:    User-defined application context - contains data needed by the
 46:    application-provided callback routines.
 47: */
 48: typedef struct {
 49:   MPI_Comm  comm;      /* communicator */
 50:   DM        da;        /* distributed array data structure */
 51:   Vec       localwork; /* local ghosted work vector */
 52:   Vec       u_local;   /* local ghosted approximate solution vector */
 53:   Vec       solution;  /* global exact solution vector */
 54:   PetscInt  m;         /* total number of grid points */
 55:   PetscReal h;         /* mesh width: h = 1/(m-1) */
 56:   PetscBool debug;     /* flag (1 indicates activation of debugging printouts) */
 57: } AppCtx;

 59: /*
 60:    User-defined routines, provided below.
 61: */
 62: extern PetscErrorCode InitialConditions(Vec, AppCtx *);
 63: extern PetscErrorCode RHSFunction(TS, PetscReal, Vec, Vec, void *);
 64: extern PetscErrorCode RHSJacobian(TS, PetscReal, Vec, Mat, Mat, void *);
 65: extern PetscErrorCode Monitor(TS, PetscInt, PetscReal, Vec, void *);
 66: extern PetscErrorCode ExactSolution(PetscReal, Vec, AppCtx *);
 67: extern PetscErrorCode SetBounds(Vec, Vec, PetscScalar, PetscScalar, AppCtx *);

 69: int main(int argc, char **argv)
 70: {
 71:   AppCtx      appctx;                /* user-defined application context */
 72:   TS          ts;                    /* timestepping context */
 73:   Mat         A;                     /* Jacobian matrix data structure */
 74:   Vec         u;                     /* approximate solution vector */
 75:   Vec         r;                     /* residual vector */
 76:   PetscInt    time_steps_max = 1000; /* default max timesteps */
 77:   PetscReal   dt;
 78:   PetscReal   time_total_max = 100.0; /* default max total time */
 79:   Vec         xl, xu;                 /* Lower and upper bounds on variables */
 80:   PetscScalar ul = 0.0, uh = 3.0;
 81:   PetscBool   mymonitor;
 82:   PetscReal   bounds[] = {1.0, 3.3};

 84:   /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 85:      Initialize program and set problem parameters
 86:      - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */

 88:   PetscFunctionBeginUser;
 89:   PetscCall(PetscInitialize(&argc, &argv, (char *)0, help));
 90:   PetscCall(PetscViewerDrawSetBounds(PETSC_VIEWER_DRAW_(PETSC_COMM_WORLD), 1, bounds));

 92:   appctx.comm = PETSC_COMM_WORLD;
 93:   appctx.m    = 60;
 94:   PetscCall(PetscOptionsGetInt(NULL, NULL, "-M", &appctx.m, NULL));
 95:   PetscCall(PetscOptionsGetScalar(NULL, NULL, "-ul", &ul, NULL));
 96:   PetscCall(PetscOptionsGetScalar(NULL, NULL, "-uh", &uh, NULL));
 97:   PetscCall(PetscOptionsHasName(NULL, NULL, "-debug", &appctx.debug));
 98:   PetscCall(PetscOptionsHasName(NULL, NULL, "-mymonitor", &mymonitor));
 99:   appctx.h = 1.0 / (appctx.m - 1.0);

101:   /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
102:      Create vector data structures
103:      - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */

105:   /*
106:      Create distributed array (DMDA) to manage parallel grid and vectors
107:      and to set up the ghost point communication pattern.  There are M
108:      total grid values spread equally among all the processors.
109:   */
110:   PetscCall(DMDACreate1d(PETSC_COMM_WORLD, DM_BOUNDARY_NONE, appctx.m, 1, 1, NULL, &appctx.da));
111:   PetscCall(DMSetFromOptions(appctx.da));
112:   PetscCall(DMSetUp(appctx.da));

114:   /*
115:      Extract global and local vectors from DMDA; we use these to store the
116:      approximate solution.  Then duplicate these for remaining vectors that
117:      have the same types.
118:   */
119:   PetscCall(DMCreateGlobalVector(appctx.da, &u));
120:   PetscCall(DMCreateLocalVector(appctx.da, &appctx.u_local));

122:   /*
123:      Create local work vector for use in evaluating right-hand-side function;
124:      create global work vector for storing exact solution.
125:   */
126:   PetscCall(VecDuplicate(appctx.u_local, &appctx.localwork));
127:   PetscCall(VecDuplicate(u, &appctx.solution));

129:   /* Create residual vector */
130:   PetscCall(VecDuplicate(u, &r));
131:   /* Create lower and upper bound vectors */
132:   PetscCall(VecDuplicate(u, &xl));
133:   PetscCall(VecDuplicate(u, &xu));
134:   PetscCall(SetBounds(xl, xu, ul, uh, &appctx));

136:   /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
137:      Create timestepping solver context; set callback routine for
138:      right-hand-side function evaluation.
139:      - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */

141:   PetscCall(TSCreate(PETSC_COMM_WORLD, &ts));
142:   PetscCall(TSSetProblemType(ts, TS_NONLINEAR));
143:   PetscCall(TSSetRHSFunction(ts, r, RHSFunction, &appctx));

145:   /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
146:      Set optional user-defined monitoring routine
147:      - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */

149:   if (mymonitor) PetscCall(TSMonitorSet(ts, Monitor, &appctx, NULL));

151:   /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
152:      For nonlinear problems, the user can provide a Jacobian evaluation
153:      routine (or use a finite differencing approximation).

155:      Create matrix data structure; set Jacobian evaluation routine.
156:      - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */

158:   PetscCall(MatCreate(PETSC_COMM_WORLD, &A));
159:   PetscCall(MatSetSizes(A, PETSC_DECIDE, PETSC_DECIDE, appctx.m, appctx.m));
160:   PetscCall(MatSetFromOptions(A));
161:   PetscCall(MatSetUp(A));
162:   PetscCall(TSSetRHSJacobian(ts, A, A, RHSJacobian, &appctx));

164:   /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
165:      Set solution vector and initial timestep
166:      - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */

168:   dt = appctx.h / 2.0;
169:   PetscCall(TSSetTimeStep(ts, dt));

171:   /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
172:      Customize timestepping solver:
173:        - Set the solution method to be the Backward Euler method.
174:        - Set timestepping duration info
175:      Then set runtime options, which can override these defaults.
176:      For example,
177:           -ts_max_steps <maxsteps> -ts_max_time <maxtime>
178:      to override the defaults set by TSSetMaxSteps()/TSSetMaxTime().
179:      - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */

181:   PetscCall(TSSetType(ts, TSBEULER));
182:   PetscCall(TSSetMaxSteps(ts, time_steps_max));
183:   PetscCall(TSSetMaxTime(ts, time_total_max));
184:   PetscCall(TSSetExactFinalTime(ts, TS_EXACTFINALTIME_STEPOVER));
185:   /* Set lower and upper bound on the solution vector for each time step */
186:   PetscCall(TSVISetVariableBounds(ts, xl, xu));
187:   PetscCall(TSSetFromOptions(ts));

189:   /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
190:      Solve the problem
191:      - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */

193:   /*
194:      Evaluate initial conditions
195:   */
196:   PetscCall(InitialConditions(u, &appctx));

198:   /*
199:      Run the timestepping solver
200:   */
201:   PetscCall(TSSolve(ts, u));

203:   /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
204:      Free work space.  All PETSc objects should be destroyed when they
205:      are no longer needed.
206:      - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */

208:   PetscCall(VecDestroy(&r));
209:   PetscCall(VecDestroy(&xl));
210:   PetscCall(VecDestroy(&xu));
211:   PetscCall(TSDestroy(&ts));
212:   PetscCall(VecDestroy(&u));
213:   PetscCall(MatDestroy(&A));
214:   PetscCall(DMDestroy(&appctx.da));
215:   PetscCall(VecDestroy(&appctx.localwork));
216:   PetscCall(VecDestroy(&appctx.solution));
217:   PetscCall(VecDestroy(&appctx.u_local));

219:   /*
220:      Always call PetscFinalize() before exiting a program.  This routine
221:        - finalizes the PETSc libraries as well as MPI
222:        - provides summary and diagnostic information if certain runtime
223:          options are chosen (e.g., -log_view).
224:   */
225:   PetscCall(PetscFinalize());
226:   return 0;
227: }
228: /* --------------------------------------------------------------------- */
229: /*
230:    InitialConditions - Computes the solution at the initial time.

232:    Input Parameters:
233:    u - uninitialized solution vector (global)
234:    appctx - user-defined application context

236:    Output Parameter:
237:    u - vector with solution at initial time (global)
238: */
239: PetscErrorCode InitialConditions(Vec u, AppCtx *appctx)
240: {
241:   PetscScalar *u_localptr, h = appctx->h, x;
242:   PetscInt     i, mybase, myend;

244:   PetscFunctionBeginUser;
245:   /*
246:      Determine starting point of each processor's range of
247:      grid values.
248:   */
249:   PetscCall(VecGetOwnershipRange(u, &mybase, &myend));

251:   /*
252:     Get a pointer to vector data.
253:     - For default PETSc vectors, VecGetArray() returns a pointer to
254:       the data array.  Otherwise, the routine is implementation dependent.
255:     - You MUST call VecRestoreArray() when you no longer need access to
256:       the array.
257:     - Note that the Fortran interface to VecGetArray() differs from the
258:       C version.  See the users manual for details.
259:   */
260:   PetscCall(VecGetArray(u, &u_localptr));

262:   /*
263:      We initialize the solution array by simply writing the solution
264:      directly into the array locations.  Alternatively, we could use
265:      VecSetValues() or VecSetValuesLocal().
266:   */
267:   for (i = mybase; i < myend; i++) {
268:     x                      = h * (PetscReal)i; /* current location in global grid */
269:     u_localptr[i - mybase] = 1.0 + x * x;
270:   }

272:   /*
273:      Restore vector
274:   */
275:   PetscCall(VecRestoreArray(u, &u_localptr));

277:   /*
278:      Print debugging information if desired
279:   */
280:   if (appctx->debug) {
281:     PetscCall(PetscPrintf(appctx->comm, "initial guess vector\n"));
282:     PetscCall(VecView(u, PETSC_VIEWER_STDOUT_WORLD));
283:   }

285:   PetscFunctionReturn(PETSC_SUCCESS);
286: }

288: /* --------------------------------------------------------------------- */
289: /*
290:   SetBounds - Sets the lower and upper bounds on the interior points

292:   Input parameters:
293:   xl - vector of lower bounds
294:   xu - vector of upper bounds
295:   ul - constant lower bound for all variables
296:   uh - constant upper bound for all variables
297:   appctx - Application context
298:  */
299: PetscErrorCode SetBounds(Vec xl, Vec xu, PetscScalar ul, PetscScalar uh, AppCtx *appctx)
300: {
301:   PetscScalar *l, *u;
302:   PetscMPIInt  rank, size;
303:   PetscInt     localsize;

305:   PetscFunctionBeginUser;
306:   PetscCall(VecSet(xl, ul));
307:   PetscCall(VecSet(xu, uh));
308:   PetscCall(VecGetLocalSize(xl, &localsize));
309:   PetscCall(VecGetArray(xl, &l));
310:   PetscCall(VecGetArray(xu, &u));

312:   PetscCallMPI(MPI_Comm_rank(appctx->comm, &rank));
313:   PetscCallMPI(MPI_Comm_size(appctx->comm, &size));
314:   if (rank == 0) {
315:     l[0] = -PETSC_INFINITY;
316:     u[0] = PETSC_INFINITY;
317:   }
318:   if (rank == size - 1) {
319:     l[localsize - 1] = -PETSC_INFINITY;
320:     u[localsize - 1] = PETSC_INFINITY;
321:   }
322:   PetscCall(VecRestoreArray(xl, &l));
323:   PetscCall(VecRestoreArray(xu, &u));
324:   PetscFunctionReturn(PETSC_SUCCESS);
325: }

327: /* --------------------------------------------------------------------- */
328: /*
329:    ExactSolution - Computes the exact solution at a given time.

331:    Input Parameters:
332:    t - current time
333:    solution - vector in which exact solution will be computed
334:    appctx - user-defined application context

336:    Output Parameter:
337:    solution - vector with the newly computed exact solution
338: */
339: PetscErrorCode ExactSolution(PetscReal t, Vec solution, AppCtx *appctx)
340: {
341:   PetscScalar *s_localptr, h = appctx->h, x;
342:   PetscInt     i, mybase, myend;

344:   PetscFunctionBeginUser;
345:   /*
346:      Determine starting and ending points of each processor's
347:      range of grid values
348:   */
349:   PetscCall(VecGetOwnershipRange(solution, &mybase, &myend));

351:   /*
352:      Get a pointer to vector data.
353:   */
354:   PetscCall(VecGetArray(solution, &s_localptr));

356:   /*
357:      Simply write the solution directly into the array locations.
358:      Alternatively, we could use VecSetValues() or VecSetValuesLocal().
359:   */
360:   for (i = mybase; i < myend; i++) {
361:     x                      = h * (PetscReal)i;
362:     s_localptr[i - mybase] = (t + 1.0) * (1.0 + x * x);
363:   }

365:   /*
366:      Restore vector
367:   */
368:   PetscCall(VecRestoreArray(solution, &s_localptr));
369:   PetscFunctionReturn(PETSC_SUCCESS);
370: }
371: /* --------------------------------------------------------------------- */
372: /*
373:    Monitor - User-provided routine to monitor the solution computed at
374:    each timestep.  This example plots the solution and computes the
375:    error in two different norms.

377:    Input Parameters:
378:    ts     - the timestep context
379:    step   - the count of the current step (with 0 meaning the
380:             initial condition)
381:    time   - the current time
382:    u      - the solution at this timestep
383:    ctx    - the user-provided context for this monitoring routine.
384:             In this case we use the application context which contains
385:             information about the problem size, workspace and the exact
386:             solution.
387: */
388: PetscErrorCode Monitor(TS ts, PetscInt step, PetscReal time, Vec u, void *ctx)
389: {
390:   AppCtx   *appctx = (AppCtx *)ctx; /* user-defined application context */
391:   PetscReal en2, en2s, enmax;
392:   PetscDraw draw;

394:   PetscFunctionBeginUser;
395:   /*
396:      We use the default X windows viewer
397:              PETSC_VIEWER_DRAW_(appctx->comm)
398:      that is associated with the current communicator. This saves
399:      the effort of calling PetscViewerDrawOpen() to create the window.
400:      Note that if we wished to plot several items in separate windows we
401:      would create each viewer with PetscViewerDrawOpen() and store them in
402:      the application context, appctx.

404:      PetscReal buffering makes graphics look better.
405:   */
406:   PetscCall(PetscViewerDrawGetDraw(PETSC_VIEWER_DRAW_(appctx->comm), 0, &draw));
407:   PetscCall(PetscDrawSetDoubleBuffer(draw));
408:   PetscCall(VecView(u, PETSC_VIEWER_DRAW_(appctx->comm)));

410:   /*
411:      Compute the exact solution at this timestep
412:   */
413:   PetscCall(ExactSolution(time, appctx->solution, appctx));

415:   /*
416:      Print debugging information if desired
417:   */
418:   if (appctx->debug) {
419:     PetscCall(PetscPrintf(appctx->comm, "Computed solution vector\n"));
420:     PetscCall(VecView(u, PETSC_VIEWER_STDOUT_WORLD));
421:     PetscCall(PetscPrintf(appctx->comm, "Exact solution vector\n"));
422:     PetscCall(VecView(appctx->solution, PETSC_VIEWER_STDOUT_WORLD));
423:   }

425:   /*
426:      Compute the 2-norm and max-norm of the error
427:   */
428:   PetscCall(VecAXPY(appctx->solution, -1.0, u));
429:   PetscCall(VecNorm(appctx->solution, NORM_2, &en2));
430:   en2s = PetscSqrtReal(appctx->h) * en2; /* scale the 2-norm by the grid spacing */
431:   PetscCall(VecNorm(appctx->solution, NORM_MAX, &enmax));

433:   /*
434:      PetscPrintf() causes only the first processor in this
435:      communicator to print the timestep information.
436:   */
437:   PetscCall(PetscPrintf(appctx->comm, "Timestep %" PetscInt_FMT ": time = %g,2-norm error = %g, max norm error = %g\n", step, (double)time, (double)en2s, (double)enmax));

439:   /*
440:      Print debugging information if desired
441:    */
442:   /*  if (appctx->debug) {
443:      PetscCall(PetscPrintf(appctx->comm,"Error vector\n"));
444:      PetscCall(VecView(appctx->solution,PETSC_VIEWER_STDOUT_WORLD));
445:    } */
446:   PetscFunctionReturn(PETSC_SUCCESS);
447: }
448: /* --------------------------------------------------------------------- */
449: /*
450:    RHSFunction - User-provided routine that evalues the right-hand-side
451:    function of the ODE.  This routine is set in the main program by
452:    calling TSSetRHSFunction().  We compute:
453:           global_out = F(global_in)

455:    Input Parameters:
456:    ts         - timesteping context
457:    t          - current time
458:    global_in  - vector containing the current iterate
459:    ctx        - (optional) user-provided context for function evaluation.
460:                 In this case we use the appctx defined above.

462:    Output Parameter:
463:    global_out - vector containing the newly evaluated function
464: */
465: PetscErrorCode RHSFunction(TS ts, PetscReal t, Vec global_in, Vec global_out, void *ctx)
466: {
467:   AppCtx            *appctx    = (AppCtx *)ctx;     /* user-defined application context */
468:   DM                 da        = appctx->da;        /* distributed array */
469:   Vec                local_in  = appctx->u_local;   /* local ghosted input vector */
470:   Vec                localwork = appctx->localwork; /* local ghosted work vector */
471:   PetscInt           i, localsize;
472:   PetscMPIInt        rank, size;
473:   PetscScalar       *copyptr, sc;
474:   const PetscScalar *localptr;

476:   PetscFunctionBeginUser;
477:   /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
478:      Get ready for local function computations
479:      - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
480:   /*
481:      Scatter ghost points to local vector, using the 2-step process
482:         DMGlobalToLocalBegin(), DMGlobalToLocalEnd().
483:      By placing code between these two statements, computations can be
484:      done while messages are in transition.
485:   */
486:   PetscCall(DMGlobalToLocalBegin(da, global_in, INSERT_VALUES, local_in));
487:   PetscCall(DMGlobalToLocalEnd(da, global_in, INSERT_VALUES, local_in));

489:   /*
490:       Access directly the values in our local INPUT work array
491:   */
492:   PetscCall(VecGetArrayRead(local_in, &localptr));

494:   /*
495:       Access directly the values in our local OUTPUT work array
496:   */
497:   PetscCall(VecGetArray(localwork, &copyptr));

499:   sc = 1.0 / (appctx->h * appctx->h * 2.0 * (1.0 + t) * (1.0 + t));

501:   /*
502:       Evaluate our function on the nodes owned by this processor
503:   */
504:   PetscCall(VecGetLocalSize(local_in, &localsize));

506:   /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
507:      Compute entries for the locally owned part
508:      - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */

510:   /*
511:      Handle boundary conditions: This is done by using the boundary condition
512:         u(t,boundary) = g(t,boundary)
513:      for some function g. Now take the derivative with respect to t to obtain
514:         u_{t}(t,boundary) = g_{t}(t,boundary)

516:      In our case, u(t,0) = t + 1, so that u_{t}(t,0) = 1
517:              and  u(t,1) = 2t+ 2, so that u_{t}(t,1) = 2
518:   */
519:   PetscCallMPI(MPI_Comm_rank(appctx->comm, &rank));
520:   PetscCallMPI(MPI_Comm_size(appctx->comm, &size));
521:   if (rank == 0) copyptr[0] = 1.0;
522:   if (rank == size - 1) copyptr[localsize - 1] = (t < .5) ? 2.0 : 0.0;

524:   /*
525:      Handle the interior nodes where the PDE is replace by finite
526:      difference operators.
527:   */
528:   for (i = 1; i < localsize - 1; i++) copyptr[i] = localptr[i] * sc * (localptr[i + 1] + localptr[i - 1] - 2.0 * localptr[i]);

530:   /*
531:      Restore vectors
532:   */
533:   PetscCall(VecRestoreArrayRead(local_in, &localptr));
534:   PetscCall(VecRestoreArray(localwork, &copyptr));

536:   /*
537:      Insert values from the local OUTPUT vector into the global
538:      output vector
539:   */
540:   PetscCall(DMLocalToGlobalBegin(da, localwork, INSERT_VALUES, global_out));
541:   PetscCall(DMLocalToGlobalEnd(da, localwork, INSERT_VALUES, global_out));

543:   /* Print debugging information if desired */
544:   /*  if (appctx->debug) {
545:      PetscCall(PetscPrintf(appctx->comm,"RHS function vector\n"));
546:      PetscCall(VecView(global_out,PETSC_VIEWER_STDOUT_WORLD));
547:    } */

549:   PetscFunctionReturn(PETSC_SUCCESS);
550: }
551: /* --------------------------------------------------------------------- */
552: /*
553:    RHSJacobian - User-provided routine to compute the Jacobian of
554:    the nonlinear right-hand-side function of the ODE.

556:    Input Parameters:
557:    ts - the TS context
558:    t - current time
559:    global_in - global input vector
560:    dummy - optional user-defined context, as set by TSetRHSJacobian()

562:    Output Parameters:
563:    AA - Jacobian matrix
564:    BB - optionally different preconditioning matrix
565:    str - flag indicating matrix structure

567:   Notes:
568:   RHSJacobian computes entries for the locally owned part of the Jacobian.
569:    - Currently, all PETSc parallel matrix formats are partitioned by
570:      contiguous chunks of rows across the processors.
571:    - Each processor needs to insert only elements that it owns
572:      locally (but any non-local elements will be sent to the
573:      appropriate processor during matrix assembly).
574:    - Always specify global row and columns of matrix entries when
575:      using MatSetValues().
576:    - Here, we set all entries for a particular row at once.
577:    - Note that MatSetValues() uses 0-based row and column numbers
578:      in Fortran as well as in C.
579: */
580: PetscErrorCode RHSJacobian(TS ts, PetscReal t, Vec global_in, Mat AA, Mat B, void *ctx)
581: {
582:   AppCtx            *appctx   = (AppCtx *)ctx;   /* user-defined application context */
583:   Vec                local_in = appctx->u_local; /* local ghosted input vector */
584:   DM                 da       = appctx->da;      /* distributed array */
585:   PetscScalar        v[3], sc;
586:   const PetscScalar *localptr;
587:   PetscInt           i, mstart, mend, mstarts, mends, idx[3], is;

589:   PetscFunctionBeginUser;
590:   /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
591:      Get ready for local Jacobian computations
592:      - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
593:   /*
594:      Scatter ghost points to local vector, using the 2-step process
595:         DMGlobalToLocalBegin(), DMGlobalToLocalEnd().
596:      By placing code between these two statements, computations can be
597:      done while messages are in transition.
598:   */
599:   PetscCall(DMGlobalToLocalBegin(da, global_in, INSERT_VALUES, local_in));
600:   PetscCall(DMGlobalToLocalEnd(da, global_in, INSERT_VALUES, local_in));

602:   /*
603:      Get pointer to vector data
604:   */
605:   PetscCall(VecGetArrayRead(local_in, &localptr));

607:   /*
608:      Get starting and ending locally owned rows of the matrix
609:   */
610:   PetscCall(MatGetOwnershipRange(B, &mstarts, &mends));
611:   mstart = mstarts;
612:   mend   = mends;

614:   /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
615:      Compute entries for the locally owned part of the Jacobian.
616:       - Currently, all PETSc parallel matrix formats are partitioned by
617:         contiguous chunks of rows across the processors.
618:       - Each processor needs to insert only elements that it owns
619:         locally (but any non-local elements will be sent to the
620:         appropriate processor during matrix assembly).
621:       - Here, we set all entries for a particular row at once.
622:       - We can set matrix entries either using either
623:         MatSetValuesLocal() or MatSetValues().
624:      - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */

626:   /*
627:      Set matrix rows corresponding to boundary data
628:   */
629:   if (mstart == 0) {
630:     v[0] = 0.0;
631:     PetscCall(MatSetValues(B, 1, &mstart, 1, &mstart, v, INSERT_VALUES));
632:     mstart++;
633:   }
634:   if (mend == appctx->m) {
635:     mend--;
636:     v[0] = 0.0;
637:     PetscCall(MatSetValues(B, 1, &mend, 1, &mend, v, INSERT_VALUES));
638:   }

640:   /*
641:      Set matrix rows corresponding to interior data.  We construct the
642:      matrix one row at a time.
643:   */
644:   sc = 1.0 / (appctx->h * appctx->h * 2.0 * (1.0 + t) * (1.0 + t));
645:   for (i = mstart; i < mend; i++) {
646:     idx[0] = i - 1;
647:     idx[1] = i;
648:     idx[2] = i + 1;
649:     is     = i - mstart + 1;
650:     v[0]   = sc * localptr[is];
651:     v[1]   = sc * (localptr[is + 1] + localptr[is - 1] - 4.0 * localptr[is]);
652:     v[2]   = sc * localptr[is];
653:     PetscCall(MatSetValues(B, 1, &i, 3, idx, v, INSERT_VALUES));
654:   }

656:   /*
657:      Restore vector
658:   */
659:   PetscCall(VecRestoreArrayRead(local_in, &localptr));

661:   /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
662:      Complete the matrix assembly process and set some options
663:      - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
664:   /*
665:      Assemble matrix, using the 2-step process:
666:        MatAssemblyBegin(), MatAssemblyEnd()
667:      Computations can be done while messages are in transition
668:      by placing code between these two statements.
669:   */
670:   PetscCall(MatAssemblyBegin(B, MAT_FINAL_ASSEMBLY));
671:   PetscCall(MatAssemblyEnd(B, MAT_FINAL_ASSEMBLY));

673:   /*
674:      Set and option to indicate that we will never add a new nonzero location
675:      to the matrix. If we do, it will generate an error.
676:   */
677:   PetscCall(MatSetOption(B, MAT_NEW_NONZERO_LOCATION_ERR, PETSC_TRUE));

679:   PetscFunctionReturn(PETSC_SUCCESS);
680: }

682: /*TEST

684:     test:
685:       args: -snes_type vinewtonrsls -ts_type glee -mymonitor -ts_max_steps 10 -nox
686:       requires: !single

688: TEST*/