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 (3D)
3///
4/// Thread safety contract:
5/// Each parallel section has a unique "owner thread" for writes.
6/// Thread i reads particles[j].* fields finalised in a prior sequential
7/// phase and 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.
13/// Per-particle query keeps each thread isolated to index i.
14///
15/// Falls back to seq backend when NUMERICS_HAS_OMP is not defined.
16
17#include "impl.hpp"
18#include "backends/seq/impl.hpp"
19#include "kernel3d.hpp"
21#include <cmath>
22#include <algorithm>
23#include <cfloat>
24
25#ifdef NUMERICS_HAS_OMP
26# include <omp.h>
27#endif
28
29namespace physics::backends::omp {
30
31void compute_density_pressure(std::vector<Particle3D>& particles,
32 const FluidParams3D& params,
33 const SpatialHash3D& grid) {
34#ifdef NUMERICS_HAS_OMP
35 const float h = params.h;
36 const float m = params.mass;
37 const float rho0 = params.rho0;
38 const float B = rho0 * params.c0 * params.c0 / static_cast<float>(params.gamma);
39 const float supp_sq = 4.0f * h * h;
40 const int n = static_cast<int>(particles.size());
41
42# pragma omp parallel for schedule(static)
43 for (int i = 0; i < n; ++i) {
44 Particle3D& pi = particles[i];
45 float rho = 0.0f;
46 grid.query(pi.x, pi.y, pi.z, [&](int j) {
47 const float rx = pi.x - particles[j].x;
48 const float ry = pi.y - particles[j].y;
49 const float rz = pi.z - particles[j].z;
50 const float r2 = rx*rx + ry*ry + rz*rz;
51 if (r2 < supp_sq) rho += m * Kernel3D::W(std::sqrt(r2), h);
52 });
53 pi.density = std::max(rho, rho0 * 0.1f);
54 pi.pressure = std::max(0.0f, B * (num::ipow<7>(pi.density / rho0) - 1.0f));
55 }
56#else
57 seq::compute_density_pressure(particles, params, grid);
58#endif
59}
60
61void compute_forces(std::vector<Particle3D>& particles,
62 const FluidParams3D& params,
63 const SpatialHash3D& grid) {
64#ifdef NUMERICS_HAS_OMP
65 const float h = params.h;
66 const float m = params.mass;
67 const float mu = params.mu;
68 const float supp_sq = 4.0f * h * h;
69 const float eps2 = 0.01f * h * h;
70 const int n = static_cast<int>(particles.size());
71
72# pragma omp parallel for schedule(static)
73 for (int i = 0; i < n; ++i) {
74 const Particle3D& pi = particles[i];
75 float ax = params.gx, ay = params.gy, az = params.gz;
76 grid.query(pi.x, pi.y, pi.z, [&](int j) {
77 if (j == i) return;
78 const Particle3D& pj = particles[j];
79 const float rx = pi.x - pj.x, ry = pi.y - pj.y, rz = pi.z - pj.z;
80 const float r2 = rx*rx + ry*ry + rz*rz;
81 if (r2 >= supp_sq || r2 < 1e-10f) return;
82 const float r = std::sqrt(r2);
83
84 float gx, gy, gz;
85 Kernel3D::Spiky_gradW(rx, ry, rz, r, h, gx, gy, gz);
86 const float pterm = pi.pressure / (pi.density * pi.density)
87 + pj.pressure / (pj.density * pj.density);
88 ax -= m * pterm * gx;
89 ay -= m * pterm * gy;
90 az -= m * pterm * gz;
91
92 const float lap = 2.0f * Kernel3D::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 az += visc * (pi.evz - pj.evz);
97 });
98 particles[i].ax = ax;
99 particles[i].ay = ay;
100 particles[i].az = az;
101 }
102#else
103 seq::compute_forces(particles, params, grid);
104#endif
105}
106
107void body_collisions(std::vector<Particle3D>& particles,
108 const std::vector<RigidBody3D>& bodies,
109 const FluidParams3D& params) {
110#ifdef NUMERICS_HAS_OMP
111 const int n = static_cast<int>(particles.size());
112# pragma omp parallel for schedule(static)
113 for (int i = 0; i < n; ++i) {
114 Particle3D& p = particles[i];
115 for (const RigidBody3D& body : bodies) {
116 const float dx = p.x - body.x, dy = p.y - body.y, dz = p.z - body.z;
117 const float d = std::sqrt(dx*dx + dy*dy + dz*dz);
118 if (d < body.radius && d > 1e-8f) {
119 const float nx = dx/d, ny = dy/d, nz = dz/d;
120 p.x = body.x + body.radius * nx;
121 p.y = body.y + body.radius * ny;
122 p.z = body.z + body.radius * nz;
123 const float vn = p.vx*nx + p.vy*ny + p.vz*nz;
124 if (vn < 0.0f) {
125 const float c = (1.0f + params.restitution) * vn;
126 p.vx -= c * nx; p.vy -= c * ny; p.vz -= c * nz;
127 }
128 }
129 }
130 }
131#else
132 seq::body_collisions(particles, bodies, params);
133#endif
134}
135
136void integrate(std::vector<Particle3D>& particles, const FluidParams3D& params) {
137#ifdef NUMERICS_HAS_OMP
138 const float dt = params.dt;
139 const int n = static_cast<int>(particles.size());
140# pragma omp parallel for schedule(static)
141 for (int i = 0; i < n; ++i) {
142 Particle3D& p = particles[i];
143 p.vx += p.ax * dt; p.vy += p.ay * dt; p.vz += p.az * dt;
144 p.x += p.vx * dt; p.y += p.vy * dt; p.z += p.vz * dt;
145 p.evx = 0.5f * (p.evx + p.vx);
146 p.evy = 0.5f * (p.evy + p.vy);
147 p.evz = 0.5f * (p.evz + p.vz);
148 p.temperature = std::clamp(p.temperature + p.dT_dt * dt, -100.0f, 300.0f);
149 }
150#else
151 seq::integrate(particles, params);
152#endif
153}
154
155void enforce_boundaries(std::vector<Particle3D>& particles, const FluidParams3D& params) {
156#ifdef NUMERICS_HAS_OMP
157 const float e = params.restitution;
158 const int n = static_cast<int>(particles.size());
159# pragma omp parallel for schedule(static)
160 for (int i = 0; i < n; ++i) {
161 Particle3D& p = particles[i];
162 if (p.x < params.xmin) { p.x = params.xmin; p.vx = std::abs(p.vx) * e; }
163 if (p.x > params.xmax) { p.x = params.xmax; p.vx = -std::abs(p.vx) * e; }
164 if (p.y < params.ymin) { p.y = params.ymin; p.vy = std::abs(p.vy) * e; }
165 if (p.y > params.ymax) { p.y = params.ymax; p.vy = -std::abs(p.vy) * e; }
166 if (p.z < params.zmin) { p.z = params.zmin; p.vz = std::abs(p.vz) * e; }
167 if (p.z > params.zmax) { p.z = params.zmax; p.vz = -std::abs(p.vz) * e; }
168 }
169#else
170 seq::enforce_boundaries(particles, params);
171#endif
172}
173
174void update_temp_range(const std::vector<Particle3D>& particles,
175 const std::vector<RigidBody3D>& bodies,
176 float& T_min, float& T_max) {
177#ifdef NUMERICS_HAS_OMP
178 if (particles.empty()) return;
179 const int n = static_cast<int>(particles.size());
180 float lo = FLT_MAX, hi = -FLT_MAX;
181# pragma omp parallel for reduction(min:lo) reduction(max:hi) schedule(static)
182 for (int i = 0; i < n; ++i) {
183 lo = std::min(lo, particles[i].temperature);
184 hi = std::max(hi, particles[i].temperature);
185 }
186 T_min = lo; T_max = hi;
187 for (const RigidBody3D& b : bodies) {
188 T_min = std::min(T_min, b.temperature);
189 T_max = std::max(T_max, b.temperature);
190 }
191#else
192 seq::update_temp_range(particles, bodies, T_min, T_max);
193#endif
194}
195
196} // namespace physics::backends::omp
void query(float px, float py, float pz, F &&f) const
Compile-time integer exponentiation via repeated squaring.
SPH smoothing kernels for 3D WCSPH — delegates to num::SPHKernel<3>.
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 mu
Dynamic viscosity [Pa*s].
Definition fluid3d.hpp:26
float c0
Speed of sound [m/s].
Definition fluid3d.hpp:25
float h
Smoothing length [m].
Definition fluid3d.hpp:22
int gamma
Tait EOS exponent.
Definition fluid3d.hpp:24
float mass
Particle mass [kg] (~= rho_0*(0.8h)^3)
Definition fluid3d.hpp:27
float rho0
Rest density [kg/m^3].
Definition fluid3d.hpp:23
static void Spiky_gradW(float rx, float ry, float rz, float r, float h, float &gx, float &gy, float &gz)
Definition kernel3d.hpp:22
static float Spiky_dW_dr(float r, float h)
Definition kernel3d.hpp:19
static float W(float r, float h)
Definition kernel3d.hpp:13
3D SPH particle – AoS layout
Definition particle3d.hpp:8
float evz
smoothed velocity (ev = (ev+v)/2 each step)
3D rigid spherical body that interacts with SPH particles