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: }