MFEM v4.9.0
Finite element discretization library
Loading...
Searching...
No Matches
navier_particles.hpp
Go to the documentation of this file.
1// Copyright (c) 2010-2025, Lawrence Livermore National Security, LLC. Produced
2// at the Lawrence Livermore National Laboratory. All Rights reserved. See files
3// LICENSE and NOTICE for details. LLNL-CODE-806117.
4//
5// This file is part of the MFEM library. For more information and source code
6// availability visit https://mfem.org.
7//
8// MFEM is free software; you can redistribute it and/or modify it under the
9// terms of the BSD-3 license. We welcome feedback and contributions, see file
10// CONTRIBUTING.md for details.
11
12#ifndef MFEM_NAVIER_PARTICLES_HPP
13#define MFEM_NAVIER_PARTICLES_HPP
14
15#include "mfem.hpp"
16
17#ifdef MFEM_USE_GSLIB
18
19#include <vector>
20#include <type_traits>
21
22namespace mfem
23{
24namespace navier
25{
26
27/** @brief Transient Navier-Stokes fluid-particles solver
28 *
29 * This class implements a Lagrangian point particle tracking model from
30 * Dutta, Som (2017). Ph.D. Dissertation: Bulle-Effect and its implications
31 * for morphodynamics of river diversions.
32 * https://www.ideals.illinois.edu/items/102343.
33 *
34 * In short, the particles are advanced in time by solving the ODE:
35 * dv/dt = κ(u-v) - γ ê + ζ (u-v) x ω,
36 * dx/dt = v,
37 * where
38 * x and v are the particle location and velocity,
39 * u is the fluid velocity at the particle location,
40 * ω is the fluid vorticity at the particle location,
41 * κ depends on the drag properties of the particle,
42 * ζ depends on the lift properties, and
43 * γ and ê depend on body forces such as gravity.
44 *
45 * The model from Dutta et al. is general but this implementation is currently
46 * limited to 2D problems. Simple reflection and recirculation boundary
47 * conditions are also supported.
48 *
49 */
51{
52protected:
53 /// Active fluid particle set.
55
56 /// Inactive fluid particle set.
57 /// Particles that leave the domain are added here.
59
61
62 /// Timestep history.
63 Array<real_t> dthist{0.0, 0.0, 0.0};
64
65 /// BDFk coefficients, k=1,2,3.
66 std::array<Array<real_t>, 3> beta_k;
67
68 /// EXTk coefficients, k=1,2,3.
69 std::array<Array<real_t>, 3> alpha_k;
70
71 /// Number of timesteps to store (includes current)
72 static constexpr int N_HIST = 4;
73
74 /// Carrier of field + tag indices. Allows for convenient access to
75 /// corresponding ParticleVector from ParticleSet.
77 {
79 {
81 int u[N_HIST];
82 int v[N_HIST];
83 int w[N_HIST];
84 int x[N_HIST-1]; // current position is in ParticleSet::Coords()
86
88 {
89 int order;
90 } tag;
92
93 /// 2D wall reflection boundary condition struct
95 {
98 const real_t e; // restitution constant [0,1]
99 const bool invert_normal; // if true, left normal points out of domain.
100 };
101
102 /// 2D recirculation boundary condition struct
112
113 using BCVariant = std::variant<ReflectionBC_2D, RecirculationBC_2D>;
114
115 /// std::vector of all boundary condition structs
116 std::vector<BCVariant> bcs;
117
118 /// Update \ref beta_k and \ref alpha_k using current \ref dthist
120
121 /// 2D particle step for particle at index \p p
122 void ParticleStep2D(const real_t &dt, int p);
123
124 /** @brief Given two 2D points, get the unit normal to the line
125 * connecting them.
126 *
127 * For \p inv_normal *false*, the normal is 90 degrees (CCW) from the line
128 * connecting \p p1 to \p p2 .
129 * For \p inv_normal *true*, the normal is 90 degrees (CW) from the line
130 * connecting \p p1 to \p p2 .
131 */
132 static void Get2DNormal(const Vector &p1, const Vector &p2, bool inv_normal,
133 Vector &normal);
134
135 /** @brief Given two 2D segments, get the intersection point if it exists.
136 *
137 * Specifically this function solves the following system of equations:
138 * r_1 = s1_start + t1*[s1_end - s1_start]
139 * r_2 = s2_start + t2*[s2_end - s2_start]
140 *
141 * and then checks if 0<=t1<=1 and 0<=t2<=1 .
142 *
143 * @param[in] s1_start First line segment start point.
144 * @param[in] s1_end First line segment end point.
145 * @param[in] s2_start Second line segment start point.
146 * @param[in] s2_end Second line segment end point.
147 * @param[in] x_int Computed intersection point (if it exists)
148 * @param[in] t1_ptr (Optional) Computed t1
149 * @param[in] t2_ptr (Optional) Computed t2
150 *
151 * @return *true* if intersection point exists, *false* otherwise
152 */
153 static bool Get2DSegmentIntersection(const Vector &s1_start,
154 const Vector &s1_end,
155 const Vector &s2_start,
156 const Vector &s2_end,
157 Vector &x_int, real_t *t1_ptr=nullptr,
158 real_t *t2_ptr=nullptr);
159
160 /// Apply 2D reflection BCs to update particle positions and velocities
161 void Apply2DReflectionBC(const ReflectionBC_2D &bc);
162
163 /// Apply 2D recirculation BCs
165
166 /// Apply all BCs in \ref bcs
167 void ApplyBCs();
168
169 /** @brief Move any particles that have left the domain to the
170 * inactive ParticleSet.
171 *
172 * This method uses the FindPointsGSLIB object internal to the class to
173 * detect if particles are within the domain or not.
174 *
175 * @param[in] findpts If true, call FindPointsGSLIB::FindPoints prior
176 * to deactivation (if particle coordinates out of
177 * sync with FindPointsGSLIB)
178 */
179 void DeactivateLostParticles(bool findpts);
180
181 // Temporary vectors for particle computation
182 mutable Vector up, vp, xpn, xp;
183 mutable Vector r, C;
184
185public:
186
187 /// Initialize NavierParticles with \p num_particles using fluid mesh \p m .
188 NavierParticles(MPI_Comm comm, int num_particles, Mesh &m);
189
190 /// Set initial timestep in time history array
191 void Setup(const real_t dt) { dthist[0] = dt; }
192
193 /** @brief Step the particles in time.
194 *
195 * @param[in] dt
196 * @param[in] u_gf Fluid velocity on fluid mesh.
197 * @param[in] w_gf Fluid vorticity on fluid mesh.
198 */
199 void Step(const real_t dt, const ParGridFunction &u_gf,
200 const ParGridFunction &w_gf);
201
202 /** @brief Interpolate fluid velocity and vorticity onto current particles'
203 * location.
204 *
205 * @param[in] u_gf Fluid velocity on fluid mesh.
206 * @param[in] w_gf Fluid vorticity on fluid mesh.
207 */
208 void InterpolateUW(const ParGridFunction &u_gf, const ParGridFunction &w_gf);
209
210 /// Get reference to the active ParticleSet.
212
213 /// Get reference to the inactive ParticleSet.
215
216 /// Get reference to the κ ParticleVector.
221
222 /// Get reference to the ζ ParticleVector.
227
228 /// Get reference to the γ ParticleVector.
233
234 /** @brief Get reference to the fluid velocity-interpolated ParticleVector at
235 * time n - \p nm .
236 */
237 ParticleVector& U(int nm=0)
238 {
239 MFEM_ASSERT(nm < N_HIST, "nm must be < " << N_HIST);
240 return fluid_particles.Field(fp_idx.field.u[nm]);
241 }
242
243 /// Get reference to the particle velocity ParticleVector at time n - \p nm .
244 ParticleVector& V(int nm=0)
245 {
246 MFEM_ASSERT(nm < N_HIST, "nm must be < " << N_HIST);
247 return fluid_particles.Field(fp_idx.field.v[nm]);
248 }
249
250 /** @brief Get reference to the fluid vorticity-interpolated ParticleVector at
251 * time n - \p nm .
252 */
253 ParticleVector& W(int nm=0)
254 {
255 MFEM_ASSERT(nm < N_HIST, "nm must be < " << N_HIST);
256 return fluid_particles.Field(fp_idx.field.w[nm]);
257 }
258
259 /// Get reference to the position ParticleVector at time n - \p nm .
260 ParticleVector& X(int nm=0)
261 {
262 MFEM_ASSERT(nm < N_HIST, "nm must be < " << N_HIST);
263 return nm == 0 ? fluid_particles.Coords() : fluid_particles.Field(
264 fp_idx.field.x[nm-1]);
265 }
266
267 /// Get reference to the order Array<int>.
269
270 /** @brief Add a 2D wall reflective boundary condition.
271 *
272 * @param[in] line_start Wall line segment start point.
273 * @param[in] line_end Wall line segment end point.
274 * @param[in] e Boundary collision reconstitution constant.
275 * 1 for perfectly elastic.
276 * @param[in] invert_normal True if left normal points out of domain.
277 * False if left normal points into domain.
278 *
279 */
280 void Add2DReflectionBC(const Vector &line_start, const Vector &line_end,
281 real_t e, bool invert_normal)
282 { bcs.push_back(ReflectionBC_2D{line_start, line_end, e, invert_normal}); }
283
284 /** @brief Add a 2D recirculation / one-way periodic boundary condition.
285 *
286 * @warning *Both* normals must be facing into the domain. See
287 * Get2DNormal() for details on the normal direction.
288 *
289 * @param[in] inlet_start Inlet line segment start point.
290 * @param[in] inlet_end Inlet line segment end point.
291 * @param[in] invert_inlet_normal Invert direction of the inlet normal.
292 * @param[in] outlet_start Outlet line segment start point.
293 * @param[in] outlet_end Outlet line segment end point.
294 * @param[in] invert_outlet_normal Invert direction of the outlet normal.
295 *
296 */
297 void Add2DRecirculationBC(const Vector &inlet_start, const Vector &inlet_end,
298 bool invert_inlet_normal,
299 const Vector &outlet_start,
300 const Vector &outlet_end,
301 bool invert_outlet_normal)
302 {
303 MFEM_ASSERT([&]()
304 {
305 real_t inlet_dist = inlet_start.DistanceTo(inlet_end);
306 real_t outlet_dist = outlet_start.DistanceTo(outlet_end);
307
308 return abs(inlet_dist-outlet_dist)/inlet_dist < 1e-12;
309
310 }(), "Inlet + outlet must be same length.");
311 bcs.push_back(RecirculationBC_2D{inlet_start, inlet_end,
312 invert_inlet_normal, outlet_start,
313 outlet_end, invert_outlet_normal});
314 }
315};
316
317} // namespace navier
318
319} // namespace mfem
320
321#endif // MFEM_USE_GSLIB
322
323#endif // MFEM_NAVIER_PARTICLES_HPP
FindPointsGSLIB can robustly evaluate a GridFunction on an arbitrary collection of points....
Definition gslib.hpp:73
Mesh data type.
Definition mesh.hpp:65
Class for parallel grid function.
Definition pgridfunc.hpp:50
ParticleSet initializes and manages data associated with particles.
ParticleVector & Coords()
Get a reference to the coordinates ParticleVector.
ParticleVector & Field(int f)
Get a reference to field f 's ParticleVector.
Array< int > & Tag(int t)
Get a reference to tag t 's Array<int>.
ParticleVector carries vector data (of a given vector dimension) for an arbitrary number of particles...
Vector data type.
Definition vector.hpp:82
real_t DistanceTo(const real_t *p) const
Compute the Euclidean distance to another vector.
Definition vector.hpp:748
Transient Navier-Stokes fluid-particles solver.
Array< real_t > dthist
Timestep history.
void SetTimeIntegrationCoefficients()
Update beta_k and alpha_k using current dthist.
ParticleVector & V(int nm=0)
Get reference to the particle velocity ParticleVector at time n - nm .
ParticleVector & W(int nm=0)
Get reference to the fluid vorticity-interpolated ParticleVector at time n - nm .
static constexpr int N_HIST
Number of timesteps to store (includes current)
struct mfem::navier::NavierParticles::FluidParticleIndices fp_idx
void InterpolateUW(const ParGridFunction &u_gf, const ParGridFunction &w_gf)
Interpolate fluid velocity and vorticity onto current particles' location.
Array< int > & Order()
Get reference to the order Array<int>.
ParticleVector & U(int nm=0)
Get reference to the fluid velocity-interpolated ParticleVector at time n - nm .
ParticleSet & GetInactiveParticles()
Get reference to the inactive ParticleSet.
void ParticleStep2D(const real_t &dt, int p)
2D particle step for particle at index p
static bool Get2DSegmentIntersection(const Vector &s1_start, const Vector &s1_end, const Vector &s2_start, const Vector &s2_end, Vector &x_int, real_t *t1_ptr=nullptr, real_t *t2_ptr=nullptr)
Given two 2D segments, get the intersection point if it exists.
ParticleSet & GetParticles()
Get reference to the active ParticleSet.
ParticleVector & X(int nm=0)
Get reference to the position ParticleVector at time n - nm .
void Setup(const real_t dt)
Set initial timestep in time history array.
void Add2DReflectionBC(const Vector &line_start, const Vector &line_end, real_t e, bool invert_normal)
Add a 2D wall reflective boundary condition.
void ApplyBCs()
Apply all BCs in bcs.
void Add2DRecirculationBC(const Vector &inlet_start, const Vector &inlet_end, bool invert_inlet_normal, const Vector &outlet_start, const Vector &outlet_end, bool invert_outlet_normal)
Add a 2D recirculation / one-way periodic boundary condition.
std::variant< ReflectionBC_2D, RecirculationBC_2D > BCVariant
ParticleVector & Gamma()
Get reference to the γ ParticleVector.
void DeactivateLostParticles(bool findpts)
Move any particles that have left the domain to the inactive ParticleSet.
void Step(const real_t dt, const ParGridFunction &u_gf, const ParGridFunction &w_gf)
Step the particles in time.
void Apply2DReflectionBC(const ReflectionBC_2D &bc)
Apply 2D reflection BCs to update particle positions and velocities.
std::array< Array< real_t >, 3 > beta_k
BDFk coefficients, k=1,2,3.
ParticleVector & Kappa()
Get reference to the κ ParticleVector.
void Apply2DRecirculationBC(const RecirculationBC_2D &bc)
Apply 2D recirculation BCs.
NavierParticles(MPI_Comm comm, int num_particles, Mesh &m)
Initialize NavierParticles with num_particles using fluid mesh m .
static void Get2DNormal(const Vector &p1, const Vector &p2, bool inv_normal, Vector &normal)
Given two 2D points, get the unit normal to the line connecting them.
ParticleSet fluid_particles
Active fluid particle set.
std::array< Array< real_t >, 3 > alpha_k
EXTk coefficients, k=1,2,3.
ParticleVector & Zeta()
Get reference to the ζ ParticleVector.
std::vector< BCVariant > bcs
std::vector of all boundary condition structs
float real_t
Definition config.hpp:46
real_t p(const Vector &x, real_t t)
MFEM_HOST_DEVICE real_t abs(const Complex &z)
struct mfem::navier::NavierParticles::FluidParticleIndices::FieldIndices field
struct mfem::navier::NavierParticles::FluidParticleIndices::TagIndices tag
2D recirculation boundary condition struct
2D wall reflection boundary condition struct