xref: /petsc/src/snes/tutorials/ex6.c (revision cac4c232dc4f93991e342196e27ef7b0655dac7b)
1 
2 static char help[] = "Newton method to solve u'' + u^{2} = f, sequentially.\n\
3 This example employs a user-defined reasonview routine.\n\n";
4 
5 /*T
6    Concepts: SNES^basic uniprocessor example
7    Concepts: SNES^setting a user-defined reasonview routine
8    Processors: 1
9 T*/
10 
11 #include <petscsnes.h>
12 
13 /*
14    User-defined routines
15 */
16 PETSC_EXTERN PetscErrorCode FormJacobian(SNES,Vec,Mat,Mat,void*);
17 PETSC_EXTERN PetscErrorCode FormFunction(SNES,Vec,Vec,void*);
18 PETSC_EXTERN PetscErrorCode FormInitialGuess(Vec);
19 PETSC_EXTERN PetscErrorCode MySNESConvergedReasonView(SNES,void*);
20 PETSC_EXTERN PetscErrorCode MyKSPConvergedReasonView(KSP,void*);
21 
22 /*
23    User-defined context for monitoring
24 */
25 typedef struct {
26   PetscViewer viewer;
27 } ReasonViewCtx;
28 
29 int main(int argc,char **argv)
30 {
31   SNES           snes;                /* SNES context */
32   KSP            ksp;                 /* KSP context */
33   Vec            x,r,F,U;             /* vectors */
34   Mat            J;                   /* Jacobian matrix */
35   ReasonViewCtx     monP;             /* monitoring context */
36   PetscInt       its,n = 5,i;
37   PetscMPIInt    size;
38   PetscScalar    h,xp,v;
39   MPI_Comm       comm;
40 
41   PetscCall(PetscInitialize(&argc,&argv,(char*)0,help));
42   PetscCallMPI(MPI_Comm_size(PETSC_COMM_WORLD,&size));
43   PetscCheck(size == 1,PETSC_COMM_SELF,PETSC_ERR_WRONG_MPI_SIZE,"This is a uniprocessor example only!");
44   PetscCall(PetscOptionsGetInt(NULL,NULL,"-n",&n,NULL));
45   h    = 1.0/(n-1);
46   comm = PETSC_COMM_WORLD;
47   /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
48      Create nonlinear solver context
49      - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
50 
51   PetscCall(SNESCreate(comm,&snes));
52 
53   /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
54      Create vector data structures; set function evaluation routine
55      - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
56   /*
57      Note that we form 1 vector from scratch and then duplicate as needed.
58   */
59   PetscCall(VecCreate(comm,&x));
60   PetscCall(VecSetSizes(x,PETSC_DECIDE,n));
61   PetscCall(VecSetFromOptions(x));
62   PetscCall(VecDuplicate(x,&r));
63   PetscCall(VecDuplicate(x,&F));
64   PetscCall(VecDuplicate(x,&U));
65 
66   /*
67      Set function evaluation routine and vector
68   */
69   PetscCall(SNESSetFunction(snes,r,FormFunction,(void*)F));
70 
71   /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
72      Create matrix data structure; set Jacobian evaluation routine
73      - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
74 
75   PetscCall(MatCreate(comm,&J));
76   PetscCall(MatSetSizes(J,PETSC_DECIDE,PETSC_DECIDE,n,n));
77   PetscCall(MatSetFromOptions(J));
78   PetscCall(MatSeqAIJSetPreallocation(J,3,NULL));
79 
80   /*
81      Set Jacobian matrix data structure and default Jacobian evaluation
82      routine. User can override with:
83      -snes_fd : default finite differencing approximation of Jacobian
84      -snes_mf : matrix-free Newton-Krylov method with no preconditioning
85                 (unless user explicitly sets preconditioner)
86      -snes_mf_operator : form preconditioning matrix as set by the user,
87                          but use matrix-free approx for Jacobian-vector
88                          products within Newton-Krylov method
89   */
90 
91   PetscCall(SNESSetJacobian(snes,J,J,FormJacobian,NULL));
92 
93   /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
94      Customize nonlinear solver; set runtime options
95    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
96 
97   /*
98      Set an optional user-defined reasonview routine
99   */
100   PetscCall(PetscViewerASCIIGetStdout(comm,&monP.viewer));
101   /* Just make sure we can not repeat addding the same function
102    * PETSc will be able to igore the repeated function
103    */
104   for (i=0; i<4; i++) {
105     PetscCall(SNESConvergedReasonViewSet(snes,MySNESConvergedReasonView,&monP,0));
106   }
107   PetscCall(SNESGetKSP(snes,&ksp));
108   /* Just make sure we can not repeat addding the same function
109    * PETSc will be able to igore the repeated function
110    */
111   for (i=0; i<4; i++) {
112     PetscCall(KSPConvergedReasonViewSet(ksp,MyKSPConvergedReasonView,&monP,0));
113   }
114   /*
115      Set SNES/KSP/KSP/PC runtime options, e.g.,
116          -snes_view -snes_monitor -ksp_type <ksp> -pc_type <pc>
117   */
118   PetscCall(SNESSetFromOptions(snes));
119 
120   /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
121      Initialize application:
122      Store right-hand-side of PDE and exact solution
123    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
124 
125   xp = 0.0;
126   for (i=0; i<n; i++) {
127     v    = 6.0*xp + PetscPowScalar(xp+1.e-12,6.0); /* +1.e-12 is to prevent 0^6 */
128     PetscCall(VecSetValues(F,1,&i,&v,INSERT_VALUES));
129     v    = xp*xp*xp;
130     PetscCall(VecSetValues(U,1,&i,&v,INSERT_VALUES));
131     xp  += h;
132   }
133 
134   /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
135      Evaluate initial guess; then solve nonlinear system
136    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
137   /*
138      Note: The user should initialize the vector, x, with the initial guess
139      for the nonlinear solver prior to calling SNESSolve().  In particular,
140      to employ an initial guess of zero, the user should explicitly set
141      this vector to zero by calling VecSet().
142   */
143   PetscCall(FormInitialGuess(x));
144   PetscCall(SNESSolve(snes,NULL,x));
145   PetscCall(SNESGetIterationNumber(snes,&its));
146 
147   /*
148      Free work space.  All PETSc objects should be destroyed when they
149      are no longer needed.
150   */
151   PetscCall(VecDestroy(&x));  PetscCall(VecDestroy(&r));
152   PetscCall(VecDestroy(&U));  PetscCall(VecDestroy(&F));
153   PetscCall(MatDestroy(&J));  PetscCall(SNESDestroy(&snes));
154   /*PetscCall(PetscViewerDestroy(&monP.viewer));*/
155   PetscCall(PetscFinalize());
156   return 0;
157 }
158 /* ------------------------------------------------------------------- */
159 /*
160    FormInitialGuess - Computes initial guess.
161 
162    Input/Output Parameter:
163 .  x - the solution vector
164 */
165 PetscErrorCode FormInitialGuess(Vec x)
166 {
167   PetscScalar    pfive = .50;
168   PetscCall(VecSet(x,pfive));
169   return 0;
170 }
171 /* ------------------------------------------------------------------- */
172 /*
173    FormFunction - Evaluates nonlinear function, F(x).
174 
175    Input Parameters:
176 .  snes - the SNES context
177 .  x - input vector
178 .  ctx - optional user-defined context, as set by SNESSetFunction()
179 
180    Output Parameter:
181 .  f - function vector
182 
183    Note:
184    The user-defined context can contain any application-specific data
185    needed for the function evaluation (such as various parameters, work
186    vectors, and grid information).  In this program the context is just
187    a vector containing the right-hand-side of the discretized PDE.
188  */
189 
190 PetscErrorCode FormFunction(SNES snes,Vec x,Vec f,void *ctx)
191 {
192   Vec               g = (Vec)ctx;
193   const PetscScalar *xx,*gg;
194   PetscScalar       *ff,d;
195   PetscInt          i,n;
196 
197   /*
198      Get pointers to vector data.
199        - For default PETSc vectors, VecGetArray() returns a pointer to
200          the data array.  Otherwise, the routine is implementation dependent.
201        - You MUST call VecRestoreArray() when you no longer need access to
202          the array.
203   */
204   PetscCall(VecGetArrayRead(x,&xx));
205   PetscCall(VecGetArray(f,&ff));
206   PetscCall(VecGetArrayRead(g,&gg));
207 
208   /*
209      Compute function
210   */
211   PetscCall(VecGetSize(x,&n));
212   d     = (PetscReal)(n - 1); d = d*d;
213   ff[0] = xx[0];
214   for (i=1; i<n-1; i++) ff[i] = d*(xx[i-1] - 2.0*xx[i] + xx[i+1]) + xx[i]*xx[i] - gg[i];
215   ff[n-1] = xx[n-1] - 1.0;
216 
217   /*
218      Restore vectors
219   */
220   PetscCall(VecRestoreArrayRead(x,&xx));
221   PetscCall(VecRestoreArray(f,&ff));
222   PetscCall(VecRestoreArrayRead(g,&gg));
223   return 0;
224 }
225 /* ------------------------------------------------------------------- */
226 /*
227    FormJacobian - Evaluates Jacobian matrix.
228 
229    Input Parameters:
230 .  snes - the SNES context
231 .  x - input vector
232 .  dummy - optional user-defined context (not used here)
233 
234    Output Parameters:
235 .  jac - Jacobian matrix
236 .  B - optionally different preconditioning matrix
237 
238 */
239 
240 PetscErrorCode FormJacobian(SNES snes,Vec x,Mat jac,Mat B,void *dummy)
241 {
242   const PetscScalar *xx;
243   PetscScalar       A[3],d;
244   PetscInt          i,n,j[3];
245 
246   /*
247      Get pointer to vector data
248   */
249   PetscCall(VecGetArrayRead(x,&xx));
250 
251   /*
252      Compute Jacobian entries and insert into matrix.
253       - Note that in this case we set all elements for a particular
254         row at once.
255   */
256   PetscCall(VecGetSize(x,&n));
257   d    = (PetscReal)(n - 1); d = d*d;
258 
259   /*
260      Interior grid points
261   */
262   for (i=1; i<n-1; i++) {
263     j[0] = i - 1; j[1] = i; j[2] = i + 1;
264     A[0] = A[2] = d; A[1] = -2.0*d + 2.0*xx[i];
265     PetscCall(MatSetValues(B,1,&i,3,j,A,INSERT_VALUES));
266   }
267 
268   /*
269      Boundary points
270   */
271   i = 0;   A[0] = 1.0;
272 
273   PetscCall(MatSetValues(B,1,&i,1,&i,A,INSERT_VALUES));
274 
275   i = n-1; A[0] = 1.0;
276 
277   PetscCall(MatSetValues(B,1,&i,1,&i,A,INSERT_VALUES));
278 
279   /*
280      Restore vector
281   */
282   PetscCall(VecRestoreArrayRead(x,&xx));
283 
284   /*
285      Assemble matrix
286   */
287   PetscCall(MatAssemblyBegin(B,MAT_FINAL_ASSEMBLY));
288   PetscCall(MatAssemblyEnd(B,MAT_FINAL_ASSEMBLY));
289   if (jac != B) {
290     PetscCall(MatAssemblyBegin(jac,MAT_FINAL_ASSEMBLY));
291     PetscCall(MatAssemblyEnd(jac,MAT_FINAL_ASSEMBLY));
292   }
293   return 0;
294 }
295 
296 PetscErrorCode MySNESConvergedReasonView(SNES snes,void *ctx)
297 {
298   ReasonViewCtx         *monP = (ReasonViewCtx*) ctx;
299   PetscViewer           viewer = monP->viewer;
300   SNESConvergedReason   reason;
301   const char            *strreason;
302 
303   PetscCall(SNESGetConvergedReason(snes,&reason));
304   PetscCall(SNESGetConvergedReasonString(snes,&strreason));
305   PetscCall(PetscViewerASCIIPrintf(viewer,"Customized SNES converged reason view\n"));
306   PetscCall(PetscViewerASCIIAddTab(viewer,1));
307   if (reason > 0) {
308     PetscCall(PetscViewerASCIIPrintf(viewer,"Converged due to %s\n",strreason));
309   } else if (reason <= 0) {
310     PetscCall(PetscViewerASCIIPrintf(viewer,"Did not converge due to %s\n",strreason));
311   }
312   PetscCall(PetscViewerASCIISubtractTab(viewer,1));
313   return 0;
314 }
315 
316 PetscErrorCode MyKSPConvergedReasonView(KSP ksp,void *ctx)
317 {
318   ReasonViewCtx         *monP = (ReasonViewCtx*) ctx;
319   PetscViewer           viewer = monP->viewer;
320   KSPConvergedReason    reason;
321   const char            *reasonstr;
322 
323   PetscCall(KSPGetConvergedReason(ksp,&reason));
324   PetscCall(KSPGetConvergedReasonString(ksp,&reasonstr));
325   PetscCall(PetscViewerASCIIAddTab(viewer,2));
326   PetscCall(PetscViewerASCIIPrintf(viewer,"Customized KSP converged reason view\n"));
327   PetscCall(PetscViewerASCIIAddTab(viewer,1));
328   if (reason > 0) {
329     PetscCall(PetscViewerASCIIPrintf(viewer,"Converged due to %s\n",reasonstr));
330   } else if (reason <= 0) {
331     PetscCall(PetscViewerASCIIPrintf(viewer,"Did not converge due to %s\n",reasonstr));
332   }
333   PetscCall(PetscViewerASCIISubtractTab(viewer,3));
334   return 0;
335 }
336 
337 /*TEST
338 
339    test:
340       suffix: 1
341       nsize: 1
342       filter: sed -e "s/CONVERGED_ATOL/CONVERGED_RTOL/g"
343 
344    test:
345       suffix: 2
346       nsize: 1
347       args: -ksp_converged_reason_view_cancel
348       filter: sed -e "s/CONVERGED_ATOL/CONVERGED_RTOL/g"
349 
350    test:
351       suffix: 3
352       nsize: 1
353       args: -ksp_converged_reason_view_cancel -ksp_converged_reason
354       filter: sed -e "s/CONVERGED_ATOL/CONVERGED_RTOL/g"
355 
356    test:
357       suffix: 4
358       nsize: 1
359       args: -snes_converged_reason_view_cancel
360       filter: sed -e "s/CONVERGED_ATOL/CONVERGED_RTOL/g"
361 
362    test:
363       suffix: 5
364       nsize: 1
365       args: -snes_converged_reason_view_cancel -snes_converged_reason
366       filter: sed -e "s/CONVERGED_ATOL/CONVERGED_RTOL/g"
367 
368 TEST*/
369