1 | /****************************************************************************/ |
---|
2 | /* TREECODE.C: new hierarchical N-body code. */ |
---|
3 | /* Copyright (c) 2001 by Joshua E. Barnes, Honolulu, Hawai`i. */ |
---|
4 | /****************************************************************************/ |
---|
5 | |
---|
6 | #include "stdinc.h" |
---|
7 | #include "mathfns.h" |
---|
8 | #include "vectmath.h" |
---|
9 | #include "getparam.h" |
---|
10 | #define global /* don't default to extern */ |
---|
11 | #include "treecode.h" |
---|
12 | |
---|
13 | /* |
---|
14 | * Default values for input parameters. |
---|
15 | */ |
---|
16 | |
---|
17 | string defv[] = { ";Hierarchical N-body code " |
---|
18 | #if defined(QUICKSCAN) |
---|
19 | "(quick scan)", |
---|
20 | #else |
---|
21 | "(theta scan)", |
---|
22 | #endif |
---|
23 | "in=", ";Input file with initial conditions", |
---|
24 | "out=", ";Output file of N-body frames", |
---|
25 | #if defined(USEFREQ) |
---|
26 | "freq=32.0", ";Leapfrog integration frequency", |
---|
27 | #else |
---|
28 | "dtime=1/32", ";Leapfrog integration timestep", |
---|
29 | #endif |
---|
30 | "eps=0.025", ";Density smoothing length", |
---|
31 | #if !defined(QUICKSCAN) |
---|
32 | "theta=1.0", ";Force accuracy parameter", |
---|
33 | #endif |
---|
34 | "usequad=false", ";If true, use quad moments", |
---|
35 | "options=", ";Various control options", |
---|
36 | "tstop=2.0", ";Time to stop integration", |
---|
37 | #if defined(USEFREQ) |
---|
38 | "freqout=4.0", ";Data output frequency", |
---|
39 | #else |
---|
40 | "dtout=1/4", ";Data output timestep", |
---|
41 | #endif |
---|
42 | "nbody=4096", ";Number of bodies for test run", |
---|
43 | "seed=123", ";Random number seed for test run", |
---|
44 | "save=", ";Write state file as code runs", |
---|
45 | "restore=", ";Continue run from state file", |
---|
46 | "VERSION=1.4", ";Joshua Barnes February 21 2001", |
---|
47 | NULL, |
---|
48 | }; |
---|
49 | |
---|
50 | /* Prototypes for local procedures. */ |
---|
51 | |
---|
52 | local void treeforce(void); /* do force calculation */ |
---|
53 | local void stepsystem(void); /* advance by one time-step */ |
---|
54 | local void startrun(void); /* initialize system state */ |
---|
55 | local void testdata(void); /* generate test data */ |
---|
56 | |
---|
57 | /* |
---|
58 | * MAIN: toplevel routine for hierarchical N-body code. |
---|
59 | */ |
---|
60 | |
---|
61 | int main(int argc, string argv[]) |
---|
62 | { |
---|
63 | initparam(argv, defv); /* initialize param access */ |
---|
64 | headline = defv[0] + 1; /* skip ";" in headline */ |
---|
65 | startrun(); /* get params & input data */ |
---|
66 | startoutput(); /* activate output code */ |
---|
67 | if (nstep == 0) { /* if data just initialized */ |
---|
68 | treeforce(); /* do complete calculation */ |
---|
69 | //output(); /* and report diagnostics */ |
---|
70 | //write_diagnostics(nstep); |
---|
71 | } |
---|
72 | #if defined(USEFREQ) |
---|
73 | if (freq != 0.0) /* if time steps requested */ |
---|
74 | while (tstop - tnow > 0.01/freq) { /* while not past tstop */ |
---|
75 | stepsystem(); /* advance step by step */ |
---|
76 | //output(); /* and output results */ |
---|
77 | } |
---|
78 | #else |
---|
79 | if (dtime != 0.0) /* if time steps requested */ |
---|
80 | while (tstop - tnow > 0.01 * dtime) { /* while not past tstop */ |
---|
81 | stepsystem(); /* advance step by step */ |
---|
82 | //output(); /* and output results */ |
---|
83 | //write_diagnostics(nstep); |
---|
84 | } |
---|
85 | #endif |
---|
86 | return (0); /* end with proper status */ |
---|
87 | } |
---|
88 | |
---|
89 | /* |
---|
90 | * TREEFORCE: common parts of force calculation. |
---|
91 | */ |
---|
92 | void write_diagnostics(int step) |
---|
93 | { |
---|
94 | double ekin = 0; // kinetic energy of the n-body system |
---|
95 | bodyptr p; |
---|
96 | int i = 0; |
---|
97 | double epot = 0; |
---|
98 | //epot = Phi(root); |
---|
99 | for (p = bodytab; p < bodytab+nbody; p++) |
---|
100 | { |
---|
101 | if(Type(p) == BODY) |
---|
102 | { |
---|
103 | for (i = 0; i < NDIM ; i++) |
---|
104 | ekin += 0.5 * Mass(p) * Vel(p)[i] * Vel(p)[i]; |
---|
105 | //fprintf(stdout, "ekin = %f\n", ekin); |
---|
106 | epot += Phi(p); |
---|
107 | } |
---|
108 | } |
---|
109 | double etot = ekin + epot; // total energy of the n-body system |
---|
110 | |
---|
111 | if (step == 0) // at first pass, pass the initial |
---|
112 | einit = etot; // energy back to the calling function |
---|
113 | |
---|
114 | fprintf(stdout, "At time t = %f , after %d steps :\n",dtime, step ); |
---|
115 | fprintf(stdout,"Einit = %f\n", einit); |
---|
116 | fprintf(stdout,"E_kin = %f\n", ekin); |
---|
117 | fprintf(stdout,"E_pot = %f\n ", epot); |
---|
118 | fprintf(stdout,"E_tot = %f\n", etot); |
---|
119 | fprintf(stdout,"Absolute energy error: E_tot - E_init = %f\n",etot - einit); |
---|
120 | fprintf(stdout, "Relative energy error: (E_tot - E_init) / E_init = %f\n", (etot - einit) / einit); |
---|
121 | } |
---|
122 | local void treeforce(void) |
---|
123 | { |
---|
124 | bodyptr p; |
---|
125 | |
---|
126 | for (p = bodytab; p < bodytab+nbody; p++) /* loop over all bodies */ |
---|
127 | Update(p) = TRUE; /* update all forces */ |
---|
128 | maketree(bodytab, nbody); /* construct tree structure */ |
---|
129 | gravcalc(); /* compute initial forces */ |
---|
130 | forcereport(); /* print force statistics */ |
---|
131 | } |
---|
132 | |
---|
133 | /* |
---|
134 | * STEPSYSTEM: advance N-body system using simple leap-frog. |
---|
135 | */ |
---|
136 | |
---|
137 | local void stepsystem(void) |
---|
138 | { |
---|
139 | #if defined(USEFREQ) |
---|
140 | real dtime = 1.0 / freq; /* set basic time-step */ |
---|
141 | #endif |
---|
142 | bodyptr p; |
---|
143 | |
---|
144 | for (p = bodytab; p < bodytab+nbody; p++) { /* loop over all bodies */ |
---|
145 | ADDMULVS(Vel(p), Acc(p), 0.5 * dtime); /* advance v by 1/2 step */ |
---|
146 | ADDMULVS(Pos(p), Vel(p), dtime); /* advance r by 1 step */ |
---|
147 | } |
---|
148 | treeforce(); /* perform force calc. */ |
---|
149 | for (p = bodytab; p < bodytab+nbody; p++) { /* loop over all bodies */ |
---|
150 | ADDMULVS(Vel(p), Acc(p), 0.5 * dtime); /* advance v by 1/2 step */ |
---|
151 | } |
---|
152 | nstep++; /* count another time step */ |
---|
153 | tnow = tnow + dtime; /* finally, advance time */ |
---|
154 | } |
---|
155 | |
---|
156 | /* |
---|
157 | * STARTRUN: startup hierarchical N-body code. |
---|
158 | */ |
---|
159 | |
---|
160 | local void startrun(void) |
---|
161 | { |
---|
162 | #if !defined(USEFREQ) |
---|
163 | double dt1, dt2; |
---|
164 | #endif |
---|
165 | |
---|
166 | infile = getparam("in"); /* set I/O file names */ |
---|
167 | outfile = getparam("out"); |
---|
168 | savefile = getparam("save"); |
---|
169 | if (strnull(getparam("restore"))) { /* if starting a new run */ |
---|
170 | eps = getdparam("eps"); /* get input parameters */ |
---|
171 | #if defined(USEFREQ) |
---|
172 | freq = getdparam("freq"); |
---|
173 | #else |
---|
174 | dtime = (sscanf(getparam("dtime"), "%lf/%lf", &dt1, &dt2) == 2 ? |
---|
175 | dt1 / dt2 : getdparam("dtime")); |
---|
176 | #endif |
---|
177 | #if !defined(QUICKSCAN) |
---|
178 | theta = getdparam("theta"); |
---|
179 | #endif |
---|
180 | usequad = getbparam("usequad"); |
---|
181 | tstop = getdparam("tstop"); |
---|
182 | #if defined(USEFREQ) |
---|
183 | freqout = getdparam("freqout"); |
---|
184 | #else |
---|
185 | dtout = (sscanf(getparam("dtout"), "%lf/%lf", &dt1, &dt2) == 2 ? |
---|
186 | dt1 / dt2 : getdparam("dtout")); |
---|
187 | #endif |
---|
188 | options = getparam("options"); |
---|
189 | if (! strnull(infile)) /* if data file was given */ |
---|
190 | inputdata_2(); /* then read inital data */ |
---|
191 | else { /* else make initial data */ |
---|
192 | nbody = getiparam("nbody"); /* get number of bodies */ |
---|
193 | if (nbody < 1) /* check for silly values */ |
---|
194 | error("startrun: absurd value for nbody\n"); |
---|
195 | srandom(getiparam("seed")); /* set random number gen. */ |
---|
196 | testdata(); /* and make plummer model */ |
---|
197 | tnow = 0.0; /* reset elapsed model time */ |
---|
198 | } |
---|
199 | rsize = 1.0; /* start root w/ unit cube */ |
---|
200 | nstep = 0; /* begin counting steps */ |
---|
201 | tout = tnow; /* schedule first output */ |
---|
202 | } else { /* else restart old run */ |
---|
203 | restorestate(getparam("restore")); /* read in state file */ |
---|
204 | if (getparamstat("eps") & ARGPARAM) /* if given, set new params */ |
---|
205 | eps = getdparam("eps"); |
---|
206 | #if !defined(QUICKSCAN) |
---|
207 | if (getparamstat("theta") & ARGPARAM) |
---|
208 | theta = getdparam("theta"); |
---|
209 | #endif |
---|
210 | if (getparamstat("usequad") & ARGPARAM) |
---|
211 | usequad = getbparam("usequad"); |
---|
212 | if (getparamstat("options") & ARGPARAM) |
---|
213 | options = getparam("options"); |
---|
214 | if (getparamstat("tstop") & ARGPARAM) |
---|
215 | tstop = getdparam("tstop"); |
---|
216 | #if defined(USEFREQ) |
---|
217 | if (getparamstat("freqout") & ARGPARAM) |
---|
218 | freqout = getdparam("freqout"); |
---|
219 | if (scanopt(options, "new-tout")) /* if output time reset */ |
---|
220 | tout = tnow + 1 / freqout; /* then offset from now */ |
---|
221 | #else |
---|
222 | dtout = (sscanf(getparam("dtout"), "%lf/%lf", &dt1, &dt2) == 2 ? |
---|
223 | dt1 / dt2 : getdparam("dtout")); |
---|
224 | if (scanopt(options, "new-tout")) /* if output time reset */ |
---|
225 | tout = tnow + dtout; /* then offset from now */ |
---|
226 | #endif |
---|
227 | } |
---|
228 | } |
---|
229 | |
---|
230 | /* |
---|
231 | * TESTDATA: generate Plummer model initial conditions for test runs, |
---|
232 | * scaled to units such that M = -4E = G = 1 (Henon, Hegge, etc). |
---|
233 | * See Aarseth, SJ, Henon, M, & Wielen, R (1974) Astr & Ap, 37, 183. |
---|
234 | */ |
---|
235 | |
---|
236 | #define MFRAC 0.999 /* cut off 1-MFRAC of mass */ |
---|
237 | |
---|
238 | local void testdata(void) |
---|
239 | { |
---|
240 | real rsc, vsc, r, v, x, y; |
---|
241 | vector rcm, vcm; |
---|
242 | bodyptr p; |
---|
243 | |
---|
244 | bodytab = (bodyptr) allocate(nbody * sizeof(body)); |
---|
245 | /* alloc space for bodies */ |
---|
246 | rsc = (3 * PI) / 16; /* and length scale factor */ |
---|
247 | vsc = rsqrt(1.0 / rsc); /* find speed scale factor */ |
---|
248 | CLRV(rcm); /* zero out cm position */ |
---|
249 | CLRV(vcm); /* zero out cm velocity */ |
---|
250 | for (p = bodytab; p < bodytab+nbody; p++) { /* loop over bodies */ |
---|
251 | Type(p) = BODY; /* tag as a body */ |
---|
252 | Mass(p) = 1.0 / nbody; /* set masses equal */ |
---|
253 | x = xrandom(0.0, MFRAC); /* pick enclosed mass */ |
---|
254 | r = 1.0 / rsqrt(rpow(x, -2.0/3.0) - 1); /* find enclosing radius */ |
---|
255 | pickshell(Pos(p), NDIM, rsc * r); /* pick position vector */ |
---|
256 | do { /* select from fn g(x) */ |
---|
257 | x = xrandom(0.0, 1.0); /* for x in range 0:1 */ |
---|
258 | y = xrandom(0.0, 0.1); /* max of g(x) is 0.092 */ |
---|
259 | } while (y > x*x * rpow(1 - x*x, 3.5)); /* using von Neumann tech */ |
---|
260 | v = x * rsqrt(2.0 / rsqrt(1 + r*r)); /* find resulting speed */ |
---|
261 | pickshell(Vel(p), NDIM, vsc * v); /* pick velocity vector */ |
---|
262 | ADDMULVS(rcm, Pos(p), 1.0 / nbody); /* accumulate cm position */ |
---|
263 | ADDMULVS(vcm, Vel(p), 1.0 / nbody); /* accumulate cm velocity */ |
---|
264 | } |
---|
265 | for (p = bodytab; p < bodytab+nbody; p++) { /* loop over bodies again */ |
---|
266 | SUBV(Pos(p), Pos(p), rcm); /* subtract cm position */ |
---|
267 | SUBV(Vel(p), Vel(p), vcm); /* subtract cm velocity */ |
---|
268 | } |
---|
269 | } |
---|