Engee 文档
Notebook

粒子运动建模,重力可视化

重力是自然界的基本力量之一,它决定了天体和物体在其表面上的运动。 然而,引力在太阳系的不同行星上显着变化:从水星上的3.7m/s2到木星上的24.79m/s2。 这些差异不仅仅是教科书中的数字—它们从根本上改变了物体运动的物理学。

在本文中,我们将分析一个交互式示例,该示例清楚地演示了相同粒子在不同行星重力影响下的行为方式。 该示例不仅展示了物理原理,还演示了该语言从计算和可视化到计算优化的各种可能性。

让我们一步一步地看一下代码,从基本的物理常量开始,到创建交互式可视化结束。 这将帮助您不仅了解重力物理学,还了解编程科学模拟的实际方面。

In [ ]:
using Random

我们首先要看的是 PLANET_GRAVITY 字典是一个常数,包含各种天体的重力加速度(g)的值。

In [ ]:
PLANET_GRAVITY = Dict(
    "汞,汞" => 3.7,
    "金星" => 8.87,
    "地球" => 9.81,
    "火星" => 3.711,
    "木星" => 24.79,
    "土星" => 10.44,
    "铀" => 8.69,
    "海王星" => 11.15,
    "月亮" => 1.62
)
Out[0]:
Dict{String, Float64} with 9 entries:
  "Земля"    => 9.81
  "Марс"     => 3.711
  "Уран"     => 8.69
  "Юпитер"   => 24.79
  "Меркурий" => 3.7
  "Сатурн"   => 10.44
  "Нептун"   => 11.15
  "Венера"   => 8.87
  "Луна"     => 1.62

进一步 Particle —这是一个可变结构,在模拟中描述单个粒子。

结构字段:

  1. x::Float32, y::Float32 -粒子在二维空间中的坐标

    -使用 Float32 在处理多个粒子时节省内存

  2. vx::Float32, vy::Float32 -粒子速度沿X轴和Y轴的分量

    -确定运动的方向和速度

  3. color::Symbol -用于可视化的粒子颜色

    -例如: :red, :blue, :green

In [ ]:
mutable struct Particle
    x::Float32
    y::Float32
    vx::Float32
    vy::Float32
    color::Symbol
end

代码中的第一个函数是 generate_same_particles() 它创建6个相同的粒子与固定的初始条件:

  • Random.seed!(42) 设置随机数生成器的晶粒,确保每次运行时生成相同

-坐标和速度的范围:

-X: -3 以前 3 (rand() * 6 - 3)
-Y: 2 以前 4 (rand() * 2 + 2)-粒子是在顶部创建的
-Vx: -1 以前 1 (rand() * 2 - 1)-随机水平速度
-Vy: 0 -零初始垂直速度

-颜色从固定列表中按顺序分配。 [:red, :blue, :green, :orange, :purple, :cyan]

该函数返回一个由6个粒子组成的数组 Particle[],这将用于在相同的初始条件下比较不同行星上的重力。

实现中有一个有趣的功能,带有后缀的数字 f0 (6f0, 3f0, 2f0, 0f0)是Float32类型的显式指示,以优化性能。 在Julia中,浮点数是默认值(6.0)有一个类型 Float64 (双精度)。 使用 Float32 它提供了两个关键优势:

  1. 保存内存-每个人 Float32 它需要4个字节而不是8个字节。 Float64
  2. 加速计算

用于可视化和物理精度模拟 Float32 (±7个十进制数字)是完全足够的,这使您可以在更短的时间内处理更多的粒子或动画帧。

In [ ]:
function generate_same_particles()
    Random.seed!(42)
    colors = [:red, :blue, :green, :orange, :purple, :cyan]
    particles = Particle[]
    for i in 1:6
        push!(particles, Particle(
            rand(Float32) * 6f0 - 3f0,  # x:-3至3
            rand(Float32) * 2f0 + 2f0,  # y:2至4
            rand(Float32) * 2f0 - 1f0,  # vx:-1至1
            0f0,                        # vy: 0
            colors[i]                   # 固定颜色
        ))
    end
    return particles
end
Out[0]:
generate_same_particles (generic function with 1 method)

功能 simulate_planet_same_particles 它是物理模拟的核心,该模拟在特定行星上的重力影响下实现粒子运动的数值积分。 它需要三个关键参数:重力加速度的值 g,初始粒子阵列 initial_particles 和时间采样参数为一步 dt 和步数 steps.

在函数开始时,通过以下方式创建原始粒子的深度副本 deepcopy,这保证了使用相同初始条件对不同行星进行模拟的独立性。 数组被初始化以存储完整的运动路径 x_trajy_traj,其大小对应于时间片的总数。 主要的计算是在一个双周期中进行的:外部的经过时间步长,内部的经过所有的粒子。 对于每个粒子,首先固定其当前坐标,之后,如果这不是最后一步,则应用物理模型。 该模型包括两个分量:重力加速度,根据公式增加粒子的垂直速度 p.vy -= g * dt,并通过位置的运动学更新 p.x += p.vx * dtp.y += p.vy * dt.

特别注意边界条件的处理。 与地板碰撞时(当坐标 y 粒子以垂直速度的20%和水平速度的10%的损失进行反射,这模拟了非弹性冲击。 同样,当到达侧壁时,粒子从它们反射,失去10%的水平速度。 这些系数确保运动随时间的实际衰减。

在处理完当前步骤的所有粒子后,它们的坐标被存储在相应的轨迹数组中。 在循环结束时,该函数返回三个数组:所有时间步长的所有粒子的X和Y坐标序列,以及它们的颜色列表,以供后续可视化。 这种数据结构不仅可以重现运动动画,还可以分析粒子系统在给定引力影响下的演变。

In [ ]:
function simulate_planet_same_particles(g::Float32, initial_particles, dt::Float32, steps::Int)
    particles = deepcopy(initial_particles)
    n = length(particles)
    x_traj = Vector{Vector{Float32}}(undef, steps+1)
    y_traj = Vector{Vector{Float32}}(undef, steps+1)
    colors = [p.color for p in particles]
    for step in 0:steps
        x_vals = Vector{Float32}(undef, n)
        y_vals = Vector{Float32}(undef, n)
        for i in 1:n
            p = particles[i]
            x_vals[i] = p.x
            y_vals[i] = p.y
            if step < steps
                p.vy -= g * dt
                p.x += p.vx * dt
                p.y += p.vy * dt
                if p.y < 0
                    p.y = 0
                    p.vy = -0.8f0 * p.vy
                    p.vx *= 0.9f0
                end
                if p.x > 4
                    p.x = 4
                    p.vx = -0.9f0 * p.vx
                elseif p.x < -4
                    p.x = -4
                    p.vx = -0.9f0 * p.vx
                end
            end
        end
        x_traj[step+1] = copy(x_vals)
        y_traj[step+1] = copy(y_vals)
    end
    return x_traj, y_traj, colors
end
Out[0]:
simulate_planet_same_particles (generic function with 1 method)

最后一段代码组织了实验的整个周期,从设置参数到生成最终动画。 它从初始化关键变量开始:粒子的数量 n_particles 它被设置为6,时间步长 dt 它固定在0.04秒(表示类型 Float32),以及总模拟时间 total_time 需要10秒。 参数 skip_frames 值为2时,它确定每第二帧将用于动画,这减小了输出文件的大小,而不会显着丢失信息内容。 名单 planets 它包括九个天体进行比较分析。

基于指定的总时间和积分步长,计算时间步长的数目。 steps 根据公式 Int(floor(total_time / dt)). 这确保了模拟将在指定的时间段内准确完成。 然后使用函数 generate_same_particles() 正在创建具有固定初始条件的六个粒子的参考集—这是正确比较不同重力影响的基础。

接下来,开始每个行星的轨迹计算周期。 对于字典中的每个天体 PLANET_GRAVITY 自由落体加速度的对应值提取 g,之后调用函数 simulate_planet_same_particles. 它返回轨迹,粒子颜色和重力值的数组,它们存储在列表中。 planet_data. 这将创建一组完整的可视化数据。

块的最后一部分负责使用宏创建动画。 @animate 从图库。 外循环遍历帧,考虑到跳过,计算当前模型时间。 t. 对于每个时间点,用3×3布局构建通用图,其中每个子图对应于一个行星。 设置包括对比度的黑色背景,通用标题和标题中的动态更新时间。

内部循环处理每个子图:设置固定轴边界,添加表示行星表面的棕色线,并在保持配色方案的同时将粒子显示在其当前位置。 一个重要的因素是信息注释:重力的绝对值及其与地面的关系(地面以上的值用红色突出显示,地面以下的值用蓝色突出显示)。

生成所有帧后,动画将保存到文件中。 particles_all_planets.gif 以每秒22帧的速度。 结果是一个可视化的比较可视化,演示了相同的粒子系统在太阳系不同引力条件下的行为。

In [ ]:
println("所有行星的测试")
dt = 0.04f0
total_time = 10.0f0
skip_frames = 2
planets = ["汞,汞", "金星", "地球", "火星", 
           "木星", "土星", "铀", "海王星", "月亮"]
steps = Int(floor(total_time / dt))

println("粒子生成。..")
same_particles = generate_same_particles()

println("轨迹的计算。..")
planet_data = []
for planet in planets
    g = Float32(PLANET_GRAVITY[planet])
    data = simulate_planet_same_particles(g, same_particles, dt, steps)
    push!(planet_data, (data..., g))
end

println("创建动画。..")
anim = @animate for frame in 0:skip_frames:steps
    t = frame * dt
    plot(layout = (3, 3), 
         size = (1200, 900),
         title = "不同行星上的相同粒子",
         titlefontsize = 14,
         plot_title = "时间:$(round(t,digits=2))",
         plot_titlefontsize = 11,
         legend = false,
         bg = :black)
    
    for (idx, planet) in enumerate(planets)
        x_traj, y_traj, colors, g = planet_data[idx]
        actual_frame = min(frame + 1, length(x_traj))
        plot!(subplot = idx,
              xlim = (-4.5, 4.5),
              ylim = (-0.5, 5.5),
              aspect_ratio = :equal,
              title = planet,
              titlefontsize = 10)
        plot!([-4.5, 4.5], [0, 0], 
              linewidth = 3, 
              color = :brown,
              subplot = idx)
        scatter!(x_traj[actual_frame], y_traj[actual_frame],
                markersize = 6,
                markercolor = colors,
                markerstrokecolor = :white,
                alpha = 0.9,
                subplot = idx)

        gravity_ratio = round(g / 9.81, digits=2)
        color_code = g > 9.81 ? :red : :lightblue
        
        annotate!(3.0, 5.2, 
                 text("g = $(g)", 8, :white),
                 subplot = idx)
        annotate!(3.0, 4.8,
                 text("$(gravity_ratio)×地球", 8, color_code),
                 subplot = idx)
    end
end
gif(anim, "particles_all_planets.gif", fps = 22)
Тест для всех планет

Генерирация частиц...
Рассчёт траекторий...
Создаие анимации...
Out[0]:
No description has been provided for this image

结论

这个例子演示了重力如何决定物理系统的动力学:使用数值积分和优化计算,我们创建了九个天体上粒子运动的比较模拟,可以清楚地看到粒子在木星上的下落和反弹速度比在月球或火星上快得多。