在我對太陽周圍8顆行星的測試粒子模擬中,我有pos_output和vel_output python個列表。我想把這些保存到一個文件中,使其可讀。這些可以定期保存我的位置和速度。
我想要3列中的x, y, z
位置坐標和其他3列中Vx, Vy, Vz
速度分量。還有一個時間列,所以我知道什么時候,是我的x,y,z, Vx, Vy, Vz
保存的。
列應為:t, x, y, z, Vx, Vy, Vz
我嘗試了一些東西,但我只得到了位置和速度的8個輸出值。但有8顆行星,它應該有(t_end/interval)個輸出。所以我應該有很多位置和速度的輸出。請幫助我將輸出保存到可讀的txt文件或類似文件中。
My code:
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
# Constants
M_Sun = 1.989e30 #Solar Mass
G = 6.67430e-11 # m^3 kg^(-1) s^(-2)
yr = 365 * 24 * 60 * 60 #1 year in seconds
# Number of particles
num_particles = 8
# Initial conditions for the particles (m and m/s)
initial_pos = np.array([
[57.9e9, 0, 0], #Mercury
[108.2e9, 0, 0], #Venus
[149.6e9, 0, 0], #Earth
[228e9, 0, 0], #Mars
[778.5e9, 0, 0], #Jupiter
[1432e9, 0, 0], #Saturn
[2867e9, 0, 0], #Uranus
[4515e9, 0, 0] #Neptune
])
initial_vel = np.array([
[0, 47400, 0],
[0, 35000, 0],
[0, 29800, 0],
[0, 24100, 0],
[0, 13100, 0],
[0, 9700, 0],
[0, 6800, 0],
[0, 5400, 0]
])
# Steps
t_end = 0.004 * yr #Total time of integration
dt_constant = 0.1
intervals = 100000 #interval between time steps after which the position and velocities are to be saved
# Arrays to store pos and vel
pos = np.zeros((num_particles, int(t_end), 3))
vel = np.zeros((num_particles, int(t_end), 3))
# Leapfrog Integration (2nd Order)
pos[:, 0] = initial_pos
vel[:, 0] = initial_vel
pos_output = []
vel_output = []
t = 1
counter = 0
while t < int(t_end):
r = np.linalg.norm(pos[:, t - 1], axis=1)
acc = -G * M_Sun / r[:, np.newaxis]**3 * pos[:, t - 1] #np.newaxis for broadcasting with pos[:, i-1]
# Calculate the time step for the current particle
current_dt = dt_constant * np.sqrt(np.linalg.norm(pos[:, t - 1], axis=1)**3 / (G * M_Sun))
min_dt = np.min(current_dt) # Use the minimum time step for all particles
half_vel = vel[:, t - 1] + 0.5 * acc * min_dt
pos[:, t] = pos[:, t - 1] + half_vel * min_dt
# Recalculate acceleration with the new position
r = np.linalg.norm(pos[:, t], axis=1)
acc = -G * M_Sun / r[:, np.newaxis]**3 * pos[:, t] #np.newaxis for broadcasting with pos[:, i-1]
vel[:, t] = half_vel + 0.5 * acc * min_dt
t += 1
counter += 1
# Save the pos and vel here
if counter % intervals == 0:
pos_output.append(pos[:,counter].copy())
vel_output.append(vel[:,counter].copy())
# Convert lists to arrays
pos_output = np.concatenate(pos_output)
vel_output = np.concatenate(vel_output)
#save to file
np.savetxt('pos_output.txt', pos_output.reshape(-1, 3), fmt='%.6e', delimiter='\t', header='X\tY\tZ')
np.savetxt('vel_output.txt', vel_output.reshape(-1, 3), fmt='%.6e', delimiter='\t', header='VX\tVY\tVZ')
# Orbit Plot
fig = plt.figure(figsize=(8, 8))
ax = fig.add_subplot(111, projection='3d')
ax.scatter(0, 0, 0, color='yellow', marker='o', s=50, label='Sun')
for particle in range(num_particles):
x_particle = pos[particle, :, 0]
y_particle = pos[particle, :, 1]
z_particle = pos[particle, :, 2]
ax.plot(x_particle, y_particle, z_particle, label=f'Particle {particle + 1} Orbit (km)')
ax.set_xlabel('X (km)')
ax.set_ylabel('Y (km)')
ax.set_zlabel('Z (km)')
ax.legend(loc='upper right', bbox_to_anchor=(1.1, 1.1))
ax.set_title('Orbits of Planets around Sun (km)')
plt.show()
我現在花了很多時間處理你的代碼,所以我希望你能感謝我的輸入。也許我也誤解了一切,請告訴我。
編輯:在評論中提供了信息后,我更改了原來的答案,以更適合您的問題。
回答您的問題
因此,首先回答你的問題:我認為主要問題是你混淆了時間步長和時間:
t
是你當前時間步長的索引。然而,通過提示t < int(t_end)
,您假設每個時間步長都有一秒的長度(由于您的自適應時間步長,它顯然沒有)。因此,您需要將while條件更改為小于t_end
的實際時間(而不是索引)。我將引入一個時間數組來跟蹤時間,這意味著我們可以將其更改為:注意:但請記住,這將在時間過去后邁出一步
t_end
。由于自適應時間步長,我們不知道我們最終會有多少時間步長。這意味著我們不能像您(以及我在之前的回答中)那樣初始化
pos
和vel
arrays,而是需要將每個新結果添加到arrray中。這意味著我們只從兩個arrays的形狀(8, 1, 3)
(包含初始條件)開始。有趣的是,你使用了一個自適應的時間步長和一個像蛙跳一樣的辛積分器。正因為如此,你的時間步長并不總是一樣的。通過初始化時間數組來跟蹤當前時間步長當前所處的時間可能是一種很好的做法,該時間數組將是1d,當然從0開始。然后在循環內部為每個時間步長添加一個相同形狀的數組。也意味著我們不再需要變量
t
。相反,軸1上的索引-1
可以訪問先前時間步長的數量。我將為每個時間步長引入當前位置、速度和時間的新內部變量:并在從(8,3)變為(8,1,3)后將它們連接到完整的數組:
每隔一段時間存儲到新列表
現在我明白你在
if counter % intervals == 0:
后面的進球了。但你又一次混淆了時間步和時間。counter
只是第二個索引,在我看來這是很難理解的。但是intervals
你是如何描述它的,是一個時間間隔,所以以單位秒為單位。然而,用實際時間替換counter
也是不正確的,因為我們不知道時間是否真的達到了間隔的倍數。我認為一個簡單的解決方案是引入一個我們已經達到的新變量'next_interval_time(you can also change
intervalsif you prefer) that keeps track which multiple of
intervals。如果時間經過一個間隔,請將當前位置和速度附加到輸出列表中,并跟蹤時間:循環結束后,我會將其轉換為numpy數組并轉置以獲得相同的軸順序:
正在將arrays存儲到文件
最好將numpy arrays存儲為txt(精度較低,效率較低),但最好使用numpy存儲arrays的方法:
np.save('array.npy', array)
可以加載的
np.load('array.npy')
最后得到完整的數組。列表也是如此。在那里,您可以使用Python泡菜方法。但是,您將所有列表轉換為arrays。
我建議的代碼
我解決了您的問題,并實施了我提出的所有更改。我改變了
t_end =165*yr
,這是一個海王星年,所以應該給你看粒子8的一個軌道。我還改為intervals=1000000
,因為我發現時間步長對于間隔來說太大了(這意味著我們存儲了每個時間步長)。下面是完整的代碼:Result
我最終得到了預期的情節:
輸出arrays具有形狀
(8, 5203, 3)
。結束時間為165*yr = 5.203E9
,這意味著我們預計有5203個輸出(end_time/interval=5203.44)。我們可以稍后加載arrays,如前所述。Improvements
也許我想到了一些需要改進的地方:
Cheers!