1 /* Defines the basic SNES object */ 2 #include <../src/snes/impls/fas/fasimpls.h> /*I "petscsnes.h" I*/ 3 4 const char *const SNESFASTypes[] = {"MULTIPLICATIVE", "ADDITIVE", "FULL", "KASKADE", "SNESFASType", "SNES_FAS", NULL}; 5 6 static PetscErrorCode SNESReset_FAS(SNES snes) 7 { 8 SNES_FAS *fas = (SNES_FAS *)snes->data; 9 10 PetscFunctionBegin; 11 PetscCall(SNESDestroy(&fas->smoothu)); 12 PetscCall(SNESDestroy(&fas->smoothd)); 13 PetscCall(MatDestroy(&fas->inject)); 14 PetscCall(MatDestroy(&fas->interpolate)); 15 PetscCall(MatDestroy(&fas->restrct)); 16 PetscCall(VecDestroy(&fas->rscale)); 17 PetscCall(VecDestroy(&fas->Xg)); 18 PetscCall(VecDestroy(&fas->Fg)); 19 if (fas->next) PetscCall(SNESReset(fas->next)); 20 PetscFunctionReturn(0); 21 } 22 23 static PetscErrorCode SNESDestroy_FAS(SNES snes) 24 { 25 SNES_FAS *fas = (SNES_FAS *)snes->data; 26 27 PetscFunctionBegin; 28 /* recursively resets and then destroys */ 29 PetscCall(SNESReset_FAS(snes)); 30 PetscCall(SNESDestroy(&fas->next)); 31 PetscCall(PetscFree(fas)); 32 PetscFunctionReturn(0); 33 } 34 35 static PetscErrorCode SNESFASSetUpLineSearch_Private(SNES snes, SNES smooth) 36 { 37 SNESLineSearch linesearch; 38 SNESLineSearch slinesearch; 39 void *lsprectx, *lspostctx; 40 PetscErrorCode (*precheck)(SNESLineSearch, Vec, Vec, PetscBool *, void *); 41 PetscErrorCode (*postcheck)(SNESLineSearch, Vec, Vec, Vec, PetscBool *, PetscBool *, void *); 42 43 PetscFunctionBegin; 44 if (!snes->linesearch) PetscFunctionReturn(0); 45 PetscCall(SNESGetLineSearch(snes, &linesearch)); 46 PetscCall(SNESGetLineSearch(smooth, &slinesearch)); 47 PetscCall(SNESLineSearchGetPreCheck(linesearch, &precheck, &lsprectx)); 48 PetscCall(SNESLineSearchGetPostCheck(linesearch, &postcheck, &lspostctx)); 49 PetscCall(SNESLineSearchSetPreCheck(slinesearch, precheck, lsprectx)); 50 PetscCall(SNESLineSearchSetPostCheck(slinesearch, postcheck, lspostctx)); 51 PetscCall(PetscObjectCopyFortranFunctionPointers((PetscObject)linesearch, (PetscObject)slinesearch)); 52 PetscFunctionReturn(0); 53 } 54 55 static PetscErrorCode SNESFASCycleSetUpSmoother_Private(SNES snes, SNES smooth) 56 { 57 SNES_FAS *fas = (SNES_FAS *)snes->data; 58 59 PetscFunctionBegin; 60 PetscCall(PetscObjectCopyFortranFunctionPointers((PetscObject)snes, (PetscObject)smooth)); 61 PetscCall(SNESSetFromOptions(smooth)); 62 PetscCall(SNESFASSetUpLineSearch_Private(snes, smooth)); 63 64 PetscCall(PetscObjectReference((PetscObject)snes->vec_sol)); 65 PetscCall(PetscObjectReference((PetscObject)snes->vec_sol_update)); 66 PetscCall(PetscObjectReference((PetscObject)snes->vec_func)); 67 smooth->vec_sol = snes->vec_sol; 68 smooth->vec_sol_update = snes->vec_sol_update; 69 smooth->vec_func = snes->vec_func; 70 71 if (fas->eventsmoothsetup) PetscCall(PetscLogEventBegin(fas->eventsmoothsetup, smooth, 0, 0, 0)); 72 PetscCall(SNESSetUp(smooth)); 73 if (fas->eventsmoothsetup) PetscCall(PetscLogEventEnd(fas->eventsmoothsetup, smooth, 0, 0, 0)); 74 PetscFunctionReturn(0); 75 } 76 77 static PetscErrorCode SNESSetUp_FAS(SNES snes) 78 { 79 SNES_FAS *fas = (SNES_FAS *)snes->data; 80 PetscInt dm_levels; 81 SNES next; 82 PetscBool isFine, hasCreateRestriction, hasCreateInjection; 83 84 PetscFunctionBegin; 85 PetscCall(SNESFASCycleIsFine(snes, &isFine)); 86 if (fas->usedmfornumberoflevels && isFine) { 87 PetscCall(DMGetRefineLevel(snes->dm, &dm_levels)); 88 dm_levels++; 89 if (dm_levels > fas->levels) { 90 /* reset the number of levels */ 91 PetscCall(SNESFASSetLevels(snes, dm_levels, NULL)); 92 PetscCall(SNESSetFromOptions(snes)); 93 } 94 } 95 PetscCall(SNESFASCycleGetCorrection(snes, &next)); 96 if (!isFine) snes->gridsequence = 0; /* no grid sequencing inside the multigrid hierarchy! */ 97 98 PetscCall(SNESSetWorkVecs(snes, 2)); /* work vectors used for intergrid transfers */ 99 100 /* set up the smoothers if they haven't already been set up */ 101 if (!fas->smoothd) PetscCall(SNESFASCycleCreateSmoother_Private(snes, &fas->smoothd)); 102 103 if (snes->dm) { 104 /* set the smoother DMs properly */ 105 if (fas->smoothu) PetscCall(SNESSetDM(fas->smoothu, snes->dm)); 106 PetscCall(SNESSetDM(fas->smoothd, snes->dm)); 107 /* construct EVERYTHING from the DM -- including the progressive set of smoothers */ 108 if (next) { 109 /* for now -- assume the DM and the evaluation functions have been set externally */ 110 if (!next->dm) { 111 PetscCall(DMCoarsen(snes->dm, PetscObjectComm((PetscObject)next), &next->dm)); 112 PetscCall(SNESSetDM(next, next->dm)); 113 } 114 /* set the interpolation and restriction from the DM */ 115 if (!fas->interpolate) { 116 PetscCall(DMCreateInterpolation(next->dm, snes->dm, &fas->interpolate, &fas->rscale)); 117 if (!fas->restrct) { 118 PetscCall(DMHasCreateRestriction(next->dm, &hasCreateRestriction)); 119 /* DM can create restrictions, use that */ 120 if (hasCreateRestriction) { 121 PetscCall(DMCreateRestriction(next->dm, snes->dm, &fas->restrct)); 122 } else { 123 PetscCall(PetscObjectReference((PetscObject)fas->interpolate)); 124 fas->restrct = fas->interpolate; 125 } 126 } 127 } 128 /* set the injection from the DM */ 129 if (!fas->inject) { 130 PetscCall(DMHasCreateInjection(next->dm, &hasCreateInjection)); 131 if (hasCreateInjection) PetscCall(DMCreateInjection(next->dm, snes->dm, &fas->inject)); 132 } 133 } 134 } 135 136 /*pass the smoother, function, and jacobian up to the next level if it's not user set already */ 137 if (fas->galerkin) { 138 if (next) PetscCall(SNESSetFunction(next, NULL, SNESFASGalerkinFunctionDefault, next)); 139 if (fas->smoothd && fas->level != fas->levels - 1) PetscCall(SNESSetFunction(fas->smoothd, NULL, SNESFASGalerkinFunctionDefault, snes)); 140 if (fas->smoothu && fas->level != fas->levels - 1) PetscCall(SNESSetFunction(fas->smoothu, NULL, SNESFASGalerkinFunctionDefault, snes)); 141 } 142 143 /* sets the down (pre) smoother's default norm and sets it from options */ 144 if (fas->smoothd) { 145 if (fas->level == 0 && fas->levels != 1) { 146 PetscCall(SNESSetNormSchedule(fas->smoothd, SNES_NORM_NONE)); 147 } else { 148 PetscCall(SNESSetNormSchedule(fas->smoothd, SNES_NORM_FINAL_ONLY)); 149 } 150 PetscCall(SNESFASCycleSetUpSmoother_Private(snes, fas->smoothd)); 151 } 152 153 /* sets the up (post) smoother's default norm and sets it from options */ 154 if (fas->smoothu) { 155 if (fas->level != fas->levels - 1) { 156 PetscCall(SNESSetNormSchedule(fas->smoothu, SNES_NORM_NONE)); 157 } else { 158 PetscCall(SNESSetNormSchedule(fas->smoothu, SNES_NORM_FINAL_ONLY)); 159 } 160 PetscCall(SNESFASCycleSetUpSmoother_Private(snes, fas->smoothu)); 161 } 162 163 if (next) { 164 /* gotta set up the solution vector for this to work */ 165 if (!next->vec_sol) PetscCall(SNESFASCreateCoarseVec(snes, &next->vec_sol)); 166 if (!next->vec_rhs) PetscCall(SNESFASCreateCoarseVec(snes, &next->vec_rhs)); 167 PetscCall(PetscObjectCopyFortranFunctionPointers((PetscObject)snes, (PetscObject)next)); 168 PetscCall(SNESFASSetUpLineSearch_Private(snes, next)); 169 PetscCall(SNESSetUp(next)); 170 } 171 172 /* setup FAS work vectors */ 173 if (fas->galerkin) { 174 PetscCall(VecDuplicate(snes->vec_sol, &fas->Xg)); 175 PetscCall(VecDuplicate(snes->vec_sol, &fas->Fg)); 176 } 177 PetscFunctionReturn(0); 178 } 179 180 static PetscErrorCode SNESSetFromOptions_FAS(SNES snes, PetscOptionItems *PetscOptionsObject) 181 { 182 SNES_FAS *fas = (SNES_FAS *)snes->data; 183 PetscInt levels = 1; 184 PetscBool flg = PETSC_FALSE, upflg = PETSC_FALSE, downflg = PETSC_FALSE, monflg = PETSC_FALSE, galerkinflg = PETSC_FALSE, continuationflg = PETSC_FALSE; 185 SNESFASType fastype; 186 const char *optionsprefix; 187 SNESLineSearch linesearch; 188 PetscInt m, n_up, n_down; 189 SNES next; 190 PetscBool isFine; 191 192 PetscFunctionBegin; 193 PetscCall(SNESFASCycleIsFine(snes, &isFine)); 194 PetscOptionsHeadBegin(PetscOptionsObject, "SNESFAS Options-----------------------------------"); 195 196 /* number of levels -- only process most options on the finest level */ 197 if (isFine) { 198 PetscCall(PetscOptionsInt("-snes_fas_levels", "Number of Levels", "SNESFASSetLevels", levels, &levels, &flg)); 199 if (!flg && snes->dm) { 200 PetscCall(DMGetRefineLevel(snes->dm, &levels)); 201 levels++; 202 fas->usedmfornumberoflevels = PETSC_TRUE; 203 } 204 PetscCall(SNESFASSetLevels(snes, levels, NULL)); 205 fastype = fas->fastype; 206 PetscCall(PetscOptionsEnum("-snes_fas_type", "FAS correction type", "SNESFASSetType", SNESFASTypes, (PetscEnum)fastype, (PetscEnum *)&fastype, &flg)); 207 if (flg) PetscCall(SNESFASSetType(snes, fastype)); 208 209 PetscCall(SNESGetOptionsPrefix(snes, &optionsprefix)); 210 PetscCall(PetscOptionsInt("-snes_fas_cycles", "Number of cycles", "SNESFASSetCycles", fas->n_cycles, &m, &flg)); 211 if (flg) PetscCall(SNESFASSetCycles(snes, m)); 212 PetscCall(PetscOptionsBool("-snes_fas_continuation", "Corrected grid-sequence continuation", "SNESFASSetContinuation", fas->continuation, &continuationflg, &flg)); 213 if (flg) PetscCall(SNESFASSetContinuation(snes, continuationflg)); 214 215 PetscCall(PetscOptionsBool("-snes_fas_galerkin", "Form coarse problems with Galerkin", "SNESFASSetGalerkin", fas->galerkin, &galerkinflg, &flg)); 216 if (flg) PetscCall(SNESFASSetGalerkin(snes, galerkinflg)); 217 218 if (fas->fastype == SNES_FAS_FULL) { 219 PetscCall(PetscOptionsBool("-snes_fas_full_downsweep", "Smooth on the initial down sweep for full FAS cycles", "SNESFASFullSetDownSweep", fas->full_downsweep, &fas->full_downsweep, &flg)); 220 if (flg) PetscCall(SNESFASFullSetDownSweep(snes, fas->full_downsweep)); 221 PetscCall(PetscOptionsBool("-snes_fas_full_total", "Use total restriction and interpolaton on the indial down and up sweeps for the full FAS cycle", "SNESFASFullSetUseTotal", fas->full_total, &fas->full_total, &flg)); 222 if (flg) PetscCall(SNESFASFullSetTotal(snes, fas->full_total)); 223 } 224 225 PetscCall(PetscOptionsInt("-snes_fas_smoothup", "Number of post-smoothing steps", "SNESFASSetNumberSmoothUp", fas->max_up_it, &n_up, &upflg)); 226 227 PetscCall(PetscOptionsInt("-snes_fas_smoothdown", "Number of pre-smoothing steps", "SNESFASSetNumberSmoothDown", fas->max_down_it, &n_down, &downflg)); 228 229 { 230 PetscViewer viewer; 231 PetscViewerFormat format; 232 PetscCall(PetscOptionsGetViewer(PetscObjectComm((PetscObject)snes), ((PetscObject)snes)->options, ((PetscObject)snes)->prefix, "-snes_fas_monitor", &viewer, &format, &monflg)); 233 if (monflg) { 234 PetscViewerAndFormat *vf; 235 PetscCall(PetscViewerAndFormatCreate(viewer, format, &vf)); 236 PetscCall(PetscObjectDereference((PetscObject)viewer)); 237 PetscCall(SNESFASSetMonitor(snes, vf, PETSC_TRUE)); 238 } 239 } 240 flg = PETSC_FALSE; 241 monflg = PETSC_TRUE; 242 PetscCall(PetscOptionsBool("-snes_fas_log", "Log times for each FAS level", "SNESFASSetLog", monflg, &monflg, &flg)); 243 if (flg) PetscCall(SNESFASSetLog(snes, monflg)); 244 } 245 246 PetscOptionsHeadEnd(); 247 248 /* setup from the determined types if there is no pointwise procedure or smoother defined */ 249 if (upflg) PetscCall(SNESFASSetNumberSmoothUp(snes, n_up)); 250 if (downflg) PetscCall(SNESFASSetNumberSmoothDown(snes, n_down)); 251 252 /* set up the default line search for coarse grid corrections */ 253 if (fas->fastype == SNES_FAS_ADDITIVE) { 254 if (!snes->linesearch) { 255 PetscCall(SNESGetLineSearch(snes, &linesearch)); 256 PetscCall(SNESLineSearchSetType(linesearch, SNESLINESEARCHL2)); 257 } 258 } 259 260 /* recursive option setting for the smoothers */ 261 PetscCall(SNESFASCycleGetCorrection(snes, &next)); 262 if (next) PetscCall(SNESSetFromOptions(next)); 263 PetscFunctionReturn(0); 264 } 265 266 #include <petscdraw.h> 267 static PetscErrorCode SNESView_FAS(SNES snes, PetscViewer viewer) 268 { 269 SNES_FAS *fas = (SNES_FAS *)snes->data; 270 PetscBool isFine, iascii, isdraw; 271 PetscInt i; 272 SNES smoothu, smoothd, levelsnes; 273 274 PetscFunctionBegin; 275 PetscCall(SNESFASCycleIsFine(snes, &isFine)); 276 if (isFine) { 277 PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERASCII, &iascii)); 278 PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERDRAW, &isdraw)); 279 if (iascii) { 280 PetscCall(PetscViewerASCIIPrintf(viewer, " type is %s, levels=%" PetscInt_FMT ", cycles=%" PetscInt_FMT "\n", SNESFASTypes[fas->fastype], fas->levels, fas->n_cycles)); 281 if (fas->galerkin) { 282 PetscCall(PetscViewerASCIIPrintf(viewer, " Using Galerkin computed coarse grid function evaluation\n")); 283 } else { 284 PetscCall(PetscViewerASCIIPrintf(viewer, " Not using Galerkin computed coarse grid function evaluation\n")); 285 } 286 for (i = 0; i < fas->levels; i++) { 287 PetscCall(SNESFASGetCycleSNES(snes, i, &levelsnes)); 288 PetscCall(SNESFASCycleGetSmootherUp(levelsnes, &smoothu)); 289 PetscCall(SNESFASCycleGetSmootherDown(levelsnes, &smoothd)); 290 if (!i) { 291 PetscCall(PetscViewerASCIIPrintf(viewer, " Coarse grid solver -- level %" PetscInt_FMT " -------------------------------\n", i)); 292 } else { 293 PetscCall(PetscViewerASCIIPrintf(viewer, " Down solver (pre-smoother) on level %" PetscInt_FMT " -------------------------------\n", i)); 294 } 295 PetscCall(PetscViewerASCIIPushTab(viewer)); 296 if (smoothd) { 297 PetscCall(SNESView(smoothd, viewer)); 298 } else { 299 PetscCall(PetscViewerASCIIPrintf(viewer, "Not yet available\n")); 300 } 301 PetscCall(PetscViewerASCIIPopTab(viewer)); 302 if (i && (smoothd == smoothu)) { 303 PetscCall(PetscViewerASCIIPrintf(viewer, " Up solver (post-smoother) same as down solver (pre-smoother)\n")); 304 } else if (i) { 305 PetscCall(PetscViewerASCIIPrintf(viewer, " Up solver (post-smoother) on level %" PetscInt_FMT " -------------------------------\n", i)); 306 PetscCall(PetscViewerASCIIPushTab(viewer)); 307 if (smoothu) { 308 PetscCall(SNESView(smoothu, viewer)); 309 } else { 310 PetscCall(PetscViewerASCIIPrintf(viewer, "Not yet available\n")); 311 } 312 PetscCall(PetscViewerASCIIPopTab(viewer)); 313 } 314 } 315 } else if (isdraw) { 316 PetscDraw draw; 317 PetscReal x, w, y, bottom, th, wth; 318 SNES_FAS *curfas = fas; 319 PetscCall(PetscViewerDrawGetDraw(viewer, 0, &draw)); 320 PetscCall(PetscDrawGetCurrentPoint(draw, &x, &y)); 321 PetscCall(PetscDrawStringGetSize(draw, &wth, &th)); 322 bottom = y - th; 323 while (curfas) { 324 if (!curfas->smoothu) { 325 PetscCall(PetscDrawPushCurrentPoint(draw, x, bottom)); 326 if (curfas->smoothd) PetscCall(SNESView(curfas->smoothd, viewer)); 327 PetscCall(PetscDrawPopCurrentPoint(draw)); 328 } else { 329 w = 0.5 * PetscMin(1.0 - x, x); 330 PetscCall(PetscDrawPushCurrentPoint(draw, x - w, bottom)); 331 if (curfas->smoothd) PetscCall(SNESView(curfas->smoothd, viewer)); 332 PetscCall(PetscDrawPopCurrentPoint(draw)); 333 PetscCall(PetscDrawPushCurrentPoint(draw, x + w, bottom)); 334 if (curfas->smoothu) PetscCall(SNESView(curfas->smoothu, viewer)); 335 PetscCall(PetscDrawPopCurrentPoint(draw)); 336 } 337 /* this is totally bogus but we have no way of knowing how low the previous one was draw to */ 338 bottom -= 5 * th; 339 if (curfas->next) curfas = (SNES_FAS *)curfas->next->data; 340 else curfas = NULL; 341 } 342 } 343 } 344 PetscFunctionReturn(0); 345 } 346 347 /* 348 Defines the action of the downsmoother 349 */ 350 static PetscErrorCode SNESFASDownSmooth_Private(SNES snes, Vec B, Vec X, Vec F, PetscReal *fnorm) 351 { 352 SNESConvergedReason reason; 353 Vec FPC; 354 SNES smoothd; 355 PetscBool flg; 356 SNES_FAS *fas = (SNES_FAS *)snes->data; 357 358 PetscFunctionBegin; 359 PetscCall(SNESFASCycleGetSmootherDown(snes, &smoothd)); 360 PetscCall(SNESSetInitialFunction(smoothd, F)); 361 if (fas->eventsmoothsolve) PetscCall(PetscLogEventBegin(fas->eventsmoothsolve, smoothd, B, X, 0)); 362 PetscCall(SNESSolve(smoothd, B, X)); 363 if (fas->eventsmoothsolve) PetscCall(PetscLogEventEnd(fas->eventsmoothsolve, smoothd, B, X, 0)); 364 /* check convergence reason for the smoother */ 365 PetscCall(SNESGetConvergedReason(smoothd, &reason)); 366 if (reason < 0 && !(reason == SNES_DIVERGED_MAX_IT || reason == SNES_DIVERGED_LOCAL_MIN || reason == SNES_DIVERGED_LINE_SEARCH)) { 367 snes->reason = SNES_DIVERGED_INNER; 368 PetscFunctionReturn(0); 369 } 370 371 PetscCall(SNESGetFunction(smoothd, &FPC, NULL, NULL)); 372 PetscCall(SNESGetAlwaysComputesFinalResidual(smoothd, &flg)); 373 if (!flg) PetscCall(SNESComputeFunction(smoothd, X, FPC)); 374 PetscCall(VecCopy(FPC, F)); 375 if (fnorm) PetscCall(VecNorm(F, NORM_2, fnorm)); 376 PetscFunctionReturn(0); 377 } 378 379 /* 380 Defines the action of the upsmoother 381 */ 382 static PetscErrorCode SNESFASUpSmooth_Private(SNES snes, Vec B, Vec X, Vec F, PetscReal *fnorm) 383 { 384 SNESConvergedReason reason; 385 Vec FPC; 386 SNES smoothu; 387 PetscBool flg; 388 SNES_FAS *fas = (SNES_FAS *)snes->data; 389 390 PetscFunctionBegin; 391 PetscCall(SNESFASCycleGetSmootherUp(snes, &smoothu)); 392 if (fas->eventsmoothsolve) PetscCall(PetscLogEventBegin(fas->eventsmoothsolve, smoothu, 0, 0, 0)); 393 PetscCall(SNESSolve(smoothu, B, X)); 394 if (fas->eventsmoothsolve) PetscCall(PetscLogEventEnd(fas->eventsmoothsolve, smoothu, 0, 0, 0)); 395 /* check convergence reason for the smoother */ 396 PetscCall(SNESGetConvergedReason(smoothu, &reason)); 397 if (reason < 0 && !(reason == SNES_DIVERGED_MAX_IT || reason == SNES_DIVERGED_LOCAL_MIN || reason == SNES_DIVERGED_LINE_SEARCH)) { 398 snes->reason = SNES_DIVERGED_INNER; 399 PetscFunctionReturn(0); 400 } 401 PetscCall(SNESGetFunction(smoothu, &FPC, NULL, NULL)); 402 PetscCall(SNESGetAlwaysComputesFinalResidual(smoothu, &flg)); 403 if (!flg) PetscCall(SNESComputeFunction(smoothu, X, FPC)); 404 PetscCall(VecCopy(FPC, F)); 405 if (fnorm) PetscCall(VecNorm(F, NORM_2, fnorm)); 406 PetscFunctionReturn(0); 407 } 408 409 /*@ 410 SNESFASCreateCoarseVec - create `Vec` corresponding to a state vector on one level coarser than current level 411 412 Collective 413 414 Input Parameter: 415 . snes - `SNESFAS` object 416 417 Output Parameter: 418 . Xcoarse - vector on level one coarser than snes 419 420 Level: developer 421 422 .seealso: `SNESFASSetRestriction()`, `SNESFASRestrict()` 423 @*/ 424 PetscErrorCode SNESFASCreateCoarseVec(SNES snes, Vec *Xcoarse) 425 { 426 SNES_FAS *fas; 427 428 PetscFunctionBegin; 429 PetscValidHeaderSpecificType(snes, SNES_CLASSID, 1, SNESFAS); 430 PetscValidPointer(Xcoarse, 2); 431 fas = (SNES_FAS *)snes->data; 432 if (fas->rscale) { 433 PetscCall(VecDuplicate(fas->rscale, Xcoarse)); 434 } else if (fas->inject) { 435 PetscCall(MatCreateVecs(fas->inject, Xcoarse, NULL)); 436 } else SETERRQ(PetscObjectComm((PetscObject)snes), PETSC_ERR_ARG_WRONGSTATE, "Must set restriction or injection"); 437 PetscFunctionReturn(0); 438 } 439 440 /*@ 441 SNESFASRestrict - restrict a `Vec` to the next coarser level 442 443 Collective 444 445 Input Parameters: 446 + fine - `SNES` from which to restrict 447 - Xfine - vector to restrict 448 449 Output Parameter: 450 . Xcoarse - result of restriction 451 452 Level: developer 453 454 .seealso: `SNES`, `SNESFAS`, `SNESFASSetRestriction()`, `SNESFASSetInjection()` 455 @*/ 456 PetscErrorCode SNESFASRestrict(SNES fine, Vec Xfine, Vec Xcoarse) 457 { 458 SNES_FAS *fas; 459 460 PetscFunctionBegin; 461 PetscValidHeaderSpecificType(fine, SNES_CLASSID, 1, SNESFAS); 462 PetscValidHeaderSpecific(Xfine, VEC_CLASSID, 2); 463 PetscValidHeaderSpecific(Xcoarse, VEC_CLASSID, 3); 464 fas = (SNES_FAS *)fine->data; 465 if (fas->inject) { 466 PetscCall(MatRestrict(fas->inject, Xfine, Xcoarse)); 467 } else { 468 PetscCall(MatRestrict(fas->restrct, Xfine, Xcoarse)); 469 PetscCall(VecPointwiseMult(Xcoarse, fas->rscale, Xcoarse)); 470 } 471 PetscFunctionReturn(0); 472 } 473 474 /* 475 476 Performs a variant of FAS using the interpolated total coarse solution 477 478 fine problem: F(x) = b 479 coarse problem: F^c(x^c) = Rb, Initial guess Rx 480 interpolated solution: x^f = I x^c (total solution interpolation 481 482 */ 483 static PetscErrorCode SNESFASInterpolatedCoarseSolution(SNES snes, Vec X, Vec X_new) 484 { 485 Vec X_c, B_c; 486 SNESConvergedReason reason; 487 SNES next; 488 Mat restrct, interpolate; 489 SNES_FAS *fasc; 490 491 PetscFunctionBegin; 492 PetscCall(SNESFASCycleGetCorrection(snes, &next)); 493 if (next) { 494 fasc = (SNES_FAS *)next->data; 495 496 PetscCall(SNESFASCycleGetRestriction(snes, &restrct)); 497 PetscCall(SNESFASCycleGetInterpolation(snes, &interpolate)); 498 499 X_c = next->vec_sol; 500 501 if (fasc->eventinterprestrict) PetscCall(PetscLogEventBegin(fasc->eventinterprestrict, snes, 0, 0, 0)); 502 /* restrict the total solution: Rb */ 503 PetscCall(SNESFASRestrict(snes, X, X_c)); 504 B_c = next->vec_rhs; 505 if (snes->vec_rhs) { 506 /* restrict the total rhs defect: Rb */ 507 PetscCall(MatRestrict(restrct, snes->vec_rhs, B_c)); 508 } else { 509 PetscCall(VecSet(B_c, 0.)); 510 } 511 if (fasc->eventinterprestrict) PetscCall(PetscLogEventEnd(fasc->eventinterprestrict, snes, 0, 0, 0)); 512 513 PetscCall(SNESSolve(next, B_c, X_c)); 514 PetscCall(SNESGetConvergedReason(next, &reason)); 515 if (reason < 0 && reason != SNES_DIVERGED_MAX_IT) { 516 snes->reason = SNES_DIVERGED_INNER; 517 PetscFunctionReturn(0); 518 } 519 /* x^f <- Ix^c*/ 520 DM dmc, dmf; 521 522 PetscCall(SNESGetDM(next, &dmc)); 523 PetscCall(SNESGetDM(snes, &dmf)); 524 if (fasc->eventinterprestrict) PetscCall(PetscLogEventBegin(fasc->eventinterprestrict, snes, 0, 0, 0)); 525 PetscCall(DMInterpolateSolution(dmc, dmf, interpolate, X_c, X_new)); 526 if (fasc->eventinterprestrict) PetscCall(PetscLogEventEnd(fasc->eventinterprestrict, snes, 0, 0, 0)); 527 PetscCall(PetscObjectSetName((PetscObject)X_c, "Coarse solution")); 528 PetscCall(VecViewFromOptions(X_c, NULL, "-fas_coarse_solution_view")); 529 PetscCall(PetscObjectSetName((PetscObject)X_new, "Updated Fine solution")); 530 PetscCall(VecViewFromOptions(X_new, NULL, "-fas_levels_1_solution_view")); 531 } 532 PetscFunctionReturn(0); 533 } 534 535 /* 536 537 Performs the FAS coarse correction as: 538 539 fine problem: F(x) = b 540 coarse problem: F^c(x^c) = b^c 541 542 b^c = F^c(Rx) - R(F(x) - b) 543 544 */ 545 PetscErrorCode SNESFASCoarseCorrection(SNES snes, Vec X, Vec F, Vec X_new) 546 { 547 Vec X_c, Xo_c, F_c, B_c; 548 SNESConvergedReason reason; 549 SNES next; 550 Mat restrct, interpolate; 551 SNES_FAS *fasc; 552 553 PetscFunctionBegin; 554 PetscCall(SNESFASCycleGetCorrection(snes, &next)); 555 if (next) { 556 fasc = (SNES_FAS *)next->data; 557 558 PetscCall(SNESFASCycleGetRestriction(snes, &restrct)); 559 PetscCall(SNESFASCycleGetInterpolation(snes, &interpolate)); 560 561 X_c = next->vec_sol; 562 Xo_c = next->work[0]; 563 F_c = next->vec_func; 564 B_c = next->vec_rhs; 565 566 if (fasc->eventinterprestrict) PetscCall(PetscLogEventBegin(fasc->eventinterprestrict, snes, 0, 0, 0)); 567 PetscCall(SNESFASRestrict(snes, X, Xo_c)); 568 /* restrict the defect: R(F(x) - b) */ 569 PetscCall(MatRestrict(restrct, F, B_c)); 570 if (fasc->eventinterprestrict) PetscCall(PetscLogEventEnd(fasc->eventinterprestrict, snes, 0, 0, 0)); 571 572 if (fasc->eventresidual) PetscCall(PetscLogEventBegin(fasc->eventresidual, next, 0, 0, 0)); 573 /* F_c = F^c(Rx) - R(F(x) - b) since the second term was sitting in next->vec_rhs */ 574 PetscCall(SNESComputeFunction(next, Xo_c, F_c)); 575 if (fasc->eventresidual) PetscCall(PetscLogEventEnd(fasc->eventresidual, next, 0, 0, 0)); 576 577 /* solve the coarse problem corresponding to F^c(x^c) = b^c = F^c(Rx) - R(F(x) - b) */ 578 PetscCall(VecCopy(B_c, X_c)); 579 PetscCall(VecCopy(F_c, B_c)); 580 PetscCall(VecCopy(X_c, F_c)); 581 /* set initial guess of the coarse problem to the projected fine solution */ 582 PetscCall(VecCopy(Xo_c, X_c)); 583 584 /* recurse to the next level */ 585 PetscCall(SNESSetInitialFunction(next, F_c)); 586 PetscCall(SNESSolve(next, B_c, X_c)); 587 PetscCall(SNESGetConvergedReason(next, &reason)); 588 if (reason < 0 && reason != SNES_DIVERGED_MAX_IT) { 589 snes->reason = SNES_DIVERGED_INNER; 590 PetscFunctionReturn(0); 591 } 592 /* correct as x <- x + I(x^c - Rx)*/ 593 PetscCall(VecAXPY(X_c, -1.0, Xo_c)); 594 595 if (fasc->eventinterprestrict) PetscCall(PetscLogEventBegin(fasc->eventinterprestrict, snes, 0, 0, 0)); 596 PetscCall(MatInterpolateAdd(interpolate, X_c, X, X_new)); 597 if (fasc->eventinterprestrict) PetscCall(PetscLogEventEnd(fasc->eventinterprestrict, snes, 0, 0, 0)); 598 PetscCall(PetscObjectSetName((PetscObject)X_c, "Coarse correction")); 599 PetscCall(VecViewFromOptions(X_c, NULL, "-fas_coarse_solution_view")); 600 PetscCall(PetscObjectSetName((PetscObject)X_new, "Updated Fine solution")); 601 PetscCall(VecViewFromOptions(X_new, NULL, "-fas_levels_1_solution_view")); 602 } 603 PetscFunctionReturn(0); 604 } 605 606 /* 607 608 The additive cycle looks like: 609 610 xhat = x 611 xhat = dS(x, b) 612 x = coarsecorrection(xhat, b_d) 613 x = x + nu*(xhat - x); 614 (optional) x = uS(x, b) 615 616 With the coarse RHS (defect correction) as below. 617 618 */ 619 static PetscErrorCode SNESFASCycle_Additive(SNES snes, Vec X) 620 { 621 Vec F, B, Xhat; 622 Vec X_c, Xo_c, F_c, B_c; 623 SNESConvergedReason reason; 624 PetscReal xnorm, fnorm, ynorm; 625 SNESLineSearchReason lsresult; 626 SNES next; 627 Mat restrct, interpolate; 628 SNES_FAS *fas = (SNES_FAS *)snes->data, *fasc; 629 630 PetscFunctionBegin; 631 PetscCall(SNESFASCycleGetCorrection(snes, &next)); 632 F = snes->vec_func; 633 B = snes->vec_rhs; 634 Xhat = snes->work[1]; 635 PetscCall(VecCopy(X, Xhat)); 636 /* recurse first */ 637 if (next) { 638 fasc = (SNES_FAS *)next->data; 639 PetscCall(SNESFASCycleGetRestriction(snes, &restrct)); 640 PetscCall(SNESFASCycleGetInterpolation(snes, &interpolate)); 641 if (fas->eventresidual) PetscCall(PetscLogEventBegin(fas->eventresidual, snes, 0, 0, 0)); 642 PetscCall(SNESComputeFunction(snes, Xhat, F)); 643 if (fas->eventresidual) PetscCall(PetscLogEventEnd(fas->eventresidual, snes, 0, 0, 0)); 644 PetscCall(VecNorm(F, NORM_2, &fnorm)); 645 X_c = next->vec_sol; 646 Xo_c = next->work[0]; 647 F_c = next->vec_func; 648 B_c = next->vec_rhs; 649 650 PetscCall(SNESFASRestrict(snes, Xhat, Xo_c)); 651 /* restrict the defect */ 652 PetscCall(MatRestrict(restrct, F, B_c)); 653 654 /* solve the coarse problem corresponding to F^c(x^c) = b^c = Rb + F^c(Rx) - RF(x) */ 655 if (fasc->eventresidual) PetscCall(PetscLogEventBegin(fasc->eventresidual, next, 0, 0, 0)); 656 PetscCall(SNESComputeFunction(next, Xo_c, F_c)); 657 if (fasc->eventresidual) PetscCall(PetscLogEventEnd(fasc->eventresidual, next, 0, 0, 0)); 658 PetscCall(VecCopy(B_c, X_c)); 659 PetscCall(VecCopy(F_c, B_c)); 660 PetscCall(VecCopy(X_c, F_c)); 661 /* set initial guess of the coarse problem to the projected fine solution */ 662 PetscCall(VecCopy(Xo_c, X_c)); 663 664 /* recurse */ 665 PetscCall(SNESSetInitialFunction(next, F_c)); 666 PetscCall(SNESSolve(next, B_c, X_c)); 667 668 /* smooth on this level */ 669 PetscCall(SNESFASDownSmooth_Private(snes, B, X, F, &fnorm)); 670 671 PetscCall(SNESGetConvergedReason(next, &reason)); 672 if (reason < 0 && reason != SNES_DIVERGED_MAX_IT) { 673 snes->reason = SNES_DIVERGED_INNER; 674 PetscFunctionReturn(0); 675 } 676 677 /* correct as x <- x + I(x^c - Rx)*/ 678 PetscCall(VecAYPX(X_c, -1.0, Xo_c)); 679 PetscCall(MatInterpolate(interpolate, X_c, Xhat)); 680 681 /* additive correction of the coarse direction*/ 682 PetscCall(SNESLineSearchApply(snes->linesearch, X, F, &fnorm, Xhat)); 683 PetscCall(SNESLineSearchGetReason(snes->linesearch, &lsresult)); 684 PetscCall(SNESLineSearchGetNorms(snes->linesearch, &xnorm, &snes->norm, &ynorm)); 685 if (lsresult) { 686 if (++snes->numFailures >= snes->maxFailures) { 687 snes->reason = SNES_DIVERGED_LINE_SEARCH; 688 PetscFunctionReturn(0); 689 } 690 } 691 } else { 692 PetscCall(SNESFASDownSmooth_Private(snes, B, X, F, &snes->norm)); 693 } 694 PetscFunctionReturn(0); 695 } 696 697 /* 698 699 Defines the FAS cycle as: 700 701 fine problem: F(x) = b 702 coarse problem: F^c(x) = b^c 703 704 b^c = F^c(Rx) - R(F(x) - b) 705 706 correction: 707 708 x = x + I(x^c - Rx) 709 710 */ 711 static PetscErrorCode SNESFASCycle_Multiplicative(SNES snes, Vec X) 712 { 713 Vec F, B; 714 SNES next; 715 716 PetscFunctionBegin; 717 F = snes->vec_func; 718 B = snes->vec_rhs; 719 /* pre-smooth -- just update using the pre-smoother */ 720 PetscCall(SNESFASCycleGetCorrection(snes, &next)); 721 PetscCall(SNESFASDownSmooth_Private(snes, B, X, F, &snes->norm)); 722 if (next) { 723 PetscCall(SNESFASCoarseCorrection(snes, X, F, X)); 724 PetscCall(SNESFASUpSmooth_Private(snes, B, X, F, &snes->norm)); 725 } 726 PetscFunctionReturn(0); 727 } 728 729 static PetscErrorCode SNESFASCycleSetupPhase_Full(SNES snes) 730 { 731 SNES next; 732 SNES_FAS *fas = (SNES_FAS *)snes->data; 733 PetscBool isFine; 734 735 PetscFunctionBegin; 736 /* pre-smooth -- just update using the pre-smoother */ 737 PetscCall(SNESFASCycleIsFine(snes, &isFine)); 738 PetscCall(SNESFASCycleGetCorrection(snes, &next)); 739 fas->full_stage = 0; 740 if (next) PetscCall(SNESFASCycleSetupPhase_Full(next)); 741 PetscFunctionReturn(0); 742 } 743 744 static PetscErrorCode SNESFASCycle_Full(SNES snes, Vec X) 745 { 746 Vec F, B; 747 SNES_FAS *fas = (SNES_FAS *)snes->data; 748 PetscBool isFine; 749 SNES next; 750 751 PetscFunctionBegin; 752 F = snes->vec_func; 753 B = snes->vec_rhs; 754 PetscCall(SNESFASCycleIsFine(snes, &isFine)); 755 PetscCall(SNESFASCycleGetCorrection(snes, &next)); 756 757 if (isFine) PetscCall(SNESFASCycleSetupPhase_Full(snes)); 758 759 if (fas->full_stage == 0) { 760 /* downsweep */ 761 if (next) { 762 if (fas->level != 1) next->max_its += 1; 763 if (fas->full_downsweep) PetscCall(SNESFASDownSmooth_Private(snes, B, X, F, &snes->norm)); 764 fas->full_downsweep = PETSC_TRUE; 765 if (fas->full_total) PetscCall(SNESFASInterpolatedCoarseSolution(snes, X, X)); 766 else PetscCall(SNESFASCoarseCorrection(snes, X, F, X)); 767 fas->full_total = PETSC_FALSE; 768 PetscCall(SNESFASUpSmooth_Private(snes, B, X, F, &snes->norm)); 769 if (fas->level != 1) next->max_its -= 1; 770 } else { 771 /* The smoother on the coarse level is the coarse solver */ 772 PetscCall(SNESFASDownSmooth_Private(snes, B, X, F, &snes->norm)); 773 } 774 fas->full_stage = 1; 775 } else if (fas->full_stage == 1) { 776 if (snes->iter == 0) PetscCall(SNESFASDownSmooth_Private(snes, B, X, F, &snes->norm)); 777 if (next) { 778 PetscCall(SNESFASCoarseCorrection(snes, X, F, X)); 779 PetscCall(SNESFASUpSmooth_Private(snes, B, X, F, &snes->norm)); 780 } 781 } 782 /* final v-cycle */ 783 if (isFine) { 784 if (next) { 785 PetscCall(SNESFASCoarseCorrection(snes, X, F, X)); 786 PetscCall(SNESFASUpSmooth_Private(snes, B, X, F, &snes->norm)); 787 } 788 } 789 PetscFunctionReturn(0); 790 } 791 792 static PetscErrorCode SNESFASCycle_Kaskade(SNES snes, Vec X) 793 { 794 Vec F, B; 795 SNES next; 796 797 PetscFunctionBegin; 798 F = snes->vec_func; 799 B = snes->vec_rhs; 800 PetscCall(SNESFASCycleGetCorrection(snes, &next)); 801 if (next) { 802 PetscCall(SNESFASCoarseCorrection(snes, X, F, X)); 803 PetscCall(SNESFASUpSmooth_Private(snes, B, X, F, &snes->norm)); 804 } else { 805 PetscCall(SNESFASDownSmooth_Private(snes, B, X, F, &snes->norm)); 806 } 807 PetscFunctionReturn(0); 808 } 809 810 PetscBool SNEScite = PETSC_FALSE; 811 const char SNESCitation[] = "@techreport{pbmkbsxt2012,\n" 812 " title = {Composing Scalable Nonlinear Algebraic Solvers},\n" 813 " author = {Peter Brune and Mathew Knepley and Barry Smith and Xuemin Tu},\n" 814 " year = 2013,\n" 815 " type = Preprint,\n" 816 " number = {ANL/MCS-P2010-0112},\n" 817 " institution = {Argonne National Laboratory}\n}\n"; 818 819 static PetscErrorCode SNESSolve_FAS(SNES snes) 820 { 821 PetscInt i; 822 Vec X, F; 823 PetscReal fnorm; 824 SNES_FAS *fas = (SNES_FAS *)snes->data, *ffas; 825 DM dm; 826 PetscBool isFine; 827 828 PetscFunctionBegin; 829 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); 830 831 PetscCall(PetscCitationsRegister(SNESCitation, &SNEScite)); 832 snes->reason = SNES_CONVERGED_ITERATING; 833 X = snes->vec_sol; 834 F = snes->vec_func; 835 836 PetscCall(SNESFASCycleIsFine(snes, &isFine)); 837 /* norm setup */ 838 PetscCall(PetscObjectSAWsTakeAccess((PetscObject)snes)); 839 snes->iter = 0; 840 snes->norm = 0; 841 PetscCall(PetscObjectSAWsGrantAccess((PetscObject)snes)); 842 if (!snes->vec_func_init_set) { 843 if (fas->eventresidual) PetscCall(PetscLogEventBegin(fas->eventresidual, snes, 0, 0, 0)); 844 PetscCall(SNESComputeFunction(snes, X, F)); 845 if (fas->eventresidual) PetscCall(PetscLogEventEnd(fas->eventresidual, snes, 0, 0, 0)); 846 } else snes->vec_func_init_set = PETSC_FALSE; 847 848 PetscCall(VecNorm(F, NORM_2, &fnorm)); /* fnorm <- ||F|| */ 849 SNESCheckFunctionNorm(snes, fnorm); 850 PetscCall(PetscObjectSAWsTakeAccess((PetscObject)snes)); 851 snes->norm = fnorm; 852 PetscCall(PetscObjectSAWsGrantAccess((PetscObject)snes)); 853 PetscCall(SNESLogConvergenceHistory(snes, fnorm, 0)); 854 PetscCall(SNESMonitor(snes, snes->iter, fnorm)); 855 856 /* test convergence */ 857 PetscUseTypeMethod(snes, converged, 0, 0.0, 0.0, fnorm, &snes->reason, snes->cnvP); 858 if (snes->reason) PetscFunctionReturn(0); 859 860 if (isFine) { 861 /* propagate scale-dependent data up the hierarchy */ 862 PetscCall(SNESGetDM(snes, &dm)); 863 for (ffas = fas; ffas->next; ffas = (SNES_FAS *)ffas->next->data) { 864 DM dmcoarse; 865 PetscCall(SNESGetDM(ffas->next, &dmcoarse)); 866 PetscCall(DMRestrict(dm, ffas->restrct, ffas->rscale, ffas->inject, dmcoarse)); 867 dm = dmcoarse; 868 } 869 } 870 871 for (i = 0; i < snes->max_its; i++) { 872 /* Call general purpose update function */ 873 PetscTryTypeMethod(snes, update, snes->iter); 874 875 if (fas->fastype == SNES_FAS_MULTIPLICATIVE) { 876 PetscCall(SNESFASCycle_Multiplicative(snes, X)); 877 } else if (fas->fastype == SNES_FAS_ADDITIVE) { 878 PetscCall(SNESFASCycle_Additive(snes, X)); 879 } else if (fas->fastype == SNES_FAS_FULL) { 880 PetscCall(SNESFASCycle_Full(snes, X)); 881 } else if (fas->fastype == SNES_FAS_KASKADE) { 882 PetscCall(SNESFASCycle_Kaskade(snes, X)); 883 } else SETERRQ(PetscObjectComm((PetscObject)snes), PETSC_ERR_ARG_WRONGSTATE, "Unsupported FAS type"); 884 885 /* check for FAS cycle divergence */ 886 if (snes->reason != SNES_CONVERGED_ITERATING) PetscFunctionReturn(0); 887 888 /* Monitor convergence */ 889 PetscCall(PetscObjectSAWsTakeAccess((PetscObject)snes)); 890 snes->iter = i + 1; 891 PetscCall(PetscObjectSAWsGrantAccess((PetscObject)snes)); 892 PetscCall(SNESLogConvergenceHistory(snes, snes->norm, 0)); 893 PetscCall(SNESMonitor(snes, snes->iter, snes->norm)); 894 /* Test for convergence */ 895 if (isFine) { 896 PetscUseTypeMethod(snes, converged, snes->iter, 0.0, 0.0, snes->norm, &snes->reason, snes->cnvP); 897 if (snes->reason) break; 898 } 899 } 900 if (i == snes->max_its) { 901 PetscCall(PetscInfo(snes, "Maximum number of iterations has been reached: %" PetscInt_FMT "\n", i)); 902 if (!snes->reason) snes->reason = SNES_DIVERGED_MAX_IT; 903 } 904 PetscFunctionReturn(0); 905 } 906 907 /*MC 908 909 SNESFAS - Full Approximation Scheme nonlinear multigrid solver. 910 911 The nonlinear problem is solved by correction using coarse versions 912 of the nonlinear problem. This problem is perturbed so that a projected 913 solution of the fine problem elicits no correction from the coarse problem. 914 915 Options Database Keys and Prefixes: 916 + -snes_fas_levels - The number of levels 917 . -snes_fas_cycles<1> - The number of cycles -- 1 for V, 2 for W 918 . -snes_fas_type<additive,multiplicative,full,kaskade> - Additive or multiplicative cycle 919 . -snes_fas_galerkin<`PETSC_FALSE`> - Form coarse problems by projection back upon the fine problem 920 . -snes_fas_smoothup<1> - The number of iterations of the post-smoother 921 . -snes_fas_smoothdown<1> - The number of iterations of the pre-smoother 922 . -snes_fas_monitor - Monitor progress of all of the levels 923 . -snes_fas_full_downsweep<`PETSC_FALSE`> - call the downsmooth on the initial downsweep of full FAS 924 . -fas_levels_snes_ - `SNES` options for all smoothers 925 . -fas_levels_cycle_snes_ - `SNES` options for all cycles 926 . -fas_levels_i_snes_ - `SNES` options for the smoothers on level i 927 . -fas_levels_i_cycle_snes_ - `SNES` options for the cycle on level i 928 - -fas_coarse_snes_ - `SNES` options for the coarsest smoother 929 930 Note: 931 The organization of the FAS solver is slightly different from the organization of `PCMG` 932 As each level has smoother `SNES` instances(down and potentially up) and a cycle `SNES` instance. 933 The cycle `SNES` instance may be used for monitoring convergence on a particular level. 934 935 Level: beginner 936 937 References: 938 . * - Peter R. Brune, Matthew G. Knepley, Barry F. Smith, and Xuemin Tu, "Composing Scalable Nonlinear Algebraic Solvers", 939 SIAM Review, 57(4), 2015 940 941 .seealso: `PCMG`, `SNESCreate()`, `SNES`, `SNESSetType()`, `SNESType`, `SNESFASSetRestriction()`, `SNESFASSetInjection()`, 942 `SNESFASFullGetTotal()` 943 M*/ 944 945 PETSC_EXTERN PetscErrorCode SNESCreate_FAS(SNES snes) 946 { 947 SNES_FAS *fas; 948 949 PetscFunctionBegin; 950 snes->ops->destroy = SNESDestroy_FAS; 951 snes->ops->setup = SNESSetUp_FAS; 952 snes->ops->setfromoptions = SNESSetFromOptions_FAS; 953 snes->ops->view = SNESView_FAS; 954 snes->ops->solve = SNESSolve_FAS; 955 snes->ops->reset = SNESReset_FAS; 956 957 snes->usesksp = PETSC_FALSE; 958 snes->usesnpc = PETSC_FALSE; 959 960 if (!snes->tolerancesset) { 961 snes->max_funcs = 30000; 962 snes->max_its = 10000; 963 } 964 965 snes->alwayscomputesfinalresidual = PETSC_TRUE; 966 967 PetscCall(PetscNew(&fas)); 968 969 snes->data = (void *)fas; 970 fas->level = 0; 971 fas->levels = 1; 972 fas->n_cycles = 1; 973 fas->max_up_it = 1; 974 fas->max_down_it = 1; 975 fas->smoothu = NULL; 976 fas->smoothd = NULL; 977 fas->next = NULL; 978 fas->previous = NULL; 979 fas->fine = snes; 980 fas->interpolate = NULL; 981 fas->restrct = NULL; 982 fas->inject = NULL; 983 fas->usedmfornumberoflevels = PETSC_FALSE; 984 fas->fastype = SNES_FAS_MULTIPLICATIVE; 985 fas->full_downsweep = PETSC_FALSE; 986 fas->full_total = PETSC_FALSE; 987 988 fas->eventsmoothsetup = 0; 989 fas->eventsmoothsolve = 0; 990 fas->eventresidual = 0; 991 fas->eventinterprestrict = 0; 992 PetscFunctionReturn(0); 993 } 994