xref: /petsc/src/snes/impls/ms/ms.c (revision b230feb8d71bf783453a19dfba30809b87a0ed73)
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   PetscBool     norms;   /* Compute norms, usually only for monitoring purposes */
29 } SNES_MS;
30 
31 /*@C
32   SNESMSRegisterAll - Registers all of the multi-stage methods in `SNESMS`
33 
34   Logically Collective
35 
36   Level: developer
37 
38 .seealso: `SNES`, `SNESMS`, `SNESMSRegisterDestroy()`
39 @*/
40 PetscErrorCode SNESMSRegisterAll(void)
41 {
42   PetscFunctionBegin;
43   if (SNESMSRegisterAllCalled) PetscFunctionReturn(PETSC_SUCCESS);
44   SNESMSRegisterAllCalled = PETSC_TRUE;
45 
46   {
47     PetscReal alpha[1] = {1.0};
48     PetscCall(SNESMSRegister(SNESMSEULER, 1, 1, 1.0, NULL, NULL, alpha));
49   }
50 
51   {
52     PetscReal gamma[3][6] = {
53       {0.0000000000000000E+00, -7.0304722367110606E-01, -1.9836719667506464E-01, -1.6023843981863788E+00, 9.4483822882855284E-02,  -1.4204296130641869E-01},
54       {1.0000000000000000E+00, 1.1111025767083920E+00,  5.6150921583923230E-01,  7.4151723494934041E-01,  3.1714538168600587E-01,  4.6479276238548706E-01 },
55       {0.0000000000000000E+00, 0.0000000000000000E+00,  0.0000000000000000E+00,  6.7968174970583317E-01,  -4.1755042846051737E-03, -1.9115668129923846E-01}
56     };
57     PetscReal delta[6]   = {1.0000000000000000E+00, 5.3275427433201750E-01, 6.0143544663985238E-01, 4.5874077053842177E-01, 2.7544386906104651E-01, 0.0000000000000000E+00};
58     PetscReal betasub[6] = {8.4753115429481929E-01, 7.4018896368655618E-01, 6.5963574086583309E-03, 4.6747795645517759E-01, 1.3314545813643919E-01, 5.3260800028018784E-01};
59     PetscCall(SNESMSRegister(SNESMSM62, 6, 3, 1.0, &gamma[0][0], delta, betasub));
60   }
61 
62   { /* Jameson (1983) */
63     PetscReal alpha[4] = {0.25, 0.5, 0.55, 1.0};
64     PetscCall(SNESMSRegister(SNESMSJAMESON83, 4, 1, 1.0, NULL, NULL, alpha));
65   }
66 
67   { /* Van Leer, Tai, and Powell (1989) 1 stage, order 1 */
68     PetscReal alpha[1] = {1.0};
69     PetscCall(SNESMSRegister(SNESMSVLTP11, 1, 1, 0.5, NULL, NULL, alpha));
70   }
71   { /* Van Leer, Tai, and Powell (1989) 2 stage, order 1 */
72     PetscReal alpha[2] = {0.3333, 1.0};
73     PetscCall(SNESMSRegister(SNESMSVLTP21, 2, 1, 1.0, NULL, NULL, alpha));
74   }
75   { /* Van Leer, Tai, and Powell (1989) 3 stage, order 1 */
76     PetscReal alpha[3] = {0.1481, 0.4000, 1.0};
77     PetscCall(SNESMSRegister(SNESMSVLTP31, 3, 1, 1.5, NULL, NULL, alpha));
78   }
79   { /* Van Leer, Tai, and Powell (1989) 4 stage, order 1 */
80     PetscReal alpha[4] = {0.0833, 0.2069, 0.4265, 1.0};
81     PetscCall(SNESMSRegister(SNESMSVLTP41, 4, 1, 2.0, NULL, NULL, alpha));
82   }
83   { /* Van Leer, Tai, and Powell (1989) 5 stage, order 1 */
84     PetscReal alpha[5] = {0.0533, 0.1263, 0.2375, 0.4414, 1.0};
85     PetscCall(SNESMSRegister(SNESMSVLTP51, 5, 1, 2.5, NULL, NULL, alpha));
86   }
87   { /* Van Leer, Tai, and Powell (1989) 6 stage, order 1 */
88     PetscReal alpha[6] = {0.0370, 0.0851, 0.1521, 0.2562, 0.4512, 1.0};
89     PetscCall(SNESMSRegister(SNESMSVLTP61, 6, 1, 3.0, NULL, NULL, alpha));
90   }
91   PetscFunctionReturn(PETSC_SUCCESS);
92 }
93 
94 /*@C
95    SNESMSRegisterDestroy - Frees the list of schemes that were registered by `SNESMSRegister()`.
96 
97    Not Collective
98 
99    Level: developer
100 
101 .seealso: `SNESMSRegister()`, `SNESMSRegisterAll()`
102 @*/
103 PetscErrorCode SNESMSRegisterDestroy(void)
104 {
105   SNESMSTableauLink link;
106 
107   PetscFunctionBegin;
108   while ((link = SNESMSTableauList)) {
109     SNESMSTableau t   = &link->tab;
110     SNESMSTableauList = link->next;
111 
112     PetscCall(PetscFree(t->name));
113     PetscCall(PetscFree(t->gamma));
114     PetscCall(PetscFree(t->delta));
115     PetscCall(PetscFree(t->betasub));
116     PetscCall(PetscFree(link));
117   }
118   SNESMSRegisterAllCalled = PETSC_FALSE;
119   PetscFunctionReturn(PETSC_SUCCESS);
120 }
121 
122 /*@C
123   SNESMSInitializePackage - This function initializes everything in the `SNESMS` package. It is called
124   from `SNESInitializePackage()`.
125 
126   Level: developer
127 
128 .seealso: `PetscInitialize()`
129 @*/
130 PetscErrorCode SNESMSInitializePackage(void)
131 {
132   PetscFunctionBegin;
133   if (SNESMSPackageInitialized) PetscFunctionReturn(PETSC_SUCCESS);
134   SNESMSPackageInitialized = PETSC_TRUE;
135 
136   PetscCall(SNESMSRegisterAll());
137   PetscCall(PetscRegisterFinalize(SNESMSFinalizePackage));
138   PetscFunctionReturn(PETSC_SUCCESS);
139 }
140 
141 /*@C
142   SNESMSFinalizePackage - This function destroys everything in the `SNESMS` package. It is
143   called from `PetscFinalize()`.
144 
145   Level: developer
146 
147 .seealso: `PetscFinalize()`
148 @*/
149 PetscErrorCode SNESMSFinalizePackage(void)
150 {
151   PetscFunctionBegin;
152   SNESMSPackageInitialized = PETSC_FALSE;
153 
154   PetscCall(SNESMSRegisterDestroy());
155   PetscFunctionReturn(PETSC_SUCCESS);
156 }
157 
158 /*@C
159    SNESMSRegister - register a multistage scheme for `SNESMS`
160 
161    Logically Collective
162 
163    Input Parameters:
164 +  name - identifier for method
165 .  nstages - number of stages
166 .  nregisters - number of registers used by low-storage implementation
167 .  stability - scaled stability region
168 .  gamma - coefficients, see Ketcheson's paper
169 .  delta - coefficients, see Ketcheson's paper
170 -  betasub - subdiagonal of Shu-Osher form
171 
172    Level: advanced
173 
174    Notes:
175    The notation is described in Ketcheson (2010) Runge-Kutta methods with minimum storage implementations.
176 
177    Many multistage schemes are of the form
178    $ X_0 = X^{(n)}
179    $ X_k = X_0 + \alpha_k * F(X_{k-1}), k = 1,\ldots,s
180    $ X^{(n+1)} = X_s
181    These methods can be registered with
182 .vb
183    SNESMSRegister("name",s,1,stability,NULL,NULL,alpha);
184 .ve
185 
186 .seealso: `SNESMS`
187 @*/
188 PetscErrorCode SNESMSRegister(SNESMSType name, PetscInt nstages, PetscInt nregisters, PetscReal stability, const PetscReal gamma[], const PetscReal delta[], const PetscReal betasub[])
189 {
190   SNESMSTableauLink link;
191   SNESMSTableau     t;
192 
193   PetscFunctionBegin;
194   PetscValidCharPointer(name, 1);
195   PetscCheck(nstages >= 1, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Must have at least one stage");
196   if (gamma || delta) {
197     PetscCheck(nregisters == 3, PETSC_COMM_SELF, PETSC_ERR_SUP, "Only support for methods written in 3-register form");
198     PetscValidRealPointer(gamma, 5);
199     PetscValidRealPointer(delta, 6);
200   } else {
201     PetscCheck(nregisters == 1, PETSC_COMM_SELF, PETSC_ERR_SUP, "Only support for methods written in 1-register form");
202   }
203   PetscValidRealPointer(betasub, 7);
204 
205   PetscCall(SNESMSInitializePackage());
206   PetscCall(PetscNew(&link));
207   t = &link->tab;
208   PetscCall(PetscStrallocpy(name, &t->name));
209   t->nstages    = nstages;
210   t->nregisters = nregisters;
211   t->stability  = stability;
212 
213   if (gamma && delta) {
214     PetscCall(PetscMalloc1(nstages * nregisters, &t->gamma));
215     PetscCall(PetscMalloc1(nstages, &t->delta));
216     PetscCall(PetscArraycpy(t->gamma, gamma, nstages * nregisters));
217     PetscCall(PetscArraycpy(t->delta, delta, nstages));
218   }
219   PetscCall(PetscMalloc1(nstages, &t->betasub));
220   PetscCall(PetscArraycpy(t->betasub, betasub, nstages));
221 
222   link->next        = SNESMSTableauList;
223   SNESMSTableauList = link;
224   PetscFunctionReturn(PETSC_SUCCESS);
225 }
226 
227 /*
228   X - initial state, updated in-place.
229   F - residual, computed at the initial X on input
230 */
231 static PetscErrorCode SNESMSStep_3Sstar(SNES snes, Vec X, Vec F)
232 {
233   SNES_MS         *ms      = (SNES_MS *)snes->data;
234   SNESMSTableau    t       = ms->tableau;
235   const PetscInt   nstages = t->nstages;
236   const PetscReal *gamma = t->gamma, *delta = t->delta, *betasub = t->betasub;
237   Vec              S1 = X, S2 = snes->work[1], S3 = snes->work[2], Y = snes->work[0], S1copy = snes->work[3];
238 
239   PetscFunctionBegin;
240   PetscCall(VecZeroEntries(S2));
241   PetscCall(VecCopy(X, S3));
242   for (PetscInt i = 0; i < nstages; i++) {
243     Vec               Ss[]     = {S1copy, S2, S3, Y};
244     const PetscScalar scoeff[] = {gamma[0 * nstages + i] - 1, gamma[1 * nstages + i], gamma[2 * nstages + i], -betasub[i] * ms->damping};
245 
246     PetscCall(VecAXPY(S2, delta[i], S1));
247     if (i > 0) PetscCall(SNESComputeFunction(snes, S1, F));
248     PetscCall(KSPSolve(snes->ksp, F, Y));
249     PetscCall(VecCopy(S1, S1copy));
250     PetscCall(VecMAXPY(S1, 4, scoeff, Ss));
251   }
252   PetscFunctionReturn(PETSC_SUCCESS);
253 }
254 
255 /*
256   X - initial state, updated in-place.
257   F - residual, computed at the initial X on input
258 */
259 static PetscErrorCode SNESMSStep_Basic(SNES snes, Vec X, Vec F)
260 {
261   SNES_MS         *ms    = (SNES_MS *)snes->data;
262   SNESMSTableau    tab   = ms->tableau;
263   const PetscReal *alpha = tab->betasub, h = ms->damping;
264   PetscInt         i, nstages              = tab->nstages;
265   Vec              X0 = snes->work[0];
266 
267   PetscFunctionBegin;
268   PetscCall(VecCopy(X, X0));
269   for (i = 0; i < nstages; i++) {
270     if (i > 0) PetscCall(SNESComputeFunction(snes, X, F));
271     PetscCall(KSPSolve(snes->ksp, F, X));
272     PetscCall(VecAYPX(X, -alpha[i] * h, X0));
273   }
274   PetscFunctionReturn(PETSC_SUCCESS);
275 }
276 
277 static PetscErrorCode SNESMSStep_Step(SNES snes, Vec X, Vec F)
278 {
279   SNES_MS      *ms  = (SNES_MS *)snes->data;
280   SNESMSTableau tab = ms->tableau;
281 
282   PetscFunctionBegin;
283   if (tab->gamma && tab->delta) {
284     PetscCall(SNESMSStep_3Sstar(snes, X, F));
285   } else {
286     PetscCall(SNESMSStep_Basic(snes, X, F));
287   }
288   PetscFunctionReturn(PETSC_SUCCESS);
289 }
290 
291 static PetscErrorCode SNESMSStep_Norms(SNES snes, PetscInt iter, Vec F)
292 {
293   SNES_MS  *ms = (SNES_MS *)snes->data;
294   PetscReal fnorm;
295 
296   PetscFunctionBegin;
297   if (ms->norms || (iter == snes->max_its && snes->normschedule >= SNES_NORM_FINAL_ONLY)) {
298     PetscCall(VecNorm(F, NORM_2, &fnorm)); /* fnorm <- ||F||  */
299     SNESCheckFunctionNorm(snes, fnorm);
300     /* Monitor convergence */
301     PetscCall(PetscObjectSAWsTakeAccess((PetscObject)snes));
302     snes->iter = iter;
303     snes->norm = fnorm;
304     PetscCall(PetscObjectSAWsGrantAccess((PetscObject)snes));
305     PetscCall(SNESLogConvergenceHistory(snes, snes->norm, 0));
306     /* Test for convergence */
307     PetscCall(SNESConverged(snes, snes->iter, 0.0, 0.0, fnorm));
308     PetscCall(SNESMonitor(snes, snes->iter, snes->norm));
309   } else if (iter > 0) {
310     PetscCall(PetscObjectSAWsTakeAccess((PetscObject)snes));
311     snes->iter = iter;
312     PetscCall(PetscObjectSAWsGrantAccess((PetscObject)snes));
313   }
314   PetscFunctionReturn(PETSC_SUCCESS);
315 }
316 
317 static PetscErrorCode SNESSolve_MS(SNES snes)
318 {
319   SNES_MS *ms = (SNES_MS *)snes->data;
320   Vec      X = snes->vec_sol, F = snes->vec_func;
321   PetscInt i;
322 
323   PetscFunctionBegin;
324   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);
325   PetscCall(PetscCitationsRegister(SNESCitation, &SNEScite));
326 
327   snes->reason = SNES_CONVERGED_ITERATING;
328   PetscCall(PetscObjectSAWsTakeAccess((PetscObject)snes));
329   snes->iter = 0;
330   snes->norm = 0;
331   PetscCall(PetscObjectSAWsGrantAccess((PetscObject)snes));
332 
333   if (!snes->vec_func_init_set) {
334     PetscCall(SNESComputeFunction(snes, X, F));
335   } else snes->vec_func_init_set = PETSC_FALSE;
336 
337   PetscCall(SNESMSStep_Norms(snes, 0, F));
338   if (snes->reason) PetscFunctionReturn(PETSC_SUCCESS);
339 
340   for (i = 0; i < snes->max_its; i++) {
341     /* Call general purpose update function */
342     PetscTryTypeMethod(snes, update, snes->iter);
343 
344     if (i == 0 && snes->jacobian) {
345       /* This method does not require a Jacobian, but it is usually preconditioned by PBJacobi */
346       PetscCall(SNESComputeJacobian(snes, snes->vec_sol, snes->jacobian, snes->jacobian_pre));
347       SNESCheckJacobianDomainerror(snes);
348       PetscCall(KSPSetOperators(snes->ksp, snes->jacobian, snes->jacobian_pre));
349     }
350 
351     PetscCall(SNESMSStep_Step(snes, X, F));
352 
353     if (i < snes->max_its - 1 || ms->norms) PetscCall(SNESComputeFunction(snes, X, F));
354 
355     PetscCall(SNESMSStep_Norms(snes, i + 1, F));
356     if (snes->reason) PetscFunctionReturn(PETSC_SUCCESS);
357   }
358   PetscFunctionReturn(PETSC_SUCCESS);
359 }
360 
361 static PetscErrorCode SNESSetUp_MS(SNES snes)
362 {
363   SNES_MS      *ms    = (SNES_MS *)snes->data;
364   SNESMSTableau tab   = ms->tableau;
365   PetscInt      nwork = tab->nregisters + 1; // +1 because VecMAXPY() in SNESMSStep_3Sstar()
366                                              // needs an extra work vec
367 
368   PetscFunctionBegin;
369   PetscCall(SNESSetWorkVecs(snes, nwork));
370   PetscCall(SNESSetUpMatrices(snes));
371   PetscFunctionReturn(PETSC_SUCCESS);
372 }
373 
374 static PetscErrorCode SNESReset_MS(SNES snes)
375 {
376   PetscFunctionBegin;
377   PetscFunctionReturn(PETSC_SUCCESS);
378 }
379 
380 static PetscErrorCode SNESDestroy_MS(SNES snes)
381 {
382   PetscFunctionBegin;
383   PetscCall(SNESReset_MS(snes));
384   PetscCall(PetscFree(snes->data));
385   PetscCall(PetscObjectComposeFunction((PetscObject)snes, "SNESMSGetType_C", NULL));
386   PetscCall(PetscObjectComposeFunction((PetscObject)snes, "SNESMSSetType_C", NULL));
387   PetscCall(PetscObjectComposeFunction((PetscObject)snes, "SNESMSGetDamping_C", NULL));
388   PetscCall(PetscObjectComposeFunction((PetscObject)snes, "SNESMSSetDamping_C", NULL));
389   PetscFunctionReturn(PETSC_SUCCESS);
390 }
391 
392 static PetscErrorCode SNESView_MS(SNES snes, PetscViewer viewer)
393 {
394   PetscBool     iascii;
395   SNES_MS      *ms  = (SNES_MS *)snes->data;
396   SNESMSTableau tab = ms->tableau;
397 
398   PetscFunctionBegin;
399   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERASCII, &iascii));
400   if (iascii) PetscCall(PetscViewerASCIIPrintf(viewer, "  multi-stage method type: %s\n", tab->name));
401   PetscFunctionReturn(PETSC_SUCCESS);
402 }
403 
404 static PetscErrorCode SNESSetFromOptions_MS(SNES snes, PetscOptionItems *PetscOptionsObject)
405 {
406   SNES_MS *ms = (SNES_MS *)snes->data;
407 
408   PetscFunctionBegin;
409   PetscOptionsHeadBegin(PetscOptionsObject, "SNES MS options");
410   {
411     SNESMSTableauLink link;
412     PetscInt          count, choice;
413     PetscBool         flg;
414     const char      **namelist;
415     SNESMSType        mstype;
416     PetscReal         damping;
417 
418     PetscCall(SNESMSGetType(snes, &mstype));
419     for (link = SNESMSTableauList, count = 0; link; link = link->next, count++)
420       ;
421     PetscCall(PetscMalloc1(count, (char ***)&namelist));
422     for (link = SNESMSTableauList, count = 0; link; link = link->next, count++) namelist[count] = link->tab.name;
423     PetscCall(PetscOptionsEList("-snes_ms_type", "Multistage smoother type", "SNESMSSetType", (const char *const *)namelist, count, mstype, &choice, &flg));
424     if (flg) PetscCall(SNESMSSetType(snes, namelist[choice]));
425     PetscCall(PetscFree(namelist));
426     PetscCall(SNESMSGetDamping(snes, &damping));
427     PetscCall(PetscOptionsReal("-snes_ms_damping", "Damping for multistage method", "SNESMSSetDamping", damping, &damping, &flg));
428     if (flg) PetscCall(SNESMSSetDamping(snes, damping));
429     PetscCall(PetscOptionsBool("-snes_ms_norms", "Compute norms for monitoring", "none", ms->norms, &ms->norms, NULL));
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`, `SNESMS`
482 @*/
483 PetscErrorCode SNESMSGetType(SNES snes, SNESMSType *mstype)
484 {
485   PetscFunctionBegin;
486   PetscValidHeaderSpecific(snes, SNES_CLASSID, 1);
487   PetscValidPointer(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`, `SNESMS`
504 @*/
505 PetscErrorCode SNESMSSetType(SNES snes, SNESMSType mstype)
506 {
507   PetscFunctionBegin;
508   PetscValidHeaderSpecific(snes, SNES_CLASSID, 1);
509   PetscValidCharPointer(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   PetscValidRealPointer(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   ms->norms   = PETSC_FALSE;
627 
628   PetscCall(PetscObjectComposeFunction((PetscObject)snes, "SNESMSGetType_C", SNESMSGetType_MS));
629   PetscCall(PetscObjectComposeFunction((PetscObject)snes, "SNESMSSetType_C", SNESMSSetType_MS));
630   PetscCall(PetscObjectComposeFunction((PetscObject)snes, "SNESMSGetDamping_C", SNESMSGetDamping_MS));
631   PetscCall(PetscObjectComposeFunction((PetscObject)snes, "SNESMSSetDamping_C", SNESMSSetDamping_MS));
632 
633   PetscCall(SNESMSSetType(snes, SNESMSDefault));
634   PetscFunctionReturn(PETSC_SUCCESS);
635 }
636