xref: /petsc/src/snes/impls/ms/ms.c (revision 0619917b5a674bb687c64e7daba2ab22be99af31)
1 #include <petsc/private/snesimpl.h> /*I "petscsnes.h" I*/
2 
3 static SNESMSType SNESMSDefault = SNESMSM62;
4 static PetscBool  SNESMSRegisterAllCalled;
5 static PetscBool  SNESMSPackageInitialized;
6 
7 typedef struct _SNESMSTableau *SNESMSTableau;
8 struct _SNESMSTableau {
9   char      *name;
10   PetscInt   nstages;    /* Number of stages */
11   PetscInt   nregisters; /* Number of registers */
12   PetscReal  stability;  /* Scaled stability region */
13   PetscReal *gamma;      /* Coefficients of 3S* method */
14   PetscReal *delta;      /* Coefficients of 3S* method */
15   PetscReal *betasub;    /* Subdiagonal of beta in Shu-Osher form */
16 };
17 
18 typedef struct _SNESMSTableauLink *SNESMSTableauLink;
19 struct _SNESMSTableauLink {
20   struct _SNESMSTableau tab;
21   SNESMSTableauLink     next;
22 };
23 static SNESMSTableauLink SNESMSTableauList;
24 
25 typedef struct {
26   SNESMSTableau tableau; /* Tableau in low-storage form */
27   PetscReal     damping; /* Damping parameter, like length of (pseudo) time step */
28 } SNES_MS;
29 
30 /*@C
31   SNESMSRegisterAll - Registers all of the multi-stage methods in `SNESMS`
32 
33   Logically Collective
34 
35   Level: developer
36 
37 .seealso: `SNES`, `SNESMS`, `SNESMSRegisterDestroy()`
38 @*/
39 PetscErrorCode SNESMSRegisterAll(void)
40 {
41   PetscFunctionBegin;
42   if (SNESMSRegisterAllCalled) PetscFunctionReturn(PETSC_SUCCESS);
43   SNESMSRegisterAllCalled = PETSC_TRUE;
44 
45   {
46     PetscReal alpha[1] = {1.0};
47     PetscCall(SNESMSRegister(SNESMSEULER, 1, 1, 1.0, NULL, NULL, alpha));
48   }
49 
50   {
51     PetscReal gamma[3][6] = {
52       {0.0000000000000000E+00, -7.0304722367110606E-01, -1.9836719667506464E-01, -1.6023843981863788E+00, 9.4483822882855284E-02,  -1.4204296130641869E-01},
53       {1.0000000000000000E+00, 1.1111025767083920E+00,  5.6150921583923230E-01,  7.4151723494934041E-01,  3.1714538168600587E-01,  4.6479276238548706E-01 },
54       {0.0000000000000000E+00, 0.0000000000000000E+00,  0.0000000000000000E+00,  6.7968174970583317E-01,  -4.1755042846051737E-03, -1.9115668129923846E-01}
55     };
56     PetscReal delta[6]   = {1.0000000000000000E+00, 5.3275427433201750E-01, 6.0143544663985238E-01, 4.5874077053842177E-01, 2.7544386906104651E-01, 0.0000000000000000E+00};
57     PetscReal betasub[6] = {8.4753115429481929E-01, 7.4018896368655618E-01, 6.5963574086583309E-03, 4.6747795645517759E-01, 1.3314545813643919E-01, 5.3260800028018784E-01};
58     PetscCall(SNESMSRegister(SNESMSM62, 6, 3, 1.0, &gamma[0][0], delta, betasub));
59   }
60 
61   { /* Jameson (1983) */
62     PetscReal alpha[4] = {0.25, 0.5, 0.55, 1.0};
63     PetscCall(SNESMSRegister(SNESMSJAMESON83, 4, 1, 1.0, NULL, NULL, alpha));
64   }
65 
66   { /* Van Leer, Tai, and Powell (1989) 1 stage, order 1 */
67     PetscReal alpha[1] = {1.0};
68     PetscCall(SNESMSRegister(SNESMSVLTP11, 1, 1, 0.5, NULL, NULL, alpha));
69   }
70   { /* Van Leer, Tai, and Powell (1989) 2 stage, order 1 */
71     PetscReal alpha[2] = {0.3333, 1.0};
72     PetscCall(SNESMSRegister(SNESMSVLTP21, 2, 1, 1.0, NULL, NULL, alpha));
73   }
74   { /* Van Leer, Tai, and Powell (1989) 3 stage, order 1 */
75     PetscReal alpha[3] = {0.1481, 0.4000, 1.0};
76     PetscCall(SNESMSRegister(SNESMSVLTP31, 3, 1, 1.5, NULL, NULL, alpha));
77   }
78   { /* Van Leer, Tai, and Powell (1989) 4 stage, order 1 */
79     PetscReal alpha[4] = {0.0833, 0.2069, 0.4265, 1.0};
80     PetscCall(SNESMSRegister(SNESMSVLTP41, 4, 1, 2.0, NULL, NULL, alpha));
81   }
82   { /* Van Leer, Tai, and Powell (1989) 5 stage, order 1 */
83     PetscReal alpha[5] = {0.0533, 0.1263, 0.2375, 0.4414, 1.0};
84     PetscCall(SNESMSRegister(SNESMSVLTP51, 5, 1, 2.5, NULL, NULL, alpha));
85   }
86   { /* Van Leer, Tai, and Powell (1989) 6 stage, order 1 */
87     PetscReal alpha[6] = {0.0370, 0.0851, 0.1521, 0.2562, 0.4512, 1.0};
88     PetscCall(SNESMSRegister(SNESMSVLTP61, 6, 1, 3.0, NULL, NULL, alpha));
89   }
90   PetscFunctionReturn(PETSC_SUCCESS);
91 }
92 
93 /*@C
94   SNESMSRegisterDestroy - Frees the list of schemes that were registered by `SNESMSRegister()`.
95 
96   Not Collective
97 
98   Level: developer
99 
100 .seealso: `SNESMSRegister()`, `SNESMSRegisterAll()`
101 @*/
102 PetscErrorCode SNESMSRegisterDestroy(void)
103 {
104   SNESMSTableauLink link;
105 
106   PetscFunctionBegin;
107   while ((link = SNESMSTableauList)) {
108     SNESMSTableau t   = &link->tab;
109     SNESMSTableauList = link->next;
110 
111     PetscCall(PetscFree(t->name));
112     PetscCall(PetscFree(t->gamma));
113     PetscCall(PetscFree(t->delta));
114     PetscCall(PetscFree(t->betasub));
115     PetscCall(PetscFree(link));
116   }
117   SNESMSRegisterAllCalled = PETSC_FALSE;
118   PetscFunctionReturn(PETSC_SUCCESS);
119 }
120 
121 /*@C
122   SNESMSInitializePackage - This function initializes everything in the `SNESMS` package. It is called
123   from `SNESInitializePackage()`.
124 
125   Level: developer
126 
127 .seealso: `PetscInitialize()`
128 @*/
129 PetscErrorCode SNESMSInitializePackage(void)
130 {
131   PetscFunctionBegin;
132   if (SNESMSPackageInitialized) PetscFunctionReturn(PETSC_SUCCESS);
133   SNESMSPackageInitialized = PETSC_TRUE;
134 
135   PetscCall(SNESMSRegisterAll());
136   PetscCall(PetscRegisterFinalize(SNESMSFinalizePackage));
137   PetscFunctionReturn(PETSC_SUCCESS);
138 }
139 
140 /*@C
141   SNESMSFinalizePackage - This function destroys everything in the `SNESMS` package. It is
142   called from `PetscFinalize()`.
143 
144   Level: developer
145 
146 .seealso: `PetscFinalize()`
147 @*/
148 PetscErrorCode SNESMSFinalizePackage(void)
149 {
150   PetscFunctionBegin;
151   SNESMSPackageInitialized = PETSC_FALSE;
152 
153   PetscCall(SNESMSRegisterDestroy());
154   PetscFunctionReturn(PETSC_SUCCESS);
155 }
156 
157 /*@C
158   SNESMSRegister - register a multistage scheme for `SNESMS`
159 
160   Logically Collective
161 
162   Input Parameters:
163 + name       - identifier for method
164 . nstages    - number of stages
165 . nregisters - number of registers used by low-storage implementation
166 . stability  - scaled stability region
167 . gamma      - coefficients, see Ketcheson's paper
168 . delta      - coefficients, see Ketcheson's paper
169 - betasub    - subdiagonal of Shu-Osher form
170 
171   Level: advanced
172 
173   Notes:
174   The notation is described in Ketcheson (2010) Runge-Kutta methods with minimum storage implementations.
175 
176   Many multistage schemes are of the form
177    $ X_0 = X^{(n)}
178    $ X_k = X_0 + \alpha_k * F(X_{k-1}), k = 1,\ldots,s
179    $ X^{(n+1)} = X_s
180   These methods can be registered with
181 .vb
182    SNESMSRegister("name",s,1,stability,NULL,NULL,alpha);
183 .ve
184 
185 .seealso: `SNESMS`
186 @*/
187 PetscErrorCode SNESMSRegister(SNESMSType name, PetscInt nstages, PetscInt nregisters, PetscReal stability, const PetscReal gamma[], const PetscReal delta[], const PetscReal betasub[])
188 {
189   SNESMSTableauLink link;
190   SNESMSTableau     t;
191 
192   PetscFunctionBegin;
193   PetscAssertPointer(name, 1);
194   PetscCheck(nstages >= 1, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Must have at least one stage");
195   if (gamma || delta) {
196     PetscCheck(nregisters == 3, PETSC_COMM_SELF, PETSC_ERR_SUP, "Only support for methods written in 3-register form");
197     PetscAssertPointer(gamma, 5);
198     PetscAssertPointer(delta, 6);
199   } else {
200     PetscCheck(nregisters == 1, PETSC_COMM_SELF, PETSC_ERR_SUP, "Only support for methods written in 1-register form");
201   }
202   PetscAssertPointer(betasub, 7);
203 
204   PetscCall(SNESMSInitializePackage());
205   PetscCall(PetscNew(&link));
206   t = &link->tab;
207   PetscCall(PetscStrallocpy(name, &t->name));
208   t->nstages    = nstages;
209   t->nregisters = nregisters;
210   t->stability  = stability;
211 
212   if (gamma && delta) {
213     PetscCall(PetscMalloc1(nstages * nregisters, &t->gamma));
214     PetscCall(PetscMalloc1(nstages, &t->delta));
215     PetscCall(PetscArraycpy(t->gamma, gamma, nstages * nregisters));
216     PetscCall(PetscArraycpy(t->delta, delta, nstages));
217   }
218   PetscCall(PetscMalloc1(nstages, &t->betasub));
219   PetscCall(PetscArraycpy(t->betasub, betasub, nstages));
220 
221   link->next        = SNESMSTableauList;
222   SNESMSTableauList = link;
223   PetscFunctionReturn(PETSC_SUCCESS);
224 }
225 
226 /*
227   X - initial state, updated in-place.
228   F - residual, computed at the initial X on input
229 */
230 static PetscErrorCode SNESMSStep_3Sstar(SNES snes, Vec X, Vec F)
231 {
232   SNES_MS         *ms      = (SNES_MS *)snes->data;
233   SNESMSTableau    t       = ms->tableau;
234   const PetscInt   nstages = t->nstages;
235   const PetscReal *gamma = t->gamma, *delta = t->delta, *betasub = t->betasub;
236   Vec              S1 = X, S2 = snes->work[1], S3 = snes->work[2], Y = snes->work[0], S1copy = snes->work[3];
237 
238   PetscFunctionBegin;
239   PetscCall(VecZeroEntries(S2));
240   PetscCall(VecCopy(X, S3));
241   for (PetscInt i = 0; i < nstages; i++) {
242     Vec               Ss[]     = {S1copy, S2, S3, Y};
243     const PetscScalar scoeff[] = {gamma[0 * nstages + i] - 1, gamma[1 * nstages + i], gamma[2 * nstages + i], -betasub[i] * ms->damping};
244 
245     PetscCall(VecAXPY(S2, delta[i], S1));
246     if (i > 0) PetscCall(SNESComputeFunction(snes, S1, F));
247     PetscCall(KSPSolve(snes->ksp, F, Y));
248     PetscCall(VecCopy(S1, S1copy));
249     PetscCall(VecMAXPY(S1, 4, scoeff, Ss));
250   }
251   PetscFunctionReturn(PETSC_SUCCESS);
252 }
253 
254 /*
255   X - initial state, updated in-place.
256   F - residual, computed at the initial X on input
257 */
258 static PetscErrorCode SNESMSStep_Basic(SNES snes, Vec X, Vec F)
259 {
260   SNES_MS         *ms    = (SNES_MS *)snes->data;
261   SNESMSTableau    tab   = ms->tableau;
262   const PetscReal *alpha = tab->betasub, h = ms->damping;
263   PetscInt         i, nstages              = tab->nstages;
264   Vec              X0 = snes->work[0];
265 
266   PetscFunctionBegin;
267   PetscCall(VecCopy(X, X0));
268   for (i = 0; i < nstages; i++) {
269     if (i > 0) PetscCall(SNESComputeFunction(snes, X, F));
270     PetscCall(KSPSolve(snes->ksp, F, X));
271     PetscCall(VecAYPX(X, -alpha[i] * h, X0));
272   }
273   PetscFunctionReturn(PETSC_SUCCESS);
274 }
275 
276 static PetscErrorCode SNESMSStep_Step(SNES snes, Vec X, Vec F)
277 {
278   SNES_MS      *ms  = (SNES_MS *)snes->data;
279   SNESMSTableau tab = ms->tableau;
280 
281   PetscFunctionBegin;
282   if (tab->gamma && tab->delta) {
283     PetscCall(SNESMSStep_3Sstar(snes, X, F));
284   } else {
285     PetscCall(SNESMSStep_Basic(snes, X, F));
286   }
287   PetscFunctionReturn(PETSC_SUCCESS);
288 }
289 
290 static PetscErrorCode SNESMSStep_Norms(SNES snes, PetscInt iter, Vec F)
291 {
292   PetscReal fnorm;
293 
294   PetscFunctionBegin;
295   if (SNESNeedNorm_Private(snes, iter)) {
296     PetscCall(VecNorm(F, NORM_2, &fnorm)); /* fnorm <- ||F||  */
297     SNESCheckFunctionNorm(snes, fnorm);
298     /* Monitor convergence */
299     PetscCall(PetscObjectSAWsTakeAccess((PetscObject)snes));
300     snes->iter = iter;
301     snes->norm = fnorm;
302     PetscCall(PetscObjectSAWsGrantAccess((PetscObject)snes));
303     PetscCall(SNESLogConvergenceHistory(snes, snes->norm, 0));
304     /* Test for convergence */
305     PetscCall(SNESConverged(snes, snes->iter, 0.0, 0.0, fnorm));
306     PetscCall(SNESMonitor(snes, snes->iter, snes->norm));
307   } else if (iter > 0) {
308     PetscCall(PetscObjectSAWsTakeAccess((PetscObject)snes));
309     snes->iter = iter;
310     PetscCall(PetscObjectSAWsGrantAccess((PetscObject)snes));
311   }
312   PetscFunctionReturn(PETSC_SUCCESS);
313 }
314 
315 static PetscErrorCode SNESSolve_MS(SNES snes)
316 {
317   Vec      X = snes->vec_sol, F = snes->vec_func;
318   PetscInt i;
319 
320   PetscFunctionBegin;
321   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);
322   PetscCall(PetscCitationsRegister(SNESCitation, &SNEScite));
323 
324   snes->reason = SNES_CONVERGED_ITERATING;
325   PetscCall(PetscObjectSAWsTakeAccess((PetscObject)snes));
326   snes->iter = 0;
327   snes->norm = 0;
328   PetscCall(PetscObjectSAWsGrantAccess((PetscObject)snes));
329 
330   if (!snes->vec_func_init_set) {
331     PetscCall(SNESComputeFunction(snes, X, F));
332   } else snes->vec_func_init_set = PETSC_FALSE;
333 
334   PetscCall(SNESMSStep_Norms(snes, 0, F));
335   if (snes->reason) PetscFunctionReturn(PETSC_SUCCESS);
336 
337   for (i = 0; i < snes->max_its; i++) {
338     /* Call general purpose update function */
339     PetscTryTypeMethod(snes, update, snes->iter);
340 
341     if (i == 0 && snes->jacobian) {
342       /* This method does not require a Jacobian, but it is usually preconditioned by PBJacobi */
343       PetscCall(SNESComputeJacobian(snes, snes->vec_sol, snes->jacobian, snes->jacobian_pre));
344       SNESCheckJacobianDomainerror(snes);
345       PetscCall(KSPSetOperators(snes->ksp, snes->jacobian, snes->jacobian_pre));
346     }
347 
348     PetscCall(SNESMSStep_Step(snes, X, F));
349 
350     if (i < snes->max_its - 1 || SNESNeedNorm_Private(snes, i + 1)) PetscCall(SNESComputeFunction(snes, X, F));
351 
352     PetscCall(SNESMSStep_Norms(snes, i + 1, F));
353     if (snes->reason) PetscFunctionReturn(PETSC_SUCCESS);
354   }
355   PetscFunctionReturn(PETSC_SUCCESS);
356 }
357 
358 static PetscErrorCode SNESSetUp_MS(SNES snes)
359 {
360   SNES_MS      *ms    = (SNES_MS *)snes->data;
361   SNESMSTableau tab   = ms->tableau;
362   PetscInt      nwork = tab->nregisters + 1; // +1 because VecMAXPY() in SNESMSStep_3Sstar()
363                                              // needs an extra work vec
364 
365   PetscFunctionBegin;
366   PetscCall(SNESSetWorkVecs(snes, nwork));
367   PetscCall(SNESSetUpMatrices(snes));
368   PetscFunctionReturn(PETSC_SUCCESS);
369 }
370 
371 static PetscErrorCode SNESReset_MS(SNES snes)
372 {
373   PetscFunctionBegin;
374   PetscFunctionReturn(PETSC_SUCCESS);
375 }
376 
377 static PetscErrorCode SNESDestroy_MS(SNES snes)
378 {
379   PetscFunctionBegin;
380   PetscCall(SNESReset_MS(snes));
381   PetscCall(PetscFree(snes->data));
382   PetscCall(PetscObjectComposeFunction((PetscObject)snes, "SNESMSGetType_C", NULL));
383   PetscCall(PetscObjectComposeFunction((PetscObject)snes, "SNESMSSetType_C", NULL));
384   PetscCall(PetscObjectComposeFunction((PetscObject)snes, "SNESMSGetDamping_C", NULL));
385   PetscCall(PetscObjectComposeFunction((PetscObject)snes, "SNESMSSetDamping_C", NULL));
386   PetscFunctionReturn(PETSC_SUCCESS);
387 }
388 
389 static PetscErrorCode SNESView_MS(SNES snes, PetscViewer viewer)
390 {
391   PetscBool     iascii;
392   SNES_MS      *ms  = (SNES_MS *)snes->data;
393   SNESMSTableau tab = ms->tableau;
394 
395   PetscFunctionBegin;
396   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERASCII, &iascii));
397   if (iascii) PetscCall(PetscViewerASCIIPrintf(viewer, "  multi-stage method type: %s\n", tab->name));
398   PetscFunctionReturn(PETSC_SUCCESS);
399 }
400 
401 static PetscErrorCode SNESSetFromOptions_MS(SNES snes, PetscOptionItems *PetscOptionsObject)
402 {
403   PetscFunctionBegin;
404   PetscOptionsHeadBegin(PetscOptionsObject, "SNES MS options");
405   {
406     SNESMSTableauLink link;
407     PetscInt          count, choice;
408     PetscBool         flg;
409     const char      **namelist;
410     SNESMSType        mstype;
411     PetscReal         damping;
412     PetscBool         norms = PETSC_FALSE;
413 
414     PetscCall(SNESMSGetType(snes, &mstype));
415     for (link = SNESMSTableauList, count = 0; link; link = link->next, count++)
416       ;
417     PetscCall(PetscMalloc1(count, (char ***)&namelist));
418     for (link = SNESMSTableauList, count = 0; link; link = link->next, count++) namelist[count] = link->tab.name;
419     PetscCall(PetscOptionsEList("-snes_ms_type", "Multistage smoother type", "SNESMSSetType", (const char *const *)namelist, count, mstype, &choice, &flg));
420     if (flg) PetscCall(SNESMSSetType(snes, namelist[choice]));
421     PetscCall(PetscFree(namelist));
422     PetscCall(SNESMSGetDamping(snes, &damping));
423     PetscCall(PetscOptionsReal("-snes_ms_damping", "Damping for multistage method", "SNESMSSetDamping", damping, &damping, &flg));
424     if (flg) PetscCall(SNESMSSetDamping(snes, damping));
425 
426     /* deprecated option */
427     PetscCall(PetscOptionsDeprecated("-snes_ms_norms", NULL, "3.20", "Use -snes_norm_schedule always"));
428     PetscCall(PetscOptionsBool("-snes_ms_norms", NULL, NULL, norms, &norms, NULL));
429     if (norms) PetscCall(SNESSetNormSchedule(snes, SNES_NORM_ALWAYS));
430   }
431   PetscOptionsHeadEnd();
432   PetscFunctionReturn(PETSC_SUCCESS);
433 }
434 
435 static PetscErrorCode SNESMSGetType_MS(SNES snes, SNESMSType *mstype)
436 {
437   SNES_MS      *ms  = (SNES_MS *)snes->data;
438   SNESMSTableau tab = ms->tableau;
439 
440   PetscFunctionBegin;
441   *mstype = tab->name;
442   PetscFunctionReturn(PETSC_SUCCESS);
443 }
444 
445 static PetscErrorCode SNESMSSetType_MS(SNES snes, SNESMSType mstype)
446 {
447   SNES_MS          *ms = (SNES_MS *)snes->data;
448   SNESMSTableauLink link;
449   PetscBool         match;
450 
451   PetscFunctionBegin;
452   if (ms->tableau) {
453     PetscCall(PetscStrcmp(ms->tableau->name, mstype, &match));
454     if (match) PetscFunctionReturn(PETSC_SUCCESS);
455   }
456   for (link = SNESMSTableauList; link; link = link->next) {
457     PetscCall(PetscStrcmp(link->tab.name, mstype, &match));
458     if (match) {
459       if (snes->setupcalled) PetscCall(SNESReset_MS(snes));
460       ms->tableau = &link->tab;
461       if (snes->setupcalled) PetscCall(SNESSetUp_MS(snes));
462       PetscFunctionReturn(PETSC_SUCCESS);
463     }
464   }
465   SETERRQ(PetscObjectComm((PetscObject)snes), PETSC_ERR_ARG_UNKNOWN_TYPE, "Could not find '%s'", mstype);
466 }
467 
468 /*@C
469   SNESMSGetType - Get the type of multistage smoother `SNESMS`
470 
471   Not Collective
472 
473   Input Parameter:
474 . snes - nonlinear solver context
475 
476   Output Parameter:
477 . mstype - type of multistage method
478 
479   Level: advanced
480 
481 .seealso: `SNESMS`, `SNESMSSetType()`, `SNESMSType`
482 @*/
483 PetscErrorCode SNESMSGetType(SNES snes, SNESMSType *mstype)
484 {
485   PetscFunctionBegin;
486   PetscValidHeaderSpecific(snes, SNES_CLASSID, 1);
487   PetscAssertPointer(mstype, 2);
488   PetscUseMethod(snes, "SNESMSGetType_C", (SNES, SNESMSType *), (snes, mstype));
489   PetscFunctionReturn(PETSC_SUCCESS);
490 }
491 
492 /*@C
493   SNESMSSetType - Set the type of multistage smoother `SNESMS`
494 
495   Logically Collective
496 
497   Input Parameters:
498 + snes   - nonlinear solver context
499 - mstype - type of multistage method
500 
501   Level: advanced
502 
503 .seealso: `SNESMS`, `SNESMSGetType()`, `SNESMSType`
504 @*/
505 PetscErrorCode SNESMSSetType(SNES snes, SNESMSType mstype)
506 {
507   PetscFunctionBegin;
508   PetscValidHeaderSpecific(snes, SNES_CLASSID, 1);
509   PetscAssertPointer(mstype, 2);
510   PetscTryMethod(snes, "SNESMSSetType_C", (SNES, SNESMSType), (snes, mstype));
511   PetscFunctionReturn(PETSC_SUCCESS);
512 }
513 
514 static PetscErrorCode SNESMSGetDamping_MS(SNES snes, PetscReal *damping)
515 {
516   SNES_MS *ms = (SNES_MS *)snes->data;
517 
518   PetscFunctionBegin;
519   *damping = ms->damping;
520   PetscFunctionReturn(PETSC_SUCCESS);
521 }
522 
523 static PetscErrorCode SNESMSSetDamping_MS(SNES snes, PetscReal damping)
524 {
525   SNES_MS *ms = (SNES_MS *)snes->data;
526 
527   PetscFunctionBegin;
528   ms->damping = damping;
529   PetscFunctionReturn(PETSC_SUCCESS);
530 }
531 
532 /*@C
533   SNESMSGetDamping - Get the damping parameter of `SNESMS` multistage scheme
534 
535   Not Collective
536 
537   Input Parameter:
538 . snes - nonlinear solver context
539 
540   Output Parameter:
541 . damping - damping parameter
542 
543   Level: advanced
544 
545 .seealso: `SNESMSSetDamping()`, `SNESMS`
546 @*/
547 PetscErrorCode SNESMSGetDamping(SNES snes, PetscReal *damping)
548 {
549   PetscFunctionBegin;
550   PetscValidHeaderSpecific(snes, SNES_CLASSID, 1);
551   PetscAssertPointer(damping, 2);
552   PetscUseMethod(snes, "SNESMSGetDamping_C", (SNES, PetscReal *), (snes, damping));
553   PetscFunctionReturn(PETSC_SUCCESS);
554 }
555 
556 /*@C
557   SNESMSSetDamping - Set the damping parameter for a `SNESMS` multistage scheme
558 
559   Logically Collective
560 
561   Input Parameters:
562 + snes    - nonlinear solver context
563 - damping - damping parameter
564 
565   Level: advanced
566 
567 .seealso: `SNESMSGetDamping()`, `SNESMS`
568 @*/
569 PetscErrorCode SNESMSSetDamping(SNES snes, PetscReal damping)
570 {
571   PetscFunctionBegin;
572   PetscValidHeaderSpecific(snes, SNES_CLASSID, 1);
573   PetscValidLogicalCollectiveReal(snes, damping, 2);
574   PetscTryMethod(snes, "SNESMSSetDamping_C", (SNES, PetscReal), (snes, damping));
575   PetscFunctionReturn(PETSC_SUCCESS);
576 }
577 
578 /*MC
579       SNESMS - multi-stage smoothers
580 
581       Options Database Keys:
582 +     -snes_ms_type - type of multi-stage smoother
583 -     -snes_ms_damping - damping for multi-stage method
584 
585       Notes:
586       These multistage methods are explicit Runge-Kutta methods that are often used as smoothers for
587       FAS multigrid for transport problems. In the linear case, these are equivalent to polynomial smoothers (such as Chebyshev).
588 
589       Multi-stage smoothers should usually be preconditioned by point-block Jacobi to ensure proper scaling and to normalize the wave speeds.
590 
591       The methods are specified in low storage form (Ketcheson 2010). New methods can be registered with `SNESMSRegister()`.
592 
593       References:
594 +     * - Ketcheson (2010) Runge Kutta methods with minimum storage implementations (https://doi.org/10.1016/j.jcp.2009.11.006).
595 .     * - Jameson (1983) Solution of the Euler equations for two dimensional transonic flow by a multigrid method (https://doi.org/10.1016/0096-3003(83)90019-X).
596 .     * - Pierce and Giles (1997) Preconditioned multigrid methods for compressible flow calculations on stretched meshes (https://doi.org/10.1006/jcph.1997.5772).
597 -     * - Van Leer, Tai, and Powell (1989) Design of optimally smoothing multi-stage schemes for the Euler equations (https://doi.org/10.2514/6.1989-1933).
598 
599       Level: advanced
600 
601 .seealso: `SNESCreate()`, `SNES`, `SNESSetType()`, `SNESMS`, `SNESFAS`, `KSPCHEBYSHEV`
602 
603 M*/
604 PETSC_EXTERN PetscErrorCode SNESCreate_MS(SNES snes)
605 {
606   SNES_MS *ms;
607 
608   PetscFunctionBegin;
609   PetscCall(SNESMSInitializePackage());
610 
611   snes->ops->setup          = SNESSetUp_MS;
612   snes->ops->solve          = SNESSolve_MS;
613   snes->ops->destroy        = SNESDestroy_MS;
614   snes->ops->setfromoptions = SNESSetFromOptions_MS;
615   snes->ops->view           = SNESView_MS;
616   snes->ops->reset          = SNESReset_MS;
617 
618   snes->usesnpc = PETSC_FALSE;
619   snes->usesksp = PETSC_TRUE;
620 
621   snes->alwayscomputesfinalresidual = PETSC_FALSE;
622 
623   PetscCall(PetscNew(&ms));
624   snes->data  = (void *)ms;
625   ms->damping = 0.9;
626 
627   PetscCall(PetscObjectComposeFunction((PetscObject)snes, "SNESMSGetType_C", SNESMSGetType_MS));
628   PetscCall(PetscObjectComposeFunction((PetscObject)snes, "SNESMSSetType_C", SNESMSSetType_MS));
629   PetscCall(PetscObjectComposeFunction((PetscObject)snes, "SNESMSGetDamping_C", SNESMSGetDamping_MS));
630   PetscCall(PetscObjectComposeFunction((PetscObject)snes, "SNESMSSetDamping_C", SNESMSSetDamping_MS));
631 
632   PetscCall(SNESMSSetType(snes, SNESMSDefault));
633   PetscFunctionReturn(PETSC_SUCCESS);
634 }
635