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