1 /* 2 Defines a SNES that can consist of a collection of SNESes on patches of the domain 3 */ 4 #include <petsc/private/vecimpl.h> /* For vec->map */ 5 #include <petsc/private/snesimpl.h> /*I "petscsnes.h" I*/ 6 #include <petsc/private/pcpatchimpl.h> /* We need internal access to PCPatch right now, until that part is moved to Plex */ 7 #include <petscsf.h> 8 #include <petscsection.h> 9 10 typedef struct { 11 PC pc; /* The linear patch preconditioner */ 12 } SNES_Patch; 13 14 static PetscErrorCode SNESPatchComputeResidual_Private(SNES snes, Vec x, Vec F, void *ctx) 15 { 16 PC pc = (PC) ctx; 17 PC_PATCH *pcpatch = (PC_PATCH *) pc->data; 18 PetscInt pt, size, i; 19 const PetscInt *indices; 20 const PetscScalar *X; 21 PetscScalar *XWithAll; 22 23 PetscFunctionBegin; 24 25 /* scatter from x to patch->patchStateWithAll[pt] */ 26 pt = pcpatch->currentPatch; 27 CHKERRQ(ISGetSize(pcpatch->dofMappingWithoutToWithAll[pt], &size)); 28 29 CHKERRQ(ISGetIndices(pcpatch->dofMappingWithoutToWithAll[pt], &indices)); 30 CHKERRQ(VecGetArrayRead(x, &X)); 31 CHKERRQ(VecGetArray(pcpatch->patchStateWithAll, &XWithAll)); 32 33 for (i = 0; i < size; ++i) { 34 XWithAll[indices[i]] = X[i]; 35 } 36 37 CHKERRQ(VecRestoreArray(pcpatch->patchStateWithAll, &XWithAll)); 38 CHKERRQ(VecRestoreArrayRead(x, &X)); 39 CHKERRQ(ISRestoreIndices(pcpatch->dofMappingWithoutToWithAll[pt], &indices)); 40 41 CHKERRQ(PCPatchComputeFunction_Internal(pc, pcpatch->patchStateWithAll, F, pt)); 42 PetscFunctionReturn(0); 43 } 44 45 static PetscErrorCode SNESPatchComputeJacobian_Private(SNES snes, Vec x, Mat J, Mat M, void *ctx) 46 { 47 PC pc = (PC) ctx; 48 PC_PATCH *pcpatch = (PC_PATCH *) pc->data; 49 PetscInt pt, size, i; 50 const PetscInt *indices; 51 const PetscScalar *X; 52 PetscScalar *XWithAll; 53 54 PetscFunctionBegin; 55 /* scatter from x to patch->patchStateWithAll[pt] */ 56 pt = pcpatch->currentPatch; 57 CHKERRQ(ISGetSize(pcpatch->dofMappingWithoutToWithAll[pt], &size)); 58 59 CHKERRQ(ISGetIndices(pcpatch->dofMappingWithoutToWithAll[pt], &indices)); 60 CHKERRQ(VecGetArrayRead(x, &X)); 61 CHKERRQ(VecGetArray(pcpatch->patchStateWithAll, &XWithAll)); 62 63 for (i = 0; i < size; ++i) { 64 XWithAll[indices[i]] = X[i]; 65 } 66 67 CHKERRQ(VecRestoreArray(pcpatch->patchStateWithAll, &XWithAll)); 68 CHKERRQ(VecRestoreArrayRead(x, &X)); 69 CHKERRQ(ISRestoreIndices(pcpatch->dofMappingWithoutToWithAll[pt], &indices)); 70 71 CHKERRQ(PCPatchComputeOperator_Internal(pc, pcpatch->patchStateWithAll, M, pcpatch->currentPatch, PETSC_FALSE)); 72 PetscFunctionReturn(0); 73 } 74 75 static PetscErrorCode PCSetUp_PATCH_Nonlinear(PC pc) 76 { 77 PC_PATCH *patch = (PC_PATCH *) pc->data; 78 const char *prefix; 79 PetscInt i, pStart, dof, maxDof = -1; 80 81 PetscFunctionBegin; 82 if (!pc->setupcalled) { 83 CHKERRQ(PetscMalloc1(patch->npatch, &patch->solver)); 84 CHKERRQ(PCGetOptionsPrefix(pc, &prefix)); 85 CHKERRQ(PetscSectionGetChart(patch->gtolCounts, &pStart, NULL)); 86 for (i = 0; i < patch->npatch; ++i) { 87 SNES snes; 88 89 CHKERRQ(SNESCreate(PETSC_COMM_SELF, &snes)); 90 CHKERRQ(SNESSetOptionsPrefix(snes, prefix)); 91 CHKERRQ(SNESAppendOptionsPrefix(snes, "sub_")); 92 CHKERRQ(PetscObjectIncrementTabLevel((PetscObject) snes, (PetscObject) pc, 2)); 93 CHKERRQ(PetscLogObjectParent((PetscObject) pc, (PetscObject) snes)); 94 patch->solver[i] = (PetscObject) snes; 95 96 CHKERRQ(PetscSectionGetDof(patch->gtolCountsWithAll, i+pStart, &dof)); 97 maxDof = PetscMax(maxDof, dof); 98 } 99 CHKERRQ(VecDuplicate(patch->localUpdate, &patch->localState)); 100 CHKERRQ(VecDuplicate(patch->patchRHS, &patch->patchResidual)); 101 CHKERRQ(VecDuplicate(patch->patchUpdate, &patch->patchState)); 102 103 CHKERRQ(VecCreateSeq(PETSC_COMM_SELF, maxDof, &patch->patchStateWithAll)); 104 CHKERRQ(VecSetUp(patch->patchStateWithAll)); 105 } 106 for (i = 0; i < patch->npatch; ++i) { 107 SNES snes = (SNES) patch->solver[i]; 108 109 CHKERRQ(SNESSetFunction(snes, patch->patchResidual, SNESPatchComputeResidual_Private, pc)); 110 CHKERRQ(SNESSetJacobian(snes, patch->mat[i], patch->mat[i], SNESPatchComputeJacobian_Private, pc)); 111 } 112 if (!pc->setupcalled && patch->optionsSet) for (i = 0; i < patch->npatch; ++i) CHKERRQ(SNESSetFromOptions((SNES) patch->solver[i])); 113 PetscFunctionReturn(0); 114 } 115 116 static PetscErrorCode PCApply_PATCH_Nonlinear(PC pc, PetscInt i, Vec patchRHS, Vec patchUpdate) 117 { 118 PC_PATCH *patch = (PC_PATCH *) pc->data; 119 PetscInt pStart, n; 120 121 PetscFunctionBegin; 122 patch->currentPatch = i; 123 CHKERRQ(PetscLogEventBegin(PC_Patch_Solve, pc, 0, 0, 0)); 124 125 /* Scatter the overlapped global state to our patch state vector */ 126 CHKERRQ(PetscSectionGetChart(patch->gtolCounts, &pStart, NULL)); 127 CHKERRQ(PCPatch_ScatterLocal_Private(pc, i+pStart, patch->localState, patch->patchState, INSERT_VALUES, SCATTER_FORWARD, SCATTER_INTERIOR)); 128 CHKERRQ(PCPatch_ScatterLocal_Private(pc, i+pStart, patch->localState, patch->patchStateWithAll, INSERT_VALUES, SCATTER_FORWARD, SCATTER_WITHALL)); 129 130 CHKERRQ(MatGetLocalSize(patch->mat[i], NULL, &n)); 131 patch->patchState->map->n = n; 132 patch->patchState->map->N = n; 133 patchUpdate->map->n = n; 134 patchUpdate->map->N = n; 135 patchRHS->map->n = n; 136 patchRHS->map->N = n; 137 /* Set initial guess to be current state*/ 138 CHKERRQ(VecCopy(patch->patchState, patchUpdate)); 139 /* Solve for new state */ 140 CHKERRQ(SNESSolve((SNES) patch->solver[i], patchRHS, patchUpdate)); 141 /* To compute update, subtract off previous state */ 142 CHKERRQ(VecAXPY(patchUpdate, -1.0, patch->patchState)); 143 144 CHKERRQ(PetscLogEventEnd(PC_Patch_Solve, pc, 0, 0, 0)); 145 PetscFunctionReturn(0); 146 } 147 148 static PetscErrorCode PCReset_PATCH_Nonlinear(PC pc) 149 { 150 PC_PATCH *patch = (PC_PATCH *) pc->data; 151 PetscInt i; 152 153 PetscFunctionBegin; 154 if (patch->solver) { 155 for (i = 0; i < patch->npatch; ++i) CHKERRQ(SNESReset((SNES) patch->solver[i])); 156 } 157 158 CHKERRQ(VecDestroy(&patch->patchResidual)); 159 CHKERRQ(VecDestroy(&patch->patchState)); 160 CHKERRQ(VecDestroy(&patch->patchStateWithAll)); 161 162 CHKERRQ(VecDestroy(&patch->localState)); 163 PetscFunctionReturn(0); 164 } 165 166 static PetscErrorCode PCDestroy_PATCH_Nonlinear(PC pc) 167 { 168 PC_PATCH *patch = (PC_PATCH *) pc->data; 169 PetscInt i; 170 171 PetscFunctionBegin; 172 if (patch->solver) { 173 for (i = 0; i < patch->npatch; ++i) CHKERRQ(SNESDestroy((SNES *) &patch->solver[i])); 174 CHKERRQ(PetscFree(patch->solver)); 175 } 176 PetscFunctionReturn(0); 177 } 178 179 static PetscErrorCode PCUpdateMultiplicative_PATCH_Nonlinear(PC pc, PetscInt i, PetscInt pStart) 180 { 181 PC_PATCH *patch = (PC_PATCH *) pc->data; 182 183 PetscFunctionBegin; 184 CHKERRQ(PCPatch_ScatterLocal_Private(pc, i + pStart, patch->patchUpdate, patch->localState, ADD_VALUES, SCATTER_REVERSE, SCATTER_INTERIOR)); 185 PetscFunctionReturn(0); 186 } 187 188 static PetscErrorCode SNESSetUp_Patch(SNES snes) 189 { 190 SNES_Patch *patch = (SNES_Patch *) snes->data; 191 DM dm; 192 Mat dummy; 193 Vec F; 194 PetscInt n, N; 195 196 PetscFunctionBegin; 197 CHKERRQ(SNESGetDM(snes, &dm)); 198 CHKERRQ(PCSetDM(patch->pc, dm)); 199 CHKERRQ(SNESGetFunction(snes, &F, NULL, NULL)); 200 CHKERRQ(VecGetLocalSize(F, &n)); 201 CHKERRQ(VecGetSize(F, &N)); 202 CHKERRQ(MatCreateShell(PetscObjectComm((PetscObject) snes), n, n, N, N, (void *) snes, &dummy)); 203 CHKERRQ(PCSetOperators(patch->pc, dummy, dummy)); 204 CHKERRQ(MatDestroy(&dummy)); 205 CHKERRQ(PCSetUp(patch->pc)); 206 /* allocate workspace */ 207 PetscFunctionReturn(0); 208 } 209 210 static PetscErrorCode SNESReset_Patch(SNES snes) 211 { 212 SNES_Patch *patch = (SNES_Patch *) snes->data; 213 214 PetscFunctionBegin; 215 CHKERRQ(PCReset(patch->pc)); 216 PetscFunctionReturn(0); 217 } 218 219 static PetscErrorCode SNESDestroy_Patch(SNES snes) 220 { 221 SNES_Patch *patch = (SNES_Patch *) snes->data; 222 223 PetscFunctionBegin; 224 CHKERRQ(SNESReset_Patch(snes)); 225 CHKERRQ(PCDestroy(&patch->pc)); 226 CHKERRQ(PetscFree(snes->data)); 227 PetscFunctionReturn(0); 228 } 229 230 static PetscErrorCode SNESSetFromOptions_Patch(PetscOptionItems *PetscOptionsObject, SNES snes) 231 { 232 SNES_Patch *patch = (SNES_Patch *) snes->data; 233 const char *prefix; 234 235 PetscFunctionBegin; 236 CHKERRQ(PetscObjectGetOptionsPrefix((PetscObject)snes, &prefix)); 237 CHKERRQ(PetscObjectSetOptionsPrefix((PetscObject)patch->pc, prefix)); 238 CHKERRQ(PCSetFromOptions(patch->pc)); 239 PetscFunctionReturn(0); 240 } 241 242 static PetscErrorCode SNESView_Patch(SNES snes,PetscViewer viewer) 243 { 244 SNES_Patch *patch = (SNES_Patch *) snes->data; 245 PetscBool iascii; 246 247 PetscFunctionBegin; 248 CHKERRQ(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERASCII, &iascii)); 249 if (iascii) { 250 CHKERRQ(PetscViewerASCIIPrintf(viewer,"SNESPATCH\n")); 251 } 252 CHKERRQ(PetscViewerASCIIPushTab(viewer)); 253 CHKERRQ(PCView(patch->pc, viewer)); 254 CHKERRQ(PetscViewerASCIIPopTab(viewer)); 255 PetscFunctionReturn(0); 256 } 257 258 static PetscErrorCode SNESSolve_Patch(SNES snes) 259 { 260 SNES_Patch *patch = (SNES_Patch *) snes->data; 261 PC_PATCH *pcpatch = (PC_PATCH *) patch->pc->data; 262 SNESLineSearch ls; 263 Vec rhs, update, state, residual; 264 const PetscScalar *globalState = NULL; 265 PetscScalar *localState = NULL; 266 PetscInt its = 0; 267 PetscReal xnorm = 0.0, ynorm = 0.0, fnorm = 0.0; 268 269 PetscFunctionBegin; 270 CHKERRQ(SNESGetSolution(snes, &state)); 271 CHKERRQ(SNESGetSolutionUpdate(snes, &update)); 272 CHKERRQ(SNESGetRhs(snes, &rhs)); 273 274 CHKERRQ(SNESGetFunction(snes, &residual, NULL, NULL)); 275 CHKERRQ(SNESGetLineSearch(snes, &ls)); 276 277 CHKERRQ(SNESSetConvergedReason(snes, SNES_CONVERGED_ITERATING)); 278 CHKERRQ(VecSet(update, 0.0)); 279 CHKERRQ(SNESComputeFunction(snes, state, residual)); 280 281 CHKERRQ(VecNorm(state, NORM_2, &xnorm)); 282 CHKERRQ(VecNorm(residual, NORM_2, &fnorm)); 283 snes->ttol = fnorm*snes->rtol; 284 285 if (snes->ops->converged) { 286 CHKERRQ((*snes->ops->converged)(snes,its,xnorm,ynorm,fnorm,&snes->reason,snes->cnvP)); 287 } else { 288 CHKERRQ(SNESConvergedSkip(snes,its,xnorm,ynorm,fnorm,&snes->reason,NULL)); 289 } 290 CHKERRQ(SNESLogConvergenceHistory(snes, fnorm, 0)); /* should we count lits from the patches? */ 291 CHKERRQ(SNESMonitor(snes, its, fnorm)); 292 293 /* The main solver loop */ 294 for (its = 0; its < snes->max_its; its++) { 295 296 CHKERRQ(SNESSetIterationNumber(snes, its)); 297 298 /* Scatter state vector to overlapped vector on all patches. 299 The vector pcpatch->localState is scattered to each patch 300 in PCApply_PATCH_Nonlinear. */ 301 CHKERRQ(VecGetArrayRead(state, &globalState)); 302 CHKERRQ(VecGetArray(pcpatch->localState, &localState)); 303 CHKERRQ(PetscSFBcastBegin(pcpatch->sectionSF, MPIU_SCALAR, globalState, localState,MPI_REPLACE)); 304 CHKERRQ(PetscSFBcastEnd(pcpatch->sectionSF, MPIU_SCALAR, globalState, localState,MPI_REPLACE)); 305 CHKERRQ(VecRestoreArray(pcpatch->localState, &localState)); 306 CHKERRQ(VecRestoreArrayRead(state, &globalState)); 307 308 /* The looping over patches happens here */ 309 CHKERRQ(PCApply(patch->pc, rhs, update)); 310 311 /* Apply a line search. This will often be basic with 312 damping = 1/(max number of patches a dof can be in), 313 but not always */ 314 CHKERRQ(VecScale(update, -1.0)); 315 CHKERRQ(SNESLineSearchApply(ls, state, residual, &fnorm, update)); 316 317 CHKERRQ(VecNorm(state, NORM_2, &xnorm)); 318 CHKERRQ(VecNorm(update, NORM_2, &ynorm)); 319 320 if (snes->ops->converged) { 321 CHKERRQ((*snes->ops->converged)(snes,its,xnorm,ynorm,fnorm,&snes->reason,snes->cnvP)); 322 } else { 323 CHKERRQ(SNESConvergedSkip(snes,its,xnorm,ynorm,fnorm,&snes->reason,NULL)); 324 } 325 CHKERRQ(SNESLogConvergenceHistory(snes, fnorm, 0)); /* FIXME: should we count lits? */ 326 CHKERRQ(SNESMonitor(snes, its, fnorm)); 327 } 328 329 if (its == snes->max_its) CHKERRQ(SNESSetConvergedReason(snes, SNES_DIVERGED_MAX_IT)); 330 PetscFunctionReturn(0); 331 } 332 333 /*MC 334 SNESPATCH - Solve a nonlinear problem by composing together many nonlinear solvers on patches 335 336 Level: intermediate 337 338 .seealso: SNESCreate(), SNESSetType(), SNESType (for list of available types), SNES, 339 PCPATCH 340 341 References: 342 . * - Peter R. Brune, Matthew G. Knepley, Barry F. Smith, and Xuemin Tu, "Composing Scalable Nonlinear Algebraic Solvers", SIAM Review, 57(4), 2015 343 344 M*/ 345 PETSC_EXTERN PetscErrorCode SNESCreate_Patch(SNES snes) 346 { 347 SNES_Patch *patch; 348 PC_PATCH *patchpc; 349 SNESLineSearch linesearch; 350 351 PetscFunctionBegin; 352 CHKERRQ(PetscNewLog(snes, &patch)); 353 354 snes->ops->solve = SNESSolve_Patch; 355 snes->ops->setup = SNESSetUp_Patch; 356 snes->ops->reset = SNESReset_Patch; 357 snes->ops->destroy = SNESDestroy_Patch; 358 snes->ops->setfromoptions = SNESSetFromOptions_Patch; 359 snes->ops->view = SNESView_Patch; 360 361 CHKERRQ(SNESGetLineSearch(snes,&linesearch)); 362 if (!((PetscObject)linesearch)->type_name) { 363 CHKERRQ(SNESLineSearchSetType(linesearch,SNESLINESEARCHBASIC)); 364 } 365 snes->usesksp = PETSC_FALSE; 366 367 snes->alwayscomputesfinalresidual = PETSC_FALSE; 368 369 snes->data = (void *) patch; 370 CHKERRQ(PCCreate(PetscObjectComm((PetscObject) snes), &patch->pc)); 371 CHKERRQ(PCSetType(patch->pc, PCPATCH)); 372 373 patchpc = (PC_PATCH*) patch->pc->data; 374 patchpc->classname = "snes"; 375 patchpc->isNonlinear = PETSC_TRUE; 376 377 patchpc->setupsolver = PCSetUp_PATCH_Nonlinear; 378 patchpc->applysolver = PCApply_PATCH_Nonlinear; 379 patchpc->resetsolver = PCReset_PATCH_Nonlinear; 380 patchpc->destroysolver = PCDestroy_PATCH_Nonlinear; 381 patchpc->updatemultiplicative = PCUpdateMultiplicative_PATCH_Nonlinear; 382 383 PetscFunctionReturn(0); 384 } 385 386 PetscErrorCode SNESPatchSetDiscretisationInfo(SNES snes, PetscInt nsubspaces, DM *dms, PetscInt *bs, PetscInt *nodesPerCell, const PetscInt **cellNodeMap, 387 const PetscInt *subspaceOffsets, PetscInt numGhostBcs, const PetscInt *ghostBcNodes, PetscInt numGlobalBcs, const PetscInt *globalBcNodes) 388 { 389 SNES_Patch *patch = (SNES_Patch *) snes->data; 390 DM dm; 391 392 PetscFunctionBegin; 393 CHKERRQ(SNESGetDM(snes, &dm)); 394 PetscCheckFalse(!dm,PetscObjectComm((PetscObject)snes), PETSC_ERR_ARG_WRONGSTATE, "DM not yet set on patch SNES"); 395 CHKERRQ(PCSetDM(patch->pc, dm)); 396 CHKERRQ(PCPatchSetDiscretisationInfo(patch->pc, nsubspaces, dms, bs, nodesPerCell, cellNodeMap, subspaceOffsets, numGhostBcs, ghostBcNodes, numGlobalBcs, globalBcNodes)); 397 PetscFunctionReturn(0); 398 } 399 400 PetscErrorCode SNESPatchSetComputeOperator(SNES snes, PetscErrorCode (*func)(PC, PetscInt, Vec, Mat, IS, PetscInt, const PetscInt *, const PetscInt *, void *), void *ctx) 401 { 402 SNES_Patch *patch = (SNES_Patch *) snes->data; 403 404 PetscFunctionBegin; 405 CHKERRQ(PCPatchSetComputeOperator(patch->pc, func, ctx)); 406 PetscFunctionReturn(0); 407 } 408 409 PetscErrorCode SNESPatchSetComputeFunction(SNES snes, PetscErrorCode (*func)(PC, PetscInt, Vec, Vec, IS, PetscInt, const PetscInt *, const PetscInt *, void *), void *ctx) 410 { 411 SNES_Patch *patch = (SNES_Patch *) snes->data; 412 413 PetscFunctionBegin; 414 CHKERRQ(PCPatchSetComputeFunction(patch->pc, func, ctx)); 415 PetscFunctionReturn(0); 416 } 417 418 PetscErrorCode SNESPatchSetConstructType(SNES snes, PCPatchConstructType ctype, PetscErrorCode (*func)(PC, PetscInt *, IS **, IS *, void *), void *ctx) 419 { 420 SNES_Patch *patch = (SNES_Patch *) snes->data; 421 422 PetscFunctionBegin; 423 CHKERRQ(PCPatchSetConstructType(patch->pc, ctype, func, ctx)); 424 PetscFunctionReturn(0); 425 } 426 427 PetscErrorCode SNESPatchSetCellNumbering(SNES snes, PetscSection cellNumbering) 428 { 429 SNES_Patch *patch = (SNES_Patch *) snes->data; 430 431 PetscFunctionBegin; 432 CHKERRQ(PCPatchSetCellNumbering(patch->pc, cellNumbering)); 433 PetscFunctionReturn(0); 434 } 435