Guest User

nbody.c

a guest
Jun 20th, 2016
61
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C 9.87 KB | None | 0 0
  1. # The Computer Language Benchmarks Game
  2. # http://benchmarksgame.alioth.debian.org/
  3. #
  4. # originally by Kevin Carson
  5. # modified by Tupteq, Fredrik Johansson, and Daniel Nanz
  6. # modified by Maciej Fijalkowski
  7. # 2to3
  8.  
  9. from libc.math cimport pow, sqrt
  10. import sys
  11.  
  12. PI = 3.14159265358979323
  13. SOLAR_MASS = 4 * PI * PI
  14. DAYS_PER_YEAR = 365.24
  15.  
  16. cdef struct body_t:
  17.     double x[3]
  18.     double v[3]
  19.     double m
  20.  
  21. DEF NBODIES = 5
  22.  
  23. BODIES = {
  24.         'sun': ([0.0, 0.0, 0.0], [0.0, 0.0, 0.0], SOLAR_MASS),
  25.  
  26.         'jupiter': ([4.84143144246472090e+00,
  27.             -1.16032004402742839e+00,
  28.             -1.03622044471123109e-01],
  29.             [1.66007664274403694e-03 * DAYS_PER_YEAR,
  30.                 7.69901118419740425e-03 * DAYS_PER_YEAR,
  31.                 -6.90460016972063023e-05 * DAYS_PER_YEAR],
  32.             9.54791938424326609e-04 * SOLAR_MASS),
  33.  
  34.         'saturn': ([8.34336671824457987e+00,
  35.             4.12479856412430479e+00,
  36.             -4.03523417114321381e-01],
  37.             [-2.76742510726862411e-03 * DAYS_PER_YEAR,
  38.                 4.99852801234917238e-03 * DAYS_PER_YEAR,
  39.                 2.30417297573763929e-05 * DAYS_PER_YEAR],
  40.             2.85885980666130812e-04 * SOLAR_MASS),
  41.  
  42.         'uranus': ([1.28943695621391310e+01,
  43.             -1.51111514016986312e+01,
  44.             -2.23307578892655734e-01],
  45.             [2.96460137564761618e-03 * DAYS_PER_YEAR,
  46.                 2.37847173959480950e-03 * DAYS_PER_YEAR,
  47.                 -2.96589568540237556e-05 * DAYS_PER_YEAR],
  48.             4.36624404335156298e-05 * SOLAR_MASS),
  49.  
  50.         'neptune': ([1.53796971148509165e+01,
  51.             -2.59193146099879641e+01,
  52.             1.79258772950371181e-01],
  53.             [2.68067772490389322e-03 * DAYS_PER_YEAR,
  54.                 1.62824170038242295e-03 * DAYS_PER_YEAR,
  55.                 -9.51592254519715870e-05 * DAYS_PER_YEAR],
  56.             5.15138902046611451e-05 * SOLAR_MASS)
  57.         }
  58.  
  59. def combinations(l):
  60.     result = []
  61.     for x in range(len(l) - 1):
  62.         ls = l[x+1:]
  63.         for y in ls:
  64.             result.append((l[x],y))
  65.     return result
  66.  
  67. cdef void make_cbodies(list bodies, body_t *cbodies, int num_cbodies):
  68.     cdef body_t *cbody
  69.     for i, body in enumerate(bodies):
  70.         if i >= num_cbodies:
  71.             break
  72.         (x, v, m) = body
  73.         cbody = &cbodies[i]
  74.         cbody.x[0], cbody.x[1], cbody.x[2] = x
  75.         cbody.v[0], cbody.v[1], cbody.v[2] = v
  76.         cbodies[i].m = m
  77.  
  78. cdef list make_pybodies(body_t *cbodies, int num_cbodies):
  79.     pybodies = []
  80.     for i in range(num_cbodies):
  81.         x = [cbodies[i].x[0], cbodies[i].x[1], cbodies[i].x[2]]
  82.         v = [cbodies[i].v[0], cbodies[i].v[1], cbodies[i].v[2]]
  83.         pybodies.append((x, v, cbodies[i].m))
  84.     return pybodies
  85.  
  86.  
  87. def advance(double dt, int n, bodies):
  88.     # Note that this does not take a `pairs` argument, and *returns* a new
  89.     # `bodies` list.  This is slightly different from the original pure-Python
  90.     # version of `advance`.
  91.  
  92.     cdef:
  93.         int i, ii, jj
  94.         double dx, dy, dz, mag, b1m, b2m, ds
  95.         body_t *body1
  96.         body_t *body2
  97.         body_t cbodies[NBODIES]
  98.  
  99.     make_cbodies(bodies, cbodies, NBODIES)
  100.  
  101.     for i in range(n):
  102.         for ii in range(NBODIES-1):
  103.             body1 = &cbodies[ii]
  104.             for jj in range(ii+1, NBODIES):
  105.                 body2 = &cbodies[jj]
  106.                 dx = body1.x[0] - body2.x[0]
  107.                 dy = body1.x[1] - body2.x[1]
  108.                 dz = body1.x[2] - body2.x[2]
  109.                 ds = dx * dx + dy * dy + dz * dz
  110.                 mag = dt / (ds * sqrt(ds))
  111.                 b1m = body1.m * mag
  112.                 b2m = body2.m * mag
  113.                 body1.v[0] -= dx * b2m
  114.                 body1.v[1] -= dy * b2m
  115.                 body1.v[2] -= dz * b2m
  116.                 body2.v[0] += dx * b1m
  117.                 body2.v[1] += dy * b1m
  118.                 body2.v[2] += dz * b1m
  119.         for ii in range(NBODIES):
  120.             body2 = &cbodies[ii]
  121.             body2.x[0] += dt * body2.v[0]
  122.             body2.x[1] += dt * body2.v[1]
  123.             body2.x[2] += dt * body2.v[2]
  124.  
  125.     return make_pybodies(cbodies, NBODIES)
  126.  
  127.  
  128. def report_energy(bodies):
  129.     # Note that this takes just a `bodies` list, and computes `pairs`
  130.     # internally, which is a slight modification from the original pure-Python
  131.     # version of `report_energy`.
  132.  
  133.     e = 0.0
  134.  
  135.     pairs = combinations(bodies)
  136.  
  137.     for (((x1, y1, z1), v1, m1),
  138.             ((x2, y2, z2), v2, m2)) in pairs:
  139.         dx = x1 - x2
  140.         dy = y1 - y2
  141.         dz = z1 - z2
  142.         e -= (m1 * m2) / ((dx * dx + dy * dy + dz * dz) ** 0.5)
  143.     for (r, [vx, vy, vz], m) in bodies:
  144.         e += m * (vx * vx + vy * vy + vz * vz) / 2.
  145.     print("%.9f" % e)
  146.  
  147.  
  148. def offset_momentum(ref, bodies):
  149.  
  150.     px = py = pz = 0.0
  151.  
  152.     for (r, [vx, vy, vz], m) in bodies:
  153.         px -= vx * m
  154.         py -= vy * m
  155.         pz -= vz * m
  156.     (r, v, m) = ref
  157.     v[0] = px / m
  158.     v[1] = py / m
  159.     v[2] = pz / m
  160.  
  161.  
  162. def main(n, bodies=BODIES, ref='sun'):
  163.     system = list(bodies.values())
  164.     offset_momentum(bodies[ref], system)
  165.     report_energy(system)
  166.     system = advance(0.01, n, system)
  167.     report_energy(system)
  168. [chris@Vonbrigdum 02-cython]$ cd ../03-pure-c/
  169. [chris@Vonbrigdum 03-pure-c]$ ls
  170. Makefile  nbody.c
  171. [chris@Vonbrigdum 03-pure-c]$ cat nbody.c
  172. /* The Computer Language Benchmarks Game
  173.  * http://benchmarksgame.alioth.debian.org/
  174.  *
  175.  * contributed by Christoph Bauer
  176.  * slightly sped up by Petr Prokhorenkov
  177.  */
  178.  
  179. #include <math.h>
  180. #include <stdio.h>
  181. #include <stdlib.h>
  182.  
  183. #define pi 3.141592653589793
  184. #define solar_mass (4 * pi * pi)
  185. #define days_per_year 365.24
  186.  
  187. struct planet {
  188.   double x, y, z;
  189.   double vx, vy, vz;
  190.   double mass;
  191. };
  192.  
  193. /*
  194.  * Here's one weird thing: inlining of this function
  195.  * decreases performance by 25%. (I.e. do not compile this with -O3)
  196.  * Advances with dt == 1.0
  197.  */
  198. void advance(int nbodies, struct planet * bodies)
  199. {
  200.   int i, j;
  201.  
  202.   for (i = 0; i < nbodies; i++) {
  203.     struct planet * b = &(bodies[i]);
  204.     for (j = i + 1; j < nbodies; j++) {
  205.       struct planet * b2 = &(bodies[j]);
  206.       double dx = b->x - b2->x;
  207.       double dy = b->y - b2->y;
  208.       double dz = b->z - b2->z;
  209.       double inv_distance = 1.0/sqrt(dx * dx + dy * dy + dz * dz);
  210.       double mag = inv_distance * inv_distance * inv_distance;
  211.       b->vx -= dx * b2->mass * mag;
  212.       b->vy -= dy * b2->mass * mag;
  213.       b->vz -= dz * b2->mass * mag;
  214.       b2->vx += dx * b->mass * mag;
  215.       b2->vy += dy * b->mass * mag;
  216.       b2->vz += dz * b->mass * mag;
  217.     }
  218.   }
  219.   for (i = 0; i < nbodies; i++) {
  220.     struct planet * b = &(bodies[i]);
  221.     b->x += b->vx;
  222.     b->y += b->vy;
  223.     b->z += b->vz;
  224.   }
  225. }
  226.  
  227. double energy(int nbodies, struct planet * bodies)
  228. {
  229.   double e;
  230.   int i, j;
  231.  
  232.   e = 0.0;
  233.   for (i = 0; i < nbodies; i++) {
  234.     struct planet * b = &(bodies[i]);
  235.     e += 0.5 * b->mass * (b->vx * b->vx + b->vy * b->vy + b->vz * b->vz);
  236.     for (j = i + 1; j < nbodies; j++) {
  237.       struct planet * b2 = &(bodies[j]);
  238.       double dx = b->x - b2->x;
  239.       double dy = b->y - b2->y;
  240.       double dz = b->z - b2->z;
  241.       double distance = sqrt(dx * dx + dy * dy + dz * dz);
  242.       e -= (b->mass * b2->mass) / distance;
  243.     }
  244.   }
  245.   return e;
  246. }
  247.  
  248. void offset_momentum(int nbodies, struct planet * bodies)
  249. {
  250.   double px = 0.0, py = 0.0, pz = 0.0;
  251.   int i;
  252.   for (i = 0; i < nbodies; i++) {
  253.     px += bodies[i].vx * bodies[i].mass;
  254.     py += bodies[i].vy * bodies[i].mass;
  255.     pz += bodies[i].vz * bodies[i].mass;
  256.   }
  257.   bodies[0].vx = - px / solar_mass;
  258.   bodies[0].vy = - py / solar_mass;
  259.   bodies[0].vz = - pz / solar_mass;
  260. }
  261.  
  262. #define NBODIES 5
  263. struct planet bodies[NBODIES] = {
  264.   {                               /* sun */
  265.     0, 0, 0, 0, 0, 0, solar_mass
  266.   },
  267.   {                               /* jupiter */
  268.     4.84143144246472090e+00,
  269.     -1.16032004402742839e+00,
  270.     -1.03622044471123109e-01,
  271.     1.66007664274403694e-03 * days_per_year,
  272.     7.69901118419740425e-03 * days_per_year,
  273.     -6.90460016972063023e-05 * days_per_year,
  274.     9.54791938424326609e-04 * solar_mass
  275.   },
  276.   {                               /* saturn */
  277.     8.34336671824457987e+00,
  278.     4.12479856412430479e+00,
  279.     -4.03523417114321381e-01,
  280.     -2.76742510726862411e-03 * days_per_year,
  281.     4.99852801234917238e-03 * days_per_year,
  282.     2.30417297573763929e-05 * days_per_year,
  283.     2.85885980666130812e-04 * solar_mass
  284.   },
  285.   {                               /* uranus */
  286.     1.28943695621391310e+01,
  287.     -1.51111514016986312e+01,
  288.     -2.23307578892655734e-01,
  289.     2.96460137564761618e-03 * days_per_year,
  290.     2.37847173959480950e-03 * days_per_year,
  291.     -2.96589568540237556e-05 * days_per_year,
  292.     4.36624404335156298e-05 * solar_mass
  293.   },
  294.   {                               /* neptune */
  295.     1.53796971148509165e+01,
  296.     -2.59193146099879641e+01,
  297.     1.79258772950371181e-01,
  298.     2.68067772490389322e-03 * days_per_year,
  299.     1.62824170038242295e-03 * days_per_year,
  300.     -9.51592254519715870e-05 * days_per_year,
  301.     5.15138902046611451e-05 * solar_mass
  302.   }
  303. };
  304.  
  305. #define DT 1e-2
  306. #define RECIP_DT (1.0/DT)
  307.  
  308. /*
  309.  * Rescale certain properties of bodies. That allows doing
  310.  * consequential advance()'s as if dt were equal to 1.0.
  311.  *
  312.  * When all advances done, rescale bodies back to obtain correct energy.
  313.  */
  314. void scale_bodies(int nbodies, struct planet * bodies, double scale) {
  315.     int i;
  316.  
  317.     for (i = 0; i < nbodies; i++) {
  318.         bodies[i].mass *= scale*scale;
  319.         bodies[i].vx *= scale;
  320.         bodies[i].vy *= scale;
  321.         bodies[i].vz *= scale;
  322.     }
  323. }
  324.  
  325. int main(int argc, char ** argv)
  326. {
  327.   int n = atoi(argv[1]);
  328.   int i;
  329.  
  330.   offset_momentum(NBODIES, bodies);
  331.   printf ("%.9f\n", energy(NBODIES, bodies));
  332.   scale_bodies(NBODIES, bodies, DT);
  333.   for (i = 1; i <= n; i++)  {
  334.     advance(NBODIES, bodies);
  335.   }
  336.   scale_bodies(NBODIES, bodies, RECIP_DT);
  337.   printf ("%.9f\n", energy(NBODIES, bodies));
  338.   return 0;
  339. }
Add Comment
Please, Sign In to add comment