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