Actual source code: ex10.c
1: /*
2: Include "petscsnes.h" so that we can use SNES solvers. Note that this
3: file automatically includes:
4: petscsys.h - base PETSc routines petscvec.h - vectors
5: petscmat.h - matrices
6: petscis.h - index sets petscksp.h - Krylov subspace methods
7: petscviewer.h - viewers petscpc.h - preconditioners
8: petscksp.h - linear solvers
9: */
10: #include <petscsnes.h>
11: #include <petscao.h>
13: static char help[] = "An Unstructured Grid Example.\n\
14: This example demonstrates how to solve a nonlinear system in parallel\n\
15: with SNES for an unstructured mesh. The mesh and partitioning information\n\
16: is read in an application defined ordering,which is later transformed\n\
17: into another convenient ordering (called the local ordering). The local\n\
18: ordering, apart from being efficient on cpu cycles and memory, allows\n\
19: the use of the SPMD model of parallel programming. After partitioning\n\
20: is done, scatters are created between local (sequential)and global\n\
21: (distributed) vectors. Finally, we set up the nonlinear solver context\n\
22: in the usual way as a structured grid (see\n\
23: petsc/src/snes/tutorials/ex5.c).\n\
24: This example also illustrates the use of parallel matrix coloring.\n\
25: The command line options include:\n\
26: -vert <Nv>, where Nv is the global number of nodes\n\
27: -elem <Ne>, where Ne is the global number of elements\n\
28: -nl_par <lambda>, where lambda is the multiplier for the non linear term (u*u) term\n\
29: -lin_par <alpha>, where alpha is the multiplier for the linear term (u)\n\
30: -fd_jacobian_coloring -mat_coloring_type lf\n";
32: /* ------------------------------------------------------------------------
34: PDE Solved : L(u) + lambda*u*u + alpha*u = 0 where L(u) is the Laplacian.
36: The Laplacian is approximated in the following way: each edge is given a weight
37: of one meaning that the diagonal term will have the weight equal to the degree
38: of a node. The off-diagonal terms will get a weight of -1.
40: -----------------------------------------------------------------------*/
42: #define MAX_ELEM 500 /* Maximum number of elements */
43: #define MAX_VERT 100 /* Maximum number of vertices */
44: #define MAX_VERT_ELEM 3 /* Vertices per element */
46: /*
47: Application-defined context for problem specific data
48: */
49: typedef struct {
50: PetscInt Nvglobal, Nvlocal; /* global and local number of vertices */
51: PetscInt Neglobal, Nelocal; /* global and local number of vertices */
52: PetscInt AdjM[MAX_VERT][50]; /* adjacency list of a vertex */
53: PetscInt itot[MAX_VERT]; /* total number of neighbors for a vertex */
54: PetscInt icv[MAX_ELEM][MAX_VERT_ELEM]; /* vertices belonging to an element */
55: PetscInt v2p[MAX_VERT]; /* processor number for a vertex */
56: PetscInt *locInd, *gloInd; /* local and global orderings for a node */
57: Vec localX, localF; /* local solution (u) and f(u) vectors */
58: PetscReal non_lin_param; /* nonlinear parameter for the PDE */
59: PetscReal lin_param; /* linear parameter for the PDE */
60: VecScatter scatter; /* scatter context for the local and
61: distributed vectors */
62: } AppCtx;
64: /*
65: User-defined routines
66: */
67: PetscErrorCode FormJacobian(SNES, Vec, Mat, Mat, void *);
68: PetscErrorCode FormFunction(SNES, Vec, Vec, void *);
69: PetscErrorCode FormInitialGuess(AppCtx *, Vec);
71: int main(int argc, char **argv)
72: {
73: SNES snes; /* SNES context */
74: SNESType type = SNESNEWTONLS; /* default nonlinear solution method */
75: Vec x, r; /* solution, residual vectors */
76: Mat Jac; /* Jacobian matrix */
77: AppCtx user; /* user-defined application context */
78: AO ao; /* Application Ordering object */
79: IS isglobal, islocal; /* global and local index sets */
80: PetscMPIInt rank, size; /* rank of a process, number of processors */
81: PetscInt rstart; /* starting index of PETSc ordering for a processor */
82: PetscInt nfails; /* number of unsuccessful Newton steps */
83: PetscInt bs = 1; /* block size for multicomponent systems */
84: PetscInt nvertices; /* number of local plus ghost nodes of a processor */
85: PetscInt *pordering; /* PETSc ordering */
86: PetscInt *vertices; /* list of all vertices (incl. ghost ones) on a processor */
87: PetscInt *verticesmask;
88: PetscInt *tmp;
89: PetscInt i, j, jstart, inode, nb, nbrs, Nvneighborstotal = 0;
90: PetscInt its, N;
91: PetscScalar *xx;
92: char str[256], form[256], part_name[256];
93: FILE *fptr, *fptr1;
94: ISLocalToGlobalMapping isl2g;
95: int dtmp;
96: #if defined(UNUSED_VARIABLES)
97: PetscDraw draw; /* drawing context */
98: PetscScalar *ff, *gg;
99: PetscReal tiny = 1.0e-10, zero = 0.0, one = 1.0, big = 1.0e+10;
100: PetscInt *tmp1, *tmp2;
101: #endif
102: MatFDColoring matfdcoloring = 0;
103: PetscBool fd_jacobian_coloring = PETSC_FALSE;
105: /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
106: Initialize program
107: - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
109: PetscFunctionBeginUser;
110: PetscCall(PetscInitialize(&argc, &argv, "options.inf", help));
111: PetscCallMPI(MPI_Comm_rank(MPI_COMM_WORLD, &rank));
112: PetscCallMPI(MPI_Comm_size(MPI_COMM_WORLD, &size));
114: /* The current input file options.inf is for 2 proc run only */
115: PetscCheck(size == 2, PETSC_COMM_WORLD, PETSC_ERR_WRONG_MPI_SIZE, "This example currently runs on 2 procs only.");
117: /*
118: Initialize problem parameters
119: */
120: user.Nvglobal = 16; /* Global # of vertices */
121: user.Neglobal = 18; /* Global # of elements */
123: PetscCall(PetscOptionsGetInt(NULL, NULL, "-vert", &user.Nvglobal, NULL));
124: PetscCall(PetscOptionsGetInt(NULL, NULL, "-elem", &user.Neglobal, NULL));
126: user.non_lin_param = 0.06;
128: PetscCall(PetscOptionsGetReal(NULL, NULL, "-nl_par", &user.non_lin_param, NULL));
130: user.lin_param = -1.0;
132: PetscCall(PetscOptionsGetReal(NULL, NULL, "-lin_par", &user.lin_param, NULL));
134: user.Nvlocal = 0;
135: user.Nelocal = 0;
137: /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
138: Read the mesh and partitioning information
139: - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
141: /*
142: Read the mesh and partitioning information from 'adj.in'.
143: The file format is as follows.
144: For each line the first entry is the processor rank where the
145: current node belongs. The second entry is the number of
146: neighbors of a node. The rest of the line is the adjacency
147: list of a node. Currently this file is set up to work on two
148: processors.
150: This is not a very good example of reading input. In the future,
151: we will put an example that shows the style that should be
152: used in a real application, where partitioning will be done
153: dynamically by calling partitioning routines (at present, we have
154: a ready interface to ParMeTiS).
155: */
156: fptr = fopen("adj.in", "r");
157: PetscCheck(fptr, PETSC_COMM_SELF, PETSC_ERR_FILE_OPEN, "Could not open adj.in");
159: /*
160: Each processor writes to the file output.<rank> where rank is the
161: processor's rank.
162: */
163: snprintf(part_name, PETSC_STATIC_ARRAY_LENGTH(part_name), "output.%d", rank);
164: fptr1 = fopen(part_name, "w");
165: PetscCheck(fptr1, PETSC_COMM_SELF, PETSC_ERR_FILE_OPEN, "Could no open output file");
166: PetscCall(PetscMalloc1(user.Nvglobal, &user.gloInd));
167: PetscCall(PetscFPrintf(PETSC_COMM_SELF, fptr1, "Rank is %d\n", rank));
168: for (inode = 0; inode < user.Nvglobal; inode++) {
169: PetscCheck(fgets(str, 256, fptr), PETSC_COMM_SELF, PETSC_ERR_FILE_READ, "fgets read failed");
170: sscanf(str, "%d", &dtmp);
171: user.v2p[inode] = dtmp;
172: if (user.v2p[inode] == rank) {
173: PetscCall(PetscFPrintf(PETSC_COMM_SELF, fptr1, "Node %" PetscInt_FMT " belongs to processor %" PetscInt_FMT "\n", inode, user.v2p[inode]));
175: user.gloInd[user.Nvlocal] = inode;
176: sscanf(str, "%*d %d", &dtmp);
177: nbrs = dtmp;
178: PetscCall(PetscFPrintf(PETSC_COMM_SELF, fptr1, "Number of neighbors for the vertex %" PetscInt_FMT " is %" PetscInt_FMT "\n", inode, nbrs));
180: user.itot[user.Nvlocal] = nbrs;
181: Nvneighborstotal += nbrs;
182: for (i = 0; i < user.itot[user.Nvlocal]; i++) {
183: form[0] = '\0';
184: for (j = 0; j < i + 2; j++) PetscCall(PetscStrlcat(form, "%*d ", sizeof(form)));
185: PetscCall(PetscStrlcat(form, "%d", sizeof(form)));
187: sscanf(str, form, &dtmp);
188: user.AdjM[user.Nvlocal][i] = dtmp;
190: PetscCall(PetscFPrintf(PETSC_COMM_SELF, fptr1, "%" PetscInt_FMT " ", user.AdjM[user.Nvlocal][i]));
191: }
192: PetscCall(PetscFPrintf(PETSC_COMM_SELF, fptr1, "\n"));
193: user.Nvlocal++;
194: }
195: }
196: PetscCall(PetscFPrintf(PETSC_COMM_SELF, fptr1, "Total # of Local Vertices is %" PetscInt_FMT " \n", user.Nvlocal));
198: /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
199: Create different orderings
200: - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
202: /*
203: Create the local ordering list for vertices. First a list using the PETSc global
204: ordering is created. Then we use the AO object to get the PETSc-to-application and
205: application-to-PETSc mappings. Each vertex also gets a local index (stored in the
206: locInd array).
207: */
208: PetscCallMPI(MPI_Scan(&user.Nvlocal, &rstart, 1, MPIU_INT, MPI_SUM, PETSC_COMM_WORLD));
209: rstart -= user.Nvlocal;
210: PetscCall(PetscMalloc1(user.Nvlocal, &pordering));
212: for (i = 0; i < user.Nvlocal; i++) pordering[i] = rstart + i;
214: /*
215: Create the AO object
216: */
217: PetscCall(AOCreateBasic(MPI_COMM_WORLD, user.Nvlocal, user.gloInd, pordering, &ao));
218: PetscCall(PetscFree(pordering));
220: /*
221: Keep the global indices for later use
222: */
223: PetscCall(PetscMalloc1(user.Nvlocal, &user.locInd));
224: PetscCall(PetscMalloc1(Nvneighborstotal, &tmp));
226: /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
227: Demonstrate the use of AO functionality
228: - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
230: PetscCall(PetscFPrintf(PETSC_COMM_SELF, fptr1, "Before AOApplicationToPetsc, local indices are : \n"));
231: for (i = 0; i < user.Nvlocal; i++) {
232: PetscCall(PetscFPrintf(PETSC_COMM_SELF, fptr1, " %" PetscInt_FMT " ", user.gloInd[i]));
234: user.locInd[i] = user.gloInd[i];
235: }
236: PetscCall(PetscFPrintf(PETSC_COMM_SELF, fptr1, "\n"));
237: jstart = 0;
238: for (i = 0; i < user.Nvlocal; i++) {
239: PetscCall(PetscFPrintf(PETSC_COMM_SELF, fptr1, "Neghbors of local vertex %" PetscInt_FMT " are : ", user.gloInd[i]));
240: for (j = 0; j < user.itot[i]; j++) {
241: PetscCall(PetscFPrintf(PETSC_COMM_SELF, fptr1, "%" PetscInt_FMT " ", user.AdjM[i][j]));
243: tmp[j + jstart] = user.AdjM[i][j];
244: }
245: jstart += user.itot[i];
246: PetscCall(PetscFPrintf(PETSC_COMM_SELF, fptr1, "\n"));
247: }
249: /*
250: Now map the vlocal and neighbor lists to the PETSc ordering
251: */
252: PetscCall(AOApplicationToPetsc(ao, user.Nvlocal, user.locInd));
253: PetscCall(AOApplicationToPetsc(ao, Nvneighborstotal, tmp));
254: PetscCall(AODestroy(&ao));
256: PetscCall(PetscFPrintf(PETSC_COMM_SELF, fptr1, "After AOApplicationToPetsc, local indices are : \n"));
257: for (i = 0; i < user.Nvlocal; i++) PetscCall(PetscFPrintf(PETSC_COMM_SELF, fptr1, " %" PetscInt_FMT " ", user.locInd[i]));
258: PetscCall(PetscFPrintf(PETSC_COMM_SELF, fptr1, "\n"));
260: jstart = 0;
261: for (i = 0; i < user.Nvlocal; i++) {
262: PetscCall(PetscFPrintf(PETSC_COMM_SELF, fptr1, "Neghbors of local vertex %" PetscInt_FMT " are : ", user.locInd[i]));
263: for (j = 0; j < user.itot[i]; j++) {
264: user.AdjM[i][j] = tmp[j + jstart];
266: PetscCall(PetscFPrintf(PETSC_COMM_SELF, fptr1, "%" PetscInt_FMT " ", user.AdjM[i][j]));
267: }
268: jstart += user.itot[i];
269: PetscCall(PetscFPrintf(PETSC_COMM_SELF, fptr1, "\n"));
270: }
272: /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
273: Extract the ghost vertex information for each processor
274: - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
275: /*
276: Next, we need to generate a list of vertices required for this processor
277: and a local numbering scheme for all vertices required on this processor.
278: vertices - integer array of all vertices needed on this processor in PETSc
279: global numbering; this list consists of first the "locally owned"
280: vertices followed by the ghost vertices.
281: verticesmask - integer array that for each global vertex lists its local
282: vertex number (in vertices) + 1. If the global vertex is not
283: represented on this processor, then the corresponding
284: entry in verticesmask is zero
286: Note: vertices and verticesmask are both Nvglobal in length; this may
287: sound terribly non-scalable, but in fact is not so bad for a reasonable
288: number of processors. Importantly, it allows us to use NO SEARCHING
289: in setting up the data structures.
290: */
291: PetscCall(PetscMalloc1(user.Nvglobal, &vertices));
292: PetscCall(PetscCalloc1(user.Nvglobal, &verticesmask));
293: nvertices = 0;
295: /*
296: First load "owned vertices" into list
297: */
298: for (i = 0; i < user.Nvlocal; i++) {
299: vertices[nvertices++] = user.locInd[i];
300: verticesmask[user.locInd[i]] = nvertices;
301: }
303: /*
304: Now load ghost vertices into list
305: */
306: for (i = 0; i < user.Nvlocal; i++) {
307: for (j = 0; j < user.itot[i]; j++) {
308: nb = user.AdjM[i][j];
309: if (!verticesmask[nb]) {
310: vertices[nvertices++] = nb;
311: verticesmask[nb] = nvertices;
312: }
313: }
314: }
316: PetscCall(PetscFPrintf(PETSC_COMM_SELF, fptr1, "\n"));
317: PetscCall(PetscFPrintf(PETSC_COMM_SELF, fptr1, "The array vertices is :\n"));
318: for (i = 0; i < nvertices; i++) PetscCall(PetscFPrintf(PETSC_COMM_SELF, fptr1, "%" PetscInt_FMT " ", vertices[i]));
319: PetscCall(PetscFPrintf(PETSC_COMM_SELF, fptr1, "\n"));
321: /*
322: Map the vertices listed in the neighbors to the local numbering from
323: the global ordering that they contained initially.
324: */
325: PetscCall(PetscFPrintf(PETSC_COMM_SELF, fptr1, "\n"));
326: PetscCall(PetscFPrintf(PETSC_COMM_SELF, fptr1, "After mapping neighbors in the local contiguous ordering\n"));
327: for (i = 0; i < user.Nvlocal; i++) {
328: PetscCall(PetscFPrintf(PETSC_COMM_SELF, fptr1, "Neghbors of local vertex %" PetscInt_FMT " are :\n", i));
329: for (j = 0; j < user.itot[i]; j++) {
330: nb = user.AdjM[i][j];
331: user.AdjM[i][j] = verticesmask[nb] - 1;
333: PetscCall(PetscFPrintf(PETSC_COMM_SELF, fptr1, "%" PetscInt_FMT " ", user.AdjM[i][j]));
334: }
335: PetscCall(PetscFPrintf(PETSC_COMM_SELF, fptr1, "\n"));
336: }
338: N = user.Nvglobal;
340: /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
341: Create vector and matrix data structures
342: - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
344: /*
345: Create vector data structures
346: */
347: PetscCall(VecCreate(MPI_COMM_WORLD, &x));
348: PetscCall(VecSetSizes(x, user.Nvlocal, N));
349: PetscCall(VecSetFromOptions(x));
350: PetscCall(VecDuplicate(x, &r));
351: PetscCall(VecCreateSeq(MPI_COMM_SELF, bs * nvertices, &user.localX));
352: PetscCall(VecDuplicate(user.localX, &user.localF));
354: /*
355: Create the scatter between the global representation and the
356: local representation
357: */
358: PetscCall(ISCreateStride(MPI_COMM_SELF, bs * nvertices, 0, 1, &islocal));
359: PetscCall(ISCreateBlock(MPI_COMM_SELF, bs, nvertices, vertices, PETSC_COPY_VALUES, &isglobal));
360: PetscCall(VecScatterCreate(x, isglobal, user.localX, islocal, &user.scatter));
361: PetscCall(ISDestroy(&isglobal));
362: PetscCall(ISDestroy(&islocal));
364: /*
365: Create matrix data structure; Just to keep the example simple, we have not done any
366: preallocation of memory for the matrix. In real application code with big matrices,
367: preallocation should always be done to expedite the matrix creation.
368: */
369: PetscCall(MatCreate(MPI_COMM_WORLD, &Jac));
370: PetscCall(MatSetSizes(Jac, PETSC_DECIDE, PETSC_DECIDE, N, N));
371: PetscCall(MatSetFromOptions(Jac));
372: PetscCall(MatSetUp(Jac));
374: /*
375: The following routine allows us to set the matrix values in local ordering
376: */
377: PetscCall(ISLocalToGlobalMappingCreate(MPI_COMM_SELF, bs, nvertices, vertices, PETSC_COPY_VALUES, &isl2g));
378: PetscCall(MatSetLocalToGlobalMapping(Jac, isl2g, isl2g));
380: /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
381: Create nonlinear solver context
382: - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
384: PetscCall(SNESCreate(MPI_COMM_WORLD, &snes));
385: PetscCall(SNESSetType(snes, type));
387: /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
388: Set routines for function and Jacobian evaluation
389: - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
390: PetscCall(SNESSetFunction(snes, r, FormFunction, (void *)&user));
392: PetscCall(PetscOptionsGetBool(NULL, NULL, "-fd_jacobian_coloring", &fd_jacobian_coloring, 0));
393: if (!fd_jacobian_coloring) {
394: PetscCall(SNESSetJacobian(snes, Jac, Jac, FormJacobian, (void *)&user));
395: } else { /* Use matfdcoloring */
396: ISColoring iscoloring;
397: MatColoring mc;
399: /* Get the data structure of Jac */
400: PetscCall(FormJacobian(snes, x, Jac, Jac, &user));
401: /* Create coloring context */
402: PetscCall(MatColoringCreate(Jac, &mc));
403: PetscCall(MatColoringSetType(mc, MATCOLORINGSL));
404: PetscCall(MatColoringSetFromOptions(mc));
405: PetscCall(MatColoringApply(mc, &iscoloring));
406: PetscCall(MatColoringDestroy(&mc));
407: PetscCall(MatFDColoringCreate(Jac, iscoloring, &matfdcoloring));
408: PetscCall(MatFDColoringSetFunction(matfdcoloring, (PetscErrorCode (*)(void))FormFunction, &user));
409: PetscCall(MatFDColoringSetFromOptions(matfdcoloring));
410: PetscCall(MatFDColoringSetUp(Jac, iscoloring, matfdcoloring));
411: /* PetscCall(MatFDColoringView(matfdcoloring,PETSC_VIEWER_STDOUT_WORLD)); */
412: PetscCall(SNESSetJacobian(snes, Jac, Jac, SNESComputeJacobianDefaultColor, matfdcoloring));
413: PetscCall(ISColoringDestroy(&iscoloring));
414: }
416: /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
417: Customize nonlinear solver; set runtime options
418: - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
420: PetscCall(SNESSetFromOptions(snes));
422: /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
423: Evaluate initial guess; then solve nonlinear system
424: - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
426: /*
427: Note: The user should initialize the vector, x, with the initial guess
428: for the nonlinear solver prior to calling SNESSolve(). In particular,
429: to employ an initial guess of zero, the user should explicitly set
430: this vector to zero by calling VecSet().
431: */
432: PetscCall(FormInitialGuess(&user, x));
434: /*
435: Print the initial guess
436: */
437: PetscCall(VecGetArray(x, &xx));
438: for (inode = 0; inode < user.Nvlocal; inode++) PetscCall(PetscFPrintf(PETSC_COMM_SELF, fptr1, "Initial Solution at node %" PetscInt_FMT " is %f \n", inode, (double)PetscRealPart(xx[inode])));
439: PetscCall(VecRestoreArray(x, &xx));
441: /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
442: Now solve the nonlinear system
443: - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
445: PetscCall(SNESSolve(snes, NULL, x));
446: PetscCall(SNESGetIterationNumber(snes, &its));
447: PetscCall(SNESGetNonlinearStepFailures(snes, &nfails));
449: /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
450: Print the output : solution vector and other information
451: Each processor writes to the file output.<rank> where rank is the
452: processor's rank.
453: - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
455: PetscCall(VecGetArray(x, &xx));
456: for (inode = 0; inode < user.Nvlocal; inode++) PetscCall(PetscFPrintf(PETSC_COMM_SELF, fptr1, "Solution at node %" PetscInt_FMT " is %f \n", inode, (double)PetscRealPart(xx[inode])));
457: PetscCall(VecRestoreArray(x, &xx));
458: fclose(fptr1);
459: PetscCall(PetscPrintf(MPI_COMM_WORLD, "number of SNES iterations = %" PetscInt_FMT ", ", its));
460: PetscCall(PetscPrintf(MPI_COMM_WORLD, "number of unsuccessful steps = %" PetscInt_FMT "\n", nfails));
462: /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
463: Free work space. All PETSc objects should be destroyed when they
464: are no longer needed.
465: - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
466: PetscCall(PetscFree(user.gloInd));
467: PetscCall(PetscFree(user.locInd));
468: PetscCall(PetscFree(vertices));
469: PetscCall(PetscFree(verticesmask));
470: PetscCall(PetscFree(tmp));
471: PetscCall(VecScatterDestroy(&user.scatter));
472: PetscCall(ISLocalToGlobalMappingDestroy(&isl2g));
473: PetscCall(VecDestroy(&x));
474: PetscCall(VecDestroy(&r));
475: PetscCall(VecDestroy(&user.localX));
476: PetscCall(VecDestroy(&user.localF));
477: PetscCall(SNESDestroy(&snes));
478: PetscCall(MatDestroy(&Jac));
479: /* PetscCall(PetscDrawDestroy(draw));*/
480: if (fd_jacobian_coloring) PetscCall(MatFDColoringDestroy(&matfdcoloring));
481: PetscCall(PetscFinalize());
482: return 0;
483: }
484: /* -------------------- Form initial approximation ----------------- */
486: /*
487: FormInitialGuess - Forms initial approximation.
489: Input Parameters:
490: user - user-defined application context
491: X - vector
493: Output Parameter:
494: X - vector
495: */
496: PetscErrorCode FormInitialGuess(AppCtx *user, Vec X)
497: {
498: PetscInt Nvlocal;
499: PetscInt *gloInd;
500: PetscScalar *x;
501: #if defined(UNUSED_VARIABLES)
502: PetscReal temp1, temp, hx, hy, hxdhy, hydhx, sc;
503: PetscInt Neglobal, Nvglobal, j, row;
504: PetscReal alpha, lambda;
506: Nvglobal = user->Nvglobal;
507: Neglobal = user->Neglobal;
508: lambda = user->non_lin_param;
509: alpha = user->lin_param;
510: #endif
512: PetscFunctionBeginUser;
513: Nvlocal = user->Nvlocal;
514: gloInd = user->gloInd;
516: /*
517: Get a pointer to vector data.
518: - For default PETSc vectors, VecGetArray() returns a pointer to
519: the data array. Otherwise, the routine is implementation dependent.
520: - You MUST call VecRestoreArray() when you no longer need access to
521: the array.
522: */
523: PetscCall(VecGetArray(X, &x));
525: /*
526: Compute initial guess over the locally owned part of the grid
527: */
528: for (PetscInt i = 0; i < Nvlocal; i++) x[i] = (PetscReal)gloInd[i];
530: /*
531: Restore vector
532: */
533: PetscCall(VecRestoreArray(X, &x));
534: PetscFunctionReturn(PETSC_SUCCESS);
535: }
536: /* -------------------- Evaluate Function F(x) --------------------- */
537: /*
538: FormFunction - Evaluates nonlinear function, F(x).
540: Input Parameters:
541: . snes - the SNES context
542: . X - input vector
543: . ptr - optional user-defined context, as set by SNESSetFunction()
545: Output Parameter:
546: . F - function vector
547: */
548: PetscErrorCode FormFunction(SNES snes, Vec X, Vec F, void *ptr)
549: {
550: AppCtx *user = (AppCtx *)ptr;
551: PetscInt Nvlocal;
552: PetscReal alpha, lambda;
553: PetscScalar *x, *f;
554: VecScatter scatter;
555: Vec localX = user->localX;
556: #if defined(UNUSED_VARIABLES)
557: PetscScalar ut, ub, ul, ur, u, *g, sc, uyy, uxx;
558: PetscReal hx, hy, hxdhy, hydhx;
559: PetscReal two = 2.0, one = 1.0;
560: PetscInt Nvglobal, Neglobal, row;
561: PetscInt *gloInd;
563: Nvglobal = user->Nvglobal;
564: Neglobal = user->Neglobal;
565: gloInd = user->gloInd;
566: #endif
568: PetscFunctionBeginUser;
569: Nvlocal = user->Nvlocal;
570: lambda = user->non_lin_param;
571: alpha = user->lin_param;
572: scatter = user->scatter;
574: /*
575: PDE : L(u) + lambda*u*u +alpha*u = 0 where L(u) is the approximate Laplacian as
576: described in the beginning of this code
578: First scatter the distributed vector X into local vector localX (that includes
579: values for ghost nodes. If we wish,we can put some other work between
580: VecScatterBegin() and VecScatterEnd() to overlap the communication with
581: computation.
582: */
583: PetscCall(VecScatterBegin(scatter, X, localX, INSERT_VALUES, SCATTER_FORWARD));
584: PetscCall(VecScatterEnd(scatter, X, localX, INSERT_VALUES, SCATTER_FORWARD));
586: /*
587: Get pointers to vector data
588: */
589: PetscCall(VecGetArray(localX, &x));
590: PetscCall(VecGetArray(F, &f));
592: /*
593: Now compute the f(x). As mentioned earlier, the computed Laplacian is just an
594: approximate one chosen for illustrative purpose only. Another point to notice
595: is that this is a local (completely parallel) calculation. In practical application
596: codes, function calculation time is a dominat portion of the overall execution time.
597: */
598: for (PetscInt i = 0; i < Nvlocal; i++) {
599: f[i] = (user->itot[i] - alpha) * x[i] - lambda * x[i] * x[i];
600: for (PetscInt j = 0; j < user->itot[i]; j++) f[i] -= x[user->AdjM[i][j]];
601: }
603: /*
604: Restore vectors
605: */
606: PetscCall(VecRestoreArray(localX, &x));
607: PetscCall(VecRestoreArray(F, &f));
608: /* PetscCall(VecView(F,PETSC_VIEWER_STDOUT_WORLD)); */
609: PetscFunctionReturn(PETSC_SUCCESS);
610: }
612: /* -------------------- Evaluate Jacobian F'(x) -------------------- */
613: /*
614: FormJacobian - Evaluates Jacobian matrix.
616: Input Parameters:
617: . snes - the SNES context
618: . X - input vector
619: . ptr - optional user-defined context, as set by SNESSetJacobian()
621: Output Parameters:
622: . A - Jacobian matrix
623: . B - optionally different preconditioning matrix
624: . flag - flag indicating matrix structure
626: */
627: PetscErrorCode FormJacobian(SNES snes, Vec X, Mat J, Mat jac, void *ptr)
628: {
629: AppCtx *user = (AppCtx *)ptr;
630: PetscInt Nvlocal, col[50];
631: PetscScalar alpha, lambda, value[50];
632: Vec localX = user->localX;
633: VecScatter scatter;
634: PetscScalar *x;
635: #if defined(UNUSED_VARIABLES)
636: PetscScalar two = 2.0, one = 1.0;
637: PetscInt row, Nvglobal, Neglobal;
638: PetscInt *gloInd;
640: Nvglobal = user->Nvglobal;
641: Neglobal = user->Neglobal;
642: gloInd = user->gloInd;
643: #endif
645: PetscFunctionBeginUser;
646: /*printf("Entering into FormJacobian \n");*/
647: Nvlocal = user->Nvlocal;
648: lambda = user->non_lin_param;
649: alpha = user->lin_param;
650: scatter = user->scatter;
652: /*
653: PDE : L(u) + lambda*u*u +alpha*u = 0 where L(u) is the approximate Laplacian as
654: described in the beginning of this code
656: First scatter the distributed vector X into local vector localX (that includes
657: values for ghost nodes. If we wish, we can put some other work between
658: VecScatterBegin() and VecScatterEnd() to overlap the communication with
659: computation.
660: */
661: PetscCall(VecScatterBegin(scatter, X, localX, INSERT_VALUES, SCATTER_FORWARD));
662: PetscCall(VecScatterEnd(scatter, X, localX, INSERT_VALUES, SCATTER_FORWARD));
664: /*
665: Get pointer to vector data
666: */
667: PetscCall(VecGetArray(localX, &x));
669: for (PetscInt i = 0; i < Nvlocal; i++) {
670: col[0] = i;
671: value[0] = user->itot[i] - 2.0 * lambda * x[i] - alpha;
672: for (PetscInt j = 0; j < user->itot[i]; j++) {
673: col[j + 1] = user->AdjM[i][j];
674: value[j + 1] = -1.0;
675: }
677: /*
678: Set the matrix values in the local ordering. Note that in order to use this
679: feature we must call the routine MatSetLocalToGlobalMapping() after the
680: matrix has been created.
681: */
682: PetscCall(MatSetValuesLocal(jac, 1, &i, 1 + user->itot[i], col, value, INSERT_VALUES));
683: }
685: /*
686: Assemble matrix, using the 2-step process:
687: MatAssemblyBegin(), MatAssemblyEnd().
688: Between these two calls, the pointer to vector data has been restored to
689: demonstrate the use of overlapping communicationn with computation.
690: */
691: PetscCall(MatAssemblyBegin(jac, MAT_FINAL_ASSEMBLY));
692: PetscCall(VecRestoreArray(localX, &x));
693: PetscCall(MatAssemblyEnd(jac, MAT_FINAL_ASSEMBLY));
695: /*
696: Tell the matrix we will never add a new nonzero location to the
697: matrix. If we do, it will generate an error.
698: */
699: PetscCall(MatSetOption(jac, MAT_NEW_NONZERO_LOCATION_ERR, PETSC_TRUE));
700: /* MatView(jac,PETSC_VIEWER_STDOUT_SELF); */
701: PetscFunctionReturn(PETSC_SUCCESS);
702: }
704: /*TEST
706: build:
707: requires: !complex
709: test:
710: nsize: 2
711: args: -snes_monitor_short
712: localrunfiles: options.inf adj.in
714: test:
715: suffix: 2
716: nsize: 2
717: args: -snes_monitor_short -fd_jacobian_coloring
718: localrunfiles: options.inf adj.in
720: TEST*/