Actual source code: ex19.c

  1: /*$Id: ex19.c,v 1.19 2001/04/10 19:37:05 bsmith Exp $*/

  3: static char help[] = "Nonlinear driven cavity with multigrid in 2d.n
  4:   n
  5: The 2D driven cavity problem is solved in a velocity-vorticity formulation.n
  6: The flow can be driven with the lid or with bouyancy or both:n
  7:   -lidvelocity <lid>, where <lid> = dimensionless velocity of lidn
  8:   -grashof <gr>, where <gr> = dimensionless temperature gradientn
  9:   -prandtl <pr>, where <pr> = dimensionless thermal/momentum diffusity ration
 10: Mesh parameters are:n
 11:   -mx <xg>, where <xg> = number of grid points in the x-directionn
 12:   -my <yg>, where <yg> = number of grid points in the y-directionn
 13:   -printg : print grid informationn
 14: Graphics of the contours of (U,V,Omega,T) are available on each grid:n
 15:   -contours : draw contour plots of solutionnn";

 17: /*T
 18:    Concepts: SNES^solving a system of nonlinear equations (parallel multicomponent example);
 19:    Concepts: DA^using distributed arrays;
 20:    Concepts: multicomponent
 21:    Processors: n
 22: T*/

 24: /* ------------------------------------------------------------------------

 26:     This code is the same as ex8.c except it uses a multigrid preconditioner

 28:     We thank David E. Keyes for contributing the driven cavity discretization
 29:     within this example code.

 31:     This problem is modeled by the partial differential equation system
 32:   
 33:         - Lap(U) - Grad_y(Omega) = 0
 34:         - Lap(V) + Grad_x(Omega) = 0
 35:         - Lap(Omega) + Div([U*Omega,V*Omega]) - GR*Grad_x(T) = 0
 36:         - Lap(T) + PR*Div([U*T,V*T]) = 0

 38:     in the unit square, which is uniformly discretized in each of x and
 39:     y in this simple encoding.

 41:     No-slip, rigid-wall Dirichlet conditions are used for [U,V].
 42:     Dirichlet conditions are used for Omega, based on the definition of
 43:     vorticity: Omega = - Grad_y(U) + Grad_x(V), where along each
 44:     constant coordinate boundary, the tangential derivative is zero.
 45:     Dirichlet conditions are used for T on the left and right walls,
 46:     and insulation homogeneous Neumann conditions are used for T on
 47:     the top and bottom walls. 

 49:     A finite difference approximation with the usual 5-point stencil 
 50:     is used to discretize the boundary value problem to obtain a 
 51:     nonlinear system of equations.  Upwinding is used for the divergence
 52:     (convective) terms and central for the gradient (source) terms.
 53:     
 54:     The Jacobian can be either
 55:       * formed via finite differencing using coloring (the default), or
 56:       * applied matrix-free via the option -snes_mf 
 57:         (for larger grid problems this variant may not converge 
 58:         without a preconditioner due to ill-conditioning).

 60:   ------------------------------------------------------------------------- */

 62: /* 
 63:    Include "petscda.h" so that we can use distributed arrays (DAs).
 64:    Include "petscsnes.h" so that we can use SNES solvers.  Note that this
 65:    file automatically includes:
 66:      petsc.h       - base PETSc routines   petscvec.h - vectors
 67:      petscsys.h    - system routines       petscmat.h - matrices
 68:      petscis.h     - index sets            petscksp.h - Krylov subspace methods
 69:      petscviewer.h - viewers               petscpc.h  - preconditioners
 70:      petscsles.h   - linear solvers 
 71: */
 72: #include "petscsnes.h"
 73: #include "petscda.h"

 75: /* 
 76:    User-defined routines and data structures
 77: */
 78: typedef struct {
 79:   Scalar u,v,omega,temp;
 80: } Field;

 82: extern int FormInitialGuess(SNES,Vec,void*);
 83: extern int FormFunction(SNES,Vec,Vec,void*);
 84: extern int FormFunctionLocal(Field**x,Field**f,DALocalInfo*info,void*);

 86: typedef struct {
 87:    double     lidvelocity,prandtl,grashof;  /* physical parameters */
 88:    PetscTruth draw_contours;                /* flag - 1 indicates drawing contours */
 89: } AppCtx;

 91: int main(int argc,char **argv)
 92: {
 93:   DMMG       *dmmg;               /* multilevel grid structure */
 94:   AppCtx     user;                /* user-defined work context */
 95:   int        mx,my,its;
 96:   int        ierr;
 97:   MPI_Comm   comm;
 98:   SNES       snes;
 99:   DA         da;
100:   PetscTruth localfunction = PETSC_TRUE;

102:   PetscInitialize(&argc,&argv,(char *)0,help);
103:   comm = PETSC_COMM_WORLD;


106:   PreLoadBegin(PETSC_TRUE,"SetUp");
107:     DMMGCreate(comm,2,&user,&dmmg);


110:     /*
111:       Create distributed array multigrid object (DMMG) to manage parallel grid and vectors
112:       for principal unknowns (x) and governing residuals (f)
113:     */
114:     DACreate2d(comm,DA_NONPERIODIC,DA_STENCIL_STAR,4,4,PETSC_DECIDE,PETSC_DECIDE,4,1,0,0,&da);
115:     DMMGSetDM(dmmg,(DM)da);
116:     DADestroy(da);

118:     DAGetInfo(DMMGGetDA(dmmg),0,&mx,&my,PETSC_IGNORE,PETSC_IGNORE,PETSC_IGNORE,PETSC_IGNORE,PETSC_IGNORE,PETSC_IGNORE,
119:                      PETSC_IGNORE,PETSC_IGNORE);
120:     /* 
121:      Problem parameters (velocity of lid, prandtl, and grashof numbers)
122:     */
123:     user.lidvelocity = 1.0/(mx*my);
124:     user.prandtl     = 1.0;
125:     user.grashof     = 1.0;
126:     PetscOptionsGetDouble(PETSC_NULL,"-lidvelocity",&user.lidvelocity,PETSC_NULL);
127:     PetscOptionsGetDouble(PETSC_NULL,"-prandtl",&user.prandtl,PETSC_NULL);
128:     PetscOptionsGetDouble(PETSC_NULL,"-grashof",&user.grashof,PETSC_NULL);
129:     PetscOptionsHasName(PETSC_NULL,"-contours",&user.draw_contours);

131:     DASetFieldName(DMMGGetDA(dmmg),0,"x-velocity");
132:     DASetFieldName(DMMGGetDA(dmmg),1,"y-velocity");
133:     DASetFieldName(DMMGGetDA(dmmg),2,"Omega");
134:     DASetFieldName(DMMGGetDA(dmmg),3,"temperature");

136:     /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
137:        Create user context, set problem data, create vector data structures.
138:        Also, compute the initial guess.
139:        - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */

141:     /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
142:        Create nonlinear solver context
143:        - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */

145:     PetscOptionsGetLogical(PETSC_NULL,"-localfunction",&localfunction,PETSC_IGNORE);
146:     if (localfunction) {
147:       DMMGSetSNESLocal(dmmg,(int(*)(Scalar**,Scalar**,DALocalInfo*,void*))FormFunctionLocal,0);
148:     } else {
149:       DMMGSetSNES(dmmg,FormFunction,0);
150:     }

152:     PetscPrintf(comm,"lid velocity = %g, prandtl # = %g, grashof # = %gn",
153:                        user.lidvelocity,user.prandtl,user.grashof);


156:     /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
157:        Solve the nonlinear system
158:        - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
159:     DMMGSetInitialGuess(dmmg,FormInitialGuess);

161:   PreLoadStage("Solve");
162:     DMMGSolve(dmmg);

164:     snes = DMMGGetSNES(dmmg);
165:     SNESGetIterationNumber(snes,&its);
166:     PetscPrintf(comm,"Number of Newton iterations = %dn", its);

168:     /*
169:       Visualize solution
170:     */

172:     if (user.draw_contours) {
173:       VecView(DMMGGetx(dmmg),PETSC_VIEWER_DRAW_WORLD);
174:     }

176:     /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
177:        Free work space.  All PETSc objects should be destroyed when they
178:        are no longer needed.
179:        - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */

181:     DMMGDestroy(dmmg);
182:   PreLoadEnd();

184:   PetscFinalize();
185:   return 0;
186: }

188: /* ------------------------------------------------------------------- */


191: /* 
192:    FormInitialGuess - Forms initial approximation.

194:    Input Parameters:
195:    user - user-defined application context
196:    X - vector

198:    Output Parameter:
199:    X - vector
200:  */
201: int FormInitialGuess(SNES snes,Vec X,void *ptr)
202: {
203:   DMMG    dmmg = (DMMG)ptr;
204:   AppCtx  *user = (AppCtx*)dmmg->user;
205:   DA      da = (DA)dmmg->dm;
206:   int     i,j,mx,ierr,xs,ys,xm,ym;
207:   double  grashof,dx;
208:   Field   **x;

210:   grashof = user->grashof;

212:   DAGetInfo(da,0,&mx,0,0,0,0,0,0,0,0,0);
213:   dx  = 1.0/(mx-1);

215:   /*
216:      Get local grid boundaries (for 2-dimensional DA):
217:        xs, ys   - starting grid indices (no ghost points)
218:        xm, ym   - widths of local grid (no ghost points)
219:   */
220:   DAGetCorners(da,&xs,&ys,PETSC_NULL,&xm,&ym,PETSC_NULL);

222:   /*
223:      Get a pointer to vector data.
224:        - For default PETSc vectors, VecGetArray() returns a pointer to
225:          the data array.  Otherwise, the routine is implementation dependent.
226:        - You MUST call VecRestoreArray() when you no longer need access to
227:          the array.
228:   */
229:   DAVecGetArray(da,X,(void**)&x);

231:   /*
232:      Compute initial guess over the locally owned part of the grid
233:      Initial condition is motionless fluid and equilibrium temperature
234:   */
235:   for (j=ys; j<ys+ym; j++) {
236:     for (i=xs; i<xs+xm; i++) {
237:       x[j][i].u     = 0.0;
238:       x[j][i].v     = 0.0;
239:       x[j][i].omega = 0.0;
240:       x[j][i].temp  = (grashof>0)*i*dx;
241:     }
242:   }

244:   /*
245:      Restore vector
246:   */
247:   DAVecRestoreArray(da,X,(void**)&x);
248:   return 0;
249: }
250: /* ------------------------------------------------------------------- */
251: /* 
252:    FormFunction - Evaluates the nonlinear function, F(x).

254:    Input Parameters:
255: .  snes - the SNES context
256: .  X - input vector
257: .  ptr - optional user-defined context, as set by SNESSetFunction()

259:    Output Parameter:
260: .  F - function vector

262:    Notes:
263:    We process the boundary nodes before handling the interior
264:    nodes, so that no conditional statements are needed within the
265:    double loop over the local grid indices. 
266:  */
267: int FormFunction(SNES snes,Vec X,Vec F,void *ptr)
268: {
269:   DMMG    dmmg = (DMMG)ptr;
270:   AppCtx  *user = (AppCtx*)dmmg->user;
271:   int     ierr,i,j,mx,my,xs,ys,xm,ym;
272:   int     xints,xinte,yints,yinte;
273:   double  two = 2.0,one = 1.0,p5 = 0.5,hx,hy,dhx,dhy,hxdhy,hydhx;
274:   double  grashof,prandtl,lid;
275:   Scalar  u,uxx,uyy,vx,vy,avx,avy,vxp,vxm,vyp,vym;
276:   Field   **x,**f;
277:   Vec     localX;
278:   DA      da = (DA)dmmg->dm;

280:   DAGetLocalVector((DA)dmmg->dm,&localX);
281:   DAGetInfo(da,0,&mx,&my,0,0,0,0,0,0,0,0);

283:   grashof = user->grashof;
284:   prandtl = user->prandtl;
285:   lid     = user->lidvelocity;

287:   /* 
288:      Define mesh intervals ratios for uniform grid.
289:      [Note: FD formulae below are normalized by multiplying through by
290:      local volume element to obtain coefficients O(1) in two dimensions.]
291:   */
292:   dhx = (double)(mx-1);     dhy = (double)(my-1);
293:   hx = one/dhx;             hy = one/dhy;
294:   hxdhy = hx*dhy;           hydhx = hy*dhx;

296:   /*
297:      Scatter ghost points to local vector, using the 2-step process
298:         DAGlobalToLocalBegin(), DAGlobalToLocalEnd().
299:      By placing code between these two statements, computations can be
300:      done while messages are in transition.
301:   */
302:   DAGlobalToLocalBegin(da,X,INSERT_VALUES,localX);
303:   DAGlobalToLocalEnd(da,X,INSERT_VALUES,localX);

305:   /*
306:      Get pointers to vector data
307:   */
308:   DAVecGetArray((DA)dmmg->dm,localX,(void**)&x);
309:   DAVecGetArray((DA)dmmg->dm,F,(void**)&f);

311:   /*
312:      Get local grid boundaries
313:   */
314:   DAGetCorners(da,&xs,&ys,PETSC_NULL,&xm,&ym,PETSC_NULL);

316:   /*
317:      Compute function over the locally owned part of the grid
318:      (physical corner points are set twice to avoid more conditionals).
319:   */
320:   xints = xs; xinte = xs+xm; yints = ys; yinte = ys+ym;

322:   /* Test whether we are on the bottom edge of the global array */
323:   if (yints == 0) {
324:     j = 0;
325:     yints = yints + 1;
326:     /* bottom edge */
327:     for (i=xs; i<xs+xm; i++) {
328:         f[j][i].u     = x[j][i].u;
329:         f[j][i].v     = x[j][i].v;
330:         f[j][i].omega = x[j][i].omega + (x[j+1][i].u - x[j][i].u)*dhy;
331:         f[j][i].temp  = x[j][i].temp-x[j+1][i].temp;
332:     }
333:   }

335:   /* Test whether we are on the top edge of the global array */
336:   if (yinte == my) {
337:     j = my - 1;
338:     yinte = yinte - 1;
339:     /* top edge */
340:     for (i=xs; i<xs+xm; i++) {
341:         f[j][i].u     = x[j][i].u - lid;
342:         f[j][i].v     = x[j][i].v;
343:         f[j][i].omega = x[j][i].omega + (x[j][i].u - x[j-1][i].u)*dhy;
344:         f[j][i].temp  = x[j][i].temp-x[j-1][i].temp;
345:     }
346:   }

348:   /* Test whether we are on the left edge of the global array */
349:   if (xints == 0) {
350:     i = 0;
351:     xints = xints + 1;
352:     /* left edge */
353:     for (j=ys; j<ys+ym; j++) {
354:       f[j][i].u     = x[j][i].u;
355:       f[j][i].v     = x[j][i].v;
356:       f[j][i].omega = x[j][i].omega - (x[j][i+1].v - x[j][i].v)*dhx;
357:       f[j][i].temp  = x[j][i].temp;
358:     }
359:   }

361:   /* Test whether we are on the right edge of the global array */
362:   if (xinte == mx) {
363:     i = mx - 1;
364:     xinte = xinte - 1;
365:     /* right edge */
366:     for (j=ys; j<ys+ym; j++) {
367:       f[j][i].u     = x[j][i].u;
368:       f[j][i].v     = x[j][i].v;
369:       f[j][i].omega = x[j][i].omega - (x[j][i].v - x[j][i-1].v)*dhx;
370:       f[j][i].temp  = x[j][i].temp - (double)(grashof>0);
371:     }
372:   }

374:   /* Compute over the interior points */
375:   for (j=yints; j<yinte; j++) {
376:     for (i=xints; i<xinte; i++) {

378:         /*
379:           convective coefficients for upwinding
380:         */
381:         vx = x[j][i].u; avx = PetscAbsScalar(vx);
382:         vxp = p5*(vx+avx); vxm = p5*(vx-avx);
383:         vy = x[j][i].v; avy = PetscAbsScalar(vy);
384:         vyp = p5*(vy+avy); vym = p5*(vy-avy);

386:         /* U velocity */
387:         u          = x[j][i].u;
388:         uxx        = (two*u - x[j][i-1].u - x[j][i+1].u)*hydhx;
389:         uyy        = (two*u - x[j-1][i].u - x[j+1][i].u)*hxdhy;
390:         f[j][i].u  = uxx + uyy - p5*(x[j+1][i].omega-x[j-1][i].omega)*hx;

392:         /* V velocity */
393:         u          = x[j][i].v;
394:         uxx        = (two*u - x[j][i-1].v - x[j][i+1].v)*hydhx;
395:         uyy        = (two*u - x[j-1][i].v - x[j+1][i].v)*hxdhy;
396:         f[j][i].v  = uxx + uyy + p5*(x[j][i+1].omega-x[j][i-1].omega)*hy;

398:         /* Omega */
399:         u          = x[j][i].omega;
400:         uxx        = (two*u - x[j][i-1].omega - x[j][i+1].omega)*hydhx;
401:         uyy        = (two*u - x[j-1][i].omega - x[j+1][i].omega)*hxdhy;
402:         f[j][i].omega = uxx + uyy +
403:                         (vxp*(u - x[j][i-1].omega) +
404:                           vxm*(x[j][i+1].omega - u)) * hy +
405:                         (vyp*(u - x[j-1][i].omega) +
406:                           vym*(x[j+1][i].omega - u)) * hx -
407:                         p5 * grashof * (x[j][i+1].temp - x[j][i-1].temp) * hy;

409:         /* Temperature */
410:         u             = x[j][i].temp;
411:         uxx           = (two*u - x[j][i-1].temp - x[j][i+1].temp)*hydhx;
412:         uyy           = (two*u - x[j-1][i].temp - x[j+1][i].temp)*hxdhy;
413:         f[j][i].temp =  uxx + uyy  + prandtl * (
414:                         (vxp*(u - x[j][i-1].temp) +
415:                           vxm*(x[j][i+1].temp - u)) * hy +
416:                         (vyp*(u - x[j-1][i].temp) +
417:                                  vym*(x[j+1][i].temp - u)) * hx);
418:     }
419:   }

421:   /*
422:      Restore vectors
423:   */
424:   DAVecRestoreArray((DA)dmmg->dm,localX,(void**)&x);
425:   DAVecRestoreArray((DA)dmmg->dm,F,(void**)&f);

427:   DARestoreLocalVector((DA)dmmg->dm,&localX);

429:   /*
430:      Flop count (multiply-adds are counted as 2 operations)
431:   */
432:   PetscLogFlops(84*ym*xm);

434:   return 0;
435: }

437: int FormFunctionLocal(Field **x,Field **f,DALocalInfo *info,void *ptr)
438:  {
439:   AppCtx  *user = (AppCtx*)ptr;
440:   int     ierr,i,j;
441:   int     xints,xinte,yints,yinte;
442:   double  hx,hy,dhx,dhy,hxdhy,hydhx;
443:   double  grashof,prandtl,lid;
444:   Scalar  u,uxx,uyy,vx,vy,avx,avy,vxp,vxm,vyp,vym;

446:   grashof = user->grashof;
447:   prandtl = user->prandtl;
448:   lid     = user->lidvelocity;

450:   /* 
451:      Define mesh intervals ratios for uniform grid.
452:      [Note: FD formulae below are normalized by multiplying through by
453:      local volume element to obtain coefficients O(1) in two dimensions.]
454:   */
455:   dhx = (double)(info->mx-1);     dhy = (double)(info->my-1);
456:   hx = 1.0/dhx;                   hy = 1.0/dhy;
457:   hxdhy = hx*dhy;                 hydhx = hy*dhx;

459:   xints = info->xs; xinte = info->xs+info->xm; yints = info->ys; yinte = info->ys+info->ym;

461:   /* Test whether we are on the bottom edge of the global array */
462:   if (yints == 0) {
463:     j = 0;
464:     yints = yints + 1;
465:     /* bottom edge */
466:     for (i=info->xs; i<info->xs+info->xm; i++) {
467:         f[j][i].u     = x[j][i].u;
468:         f[j][i].v     = x[j][i].v;
469:         f[j][i].omega = x[j][i].omega + (x[j+1][i].u - x[j][i].u)*dhy;
470:         f[j][i].temp  = x[j][i].temp-x[j+1][i].temp;
471:     }
472:   }

474:   /* Test whether we are on the top edge of the global array */
475:   if (yinte == info->my) {
476:     j = info->my - 1;
477:     yinte = yinte - 1;
478:     /* top edge */
479:     for (i=info->xs; i<info->xs+info->xm; i++) {
480:         f[j][i].u     = x[j][i].u - lid;
481:         f[j][i].v     = x[j][i].v;
482:         f[j][i].omega = x[j][i].omega + (x[j][i].u - x[j-1][i].u)*dhy;
483:         f[j][i].temp  = x[j][i].temp-x[j-1][i].temp;
484:     }
485:   }

487:   /* Test whether we are on the left edge of the global array */
488:   if (xints == 0) {
489:     i = 0;
490:     xints = xints + 1;
491:     /* left edge */
492:     for (j=info->ys; j<info->ys+info->ym; j++) {
493:       f[j][i].u     = x[j][i].u;
494:       f[j][i].v     = x[j][i].v;
495:       f[j][i].omega = x[j][i].omega - (x[j][i+1].v - x[j][i].v)*dhx;
496:       f[j][i].temp  = x[j][i].temp;
497:     }
498:   }

500:   /* Test whether we are on the right edge of the global array */
501:   if (xinte == info->mx) {
502:     i = info->mx - 1;
503:     xinte = xinte - 1;
504:     /* right edge */
505:     for (j=info->ys; j<info->ys+info->ym; j++) {
506:       f[j][i].u     = x[j][i].u;
507:       f[j][i].v     = x[j][i].v;
508:       f[j][i].omega = x[j][i].omega - (x[j][i].v - x[j][i-1].v)*dhx;
509:       f[j][i].temp  = x[j][i].temp - (double)(grashof>0);
510:     }
511:   }

513:   /* Compute over the interior points */
514:   for (j=yints; j<yinte; j++) {
515:     for (i=xints; i<xinte; i++) {

517:         /*
518:           convective coefficients for upwinding
519:         */
520:         vx = x[j][i].u; avx = PetscAbsScalar(vx);
521:         vxp = .5*(vx+avx); vxm = .5*(vx-avx);
522:         vy = x[j][i].v; avy = PetscAbsScalar(vy);
523:         vyp = .5*(vy+avy); vym = .5*(vy-avy);

525:         /* U velocity */
526:         u          = x[j][i].u;
527:         uxx        = (2.0*u - x[j][i-1].u - x[j][i+1].u)*hydhx;
528:         uyy        = (2.0*u - x[j-1][i].u - x[j+1][i].u)*hxdhy;
529:         f[j][i].u  = uxx + uyy - .5*(x[j+1][i].omega-x[j-1][i].omega)*hx;

531:         /* V velocity */
532:         u          = x[j][i].v;
533:         uxx        = (2.0*u - x[j][i-1].v - x[j][i+1].v)*hydhx;
534:         uyy        = (2.0*u - x[j-1][i].v - x[j+1][i].v)*hxdhy;
535:         f[j][i].v  = uxx + uyy + .5*(x[j][i+1].omega-x[j][i-1].omega)*hy;

537:         /* Omega */
538:         u          = x[j][i].omega;
539:         uxx        = (2.0*u - x[j][i-1].omega - x[j][i+1].omega)*hydhx;
540:         uyy        = (2.0*u - x[j-1][i].omega - x[j+1][i].omega)*hxdhy;
541:         f[j][i].omega = uxx + uyy +
542:                         (vxp*(u - x[j][i-1].omega) +
543:                           vxm*(x[j][i+1].omega - u)) * hy +
544:                         (vyp*(u - x[j-1][i].omega) +
545:                           vym*(x[j+1][i].omega - u)) * hx -
546:                         .5 * grashof * (x[j][i+1].temp - x[j][i-1].temp) * hy;

548:         /* Temperature */
549:         u             = x[j][i].temp;
550:         uxx           = (2.0*u - x[j][i-1].temp - x[j][i+1].temp)*hydhx;
551:         uyy           = (2.0*u - x[j-1][i].temp - x[j+1][i].temp)*hxdhy;
552:         f[j][i].temp =  uxx + uyy  + prandtl * (
553:                         (vxp*(u - x[j][i-1].temp) +
554:                           vxm*(x[j][i+1].temp - u)) * hy +
555:                         (vyp*(u - x[j-1][i].temp) +
556:                                  vym*(x[j+1][i].temp - u)) * hx);
557:     }
558:   }

560:   /*
561:      Flop count (multiply-adds are counted as 2 operations)
562:   */
563:   PetscLogFlops(84*info->ym*info->xm);
564:   return 0;
565: }