3
3

今日は何の日。燃えるゴミの日。GPUで圧力計算の並列化。

Last updated at Posted at 2024-08-11
前回のあらすじ。

3Dヒートマップを、各時間ステップごとに5つのグラフをプロットするように設定します。これにより、各ステップでの圧力分布の変化を3Dで視覚的に確認できます。

image.png

image.png

image.png

image.png

image.png

燃える炎のコード。

import numpy as np
import matplotlib.pyplot as plt
from scipy.stats import multivariate_normal
from mpl_toolkits.mplot3d import Axes3D

# パラメータ設定
num_particles = 1000
num_steps = 5
grid_size = 50
sigma_x = 1.0  # 横方向の広がり
sigma_y = 0.5  # 縦方向の広がり
sigma_z = 1.5  # z方向の広がり

# グリッドを作成
x_grid = np.linspace(-5, 5, grid_size)
y_grid = np.linspace(-5, 5, grid_size)
z_grid = np.linspace(-5, 5, grid_size)
x, y, z = np.meshgrid(x_grid, y_grid, z_grid)
pos = np.vstack([x.ravel(), y.ravel(), z.ravel()]).T

# パーティクルの初期位置を原点に設定
x_initial = np.zeros(num_particles)
y_initial = np.zeros(num_particles)
z_initial = np.zeros(num_particles)

# パーティクルの速度をガウス分布に基づいて生成
vx = np.random.normal(0, sigma_x, num_particles)
vy = np.random.normal(0, sigma_y, num_particles)
vz = np.random.normal(0, sigma_z, num_particles)

# 圧力分布を保存するリスト
pressure_distributions = []

# シミュレーションの実行
for step in range(num_steps):
    # パーティクルの位置を更新
    x_positions = x_initial + vx * step
    y_positions = y_initial + vy * step
    z_positions = z_initial + vz * step

    # 各パーティクルの位置に基づいて圧力分布を計算
    pressure = np.zeros((grid_size, grid_size, grid_size))
    for i in range(num_particles):
        # 各パーティクルがグリッドに与える影響をガウスカーネルで計算
        particle_pos = np.array([x_positions[i], y_positions[i], z_positions[i]])
        rv = multivariate_normal(mean=particle_pos, cov=[[sigma_x**2, 0, 0], [0, sigma_y**2, 0], [0, 0, sigma_z**2]])
        pressure += rv.pdf(pos).reshape(grid_size, grid_size, grid_size)

    pressure_distributions.append(pressure)

# 3Dプロットの設定
fig = plt.figure(figsize=(15, 15))

for step in range(num_steps):
    ax = fig.add_subplot(3, 2, step + 1, projection='3d')
    
    # グリッド座標を生成
    x_plot, y_plot, z_plot = np.meshgrid(np.linspace(-5, 5, grid_size),
                                         np.linspace(-5, 5, grid_size),
                                         np.linspace(-5, 5, grid_size))

    # 圧力の高い部分をプロット
    ax.scatter(x_plot, y_plot, z_plot, c=pressure_distributions[step].ravel(), cmap='hot', marker='o')
    ax.set_title(f'Step {step + 1}')
    ax.set_xlabel('X')
    ax.set_ylabel('Y')
    ax.set_zlabel('Z')

plt.tight_layout()
plt.show()

GPU ハイレゾリューション。 num_particles = 5000  Google corab GPU 計算時間117秒。

grid_size = 100

image.png

image.png

image.png

image.png

変更点:
圧力計算の並列化: CuPyでの並列化を活かし、各パーティクルの位置に対してグリッド全体を一度に計算するようにしました。このため、ループの中で各パーティクルに対する計算が一斉に行われる形になります。
cp.expの使用: 距離計算を直接行い、それをcp.expでまとめて計算し、圧力をグリッド全体で更新しています。
これにより、パーティクル数が増えてもGPUの並列処理の力をフルに活かすことができ、全体の計算速度が向上します。

import cupy as cp
import matplotlib.pyplot as plt
from scipy.stats import multivariate_normal
from mpl_toolkits.mplot3d import Axes3D

# パラメータ設定
num_particles = 5000
num_steps = 5
grid_size = 50
sigma_x = 1.0  # 横方向の広がり
sigma_y = 0.5  # 縦方向の広がり
sigma_z = 1.5  # z方向の広がり

# グリッドを作成
x_grid = cp.linspace(-5, 5, grid_size)
y_grid = cp.linspace(-5, 5, grid_size)
z_grid = cp.linspace(-5, 5, grid_size)
x, y, z = cp.meshgrid(x_grid, y_grid, z_grid)
pos = cp.vstack([x.ravel(), y.ravel(), z.ravel()]).T

# パーティクルの初期位置を原点に設定
x_initial = cp.zeros(num_particles)
y_initial = cp.zeros(num_particles)
z_initial = cp.zeros(num_particles)

# パーティクルの速度をガウス分布に基づいて生成
vx = cp.random.normal(0, sigma_x, num_particles)
vy = cp.random.normal(0, sigma_y, num_particles)
vz = cp.random.normal(0, sigma_z, num_particles)

# 圧力分布を保存するリスト
pressure_distributions = []

# シミュレーションの実行
for step in range(num_steps):
    # パーティクルの位置を更新
    x_positions = x_initial + vx * step
    y_positions = y_initial + vy * step
    z_positions = z_initial + vz * step

    # 各パーティクルの位置に基づいて圧力分布を計算
    pressure = cp.zeros((grid_size, grid_size, grid_size))

    # 各パーティクルの位置をグリッドに対してベクトル化して計算
    for i in range(num_particles):
        particle_pos = cp.array([x_positions[i], y_positions[i], z_positions[i]])
        dist = pos - particle_pos  # 各グリッドポイントからパーティクルまでの距離
        pressure += cp.exp(-0.5 * (dist[:, 0]**2 / sigma_x**2 +
                                   dist[:, 1]**2 / sigma_y**2 +
                                   dist[:, 2]**2 / sigma_z**2)).reshape(grid_size, grid_size, grid_size)

    pressure_distributions.append(pressure)

# 5枚目の3Dプロットの設定
fig = plt.figure(figsize=(10, 8))
ax = fig.add_subplot(111, projection='3d')

# グリッド座標を生成
x_plot, y_plot, z_plot = cp.meshgrid(cp.linspace(-5, 5, grid_size),
                                     cp.linspace(-5, 5, grid_size),
                                     cp.linspace(-5, 5, grid_size))

# 圧力の高い部分をプロット
ax.scatter(x_plot.get(), y_plot.get(), z_plot.get(), c=pressure_distributions[4].ravel().get(), cmap='hot', marker='o')
ax.set_title('Step 5')
ax.set_xlabel('X')
ax.set_ylabel('Y')
ax.set_zlabel('Z')

plt.tight_layout()
plt.show()

3
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
3