numerics
Loading...
Searching...
No Matches
fluid.cpp
Go to the documentation of this file.
1/// @file backends/omp/fluid.cpp
2/// @brief OpenMP SPH backend -- parallel for over particles
3///
4/// Thread safety contract (same principle as src/backends/omp/matrix.cpp):
5/// Each parallel section has a unique "owner thread" for writes.
6/// Thread i reads particles[j].* fields that were finalised in a prior
7/// sequential phase and are not modified during the current section.
8/// Thread i writes only to particles[i].* -> no data races without atomics.
9///
10/// Why per-particle query instead of Newton pairs:
11/// iterate_pairs writes to both particles[i] and particles[j].
12/// Parallelising over pairs would need atomic float adds -- expensive and
13/// unreadable. Per-particle query keeps each thread isolated to index i.
14/// Thread-level speedup offsets the 2x more pair evaluations for n ≳ 500.
15///
16/// Falls back to seq backend when NUMERICS_HAS_OMP is not defined,
17/// identical to the pattern in src/backends/omp/vector.cpp.
18
19#include "impl.hpp"
20#include "backends/seq/impl.hpp"
21#include "kernel.hpp"
23#include <cmath>
24#include <algorithm>
25#include <cfloat>
26
27#ifdef NUMERICS_HAS_OMP
28# include <omp.h>
29#endif
30
32
33void compute_density_pressure(std::vector<Particle>& particles,
34 const FluidParams& params,
35 const SpatialHash& grid) {
36#ifdef NUMERICS_HAS_OMP
37 const float h = params.h;
38 const float m = params.mass;
39 const float rho0 = params.rho0;
40 const float B = rho0 * params.c0 * params.c0 / static_cast<float>(params.gamma);
41 const float supp_sq = 4.0f * h * h;
42 const int n = static_cast<int>(particles.size());
43
44# pragma omp parallel for schedule(static)
45 for (int i = 0; i < n; ++i) {
46 Particle& pi = particles[i];
47 float rho = 0.0f;
48 grid.query(pi.x, pi.y, [&](int j) {
49 const float rx = pi.x - particles[j].x;
50 const float ry = pi.y - particles[j].y;
51 const float r2 = rx * rx + ry * ry;
52 if (r2 < supp_sq) rho += m * Kernel::W(std::sqrt(r2), h);
53 });
54 pi.density = std::max(rho, rho0 * 0.1f);
55 pi.pressure = std::max(0.0f, B * (num::ipow<7>(pi.density / rho0) - 1.0f));
56 }
57#else
58 seq::compute_density_pressure(particles, params, grid);
59#endif
60}
61
62void compute_forces(std::vector<Particle>& particles,
63 const FluidParams& params,
64 const SpatialHash& grid) {
65#ifdef NUMERICS_HAS_OMP
66 const float h = params.h;
67 const float m = params.mass;
68 const float mu = params.mu;
69 const float supp_sq = 4.0f * h * h;
70 const float eps2 = 0.01f * h * h;
71 const int n = static_cast<int>(particles.size());
72
73# pragma omp parallel for schedule(static)
74 for (int i = 0; i < n; ++i) {
75 const Particle& pi = particles[i];
76 float ax = params.gx, ay = params.gy;
77 grid.query(pi.x, pi.y, [&](int j) {
78 if (j == i) return;
79 const Particle& pj = particles[j];
80 const float rx = pi.x - pj.x, ry = pi.y - pj.y;
81 const float r2 = rx * rx + ry * ry;
82 if (r2 >= supp_sq || r2 < 1e-10f) return;
83 const float r = std::sqrt(r2);
84
85 float gx, gy;
86 Kernel::Spiky_gradW(rx, ry, r, h, gx, gy);
87 const float pterm = pi.pressure / (pi.density * pi.density)
88 + pj.pressure / (pj.density * pj.density);
89 ax -= m * pterm * gx;
90 ay -= m * pterm * gy;
91
92 const float lap = 2.0f * Kernel::Spiky_dW_dr(r, h) * r / (r2 + eps2);
93 const float visc = m * mu / (pi.density * pj.density) * lap;
94 ax += visc * (pi.evx - pj.evx);
95 ay += visc * (pi.evy - pj.evy);
96 });
97 particles[i].ax = ax;
98 particles[i].ay = ay;
99 }
100#else
101 seq::compute_forces(particles, params, grid);
102#endif
103}
104
105void body_collisions(std::vector<Particle>& particles,
106 const std::vector<RigidBody>& bodies,
107 const FluidParams& params) {
108#ifdef NUMERICS_HAS_OMP
109 const int n = static_cast<int>(particles.size());
110# pragma omp parallel for schedule(static)
111 for (int i = 0; i < n; ++i) {
112 Particle& p = particles[i];
113 for (const RigidBody& body : bodies) {
114 const float dx = p.x - body.x, dy = p.y - body.y;
115 const float d = std::sqrt(dx * dx + dy * dy);
116 if (d < body.radius && d > 1e-8f) {
117 const float nx = dx / d, ny = dy / d;
118 p.x = body.x + body.radius * nx;
119 p.y = body.y + body.radius * ny;
120 const float vn = p.vx * nx + p.vy * ny;
121 if (vn < 0.0f) {
122 p.vx -= (1.0f + params.restitution) * vn * nx;
123 p.vy -= (1.0f + params.restitution) * vn * ny;
124 }
125 }
126 }
127 }
128#else
129 seq::body_collisions(particles, bodies, params);
130#endif
131}
132
133void integrate(std::vector<Particle>& particles, const FluidParams& params) {
134#ifdef NUMERICS_HAS_OMP
135 const float dt = params.dt;
136 const int n = static_cast<int>(particles.size());
137# pragma omp parallel for schedule(static)
138 for (int i = 0; i < n; ++i) {
139 Particle& p = particles[i];
140 p.vx += p.ax * dt; p.vy += p.ay * dt;
141 p.x += p.vx * dt; p.y += p.vy * dt;
142 p.evx = 0.5f * (p.evx + p.vx);
143 p.evy = 0.5f * (p.evy + p.vy);
144 p.temperature = std::clamp(p.temperature + p.dT_dt * dt, -100.0f, 300.0f);
145 }
146#else
147 seq::integrate(particles, params);
148#endif
149}
150
151void enforce_boundaries(std::vector<Particle>& particles, const FluidParams& params) {
152#ifdef NUMERICS_HAS_OMP
153 const float e = params.restitution;
154 const int n = static_cast<int>(particles.size());
155# pragma omp parallel for schedule(static)
156 for (int i = 0; i < n; ++i) {
157 Particle& p = particles[i];
158 if (p.x < params.xmin) { p.x = params.xmin; p.vx = std::abs(p.vx) * e; }
159 if (p.x > params.xmax) { p.x = params.xmax; p.vx = -std::abs(p.vx) * e; }
160 if (p.y < params.ymin) { p.y = params.ymin; p.vy = std::abs(p.vy) * e; }
161 if (p.y > params.ymax) { p.y = params.ymax; p.vy = -std::abs(p.vy) * e; }
162 }
163#else
164 seq::enforce_boundaries(particles, params);
165#endif
166}
167
168void update_temp_range(const std::vector<Particle>& particles,
169 const std::vector<RigidBody>& bodies,
170 float& T_min, float& T_max) {
171#ifdef NUMERICS_HAS_OMP
172 if (particles.empty()) return;
173 const int n = static_cast<int>(particles.size());
174 float lo = FLT_MAX, hi = -FLT_MAX;
175# pragma omp parallel for reduction(min:lo) reduction(max:hi) schedule(static)
176 for (int i = 0; i < n; ++i) {
177 lo = std::min(lo, particles[i].temperature);
178 hi = std::max(hi, particles[i].temperature);
179 }
180 T_min = lo; T_max = hi;
181 for (const RigidBody& b : bodies) { // M bodies -- sequential, M is small
182 T_min = std::min(T_min, b.temperature);
183 T_max = std::max(T_max, b.temperature);
184 }
185#else
186 seq::update_temp_range(particles, bodies, T_min, T_max);
187#endif
188}
189
190} // namespace physics::backends::omp
void query(float px, float py, F &&f) const
Compile-time integer exponentiation via repeated squaring.
constexpr T ipow(T x) noexcept
Compute x^N at compile time via repeated squaring.
void body_collisions(std::vector< Particle > &particles, const std::vector< RigidBody > &bodies, const FluidParams &params)
Definition fluid.cpp:105
void compute_forces(std::vector< Particle > &particles, const FluidParams &params, const SpatialHash &grid)
Per-particle query – each thread writes only to particles[i]. O(n*k).
Definition fluid.cpp:62
void compute_density_pressure(std::vector< Particle > &particles, const FluidParams &params, const SpatialHash &grid)
Per-particle query – each thread writes only to particles[i]. O(n*k).
Definition fluid.cpp:33
void update_temp_range(const std::vector< Particle > &particles, const std::vector< RigidBody > &bodies, float &T_min, float &T_max)
reduction(min:T_min) reduction(max:T_max)
Definition fluid.cpp:168
void integrate(std::vector< Particle > &particles, const FluidParams &params)
Definition fluid.cpp:133
void enforce_boundaries(std::vector< Particle > &particles, const FluidParams &params)
Definition fluid.cpp:151
void enforce_boundaries(std::vector< Particle > &particles, const FluidParams &params)
Definition fluid.cpp:119
void body_collisions(std::vector< Particle > &particles, const std::vector< RigidBody > &bodies, const FluidParams &params)
Definition fluid.cpp:87
void integrate(std::vector< Particle > &particles, const FluidParams &params)
Definition fluid.cpp:108
void compute_density_pressure(std::vector< Particle > &particles, const FluidParams &params, const SpatialHash &grid)
Newton's 3rd law pair traversal – O(n*k/2).
Definition fluid.cpp:19
void update_temp_range(const std::vector< Particle > &particles, const std::vector< RigidBody > &bodies, float &T_min, float &T_max)
Definition fluid.cpp:129
void compute_forces(std::vector< Particle > &particles, const FluidParams &params, const SpatialHash &grid)
Newton's 3rd law pair traversal – O(n*k/2).
Definition fluid.cpp:50
std::experimental::simd butterfly for FFT.
float rho0
Rest density [kg/m^3].
Definition fluid.hpp:37
float mu
Dynamic viscosity [Pa*s].
Definition fluid.hpp:40
int gamma
Tait EOS exponent.
Definition fluid.hpp:38
float h
Smoothing length [m].
Definition fluid.hpp:34
float c0
Speed of sound [m/s] -> B = rho_0c_0^2/gamma.
Definition fluid.hpp:39
float gy
Gravity y [m/s^2].
Definition fluid.hpp:45
float restitution
Velocity restitution at walls.
Definition fluid.hpp:55
float gx
Gravity x [m/s^2].
Definition fluid.hpp:44
float dt
Timestep [s] (must satisfy CFL: dt < h/c0)
Definition fluid.hpp:48
float mass
Particle mass [kg] (~= rho_0*dx^2, dx=0.8h)
Definition fluid.hpp:41
static float Spiky_dW_dr(float r, float h)
Definition kernel.hpp:19
static void Spiky_gradW(float rx, float ry, float r, float h, float &gx, float &gy)
Definition kernel.hpp:22
static float W(float r, float h)
Definition kernel.hpp:13
A single SPH fluid particle (float precision for performance)
Definition particle.hpp:8
float dT_dt
Temperature rate [ degC/s] (updated each step)
Definition particle.hpp:17
float vy
Velocity [m/s].
Definition particle.hpp:10
float y
Position [m].
Definition particle.hpp:9
float temperature
Temperature T_i [ degC].
Definition particle.hpp:16
float pressure
Pressure p_i [Pa].
Definition particle.hpp:15
float density
SPH density rho_i [kg/m^3].
Definition particle.hpp:14
float ay
Acceleration [m/s^2] (updated each step)
Definition particle.hpp:13
A rigid spherical body that interacts with fluid particles.
Definition rigid_body.hpp:8