chombo-discharge
CD_ItoKMCStepperImplem.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_ItoKMCStepperImplem_H
13 #define CD_ItoKMCStepperImplem_H
14 
15 // Std includes
16 #include <limits>
17 
18 // Chombo includes
19 #include <ParmParse.H>
20 
21 // Our includes
23 #include <CD_ItoKMCStepper.H>
24 #include <CD_ParticleOps.H>
25 #include <CD_DataOps.H>
26 #include <CD_ParallelOps.H>
27 #include <CD_Units.H>
28 #include <CD_Timer.H>
29 #include <CD_Location.H>
30 #include <CD_NamespaceHeader.H>
31 
32 using namespace Physics::ItoKMC;
33 
34 template <typename I, typename C, typename R, typename F>
36 {
37  CH_TIME("ItoKMCStepper::ItoKMCStepper");
38 
39  m_verbosity = -1;
40  m_profile = false;
41  m_name = "ItoKMCStepper";
42  m_plasmaPhase = phase::gas;
43  m_dt = 0.0;
44  m_prevDt = -1.0;
45  m_maxGrowthDt = 1.E99;
46  m_maxShrinkDt = 1.E99;
47  m_time = 0.0;
48  m_timeStep = 0;
49  m_loadPerCell = 1.0;
50  m_redistributeCDR = true;
51  m_regridSuperparticles = true;
52  m_fluidRealm = Realm::Primal;
53  m_particleRealm = Realm::Primal;
54  m_minParticleAdvectionCFL = 0.0;
55  m_maxParticleAdvectionCFL = 1.0;
56  m_minParticleDiffusionCFL = 0.0;
57  m_maxParticleDiffusionCFL = std::numeric_limits<Real>::max();
58  m_minParticleAdvectionDiffusionCFL = std::numeric_limits<Real>::max();
59  m_maxParticleAdvectionDiffusionCFL = std::numeric_limits<Real>::max();
60  m_fluidAdvectionDiffusionCFL = 0.5;
61  m_relaxTimeFactor = std::numeric_limits<Real>::max();
64 }
65 
66 template <typename I, typename C, typename R, typename F>
67 ItoKMCStepper<I, C, R, F>::ItoKMCStepper(RefCountedPtr<ItoKMCPhysics>& a_physics) noexcept : ItoKMCStepper<I, C, R, F>()
68 {
69  CH_TIME("ItoKMCStepper::ItoKMCStepper(RefCountrPtr<ItoKMCPhysics>)");
70 
71  m_physics = a_physics;
72 
73  if (m_physics->getNumPlasmaSpecies() == 0) {
74  MayDay::Abort("ItoKMCStepper::ItoKMCStepper -- numPlasmaSpecies = 0, there's no problem to solve here!");
75  }
76 }
77 
78 template <typename I, typename C, typename R, typename F>
80 {
81  CH_TIME("ItoKMCStepper::~ItoKMCStepper");
82 }
83 
84 template <typename I, typename C, typename R, typename F>
85 void
87 {
88  CH_TIME("ItoKMCStepper::parseOptions");
89  if (m_verbosity > 5) {
90  pout() << m_name + "::parseOptions" << endl;
91  }
92 
93  this->parseVerbosity();
94  this->parseExitOnFailure();
95  this->parseRedistributeCDR();
96  this->parsePlotVariables();
97  this->parseSuperParticles();
98  this->parseDualGrid();
99  this->parseLoadBalance();
100  this->parseTimeStepRestrictions();
101  this->parseParametersEB();
102 }
103 
104 template <typename I, typename C, typename R, typename F>
105 void
107 {
108  CH_TIME("ItoKMCStepper::parseRuntimeOptions");
109  if (m_verbosity > 5) {
110  pout() << m_name + "::parseRuntimeOptions" << endl;
111  }
112 
113  this->parseVerbosity();
114  this->parseExitOnFailure();
115  this->parseRedistributeCDR();
116  this->parsePlotVariables();
117  this->parseSuperParticles();
118  this->parseLoadBalance();
119  this->parseTimeStepRestrictions();
120  this->parseParametersEB();
121 
122  m_ito->parseRuntimeOptions();
123  m_cdr->parseRuntimeOptions();
124  m_fieldSolver->parseRuntimeOptions();
125  m_rte->parseRuntimeOptions();
126  m_sigmaSolver->parseRuntimeOptions();
127 
128  m_physics->parseRuntimeOptions();
129 }
130 
131 template <typename I, typename C, typename R, typename F>
132 void
134 {
135  CH_TIME("ItoKMCStepper::parseVerbosity");
136  if (m_verbosity > 5) {
137  pout() << m_name + "::parseVerbosity" << endl;
138  }
139 
140  ParmParse pp(m_name.c_str());
141 
142  pp.get("verbosity", m_verbosity);
143  pp.get("profile", m_profile);
144 }
145 
146 template <typename I, typename C, typename R, typename F>
147 void
149 {
150  CH_TIME("ItoKMCStepper::parseExitOnFailure");
151  if (m_verbosity > 5) {
152  pout() << m_name + "::parseExitOnFailure" << endl;
153  }
154 
155  ParmParse pp(m_name.c_str());
156 
157  pp.get("abort_on_failure", m_abortOnFailure);
158 }
159 
160 template <typename I, typename C, typename R, typename F>
161 void
163 {
164  CH_TIME("ItoKMCStepper::parseRedistributeCDR");
165  if (m_verbosity > 5) {
166  pout() << m_name + "::parseRedistributeCDR" << endl;
167  }
168 
169  ParmParse pp(m_name.c_str());
170 
171  pp.get("redistribute_cdr", m_redistributeCDR);
172 }
173 
174 template <typename I, typename C, typename R, typename F>
175 void
177 {
178  CH_TIME("ItoKMCStepper::parsePlotVariables");
179  if (m_verbosity > 5) {
180  pout() << m_name + "::parsePlotVariables" << endl;
181  }
182 
183  m_plotConductivity = false;
184  m_plotCurrentDensity = false;
185  m_plotParticlesPerPatch = false;
186 
187  // Read in plot variables.
188  ParmParse pp(m_name.c_str());
189  const int num = pp.countval("plt_vars");
190 
191  if (num > 0) {
192  Vector<std::string> str(num);
193  pp.getarr("plt_vars", str, 0, num);
194 
195  // Set plot variables
196  for (int i = 0; i < num; i++) {
197  if (str[i] == "conductivity") {
198  m_plotConductivity = true;
199  }
200  else if (str[i] == "current_density") {
201  m_plotCurrentDensity = true;
202  }
203  else if (str[i] == "particles_per_patch") {
204  m_plotParticlesPerPatch = true;
205  }
206  }
207  }
208 }
209 
210 template <typename I, typename C, typename R, typename F>
211 void
213 {
214  CH_TIME("ItoKMCStepper::parseSuperParticles");
215  if (m_verbosity > 5) {
216  pout() << m_name + "::parseSuperParticles" << endl;
217  }
218 
219  ParmParse pp(m_name.c_str());
220 
221  m_particlesPerCell.resize(pp.countval("particles_per_cell"));
222 
223  pp.get("merge_interval", m_mergeInterval);
224  pp.get("regrid_superparticles", m_regridSuperparticles);
225  pp.getarr("particles_per_cell", m_particlesPerCell, 0, m_particlesPerCell.size());
226 
227  for (int lvl = 0; lvl < m_particlesPerCell.size(); lvl++) {
228  if (m_particlesPerCell[lvl] <= 0) {
229  MayDay::Error("ItoKMCStepper::parseSuperParticles -- must have 'particles_per_cell' > 0");
230  }
231  }
232 }
233 
234 template <typename I, typename C, typename R, typename F>
235 void
237 {
238  CH_TIME("ItoKMCStepper::parseDualGrid");
239  if (m_verbosity > 5) {
240  pout() << m_name + "::parseDualGrid" << endl;
241  }
242 
243  ParmParse pp(m_name.c_str());
244 
245  pp.get("dual_grid", m_dualGrid);
246 
247  if (m_dualGrid) {
248  m_particleRealm = "ParticleRealm";
249 
250  CH_assert(m_particleRealm != m_fluidRealm);
251  }
252  else {
253  m_particleRealm = m_fluidRealm;
254  }
255 }
256 
257 template <typename I, typename C, typename R, typename F>
258 void
260 {
261  CH_TIME("ItoKMCStepper::parseLoadBalance");
262  if (m_verbosity > 5) {
263  pout() << m_name + "::parseLoadBalance" << endl;
264  }
265 
266  ParmParse pp(m_name.c_str());
267 
268  std::string str;
269 
270  pp.get("load_balance_particles", m_loadBalanceParticles);
271  pp.get("load_balance_fluid", m_loadBalanceFluid);
272  pp.get("load_per_cell", m_loadPerCell);
273 
274  // Box sorting for load balancing
275  pp.get("box_sorting", str);
276  if (str == "none") {
277  m_boxSort = BoxSorting::None;
278  }
279  else if (str == "std") {
280  m_boxSort = BoxSorting::Std;
281  }
282  else if (str == "shuffle") {
283  m_boxSort = BoxSorting::Shuffle;
284  }
285  else if (str == "morton") {
286  m_boxSort = BoxSorting::Morton;
287  }
288  else {
289  const std::string err = "ItoKMCStepper::parseLoadBalance - 'box_sorting = " + str + "' not recognized";
290 
291  MayDay::Error(err.c_str());
292  }
293 
294  // Get the load balancing index.
295  const int numIndices = pp.countval("load_indices");
296 
297  if (numIndices > 0) {
298  pp.getarr("load_indices", m_loadBalanceIndices, 0, numIndices);
299  }
300  else {
301  const std::string err = "ItoKMCStepper::parseLoadBalance - 'load_indices' argument has zero entries";
302 
303  MayDay::Error(err.c_str());
304  }
305 }
306 
307 template <typename I, typename C, typename R, typename F>
308 void
310 {
311  CH_TIME("ItoKMCStepper::parseTimeStepRestrictions");
312  if (m_verbosity > 5) {
313  pout() << m_name + "::parseTimeStepRestrictions" << endl;
314  }
315 
316  ParmParse pp(m_name.c_str());
317 
318  pp.get("min_particle_advection_cfl", m_minParticleAdvectionCFL);
319  pp.get("max_particle_advection_cfl", m_maxParticleAdvectionCFL);
320  pp.get("min_particle_diffusion_cfl", m_minParticleDiffusionCFL);
321  pp.get("max_particle_diffusion_cfl", m_maxParticleDiffusionCFL);
322  pp.get("min_particle_advection_diffusion_cfl", m_minParticleAdvectionDiffusionCFL);
323  pp.get("max_particle_advection_diffusion_cfl", m_maxParticleAdvectionDiffusionCFL);
324  pp.get("fluid_advection_diffusion_cfl", m_fluidAdvectionDiffusionCFL);
325  pp.get("relax_dt_factor", m_relaxTimeFactor);
326  pp.get("min_dt", m_minDt);
327  pp.get("max_dt", m_maxDt);
328  pp.get("max_growth_dt", m_maxGrowthDt);
329  pp.get("max_shrink_dt", m_maxShrinkDt);
330 
331  if (m_maxGrowthDt <= 1.0) {
332  MayDay::Error("ItoKMCStepper::parseTimeStepRestrictions() - must have max_growth_dt > 1.0");
333  }
334 
335  if (m_maxShrinkDt <= 1.0) {
336  MayDay::Error("ItoKMCStepper::parseTimeStepRestrictions() - must have max_shrink_dt > 1.0");
337  }
338 
339  if (m_relaxTimeFactor <= 0.0) {
340  MayDay::Error("ItoKMCStepper::parseTimeStepRestrictions() - must have relax_dt > 0.0");
341  }
342 
343  if (m_minDt < 0.0) {
344  MayDay::Error("ItoKMCStepper::parseTimeStepRestrictions() - must have min_dt >= 0.0");
345  }
346 
347  if (m_maxDt < 0.0) {
348  MayDay::Error("ItoKMCStepper::parseTimeStepRestrictions() - must have max_dt >= 0.0");
349  }
350 
351  if (m_maxParticleAdvectionCFL <= 0.0) {
352  MayDay::Error("ItoKMCStepper::parseTimeStepRestrictions() - must have max_particle_advection_cfl > 0.0");
353  }
354 
355  if (m_minParticleAdvectionCFL < 0.0) {
356  MayDay::Error("ItoKMCStepper::parseTimeStepRestrictions() - must have min_particle_advection_cfl >= 0.0");
357  }
358 
359  if (m_maxParticleDiffusionCFL <= 0.0) {
360  MayDay::Error("ItoKMCStepper::parseTimeStepRestrictions() - must have particle_diffusion_cfl > 0.0");
361  }
362 
363  if (m_minParticleDiffusionCFL < 0.0) {
364  MayDay::Error("ItoKMCStepper::parseTimeStepRestrictions() - must have particle_diffusion_cfl >= 0.0");
365  }
366 
367  if (m_maxParticleAdvectionDiffusionCFL <= 0.0) {
368  MayDay::Error("ItoKMCStepper::parseTimeStepRestrictions() - must have particle_advection_diffusion_cfl > 0.0");
369  }
370 
371  if (m_minParticleAdvectionDiffusionCFL < 0.0) {
372  MayDay::Error("ItoKMCStepper::parseTimeStepRestrictions() - must have particle_advection_diffusion_cfl >= 0.0");
373  }
374 
375  if (m_fluidAdvectionDiffusionCFL <= 0.0) {
376  MayDay::Error("ItoKMCStepper::parseTimeStepRestrictions() - must have fluid_advection_diffusion_cfl > 0.0");
377  }
378 }
379 
380 template <typename I, typename C, typename R, typename F>
381 void
383 {
384  CH_TIME("ItoKMCStepper::parseTimeStepRestrictions");
385  if (m_verbosity > 5) {
386  pout() << m_name + "::parseTimeStepRestrictions" << endl;
387  }
388 
389  ParmParse pp(m_name.c_str());
390 
391  std::string str;
392 
393  pp.get("eb_tolerance", m_toleranceEB);
394 }
395 
396 template <typename I, typename C, typename R, typename F>
397 void
399 {
400  CH_TIME("ItoKMCStepper::setupSolver");
401  if (m_verbosity > 5) {
402  pout() << m_name + "::setupSolvers" << endl;
403  }
404 
405  this->setupIto();
406  this->setupCdr();
407  this->setupPoisson();
408  this->setupRadiativeTransfer();
409  this->setupSigma();
410 }
411 
412 template <typename I, typename C, typename R, typename F>
413 void
415 {
416  CH_TIME("ItoKMCStepper::setupIto");
417  if (m_verbosity > 5) {
418  pout() << m_name + "::setupIto" << endl;
419  }
420 
421  ItoFactory<ItoSolver, I> factory;
422  m_ito = factory.newLayout(m_physics->getItoSpecies());
423 
424  m_ito->parseOptions();
425  m_ito->setAmr(m_amr);
426  m_ito->setPhase(m_plasmaPhase);
427  m_ito->setComputationalGeometry(m_computationalGeometry);
428  m_ito->setRealm(m_particleRealm);
429 }
430 
431 template <typename I, typename C, typename R, typename F>
432 void
434 {
435  CH_TIME("ItoKMCStepper::setupCdr");
436  if (m_verbosity > 5) {
437  pout() << m_name + "::setupCdr" << endl;
438  }
439 
440  CdrFactory<CdrSolver, C> factory;
441  m_cdr = factory.newLayout(m_physics->getCdrSpecies());
442 
443  m_cdr->parseOptions();
444  m_cdr->setAmr(m_amr);
445  m_cdr->setPhase(m_plasmaPhase);
446  m_cdr->setComputationalGeometry(m_computationalGeometry);
447  m_cdr->setRealm(m_fluidRealm);
448 }
449 
450 template <typename I, typename C, typename R, typename F>
451 void
453 {
454  CH_TIME("ItoKMCStepper::setupRadiativeTransfer");
455  if (m_verbosity > 5) {
456  pout() << m_name + "::setupRadiativeTransfer" << endl;
457  }
458 
459  RtFactory<McPhoto, R> factory;
460  m_rte = factory.newLayout(m_physics->getRtSpecies());
461 
462  m_rte->parseOptions();
463  m_rte->setPhase(m_plasmaPhase);
464  m_rte->setAmr(m_amr);
465  m_rte->setComputationalGeometry(m_computationalGeometry);
466  m_rte->setRealm(m_particleRealm);
467  m_rte->sanityCheck();
468 }
469 
470 template <typename I, typename C, typename R, typename F>
471 void
473 {
474  CH_TIME("ItoKMCStepper::setupPoisson");
475  if (m_verbosity > 5) {
476  pout() << m_name + "::setupPoisson" << endl;
477  }
478 
479  m_fieldSolver = RefCountedPtr<FieldSolver>(new F());
480  m_fieldSolver->parseOptions();
481  m_fieldSolver->setAmr(m_amr);
482  m_fieldSolver->setComputationalGeometry(m_computationalGeometry);
483  m_fieldSolver->setVoltage(m_voltage);
484  m_fieldSolver->setRealm(m_fluidRealm);
485 }
486 
487 template <typename I, typename C, typename R, typename F>
488 void
490 {
491  CH_TIME("ItoKMCStepper::setupSigma");
492  if (m_verbosity > 5) {
493  pout() << m_name + "::setupSigma" << endl;
494  }
495 
496  m_sigmaSolver = RefCountedPtr<SurfaceODESolver<1>>(new SurfaceODESolver<1>(m_amr));
497  m_sigmaSolver->parseOptions();
498  m_sigmaSolver->setRealm(m_fluidRealm);
499  m_sigmaSolver->setPhase(m_plasmaPhase);
500  m_sigmaSolver->setName("Surface charge");
501  m_sigmaSolver->setTime(0, 0.0, 0.0);
502 }
503 
504 template <typename I, typename C, typename R, typename F>
505 void
507 {
508  CH_TIME("ItoKMCStepper::allocate");
509  if (m_verbosity > 5) {
510  pout() << m_name + "::allocate" << endl;
511  }
512 
513  m_ito->allocate();
514  m_cdr->allocate();
515  m_rte->allocate();
516  m_fieldSolver->allocate();
517  m_sigmaSolver->allocate();
518 
519  this->allocateInternals();
520 }
521 
522 template <typename I, typename C, typename R, typename F>
523 void
525 {
526  CH_TIME("ItoKMCStepper::allocateInternals");
527  if (m_verbosity > 5) {
528  pout() << m_name + "::allocateInternals" << endl;
529  }
530 
531  const int numItoSpecies = m_physics->getNumItoSpecies();
532  const int numCdrSpecies = m_physics->getNumCdrSpecies();
533  const int numPlasmaSpecies = m_physics->getNumPlasmaSpecies();
534  const int numPhotonSpecies = m_physics->getNumPhotonSpecies();
535 
536  CH_assert(numPlasmaSpecies > 0);
537 
538  // Scratch data.
539  m_amr->allocate(m_fluidScratch1, m_fluidRealm, m_plasmaPhase, 1);
540  m_amr->allocate(m_fluidScratchD, m_fluidRealm, m_plasmaPhase, SpaceDim);
541  m_amr->allocate(m_fluidScratchEB, m_fluidRealm, m_plasmaPhase, 1);
542 
543  m_amr->allocate(m_particleScratch1, m_particleRealm, m_plasmaPhase, 1);
544  m_amr->allocate(m_particleScratchD, m_particleRealm, m_plasmaPhase, SpaceDim);
545  m_amr->allocate(m_particleScratchEB, m_particleRealm, m_plasmaPhase, 1);
546 
547  // Storage for conductiviies on cell, cell faces, and EB faces.
548  m_amr->allocate(m_conductivityCell, m_fluidRealm, m_plasmaPhase, 1);
549  m_amr->allocate(m_conductivityFace, m_fluidRealm, m_plasmaPhase, 1);
550  m_amr->allocate(m_conductivityEB, m_fluidRealm, m_plasmaPhase, 1);
551 
552  // Electric field data on both realms.
553  m_amr->allocate(m_electricFieldParticle, m_particleRealm, m_plasmaPhase, SpaceDim);
554  m_amr->allocate(m_electricFieldFluid, m_fluidRealm, m_plasmaPhase, SpaceDim);
555 
556  // Data for CDR-based solver mobilities
557  m_cdrMobilities.resize(numCdrSpecies);
558  m_cdrPhotoiProducts.resize(numCdrSpecies);
559  for (int i = 0; i < numCdrSpecies; i++) {
560  m_amr->allocate(m_cdrMobilities[i], m_fluidRealm, m_plasmaPhase, 1);
561  m_amr->allocate(m_cdrPhotoiProducts[i], m_particleRealm);
562  }
563 
564  // Storage for the density gradients
565  m_fluidGradPhiIto.resize(numItoSpecies);
566  m_fluidPhiIto.resize(numItoSpecies);
567  m_fluidGradPhiCDR.resize(numCdrSpecies);
568  for (int i = 0; i < numItoSpecies; i++) {
569  m_amr->allocate(m_fluidGradPhiIto[i], m_fluidRealm, m_plasmaPhase, SpaceDim);
570  m_amr->allocate(m_fluidPhiIto[i], m_fluidRealm, m_plasmaPhase, 1);
571  }
572  for (int i = 0; i < numCdrSpecies; i++) {
573  m_amr->allocate(m_fluidGradPhiCDR[i], m_fluidRealm, m_plasmaPhase, SpaceDim);
574  }
575 
576  // Storage for secondary particle and photon emission
577  m_secondaryParticles.resize(numItoSpecies);
578  m_secondaryPhotons.resize(numPhotonSpecies);
579 
580  m_cdrFluxes.resize(numCdrSpecies);
581  m_cdrFluxesExtrap.resize(numCdrSpecies);
582 
583  for (int i = 0; i < numItoSpecies; i++) {
584  m_amr->allocate(m_secondaryParticles[i], m_particleRealm);
585  }
586 
587  for (int i = 0; i < numPhotonSpecies; i++) {
588  m_amr->allocate(m_secondaryPhotons[i], m_particleRealm);
589  }
590 
591  for (int i = 0; i < numCdrSpecies; i++) {
592  m_amr->allocate(m_cdrFluxes[i], m_particleRealm, m_plasmaPhase, 1);
593  m_amr->allocate(m_cdrFluxesExtrap[i], m_particleRealm, m_plasmaPhase, 1);
594  }
595 
596  // Current density.
597  m_amr->allocate(m_currentDensity, m_fluidRealm, m_plasmaPhase, SpaceDim);
598 
599  // Storage required for the reaction network.
600  m_amr->allocate(m_fluidPPC, m_fluidRealm, m_plasmaPhase, numPlasmaSpecies);
601 
602  if (numItoSpecies > 0) {
603  m_amr->allocate(m_particleItoPPC, m_particleRealm, m_plasmaPhase, numItoSpecies);
604  m_amr->allocate(m_particleOldItoPPC, m_particleRealm, m_plasmaPhase, numItoSpecies);
605  }
606  else {
607  // Allocate some dummy data -- makes it easier. Trust me.
608  m_amr->allocate(m_particleItoPPC, m_particleRealm, m_plasmaPhase, 1);
609  m_amr->allocate(m_particleOldItoPPC, m_particleRealm, m_plasmaPhase, 1);
610  }
611 
612  if (numCdrSpecies > 0) {
613  m_amr->allocate(m_fluidCdrPPC, m_fluidRealm, m_plasmaPhase, numCdrSpecies);
614  m_amr->allocate(m_fluidOldCdrPPC, m_fluidRealm, m_plasmaPhase, numCdrSpecies);
615  }
616  else {
617  m_amr->allocatePointer(m_fluidCdrPPC, m_fluidRealm);
618  m_amr->allocatePointer(m_fluidOldCdrPPC, m_fluidRealm);
619  }
620 
621  if (numPhotonSpecies > 0) {
622  m_amr->allocate(m_particleYPC, m_particleRealm, m_plasmaPhase, numPhotonSpecies);
623  m_amr->allocate(m_fluidYPC, m_fluidRealm, m_plasmaPhase, numPhotonSpecies);
624  }
625  else {
626  // Allocate some dummy data -- makes it easier. Trust me.
627  m_amr->allocate(m_particleYPC, m_particleRealm, m_plasmaPhase, 1);
628  m_amr->allocate(m_fluidYPC, m_fluidRealm, m_plasmaPhase, 1);
629  }
630 }
631 
632 template <typename I, typename C, typename R, typename F>
633 void
635 {
636  CH_TIME("ItoKMCStepper::postInitialize");
637  if (m_verbosity > 5) {
638  pout() << m_name + "::postInitialize" << endl;
639  }
640 }
641 
642 template <typename I, typename C, typename R, typename F>
643 void
645 {
646  CH_TIME("ItoKMCStepper::initialData");
647  if (m_verbosity > 5) {
648  pout() << m_name + "::initialData" << endl;
649  }
650 
651  CH_assert(!(m_cdr.isNull()));
652  CH_assert(!(m_ito.isNull()));
653  CH_assert(!(m_rte.isNull()));
654  CH_assert(!(m_sigmaSolver.isNull()));
655  CH_assert(!(m_fieldSolver.isNull()));
656 
657  m_ito->initialData();
658  m_cdr->initialData();
659  m_rte->initialData();
660  this->initialSigma();
661 
662  // Make superparticles.
663  m_ito->organizeParticlesByCell(ItoSolver::WhichContainer::Bulk);
664  m_ito->makeSuperparticles(ItoSolver::WhichContainer::Bulk, m_particlesPerCell);
665  m_ito->organizeParticlesByPatch(ItoSolver::WhichContainer::Bulk);
666 
667  // Solve Poisson equation and compute the E-field
668  m_fieldSolver->setPermittivities();
669  this->computeSpaceChargeDensity();
670  this->solvePoisson();
671 
672  // Fill solvers with velocities and diffusion coefficients
673  this->computeDriftVelocities();
674  this->computeDiffusionCoefficients();
675 }
676 
677 template <typename I, typename C, typename R, typename F>
678 void
680 {
681  CH_TIME("ItoKMCStepper::initialSigma");
682  if (m_verbosity > 5) {
683  pout() << m_name + "::initialSigma" << endl;
684  }
685 
686  const RealVect probLo = m_amr->getProbLo();
687 
688  EBAMRIVData& sigma = m_sigmaSolver->getPhi();
689 
690  for (int lvl = 0; lvl <= m_amr->getFinestLevel(); lvl++) {
691  const DisjointBoxLayout& dbl = m_amr->getGrids(m_sigmaSolver->getRealm())[lvl];
692  const DataIterator& dit = dbl.dataIterator();
693  const EBISLayout& ebisl = m_amr->getEBISLayout(m_sigmaSolver->getRealm(), m_sigmaSolver->getPhase())[lvl];
694  const Real dx = m_amr->getDx()[lvl];
695 
696  const int nbox = dit.size();
697 
698 #pragma omp parallel for schedule(runtime)
699  for (int mybox = 0; mybox < nbox; mybox++) {
700  const DataIndex& din = dit[mybox];
701 
702  BaseIVFAB<Real>& phi = (*sigma[lvl])[din];
703  const EBISBox& ebisbox = ebisl[din];
704 
705  CH_assert(phi.nComp() == 1);
706 
707  auto kernel = [&](const VolIndex& vof) -> void {
708  const RealVect pos = probLo + Location::position(Location::Cell::Boundary, vof, ebisbox, dx);
709 
710  phi(vof, 0) = m_physics->initialSigma(m_time, pos);
711  };
712 
713  VoFIterator& vofit = (*m_amr->getVofIterator(m_sigmaSolver->getRealm(), m_sigmaSolver->getPhase())[lvl])[din];
714 
715  BoxLoops::loop(vofit, kernel);
716  }
717  }
718 
719  // Coarsen throughout the AMR hierarchy.
720  m_amr->conservativeAverage(sigma, m_fluidRealm, m_sigmaSolver->getPhase());
721 
722  // Set surface charge to zero on electrode cut-cells.
723  m_sigmaSolver->resetElectrodes(sigma, 0.0);
724 }
725 
726 template <typename I, typename C, typename R, typename F>
727 void
729 {
730  CH_TIME("ItoKMCStepper::postCheckpointSetup");
731  if (m_verbosity > 5) {
732  pout() << m_name + "::postCheckpointSetup" << endl;
733  }
734 
735  m_ito->remap();
736 
737  // Recompute the electric field.
738  this->postCheckpointPoisson();
739 
740  // Compute velocities and diffusion coefficients so we're prepared for the next time step.
741  this->computeDriftVelocities();
742  this->computeDiffusionCoefficients();
743 }
744 
745 template <typename I, typename C, typename R, typename F>
746 void
748 {
749  CH_TIME("ItoKMCStepper::postCheckpointPoisson");
750  if (m_verbosity > 5) {
751  pout() << m_name + "::postCheckpointPoisson" << endl;
752  }
753 
754  // Do some post checkpointing stuff.
755  m_fieldSolver->postCheckpoint();
756 
757  // Update ghost cells and re-compute the electric field from the HDF5 data.
758  MFAMRCellData& potential = m_fieldSolver->getPotential();
759 
760  m_amr->conservativeAverage(potential, m_fluidRealm);
761  m_amr->interpGhostMG(potential, m_fluidRealm);
762 
763  m_fieldSolver->computeElectricField();
764 
765  // Fetch the electric field data on the plasma phase.
766  const EBAMRCellData E = m_amr->alias(m_plasmaPhase, m_fieldSolver->getElectricField());
767 
768  // Copy onto the storage holding the electric field on the fluid realm. Then interpolate to centroids.
769  m_amr->copyData(m_electricFieldFluid, E);
770  m_amr->conservativeAverage(m_electricFieldFluid, m_fluidRealm, m_plasmaPhase);
771  m_amr->interpGhostPwl(m_electricFieldFluid, m_fluidRealm, m_plasmaPhase);
772  m_amr->interpToCentroids(m_electricFieldFluid, m_fluidRealm, m_plasmaPhase);
773 
774  // Copy onto the storage holding the electric field on the particle realm.
775  m_amr->copyData(m_electricFieldParticle, E);
776  m_amr->conservativeAverage(m_electricFieldParticle, m_particleRealm, m_plasmaPhase);
777  m_amr->interpGhostPwl(m_electricFieldParticle, m_particleRealm, m_plasmaPhase);
778  m_amr->interpToCentroids(m_electricFieldParticle, m_particleRealm, m_plasmaPhase);
779 
780  // Set up the Poisson solver
781  m_fieldSolver->setupSolver();
782 }
783 
784 #ifdef CH_USE_HDF5
785 template <typename I, typename C, typename R, typename F>
786 void
787 ItoKMCStepper<I, C, R, F>::writeCheckpointHeader(HDF5HeaderData& a_header) const noexcept
788 {
789  CH_TIME("ItoKMCStepper::writeCheckpointHeader");
790  if (m_verbosity > 5) {
791  pout() << m_name + "::writeCheckpointHeader" << endl;
792  }
793 }
794 #endif
795 
796 #ifdef CH_USE_HDF5
797 template <typename I, typename C, typename R, typename F>
798 void
799 ItoKMCStepper<I, C, R, F>::readCheckpointHeader(HDF5HeaderData& a_header) noexcept
800 {
801  CH_TIME("ItoKMCStepper::readCheckpointHeader");
802  if (m_verbosity > 5) {
803  pout() << m_name + "::readCheckpointHeader" << endl;
804  }
805 }
806 #endif
807 
808 #ifdef CH_USE_HDF5
809 template <typename I, typename C, typename R, typename F>
810 void
811 ItoKMCStepper<I, C, R, F>::writeCheckpointData(HDF5Handle& a_handle, const int a_lvl) const noexcept
812 {
813  CH_TIME("ItoKMCStepper::writeCheckpointData");
814  if (m_verbosity > 5) {
815  pout() << m_name + "::writeCheckpointData" << endl;
816  }
817 
818  for (ItoIterator<ItoSolver> solverIt = m_ito->iterator(); solverIt.ok(); ++solverIt) {
819  solverIt()->writeCheckpointLevel(a_handle, a_lvl);
820  }
821 
822  for (CdrIterator<CdrSolver> solverIt = m_cdr->iterator(); solverIt.ok(); ++solverIt) {
823  solverIt()->writeCheckpointLevel(a_handle, a_lvl);
824  }
825 
826  for (RtIterator<McPhoto> solverIt = m_rte->iterator(); solverIt.ok(); ++solverIt) {
827  solverIt()->writeCheckpointLevel(a_handle, a_lvl);
828  }
829 
830  m_fieldSolver->writeCheckpointLevel(a_handle, a_lvl);
831  m_sigmaSolver->writeCheckpointLevel(a_handle, a_lvl);
832 }
833 #endif
834 
835 #ifdef CH_USE_HDF5
836 template <typename I, typename C, typename R, typename F>
837 void
838 ItoKMCStepper<I, C, R, F>::readCheckpointData(HDF5Handle& a_handle, const int a_lvl) noexcept
839 {
840  CH_TIME("ItoKMCStepper::readCheckpointData");
841  if (m_verbosity > 5) {
842  pout() << m_name + "::readCheckpointData" << endl;
843  }
844 
845  for (ItoIterator<ItoSolver> solverIt = m_ito->iterator(); solverIt.ok(); ++solverIt) {
846  solverIt()->readCheckpointLevel(a_handle, a_lvl);
847  }
848 
849  for (CdrIterator<CdrSolver> solverIt = m_cdr->iterator(); solverIt.ok(); ++solverIt) {
850  solverIt()->readCheckpointLevel(a_handle, a_lvl);
851  }
852 
853  for (RtIterator<McPhoto> solverIt = m_rte->iterator(); solverIt.ok(); ++solverIt) {
854  solverIt()->readCheckpointLevel(a_handle, a_lvl);
855  }
856 
857  m_fieldSolver->readCheckpointLevel(a_handle, a_lvl);
858  m_sigmaSolver->readCheckpointLevel(a_handle, a_lvl);
859 }
860 #endif
861 
862 template <typename I, typename C, typename R, typename F>
863 int
865 {
866  CH_TIME("ItoKMCStepper::getNumberOfPlotVariables");
867  if (m_verbosity > 5) {
868  pout() << m_name + "::getNumberOfPlotVariables" << endl;
869  }
870 
871  int numComp = 0;
872 
873  // Ito solver variables.
874  for (ItoIterator<ItoSolver> solverIt = m_ito->iterator(); solverIt.ok(); ++solverIt) {
875  numComp += solverIt()->getNumberOfPlotVariables();
876  }
877 
878  // Cdr solver variables
879  for (auto solverIt = m_cdr->iterator(); solverIt.ok(); ++solverIt) {
880  numComp += solverIt()->getNumberOfPlotVariables();
881  }
882 
883  // RTE solver variables.
884  for (RtIterator<McPhoto> solverIt = m_rte->iterator(); solverIt.ok(); ++solverIt) {
885  numComp += solverIt()->getNumberOfPlotVariables();
886  }
887 
888  // Field solver variables.
889  numComp += m_fieldSolver->getNumberOfPlotVariables();
890 
891  // Surface charge solver variables.
892  numComp += m_sigmaSolver->getNumberOfPlotVariables();
893 
894  // Conductivity
895  if (m_plotConductivity) {
896  numComp += 1;
897  }
898 
899  // Current density.
900  if (m_plotCurrentDensity) {
901  numComp += SpaceDim;
902  }
903 
904  // Number of particles per patch
905  if (m_plotParticlesPerPatch) {
906  numComp += 1;
907  }
908 
909  // Physics plot variables
910  numComp += m_physics->getNumberOfPlotVariables();
911 
912  return numComp;
913 }
914 
915 template <typename I, typename C, typename R, typename F>
916 Vector<std::string>
918 {
919  CH_TIME("ItoKMCStepper::getPlotVariableNames");
920  if (m_verbosity > 5) {
921  pout() << m_name + "::getPlotVariableNames" << endl;
922  }
923 
924  Vector<std::string> plotVarNames;
925 
926  plotVarNames.append(m_fieldSolver->getPlotVariableNames());
927  plotVarNames.append(m_sigmaSolver->getPlotVariableNames());
928 
929  for (ItoIterator<ItoSolver> solverIt = m_ito->iterator(); solverIt.ok(); ++solverIt) {
930  plotVarNames.append(solverIt()->getPlotVariableNames());
931  }
932 
933  for (auto solverIt = m_cdr->iterator(); solverIt.ok(); ++solverIt) {
934  plotVarNames.append(solverIt()->getPlotVariableNames());
935  }
936 
937  for (RtIterator<McPhoto> solverIt = m_rte->iterator(); solverIt.ok(); ++solverIt) {
938  plotVarNames.append(solverIt()->getPlotVariableNames());
939  }
940 
941  // Write the conductivity to the output
942  if (m_plotConductivity) {
943  plotVarNames.push_back("Conductivity");
944  }
945 
946  // Write the current to the output
947  if (m_plotCurrentDensity) {
948  plotVarNames.push_back("x-J");
949  plotVarNames.push_back("y-J");
950  if (SpaceDim == 3) {
951  plotVarNames.push_back("z-J");
952  }
953  }
954 
955  // Write the number of particles per patch
956  if (m_plotParticlesPerPatch) {
957  plotVarNames.push_back("Particles per patch");
958  }
959 
960  // Physics plot variable names
961  plotVarNames.append(m_physics->getPlotVariableNames());
962 
963  return plotVarNames;
964 }
965 
966 template <typename I, typename C, typename R, typename F>
967 void
968 ItoKMCStepper<I, C, R, F>::writePlotData(LevelData<EBCellFAB>& a_output,
969  int& a_icomp,
970  const std::string a_outputRealm,
971  const int a_level) const noexcept
972 {
973  CH_TIME("ItoKMCStepper::writePlotData");
974  if (m_verbosity > 5) {
975  pout() << m_name + "::writePlotData" << endl;
976  }
977 
978  // Poisson solver copies over its output data
979  m_fieldSolver->writePlotData(a_output, a_icomp, a_outputRealm, a_level);
980 
981  // Surface charge solver writes
982  m_sigmaSolver->writePlotData(a_output, a_icomp, a_outputRealm, a_level);
983 
984  // Ito solvers copy their output data
985  for (ItoIterator<ItoSolver> solverIt = m_ito->iterator(); solverIt.ok(); ++solverIt) {
986  solverIt()->writePlotData(a_output, a_icomp, a_outputRealm, a_level);
987  }
988 
989  // Cdr solvers output their data
990  for (auto solverIt = m_cdr->iterator(); solverIt.ok(); ++solverIt) {
991  solverIt()->writePlotData(a_output, a_icomp, a_outputRealm, a_level);
992  }
993 
994  // RTE solvers copy their output data
995  for (RtIterator<McPhoto> solverIt = m_rte->iterator(); solverIt.ok(); ++solverIt) {
996  solverIt()->writePlotData(a_output, a_icomp, a_outputRealm, a_level);
997  }
998 
999  // Write the conductivity to the output
1000  if (m_plotConductivity) {
1001  this->writeData(a_output, a_icomp, m_conductivityCell, a_outputRealm, a_level, false, true);
1002  }
1003 
1004  // Write the current to the output
1005  if (m_plotCurrentDensity) {
1006  this->writeData(a_output, a_icomp, m_currentDensity, a_outputRealm, a_level, false, true);
1007  }
1008 
1009  // Write the number of particles per patch
1010  if (m_plotParticlesPerPatch) {
1011  this->writeNumberOfParticlesPerPatch(a_output, a_icomp, a_outputRealm, a_level);
1012  }
1013 
1014  // Write physics plot variables
1015  if (m_physics->getNumberOfPlotVariables() > 0) {
1016  this->writeData(a_output, a_icomp, m_physicsPlotVariables, a_outputRealm, a_level, false, true);
1017  }
1018 }
1019 
1020 template <typename I, typename C, typename R, typename F>
1021 void
1022 ItoKMCStepper<I, C, R, F>::writeData(LevelData<EBCellFAB>& a_output,
1023  int& a_comp,
1024  const EBAMRCellData& a_data,
1025  const std::string a_outputRealm,
1026  const int a_level,
1027  const bool a_interpToCentroids,
1028  const bool a_interpGhost) const noexcept
1029 
1031  CH_TIMERS("ItoKMCStepper::writeData");
1032  CH_TIMER("ItoKMCStepper::writeData::allocate", t1);
1033  CH_TIMER("ItoKMCStepper::writeData::local_copy", t2);
1034  CH_TIMER("ItoKMCStepper::writeData::interp_ghost", t3);
1035  CH_TIMER("ItoKMCStepper::writeData::interp_centroid", t4);
1036  CH_TIMER("ItoKMCStepper::writeData::final_copy", t5);
1037  if (m_verbosity > 5) {
1038  pout() << m_name + "::writeData" << endl;
1039  }
1040 
1041  // Number of components we are working with.
1042  const int numComp = a_data[a_level]->nComp();
1043 
1044  // Component ranges that we copy to/from.
1045  const Interval srcInterv(0, numComp - 1);
1046  const Interval dstInterv(a_comp, a_comp + numComp - 1);
1047 
1048  CH_START(t1);
1049  LevelData<EBCellFAB> scratch;
1050  m_amr->allocate(scratch, a_data.getRealm(), m_plasmaPhase, a_level, numComp);
1051  CH_STOP(t1);
1052 
1053  CH_START(t2);
1054  m_amr->copyData(scratch, *a_data[a_level], a_level, a_data.getRealm(), a_data.getRealm());
1055  CH_START(t2);
1056 
1057  // Interpolate ghost cells
1058  CH_START(t3);
1059  if (a_level > 0 && a_interpGhost) {
1060  m_amr->interpGhost(scratch, *a_data[a_level - 1], a_level, a_data.getRealm(), m_plasmaPhase);
1061  }
1062  CH_STOP(t3);
1063 
1064  CH_START(t4);
1065  if (a_interpToCentroids) {
1066  m_amr->interpToCentroids(scratch, a_data.getRealm(), m_plasmaPhase, a_level);
1067  }
1068  CH_STOP(t4);
1069 
1070  DataOps::setCoveredValue(scratch, 0.0);
1071 
1072  CH_START(t5);
1073  m_amr->copyData(a_output,
1074  scratch,
1075  a_level,
1076  a_outputRealm,
1077  a_data.getRealm(),
1078  dstInterv,
1079  srcInterv,
1080  CopyStrategy::ValidGhost,
1081  CopyStrategy::ValidGhost);
1082  CH_STOP(t5);
1083 
1084  a_comp += numComp;
1085 }
1086 
1087 template <typename I, typename C, typename R, typename F>
1088 void
1090  int& a_icomp,
1091  const std::string a_outputRealm,
1092  const int a_level) const noexcept
1093 {
1094  CH_TIME("ItoKMCStepper::writeNumberOfParticlesPerPatch");
1095  if (m_verbosity > 5) {
1096  pout() << m_name + "::writeNumberOfParticlesPerPatch" << endl;
1097  }
1098 
1099  CH_assert(a_level >= 0);
1100  CH_assert(a_level <= m_amr->getFinestLevel());
1101 
1102  DataOps::setValue(*m_particleScratch1[a_level], 0.0, a_icomp);
1103 
1104  for (auto solverIt = m_ito->iterator(); solverIt.ok(); ++solverIt) {
1105  const ParticleContainer<ItoParticle>& particles = solverIt()->getParticles(ItoSolver::WhichContainer::Bulk);
1106 
1107  for (int lvl = 0; lvl <= m_amr->getFinestLevel(); lvl++) {
1108  const DisjointBoxLayout& dbl = m_amr->getGrids(m_particleRealm)[lvl];
1109  const DataIterator& dit = dbl.dataIterator();
1110 
1111  const int nbox = dit.size();
1113 #pragma omp parallel for schedule(runtime)
1114  for (int mybox = 0; mybox < nbox; mybox++) {
1115  const DataIndex& din = dit[mybox];
1116 
1117  (*m_particleScratch1[lvl])[din] += particles[lvl][din].numItems();
1118  }
1119  }
1120  }
1121 
1122  m_amr->copyData(a_output,
1123  *m_particleScratch1[a_level],
1124  a_level,
1125  a_outputRealm,
1126  m_particleRealm,
1127  Interval(a_icomp, a_icomp),
1128  Interval(0, 0));
1129 
1130  a_icomp += 1;
1131 }
1132 
1133 template <typename I, typename C, typename R, typename F>
1134 void
1135 ItoKMCStepper<I, C, R, F>::synchronizeSolverTimes(const int a_step, const Real a_time, const Real a_dt) noexcept
1136 {
1137  CH_TIME("ItoKMCStepper::synchronizeSolverTimes");
1138  if (m_verbosity > 5) {
1139  pout() << m_name + "::synchronizeSolverTimes" << endl;
1140  }
1142  m_timeStep = a_step;
1143  m_time = a_time;
1144  m_dt = a_dt;
1145 
1146  m_ito->setTime(a_step, a_time, a_dt);
1147  m_fieldSolver->setTime(a_step, a_time, a_dt);
1148  m_rte->setTime(a_step, a_time, a_dt);
1149  m_sigmaSolver->setTime(a_step, a_time, a_dt);
1150 }
1151 
1152 template <typename I, typename C, typename R, typename F>
1153 void
1156  CH_TIME("ItoKMCStepper::printStepReport");
1157  if (m_verbosity > 5) {
1158  pout() << m_name + "::printStepReport" << endl;
1159  }
1160 
1161  const Real Emax = this->computeMaxElectricField(m_plasmaPhase);
1162 
1163  const unsigned long long localParticlesBulk = m_ito->getNumParticles(ItoSolver::WhichContainer::Bulk, true);
1164  const unsigned long long globalParticlesBulk = m_ito->getNumParticles(ItoSolver::WhichContainer::Bulk, false);
1165  const unsigned long long localParticlesEB = m_ito->getNumParticles(ItoSolver::WhichContainer::EB, true);
1166  const unsigned long long globalParticlesEB = m_ito->getNumParticles(ItoSolver::WhichContainer::EB, false);
1167  const unsigned long long localParticlesDomain = m_ito->getNumParticles(ItoSolver::WhichContainer::Domain, true);
1168  const unsigned long long globalParticlesDomain = m_ito->getNumParticles(ItoSolver::WhichContainer::Domain, false);
1169  const unsigned long long localParticlesSource = m_ito->getNumParticles(ItoSolver::WhichContainer::Source, true);
1170  const unsigned long long globalParticlesSource = m_ito->getNumParticles(ItoSolver::WhichContainer::Source, false);
1171 
1172  Real avgParticles = 0.0;
1173  Real stdDev = 0.0;
1174 
1175  Real minParticles = 0.0;
1176  Real maxParticles = 0.0;
1177 
1178  int minRank = 0;
1179  int maxRank = 0;
1180 
1181  this->getParticleStatistics(avgParticles, stdDev, minParticles, maxParticles, minRank, maxRank);
1182 
1183  Real maxDensity = -std::numeric_limits<Real>::max();
1184  Real minDensity = +std::numeric_limits<Real>::max();
1185 
1186  std::string maxSolver = "invalid solver";
1187  std::string minSolver = "invalid solver";
1188 
1189  this->getMaxMinItoDensity(maxDensity, minDensity, maxSolver, minSolver);
1190  this->getMaxMinCDRDensity(maxDensity, minDensity, maxSolver, minSolver);
1191 
1192  std::string str;
1193  switch (m_timeCode) {
1194  case TimeCode::Physics: {
1195  str = "dt restricted by 'Physics'";
1197  break;
1198  }
1199  case TimeCode::AdvectionIto: {
1200  str = "dt restricted by 'Advection (Ito)'";
1201 
1202  break;
1203  }
1204  case TimeCode::DiffusionIto: {
1205  str = "dt restricted by 'Diffusion (Ito)'";
1206 
1207  break;
1208  }
1209  case TimeCode::AdvectionDiffusionIto: {
1210  str = "dt restricted by 'AdvectionDiffusion (Ito)'";
1211 
1212  break;
1213  }
1214  case TimeCode::AdvectionDiffusionCDR: {
1215  str = "dt restricted by 'AdvectionDiffusion (CDR)'";
1216 
1217  break;
1218  }
1219  case TimeCode::RelaxationTime: {
1220  str = "dt restricted by 'Relaxation time'";
1221 
1222  break;
1223  }
1224  case TimeCode::Hardcap: {
1225  str = "dt restricted by 'Hardcap'";
1226 
1227  break;
1228  }
1229  default: {
1230  str = "dt restricted by 'Unspecified'";
1231 
1232  break;
1233  }
1234  }
1235 
1236  // Calculate the charge
1237  const Real Qplus = this->computeQplus();
1238  const Real Qminu = this->computeQminu();
1239  const Real Qsurf = this->computeQsurf();
1240  const Real Qtot = Qplus + Qminu + Qsurf;
1241 
1242  // Print the step report.
1243 
1244  //clang-format off
1245  const std::string whitespace = " ";
1246  pout() << " " + str << endl;
1247  pout() << whitespace + "Emax = " << Emax << endl
1248  << whitespace + "Max density = " << maxDensity << " (" << maxSolver << ")" << endl
1249  << whitespace + "Qplus = " << Qplus << endl
1250  << whitespace + "Qminu = " << Qminu << endl
1251  << whitespace + "Qsurf = " << Qsurf << endl
1252  << whitespace + "Qtot = " << Qtot << endl
1253  << whitespace + "CFL (Ito) = " << m_dt / m_particleAdvectionDiffusionDt << endl
1254  << whitespace + "CFL (CDR) = " << m_dt / m_fluidAdvectionDiffusionDt << endl
1255  << whitespace + "dt/dt_relax = " << m_dt / m_relaxationTime << endl
1256  << whitespace + "#Particles = " << DischargeIO::numberFmt(localParticlesBulk) << " ("
1257  << DischargeIO::numberFmt(globalParticlesBulk) << ")" << endl
1258  << whitespace + "#EB part. = " << DischargeIO::numberFmt(localParticlesEB) << " ("
1259  << DischargeIO::numberFmt(globalParticlesEB) << ")" << endl
1260  << whitespace + "#Dom. part. = " << DischargeIO::numberFmt(localParticlesDomain) << " ("
1261  << DischargeIO::numberFmt(globalParticlesDomain) << ")" << endl
1262  << whitespace + "#Src. part. = " << DischargeIO::numberFmt(localParticlesSource) << " ("
1263  << DischargeIO::numberFmt(globalParticlesSource) << ")" << endl
1264  << whitespace + "#Min part. = " << minParticles << " (on rank = " << minRank << ")" << endl
1265  << whitespace + "#Max part. = " << maxParticles << " (on rank = " << maxRank << ")" << endl
1266  << whitespace + "#Avg. part. = " << avgParticles << endl
1267  << whitespace + "#Dev. part. = " << stdDev << " (" << 100. * stdDev / avgParticles << "%)" << endl;
1268  //clang-format on
1269 }
1270 
1271 template <typename I, typename C, typename R, typename F>
1272 void
1274  Real& a_minDensity,
1275  std::string& a_maxSolver,
1276  std::string& a_minSolver) const noexcept
1277 {
1278  CH_TIME("ItoKMCStepper::getMaxMinDensity(Realx2, std::string2x)");
1279  if (m_verbosity > 5) {
1280  pout() << m_name + "::getMaxMinDensity(Realx2, std::string2x)" << endl;
1281  }
1282 
1283  // Go through each solver and find the max/min values.
1284  for (auto solverIt = m_ito->iterator(); solverIt.ok(); ++solverIt) {
1285  const RefCountedPtr<ItoSolver>& solver = solverIt();
1286 
1287  Real curMin = std::numeric_limits<Real>::max();
1289 
1290  DataOps::getMaxMin(curMax, curMin, solverIt()->getPhi(), 0);
1291 
1292  if (curMax > a_maxDensity) {
1293  a_maxDensity = curMax;
1294  a_maxSolver = solver->getName();
1295  }
1296 
1297  if (curMin < a_minDensity) {
1298  a_minDensity = curMin;
1299  a_minSolver = solver->getName();
1300  }
1301  }
1302 }
1303 
1304 template <typename I, typename C, typename R, typename F>
1305 void
1307  Real& a_minDensity,
1308  std::string& a_maxSolver,
1309  std::string& a_minSolver) const noexcept
1310 {
1311  CH_TIME("ItoKMCStepper::getMaxMinCDRDensity(Realx2, std::string2x)");
1312  if (m_verbosity > 5) {
1313  pout() << m_name + "::getMaxMinCDRDensity(Realx2, std::string2x)" << endl;
1314  }
1315 
1316  // Go through each solver and find the max/min values.
1317  for (auto solverIt = m_cdr->iterator(); solverIt.ok(); ++solverIt) {
1318  const RefCountedPtr<CdrSolver>& solver = solverIt();
1319 
1320  Real curMin = std::numeric_limits<Real>::max();
1321  Real curMax = -std::numeric_limits<Real>::max();
1323  DataOps::getMaxMin(curMax, curMin, solverIt()->getPhi(), 0);
1324 
1325  if (curMax > a_maxDensity) {
1326  a_maxDensity = curMax;
1327  a_maxSolver = solver->getName();
1328  }
1329 
1330  if (curMin < a_minDensity) {
1331  a_minDensity = curMin;
1332  a_minSolver = solver->getName();
1333  }
1334  }
1335 }
1336 
1337 template <typename I, typename C, typename R, typename F>
1338 void
1340  Real& a_sigma,
1341  Real& a_minParticles,
1342  Real& a_maxParticles,
1343  int& a_minRank,
1344  int& a_maxRank)
1345 {
1346  CH_TIME("ItoKMCStepper::getParticleStatistics");
1347  if (m_verbosity > 5) {
1348  pout() << m_name + "::getParticleStatistics" << endl;
1349  }
1350 
1351  // TLDR: We compute the number of particles, the standard deviation of the number of particles, as well
1352  // as the ranks having the smallest/largest number of particles.
1353 
1354  const Real numParticles = 1.0 * m_ito->getNumParticles(ItoSolver::WhichContainer::Bulk, true);
1356  const std::pair<Real, int> minParticles = ParallelOps::minRank(numParticles);
1357  const std::pair<Real, int> maxParticles = ParallelOps::maxRank(numParticles);
1358 
1359  a_avgParticles = ParallelOps::average(numParticles);
1360  a_sigma = ParallelOps::standardDeviation(numParticles);
1361 
1362  a_minParticles = minParticles.first;
1363  a_maxParticles = maxParticles.first;
1364 
1365  a_minRank = minParticles.second;
1366  a_maxRank = maxParticles.second;
1368 
1369 template <typename I, typename C, typename R, typename F>
1370 Real
1372 {
1373  CH_TIME("ItoKMCStepper::computeDt");
1374  if (m_verbosity > 5) {
1375  pout() << m_name + "::computeDt" << endl;
1376  }
1377 
1378  Timer timer(m_name + "::computeDt");
1380  Real dt = std::numeric_limits<Real>::max();
1381 
1382  const Real maxGrowthDt = m_prevDt > 0.0 ? m_prevDt * m_maxGrowthDt : dt;
1383  const Real minShrinkDt = m_prevDt > 0.0 ? m_prevDt / m_maxShrinkDt : 0.0;
1384 
1385  // Compute various time steps.
1386  timer.startEvent("Advection (Ito)");
1387  m_particleAdvectionDt = m_ito->computeAdvectiveDt();
1388  timer.stopEvent("Advection (Ito)");
1389 
1390  timer.startEvent("Diffusion (Ito)");
1391  m_particleDiffusionDt = m_ito->computeDiffusiveDt();
1392  timer.stopEvent("Diffusion (Ito)");
1393 
1394  timer.startEvent("AdvectionDiffusion (Ito)");
1395  m_particleAdvectionDiffusionDt = m_ito->computeDt();
1396  timer.stopEvent("AdvectionDiffusion (Ito)");
1397 
1398  timer.startEvent("AdvectionDiffusion (CDR)");
1399  m_fluidAdvectionDiffusionDt = m_cdr->computeAdvectionDiffusionDt();
1400  timer.stopEvent("AdvectionDiffusion (CDR)");
1402  timer.startEvent("Physics");
1403  m_physicsDt = this->computePhysicsDt();
1404  timer.stopEvent("Physics");
1405 
1406  timer.startEvent("Relaxation");
1407  m_relaxationTime = this->computeRelaxationTime();
1408  timer.stopEvent("Relaxation");
1409 
1410  if (m_maxParticleAdvectionCFL * m_particleAdvectionDt < dt) {
1411  dt = m_maxParticleAdvectionCFL * m_particleAdvectionDt;
1412  m_timeCode = TimeCode::AdvectionIto;
1413  }
1414 
1415  if (m_maxParticleDiffusionCFL * m_particleDiffusionDt < dt) {
1416  dt = m_maxParticleDiffusionCFL * m_particleDiffusionDt;
1417  m_timeCode = TimeCode::DiffusionIto;
1418  }
1419 
1420  if (m_maxParticleAdvectionDiffusionCFL * m_particleAdvectionDiffusionDt < dt) {
1421  dt = m_maxParticleAdvectionDiffusionCFL * m_particleAdvectionDiffusionDt;
1422  m_timeCode = TimeCode::AdvectionDiffusionIto;
1423  }
1424 
1425  if (std::min(m_fluidAdvectionDiffusionCFL, 0.9) * m_fluidAdvectionDiffusionDt < dt) {
1426  dt = std::min(m_fluidAdvectionDiffusionCFL, 0.9) * m_fluidAdvectionDiffusionDt;
1427  m_timeCode = TimeCode::AdvectionDiffusionCDR;
1428  }
1429 
1430  if (m_relaxTimeFactor * m_relaxationTime < dt) {
1431  dt = m_relaxTimeFactor * m_relaxationTime;
1432  m_timeCode = TimeCode::RelaxationTime;
1433  }
1434 
1435  if (m_physicsDt < dt) {
1436  dt = m_physicsDt;
1437  m_timeCode = TimeCode::Physics;
1438  }
1439 
1440  if (dt < m_minParticleAdvectionCFL * m_particleAdvectionDt) {
1441  dt = m_minParticleAdvectionCFL * m_particleAdvectionDt;
1442 
1443  m_timeCode = TimeCode::AdvectionIto;
1444  }
1445 
1446  if (dt < m_minParticleDiffusionCFL * m_particleDiffusionDt) {
1447  dt = m_minParticleDiffusionCFL * m_particleDiffusionDt;
1448 
1449  m_timeCode = TimeCode::DiffusionIto;
1450  }
1452  if (dt < m_minParticleAdvectionDiffusionCFL * m_particleAdvectionDiffusionDt) {
1453  dt = m_minParticleAdvectionDiffusionCFL * m_particleAdvectionDiffusionDt;
1454 
1455  m_timeCode = TimeCode::AdvectionDiffusionIto;
1456  }
1457 
1458  if (dt > maxGrowthDt) {
1459  dt = maxGrowthDt;
1460  }
1461 
1462  if (dt < minShrinkDt) {
1463  dt = minShrinkDt;
1464  }
1465 
1466  if (m_minDt > dt) {
1467  dt = m_minDt;
1468  m_timeCode = TimeCode::Hardcap;
1469  }
1470 
1471  if (m_maxDt < dt) {
1472  dt = m_maxDt;
1473  m_timeCode = TimeCode::Hardcap;
1474  }
1475 
1476  if (m_profile) {
1477  timer.eventReport(pout(), false);
1478  }
1479 
1480  return dt;
1481 }
1482 
1483 template <typename I, typename C, typename R, typename F>
1484 void
1486 {
1487  CH_TIME("ItoKMCStepper::registerRealms");
1488  if (m_verbosity > 5) {
1489  pout() << m_name + "::registerRealms" << endl;
1490  }
1491 
1492  // TLDR: If using dual grid then m_particleRealm != m_fluidRealm and we'll have two realms.
1493  m_amr->registerRealm(m_fluidRealm);
1494  m_amr->registerRealm(m_particleRealm);
1495 }
1496 
1497 template <typename I, typename C, typename R, typename F>
1498 void
1500 {
1501  CH_TIME("ItoKMCStepper::registerOperators");
1502  if (m_verbosity > 5) {
1503  pout() << m_name + "::registerOperators" << endl;
1504  }
1505 
1506  m_ito->registerOperators();
1507  m_cdr->registerOperators();
1508  m_fieldSolver->registerOperators();
1509  m_rte->registerOperators();
1510  m_sigmaSolver->registerOperators();
1511 }
1512 
1513 template <typename I, typename C, typename R, typename F>
1514 void
1516 {
1517  CH_TIME("ItoKMCStepper::prePlot");
1518  if (m_verbosity > 5) {
1519  pout() << m_name + "::prePlot" << endl;
1520  }
1521 
1522  const int numPhysicsPlotVars = m_physics->getNumberOfPlotVariables();
1524  if (numPhysicsPlotVars > 0) {
1525  m_amr->allocate(m_physicsPlotVariables, m_fluidRealm, m_plasmaPhase, numPhysicsPlotVars);
1526  }
1527 
1528  this->computePhysicsPlotVariables(m_physicsPlotVariables);
1529  this->computeCurrentDensity(this->m_currentDensity);
1530  m_ito->depositParticles();
1531 }
1532 
1533 template <typename I, typename C, typename R, typename F>
1534 void
1536 {
1537  CH_TIME("ItoKMCStepper::postPlot");
1538  if (m_verbosity > 5) {
1539  pout() << m_name + "::postPlot" << endl;
1540  }
1541 
1542  m_physicsPlotVariables.clear();
1544 
1545 template <typename I, typename C, typename R, typename F>
1546 void
1547 ItoKMCStepper<I, C, R, F>::preRegrid(const int a_lmin, const int a_oldFinestLevel) noexcept
1548 {
1549  CH_TIME("ItoKMCStepper::preRegrid");
1550  if (m_verbosity > 5) {
1551  pout() << m_name + "::preRegrid" << endl;
1552  }
1553 
1554  const int numItoSpecies = m_physics->getNumItoSpecies();
1555  const int numCdrSpecies = m_physics->getNumCdrSpecies();
1556  const int numPlasmaSpecies = m_physics->getNumPlasmaSpecies();
1557  const int numPhotonSpecies = m_physics->getNumPhotonSpecies();
1559  // If we are load balancing then we need to store the number of particles per cell on the old grids. This
1560  // will be used to estimate computational loads on the new grids.
1561  if (m_loadBalanceParticles) {
1562  Vector<RefCountedPtr<ItoSolver>> lbSolvers = this->getLoadBalanceSolvers();
1563 
1564  m_loadBalancePPC.resize(lbSolvers.size());
1565 
1566  // Allocate and compute number of computational particles per cell.
1567  for (int i = 0; i < lbSolvers.size(); i++) {
1568  m_amr->allocate(m_loadBalancePPC[i], m_particleRealm, m_plasmaPhase, 1);
1569 
1570  EBAMRCellData& compPPC = m_loadBalancePPC[i];
1571  const ParticleContainer<ItoParticle>& particles = lbSolvers[i]->getParticles(ItoSolver::WhichContainer::Bulk);
1572 
1574  }
1575  }
1576 
1577  // Release some unecessary storage.
1578  m_fluidScratch1.clear();
1579  m_fluidScratchD.clear();
1580  m_fluidScratchEB.clear();
1581 
1582  m_particleScratch1.clear();
1583  m_particleScratchD.clear();
1584  m_particleScratchEB.clear();
1585 
1586  m_conductivityCell.clear();
1587  m_conductivityFace.clear();
1588  m_conductivityEB.clear();
1589 
1590  m_electricFieldParticle.clear();
1591  m_electricFieldFluid.clear();
1592 
1593  m_electricFieldParticle.clear();
1594  m_electricFieldFluid.clear();
1595 
1596  for (int i = 0; i < numCdrSpecies; i++) {
1597  m_cdrMobilities[i].clear();
1598  m_cdrPhotoiProducts[i].clearParticles();
1599  }
1601  for (int i = 0; i < numItoSpecies; i++) {
1602  m_fluidGradPhiIto[i].clear();
1603  m_fluidPhiIto[i].clear();
1604  }
1605  for (int i = 0; i < numCdrSpecies; i++) {
1606  m_fluidGradPhiCDR[i].clear();
1607  }
1608 
1609  for (int i = 0; i < numItoSpecies; i++) {
1610  m_secondaryParticles[i].clearParticles();
1611  }
1612  for (int i = 0; i < numPhotonSpecies; i++) {
1613  m_secondaryPhotons[i].clearParticles();
1614  }
1615 
1616  for (int i = 0; i < numCdrSpecies; i++) {
1617  m_cdrFluxes[i].clear();
1618  m_cdrFluxesExtrap[i].clear();
1619  }
1620 
1621  m_currentDensity.clear();
1622  m_fluidPPC.clear();
1623 
1624  m_particleItoPPC.clear();
1625  m_particleOldItoPPC.clear();
1626 
1627  if (numCdrSpecies > 0) {
1628  m_fluidCdrPPC.clear();
1629  m_fluidOldCdrPPC.clear();
1630  }
1631 
1632  m_particleYPC.clear();
1633  m_fluidYPC.clear();
1634 
1635  // Put solvers in pre-regrid mode.
1636  m_ito->preRegrid(a_lmin, a_oldFinestLevel);
1637  m_cdr->preRegrid(a_lmin, a_oldFinestLevel);
1638  m_fieldSolver->preRegrid(a_lmin, a_oldFinestLevel);
1639  m_rte->preRegrid(a_lmin, a_oldFinestLevel);
1640  m_sigmaSolver->preRegrid(a_lmin, a_oldFinestLevel);
1641 }
1642 
1643 template <typename I, typename C, typename R, typename F>
1644 void
1645 ItoKMCStepper<I, C, R, F>::regrid(const int a_lmin, const int a_oldFinestLevel, const int a_newFinestLevel) noexcept
1646 {
1647  CH_TIME("ItoKMCStepper::regrid");
1648  if (m_verbosity > 5) {
1649  pout() << m_name + "::regrid" << endl;
1650  }
1651 
1652  this->allocateInternals();
1653 
1654  m_ito->regrid(a_lmin, a_oldFinestLevel, a_newFinestLevel);
1655  m_cdr->regrid(a_lmin, a_oldFinestLevel, a_newFinestLevel);
1656  m_fieldSolver->regrid(a_lmin, a_oldFinestLevel, a_newFinestLevel);
1657  m_rte->regrid(a_lmin, a_oldFinestLevel, a_newFinestLevel);
1658  m_sigmaSolver->regrid(a_lmin, a_oldFinestLevel, a_newFinestLevel);
1659 
1660  if (m_regridSuperparticles) {
1661  m_ito->organizeParticlesByCell(ItoSolver::WhichContainer::Bulk);
1662  m_ito->makeSuperparticles(ItoSolver::WhichContainer::Bulk, m_particlesPerCell);
1663  m_ito->organizeParticlesByPatch(ItoSolver::WhichContainer::Bulk);
1664  }
1665 
1666  // Redeposit particles and update the electric field on the new mesh.
1667  m_ito->depositParticles();
1669  const bool converged = this->solvePoisson();
1670  if (!converged) {
1671  const std::string err = "ItoKMCStepper::regrid - Poisson solve did not converge after regrid!!!";
1672 
1673  if (m_abortOnFailure) {
1674  MayDay::Error(err.c_str());
1675  }
1676  else {
1677  MayDay::Warning(err.c_str());
1678  }
1679  }
1680 
1681  this->computeDriftVelocities();
1682  this->computeDiffusionCoefficients();
1683 }
1684 
1685 template <typename I, typename C, typename R, typename F>
1686 void
1688 {
1689  CH_TIME("ItoKMCStepper::postRegrid");
1690 
1691  if (m_loadBalanceParticles) {
1692  for (int i = 0; i < m_loadBalancePPC.size(); i++) {
1693  m_amr->deallocate(m_loadBalancePPC[i]);
1694  }
1695  }
1696 }
1698 template <typename I, typename C, typename R, typename F>
1699 void
1700 ItoKMCStepper<I, C, R, F>::setVoltage(const std::function<Real(const Real a_time)>& a_voltage) noexcept
1701 {
1702  CH_TIME("ItoKMCStepper::setVoltage");
1703  if (m_verbosity > 5) {
1704  pout() << m_name + "::setVoltage" << endl;
1705  }
1706 
1707  m_voltage = a_voltage;
1708 }
1709 
1710 template <typename I, typename C, typename R, typename F>
1711 Real
1712 ItoKMCStepper<I, C, R, F>::computeMaxElectricField(const phase::which_phase a_phase) const noexcept
1713 {
1714  CH_TIME("ItoKMCStepper::computeMaxElectricField");
1715  if (m_verbosity > 5) {
1716  pout() << m_name + "::computeMaxElectricField" << endl;
1717  }
1718 
1719  // Get a handle to the E-field. Note that this is the cell-centered field!
1720  const EBAMRCellData cellCenteredE = m_amr->alias(a_phase, m_fieldSolver->getElectricField());
1721 
1722  // Interpolate to centroids
1723  EBAMRCellData centroidCenteredE;
1724  m_amr->allocate(centroidCenteredE, m_fluidRealm, a_phase, SpaceDim);
1725 
1726  DataOps::copy(centroidCenteredE, cellCenteredE);
1727 
1728  m_amr->interpToCentroids(centroidCenteredE, m_fluidRealm, m_plasmaPhase);
1730  Real max = 0.0;
1731  Real min = 0.0;
1732 
1733  DataOps::getMaxMinNorm(max, min, centroidCenteredE);
1734 
1735  return max;
1737 
1738 template <typename I, typename C, typename R, typename F>
1739 void
1741  const phase::which_phase a_phase) const noexcept
1743  CH_TIME("ItoKMCStepper::computeElectricField(EBAMRCellData, phase)");
1744  if (m_verbosity > 5) {
1745  pout() << m_name + "::computeElectricField(EBAMRCellData, phase)" << endl;
1746  }
1747 
1748  CH_assert(a_electricField.getRealm() == m_fluidRealm);
1749 
1750  m_fieldSolver->computeElectricField(a_electricField, a_phase, m_fieldSolver->getPotential());
1751 }
1752 
1753 template <typename I, typename C, typename R, typename F>
1754 Real
1756 {
1757  CH_TIME("ItoKMCStepper::getTime");
1758  if (m_verbosity > 5) {
1759  pout() << m_name + "::getTime" << endl;
1760  }
1761 
1762  return m_time;
1763 }
1764 
1765 template <typename I, typename C, typename R, typename F>
1766 void
1768 {
1769  CH_TIME("ItoKMCStepper::computeSpaceChargeDensity()");
1770  if (m_verbosity > 5) {
1771  pout() << m_name + "::computeSpaceChargeDensity()" << endl;
1772  }
1773 
1774  this->computeSpaceChargeDensity(m_fieldSolver->getRho(), m_ito->getDensities(), m_cdr->getPhis());
1775 }
1776 
1777 template <typename I, typename C, typename R, typename F>
1778 void
1780  const Vector<EBAMRCellData*>& a_itoDensities,
1781  const Vector<EBAMRCellData*>& a_cdrDensities) noexcept
1782 {
1783  CH_TIME("ItoKMCStepper::computeSpaceChargeDensity(rho, densities)");
1784  if (m_verbosity > 5) {
1785  pout() << m_name + "::computeSpaceChargeDensity(rho, densities)" << endl;
1786  }
1787 
1788  CH_assert(a_rho.getRealm() == m_fluidRealm);
1789  CH_assert(a_itoDensities[0]->getRealm() == m_particleRealm);
1790  CH_assert(a_cdrDensities[0]->getRealm() == m_fluidRealm);
1791 
1792  // TLDR: a_itoDensities could be defined over the particle realm, so we use m_fluidScratch1 as a temporary storage.
1793 
1794  DataOps::setValue(a_rho, 0.0);
1795 
1796  // Alias for the plasma phase.
1797  EBAMRCellData rhoPhase = m_amr->alias(m_plasmaPhase, a_rho);
1798 
1799  for (auto solverIt = m_ito->iterator(); solverIt.ok(); ++solverIt) {
1800  const RefCountedPtr<ItoSolver>& solver = solverIt();
1801  const RefCountedPtr<ItoSpecies>& species = solver->getSpecies();
1802  const int idx = solverIt.index();
1803  const int Z = species->getChargeNumber();
1804 
1805  if (Z != 0) {
1806  m_amr->copyData(m_fluidScratch1, *a_itoDensities[idx]);
1807 
1808  DataOps::incr(rhoPhase, m_fluidScratch1, 1.0 * Z);
1809  }
1810  }
1811 
1812  for (auto solverIt = m_cdr->iterator(); solverIt.ok(); ++solverIt) {
1813  const RefCountedPtr<CdrSolver>& solver = solverIt();
1814  const RefCountedPtr<CdrSpecies>& species = solver->getSpecies();
1815  const int idx = solverIt.index();
1816  const int Z = species->getChargeNumber();
1817 
1818  if (Z != 0) {
1819  DataOps::incr(rhoPhase, *a_cdrDensities[idx], 1.0 * Z);
1820  }
1821  }
1822 
1823  DataOps::scale(a_rho, Units::Qe);
1824 
1825  m_amr->arithmeticAverage(a_rho, m_fluidRealm);
1826  m_amr->interpGhostPwl(a_rho, m_fluidRealm);
1827 
1828  // Interpolate to centroids.
1829  m_amr->interpToCentroids(rhoPhase, m_fluidRealm, m_plasmaPhase);
1830 }
1831 
1832 template <typename I, typename C, typename R, typename F>
1833 void
1835 {
1836  CH_TIME("ItoKMCStepper::computeConductivityCell(EBAMRCellData)");
1837  if (m_verbosity > 5) {
1838  pout() << m_name + "::computeConductivityCell(EBAMRCellData)" << endl;
1839  }
1840 
1841  this->computeConductivityCell(a_conductivity, m_ito->getParticles(ItoSolver::WhichContainer::Bulk));
1842 }
1843 
1844 template <typename I, typename C, typename R, typename F>
1845 void
1847  const Vector<ParticleContainer<ItoParticle>*>& a_particles) noexcept
1848 {
1849  CH_TIME("ItoKMCStepper::computeConductivityCell(EBAMRCellData, Particles)");
1850  if (m_verbosity > 5) {
1851  pout() << m_name + "::computeConductivityCell(EBAMRCellData, Particles)" << endl;
1852  }
1853 
1854  DataOps::setValue(a_conductivity, 0.0);
1855 
1856  // Add contribution from particle solvers
1857  for (auto solverIt = m_ito->iterator(); solverIt.ok(); ++solverIt) {
1858  RefCountedPtr<ItoSolver>& solver = solverIt();
1859  const RefCountedPtr<ItoSpecies>& species = solver->getSpecies();
1860 
1861  const int idx = solverIt.index();
1862  const int Z = species->getChargeNumber();
1863 
1864  if (Z != 0 && solver->isMobile()) {
1865  solver->depositConductivity(m_particleScratch1, *a_particles[idx]);
1866 
1867  // Add to the fluid realm.
1868  m_amr->copyData(m_fluidScratch1, m_particleScratch1);
1869  DataOps::incr(a_conductivity, m_fluidScratch1, 1.0 * std::abs(Z));
1870  }
1871  }
1872 
1873  // Add contribution from CDR solvers
1874  for (auto solverIt = m_cdr->iterator(); solverIt.ok(); ++solverIt) {
1875  const RefCountedPtr<CdrSolver>& solver = solverIt();
1876  const RefCountedPtr<CdrSpecies>& species = solver->getSpecies();
1877 
1878  const int idx = solverIt.index();
1879  const int Z = species->getChargeNumber();
1880 
1881  if (Z != 0 && solver->isMobile()) {
1882  const EBAMRCellData& phi = solver->getPhi();
1883  const EBAMRCellData& mobility = m_cdrMobilities[idx];
1884 
1885  DataOps::copy(m_fluidScratch1, phi);
1886  DataOps::multiplyScalar(m_fluidScratch1, mobility);
1887  DataOps::incr(a_conductivity, m_fluidScratch1, 1.0 * std::abs(Z));
1888  }
1889  }
1890 
1891  DataOps::scale(a_conductivity, Units::Qe);
1892 
1893  m_amr->arithmeticAverage(a_conductivity, m_fluidRealm, m_plasmaPhase);
1894  m_amr->interpGhostPwl(a_conductivity, m_fluidRealm, m_plasmaPhase);
1895 
1896  // Interpolate to centroids.
1897  m_amr->interpToCentroids(a_conductivity, m_fluidRealm, m_plasmaPhase);
1898 }
1899 
1900 template <typename I, typename C, typename R, typename F>
1901 void
1903 {
1904  CH_TIME("ItoKMCStepper::computeDensityGradients()");
1905  if (m_verbosity > 5) {
1906  pout() << m_name + "::computeDensityGradients()" << endl;
1907  }
1908 
1909  // Do the same for the Ito species.
1910  for (auto it = m_ito->iterator(); it.ok(); ++it) {
1911  const RefCountedPtr<ItoSolver>& solver = it();
1912 
1913  const int idx = it.index();
1914 
1915  // Update ghost cells and coarsenings. Then compute the gradient.
1916  m_amr->copyData(m_fluidPhiIto[idx], solver->getPhi());
1917 
1918  m_amr->arithmeticAverage(m_fluidPhiIto[idx], m_fluidRealm, m_plasmaPhase);
1919  m_amr->interpGhostPwl(m_fluidPhiIto[idx], m_fluidRealm, m_plasmaPhase);
1920 
1921  m_amr->computeGradient(m_fluidGradPhiIto[idx], m_fluidPhiIto[idx], m_fluidRealm, m_plasmaPhase);
1922  }
1923 
1924  // Compute gradients for the CDR species.
1925  for (auto it = m_cdr->iterator(); it.ok(); ++it) {
1926  const RefCountedPtr<CdrSolver>& solver = it();
1927 
1928  const int idx = it.index();
1929 
1930  // Update ghost cells and coarsenings. Then compute the gradient.
1931  m_amr->copyData(m_fluidScratch1, solver->getPhi());
1932 
1933  m_amr->arithmeticAverage(m_fluidScratch1, m_fluidRealm, m_plasmaPhase);
1934  m_amr->interpGhostPwl(m_fluidScratch1, m_fluidRealm, m_plasmaPhase);
1935 
1936  m_amr->computeGradient(m_fluidGradPhiCDR[idx], m_fluidScratch1, m_fluidRealm, m_plasmaPhase);
1937  }
1938 }
1939 
1940 template <typename I, typename C, typename R, typename F>
1941 void
1943 {
1944  CH_TIME("ItoKMCStepper::computeCurrentDensity(EBAMRCellData)");
1945  if (m_verbosity > 5) {
1946  pout() << m_name + "::computeCurrentDensity(EBAMRCellData)" << endl;
1947  }
1948 
1949  CH_assert(a_J[0]->nComp() == SpaceDim);
1950 
1951  // TLDR: a_J is defined over the fluid Realm but the computation takes place on the particle Realm.
1952  // If the Realms are different we compute on a scratch storage instead
1953 
1954  this->computeConductivityCell(m_fluidScratch1);
1955  DataOps::copy(a_J, m_electricFieldFluid);
1956 
1957  DataOps::multiplyScalar(a_J, m_fluidScratch1);
1958 }
1959 
1960 template <typename I, typename C, typename R, typename F>
1961 Real
1963 {
1964  CH_TIME("ItoKMCStepper::computeRelaxationTime()");
1965  if (m_verbosity > 5) {
1966  pout() << m_name + "::computeRelaxationTime()" << endl;
1967  }
1968 
1969  // TLDR: We compute eps0/conductivity directly.
1970 
1971  EBAMRCellData conductivity;
1972  EBAMRCellData relaxTime;
1973 
1974  m_amr->allocate(conductivity, m_fluidRealm, m_plasmaPhase, 1);
1975  m_amr->allocate(relaxTime, m_fluidRealm, m_plasmaPhase, 1);
1976 
1977  this->computeConductivityCell(conductivity);
1978 
1979  DataOps::setValue(relaxTime, Units::eps0);
1980  DataOps::divideFallback(relaxTime, conductivity, std::numeric_limits<Real>::max());
1981 
1982  m_amr->conservativeAverage(relaxTime, m_fluidRealm, m_plasmaPhase);
1983 
1986 
1987  DataOps::getMaxMinNorm(max, min, relaxTime);
1988 
1989  return min;
1990 }
1991 
1992 template <typename I, typename C, typename R, typename F>
1993 bool
1995 {
1996  CH_TIME("ItoKMCStepper::solvePoisson()");
1997  if (m_verbosity > 5) {
1998  pout() << m_name + "::solvePoisson()" << endl;
1999  }
2000 
2001  // Solve the Poisson equation and compute the cell-centered electric field.
2002  MFAMRCellData& phi = m_fieldSolver->getPotential();
2003  MFAMRCellData& rho = m_fieldSolver->getRho();
2004  EBAMRIVData& sigma = m_sigmaSolver->getPhi();
2005 
2006  const bool converged = m_fieldSolver->solve(phi, rho, sigma, false);
2007 
2008  m_fieldSolver->computeElectricField();
2009 
2010  // Copy the electric field to appropriate data holders and perform center-to-centroid
2011  // interpolation.
2012  EBAMRCellData E;
2013  m_amr->allocatePointer(E, m_fluidRealm);
2014  m_amr->alias(E, m_plasmaPhase, m_fieldSolver->getElectricField());
2015 
2016  // Fluid realm
2017  m_amr->copyData(m_electricFieldFluid, E);
2018  m_amr->conservativeAverage(m_electricFieldFluid, m_fluidRealm, m_plasmaPhase);
2019  m_amr->interpGhostPwl(m_electricFieldFluid, m_fluidRealm, m_plasmaPhase);
2020  m_amr->interpToCentroids(m_electricFieldFluid, m_fluidRealm, m_plasmaPhase);
2021 
2022  // Particle realm
2023  m_amr->copyData(m_electricFieldParticle, E);
2024  m_amr->conservativeAverage(m_electricFieldParticle, m_particleRealm, m_plasmaPhase);
2025  m_amr->interpGhostPwl(m_electricFieldParticle, m_particleRealm, m_plasmaPhase);
2026  m_amr->interpToCentroids(m_electricFieldParticle, m_particleRealm, m_plasmaPhase);
2027 
2028  return converged;
2029 }
2030 
2031 template <typename I, typename C, typename R, typename F>
2032 void
2034  const bool a_delete,
2035  const std::function<void(ItoParticle&)> a_nonDeletionModifier) noexcept
2036 {
2037  CH_TIME("ItoKMCStepper::intersectParticles(SpeciesSubset, bool, std::function)");
2038  if (m_verbosity > 5) {
2039  pout() << m_name + "::intersectParticles(SpeciesSubset, bool, std::function)" << endl;
2040  }
2041 
2042  this->intersectParticles(a_speciesSubset,
2043  ItoSolver::WhichContainer::Bulk,
2044  ItoSolver::WhichContainer::EB,
2045  ItoSolver::WhichContainer::Domain,
2046  a_delete,
2047  a_nonDeletionModifier);
2048 }
2049 
2050 template <typename I, typename C, typename R, typename F>
2051 void
2053  const ItoSolver::WhichContainer a_containerBulk,
2054  const ItoSolver::WhichContainer a_containerEB,
2055  const ItoSolver::WhichContainer a_containerDomain,
2056  const bool a_delete,
2057  const std::function<void(ItoParticle&)> a_nonDeletionModifier) noexcept
2058 {
2059  CH_TIME("ItoKMCStepper::intersectParticles(SpeciesSubset, Containerx3, bool, std::function)");
2060  if (m_verbosity > 5) {
2061  pout() << m_name + "::intersectParticles(SpeciesSubset, Containerx3, bool, std::function)" << endl;
2062  }
2063 
2064  for (auto solverIt = m_ito->iterator(); solverIt.ok(); ++solverIt) {
2065  RefCountedPtr<ItoSolver>& solver = solverIt();
2066  const RefCountedPtr<ItoSpecies>& species = solver->getSpecies();
2067 
2068  const bool mobile = solver->isMobile();
2069  const bool diffusive = solver->isDiffusive();
2070  const bool charged = (species->getChargeNumber() != 0);
2071 
2072  const EBIntersection intersectionAlgorithm = solver->getIntersectionAlgorithm();
2073 
2074  switch (a_speciesSubset) {
2075  case SpeciesSubset::All: {
2076  solver->intersectParticles(a_containerBulk,
2077  a_containerEB,
2078  a_containerDomain,
2079  intersectionAlgorithm,
2080  a_delete,
2081  a_nonDeletionModifier);
2082 
2083  break;
2084  }
2085  case SpeciesSubset::AllMobile: {
2086  if (mobile) {
2087  solver->intersectParticles(a_containerBulk,
2088  a_containerEB,
2089  a_containerDomain,
2090  intersectionAlgorithm,
2091  a_delete,
2092  a_nonDeletionModifier);
2093  }
2094 
2095  break;
2096  }
2097  case SpeciesSubset::AllDiffusive: {
2098  if (diffusive) {
2099  solver->intersectParticles(a_containerBulk,
2100  a_containerEB,
2101  a_containerDomain,
2102  intersectionAlgorithm,
2103  a_delete,
2104  a_nonDeletionModifier);
2105  }
2106 
2107  break;
2108  }
2109  case SpeciesSubset::AllMobileOrDiffusive: {
2110  if (mobile || diffusive) {
2111  solver->intersectParticles(a_containerBulk,
2112  a_containerEB,
2113  a_containerDomain,
2114  intersectionAlgorithm,
2115  a_delete,
2116  a_nonDeletionModifier);
2117  }
2118 
2119  break;
2120  }
2121  case SpeciesSubset::AllMobileAndDiffusive: {
2122  if (mobile && diffusive) {
2123  solver->intersectParticles(a_containerBulk,
2124  a_containerEB,
2125  a_containerDomain,
2126  intersectionAlgorithm,
2127  a_delete,
2128  a_nonDeletionModifier);
2129  }
2130 
2131  break;
2132  }
2133  case SpeciesSubset::Charged: {
2134  if (charged) {
2135  solver->intersectParticles(a_containerBulk,
2136  a_containerEB,
2137  a_containerDomain,
2138  intersectionAlgorithm,
2139  a_delete,
2140  a_nonDeletionModifier);
2141  }
2142 
2143  break;
2144  }
2145  case SpeciesSubset::ChargedMobile: {
2146  if (charged && mobile) {
2147  solver->intersectParticles(a_containerBulk,
2148  a_containerEB,
2149  a_containerDomain,
2150  intersectionAlgorithm,
2151  a_delete,
2152  a_nonDeletionModifier);
2153  }
2154 
2155  break;
2156  }
2157  case SpeciesSubset::ChargedDiffusive: {
2158  if (charged && diffusive) {
2159  solver->intersectParticles(a_containerBulk,
2160  a_containerEB,
2161  a_containerDomain,
2162  intersectionAlgorithm,
2163  a_delete,
2164  a_nonDeletionModifier);
2165  }
2166 
2167  break;
2168  }
2169  case SpeciesSubset::ChargedMobileOrDiffusive: {
2170  if (charged && (mobile || diffusive)) {
2171  solver->intersectParticles(a_containerBulk,
2172  a_containerEB,
2173  a_containerDomain,
2174  intersectionAlgorithm,
2175  a_delete,
2176  a_nonDeletionModifier);
2177  }
2178 
2179  break;
2180  }
2181  case SpeciesSubset::ChargedMobileAndDiffusive: {
2182  if (charged && (mobile && diffusive)) {
2183  solver->intersectParticles(a_containerBulk,
2184  a_containerEB,
2185  a_containerDomain,
2186  intersectionAlgorithm,
2187  a_delete,
2188  a_nonDeletionModifier);
2189  }
2190 
2191  break;
2192  }
2193  case SpeciesSubset::Stationary: {
2194  if (!mobile && !diffusive) {
2195  solver->intersectParticles(a_containerBulk,
2196  a_containerEB,
2197  a_containerDomain,
2198  intersectionAlgorithm,
2199  a_delete,
2200  a_nonDeletionModifier);
2201  }
2202 
2203  break;
2204  }
2205  default: {
2206  MayDay::Abort("ItoKMCStepper::intersectParticles - logic bust");
2207 
2208  break;
2209  }
2210  }
2211  }
2212 }
2213 
2214 template <typename I, typename C, typename R, typename F>
2215 void
2217  const EBRepresentation a_representation,
2218  const Real a_tolerance) noexcept
2219 {
2220  CH_TIME("ItoKMCStepper::removeCoveredParticles(SpeciesSubset, EBRepresentation, Real)");
2221  if (m_verbosity > 5) {
2222  pout() << m_name + "::removeCoveredParticles(SpeciesSubset, EBRepresentation, Real)" << endl;
2223  }
2224 
2225  this->removeCoveredParticles(a_speciesSubset, ItoSolver::WhichContainer::Bulk, a_representation, a_tolerance);
2226 }
2227 
2228 template <typename I, typename C, typename R, typename F>
2229 void
2231  const ItoSolver::WhichContainer a_container,
2232  const EBRepresentation a_representation,
2233  const Real a_tolerance) noexcept
2234 {
2235  CH_TIME("ItoKMCStepper::removeCoveredParticles(SpeciesSubset, container, EBRepresentation, tolerance)");
2236  if (m_verbosity > 5) {
2237  pout() << m_name + "::removeCoveredParticles(SpeciesSubset, container, EBRepresentation, tolerance)" << endl;
2238  }
2239 
2240  for (auto solverIt = m_ito->iterator(); solverIt.ok(); ++solverIt) {
2241  RefCountedPtr<ItoSolver>& solver = solverIt();
2242  const RefCountedPtr<ItoSpecies>& species = solver->getSpecies();
2243 
2244  const bool mobile = solver->isMobile();
2245  const bool diffusive = solver->isDiffusive();
2246  const bool charged = (species->getChargeNumber() != 0);
2247 
2248  switch (a_which) {
2249  case SpeciesSubset::All: {
2250  solver->removeCoveredParticles(a_container, a_representation, a_tolerance);
2251 
2252  break;
2253  }
2254  case SpeciesSubset::AllMobile: {
2255  if (mobile) {
2256  solver->removeCoveredParticles(a_container, a_representation, a_tolerance);
2257  }
2258 
2259  break;
2260  }
2261  case SpeciesSubset::AllDiffusive: {
2262  if (diffusive) {
2263  solver->removeCoveredParticles(a_container, a_representation, a_tolerance);
2264  }
2265 
2266  break;
2267  }
2268  case SpeciesSubset::AllMobileOrDiffusive: {
2269  if (mobile || diffusive) {
2270  solver->removeCoveredParticles(a_container, a_representation, a_tolerance);
2271  }
2272 
2273  break;
2274  }
2275  case SpeciesSubset::AllMobileAndDiffusive: {
2276  if (mobile && diffusive) {
2277  solver->removeCoveredParticles(a_container, a_representation, a_tolerance);
2278  }
2279 
2280  break;
2281  }
2282  case SpeciesSubset::Charged: {
2283  if (charged) {
2284  solver->removeCoveredParticles(a_container, a_representation, a_tolerance);
2285  }
2286 
2287  break;
2288  }
2289  case SpeciesSubset::ChargedMobile: {
2290  if (charged && mobile) {
2291  solver->removeCoveredParticles(a_container, a_representation, a_tolerance);
2292  }
2293 
2294  break;
2295  }
2296  case SpeciesSubset::ChargedDiffusive: {
2297  if (charged && diffusive) {
2298  solver->removeCoveredParticles(a_container, a_representation, a_tolerance);
2299  }
2300 
2301  break;
2302  }
2303  case SpeciesSubset::ChargedMobileOrDiffusive: {
2304  if (charged && (mobile || diffusive)) {
2305  solver->removeCoveredParticles(a_container, a_representation, a_tolerance);
2306  }
2307 
2308  break;
2309  }
2310  case SpeciesSubset::ChargedMobileAndDiffusive: {
2311  if (charged && (mobile && diffusive)) {
2312  solver->removeCoveredParticles(a_container, a_representation, a_tolerance);
2313  }
2314 
2315  break;
2316  }
2317  case SpeciesSubset::Stationary: {
2318  if (!mobile && !diffusive) {
2319  solver->removeCoveredParticles(a_container, a_representation, a_tolerance);
2320  }
2321 
2322  break;
2323  }
2324  default: {
2325  MayDay::Abort("ItoKMCStepper::removeCoveredParticles - logic bust");
2326 
2327  break;
2328  }
2329  }
2330  }
2331 }
2332 
2333 template <typename I, typename C, typename R, typename F>
2334 void
2336  const EBRepresentation a_representation,
2337  const Real a_tolerance) noexcept
2338 {
2339  CH_TIME("ItoKMCStepper::transferCoveredParticles(SpeciesSubset, EBRepresentation, Real)");
2340  if (m_verbosity > 5) {
2341  pout() << m_name + "::transferCoveredParticles(SpeciesSubset, EBRepresentation, Real)" << endl;
2342  }
2343 
2344  this->transferCoveredParticles(a_speciesSubset,
2345  ItoSolver::WhichContainer::Bulk,
2346  ItoSolver::WhichContainer::Covered,
2347  a_representation,
2348  a_tolerance);
2349 }
2350 
2351 template <typename I, typename C, typename R, typename F>
2352 void
2354  const ItoSolver::WhichContainer a_containerFrom,
2355  const ItoSolver::WhichContainer a_containerTo,
2356  const EBRepresentation a_representation,
2357  const Real a_tolerance) noexcept
2358 {
2359  CH_TIME("ItoKMCStepper::transferCoveredParticles(SpeciesSubset, Containerx2, EBRepresentation, Real)");
2360  if (m_verbosity > 5) {
2361  pout() << m_name + "::transferCoveredParticles(SpeciesSubset, Containerx2, EBRepresentation, Real)" << endl;
2362  }
2363 
2364  for (auto solverIt = m_ito->iterator(); solverIt.ok(); ++solverIt) {
2365  RefCountedPtr<ItoSolver>& solver = solverIt();
2366  const RefCountedPtr<ItoSpecies>& species = solver->getSpecies();
2367 
2368  const bool mobile = solver->isMobile();
2369  const bool diffusive = solver->isDiffusive();
2370  const bool charged = (species->getChargeNumber() != 0);
2371 
2372  switch (a_speciesSubset) {
2373  case SpeciesSubset::All: {
2374  solver->transferCoveredParticles(a_containerFrom, a_containerTo, a_representation, a_tolerance);
2375 
2376  break;
2377  }
2378  case SpeciesSubset::AllMobile: {
2379  if (mobile) {
2380  solver->transferCoveredParticles(a_containerFrom, a_containerTo, a_representation, a_tolerance);
2381  }
2382 
2383  break;
2384  }
2385  case SpeciesSubset::AllDiffusive: {
2386  if (diffusive) {
2387  solver->transferCoveredParticles(a_containerFrom, a_containerTo, a_representation, a_tolerance);
2388  }
2389 
2390  break;
2391  }
2392  case SpeciesSubset::AllMobileOrDiffusive: {
2393  if (mobile || diffusive) {
2394  solver->transferCoveredParticles(a_containerFrom, a_containerTo, a_representation, a_tolerance);
2395  }
2396 
2397  break;
2398  }
2399  case SpeciesSubset::AllMobileAndDiffusive: {
2400  if (mobile && diffusive) {
2401  solver->transferCoveredParticles(a_containerFrom, a_containerTo, a_representation, a_tolerance);
2402  }
2403 
2404  break;
2405  }
2406  case SpeciesSubset::Charged: {
2407  if (charged) {
2408  solver->transferCoveredParticles(a_containerFrom, a_containerTo, a_representation, a_tolerance);
2409  }
2410 
2411  break;
2412  }
2413  case SpeciesSubset::ChargedMobile: {
2414  if (charged && mobile) {
2415  solver->transferCoveredParticles(a_containerFrom, a_containerTo, a_representation, a_tolerance);
2416  }
2417 
2418  break;
2419  }
2420  case SpeciesSubset::ChargedDiffusive: {
2421  if (charged && diffusive) {
2422  solver->transferCoveredParticles(a_containerFrom, a_containerTo, a_representation, a_tolerance);
2423  }
2424 
2425  break;
2426  }
2427  case SpeciesSubset::ChargedMobileOrDiffusive: {
2428  if (charged && (mobile || diffusive)) {
2429  solver->transferCoveredParticles(a_containerFrom, a_containerTo, a_representation, a_tolerance);
2430  }
2431 
2432  break;
2433  }
2434  case SpeciesSubset::ChargedMobileAndDiffusive: {
2435  if (charged && (mobile && diffusive)) {
2436  solver->transferCoveredParticles(a_containerFrom, a_containerTo, a_representation, a_tolerance);
2437  }
2438 
2439  break;
2440  }
2441  case SpeciesSubset::Stationary: {
2442  if (!mobile && !diffusive) {
2443  solver->transferCoveredParticles(a_containerFrom, a_containerTo, a_representation, a_tolerance);
2444  }
2445 
2446  break;
2447  }
2448  default: {
2449  MayDay::Abort("ItoKMCStepper::transferCoveredParticles - logic bust");
2450 
2451  break;
2452  }
2453  }
2454  }
2455 }
2456 
2457 template <typename I, typename C, typename R, typename F>
2458 void
2460 {
2461  CH_TIME("ItoKMCStepper::remapParticles(SpeciesSubset)");
2462  if (m_verbosity > 5) {
2463  pout() << m_name + "::remapParticles(SpeciesSubset)" << endl;
2464  }
2465 
2466  this->remapParticles(a_speciesSubset, ItoSolver::WhichContainer::Bulk);
2467 }
2468 
2469 template <typename I, typename C, typename R, typename F>
2470 void
2472  const ItoSolver::WhichContainer a_container) noexcept
2473 {
2474  CH_TIME("ItoKMCStepper::remapParticles(SpeciesSubset, WhichContainer)");
2475  if (m_verbosity > 5) {
2476  pout() << m_name + "::remapParticles(SpeciesSubset, WhichContainer)" << endl;
2477  }
2478 
2479  for (auto solverIt = m_ito->iterator(); solverIt.ok(); ++solverIt) {
2480  RefCountedPtr<ItoSolver>& solver = solverIt();
2481  const RefCountedPtr<ItoSpecies>& species = solver->getSpecies();
2482 
2483  const bool mobile = solver->isMobile();
2484  const bool diffusive = solver->isDiffusive();
2485  const bool charged = (species->getChargeNumber() != 0);
2486 
2487  switch (a_speciesSubset) {
2488  case SpeciesSubset::All: {
2489  solver->remap(a_container);
2490 
2491  break;
2492  }
2493  case SpeciesSubset::AllMobile: {
2494  if (mobile) {
2495  solver->remap(a_container);
2496  }
2497 
2498  break;
2499  }
2500  case SpeciesSubset::AllDiffusive: {
2501  if (diffusive) {
2502  solver->remap(a_container);
2503  }
2504 
2505  break;
2506  }
2507  case SpeciesSubset::AllMobileOrDiffusive: {
2508  if (mobile || diffusive) {
2509  solver->remap(a_container);
2510  }
2511 
2512  break;
2513  }
2514  case SpeciesSubset::AllMobileAndDiffusive: {
2515  if (mobile && diffusive) {
2516  solver->remap(a_container);
2517  }
2518 
2519  break;
2520  }
2521  case SpeciesSubset::Charged: {
2522  if (charged) {
2523  solver->remap(a_container);
2524  }
2525 
2526  break;
2527  }
2528  case SpeciesSubset::ChargedMobile: {
2529  if (charged && mobile) {
2530  solver->remap(a_container);
2531  }
2532 
2533  break;
2534  }
2535  case SpeciesSubset::ChargedDiffusive: {
2536  if (charged && diffusive) {
2537  solver->remap(a_container);
2538  }
2539 
2540  break;
2541  }
2542  case SpeciesSubset::ChargedMobileOrDiffusive: {
2543  if (charged && (mobile || diffusive)) {
2544  solver->remap(a_container);
2545  }
2546 
2547  break;
2548  }
2549  case SpeciesSubset::ChargedMobileAndDiffusive: {
2550  if (charged && (mobile && diffusive)) {
2551  solver->remap(a_container);
2552  }
2553 
2554  break;
2555  }
2556  case SpeciesSubset::Stationary: {
2557  if (!mobile && !diffusive) {
2558  solver->remap(a_container);
2559  }
2560 
2561  break;
2562  }
2563  default: {
2564  MayDay::Abort("ItoKMCStepper::remapParticles - logic bust");
2565 
2566  break;
2567  }
2568  }
2569  }
2570 }
2571 
2572 template <typename I, typename C, typename R, typename F>
2573 void
2575 {
2576  CH_TIME("ItoKMCStepper::depositParticles(SpeciesSubset)");
2577  if (m_verbosity > 5) {
2578  pout() << m_name + "::depositParticles(SpeciesSubset)" << endl;
2579  }
2580 
2581  this->depositParticles(a_speciesSubset, ItoSolver::WhichContainer::Bulk);
2582 }
2583 
2584 template <typename I, typename C, typename R, typename F>
2585 void
2587  const ItoSolver::WhichContainer a_container) noexcept
2588 {
2589  CH_TIME("ItoKMCStepper::depositParticles(SpeciesSubset)");
2590  if (m_verbosity > 5) {
2591  pout() << m_name + "::depositParticles(SpeciesSubset)" << endl;
2592  }
2593 
2594  for (auto solverIt = m_ito->iterator(); solverIt.ok(); ++solverIt) {
2595  RefCountedPtr<ItoSolver>& solver = solverIt();
2596  const RefCountedPtr<ItoSpecies>& species = solver->getSpecies();
2597 
2598  const bool mobile = solver->isMobile();
2599  const bool diffusive = solver->isDiffusive();
2600  const bool charged = (species->getChargeNumber() != 0);
2601 
2602  switch (a_speciesSubset) {
2603  case SpeciesSubset::All: {
2604  solver->depositParticles(a_container);
2605 
2606  break;
2607  }
2608  case SpeciesSubset::AllMobile: {
2609  if (mobile) {
2610  solver->depositParticles(a_container);
2611  }
2612 
2613  break;
2614  }
2615  case SpeciesSubset::AllDiffusive: {
2616  if (diffusive) {
2617  solver->depositParticles(a_container);
2618  }
2619 
2620  break;
2621  }
2622  case SpeciesSubset::AllMobileOrDiffusive: {
2623  if (mobile || diffusive) {
2624  solver->depositParticles(a_container);
2625  }
2626 
2627  break;
2628  }
2629  case SpeciesSubset::AllMobileAndDiffusive: {
2630  if (mobile && diffusive) {
2631  solver->depositParticles(a_container);
2632  }
2633 
2634  break;
2635  }
2636  case SpeciesSubset::Charged: {
2637  if (charged) {
2638  solver->depositParticles(a_container);
2639  }
2640 
2641  break;
2642  }
2643  case SpeciesSubset::ChargedMobile: {
2644  if (charged && mobile) {
2645  solver->depositParticles(a_container);
2646  }
2647 
2648  break;
2649  }
2650  case SpeciesSubset::ChargedDiffusive: {
2651  if (charged && diffusive) {
2652  solver->depositParticles(a_container);
2653  }
2654 
2655  break;
2656  }
2657  case SpeciesSubset::ChargedMobileOrDiffusive: {
2658  if (charged && (mobile || diffusive)) {
2659  solver->depositParticles(a_container);
2660  }
2661 
2662  break;
2663  }
2664  case SpeciesSubset::ChargedMobileAndDiffusive: {
2665  if (charged && (mobile && diffusive)) {
2666  solver->depositParticles(a_container);
2667  }
2668 
2669  break;
2670  }
2671  case SpeciesSubset::Stationary: {
2672  if (!mobile && !diffusive) {
2673  solver->depositParticles(a_container);
2674  }
2675 
2676  break;
2677  }
2678  default: {
2679  MayDay::Abort("ItoKMCStepper::depositParticles - logic bust");
2680 
2681  break;
2682  }
2683  }
2684  }
2685 }
2686 
2687 template <typename I, typename C, typename R, typename F>
2688 void
2690 {
2691  CH_TIME("ItoKMCStepper::setItoVelocityFunctions");
2692  if (m_verbosity > 5) {
2693  pout() << m_name + "::setItoVelocityFunctions" << endl;
2694  }
2695 
2696  for (auto solverIt = m_ito->iterator(); solverIt.ok(); ++solverIt) {
2697  RefCountedPtr<ItoSolver>& solver = solverIt();
2698  const RefCountedPtr<ItoSpecies>& species = solver->getSpecies();
2699  const int Z = species->getChargeNumber();
2700 
2701  if (solver->isMobile() && Z != 0) {
2702  EBAMRCellData& velocityFunction = solver->getVelocityFunction();
2703  m_amr->copyData(velocityFunction, m_electricFieldParticle);
2704 
2705  const int Z = species->getChargeNumber();
2706 
2707  if (Z < 0) {
2708  DataOps::scale(velocityFunction, -1.0);
2709  }
2710 
2711  // Coarsen and update ghost cells.
2712  m_amr->conservativeAverage(velocityFunction, m_particleRealm, m_plasmaPhase);
2713  m_amr->interpGhostPwl(velocityFunction, m_particleRealm, m_plasmaPhase);
2714  }
2715  }
2716 }
2717 
2718 template <typename I, typename C, typename R, typename F>
2719 void
2721 {
2722  CH_TIME("ItoKMCStepper::setCdrVelocityFunctions");
2723  if (m_verbosity > 5) {
2724  pout() << m_name + "::setCdrVelocityFunctions" << endl;
2725  }
2726 
2727  for (auto solverIt = m_cdr->iterator(); solverIt.ok(); ++solverIt) {
2728  RefCountedPtr<CdrSolver>& solver = solverIt();
2729  const RefCountedPtr<CdrSpecies>& species = solver->getSpecies();
2730  const int Z = species->getChargeNumber();
2731 
2732  if (solver->isMobile() && Z != 0) {
2733  EBAMRCellData& velocity = solver->getCellCenteredVelocity();
2734  m_amr->copyData(velocity, m_electricFieldFluid);
2735 
2736  const int Z = species->getChargeNumber();
2737 
2738  if (Z < 0) {
2739  DataOps::scale(velocity, -1.0);
2740  }
2741 
2742  // Coarsen and update ghost cells.
2743  m_amr->conservativeAverage(velocity, m_fluidRealm, m_plasmaPhase);
2744  m_amr->interpGhostPwl(velocity, m_fluidRealm, m_plasmaPhase);
2745  }
2746  else if (solver->isMobile() && Z == 0) {
2747  MayDay::Warning("ItoKMCStepper::setCdrVelocityFunctions -- how to handle mobile neutral species?");
2748  }
2749  }
2750 }
2751 
2752 template <typename I, typename C, typename R, typename F>
2753 void
2755 {
2756  CH_TIME("ItoKMCStepper::multiplyCdrVelocitiesByMobilities()");
2757  if (m_verbosity > 5) {
2758  pout() << m_name + "::multiplyCdrVelocitiesByMobilities()" << endl;
2759  }
2760 
2761  for (auto solverIt = m_cdr->iterator(); solverIt.ok(); ++solverIt) {
2762  RefCountedPtr<CdrSolver>& solver = solverIt();
2763  const int idx = solverIt.index();
2764 
2765  if (solver->isMobile()) {
2766  EBAMRCellData& velocity = solver->getCellCenteredVelocity();
2767  const EBAMRCellData& mobility = m_cdrMobilities[idx];
2768 
2769  DataOps::multiplyScalar(velocity, mobility);
2770 
2771  // Coarsen and update ghost cells.
2772  m_amr->conservativeAverage(velocity, m_fluidRealm, m_plasmaPhase);
2773  m_amr->interpGhostPwl(velocity, m_fluidRealm, m_plasmaPhase);
2774  }
2775  }
2776 }
2777 
2778 template <typename I, typename C, typename R, typename F>
2779 void
2781 {
2782  CH_TIME("ItoKMCStepper::computeDriftVelocities()");
2783  if (m_verbosity > 5) {
2784  pout() << m_name + "::computeDriftVelocities()" << endl;
2785  }
2786 
2787  // Set velocities to be sgn(Z) * E
2788  this->setItoVelocityFunctions();
2789  this->setCdrVelocityFunctions();
2790 
2791  // Compute mobilities for both Ito and CDR species.
2792  this->computeMobilities();
2793 
2794  // Multiply sgn(Z) * E by the mobilities. For the CDR solvers this is just a multiplication, for the Ito solvers
2795  // we interpolate mu*E to the particle positions (in some form).
2796  for (auto solverIt = m_ito->iterator(); solverIt.ok(); ++solverIt) {
2797  solverIt()->interpolateVelocities();
2798  }
2799 
2800  this->multiplyCdrVelocitiesByMobilities();
2801 }
2802 
2803 template <typename I, typename C, typename R, typename F>
2804 void
2806 {
2807  CH_TIME("ItoKMCStepper::computeMobilities()");
2808  if (m_verbosity > 5) {
2809  pout() << m_name + "::computeMobilities()" << endl;
2810  }
2811 
2812  Vector<EBAMRCellData*> itoMobilities = m_ito->getMobilityFunctions();
2813 
2814  this->computeMobilities(itoMobilities, m_cdrMobilities, m_electricFieldFluid, m_time);
2815 }
2816 
2817 template <typename I, typename C, typename R, typename F>
2818 void
2819 ItoKMCStepper<I, C, R, F>::computeMobilities(Vector<EBAMRCellData*>& a_itoMobilities,
2820  Vector<EBAMRCellData>& a_cdrMobilities,
2821  const EBAMRCellData& a_electricField,
2822  const Real a_time) noexcept
2823 {
2824  CH_TIME("ItoKMCStepper::computeMobilities(mobilities, E, time)");
2825  if (m_verbosity > 5) {
2826  pout() << m_name + "::computeMobilities(mobilities, E, time)" << endl;
2827  }
2828 
2829  const int numItoSpecies = m_physics->getNumItoSpecies();
2830  const int numCdrSpecies = m_physics->getNumCdrSpecies();
2831 
2832  CH_assert(a_electricField.getRealm() == m_fluidRealm);
2833  CH_assert(a_itoMobilities.size() == numItoSpecies);
2834  CH_assert(a_cdrMobilities.size() == numCdrSpecies);
2835 
2836  // The mesh mobilities belong on the particle realm (they are the ItoSolver mobilities) but we need to run
2837  // the computation on the fluid realm. So, create some transient storage for that.
2838  Vector<EBAMRCellData> fluidScratchMobilities(numItoSpecies);
2839  for (int i = 0; i < numItoSpecies; i++) {
2840  m_amr->allocate(fluidScratchMobilities[i], m_fluidRealm, m_plasmaPhase, 1);
2841 
2842  DataOps::setValue(fluidScratchMobilities[i], 0.0);
2843  DataOps::setValue(*a_itoMobilities[i], 0.0);
2844 
2845  CH_assert(a_itoMobilities[i]->getRealm() == m_particleRealm);
2846  }
2847 
2848  // Now run the computation on the fluid realm, computing the mobilities into fluidScratchMobilities
2849  for (int lvl = 0; lvl <= m_amr->getFinestLevel(); lvl++) {
2850  Vector<LevelData<EBCellFAB>*> itoMobilities(numItoSpecies);
2851  Vector<LevelData<EBCellFAB>*> cdrMobilities(numCdrSpecies);
2852 
2853  for (int i = 0; i < numItoSpecies; i++) {
2854  itoMobilities[i] = &(*(fluidScratchMobilities[i])[lvl]);
2855  }
2856 
2857  for (int i = 0; i < numCdrSpecies; i++) {
2858  cdrMobilities[i] = &(*(a_cdrMobilities[i])[lvl]);
2859  }
2860 
2861  // Run the level computation, which will fill the mobilities.
2862  this->computeMobilities(itoMobilities, cdrMobilities, *a_electricField[lvl], lvl, a_time);
2863  }
2864 
2865  // Copy fluid realm data into particle realm and interpolate mobilities to the particle position.
2866  for (auto solverIt = m_ito->iterator(); solverIt.ok(); ++solverIt) {
2867  RefCountedPtr<ItoSolver>& solver = solverIt();
2868 
2869  if (solver->isMobile()) {
2870  const int idx = solverIt.index();
2871 
2872  m_amr->copyData(*a_itoMobilities[idx], fluidScratchMobilities[idx]);
2873  m_amr->conservativeAverage(*a_itoMobilities[idx], m_particleRealm, m_plasmaPhase);
2874  m_amr->interpGhostPwl(*a_itoMobilities[idx], m_particleRealm, m_plasmaPhase);
2875 
2876  solver->interpolateMobilities();
2877  }
2878  }
2879 }
2880 
2881 template <typename I, typename C, typename R, typename F>
2882 void
2883 ItoKMCStepper<I, C, R, F>::computeMobilities(Vector<LevelData<EBCellFAB>*>& a_itoMobilities,
2884  Vector<LevelData<EBCellFAB>*>& a_cdrMobilities,
2885  const LevelData<EBCellFAB>& a_electricField,
2886  const int a_level,
2887  const Real a_time) noexcept
2888 {
2889  CH_TIME("ItoKMCStepper::computeMobilities(mobilities, E, level, time)");
2890  if (m_verbosity > 5) {
2891  pout() << m_name + "::computeMobilities(mobilities, E, level, time)" << endl;
2892  }
2893 
2894  const DisjointBoxLayout& dbl = m_amr->getGrids(m_fluidRealm)[a_level];
2895  const DataIterator& dit = dbl.dataIterator();
2896 
2897  const int nbox = dit.size();
2898 
2899 #pragma omp parallel for schedule(runtime)
2900  for (int mybox = 0; mybox < nbox; mybox++) {
2901  const DataIndex& din = dit[mybox];
2902 
2903  const EBCellFAB& E = a_electricField[din];
2904  const Box cellBox = dbl[din];
2905 
2906  Vector<EBCellFAB*> itoMobilities;
2907  Vector<EBCellFAB*> cdrMobilities;
2908 
2909  for (int i = 0; i < a_itoMobilities.size(); i++) {
2910  itoMobilities.push_back(&((*a_itoMobilities[i])[din]));
2911  }
2912 
2913  for (int i = 0; i < a_cdrMobilities.size(); i++) {
2914  cdrMobilities.push_back(&((*a_cdrMobilities[i])[din]));
2915  }
2916 
2917  this->computeMobilities(itoMobilities, cdrMobilities, E, a_level, din, cellBox, a_time);
2918  }
2919 }
2920 
2921 template <typename I, typename C, typename R, typename F>
2922 void
2923 ItoKMCStepper<I, C, R, F>::computeMobilities(Vector<EBCellFAB*>& a_itoMobilities,
2924  Vector<EBCellFAB*>& a_cdrMobilities,
2925  const EBCellFAB& a_electricField,
2926  const int a_level,
2927  const DataIndex a_dit,
2928  const Box a_box,
2929  const Real a_time) noexcept
2930 {
2931  CH_TIME("ItoKMCStepper::computeMobilities(meshMobilities, E, level, dit, box, time)");
2932  if (m_verbosity > 5) {
2933  pout() << m_name + "::computeMobilities(meshMobilities, E, level, dit, box, time)" << endl;
2934  }
2935 
2936  // TLDR: We go through each and every cell and call the physics interface. This includes cells covered by a finer grid
2937  // but data is coarsened later anyways.
2938  const int numItoSpecies = m_physics->getNumItoSpecies();
2939  const int numCdrSpecies = m_physics->getNumCdrSpecies();
2940  const int numPlasmaSpecies = m_physics->getNumPlasmaSpecies();
2941 
2942  const Real dx = m_amr->getDx()[a_level];
2943  const RealVect probLo = m_amr->getProbLo();
2944  const EBISBox& ebisbox = m_amr->getEBISLayout(m_fluidRealm, m_plasmaPhase)[a_level][a_dit];
2945 
2946  // Handles to regular data.
2947  const FArrayBox& electricFieldReg = a_electricField.getFArrayBox();
2948  Vector<FArrayBox*> itoMobilitiesReg(numItoSpecies);
2949  Vector<FArrayBox*> cdrMobilitiesReg(numCdrSpecies);
2950 
2951  for (int i = 0; i < a_itoMobilities.size(); i++) {
2952  itoMobilitiesReg[i] = (&(a_itoMobilities[i]->getFArrayBox()));
2953  }
2954 
2955  for (int i = 0; i < a_cdrMobilities.size(); i++) {
2956  cdrMobilitiesReg[i] = (&(a_cdrMobilities[i]->getFArrayBox()));
2957  }
2958 
2959  // Physics interface mapping -- this maps a global index from the returned vector to an Ito or CDR solver
2960  const std::map<int, std::pair<SpeciesType, int>>& speciesMap = m_physics->getSpeciesMap();
2961 
2962  // Regular kernel
2963  auto regularKernel = [&](const IntVect& iv) -> void {
2964  const RealVect pos = m_amr->getProbLo() + dx * (RealVect(iv) + 0.5 * RealVect::Unit);
2965  const RealVect E = RealVect(D_DECL(electricFieldReg(iv, 0), electricFieldReg(iv, 1), electricFieldReg(iv, 2)));
2966 
2967  // Call physics interface and compute mobilities for each species.
2968  const Vector<Real> mobilities = m_physics->computeMobilities(a_time, pos, E);
2969 
2970  CH_assert(mobilities.size() == numPlasmaSpecies);
2971 
2972  // Put the mobilities where they belong.
2973  for (const auto& s : speciesMap) {
2974  const int& globalIndex = s.first;
2975  const SpeciesType& type = s.second.first;
2976  const int& localIndex = s.second.second;
2977 
2978  if (type == SpeciesType::Ito) {
2979  (*itoMobilitiesReg[localIndex])(iv, 0) = mobilities[globalIndex];
2980  }
2981  else if (type == SpeciesType::CDR) {
2982  (*cdrMobilitiesReg[localIndex])(iv, 0) = mobilities[globalIndex];
2983  }
2984  }
2985  };
2986 
2987  // Irregular kernel.
2988  auto irregularKernel = [&](const VolIndex& vof) -> void {
2989  const RealVect e = RealVect(D_DECL(a_electricField(vof, 0), a_electricField(vof, 1), a_electricField(vof, 2)));
2990  const RealVect pos = probLo + Location::position(Location::Cell::Centroid, vof, ebisbox, dx);
2991 
2992  // Call physics interface and compute mobilities for each species.
2993  const Vector<Real> mobilities = m_physics->computeMobilities(a_time, pos, e);
2994 
2995  CH_assert(mobilities.size() == numPlasmaSpecies);
2996 
2997  // Put the mobilities where they belong.
2998  for (const auto& s : speciesMap) {
2999  const int& globalIndex = s.first;
3000  const SpeciesType& type = s.second.first;
3001  const int& localIndex = s.second.second;
3002 
3003  if (type == SpeciesType::Ito) {
3004  (*a_itoMobilities[localIndex])(vof, 0) = mobilities[globalIndex];
3005  }
3006  else if (type == SpeciesType::CDR) {
3007  (*a_cdrMobilities[localIndex])(vof, 0) = mobilities[globalIndex];
3008  }
3009  }
3010  };
3011 
3012  VoFIterator& vofit = (*m_amr->getVofIterator(m_fluidRealm, m_plasmaPhase)[a_level])[a_dit];
3013 
3014  // Run the kernels.
3015  BoxLoops::loop(a_box, regularKernel);
3016  BoxLoops::loop(vofit, irregularKernel);
3017 
3018  // Covered is bogus.
3019  for (auto solverIt = m_ito->iterator(); solverIt.ok(); ++solverIt) {
3020  a_itoMobilities[solverIt.index()]->setCoveredCellVal(0.0, 0);
3021  }
3022 
3023  for (auto solverIt = m_cdr->iterator(); solverIt.ok(); ++solverIt) {
3024  a_cdrMobilities[solverIt.index()]->setCoveredCellVal(0.0, 0);
3025  }
3026 }
3027 
3028 template <typename I, typename C, typename R, typename F>
3029 void
3031 {
3032  CH_TIME("ItoKMCStepper::computeDiffusionCoefficients()");
3033  if (m_verbosity > 5) {
3034  pout() << m_name + "::computeDiffusionCoefficients()" << endl;
3035  }
3036 
3037  Vector<EBAMRCellData*> itoDiffusionCoefficients = m_ito->getDiffusionFunctions();
3038  Vector<EBAMRCellData*> cdrDiffusionCoefficients = m_cdr->getCellCenteredDiffusionCoefficients();
3039 
3040  this->computeDiffusionCoefficients(itoDiffusionCoefficients, cdrDiffusionCoefficients, m_electricFieldFluid, m_time);
3041  this->averageDiffusionCoefficientsCellToFace();
3042 }
3043 
3044 template <typename I, typename C, typename R, typename F>
3045 void
3046 ItoKMCStepper<I, C, R, F>::computeDiffusionCoefficients(Vector<EBAMRCellData*>& a_itoDiffusionCoefficients,
3047  Vector<EBAMRCellData*>& a_cdrDiffusionCoefficients,
3048  const EBAMRCellData& a_electricField,
3049  const Real a_time) noexcept
3050 {
3051  CH_TIME("ItoKMCStepper::computeDiffusionCoefficients(Vector<EBAMRCellData*>, EBAMRCellData, Real)");
3052  if (m_verbosity > 5) {
3053  pout() << m_name + "::computeDiffusionCoefficients(Vector<EBAMRCellData*>, EBAMRCellData, Real)" << endl;
3054  }
3055 
3056  const int numItoSpecies = m_physics->getNumItoSpecies();
3057  const int numCdrSpecies = m_physics->getNumCdrSpecies();
3058 
3059  CH_assert(a_electricField.getRealm() == m_fluidRealm);
3060  CH_assert(a_itoDiffusionCoefficients.size() == numItoSpecies);
3061  CH_assert(a_cdrDiffusionCoefficients.size() == numCdrSpecies);
3062 
3063  // Sanity check -- things need to be defined on the correct realms.
3064  for (int i = 0; i < numItoSpecies; i++) {
3065  CH_assert(a_itoDiffusionCoefficients[i]->getRealm() == m_particleRealm);
3066  }
3067  for (int i = 0; i < numCdrSpecies; i++) {
3068  CH_assert(a_cdrDiffusionCoefficients[i]->getRealm() == m_fluidRealm);
3069  }
3070 
3071  // The mesh diffusion coefficients belong on the particle realm (they are the ItoSolver diffusion coefficients) but we need to run
3072  // the computation on the fluid realm. So, create some transient storage for that.
3073  Vector<EBAMRCellData> fluidScratchDiffusion(numItoSpecies);
3074  for (int i = 0; i < numItoSpecies; i++) {
3075  m_amr->allocate(fluidScratchDiffusion[i], m_fluidRealm, m_plasmaPhase, 1);
3076 
3077  CH_assert(a_itoDiffusionCoefficients[i]->getRealm() == m_particleRealm);
3078  }
3079 
3080  // Compute mesh-based diffusion coefficients on the fluid realm.
3081  for (int lvl = 0; lvl <= m_amr->getFinestLevel(); lvl++) {
3082  Vector<LevelData<EBCellFAB>*> itoDiffusionCoefficients(numItoSpecies);
3083  Vector<LevelData<EBCellFAB>*> cdrDiffusionCoefficients(numCdrSpecies);
3084 
3085  for (int i = 0; i < numItoSpecies; i++) {
3086  itoDiffusionCoefficients[i] = &(*(fluidScratchDiffusion[i])[lvl]);
3087  }
3088  for (int i = 0; i < numCdrSpecies; i++) {
3089  cdrDiffusionCoefficients[i] = &(*(*a_cdrDiffusionCoefficients[i])[lvl]);
3090  }
3091 
3092  this->computeDiffusionCoefficients(itoDiffusionCoefficients,
3093  cdrDiffusionCoefficients,
3094  *a_electricField[lvl],
3095  lvl,
3096  a_time);
3097  }
3098 
3099  // Copy the fluid realm data over to the particle realm data and then coarsen and interpolate diffusion coefficients
3100  // to particle positions.
3101  for (auto solverIt = m_ito->iterator(); solverIt.ok(); ++solverIt) {
3102  RefCountedPtr<ItoSolver>& solver = solverIt();
3103 
3104  if (solver->isDiffusive()) {
3105  const int idx = solverIt.index();
3106 
3107  m_amr->copyData(*a_itoDiffusionCoefficients[idx], fluidScratchDiffusion[idx]);
3108  m_amr->conservativeAverage(*a_itoDiffusionCoefficients[idx], m_particleRealm, m_plasmaPhase);
3109  m_amr->interpGhostPwl(*a_itoDiffusionCoefficients[idx], m_particleRealm, m_plasmaPhase);
3110 
3111  solver->interpolateDiffusion();
3112  }
3113  }
3114 }
3115 
3116 template <typename I, typename C, typename R, typename F>
3117 void
3118 ItoKMCStepper<I, C, R, F>::computeDiffusionCoefficients(Vector<LevelData<EBCellFAB>*>& a_itoDiffusionCoefficients,
3119  Vector<LevelData<EBCellFAB>*>& a_cdrDiffusionCoefficients,
3120  const LevelData<EBCellFAB>& a_electricField,
3121  const int a_level,
3122  const Real a_time) noexcept
3123 {
3124  CH_TIME("ItoKMCStepper::computeDiffusionCoefficients(Vector<LD<EBCellFAB>*>, LD<EBCellFAB>, int, Real)");
3125  if (m_verbosity > 5) {
3126  pout() << m_name + "::computeDiffusionCoefficients(Vector<LD<EBCellFAB>*>, LD<EBCellFAB>, int, Real)" << endl;
3127  }
3128 
3129  const int numItoSpecies = m_physics->getNumItoSpecies();
3130  const int numCdrSpecies = m_physics->getNumCdrSpecies();
3131 
3132  CH_assert(a_itoDiffusionCoefficients.size() == numItoSpecies);
3133  CH_assert(a_cdrDiffusionCoefficients.size() == numCdrSpecies);
3134 
3135  const DisjointBoxLayout& dbl = m_amr->getGrids(m_fluidRealm)[a_level];
3136  const DataIterator& dit = dbl.dataIterator();
3137 
3138  const int nbox = dit.size();
3139 
3140 #pragma omp parallel for schedule(runtime)
3141  for (int mybox = 0; mybox < nbox; mybox++) {
3142  const DataIndex& din = dit[mybox];
3143 
3144  Vector<EBCellFAB*> itoDiffusionCoefficients(numItoSpecies);
3145  Vector<EBCellFAB*> cdrDiffusionCoefficients(numCdrSpecies);
3146 
3147  for (auto solverIt = m_ito->iterator(); solverIt.ok(); ++solverIt) {
3148  const int idx = solverIt.index();
3149 
3150  if (solverIt()->isDiffusive()) {
3151  itoDiffusionCoefficients[idx] = &(*a_itoDiffusionCoefficients[idx])[din];
3152  }
3153  else {
3154  itoDiffusionCoefficients[idx] = nullptr;
3155  }
3156  }
3157 
3158  for (auto solverIt = m_cdr->iterator(); solverIt.ok(); ++solverIt) {
3159  const int idx = solverIt.index();
3160 
3161  if (solverIt()->isDiffusive()) {
3162  cdrDiffusionCoefficients[idx] = &(*a_cdrDiffusionCoefficients[idx])[din];
3163  }
3164  else {
3165  cdrDiffusionCoefficients[idx] = nullptr;
3166  }
3167  }
3168 
3169  this->computeDiffusionCoefficients(itoDiffusionCoefficients,
3170  cdrDiffusionCoefficients,
3171  a_electricField[din],
3172  a_level,
3173  din,
3174  dbl[din],
3175  a_time);
3176  }
3177 }
3178 
3179 template <typename I, typename C, typename R, typename F>
3180 void
3181 ItoKMCStepper<I, C, R, F>::computeDiffusionCoefficients(Vector<EBCellFAB*>& a_itoDiffusionCoefficients,
3182  Vector<EBCellFAB*>& a_cdrDiffusionCoefficients,
3183  const EBCellFAB& a_electricField,
3184  const int a_level,
3185  const DataIndex a_dit,
3186  const Box a_box,
3187  const Real a_time) noexcept
3188 {
3189  CH_TIME("ItoKMCStepper::computeDiffusionCoefficients(Patch)");
3190  if (m_verbosity > 5) {
3191  pout() << m_name + "::computeDiffusionCoefficients(Patch)" << endl;
3192  }
3193 
3194  const int numItoSpecies = m_physics->getNumItoSpecies();
3195  const int numCdrSpecies = m_physics->getNumCdrSpecies();
3196  const int numPlasmaSpecies = m_physics->getNumPlasmaSpecies();
3197 
3198  CH_assert(a_electricField.nComp() == SpaceDim);
3199  CH_assert(a_itoDiffusionCoefficients.size() == numItoSpecies);
3200  CH_assert(a_cdrDiffusionCoefficients.size() == numCdrSpecies);
3201 
3202  // Geometric information that we need.
3203  const Real dx = m_amr->getDx()[a_level];
3204  const RealVect probLo = m_amr->getProbLo();
3205  const EBISBox& ebisbox = m_amr->getEBISLayout(m_fluidRealm, m_plasmaPhase)[a_level][a_dit];
3206 
3207  // Handle to single-valued data.
3208  const FArrayBox& electricFieldReg = a_electricField.getFArrayBox();
3209 
3210  Vector<FArrayBox*> itoDiffCoReg(numItoSpecies, nullptr);
3211  Vector<FArrayBox*> cdrDiffCoReg(numCdrSpecies, nullptr);
3212 
3213  for (auto solverIt = m_ito->iterator(); solverIt.ok(); ++solverIt) {
3214  RefCountedPtr<ItoSolver>& solver = solverIt();
3215 
3216  if (solver->isDiffusive()) {
3217  const int i = solverIt.index();
3218  itoDiffCoReg[i] = &(a_itoDiffusionCoefficients[i]->getFArrayBox());
3219  }
3220  }
3221 
3222  for (auto solverIt = m_cdr->iterator(); solverIt.ok(); ++solverIt) {
3223  RefCountedPtr<CdrSolver>& solver = solverIt();
3224 
3225  if (solver->isDiffusive()) {
3226  const int i = solverIt.index();
3227  cdrDiffCoReg[i] = &(a_cdrDiffusionCoefficients[i]->getFArrayBox());
3228  }
3229  }
3230 
3231  // Physics interface mapping -- this maps a global index from the returned vector to an Ito or CDR solver
3232  const std::map<int, std::pair<SpeciesType, int>>& speciesMap = m_physics->getSpeciesMap();
3233 
3234  // Regular kernel definition.
3235  auto regularKernel = [&](const IntVect& iv) -> void {
3236  const RealVect pos = probLo + dx * (RealVect(iv) + 0.5 * RealVect::Unit);
3237  const RealVect E = RealVect(D_DECL(electricFieldReg(iv, 0), electricFieldReg(iv, 1), electricFieldReg(iv, 2)));
3238 
3239  // Compute diffusion coefficients.
3240  const Vector<Real> diffusionCoefficients = m_physics->computeDiffusionCoefficients(a_time, pos, E);
3241 
3242  CH_assert(diffusionCoefficients.size() == numPlasmaSpecies);
3243 
3244  // Put the diffusion coefficients in the correct solver storage.
3245  for (const auto& s : speciesMap) {
3246  const int& globalIndex = s.first;
3247  const SpeciesType& type = s.second.first;
3248  const int& localIndex = s.second.second;
3249 
3250  // We need an explicit check to see if
3251  if (type == SpeciesType::Ito) {
3252  if (m_ito->getSolvers()[localIndex]->isDiffusive()) {
3253  (*itoDiffCoReg[localIndex])(iv, 0) = diffusionCoefficients[globalIndex];
3254  }
3255  }
3256  else if (type == SpeciesType::CDR) {
3257  if (m_cdr->getSolvers()[localIndex]->isDiffusive()) {
3258  (*cdrDiffCoReg[localIndex])(iv, 0) = diffusionCoefficients[globalIndex];
3259  }
3260  }
3261  }
3262  };
3263 
3264  // Irregular kernel.
3265  auto irregularKernel = [&](const VolIndex& vof) -> void {
3266  const RealVect E = RealVect(D_DECL(a_electricField(vof, 0), a_electricField(vof, 1), a_electricField(vof, 2)));
3267  const RealVect pos = probLo + Location::position(Location::Cell::Centroid, vof, ebisbox, dx);
3268 
3269  // Compute diffusion coefficients.
3270  const Vector<Real> diffusionCoefficients = m_physics->computeDiffusionCoefficients(a_time, pos, E);
3271 
3272  // Put the diffusion coefficients in the correct solver storage.
3273  for (const auto& s : speciesMap) {
3274  const int& globalIndex = s.first;
3275  const SpeciesType& type = s.second.first;
3276  const int& localIndex = s.second.second;
3277 
3278  // We need an explicit check to see if
3279  if (type == SpeciesType::Ito) {
3280  if (m_ito->getSolvers()[localIndex]->isDiffusive()) {
3281  (*a_itoDiffusionCoefficients[localIndex])(vof, 0) = diffusionCoefficients[globalIndex];
3282  }
3283  }
3284  else if (type == SpeciesType::CDR) {
3285  if (m_cdr->getSolvers()[localIndex]->isDiffusive()) {
3286  (*a_cdrDiffusionCoefficients[localIndex])(vof, 0) = diffusionCoefficients[globalIndex];
3287  }
3288  }
3289  }
3290  };
3291 
3292  // Run kernels.
3293  VoFIterator& vofit = (*m_amr->getVofIterator(m_fluidRealm, m_plasmaPhase)[a_level])[a_dit];
3294 
3295  BoxLoops::loop(a_box, regularKernel);
3296  BoxLoops::loop(vofit, irregularKernel);
3297 
3298  // Covered is bogus.
3299  for (auto solverIt = m_ito->iterator(); solverIt.ok(); ++solverIt) {
3300  if (solverIt()->isDiffusive()) {
3301  a_itoDiffusionCoefficients[solverIt.index()]->setCoveredCellVal(0.0, 0);
3302  }
3303  }
3304 
3305  for (auto solverIt = m_cdr->iterator(); solverIt.ok(); ++solverIt) {
3306  if (solverIt()->isDiffusive()) {
3307  a_cdrDiffusionCoefficients[solverIt.index()]->setCoveredCellVal(0.0, 0);
3308  }
3309  }
3310 }
3311 
3312 template <typename I, typename C, typename R, typename F>
3313 void
3315 {
3316  CH_TIME("ItoKMCStepper::averageDiffusionCoefficientsCellToFace");
3317  if (m_verbosity > 5) {
3318  pout() << m_name + "::averageDiffusionCoefficientsCellToFace" << endl;
3319  }
3320 
3321  for (auto solverIt = m_cdr->iterator(); solverIt.ok(); ++solverIt) {
3322  RefCountedPtr<CdrSolver>& solver = solverIt();
3323 
3324  if (solver->isDiffusive()) {
3325 
3326  EBAMRCellData& cellCenteredDiffusionCoefficient = solver->getCellCenteredDiffusionCoefficient();
3327  EBAMRFluxData& faceCenteredDiffusionCoefficient = solver->getFaceCenteredDiffusionCoefficient();
3328 
3329  CH_assert(cellCenteredDiffusionCoefficient.getRealm() == m_fluidRealm);
3330  CH_assert(faceCenteredDiffusionCoefficient.getRealm() == m_fluidRealm);
3331 
3332  DataOps::setValue(faceCenteredDiffusionCoefficient, std::numeric_limits<Real>::max());
3333 
3334  // Coarsen the cell-centered diffusion coefficient before averaging to faces.
3335  m_amr->arithmeticAverage(cellCenteredDiffusionCoefficient, m_fluidRealm, m_cdr->getPhase());
3336  m_amr->interpGhostPwl(cellCenteredDiffusionCoefficient, m_fluidRealm, m_cdr->getPhase());
3337 
3338  // Average to cell faces. Note that this call also includes one ghost face (required when there's an EBCF crossing).
3339  const int tanGhost = 1;
3340  const Interval interv = Interval(0, 0);
3341  const Average average = Average::Arithmetic;
3342 
3343  DataOps::averageCellToFace(faceCenteredDiffusionCoefficient,
3344  cellCenteredDiffusionCoefficient,
3345  m_amr->getDomains(),
3346  tanGhost,
3347  interv,
3348  interv,
3349  average);
3350  }
3351  }
3352 }
3353 
3354 template <typename I, typename C, typename R, typename F>
3355 void
3357 {
3358  CH_TIME("ItoKMCStepper::getPhysicalParticlesPerCell(EBAMRCellData)");
3359  if (m_verbosity > 5) {
3360  pout() << m_name + "::getPhysicaParticlesPerCell(EBAMRCellData)" << endl;
3361  }
3362 
3363  CH_assert(a_ppc.getRealm() == m_particleRealm);
3364 
3365  for (auto it = m_ito->iterator(); it.ok(); ++it) {
3366  const int idx = it.index();
3367 
3368  EBAMRCellData ppc = m_amr->slice(a_ppc, Interval(idx, idx));
3369 
3370  const ParticleContainer<ItoParticle>& particles = it()->getParticles(ItoSolver::WhichContainer::Bulk);
3371 
3372  ParticleOps::getPhysicalParticlesPerCell<ItoParticle, &ItoParticle::weight>(ppc, particles);
3373  }
3374 }
3375 
3376 template <typename I, typename C, typename R, typename F>
3377 void
3379 {
3380  CH_TIME("ItoKMCStepper::computeReactiveItoParticlesPerCell(EBAMRCellData)");
3381  if (m_verbosity > 5) {
3382  pout() << m_name + "::computeReactiveItoParticlesPerCell(EBAMRCellData)" << endl;
3383  }
3384 
3385  CH_assert(a_ppc.getRealm() == m_particleRealm);
3386 
3387  DataOps::setValue(a_ppc, 0.0);
3388 
3389  for (int lvl = 0; lvl <= m_amr->getFinestLevel(); lvl++) {
3390  this->computeReactiveItoParticlesPerCell(*a_ppc[lvl], lvl);
3391  }
3392 }
3393 
3394 template <typename I, typename C, typename R, typename F>
3395 void
3396 ItoKMCStepper<I, C, R, F>::computeReactiveItoParticlesPerCell(LevelData<EBCellFAB>& a_ppc, const int a_level) noexcept
3397 {
3398  CH_TIME("ItoKMCStepper::computeReactiveItoParticlesPerCell(LD<EBCellFAB>, int)");
3399  if (m_verbosity > 5) {
3400  pout() << m_name + "::computeReactiveItoParticlesPerCell(LD<EBCellFAB>, int)" << endl;
3401  }
3402 
3403  const int numItoSpecies = m_physics->getNumItoSpecies();
3404 
3405  CH_assert(a_ppc.nComp() == numItoSpecies);
3406 
3407  const DisjointBoxLayout& dbl = m_amr->getGrids(m_particleRealm)[a_level];
3408  const EBISLayout& ebisl = m_amr->getEBISLayout(m_particleRealm, m_plasmaPhase)[a_level];
3409  const DataIterator& dit = dbl.dataIterator();
3410 
3411  const int nbox = dit.size();
3412 
3413 #pragma omp parallel for schedule(runtime)
3414  for (int mybox = 0; mybox < nbox; mybox++) {
3415  const DataIndex& din = dit[mybox];
3416 
3417  const Box box = dbl[din];
3418  const EBISBox& ebisbox = ebisl[din];
3419 
3420  this->computeReactiveItoParticlesPerCell(a_ppc[din], a_level, din, box, ebisbox);
3421  }
3422 }
3423 
3424 template <typename I, typename C, typename R, typename F>
3425 void
3427  const int a_level,
3428  const DataIndex a_dit,
3429  const Box a_box,
3430  const EBISBox& a_ebisbox) noexcept
3431 {
3432  CH_TIME("ItoKMCStepper::computeReactiveItoParticlesPerCell(EBCellFAB, int, DataIndex, Box, EBISBox)");
3433  if (m_verbosity > 5) {
3434  pout() << m_name + "::computeReactiveItoParticlesPerCell(EBCellFAB, int, DataIndex, Box, EBISBox)" << endl;
3435  }
3436 
3437  const int numItoSpecies = m_physics->getNumItoSpecies();
3438 
3439  CH_assert(a_ppc.nComp() == numItoSpecies);
3440 
3441  const Real dx = m_amr->getDx()[a_level];
3442  const RealVect probLo = m_amr->getProbLo();
3443 
3444  // TLDR: We go through each solver and add the number of PHYSICAL particles per cell to a_ppc.
3445 
3446  FArrayBox& ppcRegular = a_ppc.getFArrayBox();
3447 
3448  for (auto solverIt = m_ito->iterator(); solverIt.ok(); ++solverIt) {
3449  RefCountedPtr<ItoSolver>& solver = solverIt();
3450  const int idx = solverIt.index();
3451 
3452  // Get the cell-sorted particles. This will issue an error if the user has not
3453  // sorted the particles by cell before calling this routine.
3454  const ParticleContainer<ItoParticle>& particles = solver->getParticles(ItoSolver::WhichContainer::Bulk);
3455  const BinFab<ItoParticle>& cellParticles = particles.getCellParticles(a_level, a_dit);
3456 
3457  // Regular cells kernel.
3458  auto regularKernel = [&](const IntVect& iv) -> void {
3459  Real num = 0.0;
3460 
3461  if (a_ebisbox.isRegular(iv)) {
3462  for (ListIterator<ItoParticle> lit(cellParticles(iv, 0)); lit.ok(); ++lit) {
3463  num += lit().weight();
3464  }
3465  }
3466 
3467  ppcRegular(iv, idx) = num;
3468  };
3469 
3470  // Irregular kernel -- note that only particles that lie inside the domain get to react.
3471  auto irregularKernel = [&](const VolIndex& vof) -> void {
3472  const IntVect iv = vof.gridIndex();
3473  const RealVect normal = a_ebisbox.normal(vof);
3474  const RealVect physCentroid = probLo + Location::position(Location::Cell::Boundary, vof, a_ebisbox, dx);
3475 
3476  Real num = 0.0;
3477 
3478  for (ListIterator<ItoParticle> lit(cellParticles(iv, 0)); lit.ok(); ++lit) {
3479  const RealVect& pos = lit().position();
3480  if ((pos - physCentroid).dotProduct(normal) >= 0.0) {
3481  num += lit().weight();
3482  }
3483  }
3484 
3485  ppcRegular(iv, idx) = num;
3486  };
3487 
3488  // Run the kernels.
3489  VoFIterator& vofit = (*m_amr->getVofIterator(m_particleRealm, m_plasmaPhase)[a_level])[a_dit];
3490 
3491  BoxLoops::loop(a_box, regularKernel);
3492  BoxLoops::loop(vofit, irregularKernel);
3493  }
3494 }
3495 
3496 template <typename I, typename C, typename R, typename F>
3497 void
3499 {
3500  CH_TIME("ItoKMCStepper::computeReactiveCdrParticlesPerCell(EBAMRCellData)");
3501  if (m_verbosity > 5) {
3502  pout() << m_name + "::computeReactiveCdrParticlesPerCell(EBAMRCellData)" << endl;
3503  }
3504 
3505  CH_assert(a_ppc.getRealm() == m_fluidRealm);
3506 
3507  DataOps::setValue(a_ppc, 0.0);
3508 
3509  for (int lvl = 0; lvl <= m_amr->getFinestLevel(); lvl++) {
3510  this->computeReactiveCdrParticlesPerCell(*a_ppc[lvl], lvl);
3511  }
3512 }
3513 
3514 template <typename I, typename C, typename R, typename F>
3515 void
3516 ItoKMCStepper<I, C, R, F>::computeReactiveCdrParticlesPerCell(LevelData<EBCellFAB>& a_ppc, const int a_level) noexcept
3517 {
3518  CH_TIME("ItoKMCStepper::computeReactiveCdrParticlesPerCell(LD<EBCellFAB>, int)");
3519  if (m_verbosity > 5) {
3520  pout() << m_name + "::computeReactiveCdrParticlesPerCell(LD<EBCellFAB>, int)" << endl;
3521  }
3522 
3523  const int numCdrSpecies = m_physics->getNumCdrSpecies();
3524 
3525  CH_assert(a_ppc.nComp() == numCdrSpecies);
3526 
3527  if (numCdrSpecies > 0) {
3528  const DisjointBoxLayout& dbl = m_amr->getGrids(m_fluidRealm)[a_level];
3529  const EBISLayout& ebisl = m_amr->getEBISLayout(m_fluidRealm, m_plasmaPhase)[a_level];
3530  const DataIterator& dit = dbl.dataIterator();
3531 
3532  const int nbox = dit.size();
3533 
3534 #pragma omp parallel for schedule(runtime)
3535  for (int mybox = 0; mybox < nbox; mybox++) {
3536  const DataIndex& din = dit[mybox];
3537 
3538  const Box box = dbl[din];
3539  const EBISBox& ebisbox = ebisl[din];
3540 
3541  this->computeReactiveCdrParticlesPerCell(a_ppc[din], a_level, din, box, ebisbox);
3542  }
3543  }
3544 }
3545 
3546 template <typename I, typename C, typename R, typename F>
3547 void
3549  const int a_level,
3550  const DataIndex a_dit,
3551  const Box a_box,
3552  const EBISBox& a_ebisbox) noexcept
3553 {
3554  CH_TIME("ItoKMCStepper::computeReactiveCdrParticlesPerCell(EBCellFAB, int, DataIndex, Box, EBISBox)");
3555  if (m_verbosity > 5) {
3556  pout() << m_name + "::computeReactiveCdrParticlesPerCell(EBCellFAB, int, DataIndex, Box, EBISBox)" << endl;
3557  }
3558 
3559  constexpr Real zero = 0.0;
3560 
3561  const int numCdrSpecies = m_physics->getNumCdrSpecies();
3562 
3563  CH_assert(a_ppc.nComp() == numCdrSpecies);
3564 
3565  const Real dx = m_amr->getDx()[a_level];
3566  const Real vol = std::pow(dx, SpaceDim);
3567  const RealVect probLo = m_amr->getProbLo();
3568 
3569  // TLDR: We go through each solver and add the number of PHYSICAL particles per cell to a_ppc.
3570  FArrayBox& ppcRegular = a_ppc.getFArrayBox();
3571 
3572  for (auto solverIt = m_cdr->iterator(); solverIt.ok(); ++solverIt) {
3573  RefCountedPtr<CdrSolver>& solver = solverIt();
3574  const int idx = solverIt.index();
3575 
3576  const EBCellFAB& phi = (*(solver->getPhi())[a_level])[a_dit];
3577  const FArrayBox& phiReg = phi.getFArrayBox();
3578 
3579  // Regular cells kernel.
3580  auto regularKernel = [&](const IntVect& iv) -> void {
3581  if (a_ebisbox.isRegular(iv)) {
3582  ppcRegular(iv, idx) = std::max(zero, std::floor(phiReg(iv, 0) * vol));
3583  }
3584  };
3585 
3586  // Irregular kernel -- note that only particles that lie inside the domain get to react.
3587  auto irregularKernel = [&](const VolIndex& vof) -> void {
3588  const Real kappa = a_ebisbox.volFrac(vof);
3589 
3590  a_ppc(vof, idx) = std::max(zero, std::floor(kappa * phi(vof, 0) * vol));
3591  };
3592 
3593  // Run the kernels.
3594  VoFIterator& vofit = (*m_amr->getVofIterator(m_fluidRealm, m_plasmaPhase)[a_level])[a_dit];
3595 
3596  BoxLoops::loop(a_box, regularKernel);
3597  BoxLoops::loop(vofit, irregularKernel);
3598  }
3599 }
3600 
3601 template <typename I, typename C, typename R, typename F>
3602 void
3604 {
3605  CH_TIME("ItoKMCStepper::computeReactiveMaeanEnergiesPerCell(EBAMRCellData)");
3606  if (m_verbosity > 5) {
3607  pout() << m_name + "::computeReactiveMaeanEnergiesPerCell(EBAMRCellData)" << endl;
3608  }
3609 
3610  CH_assert(a_meanEnergies.getRealm() == m_particleRealm);
3611 
3612  DataOps::setValue(a_meanEnergies, 0.0);
3613 
3614  for (int lvl = 0; lvl <= m_amr->getFinestLevel(); lvl++) {
3615  this->computeReactiveMeanEnergiesPerCell(*a_meanEnergies[lvl], lvl);
3616  }
3617 }
3618 
3619 template <typename I, typename C, typename R, typename F>
3620 void
3622  const int a_level) noexcept
3623 {
3624  CH_TIME("ItoKMCStepper::computeReactiveMeanEnergiesPerCell(LD<EBCellFAB>, int)");
3625  if (m_verbosity > 5) {
3626  pout() << m_name + "::computeReactiveMeanEnergiesPerCell(LD<EBCellFAB>, int)" << endl;
3627  }
3628 
3629  const int numPlasmaSpecies = m_physics->getNumItoSpecies();
3630 
3631  CH_assert(a_meanEnergies.nComp() == numPlasmaSpecies);
3632 
3633  const DisjointBoxLayout& dbl = m_amr->getGrids(m_particleRealm)[a_level];
3634  const EBISLayout& ebisl = m_amr->getEBISLayout(m_particleRealm, m_plasmaPhase)[a_level];
3635  const DataIterator& dit = dbl.dataIterator();
3636 
3637  const int nbox = dit.size();
3638 
3639 #pragma omp parallel for schedule(runtime)
3640  for (int mybox = 0; mybox < nbox; mybox++) {
3641  const DataIndex& din = dit[mybox];
3642 
3643  const Box box = dbl[din];
3644  const EBISBox& ebisbox = ebisl[din];
3645 
3646  this->computeReactiveMeanEnergiesPerCell(a_meanEnergies[din], a_level, din, box, ebisbox);
3647  }
3648 }
3649 
3650 template <typename I, typename C, typename R, typename F>
3651 void
3653  const int a_level,
3654  const DataIndex a_dit,
3655  const Box a_box,
3656  const EBISBox& a_ebisbox) noexcept
3657 {
3658  CH_TIME("ItoKMCStepper::computeReactiveMeanEnergiesPerCell(EBCellFABint, DataIndex, Box, EBISBox)");
3659  if (m_verbosity > 5) {
3660  pout() << m_name + "::computeReactiveMeanEnergiesPerCell(EBCellFABint, DataIndex, Box, EBISBox))" << endl;
3661  }
3662 
3663  const int numPlasmaSpecies = m_physics->getNumItoSpecies();
3664 
3665  CH_assert(a_meanEnergies.nComp() == numPlasmaSpecies);
3666 
3667  const Real dx = m_amr->getDx()[a_level];
3668  const RealVect probLo = m_amr->getProbLo();
3669 
3670  // Get single-valued data.
3671  FArrayBox& meanEnergiesReg = a_meanEnergies.getFArrayBox();
3672 
3673  for (auto solverIt = m_ito->iterator(); solverIt.ok(); ++solverIt) {
3674  RefCountedPtr<ItoSolver>& solver = solverIt();
3675  const int idx = solverIt.index();
3676 
3677  // Get the cell-sorted particles. This will issue an error if the user has not
3678  // sorted the particles by cell before calling this routine.
3679  const ParticleContainer<ItoParticle>& particles = solver->getParticles(ItoSolver::WhichContainer::Bulk);
3680  const BinFab<ItoParticle>& cellParticles = particles.getCellParticles(a_level, a_dit);
3681 
3682  // Regular grid cells.
3683  auto regularKernel = [&](const IntVect& iv) -> void {
3684  if (a_ebisbox.isRegular(iv)) {
3685  Real totalWeight = 0.0;
3686  Real totalEnergy = 0.0;
3687 
3688  for (ListIterator<ItoParticle> lit(cellParticles(iv, 0)); lit.ok(); ++lit) {
3689  totalWeight += lit().weight();
3690  totalEnergy += lit().weight() * lit().energy();
3691  }
3692 
3693  if (totalWeight > 0.0) {
3694  meanEnergiesReg(iv, idx) = totalEnergy / totalWeight;
3695  }
3696  else {
3697  meanEnergiesReg(iv, idx) = 0.0;
3698  }
3699  }
3700  };
3701 
3702  // Irregular cells -- note that only valid particles get to play with us.
3703  auto irregularKernel = [&](const VolIndex& vof) -> void {
3704  const IntVect iv = vof.gridIndex();
3705  const RealVect normal = a_ebisbox.normal(vof);
3706  const RealVect ebCentroid = probLo + Location::position(Location::Cell::Boundary, vof, a_ebisbox, dx);
3707 
3708  Real totalWeight = 0.0;
3709  Real totalEnergy = 0.0;
3710 
3711  for (ListIterator<ItoParticle> lit(cellParticles(iv, 0)); lit.ok(); ++lit) {
3712  const RealVect& pos = lit().position();
3713 
3714  if ((pos - ebCentroid).dotProduct(normal) >= 0.0) {
3715  totalWeight += lit().weight();
3716  totalEnergy += lit().weight() * lit().energy();
3717  }
3718  }
3719 
3720  if (totalWeight > 0.0) {
3721  meanEnergiesReg(iv, idx) = totalEnergy / totalWeight;
3722  }
3723  else {
3724  meanEnergiesReg(iv, idx) = 0.0;
3725  }
3726  };
3727 
3728  // Run the kernels.
3729  VoFIterator& vofit = (*m_amr->getVofIterator(m_particleRealm, m_plasmaPhase)[a_level])[a_dit];
3730 
3731  BoxLoops::loop(a_box, regularKernel);
3732  BoxLoops::loop(vofit, irregularKernel);
3733  }
3734 }
3735 
3736 template <typename I, typename C, typename R, typename F>
3737 void
3739 {
3740  CH_TIME("ItoKMCStepper::advanceReactionNetwork(dt)");
3741  if (m_verbosity > 5) {
3742  pout() << m_name + "::advanceReactionNetwork(dt)" << endl;
3743  }
3744 
3745  CH_assert(a_dt > 0.0);
3746 
3747  this->advanceReactionNetwork(m_electricFieldFluid, a_dt);
3748 }
3749 
3750 template <typename I, typename C, typename R, typename F>
3751 void
3752 ItoKMCStepper<I, C, R, F>::advanceReactionNetwork(const EBAMRCellData& a_electricField, const Real a_dt) noexcept
3753 {
3754  CH_TIMERS("ItoKMCStepper::advanceReactionNetwork");
3755  CH_TIMER("ItoKMCStepper::advanceReactionNetwork::compute_ppc", t1);
3756  CH_TIMER("ItoKMCStepper::advanceReactionNetwork::integrate_network", t2);
3757  CH_TIMER("ItoKMCStepper::advanceReactionNetwork::copies", t3);
3758  CH_TIMER("ItoKMCStepper::advanceReactionNetwork::reconcile_particles", t4);
3759  CH_TIMER("ItoKMCStepper::advanceReactionNetwork::reconcile_cdr", t5);
3760  if (m_verbosity > 5) {
3761  pout() << m_name + "::advanceReactionNetwork" << endl;
3762  }
3763 
3764  const int numItoSpecies = m_physics->getNumItoSpecies();
3765  const int numCdrSpecies = m_physics->getNumCdrSpecies();
3766  const int numPhotonSpecies = m_physics->getNumPhotonSpecies();
3767 
3768  CH_assert(a_electricField.getRealm() == m_fluidRealm);
3769  CH_assert(a_dt > 0.0);
3770 
3771  // Compute the number of reactive particles for both Ito and CDR species. Also do a backup of the initial number
3772  // of particles per cell. This is required when reconciling the results lateron.
3773  CH_START(t1);
3774  if (numItoSpecies > 0) {
3775  this->computeReactiveItoParticlesPerCell(m_particleItoPPC);
3776 
3777  const Interval srcInterv(0, numItoSpecies - 1);
3778  const Interval dstInterv(0, numItoSpecies - 1);
3779 
3780  m_amr->copyData(m_fluidPPC, m_particleItoPPC, dstInterv, srcInterv);
3781 
3782  DataOps::copy(m_particleOldItoPPC, m_particleItoPPC);
3783  }
3784  if (numCdrSpecies > 0) {
3785  this->computeReactiveCdrParticlesPerCell(m_fluidCdrPPC);
3786 
3787  const Interval srcInterv(0, numCdrSpecies - 1);
3788  const Interval dstInterv(numItoSpecies, numItoSpecies + numCdrSpecies - 1);
3789 
3790  m_amr->copyData(m_fluidPPC, m_fluidCdrPPC, dstInterv, srcInterv);
3791 
3792  DataOps::copy(m_fluidOldCdrPPC, m_fluidCdrPPC);
3793  }
3794 
3795  DataOps::setValue(m_fluidYPC, 0.0);
3796  DataOps::setValue(m_particleYPC, 0.0);
3797  CH_STOP(t1);
3798 
3799  // Advance the reaction network which gives us a new number of particles per cell, as well as the number of
3800  // photons that need to be generated per cell.
3801  CH_START(t2);
3802  for (int lvl = 0; lvl <= m_amr->getFinestLevel(); lvl++) {
3803  this->advanceReactionNetwork(*m_fluidPPC[lvl], *m_fluidYPC[lvl], *a_electricField[lvl], lvl, a_dt);
3804  }
3805  CH_STOP(t2);
3806 
3807  // Copy the results back to the holders that hold the number of particles per cell for Ito/Cdr solvers.
3808  CH_START(t3);
3809  if (numItoSpecies > 0) {
3810  const Interval srcInterv(0, numItoSpecies - 1);
3811  const Interval dstInterv(0, numItoSpecies - 1);
3812 
3813  m_amr->copyData(m_particleItoPPC, m_fluidPPC, dstInterv, srcInterv);
3814  }
3815  if (numCdrSpecies > 0) {
3816  const Interval srcInterv(numItoSpecies, numItoSpecies + numCdrSpecies - 1);
3817  const Interval dstInterv(0, numCdrSpecies - 1);
3818 
3819  m_amr->copyData(m_fluidCdrPPC, m_fluidPPC, dstInterv, srcInterv);
3820  }
3821  if (numPhotonSpecies > 0) {
3822  m_amr->copyData(m_particleYPC, m_fluidYPC);
3823  }
3824  CH_STOP(t3);
3825 
3826  // If we have photoionization reactions and CDR solvers we need to put the resulting particles on the mesh first,
3827  // and then add the result back into the CDR solvers.
3828  CH_START(t4);
3829  for (int i = 0; i < m_cdrPhotoiProducts.size(); i++) {
3830  m_cdrPhotoiProducts[i].clearParticles();
3831  m_cdrPhotoiProducts[i].organizeParticlesByCell();
3832  }
3833 
3834  // Reconcile the results -- for the discrete solvers we add/remove particles/photons and for the CDR solvers we update
3835  // the source terms.
3836  this->reconcileParticles(m_particleItoPPC, m_particleOldItoPPC, m_particleYPC);
3837  CH_STOP(t4);
3838 
3839  // Deposited PointParticles on the mesh and add the result to m_fluidCdrPPC. This is a bit convoluted because
3840  // the photoionization products are created in reconcileParticles and deposited them on the particle realm, but
3841  // the resulting products need to end up on in the correct data component in m_fluidCdrPPC.
3842  CH_START(t5);
3843  for (int i = 0; i < m_cdrPhotoiProducts.size(); i++) {
3844  m_cdrPhotoiProducts[i].organizeParticlesByPatch();
3845 
3846  m_amr->depositParticles<PointParticle, &PointParticle::weight>(m_particleScratch1,
3847  m_particleRealm,
3848  m_plasmaPhase,
3849  m_cdrPhotoiProducts[i],
3850  DepositionType::NGP,
3851  CoarseFineDeposition::Halo);
3852 
3853  m_amr->copyData(m_fluidScratch1, m_particleScratch1);
3854  DataOps::volumeScale(m_fluidScratch1, m_amr->getDx());
3855 
3856  EBAMRCellData fluidCdrPPC = m_amr->slice(m_fluidCdrPPC, Interval(i, i));
3857 
3858  DataOps::incr(fluidCdrPPC, m_fluidScratch1, 1.0);
3859 
3860  m_cdrPhotoiProducts[i].clearParticles();
3861  }
3862 
3863  this->reconcileCdrDensities(m_fluidCdrPPC, m_fluidOldCdrPPC, a_dt);
3864  CH_STOP(t5);
3865 }
3866 
3867 template <typename I, typename C, typename R, typename F>
3868 inline void
3869 ItoKMCStepper<I, C, R, F>::advanceReactionNetwork(LevelData<EBCellFAB>& a_particlesPerCell,
3870  LevelData<EBCellFAB>& a_newPhotonsPerCell,
3871  const LevelData<EBCellFAB>& a_electricField,
3872  const int a_level,
3873  const Real a_dt) const noexcept
3874 {
3875  CH_TIME("ItoKMCStepper::advanceReactionNetwork(LD<EBCellFAB>x3, int, Real)");
3876  if (m_verbosity > 5) {
3877  pout() << m_name + "::advanceReactionNetwork(LD<EBCellFAB>x3, int, Real)" << endl;
3878  }
3879 
3880  const int numPlasmaSpecies = m_physics->getNumPlasmaSpecies();
3881  const int numPhotonSpecies = m_physics->getNumPhotonSpecies();
3882 
3883  CH_assert(a_particlesPerCell.nComp() == numPlasmaSpecies);
3884  CH_assert(a_newPhotonsPerCell.nComp() == numPhotonSpecies);
3885  CH_assert(a_electricField.nComp() == SpaceDim);
3886 
3887  const DisjointBoxLayout& dbl = m_amr->getGrids(m_fluidRealm)[a_level];
3888  const DataIterator& dit = dbl.dataIterator();
3889 
3890  const int nbox = dit.size();
3891 
3892 #pragma omp parallel
3893  {
3894  m_physics->defineKMC();
3895 
3896 #pragma omp for schedule(runtime)
3897  for (int mybox = 0; mybox < nbox; mybox++) {
3898  const DataIndex& din = dit[mybox];
3899 
3900  this->advanceReactionNetwork(a_particlesPerCell[din],
3901  a_newPhotonsPerCell[din],
3902  a_electricField[din],
3903  a_level,
3904  din,
3905  dbl[din],
3906  m_amr->getDx()[a_level],
3907  a_dt);
3908  }
3909 
3910  m_physics->killKMC();
3911  }
3912 }
3913 
3914 template <typename I, typename C, typename R, typename F>
3915 inline void
3917  EBCellFAB& a_newPhotonsPerCell,
3918  const EBCellFAB& a_electricField,
3919  const int a_level,
3920  const DataIndex a_dit,
3921  const Box a_box,
3922  const Real a_dx,
3923  const Real a_dt) const noexcept
3924 {
3925  CH_TIME("ItoKMCStepper::advanceReactionNetwork(EBCellFABx3, int, DataIndex, Box, Realx2)");
3926  if (m_verbosity > 5) {
3927  pout() << m_name + "::advanceReactionNetwork(EBCellFABx3, int, DataIndex, Box, Realx2)" << endl;
3928  }
3929 
3930  const int numCdrSpecies = m_physics->getNumCdrSpecies();
3931  const int numItoSpecies = m_physics->getNumItoSpecies();
3932  const int numPlasmaSpecies = m_physics->getNumPlasmaSpecies();
3933  const int numPhotonSpecies = m_physics->getNumPhotonSpecies();
3934 
3935  CH_assert(a_particlesPerCell.nComp() == numPlasmaSpecies);
3936  CH_assert(a_newPhotonsPerCell.nComp() == numPhotonSpecies);
3937  CH_assert(a_electricField.nComp() == SpaceDim);
3938 
3939  // Geometric information that we require.
3940  const RealVect probLo = m_amr->getProbLo();
3941  const EBISBox& ebisbox = m_amr->getEBISLayout(m_fluidRealm, m_plasmaPhase)[a_level][a_dit];
3942 
3943  const FArrayBox& electricFieldReg = a_electricField.getFArrayBox();
3944 
3945  // Storage used by physics interface.
3946  Vector<Physics::ItoKMC::FPR> particles(numPlasmaSpecies);
3947  Vector<Physics::ItoKMC::FPR> newPhotons(numPhotonSpecies);
3948  Vector<Real> meanEnergies(numPlasmaSpecies);
3949  Vector<Real> energySources(numPlasmaSpecies);
3950  Vector<Real> densities(numPlasmaSpecies, 0.0);
3951  Vector<RealVect> densityGradients(numPlasmaSpecies, RealVect::Zero);
3952 
3953  // Populate single-valued data.
3954  FArrayBox& particlesPerCellReg = a_particlesPerCell.getFArrayBox();
3955  FArrayBox& newPhotonsReg = a_newPhotonsPerCell.getFArrayBox();
3956 
3957  // Handle to densities and density gradients for CDR and Ito species.
3958  Vector<const EBCellFAB*> densitiesIto(numItoSpecies);
3959  Vector<const EBCellFAB*> densityGradientsIto(numItoSpecies);
3960  Vector<const FArrayBox*> densitiesItoReg(numItoSpecies);
3961  Vector<const FArrayBox*> densityGradientsItoReg(numItoSpecies);
3962 
3963  Vector<const EBCellFAB*> densitiesCDR(numCdrSpecies);
3964  Vector<const EBCellFAB*> densityGradientsCDR(numCdrSpecies);
3965  Vector<const FArrayBox*> densitiesCDRReg(numCdrSpecies);
3966  Vector<const FArrayBox*> densityGradientsCDRReg(numCdrSpecies);
3967 
3968  for (auto it = m_ito->iterator(); it.ok(); ++it) {
3969  const RefCountedPtr<ItoSolver>& solver = it();
3970 
3971  const int i = it.index();
3972 
3973  densitiesIto[i] = &(*(m_fluidPhiIto[i])[a_level])[a_dit];
3974  densitiesItoReg[i] = &(densitiesIto[i]->getFArrayBox());
3975  densityGradientsIto[i] = &(*m_fluidGradPhiIto[i][a_level])[a_dit];
3976  densityGradientsItoReg[i] = &(densityGradientsIto[i]->getFArrayBox());
3977  }
3978 
3979  for (auto it = m_cdr->iterator(); it.ok(); ++it) {
3980  const RefCountedPtr<CdrSolver>& solver = it();
3981  const EBAMRCellData& phi = solver->getPhi();
3982 
3983  const int i = it.index();
3984 
3985  densitiesCDR[i] = &(*phi[a_level])[a_dit];
3986  densitiesCDRReg[i] = &(densitiesCDR[i]->getFArrayBox());
3987  densityGradientsCDR[i] = &(*m_fluidGradPhiCDR[i][a_level])[a_dit];
3988  densityGradientsCDRReg[i] = &(densityGradientsCDR[i]->getFArrayBox());
3989  }
3990 
3991  // Handle to valid grid cells.
3992  const BaseFab<bool>& validCells = (*m_amr->getValidCells(m_fluidRealm)[a_level])[a_dit];
3993 
3994  // Regular cells
3995  auto regularKernel = [&](const IntVect& iv) -> void {
3996  if (ebisbox.isRegular(iv) && validCells(iv, 0)) {
3997  const RealVect pos = probLo + a_dx * (RealVect(iv) + 0.5 * RealVect::Unit);
3998  const RealVect E = RealVect(D_DECL(electricFieldReg(iv, 0), electricFieldReg(iv, 1), electricFieldReg(iv, 2)));
3999 
4000  // Populate the data holders that the physics interface requires.
4001  for (int i = 0; i < numPlasmaSpecies; i++) {
4002  particles[i] = (long long)particlesPerCellReg(iv, i);
4003  }
4004 
4005  for (int i = 0; i < numPhotonSpecies; i++) {
4006  newPhotons[i] = 0LL;
4007  }
4008 
4009  // Populate gradients.
4010  for (int i = 0; i < numItoSpecies; i++) {
4011  densities[i] = (*densitiesItoReg[i])(iv, 0);
4012  densityGradients[i] = RealVect(D_DECL((*densityGradientsItoReg[i])(iv, 0),
4013  (*densityGradientsItoReg[i])(iv, 1),
4014  (*densityGradientsItoReg[i])(iv, 2)));
4015  }
4016 
4017  for (int i = 0; i < numCdrSpecies; i++) {
4018  densities[numItoSpecies + i] = (*densitiesCDRReg[i])(iv, 0);
4019  densityGradients[numItoSpecies + i] = RealVect(D_DECL((*densityGradientsCDRReg[i])(iv, 0),
4020  (*densityGradientsCDRReg[i])(iv, 1),
4021  (*densityGradientsCDRReg[i])(iv, 2)));
4022  }
4023 
4024  // Do the physics advance.
4025  m_physics->advanceKMC(particles, newPhotons, densities, densityGradients, a_dt, E, pos, a_dx, 1.0);
4026 
4027  // Repopulate the input data holders with the new number of particles/photons per cell.
4028  for (int i = 0; i < numPlasmaSpecies; i++) {
4029  particlesPerCellReg(iv, i) = 1.0 * particles[i];
4030  }
4031 
4032  for (int i = 0; i < numPhotonSpecies; i++) {
4033  newPhotonsReg(iv, i) = 1.0 * newPhotons[i];
4034  }
4035  }
4036  };
4037 
4038  // Irregular cells
4039  auto irregularKernel = [&](const VolIndex& vof) -> void {
4040  const IntVect iv = vof.gridIndex();
4041 
4042  if (validCells(iv, 0)) {
4043  const Real kappa = ebisbox.volFrac(vof);
4044  const RealVect pos = probLo + Location::position(Location::Cell::Centroid, vof, ebisbox, a_dx);
4045  const RealVect E = RealVect(D_DECL(a_electricField(vof, 0), a_electricField(vof, 1), a_electricField(vof, 2)));
4046 
4047  // Initialize for this cell.
4048  for (int i = 0; i < numPlasmaSpecies; i++) {
4049  particles[i] = (long long)a_particlesPerCell(vof, i);
4050  }
4051 
4052  for (int i = 0; i < numPhotonSpecies; i++) {
4053  newPhotons[i] = 0LL;
4054  }
4055 
4056  // Populate gradients.
4057  for (int i = 0; i < numItoSpecies; i++) {
4058  densities[i] = (*densitiesIto[i])(vof, 0);
4059  densityGradients[i] = RealVect(D_DECL((*densityGradientsIto[i])(vof, 0),
4060  (*densityGradientsIto[i])(vof, 1),
4061  (*densityGradientsIto[i])(vof, 2)));
4062  }
4063 
4064  for (int i = 0; i < numCdrSpecies; i++) {
4065  densities[numItoSpecies + i] = (*densitiesCDR[i])(vof, 0);
4066  densityGradients[numItoSpecies + i] = RealVect(D_DECL((*densityGradientsCDR[i])(vof, 0),
4067  (*densityGradientsCDR[i])(vof, 1),
4068  (*densityGradientsCDR[i])(vof, 2)));
4069  }
4070 
4071  // Do the physics advance
4072  m_physics->advanceKMC(particles, newPhotons, densities, densityGradients, a_dt, E, pos, a_dx, kappa);
4073 
4074  // Repopulate the input data holders with the new number of particles/photons per cell.
4075  for (int i = 0; i < numPlasmaSpecies; i++) {
4076  a_particlesPerCell(vof, i) = 1.0 * particles[i];
4077  }
4078 
4079  for (int i = 0; i < numPhotonSpecies; i++) {
4080  a_newPhotonsPerCell(vof, i) = 1.0 * newPhotons[i];
4081  }
4082  }
4083  };
4084 
4085  // Run the kernels.
4086  VoFIterator& vofit = (*m_amr->getVofIterator(m_fluidRealm, m_plasmaPhase)[a_level])[a_dit];
4087 
4088  BoxLoops::loop(a_box, regularKernel);
4089  BoxLoops::loop(vofit, irregularKernel);
4090 }
4091 
4092 template <typename I, typename C, typename R, typename F>
4093 inline void
4095  const EBAMRCellData& a_oldParticlesPerCell,
4096  const EBAMRCellData& a_newPhotonsPerCell) const noexcept
4097 {
4098  CH_TIME("ItoKMCStepper::reconcileParticles(EBAMRCellDatax3)");
4099  if (m_verbosity > 5) {
4100  pout() << m_name + "::reconcileParticles(EBAMRCellDatax3)";
4101  }
4102 
4103  CH_assert(a_newParticlesPerCell.getRealm() == m_particleRealm);
4104  CH_assert(a_oldParticlesPerCell.getRealm() == m_particleRealm);
4105  CH_assert(a_newPhotonsPerCell.getRealm() == m_particleRealm);
4106 
4107  for (int lvl = 0; lvl <= m_amr->getFinestLevel(); lvl++) {
4108  this->reconcileParticles(*a_newParticlesPerCell[lvl], *a_oldParticlesPerCell[lvl], *a_newPhotonsPerCell[lvl], lvl);
4109  }
4110 }
4111 
4112 template <typename I, typename C, typename R, typename F>
4113 inline void
4114 ItoKMCStepper<I, C, R, F>::reconcileParticles(const LevelData<EBCellFAB>& a_newParticlesPerCell,
4115  const LevelData<EBCellFAB>& a_oldParticlesPerCell,
4116  const LevelData<EBCellFAB>& a_newPhotonsPerCell,
4117  const int a_level) const noexcept
4118 {
4119  CH_TIME("ItoKMCStepper::reconcileParticles(LevelData<EBCellFAB>x3, int)");
4120  if (m_verbosity > 5) {
4121  pout() << m_name + "::reconcileParticles(LevelData<EBCellFAB>x3, int)" << endl;
4122  }
4123 
4124  const int numItoSpecies = m_physics->getNumItoSpecies();
4125  const int numPhotonSpecies = m_physics->getNumPhotonSpecies();
4126 
4127  CH_assert(a_newParticlesPerCell.nComp() == numItoSpecies);
4128  CH_assert(a_oldParticlesPerCell.nComp() == numItoSpecies);
4129  CH_assert(a_newPhotonsPerCell.nComp() == numPhotonSpecies);
4130 
4131  const DisjointBoxLayout& dbl = m_amr->getGrids(m_particleRealm)[a_level];
4132  const DataIterator& dit = dbl.dataIterator();
4133 
4134  const int nbox = dit.size();
4135 
4136 #pragma omp parallel for schedule(runtime)
4137  for (int mybox = 0; mybox < nbox; mybox++) {
4138  const DataIndex& din = dit[mybox];
4139 
4140  this->reconcileParticles(a_newParticlesPerCell[din],
4141  a_oldParticlesPerCell[din],
4142  a_newPhotonsPerCell[din],
4143  a_level,
4144  din,
4145  dbl[din],
4146  m_amr->getDx()[a_level]);
4147  }
4148 }
4149 
4150 template <typename I, typename C, typename R, typename F>
4151 inline void
4152 ItoKMCStepper<I, C, R, F>::reconcileParticles(const EBCellFAB& a_newParticlesPerCell,
4153  const EBCellFAB& a_oldParticlesPerCell,
4154  const EBCellFAB& a_newPhotonsPerCell,
4155  const int a_level,
4156  const DataIndex a_dit,
4157  const Box a_box,
4158  const Real a_dx) const noexcept
4159 {
4160  CH_TIMERS("ItoKMCStepper::reconcileParticles(patch)");
4161  CH_TIMER("ItoKMCStepper::reconcileParticles(patch)::collect_ptr", t1);
4162  CH_TIMER("ItoKMCStepper::reconcileParticles(patch)::regular_cells", t2);
4163  CH_TIMER("ItoKMCStepper::reconcileParticles(patch)::irregular_cells", t3);
4164  if (m_verbosity > 5) {
4165  pout() << m_name + "::reconcileParticles(patch)" << endl;
4166  }
4167 
4168  // TLDR: This is the main routine for generating new particles/photons after the chemistry advance have finished. We have
4169  // already computed the number of particles in each grid cell, and we now need to generate them. To do that we use
4170  // the reconciliation routines from the physics interface, which takes the per-cell responsibility for that. The main
4171  // work done in this routine is to expose the per-patch data to per-cell data that the physics interface can then use.
4172 
4173  const int numItoSpecies = m_physics->getNumItoSpecies();
4174  const int numCdrSpecies = m_physics->getNumCdrSpecies();
4175  const int numPhotonSpecies = m_physics->getNumPhotonSpecies();
4176 
4177  CH_assert(a_newParticlesPerCell.nComp() == numItoSpecies);
4178  CH_assert(a_oldParticlesPerCell.nComp() == numItoSpecies);
4179  CH_assert(a_newPhotonsPerCell.nComp() == numPhotonSpecies);
4180 
4181  // Geometric information that we need.
4182  const RealVect probLo = m_amr->getProbLo();
4183  const EBISBox& ebisbox = m_amr->getEBISLayout(m_particleRealm, m_plasmaPhase)[a_level][a_dit];
4184 
4185  // Number of computational particles to make on this level
4186  const int ppc = (a_level < m_particlesPerCell.size()) ? m_particlesPerCell[a_level] : m_particlesPerCell.back();
4187 
4188  // List of valid grid cells
4189  CH_START(t1);
4190  const BaseFab<bool>& validCells = (*m_amr->getValidCells(m_particleRealm)[a_level])[a_dit];
4191 
4192  // Pointers to cell-centered particle storages. Note that the BinFabs hold a list of particles/photons
4193  // on each grid cell. We will manipulate these lists.
4194 
4195  Vector<BinFab<ItoParticle>*> itoParticlesFAB(numItoSpecies);
4196  Vector<BinFab<PointParticle>*> cdrParticlesFAB(numCdrSpecies);
4197  Vector<BinFab<Photon>*> sourcePhotonsFAB(numPhotonSpecies);
4198  Vector<BinFab<Photon>*> bulkPhotonsFAB(numPhotonSpecies);
4199 
4200  // Get a handle to the cell-sorted particles from the Ito solvers.
4201  for (auto solverIt = m_ito->iterator(); solverIt.ok(); ++solverIt) {
4202  RefCountedPtr<ItoSolver>& solver = solverIt();
4203 
4204  const int idx = solverIt.index();
4205 
4206  ParticleContainer<ItoParticle>& solverParticles = solver->getParticles(ItoSolver::WhichContainer::Bulk);
4207 
4208  itoParticlesFAB[idx] = &(solverParticles.getCellParticles(a_level, a_dit));
4209  }
4210 
4211  // Get a handle to the cell-sorted particles for CDR photoionization products
4212  for (auto solverIt = m_cdr->iterator(); solverIt.ok(); ++solverIt) {
4213  const int idx = solverIt.index();
4214 
4215  ParticleContainer<PointParticle>& pointParticles = m_cdrPhotoiProducts[idx];
4216 
4217  cdrParticlesFAB[idx] = &(pointParticles.getCellParticles(a_level, a_dit));
4218  }
4219 
4220  // Get a handle to the cell-sorted photons from the Monte Carlo photon solvers.
4221  for (auto solverIt = m_rte->iterator(); solverIt.ok(); ++solverIt) {
4222  RefCountedPtr<McPhoto>& solver = solverIt();
4223 
4224  const int idx = solverIt.index();
4225 
4226  ParticleContainer<Photon>& solverBulkPhotons = solver->getBulkPhotons();
4227  ParticleContainer<Photon>& solverSourcePhotons = solver->getSourcePhotons();
4228 
4229  bulkPhotonsFAB[idx] = &(solverBulkPhotons.getCellParticles(a_level, a_dit));
4230  sourcePhotonsFAB[idx] = &(solverSourcePhotons.getCellParticles(a_level, a_dit));
4231  }
4232 
4233  // The physics interface takes the physical number of particles/photons as arguments
4234  // to the reconciliation routines. These need to be set from the input arguments; this is the
4235  // storage we use in the grid cells.
4236  Vector<Physics::ItoKMC::FPR> numNewParticles(numItoSpecies);
4237  Vector<Physics::ItoKMC::FPR> numOldParticles(numItoSpecies);
4238  Vector<Physics::ItoKMC::FPR> numNewPhotons(numPhotonSpecies);
4239 
4240  // The physics interface also takes the actual particles/photons as argument to its reconciliation routines. This
4241  // is the storage we use for these; note that it is repopulated in every grid cell.
4242  Vector<List<ItoParticle>*> itoParticles(numItoSpecies);
4243  Vector<List<PointParticle>*> cdrParticles(numCdrSpecies);
4244  Vector<List<Photon>*> bulkPhotons(numPhotonSpecies);
4245  Vector<List<Photon>*> sourcePhotons(numPhotonSpecies);
4246  CH_STOP(t1);
4247 
4248  // Regular cells
4249  auto regularKernel = [&](const IntVect& iv) -> void {
4250  if (ebisbox.isRegular(iv) && validCells(iv)) {
4251  const RealVect cellPos = probLo + a_dx * (RealVect(iv) + 0.5 * RealVect::Unit);
4252  const RealVect centroidPos = RealVect::Zero;
4253  const RealVect lo = -0.5 * RealVect::Unit;
4254  const RealVect hi = 0.5 * RealVect::Unit;
4255  const RealVect bndryCentroid = RealVect::Zero;
4256  const RealVect bndryNormal = RealVect::Zero;
4257  const Real kappa = 1.0;
4258 
4259  // Populate the per-cell Ito data
4260  for (int i = 0; i < numItoSpecies; i++) {
4261  itoParticles[i] = &((*itoParticlesFAB[i])(iv, 0));
4262  numNewParticles[i] = (long long)(a_newParticlesPerCell.getSingleValuedFAB()(iv, i));
4263  numOldParticles[i] = (long long)(a_oldParticlesPerCell.getSingleValuedFAB()(iv, i));
4264  }
4265 
4266  // Populate the per-cell CDR data
4267  for (int i = 0; i < numCdrSpecies; i++) {
4268  cdrParticles[i] = &((*cdrParticlesFAB[i])(iv, 0));
4269  }
4270 
4271  // Populate the per-cell photon data.
4272  for (int i = 0; i < numPhotonSpecies; i++) {
4273  bulkPhotons[i] = &((*bulkPhotonsFAB[i])(iv, 0));
4274  sourcePhotons[i] = &((*sourcePhotonsFAB[i])(iv, 0));
4275 
4276  numNewPhotons[i] = (long long)(a_newPhotonsPerCell.getSingleValuedFAB()(iv, i));
4277 
4278  // sourcePhotons will hold the NEW number of photons to be generated -- it should already
4279  // have been cleared in upstream code but I'm leaving this in for safety.
4280  sourcePhotons[i]->clear();
4281  }
4282 
4283  // Reconcile the ItoSolver particles -- this either removes weight from the original particles (if we lost physical particles)
4284  // or adds new particles (if we gained physical particles)
4285  m_physics->reconcileParticles(itoParticles,
4286  numNewParticles,
4287  numOldParticles,
4288  cellPos,
4289  centroidPos,
4290  lo,
4291  hi,
4292  bndryCentroid,
4293  bndryNormal,
4294  a_dx,
4295  kappa);
4296 
4297  // Reconcile the photon solver. This will generate new computational photons that are later added to the Monte Carlo photon
4298  // solvers.
4299  m_physics->reconcilePhotons(sourcePhotons,
4300  numNewPhotons,
4301  cellPos,
4302  centroidPos,
4303  lo,
4304  hi,
4305  bndryCentroid,
4306  bndryNormal,
4307  a_dx,
4308  kappa);
4309 
4310  // Add the photoionization term. This will adds new particles from the photoionization reactions.
4311  m_physics->reconcilePhotoionization(itoParticles, cdrParticles, bulkPhotons);
4312 
4313  // Merge the particles together.
4314  if (this->m_mergeInterval > 0) {
4315  if ((this->m_timeStep + 1) % this->m_mergeInterval == 0) {
4316  for (auto solverIt = m_ito->iterator(); solverIt.ok(); ++solverIt) {
4317  const int idx = solverIt.index();
4318 
4319  solverIt()->mergeParticles(*itoParticles[idx], CellInfo(iv, a_dx), ppc);
4320  }
4321  }
4322  }
4323  }
4324  };
4325 
4326  // Irregular cells
4327  auto irregularKernel = [&](const VolIndex& vof) -> void {
4328  const IntVect iv = vof.gridIndex();
4329  if (ebisbox.isIrregular(iv) && validCells(iv, 0)) {
4330  const Real kappa = ebisbox.volFrac(vof);
4331  const RealVect cellPos = probLo + Location::position(Location::Cell::Center, vof, ebisbox, a_dx);
4332  const RealVect centroidPos = ebisbox.centroid(vof);
4333  const RealVect bndryCentroid = ebisbox.bndryCentroid(vof);
4334  const RealVect bndryNormal = ebisbox.normal(vof);
4335 
4336  // Compute the minimum bounding box that encloses this cut-cell.
4337  RealVect lo = -0.5 * RealVect::Unit;
4338  RealVect hi = 0.5 * RealVect::Unit;
4339  if (kappa < 1.0) {
4340  DataOps::computeMinValidBox(lo, hi, bndryNormal, bndryCentroid);
4341  }
4342 
4343  // Populate the per-cell particle data.
4344  for (int i = 0; i < numItoSpecies; i++) {
4345  itoParticles[i] = &((*itoParticlesFAB[i])(iv, 0));
4346  numNewParticles[i] = (long long)(a_newParticlesPerCell(vof, i));
4347  numOldParticles[i] = (long long)(a_oldParticlesPerCell(vof, i));
4348  }
4349 
4350  // Populate the per-cell CDR data
4351  for (int i = 0; i < numCdrSpecies; i++) {
4352  cdrParticles[i] = &((*cdrParticlesFAB[i])(iv, 0));
4353  }
4354 
4355  // Populate the per-cell photon data.
4356  for (int i = 0; i < numPhotonSpecies; i++) {
4357  bulkPhotons[i] = &((*bulkPhotonsFAB[i])(iv, 0));
4358  sourcePhotons[i] = &((*sourcePhotonsFAB[i])(iv, 0));
4359 
4360  numNewPhotons[i] = (long long)(a_newPhotonsPerCell(vof, i));
4361 
4362  // sourcePhotons will hold the NEW number of photons to be generated -- it should already
4363  // have been cleared in upstream code but I'm leaving this in for safety.
4364  sourcePhotons[i]->clear();
4365  }
4366 
4367  // Reconcile the ItoSolver particles -- this either removes weight from the original particles (if we lost physical particles)
4368  // or adds new particles (if we gained physical particles)
4369  m_physics->reconcileParticles(itoParticles,
4370  numNewParticles,
4371  numOldParticles,
4372  cellPos,
4373  centroidPos,
4374  lo,
4375  hi,
4376  bndryCentroid,
4377  bndryNormal,
4378  a_dx,
4379  kappa);
4380 
4381  // Reconcile the photon solver. This will generate new computational photons that are later added to the Monte Carlo photon
4382  // solvers.
4383  m_physics->reconcilePhotons(sourcePhotons,
4384  numNewPhotons,
4385  cellPos,
4386  centroidPos,
4387  lo,
4388  hi,
4389  bndryCentroid,
4390  bndryNormal,
4391  a_dx,
4392  kappa);
4393 
4394  // Add the photoionization term. This will adds new particles from the photoionization reactions.
4395  m_physics->reconcilePhotoionization(itoParticles, cdrParticles, bulkPhotons);
4396 
4397  // Merge the particles together.
4398  if (this->m_mergeInterval > 0) {
4399  if ((this->m_timeStep + 1) % this->m_mergeInterval == 0) {
4400  for (auto solverIt = m_ito->iterator(); solverIt.ok(); ++solverIt) {
4401  const int idx = solverIt.index();
4402 
4403  const CellInfo cellInfo(iv, a_dx, kappa, bndryCentroid, bndryNormal);
4404 
4405  solverIt()->mergeParticles(*itoParticles[idx], cellInfo, ppc);
4406  }
4407  }
4408  }
4409  }
4410  };
4411 
4412  // Run the kernels.
4413  VoFIterator& vofit = (*m_amr->getVofIterator(m_particleRealm, m_plasmaPhase)[a_level])[a_dit];
4414 
4415  CH_START(t2);
4416  BoxLoops::loop(a_box, regularKernel);
4417  CH_STOP(t2);
4418 
4419  CH_START(t3);
4420  BoxLoops::loop(vofit, irregularKernel);
4421  CH_STOP(t3);
4422 }
4423 
4424 template <typename I, typename C, typename R, typename F>
4425 void
4427 {
4428  CH_TIME("ItoKMCStepper::reconcilePhotoionization()");
4429  if (m_verbosity > 5) {
4430  pout() << m_name + "::reconcilePhotoionization()" << endl;
4431  }
4432 
4433  const int numItoSpecies = m_physics->getNumItoSpecies();
4434  const int numCdrSpecies = m_physics->getNumCdrSpecies();
4435  const int numPhotonSpecies = m_physics->getNumPhotonSpecies();
4436 
4437  for (int lvl = 0; lvl <= m_amr->getFinestLevel(); lvl++) {
4438  const DisjointBoxLayout& dbl = m_amr->getGrids(m_particleRealm)[lvl];
4439  const DataIterator& dit = dbl.dataIterator();
4440 
4441  const int nbox = dit.size();
4442 
4443 #pragma omp parallel for schedule(runtime)
4444  for (int mybox = 0; mybox < nbox; mybox++) {
4445  const DataIndex& din = dit[mybox];
4446 
4447  Vector<List<ItoParticle>*> itoParticles(numItoSpecies);
4448  Vector<List<PointParticle>*> cdrParticles(numCdrSpecies);
4449  Vector<List<Photon>*> photonParticles(numPhotonSpecies);
4450 
4451  for (auto solverIt = m_ito->iterator(); solverIt.ok(); ++solverIt) {
4452  ParticleContainer<ItoParticle>& particles = solverIt()->getParticles(ItoSolver::WhichContainer::Bulk);
4453 
4454  itoParticles[solverIt.index()] = &(particles[lvl][din].listItems());
4455  }
4456 
4457  for (auto solverIt = m_cdr->iterator(); solverIt.ok(); ++solverIt) {
4458  ParticleContainer<PointParticle>& particles = m_cdrPhotoiProducts[solverIt.index()];
4459 
4460  cdrParticles[solverIt.index()] = &(particles[lvl][din].listItems());
4461  }
4462 
4463  for (auto solverIt = m_rte->iterator(); solverIt.ok(); ++solverIt) {
4464  ParticleContainer<Photon>& particles = solverIt()->getBulkPhotons();
4465 
4466  photonParticles[solverIt.index()] = &(particles[lvl][din].listItems());
4467  }
4468 
4469  m_physics->reconcilePhotoionization(itoParticles, cdrParticles, photonParticles);
4470  }
4471  }
4472 }
4473 
4474 template <typename I, typename C, typename R, typename F>
4475 void
4477  const EBAMRCellData& a_oldParticlesPerCell,
4478  const Real a_dt) noexcept
4479 {
4480  CH_TIME("ItoKMCStepper::reconcileCdrDensities(EBAMRCellDatax2, Real)");
4481  if (m_verbosity > 5) {
4482  pout() << m_name + "::reconcileCdrDensities(EBAMRCellDatax2, Real)" << endl;
4483  }
4484 
4485  const int numCdrSpecies = m_physics->getNumCdrSpecies();
4486 
4487  CH_assert(a_newParticlesPerCell.getRealm() == m_fluidRealm);
4488  CH_assert(a_oldParticlesPerCell.getRealm() == m_fluidRealm);
4489  CH_assert(a_dt > 0.0);
4490 
4491  if (numCdrSpecies > 0) {
4492 
4493  // Increment, but don't divide by kappa.
4494  for (int lvl = 0; lvl <= m_amr->getFinestLevel(); lvl++) {
4495  this->reconcileCdrDensities(*a_newParticlesPerCell[lvl], *a_oldParticlesPerCell[lvl], lvl, a_dt);
4496  }
4497 
4498  // Redistribute if user calls for it.
4499  if (m_redistributeCDR) {
4500  for (auto it = m_cdr->iterator(); it.ok(); ++it) {
4501  const int idx = it.index();
4502 
4503  const EBAMRCellData newPPC = m_amr->slice(a_newParticlesPerCell, Interval(idx, idx));
4504  const EBAMRCellData oldPPC = m_amr->slice(a_oldParticlesPerCell, Interval(idx, idx));
4505 
4506  for (int lvl = 0; lvl <= m_amr->getFinestLevel(); lvl++) {
4507  const DisjointBoxLayout& dbl = m_amr->getGrids(m_fluidRealm)[lvl];
4508  const DataIterator& dit = dbl.dataIterator();
4509  const EBISLayout& ebisl = m_amr->getEBISLayout(m_fluidRealm, m_plasmaPhase)[lvl];
4510  const Real dx = m_amr->getDx()[lvl];
4511 
4512  const int nbox = dit.size();
4513 
4514 #pragma omp parallel for schedule(runtime)
4515  for (int mybox = 0; mybox < nbox; mybox++) {
4516  const DataIndex& din = dit[mybox];
4517  const EBISBox& ebisbox = ebisl[din];
4518 
4519  BaseIVFAB<Real>& deltaMass = (*m_fluidScratchEB[lvl])[din];
4520 
4521  deltaMass.setVal(0.0);
4522 
4523  const EBCellFAB& newPPC = (*a_newParticlesPerCell[lvl])[din];
4524  const EBCellFAB& oldPPC = (*a_oldParticlesPerCell[lvl])[din];
4525 
4526  auto kernel = [&](const VolIndex& vof) -> void {
4527  const Real kappa = ebisbox.volFrac(vof);
4528 
4529  deltaMass(vof, 0) = (newPPC(vof, idx) - oldPPC(vof, idx)) * (1.0 - kappa) / std::pow(dx, SpaceDim);
4530  };
4531 
4532  VoFIterator& vofit = (*m_amr->getVofIterator(m_fluidRealm, m_plasmaPhase)[lvl])[din];
4533 
4534  BoxLoops::loop(vofit, kernel);
4535  }
4536  }
4537 
4538  const RefCountedPtr<CdrSolver>& solver = it();
4539 
4540  solver->redistribute(solver->getPhi(), m_fluidScratchEB);
4541  }
4542  }
4543 
4544  this->coarsenCDRSolvers();
4545  }
4546 }
4547 
4548 template <typename I, typename C, typename R, typename F>
4549 void
4550 ItoKMCStepper<I, C, R, F>::reconcileCdrDensities(const LevelData<EBCellFAB>& a_newParticlesPerCell,
4551  const LevelData<EBCellFAB>& a_oldParticlesPerCell,
4552  const int a_level,
4553  const Real a_dt) noexcept
4554 {
4555  CH_TIME("ItoKMCStepper::reconcileCdrDensities(LD<EBCellFAB>x2, int, Real)");
4556  if (m_verbosity > 5) {
4557  pout() << m_name + "::reconcileCdrDensities(LD<EBCellFAB>x2, int, Real)" << endl;
4558  }
4559 
4560  const int numCdrSpecies = m_physics->getNumCdrSpecies();
4561 
4562  CH_assert(a_newParticlesPerCell.nComp() == numCdrSpecies);
4563  CH_assert(a_oldParticlesPerCell.nComp() == numCdrSpecies);
4564 
4565  const DisjointBoxLayout& dbl = m_amr->getGrids(m_fluidRealm)[a_level];
4566  const DataIterator& dit = dbl.dataIterator();
4567  const Real dx = m_amr->getDx()[a_level];
4568 
4569  const int nbox = dit.size();
4570 
4571 #pragma omp parallel for schedule(runtime)
4572  for (int mybox = 0; mybox < nbox; mybox++) {
4573  const DataIndex& din = dit[mybox];
4574 
4575  this
4576  ->reconcileCdrDensities(a_newParticlesPerCell[din], a_oldParticlesPerCell[din], a_level, din, dbl[din], dx, a_dt);
4577  }
4578 }
4579 
4580 template <typename I, typename C, typename R, typename F>
4581 void
4582 ItoKMCStepper<I, C, R, F>::reconcileCdrDensities(const EBCellFAB& a_newParticlesPerCell,
4583  const EBCellFAB& a_oldParticlesPerCell,
4584  const int a_level,
4585  const DataIndex a_dit,
4586  const Box a_box,
4587  const Real a_dx,
4588  const Real a_dt) noexcept
4589 {
4590  CH_TIME("ItoKMCStepper::reconcileCdrDensities(EBCellFABx2, int, DataIndex, Box, Realx2)");
4591  if (m_verbosity > 5) {
4592  pout() << m_name + "::reconcileCdrDensities(EBCellFABx2, int, DataIndex, Box, Realx2)" << endl;
4593  }
4594 
4595  const int numCdrSpecies = m_physics->getNumCdrSpecies();
4596 
4597  CH_assert(a_newParticlesPerCell.nComp() == numCdrSpecies);
4598  CH_assert(a_oldParticlesPerCell.nComp() == numCdrSpecies);
4599 
4600  const Real volume = std::pow(a_dx, SpaceDim);
4601 
4602  for (auto solverIt = m_cdr->iterator(); solverIt.ok(); ++solverIt) {
4603  RefCountedPtr<CdrSolver>& solver = solverIt();
4604  const int index = solverIt.index();
4605 
4606  EBCellFAB& phi = (*(solver->getPhi()[a_level]))[a_dit];
4607  EBCellFAB& src = (*(solver->getSource()[a_level]))[a_dit];
4608 
4609  // Source = (newParticles - oldParticles)/volume
4610  src.setVal(0.0);
4611  src.plus(a_newParticlesPerCell, index, 0, 1);
4612  src.minus(a_oldParticlesPerCell, index, 0, 1);
4613  src /= volume;
4614 
4615  // Phi += (newParticles - oldParticles)/volume
4616  phi += src;
4617 
4618  // Source = (newParticles - oldParticles)/(volume*dt)
4619  src /= a_dt;
4620  }
4621 }
4622 
4623 template <typename I, typename C, typename R, typename F>
4624 void
4626 {
4627  CH_TIME("ItoKMCStepper::coarsenCDRSolvers");
4628  if (m_verbosity > 5) {
4629  pout() << m_name + "::coarsenCDRSolvers" << endl;
4630  }
4631 
4632  for (auto solverIt = this->m_cdr->iterator(); solverIt.ok(); ++solverIt) {
4633  auto& solver = solverIt();
4634 
4635  EBAMRCellData& phi = solver->getPhi();
4636  EBAMRCellData& src = solver->getSource();
4637 
4638  this->m_amr->conservativeAverage(phi, phi.getRealm(), this->m_plasmaPhase);
4639  this->m_amr->conservativeAverage(src, src.getRealm(), this->m_plasmaPhase);
4640 
4641  this->m_amr->interpGhostPwl(phi, phi.getRealm(), this->m_plasmaPhase);
4642  this->m_amr->interpGhostPwl(src, src.getRealm(), this->m_plasmaPhase);
4643 
4644  DataOps::setCoveredValue(phi, 0.0);
4645  DataOps::setCoveredValue(src, 0.0);
4646  }
4647 }
4648 
4649 template <typename I, typename C, typename R, typename F>
4650 void
4652 {
4653  CH_TIME("ItoKMCStepper::fillSecondaryEmissionEB(Real)");
4654  if (m_verbosity > 5) {
4655  pout() << m_name + "::fillSecondaryEmissionEB(Real)" << endl;
4656  }
4657 
4658  // Particles that left the domain
4659  Vector<ParticleContainer<ItoParticle>*> primaryParticles;
4660  for (auto it = m_ito->iterator(); it.ok(); ++it) {
4661  ParticleContainer<ItoParticle>& intersectedParticles = it()->getParticles(ItoSolver::WhichContainer::EB);
4662 
4663  primaryParticles.push_back(&intersectedParticles);
4664  }
4665 
4666  // CDR solvers extrapolate their fluxes. We then copy the extrapolated fluxes to transient data holders (which are defined over the particle realm).
4667  EBAMRIVData tmp;
4668  m_amr->allocate(tmp, m_fluidRealm, m_plasmaPhase, 1);
4669 
4670  for (auto solverIt = m_cdr->iterator(); solverIt.ok(); ++solverIt) {
4671  const int idx = solverIt.index();
4672  const RefCountedPtr<CdrSolver>& solver = solverIt();
4673 
4674  EBAMRIVData& extrapFlux = m_cdrFluxesExtrap[idx];
4675 
4676  if (solver->isMobile()) {
4677  solver->extrapolateAdvectiveFluxToEB(tmp);
4678 
4679  m_amr->copyData(extrapFlux, tmp);
4680  }
4681  else {
4682  DataOps::setValue(extrapFlux, 0.0);
4683  }
4684  }
4685 
4686  // Photons that left the domain
4687  Vector<ParticleContainer<Photon>*> primaryPhotons;
4688  for (auto it = m_rte->iterator(); it.ok(); ++it) {
4689  ParticleContainer<Photon>& intersectedPhotons = it()->getEbPhotons();
4690 
4691  primaryPhotons.push_back(&intersectedPhotons);
4692  }
4693 
4694  // Call the other version.
4695  this->fillSecondaryEmissionEB(m_secondaryParticles,
4696  m_cdrFluxes,
4697  m_secondaryPhotons,
4698  primaryParticles,
4699  m_cdrFluxesExtrap,
4700  primaryPhotons,
4701  m_electricFieldParticle,
4702  a_dt);
4703 }
4704 
4705 template <typename I, typename C, typename R, typename F>
4706 void
4708  Vector<EBAMRIVData>& a_cdrFluxes,
4709  Vector<ParticleContainer<Photon>>& a_secondaryPhotons,
4710  Vector<ParticleContainer<ItoParticle>*>& a_primaryParticles,
4711  Vector<EBAMRIVData>& a_cdrFluxesExtrap,
4712  Vector<ParticleContainer<Photon>*>& a_primaryPhotons,
4713  const EBAMRCellData& a_electricField,
4714  const Real a_dt) noexcept
4715 {
4716  CH_TIME("ItoKMCStepper::fillSecondaryEmissionEB(full)");
4717  if (m_verbosity > 5) {
4718  pout() << m_name + "::fillSecondaryEmissionEB(full)" << endl;
4719  }
4720 
4721  const int numItoSpecies = m_physics->getNumItoSpecies();
4722  const int numCdrSpecies = m_physics->getNumCdrSpecies();
4723  const int numPhotonSpecies = m_physics->getNumPhotonSpecies();
4724 
4725  CH_assert(a_secondaryParticles.size() == numItoSpecies);
4726  CH_assert(a_cdrFluxes.size() == numCdrSpecies);
4727  CH_assert(a_secondaryPhotons.size() == numPhotonSpecies);
4728  CH_assert(a_primaryParticles.size() == numItoSpecies);
4729  CH_assert(a_cdrFluxesExtrap.size() == numCdrSpecies);
4730  CH_assert(a_primaryPhotons.size() == numPhotonSpecies);
4731  CH_assert(a_electricField.getRealm() == m_particleRealm);
4732  CH_assert(a_dt >= 0.0);
4733 
4734  // Incoming/outgoing particle containers must be sorted by cell.
4735  for (int i = 0; i < numItoSpecies; i++) {
4736  CH_assert(a_secondaryParticles[i].getRealm() == m_particleRealm);
4737  CH_assert(a_primaryParticles[i]->getRealm() == m_particleRealm);
4738 
4739  a_secondaryParticles[i].clearParticles();
4740  a_secondaryParticles[i].organizeParticlesByCell();
4741 
4742  a_primaryParticles[i]->organizeParticlesByCell();
4743  }
4744 
4745  for (int i = 0; i < numCdrSpecies; i++) {
4746  CH_assert(a_cdrFluxes[i].getRealm() == m_particleRealm);
4747  CH_assert(a_cdrFluxesExtrap[i].getRealm() == m_particleRealm);
4748 
4749  DataOps::setValue(a_cdrFluxes[i], 0.0);
4750  }
4751 
4752  // Incoming/outgoing photon containers by be sorted by cell
4753  for (int i = 0; i < numPhotonSpecies; i++) {
4754  CH_assert(a_secondaryPhotons[i].getRealm() == m_particleRealm);
4755  CH_assert(a_primaryPhotons[i]->getRealm() == m_particleRealm);
4756 
4757  a_secondaryPhotons[i].clearParticles();
4758 
4759  a_secondaryPhotons[i].organizeParticlesByCell();
4760  a_primaryPhotons[i]->organizeParticlesByCell();
4761  }
4762 
4763  const RealVect probLo = m_amr->getProbLo();
4764 
4765  const Vector<Electrode>& electrodes = m_computationalGeometry->getElectrodes();
4766  const Vector<Dielectric>& dielectrics = m_computationalGeometry->getDielectrics();
4767 
4768  for (int lvl = 0; lvl <= m_amr->getFinestLevel(); lvl++) {
4769  const DisjointBoxLayout& dbl = m_amr->getGrids(m_particleRealm)[lvl];
4770  const DataIterator& dit = dbl.dataIterator();
4771  const EBISLayout& ebisl = m_amr->getEBISLayout(m_particleRealm, m_plasmaPhase)[lvl];
4772  const Real dx = m_amr->getDx()[lvl];
4773 
4774  const int nbox = dit.size();
4775 
4776 #pragma omp parallel for schedule(runtime)
4777  for (int mybox = 0; mybox < nbox; mybox++) {
4778  const DataIndex& din = dit[mybox];
4779 
4780  const EBISBox& ebisbox = ebisl[din];
4781  const EBCellFAB& electricField = (*a_electricField[lvl])[din];
4782  const BaseFab<bool>& validCells = (*m_amr->getValidCells(m_particleRealm)[lvl])[din];
4783 
4784  bool isDielectric = false;
4785 
4786  Vector<BinFab<ItoParticle>*> secondaryParticlesFAB;
4787  Vector<BinFab<ItoParticle>*> primaryParticlesFAB;
4788 
4789  Vector<BinFab<Photon>*> secondaryPhotonsFAB;
4790  Vector<BinFab<Photon>*> primaryPhotonsFAB;
4791 
4792  Vector<BaseIVFAB<Real>*> cdrFluxesFAB;
4793  Vector<BaseIVFAB<Real>*> cdrFluxesExtrapFAB;
4794 
4795  for (auto it = m_ito->iterator(); it.ok(); ++it) {
4796  secondaryParticlesFAB.push_back(&(a_secondaryParticles[it.index()].getCellParticles(lvl)[din]));
4797  primaryParticlesFAB.push_back(&(a_primaryParticles[it.index()]->getCellParticles(lvl)[din]));
4798  }
4799 
4800  for (auto it = m_cdr->iterator(); it.ok(); ++it) {
4801  cdrFluxesFAB.push_back(&((*(a_cdrFluxes[it.index()])[lvl])[din]));
4802  cdrFluxesExtrapFAB.push_back(&((*(a_cdrFluxesExtrap[it.index()])[lvl])[din]));
4803  }
4804 
4805  for (auto it = m_rte->iterator(); it.ok(); ++it) {
4806  secondaryPhotonsFAB.push_back(&(a_secondaryPhotons[it.index()].getCellParticles(lvl)[din]));
4807  primaryPhotonsFAB.push_back(&(a_primaryPhotons[it.index()]->getCellParticles(lvl)[din]));
4808  }
4809 
4810  // Kernel definition.
4811  auto irregularKernel = [&](const VolIndex& vof) -> void {
4812  const IntVect iv = vof.gridIndex();
4813 
4814  if (validCells(iv)) {
4815  const RealVect E = RealVect(D_DECL(electricField(vof, 0), electricField(vof, 1), electricField(vof, 2)));
4816  const RealVect bndryNormal = ebisbox.normal(vof);
4817  const RealVect bndryCentroid = ebisbox.bndryCentroid(vof);
4818  const RealVect cellCentroid = ebisbox.centroid(vof);
4819  const RealVect cellCenter = probLo + Location::position(Location::Cell::Center, vof, ebisbox, dx);
4820  const RealVect physPos = cellCenter + bndryCentroid * dx;
4821  const Real bndryArea = ebisbox.bndryArea(vof);
4822 
4823  Vector<List<ItoParticle>> secondaryParticles(numItoSpecies);
4824  Vector<List<ItoParticle>> primaryParticles(numItoSpecies);
4825 
4826  Vector<Real> cdrFluxes(numCdrSpecies, 0.0);
4827  Vector<Real> cdrFluxesExtrap(numCdrSpecies, 0.0);
4828 
4829  Vector<List<Photon>> secondaryPhotons(numPhotonSpecies);
4830  Vector<List<Photon>> primaryPhotons(numPhotonSpecies);
4831 
4832  for (int i = 0; i < numItoSpecies; i++) {
4833  secondaryParticles[i] = (*secondaryParticlesFAB[i])(iv, 0);
4834  primaryParticles[i] = (*primaryParticlesFAB[i])(iv, 0);
4835  }
4836 
4837  // Populate CDR fluxes
4838  for (int i = 0; i < numCdrSpecies; i++) {
4839  cdrFluxes[i] = 0.0;
4840  cdrFluxesExtrap[i] = (*cdrFluxesExtrapFAB[i])(vof, 0);
4841  }
4842 
4843  for (int i = 0; i < numPhotonSpecies; i++) {
4844  secondaryPhotons[i] = (*secondaryPhotonsFAB[i])(iv, 0);
4845  primaryPhotons[i] = (*primaryPhotonsFAB[i])(iv, 0);
4846  }
4847 
4848  // Figure out which material we are dealing with.
4849  int matIndex = -1;
4850  Real minDist = std::numeric_limits<Real>::max();
4851 
4852  for (int i = 0; i < electrodes.size(); i++) {
4853  const Real curDist = electrodes[i].getImplicitFunction()->value(physPos);
4854 
4855  if (std::abs(curDist) < std::abs(minDist)) {
4856  minDist = curDist;
4857  matIndex = i;
4858  }
4859  }
4860 
4861  for (int i = 0; i < dielectrics.size(); i++) {
4862  const Real curDist = dielectrics[i].getImplicitFunction()->value(physPos);
4863 
4864  if (std::abs(curDist) < std::abs(minDist)) {
4865  minDist = curDist;
4866  matIndex = i;
4867  isDielectric = true;
4868  }
4869  }
4870 
4871  // Call the physics framework.
4872  m_physics->secondaryEmissionEB(secondaryParticles,
4873  cdrFluxes,
4874  secondaryPhotons,
4875  primaryParticles,
4876  cdrFluxesExtrap,
4877  primaryPhotons,
4878  E,
4879  cellCenter,
4880  cellCentroid,
4881  bndryCentroid,
4882  bndryNormal,
4883  bndryArea,
4884  dx,
4885  a_dt,
4886  isDielectric,
4887  matIndex);
4888 
4889  // Fill output data holders.
4890  for (int i = 0; i < numItoSpecies; i++) {
4891  (*secondaryParticlesFAB[i])(iv, 0) = secondaryParticles[i];
4892  }
4893 
4894  for (int i = 0; i < numCdrSpecies; i++) {
4895  (*cdrFluxesFAB[i])(vof, 0) = cdrFluxes[i];
4896  }
4897 
4898  for (int i = 0; i < numPhotonSpecies; i++) {
4899  (*secondaryPhotonsFAB[i])(iv, 0) = secondaryPhotons[i];
4900  }
4901  }
4902  };
4903 
4904  // Run the kernel.
4905  VoFIterator& vofit = (*m_amr->getVofIterator(m_particleRealm, m_plasmaPhase)[lvl])[din];
4906  BoxLoops::loop(vofit, irregularKernel);
4907  }
4908  }
4909 
4910  // Sort by patch
4911  for (int i = 0; i < numItoSpecies; i++) {
4912  a_secondaryParticles[i].organizeParticlesByPatch();
4913  a_primaryParticles[i]->organizeParticlesByPatch();
4914  }
4915 
4916  for (int i = 0; i < numPhotonSpecies; i++) {
4917  a_secondaryPhotons[i].organizeParticlesByPatch();
4918  a_primaryPhotons[i]->organizeParticlesByPatch();
4919  }
4920 }
4921 
4922 template <typename I, typename C, typename R, typename F>
4923 void
4925 {
4926  CH_TIME("ItoKMCStepper::resolveSecondaryEmissionEB(short)");
4927  if (m_verbosity > 5) {
4928  pout() << m_name + "::resolveSecondaryEmissionEB(short)" << endl;
4929  }
4930 
4931  Vector<ParticleContainer<ItoParticle>*> secondaryParticles;
4932  Vector<ParticleContainer<ItoParticle>*> primaryParticles;
4933 
4934  for (auto it = m_ito->iterator(); it.ok(); ++it) {
4935  const int idx = it.index();
4936 
4937  primaryParticles.push_back(&(it()->getParticles(ItoSolver::WhichContainer::EB)));
4938  secondaryParticles.push_back(&(m_secondaryParticles[idx]));
4939  }
4940 
4941  // Copy the CDR fluxes on the particle realm over to the fluid realm.
4942  Vector<EBAMRIVData*> cdrFluxes;
4943  for (auto it = m_cdr->iterator(); it.ok(); ++it) {
4944  const RefCountedPtr<CdrSolver>& solver = it();
4945  const int idx = it.index();
4946 
4947  EBAMRIVData& ebFlux = solver->getEbFlux();
4948 
4949  m_amr->copyData(ebFlux, m_cdrFluxes[idx]);
4950 
4951  m_amr->arithmeticAverage(ebFlux, m_fluidRealm, m_plasmaPhase);
4952 
4953  cdrFluxes.push_back(&ebFlux);
4954  }
4955 
4956  // Handle to surface charge density.
4957  EBAMRIVData& surfaceChargeDensity = m_sigmaSolver->getPhi();
4958 
4959  this->resolveSecondaryEmissionEB(secondaryParticles, primaryParticles, cdrFluxes, surfaceChargeDensity, a_dt);
4960 
4961  m_sigmaSolver->resetElectrodes(0.0);
4962  m_amr->arithmeticAverage(surfaceChargeDensity, m_fluidRealm, m_plasmaPhase);
4963 }
4964 
4965 template <typename I, typename C, typename R, typename F>
4966 void
4968  Vector<ParticleContainer<ItoParticle>*>& a_primaryParticles,
4969  Vector<EBAMRIVData*>& a_cdrFluxes,
4970  EBAMRIVData& a_surfaceChargeDensity,
4971  const Real a_dt) noexcept
4972 {
4973  CH_TIME("ItoKMCStepper::resolveSecondaryEmissionEB(full)");
4974  if (m_verbosity > 5) {
4975  pout() << m_name + "::resolveSecondaryEmissionEB(full)" << endl;
4976  }
4977 
4978  const int numItoSpecies = m_physics->getNumItoSpecies();
4979  const int numCdrSpecies = m_physics->getNumCdrSpecies();
4980 
4981  CH_assert(a_secondaryParticles.size() == numItoSpecies);
4982  CH_assert(a_primaryParticles.size() == numItoSpecies);
4983  CH_assert(a_cdrFluxes.size() == numCdrSpecies);
4984  CH_assert(a_surfaceChargeDensity.getRealm() == m_fluidRealm);
4985 
4986  for (int i = 0; i < numItoSpecies; i++) {
4987  CH_assert(a_secondaryParticles[i]->getRealm() == m_particleRealm);
4988  CH_assert(a_primaryParticles[i]->getRealm() == m_particleRealm);
4989  }
4990 
4991  for (int i = 0; i < numCdrSpecies; i++) {
4992  CH_assert(a_secondaryParticles[i]->getRealm() == m_particleRealm);
4993  CH_assert(a_primaryParticles[i]->getRealm() == m_particleRealm);
4994  }
4995 
4996  // Deposit the incoming/outgoing particles on the surface and update the surface charge density.
4997  for (auto it = m_ito->iterator(); it.ok(); ++it) {
4998  const RefCountedPtr<ItoSolver>& solver = it();
4999  const RefCountedPtr<ItoSpecies>& species = solver->getSpecies();
5000 
5001  const int idx = it.index();
5002  const int Z = species->getChargeNumber();
5003 
5004  if (Z != 0) {
5005 
5006  // Add charge from primary particles
5007  m_amr->depositParticles<ItoParticle, &ItoParticle::weight>(m_particleScratchEB,
5008  m_particleRealm,
5009  m_plasmaPhase,
5010  *a_primaryParticles[idx]);
5011 
5012  m_amr->copyData(m_fluidScratchEB, m_particleScratchEB);
5013  DataOps::incr(a_surfaceChargeDensity, m_fluidScratchEB, 1.0 * Z * Units::Qe);
5014 
5015  // Subtract charge from secondary particles
5016  m_amr->depositParticles<ItoParticle, &ItoParticle::weight>(m_particleScratchEB,
5017  m_particleRealm,
5018  m_plasmaPhase,
5019  *a_secondaryParticles[idx]);
5020 
5021  m_amr->copyData(m_fluidScratchEB, m_particleScratchEB);
5022  DataOps::incr(a_surfaceChargeDensity, m_fluidScratchEB, -1.0 * Z * Units::Qe);
5023  }
5024 
5025  // Add the secondary particles into the solvers and remove the primary particles.
5026  ParticleContainer<ItoParticle>& particles = solver->getParticles(ItoSolver::WhichContainer::Bulk);
5027  particles.transferParticles(*a_secondaryParticles[idx]);
5028 
5029  a_primaryParticles[idx]->clearParticles();
5030 
5031  if (a_secondaryParticles[idx]->getNumberOfValidParticlesGlobal() > 0) {
5032  MayDay::Abort("logic bust");
5033  }
5034  }
5035 
5036  // Add CDR fluxes to the CDR solvers
5037  for (auto it = m_cdr->iterator(); it.ok(); ++it) {
5038  const RefCountedPtr<CdrSolver>& solver = it();
5039  const RefCountedPtr<CdrSpecies>& species = solver->getSpecies();
5040 
5041  const int idx = it.index();
5042  const int Z = species->getChargeNumber();
5043 
5044  if (Z != 0) {
5045  DataOps::incr(a_surfaceChargeDensity, *a_cdrFluxes[idx], Z * a_dt * Units::Qe);
5046  }
5047 
5048  // Add mass to CDR solvers -- this is an inefficient way of doing it but I don't know if it'll be a performance bottleneck as well.
5049  EBAMRCellData divG;
5050  EBAMRFluxData G;
5051 
5052  m_amr->allocate(divG, m_fluidRealm, m_plasmaPhase, 1);
5053  m_amr->allocate(G, m_fluidRealm, m_plasmaPhase, 1);
5054 
5055  DataOps::setValue(G, 0.0);
5056 
5057  solver->computeDivG(divG, G, *a_cdrFluxes[idx], false);
5058 
5059  EBAMRCellData& phi = solver->getPhi();
5060  DataOps::incr(phi, divG, -a_dt);
5061 
5062  m_amr->conservativeAverage(phi, m_fluidRealm, m_plasmaPhase);
5063  m_amr->interpGhostPwl(phi, m_fluidRealm, m_plasmaPhase);
5064 
5065  // Really don't want negative densities.
5066  DataOps::floor(phi, 0.0);
5067  }
5068 
5069  // Conservatively coarsen the surface charge density.
5070  m_amr->conservativeAverage(a_surfaceChargeDensity, m_fluidRealm, m_plasmaPhase);
5071 }
5072 
5073 template <typename I, typename C, typename R, typename F>
5074 Real
5076 {
5077  CH_TIME("ItoKMCStepper::computePhysicsDt()");
5078  if (m_verbosity > 5) {
5079  pout() << m_name + "::computePhysicsDt()" << endl;
5080  }
5081 
5082  const Real dt = this->computePhysicsDt(m_electricFieldFluid);
5083 
5084  return dt;
5085 }
5086 
5087 template <typename I, typename C, typename R, typename F>
5088 Real
5090 {
5091  CH_TIME("ItoKMCStepper::computePhysicsDt(EBAMRCellFAB, Vector<EBAMRCellFAB*>)");
5092  if (m_verbosity > 5) {
5093  pout() << m_name + "::computePhysicsDt(EBAMRCellFAB, Vector<EBAMRCellFAB*>)" << endl;
5094  }
5095 
5096  CH_assert(a_electricField.getRealm() == m_fluidRealm);
5097 
5098  const int numItoSpecies = m_physics->getNumItoSpecies();
5099  const int numCdrSpecies = m_physics->getNumCdrSpecies();
5100 
5101  Real minDt = std::numeric_limits<Real>::max();
5102 
5103  // Sort by cell so we can compute the number of particles.
5104  m_ito->organizeParticlesByCell(ItoSolver::WhichContainer::Bulk);
5105 
5106  // Compute the number of reactive particles for both Ito and CDR species.
5107  if (numItoSpecies > 0) {
5108  this->computeReactiveItoParticlesPerCell(m_particleItoPPC);
5109 
5110  const Interval srcInterv(0, numItoSpecies - 1);
5111  const Interval dstInterv(0, numItoSpecies - 1);
5112 
5113  m_amr->copyData(m_fluidPPC, m_particleItoPPC, dstInterv, srcInterv);
5114  }
5115  if (numCdrSpecies > 0) {
5116  this->computeReactiveCdrParticlesPerCell(m_fluidCdrPPC);
5117 
5118  const Interval srcInterv(0, numCdrSpecies - 1);
5119  const Interval dstInterv(numItoSpecies, numItoSpecies + numCdrSpecies - 1);
5120 
5121  m_amr->copyData(m_fluidPPC, m_fluidCdrPPC, dstInterv, srcInterv);
5122  }
5123 
5124  for (int lvl = 0; lvl <= m_amr->getFinestLevel(); lvl++) {
5125  const Real levelDt = this->computePhysicsDt(*a_electricField[lvl], *m_fluidPPC[lvl], lvl);
5126 
5127  minDt = std::min(minDt, levelDt);
5128  }
5129 
5130  // Sort by patch.
5131  m_ito->organizeParticlesByPatch(ItoSolver::WhichContainer::Bulk);
5132 
5133  return minDt;
5134 }
5135 
5136 template <typename I, typename C, typename R, typename F>
5137 Real
5138 ItoKMCStepper<I, C, R, F>::computePhysicsDt(const LevelData<EBCellFAB>& a_electricField,
5139  const LevelData<EBCellFAB>& a_particlesPerCell,
5140  const int a_level) noexcept
5141 {
5142  CH_TIME("ItoKMCStepper::computePhysicsDt(LD<EBCellFAB>, LD<EBCellFAB>, int)");
5143  if (m_verbosity > 5) {
5144  pout() << m_name + "::computePhysicsDt(LD<EBCellFAB>, LD<EBCellFAB>, int)" << endl;
5145  }
5146 
5147  const int numPlasmaSpecies = m_physics->getNumPlasmaSpecies();
5148 
5149  CH_assert(a_particlesPerCell.nComp() == numPlasmaSpecies);
5150  CH_assert(a_electricField.nComp() == SpaceDim);
5151 
5152  Real minDt = std::numeric_limits<Real>::max();
5153 
5154  const DisjointBoxLayout& dbl = m_amr->getGrids(m_fluidRealm)[a_level];
5155  const DataIterator& dit = dbl.dataIterator();
5156 
5157  const int nbox = dit.size();
5158 
5159 #pragma omp parallel for schedule(runtime) reduction(min : minDt)
5160  for (int mybox = 0; mybox < nbox; mybox++) {
5161  const DataIndex& din = dit[mybox];
5162 
5163  const Real patchDt = this->computePhysicsDt(a_electricField[din], a_particlesPerCell[din], a_level, din, dbl[din]);
5164 
5165  minDt = std::min(minDt, patchDt);
5166  }
5167 
5168  return ParallelOps::min(minDt);
5169 }
5170 
5171 template <typename I, typename C, typename R, typename F>
5172 Real
5173 ItoKMCStepper<I, C, R, F>::computePhysicsDt(const EBCellFAB& a_electricField,
5174  const EBCellFAB& a_particlesPerCell,
5175  const int a_level,
5176  const DataIndex a_dit,
5177  const Box a_box) noexcept
5178 {
5179  CH_TIME("ItoKMCStepper::computePhysicsDt(EBCellFAB, EBCellFAB, int, DataIndex, Box)");
5180  if (m_verbosity > 5) {
5181  pout() << m_name + "::computePhysicsDt(EBCellFAB, EBCellFAB, int, DataIndex, Box)" << endl;
5182  }
5183 
5184  Real minDt = std::numeric_limits<Real>::max();
5185 
5186  const int numPlasmaSpecies = m_physics->getNumPlasmaSpecies();
5187 
5188  CH_assert(a_electricField.nComp() == SpaceDim);
5189  CH_assert(a_particlesPerCell.nComp() == numPlasmaSpecies);
5190 
5191  // Geometric information that we need.
5192  const Real dx = m_amr->getDx()[a_level];
5193  const RealVect probLo = m_amr->getProbLo();
5194  const EBISBox& ebisbox = m_amr->getEBISLayout(m_fluidRealm, m_plasmaPhase)[a_level][a_dit];
5195 
5196  // Valid grid cells.
5197  const BaseFab<bool>& validCells = (*m_amr->getValidCells(m_fluidRealm)[a_level])[a_dit];
5198 
5199  // Handles to regular grid data.
5200  const FArrayBox& electricFieldReg = a_electricField.getFArrayBox();
5201  const FArrayBox& particlesPerCellReg = a_particlesPerCell.getFArrayBox();
5202 
5203  // The kernel require storage for the per-cell number of particles. This is
5204  // the storage we use for that
5205  Vector<Physics::ItoKMC::FPR> ppc(numPlasmaSpecies);
5206 
5207  // Regular grid kernel.
5208  auto regularKernel = [&](const IntVect& iv) -> void {
5209  // Only regular cells not covered by a finer grid.
5210  if (ebisbox.isRegular(iv) && validCells(iv, 0)) {
5211  const RealVect E = RealVect(D_DECL(electricFieldReg(iv, 0), electricFieldReg(iv, 1), electricFieldReg(iv, 2)));
5212  const RealVect pos = m_amr->getProbLo() + dx * (RealVect(iv) + 0.5 * RealVect::Unit);
5213 
5214  for (int i = 0; i < numPlasmaSpecies; i++) {
5215  ppc[i] = particlesPerCellReg(iv, i);
5216  }
5217 
5218  const Real cellDt = m_physics->computeDt(E, pos, ppc);
5219 
5220  minDt = std::min(minDt, cellDt);
5221  }
5222  };
5223 
5224  // Irregular grid kernel.
5225  auto irregularKernel = [&](const VolIndex& vof) -> void {
5226  const IntVect& iv = vof.gridIndex();
5227 
5228  // Only irregular cells not covered by a finer grid.
5229  if (ebisbox.isIrregular(iv) && validCells(iv, 0)) {
5230  const RealVect E = RealVect(D_DECL(a_electricField(vof, 0), a_electricField(vof, 1), a_electricField(vof, 2)));
5231  const RealVect pos = probLo + Location::position(Location::Cell::Centroid, vof, ebisbox, dx);
5232 
5233  for (int i = 0; i < numPlasmaSpecies; i++) {
5234  ppc[i] = a_particlesPerCell(vof, i);
5235  }
5236 
5237  const Real cellDt = m_physics->computeDt(E, pos, ppc);
5238 
5239  minDt = std::min(minDt, cellDt);
5240  }
5241  };
5242 
5243  // Run the kernels.
5244  VoFIterator& vofit = (*m_amr->getVofIterator(m_fluidRealm, m_plasmaPhase)[a_level])[a_dit];
5245 
5246  BoxLoops::loop(a_box, regularKernel);
5247  BoxLoops::loop(vofit, irregularKernel);
5248 
5249  return minDt;
5250 }
5251 
5252 template <typename I, typename C, typename R, typename F>
5253 Real
5255 {
5256  CH_TIME("ItoKMCStepper::computeTotalCharge()");
5257  if (m_verbosity > 5) {
5258  pout() << m_name + "::computeTotalCharge()" << endl;
5259  }
5260 
5261  const bool kappaScale = true;
5262 
5263  Real totalCharge = 0.0;
5264 
5265  totalCharge += this->computeQplus();
5266  totalCharge += this->computeQminu();
5267  totalCharge += this->computeQsurf();
5268 
5269  return totalCharge;
5270 }
5271 
5272 template <typename I, typename C, typename R, typename F>
5273 Real
5275 {
5276  CH_TIME("ItoKMCStepper::computeQplus()");
5277  if (m_verbosity > 5) {
5278  pout() << m_name + "::computeQplus()" << endl;
5279  }
5280 
5281  const bool kappaScale = true;
5282 
5283  Real totalCharge = 0.0;
5284 
5285  // Charge from Ito solvers.
5286  for (auto it = m_ito->iterator(); it.ok(); ++it) {
5287  const RefCountedPtr<ItoSolver>& solver = it();
5288  const RefCountedPtr<ItoSpecies>& species = solver->getSpecies();
5289 
5290  const int Z = species->getChargeNumber();
5291 
5292  if (Z > 0) {
5293  const ParticleContainer<ItoParticle>& particles = solver->getParticles(ItoSolver::WhichContainer::Bulk);
5294 
5295  totalCharge += Z * ParticleOps::sum<ItoParticle, &ItoParticle::weight>(particles);
5296  }
5297  }
5298 
5299  // Charge from CDR solvers
5300  for (auto it = m_cdr->iterator(); it.ok(); ++it) {
5301  const RefCountedPtr<CdrSolver>& solver = it();
5302  const RefCountedPtr<CdrSpecies>& species = solver->getSpecies();
5303 
5304  const int Z = species->getChargeNumber();
5305 
5306  if (Z > 0) {
5307  const EBAMRCellData& phi = solver->getPhi();
5308 
5309  totalCharge += Z * solver->computeMass(phi, kappaScale);
5310  }
5311  }
5312 
5313  return totalCharge * Units::Qe;
5314 }
5315 
5316 template <typename I, typename C, typename R, typename F>
5317 Real
5319 {
5320  CH_TIME("ItoKMCStepper::computeQminu()");
5321  if (m_verbosity > 5) {
5322  pout() << m_name + "::computeQminu()" << endl;
5323  }
5324 
5325  const bool kappaScale = true;
5326 
5327  Real totalCharge = 0.0;
5328 
5329  // Charge from Ito solvers.
5330  for (auto it = m_ito->iterator(); it.ok(); ++it) {
5331  const RefCountedPtr<ItoSolver>& solver = it();
5332  const RefCountedPtr<ItoSpecies>& species = solver->getSpecies();
5333 
5334  const int Z = species->getChargeNumber();
5335 
5336  if (Z < 0) {
5337  const ParticleContainer<ItoParticle>& particles = solver->getParticles(ItoSolver::WhichContainer::Bulk);
5338 
5339  totalCharge += Z * ParticleOps::sum<ItoParticle, &ItoParticle::weight>(particles);
5340  }
5341  }
5342 
5343  // Charge from CDR solvers
5344  for (auto it = m_cdr->iterator(); it.ok(); ++it) {
5345  const RefCountedPtr<CdrSolver>& solver = it();
5346  const RefCountedPtr<CdrSpecies>& species = solver->getSpecies();
5347 
5348  const int Z = species->getChargeNumber();
5349 
5350  if (Z < 0) {
5351  const EBAMRCellData& phi = solver->getPhi();
5352 
5353  totalCharge += Z * solver->computeMass(phi, kappaScale);
5354  }
5355  }
5356 
5357  return totalCharge * Units::Qe;
5358 }
5359 
5360 template <typename I, typename C, typename R, typename F>
5361 Real
5363 {
5364  CH_TIME("ItoKMCStepper::computeQsurf()");
5365  if (m_verbosity > 5) {
5366  pout() << m_name + "::computeQsurf()" << endl;
5367  }
5368 
5369  return m_sigmaSolver->computeMass();
5370 }
5371 
5372 template <typename I, typename C, typename R, typename F>
5373 void
5375 {
5376  CH_TIME("ItoKMCStepper::advancePhotons(Real)");
5377  if (m_verbosity > 5) {
5378  pout() << m_name + "::advancePhotons(Real)" << endl;
5379  }
5380 
5381  // TLDR: This will add the source photons to the "bulk" photons and then advance them. If the
5382  // solver is a true transient solver then the photons are moved and some of them are eventually
5383  // absorbed on the mesh. If the solver is an "instanteneous" solver then all source photons
5384  // are absorbed on the mesh.
5385 
5386  for (auto solverIt = m_rte->iterator(); solverIt.ok(); ++solverIt) {
5387  RefCountedPtr<McPhoto>& solver = solverIt();
5388 
5389  // To reiterate: photons are the photons that live in the solver and are moved around. bulkPhotons
5390  // are the solvers that were absorbed on the mesh, bbPhotons are the photons that collided with the EB
5391  // and domainPhotons are photons that moved out of the domain.
5392  ParticleContainer<Photon>& photons = solver->getPhotons();
5393  ParticleContainer<Photon>& bulkPhotons = solver->getBulkPhotons();
5394  ParticleContainer<Photon>& ebPhotons = solver->getEbPhotons();
5395  ParticleContainer<Photon>& domainPhotons = solver->getDomainPhotons();
5396  ParticleContainer<Photon>& sourcePhotons = solver->getSourcePhotons();
5397 
5398  solver->clear(bulkPhotons);
5399  solver->clear(ebPhotons);
5400  solver->clear(domainPhotons);
5401 
5402  if (solver->isInstantaneous()) {
5403  solver->clear(photons);
5404 
5405  // Add source Photons
5406  photons.addParticles(sourcePhotons);
5407  solver->clear(sourcePhotons);
5408 
5409  // Instantaneous advance.
5410  solver->advancePhotonsInstantaneous(bulkPhotons, ebPhotons, domainPhotons, photons);
5411  }
5412  else {
5413  // Add source Photons
5414  photons.addParticles(sourcePhotons);
5415  solver->clear(sourcePhotons);
5416 
5417  // Stationary advance
5418  solver->advancePhotonsTransient(bulkPhotons, ebPhotons, domainPhotons, photons, a_dt);
5419  }
5420  }
5421 }
5422 
5423 template <typename I, typename C, typename R, typename F>
5424 void
5426 {
5427  CH_TIME("ItoKMCStepper::sortPhotonsByCell(McPhoto::WhichContainer)");
5428  if (m_verbosity > 5) {
5429  pout() << m_name + "::sortPhotonsByCell(McPhoto::WhichContainer)" << endl;
5430  }
5431 
5432  for (auto solverIt = m_rte->iterator(); solverIt.ok(); ++solverIt) {
5433  solverIt()->sortPhotonsByCell(a_which);
5434  }
5435 }
5436 
5437 template <typename I, typename C, typename R, typename F>
5438 void
5440 {
5441  CH_TIME("ItoKMCStepper::sortPhotonsByPatch(McPhoto::WhichContainer)");
5442  if (m_verbosity > 5) {
5443  pout() << m_name + "::sortPhotonsByPatch(McPhoto::WhichContainer)" << endl;
5444  }
5445 
5446  for (auto solverIt = m_rte->iterator(); solverIt.ok(); ++solverIt) {
5447  solverIt()->sortPhotonsByPatch(a_which);
5448  }
5449 }
5450 
5451 template <typename I, typename C, typename R, typename F>
5452 Vector<RefCountedPtr<ItoSolver>>
5454 {
5455  CH_TIME("ItoKMCStepper::getLoadBalanceSolvers()");
5456  if (m_verbosity > 5) {
5457  pout() << m_name + "::getLoadBalanceSolvers()" << endl;
5458  }
5459 
5460  Vector<RefCountedPtr<ItoSolver>> lbSolvers;
5461 
5462  // If there's an index < 0 we load balance everything.
5463  bool loadBalanceAll = false;
5464  for (int i = 0; i < m_loadBalanceIndices.size(); i++) {
5465  if (m_loadBalanceIndices[i] < 0) {
5466  loadBalanceAll = true;
5467  }
5468  }
5469 
5470  if (loadBalanceAll) {
5471  for (auto solverIt = m_ito->iterator(); solverIt.ok(); ++solverIt) {
5472  lbSolvers.push_back(solverIt());
5473  }
5474  }
5475  else {
5476  for (int i = 0; i < m_loadBalanceIndices.size(); i++) {
5477  RefCountedPtr<ItoSolver>& solver = m_ito->getSolvers()[i];
5478 
5479  lbSolvers.push_back(solver);
5480  }
5481  }
5482 
5483  return lbSolvers;
5484 }
5485 
5486 template <typename I, typename C, typename R, typename F>
5487 bool
5488 ItoKMCStepper<I, C, R, F>::loadBalanceThisRealm(const std::string a_realm) const
5489 {
5490  CH_TIME("TimeStepper::loadBalanceThisRealm");
5491  if (m_verbosity > 5) {
5492  pout() << "TimeStepper::loadBalanceThisRealm" << endl;
5493  }
5494 
5495  bool ret = false;
5496 
5497  if (a_realm == m_particleRealm && m_loadBalanceParticles) {
5498  ret = true;
5499  }
5500  else if (a_realm == m_fluidRealm && m_loadBalanceFluid) {
5501  ret = true;
5502  }
5503 
5504  return ret;
5505 }
5506 
5507 template <typename I, typename C, typename R, typename F>
5508 void
5509 ItoKMCStepper<I, C, R, F>::loadBalanceBoxes(Vector<Vector<int>>& a_procs,
5510  Vector<Vector<Box>>& a_boxes,
5511  const std::string a_realm,
5512  const Vector<DisjointBoxLayout>& a_grids,
5513  const int a_lmin,
5514  const int a_finestLevel)
5515 {
5516  CH_TIME("ItoKMCStepper::loadBalanceBoxes");
5517  if (m_verbosity > 5) {
5518  pout() << m_name + "::loadBalanceBoxes" << endl;
5519  }
5520 
5521  if (m_loadBalanceParticles && a_realm == m_particleRealm) {
5522  this->loadBalanceParticleRealm(a_procs, a_boxes, a_realm, a_grids, a_lmin, a_finestLevel);
5523  }
5524  else if (m_loadBalanceFluid && a_realm == m_fluidRealm) {
5525  this->loadBalanceFluidRealm(a_procs, a_boxes, a_realm, a_grids, a_lmin, a_finestLevel);
5526  }
5527 }
5528 
5529 template <typename I, typename C, typename R, typename F>
5530 void
5532  Vector<Vector<Box>>& a_boxes,
5533  const std::string a_realm,
5534  const Vector<DisjointBoxLayout>& a_grids,
5535  const int a_lmin,
5536  const int a_finestLevel) noexcept
5537 {
5538  CH_TIME("ItoKMCStepper::loadBalanceParticleRealm(...)");
5539  if (m_verbosity > 5) {
5540  pout() << m_name + "::loadBalanceParticleRealm(...)" << endl;
5541  }
5542 
5543  // TLDR: This is a bit involved due to the fact that the simulation lives in a state between the old grids
5544  // and the new grids. We want to compute the number of computational in patch on the new grid, and
5545  // use that for load balancing. We have already computed the number of computational particles per
5546  // grid cell on the old grids, but the new grids are not ready (yet). We only have the proxy-grids
5547  // coming in through the argument (a_grids), and our job is to take this grid and reassign the patches
5548  // so that each MPI rank gets roughly the same number of computational particles. To do this we perform
5549  // the following steps:
5550  //
5551  // 1. Allocate storage on the proxy grids (a_grids) so we have something to regrid into.
5552  // 2. Define regrid operators for going between the proxy grids and the old grids.
5553  // 3. Regrid the PPC on the old grids onto the proxy grids.
5554  // 4. Go through the patches on the proxy grids and figure out the total number of particles
5555  // in each patch (there's a weird global-to-local remapping taking place through intCode()).
5556  // 5. Call our nifty load-balancing routines.
5557  //
5558 
5559  if (!m_loadBalanceParticles) {
5560  MayDay::Error("ItoKMCStepper::loadBalanceParticleRealm -- logic bust, should not have been called!");
5561  }
5562 
5563  // Get the solvers that we will use for load balancing.
5564  Vector<RefCountedPtr<ItoSolver>> lbSolvers = this->getLoadBalanceSolvers();
5565 
5566  // Decompose the DisjointBoxLayout
5567  a_procs.resize(1 + a_finestLevel);
5568  a_boxes.resize(1 + a_finestLevel);
5569 
5570  for (int lvl = a_lmin; lvl <= a_finestLevel; lvl++) {
5571  a_procs[lvl] = a_grids[lvl].procIDs();
5572  a_boxes[lvl] = a_grids[lvl].boxArray();
5573  }
5574 
5575  // 1. Allocate something that we can regrid the PPC for each species into, and something that holds the total
5576  // PPC on the new grids.
5577  EBAMRCellData totalPPC;
5578  EBAMRCellData speciesPPC;
5579 
5580  m_amr->allocate(totalPPC, m_particleRealm, m_plasmaPhase, 1);
5581  m_amr->allocate(speciesPPC, m_particleRealm, m_plasmaPhase, 1);
5582 
5583  DataOps::setValue(totalPPC, 0.0);
5584  DataOps::setValue(speciesPPC, 0.0);
5585 
5586  // 2. EBCoarseToFineInterp is not a part of the registry for ItoSolver so we just define it here ourselves. Note that
5587  // it is stored on the same level that we interpolate to.
5588  Vector<RefCountedPtr<EBCoarseToFineInterp>> interpOp(1 + a_finestLevel);
5589  for (int lvl = 1; lvl <= a_finestLevel; lvl++) {
5590  const EBLevelGrid& eblgFine = *m_amr->getEBLevelGrid(m_particleRealm, m_plasmaPhase)[lvl];
5591  const EBLevelGrid& eblgCoFi = *m_amr->getEBLevelGridCoFi(m_particleRealm, m_plasmaPhase)[lvl - 1];
5592  const EBLevelGrid& eblgCoar = *m_amr->getEBLevelGrid(m_particleRealm, m_plasmaPhase)[lvl - 1];
5593  const int refRat = m_amr->getRefinementRatios()[lvl - 1];
5594 
5595  interpOp[lvl] = RefCountedPtr<EBCoarseToFineInterp>(new EBCoarseToFineInterp(eblgFine, eblgCoFi, eblgCoar, refRat));
5596  }
5597 
5598  // 3. Go through each solver and figure out the number of particles on the new grids. Add
5599  // these to totalPPC.
5600  for (int i = 0; i < lbSolvers.size(); i++) {
5601  const EBAMRCellData& oldData = m_loadBalancePPC[i];
5602  const int oldFinestLevel = oldData.size() - 1;
5603 
5604  // These levels have not changed but ownship MIGHT have changed.
5605  for (int lvl = 0; lvl <= std::max(0, a_lmin - 1); lvl++) {
5606  oldData[lvl]->copyTo(*speciesPPC[lvl]);
5607  }
5608 
5609  // These levels have changed.
5610  for (int lvl = std::max(1, a_lmin); lvl <= a_finestLevel; lvl++) {
5611  RefCountedPtr<EBCoarseToFineInterp>& interpolator = interpOp[lvl];
5612 
5613  interpolator->interpolate(*speciesPPC[lvl],
5614  *speciesPPC[lvl - 1],
5615  Interval(0, 0),
5616  EBCoarseToFineInterp::Type::ConservativePWC);
5617 
5618  // There could be parts of the new grid that overlapped with the old grid (on level lvl) -- we don't want
5619  // to pollute the solution with interpolation there since we already have valid data.
5620  if (lvl <= std::min(oldFinestLevel, a_finestLevel)) {
5621  oldData[lvl]->copyTo(*speciesPPC[lvl]);
5622  }
5623  }
5624 
5625  // Add to totalPPC.
5626  DataOps::incr(totalPPC, speciesPPC, 1.0);
5627  }
5628 
5629  // 4. totalPPC contains the total number of computational particles per cell on the new grids,
5630  // we need to map this to something we can load balance.
5631  Vector<Vector<long int>> loads(1 + a_finestLevel, 0L);
5632  for (int lvl = 0; lvl <= a_finestLevel; lvl++) {
5633  const DisjointBoxLayout& dbl = a_grids[lvl];
5634  const DataIterator& dit = dbl.dataIterator();
5635 
5636  Vector<long int>& levelLoads = loads[lvl];
5637 
5638  levelLoads.resize(dbl.size());
5639 
5640  const int nbox = dit.size();
5641 
5642 #pragma omp parallel for schedule(runtime)
5643  for (int mybox = 0; mybox < nbox; mybox++) {
5644  const DataIndex& din = dit[mybox];
5645 
5646  const Box cellBox = dbl[din];
5647  const EBCellFAB& PPC = (*totalPPC[lvl])[din];
5648  const EBISBox& ebisbox = PPC.getEBISBox();
5649  const BaseFab<bool>& validCells = (*m_amr->getValidCells(m_particleRealm)[lvl])[din];
5650  const FArrayBox& regPPC = PPC.getFArrayBox();
5651 
5652  auto regularKernel = [&](const IntVect& iv) -> void {
5653  if (validCells(iv, 0) && ebisbox.isRegular(iv)) {
5654  levelLoads[din.intCode()] += (long int)regPPC(iv, 0);
5655  }
5656  };
5657 
5658  BoxLoops::loop(cellBox, regularKernel);
5659  }
5660 
5661  ParallelOps::vectorSum(levelLoads);
5662 
5663  // Add the "constant" load from the other PPC stuff
5664  for (LayoutIterator lit = dbl.layoutIterator(); lit.ok(); ++lit) {
5665  const Box cellBox = dbl[lit()];
5666 
5667  levelLoads[lit().intCode()] += (long int)m_loadPerCell * cellBox.numPts();
5668  }
5669  }
5670 
5671  // 5. Finally do the actual load balancing.
5672  LoadBalancing::sort(a_boxes, loads, m_boxSort);
5673 
5674  Loads rankLoads;
5675  rankLoads.resetLoads();
5676 
5677  for (int lvl = 0; lvl <= a_finestLevel; lvl++) {
5678  LoadBalancing::makeBalance(a_procs[lvl], rankLoads, loads[lvl], a_boxes[lvl]);
5679  }
5680 }
5681 
5682 template <typename I, typename C, typename R, typename F>
5683 void
5685  Vector<Vector<Box>>& a_boxes,
5686  const std::string a_realm,
5687  const Vector<DisjointBoxLayout>& a_grids,
5688  const int a_lmin,
5689  const int a_finestLevel) noexcept
5690 {
5691  CH_TIME("ItoKMCStepper::loadBalanceFluidRealm(...)");
5692  if (m_verbosity > 5) {
5693  pout() << m_name + "::loadBalanceFluidRealm(...)" << endl;
5694  }
5695 
5696  CH_assert(m_loadBalanceFluid);
5697  CH_assert(a_realm == m_fluidRealm);
5698 
5699  // TLDR: This code tries to compute a load for each grid patch by applying a relaxation operator to each box. This means that the load
5700  // should be a decent estimate that takes into account boundary conditions, coarse-fine interface arithmetic, and enlargened stencils
5701  // near the embedded boundary.
5702 
5703  a_procs.resize(1 + a_finestLevel);
5704  a_boxes.resize(1 + a_finestLevel);
5705 
5706  // We need to make AmrMesh restore some operators that we need in order to create a multigrid object. Fortunately, FieldSolver has routines
5707  // for doing that but it will not know if AmrMesh has updated it's operators or not. So, we need to regrid them.
5708  m_amr->regridOperators(m_fluidRealm, a_lmin);
5709 
5710  // Field solver needs to allocate solver and set up the multigrid solver.
5711  m_fieldSolver->allocate();
5712  m_fieldSolver->setupSolver();
5713 
5714  // Loads on each rank
5715  Loads rankLoads;
5716  rankLoads.resetLoads();
5717 
5718  // Field solver implementation gets the responsibility of computing loads on each level.
5719  for (int lvl = 0; lvl <= a_finestLevel; lvl++) {
5720  Vector<long long> boxLoads = m_fieldSolver->computeLoads(a_grids[lvl], lvl);
5721 
5722  // Do the desired sorting and load balancing
5723  a_boxes[lvl] = a_grids[lvl].boxArray();
5724 
5725  LoadBalancing::sort(a_boxes[lvl], boxLoads, m_boxSort);
5726  LoadBalancing::makeBalance(a_procs[lvl], rankLoads, boxLoads, a_boxes[lvl]);
5727  }
5728 }
5729 
5730 template <typename I, typename C, typename R, typename F>
5731 Vector<long int>
5732 ItoKMCStepper<I, C, R, F>::getCheckpointLoads(const std::string a_realm, const int a_level) const
5733 {
5734  CH_TIME("ItoKMCStepper::getCheckpointLoads(...)");
5735  if (m_verbosity > 5) {
5736  pout() << m_name + "::getCheckpointLoads(...)" << endl;
5737  }
5738 
5739  const DisjointBoxLayout& dbl = m_amr->getGrids(a_realm)[a_level];
5740  const int nbox = dbl.size();
5741 
5742  Vector<long int> loads(nbox, 0L);
5743 
5744  if (m_loadBalanceParticles && a_realm == m_particleRealm) {
5745 
5746  // If we're load balancing with particles, get the number of particles per patch
5747  // from the relevant particle solvers. Since these are Ito solvers, the loads
5748  // are equal to the number of computational particles in the grid patches.
5749  Vector<RefCountedPtr<ItoSolver>> loadBalanceProxySolvers = this->getLoadBalanceSolvers();
5750 
5751  for (int isolver = 0; isolver < loadBalanceProxySolvers.size(); isolver++) {
5752 
5753  // This solver computes loads -- there's a parallel gather operation
5754  // under the hood here.
5755  Vector<long int> solverLoads(nbox, 0L);
5756  loadBalanceProxySolvers[isolver]->computeLoads(solverLoads, dbl, a_level);
5757 
5758  // Add to total loads.
5759  for (int ibox = 0; ibox < nbox; ibox++) {
5760  loads[ibox] += solverLoads[ibox];
5761  }
5762  }
5763 
5764  // Add the "constant" loads -- these are computational loads due to the "mesh" part. We use
5765  // a heuristic where we have m_loadPerCell "cost".
5766  for (LayoutIterator lit = dbl.layoutIterator(); lit.ok(); ++lit) {
5767  const Box box = dbl[lit()];
5768 
5769  loads[lit().intCode()] += lround(m_loadPerCell * box.numPts());
5770  }
5771  }
5772  else {
5773  loads = TimeStepper::getCheckpointLoads(a_realm, a_level);
5774  }
5775 
5776  return loads;
5777 }
5778 
5779 template <typename I, typename C, typename R, typename F>
5780 void
5782 {
5783  CH_TIME("ItoKMCStepper::computeEdotJSource(a_dt)");
5784  if (m_verbosity > 5) {
5785  pout() << m_name + "::computeEdotJSource(a_dt)" << endl;
5786  }
5787 
5788  CH_assert(a_dt > 0.0);
5789 
5790  DataOps::setValue(m_EdotJ, 0.0);
5791 
5792  CH_assert(m_EdotJ.getRealm() == m_fluidRealm);
5793 
5794  // TLDR: EdotJ is an energy term for the various species, i.e. it is the rate of energy increase as the particle moves from
5795  // position A to position B, excluding friction from collision with other molecules. We compute this energy increase as
5796  //
5797  // q * V(B) - V(A)
5798  //
5799  // which means that the energy rate is q*(V(B) - V(A))/a_dt.
5800  //
5801  // We simply assign this factor to the particles and then deposit them on the mesh. However, this is more complex than
5802  // it sounds because the particles m_EdotJ live on different realms. The way we do this is that we copy the potential
5803  // over into the particle realm and we interpolate V(B) and V(A) onto some storage in the particle container. We then
5804  // assign an effective weight w * [V(B) - V(A)] to the particles which we deposit onto the mesh using the appropriate
5805  // deposition scheme that the user has assgned.
5806  //
5807 
5808  // Allocate a particle data holder with three scalar storage spaces; these are the weight, V(A), and V(B). We also
5809  // need the positions A and B so they're in here as well.
5810  using CompParticle = GenericParticle<3, 1>;
5811 
5812  ParticleContainer<CompParticle> computationParticles;
5813  m_amr->allocate(computationParticles, m_particleRealm);
5814 
5815  // Electrostatic potential on appropriate phase. This is defined on the fluid realm
5816  // but we need it on the particle realm.
5817  const EBAMRCellData potentialPhase = m_amr->alias(m_plasmaPhase, m_fieldSolver->getPotential());
5818  m_amr->copyData(m_particleScratch1, potentialPhase);
5819 
5820  m_amr->conservativeAverage(m_particleScratch1, m_particleRealm, m_plasmaPhase);
5821  m_amr->interpGhost(m_particleScratch1, m_particleRealm, m_plasmaPhase);
5822 
5823  for (auto solverIt = m_ito->iterator(); solverIt.ok(); ++solverIt) {
5824  RefCountedPtr<ItoSolver>& solver = solverIt();
5825  const RefCountedPtr<ItoSpecies>& species = solver->getSpecies();
5826 
5827  const int idx = solverIt.index();
5828  const int Z = species->getChargeNumber();
5829  const bool mobile = solver->isMobile();
5830  const bool diffusive = solver->isDiffusive();
5831 
5832  if (Z != 0 && (mobile || diffusive)) {
5833 
5834  const ParticleContainer<ItoParticle>& particles = solver->getParticles(ItoSolver::WhichContainer::Bulk);
5835 
5836  // Copy the ItoParticles to the transient particles we use for computing these things.
5837  for (int lvl = 0; lvl <= m_amr->getFinestLevel(); lvl++) {
5838  const DisjointBoxLayout& dbl = m_amr->getGrids(m_particleRealm)[lvl];
5839  const DataIterator& dit = dbl.dataIterator();
5840 
5841  const int nbox = dit.size();
5842 
5843 #pragma omp parallel for schedule(runtime)
5844  for (int mybox = 0; mybox < nbox; mybox++) {
5845  const DataIndex& din = dit[mybox];
5846 
5847  List<CompParticle>& patchCompParticles = computationParticles[lvl][din].listItems();
5848  const List<ItoParticle>& patchItoParticles = particles[lvl][din].listItems();
5849 
5850  for (ListIterator<ItoParticle> lit(patchItoParticles); lit.ok(); ++lit) {
5851  const ItoParticle& itoParticle = lit();
5852 
5853  CompParticle p;
5854 
5855  // vect<0> holds the starting position. real<0> holds the weight and
5856  // real<1> is used for V(A) and real<2> is used for V(B)
5857  p.position() = itoParticle.position();
5858  p.template real<0>() = itoParticle.weight();
5859  p.template real<1>() = 0.0;
5860  p.template real<2>() = 0.0;
5861  p.template vect<0>() = itoParticle.oldPosition();
5862 
5863  patchCompParticles.add(p);
5864  }
5865  }
5866  }
5867 
5868  // Interpolate the potential to the current particle position which gives us V(B) for the particles.
5869  m_amr->interpolateParticles<CompParticle, &CompParticle::template real<1>>(computationParticles,
5870  m_particleRealm,
5871  m_plasmaPhase,
5872  m_particleScratch1,
5873  solver->getDeposition(),
5874  false);
5875 
5876  // Move the particles back to their old positions and interpolate the potential there.
5877  for (int lvl = 0; lvl <= m_amr->getFinestLevel(); lvl++) {
5878  const DisjointBoxLayout& dbl = m_amr->getGrids(m_particleRealm)[lvl];
5879  const DataIterator& dit = dbl.dataIterator();
5880 
5881  const int nbox = dit.size();
5882 
5883 #pragma omp parallel for schedule(runtime)
5884  for (int mybox = 0; mybox < nbox; mybox++) {
5885  const DataIndex& din = dit[mybox];
5886 
5887  List<CompParticle>& particles = computationParticles[lvl][din].listItems();
5888 
5889  for (ListIterator<CompParticle> lit(particles); lit.ok(); ++lit) {
5890 
5891  // Swap positions.
5892  const RealVect tmp = lit().position();
5893 
5894  // vect<0> holds the end position.
5895  lit().position() = lit().template vect<0>();
5896  lit().template vect<0>() = tmp;
5897  }
5898  }
5899  }
5900 
5901  computationParticles.remap();
5902 
5903  // Interpolate the potential to the previous particle position which gives us V(A) for the particles.
5904  m_amr->interpolateParticles<CompParticle, &CompParticle::template real<2>>(computationParticles,
5905  m_particleRealm,
5906  m_plasmaPhase,
5907  m_particleScratch1,
5908  solver->getDeposition());
5909 
5910  // Move the particles back again and multiply the weight by Z * (V(A) - V(B))/a_dt
5911  for (int lvl = 0; lvl <= m_amr->getFinestLevel(); lvl++) {
5912  const DisjointBoxLayout& dbl = m_amr->getGrids(m_particleRealm)[lvl];
5913  const DataIterator& dit = dbl.dataIterator();
5914 
5915  const int nbox = dit.size();
5916 
5917 #pragma omp parallel for schedule(runtime)
5918  for (int mybox = 0; mybox < nbox; mybox++) {
5919  const DataIndex& din = dit[mybox];
5920 
5921  List<CompParticle>& particles = computationParticles[lvl][din].listItems();
5922 
5923  for (ListIterator<CompParticle> lit(particles); lit.ok(); ++lit) {
5924  lit().position() = lit().template vect<0>();
5925 
5926  // Compute weight * V(B) - V(A) where V(B) is stored in real<1> and V(A) is stored in real<2>
5927  lit().template real<0>() *= (lit().template real<1>() - lit().template real<2>());
5928  }
5929  }
5930  }
5931 
5932  computationParticles.remap();
5933 
5934  // Deposit the particles
5935  m_amr->depositParticles<CompParticle, &CompParticle::template real<0>>(m_particleScratch1,
5936  m_particleRealm,
5937  m_plasmaPhase,
5938  computationParticles,
5939  solver->getDeposition(),
5940  solver->getCoarseFineDeposition());
5941 
5942  // Copy data back onto the fluid realm.
5943  m_amr->copyData(m_fluidScratch1, m_particleScratch1);
5944  DataOps::scale(m_fluidScratch1, Z * Units::Qe / a_dt);
5945  DataOps::plus(m_EdotJ, m_fluidScratch1, 0, idx, 1);
5946  }
5947 
5948  computationParticles.clearParticles();
5949  }
5950 }
5951 
5952 template <typename I, typename C, typename R, typename F>
5953 void
5955 {
5956  CH_TIME("ItoKMCStepper::computePhysicsPlotVariables");
5957  if (m_verbosity > 5) {
5958  pout() << m_name + "::computePhysicsPlotVariables" << endl;
5959  }
5960 
5961  // Number of output variables from CdrPlasmaPhysics
5962  const int numVars = m_physics->getNumberOfPlotVariables();
5963  const int numItoSpecies = m_physics->getNumItoSpecies();
5964  const int numCdrSpecies = m_physics->getNumCdrSpecies();
5965  const int numPlasmaSpecies = m_physics->getNumPlasmaSpecies();
5966  const int numPhotonSpecies = m_physics->getNumPhotonSpecies();
5967 
5968  CH_assert(!(a_physicsPlotVars[0].isNull()));
5969  CH_assert(a_physicsPlotVars[0]->nComp() == numVars);
5970  CH_assert(a_physicsPlotVars.getRealm() == m_fluidRealm);
5971 
5972  // Update gradients
5973  this->computeDensityGradients();
5974 
5975  const RealVect probLo = m_amr->getProbLo();
5976 
5977  for (int lvl = 0; lvl <= m_amr->getFinestLevel(); lvl++) {
5978  const DisjointBoxLayout& dbl = m_amr->getGrids(m_fluidRealm)[lvl];
5979  const DataIterator& dit = dbl.dataIterator();
5980  const EBISLayout& ebisl = m_amr->getEBISLayout(m_fluidRealm, m_plasmaPhase)[lvl];
5981  const Real dx = m_amr->getDx()[lvl];
5982 
5983  const int nbox = dit.size();
5984 
5985 #pragma omp parallel for schedule(runtime)
5986  for (int mybox = 0; mybox < nbox; mybox++) {
5987  const DataIndex& din = dit[mybox];
5988 
5989  const Box& cellBox = dbl[din];
5990  const EBISBox& ebisBox = ebisl[din];
5991 
5992  // Handle to electric field
5993  const EBCellFAB& electricField = (*m_electricFieldFluid[lvl])[din];
5994  const FArrayBox& electricFieldReg = electricField.getFArrayBox();
5995 
5996  // Handle to densities and density gradients for CDR and Ito species.
5997  Vector<const EBCellFAB*> densitiesIto(numItoSpecies);
5998  Vector<const EBCellFAB*> densityGradientsIto(numItoSpecies);
5999  Vector<const FArrayBox*> densitiesItoReg(numItoSpecies);
6000  Vector<const FArrayBox*> densityGradientsItoReg(numItoSpecies);
6001 
6002  Vector<const EBCellFAB*> densitiesCDR(numCdrSpecies);
6003  Vector<const EBCellFAB*> densityGradientsCDR(numCdrSpecies);
6004  Vector<const FArrayBox*> densitiesCDRReg(numCdrSpecies);
6005  Vector<const FArrayBox*> densityGradientsCDRReg(numCdrSpecies);
6006 
6007  for (auto it = m_ito->iterator(); it.ok(); ++it) {
6008  const RefCountedPtr<ItoSolver>& solver = it();
6009 
6010  const int i = it.index();
6011 
6012  densitiesIto[i] = &(*(m_fluidPhiIto[i])[lvl])[din];
6013  densitiesItoReg[i] = &(densitiesIto[i]->getFArrayBox());
6014  densityGradientsIto[i] = &(*m_fluidGradPhiIto[i][lvl])[din];
6015  densityGradientsItoReg[i] = &(densityGradientsIto[i]->getFArrayBox());
6016  }
6017 
6018  for (auto it = m_cdr->iterator(); it.ok(); ++it) {
6019  const RefCountedPtr<CdrSolver>& solver = it();
6020  const EBAMRCellData& phi = solver->getPhi();
6021 
6022  const int i = it.index();
6023 
6024  densitiesCDR[i] = &(*phi[lvl])[din];
6025  densitiesCDRReg[i] = &(densitiesCDR[i]->getFArrayBox());
6026  densityGradientsCDR[i] = &(*m_fluidGradPhiCDR[i][lvl])[din];
6027  densityGradientsCDRReg[i] = &(densityGradientsCDR[i]->getFArrayBox());
6028  }
6029 
6030  // Handle to valid grid cells.
6031  const BaseFab<bool>& validCells = (*m_amr->getValidCells(m_fluidRealm)[lvl])[din];
6032 
6033  // Handle to output variables
6034  EBCellFAB& physicsPlotVars = (*a_physicsPlotVars[lvl])[din];
6035  FArrayBox& physicsPlotVarsReg = physicsPlotVars.getFArrayBox();
6036 
6037  // Things that will be populated in the kernels.
6038  Vector<Real> densities(numPlasmaSpecies);
6039  Vector<RealVect> densityGradients(numPlasmaSpecies);
6040 
6041  // Regular cells
6042  auto regularKernel = [&](const IntVect& iv) -> void {
6043  if (ebisBox.isRegular(iv) && validCells(iv, 0)) {
6044  const RealVect pos = probLo + dx * (RealVect(iv) + 0.5 * RealVect::Unit);
6045  const RealVect E = RealVect(
6046  D_DECL(electricFieldReg(iv, 0), electricFieldReg(iv, 1), electricFieldReg(iv, 2)));
6047 
6048  // Populate gradients.
6049  for (int i = 0; i < numItoSpecies; i++) {
6050  densities[i] = (*densitiesItoReg[i])(iv, 0);
6051  densityGradients[i] = RealVect(D_DECL((*densityGradientsItoReg[i])(iv, 0),
6052  (*densityGradientsItoReg[i])(iv, 1),
6053  (*densityGradientsItoReg[i])(iv, 2)));
6054  }
6055 
6056  for (int i = 0; i < numCdrSpecies; i++) {
6057  densities[numItoSpecies + i] = (*densitiesCDRReg[i])(iv, 0);
6058  densityGradients[numItoSpecies + i] = RealVect(D_DECL((*densityGradientsCDRReg[i])(iv, 0),
6059  (*densityGradientsCDRReg[i])(iv, 1),
6060  (*densityGradientsCDRReg[i])(iv, 2)));
6061  }
6062 
6063  // Do the physics advance.
6064  const Vector<Real> plotVars = m_physics->getPlotVariables(E, pos, densities, densityGradients, dx, 1.0);
6065 
6066  CH_assert(plotVars.size() == numVars);
6067 
6068  for (int i = 0; i < numVars; i++) {
6069  physicsPlotVarsReg(iv, i) = plotVars[i];
6070  }
6071  }
6072  };
6073 
6074  // Irregular cells
6075  auto irregularKernel = [&](const VolIndex& vof) -> void {
6076  if (validCells(vof.gridIndex(), 0)) {
6077  const RealVect pos = probLo + dx * (RealVect(vof.gridIndex()) + 0.5 * RealVect::Unit);
6078  const RealVect E = RealVect(D_DECL(electricField(vof, 0), electricField(vof, 1), electricField(vof, 2)));
6079 
6080  // Populate gradients.
6081  for (int i = 0; i < numItoSpecies; i++) {
6082  densities[i] = (*densitiesIto[i])(vof, 0);
6083  densityGradients[i] = RealVect(D_DECL((*densityGradientsIto[i])(vof, 0),
6084  (*densityGradientsIto[i])(vof, 1),
6085  (*densityGradientsIto[i])(vof, 2)));
6086  }
6087 
6088  for (int i = 0; i < numCdrSpecies; i++) {
6089  densities[numItoSpecies + i] = (*densitiesCDR[i])(vof, 0);
6090  densityGradients[numItoSpecies + i] = RealVect(D_DECL((*densityGradientsCDR[i])(vof, 0),
6091  (*densityGradientsCDR[i])(vof, 1),
6092  (*densityGradientsCDR[i])(vof, 2)));
6093  }
6094 
6095  // Do the physics advance.
6096  const Vector<Real> plotVars = m_physics->getPlotVariables(E, pos, densities, densityGradients, dx, 1.0);
6097 
6098  CH_assert(plotVars.size() == numVars);
6099 
6100  for (int i = 0; i < numVars; i++) {
6101  physicsPlotVars(vof, i) = plotVars[i];
6102  }
6103  }
6104  };
6105 
6106  // Run the kernels.
6107  VoFIterator& vofit = (*m_amr->getVofIterator(m_fluidRealm, m_plasmaPhase)[lvl])[din];
6108 
6109  BoxLoops::loop(cellBox, regularKernel);
6110  BoxLoops::loop(vofit, irregularKernel);
6111  }
6112  }
6113 
6114  m_amr->average(a_physicsPlotVars, m_fluidRealm, m_plasmaPhase, Average::Arithmetic, Interval(0, numVars - 1));
6115  m_amr->interpGhost(a_physicsPlotVars, m_fluidRealm, m_plasmaPhase);
6116 }
6117 
6118 #include <CD_NamespaceFooter.H>
6119 
6120 #endif
Average
Various averaging methods.
Definition: CD_Average.H:24
Agglomeration of useful data operations.
Declaration of an aggregated class for regrid operations.
EBIntersection
Enum for putting some logic into how we think about intersection between particles and EBs.
Definition: CD_EBIntersection.H:21
EBRepresentation
Enum for putting some logic into how we think about EBs. This is just a simply supporting class for v...
Definition: CD_EBRepresentation.H:22
SpeciesType
Map to species type.
Definition: CD_ItoKMCPhysics.H:66
Declaration of an abstract class for integrating the Ito-KMC-Poisson equations.
SpeciesSubset
Enum for differentiating between types of particles.
Definition: CD_ItoKMCStepper.H:40
Declaration of cell positions.
Agglomeration of basic MPI reductions.
Declaration of a static class containing some common useful particle routines that would otherwise be...
Implementation of CD_Timer.H.
Declaration of various useful units.
Factory class for CdrLayout. T is (usually) CdrSolver and S is the implementation class (e....
Definition: CD_CdrLayout.H:302
RefCountedPtr< CdrLayout< T > > newLayout(const Vector< RefCountedPtr< CdrSpecies >> &a_species) const
Factory method, create a new CdrLayout.
Definition: CD_CdrLayoutImplem.H:546
Iterator class for CdrLayout. This allows iteration through solvers (or subsets of solvers).
Definition: CD_CdrIterator.H:27
virtual bool ok() const
Ok or not.
Definition: CD_CdrIteratorImplem.H:76
Class for the cell-information that is often queried when merging particles inside a cell.
Definition: CD_CellInfo.H:25
static void scale(MFAMRCellData &a_lhs, const Real &a_scale) noexcept
Scale data by factor.
Definition: CD_DataOps.cpp:2384
static void divideFallback(EBAMRCellData &a_numerator, const EBAMRCellData &a_denominator, const Real a_fallback)
Divide data. If the denominator is zero, set the value to a fallback option.
Definition: CD_DataOps.cpp:1303
static void volumeScale(EBAMRCellData &a_data, const Vector< Real > &a_dx)
Scale data by dx^SpaceDim.
Definition: CD_DataOps.cpp:2132
static void getMaxMinNorm(Real &a_max, Real &a_min, EBAMRCellData &data)
Get maximum and minimum value of normed data.
Definition: CD_DataOps.cpp:1783
static void floor(EBAMRCellData &a_lhs, const Real a_value)
Floor values in data holder. This sets all values below a_value to a_value.
Definition: CD_DataOps.cpp:1380
static void setValue(LevelData< MFInterfaceFAB< T >> &a_lhs, const T &a_value)
Set value in an MFInterfaceFAB data holder.
Definition: CD_DataOpsImplem.H:23
static void computeMinValidBox(RealVect &a_lo, RealVect &a_hi, const RealVect a_normal, const RealVect a_centroid)
Compute the tightest possible valid box around a cut-cell volume.
Definition: CD_DataOps.cpp:3567
static void getMaxMin(Real &max, Real &min, EBAMRCellData &a_data, const int a_comp)
Get maximum and minimum value of specified component.
Definition: CD_DataOps.cpp:1637
static void incr(MFAMRCellData &a_lhs, const MFAMRCellData &a_rhs, const Real a_scale) noexcept
Function which increments data in the form a_lhs = a_lhs + a_rhs*a_scale for all components.
Definition: CD_DataOps.cpp:787
static void setCoveredValue(EBAMRCellData &a_lhs, const int a_comp, const Real a_value)
Set value in covered cells. Does specified component.
Definition: CD_DataOps.cpp:2538
static void plus(EBAMRCellData &a_lhs, const EBAMRCellData &a_rhs, const int a_srcComp, const int a_dstComp, const int a_numComp)
General addition operator for adding together data. The user can choose which components to add.
Definition: CD_DataOps.cpp:852
static void averageCellToFace(EBAMRFluxData &a_faceData, const EBAMRCellData &a_cellData, const Vector< ProblemDomain > &a_domains)
Average all components of the cell-centered data to faces.
Definition: CD_DataOps.cpp:153
static void copy(MFAMRCellData &a_dst, const MFAMRCellData &a_src)
Copy data from one data holder to another.
Definition: CD_DataOps.cpp:1118
static void multiplyScalar(EBAMRCellData &a_lhs, const EBAMRCellData &a_rhs)
Multiply data holder by another data holder.
Definition: CD_DataOps.cpp:2239
void push_back(RefCountedPtr< LevelData< T >> &a_levelData) noexcept
Push a LevelData<T> object to the back of the data vector.
Definition: CD_EBAMRDataImplem.H:128
int size() const noexcept
Get size of m_data.
Definition: CD_EBAMRDataImplem.H:66
const std::string getRealm() const noexcept
Returns the string identifier for whatever realm this data is supposed to be allocated over.
Definition: CD_EBAMRDataImplem.H:135
Class for interpolating data to fine grids. Can use constant interpolation or include limiters.
Definition: CD_EBCoarseToFineInterp.H:32
A generic particle class, holding the position and a specified number of real and vector values.
Definition: CD_GenericParticle.H:33
RealVect & position()
Get the particle position.
Definition: CD_GenericParticleImplem.H:47
Factory class for making ItoLayout.
Definition: CD_ItoLayout.H:368
RefCountedPtr< ItoLayout< T > > newLayout(const Vector< RefCountedPtr< ItoSpecies >> &a_species) const
Factory method which creates a new layout from a set of species. This can do automated casting betwee...
Definition: CD_ItoLayoutImplem.H:417
"Iterator" class for going through solvers in an ItoLayout.
Definition: CD_ItoIterator.H:24
virtual bool ok()
Ok or not.
Definition: CD_ItoIteratorImplem.H:71
A particle class for use with ItoSolvers, i.e. drifting Brownian walkers.
Definition: CD_ItoParticle.H:40
RealVect & oldPosition()
Get the old particle position.
Definition: CD_ItoParticleImplem.H:119
Real & weight()
Get particle weight.
Definition: CD_ItoParticleImplem.H:71
WhichContainer
Enum class for distinguishing various types of particle containers.
Definition: CD_ItoSolver.H:49
static void makeBalance(Vector< int > &a_ranks, const Vector< T > &a_loads, const Vector< Box > &a_boxes)
Load balancing, assigning ranks to boxes.
Definition: CD_LoadBalancingImplem.H:31
static void sort(Vector< Vector< Box >> &a_boxes, Vector< Vector< T >> &a_loads, const BoxSorting a_whichSorting)
Sorts boxes and loads over a hierarchy according to some sorting criterion.
Definition: CD_LoadBalancingImplem.H:221
Class for holding computational loads.
Definition: CD_Loads.H:30
virtual void resetLoads() noexcept
Reset loads. Sets all loads to 0.
Definition: CD_Loads.cpp:54
WhichContainer
Enum class for identifying various containers. Only used for interface reasons.
Definition: CD_McPhoto.H:45
Templated class for holding particles on an AMR hierarchy with particle remapping.
Definition: CD_ParticleContainer.H:50
AMRCellParticles< P > & getCellParticles()
Get all cell particles.
Definition: CD_ParticleContainerImplem.H:358
void addParticles(const List< P > &a_particles)
Add particles to container.
Definition: CD_ParticleContainerImplem.H:551
void clear(AMRParticles< P > &a_particles) const
Clear particles on input data holder.
Definition: CD_ParticleContainerImplem.H:1712
void clearParticles()
Clear all particles.
Definition: CD_ParticleContainerImplem.H:1670
AMRParticles< P > & getParticles()
Get all particles on all levels.
Definition: CD_ParticleContainerImplem.H:270
void transferParticles(ParticleContainer< P > &a_otherContainer)
Move particles into this container.
Definition: CD_ParticleContainerImplem.H:914
static void getComputationalParticlesPerCell(EBAMRCellData &a_ppc, const ParticleContainer< P > &a_src) noexcept
Get the number of computational particles per cell.
Definition: CD_ParticleOpsImplem.H:83
Base time stepper class that advances the Ito-KMC-Poisson system of equations. If you want a differen...
Definition: CD_ItoKMCStepper.H:60
virtual void loadBalanceBoxes(Vector< Vector< int >> &a_procs, Vector< Vector< Box >> &a_boxes, const std::string a_realm, const Vector< DisjointBoxLayout > &a_grids, const int a_lmin, const int a_finestLevel) override
Load balance grid boxes for a specified realm.
Definition: CD_ItoKMCStepperImplem.H:5509
virtual void transferCoveredParticles(const SpeciesSubset a_speciesSubset, const EBRepresentation a_representation, const Real a_tolerance) noexcept
Transfer covered particles (i.e., particles inside the EB) from the ItoSolver bulk container to EB co...
Definition: CD_ItoKMCStepperImplem.H:2335
virtual void setupCdr() noexcept
Set up the CDR solvers.
Definition: CD_ItoKMCStepperImplem.H:433
virtual void getParticleStatistics(Real &a_avgParticles, Real &a_sigma, Real &a_minParticles, Real &a_maxParticles, int &a_minRank, int &a_maxRank)
Compute some particle statistics.
Definition: CD_ItoKMCStepperImplem.H:1339
virtual void computePhysicsPlotVariables(EBAMRCellData &a_physicsPlotVars) noexcept
Compute physics plot variables.
Definition: CD_ItoKMCStepperImplem.H:5954
virtual void computeReactiveMeanEnergiesPerCell(EBAMRCellData &a_meanEnergies) noexcept
Compute the mean particle energy in all grid cells.
Definition: CD_ItoKMCStepperImplem.H:3603
virtual void parseRuntimeOptions() noexcept override
Parse runtime configurable options.
Definition: CD_ItoKMCStepperImplem.H:106
virtual void computeElectricField(EBAMRCellData &a_electricField, const phase::which_phase a_phase) const noexcept
Recompute the electric field onto the specified data holder.
Definition: CD_ItoKMCStepperImplem.H:1740
virtual void removeCoveredParticles(const SpeciesSubset a_which, const EBRepresentation a_representation, const Real a_tolerance) noexcept
Remove covered particles (i.e., particles inside the EB)
Definition: CD_ItoKMCStepperImplem.H:2216
virtual void setupRadiativeTransfer() noexcept
Set up the radiative transfer solver.
Definition: CD_ItoKMCStepperImplem.H:452
virtual void registerRealms() noexcept override
Register realms used for the simulation.
Definition: CD_ItoKMCStepperImplem.H:1485
virtual Vector< RefCountedPtr< ItoSolver > > getLoadBalanceSolvers() const noexcept
Get the solvers used for load balancing.
Definition: CD_ItoKMCStepperImplem.H:5453
virtual void computeSpaceChargeDensity() noexcept
Compute the space charge. Calls the other version.
Definition: CD_ItoKMCStepperImplem.H:1767
virtual void setVoltage(const std::function< Real(const Real a_time)> &a_voltage) noexcept
Set voltage used for the simulation.
Definition: CD_ItoKMCStepperImplem.H:1700
virtual void advanceReactionNetwork(const Real a_dt) noexcept
Chemistry advance over time a_dt.
Definition: CD_ItoKMCStepperImplem.H:3738
virtual Real getTime() const noexcept
Get current simulation time.
Definition: CD_ItoKMCStepperImplem.H:1755
virtual int getNumberOfPlotVariables() const noexcept override
Get number of plot variables for the output file.
Definition: CD_ItoKMCStepperImplem.H:864
virtual void remapParticles(const SpeciesSubset a_speciesSubset) noexcept
Remap a subset of ItoSolver particles.
Definition: CD_ItoKMCStepperImplem.H:2459
virtual void parseVerbosity() noexcept
Parse chattiness.
Definition: CD_ItoKMCStepperImplem.H:133
virtual void computeReactiveCdrParticlesPerCell(EBAMRCellData &a_ppc) noexcept
Compute the number of reactive particles per cell for the CDR solvers.
Definition: CD_ItoKMCStepperImplem.H:3498
virtual void registerOperators() noexcept override
Register operators used for the simulation.
Definition: CD_ItoKMCStepperImplem.H:1499
virtual void averageDiffusionCoefficientsCellToFace() noexcept
Average cell-centered diffusion coefficient to faces.
Definition: CD_ItoKMCStepperImplem.H:3314
virtual void parsePlotVariables() noexcept
Parse plot variables.
Definition: CD_ItoKMCStepperImplem.H:176
virtual void parseSuperParticles() noexcept
Parse the desired number of particles per cell.
Definition: CD_ItoKMCStepperImplem.H:212
virtual void writeData(LevelData< EBCellFAB > &a_output, int &a_comp, const EBAMRCellData &a_data, const std::string a_outputRealm, const int a_level, const bool a_interpToCentroids, const bool a_interpGhost) const noexcept
Write data to output. Convenience function.
Definition: CD_ItoKMCStepperImplem.H:1022
virtual void computeDensityGradients() noexcept
Compute grad(phi) and phi for both CDR and Ito species and put the result on the fluid realm.
Definition: CD_ItoKMCStepperImplem.H:1902
virtual void computeEdotJSource(const Real a_dt) noexcept
Compute the energy source term for the various plasma species.
Definition: CD_ItoKMCStepperImplem.H:5781
virtual bool solvePoisson() noexcept
Solve the electrostatic problem.
Definition: CD_ItoKMCStepperImplem.H:1994
virtual Real computeQminu() const noexcept
Compute negative charge.
Definition: CD_ItoKMCStepperImplem.H:5318
virtual void multiplyCdrVelocitiesByMobilities() noexcept
Multiply CDR solver velocities by mobilities.
Definition: CD_ItoKMCStepperImplem.H:2754
virtual void parseRedistributeCDR() noexcept
Parse CDR mass redistribution when assigning reactive products.
Definition: CD_ItoKMCStepperImplem.H:162
virtual void computeCurrentDensity(EBAMRCellData &a_J) noexcept
Compute the current density.
Definition: CD_ItoKMCStepperImplem.H:1942
virtual void writeNumberOfParticlesPerPatch(LevelData< EBCellFAB > &a_output, int &a_icomp, const std::string a_outputRealm, const int a_level) const noexcept
Write number of particles per patch to output holder.
Definition: CD_ItoKMCStepperImplem.H:1089
virtual void parseTimeStepRestrictions() noexcept
Parse time step restrictions.
Definition: CD_ItoKMCStepperImplem.H:309
virtual void fillSecondaryEmissionEB(const Real a_dt) noexcept
Resolve particle injection at EBs.
Definition: CD_ItoKMCStepperImplem.H:4651
virtual void initialSigma() noexcept
Fill surface charge solver with initial data taken from the physics interface.
Definition: CD_ItoKMCStepperImplem.H:679
ItoKMCStepper() noexcept
Default constructor. Sets default options.
Definition: CD_ItoKMCStepperImplem.H:35
virtual void printStepReport() noexcept override
Print a step report. Used by Driver for user monitoring of simulation.
Definition: CD_ItoKMCStepperImplem.H:1154
virtual void depositParticles(const SpeciesSubset a_speciesSubset) noexcept
Deposit a subset of the ItoSolver particles on the mesh.
Definition: CD_ItoKMCStepperImplem.H:2574
virtual Real computePhysicsDt() noexcept
Compute a maximum time step from the physics interface.
Definition: CD_ItoKMCStepperImplem.H:5075
virtual void getMaxMinItoDensity(Real &a_maxDensity, Real &a_minDensity, std::string &a_maxSolver, std::string &a_minSolver) const noexcept
Get maximum density of the Ito species.
Definition: CD_ItoKMCStepperImplem.H:1273
virtual void setupSigma() noexcept
Set up the surface charge solver.
Definition: CD_ItoKMCStepperImplem.H:489
virtual void loadBalanceFluidRealm(Vector< Vector< int >> &a_procs, Vector< Vector< Box >> &a_boxes, const std::string a_realm, const Vector< DisjointBoxLayout > &a_grids, const int a_lmin, const int a_finestLevel) noexcept
Routine called by loadBalanceBoxes and used for particle-based load balancing.
Definition: CD_ItoKMCStepperImplem.H:5684
virtual void setupSolvers() noexcept override
Set up solvers.
Definition: CD_ItoKMCStepperImplem.H:398
virtual void parseOptions() noexcept
Parse options.
Definition: CD_ItoKMCStepperImplem.H:86
virtual void advancePhotons(const Real a_dt) noexcept
Photon advancement routine.
Definition: CD_ItoKMCStepperImplem.H:5374
virtual void preRegrid(const int a_lmin, const int a_oldFinestLevel) noexcept override
Perform pre-regrid operations - storing relevant data from the old grids.
Definition: CD_ItoKMCStepperImplem.H:1547
virtual void loadBalanceParticleRealm(Vector< Vector< int >> &a_procs, Vector< Vector< Box >> &a_boxes, const std::string a_realm, const Vector< DisjointBoxLayout > &a_grids, const int a_lmin, const int a_finestLevel) noexcept
Routine called by loadBalanceBoxes and used for particle-based load balancing.
Definition: CD_ItoKMCStepperImplem.H:5531
virtual void parseDualGrid() noexcept
Parse dual or single realm calculations.
Definition: CD_ItoKMCStepperImplem.H:236
virtual void reconcilePhotoionization() noexcept
Reconcile the results from photoionization reactions.
Definition: CD_ItoKMCStepperImplem.H:4426
virtual Vector< std::string > getPlotVariableNames() const noexcept override
Get plot variable names.
Definition: CD_ItoKMCStepperImplem.H:917
virtual void sortPhotonsByCell(const McPhoto::WhichContainer a_which) noexcept
Sort photons by cells.
Definition: CD_ItoKMCStepperImplem.H:5425
virtual Real computeQplus() const noexcept
Compute positive charge.
Definition: CD_ItoKMCStepperImplem.H:5274
virtual void parseLoadBalance() noexcept
Parse load balancing.
Definition: CD_ItoKMCStepperImplem.H:259
virtual void computeDriftVelocities() noexcept
Compute ItoSolver velocities.
Definition: CD_ItoKMCStepperImplem.H:2780
virtual void getMaxMinCDRDensity(Real &a_maxDensity, Real &a_minDensity, std::string &a_maxSolver, std::string &a_minSolver) const noexcept
Get maximum density of the CDr species.
Definition: CD_ItoKMCStepperImplem.H:1306
virtual void setupPoisson() noexcept
Set up the electrostatic field solver.
Definition: CD_ItoKMCStepperImplem.H:472
virtual void setupIto() noexcept
Set up the Ito particle solvers.
Definition: CD_ItoKMCStepperImplem.H:414
virtual Real computeQsurf() const noexcept
Compute surface charge.
Definition: CD_ItoKMCStepperImplem.H:5362
virtual Real computeDt() override
Compute a time step used for the advance method.
Definition: CD_ItoKMCStepperImplem.H:1371
virtual void synchronizeSolverTimes(const int a_step, const Real a_time, const Real a_dt) noexcept override
Synchronize solver times for all the solvers.
Definition: CD_ItoKMCStepperImplem.H:1135
virtual Real computeTotalCharge() const noexcept
Compute total charge.
Definition: CD_ItoKMCStepperImplem.H:5254
virtual void resolveSecondaryEmissionEB(const Real a_dt) noexcept
Resolve secondary emission at the EB.
Definition: CD_ItoKMCStepperImplem.H:4924
void reconcileParticles(const EBAMRCellData &a_newParticlesPerCell, const EBAMRCellData &a_oldParticlesPerCell, const EBAMRCellData &a_newPhotonsPerCell) const noexcept
Reconcile particles. At the bottom, this will call the physics interface for particle reconciliation.
Definition: CD_ItoKMCStepperImplem.H:4094
virtual void regrid(const int a_lmin, const int a_oldFinestLevel, const int a_newFinestLevel) noexcept override
Regrid methods – puts all data on the new mesh.
Definition: CD_ItoKMCStepperImplem.H:1645
virtual Vector< long int > getCheckpointLoads(const std::string a_realm, const int a_level) const override
Get computational loads to be checkpointed.
Definition: CD_ItoKMCStepperImplem.H:5732
virtual void computeDiffusionCoefficients() noexcept
Compute mesh-based diffusion coefficients for LFA coupling.
Definition: CD_ItoKMCStepperImplem.H:3030
virtual void allocateInternals() noexcept
Allocate "internal" storage.
Definition: CD_ItoKMCStepperImplem.H:524
virtual Real computeMaxElectricField(const phase::which_phase a_phase) const noexcept
Compute the maximum electric field (norm)
Definition: CD_ItoKMCStepperImplem.H:1712
virtual void setCdrVelocityFunctions() noexcept
Set the Cdr velocities to be sgn(charge) * E.
Definition: CD_ItoKMCStepperImplem.H:2720
virtual void postRegrid() noexcept override
Perform post-regrid operations.
Definition: CD_ItoKMCStepperImplem.H:1687
virtual Real computeRelaxationTime() noexcept
Compute the dielectric relaxation time.
Definition: CD_ItoKMCStepperImplem.H:1962
virtual void parseParametersEB() noexcept
Parse parameters related to how we treat particle-EB interaction.
Definition: CD_ItoKMCStepperImplem.H:382
virtual void initialData() noexcept override
Fill solvers with initial data.
Definition: CD_ItoKMCStepperImplem.H:644
virtual void postPlot() noexcept override
Perform post-plot operations.
Definition: CD_ItoKMCStepperImplem.H:1535
virtual ~ItoKMCStepper() noexcept
Destructor.
Definition: CD_ItoKMCStepperImplem.H:79
virtual void computeReactiveItoParticlesPerCell(EBAMRCellData &a_ppc) noexcept
Compute the number of reactive particles per cell.
Definition: CD_ItoKMCStepperImplem.H:3378
virtual void writePlotData(LevelData< EBCellFAB > &a_output, int &a_icomp, const std::string a_outputRealm, const int a_level) const noexcept override
Write plot data to output holder.
Definition: CD_ItoKMCStepperImplem.H:968
virtual void allocate() noexcept override
Allocate storage for solvers.
Definition: CD_ItoKMCStepperImplem.H:506
virtual void parseExitOnFailure() noexcept
Parse exit on failure.
Definition: CD_ItoKMCStepperImplem.H:148
virtual void reconcileCdrDensities(const EBAMRCellData &a_newParticlesPerCell, const EBAMRCellData &a_oldParticlesPerCell, const Real a_dt) noexcept
Reconcile the CDR densities after the reaction network.
Definition: CD_ItoKMCStepperImplem.H:4476
virtual void computeConductivityCell(EBAMRCellData &a_conductivity) noexcept
Compute the cell-centered conductiivty.
Definition: CD_ItoKMCStepperImplem.H:1834
virtual void postCheckpointPoisson() noexcept
Do some post-checkpoint operations for the electrostatic part.
Definition: CD_ItoKMCStepperImplem.H:747
virtual bool loadBalanceThisRealm(const std::string a_realm) const override
Load balancing query for a specified realm. If this returns true for a_realm, load balancing routines...
Definition: CD_ItoKMCStepperImplem.H:5488
virtual void prePlot() noexcept override
Perform pre-plot operations.
Definition: CD_ItoKMCStepperImplem.H:1515
virtual void postInitialize() noexcept override
Post-initialization operations. Default does nothing.
Definition: CD_ItoKMCStepperImplem.H:634
virtual void coarsenCDRSolvers() noexcept
Coarsen data for CDR solvers.
Definition: CD_ItoKMCStepperImplem.H:4625
virtual void postCheckpointSetup() noexcept override
Perform post-checkpoint operations.
Definition: CD_ItoKMCStepperImplem.H:728
virtual void intersectParticles(const SpeciesSubset a_speciesSubset, const bool a_delete, const std::function< void(ItoParticle &)> a_nonDeletionModifier=[](ItoParticle &) -> void { return;}) noexcept
Intersect a subset of the particles with the domain and embedded boundary.
Definition: CD_ItoKMCStepperImplem.H:2033
virtual void computeMobilities() noexcept
Compute mesh-based mobilities for LFA coupling.
Definition: CD_ItoKMCStepperImplem.H:2805
virtual void setItoVelocityFunctions() noexcept
Set the Ito velocity functions. This is sgn(charge) * E.
Definition: CD_ItoKMCStepperImplem.H:2689
virtual void sortPhotonsByPatch(const McPhoto::WhichContainer a_which) noexcept
Sort photons by patch.
Definition: CD_ItoKMCStepperImplem.H:5439
virtual void getPhysicalParticlesPerCell(EBAMRCellData &a_ppc) const noexcept
Get the physical number of particles per cell.
Definition: CD_ItoKMCStepperImplem.H:3356
A particle class that only has a position and a weight.
Definition: CD_PointParticle.H:29
Real & weight()
Get weight.
Definition: CD_PointParticleImplem.H:38
static const std::string Primal
Identifier for perimal realm.
Definition: CD_Realm.H:47
Factory class for RtLayout.
Definition: CD_RtLayout.H:270
RefCountedPtr< RtLayout< T > > newLayout(const Vector< RefCountedPtr< RtSpecies >> &a_species) const
Get a new Layout. This will cast S to a specific class (T)
Definition: CD_RtLayoutImplem.H:457
Iterator class for RtLayout.
Definition: CD_RtIterator.H:24
virtual bool ok()
Check if we can cycle further through the solvers.
Definition: CD_RtIteratorImplem.H:63
Surface ODE solver.
Definition: CD_SurfaceODESolver.H:28
virtual Vector< long int > getCheckpointLoads(const std::string a_realm, const int a_level) const
Get computational loads to be checkpointed.
Definition: CD_TimeStepper.cpp:70
Class which is used for run-time monitoring of events.
Definition: CD_Timer.H:31
void stopEvent(const std::string a_event) noexcept
Stop an event.
Definition: CD_TimerImplem.H:88
void startEvent(const std::string a_event) noexcept
Start an event.
Definition: CD_TimerImplem.H:59
void eventReport(std::ostream &a_outputStream, const bool a_localReportOnly=false) const noexcept
Print all timed events to cout.
Definition: CD_TimerImplem.H:173
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
std::string numberFmt(const long long a_number, char a_sep=',') noexcept
Number formatting method – writes big numbers using an input separator. E.g. the number 123456 is wri...
Definition: CD_DischargeIO.cpp:25
RealVect position(const Location::Cell a_location, const VolIndex &a_vof, const EBISBox &a_ebisbox, const Real &a_dx)
Compute the position (ignoring the "origin) of a Vof.
Definition: CD_LocationImplem.H:20
Real max(const Real &a_input) noexcept
Get the maximum of the input, reduced over MPI ranks (in the Chombo communicator)
Definition: CD_ParallelOpsImplem.H:176
std::pair< Real, int > maxRank(const Real &a_val) noexcept
Get the maximum value and the rank having the maximum value.
Definition: CD_ParallelOpsImplem.H:293
Real average(const Real &a_val) noexcept
Compute the average (across MPI ranks) of the input value.
Definition: CD_ParallelOpsImplem.H:500
Real standardDeviation(const Real &a_value) noexcept
Compute the standard deviation of the input value.
Definition: CD_ParallelOpsImplem.H:512
std::pair< Real, int > minRank(const Real &a_val) noexcept
Get the minimum value and the rank having the minimum value.
Definition: CD_ParallelOpsImplem.H:323
void vectorSum(Vector< Real > &a_data) noexcept
Perform a summation of all the MPI ranks's input data.
Definition: CD_ParallelOpsImplem.H:448
Real min(const Real &a_input) noexcept
Get the minimum of the input, reduced over MPI ranks (in the Chombo communicator)
Definition: CD_ParallelOpsImplem.H:58
constexpr Real eps0
Permittivity of free space.
Definition: CD_Units.H:29
constexpr Real Qe
Elementary charge.
Definition: CD_Units.H:34