【问题标题】:Gravity and Many Particles重力和许多粒子
【发布时间】:2014-07-15 16:07:33
【问题描述】:

我在太空中有很多粒子。

每个粒子都知道它在空间中的位置以及质量、速度和加速度等信息。

public class Particle
{
    float mass;
    float velocity;
    float acceleration;
}

我有一个控制重力的类。这个类有一个List,是所有Particles。我可以使用下面的公式计算两个粒子的重力。

F1 = F2 = G*M1*M2/d^2

当我的 List 中有 5 个粒子时,我该如何计算?未来还会有更多。

元素 0 与 1,元素 1 与 2,元素 2 与 3,元素 3 与 4,以及元素 4 与 0? (这似乎不是一个好主意)。

【问题讨论】:

  • 我以为太空中有很多星星!开玩笑! :)
  • 问题是每个主体都必须与所有其他主体交互。您正在寻找的是 N 体问题 (en.wikipedia.org/wiki/N-body_problem) 或 N 体模拟 (en.wikipedia.org/wiki/N-body_simulation)。也许从三个身体开始——这已经很复杂了。
  • @PiotrWolkowski 酷我不知道它叫 N 体模拟。
  • 您需要计算每个时间范围内作用在每个粒子上的所有引力的总和,可能是一组向量。有更快的近似值,但在这些情况下,我认为您不需要它。
  • NASA 在计算轨道轨迹时的方法是计算每个天体对其他天体的引力。对于每个物体,矢量的总和作为某个周期的加速度应用于运动,并计算出一个新的位置。然后重复练习。他们将牛顿引力用于外行星。对于 Mercury,他们必须使用广义相对论,但您可能不需要。

标签: c# physics


【解决方案1】:

实际上,万有引力定律仅适用于 2 个物体。这与地球和物体在太空或表面上的一般研究一样。

https://en.wikipedia.org/wiki/Newton's_law_of_universal_gravitation

如果你有 5 个质量,你可以做的是让一个质量静止或相对。这将是M1 然后测试与它相关的所有质量,找到每个质量。也许在这里你也需要某种foreach 循环。

在太空中处理 5 个物体,即使是牛顿先生也很难,这就是为什么他给了你一个 2 个物体的方程。与是相互关联的。您可以使用这 2 个物体,并检查每个物体的重力。将它们相互关联,因为最初它们是不相关的。

【讨论】:

    【解决方案2】:

    在模拟的每个时间步,计算每个粒子上的总矢量力,并以等于总力除以粒子质量的加速度移动该粒子。

    要找到每个粒子上的总力,请计算一个粒子与其他每个粒子之间的引力,然后将它们加起来。请注意,当您计算每个力时,它必须是矢量力,即具有等式中大小的力,以及朝向另一个粒子的方向。 (即使用这个版本的牛顿万有引力定律:http://en.wikipedia.org/wiki/Newton%27s_law_of_universal_gravitation#Vector_form

    除了力之外,这里的一切都应该使用向量来完成:位置、速度、加速度和力,都作为向量。正如您所描述的问题,质量是您唯一的标量。 (也就是说,在您的Particle 类中,质量可以是浮点数,但速度和加速度需要是向量,具有 x、y 和 z 分量。)

    【讨论】:

      【解决方案3】:

      这应该是一个很好的起点:

      namespace SO.NBody
      {
          public class Particle
          {
              public double mass;
              public double[] position;
              public double[] velocity;
              public double[] acceleration;
      
              public Particle(double mass)
              {
                  this.mass=mass;
                  this.position=new double[Simulation.DOF];
                  this.velocity=new double[Simulation.DOF];
                  this.acceleration=new double[Simulation.DOF];
              }
          }
      
          public class Simulation
          {
              // Degrees of Freedom, Planar Simulation = 2, Spatial Simulation = 3
              public static int DOF=2;
              // Set Universal Gravity as Needed here
              public const double G=100;
      
              public Simulation()
              {
                  Bodies=new List<Particle>();
                  Time=0;
              }
              public List<Particle> Bodies { get; private set; }
              public double Time { get; set; }
      
              public void CalculateAllAccelerations()
              {
                  for (int i=0; i<Bodies.Count; i++)
                  {
                      Bodies[i].acceleration=new double[DOF];
                      for (int j=0; j<i; j++)
                      {
                          // Find relative position, which is needed for
                          //   a) Distance
                          //   b) Direction
                          double[] step=new double[DOF];
                          double distance=0;
                          for (int k=0; k<DOF; k++)
                          {
                              step[k]=Bodies[i].position[k]-Bodies[j].position[k];
                              // distance is |x^2+y^2+..|
                              distance+=step[k]*step[k];
                          }
                          distance=Math.Sqrt(distance);
                          // Law of gravity
                          double force=G*Bodies[i].mass*Bodies[j].mass/(distance*distance);
                          // direction vector from [j] to [i]
                          double[] direction=new double[DOF];
                          for (int k=0; k<DOF; k++)
                          {
                              direction[k]=step[k]/distance;
                          }
                          // Add equal and opposite acceleration components
                          for (int k=0; k<DOF; k++)
                          {
                              Bodies[i].acceleration[k]-=direction[k]*(force/Bodies[i].mass);
                              Bodies[j].acceleration[k]+=direction[k]*(force/Bodies[j].mass);
                          }
                      }
                  }
      
              }
      
              public void UpdatePositions(double time_step)
              {
                  CalculateAllAccelerations();
                  // Use symplectic integration
                  for (int i=0; i<Bodies.Count; i++)
                  {
                      for (int k=0; k<DOF; k++)
                      {
                          Bodies[i].velocity[k]+=time_step*Bodies[i].acceleration[k];
                          Bodies[i].position[k]+=time_step*Bodies[i].velocity[k];
                      }
                  }
                  Time+=time_step;
              }
      
              public void RunSimulation(double end_time, int steps)
              {
                  double h=(end_time-Time)/steps;
                  while (Time<end_time)
                  {
                      // Trim last step if needed so the sim ends when specified
                      if (Time+h>end_time) { h=end_time-Time; }
                      UpdatePositions(h);
                  }
              }
          }
      
          class Program
          {
              static void Main(string[] args)
              {
                  var world=new Simulation();
                  var sun=new Particle(1000);
                  var p1=new Particle(1.15)
                  {
                      position=new double[] { 100, 0 },
                      velocity = new double[] { 0, 10 }
                  };
                  var p2=new Particle(1.05)
                  {
                      position=new double[] { 120, 0 },
                      velocity=new double[] { 0, 6 }
                  };
                  // ...
      
                  world.Bodies.Add(sun);
                  world.Bodies.Add(p1);
                  world.Bodies.Add(p2);
                  //...
      
                  // Run for t=10.0, with 100 steps
                  world.RunSimulation(10.0, 100);
              }
          }
      }
      

      【讨论】:

      • 欧拉法作为积分器将稳定地增加系统的能量,直到某物达到逃逸速度。应该使用辛积分器,例如牛顿使用的 Verlet 方法或更高阶的方法。请参阅stackoverflow.com/a/23671054/3088138jsfiddle.net/4XVPH(未完全正确初始化,重心不在屏幕中心)。并与问题提供的原始实现进行比较。
      • 是的,我很清楚这一点。此讨论超出了原始问题的范围。正如我所说,这是一个起点,而不是最终解决方案。
      • 我应该从“如果你让模拟运行那么远,你会观察到......”和“你可以通过......改进它”开始,更简单的改进是减少Euler 方法中的时间步长,特别是如果模拟显示为基于帧的动画,则每帧应采用多个模拟步骤。
      • 请注意,我先更新速度,然后再更新位置,以减少显式欧拉方法的人工能量输入。这几乎和 Verlet 方法一样好。见en.wikipedia.org/wiki/Semi-implicit_Euler_method
      • 请注意,我在Bodies[i].acceleration[k]-=direction[k]... 中找到并修复了它,不知何故我有=- 而不是-=
      猜你喜欢
      • 1970-01-01
      • 2014-01-20
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-10-30
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多