Реализация метода Питона Эйлера в двух телах Проблема не работает - PullRequest
0 голосов
/ 17 декабря 2018

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

Это код, который я использую:

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)

Он должен вывести что-то вроде координат здесь, но затем также координату az: coordinate 1 (41.147123353981485, -2812171.2728945166) coordinate 2 (150013715707.77917, 2374319765.821534)

Но выводит следующее:

Солнце 0,0, 0,0, 0,0

Земля 149600000000.0, 0,0, 0,0

Примечание: Возможно, проблема в циклах for или в округлении суммы массивов, но я не уверен.

Ответы [ 2 ]

0 голосов
/ 20 декабря 2018

изображение - 1000 слов

enter image description here

Прямые ошибки в вашем коде:

  • Вы вычисляетефорсировать в неправильном направлении, это должно быть 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.Промежуточное округление до целочисленных значений не имеет смысла, оно лишь несколько снижает точность.


Я изменил ваш код, чтобы использовать массивы в качестве векторов, отделив вычисление ускорения от Эйлераобновления, убрал бессмысленное округление до целочисленных значений во время численного моделирования, удалил неиспользуемые переменные и поля, удалил промежуточные переменные для вычислений силы / ускорения, чтобы напрямую обновить поле ускорения, изменил цикл, чтобы использовать время, чтобы замечать, когда год(или 10) прошло (ваш код повторяется более 100 лет с шагом 0,1 дня, это было задумано?), ... и добавил Венеру к телам и добавил код для создания изображений, результат см. выше.

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

enter image description here

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()
0 голосов
/ 17 декабря 2018

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

Представьте себе, что «тела» - это абсолютно неизменное значение, которое определяет состояние системы в определенный момент времени.:

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)

Мне нравится использовать кортежи для такого рода вещей, чтобы избежать случайного изменения списка в какой-либо другой области (поскольку списки изменчивы).

...