xref: /petsc/src/snes/impls/multiblock/multiblock.c (revision f97672e55eacc8688507b9471cd7ec2664d7f203)
1 #include <petsc/private/snesimpl.h> /*I "petscsnes.h" I*/
2 #include <petscdmcomposite.h>
3 
4 typedef struct _BlockDesc *BlockDesc;
5 struct _BlockDesc {
6   char       *name;     /* Block name */
7   PetscInt   nfields;   /* If block is defined on a DA, the number of DA fields */
8   PetscInt   *fields;   /* If block is defined on a DA, the list of DA fields */
9   IS         is;        /* Index sets defining the block */
10   VecScatter sctx;      /* Scatter mapping global Vec to blockVec */
11   SNES       snes;      /* Solver for this block */
12   Vec        x;
13   BlockDesc  next, previous;
14 };
15 
16 typedef struct {
17   PetscBool       issetup;       /* Flag is true after the all ISs and operators have been defined */
18   PetscBool       defined;       /* Flag is true after the blocks have been defined, to prevent more blocks from being added */
19   PetscBool       defaultblocks; /* Flag is true for a system with a set of 'k' scalar fields with the same layout (and bs = k) */
20   PetscInt        numBlocks;     /* Number of blocks (can be fields, domains, etc.) */
21   PetscInt        bs;            /* Block size for IS, Vec and Mat structures */
22   PCCompositeType type;          /* Solver combination method (additive, multiplicative, etc.) */
23   BlockDesc       blocks;        /* Linked list of block descriptors */
24 } SNES_Multiblock;
25 
26 PetscErrorCode SNESReset_Multiblock(SNES snes)
27 {
28   SNES_Multiblock *mb    = (SNES_Multiblock*) snes->data;
29   BlockDesc       blocks = mb->blocks, next;
30 
31   PetscFunctionBegin;
32   while (blocks) {
33     PetscCall(SNESReset(blocks->snes));
34 #if 0
35     PetscCall(VecDestroy(&blocks->x));
36 #endif
37     PetscCall(VecScatterDestroy(&blocks->sctx));
38     PetscCall(ISDestroy(&blocks->is));
39     next   = blocks->next;
40     blocks = next;
41   }
42   PetscFunctionReturn(0);
43 }
44 
45 /*
46   SNESDestroy_Multiblock - Destroys the private SNES_Multiblock context that was created with SNESCreate_Multiblock().
47 
48   Input Parameter:
49 . snes - the SNES context
50 
51   Application Interface Routine: SNESDestroy()
52 */
53 PetscErrorCode SNESDestroy_Multiblock(SNES snes)
54 {
55   SNES_Multiblock *mb    = (SNES_Multiblock*) snes->data;
56   BlockDesc       blocks = mb->blocks, next;
57 
58   PetscFunctionBegin;
59   PetscCall(SNESReset_Multiblock(snes));
60   while (blocks) {
61     next   = blocks->next;
62     PetscCall(SNESDestroy(&blocks->snes));
63     PetscCall(PetscFree(blocks->name));
64     PetscCall(PetscFree(blocks->fields));
65     PetscCall(PetscFree(blocks));
66     blocks = next;
67   }
68   PetscCall(PetscFree(snes->data));
69   PetscFunctionReturn(0);
70 }
71 
72 /* Precondition: blocksize is set to a meaningful value */
73 static PetscErrorCode SNESMultiblockSetFieldsRuntime_Private(SNES snes)
74 {
75   SNES_Multiblock *mb = (SNES_Multiblock*) snes->data;
76   PetscInt        *ifields;
77   PetscInt        i, nfields;
78   PetscBool       flg = PETSC_TRUE;
79   char            optionname[128], name[8];
80 
81   PetscFunctionBegin;
82   PetscCall(PetscMalloc1(mb->bs, &ifields));
83   for (i = 0;; ++i) {
84     PetscCall(PetscSNPrintf(name, sizeof(name), "%" PetscInt_FMT, i));
85     PetscCall(PetscSNPrintf(optionname, sizeof(optionname), "-snes_multiblock_%" PetscInt_FMT "_fields", i));
86     nfields = mb->bs;
87     PetscCall(PetscOptionsGetIntArray(NULL,((PetscObject) snes)->prefix, optionname, ifields, &nfields, &flg));
88     if (!flg) break;
89     PetscCheck(nfields,PETSC_COMM_SELF, PETSC_ERR_USER, "Cannot list zero fields");
90     PetscCall(SNESMultiblockSetFields(snes, name, nfields, ifields));
91   }
92   if (i > 0) {
93     /* Makes command-line setting of blocks take precedence over setting them in code.
94        Otherwise subsequent calls to SNESMultiblockSetIS() or SNESMultiblockSetFields() would
95        create new blocks, which would probably not be what the user wanted. */
96     mb->defined = PETSC_TRUE;
97   }
98   PetscCall(PetscFree(ifields));
99   PetscFunctionReturn(0);
100 }
101 
102 static PetscErrorCode SNESMultiblockSetDefaults(SNES snes)
103 {
104   SNES_Multiblock *mb    = (SNES_Multiblock*) snes->data;
105   BlockDesc       blocks = mb->blocks;
106   PetscInt        i;
107 
108   PetscFunctionBegin;
109   if (!blocks) {
110     if (snes->dm) {
111       PetscBool dmcomposite;
112 
113       PetscCall(PetscObjectTypeCompare((PetscObject) snes->dm, DMCOMPOSITE, &dmcomposite));
114       if (dmcomposite) {
115         PetscInt nDM;
116         IS       *fields;
117 
118         PetscCall(PetscInfo(snes,"Setting up physics based multiblock solver using the embedded DM\n"));
119         PetscCall(DMCompositeGetNumberDM(snes->dm, &nDM));
120         PetscCall(DMCompositeGetGlobalISs(snes->dm, &fields));
121         for (i = 0; i < nDM; ++i) {
122           char name[8];
123 
124           PetscCall(PetscSNPrintf(name, sizeof(name), "%" PetscInt_FMT, i));
125           PetscCall(SNESMultiblockSetIS(snes, name, fields[i]));
126           PetscCall(ISDestroy(&fields[i]));
127         }
128         PetscCall(PetscFree(fields));
129       }
130     } else {
131       PetscBool flg    = PETSC_FALSE;
132       PetscBool stokes = PETSC_FALSE;
133 
134       if (mb->bs <= 0) {
135         if (snes->jacobian_pre) {
136           PetscCall(MatGetBlockSize(snes->jacobian_pre, &mb->bs));
137         } else mb->bs = 1;
138       }
139 
140       PetscCall(PetscOptionsGetBool(NULL,((PetscObject) snes)->prefix, "-snes_multiblock_default", &flg, NULL));
141       PetscCall(PetscOptionsGetBool(NULL,((PetscObject) snes)->prefix, "-snes_multiblock_detect_saddle_point", &stokes, NULL));
142       if (stokes) {
143         IS       zerodiags, rest;
144         PetscInt nmin, nmax;
145 
146         PetscCall(MatGetOwnershipRange(snes->jacobian_pre, &nmin, &nmax));
147         PetscCall(MatFindZeroDiagonals(snes->jacobian_pre, &zerodiags));
148         PetscCall(ISComplement(zerodiags, nmin, nmax, &rest));
149         PetscCall(SNESMultiblockSetIS(snes, "0", rest));
150         PetscCall(SNESMultiblockSetIS(snes, "1", zerodiags));
151         PetscCall(ISDestroy(&zerodiags));
152         PetscCall(ISDestroy(&rest));
153       } else {
154         if (!flg) {
155           /* Allow user to set fields from command line, if bs was known at the time of SNESSetFromOptions_Multiblock()
156            then it is set there. This is not ideal because we should only have options set in XXSetFromOptions(). */
157           PetscCall(SNESMultiblockSetFieldsRuntime_Private(snes));
158           if (mb->defined) PetscCall(PetscInfo(snes, "Blocks defined using the options database\n"));
159         }
160         if (flg || !mb->defined) {
161           PetscCall(PetscInfo(snes, "Using default splitting of fields\n"));
162           for (i = 0; i < mb->bs; ++i) {
163             char name[8];
164 
165             PetscCall(PetscSNPrintf(name, sizeof(name), "%" PetscInt_FMT, i));
166             PetscCall(SNESMultiblockSetFields(snes, name, 1, &i));
167           }
168           mb->defaultblocks = PETSC_TRUE;
169         }
170       }
171     }
172   } else if (mb->numBlocks == 1) {
173     if (blocks->is) {
174       IS       is2;
175       PetscInt nmin, nmax;
176 
177       PetscCall(MatGetOwnershipRange(snes->jacobian_pre, &nmin, &nmax));
178       PetscCall(ISComplement(blocks->is, nmin, nmax, &is2));
179       PetscCall(SNESMultiblockSetIS(snes, "1", is2));
180       PetscCall(ISDestroy(&is2));
181     } else SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONGSTATE, "Must provide at least two sets of fields to SNES multiblock");
182   }
183   PetscCheck(mb->numBlocks >= 2,PETSC_COMM_SELF, PETSC_ERR_PLIB, "Unhandled case, must have at least two blocks");
184   PetscFunctionReturn(0);
185 }
186 
187 /*
188    SNESSetUp_Multiblock - Sets up the internal data structures for the later use
189    of the SNESMULTIBLOCK nonlinear solver.
190 
191    Input Parameters:
192 +  snes - the SNES context
193 -  x - the solution vector
194 
195    Application Interface Routine: SNESSetUp()
196 */
197 PetscErrorCode SNESSetUp_Multiblock(SNES snes)
198 {
199   SNES_Multiblock *mb = (SNES_Multiblock*) snes->data;
200   BlockDesc       blocks;
201   PetscInt        i, numBlocks;
202 
203   PetscFunctionBegin;
204   PetscCall(SNESMultiblockSetDefaults(snes));
205   numBlocks = mb->numBlocks;
206   blocks    = mb->blocks;
207 
208   /* Create ISs */
209   if (!mb->issetup) {
210     PetscInt  ccsize, rstart, rend, nslots, bs;
211     PetscBool sorted;
212 
213     mb->issetup = PETSC_TRUE;
214     bs          = mb->bs;
215     PetscCall(MatGetOwnershipRange(snes->jacobian_pre, &rstart, &rend));
216     PetscCall(MatGetLocalSize(snes->jacobian_pre, NULL, &ccsize));
217     nslots      = (rend - rstart)/bs;
218     for (i = 0; i < numBlocks; ++i) {
219       if (mb->defaultblocks) {
220         PetscCall(ISCreateStride(PetscObjectComm((PetscObject)snes), nslots, rstart+i, numBlocks, &blocks->is));
221       } else if (!blocks->is) {
222         if (blocks->nfields > 1) {
223           PetscInt *ii, j, k, nfields = blocks->nfields, *fields = blocks->fields;
224 
225           PetscCall(PetscMalloc1(nfields*nslots, &ii));
226           for (j = 0; j < nslots; ++j) {
227             for (k = 0; k < nfields; ++k) {
228               ii[nfields*j + k] = rstart + bs*j + fields[k];
229             }
230           }
231           PetscCall(ISCreateGeneral(PetscObjectComm((PetscObject)snes), nslots*nfields, ii, PETSC_OWN_POINTER, &blocks->is));
232         } else {
233           PetscCall(ISCreateStride(PetscObjectComm((PetscObject)snes), nslots, rstart+blocks->fields[0], bs, &blocks->is));
234         }
235       }
236       PetscCall(ISSorted(blocks->is, &sorted));
237       PetscCheck(sorted,PETSC_COMM_SELF, PETSC_ERR_USER, "Fields must be sorted when creating split");
238       blocks = blocks->next;
239     }
240   }
241 
242 #if 0
243   /* Create matrices */
244   ilink = jac->head;
245   if (!jac->pmat) {
246     PetscCall(PetscMalloc1(nsplit,&jac->pmat));
247     for (i=0; i<nsplit; i++) {
248       PetscCall(MatCreateSubMatrix(pc->pmat,ilink->is,ilink->is,MAT_INITIAL_MATRIX,&jac->pmat[i]));
249       ilink = ilink->next;
250     }
251   } else {
252     for (i=0; i<nsplit; i++) {
253       PetscCall(MatCreateSubMatrix(pc->pmat,ilink->is,ilink->is,MAT_REUSE_MATRIX,&jac->pmat[i]));
254       ilink = ilink->next;
255     }
256   }
257   if (jac->realdiagonal) {
258     ilink = jac->head;
259     if (!jac->mat) {
260       PetscCall(PetscMalloc1(nsplit,&jac->mat));
261       for (i=0; i<nsplit; i++) {
262         PetscCall(MatCreateSubMatrix(pc->mat,ilink->is,ilink->is,MAT_INITIAL_MATRIX,&jac->mat[i]));
263         ilink = ilink->next;
264       }
265     } else {
266       for (i=0; i<nsplit; i++) {
267         if (jac->mat[i]) PetscCall(MatCreateSubMatrix(pc->mat,ilink->is,ilink->is,MAT_REUSE_MATRIX,&jac->mat[i]));
268         ilink = ilink->next;
269       }
270     }
271   } else jac->mat = jac->pmat;
272 #endif
273 
274 #if 0
275   if (jac->type != PC_COMPOSITE_ADDITIVE  && jac->type != PC_COMPOSITE_SCHUR) {
276     /* extract the rows of the matrix associated with each field: used for efficient computation of residual inside algorithm */
277     ilink = jac->head;
278     if (!jac->Afield) {
279       PetscCall(PetscMalloc1(nsplit,&jac->Afield));
280       for (i=0; i<nsplit; i++) {
281         PetscCall(MatCreateSubMatrix(pc->mat,ilink->is,NULL,MAT_INITIAL_MATRIX,&jac->Afield[i]));
282         ilink = ilink->next;
283       }
284     } else {
285       for (i=0; i<nsplit; i++) {
286         PetscCall(MatCreateSubMatrix(pc->mat,ilink->is,NULL,MAT_REUSE_MATRIX,&jac->Afield[i]));
287         ilink = ilink->next;
288       }
289     }
290   }
291 #endif
292 
293   if (mb->type == PC_COMPOSITE_SCHUR) {
294 #if 0
295     IS       ccis;
296     PetscInt rstart,rend;
297     PetscCheck(nsplit == 2,PetscObjectComm((PetscObject)pc),PETSC_ERR_ARG_INCOMP,"To use Schur complement preconditioner you must have exactly 2 fields");
298 
299     /* When extracting off-diagonal submatrices, we take complements from this range */
300     PetscCall(MatGetOwnershipRangeColumn(pc->mat,&rstart,&rend));
301 
302     /* need to handle case when one is resetting up the preconditioner */
303     if (jac->schur) {
304       ilink = jac->head;
305       PetscCall(ISComplement(ilink->is,rstart,rend,&ccis));
306       PetscCall(MatCreateSubMatrix(pc->mat,ilink->is,ccis,MAT_REUSE_MATRIX,&jac->B));
307       PetscCall(ISDestroy(&ccis));
308       ilink = ilink->next;
309       PetscCall(ISComplement(ilink->is,rstart,rend,&ccis));
310       PetscCall(MatCreateSubMatrix(pc->mat,ilink->is,ccis,MAT_REUSE_MATRIX,&jac->C));
311       PetscCall(ISDestroy(&ccis));
312       PetscCall(MatSchurComplementUpdateSubMatrices(jac->schur,jac->mat[0],jac->pmat[0],jac->B,jac->C,jac->pmat[1]));
313       PetscCall(KSPSetOperators(jac->kspschur,jac->schur,FieldSplitSchurPre(jac),pc->flag));
314 
315     } else {
316       KSP  ksp;
317       char schurprefix[256];
318 
319       /* extract the A01 and A10 matrices */
320       ilink = jac->head;
321       PetscCall(ISComplement(ilink->is,rstart,rend,&ccis));
322       PetscCall(MatCreateSubMatrix(pc->mat,ilink->is,ccis,MAT_INITIAL_MATRIX,&jac->B));
323       PetscCall(ISDestroy(&ccis));
324       ilink = ilink->next;
325       PetscCall(ISComplement(ilink->is,rstart,rend,&ccis));
326       PetscCall(MatCreateSubMatrix(pc->mat,ilink->is,ccis,MAT_INITIAL_MATRIX,&jac->C));
327       PetscCall(ISDestroy(&ccis));
328       /* Use mat[0] (diagonal block of the real matrix) preconditioned by pmat[0] */
329       PetscCall(MatCreateSchurComplement(jac->mat[0],jac->pmat[0],jac->B,jac->C,jac->mat[1],&jac->schur));
330       /* set tabbing and options prefix of KSP inside the MatSchur */
331       PetscCall(MatSchurComplementGetKSP(jac->schur,&ksp));
332       PetscCall(PetscObjectIncrementTabLevel((PetscObject)ksp,(PetscObject)pc,2));
333       PetscCall(PetscSNPrintf(schurprefix,sizeof(schurprefix),"%sfieldsplit_%s_",((PetscObject)pc)->prefix ? ((PetscObject)pc)->prefix : "",jac->head->splitname));
334       PetscCall(KSPSetOptionsPrefix(ksp,schurprefix));
335       PetscCall(MatSetFromOptions(jac->schur));
336 
337       PetscCall(KSPCreate(PetscObjectComm((PetscObject)pc),&jac->kspschur));
338       PetscCall(PetscLogObjectParent((PetscObject)pc,(PetscObject)jac->kspschur));
339       PetscCall(PetscObjectIncrementTabLevel((PetscObject)jac->kspschur,(PetscObject)pc,1));
340       PetscCall(KSPSetOperators(jac->kspschur,jac->schur,FieldSplitSchurPre(jac)));
341       if (jac->schurpre == PC_FIELDSPLIT_SCHUR_PRE_SELF) {
342         PC pc;
343         PetscCall(KSPGetPC(jac->kspschur,&pc));
344         PetscCall(PCSetType(pc,PCNONE));
345         /* Note: This is bad if there exist preconditioners for MATSCHURCOMPLEMENT */
346       }
347       PetscCall(PetscSNPrintf(schurprefix,sizeof(schurprefix),"%sfieldsplit_%s_",((PetscObject)pc)->prefix ? ((PetscObject)pc)->prefix : "",ilink->splitname));
348       PetscCall(KSPSetOptionsPrefix(jac->kspschur,schurprefix));
349       /* really want setfromoptions called in PCSetFromOptions_FieldSplit(), but it is not ready yet */
350       PetscCall(KSPSetFromOptions(jac->kspschur));
351 
352       PetscCall(PetscMalloc2(2,&jac->x,2,&jac->y));
353       PetscCall(MatCreateVecs(jac->pmat[0],&jac->x[0],&jac->y[0]));
354       PetscCall(MatCreateVecs(jac->pmat[1],&jac->x[1],&jac->y[1]));
355       ilink    = jac->head;
356       ilink->x = jac->x[0]; ilink->y = jac->y[0];
357       ilink    = ilink->next;
358       ilink->x = jac->x[1]; ilink->y = jac->y[1];
359     }
360 #endif
361   } else {
362     /* Set up the individual SNESs */
363     blocks = mb->blocks;
364     i      = 0;
365     while (blocks) {
366       /*TODO: Set these correctly */
367       /* PetscCall(SNESSetFunction(blocks->snes, blocks->x, func)); */
368       /* PetscCall(SNESSetJacobian(blocks->snes, blocks->x, jac)); */
369       PetscCall(VecDuplicate(blocks->snes->vec_sol, &blocks->x));
370       /* really want setfromoptions called in SNESSetFromOptions_Multiblock(), but it is not ready yet */
371       PetscCall(SNESSetFromOptions(blocks->snes));
372       PetscCall(SNESSetUp(blocks->snes));
373       blocks = blocks->next;
374       i++;
375     }
376   }
377 
378   /* Compute scatter contexts needed by multiplicative versions and non-default splits */
379   if (!mb->blocks->sctx) {
380     Vec xtmp;
381 
382     blocks = mb->blocks;
383     PetscCall(MatCreateVecs(snes->jacobian_pre, &xtmp, NULL));
384     while (blocks) {
385       PetscCall(VecScatterCreate(xtmp, blocks->is, blocks->x, NULL, &blocks->sctx));
386       blocks = blocks->next;
387     }
388     PetscCall(VecDestroy(&xtmp));
389   }
390   PetscFunctionReturn(0);
391 }
392 
393 /*
394   SNESSetFromOptions_Multiblock - Sets various parameters for the SNESMULTIBLOCK method.
395 
396   Input Parameter:
397 . snes - the SNES context
398 
399   Application Interface Routine: SNESSetFromOptions()
400 */
401 static PetscErrorCode SNESSetFromOptions_Multiblock(PetscOptionItems *PetscOptionsObject,SNES snes)
402 {
403   SNES_Multiblock *mb = (SNES_Multiblock*) snes->data;
404   PCCompositeType ctype;
405   PetscInt        bs;
406   PetscBool       flg;
407 
408   PetscFunctionBegin;
409   PetscOptionsHeadBegin(PetscOptionsObject,"SNES Multiblock options");
410   PetscCall(PetscOptionsInt("-snes_multiblock_block_size", "Blocksize that defines number of fields", "PCFieldSplitSetBlockSize", mb->bs, &bs, &flg));
411   if (flg) PetscCall(SNESMultiblockSetBlockSize(snes, bs));
412   PetscCall(PetscOptionsEnum("-snes_multiblock_type", "Type of composition", "PCFieldSplitSetType", PCCompositeTypes, (PetscEnum) mb->type, (PetscEnum*) &ctype, &flg));
413   if (flg) {
414     PetscCall(SNESMultiblockSetType(snes,ctype));
415   }
416   /* Only setup fields once */
417   if ((mb->bs > 0) && (mb->numBlocks == 0)) {
418     /* only allow user to set fields from command line if bs is already known, otherwise user can set them in SNESMultiblockSetDefaults() */
419     PetscCall(SNESMultiblockSetFieldsRuntime_Private(snes));
420     if (mb->defined) PetscCall(PetscInfo(snes, "Blocks defined using the options database\n"));
421   }
422   PetscOptionsHeadEnd();
423   PetscFunctionReturn(0);
424 }
425 
426 /*
427   SNESView_Multiblock - Prints info from the SNESMULTIBLOCK data structure.
428 
429   Input Parameters:
430 + SNES - the SNES context
431 - viewer - visualization context
432 
433   Application Interface Routine: SNESView()
434 */
435 static PetscErrorCode SNESView_Multiblock(SNES snes, PetscViewer viewer)
436 {
437   SNES_Multiblock *mb    = (SNES_Multiblock*) snes->data;
438   BlockDesc       blocks = mb->blocks;
439   PetscBool       iascii;
440 
441   PetscFunctionBegin;
442   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERASCII, &iascii));
443   if (iascii) {
444     PetscCall(PetscViewerASCIIPrintf(viewer,"  Multiblock with %s composition: total blocks = %" PetscInt_FMT ", blocksize = %" PetscInt_FMT "\n", PCCompositeTypes[mb->type], mb->numBlocks, mb->bs));
445     PetscCall(PetscViewerASCIIPrintf(viewer,"  Solver info for each split is in the following SNES objects:\n"));
446     PetscCall(PetscViewerASCIIPushTab(viewer));
447     while (blocks) {
448       if (blocks->fields) {
449         PetscInt j;
450 
451         PetscCall(PetscViewerASCIIPrintf(viewer, "  Block %s Fields ", blocks->name));
452         PetscCall(PetscViewerASCIIUseTabs(viewer, PETSC_FALSE));
453         for (j = 0; j < blocks->nfields; ++j) {
454           if (j > 0) {
455             PetscCall(PetscViewerASCIIPrintf(viewer, ","));
456           }
457           PetscCall(PetscViewerASCIIPrintf(viewer, " %" PetscInt_FMT, blocks->fields[j]));
458         }
459         PetscCall(PetscViewerASCIIPrintf(viewer, "\n"));
460         PetscCall(PetscViewerASCIIUseTabs(viewer, PETSC_TRUE));
461       } else {
462         PetscCall(PetscViewerASCIIPrintf(viewer, "  Block %s Defined by IS\n", blocks->name));
463       }
464       PetscCall(SNESView(blocks->snes, viewer));
465       blocks = blocks->next;
466     }
467     PetscCall(PetscViewerASCIIPopTab(viewer));
468   }
469   PetscFunctionReturn(0);
470 }
471 
472 /*
473   SNESSolve_Multiblock - Solves a nonlinear system with the Multiblock method.
474 
475   Input Parameters:
476 . snes - the SNES context
477 
478   Output Parameter:
479 . outits - number of iterations until termination
480 
481   Application Interface Routine: SNESSolve()
482 */
483 PetscErrorCode SNESSolve_Multiblock(SNES snes)
484 {
485   SNES_Multiblock *mb = (SNES_Multiblock*) snes->data;
486   Vec             X, Y, F;
487   PetscReal       fnorm;
488   PetscInt        maxits, i;
489 
490   PetscFunctionBegin;
491   PetscCheck(!snes->xl && !snes->xu && !snes->ops->computevariablebounds,PetscObjectComm((PetscObject)snes),PETSC_ERR_ARG_WRONGSTATE, "SNES solver %s does not support bounds", ((PetscObject)snes)->type_name);
492 
493   snes->reason = SNES_CONVERGED_ITERATING;
494 
495   maxits = snes->max_its;        /* maximum number of iterations */
496   X      = snes->vec_sol;        /* X^n */
497   Y      = snes->vec_sol_update; /* \tilde X */
498   F      = snes->vec_func;       /* residual vector */
499 
500   PetscCall(VecSetBlockSize(X, mb->bs));
501   PetscCall(VecSetBlockSize(Y, mb->bs));
502   PetscCall(VecSetBlockSize(F, mb->bs));
503   PetscCall(PetscObjectSAWsTakeAccess((PetscObject)snes));
504   snes->iter = 0;
505   snes->norm = 0.;
506   PetscCall(PetscObjectSAWsGrantAccess((PetscObject)snes));
507 
508   if (!snes->vec_func_init_set) {
509     PetscCall(SNESComputeFunction(snes, X, F));
510   } else snes->vec_func_init_set = PETSC_FALSE;
511 
512   PetscCall(VecNorm(F, NORM_2, &fnorm)); /* fnorm <- ||F||  */
513   SNESCheckFunctionNorm(snes,fnorm);
514   PetscCall(PetscObjectSAWsTakeAccess((PetscObject)snes));
515   snes->norm = fnorm;
516   PetscCall(PetscObjectSAWsGrantAccess((PetscObject)snes));
517   PetscCall(SNESLogConvergenceHistory(snes,fnorm,0));
518   PetscCall(SNESMonitor(snes,0,fnorm));
519 
520   /* test convergence */
521   PetscCall((*snes->ops->converged)(snes,0,0.0,0.0,fnorm,&snes->reason,snes->cnvP));
522   if (snes->reason) PetscFunctionReturn(0);
523 
524   for (i = 0; i < maxits; i++) {
525     /* Call general purpose update function */
526     if (snes->ops->update) {
527       PetscCall((*snes->ops->update)(snes, snes->iter));
528     }
529     /* Compute X^{new} from subsolves */
530     if (mb->type == PC_COMPOSITE_ADDITIVE) {
531       BlockDesc blocks = mb->blocks;
532 
533       if (mb->defaultblocks) {
534         /*TODO: Make an array of Vecs for this */
535         /* PetscCall(VecStrideGatherAll(X, mb->x, INSERT_VALUES)); */
536         while (blocks) {
537           PetscCall(SNESSolve(blocks->snes, NULL, blocks->x));
538           blocks = blocks->next;
539         }
540         /* PetscCall(VecStrideScatterAll(mb->x, X, INSERT_VALUES)); */
541       } else {
542         while (blocks) {
543           PetscCall(VecScatterBegin(blocks->sctx, X, blocks->x, INSERT_VALUES, SCATTER_FORWARD));
544           PetscCall(VecScatterEnd(blocks->sctx, X, blocks->x, INSERT_VALUES, SCATTER_FORWARD));
545           PetscCall(SNESSolve(blocks->snes, NULL, blocks->x));
546           PetscCall(VecScatterBegin(blocks->sctx, blocks->x, X, INSERT_VALUES, SCATTER_REVERSE));
547           PetscCall(VecScatterEnd(blocks->sctx, blocks->x, X, INSERT_VALUES, SCATTER_REVERSE));
548           blocks = blocks->next;
549         }
550       }
551     } else SETERRQ(PetscObjectComm((PetscObject)snes), PETSC_ERR_SUP, "Unsupported or unknown composition %d", (int) mb->type);
552     /* Compute F(X^{new}) */
553     PetscCall(SNESComputeFunction(snes, X, F));
554     PetscCall(VecNorm(F, NORM_2, &fnorm));
555     SNESCheckFunctionNorm(snes,fnorm);
556 
557     if (snes->nfuncs >= snes->max_funcs && snes->max_funcs >=0) {
558       snes->reason = SNES_DIVERGED_FUNCTION_COUNT;
559       break;
560     }
561 
562     /* Monitor convergence */
563     PetscCall(PetscObjectSAWsTakeAccess((PetscObject)snes));
564     snes->iter = i+1;
565     snes->norm = fnorm;
566     PetscCall(PetscObjectSAWsGrantAccess((PetscObject)snes));
567     PetscCall(SNESLogConvergenceHistory(snes,snes->norm,0));
568     PetscCall(SNESMonitor(snes,snes->iter,snes->norm));
569     /* Test for convergence */
570     PetscCall((*snes->ops->converged)(snes,snes->iter,0.0,0.0,fnorm,&snes->reason,snes->cnvP));
571     if (snes->reason) break;
572   }
573   if (i == maxits) {
574     PetscCall(PetscInfo(snes, "Maximum number of iterations has been reached: %" PetscInt_FMT "\n", maxits));
575     if (!snes->reason) snes->reason = SNES_DIVERGED_MAX_IT;
576   }
577   PetscFunctionReturn(0);
578 }
579 
580 PetscErrorCode SNESMultiblockSetFields_Default(SNES snes, const char name[], PetscInt n, const PetscInt fields[])
581 {
582   SNES_Multiblock *mb = (SNES_Multiblock*) snes->data;
583   BlockDesc       newblock, next = mb->blocks;
584   char            prefix[128];
585   PetscInt        i;
586 
587   PetscFunctionBegin;
588   if (mb->defined) {
589     PetscCall(PetscInfo(snes, "Ignoring new block \"%s\" because the blocks have already been defined\n", name));
590     PetscFunctionReturn(0);
591   }
592   for (i = 0; i < n; ++i) {
593     PetscCheck(fields[i] < mb->bs,PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Field %" PetscInt_FMT " requested but only %" PetscInt_FMT " exist", fields[i], mb->bs);
594     PetscCheck(fields[i] >= 0,PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Negative field %" PetscInt_FMT " requested", fields[i]);
595   }
596   PetscCall(PetscNew(&newblock));
597   if (name) {
598     PetscCall(PetscStrallocpy(name, &newblock->name));
599   } else {
600     PetscInt len = floor(log10(mb->numBlocks))+1;
601 
602     PetscCall(PetscMalloc1(len+1, &newblock->name));
603     PetscCall(PetscSNPrintf(newblock->name, len, "%" PetscInt_FMT, mb->numBlocks));
604   }
605   newblock->nfields = n;
606 
607   PetscCall(PetscMalloc1(n, &newblock->fields));
608   PetscCall(PetscArraycpy(newblock->fields, fields, n));
609 
610   newblock->next = NULL;
611 
612   PetscCall(SNESCreate(PetscObjectComm((PetscObject)snes), &newblock->snes));
613   PetscCall(PetscObjectIncrementTabLevel((PetscObject) newblock->snes, (PetscObject) snes, 1));
614   PetscCall(SNESSetType(newblock->snes, SNESNRICHARDSON));
615   PetscCall(PetscLogObjectParent((PetscObject) snes, (PetscObject) newblock->snes));
616   PetscCall(PetscSNPrintf(prefix, sizeof(prefix), "%smultiblock_%s_", ((PetscObject) snes)->prefix ? ((PetscObject) snes)->prefix : "", newblock->name));
617   PetscCall(SNESSetOptionsPrefix(newblock->snes, prefix));
618 
619   if (!next) {
620     mb->blocks         = newblock;
621     newblock->previous = NULL;
622   } else {
623     while (next->next) {
624       next = next->next;
625     }
626     next->next         = newblock;
627     newblock->previous = next;
628   }
629   mb->numBlocks++;
630   PetscFunctionReturn(0);
631 }
632 
633 PetscErrorCode SNESMultiblockSetIS_Default(SNES snes, const char name[], IS is)
634 {
635   SNES_Multiblock *mb = (SNES_Multiblock*) snes->data;
636   BlockDesc       newblock, next = mb->blocks;
637   char            prefix[128];
638 
639   PetscFunctionBegin;
640   if (mb->defined) {
641     PetscCall(PetscInfo(snes, "Ignoring new block \"%s\" because the blocks have already been defined\n", name));
642     PetscFunctionReturn(0);
643   }
644   PetscCall(PetscNew(&newblock));
645   if (name) {
646     PetscCall(PetscStrallocpy(name, &newblock->name));
647   } else {
648     PetscInt len = floor(log10(mb->numBlocks))+1;
649 
650     PetscCall(PetscMalloc1(len+1, &newblock->name));
651     PetscCall(PetscSNPrintf(newblock->name, len, "%" PetscInt_FMT, mb->numBlocks));
652   }
653   newblock->is = is;
654 
655   PetscCall(PetscObjectReference((PetscObject) is));
656 
657   newblock->next = NULL;
658 
659   PetscCall(SNESCreate(PetscObjectComm((PetscObject)snes), &newblock->snes));
660   PetscCall(PetscObjectIncrementTabLevel((PetscObject) newblock->snes, (PetscObject) snes, 1));
661   PetscCall(SNESSetType(newblock->snes, SNESNRICHARDSON));
662   PetscCall(PetscLogObjectParent((PetscObject) snes, (PetscObject) newblock->snes));
663   PetscCall(PetscSNPrintf(prefix, sizeof(prefix), "%smultiblock_%s_", ((PetscObject) snes)->prefix ? ((PetscObject) snes)->prefix : "", newblock->name));
664   PetscCall(SNESSetOptionsPrefix(newblock->snes, prefix));
665 
666   if (!next) {
667     mb->blocks         = newblock;
668     newblock->previous = NULL;
669   } else {
670     while (next->next) {
671       next = next->next;
672     }
673     next->next         = newblock;
674     newblock->previous = next;
675   }
676   mb->numBlocks++;
677   PetscFunctionReturn(0);
678 }
679 
680 PetscErrorCode  SNESMultiblockSetBlockSize_Default(SNES snes, PetscInt bs)
681 {
682   SNES_Multiblock *mb = (SNES_Multiblock*) snes->data;
683 
684   PetscFunctionBegin;
685   PetscCheck(bs >= 1,PetscObjectComm((PetscObject)snes), PETSC_ERR_ARG_OUTOFRANGE, "Blocksize must be positive, you gave %" PetscInt_FMT, bs);
686   PetscCheck(mb->bs <= 0 || mb->bs == bs,PetscObjectComm((PetscObject)snes), PETSC_ERR_ARG_WRONGSTATE, "Cannot change blocksize from %" PetscInt_FMT " to %" PetscInt_FMT " after it has been set", mb->bs, bs);
687   mb->bs = bs;
688   PetscFunctionReturn(0);
689 }
690 
691 PetscErrorCode SNESMultiblockGetSubSNES_Default(SNES snes, PetscInt *n, SNES **subsnes)
692 {
693   SNES_Multiblock *mb    = (SNES_Multiblock*) snes->data;
694   BlockDesc       blocks = mb->blocks;
695   PetscInt        cnt    = 0;
696 
697   PetscFunctionBegin;
698   PetscCall(PetscMalloc1(mb->numBlocks, subsnes));
699   while (blocks) {
700     (*subsnes)[cnt++] = blocks->snes;
701     blocks            = blocks->next;
702   }
703   PetscCheck(cnt == mb->numBlocks,PETSC_COMM_SELF, PETSC_ERR_PLIB, "Corrupt SNESMULTIBLOCK object: number of blocks in linked list %" PetscInt_FMT " does not match number in object %" PetscInt_FMT, cnt, mb->numBlocks);
704 
705   if (n) *n = mb->numBlocks;
706   PetscFunctionReturn(0);
707 }
708 
709 PetscErrorCode  SNESMultiblockSetType_Default(SNES snes, PCCompositeType type)
710 {
711   SNES_Multiblock *mb = (SNES_Multiblock*) snes->data;
712 
713   PetscFunctionBegin;
714   mb->type = type;
715   if (type == PC_COMPOSITE_SCHUR) {
716 #if 1
717     SETERRQ(PetscObjectComm((PetscObject)snes), PETSC_ERR_SUP, "The Schur composite type is not yet supported");
718 #else
719     snes->ops->solve = SNESSolve_Multiblock_Schur;
720     snes->ops->view  = SNESView_Multiblock_Schur;
721 
722     PetscCall(PetscObjectComposeFunction((PetscObject) snes, "SNESMultiblockGetSubSNES_C", SNESMultiblockGetSubSNES_Schur));
723     PetscCall(PetscObjectComposeFunction((PetscObject) snes, "SNESMultiblockSchurPrecondition_C", SNESMultiblockSchurPrecondition_Default));
724 #endif
725   } else {
726     snes->ops->solve = SNESSolve_Multiblock;
727     snes->ops->view  = SNESView_Multiblock;
728 
729     PetscCall(PetscObjectComposeFunction((PetscObject) snes, "SNESMultiblockGetSubSNES_C", SNESMultiblockGetSubSNES_Default));
730     PetscCall(PetscObjectComposeFunction((PetscObject) snes, "SNESMultiblockSchurPrecondition_C", 0));
731   }
732   PetscFunctionReturn(0);
733 }
734 
735 /*@
736   SNESMultiblockSetFields - Sets the fields for one particular block in the solver
737 
738   Logically Collective on SNES
739 
740   Input Parameters:
741 + snes   - the solver
742 . name   - name of this block, if NULL the number of the block is used
743 . n      - the number of fields in this block
744 - fields - the fields in this block
745 
746   Level: intermediate
747 
748   Notes:
749     Use SNESMultiblockSetIS() to set a completely general set of row indices as a block.
750 
751   The SNESMultiblockSetFields() is for defining blocks as a group of strided indices, or fields.
752   For example, if the vector block size is three then one can define a block as field 0, or
753   1 or 2, or field 0,1 or 0,2 or 1,2 which means
754     0xx3xx6xx9xx12 ... x1xx4xx7xx ... xx2xx5xx8xx.. 01x34x67x... 0x1x3x5x7.. x12x45x78x....
755   where the numbered entries indicate what is in the block.
756 
757   This function is called once per block (it creates a new block each time). Solve options
758   for this block will be available under the prefix -multiblock_BLOCKNAME_.
759 
760 .seealso: `SNESMultiblockGetSubSNES()`, `SNESMULTIBLOCK`, `SNESMultiblockSetBlockSize()`, `SNESMultiblockSetIS()`
761 @*/
762 PetscErrorCode SNESMultiblockSetFields(SNES snes, const char name[], PetscInt n, const PetscInt *fields)
763 {
764   PetscFunctionBegin;
765   PetscValidHeaderSpecific(snes, SNES_CLASSID, 1);
766   PetscValidCharPointer(name, 2);
767   PetscCheck(n >= 1,PetscObjectComm((PetscObject)snes), PETSC_ERR_ARG_OUTOFRANGE, "Provided number of fields %" PetscInt_FMT " in split \"%s\" not positive", n, name);
768   PetscValidIntPointer(fields, 4);
769   PetscTryMethod(snes, "SNESMultiblockSetFields_C", (SNES, const char[], PetscInt, const PetscInt*), (snes, name, n, fields));
770   PetscFunctionReturn(0);
771 }
772 
773 /*@
774   SNESMultiblockSetIS - Sets the global row indices for the block
775 
776   Logically Collective on SNES
777 
778   Input Parameters:
779 + snes - the solver context
780 . name - name of this block, if NULL the number of the block is used
781 - is   - the index set that defines the global row indices in this block
782 
783   Notes:
784   Use SNESMultiblockSetFields(), for blocks defined by strides.
785 
786   This function is called once per block (it creates a new block each time). Solve options
787   for this block will be available under the prefix -multiblock_BLOCKNAME_.
788 
789   Level: intermediate
790 
791 .seealso: `SNESMultiblockGetSubSNES()`, `SNESMULTIBLOCK`, `SNESMultiblockSetBlockSize()`
792 @*/
793 PetscErrorCode SNESMultiblockSetIS(SNES snes, const char name[], IS is)
794 {
795   PetscFunctionBegin;
796   PetscValidHeaderSpecific(snes, SNES_CLASSID, 1);
797   PetscValidCharPointer(name, 2);
798   PetscValidHeaderSpecific(is, IS_CLASSID, 3);
799   PetscTryMethod(snes, "SNESMultiblockSetIS_C", (SNES, const char[], IS), (snes, name, is));
800   PetscFunctionReturn(0);
801 }
802 
803 /*@
804   SNESMultiblockSetType - Sets the type of block combination.
805 
806   Collective on SNES
807 
808   Input Parameters:
809 + snes - the solver context
810 - type - PC_COMPOSITE_ADDITIVE, PC_COMPOSITE_MULTIPLICATIVE (default), PC_COMPOSITE_SYMMETRIC_MULTIPLICATIVE
811 
812   Options Database Key:
813 . -snes_multiblock_type <type: one of multiplicative, additive, symmetric_multiplicative> - Sets block combination type
814 
815   Level: Developer
816 
817 .seealso: `PCCompositeSetType()`
818 @*/
819 PetscErrorCode SNESMultiblockSetType(SNES snes, PCCompositeType type)
820 {
821   PetscFunctionBegin;
822   PetscValidHeaderSpecific(snes, SNES_CLASSID, 1);
823   PetscTryMethod(snes, "SNESMultiblockSetType_C", (SNES, PCCompositeType), (snes, type));
824   PetscFunctionReturn(0);
825 }
826 
827 /*@
828   SNESMultiblockSetBlockSize - Sets the block size for structured mesh block division. If not set the matrix block size is used.
829 
830   Logically Collective on SNES
831 
832   Input Parameters:
833 + snes - the solver context
834 - bs   - the block size
835 
836   Level: intermediate
837 
838 .seealso: `SNESMultiblockGetSubSNES()`, `SNESMULTIBLOCK`, `SNESMultiblockSetFields()`
839 @*/
840 PetscErrorCode SNESMultiblockSetBlockSize(SNES snes, PetscInt bs)
841 {
842   PetscFunctionBegin;
843   PetscValidHeaderSpecific(snes, SNES_CLASSID, 1);
844   PetscValidLogicalCollectiveInt(snes, bs, 2);
845   PetscTryMethod(snes, "SNESMultiblockSetBlockSize_C", (SNES, PetscInt), (snes,bs));
846   PetscFunctionReturn(0);
847 }
848 
849 /*@C
850   SNESMultiblockGetSubSNES - Gets the SNES contexts for all blocks
851 
852   Collective on SNES
853 
854   Input Parameter:
855 . snes - the solver context
856 
857   Output Parameters:
858 + n       - the number of blocks
859 - subsnes - the array of SNES contexts
860 
861   Note:
862   After SNESMultiblockGetSubSNES() the array of SNESs MUST be freed by the user
863   (not each SNES, just the array that contains them).
864 
865   You must call SNESSetUp() before calling SNESMultiblockGetSubSNES().
866 
867   Level: advanced
868 
869 .seealso: `SNESMULTIBLOCK`
870 @*/
871 PetscErrorCode SNESMultiblockGetSubSNES(SNES snes, PetscInt *n, SNES *subsnes[])
872 {
873   PetscFunctionBegin;
874   PetscValidHeaderSpecific(snes, SNES_CLASSID, 1);
875   if (n) PetscValidIntPointer(n, 2);
876   PetscUseMethod(snes, "SNESMultiblockGetSubSNES_C", (SNES, PetscInt*, SNES **), (snes, n, subsnes));
877   PetscFunctionReturn(0);
878 }
879 
880 /*MC
881   SNESMULTIBLOCK - Multiblock nonlinear solver that can use overlapping or nonoverlapping blocks, organized
882   additively (Jacobi) or multiplicatively (Gauss-Seidel).
883 
884   Level: beginner
885 
886 .seealso: `SNESCreate()`, `SNES`, `SNESSetType()`, `SNESNEWTONLS`, `SNESNEWTONTR`, `SNESNRICHARDSON`
887 M*/
888 PETSC_EXTERN PetscErrorCode SNESCreate_Multiblock(SNES snes)
889 {
890   SNES_Multiblock *mb;
891 
892   PetscFunctionBegin;
893   snes->ops->destroy        = SNESDestroy_Multiblock;
894   snes->ops->setup          = SNESSetUp_Multiblock;
895   snes->ops->setfromoptions = SNESSetFromOptions_Multiblock;
896   snes->ops->view           = SNESView_Multiblock;
897   snes->ops->solve          = SNESSolve_Multiblock;
898   snes->ops->reset          = SNESReset_Multiblock;
899 
900   snes->usesksp = PETSC_FALSE;
901   snes->usesnpc = PETSC_FALSE;
902 
903   snes->alwayscomputesfinalresidual = PETSC_TRUE;
904 
905   PetscCall(PetscNewLog(snes,&mb));
906   snes->data    = (void*) mb;
907   mb->defined   = PETSC_FALSE;
908   mb->numBlocks = 0;
909   mb->bs        = -1;
910   mb->type      = PC_COMPOSITE_MULTIPLICATIVE;
911 
912   /* We attach functions so that they can be called on another PC without crashing the program */
913   PetscCall(PetscObjectComposeFunction((PetscObject) snes, "SNESMultiblockSetFields_C",    SNESMultiblockSetFields_Default));
914   PetscCall(PetscObjectComposeFunction((PetscObject) snes, "SNESMultiblockSetIS_C",        SNESMultiblockSetIS_Default));
915   PetscCall(PetscObjectComposeFunction((PetscObject) snes, "SNESMultiblockSetType_C",      SNESMultiblockSetType_Default));
916   PetscCall(PetscObjectComposeFunction((PetscObject) snes, "SNESMultiblockSetBlockSize_C", SNESMultiblockSetBlockSize_Default));
917   PetscCall(PetscObjectComposeFunction((PetscObject) snes, "SNESMultiblockGetSubSNES_C",   SNESMultiblockGetSubSNES_Default));
918   PetscFunctionReturn(0);
919 }
920