xref: /petsc/src/snes/impls/ms/ms.c (revision 1b37a2a7cc4a4fb30c3e967db1c694c0a1013f51)
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
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 SNESReset_MS(SNES snes)
378 {
379   PetscFunctionBegin;
380   PetscFunctionReturn(PETSC_SUCCESS);
381 }
382 
383 static PetscErrorCode SNESDestroy_MS(SNES snes)
384 {
385   PetscFunctionBegin;
386   PetscCall(SNESReset_MS(snes));
387   PetscCall(PetscFree(snes->data));
388   PetscCall(PetscObjectComposeFunction((PetscObject)snes, "SNESMSGetType_C", NULL));
389   PetscCall(PetscObjectComposeFunction((PetscObject)snes, "SNESMSSetType_C", NULL));
390   PetscCall(PetscObjectComposeFunction((PetscObject)snes, "SNESMSGetDamping_C", NULL));
391   PetscCall(PetscObjectComposeFunction((PetscObject)snes, "SNESMSSetDamping_C", NULL));
392   PetscFunctionReturn(PETSC_SUCCESS);
393 }
394 
395 static PetscErrorCode SNESView_MS(SNES snes, PetscViewer viewer)
396 {
397   PetscBool     iascii;
398   SNES_MS      *ms  = (SNES_MS *)snes->data;
399   SNESMSTableau tab = ms->tableau;
400 
401   PetscFunctionBegin;
402   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERASCII, &iascii));
403   if (iascii) PetscCall(PetscViewerASCIIPrintf(viewer, "  multi-stage method type: %s\n", tab->name));
404   PetscFunctionReturn(PETSC_SUCCESS);
405 }
406 
407 static PetscErrorCode SNESSetFromOptions_MS(SNES snes, PetscOptionItems *PetscOptionsObject)
408 {
409   PetscFunctionBegin;
410   PetscOptionsHeadBegin(PetscOptionsObject, "SNES MS options");
411   {
412     SNESMSTableauLink link;
413     PetscInt          count, choice;
414     PetscBool         flg;
415     const char      **namelist;
416     SNESMSType        mstype;
417     PetscReal         damping;
418     PetscBool         norms = PETSC_FALSE;
419 
420     PetscCall(SNESMSGetType(snes, &mstype));
421     for (link = SNESMSTableauList, count = 0; link; link = link->next, count++)
422       ;
423     PetscCall(PetscMalloc1(count, (char ***)&namelist));
424     for (link = SNESMSTableauList, count = 0; link; link = link->next, count++) namelist[count] = link->tab.name;
425     PetscCall(PetscOptionsEList("-snes_ms_type", "Multistage smoother type", "SNESMSSetType", (const char *const *)namelist, count, mstype, &choice, &flg));
426     if (flg) PetscCall(SNESMSSetType(snes, namelist[choice]));
427     PetscCall(PetscFree(namelist));
428     PetscCall(SNESMSGetDamping(snes, &damping));
429     PetscCall(PetscOptionsReal("-snes_ms_damping", "Damping for multistage method", "SNESMSSetDamping", damping, &damping, &flg));
430     if (flg) PetscCall(SNESMSSetDamping(snes, damping));
431 
432     /* deprecated option */
433     PetscCall(PetscOptionsDeprecated("-snes_ms_norms", NULL, "3.20", "Use -snes_norm_schedule always"));
434     PetscCall(PetscOptionsBool("-snes_ms_norms", NULL, NULL, norms, &norms, NULL));
435     if (norms) PetscCall(SNESSetNormSchedule(snes, SNES_NORM_ALWAYS));
436   }
437   PetscOptionsHeadEnd();
438   PetscFunctionReturn(PETSC_SUCCESS);
439 }
440 
441 static PetscErrorCode SNESMSGetType_MS(SNES snes, SNESMSType *mstype)
442 {
443   SNES_MS      *ms  = (SNES_MS *)snes->data;
444   SNESMSTableau tab = ms->tableau;
445 
446   PetscFunctionBegin;
447   *mstype = tab->name;
448   PetscFunctionReturn(PETSC_SUCCESS);
449 }
450 
451 static PetscErrorCode SNESMSSetType_MS(SNES snes, SNESMSType mstype)
452 {
453   SNES_MS          *ms = (SNES_MS *)snes->data;
454   SNESMSTableauLink link;
455   PetscBool         match;
456 
457   PetscFunctionBegin;
458   if (ms->tableau) {
459     PetscCall(PetscStrcmp(ms->tableau->name, mstype, &match));
460     if (match) PetscFunctionReturn(PETSC_SUCCESS);
461   }
462   for (link = SNESMSTableauList; link; link = link->next) {
463     PetscCall(PetscStrcmp(link->tab.name, mstype, &match));
464     if (match) {
465       if (snes->setupcalled) PetscCall(SNESReset_MS(snes));
466       ms->tableau = &link->tab;
467       if (snes->setupcalled) PetscCall(SNESSetUp_MS(snes));
468       PetscFunctionReturn(PETSC_SUCCESS);
469     }
470   }
471   SETERRQ(PetscObjectComm((PetscObject)snes), PETSC_ERR_ARG_UNKNOWN_TYPE, "Could not find '%s'", mstype);
472 }
473 
474 /*@C
475   SNESMSGetType - Get the type of multistage smoother `SNESMS`
476 
477   Not Collective
478 
479   Input Parameter:
480 . snes - nonlinear solver context
481 
482   Output Parameter:
483 . mstype - type of multistage method
484 
485   Level: advanced
486 
487 .seealso: [](ch_snes), `SNESMS`, `SNESMSSetType()`, `SNESMSType`
488 @*/
489 PetscErrorCode SNESMSGetType(SNES snes, SNESMSType *mstype)
490 {
491   PetscFunctionBegin;
492   PetscValidHeaderSpecific(snes, SNES_CLASSID, 1);
493   PetscAssertPointer(mstype, 2);
494   PetscUseMethod(snes, "SNESMSGetType_C", (SNES, SNESMSType *), (snes, mstype));
495   PetscFunctionReturn(PETSC_SUCCESS);
496 }
497 
498 /*@C
499   SNESMSSetType - Set the type of multistage smoother `SNESMS`
500 
501   Logically Collective
502 
503   Input Parameters:
504 + snes   - nonlinear solver context
505 - mstype - type of multistage method
506 
507   Level: advanced
508 
509 .seealso: [](ch_snes), `SNESMS`, `SNESMSGetType()`, `SNESMSType`
510 @*/
511 PetscErrorCode SNESMSSetType(SNES snes, SNESMSType mstype)
512 {
513   PetscFunctionBegin;
514   PetscValidHeaderSpecific(snes, SNES_CLASSID, 1);
515   PetscAssertPointer(mstype, 2);
516   PetscTryMethod(snes, "SNESMSSetType_C", (SNES, SNESMSType), (snes, mstype));
517   PetscFunctionReturn(PETSC_SUCCESS);
518 }
519 
520 static PetscErrorCode SNESMSGetDamping_MS(SNES snes, PetscReal *damping)
521 {
522   SNES_MS *ms = (SNES_MS *)snes->data;
523 
524   PetscFunctionBegin;
525   *damping = ms->damping;
526   PetscFunctionReturn(PETSC_SUCCESS);
527 }
528 
529 static PetscErrorCode SNESMSSetDamping_MS(SNES snes, PetscReal damping)
530 {
531   SNES_MS *ms = (SNES_MS *)snes->data;
532 
533   PetscFunctionBegin;
534   ms->damping = damping;
535   PetscFunctionReturn(PETSC_SUCCESS);
536 }
537 
538 /*@C
539   SNESMSGetDamping - Get the damping parameter of `SNESMS` multistage scheme
540 
541   Not Collective
542 
543   Input Parameter:
544 . snes - nonlinear solver context
545 
546   Output Parameter:
547 . damping - damping parameter
548 
549   Level: advanced
550 
551 .seealso: [](ch_snes), `SNESMSSetDamping()`, `SNESMS`
552 @*/
553 PetscErrorCode SNESMSGetDamping(SNES snes, PetscReal *damping)
554 {
555   PetscFunctionBegin;
556   PetscValidHeaderSpecific(snes, SNES_CLASSID, 1);
557   PetscAssertPointer(damping, 2);
558   PetscUseMethod(snes, "SNESMSGetDamping_C", (SNES, PetscReal *), (snes, damping));
559   PetscFunctionReturn(PETSC_SUCCESS);
560 }
561 
562 /*@C
563   SNESMSSetDamping - Set the damping parameter for a `SNESMS` multistage scheme
564 
565   Logically Collective
566 
567   Input Parameters:
568 + snes    - nonlinear solver context
569 - damping - damping parameter
570 
571   Level: advanced
572 
573 .seealso: [](ch_snes), `SNESMSGetDamping()`, `SNESMS`
574 @*/
575 PetscErrorCode SNESMSSetDamping(SNES snes, PetscReal damping)
576 {
577   PetscFunctionBegin;
578   PetscValidHeaderSpecific(snes, SNES_CLASSID, 1);
579   PetscValidLogicalCollectiveReal(snes, damping, 2);
580   PetscTryMethod(snes, "SNESMSSetDamping_C", (SNES, PetscReal), (snes, damping));
581   PetscFunctionReturn(PETSC_SUCCESS);
582 }
583 
584 /*MC
585       SNESMS - multi-stage smoothers
586 
587       Options Database Keys:
588 +     -snes_ms_type    - type of multi-stage smoother
589 -     -snes_ms_damping - damping for multi-stage method
590 
591       Level: advanced
592 
593       Notes:
594       These multistage methods are explicit Runge-Kutta methods that are often used as smoothers for
595       FAS multigrid for transport problems. In the linear case, these are equivalent to polynomial smoothers (such as Chebyshev).
596 
597       Multi-stage smoothers should usually be preconditioned by point-block Jacobi to ensure proper scaling and to normalize the wave speeds.
598 
599       The methods are specified in low storage form (Ketcheson 2010). New methods can be registered with `SNESMSRegister()`.
600 
601       See {cite}`ketcheson2010runge`, {cite}`jameson1983`, {cite}`pierce1997preconditioned`, and {cite}`va1981design`
602 
603 .seealso: [](ch_snes), `SNESCreate()`, `SNES`, `SNESSetType()`, `SNESMS`, `SNESFAS`, `KSPCHEBYSHEV`, `SNESMSSetDamping()`, `SNESMSGetDamping()`,
604           `SNESMSSetType()`, `SNESMSGetType()`
605 M*/
606 
607 PETSC_EXTERN PetscErrorCode SNESCreate_MS(SNES snes)
608 {
609   SNES_MS *ms;
610 
611   PetscFunctionBegin;
612   PetscCall(SNESMSInitializePackage());
613 
614   snes->ops->setup          = SNESSetUp_MS;
615   snes->ops->solve          = SNESSolve_MS;
616   snes->ops->destroy        = SNESDestroy_MS;
617   snes->ops->setfromoptions = SNESSetFromOptions_MS;
618   snes->ops->view           = SNESView_MS;
619   snes->ops->reset          = SNESReset_MS;
620 
621   snes->usesnpc = PETSC_FALSE;
622   snes->usesksp = PETSC_TRUE;
623 
624   snes->alwayscomputesfinalresidual = PETSC_FALSE;
625 
626   PetscCall(PetscNew(&ms));
627   snes->data  = (void *)ms;
628   ms->damping = 0.9;
629 
630   PetscCall(PetscObjectComposeFunction((PetscObject)snes, "SNESMSGetType_C", SNESMSGetType_MS));
631   PetscCall(PetscObjectComposeFunction((PetscObject)snes, "SNESMSSetType_C", SNESMSSetType_MS));
632   PetscCall(PetscObjectComposeFunction((PetscObject)snes, "SNESMSGetDamping_C", SNESMSGetDamping_MS));
633   PetscCall(PetscObjectComposeFunction((PetscObject)snes, "SNESMSSetDamping_C", SNESMSSetDamping_MS));
634 
635   PetscCall(SNESMSSetType(snes, SNESMSDefault));
636   PetscFunctionReturn(PETSC_SUCCESS);
637 }
638