#gpu-accelerated #gravity #particles #n-body #physics #physics-simulation #interaction

particular

使用 Rust 编写的 N-body 模拟库,具有 BarnesHut 和 GPU 加速算法

17 个不稳定版本 (6 个破坏性更新)

0.7.0 2024 年 3 月 21 日
0.6.1 2023 年 7 月 19 日
0.5.2 2023 年 5 月 15 日
0.5.1 2023 年 3 月 30 日
0.3.1 2022 年 11 月 26 日

#3 in 模拟

MIT/Apache

115KB
2K SLoC

Particular

showcase gif

MIT/Apache 2.0 Crates.io Docs

Particular 是一个提供在 Rust 中简单模拟粒子 N-body 引力相互作用的包。

变更日志

目标

该包的主要目标是向用户提供一个简单的 API 来设置 N-body 引力模拟,这些模拟可以轻松集成到现有的游戏和物理引擎中。因此,它不涉及数值积分或其他类似工具,而只关注加速度计算。

Particular 还注重性能,并提供多种计算粒子间加速度的方法。

计算算法

目前可用的计算方法使用了 2 种算法:暴力法Barnes-Hut 模拟

一般来说,暴力算法更精确,但速度较慢。Barnes-Hut 算法可以通过增加 theta 参数以速度换取精度。
您可以在这里了解更多关于它们的相对性能。

Particular 使用 rayon 进行并行化,并使用 wgpu 进行 GPU 计算。
启用相应的 parallelgpu 功能以访问可用的计算方法。

使用 Particular

Particular 由两个“模块”组成,一个用于处理不同浮点类型和维度之间计算引力的抽象,另一个用于简化用户定义和非用户定义类型的抽象使用。对于大多数简单用例,后者就足够了。

简单用法

Particle 接口提供了在 N 维空间中对象位置和质量的内部表示与外部类型之间的主要抽象层,通过定义获取位置和引力参数的方法来实现。
这些方法分别返回一个标量数组和标量,通过使用 point_mass 方法来与底层算法实现进行接口转换。

实现 Particle 接口

当可能时,在类型上实现 Particle 是有用的。

推导

当类型有名为 positionmu 的字段时使用。

#[derive(Particle)]
#[dim(3)]
struct Body {
    position: Vec3,
    mu: f32,
//  ...
}
手动实现

当类型不直接提供位置和引力参数时使用。

struct Body {
    position: Vec3,
    mass: f32,
//  ...
}

impl Particle for Body {
    type Array = [f32; 3];

    fn position(&self) -> [f32; 3] {
        self.position.into()
    }

    fn mu(&self) -> f32 {
        self.mass * G
    }
}

如果您不能在类型上实现 Particle,您可以使用它实现了数组及其标量类型的元组的实现,而不是创建一个中间类型。

let particle = ([1.0, 1.0, 0.0], 5.0);

assert_eq!(particle.position(), [1.0, 1.0, 0.0]);
assert_eq!(particle.mu(), 5.0);

计算和使用引力加速度

为了计算粒子的加速度,您可以使用迭代器上的 accelerations 方法,传入一个对您选择的 ComputeMethod 的可变引用。它返回每个迭代项的加速度,并保留原始顺序。
因为这个方法将映射的粒子收集在 ParticleReordered 中以优化质量为零的粒子的力计算,这个方法调用会导致额外的分配。有关如何退出的信息,请参阅 高级用法 部分。

当迭代类型实现了 Particle
for (acceleration, body) in bodies.iter().accelerations(&mut cm).zip(&mut bodies) {
    body.velocity += Vec3::from(acceleration) * DT;
    body.position += body.velocity * DT;
}
当迭代类型没有实现 Particle
// Items are a tuple of a velocity, a position and a mass.
// We map them to a tuple of the positions as an array and the mu,
// since this implements `Particle`.
let accelerations = items
    .iter()
    .map(|(_, position, mass)| (*position.as_array(), *mass * G))
    .accelerations(&mut cm);

for (acceleration, (velocity, position, _)) in accelerations.zip(&mut items) {
    *velocity += Vec3::from(acceleration) * DT;
    *position += *velocity * DT;
}

高级用法

在某些情况下,particular 提供的迭代器抽象可能不够灵活。例如,您可能需要访问用于 Barnes-Hut 算法的粒子构建的树,想计算两个不同粒子集合之间的引力,或者同时做这两件事。

PointMass 类型

存储中使用的底层类型是 PointMass,它是 N 维空间中位置和引力参数的简单表示。您可以直接使用不同的泛型方法来计算 PointMass 之间的引力,这些方法针对标量和 simd 类型进行了优化,而不需要通过 ComputeMethod

示例
use particular::math::Vec2;

use storage::PointMass;

let p1 = PointMass::new(Vec2::new(0.0, 1.0), 1.0);
let p2 = PointMass::new(Vec2::new(0.0, 0.0), 1.0);
let softening = 0.0;

assert_eq!(p1.force_scalar::<false>(p2.position, p2.mass, softening), Vec2::new(0.0, -1.0));

存储和内置的 ComputeMethod 实现方案

存储是容器,便于在计算粒子的引力加速度时,对粒子集合应用某些优化或算法。

《ParticleSystem》存储定义了一个受影响的粒子切片和一个《massive》存储,允许算法计算《massive》存储中的粒子对《affected》粒子的引力。它用于实现大多数计算方法,并且与其他存储的通用实现允许使用《ParticleSliceSystem》或《ParticleTreeSystem》实现的《ComputeMethod》也适用于其他存储。

《ParticleReordered》类似地定义了一个粒子切片,但将这些粒子存储在一个《ParticleOrdered》中。这两个存储使得算法在计算粒子的引力时能够轻松跳过没有质量的粒子。

示例
use particular::math::Vec3;

let particles = vec![
    // ...
];

// Create a `ParticleOrdered` to split massive and massless particles.
let ordered = ParticleOrdered::from(&*particles);

// Build a `ParticleTree` from the massive particles.
let tree = ParticleTree::from(ordered.massive());

// Do something with the tree.
for (node, data) in std::iter::zip(&tree.get().nodes, &tree.get().data) {
    // ...
}

let bh = &mut sequential::BarnesHut { theta: 0.5 };
// The implementation computes the acceleration exerted on the particles in
// the `affected` slice.
// As such, this only computes the acceleration of the massless particles.
let accelerations = bh.compute(ParticleSystem {
    affected: ordered.massless(),
    massive: &tree,
});

自定义《ComputeMethod》实现

为了与尽可能多的案例工作,内置的计算方法实现可能不适合或未针对您的特定用例进行优化。您可以在自己的类型上实现《ComputeMethod》特质以满足特定要求,如果需要实现其他算法也可以。

示例
use particular::math::Vec3;

struct MyComputeMethod;

impl ComputeMethod<ParticleReordered<'_, Vec3, f32>> for MyComputeMethod {
    type Output = Vec<Vec3>;

    #[inline]
    fn compute(&mut self, storage: ParticleReordered<Vec3, f32>) -> Self::Output {
        // Only return the accelerations of the massless particles.
        sequential::BruteForceScalar.compute(ParticleSystem {
            affected: storage.massless(),
            massive: storage.massive(),
        })
    }
}

许可证

此项目可选择在以下许可证下使用:Apache License, Version 2.0([Apache许可证](https://github.com/Canleskis/particular/blob/main/LICENSE-APACHE))或MIT许可证([MIT许可证](https://github.com/Canleskis/particular/blob/main/LICENSE-MIT))。

贡献

除非您明确声明,否则根据Apache 2.0许可证定义,您有意提交给本项目包含的贡献将根据上述许可证双授权,没有附加条款或条件。

依赖

约2-34MB
约512K SLoC