xref: /petsc/src/snes/impls/multiblock/multiblock.c (revision fd599e2400177e0a7827d61c4aefd751aa52166c)
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(PETSC_SUCCESS);
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(PETSC_SUCCESS);
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(PETSC_SUCCESS);
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(PETSC_SUCCESS);
185 }
186 
187 PetscErrorCode SNESSetUp_Multiblock(SNES snes)
188 {
189   SNES_Multiblock *mb = (SNES_Multiblock *)snes->data;
190   BlockDesc        blocks;
191   PetscInt         i, numBlocks;
192 
193   PetscFunctionBegin;
194   PetscCall(SNESMultiblockSetDefaults(snes));
195   numBlocks = mb->numBlocks;
196   blocks    = mb->blocks;
197 
198   /* Create ISs */
199   if (!mb->issetup) {
200     PetscInt  ccsize, rstart, rend, nslots, bs;
201     PetscBool sorted;
202 
203     mb->issetup = PETSC_TRUE;
204     bs          = mb->bs;
205     PetscCall(MatGetOwnershipRange(snes->jacobian_pre, &rstart, &rend));
206     PetscCall(MatGetLocalSize(snes->jacobian_pre, NULL, &ccsize));
207     nslots = (rend - rstart) / bs;
208     for (i = 0; i < numBlocks; ++i) {
209       if (mb->defaultblocks) {
210         PetscCall(ISCreateStride(PetscObjectComm((PetscObject)snes), nslots, rstart + i, numBlocks, &blocks->is));
211       } else if (!blocks->is) {
212         if (blocks->nfields > 1) {
213           PetscInt *ii, j, k, nfields = blocks->nfields, *fields = blocks->fields;
214 
215           PetscCall(PetscMalloc1(nfields * nslots, &ii));
216           for (j = 0; j < nslots; ++j) {
217             for (k = 0; k < nfields; ++k) ii[nfields * j + k] = rstart + bs * j + fields[k];
218           }
219           PetscCall(ISCreateGeneral(PetscObjectComm((PetscObject)snes), nslots * nfields, ii, PETSC_OWN_POINTER, &blocks->is));
220         } else {
221           PetscCall(ISCreateStride(PetscObjectComm((PetscObject)snes), nslots, rstart + blocks->fields[0], bs, &blocks->is));
222         }
223       }
224       PetscCall(ISSorted(blocks->is, &sorted));
225       PetscCheck(sorted, PETSC_COMM_SELF, PETSC_ERR_USER, "Fields must be sorted when creating split");
226       blocks = blocks->next;
227     }
228   }
229 
230 #if 0
231   /* Create matrices */
232   ilink = jac->head;
233   if (!jac->pmat) {
234     PetscCall(PetscMalloc1(nsplit,&jac->pmat));
235     for (i=0; i<nsplit; i++) {
236       PetscCall(MatCreateSubMatrix(pc->pmat,ilink->is,ilink->is,MAT_INITIAL_MATRIX,&jac->pmat[i]));
237       ilink = ilink->next;
238     }
239   } else {
240     for (i=0; i<nsplit; i++) {
241       PetscCall(MatCreateSubMatrix(pc->pmat,ilink->is,ilink->is,MAT_REUSE_MATRIX,&jac->pmat[i]));
242       ilink = ilink->next;
243     }
244   }
245   if (jac->realdiagonal) {
246     ilink = jac->head;
247     if (!jac->mat) {
248       PetscCall(PetscMalloc1(nsplit,&jac->mat));
249       for (i=0; i<nsplit; i++) {
250         PetscCall(MatCreateSubMatrix(pc->mat,ilink->is,ilink->is,MAT_INITIAL_MATRIX,&jac->mat[i]));
251         ilink = ilink->next;
252       }
253     } else {
254       for (i=0; i<nsplit; i++) {
255         if (jac->mat[i]) PetscCall(MatCreateSubMatrix(pc->mat,ilink->is,ilink->is,MAT_REUSE_MATRIX,&jac->mat[i]));
256         ilink = ilink->next;
257       }
258     }
259   } else jac->mat = jac->pmat;
260 #endif
261 
262 #if 0
263   if (jac->type != PC_COMPOSITE_ADDITIVE  && jac->type != PC_COMPOSITE_SCHUR) {
264     /* extract the rows of the matrix associated with each field: used for efficient computation of residual inside algorithm */
265     ilink = jac->head;
266     if (!jac->Afield) {
267       PetscCall(PetscMalloc1(nsplit,&jac->Afield));
268       for (i=0; i<nsplit; i++) {
269         PetscCall(MatCreateSubMatrix(pc->mat,ilink->is,NULL,MAT_INITIAL_MATRIX,&jac->Afield[i]));
270         ilink = ilink->next;
271       }
272     } else {
273       for (i=0; i<nsplit; i++) {
274         PetscCall(MatCreateSubMatrix(pc->mat,ilink->is,NULL,MAT_REUSE_MATRIX,&jac->Afield[i]));
275         ilink = ilink->next;
276       }
277     }
278   }
279 #endif
280 
281   if (mb->type == PC_COMPOSITE_SCHUR) {
282 #if 0
283     IS       ccis;
284     PetscInt rstart,rend;
285     PetscCheck(nsplit == 2,PetscObjectComm((PetscObject)pc),PETSC_ERR_ARG_INCOMP,"To use Schur complement preconditioner you must have exactly 2 fields");
286 
287     /* When extracting off-diagonal submatrices, we take complements from this range */
288     PetscCall(MatGetOwnershipRangeColumn(pc->mat,&rstart,&rend));
289 
290     /* need to handle case when one is resetting up the preconditioner */
291     if (jac->schur) {
292       ilink = jac->head;
293       PetscCall(ISComplement(ilink->is,rstart,rend,&ccis));
294       PetscCall(MatCreateSubMatrix(pc->mat,ilink->is,ccis,MAT_REUSE_MATRIX,&jac->B));
295       PetscCall(ISDestroy(&ccis));
296       ilink = ilink->next;
297       PetscCall(ISComplement(ilink->is,rstart,rend,&ccis));
298       PetscCall(MatCreateSubMatrix(pc->mat,ilink->is,ccis,MAT_REUSE_MATRIX,&jac->C));
299       PetscCall(ISDestroy(&ccis));
300       PetscCall(MatSchurComplementUpdateSubMatrices(jac->schur,jac->mat[0],jac->pmat[0],jac->B,jac->C,jac->pmat[1]));
301       PetscCall(KSPSetOperators(jac->kspschur,jac->schur,FieldSplitSchurPre(jac),pc->flag));
302 
303     } else {
304       KSP  ksp;
305       char schurprefix[256];
306 
307       /* extract the A01 and A10 matrices */
308       ilink = jac->head;
309       PetscCall(ISComplement(ilink->is,rstart,rend,&ccis));
310       PetscCall(MatCreateSubMatrix(pc->mat,ilink->is,ccis,MAT_INITIAL_MATRIX,&jac->B));
311       PetscCall(ISDestroy(&ccis));
312       ilink = ilink->next;
313       PetscCall(ISComplement(ilink->is,rstart,rend,&ccis));
314       PetscCall(MatCreateSubMatrix(pc->mat,ilink->is,ccis,MAT_INITIAL_MATRIX,&jac->C));
315       PetscCall(ISDestroy(&ccis));
316       /* Use mat[0] (diagonal block of the real matrix) preconditioned by pmat[0] */
317       PetscCall(MatCreateSchurComplement(jac->mat[0],jac->pmat[0],jac->B,jac->C,jac->mat[1],&jac->schur));
318       /* set tabbing and options prefix of KSP inside the MatSchur */
319       PetscCall(MatSchurComplementGetKSP(jac->schur,&ksp));
320       PetscCall(PetscObjectIncrementTabLevel((PetscObject)ksp,(PetscObject)pc,2));
321       PetscCall(PetscSNPrintf(schurprefix,sizeof(schurprefix),"%sfieldsplit_%s_",((PetscObject)pc)->prefix ? ((PetscObject)pc)->prefix : "",jac->head->splitname));
322       PetscCall(KSPSetOptionsPrefix(ksp,schurprefix));
323       PetscCall(MatSetFromOptions(jac->schur));
324 
325       PetscCall(KSPCreate(PetscObjectComm((PetscObject)pc),&jac->kspschur));
326       PetscCall(PetscObjectIncrementTabLevel((PetscObject)jac->kspschur,(PetscObject)pc,1));
327       PetscCall(KSPSetOperators(jac->kspschur,jac->schur,FieldSplitSchurPre(jac)));
328       if (jac->schurpre == PC_FIELDSPLIT_SCHUR_PRE_SELF) {
329         PC pc;
330         PetscCall(KSPGetPC(jac->kspschur,&pc));
331         PetscCall(PCSetType(pc,PCNONE));
332         /* Note: This is bad if there exist preconditioners for MATSCHURCOMPLEMENT */
333       }
334       PetscCall(PetscSNPrintf(schurprefix,sizeof(schurprefix),"%sfieldsplit_%s_",((PetscObject)pc)->prefix ? ((PetscObject)pc)->prefix : "",ilink->splitname));
335       PetscCall(KSPSetOptionsPrefix(jac->kspschur,schurprefix));
336       /* really want setfromoptions called in PCSetFromOptions_FieldSplit(), but it is not ready yet */
337       PetscCall(KSPSetFromOptions(jac->kspschur));
338 
339       PetscCall(PetscMalloc2(2,&jac->x,2,&jac->y));
340       PetscCall(MatCreateVecs(jac->pmat[0],&jac->x[0],&jac->y[0]));
341       PetscCall(MatCreateVecs(jac->pmat[1],&jac->x[1],&jac->y[1]));
342       ilink    = jac->head;
343       ilink->x = jac->x[0]; ilink->y = jac->y[0];
344       ilink    = ilink->next;
345       ilink->x = jac->x[1]; ilink->y = jac->y[1];
346     }
347 #endif
348   } else {
349     /* Set up the individual SNESs */
350     blocks = mb->blocks;
351     i      = 0;
352     while (blocks) {
353       /*TODO: Set these correctly */
354       /* PetscCall(SNESSetFunction(blocks->snes, blocks->x, func)); */
355       /* PetscCall(SNESSetJacobian(blocks->snes, blocks->x, jac)); */
356       PetscCall(VecDuplicate(blocks->snes->vec_sol, &blocks->x));
357       /* really want setfromoptions called in SNESSetFromOptions_Multiblock(), but it is not ready yet */
358       PetscCall(SNESSetFromOptions(blocks->snes));
359       PetscCall(SNESSetUp(blocks->snes));
360       blocks = blocks->next;
361       i++;
362     }
363   }
364 
365   /* Compute scatter contexts needed by multiplicative versions and non-default splits */
366   if (!mb->blocks->sctx) {
367     Vec xtmp;
368 
369     blocks = mb->blocks;
370     PetscCall(MatCreateVecs(snes->jacobian_pre, &xtmp, NULL));
371     while (blocks) {
372       PetscCall(VecScatterCreate(xtmp, blocks->is, blocks->x, NULL, &blocks->sctx));
373       blocks = blocks->next;
374     }
375     PetscCall(VecDestroy(&xtmp));
376   }
377   PetscFunctionReturn(PETSC_SUCCESS);
378 }
379 
380 /*
381   SNESSetFromOptions_Multiblock - Sets various parameters for the SNESMULTIBLOCK method.
382 
383   Input Parameter:
384 . snes - the SNES context
385 
386   Application Interface Routine: SNESSetFromOptions()
387 */
388 static PetscErrorCode SNESSetFromOptions_Multiblock(SNES snes, PetscOptionItems *PetscOptionsObject)
389 {
390   SNES_Multiblock *mb = (SNES_Multiblock *)snes->data;
391   PCCompositeType  ctype;
392   PetscInt         bs;
393   PetscBool        flg;
394 
395   PetscFunctionBegin;
396   PetscOptionsHeadBegin(PetscOptionsObject, "SNES Multiblock options");
397   PetscCall(PetscOptionsInt("-snes_multiblock_block_size", "Blocksize that defines number of fields", "PCFieldSplitSetBlockSize", mb->bs, &bs, &flg));
398   if (flg) PetscCall(SNESMultiblockSetBlockSize(snes, bs));
399   PetscCall(PetscOptionsEnum("-snes_multiblock_type", "Type of composition", "PCFieldSplitSetType", PCCompositeTypes, (PetscEnum)mb->type, (PetscEnum *)&ctype, &flg));
400   if (flg) PetscCall(SNESMultiblockSetType(snes, ctype));
401   /* Only setup fields once */
402   if ((mb->bs > 0) && (mb->numBlocks == 0)) {
403     /* only allow user to set fields from command line if bs is already known, otherwise user can set them in SNESMultiblockSetDefaults() */
404     PetscCall(SNESMultiblockSetFieldsRuntime_Private(snes));
405     if (mb->defined) PetscCall(PetscInfo(snes, "Blocks defined using the options database\n"));
406   }
407   PetscOptionsHeadEnd();
408   PetscFunctionReturn(PETSC_SUCCESS);
409 }
410 
411 static PetscErrorCode SNESView_Multiblock(SNES snes, PetscViewer viewer)
412 {
413   SNES_Multiblock *mb     = (SNES_Multiblock *)snes->data;
414   BlockDesc        blocks = mb->blocks;
415   PetscBool        iascii;
416 
417   PetscFunctionBegin;
418   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERASCII, &iascii));
419   if (iascii) {
420     PetscCall(PetscViewerASCIIPrintf(viewer, "  Multiblock with %s composition: total blocks = %" PetscInt_FMT ", blocksize = %" PetscInt_FMT "\n", PCCompositeTypes[mb->type], mb->numBlocks, mb->bs));
421     PetscCall(PetscViewerASCIIPrintf(viewer, "  Solver info for each split is in the following SNES objects:\n"));
422     PetscCall(PetscViewerASCIIPushTab(viewer));
423     while (blocks) {
424       if (blocks->fields) {
425         PetscInt j;
426 
427         PetscCall(PetscViewerASCIIPrintf(viewer, "  Block %s Fields ", blocks->name));
428         PetscCall(PetscViewerASCIIUseTabs(viewer, PETSC_FALSE));
429         for (j = 0; j < blocks->nfields; ++j) {
430           if (j > 0) PetscCall(PetscViewerASCIIPrintf(viewer, ","));
431           PetscCall(PetscViewerASCIIPrintf(viewer, " %" PetscInt_FMT, blocks->fields[j]));
432         }
433         PetscCall(PetscViewerASCIIPrintf(viewer, "\n"));
434         PetscCall(PetscViewerASCIIUseTabs(viewer, PETSC_TRUE));
435       } else {
436         PetscCall(PetscViewerASCIIPrintf(viewer, "  Block %s Defined by IS\n", blocks->name));
437       }
438       PetscCall(SNESView(blocks->snes, viewer));
439       blocks = blocks->next;
440     }
441     PetscCall(PetscViewerASCIIPopTab(viewer));
442   }
443   PetscFunctionReturn(PETSC_SUCCESS);
444 }
445 
446 PetscErrorCode SNESSolve_Multiblock(SNES snes)
447 {
448   SNES_Multiblock *mb = (SNES_Multiblock *)snes->data;
449   Vec              X, Y, F;
450   PetscReal        fnorm;
451   PetscInt         maxits, i;
452 
453   PetscFunctionBegin;
454   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);
455 
456   snes->reason = SNES_CONVERGED_ITERATING;
457 
458   maxits = snes->max_its;        /* maximum number of iterations */
459   X      = snes->vec_sol;        /* X^n */
460   Y      = snes->vec_sol_update; /* \tilde X */
461   F      = snes->vec_func;       /* residual vector */
462 
463   PetscCall(VecSetBlockSize(X, mb->bs));
464   PetscCall(VecSetBlockSize(Y, mb->bs));
465   PetscCall(VecSetBlockSize(F, mb->bs));
466   PetscCall(PetscObjectSAWsTakeAccess((PetscObject)snes));
467   snes->iter = 0;
468   snes->norm = 0.;
469   PetscCall(PetscObjectSAWsGrantAccess((PetscObject)snes));
470 
471   if (!snes->vec_func_init_set) {
472     PetscCall(SNESComputeFunction(snes, X, F));
473   } else snes->vec_func_init_set = PETSC_FALSE;
474 
475   PetscCall(VecNorm(F, NORM_2, &fnorm)); /* fnorm <- ||F||  */
476   SNESCheckFunctionNorm(snes, fnorm);
477   PetscCall(PetscObjectSAWsTakeAccess((PetscObject)snes));
478   snes->norm = fnorm;
479   PetscCall(PetscObjectSAWsGrantAccess((PetscObject)snes));
480   PetscCall(SNESLogConvergenceHistory(snes, fnorm, 0));
481   PetscCall(SNESMonitor(snes, 0, fnorm));
482 
483   /* test convergence */
484   PetscUseTypeMethod(snes, converged, 0, 0.0, 0.0, fnorm, &snes->reason, snes->cnvP);
485   if (snes->reason) PetscFunctionReturn(PETSC_SUCCESS);
486 
487   for (i = 0; i < maxits; i++) {
488     /* Call general purpose update function */
489     PetscTryTypeMethod(snes, update, snes->iter);
490     /* Compute X^{new} from subsolves */
491     if (mb->type == PC_COMPOSITE_ADDITIVE) {
492       BlockDesc blocks = mb->blocks;
493 
494       if (mb->defaultblocks) {
495         /*TODO: Make an array of Vecs for this */
496         /* PetscCall(VecStrideGatherAll(X, mb->x, INSERT_VALUES)); */
497         while (blocks) {
498           PetscCall(SNESSolve(blocks->snes, NULL, blocks->x));
499           blocks = blocks->next;
500         }
501         /* PetscCall(VecStrideScatterAll(mb->x, X, INSERT_VALUES)); */
502       } else {
503         while (blocks) {
504           PetscCall(VecScatterBegin(blocks->sctx, X, blocks->x, INSERT_VALUES, SCATTER_FORWARD));
505           PetscCall(VecScatterEnd(blocks->sctx, X, blocks->x, INSERT_VALUES, SCATTER_FORWARD));
506           PetscCall(SNESSolve(blocks->snes, NULL, blocks->x));
507           PetscCall(VecScatterBegin(blocks->sctx, blocks->x, X, INSERT_VALUES, SCATTER_REVERSE));
508           PetscCall(VecScatterEnd(blocks->sctx, blocks->x, X, INSERT_VALUES, SCATTER_REVERSE));
509           blocks = blocks->next;
510         }
511       }
512     } else SETERRQ(PetscObjectComm((PetscObject)snes), PETSC_ERR_SUP, "Unsupported or unknown composition %d", (int)mb->type);
513     /* Compute F(X^{new}) */
514     PetscCall(SNESComputeFunction(snes, X, F));
515     PetscCall(VecNorm(F, NORM_2, &fnorm));
516     SNESCheckFunctionNorm(snes, fnorm);
517 
518     if (snes->nfuncs >= snes->max_funcs && snes->max_funcs >= 0) {
519       snes->reason = SNES_DIVERGED_FUNCTION_COUNT;
520       break;
521     }
522 
523     /* Monitor convergence */
524     PetscCall(PetscObjectSAWsTakeAccess((PetscObject)snes));
525     snes->iter = i + 1;
526     snes->norm = fnorm;
527     PetscCall(PetscObjectSAWsGrantAccess((PetscObject)snes));
528     PetscCall(SNESLogConvergenceHistory(snes, snes->norm, 0));
529     PetscCall(SNESMonitor(snes, snes->iter, snes->norm));
530     /* Test for convergence */
531     PetscUseTypeMethod(snes, converged, snes->iter, 0.0, 0.0, fnorm, &snes->reason, snes->cnvP);
532     if (snes->reason) break;
533   }
534   if (i == maxits) {
535     PetscCall(PetscInfo(snes, "Maximum number of iterations has been reached: %" PetscInt_FMT "\n", maxits));
536     if (!snes->reason) snes->reason = SNES_DIVERGED_MAX_IT;
537   }
538   PetscFunctionReturn(PETSC_SUCCESS);
539 }
540 
541 PetscErrorCode SNESMultiblockSetFields_Default(SNES snes, const char name[], PetscInt n, const PetscInt fields[])
542 {
543   SNES_Multiblock *mb = (SNES_Multiblock *)snes->data;
544   BlockDesc        newblock, next = mb->blocks;
545   char             prefix[128];
546   PetscInt         i;
547 
548   PetscFunctionBegin;
549   if (mb->defined) {
550     PetscCall(PetscInfo(snes, "Ignoring new block \"%s\" because the blocks have already been defined\n", name));
551     PetscFunctionReturn(PETSC_SUCCESS);
552   }
553   for (i = 0; i < n; ++i) {
554     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);
555     PetscCheck(fields[i] >= 0, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Negative field %" PetscInt_FMT " requested", fields[i]);
556   }
557   PetscCall(PetscNew(&newblock));
558   if (name) {
559     PetscCall(PetscStrallocpy(name, &newblock->name));
560   } else {
561     PetscInt len = floor(log10(mb->numBlocks)) + 1;
562 
563     PetscCall(PetscMalloc1(len + 1, &newblock->name));
564     PetscCall(PetscSNPrintf(newblock->name, len, "%" PetscInt_FMT, mb->numBlocks));
565   }
566   newblock->nfields = n;
567 
568   PetscCall(PetscMalloc1(n, &newblock->fields));
569   PetscCall(PetscArraycpy(newblock->fields, fields, n));
570 
571   newblock->next = NULL;
572 
573   PetscCall(SNESCreate(PetscObjectComm((PetscObject)snes), &newblock->snes));
574   PetscCall(PetscObjectIncrementTabLevel((PetscObject)newblock->snes, (PetscObject)snes, 1));
575   PetscCall(SNESSetType(newblock->snes, SNESNRICHARDSON));
576   PetscCall(PetscSNPrintf(prefix, sizeof(prefix), "%smultiblock_%s_", ((PetscObject)snes)->prefix ? ((PetscObject)snes)->prefix : "", newblock->name));
577   PetscCall(SNESSetOptionsPrefix(newblock->snes, prefix));
578 
579   if (!next) {
580     mb->blocks         = newblock;
581     newblock->previous = NULL;
582   } else {
583     while (next->next) next = next->next;
584     next->next         = newblock;
585     newblock->previous = next;
586   }
587   mb->numBlocks++;
588   PetscFunctionReturn(PETSC_SUCCESS);
589 }
590 
591 PetscErrorCode SNESMultiblockSetIS_Default(SNES snes, const char name[], IS is)
592 {
593   SNES_Multiblock *mb = (SNES_Multiblock *)snes->data;
594   BlockDesc        newblock, next = mb->blocks;
595   char             prefix[128];
596 
597   PetscFunctionBegin;
598   if (mb->defined) {
599     PetscCall(PetscInfo(snes, "Ignoring new block \"%s\" because the blocks have already been defined\n", name));
600     PetscFunctionReturn(PETSC_SUCCESS);
601   }
602   PetscCall(PetscNew(&newblock));
603   if (name) {
604     PetscCall(PetscStrallocpy(name, &newblock->name));
605   } else {
606     PetscInt len = floor(log10(mb->numBlocks)) + 1;
607 
608     PetscCall(PetscMalloc1(len + 1, &newblock->name));
609     PetscCall(PetscSNPrintf(newblock->name, len, "%" PetscInt_FMT, mb->numBlocks));
610   }
611   newblock->is = is;
612 
613   PetscCall(PetscObjectReference((PetscObject)is));
614 
615   newblock->next = NULL;
616 
617   PetscCall(SNESCreate(PetscObjectComm((PetscObject)snes), &newblock->snes));
618   PetscCall(PetscObjectIncrementTabLevel((PetscObject)newblock->snes, (PetscObject)snes, 1));
619   PetscCall(SNESSetType(newblock->snes, SNESNRICHARDSON));
620   PetscCall(PetscSNPrintf(prefix, sizeof(prefix), "%smultiblock_%s_", ((PetscObject)snes)->prefix ? ((PetscObject)snes)->prefix : "", newblock->name));
621   PetscCall(SNESSetOptionsPrefix(newblock->snes, prefix));
622 
623   if (!next) {
624     mb->blocks         = newblock;
625     newblock->previous = NULL;
626   } else {
627     while (next->next) next = next->next;
628     next->next         = newblock;
629     newblock->previous = next;
630   }
631   mb->numBlocks++;
632   PetscFunctionReturn(PETSC_SUCCESS);
633 }
634 
635 PetscErrorCode SNESMultiblockSetBlockSize_Default(SNES snes, PetscInt bs)
636 {
637   SNES_Multiblock *mb = (SNES_Multiblock *)snes->data;
638 
639   PetscFunctionBegin;
640   PetscCheck(bs >= 1, PetscObjectComm((PetscObject)snes), PETSC_ERR_ARG_OUTOFRANGE, "Blocksize must be positive, you gave %" PetscInt_FMT, bs);
641   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);
642   mb->bs = bs;
643   PetscFunctionReturn(PETSC_SUCCESS);
644 }
645 
646 PetscErrorCode SNESMultiblockGetSubSNES_Default(SNES snes, PetscInt *n, SNES **subsnes)
647 {
648   SNES_Multiblock *mb     = (SNES_Multiblock *)snes->data;
649   BlockDesc        blocks = mb->blocks;
650   PetscInt         cnt    = 0;
651 
652   PetscFunctionBegin;
653   PetscCall(PetscMalloc1(mb->numBlocks, subsnes));
654   while (blocks) {
655     (*subsnes)[cnt++] = blocks->snes;
656     blocks            = blocks->next;
657   }
658   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);
659 
660   if (n) *n = mb->numBlocks;
661   PetscFunctionReturn(PETSC_SUCCESS);
662 }
663 
664 PetscErrorCode SNESMultiblockSetType_Default(SNES snes, PCCompositeType type)
665 {
666   SNES_Multiblock *mb = (SNES_Multiblock *)snes->data;
667 
668   PetscFunctionBegin;
669   mb->type = type;
670   if (type == PC_COMPOSITE_SCHUR) {
671 #if 1
672     SETERRQ(PetscObjectComm((PetscObject)snes), PETSC_ERR_SUP, "The Schur composite type is not yet supported");
673 #else
674     snes->ops->solve = SNESSolve_Multiblock_Schur;
675     snes->ops->view  = SNESView_Multiblock_Schur;
676 
677     PetscCall(PetscObjectComposeFunction((PetscObject)snes, "SNESMultiblockGetSubSNES_C", SNESMultiblockGetSubSNES_Schur));
678     PetscCall(PetscObjectComposeFunction((PetscObject)snes, "SNESMultiblockSchurPrecondition_C", SNESMultiblockSchurPrecondition_Default));
679 #endif
680   } else {
681     snes->ops->solve = SNESSolve_Multiblock;
682     snes->ops->view  = SNESView_Multiblock;
683 
684     PetscCall(PetscObjectComposeFunction((PetscObject)snes, "SNESMultiblockGetSubSNES_C", SNESMultiblockGetSubSNES_Default));
685     PetscCall(PetscObjectComposeFunction((PetscObject)snes, "SNESMultiblockSchurPrecondition_C", 0));
686   }
687   PetscFunctionReturn(PETSC_SUCCESS);
688 }
689 
690 /*@
691   SNESMultiblockSetFields - Sets the fields for one particular block in a `SNESMULTBLOCK` solver
692 
693   Logically Collective
694 
695   Input Parameters:
696 + snes   - the solver
697 . name   - name of this block, if NULL the number of the block is used
698 . n      - the number of fields in this block
699 - fields - the fields in this block
700 
701   Level: intermediate
702 
703   Notes:
704     Use `SNESMultiblockSetIS()` to set a completely general set of row indices as a block.
705 
706   The `SNESMultiblockSetFields()` is for defining blocks as a group of strided indices, or fields.
707   For example, if the vector block size is three then one can define a block as field 0, or
708   1 or 2, or field 0,1 or 0,2 or 1,2 which means
709     0xx3xx6xx9xx12 ... x1xx4xx7xx ... xx2xx5xx8xx.. 01x34x67x... 0x1x3x5x7.. x12x45x78x....
710   where the numbered entries indicate what is in the block.
711 
712   This function is called once per block (it creates a new block each time). Solve options
713   for this block will be available under the prefix -multiblock_BLOCKNAME_.
714 
715 .seealso: `SNESMULTBLOCK`, `SNESMultiblockGetSubSNES()`, `SNESMULTIBLOCK`, `SNESMultiblockSetBlockSize()`, `SNESMultiblockSetIS()`
716 @*/
717 PetscErrorCode SNESMultiblockSetFields(SNES snes, const char name[], PetscInt n, const PetscInt *fields)
718 {
719   PetscFunctionBegin;
720   PetscValidHeaderSpecific(snes, SNES_CLASSID, 1);
721   PetscValidCharPointer(name, 2);
722   PetscCheck(n >= 1, PetscObjectComm((PetscObject)snes), PETSC_ERR_ARG_OUTOFRANGE, "Provided number of fields %" PetscInt_FMT " in split \"%s\" not positive", n, name);
723   PetscValidIntPointer(fields, 4);
724   PetscTryMethod(snes, "SNESMultiblockSetFields_C", (SNES, const char[], PetscInt, const PetscInt *), (snes, name, n, fields));
725   PetscFunctionReturn(PETSC_SUCCESS);
726 }
727 
728 /*@
729   SNESMultiblockSetIS - Sets the global row indices for one particular block in a `SNESMULTBLOCK` solver
730 
731   Logically Collective
732 
733   Input Parameters:
734 + snes - the solver context
735 . name - name of this block, if NULL the number of the block is used
736 - is   - the index set that defines the global row indices in this block
737 
738   Level: intermediate
739 
740   Notes:
741   Use `SNESMultiblockSetFields()`, for blocks defined by strides.
742 
743   This function is called once per block (it creates a new block each time). Solve options
744   for this block will be available under the prefix -multiblock_BLOCKNAME_.
745 
746 .seealso: `SNESMULTBLOCK`, `SNESMultiblockGetSubSNES()`, `SNESMULTIBLOCK`, `SNESMultiblockSetBlockSize()`
747 @*/
748 PetscErrorCode SNESMultiblockSetIS(SNES snes, const char name[], IS is)
749 {
750   PetscFunctionBegin;
751   PetscValidHeaderSpecific(snes, SNES_CLASSID, 1);
752   PetscValidCharPointer(name, 2);
753   PetscValidHeaderSpecific(is, IS_CLASSID, 3);
754   PetscTryMethod(snes, "SNESMultiblockSetIS_C", (SNES, const char[], IS), (snes, name, is));
755   PetscFunctionReturn(PETSC_SUCCESS);
756 }
757 
758 /*@
759   SNESMultiblockSetType - Sets the type of block combination used for a `SNESMULTBLOCK` solver
760 
761   Logically Collective
762 
763   Input Parameters:
764 + snes - the solver context
765 - type - `PC_COMPOSITE_ADDITIVE`, `PC_COMPOSITE_MULTIPLICATIVE` (default), `PC_COMPOSITE_SYMMETRIC_MULTIPLICATIVE`
766 
767   Options Database Key:
768 . -snes_multiblock_type <type: one of multiplicative, additive, symmetric_multiplicative> - Sets block combination type
769 
770   Level: advanced
771 
772 .seealso: `SNESMULTBLOCK`, `PCCompositeSetType()`, `PC_COMPOSITE_ADDITIVE`, `PC_COMPOSITE_MULTIPLICATIVE`, `PC_COMPOSITE_SYMMETRIC_MULTIPLICATIVE`
773 @*/
774 PetscErrorCode SNESMultiblockSetType(SNES snes, PCCompositeType type)
775 {
776   PetscFunctionBegin;
777   PetscValidHeaderSpecific(snes, SNES_CLASSID, 1);
778   PetscTryMethod(snes, "SNESMultiblockSetType_C", (SNES, PCCompositeType), (snes, type));
779   PetscFunctionReturn(PETSC_SUCCESS);
780 }
781 
782 /*@
783   SNESMultiblockSetBlockSize - Sets the block size for structured block division in a `SNESMULTBLOCK` solver. If not set the matrix block size is used.
784 
785   Logically Collective
786 
787   Input Parameters:
788 + snes - the solver context
789 - bs   - the block size
790 
791   Level: intermediate
792 
793 .seealso: `SNESMULTBLOCK`, `SNESMultiblockGetSubSNES()`, `SNESMULTIBLOCK`, `SNESMultiblockSetFields()`
794 @*/
795 PetscErrorCode SNESMultiblockSetBlockSize(SNES snes, PetscInt bs)
796 {
797   PetscFunctionBegin;
798   PetscValidHeaderSpecific(snes, SNES_CLASSID, 1);
799   PetscValidLogicalCollectiveInt(snes, bs, 2);
800   PetscTryMethod(snes, "SNESMultiblockSetBlockSize_C", (SNES, PetscInt), (snes, bs));
801   PetscFunctionReturn(PETSC_SUCCESS);
802 }
803 
804 /*@C
805   SNESMultiblockGetSubSNES - Gets the `SNES` contexts for all blocks in a `SNESMULTBLOCK` solver.
806 
807   Not Collective but each `SNES` obtained is parallel
808 
809   Input Parameter:
810 . snes - the solver context
811 
812   Output Parameters:
813 + n       - the number of blocks
814 - subsnes - the array of `SNES` contexts
815 
816   Level: advanced
817 
818   Note:
819   After `SNESMultiblockGetSubSNES()` the array of `SNES`s MUST be freed by the user
820   (not each `SNES`, just the array that contains them).
821 
822   You must call `SNESSetUp()` before calling `SNESMultiblockGetSubSNES()`.
823 
824 .seealso: `SNESMULTBLOCK`, `SNESMultiblockSetIS()`, `SNESMultiblockSetFields()`
825 @*/
826 PetscErrorCode SNESMultiblockGetSubSNES(SNES snes, PetscInt *n, SNES *subsnes[])
827 {
828   PetscFunctionBegin;
829   PetscValidHeaderSpecific(snes, SNES_CLASSID, 1);
830   if (n) PetscValidIntPointer(n, 2);
831   PetscUseMethod(snes, "SNESMultiblockGetSubSNES_C", (SNES, PetscInt *, SNES **), (snes, n, subsnes));
832   PetscFunctionReturn(PETSC_SUCCESS);
833 }
834 
835 /*MC
836   SNESMULTIBLOCK - Multiblock nonlinear solver that can use overlapping or nonoverlapping blocks, organized
837   additively (Jacobi) or multiplicatively (Gauss-Seidel).
838 
839   Level: beginner
840 
841 .seealso: `SNESCreate()`, `SNES`, `SNESSetType()`, `SNESNEWTONLS`, `SNESNEWTONTR`, `SNESNRICHARDSON`, `SNESMultiblockSetType()`,
842           `PC_COMPOSITE_ADDITIVE`, `PC_COMPOSITE_MULTIPLICATIVE`, `PC_COMPOSITE_SYMMETRIC_MULTIPLICATIVE`
843 M*/
844 PETSC_EXTERN PetscErrorCode SNESCreate_Multiblock(SNES snes)
845 {
846   SNES_Multiblock *mb;
847 
848   PetscFunctionBegin;
849   snes->ops->destroy        = SNESDestroy_Multiblock;
850   snes->ops->setup          = SNESSetUp_Multiblock;
851   snes->ops->setfromoptions = SNESSetFromOptions_Multiblock;
852   snes->ops->view           = SNESView_Multiblock;
853   snes->ops->solve          = SNESSolve_Multiblock;
854   snes->ops->reset          = SNESReset_Multiblock;
855 
856   snes->usesksp = PETSC_FALSE;
857   snes->usesnpc = PETSC_FALSE;
858 
859   snes->alwayscomputesfinalresidual = PETSC_TRUE;
860 
861   PetscCall(PetscNew(&mb));
862   snes->data    = (void *)mb;
863   mb->defined   = PETSC_FALSE;
864   mb->numBlocks = 0;
865   mb->bs        = -1;
866   mb->type      = PC_COMPOSITE_MULTIPLICATIVE;
867 
868   /* We attach functions so that they can be called on another PC without crashing the program */
869   PetscCall(PetscObjectComposeFunction((PetscObject)snes, "SNESMultiblockSetFields_C", SNESMultiblockSetFields_Default));
870   PetscCall(PetscObjectComposeFunction((PetscObject)snes, "SNESMultiblockSetIS_C", SNESMultiblockSetIS_Default));
871   PetscCall(PetscObjectComposeFunction((PetscObject)snes, "SNESMultiblockSetType_C", SNESMultiblockSetType_Default));
872   PetscCall(PetscObjectComposeFunction((PetscObject)snes, "SNESMultiblockSetBlockSize_C", SNESMultiblockSetBlockSize_Default));
873   PetscCall(PetscObjectComposeFunction((PetscObject)snes, "SNESMultiblockGetSubSNES_C", SNESMultiblockGetSubSNES_Default));
874   PetscFunctionReturn(PETSC_SUCCESS);
875 }
876