xref: /petsc/src/snes/impls/ms/ms.c (revision 0baf8eba40dbc839082666f9f7396a225d6f663c)
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: [](ch_snes), `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   Logically Collective
97 
98   Level: developer
99 
100 .seealso: [](ch_snes), `SNES`, `SNESMS`, `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: [](ch_snes), `SNES`, `SNESMS`, `SNESMSRegister()`, `SNESMSRegisterAll()`, `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: [](ch_snes), `SNES`, `SNESMS`, `SNESMSRegister()`, `SNESMSRegisterAll()`, `SNESMSInitializePackage()`, `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, No Fortran Support
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 {cite}`ketcheson2010runge`
168 . delta      - coefficients, see Ketcheson's paper {cite}`ketcheson2010runge`
169 - betasub    - subdiagonal of Shu-Osher form
170 
171   Level: advanced
172 
173   Notes:
174   The notation is described in {cite}`ketcheson2010runge` Ketcheson (2010) Runge-Kutta methods with minimum storage implementations.
175 
176   Many multistage schemes are of the form
177 
178   $$
179   \begin{align*}
180   X_0 = X^{(n)} \\
181   X_k = X_0 + \alpha_k * F(X_{k-1}), k = 1,\ldots,s \\
182   X^{(n+1)} = X_s
183   \end{align*}
184   $$
185 
186   These methods can be registered with
187 .vb
188    SNESMSRegister("name",s,1,stability,NULL,NULL,alpha);
189 .ve
190 
191 .seealso: [](ch_snes), `SNES`, `SNESMS`
192 @*/
193 PetscErrorCode SNESMSRegister(SNESMSType name, PetscInt nstages, PetscInt nregisters, PetscReal stability, const PetscReal gamma[], const PetscReal delta[], const PetscReal betasub[])
194 {
195   SNESMSTableauLink link;
196   SNESMSTableau     t;
197 
198   PetscFunctionBegin;
199   PetscAssertPointer(name, 1);
200   PetscCheck(nstages >= 1, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Must have at least one stage");
201   if (gamma || delta) {
202     PetscCheck(nregisters == 3, PETSC_COMM_SELF, PETSC_ERR_SUP, "Only support for methods written in 3-register form");
203     PetscAssertPointer(gamma, 5);
204     PetscAssertPointer(delta, 6);
205   } else {
206     PetscCheck(nregisters == 1, PETSC_COMM_SELF, PETSC_ERR_SUP, "Only support for methods written in 1-register form");
207   }
208   PetscAssertPointer(betasub, 7);
209 
210   PetscCall(SNESMSInitializePackage());
211   PetscCall(PetscNew(&link));
212   t = &link->tab;
213   PetscCall(PetscStrallocpy(name, &t->name));
214   t->nstages    = nstages;
215   t->nregisters = nregisters;
216   t->stability  = stability;
217 
218   if (gamma && delta) {
219     PetscCall(PetscMalloc1(nstages * nregisters, &t->gamma));
220     PetscCall(PetscMalloc1(nstages, &t->delta));
221     PetscCall(PetscArraycpy(t->gamma, gamma, nstages * nregisters));
222     PetscCall(PetscArraycpy(t->delta, delta, nstages));
223   }
224   PetscCall(PetscMalloc1(nstages, &t->betasub));
225   PetscCall(PetscArraycpy(t->betasub, betasub, nstages));
226 
227   link->next        = SNESMSTableauList;
228   SNESMSTableauList = link;
229   PetscFunctionReturn(PETSC_SUCCESS);
230 }
231 
232 /*
233   X - initial state, updated in-place.
234   F - residual, computed at the initial X on input
235 */
236 static PetscErrorCode SNESMSStep_3Sstar(SNES snes, Vec X, Vec F)
237 {
238   SNES_MS         *ms      = (SNES_MS *)snes->data;
239   SNESMSTableau    t       = ms->tableau;
240   const PetscInt   nstages = t->nstages;
241   const PetscReal *gamma = t->gamma, *delta = t->delta, *betasub = t->betasub;
242   Vec              S1 = X, S2 = snes->work[1], S3 = snes->work[2], Y = snes->work[0], S1copy = snes->work[3];
243 
244   PetscFunctionBegin;
245   PetscCall(VecZeroEntries(S2));
246   PetscCall(VecCopy(X, S3));
247   for (PetscInt i = 0; i < nstages; i++) {
248     Vec               Ss[]     = {S1copy, S2, S3, Y};
249     const PetscScalar scoeff[] = {gamma[0 * nstages + i] - 1, gamma[1 * nstages + i], gamma[2 * nstages + i], -betasub[i] * ms->damping};
250 
251     PetscCall(VecAXPY(S2, delta[i], S1));
252     if (i > 0) PetscCall(SNESComputeFunction(snes, S1, F));
253     PetscCall(KSPSolve(snes->ksp, F, Y));
254     PetscCall(VecCopy(S1, S1copy));
255     PetscCall(VecMAXPY(S1, 4, scoeff, Ss));
256   }
257   PetscFunctionReturn(PETSC_SUCCESS);
258 }
259 
260 /*
261   X - initial state, updated in-place.
262   F - residual, computed at the initial X on input
263 */
264 static PetscErrorCode SNESMSStep_Basic(SNES snes, Vec X, Vec F)
265 {
266   SNES_MS         *ms    = (SNES_MS *)snes->data;
267   SNESMSTableau    tab   = ms->tableau;
268   const PetscReal *alpha = tab->betasub, h = ms->damping;
269   PetscInt         i, nstages              = tab->nstages;
270   Vec              X0 = snes->work[0];
271 
272   PetscFunctionBegin;
273   PetscCall(VecCopy(X, X0));
274   for (i = 0; i < nstages; i++) {
275     if (i > 0) PetscCall(SNESComputeFunction(snes, X, F));
276     PetscCall(KSPSolve(snes->ksp, F, X));
277     PetscCall(VecAYPX(X, -alpha[i] * h, X0));
278   }
279   PetscFunctionReturn(PETSC_SUCCESS);
280 }
281 
282 static PetscErrorCode SNESMSStep_Step(SNES snes, Vec X, Vec F)
283 {
284   SNES_MS      *ms  = (SNES_MS *)snes->data;
285   SNESMSTableau tab = ms->tableau;
286 
287   PetscFunctionBegin;
288   if (tab->gamma && tab->delta) {
289     PetscCall(SNESMSStep_3Sstar(snes, X, F));
290   } else {
291     PetscCall(SNESMSStep_Basic(snes, X, F));
292   }
293   PetscFunctionReturn(PETSC_SUCCESS);
294 }
295 
296 static PetscErrorCode SNESMSStep_Norms(SNES snes, PetscInt iter, Vec F)
297 {
298   PetscReal fnorm;
299 
300   PetscFunctionBegin;
301   if (SNESNeedNorm_Private(snes, iter)) {
302     PetscCall(VecNorm(F, NORM_2, &fnorm)); /* fnorm <- ||F||  */
303     SNESCheckFunctionNorm(snes, fnorm);
304     /* Monitor convergence */
305     PetscCall(PetscObjectSAWsTakeAccess((PetscObject)snes));
306     snes->iter = iter;
307     snes->norm = fnorm;
308     PetscCall(PetscObjectSAWsGrantAccess((PetscObject)snes));
309     PetscCall(SNESLogConvergenceHistory(snes, snes->norm, 0));
310     /* Test for convergence */
311     PetscCall(SNESConverged(snes, snes->iter, 0.0, 0.0, fnorm));
312     PetscCall(SNESMonitor(snes, snes->iter, snes->norm));
313   } else if (iter > 0) {
314     PetscCall(PetscObjectSAWsTakeAccess((PetscObject)snes));
315     snes->iter = iter;
316     PetscCall(PetscObjectSAWsGrantAccess((PetscObject)snes));
317   }
318   PetscFunctionReturn(PETSC_SUCCESS);
319 }
320 
321 static PetscErrorCode SNESSolve_MS(SNES snes)
322 {
323   Vec      X = snes->vec_sol, F = snes->vec_func;
324   PetscInt i;
325 
326   PetscFunctionBegin;
327   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);
328   PetscCall(PetscCitationsRegister(SNESCitation, &SNEScite));
329 
330   snes->reason = SNES_CONVERGED_ITERATING;
331   PetscCall(PetscObjectSAWsTakeAccess((PetscObject)snes));
332   snes->iter = 0;
333   snes->norm = 0;
334   PetscCall(PetscObjectSAWsGrantAccess((PetscObject)snes));
335 
336   if (!snes->vec_func_init_set) {
337     PetscCall(SNESComputeFunction(snes, X, F));
338   } else snes->vec_func_init_set = PETSC_FALSE;
339 
340   PetscCall(SNESMSStep_Norms(snes, 0, F));
341   if (snes->reason) PetscFunctionReturn(PETSC_SUCCESS);
342 
343   for (i = 0; i < snes->max_its; i++) {
344     /* Call general purpose update function */
345     PetscTryTypeMethod(snes, update, snes->iter);
346 
347     if (i == 0 && snes->jacobian) {
348       /* This method does not require a Jacobian, but it is usually preconditioned by PBJacobi */
349       PetscCall(SNESComputeJacobian(snes, snes->vec_sol, snes->jacobian, snes->jacobian_pre));
350       SNESCheckJacobianDomainerror(snes);
351       PetscCall(KSPSetOperators(snes->ksp, snes->jacobian, snes->jacobian_pre));
352     }
353 
354     PetscCall(SNESMSStep_Step(snes, X, F));
355 
356     if (i < snes->max_its - 1 || SNESNeedNorm_Private(snes, i + 1)) PetscCall(SNESComputeFunction(snes, X, F));
357 
358     PetscCall(SNESMSStep_Norms(snes, i + 1, F));
359     if (snes->reason) PetscFunctionReturn(PETSC_SUCCESS);
360   }
361   PetscFunctionReturn(PETSC_SUCCESS);
362 }
363 
364 static PetscErrorCode SNESSetUp_MS(SNES snes)
365 {
366   SNES_MS      *ms    = (SNES_MS *)snes->data;
367   SNESMSTableau tab   = ms->tableau;
368   PetscInt      nwork = tab->nregisters + 1; // +1 because VecMAXPY() in SNESMSStep_3Sstar()
369                                              // needs an extra work vec
370 
371   PetscFunctionBegin;
372   PetscCall(SNESSetWorkVecs(snes, nwork));
373   PetscCall(SNESSetUpMatrices(snes));
374   PetscFunctionReturn(PETSC_SUCCESS);
375 }
376 
377 static PetscErrorCode SNESDestroy_MS(SNES snes)
378 {
379   PetscFunctionBegin;
380   PetscCall(PetscFree(snes->data));
381   PetscCall(PetscObjectComposeFunction((PetscObject)snes, "SNESMSGetType_C", NULL));
382   PetscCall(PetscObjectComposeFunction((PetscObject)snes, "SNESMSSetType_C", NULL));
383   PetscCall(PetscObjectComposeFunction((PetscObject)snes, "SNESMSGetDamping_C", NULL));
384   PetscCall(PetscObjectComposeFunction((PetscObject)snes, "SNESMSSetDamping_C", NULL));
385   PetscFunctionReturn(PETSC_SUCCESS);
386 }
387 
388 static PetscErrorCode SNESView_MS(SNES snes, PetscViewer viewer)
389 {
390   PetscBool     iascii;
391   SNES_MS      *ms  = (SNES_MS *)snes->data;
392   SNESMSTableau tab = ms->tableau;
393 
394   PetscFunctionBegin;
395   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERASCII, &iascii));
396   if (iascii) PetscCall(PetscViewerASCIIPrintf(viewer, "  multi-stage method type: %s\n", tab->name));
397   PetscFunctionReturn(PETSC_SUCCESS);
398 }
399 
400 static PetscErrorCode SNESSetFromOptions_MS(SNES snes, PetscOptionItems PetscOptionsObject)
401 {
402   PetscFunctionBegin;
403   PetscOptionsHeadBegin(PetscOptionsObject, "SNES MS options");
404   {
405     SNESMSTableauLink link;
406     PetscInt          count, choice;
407     PetscBool         flg;
408     const char      **namelist;
409     SNESMSType        mstype;
410     PetscReal         damping;
411     PetscBool         norms = PETSC_FALSE;
412 
413     PetscCall(SNESMSGetType(snes, &mstype));
414     for (link = SNESMSTableauList, count = 0; link; link = link->next, count++);
415     PetscCall(PetscMalloc1(count, (char ***)&namelist));
416     for (link = SNESMSTableauList, count = 0; link; link = link->next, count++) namelist[count] = link->tab.name;
417     PetscCall(PetscOptionsEList("-snes_ms_type", "Multistage smoother type", "SNESMSSetType", (const char *const *)namelist, count, mstype, &choice, &flg));
418     if (flg) PetscCall(SNESMSSetType(snes, namelist[choice]));
419     PetscCall(PetscFree(namelist));
420     PetscCall(SNESMSGetDamping(snes, &damping));
421     PetscCall(PetscOptionsReal("-snes_ms_damping", "Damping for multistage method", "SNESMSSetDamping", damping, &damping, &flg));
422     if (flg) PetscCall(SNESMSSetDamping(snes, damping));
423 
424     /* deprecated option */
425     PetscCall(PetscOptionsDeprecated("-snes_ms_norms", NULL, "3.20", "Use -snes_norm_schedule always"));
426     PetscCall(PetscOptionsBool("-snes_ms_norms", NULL, NULL, norms, &norms, NULL));
427     if (norms) PetscCall(SNESSetNormSchedule(snes, SNES_NORM_ALWAYS));
428   }
429   PetscOptionsHeadEnd();
430   PetscFunctionReturn(PETSC_SUCCESS);
431 }
432 
433 static PetscErrorCode SNESMSGetType_MS(SNES snes, SNESMSType *mstype)
434 {
435   SNES_MS      *ms  = (SNES_MS *)snes->data;
436   SNESMSTableau tab = ms->tableau;
437 
438   PetscFunctionBegin;
439   *mstype = tab->name;
440   PetscFunctionReturn(PETSC_SUCCESS);
441 }
442 
443 static PetscErrorCode SNESMSSetType_MS(SNES snes, SNESMSType mstype)
444 {
445   SNES_MS          *ms = (SNES_MS *)snes->data;
446   SNESMSTableauLink link;
447   PetscBool         match;
448 
449   PetscFunctionBegin;
450   if (ms->tableau) {
451     PetscCall(PetscStrcmp(ms->tableau->name, mstype, &match));
452     if (match) PetscFunctionReturn(PETSC_SUCCESS);
453   }
454   for (link = SNESMSTableauList; link; link = link->next) {
455     PetscCall(PetscStrcmp(link->tab.name, mstype, &match));
456     if (match) {
457       ms->tableau = &link->tab;
458       if (snes->setupcalled) PetscCall(SNESSetUp_MS(snes));
459       PetscFunctionReturn(PETSC_SUCCESS);
460     }
461   }
462   SETERRQ(PetscObjectComm((PetscObject)snes), PETSC_ERR_ARG_UNKNOWN_TYPE, "Could not find '%s'", mstype);
463 }
464 
465 /*@
466   SNESMSGetType - Get the type of multistage smoother `SNESMS`
467 
468   Not Collective
469 
470   Input Parameter:
471 . snes - nonlinear solver context
472 
473   Output Parameter:
474 . mstype - type of multistage method
475 
476   Level: advanced
477 
478 .seealso: [](ch_snes), `SNESMS`, `SNESMSSetType()`, `SNESMSType`
479 @*/
480 PetscErrorCode SNESMSGetType(SNES snes, SNESMSType *mstype)
481 {
482   PetscFunctionBegin;
483   PetscValidHeaderSpecific(snes, SNES_CLASSID, 1);
484   PetscAssertPointer(mstype, 2);
485   PetscUseMethod(snes, "SNESMSGetType_C", (SNES, SNESMSType *), (snes, mstype));
486   PetscFunctionReturn(PETSC_SUCCESS);
487 }
488 
489 /*@
490   SNESMSSetType - Set the type of multistage smoother `SNESMS`
491 
492   Logically Collective
493 
494   Input Parameters:
495 + snes   - nonlinear solver context
496 - mstype - type of multistage method
497 
498   Level: advanced
499 
500 .seealso: [](ch_snes), `SNESMS`, `SNESMSGetType()`, `SNESMSType`
501 @*/
502 PetscErrorCode SNESMSSetType(SNES snes, SNESMSType mstype)
503 {
504   PetscFunctionBegin;
505   PetscValidHeaderSpecific(snes, SNES_CLASSID, 1);
506   PetscAssertPointer(mstype, 2);
507   PetscTryMethod(snes, "SNESMSSetType_C", (SNES, SNESMSType), (snes, mstype));
508   PetscFunctionReturn(PETSC_SUCCESS);
509 }
510 
511 static PetscErrorCode SNESMSGetDamping_MS(SNES snes, PetscReal *damping)
512 {
513   SNES_MS *ms = (SNES_MS *)snes->data;
514 
515   PetscFunctionBegin;
516   *damping = ms->damping;
517   PetscFunctionReturn(PETSC_SUCCESS);
518 }
519 
520 static PetscErrorCode SNESMSSetDamping_MS(SNES snes, PetscReal damping)
521 {
522   SNES_MS *ms = (SNES_MS *)snes->data;
523 
524   PetscFunctionBegin;
525   ms->damping = damping;
526   PetscFunctionReturn(PETSC_SUCCESS);
527 }
528 
529 /*@
530   SNESMSGetDamping - Get the damping parameter of `SNESMS` multistage scheme
531 
532   Not Collective
533 
534   Input Parameter:
535 . snes - nonlinear solver context
536 
537   Output Parameter:
538 . damping - damping parameter
539 
540   Level: advanced
541 
542 .seealso: [](ch_snes), `SNESMSSetDamping()`, `SNESMS`
543 @*/
544 PetscErrorCode SNESMSGetDamping(SNES snes, PetscReal *damping)
545 {
546   PetscFunctionBegin;
547   PetscValidHeaderSpecific(snes, SNES_CLASSID, 1);
548   PetscAssertPointer(damping, 2);
549   PetscUseMethod(snes, "SNESMSGetDamping_C", (SNES, PetscReal *), (snes, damping));
550   PetscFunctionReturn(PETSC_SUCCESS);
551 }
552 
553 /*@
554   SNESMSSetDamping - Set the damping parameter for a `SNESMS` multistage scheme
555 
556   Logically Collective
557 
558   Input Parameters:
559 + snes    - nonlinear solver context
560 - damping - damping parameter
561 
562   Level: advanced
563 
564 .seealso: [](ch_snes), `SNESMSGetDamping()`, `SNESMS`
565 @*/
566 PetscErrorCode SNESMSSetDamping(SNES snes, PetscReal damping)
567 {
568   PetscFunctionBegin;
569   PetscValidHeaderSpecific(snes, SNES_CLASSID, 1);
570   PetscValidLogicalCollectiveReal(snes, damping, 2);
571   PetscTryMethod(snes, "SNESMSSetDamping_C", (SNES, PetscReal), (snes, damping));
572   PetscFunctionReturn(PETSC_SUCCESS);
573 }
574 
575 /*MC
576       SNESMS - multi-stage smoothers
577 
578       Options Database Keys:
579 +     -snes_ms_type    - type of multi-stage smoother
580 -     -snes_ms_damping - damping for multi-stage method
581 
582       Level: advanced
583 
584       Notes:
585       These multistage methods are explicit Runge-Kutta methods that are often used as smoothers for
586       FAS multigrid for transport problems. In the linear case, these are equivalent to polynomial smoothers (such as Chebyshev).
587 
588       Multi-stage smoothers should usually be preconditioned by point-block Jacobi to ensure proper scaling and to normalize the wave speeds.
589 
590       The methods are specified in low storage form (Ketcheson 2010). New methods can be registered with `SNESMSRegister()`.
591 
592       See {cite}`ketcheson2010runge`, {cite}`jameson1983`, {cite}`pierce1997preconditioned`, and {cite}`va1981design`
593 
594 .seealso: [](ch_snes), `SNESCreate()`, `SNES`, `SNESSetType()`, `SNESMS`, `SNESFAS`, `KSPCHEBYSHEV`, `SNESMSSetDamping()`, `SNESMSGetDamping()`,
595           `SNESMSSetType()`, `SNESMSGetType()`
596 M*/
597 
598 PETSC_EXTERN PetscErrorCode SNESCreate_MS(SNES snes)
599 {
600   SNES_MS *ms;
601 
602   PetscFunctionBegin;
603   PetscCall(SNESMSInitializePackage());
604 
605   snes->ops->setup          = SNESSetUp_MS;
606   snes->ops->solve          = SNESSolve_MS;
607   snes->ops->destroy        = SNESDestroy_MS;
608   snes->ops->setfromoptions = SNESSetFromOptions_MS;
609   snes->ops->view           = SNESView_MS;
610 
611   snes->usesnpc = PETSC_FALSE;
612   snes->usesksp = PETSC_TRUE;
613 
614   snes->alwayscomputesfinalresidual = PETSC_FALSE;
615 
616   PetscCall(SNESParametersInitialize(snes));
617 
618   PetscCall(PetscNew(&ms));
619   snes->data  = (void *)ms;
620   ms->damping = 0.9;
621 
622   PetscCall(PetscObjectComposeFunction((PetscObject)snes, "SNESMSGetType_C", SNESMSGetType_MS));
623   PetscCall(PetscObjectComposeFunction((PetscObject)snes, "SNESMSSetType_C", SNESMSSetType_MS));
624   PetscCall(PetscObjectComposeFunction((PetscObject)snes, "SNESMSGetDamping_C", SNESMSGetDamping_MS));
625   PetscCall(PetscObjectComposeFunction((PetscObject)snes, "SNESMSSetDamping_C", SNESMSSetDamping_MS));
626 
627   PetscCall(SNESMSSetType(snes, SNESMSDefault));
628   PetscFunctionReturn(PETSC_SUCCESS);
629 }
630