chombo-discharge
Loading...
Searching...
No Matches
CD_ItoKMCPhysicsImplem.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_ITOKMCPHYSICSIMPLEM_H
14#define CD_ITOKMCPHYSICSIMPLEM_H
15
16// Chombo includes
17#include <ParmParse.H>
18
19// Our includes
20#include <CD_ItoKMCPhysics.H>
22#include <CD_Random.H>
23#include <CD_Units.H>
24#include <CD_DataOps.H>
25#include <CD_NamespaceHeader.H>
26
27using namespace Physics::ItoKMC;
28
30{
31 CH_TIME("ItoKMCPhysics::ItoKMCPhysics");
32
33 m_className = "ItoKMCPhysics";
34
35 m_kmcReactions.clear();
36 m_photoReactions.clear();
37
38 // Some default settings (mostly in case user forgets to call the parsing algorithms).
39 m_isDefined = false;
40 m_debug = true;
41 m_hasKMCSolver = false;
45 m_maxNewPhotons = 32;
46 m_Ncrit = 5;
47 m_eps = 2.0;
48 m_NSSA = 5;
49 m_maxIter = 10;
50 m_SSAlim = 5.0;
51 m_exitTol = 1.E-6;
54}
55
57{
58 CH_TIME("ItoKMCPhysics::~ItoKMCPhysics");
59}
60
61inline void
63{
64 CH_TIME("ItoKMCPhysics::define");
65
66 this->defineSpeciesMap();
67 this->definePhotoPathways();
68
69 // Safety hook -- make sure no one defines reactions using an out-of-range species index.
70#ifndef NDEBUG
71 for (const auto& R : m_kmcReactions) {
72 const auto& lhsReactants = R.getReactants();
73 const auto& rhsReactants = R.getReactiveProducts();
74 const auto& rhsPhotons = R.getNonReactiveProducts();
75
76 for (const auto& r : lhsReactants) {
77 CH_assert(r < m_itoSpecies.size() + m_cdrSpecies.size());
78 }
79 for (const auto& r : rhsReactants) {
80 CH_assert(r < m_itoSpecies.size() + m_cdrSpecies.size());
81 }
82 for (const auto& r : rhsPhotons) {
83 CH_assert(r < m_rtSpecies.size());
84 }
85 }
86#endif
87
88 m_isDefined = true;
89}
90
91inline void
93{
94 CH_TIME("ItoKMCPhysics::defineSpeciesMap");
95
96 const int numItoSpecies = this->getNumItoSpecies();
97 const int numCdrSpecies = this->getNumCdrSpecies();
98
99 int species = 0;
100 for (int i = 0; i < numItoSpecies; i++, species++) {
101 m_speciesMap.emplace(species, std::make_pair(SpeciesType::Ito, i));
102 }
103
104 for (int i = 0; i < numCdrSpecies; i++, species++) {
105 m_speciesMap.emplace(species, std::make_pair(SpeciesType::CDR, i));
106 }
107}
108
109inline void
111{
112 CH_TIME("ItoKMCPhysics::defineKMC");
113
114 CH_assert(!m_hasKMCSolver);
115
116 // Deep copy of reaction rates
118 for (const auto& r : m_kmcReactions) {
119 m_kmcReactionsThreadLocal.emplace_back(std::make_shared<const KMCReaction>(r));
120 }
121
124 m_kmcState.define(m_itoSpecies.size() + m_cdrSpecies.size(), m_rtSpecies.size());
126
128
129 m_hasKMCSolver = true;
130}
131
132inline void
134{
135 CH_TIME("ItoKMCPhysics::killKMC");
136
137 CH_assert(m_hasKMCSolver);
138
141 m_kmcState.define(0, 0);
143
144 m_kmcPropensityScratch.resize(0);
145
146 m_hasKMCSolver = false;
147}
148
149inline void
151{
152 CH_TIME("ItoKMCPhysics::definePhotoPathways");
153
154 // Build a temporary list of pathways. I.e. restructure the list of reactions
155 //
156 // Y1 -> A
157 // Y1 -> B
158 // Y2 -> C
159 // Y2 -> D
160 //
161 // into separate lists for Y1, Y2, ....
162 //
163 std::map<int, std::vector<std::pair<int, Real>>> pathways;
164
165 for (int i = 0; i < m_photoReactions.size(); i++) {
167
168 const size_t& src = r.getSourcePhoton();
169 const Real efficiency = r.getEfficiency();
170
171 pathways[src].emplace_back(std::make_pair(i, efficiency));
172 }
173
174 // Go through the temporary pathways list and compute the relative efficiencies of one of the
175 // photons triggering a reaction. The relative efficiencies are given by
176 //
177 // p(i) = R(i)/sum_j R(j).
178 //
179 for (const auto& p : pathways) {
180 const int photoSpecies = p.first;
181 const std::vector<std::pair<int, Real>> reactionsAndEfficiencies = p.second;
182
183 std::map<int, int> localToGlobalMap;
184 std::list<double> efficiencies;
185 double sumEfficiencies = 0.0;
186
187 for (int i = 0; i < reactionsAndEfficiencies.size(); i++) {
188 sumEfficiencies += (double)reactionsAndEfficiencies[i].second;
189 }
190
191 for (int i = 0; i < reactionsAndEfficiencies.size(); i++) {
192 localToGlobalMap.emplace(i, reactionsAndEfficiencies[i].first);
193 efficiencies.emplace_back((double)reactionsAndEfficiencies[i].second / sumEfficiencies);
194 }
195
196 std::discrete_distribution<int> distribution(efficiencies.begin(), efficiencies.end());
197
198 m_photoPathways.insert(std::make_pair((int)photoSpecies, std::make_pair(distribution, localToGlobalMap)));
199 }
200}
201
202inline const std::map<int, std::pair<SpeciesType, int>>&
204{
205 CH_TIME("ItoKMCPhysics::getSpeciesMap");
206
207 return m_speciesMap;
208}
209
210inline void
212{
213 CH_TIME("ItoKMCPhysics::parseRuntimeOptions");
214
215 this->parsePPC();
216 this->parseDebug();
217 this->parseAlgorithm();
218}
219
220inline void
222{
223 CH_TIME("ItoKMCPhysics::parsePPC");
224
225 ParmParse pp(m_className.c_str());
226
227 pp.get("max_new_particles", m_maxNewParticles);
228 pp.get("max_new_photons", m_maxNewPhotons);
229 pp.get("increment_weights", m_incrementNewParticles);
230}
231
232inline void
234{
235 CH_TIME("ItoKMCPhysics::parseDebug");
236
237 ParmParse pp(m_className.c_str());
238
239 pp.get("debug", m_debug);
240}
241
242inline void
244{
245 CH_TIME("ItoKMCPhysics::parseAlgorithm");
246
247 ParmParse pp(m_className.c_str());
248
249 std::string str;
250
251 pp.get("algorithm", str);
252 pp.get("crit_num", m_Ncrit);
253 pp.get("SSA_num", m_NSSA);
254 pp.get("prop_eps", m_eps);
255 pp.get("SSA_lim", m_SSAlim);
256 pp.get("max_iter", m_maxIter);
257 pp.get("exit_tolerance", m_exitTol);
258
259 if (str == "ssa") {
261 }
262 else if (str == "explicit_euler") {
264 }
265 else if (str == "midpoint") {
267 }
268 else if (str == "prc") {
270 }
271 else if (str == "implicit_euler") {
273 }
274 else if (str == "hybrid_explicit_euler") {
276 }
277 else if (str == "hybrid_midpoint") {
279 }
280 else if (str == "hybrid_prc") {
282 }
283 else if (str == "hybrid_implicit_euler") {
285 }
286 else {
287 MayDay::Error("ItoKMCPhysics::parseAlgorithm - unknown algorithm requested");
288 }
289}
290
291inline const Vector<RefCountedPtr<ItoSpecies>>&
293{
294 return m_itoSpecies;
295}
296
297inline const Vector<RefCountedPtr<CdrSpecies>>&
299{
300 return m_cdrSpecies;
301}
302
303inline const Vector<RefCountedPtr<RtSpecies>>&
305{
306 return m_rtSpecies;
307}
308
309inline const Vector<DiffusionFunction>&
314
315inline int
317{
318 return m_itoSpecies.size();
319}
320
321inline int
323{
324 return m_cdrSpecies.size();
325}
326
327inline int
329{
330 return m_itoSpecies.size() + m_cdrSpecies.size();
331}
332
333inline int
335{
336 return m_rtSpecies.size();
337}
338
339inline Real
340ItoKMCPhysics::initialSigma(const Real a_time, const RealVect& a_pos) const
341{
342 return 0.0;
343}
344
345inline void
346ItoKMCPhysics::advanceKMC(Vector<FPR>& a_numParticles,
347 Vector<FPR>& a_numNewPhotons,
348 Real& a_physicsDt,
349 const Vector<Real>& a_phi,
350 const Vector<RealVect>& a_gradPhi,
351 const Real a_dt,
352 const RealVect a_E,
353 const RealVect a_pos,
354 const Real a_dx,
355 const Real a_kappa) const
356{
357 // Note: This is called PER GRID CELL, i.e. within OpenMP parallel regions. For this reason the KMC solver
358 // must be defined through defineKMC() (which must be later killed).
359 CH_assert(m_isDefined);
360 CH_assert(m_hasKMCSolver);
361
362 std::vector<FPR>& kmcParticles = m_kmcState.getReactiveState();
363 std::vector<FPR>& kmcPhotons = m_kmcState.getNonReactiveState();
364
365 for (size_t i = 0; i < a_numParticles.size(); i++) {
366 kmcParticles[i] = a_numParticles[i];
367 }
368
369 for (auto& p : kmcPhotons) {
370 p = 0LL;
371 }
372
373 // Lambda function used for computing charge before and after reactions. Used only in debug mode
374 // for ensuring that nothing goes wrong with charge conservation in the chemistry integration.
375 auto computeCharge = [&]() -> long long {
376 long long Q = 0.0;
377 for (int i = 0; i < kmcParticles.size(); i++) {
378 const SpeciesType& speciesType = m_speciesMap.at(i).first;
379 const int& speciesIndex = m_speciesMap.at(i).second;
380
381 int Z = 0;
382
383 switch (speciesType) {
384 case SpeciesType::Ito: {
385 Z = m_itoSpecies[speciesIndex]->getChargeNumber();
386
387 break;
388 }
389 case SpeciesType::CDR: {
390 Z = m_cdrSpecies[speciesIndex]->getChargeNumber();
391
392 break;
393 }
394 default: {
395 MayDay::Abort("ItoKMCPhysics::advanceKMC -- logic bust in computeCharge()");
396
397 break;
398 }
399 }
400
401 Q += llround(kmcParticles[i]) * Z;
402 }
403
404 return Q;
405 };
406
407 // In debug mode, compute the total charge.
408 const long long chargeBefore = m_debug ? computeCharge() : 0LL;
409
410 // Update the reaction rates to be used by the KMC solver.
411 this->updateReactionRates(m_kmcReactionsThreadLocal, a_E, a_pos, a_phi, a_gradPhi, a_dt, a_dx, a_kappa);
412
413 // Run the KMC solver.
414 switch (m_algorithm) {
415 case Algorithm::SSA: {
417
418 break;
419 }
422
423 break;
424 }
425 case Algorithm::Midpoint: {
427
428 break;
429 }
430 case Algorithm::PRC: {
432
433 break;
434 }
437
438 break;
439 }
442
443 break;
444 }
447
448 break;
449 }
452
453 break;
454 }
457
458 break;
459 }
460 default: {
461 MayDay::Error("ItoKMCPhysics::advanceKMC - logic bust");
462 }
463 }
464
465 // Put KMC back into ItoKMC
466 for (size_t i = 0; i < a_numParticles.size(); i++) {
467 a_numParticles[i] = (FPR)kmcParticles[i];
468 }
469 for (size_t i = 0; i < a_numNewPhotons.size(); i++) {
470 a_numNewPhotons[i] = (FPR)kmcPhotons[i];
471 }
472
473 const long long chargeAfter = m_debug ? computeCharge() : 0LL;
474
475 if (chargeAfter != chargeBefore) {
476 MayDay::Warning("ItoKMCPhysics::advanceKMC -- charge not conserved!");
477 }
478
479 // This loop is for isolating reactions that the user will explicitly ask to fire before computing the physics-based
480 // time step. It exists because if there are no electrons but lots of ions, one may get a time step that is too large
481 // because X/|sum mu| is zero for the electrons. Similarly, one may get a time step that is too small away from
482 // ionization regions because X/|sum mu| may be tiny if there is, say, 1 electron but lots of detachment.
483 // Fills the reusable propensity buffer rather than returning one, so that the probes below do not
484 // allocate once per reaction per grid cell.
485 auto fillPropensitiesDt = [&](const KMCState& a_state) -> void {
486 for (size_t i = 0; i < m_kmcReactionsThreadLocal.size(); i++) {
488 }
489 };
490
491 // Do a time step limitation on the complete state, using the user-specified reactions.
492 fillPropensitiesDt(m_kmcState);
493
494 a_physicsDt = std::min(a_physicsDt,
496
497 // Go through reactions that are potentially detaching species and do the calculation again.
498 for (const auto& reaction : m_kmcReactionsThreadLocal) {
499 const auto N = reaction->computeCriticalNumberOfReactions(m_kmcState);
500
501 // Trigger on both the reactions and the reactions with completely consumed reactants. The state
502 // is only copied inside the branch because that is the only place it is read -- copying it above
503 // the test meant paying for a copy per reaction per grid cell, including for the reactions that
504 // never enter here.
505 if (N < std::numeric_limits<FPR>::max()) {
507
508 reaction->advanceState(m_kmcStateScratch, N);
509
510 fillPropensitiesDt(m_kmcStateScratch);
511
512 a_physicsDt = std::min(
513 a_physicsDt,
515 }
516 }
517}
518
519inline void
521 const Vector<FPR>& a_newNumParticles,
522 const Vector<FPR>& a_oldNumParticles,
523 const RealVect a_electricField,
524 const RealVect a_cellPos,
525 const RealVect a_centroidPos,
526 const RealVect a_lo,
527 const RealVect a_hi,
528 const RealVect a_bndryCentroid,
529 const RealVect a_bndryNormal,
530 const Real a_dx,
531 const Real a_kappa) const noexcept
532{
533 CH_assert(m_isDefined);
534 CH_assert(a_particles.size() == a_newNumParticles.size());
535 CH_assert(a_oldNumParticles.size() == a_newNumParticles.size());
536
537 if (m_debug) {
538 for (int i = 0; i < a_particles.size(); i++) {
539 const FPR& numNew = a_newNumParticles[i];
540 const FPR& numOld = a_oldNumParticles[i];
541
542 if (numNew < (FPR)0) {
543 MayDay::Warning("ItoKMCPhysics::reconcileParticles - new number of particles is < 0 (overflow issue?)");
544 }
545 else if (static_cast<long long>(numNew) < 0LL) {
546 MayDay::Warning("ItoKMCPhysics::reconcileParticles - integer overflow!");
547 }
548
549 if (numOld < 0) {
550 MayDay::Warning("ItoKMCPhysics::reconcileParticles - old number of particles is < 0");
551 }
552 else if (static_cast<long long>(numOld) < 0LL) {
553 MayDay::Warning("ItoKMCPhysics::reconcileParticles - integer overflow for old particles!");
554 }
555 }
556 }
557
558 // Compute the upstream position of the particles (which is usually the electrons).
559 bool hasDownstream = false;
560 RealVect upstreamPosition = RealVect::Zero;
561 RealVect upstreamLo = -0.5 * RealVect::Unit;
562 RealVect upstreamHi = +0.5 * RealVect::Unit;
563 RealVect v = RealVect::Zero;
564 int Z = 0;
565
566 if (m_particlePlacement == ParticlePlacement::Downstream) {
567 CH_assert(m_downstreamSpecies >= 0);
568
569 Z = m_itoSpecies[m_downstreamSpecies]->getChargeNumber();
570
571 if (Z != 0) {
572 v = Z * a_electricField;
573 v = v / v.vectorLength();
574 }
575
576 hasDownstream = this->computeUpstreamPosition(upstreamPosition,
577 upstreamLo,
578 upstreamHi,
579 Z,
580 *a_particles[m_downstreamSpecies],
581 a_electricField,
582 a_cellPos,
583 a_dx);
584 }
585
586 for (int i = 0; i < a_particles.size(); i++) {
587 const long long diff = static_cast<long long>(a_newNumParticles[i] - a_oldNumParticles[i]);
588
589 if (diff > 0LL) {
590 const long long numParticles = static_cast<long long>(a_particles[i]->size());
591
592 if (numParticles > 0LL && m_incrementNewParticles) {
593 // If the cell already contains particles, partition the new particle weights and increment the existing
594 // particles
595 const std::vector<long long> particleWeights = ParticleManagement::partitionParticleWeights(diff, numParticles);
596
597 ParticleSoA<ItoParticle>& particles = *a_particles[i];
598
599 for (std::size_t j = 0; j < particles.size() && j < particleWeights.size(); j++) {
600 particles.weight(j) += 1.0 * particleWeights[j];
601 }
602 }
603 else {
604 // Adding new particles, which is fairly simple. Just choose weights for the particles and go with one of the
605 // placement algorithms.
606 const std::vector<long long> particleWeights = ParticleManagement::partitionParticleWeights(
607 diff,
608 static_cast<long long>(m_maxNewParticles));
609
610 // Total weight of the particles that were already in the cell. Only the leading numParticles entries are
611 // eligible parents -- the appends below grow the container, and a freshly created particle must not become
612 // a parent for its siblings. Computed once here rather than per new particle.
613 Real parentWeightSum = 0.0;
614
615 if (m_particlePlacement == ParticlePlacement::Parent) {
616
617 for (long long j = 0; j < numParticles; j++) {
618 parentWeightSum += a_particles[i]->weight(static_cast<std::size_t>(j));
619 }
620 }
621
622 for (const auto& w : particleWeights) {
623 RealVect x = RealVect::Zero;
624
625 // Figure out where to place the particles.
626 switch (m_particlePlacement) {
627 case ParticlePlacement::Centroid: {
628 x = a_cellPos + a_centroidPos * a_dx;
629
630 break;
631 }
632 case ParticlePlacement::Random: {
633 x = Random::randomPosition(a_cellPos, a_lo, a_hi, a_bndryCentroid, a_bndryNormal, a_dx, a_kappa);
634
635 break;
636 }
637 case ParticlePlacement::Parent: {
638 // Ionization happens at the electrons, so put the new particle on top of one of the particles that were
639 // already there, drawn with probability proportional to the parent weight. This is the placement that
640 // introduces no sub-grid transport of its own: it neither scatters the new weight across the cell (which
641 // acts as a numerical diffusion) nor pulls it towards the cell centre (which acts as a drag on the front).
642 // The inherited position is by construction on the fluid side of the EB, so no cut-cell test is needed.
643 //
644 // Falls back to a random position when there is nothing to inherit from, e.g. photoionization products
645 // landing in a cell that holds no particles of this species yet.
646 if (numParticles > 0LL && parentWeightSum > 0.0) {
647 const Real r = Random::getUniformReal01() * parentWeightSum;
648
649 Real runningSum = 0.0;
650 long long parent = numParticles - 1;
651
652 for (long long j = 0; j < numParticles; j++) {
653 runningSum += a_particles[i]->weight(static_cast<std::size_t>(j));
654
655 if (runningSum >= r) {
656 parent = j;
657
658 break;
659 }
660 }
661
662 x = a_particles[i]->position(static_cast<std::size_t>(parent));
663 }
664 else {
665 x = Random::randomPosition(a_cellPos, a_lo, a_hi, a_bndryCentroid, a_bndryNormal, a_dx, a_kappa);
666 }
667
668 break;
669 }
670 case ParticlePlacement::Downstream: {
671 if (hasDownstream) {
672 x = Random::randomPosition(upstreamLo, upstreamHi, upstreamPosition, v);
673
674 if ((x - a_bndryCentroid).dotProduct(a_bndryNormal) < 0.0) {
675 x = upstreamPosition;
676 }
677
678 x = a_cellPos + x * a_dx;
679 }
680 else {
681
682 x = Random::randomPosition(a_cellPos, a_lo, a_hi, a_bndryCentroid, a_bndryNormal, a_dx, a_kappa);
683 }
684
685 break;
686 }
687 default: {
688 MayDay::Error("ItoKMCPhysics::reconcileParticles - logic bust");
689
690 break;
691 }
692 }
693
694 a_particles[i]->append(x, 1.0 * w, ItoParticle{});
695 }
696 }
697 }
698 else if (diff < 0LL) {
699 // Removing particles is a bit more difficult because we need to manipulate weights.
700 this->removeParticles(*a_particles[i], -diff);
701 }
702 }
703}
704
705inline bool
707 RealVect& a_lo,
708 RealVect& a_hi,
709 const int& a_Z,
710 const ParticleSoA<ItoParticle>& a_particles,
711 const RealVect& a_electricField,
712 const RealVect& a_cellPos,
713 const Real& a_dx) const noexcept
714{
715 CH_assert(a_dx > 0.0);
716
717 a_pos = RealVect::Zero;
718 a_lo = -0.5 * RealVect::Unit;
719 a_hi = +0.5 * RealVect::Unit;
720
721 const int Z = (a_Z > 0) ? 1 : (a_Z < 0) ? -1 : 0;
722 const RealVect E = a_electricField / a_electricField.vectorLength();
723 const RealVect v = Z * E;
724
725 // Upstream can only exist if there is a velocity direction and there are particles.
726 const bool hasDownstream = (a_particles.size() > 0) && (v.vectorLength() > 0.0);
727
728 if (hasDownstream) {
729
730 Real D = std::numeric_limits<Real>::max();
731
732 for (std::size_t i = 0; i < a_particles.size(); i++) {
733 const RealVect x = (a_particles.position(i) - a_cellPos) / a_dx;
734 const Real d = x.dotProduct(v);
735
736 if (d < D) {
737 D = d;
738 a_pos = x;
739 }
740 }
741
742 DataOps::computeMinValidBox(a_lo, a_hi, v, a_pos);
743 }
744
745 if (m_debug) {
746 for (int dir = 0; dir < SpaceDim; dir++) {
747 if (a_pos[dir] > 0.5 || a_pos[dir] < -0.5) {
748 MayDay::Abort("ItoKMCPhysics::computeUpstreamPosition - logic bust");
749 }
750 }
751 }
752
753 return hasDownstream;
754}
755
756inline void
757ItoKMCPhysics::removeParticles(ParticleSoA<ItoParticle>& a_particles, const long long a_numParticlesToRemove) const
758{
759 constexpr long long zero = 0LL;
760
761 CH_assert(m_isDefined);
762 CH_assert(a_numParticlesToRemove >= zero);
763
764 // Quick lambda for getting total particle weight. Used for debugging.
765 auto getTotalWeight = [&]() -> long long {
766 long long W = zero;
767
768 for (std::size_t i = 0; i < a_particles.size(); i++) {
769 W += llround(a_particles.weight(i));
770
771 if (a_particles.weight(i) < 1.0) {
772 MayDay::Error("ItoKMCPhysics::removeParticles -- bad particle mass!");
773 }
774 }
775
776 return W;
777 };
778
779 if (a_numParticlesToRemove > zero) {
780
781 // For debugging only.
782 long long totalWeightBefore = 0;
783 long long totalWeightAfter = 0;
784
785 // Debug hook, compute the total particle weight before we start removing weights.
786 if (m_debug) {
787 totalWeightBefore = getTotalWeight();
788
789 if (totalWeightBefore < a_numParticlesToRemove) {
790 MayDay::Error("ItoKMCPhysics::removeParticles: logic bust (trying to remove too many particles)");
791 }
792 }
793
794 // Remove physical particles.
795 ParticleManagement::removePhysicalParticles(a_particles, a_numParticlesToRemove);
796
797 // Remove particles with too low weight.
798 ParticleManagement::deleteParticles(a_particles, std::numeric_limits<Real>::min());
799
800 // Debug hook, make sure that particle weights are > 0 AND we've removed the desired
801 // particle weight.
802 if (m_debug) {
803 totalWeightAfter = getTotalWeight();
804
805 const long long errDiff = std::abs(totalWeightBefore - totalWeightAfter) - a_numParticlesToRemove;
806 if (std::abs(errDiff) != zero) {
807
808 pout() << "ItoKMCPhysics::removeParticles: Total weight before = " << totalWeightBefore << endl;
809 pout() << "ItoKMCPhysics::removeParticles: Total weight after = " << totalWeightAfter << endl;
810 pout() << "ItoKMCPhysics::removeParticles: Should have removed = " << a_numParticlesToRemove << endl;
811 pout() << "ItoKMCPhysics::removeParticles: Error = " << errDiff << endl;
812
813 MayDay::Abort("ItoKMCPhysics::removeParticles - incorrect mass removed");
814 }
815 }
816 }
817}
818
819inline void
821 const Vector<FPR>& a_numNewPhotons,
822 const RealVect a_cellPos,
823 const RealVect a_centroidPos,
824 const RealVect a_lo,
825 const RealVect a_hi,
826 const RealVect a_bndryCentroid,
827 const RealVect a_bndryNormal,
828 const Real a_dx,
829 const Real a_kappa) const noexcept
830{
831 CH_assert(m_isDefined);
832
833 for (int i = 0; i < a_newPhotons.size(); i++) {
834 if (a_numNewPhotons[i] > 0LL) {
835
836 const std::vector<long long> photonWeights = ParticleManagement::partitionParticleWeights(
837 static_cast<long long>(a_numNewPhotons[i]),
838 static_cast<long long>(m_maxNewPhotons));
839
840 for (const auto& w : photonWeights) {
841 const RealVect x = Random::randomPosition(a_cellPos, a_lo, a_hi, a_bndryCentroid, a_bndryNormal, a_dx, a_kappa);
842 const RealVect v = Units::c * Random::getDirection();
843 const Real kappa = m_rtSpecies[i]->getAbsorptionCoefficient(x);
844
845 a_newPhotons[i]->append(
846 x,
847 1.0 * w,
848 Photon{
849 static_cast<ParticleReal>(kappa),
850 D_DECL(static_cast<ParticleReal>(v[0]), static_cast<ParticleReal>(v[1]), static_cast<ParticleReal>(v[2]))});
851 }
852 }
853 }
854}
855
856inline void
858 Vector<ParticleSoA<NoPayload>*>& a_cdrParticles,
859 const Vector<ParticleSoA<Photon>*>& a_absorbedPhotons) const noexcept
860{
861 CH_assert(m_isDefined);
862 CH_assert(a_itoParticles.size() == m_itoSpecies.size());
863 CH_assert(a_cdrParticles.size() == m_cdrSpecies.size());
864
865 for (int i = 0; i < a_absorbedPhotons.size(); i++) {
866 if (m_photoPathways.find(i) != m_photoPathways.end()) {
867 std::discrete_distribution<int> d = m_photoPathways.at(i).first;
868 const std::map<int, int>& localToGlobalMap = m_photoPathways.at(i).second;
869
870 const ParticleSoA<Photon>& absorbedPhotons = *a_absorbedPhotons[i];
871
872 for (std::size_t p = 0; p < absorbedPhotons.size(); p++) {
873 const RealVect x = absorbedPhotons.position(p);
874 const Real w = absorbedPhotons.weight(p);
875
876 // Determine the photo-reaction type.
877 const int localReaction = Random::get(d);
878 const int globalReaction = localToGlobalMap.at(localReaction);
879
880 const ItoKMCPhotoReaction& photoReaction = m_photoReactions[globalReaction];
881 const std::list<size_t>& plasmaTargets = photoReaction.getTargetSpecies();
882
883 for (const auto& t : plasmaTargets) {
884 const SpeciesType& type = m_speciesMap.at(t).first;
885 const int& localIndex = m_speciesMap.at(t).second;
886
887 if (type == SpeciesType::Ito) {
888 a_itoParticles[localIndex]->append(x, w, ItoParticle{});
889 }
890 else if (type == SpeciesType::CDR) {
891 a_cdrParticles[localIndex]->append(x, w);
892 }
893 else {
894 MayDay::Error("CD_ItoKMCPhysics.H - logic bust in reconcilePhotoionization");
895 }
896 }
897 }
898 }
899 }
900}
901
902inline RealVect
903ItoKMCPhysics::noDiffusion(const ItoParticle& a_particle, const Real a_dt) const noexcept
904{
905 return RealVect::Zero;
906}
907
908inline RealVect
909ItoKMCPhysics::isotropicDiffusion(const ItoParticle& a_particle, const Real a_dt) const noexcept
910{
911 RealVect r = RealVect::Zero;
912
913 for (int dir = 0; dir < SpaceDim; dir++) {
914 r[dir] = Random::getNormal01();
915 }
916
917 return sqrt(2.0 * a_particle.diffusion * a_dt) * r;
918}
919
920inline RealVect
921ItoKMCPhysics::forwardIsotropicDiffusion(const ItoParticle& a_particle, const Real a_dt) const noexcept
922{
923 RealVect hop = this->isotropicDiffusion(a_particle, a_dt);
924
925 const RealVect v = RealVect(D_DECL(a_particle.vx, a_particle.vy, a_particle.vz));
926
927 if (v != RealVect::Zero) {
928 const RealVect u = v / v.vectorLength();
929 const Real d = u.dotProduct(hop);
930
931 hop -= std::min(d, 0.0) * u;
932 }
933
934 return hop;
935}
936
937#include <CD_NamespaceFooter.H>
938
939#endif
Agglomeration of useful data operations.
Declaration of the Physics::ItoKMC::ItoKMCPhysics abstract base class.
Real FPR
Floating-point type used to represent particle counts in the KMC state.
Definition CD_ItoKMCPhysics.H:45
SpeciesType
Tag for distinguishing species solved with an Ito diffusion or CDR fluid formalism.
Definition CD_ItoKMCPhysics.H:71
@ ImplicitEuler
Implicit Euler tau leaping.
@ Midpoint
Gillespie's midpoint method.
@ ExplicitEuler
Regular tau leaping.
@ PRC
Hu and Li's Poisson random correction method.
Namespace containing various particle management utilities.
CD_PARTICLE_REAL ParticleReal
Floating-point type a user may use for payload columns.
Definition CD_ParticleSoA.H:156
File containing some useful static methods related to random number generation.
Declaration of various useful units.
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
Declaration of a "dual state" for advancing with the Kinetic Monte Carlo module.
Definition CD_KMCDualState.H:32
State & getNonReactiveState() noexcept
Get modifiable non-reactive state.
Definition CD_KMCDualStateImplem.H:114
void define(const size_t a_numReactiveSpecies, const size_t a_numNonReactiveSpecies) noexcept
Define function.
Definition CD_KMCDualStateImplem.H:37
State & getReactiveState() noexcept
Get modifiable reactive state.
Definition CD_KMCDualStateImplem.H:100
void setSolverParameters(T a_numCrit, T a_numSSA, T a_maxIter, Real a_eps, Real a_SSAlim, Real a_exitTol) noexcept
Set solver parameters.
Definition CD_KMCSolverImplem.H:59
Real computeDt(const State &a_state, const ReactionList &a_reactions, const std::vector< Real > &a_propensities, Real a_epsilon) const noexcept
Compute a time step using the leap condition on the mean value.
Definition CD_KMCSolverImplem.H:375
void define(const ReactionList &a_reactions) noexcept
Define function. Sets the reactions.
Definition CD_KMCSolverImplem.H:49
void advanceSSA(State &a_state, Real a_dt) const noexcept
Advance with the SSA over the input time. This can end up using substepping.
Definition CD_KMCSolverImplem.H:502
void advanceHybrid(State &a_state, Real a_dt, const KMCLeapPropagator &a_leapPropagator=KMCLeapPropagator::ExplicitEuler) const noexcept
Advance using Cao et. al. hybrid algorithm over the input time. This can end up using substepping.
Definition CD_KMCSolverImplem.H:919
void advanceTau(State &a_state, const Real &a_dt, const KMCLeapPropagator &a_leapPropagator=KMCLeapPropagator::ExplicitEuler) const noexcept
Advance using a specified tau-leaping algorithm.
Definition CD_KMCSolverImplem.H:839
Arena-backed Struct-of-Arrays particle container for a single grid patch.
Definition CD_ParticleSoA.H:655
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
Reaction class for describing photoionization in ItoKMCPhysics.
Definition CD_ItoKMCPhotoReaction.H:32
const Real & getEfficiency() const noexcept
Get the reaction efficiency.
Definition CD_ItoKMCPhotoReactionImplem.H:60
const size_t & getSourcePhoton() const noexcept
Get the source photon species index.
Definition CD_ItoKMCPhotoReactionImplem.H:48
const std::list< size_t > & getTargetSpecies() const noexcept
Get the plasma product species indices.
Definition CD_ItoKMCPhotoReactionImplem.H:54
int m_maxNewParticles
Maximum new number of particles generated by the chemistry advance.
Definition CD_ItoKMCPhysics.H:561
int m_NSSA
Solver setting for the Cao et. al algorithm.
Definition CD_ItoKMCPhysics.H:579
bool m_incrementNewParticles
If true, increment onto existing particles rather than creating new ones.
Definition CD_ItoKMCPhysics.H:465
Vector< RefCountedPtr< RtSpecies > > m_rtSpecies
List of solver-tracked photon species.
Definition CD_ItoKMCPhysics.H:549
int m_downstreamSpecies
An internal integer describing which species is the "ionizing" species.
Definition CD_ItoKMCPhysics.H:556
virtual void updateReactionRates(std::vector< std::shared_ptr< const KMCReaction > > &a_kmcReactions, const RealVect a_E, const RealVect a_pos, const Vector< Real > &a_phi, const Vector< RealVect > &a_gradPhi, const Real a_dt, const Real a_dx, const Real a_kappa) const noexcept=0
Update reaction rates.
RealVect forwardIsotropicDiffusion(const ItoParticle &a_particle, const Real a_dt) const noexcept
Quasi-isotropic diffusion function for a particle which does not permit backward diffusion.
Definition CD_ItoKMCPhysicsImplem.H:921
Vector< DiffusionFunction > m_itoDiffusionFunctions
Diffusion functions for the various Ito species.
Definition CD_ItoKMCPhysics.H:534
Real m_eps
Solver setting for the Cao et. al. algorithm.
Definition CD_ItoKMCPhysics.H:596
bool m_debug
Turn on/off debugging.
Definition CD_ItoKMCPhysics.H:455
std::vector< Real > m_reactiveDtFactors
List of reactions that are a part of the time step limitation.
Definition CD_ItoKMCPhysics.H:516
int m_maxNewPhotons
Maximum new number of photons generated by the chemistry advance.
Definition CD_ItoKMCPhysics.H:566
void reconcilePhotons(Vector< ParticleSoA< Photon > * > &a_newPhotons, const Vector< FPR > &a_numNewPhotons, const RealVect a_cellPos, const RealVect a_centroidPos, const RealVect a_lo, const RealVect a_hi, const RealVect a_bndryCentroid, const RealVect a_bndryNormal, const Real a_dx, const Real a_kappa) const noexcept
Generate new photons.
Definition CD_ItoKMCPhysicsImplem.H:820
void defineKMC() const noexcept
Define the KMC solver and state.
Definition CD_ItoKMCPhysicsImplem.H:110
RealVect isotropicDiffusion(const ItoParticle &a_particle, const Real a_dt) const noexcept
Isotropic diffusion function for a particle.
Definition CD_ItoKMCPhysicsImplem.H:909
void advanceKMC(Vector< FPR > &a_numParticles, Vector< FPR > &a_numNewPhotons, Real &a_physicsDt, const Vector< Real > &a_phi, const Vector< RealVect > &a_gradPhi, const Real a_dt, const RealVect a_E, const RealVect a_pos, const Real a_dx, const Real a_kappa) const
Advance the reaction network using the KMC algorithm.
Definition CD_ItoKMCPhysicsImplem.H:346
std::string m_className
Class name. Used for options parsing.
Definition CD_ItoKMCPhysics.H:450
const Vector< DiffusionFunction > & getItoDiffusionFunctions() const noexcept
Get diffusion functions for all Ito species.
Definition CD_ItoKMCPhysicsImplem.H:310
std::vector< ItoKMCPhotoReaction > m_photoReactions
List of photoionization reactions.
Definition CD_ItoKMCPhysics.H:511
const Vector< RefCountedPtr< RtSpecies > > & getRtSpecies() const
Get all photon species.
Definition CD_ItoKMCPhysicsImplem.H:304
Vector< RefCountedPtr< ItoSpecies > > m_itoSpecies
List of solver-tracked particle drift-diffusion species.
Definition CD_ItoKMCPhysics.H:539
Vector< RefCountedPtr< CdrSpecies > > m_cdrSpecies
List of solver-tracked fluid drift-diffusion species.
Definition CD_ItoKMCPhysics.H:544
virtual Real initialSigma(const Real a_time, const RealVect &a_pos) const
Set initial surface charge. Default is 0, override if you want.
Definition CD_ItoKMCPhysicsImplem.H:340
Real m_exitTol
Exit tolerance for implicit KMC-leaping algorithms.
Definition CD_ItoKMCPhysics.H:601
virtual ~ItoKMCPhysics() noexcept
Destructor. Does nothing.
Definition CD_ItoKMCPhysicsImplem.H:56
static thread_local KMCState m_kmcStateScratch
Perturbed KMC state used by the time step tail of advanceKMC.
Definition CD_ItoKMCPhysics.H:488
static thread_local KMCSolverType m_kmcSolver
Kinetic Monte Carlo solver used in advanceReactionNetwork.
Definition CD_ItoKMCPhysics.H:475
void defineSpeciesMap() noexcept
Build internal representation of how we distinguish the Ito and CDR solvers.
Definition CD_ItoKMCPhysicsImplem.H:92
const Vector< RefCountedPtr< ItoSpecies > > & getItoSpecies() const
Get all particle drift-diffusion species.
Definition CD_ItoKMCPhysicsImplem.H:292
Real m_SSAlim
Solver setting for the Cao et. al. algorithm.
Definition CD_ItoKMCPhysics.H:590
void parseAlgorithm() noexcept
Parse reaction algorithm.
Definition CD_ItoKMCPhysicsImplem.H:243
void removeParticles(ParticleSoA< ItoParticle > &a_particles, const long long a_numToRemove) const
Remove particles from the input list.
Definition CD_ItoKMCPhysicsImplem.H:757
int m_maxIter
Maximum number of iterations for implicit KMC-leaping algorithms.
Definition CD_ItoKMCPhysics.H:584
int m_Ncrit
Solver setting for the Cao et. al algorithm.
Definition CD_ItoKMCPhysics.H:573
const std::map< int, std::pair< SpeciesType, int > > & getSpeciesMap() const noexcept
Get the internal mapping from plasma-species index to solver type and solver index.
Definition CD_ItoKMCPhysicsImplem.H:203
bool computeUpstreamPosition(RealVect &a_pos, RealVect &a_lo, RealVect &a_hi, const int &a_Z, const ParticleSoA< ItoParticle > &a_particles, const RealVect &a_electricField, const RealVect &a_cellPos, const Real &a_dx) const noexcept
Compute the upstream position in a grid cell. Returns false if an upstream position was undefinable.
Definition CD_ItoKMCPhysicsImplem.H:706
std::map< int, std::pair< std::discrete_distribution< int >, std::map< int, int > > > m_photoPathways
Random number generators for photoionization pathways.
Definition CD_ItoKMCPhysics.H:524
@ HybridMidpoint
Hybrid SSA / midpoint (Cao et al.).
@ ImplicitEuler
Implicit tau-leaping with Euler steps.
@ SSA
Gillespie's Stochastic Simulation Algorithm (exact).
@ Midpoint
Explicit tau-leaping with midpoint (second-order) steps.
@ ExplicitEuler
Explicit tau-leaping with Euler steps.
@ HybridImplicitEuler
Hybrid SSA / implicit Euler (Cao et al.).
@ HybridPRC
Hybrid SSA / PRC (Cao et al.).
@ HybridExplicitEuler
Hybrid SSA / explicit Euler (Cao et al.).
@ PRC
Partially-rejected corrections tau-leaping.
Algorithm m_algorithm
Algorithm to use for KMC advance.
Definition CD_ItoKMCPhysics.H:435
virtual void parseRuntimeOptions() noexcept
Parse run-time options.
Definition CD_ItoKMCPhysicsImplem.H:211
@ Random
Place particles at a uniformly random position within the cell.
void reconcileParticles(Vector< ParticleSoA< ItoParticle > * > &a_particles, const Vector< FPR > &a_newNumParticles, const Vector< FPR > &a_oldNumParticles, const RealVect a_electricField, const RealVect a_cellPos, const RealVect a_centroidPos, const RealVect a_lo, const RealVect a_hi, const RealVect a_bndryCentroid, const RealVect a_bndryNormal, const Real a_dx, const Real a_kappa) const noexcept
Reconcile the number of particles.
Definition CD_ItoKMCPhysicsImplem.H:520
const Vector< RefCountedPtr< CdrSpecies > > & getCdrSpecies() const
Get all fluid drift-diffusion species.
Definition CD_ItoKMCPhysicsImplem.H:298
static thread_local std::vector< std::shared_ptr< const KMCReaction > > m_kmcReactionsThreadLocal
Thread-local copies of KMC reactions used in advanceReactionNetwork.
Definition CD_ItoKMCPhysics.H:501
std::vector< KMCReaction > m_kmcReactions
List of reactions for the KMC solver.
Definition CD_ItoKMCPhysics.H:506
int getNumPhotonSpecies() const
Return number of RTE solvers.
Definition CD_ItoKMCPhysicsImplem.H:334
int getNumPlasmaSpecies() const
Return total number of plasma species.
Definition CD_ItoKMCPhysicsImplem.H:328
int getNumItoSpecies() const
Return number of Ito solvers.
Definition CD_ItoKMCPhysicsImplem.H:316
void define() noexcept
Define method – defines all the internal machinery.
Definition CD_ItoKMCPhysicsImplem.H:62
std::map< int, std::pair< SpeciesType, int > > m_speciesMap
Map for associating a plasma species with an Ito solver or CDR solver.
Definition CD_ItoKMCPhysics.H:445
RealVect noDiffusion(const ItoParticle &a_particle, const Real a_dt) const noexcept
No diffusion function for a particle.
Definition CD_ItoKMCPhysicsImplem.H:903
void killKMC() const noexcept
Kill the KMC solver.
Definition CD_ItoKMCPhysicsImplem.H:133
void definePhotoPathways() noexcept
Define pathways for photo-reactions.
Definition CD_ItoKMCPhysicsImplem.H:150
void reconcilePhotoionization(Vector< ParticleSoA< ItoParticle > * > &a_itoParticles, Vector< ParticleSoA< NoPayload > * > &a_cdrParticles, const Vector< ParticleSoA< Photon > * > &a_absorbedPhotons) const noexcept
Reconcile photoionization reactions.
Definition CD_ItoKMCPhysicsImplem.H:857
void parsePPC() noexcept
Parse the maximum number of particles generated per cell.
Definition CD_ItoKMCPhysicsImplem.H:221
static thread_local bool m_hasKMCSolver
Is the KMC solver defined or not.
Definition CD_ItoKMCPhysics.H:470
int getNumCdrSpecies() const
Return number of CDR solvers.
Definition CD_ItoKMCPhysicsImplem.H:322
ParticlePlacement m_particlePlacement
Particle placement algorithm.
Definition CD_ItoKMCPhysics.H:440
void parseDebug() noexcept
Parse the maximum number of particles generated per cell.
Definition CD_ItoKMCPhysicsImplem.H:233
bool m_isDefined
Is defined or not.
Definition CD_ItoKMCPhysics.H:460
static thread_local std::vector< Real > m_kmcPropensityScratch
Propensity buffer used by the time step tail of advanceKMC.
Definition CD_ItoKMCPhysics.H:495
static thread_local KMCState m_kmcState
KMC state used in advanceReactionNetwork.
Definition CD_ItoKMCPhysics.H:480
ItoKMCPhysics() noexcept
Constructor. Does nothing.
Definition CD_ItoKMCPhysicsImplem.H:29
static Real get(T &a_distribution)
For getting a random number from a user-supplied distribution. T must be a distribution for which we ...
Definition CD_RandomImplem.H:217
static RealVect getDirection()
Get a random direction in space.
Definition CD_RandomImplem.H:180
static Real getUniformReal01()
Get a uniform real number on the interval [0,1].
Definition CD_RandomImplem.H:156
static Real getNormal01()
Get a number from a normal distribution centered on zero and variance 1.
Definition CD_RandomImplem.H:172
static RealVect randomPosition(const RealVect &a_lo, const RealVect &a_hi) noexcept
Return a random position in the cube (a_lo, a_hi);.
Definition CD_RandomImplem.H:284
constexpr Real c
Speed of light.
Definition CD_Units.H:40
SoA payload for ItoSolver particles, i.e. drifting Brownian walkers.
Definition CD_ItoParticle.H:31
SoA payload for Monte Carlo radiative-transfer photons.
Definition CD_Photon.H:29