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