113a62661SPeter Brune #include <../src/snes/impls/ngmres/snesngmres.h> /*I "petscsnes.h" I*/ 219653cdaSPeter Brune #include <petscblaslapack.h> 3fced5a79SAsbjørn Nilsen Riseth #include <petscdm.h> 4a312c225SMatthew G Knepley 59e5d0892SLisandro Dalcin const char *const SNESNGMRESRestartTypes[] = {"NONE", "PERIODIC", "DIFFERENCE", "SNESNGMRESRestartType", "SNES_NGMRES_RESTART_", NULL}; 69e5d0892SLisandro Dalcin const char *const SNESNGMRESSelectTypes[] = {"NONE", "DIFFERENCE", "LINESEARCH", "SNESNGMRESSelectType", "SNES_NGMRES_SELECT_", NULL}; 713a62661SPeter Brune 8*d71ae5a4SJacob Faibussowitsch PetscErrorCode SNESReset_NGMRES(SNES snes) 9*d71ae5a4SJacob Faibussowitsch { 10a312c225SMatthew G Knepley SNES_NGMRES *ngmres = (SNES_NGMRES *)snes->data; 11a312c225SMatthew G Knepley 12a312c225SMatthew G Knepley PetscFunctionBegin; 139566063dSJacob Faibussowitsch PetscCall(VecDestroyVecs(ngmres->msize, &ngmres->Fdot)); 149566063dSJacob Faibussowitsch PetscCall(VecDestroyVecs(ngmres->msize, &ngmres->Xdot)); 159566063dSJacob Faibussowitsch PetscCall(SNESLineSearchDestroy(&ngmres->additive_linesearch)); 16a312c225SMatthew G Knepley PetscFunctionReturn(0); 17a312c225SMatthew G Knepley } 18a312c225SMatthew G Knepley 19*d71ae5a4SJacob Faibussowitsch PetscErrorCode SNESDestroy_NGMRES(SNES snes) 20*d71ae5a4SJacob Faibussowitsch { 2178440776SJed Brown SNES_NGMRES *ngmres = (SNES_NGMRES *)snes->data; 22a312c225SMatthew G Knepley 23a312c225SMatthew G Knepley PetscFunctionBegin; 249566063dSJacob Faibussowitsch PetscCall(SNESReset_NGMRES(snes)); 259566063dSJacob Faibussowitsch PetscCall(PetscFree4(ngmres->h, ngmres->beta, ngmres->xi, ngmres->q)); 269566063dSJacob Faibussowitsch PetscCall(PetscFree3(ngmres->xnorms, ngmres->fnorms, ngmres->s)); 27dd63322aSSatish Balay #if defined(PETSC_USE_COMPLEX) 289566063dSJacob Faibussowitsch PetscCall(PetscFree(ngmres->rwork)); 2919653cdaSPeter Brune #endif 309566063dSJacob Faibussowitsch PetscCall(PetscFree(ngmres->work)); 312e956fe4SStefano Zampini PetscCall(PetscObjectComposeFunction((PetscObject)snes, "SNESNGMRESSetSelectType_C", NULL)); 322e956fe4SStefano Zampini PetscCall(PetscObjectComposeFunction((PetscObject)snes, "SNESNGMRESSetRestartType_C", NULL)); 332e956fe4SStefano Zampini PetscCall(PetscObjectComposeFunction((PetscObject)snes, "SNESNGMRESSetRestartFmRise_C", NULL)); 342e956fe4SStefano Zampini PetscCall(PetscObjectComposeFunction((PetscObject)snes, "SNESNGMRESGetRestartFmRise_C", NULL)); 359566063dSJacob Faibussowitsch PetscCall(PetscFree(snes->data)); 36a312c225SMatthew G Knepley PetscFunctionReturn(0); 37a312c225SMatthew G Knepley } 38a312c225SMatthew G Knepley 39*d71ae5a4SJacob Faibussowitsch PetscErrorCode SNESSetUp_NGMRES(SNES snes) 40*d71ae5a4SJacob Faibussowitsch { 41a312c225SMatthew G Knepley SNES_NGMRES *ngmres = (SNES_NGMRES *)snes->data; 42e7058c64SPeter Brune const char *optionsprefix; 4319653cdaSPeter Brune PetscInt msize, hsize; 44fced5a79SAsbjørn Nilsen Riseth DM dm; 45a312c225SMatthew G Knepley 46a312c225SMatthew G Knepley PetscFunctionBegin; 47efd4aadfSBarry Smith if (snes->npc && snes->npcside == PC_LEFT && snes->functype == SNES_FUNCTION_UNPRECONDITIONED) { 4846159c86SPeter Brune SETERRQ(PetscObjectComm((PetscObject)snes), PETSC_ERR_ARG_WRONGSTATE, "SNESNGMRES does not support left preconditioning with unpreconditioned function"); 4946159c86SPeter Brune } 50efd4aadfSBarry Smith if (snes->npcside == PC_LEFT && snes->functype == SNES_FUNCTION_DEFAULT) snes->functype = SNES_FUNCTION_PRECONDITIONED; 519566063dSJacob Faibussowitsch PetscCall(SNESSetWorkVecs(snes, 5)); 52fced5a79SAsbjørn Nilsen Riseth 53fced5a79SAsbjørn Nilsen Riseth if (!snes->vec_sol) { 549566063dSJacob Faibussowitsch PetscCall(SNESGetDM(snes, &dm)); 559566063dSJacob Faibussowitsch PetscCall(DMCreateGlobalVector(dm, &snes->vec_sol)); 56fced5a79SAsbjørn Nilsen Riseth } 57fced5a79SAsbjørn Nilsen Riseth 589566063dSJacob Faibussowitsch if (!ngmres->Xdot) PetscCall(VecDuplicateVecs(snes->vec_sol, ngmres->msize, &ngmres->Xdot)); 599566063dSJacob Faibussowitsch if (!ngmres->Fdot) PetscCall(VecDuplicateVecs(snes->vec_sol, ngmres->msize, &ngmres->Fdot)); 6078440776SJed Brown if (!ngmres->setup_called) { 61087dfb9eSxuemin msize = ngmres->msize; /* restart size */ 6219653cdaSPeter Brune hsize = msize * msize; 63087dfb9eSxuemin 6498b3e84cSPeter Brune /* explicit least squares minimization solve */ 659566063dSJacob Faibussowitsch PetscCall(PetscCalloc4(hsize, &ngmres->h, msize, &ngmres->beta, msize, &ngmres->xi, hsize, &ngmres->q)); 669566063dSJacob Faibussowitsch PetscCall(PetscMalloc3(msize, &ngmres->xnorms, msize, &ngmres->fnorms, msize, &ngmres->s)); 6719653cdaSPeter Brune ngmres->nrhs = 1; 6819653cdaSPeter Brune ngmres->lda = msize; 6919653cdaSPeter Brune ngmres->ldb = msize; 7019653cdaSPeter Brune ngmres->lwork = 12 * msize; 71dd63322aSSatish Balay #if defined(PETSC_USE_COMPLEX) 729566063dSJacob Faibussowitsch PetscCall(PetscMalloc1(ngmres->lwork, &ngmres->rwork)); 7319653cdaSPeter Brune #endif 749566063dSJacob Faibussowitsch PetscCall(PetscMalloc1(ngmres->lwork, &ngmres->work)); 7578440776SJed Brown } 76e7058c64SPeter Brune 77e7058c64SPeter Brune /* linesearch setup */ 789566063dSJacob Faibussowitsch PetscCall(SNESGetOptionsPrefix(snes, &optionsprefix)); 79e7058c64SPeter Brune 8013a62661SPeter Brune if (ngmres->select_type == SNES_NGMRES_SELECT_LINESEARCH) { 819566063dSJacob Faibussowitsch PetscCall(SNESLineSearchCreate(PetscObjectComm((PetscObject)snes), &ngmres->additive_linesearch)); 829566063dSJacob Faibussowitsch PetscCall(SNESLineSearchSetSNES(ngmres->additive_linesearch, snes)); 8348a46eb9SPierre Jolivet if (!((PetscObject)ngmres->additive_linesearch)->type_name) PetscCall(SNESLineSearchSetType(ngmres->additive_linesearch, SNESLINESEARCHL2)); 849566063dSJacob Faibussowitsch PetscCall(SNESLineSearchAppendOptionsPrefix(ngmres->additive_linesearch, "additive_")); 859566063dSJacob Faibussowitsch PetscCall(SNESLineSearchAppendOptionsPrefix(ngmres->additive_linesearch, optionsprefix)); 869566063dSJacob Faibussowitsch PetscCall(SNESLineSearchSetFromOptions(ngmres->additive_linesearch)); 87e7058c64SPeter Brune } 88e7058c64SPeter Brune 8978440776SJed Brown ngmres->setup_called = PETSC_TRUE; 90a312c225SMatthew G Knepley PetscFunctionReturn(0); 91a312c225SMatthew G Knepley } 92a312c225SMatthew G Knepley 93*d71ae5a4SJacob Faibussowitsch PetscErrorCode SNESSetFromOptions_NGMRES(SNES snes, PetscOptionItems *PetscOptionsObject) 94*d71ae5a4SJacob Faibussowitsch { 95a312c225SMatthew G Knepley SNES_NGMRES *ngmres = (SNES_NGMRES *)snes->data; 9694ae4db5SBarry Smith PetscBool debug = PETSC_FALSE; 970adebc6cSBarry Smith 98a312c225SMatthew G Knepley PetscFunctionBegin; 99d0609cedSBarry Smith PetscOptionsHeadBegin(PetscOptionsObject, "SNES NGMRES options"); 100d0609cedSBarry Smith PetscCall(PetscOptionsEnum("-snes_ngmres_select_type", "Select type", "SNESNGMRESSetSelectType", SNESNGMRESSelectTypes, (PetscEnum)ngmres->select_type, (PetscEnum *)&ngmres->select_type, NULL)); 101d0609cedSBarry Smith PetscCall(PetscOptionsEnum("-snes_ngmres_restart_type", "Restart type", "SNESNGMRESSetRestartType", SNESNGMRESRestartTypes, (PetscEnum)ngmres->restart_type, (PetscEnum *)&ngmres->restart_type, NULL)); 1029566063dSJacob Faibussowitsch PetscCall(PetscOptionsBool("-snes_ngmres_candidate", "Use candidate storage", "SNES", ngmres->candidate, &ngmres->candidate, NULL)); 1039566063dSJacob Faibussowitsch PetscCall(PetscOptionsBool("-snes_ngmres_approxfunc", "Linearly approximate the function", "SNES", ngmres->approxfunc, &ngmres->approxfunc, NULL)); 1049566063dSJacob Faibussowitsch PetscCall(PetscOptionsInt("-snes_ngmres_m", "Number of directions", "SNES", ngmres->msize, &ngmres->msize, NULL)); 1059566063dSJacob Faibussowitsch PetscCall(PetscOptionsInt("-snes_ngmres_restart", "Iterations before forced restart", "SNES", ngmres->restart_periodic, &ngmres->restart_periodic, NULL)); 1069566063dSJacob Faibussowitsch PetscCall(PetscOptionsInt("-snes_ngmres_restart_it", "Tolerance iterations before restart", "SNES", ngmres->restart_it, &ngmres->restart_it, NULL)); 1079566063dSJacob Faibussowitsch PetscCall(PetscOptionsBool("-snes_ngmres_monitor", "Monitor actions of NGMRES", "SNES", ngmres->monitor ? PETSC_TRUE : PETSC_FALSE, &debug, NULL)); 108ad540459SPierre Jolivet if (debug) ngmres->monitor = PETSC_VIEWER_STDOUT_(PetscObjectComm((PetscObject)snes)); 1099566063dSJacob Faibussowitsch PetscCall(PetscOptionsReal("-snes_ngmres_gammaA", "Residual selection constant", "SNES", ngmres->gammaA, &ngmres->gammaA, NULL)); 1109566063dSJacob Faibussowitsch PetscCall(PetscOptionsReal("-snes_ngmres_gammaC", "Residual restart constant", "SNES", ngmres->gammaC, &ngmres->gammaC, NULL)); 1119566063dSJacob Faibussowitsch PetscCall(PetscOptionsReal("-snes_ngmres_epsilonB", "Difference selection constant", "SNES", ngmres->epsilonB, &ngmres->epsilonB, NULL)); 1129566063dSJacob Faibussowitsch PetscCall(PetscOptionsReal("-snes_ngmres_deltaB", "Difference residual selection constant", "SNES", ngmres->deltaB, &ngmres->deltaB, NULL)); 1139566063dSJacob Faibussowitsch PetscCall(PetscOptionsBool("-snes_ngmres_single_reduction", "Aggregate reductions", "SNES", ngmres->singlereduction, &ngmres->singlereduction, NULL)); 1149566063dSJacob Faibussowitsch PetscCall(PetscOptionsBool("-snes_ngmres_restart_fm_rise", "Restart on F_M residual rise", "SNESNGMRESSetRestartFmRise", ngmres->restart_fm_rise, &ngmres->restart_fm_rise, NULL)); 115d0609cedSBarry Smith PetscOptionsHeadEnd(); 1166a7cf640SPeter Brune if ((ngmres->gammaA > ngmres->gammaC) && (ngmres->gammaC > 2.)) ngmres->gammaC = ngmres->gammaA; 117a312c225SMatthew G Knepley PetscFunctionReturn(0); 118a312c225SMatthew G Knepley } 119a312c225SMatthew G Knepley 120*d71ae5a4SJacob Faibussowitsch PetscErrorCode SNESView_NGMRES(SNES snes, PetscViewer viewer) 121*d71ae5a4SJacob Faibussowitsch { 122a312c225SMatthew G Knepley SNES_NGMRES *ngmres = (SNES_NGMRES *)snes->data; 123a312c225SMatthew G Knepley PetscBool iascii; 124a312c225SMatthew G Knepley 125a312c225SMatthew G Knepley PetscFunctionBegin; 1269566063dSJacob Faibussowitsch PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERASCII, &iascii)); 127a312c225SMatthew G Knepley if (iascii) { 12863a3b9bcSJacob Faibussowitsch PetscCall(PetscViewerASCIIPrintf(viewer, " Number of stored past updates: %" PetscInt_FMT "\n", ngmres->msize)); 12963a3b9bcSJacob Faibussowitsch PetscCall(PetscViewerASCIIPrintf(viewer, " Residual selection: gammaA=%1.0e, gammaC=%1.0e\n", (double)ngmres->gammaA, (double)ngmres->gammaC)); 13063a3b9bcSJacob Faibussowitsch PetscCall(PetscViewerASCIIPrintf(viewer, " Difference restart: epsilonB=%1.0e, deltaB=%1.0e\n", (double)ngmres->epsilonB, (double)ngmres->deltaB)); 13163a3b9bcSJacob Faibussowitsch PetscCall(PetscViewerASCIIPrintf(viewer, " Restart on F_M residual increase: %s\n", PetscBools[ngmres->restart_fm_rise])); 132a312c225SMatthew G Knepley } 133a312c225SMatthew G Knepley PetscFunctionReturn(0); 134a312c225SMatthew G Knepley } 135a312c225SMatthew G Knepley 136*d71ae5a4SJacob Faibussowitsch PetscErrorCode SNESSolve_NGMRES(SNES snes) 137*d71ae5a4SJacob Faibussowitsch { 138087dfb9eSxuemin SNES_NGMRES *ngmres = (SNES_NGMRES *)snes->data; 13998b3e84cSPeter Brune /* present solution, residual, and preconditioned residual */ 1409f425c49SPeter Brune Vec X, F, B, D, Y; 141f109b39eSPeter Brune 142f109b39eSPeter Brune /* candidate linear combination answers */ 143ddd40ce5SPeter Brune Vec XA, FA, XM, FM; 14419653cdaSPeter Brune 14598b3e84cSPeter Brune /* coefficients and RHS to the minimization problem */ 14618aa0c0cSPeter Brune PetscReal fnorm, fMnorm, fAnorm; 147b3c6a99cSPeter Brune PetscReal xnorm, xMnorm, xAnorm; 148b3c6a99cSPeter Brune PetscReal ynorm, yMnorm, yAnorm; 14938774f0aSPeter Brune PetscInt k, k_restart, l, ivec, restart_count = 0; 15019653cdaSPeter Brune 15198b3e84cSPeter Brune /* solution selection data */ 15238774f0aSPeter Brune PetscBool selectRestart; 15361ba4676SBarry Smith /* 15461ba4676SBarry Smith These two variables are initialized to prevent compilers/analyzers from producing false warnings about these variables being passed 15561ba4676SBarry Smith to SNESNGMRESSelect_Private() without being set when SNES_NGMRES_RESTART_DIFFERENCE, the values are not used in the subroutines in that case 15661ba4676SBarry Smith so the code is correct as written. 15761ba4676SBarry Smith */ 15861ba4676SBarry Smith PetscReal dnorm = 0.0, dminnorm = 0.0; 159b3c6a99cSPeter Brune PetscReal fminnorm; 16019653cdaSPeter Brune 1611e633543SBarry Smith SNESConvergedReason reason; 162422a814eSBarry Smith SNESLineSearchReason lssucceed; 163a312c225SMatthew G Knepley 164a312c225SMatthew G Knepley PetscFunctionBegin; 1650b121fc5SBarry Smith 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); 166c579b300SPatrick Farrell 1679566063dSJacob Faibussowitsch PetscCall(PetscCitationsRegister(SNESCitation, &SNEScite)); 16898b3e84cSPeter Brune /* variable initialization */ 169a312c225SMatthew G Knepley snes->reason = SNES_CONVERGED_ITERATING; 170f109b39eSPeter Brune X = snes->vec_sol; 171f109b39eSPeter Brune F = snes->vec_func; 172f109b39eSPeter Brune B = snes->vec_rhs; 173f109b39eSPeter Brune XA = snes->vec_sol_update; 174f109b39eSPeter Brune FA = snes->work[0]; 175f109b39eSPeter Brune D = snes->work[1]; 176f109b39eSPeter Brune 177f109b39eSPeter Brune /* work for the line search */ 178f109b39eSPeter Brune Y = snes->work[2]; 1799f425c49SPeter Brune XM = snes->work[3]; 1809f425c49SPeter Brune FM = snes->work[4]; 181a312c225SMatthew G Knepley 1829566063dSJacob Faibussowitsch PetscCall(PetscObjectSAWsTakeAccess((PetscObject)snes)); 183a312c225SMatthew G Knepley snes->iter = 0; 184a312c225SMatthew G Knepley snes->norm = 0.; 1859566063dSJacob Faibussowitsch PetscCall(PetscObjectSAWsGrantAccess((PetscObject)snes)); 18619653cdaSPeter Brune 18798b3e84cSPeter Brune /* initialization */ 18819653cdaSPeter Brune 189efd4aadfSBarry Smith if (snes->npc && snes->npcside == PC_LEFT) { 1909566063dSJacob Faibussowitsch PetscCall(SNESApplyNPC(snes, X, NULL, F)); 1919566063dSJacob Faibussowitsch PetscCall(SNESGetConvergedReason(snes->npc, &reason)); 1923a2ae377SPeter Brune if (reason < 0 && reason != SNES_DIVERGED_MAX_IT) { 1933a2ae377SPeter Brune snes->reason = SNES_DIVERGED_INNER; 1943a2ae377SPeter Brune PetscFunctionReturn(0); 1953a2ae377SPeter Brune } 1969566063dSJacob Faibussowitsch PetscCall(VecNorm(F, NORM_2, &fnorm)); 1973a2ae377SPeter Brune } else { 198e4ed7901SPeter Brune if (!snes->vec_func_init_set) { 1999566063dSJacob Faibussowitsch PetscCall(SNESComputeFunction(snes, X, F)); 2001aa26658SKarl Rupp } else snes->vec_func_init_set = PETSC_FALSE; 201c1c75074SPeter Brune 2029566063dSJacob Faibussowitsch PetscCall(VecNorm(F, NORM_2, &fnorm)); 203422a814eSBarry Smith SNESCheckFunctionNorm(snes, fnorm); 2043a2ae377SPeter Brune } 205e4ed7901SPeter Brune fminnorm = fnorm; 20619653cdaSPeter Brune 2079566063dSJacob Faibussowitsch PetscCall(PetscObjectSAWsTakeAccess((PetscObject)snes)); 208f109b39eSPeter Brune snes->norm = fnorm; 2099566063dSJacob Faibussowitsch PetscCall(PetscObjectSAWsGrantAccess((PetscObject)snes)); 2109566063dSJacob Faibussowitsch PetscCall(SNESLogConvergenceHistory(snes, fnorm, 0)); 2119566063dSJacob Faibussowitsch PetscCall(SNESMonitor(snes, 0, fnorm)); 212dbbe0bcdSBarry Smith PetscUseTypeMethod(snes, converged, 0, 0.0, 0.0, fnorm, &snes->reason, snes->cnvP); 213a312c225SMatthew G Knepley if (snes->reason) PetscFunctionReturn(0); 214b3c6a99cSPeter Brune SNESNGMRESUpdateSubspace_Private(snes, 0, 0, F, fnorm, X); 215a312c225SMatthew G Knepley 21619653cdaSPeter Brune k_restart = 1; 21719653cdaSPeter Brune l = 1; 218b3c6a99cSPeter Brune ivec = 0; 21909c08436SPeter Brune for (k = 1; k < snes->max_its + 1; k++) { 22098b3e84cSPeter Brune /* Computation of x^M */ 221efd4aadfSBarry Smith if (snes->npc && snes->npcside == PC_RIGHT) { 2229566063dSJacob Faibussowitsch PetscCall(VecCopy(X, XM)); 2239566063dSJacob Faibussowitsch PetscCall(SNESSetInitialFunction(snes->npc, F)); 22463e7833aSPeter Brune 2259566063dSJacob Faibussowitsch PetscCall(PetscLogEventBegin(SNES_NPCSolve, snes->npc, XM, B, 0)); 2269566063dSJacob Faibussowitsch PetscCall(SNESSolve(snes->npc, B, XM)); 2279566063dSJacob Faibussowitsch PetscCall(PetscLogEventEnd(SNES_NPCSolve, snes->npc, XM, B, 0)); 22863e7833aSPeter Brune 2299566063dSJacob Faibussowitsch PetscCall(SNESGetConvergedReason(snes->npc, &reason)); 2308cc86e31SPeter Brune if (reason < 0 && reason != SNES_DIVERGED_MAX_IT) { 2318cc86e31SPeter Brune snes->reason = SNES_DIVERGED_INNER; 2328cc86e31SPeter Brune PetscFunctionReturn(0); 2338cc86e31SPeter Brune } 2349566063dSJacob Faibussowitsch PetscCall(SNESGetNPCFunction(snes, FM, &fMnorm)); 2358cc86e31SPeter Brune } else { 236f109b39eSPeter Brune /* no preconditioner -- just take gradient descent with line search */ 2379566063dSJacob Faibussowitsch PetscCall(VecCopy(F, Y)); 2389566063dSJacob Faibussowitsch PetscCall(VecCopy(F, FM)); 2399566063dSJacob Faibussowitsch PetscCall(VecCopy(X, XM)); 2401aa26658SKarl Rupp 241e7058c64SPeter Brune fMnorm = fnorm; 2421aa26658SKarl Rupp 2439566063dSJacob Faibussowitsch PetscCall(SNESLineSearchApply(snes->linesearch, XM, FM, &fMnorm, Y)); 2449566063dSJacob Faibussowitsch PetscCall(SNESLineSearchGetReason(snes->linesearch, &lssucceed)); 245422a814eSBarry Smith if (lssucceed) { 246f109b39eSPeter Brune if (++snes->numFailures >= snes->maxFailures) { 247f109b39eSPeter Brune snes->reason = SNES_DIVERGED_LINE_SEARCH; 248f109b39eSPeter Brune PetscFunctionReturn(0); 249f109b39eSPeter Brune } 250f109b39eSPeter Brune } 2516634f59bSPeter Brune } 25223b3e82cSAsbjørn Nilsen Riseth 2539566063dSJacob Faibussowitsch PetscCall(SNESNGMRESFormCombinedSolution_Private(snes, ivec, l, XM, FM, fMnorm, X, XA, FA)); 25498b3e84cSPeter Brune /* r = F(x) */ 2559f425c49SPeter Brune if (fminnorm > fMnorm) fminnorm = fMnorm; /* the minimum norm is now of F^M */ 25619653cdaSPeter Brune 2579f425c49SPeter Brune /* differences for selection and restart */ 25813a62661SPeter Brune if (ngmres->restart_type == SNES_NGMRES_RESTART_DIFFERENCE || ngmres->select_type == SNES_NGMRES_SELECT_DIFFERENCE) { 2599566063dSJacob Faibussowitsch PetscCall(SNESNGMRESNorms_Private(snes, l, X, F, XM, FM, XA, FA, D, &dnorm, &dminnorm, &xMnorm, NULL, &yMnorm, &xAnorm, &fAnorm, &yAnorm)); 26013a62661SPeter Brune } else { 2619566063dSJacob Faibussowitsch PetscCall(SNESNGMRESNorms_Private(snes, l, X, F, XM, FM, XA, FA, D, NULL, NULL, &xMnorm, NULL, &yMnorm, &xAnorm, &fAnorm, &yAnorm)); 26213a62661SPeter Brune } 263422a814eSBarry Smith SNESCheckFunctionNorm(snes, fnorm); 2641aa26658SKarl Rupp 2659f425c49SPeter Brune /* combination (additive) or selection (multiplicative) of the N-GMRES solution */ 2669566063dSJacob Faibussowitsch PetscCall(SNESNGMRESSelect_Private(snes, k_restart, XM, FM, xMnorm, fMnorm, yMnorm, XA, FA, xAnorm, fAnorm, yAnorm, dnorm, fminnorm, dminnorm, X, F, Y, &xnorm, &fnorm, &ynorm)); 26719653cdaSPeter Brune selectRestart = PETSC_FALSE; 26823b3e82cSAsbjørn Nilsen Riseth 26913a62661SPeter Brune if (ngmres->restart_type == SNES_NGMRES_RESTART_DIFFERENCE) { 2709566063dSJacob Faibussowitsch PetscCall(SNESNGMRESSelectRestart_Private(snes, l, fMnorm, fAnorm, dnorm, fminnorm, dminnorm, &selectRestart)); 27123b3e82cSAsbjørn Nilsen Riseth 27228ed4a04SPeter Brune /* if the restart conditions persist for more than restart_it iterations, restart. */ 2731aa26658SKarl Rupp if (selectRestart) restart_count++; 2741aa26658SKarl Rupp else restart_count = 0; 27513a62661SPeter Brune } else if (ngmres->restart_type == SNES_NGMRES_RESTART_PERIODIC) { 27613a62661SPeter Brune if (k_restart > ngmres->restart_periodic) { 27763a3b9bcSJacob Faibussowitsch if (ngmres->monitor) PetscCall(PetscViewerASCIIPrintf(ngmres->monitor, "periodic restart after %" PetscInt_FMT " iterations\n", k_restart)); 27813a62661SPeter Brune restart_count = ngmres->restart_it; 27913a62661SPeter Brune } 28013a62661SPeter Brune } 28123b3e82cSAsbjørn Nilsen Riseth 282b3c6a99cSPeter Brune ivec = k_restart % ngmres->msize; /* replace the last used part of the subspace */ 28323b3e82cSAsbjørn Nilsen Riseth 28428ed4a04SPeter Brune /* restart after restart conditions have persisted for a fixed number of iterations */ 28528ed4a04SPeter Brune if (restart_count >= ngmres->restart_it) { 28648a46eb9SPierre Jolivet if (ngmres->monitor) PetscCall(PetscViewerASCIIPrintf(ngmres->monitor, "Restarted at iteration %" PetscInt_FMT "\n", k_restart)); 28728ed4a04SPeter Brune restart_count = 0; 28819653cdaSPeter Brune k_restart = 1; 28919653cdaSPeter Brune l = 1; 290b3c6a99cSPeter Brune ivec = 0; 29198b3e84cSPeter Brune /* q_{00} = nu */ 2929566063dSJacob Faibussowitsch PetscCall(SNESNGMRESUpdateSubspace_Private(snes, 0, 0, FM, fMnorm, XM)); 293d2e16ddcSPeter Brune } else { 29498b3e84cSPeter Brune /* select the current size of the subspace */ 2951e633543SBarry Smith if (l < ngmres->msize) l++; 29619653cdaSPeter Brune k_restart++; 29798b3e84cSPeter Brune /* place the current entry in the list of previous entries */ 29838774f0aSPeter Brune if (ngmres->candidate) { 29938774f0aSPeter Brune if (fminnorm > fMnorm) fminnorm = fMnorm; 3009566063dSJacob Faibussowitsch PetscCall(SNESNGMRESUpdateSubspace_Private(snes, ivec, l, FM, fMnorm, XM)); 301d2e16ddcSPeter Brune } else { 30238774f0aSPeter Brune if (fminnorm > fnorm) fminnorm = fnorm; 3039566063dSJacob Faibussowitsch PetscCall(SNESNGMRESUpdateSubspace_Private(snes, ivec, l, F, fnorm, X)); 30419653cdaSPeter Brune } 305d2e16ddcSPeter Brune } 30619653cdaSPeter Brune 3079566063dSJacob Faibussowitsch PetscCall(PetscObjectSAWsTakeAccess((PetscObject)snes)); 308087dfb9eSxuemin snes->iter = k; 309f109b39eSPeter Brune snes->norm = fnorm; 3109566063dSJacob Faibussowitsch PetscCall(PetscObjectSAWsGrantAccess((PetscObject)snes)); 3119566063dSJacob Faibussowitsch PetscCall(SNESLogConvergenceHistory(snes, snes->norm, snes->iter)); 3129566063dSJacob Faibussowitsch PetscCall(SNESMonitor(snes, snes->iter, snes->norm)); 313dbbe0bcdSBarry Smith PetscUseTypeMethod(snes, converged, snes->iter, 0, 0, fnorm, &snes->reason, snes->cnvP); 314087dfb9eSxuemin if (snes->reason) PetscFunctionReturn(0); 315a312c225SMatthew G Knepley } 316a312c225SMatthew G Knepley snes->reason = SNES_DIVERGED_MAX_IT; 317a312c225SMatthew G Knepley PetscFunctionReturn(0); 318a312c225SMatthew G Knepley } 319a312c225SMatthew G Knepley 32023b3e82cSAsbjørn Nilsen Riseth /*@ 32123b3e82cSAsbjørn Nilsen Riseth SNESNGMRESSetRestartFmRise - Increase the restart count if the step x_M increases the residual F_M 32223b3e82cSAsbjørn Nilsen Riseth 32323b3e82cSAsbjørn Nilsen Riseth Input Parameters: 324f6dfbefdSBarry Smith + snes - the `SNES` context. 325f6dfbefdSBarry Smith - flg - boolean value deciding whether to use the option or not, default is `PETSC_FALSE` 32623b3e82cSAsbjørn Nilsen Riseth 327f6dfbefdSBarry Smith Options Database Key: 328f6dfbefdSBarry Smith . -snes_ngmres_restart_fm_rise - Increase the restart count if the step x_M increases the residual F_M 32923b3e82cSAsbjørn Nilsen Riseth 33023b3e82cSAsbjørn Nilsen Riseth Level: intermediate 33123b3e82cSAsbjørn Nilsen Riseth 33223b3e82cSAsbjørn Nilsen Riseth Notes: 33323b3e82cSAsbjørn Nilsen Riseth If the proposed step x_M increases the residual F_M, it might be trying to get out of a stagnation area. 33423b3e82cSAsbjørn Nilsen Riseth To help the solver do that, reset the Krylov subspace whenever F_M increases. 33523b3e82cSAsbjørn Nilsen Riseth 336f6dfbefdSBarry Smith This option must be used with the `SNESNGMRES` `SNESNGMRESRestartType` of `SNES_NGMRES_RESTART_DIFFERENCE` 33723b3e82cSAsbjørn Nilsen Riseth 338f6dfbefdSBarry Smith .seealso: `SNES_NGMRES_RESTART_DIFFERENCE`, `SNESNGMRES`, `SNESNGMRESRestartType`, `SNESNGMRESSetRestartType()` 33923b3e82cSAsbjørn Nilsen Riseth @*/ 340*d71ae5a4SJacob Faibussowitsch PetscErrorCode SNESNGMRESSetRestartFmRise(SNES snes, PetscBool flg) 341*d71ae5a4SJacob Faibussowitsch { 34223b3e82cSAsbjørn Nilsen Riseth PetscErrorCode (*f)(SNES, PetscBool); 34323b3e82cSAsbjørn Nilsen Riseth 34423b3e82cSAsbjørn Nilsen Riseth PetscFunctionBegin; 3459566063dSJacob Faibussowitsch PetscCall(PetscObjectQueryFunction((PetscObject)snes, "SNESNGMRESSetRestartFmRise_C", &f)); 3469566063dSJacob Faibussowitsch if (f) PetscCall((f)(snes, flg)); 34723b3e82cSAsbjørn Nilsen Riseth PetscFunctionReturn(0); 34823b3e82cSAsbjørn Nilsen Riseth } 34923b3e82cSAsbjørn Nilsen Riseth 350*d71ae5a4SJacob Faibussowitsch PetscErrorCode SNESNGMRESSetRestartFmRise_NGMRES(SNES snes, PetscBool flg) 351*d71ae5a4SJacob Faibussowitsch { 35223b3e82cSAsbjørn Nilsen Riseth SNES_NGMRES *ngmres = (SNES_NGMRES *)snes->data; 35323b3e82cSAsbjørn Nilsen Riseth 35423b3e82cSAsbjørn Nilsen Riseth PetscFunctionBegin; 35523b3e82cSAsbjørn Nilsen Riseth ngmres->restart_fm_rise = flg; 35623b3e82cSAsbjørn Nilsen Riseth PetscFunctionReturn(0); 35723b3e82cSAsbjørn Nilsen Riseth } 35823b3e82cSAsbjørn Nilsen Riseth 359*d71ae5a4SJacob Faibussowitsch PetscErrorCode SNESNGMRESGetRestartFmRise(SNES snes, PetscBool *flg) 360*d71ae5a4SJacob Faibussowitsch { 36123b3e82cSAsbjørn Nilsen Riseth PetscErrorCode (*f)(SNES, PetscBool *); 36223b3e82cSAsbjørn Nilsen Riseth 36323b3e82cSAsbjørn Nilsen Riseth PetscFunctionBegin; 3649566063dSJacob Faibussowitsch PetscCall(PetscObjectQueryFunction((PetscObject)snes, "SNESNGMRESGetRestartFmRise_C", &f)); 3659566063dSJacob Faibussowitsch if (f) PetscCall((f)(snes, flg)); 36623b3e82cSAsbjørn Nilsen Riseth PetscFunctionReturn(0); 36723b3e82cSAsbjørn Nilsen Riseth } 36823b3e82cSAsbjørn Nilsen Riseth 369*d71ae5a4SJacob Faibussowitsch PetscErrorCode SNESNGMRESGetRestartFmRise_NGMRES(SNES snes, PetscBool *flg) 370*d71ae5a4SJacob Faibussowitsch { 37123b3e82cSAsbjørn Nilsen Riseth SNES_NGMRES *ngmres = (SNES_NGMRES *)snes->data; 37223b3e82cSAsbjørn Nilsen Riseth 37323b3e82cSAsbjørn Nilsen Riseth PetscFunctionBegin; 37423b3e82cSAsbjørn Nilsen Riseth *flg = ngmres->restart_fm_rise; 37523b3e82cSAsbjørn Nilsen Riseth PetscFunctionReturn(0); 37623b3e82cSAsbjørn Nilsen Riseth } 37723b3e82cSAsbjørn Nilsen Riseth 37813a62661SPeter Brune /*@ 379f6dfbefdSBarry Smith SNESNGMRESSetRestartType - Sets the restart type for `SNESNGMRES`. 38013a62661SPeter Brune 381f6dfbefdSBarry Smith Logically Collective on snes 38213a62661SPeter Brune 38313a62661SPeter Brune Input Parameters: 38413a62661SPeter Brune + snes - the iterative context 38513a62661SPeter Brune - rtype - restart type 38613a62661SPeter Brune 387f6dfbefdSBarry Smith Options Databas Keys: 38813a62661SPeter Brune + -snes_ngmres_restart_type<difference,periodic,none> - set the restart type 3890c777b0cSPeter Brune - -snes_ngmres_restart[30] - sets the number of iterations before restart for periodic 39013a62661SPeter Brune 391f6dfbefdSBarry Smith `SNESNGMRESRestartType`s: 392f6dfbefdSBarry Smith + `SNES_NGMRES_RESTART_NONE` - never restart 393f6dfbefdSBarry Smith . `SNES_NGMRES_RESTART_DIFFERENCE` - restart based upon difference criteria 394f6dfbefdSBarry Smith - `SNES_NGMRES_RESTART_PERIODIC` - restart after a fixed number of iterations 395f6dfbefdSBarry Smith 39613a62661SPeter Brune Level: intermediate 39713a62661SPeter Brune 398f6dfbefdSBarry Smith .seealso: `SNES_NGMRES_RESTART_DIFFERENCE`, `SNESNGMRES`, `SNESNGMRESRestartType`, `SNESNGMRESSetRestartFmRise()` 39913a62661SPeter Brune @*/ 400*d71ae5a4SJacob Faibussowitsch PetscErrorCode SNESNGMRESSetRestartType(SNES snes, SNESNGMRESRestartType rtype) 401*d71ae5a4SJacob Faibussowitsch { 40213a62661SPeter Brune PetscFunctionBegin; 40313a62661SPeter Brune PetscValidHeaderSpecific(snes, SNES_CLASSID, 1); 404cac4c232SBarry Smith PetscTryMethod(snes, "SNESNGMRESSetRestartType_C", (SNES, SNESNGMRESRestartType), (snes, rtype)); 40513a62661SPeter Brune PetscFunctionReturn(0); 40613a62661SPeter Brune } 40713a62661SPeter Brune 40813a62661SPeter Brune /*@ 409f6dfbefdSBarry Smith SNESNGMRESSetSelectType - Sets the selection type for `SNESNGMRES`. This determines how the candidate solution and 41013a62661SPeter Brune combined solution are used to create the next iterate. 41113a62661SPeter Brune 412f6dfbefdSBarry Smith Logically Collective on snes 41313a62661SPeter Brune 41413a62661SPeter Brune Input Parameters: 41513a62661SPeter Brune + snes - the iterative context 41613a62661SPeter Brune - stype - selection type 41713a62661SPeter Brune 418f6dfbefdSBarry Smith Options Database Key: 41967b8a455SSatish Balay . -snes_ngmres_select_type<difference,none,linesearch> - select type 42013a62661SPeter Brune 42113a62661SPeter Brune Level: intermediate 42213a62661SPeter Brune 423f6dfbefdSBarry Smith `SNESNGMRESSelectType`s: 424f6dfbefdSBarry Smith + `SNES_NGMRES_SELECT_NONE` - choose the combined solution all the time 425f6dfbefdSBarry Smith . `SNES_NGMRES_SELECT_DIFFERENCE` - choose based upon the selection criteria 426f6dfbefdSBarry Smith - `SNES_NGMRES_SELECT_LINESEARCH` - choose based upon line search combination 42713a62661SPeter Brune 428f6dfbefdSBarry Smith Note: 429f6dfbefdSBarry Smith The default line search used is the `SNESLINESEARCHL2` line search and it requires two additional function evaluations. 43013a62661SPeter Brune 431f6dfbefdSBarry Smith .seealso: `SNESNGMRESSelectType()`, `SNES_NGMRES_SELECT_NONE`, `SNES_NGMRES_SELECT_DIFFERENCE`, `SNES_NGMRES_SELECT_LINESEARCH` 43213a62661SPeter Brune @*/ 433*d71ae5a4SJacob Faibussowitsch PetscErrorCode SNESNGMRESSetSelectType(SNES snes, SNESNGMRESSelectType stype) 434*d71ae5a4SJacob Faibussowitsch { 43513a62661SPeter Brune PetscFunctionBegin; 43613a62661SPeter Brune PetscValidHeaderSpecific(snes, SNES_CLASSID, 1); 437cac4c232SBarry Smith PetscTryMethod(snes, "SNESNGMRESSetSelectType_C", (SNES, SNESNGMRESSelectType), (snes, stype)); 43813a62661SPeter Brune PetscFunctionReturn(0); 43913a62661SPeter Brune } 44013a62661SPeter Brune 441*d71ae5a4SJacob Faibussowitsch PetscErrorCode SNESNGMRESSetSelectType_NGMRES(SNES snes, SNESNGMRESSelectType stype) 442*d71ae5a4SJacob Faibussowitsch { 44313a62661SPeter Brune SNES_NGMRES *ngmres = (SNES_NGMRES *)snes->data; 4445fd66863SKarl Rupp 44513a62661SPeter Brune PetscFunctionBegin; 44613a62661SPeter Brune ngmres->select_type = stype; 44713a62661SPeter Brune PetscFunctionReturn(0); 44813a62661SPeter Brune } 44913a62661SPeter Brune 450*d71ae5a4SJacob Faibussowitsch PetscErrorCode SNESNGMRESSetRestartType_NGMRES(SNES snes, SNESNGMRESRestartType rtype) 451*d71ae5a4SJacob Faibussowitsch { 45213a62661SPeter Brune SNES_NGMRES *ngmres = (SNES_NGMRES *)snes->data; 4535fd66863SKarl Rupp 45413a62661SPeter Brune PetscFunctionBegin; 45513a62661SPeter Brune ngmres->restart_type = rtype; 45613a62661SPeter Brune PetscFunctionReturn(0); 45713a62661SPeter Brune } 45813a62661SPeter Brune 459dfbf837cSBarry Smith /*MC 4601867fe5bSPeter Brune SNESNGMRES - The Nonlinear Generalized Minimum Residual method. 461a312c225SMatthew G Knepley 462dfbf837cSBarry Smith Level: beginner 463dfbf837cSBarry Smith 464f6dfbefdSBarry Smith Options Database Keys: 46513a62661SPeter Brune + -snes_ngmres_select_type<difference,none,linesearch> - choose the select between candidate and combined solution 46638774f0aSPeter Brune . -snes_ngmres_restart_type<difference,none,periodic> - choose the restart conditions 467f6dfbefdSBarry Smith . -snes_ngmres_candidate - Use `SNESNGMRES` variant which combines candidate solutions instead of actual solutions 46813a62661SPeter Brune . -snes_ngmres_m - Number of stored previous solutions and residuals 46913a62661SPeter Brune . -snes_ngmres_restart_it - Number of iterations the restart conditions hold before restart 47013a62661SPeter Brune . -snes_ngmres_gammaA - Residual tolerance for solution select between the candidate and combination 47113a62661SPeter Brune . -snes_ngmres_gammaC - Residual tolerance for restart 47213a62661SPeter Brune . -snes_ngmres_epsilonB - Difference tolerance between subsequent solutions triggering restart 47313a62661SPeter Brune . -snes_ngmres_deltaB - Difference tolerance between residuals triggering restart 47423b3e82cSAsbjørn Nilsen Riseth . -snes_ngmres_restart_fm_rise - Restart on residual rise from x_M step 47513a62661SPeter Brune . -snes_ngmres_monitor - Prints relevant information about the ngmres iteration 4765c3e6ab7SPeter Brune . -snes_linesearch_type <basic,l2,cp> - Line search type used for the default smoother 47713a62661SPeter Brune - -additive_snes_linesearch_type - linesearch type used to select between the candidate and combined solution with additive select type 4781867fe5bSPeter Brune 4791867fe5bSPeter Brune Notes: 4801867fe5bSPeter Brune The N-GMRES method combines m previous solutions into a minimum-residual solution by solving a small linearized 4811867fe5bSPeter Brune optimization problem at each iteration. 4821867fe5bSPeter Brune 483f6dfbefdSBarry Smith Very similar to the `SNESANDERSON` algorithm. 4844f02bc6aSBarry Smith 4851867fe5bSPeter Brune References: 486606c0280SSatish Balay + * - C. W. Oosterlee and T. Washio, "Krylov Subspace Acceleration of Nonlinear Multigrid with Application to Recirculating Flows", 487dfbf837cSBarry Smith SIAM Journal on Scientific Computing, 21(5), 2000. 488606c0280SSatish Balay - * - Peter R. Brune, Matthew G. Knepley, Barry F. Smith, and Xuemin Tu, "Composing Scalable Nonlinear Algebraic Solvers", 4894f02bc6aSBarry Smith SIAM Review, 57(4), 2015 4904f02bc6aSBarry Smith 491f6dfbefdSBarry Smith .seealso: `SNESCreate()`, `SNES`, `SNESSetType()`, `SNESType`, `SNESANDERSON`, `SNESNGMRESSetSelectType()`, `SNESNGMRESSetRestartType()`, 492f6dfbefdSBarry Smith `SNESNGMRESSetRestartFmRise()` 493dfbf837cSBarry Smith M*/ 494a312c225SMatthew G Knepley 495*d71ae5a4SJacob Faibussowitsch PETSC_EXTERN PetscErrorCode SNESCreate_NGMRES(SNES snes) 496*d71ae5a4SJacob Faibussowitsch { 497a312c225SMatthew G Knepley SNES_NGMRES *ngmres; 498d8d34be6SBarry Smith SNESLineSearch linesearch; 499a312c225SMatthew G Knepley 500a312c225SMatthew G Knepley PetscFunctionBegin; 501a312c225SMatthew G Knepley snes->ops->destroy = SNESDestroy_NGMRES; 502a312c225SMatthew G Knepley snes->ops->setup = SNESSetUp_NGMRES; 503a312c225SMatthew G Knepley snes->ops->setfromoptions = SNESSetFromOptions_NGMRES; 504a312c225SMatthew G Knepley snes->ops->view = SNESView_NGMRES; 505a312c225SMatthew G Knepley snes->ops->solve = SNESSolve_NGMRES; 506a312c225SMatthew G Knepley snes->ops->reset = SNESReset_NGMRES; 507a312c225SMatthew G Knepley 508efd4aadfSBarry Smith snes->usesnpc = PETSC_TRUE; 5092c155ee1SBarry Smith snes->usesksp = PETSC_FALSE; 510efd4aadfSBarry Smith snes->npcside = PC_RIGHT; 5112c155ee1SBarry Smith 5124fc747eaSLawrence Mitchell snes->alwayscomputesfinalresidual = PETSC_TRUE; 5134fc747eaSLawrence Mitchell 5144dfa11a4SJacob Faibussowitsch PetscCall(PetscNew(&ngmres)); 515a312c225SMatthew G Knepley snes->data = (void *)ngmres; 516d2e16ddcSPeter Brune ngmres->msize = 30; 51719653cdaSPeter Brune 51888976e71SPeter Brune if (!snes->tolerancesset) { 5190e444f03SPeter Brune snes->max_funcs = 30000; 5200e444f03SPeter Brune snes->max_its = 10000; 52188976e71SPeter Brune } 5220e444f03SPeter Brune 52338774f0aSPeter Brune ngmres->candidate = PETSC_FALSE; 524d2e16ddcSPeter Brune 5259566063dSJacob Faibussowitsch PetscCall(SNESGetLineSearch(snes, &linesearch)); 52648a46eb9SPierre Jolivet if (!((PetscObject)linesearch)->type_name) PetscCall(SNESLineSearchSetType(linesearch, SNESLINESEARCHBASIC)); 527d8d34be6SBarry Smith 5280298fd71SBarry Smith ngmres->additive_linesearch = NULL; 529077c4231SPeter Brune ngmres->approxfunc = PETSC_FALSE; 53028ed4a04SPeter Brune ngmres->restart_it = 2; 53113a62661SPeter Brune ngmres->restart_periodic = 30; 532f109b39eSPeter Brune ngmres->gammaA = 2.0; 533f109b39eSPeter Brune ngmres->gammaC = 2.0; 534cac108bcSPeter Brune ngmres->deltaB = 0.9; 535cac108bcSPeter Brune ngmres->epsilonB = 0.1; 53623b3e82cSAsbjørn Nilsen Riseth ngmres->restart_fm_rise = PETSC_FALSE; 537e7058c64SPeter Brune 53813a62661SPeter Brune ngmres->restart_type = SNES_NGMRES_RESTART_DIFFERENCE; 53913a62661SPeter Brune ngmres->select_type = SNES_NGMRES_SELECT_DIFFERENCE; 54013a62661SPeter Brune 5419566063dSJacob Faibussowitsch PetscCall(PetscObjectComposeFunction((PetscObject)snes, "SNESNGMRESSetSelectType_C", SNESNGMRESSetSelectType_NGMRES)); 5429566063dSJacob Faibussowitsch PetscCall(PetscObjectComposeFunction((PetscObject)snes, "SNESNGMRESSetRestartType_C", SNESNGMRESSetRestartType_NGMRES)); 5439566063dSJacob Faibussowitsch PetscCall(PetscObjectComposeFunction((PetscObject)snes, "SNESNGMRESSetRestartFmRise_C", SNESNGMRESSetRestartFmRise_NGMRES)); 5449566063dSJacob Faibussowitsch PetscCall(PetscObjectComposeFunction((PetscObject)snes, "SNESNGMRESGetRestartFmRise_C", SNESNGMRESGetRestartFmRise_NGMRES)); 545a312c225SMatthew G Knepley PetscFunctionReturn(0); 546a312c225SMatthew G Knepley } 547