pure-cpp 1.0.0
A C++ physics simulation benchmark comparing performance with Python implementations
space.hpp
Go to the documentation of this file.
1#ifndef SPACE_HPP
2#define SPACE_HPP
3
4/**
5 * \file space.hpp
6 * \brief N-body simulation space with gravitational interaction and collision
7 * response.
8 * \author Le Bars, Yoann
9 * \ingroup PhysicsCore
10 *
11 * This file defines the `Space` class, which manages the state and dynamics of
12 * all bodies in the simulation. The physics model includes:
13 *
14 * - **Gravitational Interaction**: All bodies exert a gravitational force on
15 * each other, calculated using Newton's law of universal gravitation.
16 *
17 * - **Collision Response**: Collisions between spherical bodies are handled
18 * using an impulse-based method that accounts for both linear and
19 * rotational motion, including friction.
20 *
21 * - **Integration**: The simulation state (position and velocity) is
22 * advanced using the **Velocity Verlet** algorithm, a time-reversible and
23 * energy-conserving semi-implicit Euler integration scheme.
24 *
25 * - **Broad-Phase Detection**: A k-d tree is used to efficiently find
26 * potentially colliding pairs of bodies, avoiding an O(N²) check.
27 *
28 * This file is part of the pure C++ benchmark.
29 *
30 * This program is free software: you can redistribute it and/or modify it
31 * under the terms of the GNU General Public License as published by the Free
32 * Software Foundation, either version 3 of the License, or (at your option)
33 * any later version.
34 *
35 * This program is distributed in the hope that it will be useful, but WITHOUT
36 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
37 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
38 * more details.
39 *
40 * You should have received a copy of the GNU General Public License along with
41 * this program. If not, see <https://www.gnu.org/licenses/>.
42 */
43
44#include <omp.h>
45
46#include <chrono>
47#include <cstddef>
48#include <cstdint>
49#include <memory>
50#include <optional>
51#include <stdexcept>
52#include <vector>
53
54#include "body.hpp"
55#include "config.hpp"
56#include "constants.hpp"
57#include "kdtree.hpp"
58
59// Forward-declare the test fixture class from the global namespace.
60class SpaceTest;
61
62namespace Model {
63
64 /**
65 * \brief A data container to hold all relevant information for a single
66 * collision event.
67 */
69 /// \brief A proxy to the first body in the collision.
71 /// \brief A proxy to the second body in the collision.
73 /// \brief The normalised vector pointing from body 1 to body 2.
74 Vector3d nVec_;
75 /// \brief The vector from the centre of body 1 to the contact point.
76 Vector3d r1Vec_;
77 /// \brief The vector from the centre of body 2 to the contact point.
78 Vector3d r2Vec_;
79 /// \brief The relative velocity between the two bodies at the contact
80 /// point.
81 Vector3d vRel_;
82 /// \brief The penetration depth of the two bodies.
83 double overlap_;
84 };
85
86 /**
87 * \brief Class describing a space in which move several bodies.
88 * \ingroup PhysicsCore
89 */
90 class Space {
91 public:
92 /* Grant the test fixture from the global namespace access to protected
93 and private members. */
94 friend class ::SpaceTest;
95
96 /// \brief Default constructor.
98
99 /**
100 * \brief Initialise the space.
101 *
102 * \param config The simulation configuration object containing all
103 * simulation parameters.
104 */
105 explicit Space(const Configuration::SimulationConfig& config);
106
107 /// \brief Destructor to clean up OpenMP locks.
108 ~Space();
109
110 /**
111 * \brief Number of bodies getter.
112 *
113 * \return The number of bodies in the space.
114 */
115 std::size_t n() const { return bodies_.size(); }
116
117 /**
118 * \brief Get a proxy to the i-th body.
119 *
120 * \param i Body index.
121 *
122 * \returns A mutable proxy object for the i-th body.
123 */
124 BodyProxy body(std::size_t i) { return bodies_[i]; }
125
126 /**
127 * \brief Get a const proxy to the i-th body.
128 *
129 * \param i Body index.
130 *
131 * \returns A const proxy object for the i-th body.
132 */
133 ConstBodyProxy body(std::size_t i) const { return bodies_[i]; }
134
135 /**
136 * \brief Get the time step used in the last completed simulation
137 * frame.
138 *
139 * \return The previous time step value.
140 */
141 [[nodiscard]] double getPreviousTimeStep() const {
142 return previous_dt_;
143 }
144
145 /**
146 * \brief Computes one full step of the simulation.
147 *
148 * This method orchestrates the main simulation loop, including:
149 * 1. Advancing positions and velocities (Velocity Verlet).
150 * 2. Detecting and resolving collisions.
151 * 3. Calculating new forces (gravity).
152 * \param iteration The current simulation iteration number.
153 */
154 void computeDynamics(std::size_t iteration);
155
156 /**
157 * \brief Calculates the kinetic and potential energy of the system.
158 * \return A tuple containing:
159 * 1. Translational Kinetic Energy
160 * 2. Rotational Kinetic Energy
161 * 3. Potential Energy
162 */
163 [[nodiscard]]
164 std::tuple<double, double, double> calculateSystemEnergy() const;
165
166 /**
167 * \brief Retrieves the current positions and orientations of all
168 * bodies.
169 * \return A tuple containing a vector of positions and a vector of
170 * quaternions.
171 */
172 [[nodiscard]] std::tuple<Vector3dVec, QuaterniondVec>
173 getAllBodyTransforms() const;
174
175 /**
176 * \brief Retrieves all data required for display for all bodies.
177 *
178 * \param out_positions Output vector for body positions.
179 * \param out_quaternions Output vector for body orientations.
180 * \param out_torques Output vector for body torques.
181 * \param out_alphas Output vector for body angular accelerations.
182 */
183 void getDataForDisplay(Vector3dVec& out_positions,
184 QuaterniondVec& out_quaternions,
185 Vector3dVec& out_torques,
186 Vector3dVec& out_alphas) const;
187 void printProfilingReport() const;
188
189 // --- Configuration Getters/Setters ---
190 /**
191 * \brief Get the universal gravitational constant.
192 * \return The gravitational constant G.
193 */
194 [[nodiscard]] double getG() const { return G_; }
195
196 /**
197 * \brief Set the universal gravitational constant.
198 * \param g The new gravitational constant value.
199 */
200 void setG(double g) { G_ = g; }
201
202 /**
203 * \brief Get the numerical precision threshold.
204 * \return The epsilon value.
205 */
206 [[nodiscard]] double getEpsilon() const { return epsilon_; }
207
208 /**
209 * \brief Set the numerical precision threshold.
210 * \param eps The new epsilon value.
211 */
212 void setEpsilon(double eps) { epsilon_ = eps; }
213
214 /**
215 * \brief Get the coefficient of restitution.
216 * \return The coefficient of restitution.
217 */
218 [[nodiscard]] double getCoeffRestitution() const {
219 return coeffRestitution_;
220 }
221
222 /**
223 * \brief Set the coefficient of restitution.
224 * \param e The new coefficient of restitution (typically in [0, 1]).
225 */
227
228 /**
229 * \brief Get the coefficient of kinetic friction.
230 * \return The coefficient of kinetic friction.
231 */
232 [[nodiscard]] double getCoeffFriction() const { return coeffFriction_; }
233
234 /**
235 * \brief Set the coefficient of kinetic friction.
236 * \param mu The new coefficient of kinetic friction.
237 */
238 void setCoeffFriction(double mu) { coeffFriction_ = mu; }
239
240 /**
241 * \brief Get the coefficient of static friction.
242 * \return The coefficient of static friction.
243 */
244 [[nodiscard]] double getCoeffStaticFriction() const {
246 }
247
248 /**
249 * \brief Set the coefficient of static friction.
250 * \param mu_s The new coefficient of static friction.
251 */
252 void setCoeffStaticFriction(double mu_s) {
254 }
255
256 /**
257 * \brief Get the linear damping factor.
258 * \return The linear damping factor.
259 */
260 [[nodiscard]] double getLinearDamping() const { return linearDamping_; }
261
262 /**
263 * \brief Set the linear damping factor.
264 * \param damping The new linear damping factor (typically in [0, 1]).
265 */
266 void setLinearDamping(double damping) { linearDamping_ = damping; }
267
268 /**
269 * \brief Get the angular damping factor.
270 * \return The angular damping factor.
271 */
272 [[nodiscard]] double getAngularDamping() const {
273 return angularDamping_;
274 }
275
276 /**
277 * \brief Set the angular damping factor.
278 * \param damping The new angular damping factor (typically in [0, 1]).
279 */
280 void setAngularDamping(double damping) { angularDamping_ = damping; }
281
282 /**
283 * \brief Get the current time step.
284 * \return The current time step value.
285 */
286 [[nodiscard]] double getTimeStep() const { return dt_; }
287
288 /**
289 * \brief Set the current time step.
290 * \param dt The new time step value.
291 */
292 void setTimeStep(double dt) { dt_ = dt; }
293
294 /**
295 * \brief Get the positional correction factor.
296 * \return The positional correction factor.
297 */
298 [[nodiscard]] double getPositionalCorrectionFactor() const {
300 }
301
302 /**
303 * \brief Set the positional correction factor.
304 * \param factor The new positional correction factor.
305 */
306 void setPositionalCorrectionFactor(double factor) {
308 }
309
310 /**
311 * \brief Check if diagnostics are enabled.
312 * \return True if diagnostics are enabled, false otherwise.
313 */
314 [[nodiscard]] bool isDiagnosticsEnabled() const {
315 return diagnosticsEnabled_;
316 }
317
318 /**
319 * \brief Enable or disable diagnostics.
320 * \param enabled True to enable diagnostics, false to disable.
321 */
322 void setDiagnosticsEnabled(bool enabled) {
323 diagnosticsEnabled_ = enabled;
324 }
325
326 // --- Test API (protected but accessible via friend) ---
327 /**
328 * \brief Provides direct access to the Bodies container for testing.
329 * \return A reference to the internal Bodies object.
330 * \note This method is public but intended for testing only.
331 * Access is controlled via the friend declaration.
332 */
333 [[nodiscard]] Bodies& getBodiesForTest() { return bodies_; }
334
335 /**
336 * \brief Factory method to create a CollisionContext if two bodies are
337 * colliding.
338 *
339 * This method is part of the internal collision handling API and is
340 * exposed to tests for unit testing collision logic.
341 *
342 * \param b1 First body.
343 * \param b2 Second body.
344 * \return A CollisionContext object if a collision occurs, otherwise
345 * `std::nullopt`.
346 * \note This method is public but intended for testing only.
347 */
348 [[nodiscard]] std::optional<CollisionContext> createCollisionContext(
349 BodyProxy& b1, BodyProxy& b2);
350
351 /**
352 * \brief Calculates and applies the normal impulse (restitution) for a
353 * collision.
354 *
355 * This method is part of the internal collision handling API and is
356 * exposed to tests for unit testing collision logic.
357 *
358 * \param ctx The collision context.
359 * \return A tuple containing the total impulse vector, its magnitude,
360 * and the magnitude of the bias impulse component.
361 * \note This method is public but intended for testing only.
362 */
363 [[nodiscard]] std::tuple<Vector3d, double, double>
365
366 /**
367 * \brief Calculates the tangential (frictional) impulse for a
368 * collision.
369 *
370 * This method is part of the internal collision handling API and is
371 * exposed to tests for unit testing collision logic.
372 *
373 * \param ctx The collision context.
374 * \param jn The magnitude of the normal impulse, used for the friction
375 * limit.
376 * \param impulseN The normal impulse vector.
377 * \return The friction impulse vector.
378 * \note This method is public but intended for testing only.
379 */
380 [[nodiscard]] Vector3d applyFrictionImpulse(const CollisionContext& ctx,
381 double jn,
382 const Vector3d& impulseN);
383
384 /**
385 * \brief A greedy graph colouring algorithm to assign a colour
386 * (integer) to each body such that no two adjacent bodies (i.e.,
387 * colliding bodies) share the same colour.
388 *
389 * This method implements a parallel greedy graph colouring algorithm
390 * used to partition collision pairs into independent sets. Bodies with
391 * the same colour can have their collisions resolved in parallel
392 * without conflicts.
393 *
394 * \par Algorithm Overview
395 * The algorithm uses an iterative approach with two passes per
396 * iteration:
397 * - <b>Pass 1</b>: Each body (in parallel) assigns itself the
398 * smallest available colour not used by any of its neighbours.
399 * - <b>Pass 2</b>: Detect conflicts where adjacent bodies share the
400 * same colour (can occur due to race conditions in parallel
401 * execution).
402 *
403 * The algorithm iterates until no conflicts remain.
404 *
405 * \par Example
406 * Consider a collision graph with 4 bodies:
407 * \code
408 * graph[0] = {1, 2} // Body 0 collides with bodies 1 and 2
409 * graph[1] = {0, 3} // Body 1 collides with bodies 0 and 3
410 * graph[2] = {0} // Body 2 collides with body 0
411 * graph[3] = {1} // Body 3 collides with body 1
412 * \endcode
413 *
414 * After colouring, a possible result is:
415 * \code
416 * colors[0] = 0 // Bodies 0 and 3 can be processed in parallel
417 * colors[1] = 1 // Body 1 can be processed alone
418 * colors[2] = 1 // Bodies 1 and 2 can be processed in parallel
419 * colors[3] = 0 // (same as body 0)
420 * \endcode
421 *
422 * \par Thread Safety
423 * This method uses OpenMP locks to synchronize access to the shared
424 * `colors_` vector. Reads of neighbour colours are protected by locks
425 * to avoid data races, while writes are minimal and lock-protected.
426 *
427 * \param graph The collision graph where `graph[i]` contains
428 * indices of bodies colliding with `i`.
429 * The graph should be undirected (if body i collides with
430 * j, then j should appear in graph[i] and i in graph[j]).
431 *
432 * \note This method is public but intended for testing only.
433 * \note The algorithm is guaranteed to terminate as conflicts decrease
434 * with each iteration (worst-case: O(N) iterations for N bodies).
435 */
436 void colorGraph(const std::vector<std::vector<std::size_t>>& graph);
437
438 private:
439 /**
440 * \brief Handles collision detection and response between two
441 * bodies.
442 *
443 * \param b1 A proxy to the first body in the collision.
444 * \param b2 A proxy to the second body in the collision.
445 */
446 void handleCollision(BodyProxy& b1, BodyProxy& b2);
447
448 /**
449 * \brief Initialises the bodies in the simulation with random
450 * properties.
451 *
452 * \param n The number of bodies to create.
453 * \param dens The density of the bodies.
454 * \param seed The seed for random number generation.
455 */
456 void initializeBodies(std::size_t n, double dens, unsigned int seed);
457
458 /**
459 * \brief Resolves interpenetration by moving bodies apart along the
460 * collision normal.
461 */
463
464 /**
465 * \brief Calculates the effective mass of two colliding bodies in a
466 * given direction.
467 *
468 * \param ctx The collision context.
469 * \param direction_vec The direction vector (e.g., normal or
470 * tangent). \return The scalar effective mass.
471 */
472 [[nodiscard]] double getEffectiveMass(const CollisionContext& ctx,
473 const Vector3d& direction_vec);
474
475 // --- Member Variables ---
476 // --- Simulation Stages ---
477 /**
478 * \brief Logs system energy and checks for instability if diagnostics
479 * are enabled.
480 * \param iteration The current simulation iteration number.
481 */
482 void logSystemEnergy(std::size_t iteration);
483
484 /**
485 * \brief Builds the collision graph using broad-phase and
486 * narrow-phase detection.
487 *
488 * This method implements a two-phase collision detection algorithm
489 * to efficiently identify all colliding body pairs in the simulation.
490 * The result is stored in `collisionGraph_`, where `collisionGraph_[i]`
491 * contains the indices of all bodies colliding with body `i`.
492 *
493 * \par Algorithm Overview
494 * The algorithm uses a two-phase approach:
495 *
496 * <b>Phase 1: Broad-Phase Detection (Spatial Acceleration)</b>
497 * - Rebuilds the k-d tree with current body positions
498 * - For each body, performs a radius search to find all bodies within
499 * a safe distance (body radius + maximum body radius in system)
500 * - Uses thread-local storage to collect candidate pairs in parallel
501 * - Complexity: O(N log N) where N is the number of bodies
502 *
503 * <b>Phase 2: Narrow-Phase Detection (Exact Collision Test)</b>
504 * - For each candidate pair from Phase 1, performs an exact collision
505 * test using distance and radius comparison
506 * - Only pairs that actually overlap are added to the collision graph
507 * - Complexity: O(C) where C is the number of candidate pairs
508 *
509 * \par Example
510 * Consider a system with 3 bodies:
511 * \code
512 * Body 0: position (0, 0, 0), radius 1.0
513 * Body 1: position (1.5, 0, 0), radius 1.0 // Overlaps with body 0
514 * Body 2: position (5, 0, 0), radius 1.0 // No collision
515 * \endcode
516 *
517 * After execution:
518 * \code
519 * collisionGraph_[0] = {1} // Body 0 collides with body 1
520 * collisionGraph_[1] = {0} // Body 1 collides with body 0
521 * collisionGraph_[2] = {} // Body 2 has no collisions
522 * \endcode
523 *
524 * \par Thread Safety
525 * This method is fully parallelized using OpenMP:
526 * - Broad-phase searches are performed in parallel using thread-local
527 * storage to avoid race conditions
528 * - Candidate pairs are collected per-thread and merged sequentially
529 * after parallel execution
530 * - The final collision graph is built sequentially from thread-local
531 * results
532 *
533 * \par Performance Optimizations
534 * - Maximum radius is cached and only recalculated when bodies are
535 * added/removed (not every frame)
536 * - Results vectors are pre-allocated to avoid repeated allocations
537 * - k-d tree is rebuilt each frame to reflect position changes
538 * - Thread-local storage minimizes lock contention
539 *
540 * \note This method is called once per simulation step in
541 * `computeDynamics()`.
542 * \note The collision graph is undirected: if body i collides with j,
543 * then j appears in `collisionGraph_[i]` and i appears in
544 * `collisionGraph_[j]`.
545 */
546 void buildCollisionGraph();
547
548 /// \brief Resolves all detected collisions using graph colouring.
549 void resolveCollisions();
550
551 /// \brief Calculates and applies gravitational forces, and completes
552 /// Velocity Verlet part 2.
553 void applyGravity(double current_dt, double linear_damping,
554 double angular_damping);
555
556 /**
557 * \brief Determines the optimal time step for the next frame based on
558 * current velocities and accelerations.
559 */
561
562 /// \brief Time step (in s).
563 double dt_;
564
565 /// \brief Previous time step (in s).
567
568 /// \brief Universal gravitational constant (in m³ / kg /s²).
569 double G_;
570
571 /// \brief Numerical precision threshold.
572 double epsilon_;
573
574 /// \brief Coefficient of restitution for collisions.
576
577 /// \brief Coefficient of kinetic (sliding) friction.
579
580 /// \brief Coefficient of static friction.
582
583 /// \brief The percentage of overlap to correct in each frame.
585
586 /// \brief Damping factor for linear velocity.
588
589 /// \brief Damping factor for angular velocity.
591
592 /// \brief Flag indicating if diagnostic output is enabled.
594
595 /// \brief Frequency of energy log output (every N iterations).
596 std::size_t logFreq_;
597
598 /// \brief SoA container for all bodies in the simulation.
599 Bodies bodies_; // NOLINT
600
601 /// \brief k-d tree for broad-phase collision detection.
603
604 /// \brief Adjacency list for the collision graph.
605 std::vector<std::vector<std::size_t>> collisionGraph_; // NOLINT
606
607 /**
608 * \brief Stores the colour of each body for parallel collision
609 * processing.
610 */
611 std::vector<int> colors_; // NOLINT
612
613 /// \brief OpenMP locks for thread-safe collision graph updates.
614 std::vector<omp_lock_t> graphLocks_;
615
616 /**
617 * \brief Thread-local storage for collision pairs found during
618 * broad-phase. This reduces lock contention on the global
619 * `collisionGraph_` during parallel processing.
620 */
621 std::vector<std::vector<std::pair<std::size_t, std::size_t>>>
623
624 /// \brief Thread-local storage for broad-phase search results to avoid
625 /// allocations.
626#if defined(NANOFLANN_VERSION) && NANOFLANN_VERSION < 0x150
627 std::vector<std::vector<std::pair<std::size_t, double>>>
629#else
630 std::vector<std::vector<nanoflann::ResultItem<std::size_t, double>>>
632#endif
633
634 /// \brief Thread-local storage for graph coloring used colours flags.
635 std::vector<std::vector<uint8_t>> thread_local_used_colors_;
636
637 /// \brief Thread-local storage for graph coloring neighbor colours.
638 std::vector<std::vector<int>> thread_local_neighbour_colours_;
639
640 /// \brief Persistent storage for graph coloring conflict flags.
641 std::vector<uint8_t> conflicts_;
642
643 /// \brief The total energy from the previous logged step.
644 double lastTotalEnergy_ = std::numeric_limits<double>::infinity();
645
646 /// \brief Cached maximum radius for collision detection optimization.
647 /// This is recalculated only when bodies are added/removed, not every
648 /// frame.
649 mutable double cached_max_radius_ = 0.0;
650
651 // --- Profiling Timers ---
652 /* These members accumulate the time spent in different simulation
653 stages. */
654 /// \brief Time spent in integration steps.
655 mutable double profIntegration_ = 0.0;
656
657 /// \brief Time spent in collision graph building.
658 mutable double profCollisionGraph_ = 0.0;
659
660 /// \brief Time spent in collision response.
661 mutable double profCollisionResponse_ = 0.0;
662
663 /// \brief Time spent in force calculation.
664 mutable double profForceCalculation_ = 0.0;
665 };
666}
667
668#endif // SPACE_HPP
SoA container for simulation bodies and proxies for AoS-like access.
Structure-of-Arrays (SoA) container for all bodies in the simulation.
Definition: body.hpp:275
std::size_t size() const
Get number of bodies in the simulation.
Definition: body.hpp:370
A proxy object that provides an AoS-like interface to a body stored in the Bodies SoA container.
Definition: body.hpp:142
A const proxy object that provides a read-only AoS-like interface to a body stored in the Bodies SoA ...
Definition: body.hpp:253
A k-d tree for fast nearest neighbour searches, specialized for our Bodies container.
Definition: kdtree.hpp:75
Class describing a space in which move several bodies.
Definition: space.hpp:90
Vector3d applyFrictionImpulse(const CollisionContext &ctx, double jn, const Vector3d &impulseN)
Calculates the tangential (frictional) impulse for a collision.
Definition: space.cpp:563
double G_
Universal gravitational constant (in m³ / kg /s²).
Definition: space.hpp:569
double profCollisionResponse_
Time spent in collision response.
Definition: space.hpp:661
~Space()
Destructor to clean up OpenMP locks.
Definition: space.cpp:203
double previous_dt_
Previous time step (in s).
Definition: space.hpp:566
double getTimeStep() const
Get the current time step.
Definition: space.hpp:286
double coeffStaticFriction_
Coefficient of static friction.
Definition: space.hpp:581
void initializeBodies(std::size_t n, double dens, unsigned int seed)
Initialises the bodies in the simulation with random properties.
Definition: space.cpp:210
void resolveInterpenetration(CollisionContext &ctx)
Resolves interpenetration by moving bodies apart along the collision normal.
Definition: space.cpp:500
double getPositionalCorrectionFactor() const
Get the positional correction factor.
Definition: space.hpp:298
std::vector< omp_lock_t > graphLocks_
OpenMP locks for thread-safe collision graph updates.
Definition: space.hpp:614
double getCoeffFriction() const
Get the coefficient of kinetic friction.
Definition: space.hpp:232
BodyProxy body(std::size_t i)
Get a proxy to the i-th body.
Definition: space.hpp:124
void setCoeffRestitution(double e)
Set the coefficient of restitution.
Definition: space.hpp:226
double getCoeffStaticFriction() const
Get the coefficient of static friction.
Definition: space.hpp:244
void setLinearDamping(double damping)
Set the linear damping factor.
Definition: space.hpp:266
double getLinearDamping() const
Get the linear damping factor.
Definition: space.hpp:260
double getEffectiveMass(const CollisionContext &ctx, const Vector3d &direction_vec)
Calculates the effective mass of two colliding bodies in a given direction.
Definition: space.cpp:522
void logSystemEnergy(std::size_t iteration)
Logs system energy and checks for instability if diagnostics are enabled.
Definition: space.cpp:791
std::vector< uint8_t > conflicts_
Persistent storage for graph coloring conflict flags.
Definition: space.hpp:641
void resolveCollisions()
Resolves all detected collisions using graph colouring.
Definition: space.cpp:956
void setPositionalCorrectionFactor(double factor)
Set the positional correction factor.
Definition: space.hpp:306
void setTimeStep(double dt)
Set the current time step.
Definition: space.hpp:292
void colorGraph(const std::vector< std::vector< std::size_t > > &graph)
A greedy graph colouring algorithm to assign a colour (integer) to each body such that no two adjacen...
Definition: space.cpp:713
void getDataForDisplay(Vector3dVec &out_positions, QuaterniondVec &out_quaternions, Vector3dVec &out_torques, Vector3dVec &out_alphas) const
Retrieves all data required for display for all bodies.
Definition: space.cpp:409
void computeDynamics(std::size_t iteration)
Computes one full step of the simulation.
Definition: space.cpp:907
std::vector< std::vector< uint8_t > > thread_local_used_colors_
Thread-local storage for graph coloring used colours flags.
Definition: space.hpp:635
double lastTotalEnergy_
The total energy from the previous logged step.
Definition: space.hpp:644
std::vector< std::vector< int > > thread_local_neighbour_colours_
Thread-local storage for graph coloring neighbor colours.
Definition: space.hpp:638
double linearDamping_
Damping factor for linear velocity.
Definition: space.hpp:587
double getPreviousTimeStep() const
Get the time step used in the last completed simulation frame.
Definition: space.hpp:141
double getG() const
Get the universal gravitational constant.
Definition: space.hpp:194
bool isDiagnosticsEnabled() const
Check if diagnostics are enabled.
Definition: space.hpp:314
void applyGravity(double current_dt, double linear_damping, double angular_damping)
Calculates and applies gravitational forces, and completes Velocity Verlet part 2.
Definition: space.cpp:1078
std::vector< std::vector< std::size_t > > collisionGraph_
Adjacency list for the collision graph.
Definition: space.hpp:605
std::vector< std::vector< nanoflann::ResultItem< std::size_t, double > > > thread_local_search_results_
Thread-local storage for broad-phase search results to avoid allocations.
Definition: space.hpp:631
double profCollisionGraph_
Time spent in collision graph building.
Definition: space.hpp:658
void setG(double g)
Set the universal gravitational constant.
Definition: space.hpp:200
std::size_t logFreq_
Frequency of energy log output (every N iterations).
Definition: space.hpp:596
std::size_t n() const
Number of bodies getter.
Definition: space.hpp:115
void setCoeffStaticFriction(double mu_s)
Set the coefficient of static friction.
Definition: space.hpp:252
Space()
Default constructor.
Definition: space.hpp:97
Bodies bodies_
SoA container for all bodies in the simulation.
Definition: space.hpp:599
double profIntegration_
Time spent in integration steps.
Definition: space.hpp:655
bool diagnosticsEnabled_
Flag indicating if diagnostic output is enabled.
Definition: space.hpp:593
void setCoeffFriction(double mu)
Set the coefficient of kinetic friction.
Definition: space.hpp:238
double positionalCorrectionFactor_
The percentage of overlap to correct in each frame.
Definition: space.hpp:584
Bodies & getBodiesForTest()
Provides direct access to the Bodies container for testing.
Definition: space.hpp:333
void setEpsilon(double eps)
Set the numerical precision threshold.
Definition: space.hpp:212
ConstBodyProxy body(std::size_t i) const
Get a const proxy to the i-th body.
Definition: space.hpp:133
std::optional< CollisionContext > createCollisionContext(BodyProxy &b1, BodyProxy &b2)
Factory method to create a CollisionContext if two bodies are colliding.
Definition: space.cpp:659
double cached_max_radius_
Cached maximum radius for collision detection optimization. This is recalculated only when bodies are...
Definition: space.hpp:649
double angularDamping_
Damping factor for angular velocity.
Definition: space.hpp:590
double dt_
Time step (in s).
Definition: space.hpp:563
double profForceCalculation_
Time spent in force calculation.
Definition: space.hpp:664
void updateAdaptiveTimeStep()
Determines the optimal time step for the next frame based on current velocities and accelerations.
Definition: space.cpp:988
KDTree kdTree_
k-d tree for broad-phase collision detection.
Definition: space.hpp:602
void setAngularDamping(double damping)
Set the angular damping factor.
Definition: space.hpp:280
double coeffRestitution_
Coefficient of restitution for collisions.
Definition: space.hpp:575
std::tuple< Vector3d, double, double > applyRestitutionImpulse(CollisionContext &ctx)
Calculates and applies the normal impulse (restitution) for a collision.
Definition: space.cpp:538
std::vector< std::vector< std::pair< std::size_t, std::size_t > > > thread_local_collision_pairs_
Thread-local storage for collision pairs found during broad-phase. This reduces lock contention on th...
Definition: space.hpp:622
double getCoeffRestitution() const
Get the coefficient of restitution.
Definition: space.hpp:218
void setDiagnosticsEnabled(bool enabled)
Enable or disable diagnostics.
Definition: space.hpp:322
double coeffFriction_
Coefficient of kinetic (sliding) friction.
Definition: space.hpp:578
double epsilon_
Numerical precision threshold.
Definition: space.hpp:572
std::vector< int > colors_
Stores the colour of each body for parallel collision processing.
Definition: space.hpp:611
double getEpsilon() const
Get the numerical precision threshold.
Definition: space.hpp:206
void buildCollisionGraph()
Builds the collision graph using broad-phase and narrow-phase detection.
Definition: space.cpp:825
std::tuple< Vector3dVec, QuaterniondVec > getAllBodyTransforms() const
Retrieves the current positions and orientations of all bodies.
Definition: space.cpp:395
double getAngularDamping() const
Get the angular damping factor.
Definition: space.hpp:272
void handleCollision(BodyProxy &b1, BodyProxy &b2)
Handles collision detection and response between two bodies.
Definition: space.cpp:681
std::tuple< double, double, double > calculateSystemEnergy() const
Calculates the kinetic and potential energy of the system.
Definition: space.cpp:450
Constants for the model.
constexpr double EPSILON
Default computing precision value.
Definition: constants.hpp:32
constexpr double G
Default universal gravitational constant (in m³⋅kg⁻¹⋅s⁻²).
Definition: constants.hpp:35
A k-d tree wrapper for broad-phase collision detection using nanoflann.
A data container to hold all relevant information for a single collision event.
Definition: space.hpp:68
Vector3d vRel_
The relative velocity between the two bodies at the contact point.
Definition: space.hpp:81
BodyProxy & b1
A proxy to the first body in the collision.
Definition: space.hpp:70
BodyProxy & b2
A proxy to the second body in the collision.
Definition: space.hpp:72
Vector3d r1Vec_
The vector from the centre of body 1 to the contact point.
Definition: space.hpp:76
Vector3d r2Vec_
The vector from the centre of body 2 to the contact point.
Definition: space.hpp:78
Vector3d nVec_
The normalised vector pointing from body 1 to body 2.
Definition: space.hpp:74
double overlap_
The penetration depth of the two bodies.
Definition: space.hpp:83