xref: /petsc/src/ts/impls/symplectic/basicsymplectic/basicsymplectic.c (revision 8fb5bd83c3955fefcf33a54e3bb66920a9fa884b)
1 /*
2   Code for Timestepping with basic symplectic integrators for separable Hamiltonian systems
3 */
4 #include <petsc/private/tsimpl.h>                /*I   "petscts.h"   I*/
5 #include <petscdm.h>
6 
7 static TSBasicSymplecticType TSBasicSymplecticDefault = TSBASICSYMPLECTICSIEULER;
8 static PetscBool TSBasicSymplecticRegisterAllCalled;
9 static PetscBool TSBasicSymplecticPackageInitialized;
10 
11 typedef struct _BasicSymplecticScheme *BasicSymplecticScheme;
12 typedef struct _BasicSymplecticSchemeLink *BasicSymplecticSchemeLink;
13 
14 struct _BasicSymplecticScheme {
15   char      *name;
16   PetscInt  order;
17   PetscInt  s;       /* number of stages */
18   PetscReal *c,*d;
19 };
20 struct _BasicSymplecticSchemeLink {
21   struct _BasicSymplecticScheme sch;
22   BasicSymplecticSchemeLink     next;
23 };
24 static BasicSymplecticSchemeLink BasicSymplecticSchemeList;
25 typedef struct {
26   TS                    subts_p,subts_q; /* sub TS contexts that holds the RHSFunction pointers */
27   IS                    is_p,is_q; /* IS sets for position and momentum respectively */
28   Vec                   update;    /* a nest work vector for generalized coordinates */
29   BasicSymplecticScheme scheme;
30 } TS_BasicSymplectic;
31 
32 /*MC
33   TSBASICSYMPLECTICSIEULER - first order semi-implicit Euler method
34   Level: intermediate
35 .seealso: `TSBASICSYMPLECTIC`
36 M*/
37 
38 /*MC
39   TSBASICSYMPLECTICVELVERLET - second order Velocity Verlet method (leapfrog method with starting process and determing velocity and position at the same time)
40 Level: intermediate
41 .seealso: `TSBASICSYMPLECTIC`
42 M*/
43 
44 /*@C
45   TSBasicSymplecticRegisterAll - Registers all of the basic symplectic integration methods in TSBasicSymplectic
46 
47   Not Collective, but should be called by all processes which will need the schemes to be registered
48 
49   Level: advanced
50 
51 .seealso: `TSBasicSymplecticRegisterDestroy()`
52 @*/
53 PetscErrorCode TSBasicSymplecticRegisterAll(void)
54 {
55   PetscFunctionBegin;
56   if (TSBasicSymplecticRegisterAllCalled) PetscFunctionReturn(0);
57   TSBasicSymplecticRegisterAllCalled = PETSC_TRUE;
58   {
59     PetscReal c[1] = {1.0},d[1] = {1.0};
60     PetscCall(TSBasicSymplecticRegister(TSBASICSYMPLECTICSIEULER,1,1,c,d));
61   }
62   {
63     PetscReal c[2] = {0,1.0},d[2] = {0.5,0.5};
64     PetscCall(TSBasicSymplecticRegister(TSBASICSYMPLECTICVELVERLET,2,2,c,d));
65   }
66   {
67     PetscReal c[3] = {1,-2.0/3.0,2.0/3.0},d[3] = {-1.0/24.0,3.0/4.0,7.0/24.0};
68     PetscCall(TSBasicSymplecticRegister(TSBASICSYMPLECTIC3,3,3,c,d));
69   }
70   {
71 #define CUBEROOTOFTWO 1.2599210498948731647672106
72     PetscReal c[4] = {1.0/2.0/(2.0-CUBEROOTOFTWO),(1.0-CUBEROOTOFTWO)/2.0/(2.0-CUBEROOTOFTWO),(1.0-CUBEROOTOFTWO)/2.0/(2.0-CUBEROOTOFTWO),1.0/2.0/(2.0-CUBEROOTOFTWO)},d[4] = {1.0/(2.0-CUBEROOTOFTWO),-CUBEROOTOFTWO/(2.0-CUBEROOTOFTWO),1.0/(2.0-CUBEROOTOFTWO),0};
73     PetscCall(TSBasicSymplecticRegister(TSBASICSYMPLECTIC4,4,4,c,d));
74   }
75   PetscFunctionReturn(0);
76 }
77 
78 /*@C
79    TSBasicSymplecticRegisterDestroy - Frees the list of schemes that were registered by TSBasicSymplecticRegister().
80 
81    Not Collective
82 
83    Level: advanced
84 
85 .seealso: `TSBasicSymplecticRegister()`, `TSBasicSymplecticRegisterAll()`
86 @*/
87 PetscErrorCode TSBasicSymplecticRegisterDestroy(void)
88 {
89   BasicSymplecticSchemeLink link;
90 
91   PetscFunctionBegin;
92   while ((link = BasicSymplecticSchemeList)) {
93     BasicSymplecticScheme scheme = &link->sch;
94     BasicSymplecticSchemeList = link->next;
95     PetscCall(PetscFree2(scheme->c,scheme->d));
96     PetscCall(PetscFree(scheme->name));
97     PetscCall(PetscFree(link));
98   }
99   TSBasicSymplecticRegisterAllCalled = PETSC_FALSE;
100   PetscFunctionReturn(0);
101 }
102 
103 /*@C
104   TSBasicSymplecticInitializePackage - This function initializes everything in the TSBasicSymplectic package. It is called
105   from TSInitializePackage().
106 
107   Level: developer
108 
109 .seealso: `PetscInitialize()`
110 @*/
111 PetscErrorCode TSBasicSymplecticInitializePackage(void)
112 {
113   PetscFunctionBegin;
114   if (TSBasicSymplecticPackageInitialized) PetscFunctionReturn(0);
115   TSBasicSymplecticPackageInitialized = PETSC_TRUE;
116   PetscCall(TSBasicSymplecticRegisterAll());
117   PetscCall(PetscRegisterFinalize(TSBasicSymplecticFinalizePackage));
118   PetscFunctionReturn(0);
119 }
120 
121 /*@C
122   TSBasicSymplecticFinalizePackage - This function destroys everything in the TSBasicSymplectic package. It is
123   called from PetscFinalize().
124 
125   Level: developer
126 
127 .seealso: `PetscFinalize()`
128 @*/
129 PetscErrorCode TSBasicSymplecticFinalizePackage(void)
130 {
131   PetscFunctionBegin;
132   TSBasicSymplecticPackageInitialized = PETSC_FALSE;
133   PetscCall(TSBasicSymplecticRegisterDestroy());
134   PetscFunctionReturn(0);
135 }
136 
137 /*@C
138    TSBasicSymplecticRegister - register a basic symplectic integration scheme by providing the coefficients.
139 
140    Not Collective, but the same schemes should be registered on all processes on which they will be used
141 
142    Input Parameters:
143 +  name - identifier for method
144 .  order - approximation order of method
145 .  s - number of stages, this is the dimension of the matrices below
146 .  c - coefficients for updating generalized position (dimension s)
147 -  d - coefficients for updating generalized momentum (dimension s)
148 
149    Notes:
150    Several symplectic methods are provided, this function is only needed to create new methods.
151 
152    Level: advanced
153 
154 .seealso: `TSBasicSymplectic`
155 @*/
156 PetscErrorCode TSBasicSymplecticRegister(TSRosWType name,PetscInt order,PetscInt s,PetscReal c[],PetscReal d[])
157 {
158   BasicSymplecticSchemeLink link;
159   BasicSymplecticScheme     scheme;
160 
161   PetscFunctionBegin;
162   PetscValidCharPointer(name,1);
163   PetscValidRealPointer(c,4);
164   PetscValidRealPointer(d,5);
165 
166   PetscCall(TSBasicSymplecticInitializePackage());
167   PetscCall(PetscNew(&link));
168   scheme = &link->sch;
169   PetscCall(PetscStrallocpy(name,&scheme->name));
170   scheme->order = order;
171   scheme->s = s;
172   PetscCall(PetscMalloc2(s,&scheme->c,s,&scheme->d));
173   PetscCall(PetscArraycpy(scheme->c,c,s));
174   PetscCall(PetscArraycpy(scheme->d,d,s));
175   link->next = BasicSymplecticSchemeList;
176   BasicSymplecticSchemeList = link;
177   PetscFunctionReturn(0);
178 }
179 
180 /*
181 The simplified form of the equations are:
182 
183 $ p_{i+1} = p_i + c_i*g(q_i)*h
184 $ q_{i+1} = q_i + d_i*f(p_{i+1},t_{i+1})*h
185 
186 Several symplectic integrators are given below. An illustrative way to use them is to consider a particle with position q and velocity p.
187 
188 To apply a timestep with values c_{1,2},d_{1,2} to the particle, carry out the following steps:
189 
190 - Update the velocity of the particle by adding to it its acceleration multiplied by c_1
191 - Update the position of the particle by adding to it its (updated) velocity multiplied by d_1
192 - Update the velocity of the particle by adding to it its acceleration (at the updated position) multiplied by c_2
193 - Update the position of the particle by adding to it its (double-updated) velocity multiplied by d_2
194 
195 */
196 static PetscErrorCode TSStep_BasicSymplectic(TS ts)
197 {
198   TS_BasicSymplectic    *bsymp = (TS_BasicSymplectic*)ts->data;
199   BasicSymplecticScheme scheme = bsymp->scheme;
200   Vec                   solution = ts->vec_sol,update = bsymp->update,q,p,q_update,p_update;
201   IS                    is_q = bsymp->is_q,is_p = bsymp->is_p;
202   TS                    subts_q = bsymp->subts_q,subts_p = bsymp->subts_p;
203   PetscBool             stageok;
204   PetscReal             next_time_step = ts->time_step;
205   PetscInt              iter;
206 
207   PetscFunctionBegin;
208   PetscCall(VecGetSubVector(solution,is_q,&q));
209   PetscCall(VecGetSubVector(solution,is_p,&p));
210   PetscCall(VecGetSubVector(update,is_q,&q_update));
211   PetscCall(VecGetSubVector(update,is_p,&p_update));
212 
213   for (iter = 0;iter<scheme->s;iter++) {
214     PetscCall(TSPreStage(ts,ts->ptime));
215     /* update velocity p */
216     if (scheme->c[iter]) {
217       PetscCall(TSComputeRHSFunction(subts_p,ts->ptime,q,p_update));
218       PetscCall(VecAXPY(p,scheme->c[iter]*ts->time_step,p_update));
219     }
220     /* update position q */
221     if (scheme->d[iter]) {
222       PetscCall(TSComputeRHSFunction(subts_q,ts->ptime,p,q_update));
223       PetscCall(VecAXPY(q,scheme->d[iter]*ts->time_step,q_update));
224       ts->ptime = ts->ptime+scheme->d[iter]*ts->time_step;
225     }
226     PetscCall(TSPostStage(ts,ts->ptime,0,&solution));
227     PetscCall(TSAdaptCheckStage(ts->adapt,ts,ts->ptime,solution,&stageok));
228     if (!stageok) {ts->reason = TS_DIVERGED_STEP_REJECTED; PetscFunctionReturn(0);}
229     PetscCall(TSFunctionDomainError(ts,ts->ptime+ts->time_step,update,&stageok));
230     if (!stageok) {ts->reason = TS_DIVERGED_STEP_REJECTED; PetscFunctionReturn(0);}
231   }
232 
233   ts->time_step = next_time_step;
234   PetscCall(VecRestoreSubVector(solution,is_q,&q));
235   PetscCall(VecRestoreSubVector(solution,is_p,&p));
236   PetscCall(VecRestoreSubVector(update,is_q,&q_update));
237   PetscCall(VecRestoreSubVector(update,is_p,&p_update));
238   PetscFunctionReturn(0);
239 }
240 
241 static PetscErrorCode DMCoarsenHook_BasicSymplectic(DM fine,DM coarse,void *ctx)
242 {
243   PetscFunctionBegin;
244   PetscFunctionReturn(0);
245 }
246 
247 static PetscErrorCode DMRestrictHook_BasicSymplectic(DM fine,Mat restrct,Vec rscale,Mat inject,DM coarse,void *ctx)
248 {
249   PetscFunctionBegin;
250   PetscFunctionReturn(0);
251 }
252 
253 static PetscErrorCode DMSubDomainHook_BasicSymplectic(DM dm,DM subdm,void *ctx)
254 {
255   PetscFunctionBegin;
256   PetscFunctionReturn(0);
257 }
258 
259 static PetscErrorCode DMSubDomainRestrictHook_BasicSymplectic(DM dm,VecScatter gscat,VecScatter lscat,DM subdm,void *ctx)
260 {
261   PetscFunctionBegin;
262   PetscFunctionReturn(0);
263 }
264 
265 static PetscErrorCode TSSetUp_BasicSymplectic(TS ts)
266 {
267   TS_BasicSymplectic *bsymp = (TS_BasicSymplectic*)ts->data;
268   DM                 dm;
269 
270   PetscFunctionBegin;
271   PetscCall(TSRHSSplitGetIS(ts,"position",&bsymp->is_q));
272   PetscCall(TSRHSSplitGetIS(ts,"momentum",&bsymp->is_p));
273   PetscCheck(bsymp->is_q && bsymp->is_p,PetscObjectComm((PetscObject)ts),PETSC_ERR_USER,"Must set up RHSSplits with TSRHSSplitSetIS() using split names positon and momentum respectively in order to use -ts_type basicsymplectic");
274   PetscCall(TSRHSSplitGetSubTS(ts,"position",&bsymp->subts_q));
275   PetscCall(TSRHSSplitGetSubTS(ts,"momentum",&bsymp->subts_p));
276   PetscCheck(bsymp->subts_q && bsymp->subts_p,PetscObjectComm((PetscObject)ts),PETSC_ERR_USER,"Must set up the RHSFunctions for position and momentum using TSRHSSplitSetRHSFunction() or calling TSSetRHSFunction() for each sub-TS");
277 
278   PetscCall(VecDuplicate(ts->vec_sol,&bsymp->update));
279 
280   PetscCall(TSGetAdapt(ts,&ts->adapt));
281   PetscCall(TSAdaptCandidatesClear(ts->adapt)); /* make sure to use fixed time stepping */
282   PetscCall(TSGetDM(ts,&dm));
283   if (dm) {
284     PetscCall(DMCoarsenHookAdd(dm,DMCoarsenHook_BasicSymplectic,DMRestrictHook_BasicSymplectic,ts));
285     PetscCall(DMSubDomainHookAdd(dm,DMSubDomainHook_BasicSymplectic,DMSubDomainRestrictHook_BasicSymplectic,ts));
286   }
287   PetscFunctionReturn(0);
288 }
289 
290 static PetscErrorCode TSReset_BasicSymplectic(TS ts)
291 {
292   TS_BasicSymplectic *bsymp = (TS_BasicSymplectic*)ts->data;
293 
294   PetscFunctionBegin;
295   PetscCall(VecDestroy(&bsymp->update));
296   PetscFunctionReturn(0);
297 }
298 
299 static PetscErrorCode TSDestroy_BasicSymplectic(TS ts)
300 {
301   PetscFunctionBegin;
302   PetscCall(TSReset_BasicSymplectic(ts));
303   PetscCall(PetscObjectComposeFunction((PetscObject)ts,"TSBasicSymplecticSetType_C",NULL));
304   PetscCall(PetscObjectComposeFunction((PetscObject)ts,"TSBasicSymplecticGetType_C",NULL));
305   PetscCall(PetscFree(ts->data));
306   PetscFunctionReturn(0);
307 }
308 
309 static PetscErrorCode TSSetFromOptions_BasicSymplectic(PetscOptionItems *PetscOptionsObject,TS ts)
310 {
311   TS_BasicSymplectic *bsymp = (TS_BasicSymplectic*)ts->data;
312 
313   PetscFunctionBegin;
314   PetscOptionsHeadBegin(PetscOptionsObject,"Basic symplectic integrator options");
315   {
316     BasicSymplecticSchemeLink link;
317     PetscInt                  count,choice;
318     PetscBool                 flg;
319     const char                **namelist;
320 
321     for (link=BasicSymplecticSchemeList,count=0; link; link=link->next,count++) ;
322     PetscCall(PetscMalloc1(count,(char***)&namelist));
323     for (link=BasicSymplecticSchemeList,count=0; link; link=link->next,count++) namelist[count] = link->sch.name;
324     PetscCall(PetscOptionsEList("-ts_basicsymplectic_type","Family of basic symplectic integration method","TSBasicSymplecticSetType",(const char*const*)namelist,count,bsymp->scheme->name,&choice,&flg));
325     if (flg) PetscCall(TSBasicSymplecticSetType(ts,namelist[choice]));
326     PetscCall(PetscFree(namelist));
327   }
328   PetscOptionsHeadEnd();
329   PetscFunctionReturn(0);
330 }
331 
332 static PetscErrorCode TSView_BasicSymplectic(TS ts,PetscViewer viewer)
333 {
334   PetscFunctionBegin;
335   PetscFunctionReturn(0);
336 }
337 
338 static PetscErrorCode TSInterpolate_BasicSymplectic(TS ts,PetscReal t,Vec X)
339 {
340   TS_BasicSymplectic *bsymp = (TS_BasicSymplectic*)ts->data;
341   Vec                update = bsymp->update;
342   PetscReal          alpha = (ts->ptime - t)/ts->time_step;
343 
344   PetscFunctionBegin;
345   PetscCall(VecWAXPY(X,-ts->time_step,update,ts->vec_sol));
346   PetscCall(VecAXPBY(X,1.0-alpha,alpha,ts->vec_sol));
347   PetscFunctionReturn(0);
348 }
349 
350 static PetscErrorCode TSComputeLinearStability_BasicSymplectic(TS ts,PetscReal xr,PetscReal xi,PetscReal *yr,PetscReal *yi)
351 {
352   PetscFunctionBegin;
353   *yr = 1.0 + xr;
354   *yi = xi;
355   PetscFunctionReturn(0);
356 }
357 
358 /*@C
359   TSBasicSymplecticSetType - Set the type of the basic symplectic method
360 
361   Logically Collective on TS
362 
363   Input Parameters:
364 +  ts - timestepping context
365 -  bsymptype - type of the symplectic scheme
366 
367   Options Database:
368 .  -ts_basicsymplectic_type <scheme> - select the scheme
369 
370   Notes:
371     The symplectic solver always expects a two-way splitting with the split names being "position" and "momentum". Each split is associated with an IS object and a sub-TS
372     that is intended to store the user-provided RHS function.
373 
374   Level: intermediate
375 @*/
376 PetscErrorCode TSBasicSymplecticSetType(TS ts,TSBasicSymplecticType bsymptype)
377 {
378   PetscFunctionBegin;
379   PetscValidHeaderSpecific(ts,TS_CLASSID,1);
380   PetscTryMethod(ts,"TSBasicSymplecticSetType_C",(TS,TSBasicSymplecticType),(ts,bsymptype));
381   PetscFunctionReturn(0);
382 }
383 
384 /*@C
385   TSBasicSymplecticGetType - Get the type of the basic symplectic method
386 
387   Logically Collective on TS
388 
389   Input Parameters:
390 +  ts - timestepping context
391 -  bsymptype - type of the basic symplectic scheme
392 
393   Level: intermediate
394 @*/
395 PetscErrorCode TSBasicSymplecticGetType(TS ts,TSBasicSymplecticType *bsymptype)
396 {
397   PetscFunctionBegin;
398   PetscValidHeaderSpecific(ts,TS_CLASSID,1);
399   PetscUseMethod(ts,"TSBasicSymplecticGetType_C",(TS,TSBasicSymplecticType*),(ts,bsymptype));
400   PetscFunctionReturn(0);
401 }
402 
403 static PetscErrorCode TSBasicSymplecticSetType_BasicSymplectic(TS ts,TSBasicSymplecticType bsymptype)
404 {
405   TS_BasicSymplectic        *bsymp = (TS_BasicSymplectic*)ts->data;
406   BasicSymplecticSchemeLink link;
407   PetscBool                 match;
408 
409   PetscFunctionBegin;
410   if (bsymp->scheme) {
411     PetscCall(PetscStrcmp(bsymp->scheme->name,bsymptype,&match));
412     if (match) PetscFunctionReturn(0);
413   }
414   for (link = BasicSymplecticSchemeList; link; link=link->next) {
415     PetscCall(PetscStrcmp(link->sch.name,bsymptype,&match));
416     if (match) {
417       bsymp->scheme = &link->sch;
418       PetscFunctionReturn(0);
419     }
420   }
421   SETERRQ(PetscObjectComm((PetscObject)ts),PETSC_ERR_ARG_UNKNOWN_TYPE,"Could not find '%s'",bsymptype);
422 }
423 
424 static PetscErrorCode  TSBasicSymplecticGetType_BasicSymplectic(TS ts,TSBasicSymplecticType *bsymptype)
425 {
426   TS_BasicSymplectic *bsymp = (TS_BasicSymplectic*)ts->data;
427 
428   PetscFunctionBegin;
429   *bsymptype = bsymp->scheme->name;
430   PetscFunctionReturn(0);
431 }
432 
433 /*MC
434   TSBasicSymplectic - ODE solver using basic symplectic integration schemes
435 
436   These methods are intened for separable Hamiltonian systems
437 
438 $  qdot = dH(q,p,t)/dp
439 $  pdot = -dH(q,p,t)/dq
440 
441   where the Hamiltonian can be split into the sum of kinetic energy and potential energy
442 
443 $  H(q,p,t) = T(p,t) + V(q,t).
444 
445   As a result, the system can be genearlly represented by
446 
447 $  qdot = f(p,t) = dT(p,t)/dp
448 $  pdot = g(q,t) = -dV(q,t)/dq
449 
450   and solved iteratively with
451 
452 $  q_new = q_old + d_i*h*f(p_old,t_old)
453 $  t_new = t_old + d_i*h
454 $  p_new = p_old + c_i*h*g(p_new,t_new)
455 $  i=0,1,...,n.
456 
457   The solution vector should contain both q and p, which correspond to (generalized) position and momentum respectively. Note that the momentum component could simply be velocity in some representations.
458   The symplectic solver always expects a two-way splitting with the split names being "position" and "momentum". Each split is associated with an IS object and a sub-TS that is intended to store the user-provided RHS function.
459 
460   Reference: wikipedia (https://en.wikipedia.org/wiki/Symplectic_integrator)
461 
462   Level: beginner
463 
464 .seealso: `TSCreate()`, `TSSetType()`, `TSRHSSplitSetIS()`, `TSRHSSplitSetRHSFunction()`
465 
466 M*/
467 PETSC_EXTERN PetscErrorCode TSCreate_BasicSymplectic(TS ts)
468 {
469   TS_BasicSymplectic *bsymp;
470 
471   PetscFunctionBegin;
472   PetscCall(TSBasicSymplecticInitializePackage());
473   PetscCall(PetscNewLog(ts,&bsymp));
474   ts->data = (void*)bsymp;
475 
476   ts->ops->setup           = TSSetUp_BasicSymplectic;
477   ts->ops->step            = TSStep_BasicSymplectic;
478   ts->ops->reset           = TSReset_BasicSymplectic;
479   ts->ops->destroy         = TSDestroy_BasicSymplectic;
480   ts->ops->setfromoptions  = TSSetFromOptions_BasicSymplectic;
481   ts->ops->view            = TSView_BasicSymplectic;
482   ts->ops->interpolate     = TSInterpolate_BasicSymplectic;
483   ts->ops->linearstability = TSComputeLinearStability_BasicSymplectic;
484 
485   PetscCall(PetscObjectComposeFunction((PetscObject)ts,"TSBasicSymplecticSetType_C",TSBasicSymplecticSetType_BasicSymplectic));
486   PetscCall(PetscObjectComposeFunction((PetscObject)ts,"TSBasicSymplecticGetType_C",TSBasicSymplecticGetType_BasicSymplectic));
487 
488   PetscCall(TSBasicSymplecticSetType(ts,TSBasicSymplecticDefault));
489   PetscFunctionReturn(0);
490 }
491