#gravity #nbody

bigbang

An optimized framework for n-(hard)-body gravitational simulation

10 releases

0.0.11 May 2, 2021
0.0.10 May 2, 2021
0.0.9 Jan 23, 2020
0.0.6 Dec 8, 2019
0.0.5 Sep 19, 2019

#61 in Simulation

Custom license

1.5MB
739 lines

docs.rs crates.io Build Status Have you used this project in your work? I'd love to hear about it and work with you. Email me at alex@alex-hansen.com.

About the project

This is a project in re-implementing a C++ particle simulation in Rust for speed comparison purposes. I originally created this tree at Trinity University with Dr. Mark Lewis around 2015. Rust changed a lot in the following years, and so I re-wrote it in 2019.

example in 3d 3d websocket-based simulation is available in the examples directory and was provided by Casey Primozic.

What exactly does it do?

It constructs a spatial k-dimensional tree (3 dimensions) and optimally calculates gravitational acceleration forces and collisions.

Getting started with bigbang

Implementing the AsEntity and Responsive traits

In order to use your arbitrary type inside this tree, your struct must be AsEntity + Responsive + Clone + Send + Sync. I'd like to eventually get rid of the Clone requirement, but currently the tree works in an immutable way where each time step an entirely new tree is constructed with the gravitational acceleration applied to it. This makes parallelism easier to reason about and safer, and requires Clone. Send and Sync are required for the parallelism.

AsEntity requires that you represent your struct as a gravitational entity, which entails providing velocity and position vectors, as well as a radius and a mass (all bodies are spheres in this simulation).

The real meat and potatoes you must implement is the trait Responsive. This trait defines how your struct responds to the simulation's results at every time step.

impl bigbang::AsEntity for MyStruct {
    fn as_entity(&self) -> bigbang::Entity;
}

impl bigbang::Responsive for MyStruct {
    fn respond(&self, simulation_result: bigbang::SimulationResult, time_step: f64) -> Self;
}

as_entity must take your struct and return it as a gravitational entity consisting of a velocity vector, a position vector, a radius, and a mass:

struct MyEntity {
    pub vx: f64,
    pub vy: f64,
    pub vz: f64,
    pub x: f64,
    pub y: f64,
    pub z: f64,
    pub radius: f64,
    pub mass: f64,
}

If your struct has these fields and they are named this way already, you can derive the AsEntity trait.

use bigbang::AsEntity;
#[derive(AsEntity)]
struct MyEntity {
    pub vx: f64, // because these fields are named 
    pub vy: f64, // the standard way, the same as in
    pub vz: f64, // the `AsEntity` trait, we can use
    pub x: f64,  // the automatic derive.
    pub y: f64,
    pub z: f64,
    pub radius: f64,
    pub mass: f64,
}

respond(&self, simulation_result: SimulationResult, time_step: f64) -> Self allows the user to decide how to respond to the simulation results. There are examples of this throughout the examples directory, as well as some helper collision calculation functions in the collisions module.

Starting the Simulation

Now that you have a compliant type with sufficient trait implementations, you may construct a vector with the starting positions for all of these entities. Pass a mutable reference to that vector and a time_step coefficent into GravTree::new() and you'll be off to the races:

use bigbang::{ GravTree, AsEntity };

struct MyEntity { ... }

impl AsEntity for MyEntity { ... }

impl Responsive for MyEntity { ... }

let mut my_vec:Vec<MyEntity> = vec![entity1, entity2, entity3];
let grav_tree = GravTree::new(&my_vec, 0.2);

The time_step coefficient is later passed into respond(). It can be used to effectively control the granularity of the simulation, i.e. how much each simulation frame actually impacts the movement of the entities. A smaller time_step will result in a more granular, more precise simulation. You'll probably have to play around with the constants a little bit to find something ideal for your use case. In order to advance the simulation, call grav_tree.time_step().

See the examples directory for a minimalist working example.

C/C++ Interface

There used to be a robust C/C++ interface for this library, but it was difficult to maintain with rapid API changes during development. If you were using this FFI, and would like for it to be re-introduced, please contact me and I will help you set it up. Otherwise, FFI is on hold until this crate stabilizes.

Dependencies