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 CHKERRQ(SNESReset(blocks->snes)); 34 #if 0 35 CHKERRQ(VecDestroy(&blocks->x)); 36 #endif 37 CHKERRQ(VecScatterDestroy(&blocks->sctx)); 38 CHKERRQ(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 CHKERRQ(SNESReset_Multiblock(snes)); 60 while (blocks) { 61 next = blocks->next; 62 CHKERRQ(SNESDestroy(&blocks->snes)); 63 CHKERRQ(PetscFree(blocks->name)); 64 CHKERRQ(PetscFree(blocks->fields)); 65 CHKERRQ(PetscFree(blocks)); 66 blocks = next; 67 } 68 CHKERRQ(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 CHKERRQ(PetscMalloc1(mb->bs, &ifields)); 83 for (i = 0;; ++i) { 84 CHKERRQ(PetscSNPrintf(name, sizeof(name), "%D", i)); 85 CHKERRQ(PetscSNPrintf(optionname, sizeof(optionname), "-snes_multiblock_%D_fields", i)); 86 nfields = mb->bs; 87 CHKERRQ(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 CHKERRQ(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 CHKERRQ(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 CHKERRQ(PetscObjectTypeCompare((PetscObject) snes->dm, DMCOMPOSITE, &dmcomposite)); 114 if (dmcomposite) { 115 PetscInt nDM; 116 IS *fields; 117 118 CHKERRQ(PetscInfo(snes,"Setting up physics based multiblock solver using the embedded DM\n")); 119 CHKERRQ(DMCompositeGetNumberDM(snes->dm, &nDM)); 120 CHKERRQ(DMCompositeGetGlobalISs(snes->dm, &fields)); 121 for (i = 0; i < nDM; ++i) { 122 char name[8]; 123 124 CHKERRQ(PetscSNPrintf(name, sizeof(name), "%D", i)); 125 CHKERRQ(SNESMultiblockSetIS(snes, name, fields[i])); 126 CHKERRQ(ISDestroy(&fields[i])); 127 } 128 CHKERRQ(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 CHKERRQ(MatGetBlockSize(snes->jacobian_pre, &mb->bs)); 137 } else mb->bs = 1; 138 } 139 140 CHKERRQ(PetscOptionsGetBool(NULL,((PetscObject) snes)->prefix, "-snes_multiblock_default", &flg, NULL)); 141 CHKERRQ(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 CHKERRQ(MatGetOwnershipRange(snes->jacobian_pre, &nmin, &nmax)); 147 CHKERRQ(MatFindZeroDiagonals(snes->jacobian_pre, &zerodiags)); 148 CHKERRQ(ISComplement(zerodiags, nmin, nmax, &rest)); 149 CHKERRQ(SNESMultiblockSetIS(snes, "0", rest)); 150 CHKERRQ(SNESMultiblockSetIS(snes, "1", zerodiags)); 151 CHKERRQ(ISDestroy(&zerodiags)); 152 CHKERRQ(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 CHKERRQ(SNESMultiblockSetFieldsRuntime_Private(snes)); 158 if (mb->defined) CHKERRQ(PetscInfo(snes, "Blocks defined using the options database\n")); 159 } 160 if (flg || !mb->defined) { 161 CHKERRQ(PetscInfo(snes, "Using default splitting of fields\n")); 162 for (i = 0; i < mb->bs; ++i) { 163 char name[8]; 164 165 CHKERRQ(PetscSNPrintf(name, sizeof(name), "%D", i)); 166 CHKERRQ(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 CHKERRQ(MatGetOwnershipRange(snes->jacobian_pre, &nmin, &nmax)); 178 CHKERRQ(ISComplement(blocks->is, nmin, nmax, &is2)); 179 CHKERRQ(SNESMultiblockSetIS(snes, "1", is2)); 180 CHKERRQ(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 PetscCheckFalse(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 CHKERRQ(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 CHKERRQ(MatGetOwnershipRange(snes->jacobian_pre, &rstart, &rend)); 216 CHKERRQ(MatGetLocalSize(snes->jacobian_pre, NULL, &ccsize)); 217 nslots = (rend - rstart)/bs; 218 for (i = 0; i < numBlocks; ++i) { 219 if (mb->defaultblocks) { 220 CHKERRQ(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 CHKERRQ(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 CHKERRQ(ISCreateGeneral(PetscObjectComm((PetscObject)snes), nslots*nfields, ii, PETSC_OWN_POINTER, &blocks->is)); 232 } else { 233 CHKERRQ(ISCreateStride(PetscObjectComm((PetscObject)snes), nslots, rstart+blocks->fields[0], bs, &blocks->is)); 234 } 235 } 236 CHKERRQ(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 CHKERRQ(PetscMalloc1(nsplit,&jac->pmat)); 247 for (i=0; i<nsplit; i++) { 248 CHKERRQ(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 CHKERRQ(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 CHKERRQ(PetscMalloc1(nsplit,&jac->mat)); 261 for (i=0; i<nsplit; i++) { 262 CHKERRQ(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]) CHKERRQ(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 CHKERRQ(PetscMalloc1(nsplit,&jac->Afield)); 280 for (i=0; i<nsplit; i++) { 281 CHKERRQ(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 CHKERRQ(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 PetscCheckFalse(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 CHKERRQ(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 CHKERRQ(ISComplement(ilink->is,rstart,rend,&ccis)); 306 CHKERRQ(MatCreateSubMatrix(pc->mat,ilink->is,ccis,MAT_REUSE_MATRIX,&jac->B)); 307 CHKERRQ(ISDestroy(&ccis)); 308 ilink = ilink->next; 309 CHKERRQ(ISComplement(ilink->is,rstart,rend,&ccis)); 310 CHKERRQ(MatCreateSubMatrix(pc->mat,ilink->is,ccis,MAT_REUSE_MATRIX,&jac->C)); 311 CHKERRQ(ISDestroy(&ccis)); 312 CHKERRQ(MatSchurComplementUpdateSubMatrices(jac->schur,jac->mat[0],jac->pmat[0],jac->B,jac->C,jac->pmat[1])); 313 CHKERRQ(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 CHKERRQ(ISComplement(ilink->is,rstart,rend,&ccis)); 322 CHKERRQ(MatCreateSubMatrix(pc->mat,ilink->is,ccis,MAT_INITIAL_MATRIX,&jac->B)); 323 CHKERRQ(ISDestroy(&ccis)); 324 ilink = ilink->next; 325 CHKERRQ(ISComplement(ilink->is,rstart,rend,&ccis)); 326 CHKERRQ(MatCreateSubMatrix(pc->mat,ilink->is,ccis,MAT_INITIAL_MATRIX,&jac->C)); 327 CHKERRQ(ISDestroy(&ccis)); 328 /* Use mat[0] (diagonal block of the real matrix) preconditioned by pmat[0] */ 329 CHKERRQ(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 CHKERRQ(MatSchurComplementGetKSP(jac->schur,&ksp)); 332 CHKERRQ(PetscObjectIncrementTabLevel((PetscObject)ksp,(PetscObject)pc,2)); 333 CHKERRQ(PetscSNPrintf(schurprefix,sizeof(schurprefix),"%sfieldsplit_%s_",((PetscObject)pc)->prefix ? ((PetscObject)pc)->prefix : "",jac->head->splitname)); 334 CHKERRQ(KSPSetOptionsPrefix(ksp,schurprefix)); 335 CHKERRQ(MatSetFromOptions(jac->schur)); 336 337 CHKERRQ(KSPCreate(PetscObjectComm((PetscObject)pc),&jac->kspschur)); 338 CHKERRQ(PetscLogObjectParent((PetscObject)pc,(PetscObject)jac->kspschur)); 339 CHKERRQ(PetscObjectIncrementTabLevel((PetscObject)jac->kspschur,(PetscObject)pc,1)); 340 CHKERRQ(KSPSetOperators(jac->kspschur,jac->schur,FieldSplitSchurPre(jac))); 341 if (jac->schurpre == PC_FIELDSPLIT_SCHUR_PRE_SELF) { 342 PC pc; 343 CHKERRQ(KSPGetPC(jac->kspschur,&pc)); 344 CHKERRQ(PCSetType(pc,PCNONE)); 345 /* Note: This is bad if there exist preconditioners for MATSCHURCOMPLEMENT */ 346 } 347 CHKERRQ(PetscSNPrintf(schurprefix,sizeof(schurprefix),"%sfieldsplit_%s_",((PetscObject)pc)->prefix ? ((PetscObject)pc)->prefix : "",ilink->splitname)); 348 CHKERRQ(KSPSetOptionsPrefix(jac->kspschur,schurprefix)); 349 /* really want setfromoptions called in PCSetFromOptions_FieldSplit(), but it is not ready yet */ 350 CHKERRQ(KSPSetFromOptions(jac->kspschur)); 351 352 CHKERRQ(PetscMalloc2(2,&jac->x,2,&jac->y)); 353 CHKERRQ(MatCreateVecs(jac->pmat[0],&jac->x[0],&jac->y[0])); 354 CHKERRQ(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 /*CHKERRQ(SNESSetFunction(blocks->snes, blocks->x, func));*/ 368 /*CHKERRQ(SNESSetJacobian(blocks->snes, blocks->x, jac));*/ 369 CHKERRQ(VecDuplicate(blocks->snes->vec_sol, &blocks->x)); 370 /* really want setfromoptions called in SNESSetFromOptions_Multiblock(), but it is not ready yet */ 371 CHKERRQ(SNESSetFromOptions(blocks->snes)); 372 CHKERRQ(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 CHKERRQ(MatCreateVecs(snes->jacobian_pre, &xtmp, NULL)); 384 while (blocks) { 385 CHKERRQ(VecScatterCreate(xtmp, blocks->is, blocks->x, NULL, &blocks->sctx)); 386 blocks = blocks->next; 387 } 388 CHKERRQ(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 CHKERRQ(PetscOptionsHead(PetscOptionsObject,"SNES Multiblock options")); 410 CHKERRQ(PetscOptionsInt("-snes_multiblock_block_size", "Blocksize that defines number of fields", "PCFieldSplitSetBlockSize", mb->bs, &bs, &flg)); 411 if (flg) CHKERRQ(SNESMultiblockSetBlockSize(snes, bs)); 412 CHKERRQ(PetscOptionsEnum("-snes_multiblock_type", "Type of composition", "PCFieldSplitSetType", PCCompositeTypes, (PetscEnum) mb->type, (PetscEnum*) &ctype, &flg)); 413 if (flg) { 414 CHKERRQ(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 CHKERRQ(SNESMultiblockSetFieldsRuntime_Private(snes)); 420 if (mb->defined) CHKERRQ(PetscInfo(snes, "Blocks defined using the options database\n")); 421 } 422 CHKERRQ(PetscOptionsTail()); 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 CHKERRQ(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERASCII, &iascii)); 443 if (iascii) { 444 CHKERRQ(PetscViewerASCIIPrintf(viewer," Multiblock with %s composition: total blocks = %D, blocksize = %D\n", PCCompositeTypes[mb->type], mb->numBlocks, mb->bs)); 445 CHKERRQ(PetscViewerASCIIPrintf(viewer," Solver info for each split is in the following SNES objects:\n")); 446 CHKERRQ(PetscViewerASCIIPushTab(viewer)); 447 while (blocks) { 448 if (blocks->fields) { 449 PetscInt j; 450 451 CHKERRQ(PetscViewerASCIIPrintf(viewer, " Block %s Fields ", blocks->name)); 452 CHKERRQ(PetscViewerASCIIUseTabs(viewer, PETSC_FALSE)); 453 for (j = 0; j < blocks->nfields; ++j) { 454 if (j > 0) { 455 CHKERRQ(PetscViewerASCIIPrintf(viewer, ",")); 456 } 457 CHKERRQ(PetscViewerASCIIPrintf(viewer, " %D", blocks->fields[j])); 458 } 459 CHKERRQ(PetscViewerASCIIPrintf(viewer, "\n")); 460 CHKERRQ(PetscViewerASCIIUseTabs(viewer, PETSC_TRUE)); 461 } else { 462 CHKERRQ(PetscViewerASCIIPrintf(viewer, " Block %s Defined by IS\n", blocks->name)); 463 } 464 CHKERRQ(SNESView(blocks->snes, viewer)); 465 blocks = blocks->next; 466 } 467 CHKERRQ(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 PetscCheckFalse(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 CHKERRQ(VecSetBlockSize(X, mb->bs)); 501 CHKERRQ(VecSetBlockSize(Y, mb->bs)); 502 CHKERRQ(VecSetBlockSize(F, mb->bs)); 503 CHKERRQ(PetscObjectSAWsTakeAccess((PetscObject)snes)); 504 snes->iter = 0; 505 snes->norm = 0.; 506 CHKERRQ(PetscObjectSAWsGrantAccess((PetscObject)snes)); 507 508 if (!snes->vec_func_init_set) { 509 CHKERRQ(SNESComputeFunction(snes, X, F)); 510 } else snes->vec_func_init_set = PETSC_FALSE; 511 512 CHKERRQ(VecNorm(F, NORM_2, &fnorm)); /* fnorm <- ||F|| */ 513 SNESCheckFunctionNorm(snes,fnorm); 514 CHKERRQ(PetscObjectSAWsTakeAccess((PetscObject)snes)); 515 snes->norm = fnorm; 516 CHKERRQ(PetscObjectSAWsGrantAccess((PetscObject)snes)); 517 CHKERRQ(SNESLogConvergenceHistory(snes,fnorm,0)); 518 CHKERRQ(SNESMonitor(snes,0,fnorm)); 519 520 /* test convergence */ 521 CHKERRQ((*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 CHKERRQ((*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 /*CHKERRQ(VecStrideGatherAll(X, mb->x, INSERT_VALUES));*/ 536 while (blocks) { 537 CHKERRQ(SNESSolve(blocks->snes, NULL, blocks->x)); 538 blocks = blocks->next; 539 } 540 /*CHKERRQ(VecStrideScatterAll(mb->x, X, INSERT_VALUES));*/ 541 } else { 542 while (blocks) { 543 CHKERRQ(VecScatterBegin(blocks->sctx, X, blocks->x, INSERT_VALUES, SCATTER_FORWARD)); 544 CHKERRQ(VecScatterEnd(blocks->sctx, X, blocks->x, INSERT_VALUES, SCATTER_FORWARD)); 545 CHKERRQ(SNESSolve(blocks->snes, NULL, blocks->x)); 546 CHKERRQ(VecScatterBegin(blocks->sctx, blocks->x, X, INSERT_VALUES, SCATTER_REVERSE)); 547 CHKERRQ(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", (int) mb->type); 552 /* Compute F(X^{new}) */ 553 CHKERRQ(SNESComputeFunction(snes, X, F)); 554 CHKERRQ(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 CHKERRQ(PetscObjectSAWsTakeAccess((PetscObject)snes)); 564 snes->iter = i+1; 565 snes->norm = fnorm; 566 CHKERRQ(PetscObjectSAWsGrantAccess((PetscObject)snes)); 567 CHKERRQ(SNESLogConvergenceHistory(snes,snes->norm,0)); 568 CHKERRQ(SNESMonitor(snes,snes->iter,snes->norm)); 569 /* Test for convergence */ 570 CHKERRQ((*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 CHKERRQ(PetscInfo(snes, "Maximum number of iterations has been reached: %D\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 CHKERRQ(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 PetscCheckFalse(fields[i] >= mb->bs,PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Field %D requested but only %D exist", fields[i], mb->bs); 594 PetscCheckFalse(fields[i] < 0,PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Negative field %D requested", fields[i]); 595 } 596 CHKERRQ(PetscNew(&newblock)); 597 if (name) { 598 CHKERRQ(PetscStrallocpy(name, &newblock->name)); 599 } else { 600 PetscInt len = floor(log10(mb->numBlocks))+1; 601 602 CHKERRQ(PetscMalloc1(len+1, &newblock->name)); 603 CHKERRQ(PetscSNPrintf(newblock->name, len, "%s", mb->numBlocks)); 604 } 605 newblock->nfields = n; 606 607 CHKERRQ(PetscMalloc1(n, &newblock->fields)); 608 CHKERRQ(PetscArraycpy(newblock->fields, fields, n)); 609 610 newblock->next = NULL; 611 612 CHKERRQ(SNESCreate(PetscObjectComm((PetscObject)snes), &newblock->snes)); 613 CHKERRQ(PetscObjectIncrementTabLevel((PetscObject) newblock->snes, (PetscObject) snes, 1)); 614 CHKERRQ(SNESSetType(newblock->snes, SNESNRICHARDSON)); 615 CHKERRQ(PetscLogObjectParent((PetscObject) snes, (PetscObject) newblock->snes)); 616 CHKERRQ(PetscSNPrintf(prefix, sizeof(prefix), "%smultiblock_%s_", ((PetscObject) snes)->prefix ? ((PetscObject) snes)->prefix : "", newblock->name)); 617 CHKERRQ(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 CHKERRQ(PetscInfo(snes, "Ignoring new block \"%s\" because the blocks have already been defined\n", name)); 642 PetscFunctionReturn(0); 643 } 644 CHKERRQ(PetscNew(&newblock)); 645 if (name) { 646 CHKERRQ(PetscStrallocpy(name, &newblock->name)); 647 } else { 648 PetscInt len = floor(log10(mb->numBlocks))+1; 649 650 CHKERRQ(PetscMalloc1(len+1, &newblock->name)); 651 CHKERRQ(PetscSNPrintf(newblock->name, len, "%s", mb->numBlocks)); 652 } 653 newblock->is = is; 654 655 CHKERRQ(PetscObjectReference((PetscObject) is)); 656 657 newblock->next = NULL; 658 659 CHKERRQ(SNESCreate(PetscObjectComm((PetscObject)snes), &newblock->snes)); 660 CHKERRQ(PetscObjectIncrementTabLevel((PetscObject) newblock->snes, (PetscObject) snes, 1)); 661 CHKERRQ(SNESSetType(newblock->snes, SNESNRICHARDSON)); 662 CHKERRQ(PetscLogObjectParent((PetscObject) snes, (PetscObject) newblock->snes)); 663 CHKERRQ(PetscSNPrintf(prefix, sizeof(prefix), "%smultiblock_%s_", ((PetscObject) snes)->prefix ? ((PetscObject) snes)->prefix : "", newblock->name)); 664 CHKERRQ(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 PetscCheckFalse(bs < 1,PetscObjectComm((PetscObject)snes), PETSC_ERR_ARG_OUTOFRANGE, "Blocksize must be positive, you gave %D", bs); 686 PetscCheckFalse(mb->bs > 0 && mb->bs != bs,PetscObjectComm((PetscObject)snes), PETSC_ERR_ARG_WRONGSTATE, "Cannot change blocksize from %D to %D 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 CHKERRQ(PetscMalloc1(mb->numBlocks, subsnes)); 699 while (blocks) { 700 (*subsnes)[cnt++] = blocks->snes; 701 blocks = blocks->next; 702 } 703 PetscCheckFalse(cnt != mb->numBlocks,PETSC_COMM_SELF, PETSC_ERR_PLIB, "Corrupt SNESMULTIBLOCK object: number of blocks in linked list %D does not match number in object %D", 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 CHKERRQ(PetscObjectComposeFunction((PetscObject) snes, "SNESMultiblockGetSubSNES_C", SNESMultiblockGetSubSNES_Schur)); 723 CHKERRQ(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 CHKERRQ(PetscObjectComposeFunction((PetscObject) snes, "SNESMultiblockGetSubSNES_C", SNESMultiblockGetSubSNES_Default)); 730 CHKERRQ(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 PetscCheckFalse(n < 1,PetscObjectComm((PetscObject)snes), PETSC_ERR_ARG_OUTOFRANGE, "Provided number of fields %D in split \"%s\" not positive", n, name); 768 PetscValidIntPointer(fields, 4); 769 CHKERRQ(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 CHKERRQ(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 CHKERRQ(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 CHKERRQ(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 CHKERRQ(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 CHKERRQ(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 CHKERRQ(PetscObjectComposeFunction((PetscObject) snes, "SNESMultiblockSetFields_C", SNESMultiblockSetFields_Default)); 914 CHKERRQ(PetscObjectComposeFunction((PetscObject) snes, "SNESMultiblockSetIS_C", SNESMultiblockSetIS_Default)); 915 CHKERRQ(PetscObjectComposeFunction((PetscObject) snes, "SNESMultiblockSetType_C", SNESMultiblockSetType_Default)); 916 CHKERRQ(PetscObjectComposeFunction((PetscObject) snes, "SNESMultiblockSetBlockSize_C", SNESMultiblockSetBlockSize_Default)); 917 CHKERRQ(PetscObjectComposeFunction((PetscObject) snes, "SNESMultiblockGetSubSNES_C", SNESMultiblockGetSubSNES_Default)); 918 PetscFunctionReturn(0); 919 } 920