Actual source code: ex26.c

  1: /*$Id: gs-cylinder.c, xtang Exp $*/

  3: static char help[] = "Grad-Shafranov solver for one dimensional CHI equilibrium.n
  4: The command line options include:n
  5:   -n <n> number of grid pointsn
  6:   -psi_axis <axis> n
  7:   -r_min <min> n
  8:   -param <param> nn";

 10: /*T
 11:    Concepts: SNES^parallel CHI equilibrium
 12:    Concepts: DA^using distributed arrays;
 13:    Processors: n
 14: T*/

 16: /* ------------------------------------------------------------------------

 18:    Grad-Shafranov solver for one dimensional CHI equilibrium
 19:   
 20:     A finite difference approximation with the usual 3-point stencil
 21:     is used to discretize the boundary value problem to obtain a nonlinear 
 22:     system of equations.

 24:     Contributed by Xianzhu Tang, LANL

 26:     An interesting feature of this example is that as you refine the grid
 27:     (with a larger -n <n> you cannot force the residual norm as small. This
 28:     appears to be due to "NOISE" in the function, the FormFunctionLocal() cannot
 29:     be computed as accurately with a finer grid.
 30:   ------------------------------------------------------------------------- */

 32:  #include petscda.h
 33:  #include petscsnes.h

 35: /* 
 36:    User-defined application context - contains data needed by the 
 37:    application-provided call-back routines, FormJacobian() and
 38:    FormFunction().
 39: */
 40: typedef struct {
 41:   DA            da;               /* distributed array data structure */
 42:   Vec           psi,r;            /* solution, residual vectors */
 43:   Mat           A,J;              /* Jacobian matrix */
 44:   Vec           coordinates;      /* grid coordinates */
 45:   PassiveReal   psi_axis,psi_bdy;
 46:   PassiveReal   r_min;
 47:   PassiveReal   param;            /* test problem parameter */
 48: } AppCtx;

 50: #define GdGdPsi(r,u)      (((r) < 0.05) ? 0.0 : (user->param*((r)-0.05)*(1.0-(u)*(u))*(1.0-(u)*(u))))
 51: #define CurrentWire(r)    (((r) < .05) ? -3.E2 : 0.0)
 52: #define GdGdPsiPrime(r,u) (((r) < 0.05) ? 0.0 : -4.*(user->param*((r)-0.05)*(1.0-(u)*(u)))*u)

 54: /* 
 55:    User-defined routines
 56: */
 57: extern int FormInitialGuess(AppCtx*,Vec);
 58: extern int FormJacobian(SNES,Vec,Mat*,Mat*,MatStructure*,void*);
 59: extern int FormFunctionLocal(DALocalInfo*,PetscScalar*,PetscScalar*,AppCtx*);

 61: int main(int argc,char **argv)
 62: {
 63:   SNES                   snes;                 /* nonlinear solver */
 64:   AppCtx                 user;                 /* user-defined work context */
 65:   int                    its;                  /* iterations for convergence */
 66:   PetscTruth             fd_jacobian = PETSC_FALSE;
 67:   PetscTruth             adicmf_jacobian = PETSC_FALSE;
 68:   int                    grids = 100, dof = 1, stencil_width = 1;
 69:   int                    ierr;
 70:   PetscReal              fnorm;
 71:   MatFDColoring          matfdcoloring = 0;
 72:   ISColoring             iscoloring;

 74:   /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 75:      Initialize program
 76:      - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */

 78:   PetscInitialize(&argc,&argv,(char *)0,help);
 79: 
 80:   /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 81:      Initialize problem parameters
 82:   - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */

 84:   user.psi_axis=0.0;
 85:   PetscOptionsGetReal(PETSC_NULL,"-psi_axis",&user.psi_axis,PETSC_NULL);
 86:   user.psi_bdy=1.0;
 87:   PetscOptionsGetReal(PETSC_NULL,"-psi_bdy",&user.psi_bdy,PETSC_NULL);
 88:   user.r_min=0.0;
 89:   PetscOptionsGetReal(PETSC_NULL,"-r_min",&user.r_min,PETSC_NULL);
 90:   user.param=-1.E1;
 91:   PetscOptionsGetReal(PETSC_NULL,"-param",&user.param,PETSC_NULL);

 93:   /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 94:      Create nonlinear solver context
 95:      - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
 96:   SNESCreate(PETSC_COMM_WORLD,&snes);
 97: 
 98:   /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 99:      Create distributed array (DA) to manage parallel grid and vectors
100:      - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
101:   PetscOptionsGetInt(PETSC_NULL,"-n",&grids,PETSC_NULL);
102:   DACreate1d(PETSC_COMM_WORLD,DA_NONPERIODIC,grids,dof,stencil_width,PETSC_NULL,&user.da);
103: 
104:   /*  - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
105:      Extract global vectors from DA; then duplicate for remaining
106:      vectors that are the same types
107:    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
108:   DACreateGlobalVector(user.da,&user.psi);
109:   VecDuplicate(user.psi,&user.r);

111:   /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
112:      Set local function evaluation routine
113:   - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */

115:   DASetLocalFunction(user.da,(DALocalFunction1)FormFunctionLocal);

117:   SNESSetFunction(snes,user.r,SNESDAFormFunction,&user);

119:   /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
120:      Create matrix data structure; set Jacobian evaluation routine

122:      Set Jacobian matrix data structure and default Jacobian evaluation
123:      routine. User can override with:
124:      -snes_mf : matrix-free Newton-Krylov method with no preconditioning
125:                 (unless user explicitly sets preconditioner) 
126:      -snes_mf_operator : form preconditioning matrix as set by the user,
127:                          but use matrix-free approx for Jacobian-vector
128:                          products within Newton-Krylov method

130:      - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
131:   PetscOptionsGetLogical(PETSC_NULL,"-fd_jacobian",&fd_jacobian,0);
132:   /*
133:        Note that fd_jacobian DOES NOT compute the finite difference approximation to 
134:     the ENTIRE Jacobian. Rather it removes the global coupling from the Jacobian and
135:     computes the finite difference approximation only for the "local" coupling.

137:        Thus running with fd_jacobian and not -snes_mf_operator or -adicmf_jacobian
138:     won't converge.
139:   */
140:   if (!fd_jacobian) {
141:     ierr      = MatCreateMPIAIJ(PETSC_COMM_WORLD,PETSC_DECIDE,PETSC_DECIDE,grids,grids,
142:                                 5,PETSC_NULL,3,PETSC_NULL,&user.J);
143:     ierr      = MatSetFromOptions(user.J);
144:     user.A    = user.J;
145:   }
146:   else {
147:     ierr      = DAGetMatrix(user.da,MATMPIAIJ,&user.J);
148:     user.A    = user.J;
149:   }

151:   PetscOptionsGetLogical(PETSC_NULL,"-adicmf_jacobian",&adicmf_jacobian,0);
152: #if defined(PETSC_HAVE_ADIC) && !defined(PETSC_USE_COMPLEX) && !defined(PETSC_USE_SINGLE)
153:   if (adicmf_jacobian) {
154:     DASetLocalAdicMFFunction(user.da,admf_FormFunctionLocal);
155:     MatRegisterDAAD();
156:     MatCreateDAAD(user.da,&user.A);
157:     MatDAADSetSNES(user.A,snes);
158:     MatDAADSetCtx(user.A,&user);
159:   }
160: #endif

162:   if (fd_jacobian) {
163:     DAGetColoring(user.da,IS_COLORING_LOCAL,&iscoloring);
164:     MatFDColoringCreate(user.J,iscoloring,&matfdcoloring);
165:     ISColoringDestroy(iscoloring);
166:     MatFDColoringSetFunction(matfdcoloring,(int (*)(void))SNESDAFormFunction,&user);
167:     MatFDColoringSetFromOptions(matfdcoloring);
168:     SNESSetJacobian(snes,user.A,user.J,SNESDefaultComputeJacobianColor,matfdcoloring);
169:   } else {
170:     SNESSetJacobian(snes,user.A,user.J,FormJacobian,&user);
171:   }

173:   /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
174:      Customize nonlinear solver; set runtime options
175:    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
176:   SNESSetFromOptions(snes);

178:   /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
179:      Evaluate initial guess
180:      Note: The user should initialize the vector, x, with the initial guess
181:      for the nonlinear solver prior to calling SNESSolve().  In particular,
182:      to employ an initial guess of zero, the user should explicitly set
183:      this vector to zero by calling VecSet().
184:   */
185:   FormInitialGuess(&user,user.psi);

187:   /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
188:      Solve nonlinear system
189:      - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
190:   SNESSolve(snes,user.psi,&its);

192:   /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
193:      Explicitly check norm of the residual of the solution
194:    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
195:   SNESDAFormFunction(snes,user.psi,user.r,(void *)&user);
196:   VecNorm(user.r,NORM_MAX,&fnorm);
197:   PetscPrintf(PETSC_COMM_WORLD,"Number of Newton iterations = %d fnorm %gn",its,fnorm);

199:   /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 
200:      Output the solution vector
201:      - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
202:   {
203:     PetscViewer view_out;
204:     PetscViewerBinaryOpen(PETSC_COMM_WORLD,"psi.binary",PETSC_BINARY_CREATE,&view_out);
205:     VecView(user.psi,view_out);
206:     PetscViewerDestroy(view_out);
207:     PetscViewerASCIIOpen(PETSC_COMM_WORLD,"psi.out",&view_out);
208:     VecView(user.psi,view_out);
209:     PetscViewerDestroy(view_out);
210:   }

212:   /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
213:      Free work space.  All PETSc objects should be destroyed when they
214:      are no longer needed.
215:    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */

217:   if (user.A != user.J) {
218:     MatDestroy(user.A);
219:   }
220:   MatDestroy(user.J);
221:   if (matfdcoloring) {
222:     MatFDColoringDestroy(matfdcoloring);
223:   }
224:   VecDestroy(user.psi);
225:   VecDestroy(user.r);
226:   SNESDestroy(snes);
227:   DADestroy(user.da);
228:   PetscFinalize();

230:   return(0);
231: }
232: /* ------------------------------------------------------------------- */
233: /* 
234:    FormInitialGuess - Forms initial approximation.

236:    Input Parameters:
237:    user - user-defined application context
238:    X - vector

240:    Output Parameter:
241:    X - vector
242:  */
243: int FormInitialGuess(AppCtx *user,Vec X)
244: {
245:   int         i,Mx,ierr,xs,xm;
246:   PetscScalar *x;

249:   DAGetInfo(user->da,PETSC_IGNORE,&Mx,PETSC_IGNORE,PETSC_IGNORE,PETSC_IGNORE,PETSC_IGNORE,
250:                    PETSC_IGNORE,PETSC_IGNORE,PETSC_IGNORE,PETSC_IGNORE,PETSC_IGNORE);

252:   /*
253:      Get a pointer to vector data.
254:        - For default PETSc vectors, VecGetArray() returns a pointer to
255:          the data array.  Otherwise, the routine is implementation dependent.
256:        - You MUST call VecRestoreArray() when you no longer need access to
257:          the array.
258:   */
259:   DAVecGetArray(user->da,X,(void**)&x);

261:   /*
262:      Get local grid boundaries (for 2-dimensional DA):
263:        xs, ys   - starting grid indices (no ghost points)
264:        xm, ym   - widths of local grid (no ghost points)

266:   */
267:   DAGetCorners(user->da,&xs,PETSC_NULL,PETSC_NULL,&xm,PETSC_NULL,PETSC_NULL);

269:   /*
270:      Compute initial guess over the locally owned part of the grid
271:   */
272:   for (i=xs; i<xs+xm; i++) {
273:     x[i] = user->psi_axis + i*(user->psi_bdy - user->psi_axis)/(PetscReal)(Mx-1);
274:   }

276:   /*
277:      Restore vector
278:   */
279:   DAVecRestoreArray(user->da,X,(void**)&x);

281:   /* 
282:      Check to see if we can import an initial guess from disk
283:   */
284:   {
285:     char         filename[256];
286:     PetscTruth   flg;
287:     PetscViewer  view_in;
288:     PetscReal    fnorm;
289:     Vec          Y;
290:     PetscOptionsGetString(PETSC_NULL,"-initialGuess",filename,256,&flg);
291:     if (flg) {
292:       PetscViewerBinaryOpen(PETSC_COMM_WORLD,filename,PETSC_BINARY_RDONLY,&view_in);
293:       VecLoad(view_in,&Y);
294:       PetscViewerDestroy(view_in);
295:       VecMax(Y,PETSC_NULL,&user->psi_bdy);
296:       SNESDAFormFunction(PETSC_NULL,Y,user->r,(void*)user);
297:       VecNorm(user->r,NORM_2,&fnorm);
298:       PetscPrintf(PETSC_COMM_WORLD,"In initial guess: psi_bdy = %f, fnorm = %g.n",user->psi_bdy,fnorm);
299:       VecCopy(Y,X);
300:       VecDestroy(Y);
301:     }
302:   }

304:   return(0);
305: }
306: /* ------------------------------------------------------------------- */
307: /* 
308:    FormFunctionLocal - Evaluates nonlinear function, F(x).
309:    
310:    Process adiC: FormFunctionLocal
311:    
312: */
313: int FormFunctionLocal(DALocalInfo *info,PetscScalar *x,PetscScalar *f,AppCtx *user)
314: {
315:   int         ierr,i,xint,xend;
316:   PetscReal   hx,dhx,r;
317:   PetscScalar u,uxx,min = 100000.0,max = -10000.0;
318:   PetscScalar psi_0=0.0, psi_a=1.0;
319: 
321:   for (i=info->xs; i<info->xs + info->xm; i++) {
322:     if (x[i] > max) max = x[i];
323:     if (x[i] < min) min = x[i];
324:   }
325:   PetscGlobalMax(&max,&psi_a,PETSC_COMM_WORLD);
326:   PetscGlobalMin(&min,&psi_0,PETSC_COMM_WORLD);

328:   hx     = 1.0/(PetscReal)(info->mx-1);  dhx    = 1.0/hx;
329: 
330:   /*
331:     Compute function over the locally owned part of the grid
332:   */
333:   if (info->xs == 0) {
334:     xint = info->xs + 1; f[0] = (4.*x[1] - x[2] - 3.*x[0])*dhx; /* f[0] = x[0] - user->psi_axis; */
335:   }
336:   else {
337:     xint = info->xs;
338:   }
339:   if ((info->xs+info->xm) == info->mx) {
340:     xend = info->mx - 1; f[info->mx-1] = -(x[info->mx-1] - user->psi_bdy)*dhx;
341:   }
342:   else {
343:     xend = info->xs + info->xm;
344:   }
345: 
346:   for (i=xint; i<xend; i++) {
347:     r       = i*hx + user->r_min;   /* r coordinate */
348:     u       = (x[i]-psi_0)/(psi_a - psi_0);
349:     uxx     = ((r+0.5*hx)*(x[i+1]-x[i]) - (r-0.5*hx)*(x[i]-x[i-1]))*dhx; /* r*nabla^2psi */
350:     f[i] = uxx  + r*GdGdPsi(r,u)*hx  + r*CurrentWire(r)*hx ;
351:   }

353:   PetscLogFlops(11*info->ym*info->xm);
354:   return(0);
355: }
356: /* ------------------------------------------------------------------- */
357: /*
358:    FormJacobian - Evaluates Jacobian matrix.

360:    Input Parameters:
361: .  snes - the SNES context
362: .  x - input vector
363: .  ptr - optional user-defined context, as set by SNESSetJacobian()

365:    Output Parameters:
366: .  A - Jacobian matrix
367: .  B - optionally different preconditioning matrix
368: .  flag - flag indicating matrix structure

370: */
371: int FormJacobian(SNES snes,Vec X,Mat *J,Mat *B,MatStructure *flag,void *ptr)
372: {
373:   AppCtx       *user = (AppCtx*)ptr;  /* user-defined application context */
374:   Mat          jac = *B;                /* Jacobian matrix */
375:   Vec          localX;
376:   int          ierr,i,j;
377:   int          col[6],row;
378:   int          xs,xm,Mx,xint,xend;
379:   PetscScalar  v[6],hx,dhx,*x;
380:   PetscReal    r, u, psi_0=0.0, psi_a=1.0;
381:   int          imin, imax;

384:   MatZeroEntries(*B);

386:   DAGetLocalVector(user->da,&localX);
387:   DAGetInfo(user->da,PETSC_IGNORE,&Mx,PETSC_IGNORE,PETSC_IGNORE,PETSC_IGNORE,PETSC_IGNORE,
388:                    PETSC_IGNORE,PETSC_IGNORE,PETSC_IGNORE,PETSC_IGNORE,PETSC_IGNORE);

390:   hx     = 1.0/(PetscReal)(Mx-1);  dhx    = 1.0/hx;

392:   imin = 0; imax = Mx-1;
393:   VecMin(X,&imin,&psi_0);
394:   VecMax(X,&imax,&psi_a);
395:   PetscPrintf(PETSC_COMM_WORLD,"psi_0(%d)=%g, psi_a(%d)=%g.n",imin,psi_0,imax,psi_a);

397:   /*
398:      Scatter ghost points to local vector, using the 2-step process
399:         DAGlobalToLocalBegin(), DAGlobalToLocalEnd().
400:      By placing code between these two statements, computations can be
401:      done while messages are in transition.
402:   */
403:   DAGlobalToLocalBegin(user->da,X,INSERT_VALUES,localX);
404:   DAGlobalToLocalEnd(user->da,X,INSERT_VALUES,localX);

406:   /*
407:      Get pointer to vector data
408:   */
409:   DAVecGetArray(user->da,localX,(void**)&x);

411:   /*
412:      Get local grid boundaries
413:   */
414:   DAGetCorners(user->da,&xs,PETSC_NULL,PETSC_NULL,&xm,PETSC_NULL,PETSC_NULL);

416:   /* 
417:      Compute entries for the locally owned part of the Jacobian.
418:       - Currently, all PETSc parallel matrix formats are partitioned by
419:         contiguous chunks of rows across the processors. 
420:       - Each processor needs to insert only elements that it owns
421:         locally (but any non-local elements will be sent to the
422:         appropriate processor during matrix assembly). 
423:       - Here, we set all entries for a particular row at once.
424:       - We can set matrix entries either using either
425:         MatSetValuesLocal() or MatSetValues(), as discussed above.
426:   */
427:   if (xs == 0) {
428:     xint = xs + 1; /* f[0] = 4.*x[1] - x[2] - 3.*x[0]; */
429:     row  = 0;     /* first row */
430:     v[0] = -3.0*dhx;                                              col[0]=row;
431:     v[1] = 4.0*dhx;                                               col[1]=row+1;
432:     v[2] = -1.0*dhx;                                              col[2]=row+2;
433:     MatSetValues(jac,1,&row,3,col,v,ADD_VALUES);
434:   }
435:   else {
436:     xint = xs;
437:   }
438:   if ((xs+xm) == Mx) {
439:     xend = Mx - 1;   /* f[Mx-1] = x[Mx-1] - user->psi_bdy; */
440:     row  = Mx - 1;  /* last row */
441:     v[0] = -1.0*dhx;
442:     MatSetValue(jac,row,row,v[0],ADD_VALUES);
443:   }
444:   else {
445:     xend = xs + xm;
446:   }

448:   for (i=xint; i<xend; i++) {
449:     r       = i*hx + user->r_min;   /* r coordinate */
450:     u       = (x[i]-psi_0)/(psi_a - psi_0);
451:     /* uxx     = ((r+0.5*hx)*(x[i+1]-x[i]) - (r-0.5*hx)*(x[i]-x[i-1]))*dhx*dhx; */ /* r*nabla^2psi */
452:     row  = i;
453:     v[0] = (r-0.5*hx)*dhx;                                                              col[0] = i-1;
454:     v[1] = -2.*r*dhx + hx*r*GdGdPsiPrime(r,u)/(psi_a - psi_0);                          col[1] = i;
455:     v[2] = (r+0.5*hx)*dhx;                                                              col[2] = i+1;
456:     v[3] = hx*r*GdGdPsiPrime(r,u)*(x[i] - psi_a)/((psi_a - psi_0)*(psi_a - psi_0));     col[3] = imin;
457:     v[4] = hx*r*GdGdPsiPrime(r,u)*(psi_0 - x[i])/((psi_a - psi_0)*(psi_a - psi_0));     col[4] = imax;
458:     MatSetValues(jac,1,&row,5,col,v,ADD_VALUES);
459:   }
460: 
461:   DAVecRestoreArray(user->da,localX,(void**)&x);
462:   DARestoreLocalVector(user->da,&localX);

464:   /* 
465:      Assemble matrix, using the 2-step process:
466:        MatAssemblyBegin(), MatAssemblyEnd().
467:   */
468:   MatAssemblyBegin(jac,MAT_FINAL_ASSEMBLY);
469:   MatAssemblyEnd(jac,MAT_FINAL_ASSEMBLY);

471:   /*
472:      Normally since the matrix has already been assembled above; this
473:      would do nothing. But in the matrix free mode -snes_mf_operator
474:      this tells the "matrix-free" matrix that a new linear system solve
475:      is about to be done.
476:   */
477:   MatAssemblyBegin(*J,MAT_FINAL_ASSEMBLY);
478:   MatAssemblyEnd(*J,MAT_FINAL_ASSEMBLY);

480:   /*
481:      Set flag to indicate that the Jacobian matrix retains an identical
482:      nonzero structure throughout all nonlinear iterations (although the
483:      values of the entries change). Thus, we can save some work in setting
484:      up the preconditioner (e.g., no need to redo symbolic factorization for
485:      ILU/ICC preconditioners).
486:       - If the nonzero structure of the matrix is different during
487:         successive linear solves, then the flag DIFFERENT_NONZERO_PATTERN
488:         must be used instead.  If you are unsure whether the matrix
489:         structure has changed or not, use the flag DIFFERENT_NONZERO_PATTERN.
490:       - Caution:  If you specify SAME_NONZERO_PATTERN, PETSc
491:         believes your assertion and does not check the structure
492:         of the matrix.  If you erroneously claim that the structure
493:         is the same when it actually is not, the new preconditioner
494:         will not function correctly.  Thus, use this optimization
495:         feature with caution!
496:   */
497:   *flag = SAME_NONZERO_PATTERN;

499:   /*
500:      Tell the matrix we will never add a new nonzero location to the
501:      matrix. If we do, it will generate an error.
502:   */

504:   return(0);
505: }