xref: /petsc/src/snes/impls/composite/snescomposite.c (revision 28b400f66ebc7ae0049166a2294dfcd3df27e64b)
1 
2 /*
3       Defines a SNES that can consist of a collection of SNESes
4 */
5 #include <petsc/private/snesimpl.h> /*I "petscsnes.h" I*/
6 #include <petscblaslapack.h>
7 
8 const char *const        SNESCompositeTypes[]   = {"ADDITIVE","MULTIPLICATIVE","ADDITIVEOPTIMAL","SNESCompositeType","SNES_COMPOSITE",NULL};
9 
10 typedef struct _SNES_CompositeLink *SNES_CompositeLink;
11 struct _SNES_CompositeLink {
12   SNES               snes;
13   PetscReal          dmp;
14   Vec                X;
15   SNES_CompositeLink next;
16   SNES_CompositeLink previous;
17 };
18 
19 typedef struct {
20   SNES_CompositeLink head;
21   PetscInt           nsnes;
22   SNESCompositeType  type;
23   Vec                Xorig;
24   PetscInt           innerFailures; /* the number of inner failures we've seen */
25 
26   /* context for ADDITIVEOPTIMAL */
27   Vec                *Xes,*Fes;      /* solution and residual vectors for the subsolvers */
28   PetscReal          *fnorms;        /* norms of the solutions */
29   PetscScalar        *h;             /* the matrix formed as q_ij = (rdot_i, rdot_j) */
30   PetscScalar        *g;             /* the dotproducts of the previous function with the candidate functions */
31   PetscBLASInt       n;              /* matrix dimension -- nsnes */
32   PetscBLASInt       nrhs;           /* the number of right hand sides */
33   PetscBLASInt       lda;            /* the padded matrix dimension */
34   PetscBLASInt       ldb;            /* the padded vector dimension */
35   PetscReal          *s;             /* the singular values */
36   PetscScalar        *beta;          /* the RHS and combination */
37   PetscReal          rcond;          /* the exit condition */
38   PetscBLASInt       rank;           /* the effective rank */
39   PetscScalar        *work;          /* the work vector */
40   PetscReal          *rwork;         /* the real work vector used for complex */
41   PetscBLASInt       lwork;          /* the size of the work vector */
42   PetscBLASInt       info;           /* the output condition */
43 
44   PetscReal          rtol;           /* restart tolerance for accepting the combination */
45   PetscReal          stol;           /* restart tolerance for the combination */
46 } SNES_Composite;
47 
48 static PetscErrorCode SNESCompositeApply_Multiplicative(SNES snes,Vec X,Vec B,Vec F,PetscReal *fnorm)
49 {
50   SNES_Composite      *jac = (SNES_Composite*)snes->data;
51   SNES_CompositeLink  next = jac->head;
52   Vec                 FSub;
53   SNESConvergedReason reason;
54 
55   PetscFunctionBegin;
56   PetscCheck(next,PetscObjectComm((PetscObject)snes),PETSC_ERR_ARG_WRONGSTATE,"No composite SNESes supplied via SNESCompositeAddSNES() or -snes_composite_sneses");
57   if (snes->normschedule == SNES_NORM_ALWAYS) {
58     CHKERRQ(SNESSetInitialFunction(next->snes,F));
59   }
60   CHKERRQ(SNESSolve(next->snes,B,X));
61   CHKERRQ(SNESGetConvergedReason(next->snes,&reason));
62   if (reason < 0 && reason != SNES_DIVERGED_MAX_IT) {
63     jac->innerFailures++;
64     if (jac->innerFailures >= snes->maxFailures) {
65       snes->reason = SNES_DIVERGED_INNER;
66       PetscFunctionReturn(0);
67     }
68   }
69 
70   while (next->next) {
71     /* only copy the function over in the case where the functions correspond */
72     if (next->snes->npcside== PC_RIGHT && next->snes->normschedule != SNES_NORM_NONE) {
73       CHKERRQ(SNESGetFunction(next->snes,&FSub,NULL,NULL));
74       next = next->next;
75       CHKERRQ(SNESSetInitialFunction(next->snes,FSub));
76     } else {
77       next = next->next;
78     }
79     CHKERRQ(SNESSolve(next->snes,B,X));
80     CHKERRQ(SNESGetConvergedReason(next->snes,&reason));
81     if (reason < 0 && reason != SNES_DIVERGED_MAX_IT) {
82       jac->innerFailures++;
83       if (jac->innerFailures >= snes->maxFailures) {
84         snes->reason = SNES_DIVERGED_INNER;
85         PetscFunctionReturn(0);
86       }
87     }
88   }
89   if (next->snes->npcside== PC_RIGHT) {
90     CHKERRQ(SNESGetFunction(next->snes,&FSub,NULL,NULL));
91     CHKERRQ(VecCopy(FSub,F));
92     if (fnorm) {
93       if (snes->xl && snes->xu) {
94         CHKERRQ(SNESVIComputeInactiveSetFnorm(snes, F, X, fnorm));
95       } else {
96         CHKERRQ(VecNorm(F, NORM_2, fnorm));
97       }
98       SNESCheckFunctionNorm(snes,*fnorm);
99     }
100   } else if (snes->normschedule == SNES_NORM_ALWAYS) {
101     CHKERRQ(SNESComputeFunction(snes,X,F));
102     if (fnorm) {
103       if (snes->xl && snes->xu) {
104         CHKERRQ(SNESVIComputeInactiveSetFnorm(snes, F, X, fnorm));
105       } else {
106         CHKERRQ(VecNorm(F, NORM_2, fnorm));
107       }
108       SNESCheckFunctionNorm(snes,*fnorm);
109     }
110   }
111   PetscFunctionReturn(0);
112 }
113 
114 static PetscErrorCode SNESCompositeApply_Additive(SNES snes,Vec X,Vec B,Vec F,PetscReal *fnorm)
115 {
116   SNES_Composite      *jac = (SNES_Composite*)snes->data;
117   SNES_CompositeLink  next = jac->head;
118   Vec                 Y,Xorig;
119   SNESConvergedReason reason;
120 
121   PetscFunctionBegin;
122   Y = snes->vec_sol_update;
123   if (!jac->Xorig) CHKERRQ(VecDuplicate(X,&jac->Xorig));
124   Xorig = jac->Xorig;
125   CHKERRQ(VecCopy(X,Xorig));
126   PetscCheck(next,PetscObjectComm((PetscObject)snes),PETSC_ERR_ARG_WRONGSTATE,"No composite SNESes supplied via SNESCompositeAddSNES() or -snes_composite_sneses");
127   if (snes->normschedule == SNES_NORM_ALWAYS) {
128     CHKERRQ(SNESSetInitialFunction(next->snes,F));
129     while (next->next) {
130       next = next->next;
131       CHKERRQ(SNESSetInitialFunction(next->snes,F));
132     }
133   }
134   next = jac->head;
135   CHKERRQ(VecCopy(Xorig,Y));
136   CHKERRQ(SNESSolve(next->snes,B,Y));
137   CHKERRQ(SNESGetConvergedReason(next->snes,&reason));
138   if (reason < 0 && reason != SNES_DIVERGED_MAX_IT) {
139     jac->innerFailures++;
140     if (jac->innerFailures >= snes->maxFailures) {
141       snes->reason = SNES_DIVERGED_INNER;
142       PetscFunctionReturn(0);
143     }
144   }
145   CHKERRQ(VecAXPY(Y,-1.0,Xorig));
146   CHKERRQ(VecAXPY(X,next->dmp,Y));
147   while (next->next) {
148     next = next->next;
149     CHKERRQ(VecCopy(Xorig,Y));
150     CHKERRQ(SNESSolve(next->snes,B,Y));
151     CHKERRQ(SNESGetConvergedReason(next->snes,&reason));
152     if (reason < 0 && reason != SNES_DIVERGED_MAX_IT) {
153       jac->innerFailures++;
154       if (jac->innerFailures >= snes->maxFailures) {
155         snes->reason = SNES_DIVERGED_INNER;
156         PetscFunctionReturn(0);
157       }
158     }
159     CHKERRQ(VecAXPY(Y,-1.0,Xorig));
160     CHKERRQ(VecAXPY(X,next->dmp,Y));
161   }
162   if (snes->normschedule == SNES_NORM_ALWAYS) {
163     CHKERRQ(SNESComputeFunction(snes,X,F));
164     if (fnorm) {
165       if (snes->xl && snes->xu) {
166         CHKERRQ(SNESVIComputeInactiveSetFnorm(snes, F, X, fnorm));
167       } else {
168         CHKERRQ(VecNorm(F, NORM_2, fnorm));
169       }
170       SNESCheckFunctionNorm(snes,*fnorm);
171     }
172   }
173   PetscFunctionReturn(0);
174 }
175 
176 /* approximately solve the overdetermined system:
177 
178  2*F(x_i)\cdot F(\x_j)\alpha_i = 0
179  \alpha_i                      = 1
180 
181  Which minimizes the L2 norm of the linearization of:
182  ||F(\sum_i \alpha_i*x_i)||^2
183 
184  With the constraint that \sum_i\alpha_i = 1
185  Where x_i is the solution from the ith subsolver.
186  */
187 static PetscErrorCode SNESCompositeApply_AdditiveOptimal(SNES snes,Vec X,Vec B,Vec F,PetscReal *fnorm)
188 {
189   SNES_Composite      *jac = (SNES_Composite*)snes->data;
190   SNES_CompositeLink  next = jac->head;
191   Vec                 *Xes = jac->Xes,*Fes = jac->Fes;
192   PetscInt            i,j;
193   PetscScalar         tot,total,ftf;
194   PetscReal           min_fnorm;
195   PetscInt            min_i;
196   SNESConvergedReason reason;
197 
198   PetscFunctionBegin;
199   PetscCheck(next,PetscObjectComm((PetscObject)snes),PETSC_ERR_ARG_WRONGSTATE,"No composite SNESes supplied via SNESCompositeAddSNES() or -snes_composite_sneses");
200 
201   if (snes->normschedule == SNES_NORM_ALWAYS) {
202     next = jac->head;
203     CHKERRQ(SNESSetInitialFunction(next->snes,F));
204     while (next->next) {
205       next = next->next;
206       CHKERRQ(SNESSetInitialFunction(next->snes,F));
207     }
208   }
209 
210   next = jac->head;
211   i = 0;
212   CHKERRQ(VecCopy(X,Xes[i]));
213   CHKERRQ(SNESSolve(next->snes,B,Xes[i]));
214   CHKERRQ(SNESGetConvergedReason(next->snes,&reason));
215   if (reason < 0 && reason != SNES_DIVERGED_MAX_IT) {
216     jac->innerFailures++;
217     if (jac->innerFailures >= snes->maxFailures) {
218       snes->reason = SNES_DIVERGED_INNER;
219       PetscFunctionReturn(0);
220     }
221   }
222   while (next->next) {
223     i++;
224     next = next->next;
225     CHKERRQ(VecCopy(X,Xes[i]));
226     CHKERRQ(SNESSolve(next->snes,B,Xes[i]));
227     CHKERRQ(SNESGetConvergedReason(next->snes,&reason));
228     if (reason < 0 && reason != SNES_DIVERGED_MAX_IT) {
229       jac->innerFailures++;
230       if (jac->innerFailures >= snes->maxFailures) {
231         snes->reason = SNES_DIVERGED_INNER;
232         PetscFunctionReturn(0);
233       }
234     }
235   }
236 
237   /* all the solutions are collected; combine optimally */
238   for (i=0;i<jac->n;i++) {
239     for (j=0;j<i+1;j++) {
240       CHKERRQ(VecDotBegin(Fes[i],Fes[j],&jac->h[i + j*jac->n]));
241     }
242     CHKERRQ(VecDotBegin(Fes[i],F,&jac->g[i]));
243   }
244 
245   for (i=0;i<jac->n;i++) {
246     for (j=0;j<i+1;j++) {
247       CHKERRQ(VecDotEnd(Fes[i],Fes[j],&jac->h[i + j*jac->n]));
248       if (i == j) jac->fnorms[i] = PetscSqrtReal(PetscRealPart(jac->h[i + j*jac->n]));
249     }
250     CHKERRQ(VecDotEnd(Fes[i],F,&jac->g[i]));
251   }
252 
253   ftf = (*fnorm)*(*fnorm);
254 
255   for (i=0; i<jac->n; i++) {
256     for (j=i+1;j<jac->n;j++) {
257       jac->h[i + j*jac->n] = jac->h[j + i*jac->n];
258     }
259   }
260 
261   for (i=0; i<jac->n; i++) {
262     for (j=0; j<jac->n; j++) {
263       jac->h[i + j*jac->n] = jac->h[i + j*jac->n] - jac->g[j] - jac->g[i] + ftf;
264     }
265     jac->beta[i] = ftf - jac->g[i];
266   }
267 
268   jac->info  = 0;
269   jac->rcond = -1.;
270   CHKERRQ(PetscFPTrapPush(PETSC_FP_TRAP_OFF));
271 #if defined(PETSC_USE_COMPLEX)
272   PetscStackCallBLAS("LAPACKgelss",LAPACKgelss_(&jac->n,&jac->n,&jac->nrhs,jac->h,&jac->lda,jac->beta,&jac->lda,jac->s,&jac->rcond,&jac->rank,jac->work,&jac->lwork,jac->rwork,&jac->info));
273 #else
274   PetscStackCallBLAS("LAPACKgelss",LAPACKgelss_(&jac->n,&jac->n,&jac->nrhs,jac->h,&jac->lda,jac->beta,&jac->lda,jac->s,&jac->rcond,&jac->rank,jac->work,&jac->lwork,&jac->info));
275 #endif
276   CHKERRQ(PetscFPTrapPop());
277   PetscCheckFalse(jac->info < 0,PetscObjectComm((PetscObject)snes),PETSC_ERR_LIB,"Bad argument to GELSS");
278   PetscCheckFalse(jac->info > 0,PetscObjectComm((PetscObject)snes),PETSC_ERR_LIB,"SVD failed to converge");
279   tot = 0.;
280   total = 0.;
281   for (i=0; i<jac->n; i++) {
282     PetscCheckFalse(snes->errorifnotconverged && PetscIsInfOrNanScalar(jac->beta[i]),PetscObjectComm((PetscObject)snes),PETSC_ERR_LIB,"SVD generated inconsistent output");
283     CHKERRQ(PetscInfo(snes,"%D: %g\n",i,(double)PetscRealPart(jac->beta[i])));
284     tot += jac->beta[i];
285     total += PetscAbsScalar(jac->beta[i]);
286   }
287   CHKERRQ(VecScale(X,(1. - tot)));
288   CHKERRQ(VecMAXPY(X,jac->n,jac->beta,Xes));
289   CHKERRQ(SNESComputeFunction(snes,X,F));
290 
291   if (snes->xl && snes->xu) {
292     CHKERRQ(SNESVIComputeInactiveSetFnorm(snes, F, X, fnorm));
293   } else {
294     CHKERRQ(VecNorm(F, NORM_2, fnorm));
295   }
296 
297   /* take the minimum-normed candidate if it beats the combination by a factor of rtol or the combination has stagnated */
298   min_fnorm = jac->fnorms[0];
299   min_i     = 0;
300   for (i=0; i<jac->n; i++) {
301     if (jac->fnorms[i] < min_fnorm) {
302       min_fnorm = jac->fnorms[i];
303       min_i     = i;
304     }
305   }
306 
307   /* stagnation or divergence restart to the solution of the solver that failed the least */
308   if (PetscRealPart(total) < jac->stol || min_fnorm*jac->rtol < *fnorm) {
309     CHKERRQ(VecCopy(jac->Xes[min_i],X));
310     CHKERRQ(VecCopy(jac->Fes[min_i],F));
311     *fnorm = min_fnorm;
312   }
313   PetscFunctionReturn(0);
314 }
315 
316 static PetscErrorCode SNESSetUp_Composite(SNES snes)
317 {
318   DM                 dm;
319   SNES_Composite     *jac = (SNES_Composite*)snes->data;
320   SNES_CompositeLink next = jac->head;
321   PetscInt           n=0,i;
322   Vec                F;
323 
324   PetscFunctionBegin;
325   CHKERRQ(SNESGetDM(snes,&dm));
326 
327   if (snes->ops->computevariablebounds) {
328     /* SNESVI only ever calls computevariablebounds once, so calling it once here is justified */
329     if (!snes->xl) CHKERRQ(VecDuplicate(snes->vec_sol,&snes->xl));
330     if (!snes->xu) CHKERRQ(VecDuplicate(snes->vec_sol,&snes->xu));
331     CHKERRQ((*snes->ops->computevariablebounds)(snes,snes->xl,snes->xu));
332   }
333 
334   while (next) {
335     n++;
336     CHKERRQ(SNESSetDM(next->snes,dm));
337     CHKERRQ(SNESSetApplicationContext(next->snes, snes->user));
338     if (snes->xl && snes->xu) {
339       if (snes->ops->computevariablebounds) {
340         CHKERRQ(SNESVISetComputeVariableBounds(next->snes, snes->ops->computevariablebounds));
341       } else {
342         CHKERRQ(SNESVISetVariableBounds(next->snes,snes->xl,snes->xu));
343       }
344     }
345 
346     next = next->next;
347   }
348   jac->nsnes = n;
349   CHKERRQ(SNESGetFunction(snes,&F,NULL,NULL));
350   if (jac->type == SNES_COMPOSITE_ADDITIVEOPTIMAL) {
351     CHKERRQ(VecDuplicateVecs(F,jac->nsnes,&jac->Xes));
352     CHKERRQ(PetscMalloc1(n,&jac->Fes));
353     CHKERRQ(PetscMalloc1(n,&jac->fnorms));
354     next = jac->head;
355     i = 0;
356     while (next) {
357       CHKERRQ(SNESGetFunction(next->snes,&F,NULL,NULL));
358       jac->Fes[i] = F;
359       CHKERRQ(PetscObjectReference((PetscObject)F));
360       next = next->next;
361       i++;
362     }
363     /* allocate the subspace direct solve area */
364     jac->nrhs  = 1;
365     jac->lda   = jac->nsnes;
366     jac->ldb   = jac->nsnes;
367     jac->n     = jac->nsnes;
368 
369     CHKERRQ(PetscMalloc1(jac->n*jac->n,&jac->h));
370     CHKERRQ(PetscMalloc1(jac->n,&jac->beta));
371     CHKERRQ(PetscMalloc1(jac->n,&jac->s));
372     CHKERRQ(PetscMalloc1(jac->n,&jac->g));
373     jac->lwork = 12*jac->n;
374 #if defined(PETSC_USE_COMPLEX)
375     CHKERRQ(PetscMalloc1(jac->lwork,&jac->rwork));
376 #endif
377     CHKERRQ(PetscMalloc1(jac->lwork,&jac->work));
378   }
379 
380   PetscFunctionReturn(0);
381 }
382 
383 static PetscErrorCode SNESReset_Composite(SNES snes)
384 {
385   SNES_Composite     *jac = (SNES_Composite*)snes->data;
386   SNES_CompositeLink next = jac->head;
387 
388   PetscFunctionBegin;
389   while (next) {
390     CHKERRQ(SNESReset(next->snes));
391     next = next->next;
392   }
393   CHKERRQ(VecDestroy(&jac->Xorig));
394   if (jac->Xes) CHKERRQ(VecDestroyVecs(jac->nsnes,&jac->Xes));
395   if (jac->Fes) CHKERRQ(VecDestroyVecs(jac->nsnes,&jac->Fes));
396   CHKERRQ(PetscFree(jac->fnorms));
397   CHKERRQ(PetscFree(jac->h));
398   CHKERRQ(PetscFree(jac->s));
399   CHKERRQ(PetscFree(jac->g));
400   CHKERRQ(PetscFree(jac->beta));
401   CHKERRQ(PetscFree(jac->work));
402   CHKERRQ(PetscFree(jac->rwork));
403   PetscFunctionReturn(0);
404 }
405 
406 static PetscErrorCode SNESDestroy_Composite(SNES snes)
407 {
408   SNES_Composite     *jac = (SNES_Composite*)snes->data;
409   SNES_CompositeLink next = jac->head,next_tmp;
410 
411   PetscFunctionBegin;
412   CHKERRQ(SNESReset_Composite(snes));
413   while (next) {
414     CHKERRQ(SNESDestroy(&next->snes));
415     next_tmp = next;
416     next     = next->next;
417     CHKERRQ(PetscFree(next_tmp));
418   }
419   CHKERRQ(PetscFree(snes->data));
420   PetscFunctionReturn(0);
421 }
422 
423 static PetscErrorCode SNESSetFromOptions_Composite(PetscOptionItems *PetscOptionsObject,SNES snes)
424 {
425   SNES_Composite     *jac = (SNES_Composite*)snes->data;
426   PetscInt           nmax = 8,i;
427   SNES_CompositeLink next;
428   char               *sneses[8];
429   PetscReal          dmps[8];
430   PetscBool          flg;
431 
432   PetscFunctionBegin;
433   CHKERRQ(PetscOptionsHead(PetscOptionsObject,"Composite preconditioner options"));
434   CHKERRQ(PetscOptionsEnum("-snes_composite_type","Type of composition","SNESCompositeSetType",SNESCompositeTypes,(PetscEnum)jac->type,(PetscEnum*)&jac->type,&flg));
435   if (flg) {
436     CHKERRQ(SNESCompositeSetType(snes,jac->type));
437   }
438   CHKERRQ(PetscOptionsStringArray("-snes_composite_sneses","List of composite solvers","SNESCompositeAddSNES",sneses,&nmax,&flg));
439   if (flg) {
440     for (i=0; i<nmax; i++) {
441       CHKERRQ(SNESCompositeAddSNES(snes,sneses[i]));
442       CHKERRQ(PetscFree(sneses[i]));   /* deallocate string sneses[i], which is allocated in PetscOptionsStringArray() */
443     }
444   }
445   CHKERRQ(PetscOptionsRealArray("-snes_composite_damping","Damping of the additive composite solvers","SNESCompositeSetDamping",dmps,&nmax,&flg));
446   if (flg) {
447     for (i=0; i<nmax; i++) {
448       CHKERRQ(SNESCompositeSetDamping(snes,i,dmps[i]));
449     }
450   }
451   CHKERRQ(PetscOptionsReal("-snes_composite_stol","Step tolerance for restart on the additive composite solvers","",jac->stol,&jac->stol,NULL));
452   CHKERRQ(PetscOptionsReal("-snes_composite_rtol","Residual tolerance for the additive composite solvers","",jac->rtol,&jac->rtol,NULL));
453   CHKERRQ(PetscOptionsTail());
454 
455   next = jac->head;
456   while (next) {
457     CHKERRQ(SNESSetFromOptions(next->snes));
458     next = next->next;
459   }
460   PetscFunctionReturn(0);
461 }
462 
463 static PetscErrorCode SNESView_Composite(SNES snes,PetscViewer viewer)
464 {
465   SNES_Composite     *jac = (SNES_Composite*)snes->data;
466   SNES_CompositeLink next = jac->head;
467   PetscBool          iascii;
468 
469   PetscFunctionBegin;
470   CHKERRQ(PetscObjectTypeCompare((PetscObject)viewer,PETSCVIEWERASCII,&iascii));
471   if (iascii) {
472     CHKERRQ(PetscViewerASCIIPrintf(viewer,"  type - %s\n",SNESCompositeTypes[jac->type]));
473     CHKERRQ(PetscViewerASCIIPrintf(viewer,"  SNESes on composite preconditioner follow\n"));
474     CHKERRQ(PetscViewerASCIIPrintf(viewer,"  ---------------------------------\n"));
475   }
476   if (iascii) {
477     CHKERRQ(PetscViewerASCIIPushTab(viewer));
478   }
479   while (next) {
480     CHKERRQ(SNESView(next->snes,viewer));
481     next = next->next;
482   }
483   if (iascii) {
484     CHKERRQ(PetscViewerASCIIPopTab(viewer));
485     CHKERRQ(PetscViewerASCIIPrintf(viewer,"  ---------------------------------\n"));
486   }
487   PetscFunctionReturn(0);
488 }
489 
490 /* ------------------------------------------------------------------------------*/
491 
492 static PetscErrorCode  SNESCompositeSetType_Composite(SNES snes,SNESCompositeType type)
493 {
494   SNES_Composite *jac = (SNES_Composite*)snes->data;
495 
496   PetscFunctionBegin;
497   jac->type = type;
498   PetscFunctionReturn(0);
499 }
500 
501 static PetscErrorCode  SNESCompositeAddSNES_Composite(SNES snes,SNESType type)
502 {
503   SNES_Composite     *jac;
504   SNES_CompositeLink next,ilink;
505   PetscInt           cnt = 0;
506   const char         *prefix;
507   char               newprefix[20];
508   DM                 dm;
509 
510   PetscFunctionBegin;
511   CHKERRQ(PetscNewLog(snes,&ilink));
512   ilink->next = NULL;
513   CHKERRQ(SNESCreate(PetscObjectComm((PetscObject)snes),&ilink->snes));
514   CHKERRQ(PetscLogObjectParent((PetscObject)snes,(PetscObject)ilink->snes));
515   CHKERRQ(SNESGetDM(snes,&dm));
516   CHKERRQ(SNESSetDM(ilink->snes,dm));
517   CHKERRQ(SNESSetTolerances(ilink->snes,snes->abstol,snes->rtol,snes->stol,1,snes->max_funcs));
518   CHKERRQ(PetscObjectCopyFortranFunctionPointers((PetscObject)snes,(PetscObject)ilink->snes));
519   jac  = (SNES_Composite*)snes->data;
520   next = jac->head;
521   if (!next) {
522     jac->head       = ilink;
523     ilink->previous = NULL;
524   } else {
525     cnt++;
526     while (next->next) {
527       next = next->next;
528       cnt++;
529     }
530     next->next      = ilink;
531     ilink->previous = next;
532   }
533   CHKERRQ(SNESGetOptionsPrefix(snes,&prefix));
534   CHKERRQ(SNESSetOptionsPrefix(ilink->snes,prefix));
535   CHKERRQ(PetscSNPrintf(newprefix,sizeof(newprefix),"sub_%d_",(int)cnt));
536   CHKERRQ(SNESAppendOptionsPrefix(ilink->snes,newprefix));
537   CHKERRQ(PetscObjectIncrementTabLevel((PetscObject)ilink->snes,(PetscObject)snes,1));
538   CHKERRQ(SNESSetType(ilink->snes,type));
539   CHKERRQ(SNESSetNormSchedule(ilink->snes, SNES_NORM_FINAL_ONLY));
540 
541   ilink->dmp = 1.0;
542   jac->nsnes++;
543   PetscFunctionReturn(0);
544 }
545 
546 static PetscErrorCode  SNESCompositeGetSNES_Composite(SNES snes,PetscInt n,SNES *subsnes)
547 {
548   SNES_Composite     *jac;
549   SNES_CompositeLink next;
550   PetscInt           i;
551 
552   PetscFunctionBegin;
553   jac  = (SNES_Composite*)snes->data;
554   next = jac->head;
555   for (i=0; i<n; i++) {
556     PetscCheck(next->next,PetscObjectComm((PetscObject)snes),PETSC_ERR_ARG_INCOMP,"Not enough SNESes in composite preconditioner");
557     next = next->next;
558   }
559   *subsnes = next->snes;
560   PetscFunctionReturn(0);
561 }
562 
563 /* -------------------------------------------------------------------------------- */
564 /*@C
565    SNESCompositeSetType - Sets the type of composite preconditioner.
566 
567    Logically Collective on SNES
568 
569    Input Parameters:
570 +  snes - the preconditioner context
571 -  type - SNES_COMPOSITE_ADDITIVE (default), SNES_COMPOSITE_MULTIPLICATIVE
572 
573    Options Database Key:
574 .  -snes_composite_type <type: one of multiplicative, additive, special> - Sets composite preconditioner type
575 
576    Level: Developer
577 
578 @*/
579 PetscErrorCode  SNESCompositeSetType(SNES snes,SNESCompositeType type)
580 {
581   PetscFunctionBegin;
582   PetscValidHeaderSpecific(snes,SNES_CLASSID,1);
583   PetscValidLogicalCollectiveEnum(snes,type,2);
584   CHKERRQ(PetscTryMethod(snes,"SNESCompositeSetType_C",(SNES,SNESCompositeType),(snes,type)));
585   PetscFunctionReturn(0);
586 }
587 
588 /*@C
589    SNESCompositeAddSNES - Adds another SNES to the composite SNES.
590 
591    Collective on SNES
592 
593    Input Parameters:
594 +  snes - the preconditioner context
595 -  type - the type of the new preconditioner
596 
597    Level: Developer
598 
599 @*/
600 PetscErrorCode  SNESCompositeAddSNES(SNES snes,SNESType type)
601 {
602   PetscFunctionBegin;
603   PetscValidHeaderSpecific(snes,SNES_CLASSID,1);
604   CHKERRQ(PetscTryMethod(snes,"SNESCompositeAddSNES_C",(SNES,SNESType),(snes,type)));
605   PetscFunctionReturn(0);
606 }
607 
608 /*@
609    SNESCompositeGetSNES - Gets one of the SNES objects in the composite SNES.
610 
611    Not Collective
612 
613    Input Parameters:
614 +  snes - the preconditioner context
615 -  n - the number of the snes requested
616 
617    Output Parameters:
618 .  subsnes - the SNES requested
619 
620    Level: Developer
621 
622 .seealso: SNESCompositeAddSNES()
623 @*/
624 PetscErrorCode  SNESCompositeGetSNES(SNES snes,PetscInt n,SNES *subsnes)
625 {
626   PetscFunctionBegin;
627   PetscValidHeaderSpecific(snes,SNES_CLASSID,1);
628   PetscValidPointer(subsnes,3);
629   CHKERRQ(PetscUseMethod(snes,"SNESCompositeGetSNES_C",(SNES,PetscInt,SNES*),(snes,n,subsnes)));
630   PetscFunctionReturn(0);
631 }
632 
633 /*@
634    SNESCompositeGetNumber - Get the number of subsolvers in the composite SNES.
635 
636    Logically Collective on SNES
637 
638    Input Parameter:
639    snes - the preconditioner context
640 
641    Output Parameter:
642    n - the number of subsolvers
643 
644    Level: Developer
645 
646 @*/
647 PetscErrorCode  SNESCompositeGetNumber(SNES snes,PetscInt *n)
648 {
649   SNES_Composite     *jac;
650   SNES_CompositeLink next;
651 
652   PetscFunctionBegin;
653   jac  = (SNES_Composite*)snes->data;
654   next = jac->head;
655 
656   *n = 0;
657   while (next) {
658     *n = *n + 1;
659     next = next->next;
660   }
661   PetscFunctionReturn(0);
662 }
663 
664 static PetscErrorCode  SNESCompositeSetDamping_Composite(SNES snes,PetscInt n,PetscReal dmp)
665 {
666   SNES_Composite     *jac;
667   SNES_CompositeLink next;
668   PetscInt           i;
669 
670   PetscFunctionBegin;
671   jac  = (SNES_Composite*)snes->data;
672   next = jac->head;
673   for (i=0; i<n; i++) {
674     PetscCheck(next->next,PetscObjectComm((PetscObject)snes),PETSC_ERR_ARG_INCOMP,"Not enough SNESes in composite preconditioner");
675     next = next->next;
676   }
677   next->dmp = dmp;
678   PetscFunctionReturn(0);
679 }
680 
681 /*@
682    SNESCompositeSetDamping - Sets the damping of a subsolver when using additive composite SNES.
683 
684    Not Collective
685 
686    Input Parameters:
687 +  snes - the preconditioner context
688 .  n - the number of the snes requested
689 -  dmp - the damping
690 
691    Level: Developer
692 
693 .seealso: SNESCompositeAddSNES()
694 @*/
695 PetscErrorCode  SNESCompositeSetDamping(SNES snes,PetscInt n,PetscReal dmp)
696 {
697   PetscFunctionBegin;
698   PetscValidHeaderSpecific(snes,SNES_CLASSID,1);
699   CHKERRQ(PetscUseMethod(snes,"SNESCompositeSetDamping_C",(SNES,PetscInt,PetscReal),(snes,n,dmp)));
700   PetscFunctionReturn(0);
701 }
702 
703 static PetscErrorCode SNESSolve_Composite(SNES snes)
704 {
705   Vec              F,X,B,Y;
706   PetscInt         i;
707   PetscReal        fnorm = 0.0, xnorm = 0.0, snorm = 0.0;
708   SNESNormSchedule normtype;
709   SNES_Composite   *comp = (SNES_Composite*)snes->data;
710 
711   PetscFunctionBegin;
712   X = snes->vec_sol;
713   F = snes->vec_func;
714   B = snes->vec_rhs;
715   Y = snes->vec_sol_update;
716 
717   CHKERRQ(PetscObjectSAWsTakeAccess((PetscObject)snes));
718   snes->iter   = 0;
719   snes->norm   = 0.;
720   comp->innerFailures = 0;
721   CHKERRQ(PetscObjectSAWsGrantAccess((PetscObject)snes));
722   snes->reason = SNES_CONVERGED_ITERATING;
723   CHKERRQ(SNESGetNormSchedule(snes, &normtype));
724   if (normtype == SNES_NORM_ALWAYS || normtype == SNES_NORM_INITIAL_ONLY || normtype == SNES_NORM_INITIAL_FINAL_ONLY) {
725     if (!snes->vec_func_init_set) {
726       CHKERRQ(SNESComputeFunction(snes,X,F));
727     } else snes->vec_func_init_set = PETSC_FALSE;
728 
729     if (snes->xl && snes->xu) {
730       CHKERRQ(SNESVIComputeInactiveSetFnorm(snes, F, X, &fnorm));
731     } else {
732       CHKERRQ(VecNorm(F, NORM_2, &fnorm)); /* fnorm <- ||F||  */
733     }
734     SNESCheckFunctionNorm(snes,fnorm);
735     CHKERRQ(PetscObjectSAWsTakeAccess((PetscObject)snes));
736     snes->iter = 0;
737     snes->norm = fnorm;
738     CHKERRQ(PetscObjectSAWsGrantAccess((PetscObject)snes));
739     CHKERRQ(SNESLogConvergenceHistory(snes,snes->norm,0));
740     CHKERRQ(SNESMonitor(snes,0,snes->norm));
741 
742     /* test convergence */
743     CHKERRQ((*snes->ops->converged)(snes,0,0.0,0.0,fnorm,&snes->reason,snes->cnvP));
744     if (snes->reason) PetscFunctionReturn(0);
745   } else {
746     CHKERRQ(PetscObjectSAWsGrantAccess((PetscObject)snes));
747     CHKERRQ(SNESLogConvergenceHistory(snes,snes->norm,0));
748     CHKERRQ(SNESMonitor(snes,0,snes->norm));
749   }
750 
751   for (i = 0; i < snes->max_its; i++) {
752     /* Call general purpose update function */
753     if (snes->ops->update) {
754       CHKERRQ((*snes->ops->update)(snes, snes->iter));
755     }
756 
757     /* Copy the state before modification by application of the composite solver;
758        we will subtract the new state after application */
759     CHKERRQ(VecCopy(X, Y));
760 
761     if (comp->type == SNES_COMPOSITE_ADDITIVE) {
762       CHKERRQ(SNESCompositeApply_Additive(snes,X,B,F,&fnorm));
763     } else if (comp->type == SNES_COMPOSITE_MULTIPLICATIVE) {
764       CHKERRQ(SNESCompositeApply_Multiplicative(snes,X,B,F,&fnorm));
765     } else if (comp->type == SNES_COMPOSITE_ADDITIVEOPTIMAL) {
766       CHKERRQ(SNESCompositeApply_AdditiveOptimal(snes,X,B,F,&fnorm));
767     } else SETERRQ(PetscObjectComm((PetscObject)snes),PETSC_ERR_ARG_WRONGSTATE,"Unsupported SNESComposite type");
768     if (snes->reason < 0) break;
769 
770     /* Compute the solution update for convergence testing */
771     CHKERRQ(VecAYPX(Y, -1.0, X));
772 
773     if ((i == snes->max_its - 1) && (normtype == SNES_NORM_INITIAL_FINAL_ONLY || normtype == SNES_NORM_FINAL_ONLY)) {
774       CHKERRQ(SNESComputeFunction(snes,X,F));
775 
776       if (snes->xl && snes->xu) {
777         CHKERRQ(VecNormBegin(X, NORM_2, &xnorm));
778         CHKERRQ(VecNormBegin(Y, NORM_2, &snorm));
779         CHKERRQ(SNESVIComputeInactiveSetFnorm(snes, F, X, &fnorm));
780         CHKERRQ(VecNormEnd(X, NORM_2, &xnorm));
781         CHKERRQ(VecNormEnd(Y, NORM_2, &snorm));
782       } else {
783         CHKERRQ(VecNormBegin(F, NORM_2, &fnorm));
784         CHKERRQ(VecNormBegin(X, NORM_2, &xnorm));
785         CHKERRQ(VecNormBegin(Y, NORM_2, &snorm));
786 
787         CHKERRQ(VecNormEnd(F, NORM_2, &fnorm));
788         CHKERRQ(VecNormEnd(X, NORM_2, &xnorm));
789         CHKERRQ(VecNormEnd(Y, NORM_2, &snorm));
790       }
791       SNESCheckFunctionNorm(snes,fnorm);
792     } else if (normtype == SNES_NORM_ALWAYS) {
793       CHKERRQ(VecNormBegin(X, NORM_2, &xnorm));
794       CHKERRQ(VecNormBegin(Y, NORM_2, &snorm));
795       CHKERRQ(VecNormEnd(X, NORM_2, &xnorm));
796       CHKERRQ(VecNormEnd(Y, NORM_2, &snorm));
797     }
798     /* Monitor convergence */
799     CHKERRQ(PetscObjectSAWsTakeAccess((PetscObject)snes));
800     snes->iter = i+1;
801     snes->norm = fnorm;
802     snes->xnorm = xnorm;
803     snes->ynorm = snorm;
804     CHKERRQ(PetscObjectSAWsGrantAccess((PetscObject)snes));
805     CHKERRQ(SNESLogConvergenceHistory(snes,snes->norm,0));
806     CHKERRQ(SNESMonitor(snes,snes->iter,snes->norm));
807     /* Test for convergence */
808     if (normtype == SNES_NORM_ALWAYS) CHKERRQ((*snes->ops->converged)(snes,snes->iter,xnorm,snorm,fnorm,&snes->reason,snes->cnvP));
809     if (snes->reason) break;
810   }
811   if (normtype == SNES_NORM_ALWAYS) {
812     if (i == snes->max_its) {
813       CHKERRQ(PetscInfo(snes,"Maximum number of iterations has been reached: %D\n",snes->max_its));
814       if (!snes->reason) snes->reason = SNES_DIVERGED_MAX_IT;
815     }
816   } else if (!snes->reason) snes->reason = SNES_CONVERGED_ITS;
817   PetscFunctionReturn(0);
818 }
819 
820 /* -------------------------------------------------------------------------------------------*/
821 
822 /*MC
823      SNESCOMPOSITE - Build a preconditioner by composing together several nonlinear solvers
824 
825    Options Database Keys:
826 +  -snes_composite_type <type: one of multiplicative, additive, symmetric_multiplicative, special> - Sets composite preconditioner type
827 -  -snes_composite_sneses - <snes0,snes1,...> list of SNESes to compose
828 
829    Level: intermediate
830 
831 .seealso:  SNESCreate(), SNESSetType(), SNESType (for list of available types), SNES,
832            SNESSHELL, SNESCompositeSetType(), SNESCompositeSpecialSetAlpha(), SNESCompositeAddSNES(),
833            SNESCompositeGetSNES()
834 
835    References:
836 .  * - Peter R. Brune, Matthew G. Knepley, Barry F. Smith, and Xuemin Tu, "Composing Scalable Nonlinear Algebraic Solvers",
837    SIAM Review, 57(4), 2015
838 
839 M*/
840 
841 PETSC_EXTERN PetscErrorCode SNESCreate_Composite(SNES snes)
842 {
843   SNES_Composite *jac;
844 
845   PetscFunctionBegin;
846   CHKERRQ(PetscNewLog(snes,&jac));
847 
848   snes->ops->solve           = SNESSolve_Composite;
849   snes->ops->setup           = SNESSetUp_Composite;
850   snes->ops->reset           = SNESReset_Composite;
851   snes->ops->destroy         = SNESDestroy_Composite;
852   snes->ops->setfromoptions  = SNESSetFromOptions_Composite;
853   snes->ops->view            = SNESView_Composite;
854 
855   snes->usesksp        = PETSC_FALSE;
856 
857   snes->alwayscomputesfinalresidual = PETSC_FALSE;
858 
859   snes->data = (void*)jac;
860   jac->type  = SNES_COMPOSITE_ADDITIVEOPTIMAL;
861   jac->Fes   = NULL;
862   jac->Xes   = NULL;
863   jac->fnorms = NULL;
864   jac->nsnes = 0;
865   jac->head  = NULL;
866   jac->stol  = 0.1;
867   jac->rtol  = 1.1;
868 
869   jac->h     = NULL;
870   jac->s     = NULL;
871   jac->beta  = NULL;
872   jac->work  = NULL;
873   jac->rwork = NULL;
874 
875   CHKERRQ(PetscObjectComposeFunction((PetscObject)snes,"SNESCompositeSetType_C",SNESCompositeSetType_Composite));
876   CHKERRQ(PetscObjectComposeFunction((PetscObject)snes,"SNESCompositeAddSNES_C",SNESCompositeAddSNES_Composite));
877   CHKERRQ(PetscObjectComposeFunction((PetscObject)snes,"SNESCompositeGetSNES_C",SNESCompositeGetSNES_Composite));
878   CHKERRQ(PetscObjectComposeFunction((PetscObject)snes,"SNESCompositeSetDamping_C",SNESCompositeSetDamping_Composite));
879   PetscFunctionReturn(0);
880 }
881