Body

BodyProxy Design Pattern

The simulation uses a Structure-of-Arrays (SoA) layout for its core physics data (implemented in the src.body.Bodies class) to maximize cache efficiency and enable highly vectorized computations with NumPy. While this is excellent for performance, directly manipulating individual body properties in an SoA can sometimes be less intuitive than an Array-of-Structures (AoS) approach, where each body is a distinct object with its own attributes.

To bridge this gap, the src.body.BodyProxy class implements the Proxy design pattern.

Purpose of BodyProxy:

The src.body.BodyProxy acts as a convenient, object-oriented wrapper around a single body’s data within the src.body.Bodies container. It provides an AoS-like interface, allowing developers to access and modify properties of an individual body (e.g., body.x for position, body.v for velocity) as if it were a standalone object.

How it Works:

  1. Reference to SoA: Each src.body.BodyProxy instance holds a reference to the main src.body.Bodies container and the _index of the specific body it represents.

  2. Property Accessors: All properties (like m, r, x, v, q, etc.) are implemented as Python properties that directly access the corresponding slice of the NumPy arrays within the src.body.Bodies container using the stored _index.

  3. Mutable Operations: Methods like set_acceleration, accumulate_acceleration, and apply_impulse directly modify the underlying NumPy arrays in the src.body.Bodies container at the specified index.

Benefits:

  • Readability and Ergonomics: Provides a more natural, object-oriented way to interact with individual bodies, improving code readability and developer experience.

  • Performance Preservation: Crucially, it does so without sacrificing the performance benefits of the underlying SoA layout, as the actual data remains contiguous and accessible for vectorized operations.

  • Encapsulation: It encapsulates the details of the SoA implementation, allowing the rest of the codebase to work with individual bodies more abstractly.

This pattern allows the simulation to leverage the best aspects of both data organization strategies: high-performance vectorized operations on the src.body.Bodies container and intuitive, object-oriented access to individual bodies via src.body.BodyProxy.

Module Overview

Inheritance diagram of src.body

Provides a Structure-of-Arrays (SoA) container for simulation bodies and a proxy for Array-of-Structures (AoS)-like access.

The Bodies class uses an SoA layout (e.g., all masses in one array, all positions in another) for cache-efficient computations. The BodyProxy class provides a convenient AoS-like interface (body.x, body.v) for a single body, combining the best of both worlds.

module:

body

author:

Le Bars, Yoann

class src.body.Bodies(n: int)[source]

Bases: object

Structure-of-Arrays (SoA) container for all bodies in the simulation. This layout improves cache performance by storing related data contiguously.

Variables:
  • m (np.ndarray) – Mass of each body.

  • inv_m (np.ndarray) – Inverse mass of each body.

  • r (np.ndarray) – Radius of each body.

  • i_inv (np.ndarray) – Inverse moment of inertia of each body.

  • x (np.ndarray) – Position of each body (n, 3).

  • v (np.ndarray) – Velocity of each body.

  • a (np.ndarray) – Acceleration of each body.

  • q (np.ndarray) – Orientation of each body.

  • omega (np.ndarray) – Angular velocity of each body.

  • alpha (np.ndarray) – Angular acceleration of each body.

  • torque (np.ndarray) – Net torque applied to each body in a frame.

  • _size (int) – Number of bodies.

a: ndarray
alpha: ndarray
emplace_back(m: float, r: float, x0: ndarray, v0: ndarray) None[source]

Adds a new body to the data structure.

Parameters:
  • m (float) – Body mass.

  • r (float) – Body radius.

  • x0 (np.ndarray) – Body position.

  • v0 (np.ndarray) – Body velocity.

i_inv: ndarray
integrate_part1(dt: float) None[source]

Velocity Verlet: Part 1 (Vectorised). Updates positions and half-updates velocities for all bodies.

Parameters:

dt (float) – Time step.

integrate_part2(dt: float) None[source]

Velocity Verlet: Part 2 (Vectorised). Completes the velocity updates for all bodies.

Parameters:

dt (float) – Time step.

inv_m: ndarray
m: ndarray
omega: ndarray
q: ndarray
r: ndarray
torque: ndarray
v: ndarray
x: ndarray
class src.body.BodyProxy(bodies: Bodies, index: int)[source]

Bases: object

A proxy object that provides an AoS-like interface to a body stored in the Bodies SoA container.

The Bodies class uses a Structure-of-Arrays (SoA) layout for performance, where all positions are in one array, all velocities in another, etc. This is ideal for vectorised physics calculations.

However, it can be inconvenient to work with a single body’s properties. This BodyProxy class solves that problem by implementing the Proxy design pattern. It provides a classic Array-of-Structures (AoS) interface (e.g., body.x, body.v) for a single body, while the underlying data remains in the efficient SoA format. This gives us the best of both worlds: high-performance vectorised operations on the Bodies container and intuitive, object-oriented access to individual bodies.

Variables:
  • _bodies (Bodies) – The main SoA container.

  • _index (int) – The index of the body this proxy refers to.

property a: ndarray

Body acceleration.

accumulate_acceleration(accel: ndarray) None[source]

Accumulate an acceleration.

Parameters:

accel (np.ndarray) – Acceleration vector.

accumulate_angular_acceleration(angular_accel: ndarray) None[source]

Accumulate an angular acceleration.

Parameters:

angular_accel (np.ndarray) – Angular acceleration vector.

accumulate_torque(torque_vec: ndarray) None[source]

Accumulate a torque vector.

Parameters:

torque_vec (np.ndarray) – Torque vector to add.

property alpha: ndarray

Body angular acceleration.

apply_impulse(j_total: ndarray, r_vec: ndarray, epsilon: float) None[source]

Apply an impulse to the body, updating its linear and angular velocity based on the impulse-momentum theorem.

Parameters:
  • j_total (np.ndarray) – Total impulse vector.

  • r_vec (np.ndarray) – Vector from the body’s centre of mass to the point of impulse application.

  • epsilon (float) – Small value to avoid division by zero.

dampen_velocity(linear_damping: float, angular_damping: float) None[source]

Applies damping to linear and angular velocities to stabilise the simulation.

Parameters:
  • linear_damping (float) – Damping factor for linear velocity.

  • angular_damping (float) – Damping factor for angular velocity.

property i_inv: float

Inverse of the moment of inertia.

property inv_m: float

Inverse of the body mass.

property m: float

Body mass.

property omega: ndarray

Body angular velocity.

property q: Rotation

Body orientation.

property r: float

Body radius.

set_acceleration(new_a: ndarray) None[source]

Set the acceleration.

Parameters:

new_a (np.ndarray) – New acceleration vector.

property torque: ndarray

Net torque on the body for the current frame.

property v: ndarray

Body velocity.

property x: ndarray

Body position.