1 #include <../src/snes/impls/gs/gsimpl.h> /*I "petscsnes.h" I*/ 2 3 /*@ 4 SNESNGSSetTolerances - Sets various parameters used in convergence tests. 5 6 Logically Collective on SNES 7 8 Input Parameters: 9 + snes - the SNES context 10 . abstol - absolute convergence tolerance 11 . rtol - relative convergence tolerance 12 . stol - convergence tolerance in terms of the norm of the change in the solution between steps, || delta x || < stol*|| x || 13 - maxit - maximum number of iterations 14 15 Options Database Keys: 16 + -snes_ngs_atol <abstol> - Sets abstol 17 . -snes_ngs_rtol <rtol> - Sets rtol 18 . -snes_ngs_stol <stol> - Sets stol 19 - -snes_max_it <maxit> - Sets maxit 20 21 Level: intermediate 22 23 .seealso: SNESSetTrustRegionTolerance() 24 @*/ 25 PetscErrorCode SNESNGSSetTolerances(SNES snes,PetscReal abstol,PetscReal rtol,PetscReal stol,PetscInt maxit) 26 { 27 SNES_NGS *gs = (SNES_NGS*)snes->data; 28 29 PetscFunctionBegin; 30 PetscValidHeaderSpecific(snes,SNES_CLASSID,1); 31 32 if (abstol != PETSC_DEFAULT) { 33 PetscCheckFalse(abstol < 0.0,PetscObjectComm((PetscObject)snes),PETSC_ERR_ARG_OUTOFRANGE,"Absolute tolerance %g must be non-negative",(double)abstol); 34 gs->abstol = abstol; 35 } 36 if (rtol != PETSC_DEFAULT) { 37 PetscCheckFalse(rtol < 0.0 || 1.0 <= rtol,PetscObjectComm((PetscObject)snes),PETSC_ERR_ARG_OUTOFRANGE,"Relative tolerance %g must be non-negative and less than 1.0",(double)rtol); 38 gs->rtol = rtol; 39 } 40 if (stol != PETSC_DEFAULT) { 41 PetscCheckFalse(stol < 0.0,PetscObjectComm((PetscObject)snes),PETSC_ERR_ARG_OUTOFRANGE,"Step tolerance %g must be non-negative",(double)stol); 42 gs->stol = stol; 43 } 44 if (maxit != PETSC_DEFAULT) { 45 PetscCheckFalse(maxit < 0,PetscObjectComm((PetscObject)snes),PETSC_ERR_ARG_OUTOFRANGE,"Maximum number of iterations %D must be non-negative",maxit); 46 gs->max_its = maxit; 47 } 48 PetscFunctionReturn(0); 49 } 50 51 /*@ 52 SNESNGSGetTolerances - Gets various parameters used in convergence tests. 53 54 Not Collective 55 56 Input Parameters: 57 + snes - the SNES context 58 . atol - absolute convergence tolerance 59 . rtol - relative convergence tolerance 60 . stol - convergence tolerance in terms of the norm 61 of the change in the solution between steps 62 - maxit - maximum number of iterations 63 64 Notes: 65 The user can specify NULL for any parameter that is not needed. 66 67 Level: intermediate 68 69 .seealso: SNESSetTolerances() 70 @*/ 71 PetscErrorCode SNESNGSGetTolerances(SNES snes,PetscReal *atol,PetscReal *rtol,PetscReal *stol,PetscInt *maxit) 72 { 73 SNES_NGS *gs = (SNES_NGS*)snes->data; 74 75 PetscFunctionBegin; 76 PetscValidHeaderSpecific(snes,SNES_CLASSID,1); 77 if (atol) *atol = gs->abstol; 78 if (rtol) *rtol = gs->rtol; 79 if (stol) *stol = gs->stol; 80 if (maxit) *maxit = gs->max_its; 81 PetscFunctionReturn(0); 82 } 83 84 /*@ 85 SNESNGSSetSweeps - Sets the number of sweeps of GS to use. 86 87 Input Parameters: 88 + snes - the SNES context 89 - sweeps - the number of sweeps of GS to perform. 90 91 Level: intermediate 92 93 .seealso: SNESSetNGS(), SNESGetNGS(), SNESSetNPC(), SNESNGSGetSweeps() 94 @*/ 95 96 PetscErrorCode SNESNGSSetSweeps(SNES snes, PetscInt sweeps) 97 { 98 SNES_NGS *gs = (SNES_NGS*)snes->data; 99 100 PetscFunctionBegin; 101 PetscValidHeaderSpecific(snes,SNES_CLASSID,1); 102 gs->sweeps = sweeps; 103 PetscFunctionReturn(0); 104 } 105 106 /*@ 107 SNESNGSGetSweeps - Gets the number of sweeps GS will use. 108 109 Input Parameters: 110 . snes - the SNES context 111 112 Output Parameters: 113 . sweeps - the number of sweeps of GS to perform. 114 115 Level: intermediate 116 117 .seealso: SNESSetNGS(), SNESGetNGS(), SNESSetNPC(), SNESNGSSetSweeps() 118 @*/ 119 PetscErrorCode SNESNGSGetSweeps(SNES snes, PetscInt * sweeps) 120 { 121 SNES_NGS *gs = (SNES_NGS*)snes->data; 122 123 PetscFunctionBegin; 124 PetscValidHeaderSpecific(snes,SNES_CLASSID,1); 125 *sweeps = gs->sweeps; 126 PetscFunctionReturn(0); 127 } 128 129 PetscErrorCode SNESReset_NGS(SNES snes) 130 { 131 SNES_NGS *gs = (SNES_NGS*)snes->data; 132 133 PetscFunctionBegin; 134 PetscCall(ISColoringDestroy(&gs->coloring)); 135 PetscFunctionReturn(0); 136 } 137 138 PetscErrorCode SNESDestroy_NGS(SNES snes) 139 { 140 PetscFunctionBegin; 141 PetscCall(SNESReset_NGS(snes)); 142 PetscCall(PetscFree(snes->data)); 143 PetscFunctionReturn(0); 144 } 145 146 PetscErrorCode SNESSetUp_NGS(SNES snes) 147 { 148 PetscErrorCode (*f)(SNES,Vec,Vec,void*); 149 150 PetscFunctionBegin; 151 PetscCall(SNESGetNGS(snes,&f,NULL)); 152 if (!f) { 153 PetscCall(SNESSetNGS(snes,SNESComputeNGSDefaultSecant,NULL)); 154 } 155 PetscFunctionReturn(0); 156 } 157 158 PetscErrorCode SNESSetFromOptions_NGS(PetscOptionItems *PetscOptionsObject,SNES snes) 159 { 160 SNES_NGS *gs = (SNES_NGS*)snes->data; 161 PetscInt sweeps,max_its=PETSC_DEFAULT; 162 PetscReal rtol=PETSC_DEFAULT,atol=PETSC_DEFAULT,stol=PETSC_DEFAULT; 163 PetscBool flg,flg1,flg2,flg3; 164 165 PetscFunctionBegin; 166 PetscCall(PetscOptionsHead(PetscOptionsObject,"SNES GS options")); 167 /* GS Options */ 168 PetscCall(PetscOptionsInt("-snes_ngs_sweeps","Number of sweeps of GS to apply","SNESComputeGS",gs->sweeps,&sweeps,&flg)); 169 if (flg) { 170 PetscCall(SNESNGSSetSweeps(snes,sweeps)); 171 } 172 PetscCall(PetscOptionsReal("-snes_ngs_atol","Absolute residual tolerance for GS iteration","SNESComputeGS",gs->abstol,&atol,&flg)); 173 PetscCall(PetscOptionsReal("-snes_ngs_rtol","Relative residual tolerance for GS iteration","SNESComputeGS",gs->rtol,&rtol,&flg1)); 174 PetscCall(PetscOptionsReal("-snes_ngs_stol","Absolute update tolerance for GS iteration","SNESComputeGS",gs->stol,&stol,&flg2)); 175 PetscCall(PetscOptionsInt("-snes_ngs_max_it","Maximum number of sweeps of GS to apply","SNESComputeGS",gs->max_its,&max_its,&flg3)); 176 if (flg || flg1 || flg2 || flg3) { 177 PetscCall(SNESNGSSetTolerances(snes,atol,rtol,stol,max_its)); 178 } 179 flg = PETSC_FALSE; 180 PetscCall(PetscOptionsBool("-snes_ngs_secant","Use finite difference secant approximation with coloring","",flg,&flg,NULL)); 181 if (flg) { 182 PetscCall(SNESSetNGS(snes,SNESComputeNGSDefaultSecant,NULL)); 183 PetscCall(PetscInfo(snes,"Setting default finite difference secant approximation with coloring\n")); 184 } 185 PetscCall(PetscOptionsReal("-snes_ngs_secant_h","Differencing parameter for secant search","",gs->h,&gs->h,NULL)); 186 PetscCall(PetscOptionsBool("-snes_ngs_secant_mat_coloring","Use the graph coloring of the Jacobian for the secant GS","",gs->secant_mat,&gs->secant_mat,&flg)); 187 188 PetscCall(PetscOptionsTail()); 189 PetscFunctionReturn(0); 190 } 191 192 PetscErrorCode SNESView_NGS(SNES snes, PetscViewer viewer) 193 { 194 PetscErrorCode (*f)(SNES,Vec,Vec,void*); 195 SNES_NGS *gs = (SNES_NGS*)snes->data; 196 PetscBool iascii; 197 198 PetscFunctionBegin; 199 PetscCall(PetscObjectTypeCompare((PetscObject)viewer,PETSCVIEWERASCII,&iascii)); 200 if (iascii) { 201 PetscCall(DMSNESGetNGS(snes->dm,&f,NULL)); 202 if (f == SNESComputeNGSDefaultSecant) { 203 PetscCall(PetscViewerASCIIPrintf(viewer," Use finite difference secant approximation with coloring with h = %g \n",(double)gs->h)); 204 } 205 } 206 PetscFunctionReturn(0); 207 } 208 209 PetscErrorCode SNESSolve_NGS(SNES snes) 210 { 211 Vec F; 212 Vec X; 213 Vec B; 214 PetscInt i; 215 PetscReal fnorm; 216 SNESNormSchedule normschedule; 217 218 PetscFunctionBegin; 219 220 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); 221 222 PetscCall(PetscCitationsRegister(SNESCitation,&SNEScite)); 223 X = snes->vec_sol; 224 F = snes->vec_func; 225 B = snes->vec_rhs; 226 227 PetscCall(PetscObjectSAWsTakeAccess((PetscObject)snes)); 228 snes->iter = 0; 229 snes->norm = 0.; 230 PetscCall(PetscObjectSAWsGrantAccess((PetscObject)snes)); 231 snes->reason = SNES_CONVERGED_ITERATING; 232 233 PetscCall(SNESGetNormSchedule(snes, &normschedule)); 234 if (normschedule == SNES_NORM_ALWAYS || normschedule == SNES_NORM_INITIAL_ONLY || normschedule == SNES_NORM_INITIAL_FINAL_ONLY) { 235 /* compute the initial function and preconditioned update delX */ 236 if (!snes->vec_func_init_set) { 237 PetscCall(SNESComputeFunction(snes,X,F)); 238 } else snes->vec_func_init_set = PETSC_FALSE; 239 240 PetscCall(VecNorm(F, NORM_2, &fnorm)); /* fnorm <- ||F|| */ 241 SNESCheckFunctionNorm(snes,fnorm); 242 PetscCall(PetscObjectSAWsTakeAccess((PetscObject)snes)); 243 snes->iter = 0; 244 snes->norm = fnorm; 245 PetscCall(PetscObjectSAWsGrantAccess((PetscObject)snes)); 246 PetscCall(SNESLogConvergenceHistory(snes,snes->norm,0)); 247 PetscCall(SNESMonitor(snes,0,snes->norm)); 248 249 /* test convergence */ 250 PetscCall((*snes->ops->converged)(snes,0,0.0,0.0,fnorm,&snes->reason,snes->cnvP)); 251 if (snes->reason) PetscFunctionReturn(0); 252 } else { 253 PetscCall(PetscObjectSAWsGrantAccess((PetscObject)snes)); 254 PetscCall(SNESLogConvergenceHistory(snes,snes->norm,0)); 255 } 256 257 /* Call general purpose update function */ 258 if (snes->ops->update) { 259 PetscCall((*snes->ops->update)(snes, snes->iter)); 260 } 261 262 for (i = 0; i < snes->max_its; i++) { 263 PetscCall(SNESComputeNGS(snes, B, X)); 264 /* only compute norms if requested or about to exit due to maximum iterations */ 265 if (normschedule == SNES_NORM_ALWAYS || ((i == snes->max_its - 1) && (normschedule == SNES_NORM_INITIAL_FINAL_ONLY || normschedule == SNES_NORM_FINAL_ONLY))) { 266 PetscCall(SNESComputeFunction(snes,X,F)); 267 PetscCall(VecNorm(F, NORM_2, &fnorm)); /* fnorm <- ||F|| */ 268 SNESCheckFunctionNorm(snes,fnorm); 269 /* Monitor convergence */ 270 PetscCall(PetscObjectSAWsTakeAccess((PetscObject)snes)); 271 snes->iter = i+1; 272 snes->norm = fnorm; 273 PetscCall(PetscObjectSAWsGrantAccess((PetscObject)snes)); 274 PetscCall(SNESLogConvergenceHistory(snes,snes->norm,0)); 275 PetscCall(SNESMonitor(snes,snes->iter,snes->norm)); 276 } 277 /* Test for convergence */ 278 if (normschedule == SNES_NORM_ALWAYS) PetscCall((*snes->ops->converged)(snes,snes->iter,0.0,0.0,fnorm,&snes->reason,snes->cnvP)); 279 if (snes->reason) PetscFunctionReturn(0); 280 /* Call general purpose update function */ 281 if (snes->ops->update) { 282 PetscCall((*snes->ops->update)(snes, snes->iter)); 283 } 284 } 285 if (normschedule == SNES_NORM_ALWAYS) { 286 if (i == snes->max_its) { 287 PetscCall(PetscInfo(snes,"Maximum number of iterations has been reached: %D\n",snes->max_its)); 288 if (!snes->reason) snes->reason = SNES_DIVERGED_MAX_IT; 289 } 290 } else if (!snes->reason) snes->reason = SNES_CONVERGED_ITS; /* GS is meant to be used as a preconditioner */ 291 PetscFunctionReturn(0); 292 } 293 294 /*MC 295 SNESNGS - Either calls the user-provided solution routine provided with SNESSetNGS() or does a finite difference secant approximation 296 using coloring. 297 298 Level: advanced 299 300 Options Database: 301 + -snes_ngs_sweeps <n> - Number of sweeps of GS to apply 302 . -snes_ngs_atol <atol> - Absolute residual tolerance for GS iteration 303 . -snes_ngs_rtol <rtol> - Relative residual tolerance for GS iteration 304 . -snes_ngs_stol <stol> - Absolute update tolerance for GS iteration 305 . -snes_ngs_max_it <maxit> - Maximum number of sweeps of GS to apply 306 . -snes_ngs_secant - Use pointwise secant local Jacobian approximation with coloring instead of user provided Gauss-Seidel routine, this is 307 used by default if no user provided Gauss-Seidel routine is available. Requires either that a DM that can compute a coloring 308 is available or a Jacobian sparse matrix is provided (from which to get the coloring). 309 . -snes_ngs_secant_h <h> - Differencing parameter for secant approximation 310 . -snes_ngs_secant_mat_coloring - Use the graph coloring of the Jacobian for the secant GS even if a DM is available. 311 - -snes_norm_schedule <none, always, initialonly, finalonly, initialfinalonly> - how often the residual norms are computed 312 313 Notes: 314 the Gauss-Seidel smoother is inherited through composition. If a solver has been created with SNESGetNPC(), it will have 315 its parent's Gauss-Seidel routine associated with it. 316 317 By default this routine computes the solution norm at each iteration, this can be time consuming, you can turn this off with SNESSetNormSchedule() or -snes_norm_schedule 318 References: 319 . * - Peter R. Brune, Matthew G. Knepley, Barry F. Smith, and Xuemin Tu, "Composing Scalable Nonlinear Algebraic Solvers", 320 SIAM Review, 57(4), 2015 321 322 .seealso: SNESCreate(), SNES, SNESSetType(), SNESSetNGS(), SNESType (for list of available types), SNESNGSSetSweeps(), SNESNGSSetTolerances(), 323 SNESSetNormSchedule() 324 M*/ 325 326 PETSC_EXTERN PetscErrorCode SNESCreate_NGS(SNES snes) 327 { 328 SNES_NGS *gs; 329 330 PetscFunctionBegin; 331 snes->ops->destroy = SNESDestroy_NGS; 332 snes->ops->setup = SNESSetUp_NGS; 333 snes->ops->setfromoptions = SNESSetFromOptions_NGS; 334 snes->ops->view = SNESView_NGS; 335 snes->ops->solve = SNESSolve_NGS; 336 snes->ops->reset = SNESReset_NGS; 337 338 snes->usesksp = PETSC_FALSE; 339 snes->usesnpc = PETSC_FALSE; 340 341 snes->alwayscomputesfinalresidual = PETSC_FALSE; 342 343 if (!snes->tolerancesset) { 344 snes->max_its = 10000; 345 snes->max_funcs = 10000; 346 } 347 348 PetscCall(PetscNewLog(snes,&gs)); 349 350 gs->sweeps = 1; 351 gs->rtol = 1e-5; 352 gs->abstol = PETSC_MACHINE_EPSILON; 353 gs->stol = 1000*PETSC_MACHINE_EPSILON; 354 gs->max_its = 50; 355 gs->h = PETSC_SQRT_MACHINE_EPSILON; 356 357 snes->data = (void*) gs; 358 PetscFunctionReturn(0); 359 } 360