source: proiecte/NBody/NBI/NBIparalel.c @ 124

Last change on this file since 124 was 124, checked in by (none), 14 years ago

added NBI

File size: 60.6 KB
Line 
1
2
3#define CODENAME "NBI"
4#define VERSION    "1"
5
6/* ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
7
8                        NBI
9  A set of numerical integrators for the gravitational N-body problem.
10
11        (C) Copyright 1996 Ferenc Varadi.  All rights reserved.
12  This copyright notice is intended to prevent the commercial use of this code.
13  The code is free and can be freely distributed.
14  Do not remove this copyright notice unless you make changes in the code.
15  If you make changes in the code, add your name to the copyright notice.
16
17  The usual disclaimers apply, i.e., use the code at your own risk.
18  The author does not guarantee anything.
19
20  This code was developed by F. Varadi in collaboration
21  with M. Ghil, W. I. Newman, W. M. Kaula, K. Grazier, D. Goldstein
22  and M. Lessnick. The partial financial support of NSF Grant
23  ATM95-23787 (M. Ghil and F. Varadi) is gratefully acknowledged.
24
25  If you have any questions, suggestions etc., contact F. Varadi,
26  e-mail: varadi@ucla.edu
27  WWW: http://www.atmos.ucla.edu/~varadi
28
29 
30  For the impatient:
31        Save this file as NBI.c
32        Compile the code as
33        cc -o NBI NBI.c -lm
34
35        Download the sample input file NBIsampleinput and save it as
36        NBIsampleinput.  It is an input file for the outer Solar System, with
37        the planets from Jupiter through Neptune based on the Jet Propulsion
38        Laboratory's DE403 (Digital Ephemeris 403).  This was obtained
39        from the ftp site navigator.jpl.nasa.gov, updates can be accessed
40        there.
41
42        Run the code:
43        NBI NBIsampleinput
44        Take a look at the output in NBIsampleoutput and the log file
45        NBIsampleoutput.log Change the MAXNBODIES macro, i.e., the
46        maximum number of particles, to suit your needs.  It is set to
47        2100 in this version.
48
491) Description:
50        Four numerical integrators for the gravitational (nonrelativistic)
51        N-body problem are bundled into a single source code. The N bodies
52        are assumed to be point masses. Particles with zero mass are called
53        test particles; they are assumed not to influence each other and
54        the particles with nonzero mass.
55
56        The integrators are:
57
58        a) A Wisdom-Holman-type mapping for hierarchical N-body systems.
59                Code name: HWH, maximum global order = 4
60           This can deal with more general arrangements than the standard
61           Wisdom-Holman mapping.  Hierarchical N-body systems are described
62           by Roy (1988). These are represented in the code by binary trees.
63           The integrator takes advantage of certain properties of
64           symplectic forms and Jacobi coordinates, these will be described
65           in a paper (in preparation). We also use singularly weighted
66           symplectic forms, these are discussed by Varadi et al. (1995).
67
68        b) A modified Cowell-Stormer integrator, with modifications by
69           W. I. Newman and his students.
70                Code name: CSN, max global order = 15
71           Note: (global order) = (local order) - 2 (Goldstein, 1996)
72
73        c) A Gragg-Bulirsch-Stoer integrator, as it is described by
74           Hairer et al., (1993).
75                Code name: GBS, max order = 20 (max stage = 10)
76        IMPORTANT: The user has to specify the number of stages
77        in the input file and not the order. Order = 2*(number of stages),
78        see the Notes below.
79
80        d) A symplectic mapping with Kinetic-Potential energy splitting
81                Code name: SKP, max global order = 4
82           This is included for the sake of completeness.
83
84       
852) References:
86       
87        Goldstein, D.: 1996, The Near-Optimality of Stormer
88        Methods for Long Time Integrations of y''=g(y),
89        Ph.D. Dissertation, Univ. of California, Los Angeles,
90        Dept. of Mathematics
91
92        Hairer, E., Norsett, S. P. and Wanner, G.: 1993,
93        Solving Ordinary Differential Equations I.  Nonstiff Problems.
94        Second Revised Edition
95       
96        Lessnick, M.: 1996, Stability Analysis of Symplectic
97        Integration Schemes, Ph.D. Dissertation, Univ. of California,
98        Los Angeles, Dept. of Mathematics
99
100        Roy, A. E.: 1988, Orbital Motion, Institute of Physics
101        Publishing, Bristol
102       
103        Varadi, F. De la Barre, Kaula, W. M. and Ghil, M.: 1995,
104        Singularly Weighted Symplectic Forms and Applications to
105        Asteroid Motion, Celestial Mechanics and Dynamical Astronomy,
106        vol. 62, pp. 23-41
107
108        Yoshida, H.: 1993, Recent Progress in The Theory and
109        Application of Symplectic Integrators,
110        Celestial Mechanics and Dynamical Astronomy, vol. 56, pp. 27-43
111       
112       
113
1143) Notes
115       
116        All long-term integrations suffer from roundoff errors.
117        Symplectic schemes guarantee symplecticity for short
118        integrations but they are not strictly symplectic
119        for very long integrations.  The implementation of
120        the Wisdom-Holman mapping in this code was aimed at
121        reducing the effects of roundoff errors and thus make
122        the scheme as symplectic as possible.
123       
124        The Cowell-Stormer integrator, being a multistep
125        integrator, has to be intialized.  In theory, the starting
126        points should be computed with machine precision. This
127        can be done using quadrupole precision arithmetic but
128        the Gragg-Bulirsch-Stoer scheme in double precision
129        appers to be quite adequate. It should be noted that
130        the stability of the Cowell-Stormer scheme ensures
131        that initial errors do not grow due to computational
132        limitations (Goldstein, 1996).
133
134        In the case of the Gragg-Bulirsch-Stoer scheme one
135        specifies the number of stages k in the extrapolation
136        scheme. The order of the method is then 2*k.
137        This factor of 2 comes from the fact that the
138        underlying method (Gragg's) has only even powers
139        of the step size in the expansion of the local error
140        (cf. Hairer et al., 1993, especially equation 9.22).
141
142        All integrators use only G*mass, there is no need for
143        the masses themselves nor the value of G.  Any set of units
144        can be used:
145        distance_unit for position,
146        time_unit for time and step size,
147        distance_unit/time_unit for velocity.
148        E.g., one can use AU, day and AU/day as units.
149
150        None of the codes is regularized in any sense. We intend
151        to provide regularized code in the future. For the Wisdom-Holman
152        mapping this involves changing the tree of Jacobian reference
153        frames and dealing with the transition region accurately.
154
155        In the case of the Wisdom-Holman mapping, the computation of
156        acceleration differences between Jacobi and inertial coordinates
157        could be improved in order to further reduce roundoff error.
158        These differences can be represented by multi-pole expansions
159        of the potential but we have not explored the details yet.
160
161        The present version of the integrator does not monitor error.
162
163        The choice of step size is up to the user. In most cases
164        one has to adjust the step size to the shortest orbital
165        period T in the system. The Wisdom-Holman mapping at second
166        order is usually used with step size of T/10-T/100.
167        We recommend T/1000 for Cowell-Stormer integrator
168        since this renders the calculation EXACT to double
169        precision, even for high (e=0.5) eccentricities.
170        The cost of using this short time step is substantially
171        compensated by reduced computational complexity (as
172        compared to the Wisdom-Holman mapping).  For much larger
173        step sizes the integrator is not stable.
174
175        The Gragg-Bulirsch-Stoer method appears to be stable
176        for large orders and step sizes. For order 20 one can
177        even use T/5 as the step size and still get accurate results.
178        Since this is an accurate but compuationally very expensive
179        integrator, it is mainly used by us for short integrations.
180        We have not investigated the propagation of roundoff
181        error in the GBS method.
182       
183
1844) How to compile:
185        Since there is only a single source file, it is very easy to
186        compile the code with whatever C compiler is available. The
187        code was compiled and tested on Sun SPARCstations (Solaris 2.5.1
188        and earlier) and DEC Alpha workstations (Digital UNIX V3.2).
189        For instance, on the SPARCstations,
190       
191        cc -fast -xO5 -xdepend -o NBI NBI.c -lm
192
193        should work with Sun's C compiler.  The code uses only the standard
194        C library, more exactly: I/O functions, exit, malloc and free for
195        the tree, sqrt, cbrt, sin, cos, sinh, cosh.
196
197        Caution: optimization may introduce problems with some compilers
198        and it is prudent to check the correctness of the optimized code
199        by compiling without optimization, e.g.,
200        cc -g -o NBI_no_op NBI.c -lm
201        By comparing the output from NBI and NBI_no_op one can detect if
202        the optimization leads to any problems.  (It should not but in
203        practice the situation is not so clear.)
204
205        One also can play with various compiler switches which control
206        roundoff but we have not explored these.
207
2085) How to run
209
210        NBI inputfile
211
212        (Everything has to be specified in inputfile.)
213
2146) On hierarchical N-body systems
215
216        We can give only a brief introduction into these; a detailed technical
217        description is in preparation.  Our approach is somewhat different
218        from that of Roy (1988).
219
220        One starts with the concept of joining subsystems and applies this
221        concept recursively to form new systems. When two subsystems are
222        joined, the two subsystems are assumed to behave as point masses
223        and thus define the appropriate two-body problem for the Wisdom-Holman
224        mapping. The subsystems are, in general, not point masses and one
225        takes this into account in the perturbation step of the Wisdom-Holman
226        mapping.
227
228        Instead of a simple chain of Jacobi coordinates, the code uses a
229        tree of Jacobi coordinates. This makes it possible to use the code
230        e.g., in situations when the massive particles have sattelites, either
231        massive ones or not. It also reduces, when carefully specified, the
232        integration error since more of the perturbations are absorbed into
233        two-body interactions. The tree has to be specified in the input file. 
234        One can define this tree formally as it is processed by the code which
235        works like a simple finite automaton but we did not use lex, yacc or
236        any other compiler-compiler tools.
237
238 Tree description rules:
239 Rule 1) particles are indexed by consecutive natural numbers, from 1 to N,
240 Rule 2) particles are subsystems,
241 Rule 3) (x y) : join the subsystems x and y to create a new subsystem.,
242 Rule 4) [i1 - i2] denotes a set of test particles, from index i1 to index i2,
243        which are treated the same way,
244        Note: the space before and after - is necessary!
245 Rule 5) ([i1 - i2] i3) is forbidden, use instead (i3 [i1 - i2])
246
247 Example: 2000 main belt asteroids, from  6 to 2006, and
248        Sun, Jupiter, Saturn, Uranus and Neptune (1 - 5)
249        can described as
250 (((((1 [6 - 2006]) 2) 3) 4) 5)
251
252 Another example for the tree specification when test particles between
253 Jupiter and Saturn are added with indeces from 6 to 1000 and another set
254 of test particles is added between Saturn and Uranus with indices from
255 1001 to 2000. The indices for the massive bodies are:
256 Sun 1, Jupiter, 2, Saturn 3, Uranus 4, Neptune 5.
257((((((1 2) [6 - 1000]) 3) [1001 - 2000]) 4) 5)
258
259 Yet another example: the Sun, Earth, Moon, Jupiter and Io with indeces
260 1,2,3,4 and 5, respectively, for which the tree is
261 ((1 (2 3)) (4 5))
262
263
2647) Format of the input file
265Id method order step_size  total_integration_time printing_time
266        number_of_particles
267
268 tree_description
269
270GMs of particles, possibly zeros
271
272Initial conditions: x,y,z, vx, vy, vz
273
274Explanation:
275
276        Id is an arbitrary string which specifies the name of the experiment.
277
278        Method can be SKP, HWH, CSN and GBS.
279
280        Tree_description is used only when the method is HWH or the HWH is used
281        to initialize CSN. One can put a dummy tree description of the form
282        (1 2)
283        in the input file when HWH is not used.  This will satisfy the parser
284        which processes the tree description but will not play any role in the
285        integration.
286
287
288Example:
289 For Sun, Jupiter, Saturn, Uranus and Neptune,
290 with indeces 1,2,3,4 and 5, in heliocentric coordinates.
291
292SomeId HWH 4 100.0 365.0e+4 1.0e+4
2935
294((((1 2) 3) 4) 5)
295
2960.295912208285591095e-03   
2970.282534590952422643e-06  0.845971518568065874e-07 
2980.129202491678196939e-07   0.152435890078427628e-07 
299
3000.0 0.0 0.0 0.0 0.0 0.0
301
302-0.538420940678015203e+01   -0.831247035699103631e+00 -0.225095848657298592e+00 
3030.109236311953937086e-02   -0.652329334256263223e-02 -0.282301443780311710e-02 
304
3050.788989169798583756e+01   0.459570785756998301e+01  0.155843220515231762e+01
306-0.321720261683698921e-02   0.433063255278440598e-02  0.192641782724487106e-02
307
308-0.182698980946940850e+02   -0.116271406901560681e+01 -0.250371938307287545e+00 
3090.221540447608191936e-03   -0.376765389967669917e-02 -0.165324449144407023e-02
310
311-0.160595376822070470e+02   -0.239429495784517528e+02 -0.940042953888094424e+01 
3120.264312302715172384e-02   -0.150349219713705076e-02 -0.681271071793930938e-03
313
314
315There are 2 output files:
316        1) SomeId contains the actual integration data
317        2) SomeId.log contains a copy of the input data and error messages.
318        Some error messages might be sent to the standard output and the user
319        has to redirect it in order to capture them.
320
321In the case of the Cowell-Stormer integrator, an additional file, SomeId.lerr
322is created. This contains the LOCAL truncation error of the integration,
323computed for each step but only printed for the last step in the printing
324cycle. This also should help to determine the appropriate step size.
325
326The output is in the original coordinate system, i.e., inertial and not Jacobian.
327Output is produced when time is an integer multiple of the specified printing
328time. The following is printed:
329time    relative_change_in_total_energy relative_change_in_ang.mom.z.comp.
330after which
331x y z
332dx dy dz
333for each particle.
334
335
3369) The uset can change the code, of course. The section MAIN LOOP is where
337   this can be done easily. You can insert code to do more things, e.g.,
338   detecting close approaches.
339
34010) MACROS TO GIVE SOME CONTROL OVER THINGS.
341Changes in the code that you can make HERE:
342        CHANGE the DIM macro to 2 for planar motions.
343        (Note: a variables dim is also used, easy to change
344                it everywhere to DIM. It is still better to keep
345                the Kepler solver in the more general 3-dimensional form.)
346        CHANGE the MAXNBODIES macro to whatever you want,
347                including test particles.
348
349**************** END OF DESCRIPTION ********************** */
350
351/* +++++++++++++++++++++++++++++++++++++++++++++++++++++++++
352        USER-CHANGEABLE MACROS    */
353
354/* dimension of physical space */
355#define DIM                     3
356/* max number of bodies, obviously */
357#define MAXNBODIES              2100
358
359/* TOLERANCES */
360
361/* the extrapolation scheme stops when
362        Tj,k - T(j-1),k becomes smaller than this */
363#define GBSTOL                  5.0e-32
364
365/* The solution of Kepler's equation is accepted when
366   when iterates change by an amount smaller than this  */
367#define KEPLERSOLVERTOLERANCE   5.0e-16
368
369/* what method and order to use to initialize the CSN integrator, GBS or HWH  */
370#define CSNINITGBS              1
371#define CSNINITGBSORDER         4       
372#define CSNINITHWH              2
373#define CSNINITHWHORDER         4
374int csninit = CSNINITGBS;
375
376
377/* which extrapolation sequence to use in the GBS method */
378#define EXSEQHARM int extrapolseq[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
379#define EXSEQBUL  int extrapolseq[] = { 0, 1, 2, 3, 4, 6, 8, 12, 16, 24, 32};
380
381/* cange this line accordingly */
382EXSEQBUL
383
384
385/* VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV */
386
387/*      The actual code.
388        Change things below this line at your own peril.
389       
390 */
391
392/* MACROS TO DISTINGUISH BETWEEN TEST PARTICLES AND
393   PARTICLES WITH FINITE MASS
394   You can add more types here, these values are stored
395        in tpstore for each particle
396   e.g., you could define stopped test particles,
397   but you have to change the code accordingly.
398   It is perhaps easier to add more integer arrays.
399   IMPORTANT: THE TREE MAINTAINS ITS OWN INTEGER ARRAYS
400   WHICH HOLD TEST PARTICLE INDICES.
401*/
402
403#define NOTTESTPARTICLE         1
404#define TESTPARTICLE            0
405
406/* the header files we need, only standard stuff */
407#include <stdio.h>
408#include <stdlib.h>
409#include <string.h>
410#include <math.h>
411#include "omp.h"
412
413/* Macro definitions */
414#define NUMMETHODS      4
415#define SKP 1
416#define HWH 2
417#define CSN 3
418#define GBS 4
419
420/* it is really the number of stages for GBS */
421char *methodnames[] = { "   ", "SKP", "HWH", "CSN", "GBS" };
422#define MAXCSN  15
423#define MAXGBS  10
424int MAXORDERS[] = { 0, 4, 4, MAXCSN, MAXGBS };
425
426/*
427   MACROS WHICH DEFINE WHAT IS STORED FOR THE PARTICLES
428        from 1 to 6: x y z vx vy vz
429                  7: G*mass
430        from 8 to 10:  accelerations in x, y and z
431   e.g., particle i's y velocity is
432         parts[i][5]
433*/
434
435#define TMPSIZE      (2*DIM+1)
436/* Note: we need only the G*mass for the actual integrations */ 
437#define GMASSCO      (7)
438#define ACC          (7)
439/* you can add more data for particles here
440   but keep NPARTDATA to specify the total number of
441   coordinates and other components  */
442#define NPARTDATA    (ACC+3)
443/* this is used from index 1 in both indeces */
444#define DECLARR [MAXNBODIES+1][NPARTDATA+1]
445
446/* Cowell-Stormer-Newman array */
447#define DECLSTORMER [MAXNBODIES+1][DIM+1][MAXCSN+2]
448/* Gragg-Bulirsch-Stoer array */
449#define DECLBS [MAXNBODIES+1][2*DIM+1][2][MAXGBS+2]
450
451#define DIM2         (2*DIM)
452
453/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
454                GLOBAL DATA
455        ARRAYS THAT HOLD THE PARTICLES AND AUXILIARY DATA
456*/
457
458/* ARRAYS THAT HOLD THE PARTICLES' DATA.
459        This can be moved into main() without
460        any adverse effect.  */
461double parts DECLARR;
462
463/* This holds test particle indices.
464   It seems easier to keep this global. It is used, e.g.,
465   by PlainAccel to avoid the computation of force between
466   test particles.  */
467int tpstore[MAXNBODIES+1];
468
469/* this is needed for error messages from deep within the integrator */
470FILE *FLOG;
471
472/* Local error monitoring for the Cowell-Stormer integrator */
473double CSNerror = 0;
474
475
476/* FUNCTION AND STRUCT DECLARATIONS */
477
478/* the tree to store Jacobi coordinates */
479struct tree {
480        double tmass; /* total G*mass */
481        double rm1;   /* total mass / mass1 */
482        double rm2;   /* total mass / mass2 */ 
483        double bc[DIM2+1]; /* barycenter: position and velocity
484                                for actual particle these  are pos. and vel. */
485        double acc[DIM+1];
486/* nonzero particle index indicates that this is one with finite mass
487 or it is an array of test particles which can be processed in a simple loop,
488   zero means that this is a node in the tree
489*/
490        int particleindex;  /* nonzero indicates actual particle(s) */
491        struct tree *p1;
492        struct tree *p2;
493        int *testparticles; /* nonzero indicates array of test particles */
494        };
495
496
497int CStep(int ord, double h, int nump, double parts DECLARR, 
498                struct tree *treein);
499
500int GBSStep(int ord, double h, int nump, double parts DECLARR);
501
502/* makes a simple chain, usual planetary motion case */
503struct tree *MakeSimpleTree(int nump);
504/* makes an arbitrary tree from a textual description, i.e.,
505  parses the text and builds the tree */
506struct tree *MakeTree(FILE *f);
507
508/* Fill tree with barycenters, used to initialize the tree */
509int FillCMinTree(struct tree *tree, double p DECLARR);
510/* copy particle coordinates from tree;
511   since the tree is the primary storage for the coordinates
512   of particles with finite mass, one needs to call this
513   before accessing data in the "parts" array
514 */
515int ParticlesFromTree(struct tree *tree, double p DECLARR);
516
517/* The actual HWH integrator */
518int HWHStep(int order, struct tree *tree, double stepsize, double p DECLARR);
519
520/* Kinetic-potential energy split */
521int SKPStep(int order, double stepsize, int nump, double p DECLARR);
522
523
524/* access integer arrays in trees that hold the indeces
525        of test particles attached to that point,
526        returns pointer to testparticles,
527        included for the sake of completeness, not actually used */
528int **GetTestParticles(struct tree *tree, int ind);
529
530
531/*    VERY SIMPLE IO, you can write your own if you need something more */
532int readdata(FILE *f, int num, double p DECLARR) {
533        int i, j; double tmp;
534        for ( i = 1; i <= num; ++i ) {
535                        fscanf(f, "%le", &tmp); p[i][GMASSCO] = tmp; }
536        for ( i = 1; i <= num; ++i ) {
537                for ( j = 1; j <= 6; ++j ) {
538                        fscanf(f, "%le", &tmp); p[i][j] = tmp; } }
539        return num; }
540
541// X
542int printdataA(int num, FILE *f, double p DECLARR) {
543        int i, j;
544        for ( i = 1; i <= num; ++i ) {
545                for ( j = 1; j <= DIM; ++j ) { 
546                        fprintf(f, "%.14le  ", p[i][j]); 
547                } 
548                fprintf(f, "%c", '\n');
549                for ( j = DIM+1; j <= 2*DIM; ++j ) { 
550                        fprintf(f, "%.14le  ", p[i][j]); 
551                } 
552                fprintf(f, "%c", '\n');
553        }
554        return num; 
555}
556
557// X
558int printdata(int num, FILE *f, double p DECLARR, int c1, int c2) {
559        int i, j;
560        for ( i = 1; i <= num; ++i ) {
561                for ( j = c1; j <= c2; ++j ) {
562                        fprintf(f, "%.14le  ", p[i][j]); } 
563                    fprintf(f, "%c", '\n');
564                }
565        return num; 
566}
567
568//X total energy for particles with finite mass
569double energy(int nump, int dim, double p DECLARR, int *tps) {
570        double h = 0, tmp, tmp2;
571        int i,j,k;
572       
573        for ( i = 1; i <= nump; ++i ) {
574                if ( tps[i] == 0 ) 
575                        continue;
576                tmp = 0.0;
577               
578                #pragma omp parallel for reduction(+:tmp) schedule(runtime)
579                for ( k = 1; k <= dim; ++k ) {
580                        tmp = tmp + p[i][k+dim]*p[i][k+dim]; 
581                }
582                tmp = tmp*p[i][GMASSCO]/2.0;
583               
584                h = h + tmp;
585                for ( j = i+1; j <= nump; ++j ) {
586                        tmp = 0.0;
587                        #pragma omp parallel for reduction(+:tmp) schedule(runtime)
588                        for ( k = 1; k <= dim; ++k ) {
589                                tmp2 = p[i][k]-p[j][k];
590                                tmp = tmp+tmp2*tmp2; 
591                        }
592                        tmp = 1/sqrt(tmp);
593                        h = h - tmp*p[i][GMASSCO]*p[j][GMASSCO]; 
594                }
595        }
596       
597        return h; 
598}
599
600// X z component of angular momentum
601double angmomz(int nump, int dim, double p DECLARR, int *tps) {
602        double az = 0.0;
603        int i;
604       
605        for ( i = 1; i <= nump; ++i ) {
606                if ( tps[i] == 0 ) 
607                        continue;
608                // x*vy - y*vx 
609                az = az + (p[i][1]*p[i][2+dim] - p[i][2]*p[i][1+dim]) * p[i][GMASSCO]; 
610        }
611        return az; 
612}
613
614// X transforms all particles to coordinates relative to barycenter
615int tobarycenter(int nump, int dim, double p DECLARR) {
616        double bc[10];
617        int i, j;
618       
619        #pragma omp parallel private(j)
620        #pragma omp for schedule(runtime)
621        for (j = 0; j <= 2*dim; ++j ) 
622                bc[j] = 0.0;
623       
624        for ( i = nump; i >= 1; --i ) {
625                bc[0] += p[i][GMASSCO];
626                #pragma omp parallel private(j)
627                #pragma omp for schedule(runtime)
628                for (j = 1; j <= 2*dim; ++j ) 
629                        bc[j] += p[i][j]*p[i][GMASSCO]; 
630        }
631       
632        #pragma omp parallel private(j)
633        #pragma omp for schedule(runtime)
634        for (j = 1; j <= 2*dim; ++j ) 
635                bc[j] = bc[j]/bc[0];
636               
637   
638        for ( i = nump; i >= 1; --i ) {
639                #pragma omp parallel private(j)
640                #pragma omp for schedule(runtime)
641                for (j = 1; j <= 2*dim; ++j ) 
642                        p[i][j] -= bc[j]; 
643        }
644        return 0; 
645}
646
647
648
649
650/* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""
651        MAIN
652*/
653
654int main(int argc, char **argv) {
655        FILE *f, *fin, *flog, *flocerr;
656        char sicode[100];
657        int icode; /* SKP = 1 HWH = 2 CSN = 3 GBS = 4 */
658        struct tree *tree; int num, num2;
659        int ii, i, order = 1, nin, nps;
660        double stepsize;
661        double time = 0.0, ftime, ptime;
662        char fnamin[1000]; char id[1000];
663        double az, az0, toten, toten0;
664
665        // get command-line argument
666        if ( argc < 2 ) { exit(1); }
667        sscanf(argv[1], "%s", fnamin); 
668
669        // read input file
670        fin = fopen(fnamin, "r");
671        fscanf(fin, "%s", id); strcpy(fnamin, id);
672       
673        // create output and log file
674        // id - name of output file     
675        f = fopen(id, "w"); strcat(fnamin, ".log"); flog = fopen(fnamin, "w"); 
676        FLOG = flog;
677
678        #define FAILEDIN { fclose(fin); fclose(f); fclose(flog); exit(1); }
679
680        fprintf(flog, "ID = %s\n", id);
681        fscanf(fin, "%s", sicode); 
682        fscanf(fin, "%d", &order); fprintf(flog, "ORDER = %2d\n", order);
683        icode = 0;
684        for ( icode = 1; icode <= NUMMETHODS; ++icode ) {
685                if ( strcmp(sicode, methodnames[icode] ) == 0 ) { 
686                        if ( order < 1 || order > MAXORDERS[icode] ) {
687                                fprintf(flog, " Order %2d for method %s is not implemented \n", order, sicode);
688                                FAILEDIN
689                        }
690                        break; 
691                } 
692        }
693       
694        if ( icode > NUMMETHODS ) {
695                fprintf(flog, " Invalid method code %s\n", sicode);
696                FAILEDIN
697        }
698
699
700        fscanf(fin, "%le", &stepsize); fprintf(flog, "STEPSIZE = %.12f\n", stepsize);
701        fscanf(fin, "%le", &ftime); fprintf(flog, "FINAL TIME = %.12f\n", ftime);
702        fscanf(fin, "%le", &ptime); fprintf(flog, "PRINTING TIME = %.12f\n", ptime);
703        fscanf(fin, "%d", &num); fprintf(flog, "NUMBER OF PARTS = %2d\n", num);
704       
705        if ( num > MAXNBODIES ) {
706                fprintf(flog, " Too many particles, change MAXNBODIES macro in code ");
707                FAILEDIN
708        }
709
710        /* initialize tpstore */
711       
712        for ( i = 1; i <= num+2; ++i ) { 
713                tpstore[i] = NOTTESTPARTICLE; 
714        }
715
716        /* this will TESTPARTICLE for test particles in tpstore */
717        tree = MakeTree(fin);
718
719        /* For CSN create error file */
720        if ( icode == CSN ) {
721                strcpy(fnamin, id); strcat(fnamin, ".lerr"); flocerr = fopen(fnamin, "w"); 
722        }
723
724        num2 = readdata(fin, num, parts);
725        /* DONE READING ALL INPUTS */
726        fclose(fin);
727
728        /* zero out test particles' tpstore, again, just in case */
729        for ( i = 1; i <= num; ++i ) { 
730                if ( parts[i][GMASSCO] == 0.0 ) { 
731                        tpstore[i] = TESTPARTICLE; 
732                } 
733        }
734
735        if ( num != num2 ) exit(1);
736        fprintf(flog, "GMs\n");
737        printdata(num, flog, parts, GMASSCO, GMASSCO); 
738       
739        fprintf(flog, "Inits \n");
740        printdataA(num, flog, parts);
741       
742        fflush(flog);
743
744        /* WATCH OUT FOR THIS
745           The transformation to barycenter
746           tends to improve things.
747        */
748        tobarycenter(num, DIM, parts);
749        fprintf(flog, "Actual Barycentric Inits \n"); 
750        printdataA(num, flog, parts);
751
752        fprintf(flog, "Actual integrator is %s \n", sicode); 
753        fprintf(flog, "CODE: %s \n", CODENAME); 
754        fprintf(flog, "VERSION: %s \n", VERSION); 
755        fflush(flog);
756
757
758        // z component of total angular momentum, it is used to monitor accumulation of roundoff
759        az0 = angmomz(num, DIM, parts, tpstore);
760        toten0 = energy(num, DIM, parts, tpstore);
761
762
763
764        /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
765                ACTUAL INTEGRATION
766        */
767       
768        // the length of the inner loop, i.e.,  between printing
769        nin = (int) (fabs(ptime)/fabs(stepsize)); 
770       
771        if ( nin == 0 ) {
772                fprintf(flog, "%s\n", " Stepsize is larger than printing time, aborted");
773                exit(1); 
774        }
775       
776        // the length of the outer loop, i.e., printings from beginning to end
777        nps = (int) (fabs(ftime)/fabs(ptime)); 
778
779        fprintf(flog, "nin = %d nps = %d", nin, nps);
780       
781        /* WARNING: VERY LONG INTEGRATIONS CAN PRODUCE VERY LARGE
782                INTEGERS AND ONE HAS TO MAKE SURE THAT THEY ARE STILL
783                SMALLER THAN THE LARGEST REPRESENTABLE INTEGER
784          ANOTHER WARNING: ROUNDOFF EFFECTS MAY CAUSE nin AND/OR nps TO BE
785          DIFFERENT FROM WHAT THE USER EXPECTS
786        */
787
788
789        /* print the first data point, i.e., the initial data */ 
790        #define PRINTDATA fprintf(f, "%.12f   ", time);\
791                az = angmomz(num, DIM, parts, tpstore); \
792                toten = energy(num, DIM, parts, tpstore); \
793                fprintf(f, "%.16e  ", (toten-toten0)/toten0); \
794                fprintf(f, "%.16e\n", (az-az0)/az0); \
795                printdataA(num, f, parts);
796
797        #define PRINTERROR \
798                if ( icode == CSN ) { \
799                        fprintf(flocerr, "%.12f    %.12e\n", time, CSNerror); }
800
801        PRINTDATA
802        PRINTERROR
803
804
805        /* MAIN LOOP */
806        for ( ii = 1; ii <= nps; ++ii ) {
807                for ( i = 1; i <= nin; ++i ) {
808                        switch(icode) {
809                        case SKP:
810                          SKPStep(order, stepsize, num, parts); break;
811                        case HWH:
812                          HWHStep(order, tree, stepsize, parts); break;
813                        case CSN:
814                          CStep(order, stepsize, num, parts, tree); break;
815                        case GBS:
816                          GBSStep(order, stepsize, num, parts); break;
817                        default: 
818                                fprintf(flog, "Not implemented\n");
819                                fclose(flog); fclose(f); exit(1); break;
820                        } 
821               
822                /* DONE ONE TIME STEP
823                ADD MORE CODE HERE IF NECESSARY
824                variables you can use:
825                double time;
826                double parts DECLARR;
827                Note that actual time should be computed as
828                actualtime = time+stepsize*i
829                */
830
831                }
832
833                time += stepsize*nin;
834                fprintf(f, "end of step, time = %f\n", time);
835               
836                if ( icode == HWH ) { ParticlesFromTree(tree, parts); }
837
838                /* CHANGE THIS PART OR ADD YOUR CODE IF NECESSARY */
839       
840                PRINTDATA
841                PRINTERROR
842
843                if ( stepsize > 0.0 && time > ftime ) break;
844                if ( stepsize < 0.0 && time < ftime ) break;
845
846        }
847
848
849        fclose(f);
850        fprintf(flog, "\n Finished \n");
851        fclose(flog);
852        if ( icode == CSN ) fclose(flocerr);
853       
854        return 0;
855}
856
857
858/* >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
859        FOR TEST: THE SIMPLE SPLIT, I.E., Kinetic - Potential
860        It does not provide special treatment for test particles.
861*/
862int PlainSplitKineticStep(int num, double stepsize, double p DECLARR) {
863        int i, k;
864        for ( i = 1; i <= num; ++i ) {
865                for ( k = 1; k <= DIM; ++k ) {
866                        p[i][k] += stepsize*p[i][k+DIM]; } }
867        return 0; }
868
869int PlainSplitPotentialStep(int num, double stepsize, double p DECLARR) {
870        int i, j, k;
871        double tmp, tmp2, tmp3;
872        for ( i = 1; i <= num; ++i ) {
873                tmp = 0.0;
874                for ( j = i+1; j <= num; ++j ) {
875                        tmp2 = 0.0;
876                        for ( k = 1; k <= DIM; ++k ) {
877                                tmp3 = p[i][k] - p[j][k];
878                                tmp2 = tmp2 + tmp3*tmp3; }
879                        tmp3 = 1.0/sqrt(tmp2);
880                        tmp2 = tmp3/tmp2;
881                        for ( k = 1; k <= DIM; ++k ) {
882                                tmp3 = p[i][k] - p[j][k];
883                                tmp = -tmp3*tmp2*p[j][GMASSCO]*stepsize;
884                                p[i][k+DIM] += tmp;
885                                tmp = tmp3*tmp2*p[i][GMASSCO]*stepsize;
886                                p[j][k+DIM] += tmp; }
887                        }
888        }
889        return 0; }
890
891
892
893/* >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
894        COWELL-STORMER INTEGRATOR */
895
896
897/* this computes accelerations, used both for CSN and GBS,
898        not to be confused with Accel used for HWH */
899
900int PlainAccel(int num, double p DECLARR) {
901        int i, j, k; int tpi, tpj;
902        double tmp, tmp2, tmp3;
903        double massi, massj;
904        for ( i = 1; i <= num; ++i ) {
905                for ( k = 1; k <= DIM; ++k ) { p[i][ACC+k] = 0.0; } }
906/* for testing things
907for ( i = 1; i <= num; ++i ) {
908        for ( k = 1; k <= DIM; ++k ) { p[i][ACC+k] = 1.0; } }
909return 0;
910*/
911        for ( i = num; i >= 1; --i ) { 
912                tpi = tpstore[i];
913                massi = p[i][GMASSCO];
914                for ( j = i+1; j <= num; ++j ) {
915                        tpj = tpstore[j];
916                        if ( tpi == TESTPARTICLE && tpj == TESTPARTICLE ) continue;
917                        tmp2 = 0.0;
918                        for ( k = 1; k <= DIM; ++k ) {
919                                tmp3 = p[i][k] - p[j][k];
920                                tmp2 = tmp2 + tmp3*tmp3; }
921                        tmp3 = 1.0/sqrt(tmp2);
922                        tmp2 = tmp3/tmp2;
923                        massj = p[j][GMASSCO];
924                        for ( k = 1; k <= DIM; ++k ) {
925                                tmp3 = p[i][k] - p[j][k];
926                                tmp = tmp3*tmp2;
927                        if ( tpj == NOTTESTPARTICLE ) {
928                                p[i][ACC+k] -= (tmp*massj); }
929                        if ( tpi == NOTTESTPARTICLE ) {
930                                p[j][ACC+k] += (tmp*massi); }
931                                }
932                        }
933        }
934        return 0; }
935
936/* Cowell-Stormer-Newman 14(6)th order */
937
938
939/* Newman's 14(6)th order Stormer's coeffs */
940
941double CSalpha[] =  {   0.0,
942                        1.000000000000000000000,
943                        0.000000000000000000000,
944                        0.083333333333333333333,
945                        0.083333333333333333333,
946                        0.079166666666666666666,
947                        0.075000000000000000000,
948                        0.071345899470899470899,
949                        0.068204365079365079365,
950                        0.065495756172839506172,
951                        0.063140432098765432098,
952                        0.061072649861712361712,
953                        0.059240564123376623376,
954                        0.057603625837453135733,
955                        0.056129980884507174190,
956                        0.054794379107071147139
957                                                 };
958 
959 
960double CSbeta[] =  {    0.0,
961                        1.50000000000000000000,
962                        0.33333333333333333333,
963                        0.37500000000000000000,
964                        0.35277777777777777777,
965                        0.33402777777777777777,
966                        0.31924603174603174603,
967                        0.30736607142857142857,
968                        0.29757660934744268077,
969                        0.28933077050264550264,
970                        0.28225737868098979210,
971                        0.27609762576993479771,
972                        0.27066578505957226195,
973                        0.26582499331955247133,
974                        0.26147199790503706399,
975                        0.25752728193649391773
976                                               };
977 
978       
979
980double  sumfrombottom(double *a, int nr, double *coef) {
981        int i;
982        double sum = 0.0;
983        for ( i = nr; i >= 1;  --i ) {
984                sum += a[i]*coef[i]; }
985        return sum; }
986
987int differencing(double *a, int nr, double in) {
988        int i, j, p;
989        double tmp1, tmp2;
990        tmp1 = a[1]; a[1] = in;
991        for ( i = 2; i <= nr; ++i ) {
992                tmp2 = a[i]; a[i] = a[i-1]-tmp1; tmp1 = tmp2; } 
993        return 0; }
994
995
996
997static int CStepCount = 0;
998static int CStepInit = 0;
999static struct tree *initCStree;
1000
1001int CStep(int ord, double h, int nump, 
1002                double parts DECLARR, struct tree *treein) {
1003        double *a, asav, vx, sum, tmp; int p,j, i;
1004static double df DECLSTORMER;
1005/* actual local order to determine how terms to sum */
1006if ( CStepInit == 0 ) {
1007        initCStree = treein;
1008        CStepInit = 1;
1009        for ( i = 1; i <= ord+1; ++i ) {
1010        for ( p = 1; p <= nump; ++p ) {
1011        for ( j = 1; j <= DIM; ++j ) {
1012                df[p][j][i] = 0.0; } } }        }
1013if ( CStepInit == 1 ) {
1014/* initialization stage */
1015                for ( p = 1; p <= nump; ++p ) {
1016                for ( j = 1; j <= DIM; ++j ) {
1017                        df[p][j][0] = -parts[p][j]; } }
1018
1019                if ( csninit == CSNINITHWH ) {
1020                        HWHStep(CSNINITHWHORDER, initCStree, h, parts); 
1021                        ParticlesFromTree(initCStree, parts);  }
1022                else { GBSStep(CSNINITGBSORDER, h, nump, parts); }
1023                if ( CStepCount > ord ) CStepInit = 2;
1024                for ( p = 1; p <= nump; ++p ) {
1025                for ( j = 1; j <= DIM; ++j ) {
1026                        df[p][j][0] += parts[p][j]; } }
1027                }
1028else {
1029/* actual run */
1030CSNerror = 0;
1031for ( p = 1; p <= nump; ++p ) {
1032for ( j = 1; j <= DIM; ++j ) {
1033/* formula: x(n+1) - x(n) = (x(n) - x(n-1)) + h^2*sum
1034        with X(n) = x(n)-x(n-1)
1035        X(n+1) = X(n) + h^2*sum
1036        x(n+1) = X(N+1) + x(n)
1037X(n) is in a[0]
1038*/
1039        a = df[p][j];
1040        asav = a[0];
1041        sum = sumfrombottom(a, ord, CSalpha);
1042        sum *= h*h;
1043        a[0] += sum;
1044        parts[p][j] += a[0];
1045/* vx = (1.0/h)*(x(n)-x(n-1)) + h*sum */
1046        sum = h*sumfrombottom(a, ord, CSbeta);
1047        vx = asav/h + sum;
1048        parts[p][DIM+j] = vx;
1049/* record error */
1050        tmp = fabs(CSalpha[ord]*a[ord]*h*h);
1051        if ( CSNerror < tmp ) CSNerror = tmp;
1052        } } }
1053PlainAccel(nump, parts);
1054for ( p = 1; p <= nump; ++p ) {
1055for ( j = 1; j <= DIM; ++j ) {
1056        differencing(df[p][j], ord, parts[p][ACC+j]);
1057        parts[p][ACC+j] = 0.0;
1058        } }
1059++CStepCount;
1060return 0; }
1061
1062
1063
1064/* +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1065        BULIRSCH-STOER
1066        actually, GRAGG-BULIRSCH-STOER, following Hairer et al., 1993
1067*/
1068
1069/* Gragg's symmetric integrator, this is used to generate Tj,1  */
1070int GStep(int nump, double parts DECLARR, double s DECLARR, double h, int len) {
1071int k, j, p, d, knext, kprev, m;
1072double dif, difsum, tmp1, astep;
1073static double y0 DECLARR;
1074static double y1 DECLARR;
1075/* save originial in y0 */
1076for ( p = 1; p <= nump; ++p ) {
1077                y0[p][GMASSCO] = parts[p][GMASSCO];
1078for ( d = 1; d <= DIM; ++d ) { 
1079                y0[p][d] = parts[p][d]; 
1080                y0[p][d+DIM] = parts[p][d+DIM]; } }
1081/* consult Hairer et al. why we need this
1082        in short: we need a method which has only even powers
1083        of h in the expansion of its error, i.e., a symmetric method
1084 */
1085h = h/2.0;
1086PlainAccel(nump, y0);
1087for ( p = 1; p <= nump; ++p ) {
1088/* load masses into y1 */
1089                y1[p][GMASSCO] = y0[p][GMASSCO];
1090for ( d = 1; d <= DIM; ++d ) { 
1091                y1[p][d] = y0[p][d]+h*y0[p][DIM+d]; 
1092                y1[p][d+DIM] = y0[p][d+DIM]+h*y0[p][ACC+d]; } }
1093/* compute y2 (m=1), y3' (m=2), y4 (m=3), y5' (m=4) y(2n+1)' (m=2*n)
1094        n = len
1095        */
1096for ( m = 1; m <= 2*len; ++m ) {
1097        if ( (m%2) ) {
1098        for ( p = 1; p <= nump; ++p ) {
1099        for ( d = 1; d <= DIM; ++d ) { 
1100                y1[p][d] = y0[p][d]+2.0*h*y1[p][d+DIM]; 
1101                s[p][d] = y1[p][d]; y0[p][d] = y1[p][d]; 
1102                }  } }
1103        else {
1104        PlainAccel(nump, y1);
1105        for ( p = 1; p <= nump; ++p ) {
1106        for ( d = 1; d <= DIM; ++d ) { 
1107                y0[p][d+DIM] = y1[p][d+DIM]; 
1108                y1[p][d+DIM] = y0[p][d+DIM]+2.0*h*y1[p][ACC+d]; 
1109                s[p][d+DIM] = (y0[p][d+DIM]+y1[p][d+DIM])/2.0; 
1110                } } 
1111                }
1112        }
1113return 0; }
1114
1115
1116int GBSStep(int bsord, double h, int nump, double parts DECLARR) {
1117int k, j, jfinal, p, d, jnext, jprev, len;
1118static double gbsarr DECLBS;
1119double dif, difsum, tmp1, astep;
1120int *n = extrapolseq;
1121static double s DECLARR;
1122/* for testing GStep
1123        astep = h; len = 1;
1124        GStep(nump, parts, s, astep, len);
1125for ( p = 1; p <= nump; ++p ) {
1126for ( d = 1; d <= 2*DIM; ++d ) { parts[p][d] = s[p][d]; } }
1127return 0;
1128*/
1129jfinal = bsord;
1130for ( j = 1; j <= bsord; ++j ) {
1131        difsum = 0.0;
1132/* map j and j-1 to storage index */
1133        jnext = ((j)%2); 
1134        jprev = ((j-1)%2);
1135/* compute Tj,1 */
1136        len = n[j];
1137        astep = h/len;
1138        GStep(nump, parts, s, astep, len);
1139        for ( p = 1; p <= nump; ++p ) {
1140        for ( d = 1; d <= 2*DIM; ++d ) { gbsarr[p][d][jnext][1] = s[p][d]; } }
1141/* compute Tj,k */
1142for ( k = 1; k <= j-1; ++k ) {
1143/* Aitken-Neville for nonsymmetric underlying method */
1144        tmp1 = ((double) n[j])/((double) n[j-k]); 
1145/* Aitken-Neville for symmetric underlying method (square of previous) */
1146        tmp1 = tmp1*tmp1;
1147for ( p = 1; p <= nump; ++p ) {
1148for ( d = 1; d <= 2*DIM; ++d ) {
1149        dif = (gbsarr[p][d][jnext][k] - gbsarr[p][d][jprev][k]);
1150        if ( k == j-1 ) difsum += fabs(dif);
1151        gbsarr[p][d][jnext][k+1] = gbsarr[p][d][jnext][k] + dif/(tmp1-1.0);
1152        } }
1153        } 
1154/* stop computing more is reached roundoff level */
1155        if ( j > 1 && difsum <= GBSTOL ) { jfinal = j; break; }
1156        }
1157j = jfinal; k = j; jnext = ((j)%2); 
1158for ( p = 1; p <= nump; ++p ) {
1159for ( d = 1; d <= 2*DIM; ++d ) { parts[p][d] = gbsarr[p][d][jnext][k]; } } 
1160return 0; }
1161       
1162
1163
1164
1165
1166
1167/* >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
1168        Actual  HWH integration */
1169int MoveCMandKepler(struct tree *tree, double *cmove, double stepsize, 
1170                double p DECLARR);
1171int KeplerOnTree(struct tree *tree, double stepsize, double p DECLARR);
1172int RecursivePerturbations(struct tree *tree, double stepsize, double p DECLARR);
1173int RecPertAcc(struct tree *tree, double stepsize, double p DECLARR);
1174
1175/* fourth order coeffs */
1176static double Coeff4x0 = 0.0;
1177static double Coeff4x1 = 0.0;
1178
1179static int SympCalled = 0;
1180
1181void initSympCoefs() {
1182        double cbrt2 = cbrt(2.0);
1183        Coeff4x0 = -cbrt2/(2.0-cbrt2);
1184        Coeff4x1 = 1.0/(2.0-cbrt2);
1185        SympCalled = 1; }
1186       
1187
1188static int HWHCalled = 0;
1189
1190// X
1191int HWHStep(int order, struct tree *tree, double stepsize, double p DECLARR) {
1192        if ( SympCalled == 0 ) { 
1193                initSympCoefs(); 
1194        }
1195        if ( HWHCalled == 0) {
1196                // initialize tree
1197                FillCMinTree(tree, p); 
1198                HWHCalled = 1; 
1199        }
1200        if ( order == 1 ) {
1201                KeplerOnTree(tree, stepsize, p);
1202                RecursivePerturbations(tree, stepsize, p);
1203                return 0; 
1204        }
1205        if ( order == 2 ) {
1206                KeplerOnTree(tree, stepsize/2.0, p);
1207                RecursivePerturbations(tree, stepsize, p);
1208                KeplerOnTree(tree, stepsize/2.0, p); 
1209                return 0; 
1210        }
1211        if ( order == 4 ) {
1212                HWHStep(2, tree, stepsize*Coeff4x1, p);
1213                HWHStep(2, tree, stepsize*Coeff4x0, p);
1214                HWHStep(2, tree, stepsize*Coeff4x1, p);
1215                return 0; 
1216        }
1217        printf(" Order %2d  is not implemented, Aborted", order);
1218        exit(1); 
1219}
1220
1221
1222int SKPStep(int order, double stepsize, int nump, double p DECLARR) {
1223        if ( SympCalled == 0 ) { initSympCoefs(); }
1224        if ( order == 1 ) {
1225                PlainSplitKineticStep(nump, stepsize, p);
1226                PlainSplitPotentialStep(nump, stepsize, p);
1227                return 0; }
1228        if ( order == 2 ) {
1229                PlainSplitKineticStep(nump, stepsize/2.0, p);
1230                PlainSplitPotentialStep(nump, stepsize, p);
1231                PlainSplitKineticStep(nump, stepsize/2.0, p);
1232                return 0; }
1233        if ( order == 4 ) {
1234                SKPStep(2, stepsize*Coeff4x1, nump, p);
1235                SKPStep(2, stepsize*Coeff4x0, nump, p);
1236                SKPStep(2, stepsize*Coeff4x1, nump, p);
1237                return 0; }
1238        printf(" Order %2d  is not implemented, Aborted", order);
1239        exit(1); }
1240
1241
1242
1243/* +++++++++++++++++++++++++++++++++++++++++++++++++++++
1244/       TREE
1245/
1246/
1247*/
1248
1249
1250int inittree(struct tree *tmp) {
1251        int i; tmp->tmass = 0.0; tmp->particleindex = 0;
1252        tmp->p1 = 0; tmp->p2 = 0;
1253        tmp->testparticles = 0; tmp->rm1 = 0; tmp->rm2 = 0;
1254        for ( i = 1; i <= DIM2; ++i ) { tmp->bc[i] = 0; }
1255        for ( i = 1; i <= DIM; ++i ) { tmp->acc[i] = 0; }
1256        return 0; }
1257
1258
1259struct tree *newtree() {
1260        struct tree *tmp = (struct tree*) malloc(sizeof(struct tree)); 
1261        inittree(tmp); return tmp; }
1262
1263void deletetree(struct tree *p) { 
1264        if ( p->p1 ) deletetree(p->p1);
1265        if ( p->p2 ) deletetree(p->p2);
1266        free((void *) p); }
1267
1268
1269struct tree *MakeSimpleTree(int nump) {
1270        int i;
1271        struct tree *root;
1272        struct tree *t1, *p1, *p2;
1273        root = newtree();
1274        t1 = root;
1275        for ( i = nump; i > 1; --i ) {
1276                p1 = newtree();
1277                p2 = newtree();
1278                p1->particleindex = 0;
1279                p2->particleindex = i;
1280                t1->p1 = p1; t1->p2 = p2;
1281                t1 = p1; }
1282        t1->particleindex = 1;
1283        return root; }
1284
1285int eatwhite(FILE *f) {
1286        int c; 
1287        for ( ; ; ) {
1288                c = getc(f);
1289                if ( !isspace(c) ) break; 
1290        }
1291        ungetc(c, f); 
1292        return c;
1293}
1294
1295
1296/* just an array to hold indeces read in,
1297        portions of this are assigned to
1298        tpa's below */
1299int tmpReadList[MAXNBODIES+1000];
1300/* bookkeeping */
1301int freelist = 0;
1302
1303int *ReadList(FILE *f) {
1304        int i1, i2; int c; int i; int *arr;
1305        c = eatwhite(f);
1306        if ( c != '[' ) {
1307                printf(" Error: bad list, aborted \n"); exit(1); 
1308        }
1309        c = getc(f);
1310        fscanf(f, "%d - %d", &i1, &i2);
1311        c = eatwhite(f);
1312        if ( c != ']' ) {
1313                printf(" Error: bad list, aborted \n"); exit(1); 
1314        }
1315        c = getc(f); 
1316        arr = tmpReadList+freelist;
1317       
1318        /* put zero in tpstore */
1319        for ( i = i1; i <= i2; ++i ) tpstore[i] = TESTPARTICLE;
1320        for ( i = i1; i <= i2; ++i ) arr[i-i1+1] = i;
1321       
1322        arr[0] = i2-i1+1;
1323        freelist += i2-i1+2;
1324        return arr; 
1325}
1326       
1327               
1328
1329struct tree *MakeTree(FILE *f) {
1330        int i; int c; int pind; int *tpa;
1331        struct tree *root;
1332        struct tree *p1, *p2;
1333        c = eatwhite(f);
1334        if ( c != '(' ) {
1335                if ( isdigit(c) ) {
1336                        // single particle
1337                        fscanf(f,"%d", &pind);
1338                        root = newtree();
1339                        root->particleindex = pind;
1340                        return root;
1341                } 
1342                // set of test particles
1343                tpa = ReadList(f);
1344                root = newtree();
1345                root->particleindex = tpa[1];
1346                root->testparticles = tpa;
1347                return root;
1348        }
1349        c = getc(f);
1350        root = newtree();
1351        p1 = MakeTree(f);
1352        p2 = MakeTree(f);
1353        root->p1 = p1; root->p2 = p2;
1354        root->particleindex = 0;
1355        c = eatwhite(f);
1356       
1357        if ( c != ')' ) {
1358                printf(" Error: bad tree, aborted \n"); exit(1); 
1359        }
1360        c = getc(f);
1361        return root; 
1362}
1363
1364
1365
1366/* not used now but might come handy */
1367struct tree *getparticleintree(struct tree *tree, int ind) {
1368        struct tree *tmp;
1369        if ( tree->particleindex ) { 
1370                if ( tree->particleindex == ind ) { return tree; }
1371                return 0; }
1372        tmp = getparticleintree(tree->p1, ind);
1373        if ( tmp != 0 ) return tmp;
1374        tmp = getparticleintree(tree->p2, ind);
1375        if ( tmp != 0 ) return tmp;
1376        return 0; }
1377       
1378
1379/* not used now but might come handy */
1380int **GetTestParticles(struct tree *tree, int ind) {
1381        struct tree *tmp = getparticleintree(tree, ind);
1382        if ( tmp == 0 ) return 0;
1383        return &(tmp->testparticles); }
1384       
1385
1386/* +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1387        ACTUAL NUMERICAL STUFF ON TREE
1388*/
1389
1390// X fills in initial data: masses, mass ratios, barycenters
1391int FillCMinTree(struct tree *tree, double p DECLARR) {
1392        int j; double *bc = tree->bc;
1393        int i, ind, tp, num, *tpa = tree->testparticles;
1394       
1395        #pragma omp parallel for private(j) schedule(runtime)
1396        for ( j = 1; j <= DIM; ++j ) 
1397                tree->acc[j] = 0.0;
1398       
1399        tree->tmass = 0.0; tree->rm1 = 0.0; tree->rm2 = 0.0;
1400        #pragma omp parallel for private(j) schedule(runtime)
1401        for ( j = 1; j <= DIM2; ++j ) 
1402                bc[j] = 0.0;
1403               
1404        ind = tree->particleindex;
1405        if ( ind ) {
1406            if ( tpa != 0 ) { 
1407                        num = tpa[0];
1408                        for ( tp = 1; tp <= num; ++tp ) { 
1409                                ind = tpa[tp]; 
1410                                if ( ind < 1 ) 
1411                                        continue;
1412                                #pragma omp parallel for private(i) schedule(runtime)
1413                                for ( i = 1; i <= DIM; ++i ) 
1414                                        p[ind][ACC+i] = 0.0;
1415                                } 
1416                        } 
1417            else { 
1418                        tree->tmass = p[ind][GMASSCO];
1419                        #pragma omp parallel for private(j) schedule(runtime)
1420                        for ( j = 1; j <= 2*DIM; ++j ) 
1421                                bc[j] = p[ind][j]; 
1422                }
1423            return 0; 
1424        } 
1425        FillCMinTree(tree->p1, p);
1426        FillCMinTree(tree->p2, p);
1427        tree->tmass = tree->p1->tmass + tree->p2->tmass;
1428        tree->rm1 = (tree->p1->tmass)/(tree->tmass); 
1429        tree->rm2 = (tree->p2->tmass)/(tree->tmass);
1430       
1431        #pragma omp parallel for private(j) schedule(runtime)
1432        for ( j = 1; j <= DIM2; ++j ) {
1433                bc[j] = tree->p1->bc[j] * tree->rm1 + tree->p2->bc[j] * tree->rm2; 
1434        } 
1435        return 0; 
1436}
1437
1438/* copy from tree into p */
1439int ParticlesFromTree(struct tree *tree, double p DECLARR) {
1440        int j; double *bc = tree->bc;
1441        int i, ind, *tpa = tree->testparticles;
1442        ind = tree->particleindex;
1443        if ( ind ) {
1444           if ( tpa == 0 ) {
1445                tree->tmass = p[ind][GMASSCO];
1446                for ( j = 1; j <= DIM2; ++j ) p[ind][j] = bc[j]; }
1447           return 0; } 
1448        ParticlesFromTree(tree->p1, p);
1449        ParticlesFromTree(tree->p2, p);
1450        return 0; }
1451
1452
1453/* +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1454        KEPLER STEP ON TREE
1455*/
1456
1457/* computes Stumpff functions several ways, depending on x */
1458int StumpffFunctions(double *vals, double x);
1459/* advances the Kepler motion 1/2*v^2 - mu/|x| for time deltat */
1460double KeplerStumpffStep(int dim, double mu, double *xv, double deltat);
1461/* return only the increment in xv */
1462double IncrementKeplerStumpffStep(int dim, double mu, double *xv, double deltat);
1463
1464// X NOTE: this does not CHANGE centers of masses
1465int KeplerOnTree(struct tree *tree, double stepsize, double p DECLARR) { 
1466        // move the center of mass
1467        double cmove[10]; 
1468        int i;
1469        #pragma omp parallel for private(i) schedule(runtime)
1470        for ( i = 1; i <= 2*DIM; ++i ) 
1471                cmove[i] = 0.0;
1472
1473        // move by 1/2*(Tmass)*v^2 */
1474        #pragma omp parallel for private(i) schedule(runtime)
1475        for ( i = 1; i <= DIM; ++i ) 
1476                cmove[i] = (tree->bc[DIM+i])*stepsize; 
1477
1478        MoveCMandKepler(tree, cmove, stepsize, p);
1479        return 0; 
1480}
1481
1482
1483// X Assume that test particles are attached to 2nd
1484int DoTestPartKepler(struct tree *tree, double *cmove, double stepsize, double p DECLARR) {
1485        int i, tp, num, *tpa, ind;
1486        double mu, tmpkep[TMPSIZE], *bc;
1487        tpa = tree->p2->testparticles; 
1488        num = tpa[0];
1489        if ( num > 0 ) {
1490                bc = tree->p1->bc; 
1491                mu = tree->tmass;
1492                for ( tp = 1; tp <= num; ++tp ) { 
1493                        ind = tpa[tp]; 
1494                        if ( ind < 1 ) continue;
1495                        #pragma omp parallel for private(i) schedule(runtime)
1496                        for ( i = 1; i <= DIM2; ++i )   
1497                                tmpkep[i] = p[ind][i] - bc[i];
1498                        IncrementKeplerStumpffStep(DIM, mu, tmpkep, stepsize); 
1499                        for ( i = 1; i <= DIM2; ++i ) 
1500                                p[ind][i] += (cmove[i]+tmpkep[i]);
1501                } 
1502        }
1503        return 0; 
1504}
1505
1506// X move the center of mass and do a Kepler step on relative coordinates, transmit the new "moves" down
1507int MoveCMandKepler(struct tree *tree, double *cmove, double stepsize, double p DECLARR) {
1508        int i, *tpa, ind;
1509        double tmp[TMPSIZE], tmpkep[TMPSIZE], *xv1, *xv2, mu;
1510        double *bc = tree->bc;
1511       
1512        for ( i = 1; i <= DIM2; ++i ) { 
1513                bc[i] += cmove[i]; 
1514        }
1515        // do nothing else for actual particles
1516        if ( tree->particleindex ) { 
1517                return 0; 
1518        }
1519        if ( tree->p2->testparticles ) {       
1520                // this is a pair where p2 is an array of test particles
1521                DoTestPartKepler(tree, cmove, stepsize, p); 
1522                MoveCMandKepler(tree->p1, cmove, stepsize, p); 
1523                return 0; 
1524        }
1525        mu = tree->tmass;
1526        xv1 = tree->p1->bc; 
1527        xv2 = tree->p2->bc;
1528        #pragma omp parallel for private(i) schedule(runtime)
1529        for ( i = 1; i <= DIM2; ++i ) { 
1530                tmpkep[i] = xv2[i]-xv1[i]; 
1531        }
1532        // do Kepler step on tmpkep, we get back the increment
1533        IncrementKeplerStumpffStep(DIM, mu, tmpkep, stepsize); 
1534        #pragma omp parallel for private(i) schedule(runtime)
1535        // use only the relative change
1536        for ( i = 1; i <= DIM2; ++i ) { 
1537                tmp[i] = cmove[i] - tmpkep[i]*(tree->rm2); 
1538        }
1539        MoveCMandKepler(tree->p1, tmp, stepsize, p);
1540        #pragma omp parallel for private(i) schedule(runtime)
1541        for ( i = 1; i <= DIM2; ++i ) { 
1542                tmp[i] = cmove[i] + tmpkep[i]*(tree->rm1); 
1543        }
1544        MoveCMandKepler(tree->p2, tmp, stepsize, p);
1545        return 0; 
1546}
1547       
1548/*  ++++++++++++++++++++++++++++++++++++++++++++++++++++++
1549        PERTURBATION ACCELERATIONS
1550*/
1551
1552/* X simple acceleration */
1553int Accel(double *x1, double *x2, double *acc) {
1554        double tmp[10], r = 0.0; int i; 
1555        for ( i = 1; i <= DIM; ++i ) {
1556                tmp[i] = x1[i]-x2[i];
1557                r = r + tmp[i]*tmp[i]; 
1558        }
1559        r = sqrt(r);
1560        r = r*r*r;
1561        for ( i = 1; i <= DIM; ++i ) { 
1562                acc[i] = -tmp[i]/r; 
1563        }
1564        return 0; 
1565}
1566
1567int AdvanceTestParticleVel(struct tree *tree, double stepsize, double p DECLARR) {
1568                int i, ind, tp, num, *tpa = tree->testparticles;
1569                if ( tpa == 0 ) return 0; num = tpa[0];
1570        for ( tp = 1; tp <= num; ++tp ) {
1571                ind = tpa[tp]; if ( ind < 1 ) continue;
1572                for ( i = 1; i <= DIM; ++i ) {
1573                        p[ind][DIM+i] += stepsize*p[ind][ACC+i]; 
1574                        p[ind][ACC+i] = 0.0; }
1575                        } 
1576        return 0; }
1577                       
1578       
1579/* +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1580        PERTURBATIONS ON TREE
1581*/
1582// X
1583int RecursivePerturbations(struct tree *tree, double stepsize, double p DECLARR) {
1584        int j, ind; double *bc = tree->bc;
1585        /* advance attached test particles
1586        if ( tree->particleindex ) {
1587        /* if particle we are ready to advance v */
1588        if ( tree->testparticles ) {
1589                AdvanceTestParticleVel(tree, stepsize, p); 
1590                return 0; 
1591        }
1592        // this is a nonzero mass actual particle, advance v
1593                for ( j = 1; j <= DIM; ++j ) {
1594                        bc[DIM+j] += stepsize*tree->acc[j]; 
1595                        tree->acc[j] = 0.0; 
1596                }
1597                return 0; 
1598       
1599        /* if not, first we take care of accs. for pairs with the two parts 
1600                IMPORTANT TO DO THIS FIRST
1601        */
1602        RecPertAcc(tree, stepsize, p);
1603        // now descend and do the same for both parts
1604        RecursivePerturbations(tree->p2, stepsize, p);
1605        RecursivePerturbations(tree->p1, stepsize, p);
1606        /* now regenerate center of mass etc. */
1607        if ( tree->p2->testparticles ) {
1608                #pragma omp parallel private(j)
1609                #pragma omp for schedule(runtime)
1610                for ( j = 1; j <= DIM2; ++j ) { 
1611                        bc[j] = tree->p1->bc[j]; 
1612                }
1613                return 0; 
1614        }
1615       
1616        #pragma omp parallel private(j)
1617        #pragma omp for schedule(runtime)
1618        for ( j = 1; j <= DIM2; ++j ) {
1619                bc[j] = tree->p1->bc[j] * tree->rm1 + tree->p2->bc[j] * tree->rm2; 
1620        }
1621        return 0; 
1622}
1623
1624/* assume test particles in tree2 for dir > 1, reverse signs for dir < 0 */
1625int TestParticleAcc(int dir, double *cacc, struct tree *tree1, struct tree *tree2, 
1626                                double p DECLARR) {
1627                int i, ind, tp, num, *tpa = tree2->testparticles;
1628                double acc[TMPSIZE];
1629                double *bc = tree1->bc;
1630                double tmp, gm1 = tree1->tmass;
1631                num = tpa[0];
1632        for ( tp = 1; tp <= num; ++tp ) {
1633                ind = tpa[tp]; if ( ind < 1 ) continue;
1634/* rewrite this to avoid bad numerical attitude */
1635                        Accel(bc, &p[ind][0], acc);
1636                for ( i = 1; i <= DIM; ++i ) {
1637/* since test particles are in 2nd */
1638                        tmp = acc[i]-cacc[i]*dir;
1639                        p[ind][ACC+i] += -gm1*tmp; }
1640                        } 
1641        return 0; }
1642                       
1643
1644/* X c1 and c2 are for possible future use */
1645int RecPertAccSub(struct tree *tree1, 
1646        struct tree *tree2, double *cacc, double *c1, double *c2, double p DECLARR) {
1647        /* do the work if both are particles */
1648        int i, ind1, ind2; double acc[TMPSIZE];
1649        double tmp, gm1 = tree1->tmass, gm2 = tree2->tmass;
1650        ind1 = tree1->particleindex;
1651        ind2 = tree2->particleindex;
1652        if ( ind1 && ind2 ) {
1653                /* do nothing if both are test particle arrays */
1654                if ( tree1->testparticles  && tree2->testparticles  ) 
1655                        return 0; 
1656                /* test particles first */
1657                if ( tree2->testparticles ) {
1658                        TestParticleAcc(1, cacc, tree1, tree2, p); 
1659                        return 0; 
1660                }
1661                if ( tree1->testparticles ) {
1662                        TestParticleAcc( -1, cacc, tree2, tree1, p); 
1663                        return 0; 
1664                }
1665                /* if actual particles with nonzero mass */
1666                Accel(tree1->bc, tree2->bc, acc);
1667                for ( i = 1; i <= DIM; ++i ) {
1668                        /* rewrite this to avoid bad numerical attitude */
1669                        tmp = acc[i]-cacc[i];
1670                        tree1->acc[i] += gm2*tmp; 
1671                        tree2->acc[i] += -gm1*tmp; 
1672                }
1673                return 0; 
1674        }
1675        /* if not, descend but only for one branch, the other is taken care in turn */
1676        if ( !ind1 ) {
1677                RecPertAccSub(tree1->p2, tree2, cacc, c1, c2, p);
1678                RecPertAccSub(tree1->p1, tree2, cacc, c1, c2, p);
1679                return 0; 
1680        }
1681        RecPertAccSub(tree1, tree2->p1, cacc, c1, c2, p);
1682        RecPertAccSub(tree1, tree2->p2, cacc, c1, c2, p);
1683        return 0; 
1684}
1685
1686/* assume test particles in tree2 */
1687int TestParticleAccSpec(double *cm, struct tree *tree1, struct tree *tree2, double p DECLARR) {
1688        int i, ind, tp, num, *tpa = tree2->testparticles;
1689        double acc[TMPSIZE], cacc[TMPSIZE];
1690        double *bc = tree1->bc;
1691        double tmp, gm1 = tree1->tmass;
1692        num = tpa[0];
1693        for ( tp = 1; tp <= num; ++tp ) {
1694                ind = tpa[tp]; if ( ind < 1 ) continue;
1695                /* rewrite this to avoid bad numerical attitude */
1696                Accel(bc, &p[ind][0], acc);
1697                Accel(cm, &p[ind][0], cacc);
1698                for ( i = 1; i <= DIM; ++i ) {
1699                        /* since test particles are in 2nd */
1700                        tmp = acc[i]-cacc[i];
1701                        p[ind][ACC+i] += -gm1*tmp; 
1702                }
1703        } 
1704        return 0; 
1705}
1706                       
1707
1708/* c1 and c2 are for possible future use */
1709int RecPertAccSubTest(double *cm,  struct tree *tree1, struct tree *tree2, double p DECLARR) {
1710        int ind1;
1711        ind1 = tree1->particleindex;
1712        if ( ind1 ) {
1713                /* do nothing if tree1 has only test particles */
1714                if ( tree1->testparticles ) 
1715                        return 0;
1716                TestParticleAccSpec(cm, tree1, tree2, p); 
1717                return 0; 
1718        }
1719        /* if not, descend for both branches */
1720        RecPertAccSubTest(cm, tree1->p1, tree2, p);
1721        RecPertAccSubTest(cm, tree1->p2, tree2, p);
1722        return 0; 
1723}
1724
1725
1726int RecPertAcc(struct tree *tree, double stepsize, double p DECLARR) {
1727        double cacc[TMPSIZE];
1728        /* do nothing if all in p2 are attached to p1: pure Kepler motion */
1729        if ( tree->p1->particleindex &&  tree->p2->particleindex  ) 
1730                return 0; 
1731        if ( tree->p2->testparticles == 0 ) {
1732                /* actual nonzero masses in p2 */
1733                Accel(tree->p1->bc, tree->p2->bc, cacc);
1734               
1735                RecPertAccSub(tree->p1, tree->p2, cacc, tree->p1->bc, tree->p2->bc, p); 
1736                return 0;
1737        }
1738        /* test particles in p2 */
1739        RecPertAccSubTest(tree->p1->bc, tree->p1, tree->p2, p); 
1740        return 0; 
1741}
1742
1743
1744
1745
1746/*
1747// ===============================================================================
1748//
1749//   KEPLER MOTION USING STUMPFF FUCNTIONS
1750//      FOLLOWING DANBY, 1992
1751//      (quartic Newtonian solver, Stumpff function definitions,
1752//       f and g functions)
1753//      see also: Stiefel and Sheifele, Linear and Regular
1754//      Celestial Mechanics
1755//
1756// ===============================================================================
1757
1758// Stumpff functions (c0, c1, c2, c3)
1759*/
1760
1761/* CHANGE these numbers to tune accuracy */
1762/* this one can be changed runtime */
1763static double StumpffQSTol = KEPLERSOLVERTOLERANCE;
1764
1765/* the limit for which trig and hyp. trig functions are used */
1766#define STUMPFFPLAINLIM (5.0)
1767/* reduction limit */
1768#define STUMPFFRED (1.0/32.0)
1769#define STUMPFFSHORTTOL (1.0e-6)
1770
1771/* this one is not used now */
1772static double StumpffSerTol = 1.0e-25;
1773
1774static int StumpffErrorCount = 0;
1775static int StumpffErrorCountLimit = 100;
1776
1777CheckStumpffErrorCount() {
1778        ++StumpffErrorCount;
1779        if ( StumpffErrorCount > StumpffErrorCountLimit ) {
1780                fprintf(FLOG, "\n Too many errors, aborted \n");
1781                fflush(FLOG);
1782                exit(1); 
1783        }
1784        return 0; 
1785}
1786
1787double util_fact(int k) {
1788        int i; double fact = 1;
1789        for ( i = 1; i <= k; ++i ) {
1790                fact *= i; 
1791        }
1792        return fact; 
1793}
1794
1795/*
1796In case we need the coefficients, produced by Mathematica
1797static double c2_0 = 0.5;
1798static double c2_1 = -1.0/24;
1799static double c2_2 = 1.0/720;
1800static double c2_3 = -1.0/40320;
1801static double c2_4 = 1.0/3628800;
1802static double c2_5 = -1.0/479001600;
1803static double c2_6 = 1.0/87178291200;
1804static double c2_7 = -1.0/20922789888000;
1805static double c2_8 = 1.0/6402373705728000;
1806static double c2_9 = -1.0/2432902008176640000;                         
1807static double c2_10 = 1.0/1124000727777607680000;
1808
1809static double c3_0 = 1.0/6;
1810static double c3_1 = -1.0/120;
1811static double c3_2 = 1.0/5040;
1812static double c3_3 = -1.0/362880;
1813static double c3_4 = 1.0/39916800;
1814static double c3_5 = -1.0/6227020800;
1815static double c3_6 = 1.0/1307674368000;
1816static double c3_7 = -1.0/355687428096000;
1817static double c3_8 = 1.0/121645100408832000;
1818static double c3_9 = -1.0/51090942171709440000;
1819static double c3_10 = 1.0/25852016738884976640000;
1820*/
1821
1822
1823/* This is what is actually used, generated by factoring the coefficients */
1824double HornerStumpff2(double x) {
1825        return
1826(1.0 - x*(1 - x*(1 - x*(1 - x*(1 - x *(1 - x
1827/* up to degree 10, if you need it
1828*(1 - x*(1 - x*(1 - (1 - x/462)*x/380)/306)/240)        */
1829/182)/132)/90)/56)/30)/12)/2;
1830        }
1831
1832double HornerStumpff3(double x) {
1833        return
1834 (1.0 - x*(1 - x*(1 - x*(1 - x*(1 - x*(1 - x
1835/* up to degree 10, if you need it
1836*(1 - x*(1 - x*(1 - (1 - x/506)*x/420)/342)/272)        */
1837/210)/156)/110)/72)/42)/20)/6;
1838        }
1839
1840
1841/* first 4 terms, ie., up to x^3 */
1842double Stumpff2Short(double x) {
1843        return (1.0 - x*(1 - x*(1 - x/56)/30)/12)/2;
1844        }
1845
1846double Stumpff3Short(double x) {
1847        return (1.0 - x*(1 - x*(1 - x/72)/42)/20)/6;
1848        }
1849
1850
1851
1852
1853// Original for reference
1854double StumpffSeriesCk(int k, double x) {
1855        int i;
1856        double term, sum = 0.0;
1857        double terms[100], num;
1858        term = 1.0;
1859        for ( i = 1; i <= 50; ++i ) {
1860                num = (k+2*i-1)*(k+2*i);
1861                term = term*(-x/num);
1862                terms[i] = term;
1863                if ( fabs(term) < StumpffSerTol ) break; 
1864                }
1865        for ( ; i > 0; --i )  sum += terms[i];
1866        sum = (1.0+sum)/util_fact(k);
1867        return sum; }
1868
1869// X
1870int StumpffFunctions(double *c, double x) {
1871        double x2, tmp;
1872        int i;
1873/* What this is supposed to be
1874c[0] = StumpffSeriesCk(0, x);
1875c[1] = StumpffSeriesCk(1, x);
1876c[2] = StumpffSeriesCk(2, x);
1877c[3] = StumpffSeriesCk(3, x);
1878return 0;
1879*/
1880/* FOR REFERENCE, THIS COULD BE USED FOR LARGE VALUES, MAYBE
1881        if ( x > STUMPFFPLAINLIM ) {
1882                x2 = sqrt(x);
1883                c[0] = cos(x2);
1884                c[1] = sin(x2)/x2;
1885                c[2] = (1.0-cos(x2))/x;
1886                c[3] = (x2-sin(x2))/(x*x2);
1887                return 0; }
1888        if ( x < -STUMPFFPLAINLIM ) {
1889                x2 = sqrt(-x);
1890                c[0] = cosh(x2);
1891                c[1] = sinh(x2)/x2;
1892                c[2] = (cosh(x2)-1.0)/x;
1893                c[3] = (sinh(x2)-x2)/(x*x2);
1894                return 0; }
1895*/
1896        if ( fabs(x) < STUMPFFSHORTTOL ) {
1897                c[2] = Stumpff2Short(x); 
1898                c[3] = Stumpff3Short(x);
1899                c[1] = 1.0 - x*c[3]; 
1900                c[0] = 1.0 - x*c[2];
1901                return 0;
1902        }
1903        if ( fabs(x) < STUMPFFRED ) {
1904                c[2] = HornerStumpff2(x); 
1905                c[3] = HornerStumpff3(x); 
1906                c[1] = 1.0 - x*c[3]; 
1907                c[0] = 1.0 - x*c[2];
1908                return 0;
1909        }
1910        // reduce value to small
1911        x2 = x;
1912        for ( i = 1; i <= 20; ++i ) {
1913                x2 = x2/4.0;
1914                if ( fabs(x2) < STUMPFFRED ) 
1915                        break; 
1916        }
1917        if ( i > 20 ) {
1918                fprintf(FLOG, "%s"," StumpfReduction No Good \n"); 
1919                CheckStumpffErrorCount();
1920                fflush(FLOG);
1921                return 0; 
1922        } 
1923        /* original
1924                c[2] = StumpffSeriesCk(2, x2);
1925                c[3] = StumpffSeriesCk(3, x2);
1926        */
1927        c[2] = HornerStumpff2(x2);
1928        c[3] = HornerStumpff3(x2);
1929        c[1] = 1.0 - x2*c[3];
1930        c[0] = 1.0 - x2*c[2];
1931        for ( ; i > 0; --i ) {
1932                tmp = c[2];
1933                c[2] = (c[1]*c[1])/2.0; 
1934                c[3] = (tmp+c[0]*c[3])/4.0; 
1935                c[1] = c[0]*c[1];
1936                c[0] = (2.0*c[0]*c[0])-1.0;
1937        }
1938        return i; 
1939}
1940
1941
1942/* ===============================================================================
1943*/
1944
1945/* NOTE:
1946It seems that there might be a systematic error here, not sure yet.
1947The Newton solvers tend to end up systematically on a preferred side
1948of the solution.  E.g., for x^2: if starting from above the solution,
1949it ends at a slightly larger value than the solution.  Starting from
1950 below, in the first step it will get to the previous case.  Since
1951the solution cannot be computed to machine precision, we could have
1952a systematic error.  Since the orbit spends more time near apocenter
1953than near pericenter, this seems to lead an increase in energy and
1954decrease in angular momentum.  Perhaps this could be randomized with
1955an additional bisection at the end?
1956*/
1957// X
1958double QuarticStumpffSolverIterator(double mu, double alpha, double s, 
1959                double r0, double u, double deltat) {
1960        double c[5];
1961        double d0, d1, d2, d3, s2, ssav = s, tmp1, tmp2, tmp3;
1962        double delta1, delta2, delta3;
1963        int i;
1964        for ( i = 1; i <= 20; ++i ) {
1965                s2 = s*s;
1966                StumpffFunctions(c, alpha*s2);
1967                tmp1 = s*c[1];
1968                tmp2 = s2*c[2];
1969                tmp3 = -r0*alpha+mu;
1970                d0 = u*tmp2+mu*s2*s*c[3];
1971                d0 += r0*tmp1;
1972                d0 -= deltat; 
1973                d1 = u*tmp1+mu*tmp2; 
1974                d1 += r0*c[0];
1975                d2 = u*c[0]+tmp3*tmp1; 
1976                d3 = tmp3*c[0]-u*alpha*c[1]; 
1977                delta1 = -d0/d1;
1978                delta2 = -d0/(d1+delta1*d2/2);
1979                delta3 = -d0/(d1+delta1*d2/2+delta2*delta2*d3/6);
1980                ssav= s;
1981                s = s + delta3;
1982                if ( fabs(delta3/s) < StumpffQSTol ) return s; 
1983        }
1984        fprintf(FLOG, "%s"," QUARTIC STUMPFF SOLVER NOT CONVERGENT? \n"); 
1985        fprintf(FLOG, "    s = %.16e \n", s); 
1986        fprintf(FLOG, "sprev = %.16e \n", ssav); 
1987        fprintf(FLOG, "delta3/s = %.16e \n", delta3/s); 
1988        fprintf(FLOG, "Stumpff x = %.16e \n", alpha*s2); 
1989        fprintf(FLOG, "deltas = %.16e ", delta1); fprintf(FLOG, "%.16e ", delta2); 
1990        fprintf(FLOG, "%.16e \n", delta3);
1991        CheckStumpffErrorCount();
1992        fflush(FLOG);
1993        return s; 
1994}
1995
1996// X
1997double QuarticStumpffSolver(double mu, double alpha, double s, double r0, double u, double deltat) {
1998        /* initial guess */
1999        double tmp = deltat/r0;
2000        double s0 = tmp;
2001        double r0dot = u/r0;
2002        tmp = tmp*tmp;
2003        s0 -= 0.5*r0dot*tmp;
2004        /* third order in deltat */
2005        tmp = tmp*deltat/r0;
2006        s0 += (0.5*r0dot*r0dot-1/6*(mu-alpha*r0)/r0)*tmp;
2007        s = QuarticStumpffSolverIterator( mu, alpha, s0, r0, u, deltat);
2008        return s; 
2009}
2010
2011/* ===============================================================================
2012*/
2013// X
2014void IncfgAndDerivsStumpff(double *vals, double mu, double alpha, double s, 
2015                double r0, double u, double deltat) {
2016        double r, s2, tmp1, tmp2, tmp3;
2017        double c[5];
2018        s = QuarticStumpffSolver( mu, alpha, s, r0, u, deltat);
2019        s2 = s*s;
2020        StumpffFunctions(c, alpha*s2);
2021        //recompute c[1] from angular mom.
2022        tmp1 = s*c[1];
2023        tmp2 = s2*c[2];
2024        tmp3 = u*tmp1+mu*tmp2;
2025        /* r */
2026        r = tmp3+(r0*c[0]);
2027        vals[0] = r;
2028        tmp3 = -mu*tmp2;
2029        /* f */
2030        vals[1] = tmp3/r0;
2031        /* g, this is usually bad in terms of roundoff */
2032        vals[2] = r0*tmp1+u*tmp2;
2033        /* derivs */
2034        vals[3] = -(mu*tmp1)/(r0*r);
2035        vals[4] =  tmp3/r;
2036        return; 
2037}
2038
2039void fgAndDerivsStumpff(double *vals, double mu, double alpha, double s, 
2040                double r0, double u, double deltat) {
2041        IncfgAndDerivsStumpff(vals, mu, alpha, s, r0, u, deltat);
2042        vals[1] += 1.0;
2043        vals[4] += 1.0;
2044        return; }
2045
2046double KeplerStumpffStep(int dim, double mu, double *xv, double deltat) {
2047        double fgvals[5], tmp;
2048        double *x = xv;
2049        double *v = xv+dim;
2050        double v02 = 0.0, u = 0.0, r0 = 0.0;
2051        double r0dot, alpha;
2052        int i;
2053        for ( i = 1; i <= dim; ++i ) {
2054                v02 += v[i]*v[i];
2055                u += x[i]*v[i];
2056                r0 += x[i]*x[i]; }
2057        r0 = sqrt(r0);
2058        r0dot = u/r0;
2059/* a = 1.0/(2.0/r0 - v02/mu); alpha = mu/a; */
2060        alpha = 2.0*mu/r0 - v02;
2061        fgAndDerivsStumpff(fgvals, mu, alpha, 0.0, r0, u, deltat);
2062/* now update */
2063        for ( i = 1; i <= dim; ++i ) {
2064                tmp = x[i];
2065                x[i] = fgvals[1]*x[i]+fgvals[2]*v[i];
2066                v[i] = fgvals[3]*tmp+fgvals[4]*v[i]; }
2067        return 0.0; }
2068
2069
2070// X return only the increment
2071double IncrementKeplerStumpffStep(int dim, double mu, double *xv, double deltat) {
2072        double fg[5], tmp, tmp2;
2073        double *x = xv;
2074        double *v = xv+dim;
2075        double alpha, v02 = 0.0, u = 0.0, r02 = 0.0, r0;
2076        int i, ii;
2077        double tmp1, r, delta1;
2078        for ( i = 1; i <= dim; ++i ) {
2079                v02 += v[i]*v[i];
2080                u += x[i]*v[i];
2081                r02 += x[i]*x[i]; 
2082        }
2083        r0 = sqrt(r02);
2084        /* a = 1.0/(2.0/r0 - v02/mu); alpha = mu/a; */
2085        alpha = 2.0*mu/r0 - v02;
2086        IncfgAndDerivsStumpff(fg, mu, alpha, 0.0, r0, u, deltat);
2087
2088        // using fg'-gf' = 1 ( ' = dot) or total energy do not help much
2089               
2090        // now update
2091        for ( i = 1; i <= dim; ++i ) {
2092                tmp = x[i];
2093                x[i] = fg[1]*x[i]+fg[2]*v[i]; 
2094                v[i] = fg[3]*tmp+fg[4]*v[i]; 
2095        }
2096        return 0.0; 
2097}
2098
2099/* FOR TEST
2100double IncrementKeplerStumpffStep(int dim, double mu, double *xv, double deltat) {
2101        double tmp[7], tmp2[7];
2102        int i;
2103        for ( i = 1; i <= 2*dim; ++i ) { tmp[i] = tmp2[i] = xv[i]; }
2104IncrementKeplerStumpffStepSub(dim, mu, tmp, deltat);
2105        for ( i = 1; i <= 2*dim; ++i ) tmp2[i] += tmp[i];
2106IncrementKeplerStumpffStepSub(dim, mu, tmp2, -deltat);
2107        for ( i = 1; i <= 2*dim; ++i ) xv[i] = (tmp[i]-tmp2[i])/2.0;
2108        return 0.0; }
2109*/
2110
2111       
2112
2113
Note: See TracBrowser for help on using the repository browser.