chombo-discharge
CD_EBParticleMeshImplem.H
Go to the documentation of this file.
1 /* chombo-discharge
2  * Copyright © 2021 SINTEF Energy Research.
3  * Please refer to Copyright.txt and LICENSE in the chombo-discharge root directory.
4  */
5 
12 #ifndef CD_EBParticleMeshImplem_H
13 #define CD_EBParticleMeshImplem_H
14 
15 // Chombo includes
16 #include <CH_Timer.H>
17 
18 // Our includes
19 #include <CD_EBParticleMesh.H>
20 #include <CD_BoxLoops.H>
21 #include <CD_NamespaceHeader.H>
22 
23 template <class P, const Real& (P::*particleScalarField)() const>
24 void
25 EBParticleMesh::deposit(const List<P>& a_particleList,
26  EBCellFAB& a_rho,
27  const DepositionType a_depositionType,
28  const bool a_forceIrregNGP) const
29 {
30  CH_TIME("EBParticleMesh::deposit");
31 
32  const Interval variables(0, 0);
33 
34  for (ListIterator<P> lit(a_particleList); lit; ++lit) {
35  const P& curParticle = lit();
36  const RealVect& curPosition = curParticle.position();
37  const Real& curStrength = (curParticle.*particleScalarField)();
38 
39  this
40  ->depositParticle(a_rho, m_probLo, m_dx, curPosition, &curStrength, variables, a_depositionType, a_forceIrregNGP);
41  }
42 }
43 
44 template <class P, Real (P::*particleScalarField)() const>
45 void
46 EBParticleMesh::deposit(const List<P>& a_particleList,
47  EBCellFAB& a_rho,
48  const DepositionType a_depositionType,
49  const bool a_forceIrregNGP) const
50 {
51  CH_TIME("EBParticleMesh::deposit");
52 
53  const Interval variables(0, 0);
54 
55  for (ListIterator<P> lit(a_particleList); lit; ++lit) {
56  const P& curParticle = lit();
57  const RealVect& curPosition = curParticle.position();
58  const Real& curStrength = (curParticle.*particleScalarField)();
59 
60  this
61  ->depositParticle(a_rho, m_probLo, m_dx, curPosition, &curStrength, variables, a_depositionType, a_forceIrregNGP);
62  }
63 }
64 
65 template <class P, const Real& (P::*particleScalarField)() const>
66 void
67 EBParticleMesh::deposit2(const List<P>& a_particleList,
68  EBCellFAB& a_rho,
69  const DepositionType a_depositionType,
70  const bool a_forceIrregNGP) const
71 {
72  CH_TIME("EBParticleMesh::deposit2");
73 
74  const Interval variables(0, 0);
75 
76  for (ListIterator<P> lit(a_particleList); lit; ++lit) {
77  const P& curParticle = lit();
78  const RealVect& curPosition = curParticle.position();
79  const Real& curStrength = (curParticle.*particleScalarField)();
80 
81  this->depositParticle2(a_rho,
82  m_probLo,
83  m_dx,
84  curPosition,
85  &curStrength,
86  variables,
87  a_depositionType,
88  a_forceIrregNGP);
89  }
90 }
91 
92 template <class P, Real (P::*particleScalarField)() const>
93 void
94 EBParticleMesh::deposit2(const List<P>& a_particleList,
95  EBCellFAB& a_rho,
96  const DepositionType a_depositionType,
97  const bool a_forceIrregNGP) const
98 {
99  CH_TIME("EBParticleMesh::deposit2");
100 
101  const Interval variables(0, 0);
102 
103  for (ListIterator<P> lit(a_particleList); lit; ++lit) {
104  const P& curParticle = lit();
105  const RealVect& curPosition = curParticle.position();
106  const Real& curStrength = (curParticle.*particleScalarField)();
107 
108  this->depositParticle2(a_rho,
109  m_probLo,
110  m_dx,
111  curPosition,
112  &curStrength,
113  variables,
114  a_depositionType,
115  a_forceIrregNGP);
116  }
117 }
118 
119 template <class P, const Real& (P::*particleScalarField)() const>
120 void
121 EBParticleMesh::deposit4(const List<P>& a_particleList,
122  EBCellFAB& a_rho,
123  const DepositionType a_depositionType,
124  const bool a_forceIrregNGP) const
125 {
126  CH_TIME("EBParticleMesh::deposit4");
127 
128  const Interval variables(0, 0);
129 
130  for (ListIterator<P> lit(a_particleList); lit; ++lit) {
131  const P& curParticle = lit();
132  const RealVect& curPosition = curParticle.position();
133  const Real& curStrength = (curParticle.*particleScalarField)();
134 
135  this->depositParticle4(a_rho,
136  m_probLo,
137  m_dx,
138  curPosition,
139  &curStrength,
140  variables,
141  a_depositionType,
142  a_forceIrregNGP);
143  }
144 }
145 
146 template <class P, Real (P::*particleScalarField)() const>
147 void
148 EBParticleMesh::deposit4(const List<P>& a_particleList,
149  EBCellFAB& a_rho,
150  const DepositionType a_depositionType,
151  const bool a_forceIrregNGP) const
152 {
153  CH_TIME("EBParticleMesh::deposit4");
154 
155  const Interval variables(0, 0);
156 
157  for (ListIterator<P> lit(a_particleList); lit; ++lit) {
158  const P& curParticle = lit();
159  const RealVect& curPosition = curParticle.position();
160  const Real& curStrength = (curParticle.*particleScalarField)();
161 
162  this->depositParticle4(a_rho,
163  m_probLo,
164  m_dx,
165  curPosition,
166  &curStrength,
167  variables,
168  a_depositionType,
169  a_forceIrregNGP);
170  }
171 }
172 
173 template <class P, const RealVect& (P::*particleVectorField)() const>
174 void
175 EBParticleMesh::deposit(const List<P>& a_particleList,
176  EBCellFAB& a_rho,
177  const DepositionType a_depositionType,
178  const bool a_forceIrregNGP) const
179 {
180  CH_TIME("EBParticleMesh::deposit");
181 
182  // TLDR: This is a jack-of-all-trades deposition function. The user will use this function to supply a pointer to the field that will be
183  // deposited. As per API, this function must be of the type 'const Real& myParticleClass::myDepositionField() const'
184 
185  const Interval variables(0, SpaceDim - 1);
186 
187  for (ListIterator<P> lit(a_particleList); lit; ++lit) {
188  const P& curParticle = lit();
189  const RealVect& curPosition = curParticle.position();
190  const RealVect& curStrength = (curParticle.*particleVectorField)();
191 
192  this->depositParticle(a_rho,
193  m_probLo,
194  m_dx,
195  curPosition,
196  curStrength.dataPtr(),
197  variables,
198  a_depositionType,
199  a_forceIrregNGP);
200  }
201 }
202 
203 template <class P, RealVect (P::*particleVectorField)() const>
204 void
205 EBParticleMesh::deposit(const List<P>& a_particleList,
206  EBCellFAB& a_rho,
207  const DepositionType a_depositionType,
208  const bool a_forceIrregNGP) const
209 {
210  CH_TIME("EBParticleMesh::deposit");
211 
212  const Interval variables(0, SpaceDim - 1);
213 
214  for (ListIterator<P> lit(a_particleList); lit; ++lit) {
215  const P& curParticle = lit();
216  const RealVect& curPosition = curParticle.position();
217  const RealVect& curStrength = (curParticle.*particleVectorField)();
218 
219  this->depositParticle(a_rho,
220  m_probLo,
221  m_dx,
222  curPosition,
223  curStrength.dataPtr(),
224  variables,
225  a_depositionType,
226  a_forceIrregNGP);
227  }
228 }
229 
230 template <class P, const RealVect& (P::*particleVectorField)() const>
231 void
232 EBParticleMesh::deposit2(const List<P>& a_particleList,
233  EBCellFAB& a_rho,
234  const DepositionType a_depositionType,
235  const bool a_forceIrregNGP) const
236 {
237  CH_TIME("EBParticleMesh::deposit2");
238 
239  const Interval variables(0, SpaceDim - 1);
240 
241  for (ListIterator<P> lit(a_particleList); lit; ++lit) {
242  const P& curParticle = lit();
243  const RealVect& curPosition = curParticle.position();
244  const RealVect& curStrength = (curParticle.*particleVectorField)();
245 
246  this->depositParticle2(a_rho,
247  m_probLo,
248  m_dx,
249  curPosition,
250  curStrength.dataPtr(),
251  variables,
252  a_depositionType,
253  a_forceIrregNGP);
254  }
255 }
256 
257 template <class P, RealVect (P::*particleVectorField)() const>
258 void
259 EBParticleMesh::deposit2(const List<P>& a_particleList,
260  EBCellFAB& a_rho,
261  const DepositionType a_depositionType,
262  const bool a_forceIrregNGP) const
263 {
264  CH_TIME("EBParticleMesh::deposit2");
265 
266  const Interval variables(0, SpaceDim - 1);
267 
268  for (ListIterator<P> lit(a_particleList); lit; ++lit) {
269  const P& curParticle = lit();
270  const RealVect& curPosition = curParticle.position();
271  const RealVect& curStrength = (curParticle.*particleVectorField)();
272 
273  this->depositParticle2(a_rho,
274  m_probLo,
275  m_dx,
276  curPosition,
277  curStrength.dataPtr(),
278  variables,
279  a_depositionType,
280  a_forceIrregNGP);
281  }
282 }
283 
284 template <class P, const RealVect& (P::*particleVectorField)() const>
285 void
286 EBParticleMesh::deposit4(const List<P>& a_particleList,
287  EBCellFAB& a_rho,
288  const DepositionType a_depositionType,
289  const bool a_forceIrregNGP) const
290 {
291  CH_TIME("EBParticleMesh::deposit4");
292 
293  const Interval variables(0, SpaceDim - 1);
294 
295  for (ListIterator<P> lit(a_particleList); lit; ++lit) {
296  const P& curParticle = lit();
297  const RealVect& curPosition = curParticle.position();
298  const RealVect& curStrength = (curParticle.*particleVectorField)();
299 
300  this->depositParticle4(a_rho,
301  m_probLo,
302  m_dx,
303  curPosition,
304  curStrength.dataPtr(),
305  variables,
306  a_depositionType,
307  a_forceIrregNGP);
308  }
309 }
310 
311 template <class P, RealVect (P::*particleVectorField)() const>
312 void
313 EBParticleMesh::deposit4(const List<P>& a_particleList,
314  EBCellFAB& a_rho,
315  const DepositionType a_depositionType,
316  const bool a_forceIrregNGP) const
317 {
318  CH_TIME("EBParticleMesh::deposit4");
319 
320  const Interval variables(0, SpaceDim - 1);
321 
322  for (ListIterator<P> lit(a_particleList); lit; ++lit) {
323  const P& curParticle = lit();
324  const RealVect& curPosition = curParticle.position();
325  const RealVect& curStrength = (curParticle.*particleVectorField)();
326 
327  this->depositParticle4(a_rho,
328  m_probLo,
329  m_dx,
330  curPosition,
331  curStrength.dataPtr(),
332  variables,
333  a_depositionType,
334  a_forceIrregNGP);
335  }
336 }
337 
338 template <class P, Real& (P::*particleScalarField)()>
339 void
340 EBParticleMesh::interpolate(List<P>& a_particleList,
341  const EBCellFAB& a_meshScalarField,
342  const DepositionType a_interpType,
343  const bool a_forceIrregNGP) const
344 {
345  CH_TIME("EBParticleMesh::interpolate(Real)");
346 
347  CH_assert(a_meshScalarField.nComp() == 1);
348 
349  const Interval variables(0, 0);
350 
351  Box validBox = m_domain.domainBox();
352 
353  switch (a_interpType) {
354  case DepositionType::NGP: {
355  validBox = m_domain.domainBox();
356 
357  break;
358  }
359  case DepositionType::CIC: {
360  validBox = grow(validBox, -1);
361 
362  break;
363  }
364  case DepositionType::TSC: {
365  validBox = grow(validBox, -2);
366 
367  break;
368  }
369  case DepositionType::W4: {
370  validBox = grow(validBox, -3);
371 
372  break;
373  }
374  default: {
375  MayDay::Error("EBParticleMesh::interpolate - logic bust");
376  }
377  }
378 
379  for (ListIterator<P> lit(a_particleList); lit; ++lit) {
380  P& curParticle = lit();
381  const RealVect& curPosition = curParticle.position();
382 
383  Real& curParticleField = (curParticle.*particleScalarField)();
384 
385  this->interpolateParticle(&curParticleField,
386  a_meshScalarField,
387  validBox,
388  m_probLo,
389  m_dx,
390  curPosition,
391  variables,
392  a_interpType,
393  a_forceIrregNGP);
394  }
395 }
396 
397 template <class P, RealVect& (P::*particleVectorField)()>
398 void
399 EBParticleMesh::interpolate(List<P>& a_particleList,
400  const EBCellFAB& a_meshVectorField,
401  const DepositionType a_interpType,
402  const bool a_forceIrregNGP) const
403 {
404  CH_TIME("EBParticleMesh::interpolate(RealVect)");
405 
406  CH_assert(a_meshVectorField.nComp() == SpaceDim);
407 
408  const Interval variables(0, SpaceDim - 1);
409 
410  // TLDR: This is a jack-of-all-trades interpolation function. The user will use this function to supply a pointer to the field that will be
411  // interpolated to. As per API, this function must be of the type 'RealVect& myParticleClass::myVectorVariable()'
412 
413  Box validBox = m_domain.domainBox();
414 
415  switch (a_interpType) {
416  case DepositionType::NGP: {
417  validBox = m_domain.domainBox();
418 
419  break;
420  }
421  case DepositionType::CIC: {
422  validBox = grow(validBox, -1);
423 
424  break;
425  }
426  case DepositionType::TSC: {
427  validBox = grow(validBox, -2);
428 
429  break;
430  }
431  case DepositionType::W4: {
432  validBox = grow(validBox, -3);
433 
434  break;
435  }
436  default: {
437  MayDay::Error("EBParticleMesh::interpolate - logic bust");
438  }
439  }
440 
441  for (ListIterator<P> lit(a_particleList); lit; ++lit) {
442  P& curParticle = lit();
443  const RealVect& curPosition = curParticle.position();
444 
445  RealVect& curParticleField = (curParticle.*particleVectorField)();
446 
447  this->interpolateParticle(curParticleField.dataPtr(),
448  a_meshVectorField,
449  validBox,
450  m_probLo,
451  m_dx,
452  curPosition,
453  variables,
454  a_interpType,
455  a_forceIrregNGP);
456  }
457 }
458 
459 inline void
461  const RealVect& a_probLo,
462  const RealVect& a_dx,
463  const RealVect& a_position,
464  const Real* a_strength,
465  const Interval a_components,
466  const DepositionType a_depositionType,
467  const bool a_forceIrregNGP) const
468 {
469  CH_TIME("EBParticleMesh::depositParticle");
470 
471  const int startComp = a_components.begin();
472  const int endComp = a_components.end();
473 
474  CH_assert(a_rho.nComp() >= endComp - 1);
475 
476  // TLDR: This performs regular deposition as if the particle lives on regular mesh data. If the cell is irregular we can use a class option
477  // to enforce NGP deposition in those cells. If the particle lives in a multi-valued cell I have no idea how to handle deposition.
478 
479  // Nifty lambda for converting RealVect position to IntVect (lower-left corner). Note that the input vector
480  // is the displacement from a_probLo and not the physical position.
481  const auto& dx = a_dx;
482  auto getCellIndex = [&dx](const RealVect a_rv) -> IntVect {
483  return IntVect(D_DECL(std::floor(a_rv[0] / dx[0]), std::floor(a_rv[1] / dx[1]), std::floor(a_rv[2] / dx[2])));
484  };
485 
486  // Get grid cell corresponding to this particle.
487  const IntVect particleIndex = getCellIndex(a_position - a_probLo);
488 
489  // Assertion -- particle must live on this patch.
490  CH_assert(m_region.contains(particleIndex));
491 
492  // Factors needed for the kernels. The little lambda will give us the distance
493  // between the cell we're looking at and the particle position (in units of the grid resolution)
494  const Real invVol = 1. / std::pow(a_dx[0], SpaceDim);
495 
496  auto particleDisplacement = [&](const IntVect& iv) -> RealVect {
497  return (a_probLo + (RealVect(iv) + 0.5 * RealVect::Unit) * a_dx - a_position) / a_dx;
498  };
499 
500  // Get regular data.
501  FArrayBox& rho = a_rho.getFArrayBox();
502 
503  // We can force NGP deposition in cut-cells if we want.
504  if (m_ebisbox.isIrregular(particleIndex) && a_forceIrregNGP) {
505  for (int comp = startComp; comp <= endComp; comp++) {
506  rho(particleIndex, comp) += a_strength[comp] * invVol;
507  }
508  }
509  else {
510  switch (a_depositionType) {
511  case DepositionType::NGP: {
512  for (int comp = startComp; comp <= endComp; comp++) {
513  rho(particleIndex, comp) += a_strength[comp] * invVol;
514  }
515  break;
516  }
517  case DepositionType::CIC: {
518  // Compute the index of the cell that contains the lower-left corner of this particle cloud. Also compute
519  // the box corresponding to the cloud.
520  const IntVect loIndex = getCellIndex(a_position - a_probLo - 0.5 * a_dx);
521  const Box cicBox = Box(loIndex, loIndex + IntVect::Unit);
522 
523  // This is the cloud-in-cell deposition kernel.
524  auto cicKernel = [&](const IntVect& iv) -> void {
525  Real weight = invVol;
526 
527  // L is the distance between the grid cell and the particle.
528  const RealVect L = particleDisplacement(iv);
529  for (int dir = 0; dir < SpaceDim; dir++) {
530  weight *= (1. - std::abs(L[dir]));
531  }
532 
533  for (int comp = startComp; comp <= endComp; comp++) {
534  rho(iv, comp) += weight * a_strength[comp];
535  }
536  };
537 
538  // Add mass to cells.
539  BoxLoops::loop(cicBox, cicKernel);
540 
541  break;
542  }
543  case DepositionType::TSC: {
544 
545  // Compute the index of the cell that contains the lower-left corner of this particle cloud. Also compute
546  // the box corresponding to the cloud.
547  const IntVect loIndex = getCellIndex(a_position - a_probLo - a_dx);
548  const Box tscBox = Box(loIndex, loIndex + 2 * IntVect::Unit);
549 
550  // This is the triangle-shaped cloud kernel.
551  auto tscKernel = [&](const IntVect& iv) -> void {
552  Real weight = invVol;
553 
554  const RealVect L = particleDisplacement(iv);
555  for (int dir = 0; dir < SpaceDim; dir++) {
556  const Real l = std::abs(L[dir]);
557 
558  if (l < 0.5) {
559  weight *= 0.75 - l * l;
560  }
561  else {
562  weight *= 0.5 * (1.5 - l) * (1.5 - l);
563  }
564  }
565 
566  for (int comp = startComp; comp <= endComp; comp++) {
567  rho(iv, comp) += weight * a_strength[comp];
568  }
569  };
570 
571  // Run the kernel.
572  BoxLoops::loop(tscBox, tscKernel);
573 
574  break;
575  }
576  case DepositionType::W4: {
577 
578  // Compute the index of the cell that contains the lower-left corner of this particle cloud. Also compute
579  // the box corresponding to the cloud.
580  const IntVect loIndex = getCellIndex(a_position - a_probLo - 1.5 * a_dx);
581  const Box w4Box = Box(loIndex, loIndex + 3 * IntVect::Unit);
582 
583  // This is the fourth order kernel.
584  auto w4Kernel = [&](const IntVect& iv) -> void {
585  Real weight = invVol;
586 
587  // L is the distance between the grid cell and the particle.
588  const RealVect L = particleDisplacement(iv);
589  for (int dir = 0; dir < SpaceDim; dir++) {
590  const Real l = std::abs(L[dir]);
591 
592  if (l < 1.0) {
593  weight *= 1.0 - 2.5 * l * l + 1.5 * l * l * l;
594  }
595  else {
596  weight *= 0.5 * (2. - l) * (2. - l) * (1. - l);
597  }
598  }
599 
600  for (int comp = startComp; comp <= endComp; comp++) {
601  rho(iv, comp) += weight * a_strength[comp];
602  }
603  };
604 
605  // Run the kernel.
606  BoxLoops::loop(w4Box, w4Kernel);
607 
608  break;
609  }
610  default: {
611  MayDay::Error("EBParticleMesh::depositParticle - logic bust, unknown particle deposition.");
612  break;
613  }
614  }
615  }
616 }
617 
618 inline void
620  const RealVect& a_probLo,
621  const RealVect& a_dx,
622  const RealVect& a_position,
623  const Real* a_strength,
624  const Interval a_components,
625  const DepositionType a_depositionType,
626  const bool a_forceIrregNGP) const
627 {
628  CH_TIME("EBParticleMesh::depositParticle2");
629 
630  const int startComp = a_components.begin();
631  const int endComp = a_components.end();
632 
633  CH_assert(a_rho.nComp() >= endComp - 1);
634 
635  // TLDR: This performs regular deposition as if the particle lives on regular mesh data. If the cell is irregular we can use a class option
636  // to enforce NGP deposition in those cells. If the particle lives in a multi-valued cell I have no idea how to handle deposition.
637  //
638  // Note that this is the version which deposits with particle widths that are 4 times the "usual" width. Currently, only NGP
639  // and CIC is supported.
640 
641  // Nifty lambda for converting RealVect position to IntVect (lower-left corner). Note that the input vector
642  // is the displacement from a_probLo and not the physical position.
643  const auto& dx = a_dx;
644  auto getCellIndex = [&dx](const RealVect a_rv) -> IntVect {
645  return IntVect(D_DECL(std::floor(a_rv[0] / dx[0]), std::floor(a_rv[1] / dx[1]), std::floor(a_rv[2] / dx[2])));
646  };
647 
648  // Get grid cell corresponding to this particle.
649  const IntVect particleIndex = getCellIndex(a_position - a_probLo);
650 
651  // Assertion -- particle must live on this patch.
652  CH_assert(m_region.contains(particleIndex));
653 
654  // Factors needed for the kernels. The little lambda will give us the distance
655  // between the cell we're looking at and the particle position (in units of the grid resolution)
656  const Real invVol = 1. / std::pow(a_dx[0], SpaceDim);
657 
658  auto particleDisplacement = [&](const IntVect& iv) -> RealVect {
659  return (a_probLo + (RealVect(iv) + 0.5 * RealVect::Unit) * a_dx - a_position) / a_dx;
660  };
661 
662  // Get regular data.
663  FArrayBox& rho = a_rho.getFArrayBox();
664 
665  // Force NGP in cut-cells if we want.
666  if (m_ebisbox.isIrregular(particleIndex) && a_forceIrregNGP) {
667  for (int comp = startComp; comp <= endComp; comp++) {
668  rho(particleIndex, comp) += a_strength[comp] * invVol;
669  }
670  }
671  else {
672  switch (a_depositionType) {
673  case DepositionType::NGP: {
674  for (int comp = startComp; comp <= endComp; comp++) {
675  rho(particleIndex, comp) += a_strength[comp] * invVol;
676  }
677 
678  break;
679  }
680  case DepositionType::CIC: {
681  // Compute the index of the cell that contains the lower-left corner of this particle cloud. Also compute
682  // the box corresponding to the cloud.
683  const IntVect loIndex = getCellIndex(a_position - a_probLo - 1.0 * a_dx);
684  const Box cic2Box = Box(loIndex, loIndex + 2 * IntVect::Unit);
685 
686  // This is the kernel for a CIC particle with with 2*dx
687  auto cic2Kernel = [&](const IntVect& iv) -> void {
688  Real weight = invVol;
689 
690  // L is the distance between the grid cell and the particle.
691  const RealVect L = particleDisplacement(iv);
692  for (int dir = 0; dir < SpaceDim; dir++) {
693  const Real l = std::abs(L[dir]);
694 
695  if (l > 0.5) {
696  weight *= 0.5 * (1.5 - l);
697  }
698  else {
699  weight *= 0.5;
700  }
701  }
702 
703  for (int comp = startComp; comp <= endComp; comp++) {
704  rho(iv, comp) += a_strength[comp] * weight;
705  }
706  };
707 
708  BoxLoops::loop(cic2Box, cic2Kernel);
709 
710  break;
711  }
712  default: {
713  MayDay::Error(
714  "EBParticleMesh::depositParticle2 - Invalid deposition type - only NGP and CIC supported for this deposition method. TSC/W4 have not been worked out.");
715  break;
716  }
717  }
718  }
719 }
720 
721 inline void
723  const RealVect& a_probLo,
724  const RealVect& a_dx,
725  const RealVect& a_position,
726  const Real* a_strength,
727  const Interval a_components,
728  const DepositionType a_depositionType,
729  const bool a_forceIrregNGP) const
730 {
731  CH_TIME("EBParticleMesh::depositParticle2");
732 
733  const int startComp = a_components.begin();
734  const int endComp = a_components.end();
735 
736  CH_assert(a_rho.nComp() >= endComp - 1);
737 
738  // TLDR: This performs regular deposition as if the particle lives on regular mesh data. If the cell is irregular we can use a class option
739  // to enforce NGP deposition in those cells. If the particle lives in a multi-valued cell I have no idea how to handle deposition.
740  //
741  // Note that this is the version which deposits with particle widths that are 4 times the "usual" width. Currently, only NGP
742  // and CIC is supported.
743 
744  // Nifty lambda for converting RealVect position to IntVect (lower-left corner). Note that the input vector
745  // is the displacement from a_probLo and not the physical position.
746  const auto& dx = a_dx;
747  auto getCellIndex = [&dx](const RealVect a_rv) -> IntVect {
748  return IntVect(D_DECL(std::floor(a_rv[0] / dx[0]), std::floor(a_rv[1] / dx[1]), std::floor(a_rv[2] / dx[2])));
749  };
750 
751  // Get grid cell corresponding to this particle.
752  const IntVect particleIndex = getCellIndex(a_position - a_probLo);
753 
754  // Assertion -- particle must live on this patch.
755  CH_assert(m_region.contains(particleIndex));
756 
757  // Factors needed for the kernels. The little lambda will give us the distance
758  // between the cell we're looking at and the particle position (in units of the grid resolution)
759  const Real invVol = 1.0 / std::pow(a_dx[0], SpaceDim);
760 
761  auto particleDisplacement = [&](const IntVect& iv) -> RealVect {
762  return (a_probLo + (RealVect(iv) + 0.5 * RealVect::Unit) * a_dx - a_position) / a_dx;
763  };
764 
765  // Get regular data.
766  FArrayBox& rho = a_rho.getFArrayBox();
767 
768  // Force NGP in cut-cells if we want.
769  if (m_ebisbox.isIrregular(particleIndex) && a_forceIrregNGP) {
770  for (int comp = startComp; comp <= endComp; comp++) {
771  rho(particleIndex, comp) += a_strength[comp] * invVol;
772  }
773  }
774  else {
775  switch (a_depositionType) {
776  case DepositionType::NGP: {
777  for (int comp = startComp; comp <= endComp; comp++) {
778  rho(particleIndex, comp) += a_strength[comp] * invVol;
779  }
780  break;
781  }
782  case DepositionType::CIC: {
783  // Compute the index of the cell that contains the lower-left corner of this particle cloud. Also compute
784  // the box corresponding to the cloud.
785  const IntVect loIndex = getCellIndex(a_position - a_probLo - 2.0 * a_dx);
786  const Box cic4Box = Box(loIndex, loIndex + 4 * IntVect::Unit);
787 
788  // This is the kernel for a CIC particle with with 4*dx
789  auto cic4Kernel = [&](const IntVect& iv) -> void {
790  Real weight = invVol;
791 
792  // L is the distance between the grid cell and the particle -- in units of dx.
793  const RealVect L = particleDisplacement(iv);
794  for (int dir = 0; dir < SpaceDim; dir++) {
795  const Real l = std::abs(L[dir]);
796 
797  if (l >= 1.5) {
798  weight *= 0.25 * (2.5 - l);
799  }
800  else {
801  weight *= 0.25;
802  }
803  }
804 
805  for (int comp = startComp; comp <= endComp; comp++) {
806  rho(iv, comp) += a_strength[comp] * weight;
807  }
808  };
809 
810  BoxLoops::loop(cic4Box, cic4Kernel);
811 
812  break;
813  }
814  default: {
815  MayDay::Error(
816  "EBParticleMesh::depositParticle4 - Invalid deposition type - only NGP and CIC supported for this deposition method. TSC/W4 have not been worked out.");
817  break;
818  }
819  }
820  }
821 }
822 
823 inline void
825  const EBCellFAB& a_meshField,
826  const Box& a_validBox,
827  const RealVect& a_probLo,
828  const RealVect& a_dx,
829  const RealVect& a_position,
830  const Interval& a_interval,
831  const DepositionType a_interpType,
832  const bool a_forceIrregNGP) const
833 {
834  CH_TIME("EBParticleMesh::interpolateParticle");
835 
836  const int startComp = a_interval.begin();
837  const int endComp = a_interval.end();
838 
839  CH_assert(a_meshField.nComp() >= endComp - 1);
840 
841  // TLDR: This performs regular deposition as if the particle lives on regular mesh data. If the cell is irregular we can use a class option
842  // to enforce NGP deposition in those cells. If the particle lives in a multi-valued cell I have no idea how to handle deposition.
843 
844  // Nifty lambda for converting RealVect position to IntVect (lower-left corner). Note that the input vector
845  // is the displacement from a_probLo and not the physical position.
846  const auto& dx = a_dx;
847 
848  auto getCellIndex = [&dx](const RealVect a_rv) -> IntVect {
849  return IntVect(D_DECL(std::floor(a_rv[0] / dx[0]), std::floor(a_rv[1] / dx[1]), std::floor(a_rv[2] / dx[2])));
850  };
851 
852  // Get grid cell corresponding to this particle.
853  const IntVect particleIndex = getCellIndex(a_position - a_probLo);
854 
855  // Assertion -- particle must live on this patch.
856  CH_assert(m_region.contains(particleIndex));
857 
858  // Nifty little lambda for computing the displacement between the particle and a grid cell.
859  auto particleDisplacement = [&](const IntVect& iv) -> RealVect {
860  return (a_probLo + (RealVect(iv) + 0.5 * RealVect::Unit) * a_dx - a_position) / a_dx;
861  };
862 
863  // Get regular data.
864  const FArrayBox& meshField = a_meshField.getFArrayBox();
865 
866  // Irregular cells always do an NGP deposit to prevent clouds leaking into the other side.
867  if ((m_ebisbox.isIrregular(particleIndex) && a_forceIrregNGP) || !(a_validBox.contains(particleIndex))) {
868  for (int comp = startComp; comp <= endComp; comp++) {
869  a_particleField[comp] = meshField(particleIndex, comp);
870  }
871  }
872  else if (m_ebisbox.isCovered(particleIndex)) { // Need to set to something.
873  for (int comp = startComp; comp <= endComp; comp++) {
874  a_particleField[comp] = 0.0;
875  }
876  }
877  else {
878  switch (a_interpType) {
879  case DepositionType::NGP: // Hook for nearest-grid-point interpolation.
880  {
881  for (int comp = startComp; comp <= endComp; comp++) {
882  a_particleField[comp] = meshField(particleIndex, comp);
883  }
884 
885  break;
886  }
887  case DepositionType::CIC: // Hook for cloud-in-cell interpolation.
888  {
889  // Compute the index of the cell that contains the lower-left corner of this particle cloud. Also compute
890  // the box corresponding to the cloud. This region contains the cells where we gather the field.
891  const IntVect loIndex = getCellIndex(a_position - a_probLo - 0.5 * a_dx);
892  const Box cicBox = Box(loIndex, loIndex + IntVect::Unit);
893 
894  // This is the cloud-in-cell interpolation kernel. It computes the weight from the current cell and
895  // gathers the force.
896  auto cicKernel = [&](const IntVect& iv) -> void {
897  Real weight = 1.0;
898 
899  // L is the distance between the grid cell and the particle.
900  const RealVect L = particleDisplacement(iv);
901  for (int dir = 0; dir < SpaceDim; dir++) {
902  weight *= (1. - std::abs(L[dir]));
903  }
904 
905  for (int comp = startComp; comp <= endComp; comp++) {
906  a_particleField[comp] += weight * meshField(iv, comp);
907  }
908  };
909 
910  // Run kernel and gather field from mesh.
911  for (int comp = startComp; comp <= endComp; comp++) {
912  a_particleField[comp] = 0.0;
913  }
914 
915  BoxLoops::loop(cicBox, cicKernel);
916 
917  break;
918  }
919  case DepositionType::TSC: // Hook for triangle-shaped cloud interpolation.
920  {
921  // Compute the index of the cell that contains the lower-left corner of this particle cloud. Also compute
922  // the box corresponding to the cloud. This region contains the cells where we gather the field.
923  const IntVect loIndex = getCellIndex(a_position - a_probLo - 1.0 * a_dx);
924  const Box tscBox = Box(loIndex, loIndex + 2 * IntVect::Unit);
925 
926  // This is the triangle-shaped-cloud interpolation kernel. It computes the weight from the current cell and
927  // gather the force.
928  auto tscKernel = [&](const IntVect& iv) -> void {
929  Real weight = 1.0;
930 
931  // L is the distance between the grid cell and the particle.
932  const RealVect L = particleDisplacement(iv);
933  for (int dir = 0; dir < SpaceDim; dir++) {
934  const Real& l = std::abs(L[dir]);
935 
936  if (l < 0.5) {
937  weight *= 0.75 - l * l;
938  }
939  else {
940  weight *= 0.5 * (1.5 - l) * (1.5 - l);
941  }
942  }
943 
944  for (int comp = startComp; comp <= endComp; comp++) {
945  a_particleField[comp] += weight * meshField(iv, comp);
946  }
947  };
948 
949  // Run kernel and gather field from mesh.
950  for (int comp = startComp; comp <= endComp; comp++) {
951  a_particleField[comp] = 0.0;
952  }
953 
954  BoxLoops::loop(tscBox, tscKernel);
955 
956  break;
957  }
958  case DepositionType::W4: // Hook for fourth order interpolation.
959  {
960  // Compute the index of the cell that contains the lower-left corner of this particle cloud. Also compute
961  // the box corresponding to the cloud. This region contains the cells where we gather the field.
962  const IntVect loIndex = getCellIndex(a_position - a_probLo - 1.5 * a_dx);
963  const Box w4Box = Box(loIndex, loIndex + 3 * IntVect::Unit);
964 
965  // This is the fourth order kernel.
966  auto w4Kernel = [&](const IntVect& iv) -> void {
967  Real weight = 1.0;
968 
969  // L is the distance between the grid cell and the particle.
970  const RealVect L = particleDisplacement(iv);
971  for (int dir = 0; dir < SpaceDim; dir++) {
972  const Real l = std::abs(L[dir]);
973 
974  if (l < 1.0) {
975  weight *= 1.0 - 2.5 * l * l + 1.5 * l * l * l;
976  }
977  else {
978  weight *= 0.5 * (2. - l) * (2. - l) * (1. - l);
979  }
980  }
981 
982  for (int comp = startComp; comp <= endComp; comp++) {
983  a_particleField[comp] += weight * meshField(iv, comp);
984  }
985  };
986 
987  // Run kernel and gather field from mesh.
988  for (int comp = startComp; comp <= endComp; comp++) {
989  a_particleField[comp] = 0.0;
990  }
991 
992  BoxLoops::loop(w4Box, w4Kernel);
993 
994  break;
995  }
996  default:
997  MayDay::Error("EBParticleMesh::interpolateParticle(RealVect) - Invalid interpolation type requested.");
998  }
999  }
1000 }
1001 
1002 #include <CD_NamespaceFooter.H>
1003 
1004 #endif
Declaration of a namespace for proto-typing grid and EB loops.
DepositionType
Deposition types.
Definition: CD_DepositionType.H:23
Declaration of a class for handling particle-mesh interpolation and deposition.
Box m_region
Cell-centered box, i.e. valid region.
Definition: CD_EBParticleMesh.H:437
void interpolate(List< P > &a_particleList, const EBCellFAB &a_meshScalarField, const DepositionType a_interpType, const bool a_forceIrregNGP=false) const
Interpolate a scalar field onto the particle position.
Definition: CD_EBParticleMeshImplem.H:340
void deposit2(const List< P > &a_particleList, EBCellFAB &a_rho, const DepositionType a_depositionType, const bool a_forceIrregNGP=false) const
Deposit particle onto the mesh using twice the standard cloud width.
Definition: CD_EBParticleMeshImplem.H:67
void interpolateParticle(Real *a_particleField, const EBCellFAB &a_field, const Box &a_validBox, const RealVect &a_probLo, const RealVect &a_dx, const RealVect &a_position, const Interval &a_interval, const DepositionType a_interpType, const bool a_forceIrregNGP) const
Wrapper function that interpolates a particle fields onto positions.
Definition: CD_EBParticleMeshImplem.H:824
void depositParticle(EBCellFAB &a_rho, const RealVect &a_probLo, const RealVect &a_dx, const RealVect &a_position, const Real *a_strength, const Interval a_components, const DepositionType a_depositionType, const bool a_forceIrregNGP) const
Wrapper function for depositing a single particle.
Definition: CD_EBParticleMeshImplem.H:460
void deposit4(const List< P > &a_particleList, EBCellFAB &a_rho, const DepositionType a_depositionType, const bool a_forceIrregNGP=false) const
Deposit particle onto the mesh using 4x the standard cloud width.
Definition: CD_EBParticleMeshImplem.H:121
void deposit(const List< P > &a_particleList, EBCellFAB &a_rho, const DepositionType a_depositionType, const bool a_forceIrregNGP=false) const
Deposit particle onto the mesh using a standard cloud width.
Definition: CD_EBParticleMeshImplem.H:25
RealVect m_dx
Grid resolution.
Definition: CD_EBParticleMesh.H:447
ProblemDomain m_domain
Problem domain.
Definition: CD_EBParticleMesh.H:432
void depositParticle2(EBCellFAB &a_rho, const RealVect &a_probLo, const RealVect &a_dx, const RealVect &a_position, const Real *a_strength, const Interval a_components, const DepositionType a_depositionType, const bool a_forceIrregNGP) const
Wrapper function for depositing a single particle which has twice the usual cloud width.
Definition: CD_EBParticleMeshImplem.H:619
EBISBox m_ebisbox
EBIS box.
Definition: CD_EBParticleMesh.H:442
RealVect m_probLo
Lower-left corner of computational domain.
Definition: CD_EBParticleMesh.H:452
void depositParticle4(EBCellFAB &a_rho, const RealVect &a_probLo, const RealVect &a_dx, const RealVect &a_position, const Real *a_strength, const Interval a_components, const DepositionType a_depositionType, const bool a_forceIrregNGP) const
Wrapper function for depositing a single particle which has four times the usual cloud width.
Definition: CD_EBParticleMeshImplem.H:722
ALWAYS_INLINE void loop(const Box &a_computeBox, Functor &&kernel, const IntVect &a_stride=IntVect::Unit)
Launch a C++ kernel over a regular grid.
Definition: CD_BoxLoopsImplem.H:20