2
0

nbody.lua 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168
  1. -- N-body benchmark from benchmarks game
  2. -- https://benchmarksgame-team.pages.debian.net/benchmarksgame/description/nbody.html
  3. --
  4. -- Original code by Mike Pall and Geoff Leyland, with modifications by Hugo
  5. -- Gualandi for correctness, clarity, and compatibility with Pallene.
  6. -- * Fix implementation of advance() to use two outer loops.
  7. -- * Use #xs instead of passing "n" as a parameter to all the functions
  8. -- * Use math.math.sqrt instead of caching math.sqrt in a local variable
  9. -- * Use 0.0 instead of 0 where appropriate.
  10. -- * Don't use multiple assignments.
  11. --
  12. -- Expected output (N = 1000):
  13. -- -0.169075164
  14. -- -0.169087605
  15. --
  16. -- Expected output (N = 50000000):
  17. -- -0.169075164
  18. -- -0.169059907
  19. local function new_body(x, y, z, vx, vy, vz, mass)
  20. return {
  21. x = x,
  22. y = y,
  23. z = z,
  24. vx = vx,
  25. vy = vy,
  26. vz = vz,
  27. mass = mass,
  28. }
  29. end
  30. local function offset_momentum(bodies)
  31. local n = #bodies
  32. local px = 0.0
  33. local py = 0.0
  34. local pz = 0.0
  35. for i= 1, n do
  36. local bi = bodies[i]
  37. local bim = bi.mass
  38. px = px + (bi.vx * bim)
  39. py = py + (bi.vy * bim)
  40. pz = pz + (bi.vz * bim)
  41. end
  42. local sun = bodies[1]
  43. local solar_mass = sun.mass
  44. sun.vx = sun.vx - px / solar_mass
  45. sun.vy = sun.vy - py / solar_mass
  46. sun.vz = sun.vz - pz / solar_mass
  47. end
  48. local function advance(bodies, dt)
  49. local n = #bodies
  50. for i = 1, n do
  51. local bi = bodies[i]
  52. for j=i+1,n do
  53. local bj = bodies[j]
  54. local dx = bi.x - bj.x
  55. local dy = bi.y - bj.y
  56. local dz = bi.z - bj.z
  57. local dist = math.sqrt(dx*dx + dy*dy + dz*dz)
  58. local mag = dt / (dist * dist * dist)
  59. local bjm = bj.mass * mag
  60. bi.vx = bi.vx - (dx * bjm)
  61. bi.vy = bi.vy - (dy * bjm)
  62. bi.vz = bi.vz - (dz * bjm)
  63. local bim = bi.mass * mag
  64. bj.vx = bj.vx + (dx * bim)
  65. bj.vy = bj.vy + (dy * bim)
  66. bj.vz = bj.vz + (dz * bim)
  67. end
  68. end
  69. for i = 1, n do
  70. local bi = bodies[i]
  71. bi.x = bi.x + dt * bi.vx
  72. bi.y = bi.y + dt * bi.vy
  73. bi.z = bi.z + dt * bi.vz
  74. end
  75. end
  76. local function advance_multiple_steps(nsteps, bodies, dt)
  77. for _ = 1, nsteps do
  78. advance(bodies, dt)
  79. end
  80. end
  81. local function energy(bodies)
  82. local n = #bodies
  83. local e = 0.0
  84. for i = 1, n do
  85. local bi = bodies[i]
  86. local vx = bi.vx
  87. local vy = bi.vy
  88. local vz = bi.vz
  89. e = e + (0.5 * bi.mass * (vx*vx + vy*vy + vz*vz))
  90. for j = i+1, n do
  91. local bj = bodies[j]
  92. local dx = bi.x-bj.x
  93. local dy = bi.y-bj.y
  94. local dz = bi.z-bj.z
  95. local distance = math.sqrt(dx*dx + dy*dy + dz*dz)
  96. e = e - ((bi.mass * bj.mass) / distance)
  97. end
  98. end
  99. return e
  100. end
  101. local PI = 3.141592653589793
  102. local SOLAR_MASS = 4 * PI * PI
  103. local DAYS_PER_YEAR = 365.24
  104. local bodies = {
  105. -- Sun
  106. new_body(
  107. 0.0,
  108. 0.0,
  109. 0.0,
  110. 0.0,
  111. 0.0,
  112. 0.0,
  113. SOLAR_MASS),
  114. -- Jupiter
  115. new_body(
  116. 4.84143144246472090e+00,
  117. -1.16032004402742839e+00,
  118. -1.03622044471123109e-01,
  119. 1.66007664274403694e-03 * DAYS_PER_YEAR,
  120. 7.69901118419740425e-03 * DAYS_PER_YEAR,
  121. -6.90460016972063023e-05 * DAYS_PER_YEAR,
  122. 9.54791938424326609e-04 * SOLAR_MASS ),
  123. -- Saturn
  124. new_body(
  125. 8.34336671824457987e+00,
  126. 4.12479856412430479e+00,
  127. -4.03523417114321381e-01,
  128. -2.76742510726862411e-03 * DAYS_PER_YEAR,
  129. 4.99852801234917238e-03 * DAYS_PER_YEAR,
  130. 2.30417297573763929e-05 * DAYS_PER_YEAR,
  131. 2.85885980666130812e-04 * SOLAR_MASS ),
  132. -- Uranus
  133. new_body(
  134. 1.28943695621391310e+01,
  135. -1.51111514016986312e+01,
  136. -2.23307578892655734e-01,
  137. 2.96460137564761618e-03 * DAYS_PER_YEAR,
  138. 2.37847173959480950e-03 * DAYS_PER_YEAR,
  139. -2.96589568540237556e-05 * DAYS_PER_YEAR,
  140. 4.36624404335156298e-05 * SOLAR_MASS ),
  141. -- Neptune
  142. new_body(
  143. 1.53796971148509165e+01,
  144. -2.59193146099879641e+01,
  145. 1.79258772950371181e-01,
  146. 2.68067772490389322e-03 * DAYS_PER_YEAR,
  147. 1.62824170038242295e-03 * DAYS_PER_YEAR,
  148. -9.51592254519715870e-05 * DAYS_PER_YEAR,
  149. 5.15138902046611451e-05 * SOLAR_MASS ),
  150. }
  151. return function (N)
  152. N = N or 50000000
  153. offset_momentum(bodies)
  154. print(string.format("%0.9f", energy(bodies)))
  155. advance_multiple_steps(N, bodies, 0.01)
  156. print(string.format("%0.9f", energy(bodies)))
  157. end