Actual source code: snescomposite.c


  2: /*
  3:       Defines a SNES that can consist of a collection of SNESes
  4: */
  5: #include <petsc/private/snesimpl.h>
  6: #include <petscblaslapack.h>

  8: const char *const        SNESCompositeTypes[]   = {"ADDITIVE","MULTIPLICATIVE","ADDITIVEOPTIMAL","SNESCompositeType","SNES_COMPOSITE",NULL};

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

 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 */

 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 */

 44:   PetscReal          rtol;           /* restart tolerance for accepting the combination */
 45:   PetscReal          stol;           /* restart tolerance for the combination */
 46: } SNES_Composite;

 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;

 56:   if (snes->normschedule == SNES_NORM_ALWAYS) {
 57:     SNESSetInitialFunction(next->snes,F);
 58:   }
 59:   SNESSolve(next->snes,B,X);
 60:   SNESGetConvergedReason(next->snes,&reason);
 61:   if (reason < 0 && reason != SNES_DIVERGED_MAX_IT) {
 62:     jac->innerFailures++;
 63:     if (jac->innerFailures >= snes->maxFailures) {
 64:       snes->reason = SNES_DIVERGED_INNER;
 65:       return 0;
 66:     }
 67:   }

 69:   while (next->next) {
 70:     /* only copy the function over in the case where the functions correspond */
 71:     if (next->snes->npcside== PC_RIGHT && next->snes->normschedule != SNES_NORM_NONE) {
 72:       SNESGetFunction(next->snes,&FSub,NULL,NULL);
 73:       next = next->next;
 74:       SNESSetInitialFunction(next->snes,FSub);
 75:     } else {
 76:       next = next->next;
 77:     }
 78:     SNESSolve(next->snes,B,X);
 79:     SNESGetConvergedReason(next->snes,&reason);
 80:     if (reason < 0 && reason != SNES_DIVERGED_MAX_IT) {
 81:       jac->innerFailures++;
 82:       if (jac->innerFailures >= snes->maxFailures) {
 83:         snes->reason = SNES_DIVERGED_INNER;
 84:         return 0;
 85:       }
 86:     }
 87:   }
 88:   if (next->snes->npcside== PC_RIGHT) {
 89:     SNESGetFunction(next->snes,&FSub,NULL,NULL);
 90:     VecCopy(FSub,F);
 91:     if (fnorm) {
 92:       if (snes->xl && snes->xu) {
 93:         SNESVIComputeInactiveSetFnorm(snes, F, X, fnorm);
 94:       } else {
 95:         VecNorm(F, NORM_2, fnorm);
 96:       }
 97:       SNESCheckFunctionNorm(snes,*fnorm);
 98:     }
 99:   } else if (snes->normschedule == SNES_NORM_ALWAYS) {
100:     SNESComputeFunction(snes,X,F);
101:     if (fnorm) {
102:       if (snes->xl && snes->xu) {
103:         SNESVIComputeInactiveSetFnorm(snes, F, X, fnorm);
104:       } else {
105:         VecNorm(F, NORM_2, fnorm);
106:       }
107:       SNESCheckFunctionNorm(snes,*fnorm);
108:     }
109:   }
110:   return 0;
111: }

113: static PetscErrorCode SNESCompositeApply_Additive(SNES snes,Vec X,Vec B,Vec F,PetscReal *fnorm)
114: {
115:   SNES_Composite      *jac = (SNES_Composite*)snes->data;
116:   SNES_CompositeLink  next = jac->head;
117:   Vec                 Y,Xorig;
118:   SNESConvergedReason reason;

120:   Y = snes->vec_sol_update;
121:   if (!jac->Xorig) VecDuplicate(X,&jac->Xorig);
122:   Xorig = jac->Xorig;
123:   VecCopy(X,Xorig);
125:   if (snes->normschedule == SNES_NORM_ALWAYS) {
126:     SNESSetInitialFunction(next->snes,F);
127:     while (next->next) {
128:       next = next->next;
129:       SNESSetInitialFunction(next->snes,F);
130:     }
131:   }
132:   next = jac->head;
133:   VecCopy(Xorig,Y);
134:   SNESSolve(next->snes,B,Y);
135:   SNESGetConvergedReason(next->snes,&reason);
136:   if (reason < 0 && reason != SNES_DIVERGED_MAX_IT) {
137:     jac->innerFailures++;
138:     if (jac->innerFailures >= snes->maxFailures) {
139:       snes->reason = SNES_DIVERGED_INNER;
140:       return 0;
141:     }
142:   }
143:   VecAXPY(Y,-1.0,Xorig);
144:   VecAXPY(X,next->dmp,Y);
145:   while (next->next) {
146:     next = next->next;
147:     VecCopy(Xorig,Y);
148:     SNESSolve(next->snes,B,Y);
149:     SNESGetConvergedReason(next->snes,&reason);
150:     if (reason < 0 && reason != SNES_DIVERGED_MAX_IT) {
151:       jac->innerFailures++;
152:       if (jac->innerFailures >= snes->maxFailures) {
153:         snes->reason = SNES_DIVERGED_INNER;
154:         return 0;
155:       }
156:     }
157:     VecAXPY(Y,-1.0,Xorig);
158:     VecAXPY(X,next->dmp,Y);
159:   }
160:   if (snes->normschedule == SNES_NORM_ALWAYS) {
161:     SNESComputeFunction(snes,X,F);
162:     if (fnorm) {
163:       if (snes->xl && snes->xu) {
164:         SNESVIComputeInactiveSetFnorm(snes, F, X, fnorm);
165:       } else {
166:         VecNorm(F, NORM_2, fnorm);
167:       }
168:       SNESCheckFunctionNorm(snes,*fnorm);
169:     }
170:   }
171:   return 0;
172: }

174: /* approximately solve the overdetermined system:

176:  2*F(x_i)\cdot F(\x_j)\alpha_i = 0
177:  \alpha_i                      = 1

179:  Which minimizes the L2 norm of the linearization of:
180:  ||F(\sum_i \alpha_i*x_i)||^2

182:  With the constraint that \sum_i\alpha_i = 1
183:  Where x_i is the solution from the ith subsolver.
184:  */
185: static PetscErrorCode SNESCompositeApply_AdditiveOptimal(SNES snes,Vec X,Vec B,Vec F,PetscReal *fnorm)
186: {
187:   SNES_Composite      *jac = (SNES_Composite*)snes->data;
188:   SNES_CompositeLink  next = jac->head;
189:   Vec                 *Xes = jac->Xes,*Fes = jac->Fes;
190:   PetscInt            i,j;
191:   PetscScalar         tot,total,ftf;
192:   PetscReal           min_fnorm;
193:   PetscInt            min_i;
194:   SNESConvergedReason reason;


198:   if (snes->normschedule == SNES_NORM_ALWAYS) {
199:     next = jac->head;
200:     SNESSetInitialFunction(next->snes,F);
201:     while (next->next) {
202:       next = next->next;
203:       SNESSetInitialFunction(next->snes,F);
204:     }
205:   }

207:   next = jac->head;
208:   i = 0;
209:   VecCopy(X,Xes[i]);
210:   SNESSolve(next->snes,B,Xes[i]);
211:   SNESGetConvergedReason(next->snes,&reason);
212:   if (reason < 0 && reason != SNES_DIVERGED_MAX_IT) {
213:     jac->innerFailures++;
214:     if (jac->innerFailures >= snes->maxFailures) {
215:       snes->reason = SNES_DIVERGED_INNER;
216:       return 0;
217:     }
218:   }
219:   while (next->next) {
220:     i++;
221:     next = next->next;
222:     VecCopy(X,Xes[i]);
223:     SNESSolve(next->snes,B,Xes[i]);
224:     SNESGetConvergedReason(next->snes,&reason);
225:     if (reason < 0 && reason != SNES_DIVERGED_MAX_IT) {
226:       jac->innerFailures++;
227:       if (jac->innerFailures >= snes->maxFailures) {
228:         snes->reason = SNES_DIVERGED_INNER;
229:         return 0;
230:       }
231:     }
232:   }

234:   /* all the solutions are collected; combine optimally */
235:   for (i=0;i<jac->n;i++) {
236:     for (j=0;j<i+1;j++) {
237:       VecDotBegin(Fes[i],Fes[j],&jac->h[i + j*jac->n]);
238:     }
239:     VecDotBegin(Fes[i],F,&jac->g[i]);
240:   }

242:   for (i=0;i<jac->n;i++) {
243:     for (j=0;j<i+1;j++) {
244:       VecDotEnd(Fes[i],Fes[j],&jac->h[i + j*jac->n]);
245:       if (i == j) jac->fnorms[i] = PetscSqrtReal(PetscRealPart(jac->h[i + j*jac->n]));
246:     }
247:     VecDotEnd(Fes[i],F,&jac->g[i]);
248:   }

250:   ftf = (*fnorm)*(*fnorm);

252:   for (i=0; i<jac->n; i++) {
253:     for (j=i+1;j<jac->n;j++) {
254:       jac->h[i + j*jac->n] = jac->h[j + i*jac->n];
255:     }
256:   }

258:   for (i=0; i<jac->n; i++) {
259:     for (j=0; j<jac->n; j++) {
260:       jac->h[i + j*jac->n] = jac->h[i + j*jac->n] - jac->g[j] - jac->g[i] + ftf;
261:     }
262:     jac->beta[i] = ftf - jac->g[i];
263:   }

265:   jac->info  = 0;
266:   jac->rcond = -1.;
267:   PetscFPTrapPush(PETSC_FP_TRAP_OFF);
268: #if defined(PETSC_USE_COMPLEX)
269:   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));
270: #else
271:   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));
272: #endif
273:   PetscFPTrapPop();
276:   tot = 0.;
277:   total = 0.;
278:   for (i=0; i<jac->n; i++) {
280:     PetscInfo(snes,"%D: %g\n",i,(double)PetscRealPart(jac->beta[i]));
281:     tot += jac->beta[i];
282:     total += PetscAbsScalar(jac->beta[i]);
283:   }
284:   VecScale(X,(1. - tot));
285:   VecMAXPY(X,jac->n,jac->beta,Xes);
286:   SNESComputeFunction(snes,X,F);

288:   if (snes->xl && snes->xu) {
289:     SNESVIComputeInactiveSetFnorm(snes, F, X, fnorm);
290:   } else {
291:     VecNorm(F, NORM_2, fnorm);
292:   }

294:   /* take the minimum-normed candidate if it beats the combination by a factor of rtol or the combination has stagnated */
295:   min_fnorm = jac->fnorms[0];
296:   min_i     = 0;
297:   for (i=0; i<jac->n; i++) {
298:     if (jac->fnorms[i] < min_fnorm) {
299:       min_fnorm = jac->fnorms[i];
300:       min_i     = i;
301:     }
302:   }

304:   /* stagnation or divergence restart to the solution of the solver that failed the least */
305:   if (PetscRealPart(total) < jac->stol || min_fnorm*jac->rtol < *fnorm) {
306:     VecCopy(jac->Xes[min_i],X);
307:     VecCopy(jac->Fes[min_i],F);
308:     *fnorm = min_fnorm;
309:   }
310:   return 0;
311: }

313: static PetscErrorCode SNESSetUp_Composite(SNES snes)
314: {
315:   DM                 dm;
316:   SNES_Composite     *jac = (SNES_Composite*)snes->data;
317:   SNES_CompositeLink next = jac->head;
318:   PetscInt           n=0,i;
319:   Vec                F;

321:   SNESGetDM(snes,&dm);

323:   if (snes->ops->computevariablebounds) {
324:     /* SNESVI only ever calls computevariablebounds once, so calling it once here is justified */
325:     if (!snes->xl) VecDuplicate(snes->vec_sol,&snes->xl);
326:     if (!snes->xu) VecDuplicate(snes->vec_sol,&snes->xu);
327:     (*snes->ops->computevariablebounds)(snes,snes->xl,snes->xu);
328:   }

330:   while (next) {
331:     n++;
332:     SNESSetDM(next->snes,dm);
333:     SNESSetApplicationContext(next->snes, snes->user);
334:     if (snes->xl && snes->xu) {
335:       if (snes->ops->computevariablebounds) {
336:         SNESVISetComputeVariableBounds(next->snes, snes->ops->computevariablebounds);
337:       } else {
338:         SNESVISetVariableBounds(next->snes,snes->xl,snes->xu);
339:       }
340:     }

342:     next = next->next;
343:   }
344:   jac->nsnes = n;
345:   SNESGetFunction(snes,&F,NULL,NULL);
346:   if (jac->type == SNES_COMPOSITE_ADDITIVEOPTIMAL) {
347:     VecDuplicateVecs(F,jac->nsnes,&jac->Xes);
348:     PetscMalloc1(n,&jac->Fes);
349:     PetscMalloc1(n,&jac->fnorms);
350:     next = jac->head;
351:     i = 0;
352:     while (next) {
353:       SNESGetFunction(next->snes,&F,NULL,NULL);
354:       jac->Fes[i] = F;
355:       PetscObjectReference((PetscObject)F);
356:       next = next->next;
357:       i++;
358:     }
359:     /* allocate the subspace direct solve area */
360:     jac->nrhs  = 1;
361:     jac->lda   = jac->nsnes;
362:     jac->ldb   = jac->nsnes;
363:     jac->n     = jac->nsnes;

365:     PetscMalloc1(jac->n*jac->n,&jac->h);
366:     PetscMalloc1(jac->n,&jac->beta);
367:     PetscMalloc1(jac->n,&jac->s);
368:     PetscMalloc1(jac->n,&jac->g);
369:     jac->lwork = 12*jac->n;
370: #if defined(PETSC_USE_COMPLEX)
371:     PetscMalloc1(jac->lwork,&jac->rwork);
372: #endif
373:     PetscMalloc1(jac->lwork,&jac->work);
374:   }

376:   return 0;
377: }

379: static PetscErrorCode SNESReset_Composite(SNES snes)
380: {
381:   SNES_Composite     *jac = (SNES_Composite*)snes->data;
382:   SNES_CompositeLink next = jac->head;

384:   while (next) {
385:     SNESReset(next->snes);
386:     next = next->next;
387:   }
388:   VecDestroy(&jac->Xorig);
389:   if (jac->Xes) VecDestroyVecs(jac->nsnes,&jac->Xes);
390:   if (jac->Fes) VecDestroyVecs(jac->nsnes,&jac->Fes);
391:   PetscFree(jac->fnorms);
392:   PetscFree(jac->h);
393:   PetscFree(jac->s);
394:   PetscFree(jac->g);
395:   PetscFree(jac->beta);
396:   PetscFree(jac->work);
397:   PetscFree(jac->rwork);
398:   return 0;
399: }

401: static PetscErrorCode SNESDestroy_Composite(SNES snes)
402: {
403:   SNES_Composite     *jac = (SNES_Composite*)snes->data;
404:   SNES_CompositeLink next = jac->head,next_tmp;

406:   SNESReset_Composite(snes);
407:   while (next) {
408:     SNESDestroy(&next->snes);
409:     next_tmp = next;
410:     next     = next->next;
411:     PetscFree(next_tmp);
412:   }
413:   PetscFree(snes->data);
414:   return 0;
415: }

417: static PetscErrorCode SNESSetFromOptions_Composite(PetscOptionItems *PetscOptionsObject,SNES snes)
418: {
419:   SNES_Composite     *jac = (SNES_Composite*)snes->data;
420:   PetscInt           nmax = 8,i;
421:   SNES_CompositeLink next;
422:   char               *sneses[8];
423:   PetscReal          dmps[8];
424:   PetscBool          flg;

426:   PetscOptionsHead(PetscOptionsObject,"Composite preconditioner options");
427:   PetscOptionsEnum("-snes_composite_type","Type of composition","SNESCompositeSetType",SNESCompositeTypes,(PetscEnum)jac->type,(PetscEnum*)&jac->type,&flg);
428:   if (flg) {
429:     SNESCompositeSetType(snes,jac->type);
430:   }
431:   PetscOptionsStringArray("-snes_composite_sneses","List of composite solvers","SNESCompositeAddSNES",sneses,&nmax,&flg);
432:   if (flg) {
433:     for (i=0; i<nmax; i++) {
434:       SNESCompositeAddSNES(snes,sneses[i]);
435:       PetscFree(sneses[i]);   /* deallocate string sneses[i], which is allocated in PetscOptionsStringArray() */
436:     }
437:   }
438:   PetscOptionsRealArray("-snes_composite_damping","Damping of the additive composite solvers","SNESCompositeSetDamping",dmps,&nmax,&flg);
439:   if (flg) {
440:     for (i=0; i<nmax; i++) {
441:       SNESCompositeSetDamping(snes,i,dmps[i]);
442:     }
443:   }
444:   PetscOptionsReal("-snes_composite_stol","Step tolerance for restart on the additive composite solvers","",jac->stol,&jac->stol,NULL);
445:   PetscOptionsReal("-snes_composite_rtol","Residual tolerance for the additive composite solvers","",jac->rtol,&jac->rtol,NULL);
446:   PetscOptionsTail();

448:   next = jac->head;
449:   while (next) {
450:     SNESSetFromOptions(next->snes);
451:     next = next->next;
452:   }
453:   return 0;
454: }

456: static PetscErrorCode SNESView_Composite(SNES snes,PetscViewer viewer)
457: {
458:   SNES_Composite     *jac = (SNES_Composite*)snes->data;
459:   SNES_CompositeLink next = jac->head;
460:   PetscBool          iascii;

462:   PetscObjectTypeCompare((PetscObject)viewer,PETSCVIEWERASCII,&iascii);
463:   if (iascii) {
464:     PetscViewerASCIIPrintf(viewer,"  type - %s\n",SNESCompositeTypes[jac->type]);
465:     PetscViewerASCIIPrintf(viewer,"  SNESes on composite preconditioner follow\n");
466:     PetscViewerASCIIPrintf(viewer,"  ---------------------------------\n");
467:   }
468:   if (iascii) {
469:     PetscViewerASCIIPushTab(viewer);
470:   }
471:   while (next) {
472:     SNESView(next->snes,viewer);
473:     next = next->next;
474:   }
475:   if (iascii) {
476:     PetscViewerASCIIPopTab(viewer);
477:     PetscViewerASCIIPrintf(viewer,"  ---------------------------------\n");
478:   }
479:   return 0;
480: }

482: /* ------------------------------------------------------------------------------*/

484: static PetscErrorCode  SNESCompositeSetType_Composite(SNES snes,SNESCompositeType type)
485: {
486:   SNES_Composite *jac = (SNES_Composite*)snes->data;

488:   jac->type = type;
489:   return 0;
490: }

492: static PetscErrorCode  SNESCompositeAddSNES_Composite(SNES snes,SNESType type)
493: {
494:   SNES_Composite     *jac;
495:   SNES_CompositeLink next,ilink;
496:   PetscInt           cnt = 0;
497:   const char         *prefix;
498:   char               newprefix[20];
499:   DM                 dm;

501:   PetscNewLog(snes,&ilink);
502:   ilink->next = NULL;
503:   SNESCreate(PetscObjectComm((PetscObject)snes),&ilink->snes);
504:   PetscLogObjectParent((PetscObject)snes,(PetscObject)ilink->snes);
505:   SNESGetDM(snes,&dm);
506:   SNESSetDM(ilink->snes,dm);
507:   SNESSetTolerances(ilink->snes,snes->abstol,snes->rtol,snes->stol,1,snes->max_funcs);
508:   PetscObjectCopyFortranFunctionPointers((PetscObject)snes,(PetscObject)ilink->snes);
509:   jac  = (SNES_Composite*)snes->data;
510:   next = jac->head;
511:   if (!next) {
512:     jac->head       = ilink;
513:     ilink->previous = NULL;
514:   } else {
515:     cnt++;
516:     while (next->next) {
517:       next = next->next;
518:       cnt++;
519:     }
520:     next->next      = ilink;
521:     ilink->previous = next;
522:   }
523:   SNESGetOptionsPrefix(snes,&prefix);
524:   SNESSetOptionsPrefix(ilink->snes,prefix);
525:   PetscSNPrintf(newprefix,sizeof(newprefix),"sub_%d_",(int)cnt);
526:   SNESAppendOptionsPrefix(ilink->snes,newprefix);
527:   PetscObjectIncrementTabLevel((PetscObject)ilink->snes,(PetscObject)snes,1);
528:   SNESSetType(ilink->snes,type);
529:   SNESSetNormSchedule(ilink->snes, SNES_NORM_FINAL_ONLY);

531:   ilink->dmp = 1.0;
532:   jac->nsnes++;
533:   return 0;
534: }

536: static PetscErrorCode  SNESCompositeGetSNES_Composite(SNES snes,PetscInt n,SNES *subsnes)
537: {
538:   SNES_Composite     *jac;
539:   SNES_CompositeLink next;
540:   PetscInt           i;

542:   jac  = (SNES_Composite*)snes->data;
543:   next = jac->head;
544:   for (i=0; i<n; i++) {
546:     next = next->next;
547:   }
548:   *subsnes = next->snes;
549:   return 0;
550: }

552: /* -------------------------------------------------------------------------------- */
553: /*@C
554:    SNESCompositeSetType - Sets the type of composite preconditioner.

556:    Logically Collective on SNES

558:    Input Parameters:
559: +  snes - the preconditioner context
560: -  type - SNES_COMPOSITE_ADDITIVE (default), SNES_COMPOSITE_MULTIPLICATIVE

562:    Options Database Key:
563: .  -snes_composite_type <type: one of multiplicative, additive, special> - Sets composite preconditioner type

565:    Level: Developer

567: @*/
568: PetscErrorCode  SNESCompositeSetType(SNES snes,SNESCompositeType type)
569: {
572:   PetscTryMethod(snes,"SNESCompositeSetType_C",(SNES,SNESCompositeType),(snes,type));
573:   return 0;
574: }

576: /*@C
577:    SNESCompositeAddSNES - Adds another SNES to the composite SNES.

579:    Collective on SNES

581:    Input Parameters:
582: +  snes - the preconditioner context
583: -  type - the type of the new preconditioner

585:    Level: Developer

587: @*/
588: PetscErrorCode  SNESCompositeAddSNES(SNES snes,SNESType type)
589: {
591:   PetscTryMethod(snes,"SNESCompositeAddSNES_C",(SNES,SNESType),(snes,type));
592:   return 0;
593: }

595: /*@
596:    SNESCompositeGetSNES - Gets one of the SNES objects in the composite SNES.

598:    Not Collective

600:    Input Parameters:
601: +  snes - the preconditioner context
602: -  n - the number of the snes requested

604:    Output Parameters:
605: .  subsnes - the SNES requested

607:    Level: Developer

609: .seealso: SNESCompositeAddSNES()
610: @*/
611: PetscErrorCode  SNESCompositeGetSNES(SNES snes,PetscInt n,SNES *subsnes)
612: {
615:   PetscUseMethod(snes,"SNESCompositeGetSNES_C",(SNES,PetscInt,SNES*),(snes,n,subsnes));
616:   return 0;
617: }

619: /*@
620:    SNESCompositeGetNumber - Get the number of subsolvers in the composite SNES.

622:    Logically Collective on SNES

624:    Input Parameter:
625:    snes - the preconditioner context

627:    Output Parameter:
628:    n - the number of subsolvers

630:    Level: Developer

632: @*/
633: PetscErrorCode  SNESCompositeGetNumber(SNES snes,PetscInt *n)
634: {
635:   SNES_Composite     *jac;
636:   SNES_CompositeLink next;

638:   jac  = (SNES_Composite*)snes->data;
639:   next = jac->head;

641:   *n = 0;
642:   while (next) {
643:     *n = *n + 1;
644:     next = next->next;
645:   }
646:   return 0;
647: }

649: static PetscErrorCode  SNESCompositeSetDamping_Composite(SNES snes,PetscInt n,PetscReal dmp)
650: {
651:   SNES_Composite     *jac;
652:   SNES_CompositeLink next;
653:   PetscInt           i;

655:   jac  = (SNES_Composite*)snes->data;
656:   next = jac->head;
657:   for (i=0; i<n; i++) {
659:     next = next->next;
660:   }
661:   next->dmp = dmp;
662:   return 0;
663: }

665: /*@
666:    SNESCompositeSetDamping - Sets the damping of a subsolver when using additive composite SNES.

668:    Not Collective

670:    Input Parameters:
671: +  snes - the preconditioner context
672: .  n - the number of the snes requested
673: -  dmp - the damping

675:    Level: Developer

677: .seealso: SNESCompositeAddSNES()
678: @*/
679: PetscErrorCode  SNESCompositeSetDamping(SNES snes,PetscInt n,PetscReal dmp)
680: {
682:   PetscUseMethod(snes,"SNESCompositeSetDamping_C",(SNES,PetscInt,PetscReal),(snes,n,dmp));
683:   return 0;
684: }

686: static PetscErrorCode SNESSolve_Composite(SNES snes)
687: {
688:   Vec              F,X,B,Y;
689:   PetscInt         i;
690:   PetscReal        fnorm = 0.0, xnorm = 0.0, snorm = 0.0;
691:   SNESNormSchedule normtype;
692:   SNES_Composite   *comp = (SNES_Composite*)snes->data;

694:   X = snes->vec_sol;
695:   F = snes->vec_func;
696:   B = snes->vec_rhs;
697:   Y = snes->vec_sol_update;

699:   PetscObjectSAWsTakeAccess((PetscObject)snes);
700:   snes->iter   = 0;
701:   snes->norm   = 0.;
702:   comp->innerFailures = 0;
703:   PetscObjectSAWsGrantAccess((PetscObject)snes);
704:   snes->reason = SNES_CONVERGED_ITERATING;
705:   SNESGetNormSchedule(snes, &normtype);
706:   if (normtype == SNES_NORM_ALWAYS || normtype == SNES_NORM_INITIAL_ONLY || normtype == SNES_NORM_INITIAL_FINAL_ONLY) {
707:     if (!snes->vec_func_init_set) {
708:       SNESComputeFunction(snes,X,F);
709:     } else snes->vec_func_init_set = PETSC_FALSE;

711:     if (snes->xl && snes->xu) {
712:       SNESVIComputeInactiveSetFnorm(snes, F, X, &fnorm);
713:     } else {
714:       VecNorm(F, NORM_2, &fnorm); /* fnorm <- ||F||  */
715:     }
716:     SNESCheckFunctionNorm(snes,fnorm);
717:     PetscObjectSAWsTakeAccess((PetscObject)snes);
718:     snes->iter = 0;
719:     snes->norm = fnorm;
720:     PetscObjectSAWsGrantAccess((PetscObject)snes);
721:     SNESLogConvergenceHistory(snes,snes->norm,0);
722:     SNESMonitor(snes,0,snes->norm);

724:     /* test convergence */
725:     (*snes->ops->converged)(snes,0,0.0,0.0,fnorm,&snes->reason,snes->cnvP);
726:     if (snes->reason) return 0;
727:   } else {
728:     PetscObjectSAWsGrantAccess((PetscObject)snes);
729:     SNESLogConvergenceHistory(snes,snes->norm,0);
730:     SNESMonitor(snes,0,snes->norm);
731:   }

733:   for (i = 0; i < snes->max_its; i++) {
734:     /* Call general purpose update function */
735:     if (snes->ops->update) {
736:       (*snes->ops->update)(snes, snes->iter);
737:     }

739:     /* Copy the state before modification by application of the composite solver;
740:        we will subtract the new state after application */
741:     VecCopy(X, Y);

743:     if (comp->type == SNES_COMPOSITE_ADDITIVE) {
744:       SNESCompositeApply_Additive(snes,X,B,F,&fnorm);
745:     } else if (comp->type == SNES_COMPOSITE_MULTIPLICATIVE) {
746:       SNESCompositeApply_Multiplicative(snes,X,B,F,&fnorm);
747:     } else if (comp->type == SNES_COMPOSITE_ADDITIVEOPTIMAL) {
748:       SNESCompositeApply_AdditiveOptimal(snes,X,B,F,&fnorm);
749:     } else SETERRQ(PetscObjectComm((PetscObject)snes),PETSC_ERR_ARG_WRONGSTATE,"Unsupported SNESComposite type");
750:     if (snes->reason < 0) break;

752:     /* Compute the solution update for convergence testing */
753:     VecAYPX(Y, -1.0, X);

755:     if ((i == snes->max_its - 1) && (normtype == SNES_NORM_INITIAL_FINAL_ONLY || normtype == SNES_NORM_FINAL_ONLY)) {
756:       SNESComputeFunction(snes,X,F);

758:       if (snes->xl && snes->xu) {
759:         VecNormBegin(X, NORM_2, &xnorm);
760:         VecNormBegin(Y, NORM_2, &snorm);
761:         SNESVIComputeInactiveSetFnorm(snes, F, X, &fnorm);
762:         VecNormEnd(X, NORM_2, &xnorm);
763:         VecNormEnd(Y, NORM_2, &snorm);
764:       } else {
765:         VecNormBegin(F, NORM_2, &fnorm);
766:         VecNormBegin(X, NORM_2, &xnorm);
767:         VecNormBegin(Y, NORM_2, &snorm);

769:         VecNormEnd(F, NORM_2, &fnorm);
770:         VecNormEnd(X, NORM_2, &xnorm);
771:         VecNormEnd(Y, NORM_2, &snorm);
772:       }
773:       SNESCheckFunctionNorm(snes,fnorm);
774:     } else if (normtype == SNES_NORM_ALWAYS) {
775:       VecNormBegin(X, NORM_2, &xnorm);
776:       VecNormBegin(Y, NORM_2, &snorm);
777:       VecNormEnd(X, NORM_2, &xnorm);
778:       VecNormEnd(Y, NORM_2, &snorm);
779:     }
780:     /* Monitor convergence */
781:     PetscObjectSAWsTakeAccess((PetscObject)snes);
782:     snes->iter = i+1;
783:     snes->norm = fnorm;
784:     snes->xnorm = xnorm;
785:     snes->ynorm = snorm;
786:     PetscObjectSAWsGrantAccess((PetscObject)snes);
787:     SNESLogConvergenceHistory(snes,snes->norm,0);
788:     SNESMonitor(snes,snes->iter,snes->norm);
789:     /* Test for convergence */
790:     if (normtype == SNES_NORM_ALWAYS) (*snes->ops->converged)(snes,snes->iter,xnorm,snorm,fnorm,&snes->reason,snes->cnvP);
791:     if (snes->reason) break;
792:   }
793:   if (normtype == SNES_NORM_ALWAYS) {
794:     if (i == snes->max_its) {
795:       PetscInfo(snes,"Maximum number of iterations has been reached: %D\n",snes->max_its);
796:       if (!snes->reason) snes->reason = SNES_DIVERGED_MAX_IT;
797:     }
798:   } else if (!snes->reason) snes->reason = SNES_CONVERGED_ITS;
799:   return 0;
800: }

802: /* -------------------------------------------------------------------------------------------*/

804: /*MC
805:      SNESCOMPOSITE - Build a preconditioner by composing together several nonlinear solvers

807:    Options Database Keys:
808: +  -snes_composite_type <type: one of multiplicative, additive, symmetric_multiplicative, special> - Sets composite preconditioner type
809: -  -snes_composite_sneses - <snes0,snes1,...> list of SNESes to compose

811:    Level: intermediate

813: .seealso:  SNESCreate(), SNESSetType(), SNESType (for list of available types), SNES,
814:            SNESSHELL, SNESCompositeSetType(), SNESCompositeSpecialSetAlpha(), SNESCompositeAddSNES(),
815:            SNESCompositeGetSNES()

817:    References:
818: .  * - Peter R. Brune, Matthew G. Knepley, Barry F. Smith, and Xuemin Tu, "Composing Scalable Nonlinear Algebraic Solvers",
819:    SIAM Review, 57(4), 2015

821: M*/

823: PETSC_EXTERN PetscErrorCode SNESCreate_Composite(SNES snes)
824: {
825:   SNES_Composite *jac;

827:   PetscNewLog(snes,&jac);

829:   snes->ops->solve           = SNESSolve_Composite;
830:   snes->ops->setup           = SNESSetUp_Composite;
831:   snes->ops->reset           = SNESReset_Composite;
832:   snes->ops->destroy         = SNESDestroy_Composite;
833:   snes->ops->setfromoptions  = SNESSetFromOptions_Composite;
834:   snes->ops->view            = SNESView_Composite;

836:   snes->usesksp        = PETSC_FALSE;

838:   snes->alwayscomputesfinalresidual = PETSC_FALSE;

840:   snes->data = (void*)jac;
841:   jac->type  = SNES_COMPOSITE_ADDITIVEOPTIMAL;
842:   jac->Fes   = NULL;
843:   jac->Xes   = NULL;
844:   jac->fnorms = NULL;
845:   jac->nsnes = 0;
846:   jac->head  = NULL;
847:   jac->stol  = 0.1;
848:   jac->rtol  = 1.1;

850:   jac->h     = NULL;
851:   jac->s     = NULL;
852:   jac->beta  = NULL;
853:   jac->work  = NULL;
854:   jac->rwork = NULL;

856:   PetscObjectComposeFunction((PetscObject)snes,"SNESCompositeSetType_C",SNESCompositeSetType_Composite);
857:   PetscObjectComposeFunction((PetscObject)snes,"SNESCompositeAddSNES_C",SNESCompositeAddSNES_Composite);
858:   PetscObjectComposeFunction((PetscObject)snes,"SNESCompositeGetSNES_C",SNESCompositeGetSNES_Composite);
859:   PetscObjectComposeFunction((PetscObject)snes,"SNESCompositeSetDamping_C",SNESCompositeSetDamping_Composite);
860:   return 0;
861: }