chombo-discharge
Loading...
Searching...
No Matches
CD_EBParticleMeshImplem.H
Go to the documentation of this file.
1/*
2 * SPDX-FileCopyrightText: 2021-2026 SINTEF Energy Research
3 *
4 * SPDX-License-Identifier: GPL-3.0-or-later
5 */
6
13#ifndef CD_EBPARTICLEMESHIMPLEM_H
14#define CD_EBPARTICLEMESHIMPLEM_H
15
16// Chombo includes
17#include <CH_Timer.H>
18
19// Our includes
20#include <CD_EBParticleMesh.H>
21#include <CD_BoxLoops.H>
22#include <CD_ParticleOps.H>
23#include <CD_NamespaceHeader.H>
24
25template <class P, class Ret, Ret (P::*MemberFunc)() const>
26void
28 const List<P>& a_particles,
30 const Real a_widthScale,
31 const bool a_forceIrregNGP) const
32{
33 CH_TIME("EBParticleMesh::deposit");
34 if (m_verbose) {
35 pout() << "EBParticleMesh::deposit" << endl;
36 }
37
38 constexpr int numComp = EBParticleMesh::sanitize<Ret>();
39
40 Real invVol = 1.0;
41 for (int dir = 0; dir < SpaceDim; dir++) {
42 invVol /= m_dx[dir];
43 }
44
45 // These are the default _total_ particle widths.
48
49 const Box cicBox(-1 * IntVect::Unit, 1 * IntVect::Unit);
50 const Box tscBox(-2 * IntVect::Unit, 2 * IntVect::Unit);
51
55
56 // Various scaling factors since particle deposition functions require different normalizations.
57 for (int dir = 0; dir < SpaceDim; dir++) {
58 cicFactor *= 1.0 / cicWidth[dir];
59 tscFactor *= 2.0 / tscWidth[dir];
60 }
61
62 // Switch between deposition types.
63 switch (a_depositionType) {
64 case DepositionType::NGP: {
65 for (ListIterator<P> lit(a_particles); lit.ok(); ++lit) {
66 const Ret w = (lit().*MemberFunc)();
67
68 this->depositParticleNGP(a_meshData, lit().position(), ngpFactor, &w, numComp);
69 }
70
71 break;
72 }
73 case DepositionType::CIC: {
74 for (ListIterator<P> lit(a_particles); lit.ok(); ++lit) {
75 const Ret w = (lit().*MemberFunc)();
76
77 this->depositParticleCIC(a_meshData, lit().position(), cicWidth, cicBox, cicFactor, &w, numComp, a_forceIrregNGP);
78 }
79
80 break;
81 }
82 case DepositionType::TSC: {
83 for (ListIterator<P> lit(a_particles); lit.ok(); ++lit) {
84 const Ret w = (lit().*MemberFunc)();
85
86 this->depositParticleTSC(a_meshData, lit().position(), tscWidth, tscBox, tscFactor, &w, numComp, a_forceIrregNGP);
87 }
88
89 break;
90 }
91 default: {
92 MayDay::Abort("EBParticleMesh::deposit -- logic bust");
93
94 break;
95 }
96 }
97}
98
99template <class P, class Ret, Ret (P::*MemberFunc)()>
100void
102 const EBCellFAB& a_meshData,
104 const bool a_forceIrregNGP) const
105{
106 CH_TIME("EBParticleMesh::interpolate(Real)");
107
108 static_assert(std::is_same<Ret, Real&>::value || std::is_same<Ret, RealVect&>::value,
109 "Ret should be Real& or RealVect&");
110
111 constexpr int numComp = std::is_same<Ret, Real&>::value ? 1 : SpaceDim;
112
113 CH_assert(a_meshData.nComp() == numComp);
114
115 const Interval variables = Interval(0, numComp - 1);
116
117 // validBox is the domain in which we can use the specified interpolation scheme. E.g., TSC fails nears the domain boundaries
118 // since we don't have enough ghost cells to interpolate from.
119 //
120 // gatherBox is the box from which we gather the field.
121 //
124
125 switch (a_interpType) {
126 case DepositionType::NGP: {
127 validBox = m_domain.domainBox();
129
130 break;
131 }
132 case DepositionType::CIC: {
133 validBox = grow(m_domain.domainBox(), -1);
135
136 break;
137 }
138 case DepositionType::TSC: {
139 validBox = grow(m_domain.domainBox(), -2);
141
142 break;
143 }
144 default: {
145 MayDay::Error("EBParticleMesh::interpolate - logic bust");
146 }
147 }
148
153 struct GetPointer
154 {
158 Real*
159 operator()(Real& r) const
160 {
161 return &r;
162 }
163
167 auto
168 operator()(RealVect& r) const
169 {
170 return r.dataPtr();
171 }
172 };
173
174 switch (a_interpType) {
175 case DepositionType::NGP: {
177 auto& p = lit();
178 auto&& w = (p.*MemberFunc)();
179 const auto& x = p.position();
180
182 }
183
184 break;
185 }
186 case DepositionType::CIC: {
188 auto& p = lit();
189 auto&& w = (p.*MemberFunc)();
190 const auto& x = p.position();
191
193 }
194
195 break;
196 }
197 case DepositionType::TSC: {
199 auto& p = lit();
200 auto&& w = (p.*MemberFunc)();
201 const auto& x = p.position();
202
204 }
205
206 break;
207 }
208 default: {
209 MayDay::Abort("EBParticleMesh::interpolate -- logic bust");
210
211 break;
212 }
213 }
214}
215
216inline void
218 const RealVect& a_position,
219 const Real& a_volumeFactor,
220 const Real* a_strength,
221 const int& a_numComp) const noexcept
222{
223 CH_TIME("EBParticleMesh::depositParticleNGP");
224
226
227 CH_assert(m_region.contains(particleIV));
228
229 FArrayBox& rho = a_rho.getFArrayBox();
230
231 for (int comp = 0; comp < a_numComp; comp++) {
233 }
234}
235
236inline void
238 const RealVect& a_position,
240 const Box& a_particleBox,
241 const Real& a_volumeFactor,
242 const Real* a_strength,
243 const int& a_numComp,
244 const bool a_forceIrregNGP) const noexcept
245{
246 CH_TIME("EBParticleMesh::depositParticleCIC");
247
249
250 CH_assert(m_region.contains(particleIV));
251
252 FArrayBox& rho = a_rho.getFArrayBox();
253
254 if (m_ebisbox.isIrregular(particleIV) && a_forceIrregNGP) {
255 for (int comp = 0; comp < a_numComp; comp++) {
257 }
258 }
259 else {
260 auto cicKernel = [&](const IntVect& iv) -> void {
261 Real weight = a_volumeFactor;
262
263 // Below, we've transformed the coordinates so that the cell center of cell iv is at the origin.
264 for (int dir = 0; dir < SpaceDim; dir++) {
265 const Real a = (m_probLo[dir] - a_position[dir]) / m_dx[dir] + iv[dir];
266 const Real b = a + 1.0;
267 const Real L = 0.5 * a_particleWidth[dir];
268
269 weight *= std::max(0.0, std::min(b, L) - std::max(a, -L));
270 }
271
272 for (int comp = 0; comp < a_numComp; comp++) {
273 rho(iv, comp) += weight * a_strength[comp];
274 }
275 };
276
278
279 // Not vectorizable: per-particle scatter into rho over a tiny (3^D) runtime-sized stencil box.
280 // The separable weight uses min/max clamps (control flow in loop) and the trip counts are tiny
281 // (typically fully unrolled). Verified via opt-record (control flow in loop / couldn't vectorize).
282 BoxLoops::loop<D_DECL(1, 1, 1)>(particleBox, cicKernel);
283 }
284}
285
286inline void
288 const RealVect& a_position,
290 const Box& a_particleBox,
291 const Real& a_volumeFactor,
292 const Real* a_strength,
293 const int& a_numComp,
294 const bool a_forceIrregNGP) const noexcept
295{
296 CH_TIME("EBParticleMesh::depositParticleTSC");
297
299
300 CH_assert(m_region.contains(particleIV));
301
302 FArrayBox& rho = a_rho.getFArrayBox();
303
304 if (m_ebisbox.isIrregular(particleIV) && a_forceIrregNGP) {
305 for (int comp = 0; comp < a_numComp; comp++) {
307 }
308 }
309 else {
310 auto tscKernel = [&](const IntVect& iv) -> void {
311 Real weight = a_volumeFactor;
312
313 for (int dir = 0; dir < SpaceDim; dir++) {
314 const Real a = (m_probLo[dir] - a_position[dir]) / m_dx[dir] + iv[dir];
315 const Real b = a + 1.0;
316 const Real L = a_particleWidth[dir];
317
318 const Real alpha = std::max(a, -0.5 * L);
319 const Real beta = std::min(b, +0.5 * L);
320 const Real factor = (alpha < beta) ? 1.0 : 0.0;
321
322 weight *= factor * (beta - alpha) - (beta * std::abs(beta) - alpha * std::abs(alpha)) / L;
323 }
324
325 for (int comp = 0; comp < a_numComp; comp++) {
326 rho(iv, comp) += weight * a_strength[comp];
327 }
328 };
329
331
332 // Not vectorizable: per-particle scatter into rho over a tiny (5^D) runtime-sized stencil box.
333 // The separable weight uses min/max clamps + an (alpha<beta) ternary (control flow in loop) and
334 // the trip counts are tiny. Verified via opt-record (control flow in loop / couldn't vectorize).
335 BoxLoops::loop<D_DECL(1, 1, 1)>(particleBox, tscKernel);
336 }
337}
338
339inline void
341 const EBCellFAB& a_meshData,
342 const RealVect& a_position,
343 const int& a_numComp) const noexcept
344{
345 CH_TIME("EBParticleMesh::interpolateParticleNGP");
346
348 const FArrayBox& meshData = a_meshData.getFArrayBox();
349 const Real factor = m_ebisbox.isCovered(particleIV) ? 0.0 : 1.0;
350
351 for (int comp = 0; comp < a_numComp; comp++) {
353 }
354}
355
356inline void
358 const EBCellFAB& a_meshData,
359 const Box& a_validBox,
360 const Box& a_gatherBox,
361 const RealVect& a_position,
362 const int& a_numComp,
363 const bool a_forceIrregNGP) const noexcept
364{
365 CH_TIME("EBParticleMesh::interpolateParticleCIC");
366
367 // particleIV is the cell index where the particle center lies.
368 // loIndex is the cell index of the lower-left corner of the particle.
370 const IntVect loIndex = ParticleOps::getParticleCellIndex(a_position - 0.5 * m_dx, m_probLo, m_dx);
371
372 CH_assert(m_region.contains(particleIV));
373
374 // Get regular data.
375 const FArrayBox& meshData = a_meshData.getFArrayBox();
376
377 // Reset the particle field.
378 for (int comp = 0; comp < a_numComp; comp++) {
379 a_particleField[comp] = 0.0;
380 }
381
382 // NGP interpolation if called for it, or particle abuts the domain boundary.
383 if ((m_ebisbox.isIrregular(particleIV) && a_forceIrregNGP) || !(a_validBox.contains(particleIV))) {
384 for (int comp = 0; comp < a_numComp; comp++) {
386 }
387 }
388 else if (!(m_ebisbox.isCovered(particleIV))) {
389 auto cicKernel = [&](const IntVect& iv) -> void {
390 Real weight = 1.0;
391
392 const RealVect L = (m_probLo - a_position) / m_dx + (RealVect(iv) + 0.5 * RealVect::Unit);
393
394 for (int dir = 0; dir < SpaceDim; dir++) {
395 weight *= (1. - std::abs(L[dir]));
396 }
397
398 for (int comp = 0; comp < a_numComp; comp++) {
399 a_particleField[comp] += weight * meshData(iv, comp);
400 }
401 };
402
404
405 // Not vectorizable: per-particle gather-reduction accumulating into a_particleField (FP sum across
406 // the tiny 2^D gather box). The reduction blocks vectorization without FP reassociation, and the
407 // small box is fully unrolled (no BoxLoops loop survives in the opt-record).
408 BoxLoops::loop<D_DECL(1, 1, 1)>(gatherBox, cicKernel);
409 }
410}
411
412inline void
414 const EBCellFAB& a_meshData,
415 const Box& a_validBox,
416 const Box& a_gatherBox,
417 const RealVect& a_position,
418 const int& a_numComp,
419 const bool a_forceIrregNGP) const noexcept
420{
421 CH_TIME("EBParticleMesh::interpolateParticleTSC");
422
423 // particleIV is the cell index where the particle center lies.
424 // loIndex is the cell index of the lower-left corner of the particle.
426 const IntVect loIndex = ParticleOps::getParticleCellIndex(a_position - m_dx, m_probLo, m_dx);
427
428 CH_assert(m_region.contains(particleIV));
429
430 // Get regular data.
431 const FArrayBox& meshData = a_meshData.getFArrayBox();
432
433 // Reset the particle field.
434 for (int comp = 0; comp < a_numComp; comp++) {
435 a_particleField[comp] = 0.0;
436 }
437
438 // NGP interpolation if called for it, or particle abuts the domain boundary.
439 if ((m_ebisbox.isIrregular(particleIV) && a_forceIrregNGP) || !(a_validBox.contains(particleIV))) {
440 for (int comp = 0; comp < a_numComp; comp++) {
442 }
443 }
444 else if (!(m_ebisbox.isCovered(particleIV))) {
445 auto tscKernel = [&](const IntVect& iv) -> void {
446 Real weight = 1.0;
447
448 const RealVect L = (m_probLo - a_position) / m_dx + (RealVect(iv) + 0.5 * RealVect::Unit);
449
450 for (int dir = 0; dir < SpaceDim; dir++) {
451 const Real& l = std::abs(L[dir]);
452
453 if (l < 0.5) {
454 weight *= 0.75 - l * l;
455 }
456 else {
457 weight *= 0.5 * (1.5 - l) * (1.5 - l);
458 }
459 }
460
461 for (int comp = 0; comp < a_numComp; comp++) {
462 a_particleField[comp] += weight * meshData(iv, comp);
463 }
464 };
465
467
468 // Not vectorizable: per-particle gather-reduction accumulating into a_particleField (FP sum across
469 // the tiny 3^D gather box). The reduction blocks vectorization without FP reassociation, and the
470 // small box is fully unrolled (no BoxLoops loop survives in the opt-record).
471 BoxLoops::loop<D_DECL(1, 1, 1)>(gatherBox, tscKernel);
472 }
473}
474
475#include <CD_NamespaceFooter.H>
476
477#endif
Declaration of a namespace for proto-typing grid and EB loops.
DepositionType
Deposition types.
Definition CD_DepositionType.H:24
Declaration of a class for handling particle-mesh interpolation and deposition.
Declaration of a static class containing some common useful particle routines that would otherwise be...
bool m_verbose
Verbose or not.
Definition CD_EBParticleMesh.H:133
void depositParticleCIC(EBCellFAB &a_rho, const RealVect &a_position, const RealVect &a_particleWidth, const Box &a_particleBox, const Real &a_volumeFactor, const Real *a_strength, const int &a_numComp, const bool a_forceIrregNGP) const noexcept
Function for depositing a single particle with a CIC scheme.
Definition CD_EBParticleMeshImplem.H:237
void interpolateParticleNGP(Real *a_particleField, const EBCellFAB &a_meshData, const RealVect &a_position, const int &a_numComp) const noexcept
Particle interpolation function using an NGP scheme.
Definition CD_EBParticleMeshImplem.H:340
void interpolateParticleTSC(Real *a_particleField, const EBCellFAB &a_meshData, const Box &a_validBox, const Box &a_gatherBox, const RealVect &a_position, const int &a_numComp, const bool a_forceIrregNGP) const noexcept
Particle interpolation function using a TSC scheme.
Definition CD_EBParticleMeshImplem.H:413
void depositParticleNGP(EBCellFAB &a_rho, const RealVect &a_position, const Real &a_volumeFactor, const Real *a_strength, const int &a_numComp) const noexcept
Wrapper function for depositing a single particle with a standard NGP scheme.
Definition CD_EBParticleMeshImplem.H:217
void interpolate(List< P > &a_particles, const EBCellFAB &a_meshData, const DepositionType a_interpType, const bool a_forceIrregNGP=false) const
Interpolate mesh data onto a particle.
Definition CD_EBParticleMeshImplem.H:101
void interpolateParticleCIC(Real *a_particleField, const EBCellFAB &a_meshData, const Box &a_validBox, const Box &a_gatherBox, const RealVect &a_position, const int &a_numComp, const bool a_forceIrregNGP) const noexcept
Particle interpolation function using a CIC scheme.
Definition CD_EBParticleMeshImplem.H:357
void depositParticleTSC(EBCellFAB &a_rho, const RealVect &a_position, const RealVect &a_particleWidth, const Box &a_particleBox, const Real &a_volumeFactor, const Real *a_strength, const int &a_numComp, const bool a_forceIrregNGP) const noexcept
Function for depositing a single particle with a TSC scheme.
Definition CD_EBParticleMeshImplem.H:287
RealVect m_dx
Grid resolution.
Definition CD_EBParticleMesh.H:148
ProblemDomain m_domain
Problem domain.
Definition CD_EBParticleMesh.H:128
void deposit(EBCellFAB &a_meshData, const List< P > &a_particles, const DepositionType a_depositionType, const Real a_widthScale, const bool a_forceIrregNGP=false) const
Deposit a particle onto the mesh.
Definition CD_EBParticleMeshImplem.H:27
static IntVect getParticleCellIndex(const RealVect &a_particlePosition, const RealVect &a_probLo, const Real &a_dx) noexcept
Get the cell index corresponding to the particle position.
Definition CD_ParticleOpsImplem.H:31
Base class for a tracer particle solver. This solver can advance particles in a pre-defined velocity ...
Definition CD_TracerParticleSolver.H:38
TracerParticleSolver()
Default constructor.
Definition CD_TracerParticleSolverImplem.H:26