2013-03-02 2 views
2

Согласно совету, данному мне в this answer, я применил интегратор Runge-Kutta в моем симуляторе силы тяжести.Что не так с моим симуляцией гравитации?

Однако после того, как я смоделировал один год солнечной системы, позиции по-прежнему отключены примерно на 110 000 километров, что неприемлемо.

Мои исходные данные были предоставлены системой ГОРИЗОНТОВ НАСА. Через него я получил положения и скорости векторов планет, Плутона, Луны, Деймоса и Фобоса в определенный момент времени.

Эти векторы были 3D, однако некоторые люди сказали мне, что я мог бы игнорировать третье измерение, поскольку планеты выровнялись в пластине вокруг Солнца, и поэтому я это сделал. Я просто скопировал координаты x-y в свои файлы.

Это код моего усовершенствованного способа обновления:

""" 
Measurement units: 

[time] = s 
[distance] = m 
[mass] = kg 
[velocity] = ms^-1 
[acceleration] = ms^-2 
""" 

class Uni: 
    def Fg(self, b1, b2): 
     """Returns the gravitational force acting between two bodies as a Vector2.""" 

     a = abs(b1.position.x - b2.position.x) #Distance on the x axis 
     b = abs(b1.position.y - b2.position.y) #Distance on the y axis 

     r = math.sqrt(a*a + b*b) 

     fg = (self.G * b1.m * b2.m)/pow(r, 2) 

     return Vector2(a/r * fg, b/r * fg) 

    #After this is ran, all bodies have the correct accelerations: 
    def updateAccel(self): 
     #For every combination of two bodies (b1 and b2) out of all bodies: 
     for b1, b2 in combinations(self.bodies.values(), 2): 
      fg = self.Fg(b1, b2) #Calculate the gravitational force between them 

      #Add this force to the current force vector of the body: 
      if b1.position.x > b2.position.x: 
       b1.force.x -= fg.x 
       b2.force.x += fg.x 
      else: 
       b1.force.x += fg.x 
       b2.force.x -= fg.x 


      if b1.position.y > b2.position.y: 
       b1.force.y -= fg.y 
       b2.force.y += fg.y 
      else: 
       b1.force.y += fg.y 
       b2.force.y -= fg.y 

     #For body (b) in all bodies (self.bodies.itervalues()): 
     for b in self.bodies.itervalues(): 
      b.acceleration.x = b.force.x/b.m 
      b.acceleration.y = b.force.y/b.m 
      b.force.null() #Reset the force as it's not needed anymore. 

    def RK4(self, dt, stage): 
     #For body (b) in all bodies (self.bodies.itervalues()): 
     for b in self.bodies.itervalues(): 
      rd = b.rk4data #rk4data is an object where the integrator stores its intermediate data 

      if stage == 1: 
       rd.px[0] = b.position.x 
       rd.py[0] = b.position.y 
       rd.vx[0] = b.velocity.x 
       rd.vy[0] = b.velocity.y 
       rd.ax[0] = b.acceleration.x 
       rd.ay[0] = b.acceleration.y 

      if stage == 2: 
       rd.px[1] = rd.px[0] + 0.5*rd.vx[0]*dt 
       rd.py[1] = rd.py[0] + 0.5*rd.vy[0]*dt 
       rd.vx[1] = rd.vx[0] + 0.5*rd.ax[0]*dt 
       rd.vy[1] = rd.vy[0] + 0.5*rd.ay[0]*dt 
       rd.ax[1] = b.acceleration.x 
       rd.ay[1] = b.acceleration.y 

      if stage == 3: 
       rd.px[2] = rd.px[0] + 0.5*rd.vx[1]*dt 
       rd.py[2] = rd.py[0] + 0.5*rd.vy[1]*dt 
       rd.vx[2] = rd.vx[0] + 0.5*rd.ax[1]*dt 
       rd.vy[2] = rd.vy[0] + 0.5*rd.ay[1]*dt 
       rd.ax[2] = b.acceleration.x 
       rd.ay[2] = b.acceleration.y 

      if stage == 4: 
       rd.px[3] = rd.px[0] + rd.vx[2]*dt 
       rd.py[3] = rd.py[0] + rd.vy[2]*dt 
       rd.vx[3] = rd.vx[0] + rd.ax[2]*dt 
       rd.vy[3] = rd.vy[0] + rd.ay[2]*dt 
       rd.ax[3] = b.acceleration.x 
       rd.ay[3] = b.acceleration.y 

      b.position.x = rd.px[stage-1] 
      b.position.y = rd.py[stage-1] 

    def update (self, dt): 
     """Pushes the uni 'dt' seconds forward in time.""" 

     #Repeat four times: 
     for i in range(1, 5, 1): 
      self.updateAccel() #Calculate the current acceleration of all bodies 
      self.RK4(dt, i) #ith Runge-Kutta step 

     #Set the results of the Runge-Kutta algorithm to the bodies: 
     for b in self.bodies.itervalues(): 
      rd = b.rk4data 
      b.position.x = b.rk4data.px[0] + (dt/6.0)*(rd.vx[0] + 2*rd.vx[1] + 2*rd.vx[2] + rd.vx[3]) #original_x + delta_x 
      b.position.y = b.rk4data.py[0] + (dt/6.0)*(rd.vy[0] + 2*rd.vy[1] + 2*rd.vy[2] + rd.vy[3]) 

      b.velocity.x = b.rk4data.vx[0] + (dt/6.0)*(rd.ax[0] + 2*rd.ax[1] + 2*rd.ax[2] + rd.ax[3]) 
      b.velocity.y = b.rk4data.vy[0] + (dt/6.0)*(rd.ay[0] + 2*rd.ay[1] + 2*rd.ay[2] + rd.ay[3]) 

     self.time += dt #Internal time variable 

алгоритм выглядит следующим образом:

  1. Update ускорения всех тел в системе
  2. RK4 (первый этап)
  3. goto 1
  4. RK4 (второй)
  5. Гото 1
  6. RK4 (третий)
  7. Гото 1
  8. RK4 (четвёртое)

ли я что-то запутались с моей реализации RK4? Или я только начал с испорченных данных (слишком мало важных органов и игнорировало 3-е измерение)?

Как это можно исправить?


Объяснение моих данных и т.д. ...

Все мои координаты относительно Солнца (то есть Солнце находится в точке (0, 0)).

./my_simulator 1yr 

Earth position: (-1.47589927462e+11, 18668756050.4) 

HORIZONS (NASA): 

Earth position: (-1.474760457316177E+11, 1.900200786726017E+10) 

я получил ошибку 110 000 km путем вычитания х Земли координат задается НАСА из одного предсказанного моего тренажере.

relative error = (my_x_coordinate - nasa_x_coordinate)/nasa_x_coordinate * 100 
       = (-1.47589927462e+11 + 1.474760457316177E+11)/-1.474760457316177E+11 * 100 
       = 0.077% 

Относительная ошибка кажется мизерной, но это просто потому, что Земля находится очень далеко от Солнца, как в моем моделировании и в НАСА. Расстояние все еще огромный и делает мой симулятор бесполезным.

+2

Это не вопрос программирования, это проблема математики (от некоторых ва riety). –

+0

Хорошая мысль, но я считаю, что она хорошо подходит для SO, потому что речь идет о конкретной реализации алгоритма - это то, чего вы не делаете в математике, но в основном в программировании. Я также задал очень похожий вопрос на этот (но принципиально иной), и он был очень хорошо принят. – corazza

+1

Хорошо, но вы по сути просто просите людей обнаружить ошибки в вашем коде путем проверки, что не является большим вопросом. –

ответ

2

110 000 km абсолютная ошибка означает, что относительная ошибка?

Я получил значение 110 000 км, вычитая координату координат предсказанной Земли x с координатой x Земли НАСА.

Я не уверен, что вы здесь вычисляете или что вы подразумеваете под «координатой координат Земли НАСА».Это расстояние от какого происхождения, в какой системе координат, в какое время? (Насколько я знаю, Земля движется по орбите вокруг Солнца, поэтому ее координата x по системе координат, центрированной на солнце, меняется все время.)

В любом случае вы рассчитали абсолютную ошибку 110 000 км, вычитая рассчитанное значение из «координат Земли Земли НАСА». Кажется, вы думаете, что это плохой ответ. Каковы ваши ожидания? Чтобы попасть туда? Быть в метрах? Один км? Что приемлемо для вас и почему?

Вы получаете относительную ошибку, разделив разницу в ошибках на «координату x Земли NASA». Подумайте об этом как о проценте. Какую ценность вы получаете? Если это 1% или меньше, поздравляйте себя. Это было бы неплохо.

Вы должны знать, что floating point numbers aren't exact on computers. (Вы не можете представить 0,1 точно в двоичном виде больше, чем вы можете представлять 1/3 точно в десятичном формате.) Будут ошибки. Ваша работа в качестве симулятора - это понять ошибки и свести их к минимуму, насколько сможете.

У вас может возникнуть проблема с настройкой. Постарайтесь уменьшить свой размер шага в полтора раза и посмотреть, улучшаете ли вы себя. Если вы это сделаете, в нем говорится, что ваши результаты не сходились. Уменьшите наполовину до тех пор, пока не получите приемлемую ошибку.

Ваши уравнения могут быть плохо кондиционированы. Небольшие начальные ошибки будут усиливаться со временем, если это правда.

Я бы предположил, что вы безразмерности своих уравнений и вычислите размер шага предела стабильности. Ваша интуиция о том, какой размер шага достаточно мала, может удивить вас.

Я также прочитал немного больше о many body problem. Это тонко.

Вы также можете попробовать цифровую библиотеку интеграции вместо вашей схемы интеграции. Вы запрограммируете свои уравнения и передадите их промышленному силовому интегратору. Это может дать некоторое представление о том, создает ли это ваша реализация или физика.

Лично мне не нравится ваша реализация. Это было бы лучшим решением, если бы вы сделали это с учетом математических векторов. Тест «если» для относительных положений оставляет меня холодным. Векторная механика заставит знаки выходить естественным образом.

UPDATE:

ОК, ваши относительные ошибки довольно малы.

Конечно, абсолютная ошибка имеет значение - в зависимости от ваших требований. Если вы приземляете автомобиль на планете, вы не хотите, чтобы это было так.

Поэтому вам нужно прекратить делать предположения о том, что составляет слишком маленький размер шага, и делать то, что нужно, чтобы довести ошибки до приемлемого уровня.

Все ли количество в вашем вычислении 64-битных чисел с плавающей точкой IEEE? Если нет, вы никогда туда не попадете.

64-битный номер с плавающей запятой насчитывает около 16 digits of accuracy. Если вам нужно больше, вам придется использовать объект с бесконечной точностью, такой как BigDecimal Java или - дождаться его - перемасштабируйте ваши уравнения, чтобы использовать блок длины, отличный от километров. Если вы масштабируете все свои расстояния с помощью чего-то значимого для вашей проблемы (например, диаметра земли или длины главной/вспомогательной оси земной орбиты), вы можете сделать лучше.

+0

Я получил значение 110 000 км, вычитая свою предсказанную координату x Земли с координатой x Земли НАСА. Размер шага был '60s', что на самом деле довольно мало, и я не думаю, что это проблема. Я попробую сделать это в два раза и посмотреть, поможет ли это. – corazza

+0

Что означает «безразмерность»? Не считая единиц измерения или чего-то еще? Как это поможет? – corazza

+0

Вам лучше это сделать в Google. Неразмерность не означает игнорирование единиц. Любой, кто понял физику, знал бы, что это значит. (Подумайте, номер Рейнольдса в механике жидкости.) Это даст вам некоторые характерные числа, которые расскажут вам о вашей системе. – duffymo

3

Чтобы сделать интеграцию солнечной системы RK4, вам нужна очень хорошая точность или ваше решение будет расходиться быстро. Предполагая, что вы все правильно выполнили, вы можете увидеть недостатки с РК для такого рода моделирования.

Чтобы проверить, действительно ли это так: попробуйте использовать другой алгоритм интеграции. Я обнаружил, что с использованием Verlet integration симуляция солнечной системы будет намного менее хаотичной. Verlet намного проще реализовать, чем RK4.

Причина, по которой Verlet (и производные методы) часто лучше, чем RK4 для долгосрочного прогнозирования (например, полные орбиты), состоит в том, что они являются симплектическими, то есть сохраняют импульс, который RK4 не делает. Таким образом, Верлет даст лучшее поведение даже после его расхождения (реалистичное моделирование, но с ошибкой в ​​нем), тогда как RK будет давать нефизическое поведение после его расхождения.

Также: убедитесь, что вы используете плавающие точки так хорошо, как можете. Не используйте расстояния в метрах в солнечной системе, так как точность чисел с плавающей точкой намного лучше в интервале 0..1. Использование AU или некоторых других нормализованных масштабов намного лучше, чем использование счетчиков. Как было предложено по другой теме, убедитесь, что вы используете эпоху на время, чтобы избежать накопления ошибок при добавлении временных шагов.

2

Такие симуляции, как известно, ненадежны. Ошибки округления накапливаются и приводят к нестабильности. Повышение точности не очень помогает; проблема в том, что вы (должны) использовать размер конечного шага, а природа использует нулевой размер шага.

Вы можете уменьшить проблему, уменьшив размер шага, поэтому ошибки становятся более очевидными. Если вы не делаете этого в режиме реального времени, вы можете использовать динамический размер шага, который уменьшает, если два или более тела очень близки.

Одна вещь, которую я делаю с этими симуляторами, - «нормализовать» после каждого шага, чтобы сделать общую энергию одинаковой. Сумма гравитационной плюс кинетической энергии для системы в целом должна быть постоянной (сохранение энергии). Выработать полную энергию после каждого шага, а затем масштабировать все скорости объекта на постоянное количество, чтобы поддерживать полную энергию постоянной. Это, по крайней мере, делает вывод более правдоподобным. Без этого масштабирования крошечное количество энергии либо добавляется, либо удаляется из системы после каждого шага, и орбиты имеют тенденцию взорваться до бесконечности или спирали на солнце.

2

Очень простые изменения, которые позволят улучшить вещи (правильное использование значений с плавающей запятой)

  • Изменить системный блок, чтобы использовать как можно больше мантисс биты, насколько это возможно. Используя счетчики, вы делаете это неправильно ... Используйте AU, как было предложено выше. Еще лучше, масштабируйте вещи так, чтобы солнечная система входила в коробку 1x1x1.
  • Уже сказано в другом посте: ваше время, вычислите его как time = epoch_count * time_step, а не добавлением! Таким образом, вы избегаете накопления ошибок.
  • При суммировании нескольких значений используйте алгоритм суммирования с высокой точностью, такой как суммирование Кахана. В python math.fsum делает это за вас.
+0

Это хорошее соотношение полезной информации/пространства, которое у вас есть, большое спасибо! – corazza

0

Если не разложение силы быть

th = atan(b, a); 
return Vector2(cos(th) * fg, sin(th) * fg) 

(см или https://fiftyexamples.readthedocs.org/en/latest/gravity.html#solution)

КСТАТИ: вы берете квадратный корень, чтобы вычислить расстояние, но вы на самом деле нужно квадрат расстояния. ..

Почему бы не упростить

r = math.sqrt(a*a + b*b) 
fg = (self.G * b1.m * b2.m)/pow(r, 2) 

в

r2 = a*a + b*b 
fg = (self.G * b1.m * b2.m)/r2 

Я не уверен, питона, но в некоторых случаях вы получите более точные расчеты для промежуточных результатов (процессоры Intel поддерживают 80 бит поплавки, но при присвоении переменных они усекаются до 64 бит): Floating point computation changes if stored in intermediate "double" variable

Смежные вопросы