Python Euler方法在两体问题中的实现不起作用

Python Euler方法在两体问题中的实现不起作用,python,class,numerical-methods,differential-equations,orbital-mechanics,Python,Class,Numerical Methods,Differential Equations,Orbital Mechanics,我目前正试图解决一个双体问题,然后我可以升级到更多的行星,但它不起作用。它正在输出我不可能的位置。有人知道是什么引起的吗 这是我使用的代码: day = 60*60*24 # Constants G = 6.67408e-11 dt = 0.1*day au = 1.496e11 t = 0 class CelBody: def __init__(self, id, name, x0, y0, z0, vx0, vy0, vz0, mass, vector, ax0, ay0, a

我目前正试图解决一个双体问题,然后我可以升级到更多的行星,但它不起作用。它正在输出我不可能的位置。有人知道是什么引起的吗

这是我使用的代码:

day = 60*60*24
# Constants
G = 6.67408e-11
dt = 0.1*day
au = 1.496e11
t = 0


class CelBody:

    def __init__(self, id, name, x0, y0, z0, vx0, vy0, vz0, mass, vector, ax0, ay0, az0, totalforcex, totalforcey, totalforcez):
        self.ax0 = ax0
        self.ay0 = ay0
        self.az0 = az0

        self.ax = self.ax0
        self.ay = self.ay0
        self.az = self.az0

        # Constants of nature
        # Universal constant of gravitation
        self.G = 6.67408e-11
        # Name of the body (string)
        self.id = id
        self.name = name
        # Initial position of the body (au)
        self.x0 = x0
        self.y0 = y0
        self.z0 = z0
        # Position (au). Set to initial value.
        self.x = self.x0
        self.y = self.y0
        self.z = self.z0
        # Initial velocity of the body (au/s)
        self.vx0 = vx0
        self.vy0 = vy0
        self.vz0 = vz0
        # Velocity (au/s). Set to initial value.
        self.vx = self.vx0
        self.vy = self.vy0
        self.vz = self.vz0
        # Mass of the body (kg)
        self.M = mass
        # Short name
        self.vector = vector

        self.totalforcex = totalforcex
        self.totalforcey = totalforcey
        self.totalforcez = totalforcez

# All Celestial Bodies

forcex = 0
forcey = 0
forcez = 0

Bodies = [
    CelBody(0, 'Sun', 1, 1, 1, 0, 0, 0, 1.989e30, 'sun', 0, 0, 0, 0, 0, 0),
    CelBody(1, 'Mercury', 1*au, 1, 1, 0, 29780, 0, 3.3e23, 'earth', 0, 0, 0, 0, 0, 0),
    ]

leftover_bin = []
templistx = []
templisty = []
templistz = []

for v in range(365242):
    for n in range(len(Bodies)):
        #Need to initialize the bodies

        planetinit = Bodies[n]

        for x in range(len(Bodies)):
            # Temporary lists and initial conditions
            planet = Bodies[x]

            if (planet == planetinit):
                pass

            else:
                rx = Bodies[x].x - Bodies[n].x
                ry = Bodies[x].y - Bodies[n].y
                rz = Bodies[x].z - Bodies[n].z

                r3 = (rx**2+ry**2+rz**2)**1.5
                gravconst = G*Bodies[n].M*Bodies[x].M
                fx = -gravconst*rx/r3
                fy = -gravconst*ry/r3
                fz = -gravconst*rz/r3


                # Make a temporary list of the total forces and then add them to get the resulting force
                templistx.append(fx)
                templisty.append(fy)
                templistz.append(fz)

        forcex = sum(templistx)
        forcey = sum(templisty)
        forcez = sum(templistz)
        templistx.clear()
        templisty.clear()
        templistz.clear()

        x = int(Bodies[n].x) + int(Bodies[n].vx) * dt
        y = int(Bodies[n].y) + int(Bodies[n].vx) * dt
        z = int(Bodies[n].z) + int(Bodies[n].vz) * dt

        Bodies[n].x = x
        Bodies[n].y = y
        Bodies[n].z = z

        vx = int(Bodies[n].vx) + forcex/int(Bodies[n].M)*dt
        vy = int(Bodies[n].vy) + forcey/int(Bodies[n].M)*dt
        vz = int(Bodies[n].vz) + forcez/int(Bodies[n].M)*dt

        Bodies[n].vx = vx
        Bodies[n].vy = vy
        Bodies[n].vz = vz

        t += dt




print(Bodies[0].name)
print(Bodies[0].x)
print(Bodies[0].y)
print(Bodies[0].z)


print(Bodies[1].name)
print(Bodies[1].x)
print(Bodies[1].y)
print(Bodies[1].z)
它应该输出类似于此处坐标的内容,但也输出一个z坐标:
坐标1(41.147123353981485,-2812171.2728945166)
坐标2(150013715707.77917、2374319765.821534)

但它的输出如下:

太阳 0.0,0.0,0.0

土 1496000000.0,0.0,0.0


注意:问题可能在于for循环或数组总和的舍入,但我不确定。

我认为问题的核心在于,您没有将其视为状态引擎

设想“Bodies”是一个完全不变的值,它决定了系统在某个时间点的状态:

bodies_at_time_0 = ((sun, position, velocity, mass), (earth, position, velocity, mass))
下一个状态如下所示:

bodies_at_time_1 = apply_euler_method_for_one_tick( bodies_at_time_0 )
因此,你的“身体”在某一时刻是完全固定的,你在下一次计算一个全新的“身体”。在计算中,您总是使用输入中的数据,这就是它们现在所在的位置。你正在做的是移动一些东西,然后根据错误的数字计算其他东西移动到哪里(因为你已经移动了其他东西)

确保函数使用输入状态并返回输出状态后,就可以更轻松地对其进行分解:

# advance all bodies one time interval, using their frozen state 
def compute(bodies):
    new_bodies = []
    for body in bodies:
        new_bodies.append(compute_one_body(body, bodies))
    return new_bodies

# figure out where one body will move to, return its new state
def compute_one_body(start, bodies):
    end = math stuff using the fixed state in bodies
    return end

# MAIN
bodies = initial_state
for timepoint in whatever:
    bodies = compute(bodies)
我喜欢使用元组来处理这类事情,以避免意外地更改其他范围中的列表(因为列表是可变的)。

picture-1000字

代码中的直接错误是

  • 如果计算的力方向错误,则应为
    rx=b[n].x-b[x].x
    等。或者需要在几行之后删除减号

  • 在单个坐标中的计算会导致复制粘贴错误,如中所示

    x = int(Bodies[n].x) + int(Bodies[n].vx) * dt
    y = int(Bodies[n].y) + int(Bodies[n].vx) * dt
    z = int(Bodies[n].z) + int(Bodies[n].vz) * dt
    
    y
    坐标中,您仍然使用
    vx
    。中间舍入为整数值没有意义,它只会在一定程度上降低精度


我将你的代码改为使用numpy数组作为向量,将加速度计算与Euler更新分离,在数值模拟过程中删除非感官舍入为整数值,删除未使用的变量和字段,删除力/加速度计算的中间变量以直接更新加速度场,更改循环以使用时间来通知一年(或10年)的过去(您的代码以0.1天的增量迭代100年,这是有意的吗。。。并将金星添加到天体中,并添加代码生成图像,结果见上文

这种螺旋是Euler方法的典型特征。通过将Euler更新更改为辛Euler更新,可以轻松改进该模式,这意味着首先更新速度并使用新速度计算位置。在其他一切都相同的情况下,这给人一种印象

day=60*60*24
#常数
G=6.67408e-11
au=1.496e11
类主体(对象):
#自然常数
#万有引力常数
定义初始值(自身、id、名称、x0、v0、质量、颜色、lw):
#正文的名称(字符串)
self.id=id
self.name=名称
#车身质量(kg)
self.M=质量
#主体的初始位置(au)
self.x0=np.asarray(x0,dtype=float)
#位置(au)。设置为初始值。
self.x=self.x0.copy()
#物体的初始速度(au/s)
self.v0=np.asarray(v0,dtype=float)
#速度(au/s)。设置为初始值。
self.v=self.v0.copy()
self.a=np.zero([3],dtype=float)
self.color=颜色
self.lw=lw
#所有天体
t=0
dt=0.1*天
机构=[
CelBody(0,'太阳',[0,0,0],[0,0,0],1.989e30,'黄色',10),
CelBody(1,'地球',[-1*au,0,0],[0297783,0],5.9742e24,'蓝色',3),
CelBody(2'金星',[0,0.723*au,0],[35020,0,0],4.8685e24'红色',2),
]
路径=[[b.x[:2].copy()]用于正文中的b]
#十个天文年
v=0
当t<10*365.242*天时:
#计算力/加速度
对于身体中的身体:
body.a*=0
其他机构:
#本身没有力量
if(body==other):继续#跳转到下一个循环
rx=主体.x-其他.x
r3=总和(rx**2)**1.5
body.a+=-G*其他.M*rx/r3
对于n,枚举中的行星(体):
#使用辛欧拉方法更好地守恒运动常数
行星v+=行星a*dt
行星x+=行星v*dt
路径[n]。追加(planet.x[:2]。复制())
#打印(“%10s x:%53s v:%53s”%(planet.name,planet.x,planet.v))
如果t>v:
打印(“t=%f”%t)
对于正文中的b:打印(“%10s%s”%(b.name,b.x))
v+=30.5*天
t+=dt
plt.图(figsize=(8,8))
对于n,枚举中的行星(体):
px,py=np.array(路径[n]).T;
plt.plot(px,py,color=planet.color,lw=planet.lw)
plt.show()

尝试将其分解为目标明确的功能。您有一个大规模的代码转储,但看起来您有两个实体循环,您在其中一个实体循环中重新初始化,并且在实体循环中将状态更改应用于实体(因此您将一个实体的未来状态应用于另一个实体的过去状态)。你需要把它分解成清晰的函数,这样你就可以调试它,并在一个步骤(而不是整个运行过程)后手动检查数字。它不会输出你所说的内容。然后它会输出什么?从根本上说,你需要一个在所有计算中使用的固定状态,然后返回下一个状态(在每个时间步)。可能还有其他错误,但这是一个常见的错误。你真的需要发布正确的答案(而不仅仅是说这是错误的)。它输出的是Sun 1.0 1.0 1.0 Mercury 1020897
day = 60*60*24
# Constants
G = 6.67408e-11
au = 1.496e11

class CelBody(object):
    # Constants of nature
    # Universal constant of gravitation
    def __init__(self, id, name, x0, v0, mass, color, lw):
        # Name of the body (string)
        self.id = id
        self.name = name
        # Mass of the body (kg)
        self.M = mass
        # Initial position of the body (au)
        self.x0 = np.asarray(x0, dtype=float)
        # Position (au). Set to initial value.
        self.x = self.x0.copy()
        # Initial velocity of the body (au/s)
        self.v0 = np.asarray(v0, dtype=float)
        # Velocity (au/s). Set to initial value.
        self.v = self.v0.copy()
        self.a = np.zeros([3], dtype=float)
        self.color = color
        self.lw = lw

# All Celestial Bodies

t = 0
dt = 0.1*day

Bodies = [
    CelBody(0, 'Sun', [0, 0, 0], [0, 0, 0], 1.989e30, 'yellow', 10),
    CelBody(1, 'Earth', [-1*au, 0, 0], [0, 29783, 0], 5.9742e24, 'blue', 3),
    CelBody(2, 'Venus', [0, 0.723 * au, 0], [ 35020, 0, 0], 4.8685e24, 'red', 2),
    ]

paths = [ [ b.x[:2].copy() ] for b in Bodies]

# loop over ten astronomical years
v = 0
while t < 10*365.242*day:
    # compute forces/accelerations
    for body in Bodies:
        body.a *= 0
        for other in Bodies:
            # no force on itself
            if (body == other): continue # jump to next loop
            rx = body.x - other.x
            r3 = sum(rx**2)**1.5
            body.a += -G*other.M*rx/r3

    for n, planet in enumerate(Bodies):
        # use the symplectic Euler method for better conservation of the constants of motion
        planet.v += planet.a*dt
        planet.x += planet.v*dt
        paths[n].append( planet.x[:2].copy() )
        #print("%10s x:%53s v:%53s"%(planet.name,planet.x, planet.v))
    if t > v:
        print("t=%f"%t)
        for b in Bodies: print("%10s %s"%(b.name,b.x))
        v += 30.5*day
    t += dt

plt.figure(figsize=(8,8))
for n, planet in enumerate(Bodies): 
    px, py=np.array(paths[n]).T; 
    plt.plot(px, py, color=planet.color, lw=planet.lw)
plt.show()