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