chombo-discharge
Loading...
Searching...
No Matches
CD_DischargeInceptionStepperImplem.H
Go to the documentation of this file.
1
16#ifndef CD_DischargeInceptionStepperImplem_H
17#define CD_DischargeInceptionStepperImplem_H
18
19// Std includes
20#include <iostream>
21#include <fstream>
22
23// Chombo includes
24#include <CH_Timer.H>
25
26// Our includes
27#include <CD_Units.H>
28#include <CD_OpenMP.H>
31#include <CD_PolyUtils.H>
32#include <CD_NamespaceHeader.H>
33
34using namespace Physics::DischargeInception;
35
36template <typename P, typename F, typename C>
38{
39 CH_TIME("DischargeInceptionStepper::DischargeInceptionStepper");
40 if (m_verbosity > 5) {
41 pout() << "DischargeInceptionStepper::DischargeInceptionStepper" << endl;
42 }
43
44 // Default settings.
45 m_verbosity = -1;
46 m_realm = Realm::Primal;
47 m_phase = phase::gas;
48 m_mode = Mode::Stationary;
49 m_transportAlgorithm = TransportAlgorithm::ImExCTU;
50 m_timeStepRestriction = TimeStepRestriction::Unknown;
51 m_profile = false;
52 m_debug = false;
53 m_fullIntegration = false;
54 m_evaluateTownsend = false;
55 m_relativeDeltaU = 1 + 0.05;
56 m_deltaK = 1.0;
57 m_maxKLimit = 15.0;
58
59 this->parseOptions();
60
61 m_rho = [](const RealVect x) -> Real {
62 return 0.0;
63 };
64
65 m_sigma = [](const RealVect x) -> Real {
66 return 0.0;
67 };
68
69 m_alpha = [](const Real E, const RealVect x) -> Real {
70 return 0.0;
71 };
72
73 m_eta = [](const Real E, const RealVect x) -> Real {
74 return 0.0;
75 };
76
77 m_voltageCurve = [](const Real a_time) -> Real {
78 return 1.0;
79 };
80
81 m_backgroundRate = [](const Real E, const RealVect x) -> Real {
82 return 0.0;
83 };
84
85 m_detachmentRate = [](const Real E, const RealVect x) -> Real {
86 return 0.0;
87 };
88
89 m_fieldEmission = [](const Real E, const RealVect x) -> Real {
90 return 0.0;
91 };
92
93 m_secondaryEmission = [](const Real E, const RealVect x) -> Real {
94 return 0.0;
95 };
96
97 m_initialIonDensity = [](const RealVect x) -> Real {
98 return 0.0;
99 };
100
101 m_ionMobility = [](const Real E) -> Real {
102 return 0.0;
103 };
104
105 m_ionDiffusion = [](const Real E) -> Real {
106 return 0.0;
107 };
108}
109
110template <typename P, typename F, typename C>
112{
113 CH_TIME("DischargeInceptionStepper::~DischargeInceptionStepper");
114 if (m_verbosity > 5) {
115 pout() << "DischargeInceptionStepper::~DischargeInceptionStepper" << endl;
116 }
117}
118
119template <typename P, typename F, typename C>
120void
122{
123 CH_TIME("DischargeInceptionStepper::setupSolvers");
124 if (m_verbosity > 5) {
125 pout() << "DischargeInceptionStepper::setupSolvers" << endl;
126 }
127
128 // Always solve for the field using a voltage of one (and then scale up/down later on)
129 auto voltage = [](const Real a_time) -> Real {
130 return 1.0;
131 };
132
133 // Instantiate the field solver.
134 m_fieldSolver = RefCountedPtr<FieldSolver>(new F());
135 m_fieldSolver->setVerbosity(m_verbosity);
136 m_fieldSolver->parseOptions();
137 m_fieldSolver->setAmr(m_amr);
138 m_fieldSolver->setComputationalGeometry(m_computationalGeometry);
139 m_fieldSolver->setVoltage(voltage);
140 m_fieldSolver->setRealm(m_realm);
141 m_fieldSolver->setTime(0, 0.0, 0.0);
142
143 // Instantiate the tracer particle solver.
144 m_tracerParticleSolver = RefCountedPtr<TracerParticleSolver<P>>(new TracerParticleSolver<P>());
145 m_tracerParticleSolver->parseOptions();
146 m_tracerParticleSolver->setAmr(m_amr);
147 m_tracerParticleSolver->setComputationalGeometry(m_computationalGeometry);
148 m_tracerParticleSolver->setRealm(m_realm);
149 m_tracerParticleSolver->setPhase(m_phase);
150 m_tracerParticleSolver->setTime(0, 0.0, 0.0);
151 m_tracerParticleSolver->setName("Tracer particle solver");
152 m_tracerParticleSolver->setVolumeScale(true);
153 m_tracerParticleSolver->setDeposition(DepositionType::NGP);
154
155 // Instantiate the ion solver.
156 m_ionSolver = RefCountedPtr<CdrSolver>(new C());
157
158 m_ionSolver->parseOptions();
159 m_ionSolver->setPhase(m_phase);
160 m_ionSolver->setAmr(m_amr);
161 m_ionSolver->setComputationalGeometry(m_computationalGeometry);
162 m_ionSolver->setRealm(m_realm);
163
164 auto species = RefCountedPtr<CdrSpecies>(new DischargeInceptionSpecies(m_initialIonDensity, true, true));
165
166 m_ionSolver->setSpecies(species);
167}
168
169template <typename P, typename F, typename C>
170void
172{
173 CH_TIME("DischargeInceptionStepper::allocate");
174 if (m_verbosity > 5) {
175 pout() << "DischargeInceptionStepper::allocate" << endl;
176 }
177
178 m_fieldSolver->allocate();
179 m_tracerParticleSolver->allocate();
180 m_ionSolver->allocate();
181
182 // Needed for all modes.
183 m_amr->allocate(m_potential, m_realm, 1);
184 m_amr->allocate(m_potentialHomo, m_realm, 1);
185 m_amr->allocate(m_potentialInho, m_realm, 1);
186
187 m_amr->allocate(m_electricField, m_realm, SpaceDim);
188 m_amr->allocate(m_electricFieldHomo, m_realm, SpaceDim);
189 m_amr->allocate(m_electricFieldInho, m_realm, SpaceDim);
190
191 m_amr->allocate(m_gradAlpha, m_realm, m_phase, SpaceDim);
192
193 DataOps::setValue(m_potentialHomo, 0.0);
194 DataOps::setValue(m_potentialInho, 0.0);
195
196 switch (m_mode) {
197 case Mode::Stationary: {
198 m_amr->allocate(m_inceptionVoltagePlus, m_realm, m_phase, 1);
199 m_amr->allocate(m_inceptionVoltageMinu, m_realm, m_phase, 1);
200 m_amr->allocate(m_streamerInceptionVoltagePlus, m_realm, m_phase, 1);
201 m_amr->allocate(m_streamerInceptionVoltageMinu, m_realm, m_phase, 1);
202 m_amr->allocate(m_townsendInceptionVoltagePlus, m_realm, m_phase, 1);
203 m_amr->allocate(m_townsendInceptionVoltageMinu, m_realm, m_phase, 1);
204
205 DataOps::setValue(m_inceptionVoltagePlus, 0.0);
206 DataOps::setValue(m_inceptionVoltageMinu, 0.0);
207 DataOps::setValue(m_streamerInceptionVoltagePlus, 0.0);
208 DataOps::setValue(m_streamerInceptionVoltageMinu, 0.0);
209 DataOps::setValue(m_townsendInceptionVoltagePlus, 0.0);
210 DataOps::setValue(m_townsendInceptionVoltageMinu, 0.0);
211
212 break;
213 }
214 case Mode::Transient: {
215 m_amr->allocate(m_inceptionIntegral, m_realm, m_phase, 1);
216 m_amr->allocate(m_townsendCriterion, m_realm, m_phase, 1);
217 m_amr->allocate(m_emissionRate, m_realm, m_phase, 1);
218 m_amr->allocate(m_backgroundIonization, m_realm, m_phase, 1);
219 m_amr->allocate(m_detachment, m_realm, m_phase, 1);
220
221 DataOps::setValue(m_inceptionIntegral, 0.0);
222 DataOps::setValue(m_townsendCriterion, 0.0);
223 DataOps::setValue(m_emissionRate, 0.0);
224 DataOps::setValue(m_backgroundIonization, 0.0);
225 DataOps::setValue(m_detachment, 0.0);
226
227 break;
228 }
229 default: {
230 break;
231 }
232 }
233}
234
235template <typename P, typename F, typename C>
236void
238{
239 CH_TIME("DischargeInceptionStepper::initialData");
240 if (m_verbosity > 5) {
241 pout() << "DischargeInceptionStepper::initialData" << endl;
242 }
243
244 this->solvePoisson();
245}
246
247template <typename P, typename F, typename C>
248void
250{
251 CH_TIME("DischargeInceptionStepper::solvePoisson");
252 if (m_verbosity > 5) {
253 pout() << "DischargeInceptionStepper::solvePoisson" << endl;
254 }
255
256 // Solve for the inhomogeneous part
257 m_fieldSolver->setRho(m_rho);
258 m_fieldSolver->setSigma(m_sigma);
259 m_fieldSolver->setVoltage([](const Real& a_time) {
260 return 0.0;
261 });
262
263 // Solve using our m_potentialInho as an initial guess.
264 DataOps::copy(m_fieldSolver->getPotential(), m_potentialInho);
265 const bool convergedInho = m_fieldSolver->solve(m_fieldSolver->getPotential(),
266 m_fieldSolver->getRho(),
267 m_fieldSolver->getSigma(),
268 false);
269
270 if (!convergedInho) {
271 MayDay::Warning("DischargeInceptionStepper::solvePoisson -- could not solve the inhomogeneous Poisson equation. ");
272 }
273
274 DataOps::copy(m_potentialInho, m_fieldSolver->getPotential());
275 DataOps::copy(m_electricFieldInho, m_fieldSolver->getElectricField());
276
277 // Solve for the homogeneous part
278 m_fieldSolver->setRho([](const RealVect& a_pos) {
279 return 0.0;
280 });
281 m_fieldSolver->setSigma([](const RealVect& a_pos) {
282 return 0.0;
283 });
284 m_fieldSolver->setVoltage([](const Real& a_time) {
285 return 1.0;
286 });
287
288 DataOps::copy(m_fieldSolver->getPotential(), m_potentialHomo);
289 const bool convergedHomo = m_fieldSolver->solve(m_fieldSolver->getPotential(),
290 m_fieldSolver->getRho(),
291 m_fieldSolver->getSigma(),
292 false);
293
294 if (!convergedHomo) {
295 MayDay::Warning("DischargeInceptionStepper::solvePoisson -- could not solve the homogeneous Poisson equation. ");
296 }
297
298 DataOps::copy(m_potentialHomo, m_fieldSolver->getPotential());
299 DataOps::copy(m_electricFieldHomo, m_fieldSolver->getElectricField());
300
301 // Alias the field to send to the cell tagger
302 m_homogeneousFieldGas = m_amr->alias(phase::gas, m_electricFieldHomo);
303
304 if (m_debug) {
305
306 // Do a dummy check if the homogeneous and inhomogeneous solutions with a potential of one, and check that the
307 // difference is "sufficiently small"
308 const Real testVoltage = 1.0;
309 const Real errorThresh = 1.E-6;
310
311 // This is the sum of the two potentials
312 DataOps::setValue(m_potential, 0.0);
313 DataOps::incr(m_potential, m_potentialHomo, testVoltage);
314 DataOps::incr(m_potential, m_potentialInho, 1.0);
315
316 // Do a true solution with a test voltage and space/surface charge.
317 auto voltage = [V = testVoltage](const Real& a_time) {
318 return V;
319 };
320
321 DataOps::copy(m_fieldSolver->getPotential(), m_potential);
322
323 m_fieldSolver->setRho(m_rho);
324 m_fieldSolver->setSigma(m_sigma);
325 m_fieldSolver->setVoltage(voltage);
326 m_fieldSolver->solve(m_fieldSolver->getPotential(), m_fieldSolver->getRho(), m_fieldSolver->getSigma(), false);
327
328 // Do the difference between the two potentials and figure out the max error. It should be < 1.E-6 * testVoltage
329 DataOps::incr(m_potential, m_fieldSolver->getPotential(), -1.0);
330
331 EBAMRCellData phiGas = m_amr->alias(phase::gas, m_potential);
332
333 Real max;
334 Real min;
335
336 DataOps::getMaxMin(max, min, phiGas, 0);
337
338 if (std::max(std::abs(max), std::abs(min)) > errorThresh * testVoltage) {
339 MayDay::Error("DischargeInceptionStepper::solvePoisson - debug test failed. Check your BCs!");
340 }
341 }
342}
343
344template <typename P, typename F, typename C>
345void
347{
348 CH_TIME("DischargeInceptionStepper::registerRealms");
349 if (m_verbosity > 5) {
350 pout() << "DischargeInceptionStepper::registerRealms" << endl;
351 }
352
353 m_amr->registerRealm(m_realm);
354}
355
356template <typename P, typename F, typename C>
357void
359{
360 CH_TIME("DischargeInceptionStepper::registerOperators");
361 if (m_verbosity > 5) {
362 pout() << "DischargeInceptionStepper::registerOperators" << endl;
363 }
364
365 m_fieldSolver->registerOperators();
366 m_tracerParticleSolver->registerOperators();
367 m_ionSolver->registerOperators();
368}
369
370template <typename P, typename F, typename C>
371void
373{
374 CH_TIME("DischargeInceptionStepper::parseOptions");
375
376 this->parseVerbosity();
377 this->parseMode();
378 this->parseVoltages();
379 this->parseOutput();
380 this->parsePlotVariables();
381 this->parseInceptionAlgorithm();
382 this->parseTransportAlgorithm();
383}
384
385template <typename P, typename F, typename C>
386void
388{
389 CH_TIME("DischargeInceptionStepper::parseRuntimeOptions");
390 if (m_verbosity > 5) {
391 pout() << "DischargeInceptionStepper::parseRuntimeOptions" << endl;
392 }
393
394 this->parseVerbosity();
395 this->parsePlotVariables();
396 this->parseInceptionAlgorithm();
397 this->parseTransportAlgorithm();
398}
399
400template <typename P, typename F, typename C>
401void
403{
404 CH_TIME("DischargeInceptionStepper::parseVerbosity");
405 if (m_verbosity > 5) {
406 pout() << "DischargeInceptionStepper::parseVerbosity" << endl;
407 }
408
409 ParmParse pp("DischargeInceptionStepper");
410 pp.get("verbosity", m_verbosity);
411 pp.get("profile", m_profile);
412 pp.query("debug", m_debug);
413}
414
415template <typename P, typename F, typename C>
416void
418{
419 CH_TIME("DischargeInceptionStepper::parseMode");
420 if (m_verbosity > 5) {
421 pout() << "DischargeInceptionStepper::parseMode" << endl;
422 }
423
424 ParmParse pp("DischargeInceptionStepper");
425
427
428 pp.get("mode", str);
429 if (str == "stationary") {
430 m_mode = Mode::Stationary;
431 }
432 else if (str == "transient") {
433 m_mode = Mode::Transient;
434 }
435 else {
436 MayDay::Error("Expected 'none', 'stationary', or 'transient' for 'DischargeInceptionStepper.mode'");
437 }
438}
439
440template <typename P, typename F, typename C>
441void
443{
444 CH_TIME("DischargeInceptionStepper::parseVoltages");
445 if (m_verbosity > 5) {
446 pout() << "DischargeInceptionStepper::parseVoltages" << endl;
447 }
448
449 ParmParse pp("DischargeInceptionStepper");
450
451 pp.get("rel_step_dU", m_relativeDeltaU);
452 pp.get("step_dK", m_deltaK);
453 pp.get("limit_max_K", m_maxKLimit);
454 pp.get("K_inception", m_inceptionK);
455 pp.get("eval_townsend", m_evaluateTownsend);
456
457 m_relativeDeltaU = 1.0 + m_relativeDeltaU;
458}
459
460template <typename P, typename F, typename C>
461void
463{
464 CH_TIME("DischargeInceptionStepper::parseOutput");
465 if (m_verbosity > 5) {
466 pout() << "DischargeInceptionStepper::parseOutput" << endl;
467 }
468
469 ParmParse pp("DischargeInceptionStepper");
470
471 pp.get("output_file", m_outputFile);
472}
473
474template <typename P, typename F, typename C>
475void
477{
478 CH_TIME("DischargeInceptionStepper::parseInceptionAlgorithm");
479 if (m_verbosity > 5) {
480 pout() << "DischargeInceptionStepper::parseInceptionAlgorithm" << endl;
481 }
482
483 ParmParse pp("DischargeInceptionStepper");
484
486
487 // Get the inception algorithm
488 pp.get("full_integration", m_fullIntegration);
489 pp.get("inception_alg", str);
490 if (str == "euler") {
491 m_inceptionAlgorithm = IntegrationAlgorithm::Euler;
492 }
493 else if (str == "trapz") {
494 m_inceptionAlgorithm = IntegrationAlgorithm::Trapezoidal;
495 }
496 else {
497 MayDay::Error("Expected 'euler' or 'trapz' for 'DischargeInceptionStepper.inception_alg'");
498 }
499
500 pp.get("min_phys_dx", m_minPhysDx);
501 pp.get("max_phys_dx", m_maxPhysDx);
502 pp.get("min_grid_dx", m_minGridDx);
503 pp.get("max_grid_dx", m_maxGridDx);
504 pp.get("alpha_dx", m_alphaDx);
505 pp.get("grad_alpha_dx", m_gradAlphaDx);
506 pp.get("townsend_grid_dx", m_townsendGridDx);
507
508 if (m_minPhysDx <= 0.0) {
509 MayDay::Abort("DischargeInceptionStepper.min_phys_dx must be > 0.0");
510 }
511 if (m_maxPhysDx <= 0.0) {
512 MayDay::Abort("DischargeInceptionStepper.max_phys_dx must be > 0.0");
513 }
514 if (m_minGridDx <= 0.0) {
515 MayDay::Abort("DischargeInceptionStepper.min_grid_dx must be > 0.0");
516 }
517 if (m_maxGridDx <= 0.0) {
518 MayDay::Abort("DischargeInceptionStepper.max_grid_dx must be > 0.0");
519 }
520 if (m_alphaDx <= 0.0) {
521 MayDay::Abort("DischargeInceptionStepper.alpha_dx must be > 0.0");
522 }
523 if (m_gradAlphaDx <= 0.0) {
524 MayDay::Abort("DischargeInceptionStepper.grad_alpha_dx must be > 0.0");
525 }
526 if (m_townsendGridDx <= 0.0) {
527 MayDay::Abort("DischargeInceptionStepper.townsend_grid_dx must be > 0.0");
528 }
529}
530
531template <typename P, typename F, typename C>
532void
534{
535 CH_TIME("DischargeInceptionStepper::parseTransportAlgorithm");
536 if (m_verbosity > 5) {
537 pout() << "DischargeInceptionStepper::parseTransportAlgorithm" << endl;
538 }
539
540 ParmParse pp("DischargeInceptionStepper");
541
543
544 pp.get("transport_alg", str);
545 pp.get("ion_transport", m_ionTransport);
546 pp.get("cfl", m_cfl);
547 pp.get("first_dt", m_firstDt);
548 pp.get("min_dt", m_minDt);
549 pp.get("max_dt", m_maxDt);
550 pp.get("max_dt_growth", m_maxDtGrowth);
551 pp.get("voltage_eps", m_epsVoltage);
552
553 CH_assert(m_minDt >= 0.0);
554 CH_assert(m_maxDt >= 0.0);
555 CH_assert(m_firstDt > 0.0);
556
557 if (str == "euler") {
558 m_transportAlgorithm = TransportAlgorithm::Euler;
559 }
560 else if (str == "heun") {
561 m_transportAlgorithm = TransportAlgorithm::Heun;
562 }
563 else if (str == "imex") {
564 m_transportAlgorithm = TransportAlgorithm::ImExCTU;
565 }
566 else {
567 MayDay::Error("Expected 'euler', 'heun', or 'imex' for 'DischargeInceptionStepper.transport_alg'");
568 }
569}
570
571template <typename P, typename F, typename C>
572void
574{
575 CH_TIME("DischargeInceptionStepper::parsePlotVariables");
576 if (m_verbosity > 5) {
577 pout() << "DischargeInceptionStepper::parsePlotVariables" << endl;
578 }
579
580 m_plotField = false;
581 m_plotPoisson = false;
582 m_plotTracer = false;
583 m_plotNegativeIons = false;
584 m_plotInceptionIntegral = false;
585 m_plotInceptionVoltage = false;
586 m_plotBackgroundIonization = false;
587 m_plotDetachment = false;
588 m_plotFieldEmission = false;
589 m_plotAlpha = false;
590 m_plotEta = false;
591 m_plotTownsend = false;
592
593 ParmParse pp("DischargeInceptionStepper");
594
595 // Get plot variables.
596 const int num = pp.countval("plt_vars");
597 if (num > 0) {
599 pp.getarr("plt_vars", plotVars, 0, num);
600
601 for (int i = 0; i < num; i++) {
602 if (plotVars[i] == "field") {
603 m_plotField = true;
604 }
605 else if (plotVars[i] == "poisson") {
606 m_plotPoisson = true;
607 }
608 else if (plotVars[i] == "tracer") {
609 m_plotTracer = true;
610 }
611 else if (plotVars[i] == "ions") {
612 m_plotNegativeIons = true;
613 }
614 else if (plotVars[i] == "K") {
615 m_plotInceptionIntegral = true;
616 }
617 else if (plotVars[i] == "Uinc") {
618 m_plotInceptionVoltage = true;
619 }
620 else if (plotVars[i] == "bg_rate") {
621 m_plotBackgroundIonization = true;
622 }
623 else if (plotVars[i] == "detachment") {
624 m_plotDetachment = true;
625 }
626 else if (plotVars[i] == "emission") {
627 m_plotFieldEmission = true;
628 }
629 else if (plotVars[i] == "alpha") {
630 m_plotAlpha = true;
631 }
632 else if (plotVars[i] == "eta") {
633 m_plotEta = true;
634 }
635 else if (plotVars[i] == "T") {
636 m_plotTownsend = true;
637 }
638 }
639 }
640}
641
642#ifdef CH_USE_HDF5
643template <typename P, typename F, typename C>
644void
646{
647 CH_TIME("DischargeInceptionStepper::writeCheckpointData");
648 if (m_verbosity > 5) {
649 pout() << "DischargeInceptionStepper::writeCheckpointData" << endl;
650 }
651}
652#endif
653
654#ifdef CH_USE_HDF5
655template <typename P, typename F, typename C>
656void
658{
659 CH_TIME("DischargeInceptionStepper::readCheckpointData");
660 if (m_verbosity > 5) {
661 pout() << "DischargeInceptionStepper::readCheckpointData" << endl;
662 }
663
664 MayDay::Error("DischargeInceptionStepper::readCheckpointData -- restart not supported. Use Driver.restart=0");
665}
666#endif
667
668template <typename P, typename F, typename C>
669int
671{
672 CH_TIME("DischargeInceptionStepper::getNumberOfPlotVariables");
673 if (m_verbosity > 5) {
674 pout() << "DischargeInceptionStepper::getNumberOfPlotVariables" << endl;
675 }
676
677 int ncomp = 0;
678
679 if (m_plotPoisson) {
680 ncomp += m_fieldSolver->getNumberOfPlotVariables();
681 }
682 if (m_plotTracer) {
683 ncomp += m_tracerParticleSolver->getNumberOfPlotVariables();
684 }
685 if (m_plotNegativeIons) {
686 ncomp += m_ionSolver->getNumberOfPlotVariables();
687 }
688
689 switch (m_mode) {
690 case Mode::Stationary: {
691
692 if (m_plotField) {
693 // Electric potential for each voltage
694 ncomp += 2 * m_voltageSweeps.size();
695
696 // Electric field magnitude
697 ncomp += 2 * m_voltageSweeps.size();
698
699 // All the electric field components
700 ncomp += 2 * SpaceDim * m_voltageSweeps.size();
701
702 // Surface and space charge densities
703 ncomp += 2;
704 }
705
706 // K-values
707 if (m_plotInceptionIntegral) {
708 ncomp += 2 * m_voltageSweeps.size();
709 }
710
711 // Inception voltage.
712 if (m_plotInceptionVoltage) {
713 ncomp += 6;
714 }
715
716 // Background ionization rates
717 if (m_plotBackgroundIonization) {
718 ncomp += m_voltageSweeps.size();
719 }
720
721 // Detachment rates
722 if (m_plotDetachment) {
723 ncomp += m_voltageSweeps.size();
724 }
725
726 // Field emission rates
727 if (m_plotFieldEmission) {
728 ncomp += 2 * m_voltageSweeps.size();
729 }
730
731 // Plotting alpha
732 if (m_plotAlpha) {
733 ncomp += m_voltageSweeps.size();
734 }
735
736 // Plotting eta
737 if (m_plotEta) {
738 ncomp += m_voltageSweeps.size();
739 }
740
741 // When plotting both, add the effective ionization coefficient
742 if (m_plotAlpha && m_plotEta) {
743 ncomp += m_voltageSweeps.size();
744 }
745
746 // Plotting gamma coefficient
747 if (m_plotTownsend) {
748 ncomp += 2 * m_voltageSweeps.size();
749 }
750
751 break;
752 }
753 case Mode::Transient: {
754
755 if (m_plotField) {
756
757 // Potential
758 ncomp += 1;
759
760 // Field magnitude
761 ncomp += 1;
762
763 // Field components
764 ncomp += SpaceDim;
765
766 // Space and surface charge
767 ncomp += 2;
768 }
769
770 if (m_plotInceptionIntegral) {
771 ncomp += 1;
772 }
773 if (m_plotTownsend) {
774 ncomp += 1;
775 }
776 if (m_plotBackgroundIonization) {
777 ncomp += 1;
778 }
779 if (m_plotDetachment) {
780 ncomp += 1;
781 }
782 if (m_plotFieldEmission) {
783 ncomp += 1;
784 }
785 if (m_plotAlpha) {
786 ncomp += 1;
787 }
788 if (m_plotEta) {
789 ncomp += 1;
790 }
791 if (m_plotAlpha && m_plotEta) {
792 ncomp += 1;
793 }
794
795 break;
796 }
797 default: {
798 break;
799 }
800 }
801
802 return ncomp;
803}
804
805template <typename P, typename F, typename C>
808{
809 CH_TIME("DischargeInceptionStepper::getPlotVariableNames");
810 if (m_verbosity > 5) {
811 pout() << "DischargeInceptionStepper::getPlotVariableNames" << endl;
812 }
813
815
816 if (m_plotPoisson) {
818 for (int i = 0; i < poissonVars.size(); i++) {
819 poissonVars[i] = "Poisson/" + poissonVars[i];
820 }
821 plotVars.append(poissonVars);
822 }
823
824 if (m_plotTracer) {
825 Vector<std::string> tracerVars = m_tracerParticleSolver->getPlotVariableNames();
826 for (int i = 0; i < tracerVars.size(); i++) {
827 tracerVars[i] = "Tracer/" + tracerVars[i];
828 }
829 plotVars.append(tracerVars);
830 }
831
832 if (m_plotNegativeIons) {
834 for (int i = 0; i < cdrVars.size(); i++) {
835 cdrVars[i] = "CDR/" + cdrVars[i];
836 }
837 plotVars.append(cdrVars);
838 }
839
840 switch (m_mode) {
841 case Mode::Stationary: {
842 plotVars.append(this->getStationaryPlotVariableNames());
843
844 break;
845 }
846 case Mode::Transient: {
847 plotVars.append(this->getTransientPlotVariableNames());
848
849 break;
850 }
851 default: {
852 MayDay::Error("DischargeInceptionStepper::getPlotVariableNames - logic bust");
853
854 break;
855 }
856 }
857
858 return plotVars;
859}
860
861template <typename P, typename F, typename C>
864{
865 CH_TIME("DischargeInceptionStepper::getStationaryPlotVariableNames");
866 if (m_verbosity > 5) {
867 pout() << "DischargeInceptionStepper::getStationaryPlotVariableNames" << endl;
868 }
869
871
872 const std::string prefix = "DischargeInceptionStepper/";
873
874 // Always plotted.
875 if (m_plotField) {
876 for (const Real& V : m_voltageSweeps) {
877 plotVars.push_back(prefix + "Potential/+/ V = +" + std::to_string(V));
878 plotVars.push_back(prefix + "Potential/-/ V = -" + std::to_string(V));
879 }
880
881 for (const Real& V : m_voltageSweeps) {
882 plotVars.push_back(prefix + "E/+/ V = +" + std::to_string(V));
883 plotVars.push_back(prefix + "x-E/+/ V= +" + std::to_string(V));
884 plotVars.push_back(prefix + "y-E/+/ V= +" + std::to_string(V));
885 if (SpaceDim == 3) {
886 plotVars.push_back(prefix + "z-E/+/ V= +" + std::to_string(V));
887 }
888
889 plotVars.push_back(prefix + "E/-/ V = -" + std::to_string(V));
890 plotVars.push_back(prefix + "x-E/-/ V = -" + std::to_string(V));
891 plotVars.push_back(prefix + "y-E/-/ V = -" + std::to_string(V));
892 if (SpaceDim == 3) {
893 plotVars.push_back(prefix + "z-E/-/ V= -" + std::to_string(V));
894 }
895 }
896
897 // Surface charge
898 plotVars.push_back(prefix + "Space charge density");
899 plotVars.push_back(prefix + "Surface charge density");
900 }
901
902 if (m_plotInceptionVoltage) {
903 plotVars.push_back(prefix + "Minimum inception voltage +");
904 plotVars.push_back(prefix + "Minimum inception voltage -");
905 plotVars.push_back(prefix + "Streamer inception voltage +");
906 plotVars.push_back(prefix + "Streamer inception voltage -");
907 plotVars.push_back(prefix + "Townsend inception voltage +");
908 plotVars.push_back(prefix + "Townsend inception voltage -");
909 }
910
911 if (m_plotInceptionIntegral) {
913
914 for (const Real& V : m_voltageSweeps) {
915 varName = prefix + "K-value/+/ V = +" + std::to_string(V);
916 plotVars.push_back(varName);
917 }
918
919 for (const Real& V : m_voltageSweeps) {
920 varName = prefix + "K-value/-/ V = -" + std::to_string(V);
921 plotVars.push_back(varName);
922 }
923 }
924
925 // Secondary emission coefficient for initiatory ions
926 if (m_plotTownsend) {
927 for (size_t i = 0; i < m_voltageSweeps.size(); i++) {
928 const std::string varName = prefix + "T-value/+/ V = +" + std::to_string(m_voltageSweeps[i]);
929
930 plotVars.push_back(varName);
931 }
932
933 for (size_t i = 0; i < m_voltageSweeps.size(); i++) {
934 const std::string varName = prefix + "T-value/-/ V = -" + std::to_string(m_voltageSweeps[i]);
935
936 plotVars.push_back(varName);
937 }
938 }
939
940 // Write the background ionization rates
941 if (m_plotBackgroundIonization) {
943
944 for (const Real& V : m_voltageSweeps) {
945 varName = prefix + "Background ionization rate/ V = " + std::to_string(V);
946 plotVars.push_back(varName);
947 }
948 }
949
950 // Write detachment rates
951 if (m_plotDetachment) {
953
954 for (const Real& V : m_voltageSweeps) {
955 varName = prefix + "Detachment rate/ V = " + std::to_string(V);
956 plotVars.push_back(varName);
957 }
958 }
959
960 // Write field emission rates
961 if (m_plotFieldEmission) {
963
964 for (const Real& V : m_voltageSweeps) {
965 varName = prefix + "Field emission rate/+/ V = +" + std::to_string(V);
966 plotVars.push_back(varName);
967 }
968
969 for (const Real& V : m_voltageSweeps) {
970 varName = prefix + "Field emission rate/-/ V = -" + std::to_string(V);
971 plotVars.push_back(varName);
972 }
973 }
974
975 // Alpha value
976 if (m_plotAlpha) {
977 for (size_t i = 0; i < m_voltageSweeps.size(); i++) {
978
979 const std::string varName = prefix + "Alpha coefficient/ V = " + std::to_string(m_voltageSweeps[i]);
980
981 plotVars.push_back(varName);
982 }
983 }
984
985 // Eta
986 if (m_plotEta) {
987 for (size_t i = 0; i < m_voltageSweeps.size(); i++) {
988 const std::string varName = prefix + "Eta coefficient/ V = " + std::to_string(m_voltageSweeps[i]);
989
990 plotVars.push_back(varName);
991 }
992 }
993
994 // Effective alpha coefficient
995 if (m_plotAlpha && m_plotEta) {
996 for (size_t i = 0; i < m_voltageSweeps.size(); i++) {
997 const std::string varName = prefix + "Alpha_eff/ V = " + std::to_string(m_voltageSweeps[i]);
998
999 plotVars.push_back(varName);
1000 }
1001 }
1002
1003 return plotVars;
1004}
1005
1006template <typename P, typename F, typename C>
1009{
1010 CH_TIME("DischargeInceptionStepper::getTransientPlotVariableNames");
1011 if (m_verbosity > 5) {
1012 pout() << "DischargeInceptionStepper::getTransientPlotVariableNames" << endl;
1013 }
1014
1016
1017 if (m_plotField) {
1018 // Add potential and electric field to output
1019 plotVars.push_back("Electric potential");
1020 plotVars.push_back("E");
1021 plotVars.push_back("x-E");
1022 plotVars.push_back("y-E");
1023 if (SpaceDim == 3) {
1024 plotVars.push_back("z-E");
1025 }
1026
1027 // Space and surface charge
1028 plotVars.push_back("Space charge density");
1029 plotVars.push_back("Surface charge density");
1030 }
1031
1032 if (m_plotInceptionIntegral) {
1033 plotVars.push_back("Inception integral");
1034 }
1035
1036 if (m_plotTownsend) {
1037 plotVars.push_back("Townsend criterion");
1038 }
1039
1040 if (m_plotBackgroundIonization) {
1041 plotVars.push_back("Background rate");
1042 }
1043
1044 if (m_plotDetachment) {
1045 plotVars.push_back("Detachment rate");
1046 }
1047
1048 if (m_plotFieldEmission) {
1049 plotVars.push_back("Field emission");
1050 }
1051
1052 if (m_plotAlpha) {
1053 plotVars.push_back("Townsend alpha coefficient");
1054 }
1055
1056 if (m_plotEta) {
1057 plotVars.push_back("Townsend eta coefficient");
1058 }
1059
1060 if (m_plotAlpha && m_plotEta) {
1061 plotVars.push_back("Effective Townsend coefficient");
1062 }
1063
1064 for (int i = 0; i < plotVars.size(); i++) {
1065 plotVars[i] = "DischargeInceptionStepper/" + plotVars[i];
1066 }
1067
1068 return plotVars;
1069}
1070
1071template <typename P, typename F, typename C>
1072void
1074 int& a_icomp,
1076 const int a_level) const
1077{
1078 CH_TIME("DischargeInceptionStepper::writePlotData");
1079 if (m_verbosity > 5) {
1080 pout() << "DischargeInceptionStepper::writePlotData" << endl;
1081 }
1082
1083 if (m_plotPoisson) {
1084 m_fieldSolver->writePlotData(a_output, a_icomp, a_outputRealm, a_level);
1085 }
1086
1087 if (m_plotTracer) {
1088 m_tracerParticleSolver->writePlotData(a_output, a_icomp, a_outputRealm, a_level);
1089 }
1090
1091 if (m_plotNegativeIons) {
1092 m_ionSolver->writePlotData(a_output, a_icomp, a_outputRealm, a_level);
1093 }
1094
1095 // Write internal data -- this differs between the 'stationary' and 'transient' modes
1096 switch (m_mode) {
1097 case Mode::Stationary: {
1098 this->writePlotDataStationary(a_output, a_icomp, a_outputRealm, a_level);
1099
1100 break;
1101 }
1102 case Mode::Transient:
1103 this->writePlotDataTransient(a_output, a_icomp, a_outputRealm, a_level);
1104
1105 break;
1106 default: {
1107 MayDay::Error("DischargeInceptionStepper::writePlotData - logic bust");
1108
1109 break;
1110 }
1111 }
1112}
1113
1114template <typename P, typename F, typename C>
1115void
1117 int& a_icomp,
1119 const int a_level) const noexcept
1120{
1121 CH_TIME("DischargeInceptionStepper::writePlotDataStationary");
1122 if (m_verbosity > 5) {
1123 pout() << "DischargeInceptionStepper::writePlotDataStationary" << endl;
1124 }
1125
1126 const std::string prefix = "DischargeInceptionStepper/";
1127
1128 if (m_plotField) {
1131
1132 m_amr->allocate(scratch, m_realm, 1);
1133 m_amr->allocate(scratchSurf, m_realm, phase::gas, 1);
1134
1135 for (const auto& V : m_voltageSweeps) {
1136
1137 // Potential for positive applied voltage
1138 DataOps::setValue(m_potential, 0.0);
1139 DataOps::incr(m_potential, m_potentialHomo, Real(V));
1140 DataOps::incr(m_potential, m_potentialInho, 1.0);
1141 m_fieldSolver->writeMultifluidData(a_output, a_icomp, m_potential, phase::gas, a_outputRealm, a_level, true);
1142
1143 // Potential for negative applied voltage
1144 DataOps::setValue(m_potential, 0.0);
1145 DataOps::incr(m_potential, m_potentialHomo, -Real(V));
1146 DataOps::incr(m_potential, m_potentialInho, 1.0);
1147 m_fieldSolver->writeMultifluidData(a_output, a_icomp, m_potential, phase::gas, a_outputRealm, a_level, true);
1148 }
1149
1150 for (const auto& V : m_voltageSweeps) {
1151 // Field magnitude for positive applied field
1152 DataOps::setValue(m_electricField, 0.0);
1153 DataOps::incr(m_electricField, m_electricFieldHomo, Real(V));
1154 DataOps::incr(m_electricField, m_electricFieldInho, 1.0);
1155 DataOps::dotProduct(scratch, m_electricField, m_electricField);
1157 m_fieldSolver->writeMultifluidData(a_output, a_icomp, scratch, phase::gas, a_outputRealm, a_level, true);
1158 m_fieldSolver->writeMultifluidData(a_output, a_icomp, m_electricField, phase::gas, a_outputRealm, a_level, true);
1159
1160 // Field magnitude for positive applied field
1161 DataOps::setValue(m_electricField, 0.0);
1162 DataOps::incr(m_electricField, m_electricFieldHomo, -Real(V));
1163 DataOps::incr(m_electricField, m_electricFieldInho, 1.0);
1164 DataOps::dotProduct(scratch, m_electricField, m_electricField);
1166 m_fieldSolver->writeMultifluidData(a_output, a_icomp, scratch, phase::gas, a_outputRealm, a_level, true);
1167 m_fieldSolver->writeMultifluidData(a_output, a_icomp, m_electricField, phase::gas, a_outputRealm, a_level, true);
1168 }
1169
1170 // Write the space charge density
1171 DataOps::setValue(scratch, m_rho, m_amr->getProbLo(), m_amr->getDx(), 0);
1172 m_amr->arithmeticAverage(scratch, m_realm);
1173 m_amr->interpGhostPwl(scratch, m_realm);
1174 m_fieldSolver->writeMultifluidData(a_output, a_icomp, scratch, phase::gas, a_outputRealm, a_level, true);
1175
1176 DataOps::setValue(scratchSurf, m_sigma, m_amr->getProbLo(), m_amr->getDx(), 0);
1177 m_fieldSolver->writeSurfaceData(a_output, a_icomp, *scratchSurf[a_level], a_outputRealm, a_level);
1178 }
1179
1180 if (m_plotInceptionVoltage) {
1181 this->writeData(a_output, a_icomp, m_inceptionVoltagePlus, a_outputRealm, a_level, false, true);
1182 this->writeData(a_output, a_icomp, m_inceptionVoltageMinu, a_outputRealm, a_level, false, true);
1183 this->writeData(a_output, a_icomp, m_streamerInceptionVoltagePlus, a_outputRealm, a_level, false, true);
1184 this->writeData(a_output, a_icomp, m_streamerInceptionVoltageMinu, a_outputRealm, a_level, false, true);
1185 this->writeData(a_output, a_icomp, m_townsendInceptionVoltagePlus, a_outputRealm, a_level, false, true);
1186 this->writeData(a_output, a_icomp, m_townsendInceptionVoltageMinu, a_outputRealm, a_level, false, true);
1187 }
1188
1189 const int numVoltages = m_voltageSweeps.size();
1190
1191 if (m_plotInceptionIntegral) {
1192 for (int i = 0; i < m_voltageSweeps.size(); i++) {
1193 this->writeData(a_output, a_icomp, m_inceptionIntegralPlus[i], a_outputRealm, a_level, false, true);
1194 }
1195 for (int i = 0; i < m_voltageSweeps.size(); i++) {
1196 this->writeData(a_output, a_icomp, m_inceptionIntegralMinu[i], a_outputRealm, a_level, false, true);
1197 }
1198 }
1199
1200 if (m_plotTownsend) {
1201 for (int i = 0; i < m_voltageSweeps.size(); i++) {
1202 this->writeData(a_output, a_icomp, m_townsendCriterionPlus[i], a_outputRealm, a_level, false, true);
1203 }
1204
1205 for (int i = 0; i < m_voltageSweeps.size(); i++) {
1206 this->writeData(a_output, a_icomp, m_townsendCriterionMinu[i], a_outputRealm, a_level, false, true);
1207 }
1208 }
1209
1210 if (m_plotBackgroundIonization) {
1211 for (int i = 0; i < m_voltageSweeps.size(); i++) {
1212 this->writeData(a_output, a_icomp, m_backgroundIonizationStationary[i], a_outputRealm, a_level, false, true);
1213 }
1214 }
1215
1216 if (m_plotDetachment) {
1217 for (int i = 0; i < m_voltageSweeps.size(); i++) {
1218 this->writeData(a_output, a_icomp, m_detachmentStationary[i], a_outputRealm, a_level, false, true);
1219 }
1220 }
1221
1222 if (m_plotFieldEmission) {
1223 for (int i = 0; i < m_voltageSweeps.size(); i++) {
1224 this->writeData(a_output, a_icomp, m_emissionRatesPlus[i], a_outputRealm, a_level, false, true);
1225 }
1226 for (int i = 0; i < m_voltageSweeps.size(); i++) {
1227 this->writeData(a_output, a_icomp, m_emissionRatesMinu[i], a_outputRealm, a_level, false, true);
1228 }
1229 }
1230
1231 if (m_plotAlpha) {
1234
1235 m_amr->allocate(alpha, m_realm, m_phase, a_level, 1);
1236 if (a_level > 0) {
1237 m_amr->allocate(alphaCoar, m_realm, m_phase, a_level - 1, 1);
1238 }
1239
1240 for (size_t i = 0; i < m_voltageSweeps.size(); i++) {
1241 this->evaluateFunction(alpha, m_voltageSweeps[i], m_alpha, a_level);
1242 if (a_level > 0) {
1243 this->evaluateFunction(alphaCoar, m_voltageSweeps[i], m_alpha, a_level - 1);
1244 m_amr->interpGhost(alpha, alphaCoar, a_level, m_realm, m_phase);
1245 }
1246 else {
1247 alpha.exchange();
1248 }
1249
1250 m_amr->copyData(a_output, alpha, a_level, a_outputRealm, m_realm, Interval(a_icomp, a_icomp), Interval(0, 0));
1251
1252 a_icomp++;
1253 }
1254 }
1255
1256 if (m_plotEta) {
1259
1260 m_amr->allocate(eta, m_realm, m_phase, a_level, 1);
1261 if (a_level > 0) {
1262 m_amr->allocate(etaCoar, m_realm, m_phase, a_level - 1, 1);
1263 }
1264
1265 for (size_t i = 0; i < m_voltageSweeps.size(); i++) {
1266 this->evaluateFunction(eta, m_voltageSweeps[i], m_eta, a_level);
1267 if (a_level > 0) {
1268 this->evaluateFunction(etaCoar, m_voltageSweeps[i], m_eta, a_level - 1);
1269 m_amr->interpGhost(eta, etaCoar, a_level, m_realm, m_phase);
1270 }
1271 else {
1272 eta.exchange();
1273 }
1274
1275 m_amr->copyData(a_output, eta, a_level, a_outputRealm, m_realm, Interval(a_icomp, a_icomp), Interval(0, 0));
1276
1277 a_icomp++;
1278 }
1279 }
1280
1281 if (m_plotAlpha && m_plotEta) {
1284
1285 m_amr->allocate(alphaEff, m_realm, m_phase, a_level, 1);
1286 if (a_level > 0) {
1287 m_amr->allocate(alphaEffCoar, m_realm, m_phase, a_level - 1, 1);
1288 }
1289
1290 auto alphaFunc = [alpha = this->m_alpha, eta = this->m_eta](const Real E, const RealVect x) -> Real {
1291 return alpha(E, x) - eta(E, x);
1292 };
1293
1294 for (size_t i = 0; i < m_voltageSweeps.size(); i++) {
1295 this->evaluateFunction(alphaEff, m_voltageSweeps[i], alphaFunc, a_level);
1296 if (a_level > 0) {
1297 this->evaluateFunction(alphaEffCoar, m_voltageSweeps[i], alphaFunc, a_level - 1);
1298 m_amr->interpGhost(alphaEff, alphaEffCoar, a_level, m_realm, m_phase);
1299 }
1300 else {
1301 alphaEff.exchange();
1302 }
1303
1304 // Do a copy, but note that this does not include ghost cells across the CF interface since
1305 // we are only computing for a single grid level.
1306 m_amr->copyData(a_output, alphaEff, a_level, a_outputRealm, m_realm, Interval(a_icomp, a_icomp), Interval(0, 0));
1307
1308 a_icomp++;
1309 }
1310 }
1311}
1312
1313template <typename P, typename F, typename C>
1314void
1316 int& a_icomp,
1318 const int a_level) const noexcept
1319{
1320 CH_TIME("DischargeInceptionStepper::writePlotDataTransient");
1321 if (m_verbosity > 5) {
1322 pout() << "DischargeInceptionStepper::writePlotDataTransient" << endl;
1323 }
1324
1325 CH_assert(a_level >= 0);
1326 CH_assert(a_level <= m_amr->getFinestLevel());
1327
1328 // Add potential and electric field to output. Note that we need to scale appropriately.
1329 if (m_plotField) {
1330
1331 // Holds the field magnitude
1334
1335 m_amr->allocate(scratch, m_realm, 1);
1336 m_amr->allocate(scratchSurf, m_realm, phase::gas, 1);
1337
1338 DataOps::dotProduct(scratch, m_electricField, m_electricField);
1340 m_amr->arithmeticAverage(scratch, m_realm);
1341 m_amr->interpGhostPwl(scratch, m_realm);
1342
1343 m_fieldSolver->writeMultifluidData(a_output, a_icomp, m_potential, phase::gas, a_outputRealm, a_level, true);
1344 m_fieldSolver->writeMultifluidData(a_output, a_icomp, scratch, phase::gas, a_outputRealm, a_level, true);
1345 m_fieldSolver->writeMultifluidData(a_output, a_icomp, m_electricField, phase::gas, a_outputRealm, a_level, true);
1346
1347 // Write the space charge density
1348 DataOps::setValue(scratch, m_rho, m_amr->getProbLo(), m_amr->getDx(), 0);
1349 m_amr->arithmeticAverage(scratch, m_realm);
1350 m_amr->interpGhostPwl(scratch, m_realm);
1351 m_fieldSolver->writeMultifluidData(a_output, a_icomp, scratch, phase::gas, a_outputRealm, a_level, true);
1352
1353 DataOps::setValue(scratchSurf, m_sigma, m_amr->getProbLo(), m_amr->getDx(), 0);
1354 m_fieldSolver->writeSurfaceData(a_output, a_icomp, *scratchSurf[a_level], a_outputRealm, a_level);
1355 }
1356
1357 if (m_plotInceptionIntegral) {
1358 this->writeData(a_output, a_icomp, m_inceptionIntegral, a_outputRealm, a_level, false, true);
1359 }
1360
1361 if (m_plotTownsend) {
1362 this->writeData(a_output, a_icomp, m_townsendCriterion, a_outputRealm, a_level, false, true);
1363 }
1364
1365 if (m_plotBackgroundIonization) {
1367 m_amr->allocate(bgIonization, m_realm, m_phase, a_level, 1);
1368
1369 this->evaluateFunction(bgIonization, m_voltageCurve(m_time), m_backgroundRate, a_level);
1370
1371 // Do a copy, but note that this does not include ghost cells across the CF interface since
1372 // we are only computing for a single grid level.
1373 m_amr
1374 ->copyData(a_output, bgIonization, a_level, a_outputRealm, m_realm, Interval(a_icomp, a_icomp), Interval(0, 0));
1375
1376 a_icomp++;
1377 }
1378
1379 if (m_plotDetachment) {
1382
1383 m_amr->allocate(detachRate, m_realm, m_phase, a_level, 1);
1384 if (a_level > 0) {
1385 m_amr->allocate(detachRateCoar, m_realm, m_phase, a_level - 1, 1);
1386 }
1387
1388 this->evaluateFunction(detachRate, m_voltageCurve(m_time), m_detachmentRate, a_level);
1389 if (a_level > 0) {
1390 this->evaluateFunction(detachRateCoar, m_voltageCurve(m_time), m_detachmentRate, a_level - 1);
1391 m_amr->interpGhost(detachRate, detachRateCoar, a_level, m_realm, m_phase);
1392 }
1393
1394 m_amr->copyData(a_output, detachRate, a_level, a_outputRealm, m_realm, Interval(a_icomp, a_icomp), Interval(0, 0));
1395
1396 a_icomp++;
1397 }
1398
1399 if (m_plotFieldEmission) {
1400 this->writeData(a_output, a_icomp, m_emissionRate, a_outputRealm, a_level, false, false);
1401 }
1402
1403 if (m_plotAlpha) {
1406
1407 m_amr->allocate(alpha, m_realm, m_phase, a_level, 1);
1408 if (a_level > 0) {
1409 m_amr->allocate(alphaCoar, m_realm, m_phase, a_level - 1, 1);
1410 }
1411
1412 this->evaluateFunction(alpha, m_voltageCurve(m_time), m_alpha, a_level);
1413 if (a_level > 0) {
1414 this->evaluateFunction(alphaCoar, m_voltageCurve(m_time), m_alpha, a_level - 1);
1415 m_amr->interpGhost(alpha, alphaCoar, a_level, m_realm, m_phase);
1416 }
1417 else {
1418 alpha.exchange();
1419 }
1420
1421 // Do a copy, but note that this does not include ghost cells across the CF interface since
1422 // we are only computing for a single grid level.
1423 m_amr->copyData(a_output, alpha, a_level, a_outputRealm, m_realm, Interval(a_icomp, a_icomp), Interval(0, 0));
1424
1425 a_icomp++;
1426 }
1427
1428 if (m_plotEta) {
1431
1432 m_amr->allocate(eta, m_realm, m_phase, a_level, 1);
1433 if (a_level > 0) {
1434 m_amr->allocate(etaCoar, m_realm, m_phase, a_level - 1, 1);
1435 }
1436
1437 this->evaluateFunction(eta, m_voltageCurve(m_time), m_eta, a_level);
1438 if (a_level > 0) {
1439 this->evaluateFunction(etaCoar, m_voltageCurve(m_time), m_eta, a_level - 1);
1440 m_amr->interpGhost(eta, etaCoar, a_level, m_realm, m_phase);
1441 }
1442 else {
1443 eta.exchange();
1444 }
1445
1446 // Do a copy, but note that this does not include ghost cells across the CF interface since
1447 // we are only computing for a single grid level.
1448 m_amr->copyData(a_output, eta, a_level, a_outputRealm, m_realm, Interval(a_icomp, a_icomp), Interval(0, 0));
1449
1450 a_icomp++;
1451 }
1452
1453 if (m_plotAlpha && m_plotEta) {
1456
1457 m_amr->allocate(alphaEff, m_realm, m_phase, a_level, 1);
1458 if (a_level > 0) {
1459 m_amr->allocate(alphaEffCoar, m_realm, m_phase, a_level - 1, 1);
1460 }
1461
1462 auto alphaFunc = [alpha = this->m_alpha, eta = this->m_eta](const Real E, const RealVect x) -> Real {
1463 return alpha(E, x) - eta(E, x);
1464 };
1465
1466 this->evaluateFunction(alphaEff, m_voltageCurve(m_time), alphaFunc, a_level);
1467 if (a_level > 0) {
1468 this->evaluateFunction(alphaEffCoar, m_voltageCurve(m_time), alphaFunc, a_level - 1);
1469 m_amr->interpGhost(alphaEff, alphaEffCoar, a_level, m_realm, m_phase);
1470 }
1471 else {
1472 alphaEff.exchange();
1473 }
1474
1475 // Do a copy, but note that this does not include ghost cells across the CF interface since
1476 // we are only computing for a single grid level.
1477 m_amr->copyData(a_output, alphaEff, a_level, a_outputRealm, m_realm, Interval(a_icomp, a_icomp), Interval(0, 0));
1478
1479 a_icomp++;
1480 }
1481}
1482
1483template <typename P, typename F, typename C>
1484Real
1486{
1487 CH_TIME("DischargeInceptionStepper::computeDt");
1488 if (m_verbosity > 5) {
1489 pout() << "DischargeInceptionStepper::computeDt" << endl;
1490 }
1491
1492 Real dt = std::numeric_limits<Real>::max();
1493
1494 if (m_mode == Mode::Transient) {
1495
1496 // Compute time step from ion solver.
1497 m_timeStepRestriction = TimeStepRestriction::Unknown;
1498
1499 // Restrict by ion solver
1500 if (m_ionTransport) {
1501 Real ionDt = std::numeric_limits<Real>::infinity();
1502 switch (m_transportAlgorithm) {
1503 case TransportAlgorithm::Euler: {
1504 ionDt = m_cfl * m_ionSolver->computeAdvectionDiffusionDt();
1505
1506 break;
1507 }
1508 case TransportAlgorithm::Heun: {
1509 ionDt = m_cfl * m_ionSolver->computeAdvectionDiffusionDt();
1510
1511 break;
1512 }
1513 case TransportAlgorithm::ImExCTU: {
1514 ionDt = m_cfl * m_ionSolver->computeAdvectionDt();
1515
1516 break;
1517 }
1518 default: {
1519 MayDay::Error("DischargeInceptionStepper::computDt -- logic bust");
1520
1521 break;
1522 }
1523 }
1524
1525 if (ionDt < dt) {
1526 dt = ionDt;
1527 m_timeStepRestriction = TimeStepRestriction::CDR;
1528 }
1529 }
1530
1531 // Restrict by voltage curve.
1532 Real curveDt = std::numeric_limits<Real>::max();
1533 if (m_timeStep == 0) {
1534 curveDt = m_firstDt;
1535 }
1536 else {
1537 const Real preU = m_voltageCurve(m_time - m_dt);
1538 const Real curU = m_voltageCurve(m_time);
1539 const Real dVdt = std::abs(curU - preU) / dt;
1540
1541 if (dVdt > std::numeric_limits<Real>::epsilon()) {
1542 curveDt = m_epsVoltage * std::abs(curU) / dVdt;
1543 }
1544
1545 // Do not grow too fast.
1546 curveDt = std::min(m_dt * (1.0 + m_maxDtGrowth), curveDt);
1547 curveDt = std::max(m_dt * (1.0 - m_maxDtGrowth), curveDt);
1548 }
1549
1550 if (curveDt < dt) {
1551 dt = curveDt;
1552 m_timeStepRestriction = TimeStepRestriction::VoltageCurve;
1553 }
1554
1555 // Restrict by hardcaps
1556 if (dt > m_maxDt) {
1557 dt = m_maxDt;
1558 m_timeStepRestriction = TimeStepRestriction::MaxHardcap;
1559 }
1560
1561 if (dt < m_minDt) {
1562 dt = m_minDt;
1563 m_timeStepRestriction = TimeStepRestriction::MinHardcap;
1564 }
1565 }
1566
1567 return dt;
1568}
1569
1570template <typename P, typename F, typename C>
1571Real
1573{
1574 CH_TIME("DischargeInceptionStepper::advance");
1575 if (m_verbosity > 5) {
1576 pout() << "DischargeInceptionStepper::advance" << endl;
1577 }
1578
1579 if (m_mode == Mode::Transient) {
1580 Timer timer("DischargeInceptionStepper::advance");
1581
1582 const Real curTime = m_time + a_dt;
1583 const Real curVoltage = m_voltageCurve(curTime);
1584
1585 // Compute the electric potential and field at the current time step -- these are used in the output routines
1586 DataOps::setValue(m_potential, 0.0);
1587 DataOps::incr(m_potential, m_potentialHomo, curVoltage);
1588 DataOps::incr(m_potential, m_potentialInho, 1.0);
1589
1590 m_amr->arithmeticAverage(m_potential, m_realm);
1591 m_amr->interpGhostPwl(m_potential, m_realm);
1592
1593 DataOps::setValue(m_electricField, 0.0);
1594 DataOps::incr(m_electricField, m_electricFieldHomo, curVoltage);
1595 DataOps::incr(m_electricField, m_electricFieldInho, 1.0);
1596
1597 m_amr->arithmeticAverage(m_electricField, m_realm);
1598 m_amr->interpGhostPwl(m_electricField, m_realm);
1599
1600 // Move the negative ions -- still want this for the time step.
1601 timer.startEvent("Ion advance");
1602 if (m_ionTransport) {
1603 this->computeIonVelocity(curVoltage);
1604 this->computeIonDiffusion(curVoltage);
1605 this->advanceIons(a_dt);
1606 }
1607 timer.stopEvent("Ion advance");
1608
1609 // Seed new particles and compute the inception integral.
1610 timer.startEvent("Inception integral");
1611 this->seedIonizationParticles(curVoltage);
1612 this->computeInceptionIntegralTransient(curVoltage);
1613 timer.stopEvent("Inception integral");
1614
1615 if (m_evaluateTownsend) {
1616 timer.startEvent("Townsend criterion");
1617 this->seedIonizationParticles(curVoltage);
1618 this->computeTownsendCriterionTransient(curVoltage);
1619 timer.stopEvent("Townsend criterion");
1620 }
1621
1622 // Compute the critical volume, critical area, the ionization volume and the electron appearance rate.
1623 timer.startEvent("Compute Vcr");
1624 const Real Vcr = this->computeCriticalVolumeTransient();
1625 const Real Acr = this->computeCriticalAreaTransient();
1626 const Real Vion = this->computeIonizationVolumeTransient(curVoltage);
1627 const Real Rdot = this->computeRdot(curVoltage);
1628 timer.stopEvent("Compute Vcr");
1629
1630 m_criticalVolume.emplace_back(curTime, Vcr);
1631 m_criticalArea.emplace_back(curTime, Acr);
1632 m_ionizationVolumeTransient.emplace_back(curTime, Vion);
1633 m_Rdot.emplace_back(curTime, Rdot);
1634
1635 // Compute the inception probability using the trapezoidal rule.
1636 if (m_Rdot.size() >= 2) {
1637 Real p = 0.0;
1638
1639 for (size_t i = 0; i < m_Rdot.size() - 1; i++) {
1640 const Real dt = m_Rdot[i + 1].first - m_Rdot[i].first;
1641
1642 p += 0.5 * dt * (m_Rdot[i + 1].second + m_Rdot[i].second);
1643 }
1644
1645 m_inceptionProbability.emplace_back(curTime, 1.0 - exp(-p));
1646 }
1647
1648 // Get the maximum K and T values
1649 timer.startEvent("Get max/min K");
1650 Real maxK = -std::numeric_limits<Real>::max();
1651 Real minK = +std::numeric_limits<Real>::max();
1652
1653 Real maxT = -std::numeric_limits<Real>::max();
1654 Real minT = +std::numeric_limits<Real>::max();
1655
1656 DataOps::getMaxMin(maxK, minK, m_inceptionIntegral, 0);
1657 DataOps::getMaxMin(maxT, minT, m_townsendCriterion, 0);
1658
1659 if (!m_fullIntegration) {
1660 maxK = std::min(maxK, m_inceptionK);
1661 maxT = std::min(maxT, 1.0);
1662 }
1663 m_maxK.emplace_back(m_time + a_dt, maxK);
1664 m_maxT.emplace_back(m_time + a_dt, maxT);
1665 timer.stopEvent("Get max/min K");
1666
1667 timer.startEvent("Write report");
1668 this->writeReportTransient();
1669 timer.stopEvent("Write report");
1670
1671 if (m_profile) {
1672 timer.eventReport(pout(), false);
1673 }
1674 }
1675 else {
1676 MayDay::Error("DischargeInceptionStepper::advance -- must have 'DischargeInceptionStepper.mode = transient'");
1677 }
1678
1679 return a_dt;
1680}
1681
1682template <typename P, typename F, typename C>
1683void
1685{
1686 CH_TIME("DischargeInceptionStepper::advanceIons");
1687 if (m_verbosity > 5) {
1688 pout() << "DischargeInceptionStepper::advanceIons" << endl;
1689 }
1690
1691 EBAMRCellData& phi = m_ionSolver->getPhi();
1692
1693 // Use an outflow BC for transport. Note that extrapolateAdvectiveFluxToEB computes
1694 // -n.(phi * v). Since we compute phi^(k+1) = phi^k - dt*sum(fluxes) this means that a negative
1695 // flux is an incoming flux.
1696 EBAMRIVData& ebFlux = m_ionSolver->getEbFlux();
1697 m_ionSolver->extrapolateAdvectiveFluxToEB(ebFlux);
1698 DataOps::floor(ebFlux, 0.0);
1699
1700 switch (m_transportAlgorithm) {
1701 case TransportAlgorithm::Euler: {
1703 m_amr->allocate(divJ, m_realm, m_phase, 1.0);
1704
1705 m_ionSolver->computeDivJ(divJ, phi, 0.0, false, true, false);
1706
1708
1709 break;
1710 }
1711 case TransportAlgorithm::Heun: {
1712 // Transient storage
1716
1717 m_amr->allocate(yp, m_realm, m_phase, 1);
1718 m_amr->allocate(k1, m_realm, m_phase, 1);
1719 m_amr->allocate(k2, m_realm, m_phase, 1);
1720
1721 // Compute k1 coefficient
1722 m_ionSolver->computeDivJ(k1, phi, 0.0, false, true, false);
1724 DataOps::incr(yp, k1, -a_dt);
1725
1726 // Compute k2 coefficient and final state
1727 m_ionSolver->computeDivJ(k2, yp, 0.0, false, true, false);
1728 DataOps::incr(phi, k1, -0.5 * a_dt);
1729 DataOps::incr(phi, k2, -0.5 * a_dt);
1730
1731 break;
1732 }
1733 case TransportAlgorithm::ImExCTU: {
1734 const bool addEbFlux = true;
1735 const bool addDomainFlux = true;
1736
1737 // Transient storage
1740
1741 m_amr->allocate(k1, m_realm, m_phase, 1);
1742 m_amr->allocate(k2, m_realm, m_phase, 1);
1743
1744 m_ionSolver->computeDivF(k1, phi, a_dt, false, true, true);
1745
1747 DataOps::scale(k1, -1.0);
1748
1749 // Use k1 as the old solution.
1751
1752 // Do the Euler solve.
1753 m_ionSolver->advanceCrankNicholson(phi, k2, k1, a_dt);
1754
1755 break;
1756 }
1757 default: {
1758 MayDay::Error("DischargeInceptionStepper::advanceIons -- logic bust");
1759 }
1760 }
1761
1762 DataOps::floor(phi, 0.0);
1763
1764 m_amr->average(phi, m_realm, m_phase, Average::Conservative);
1765 m_amr->interpGhost(phi, m_realm, m_phase);
1766}
1767
1768template <typename P, typename F, typename C>
1769void
1771{
1772 CH_TIME("DischargeInceptionStepper::synchronizeSolverTimes");
1773 if (m_verbosity > 5) {
1774 pout() << "DischargeInceptionStepper::synchronizeSolverTimes" << endl;
1775 }
1776
1778 m_time = a_time;
1779 m_dt = a_dt;
1780
1781 m_fieldSolver->setTime(a_step, a_time, a_dt);
1782 m_tracerParticleSolver->setTime(a_step, a_time, a_dt);
1783 m_ionSolver->setTime(a_step, a_time, a_dt);
1784}
1785
1786template <typename P, typename F, typename C>
1787void
1789{
1790 CH_TIME("DischargeInceptionStepper::printStepReport");
1791#ifndef NDEBUG
1792 if (m_verbosity > 5) {
1793 pout() << "DischargeInceptionStepper::printStepReport" << endl;
1794 }
1795#endif
1796
1798 switch (m_timeStepRestriction) {
1799 case TimeStepRestriction::Unknown: {
1800 timeStepMessage = "Unknown";
1801
1802 break;
1803 }
1804 case TimeStepRestriction::CDR: {
1805 timeStepMessage = "CFL";
1806
1807 break;
1808 }
1809 case TimeStepRestriction::VoltageCurve: {
1810 timeStepMessage = "Voltage curve";
1811
1812 break;
1813 }
1814 case TimeStepRestriction::MinHardcap: {
1815 timeStepMessage = "Min hardcap";
1816
1817 break;
1818 }
1819 case TimeStepRestriction::MaxHardcap: {
1820 timeStepMessage = "Max hardcap";
1821
1822 break;
1823 }
1824 default: {
1825 MayDay::Warning("DischargeInceptionStepper::printStepReport - logic bust");
1826
1827 break;
1828 }
1829 }
1830
1831 // clang-format off
1832 pout() << " ** Voltage = " << m_voltageCurve(m_time) << endl;
1833 pout() << " ** Crit. volume = " << m_criticalVolume.back().second << endl;
1834 pout() << " ** Inception probability = " << m_inceptionProbability.back().second << endl;
1835 pout() << " ** Time step restriction = " << "'" << timeStepMessage << "'" << endl;
1836 // clang-format on
1837}
1838
1839template <typename P, typename F, typename C>
1840void
1842{
1843 CH_TIME("DischargeInceptionStepper::preRegrid");
1844 if (m_verbosity > 5) {
1845 pout() << "DischargeInceptionStepper::preRegrid" << endl;
1846 }
1847
1848 m_fieldSolver->preRegrid(a_lmin, a_oldFinestLevel);
1849 m_tracerParticleSolver->preRegrid(a_lmin, a_oldFinestLevel);
1850 m_ionSolver->preRegrid(a_lmin, a_oldFinestLevel);
1851
1852 m_amr->allocate(m_scratchHomo, m_realm, 1);
1853 m_amr->allocate(m_scratchInho, m_realm, 1);
1854
1855 m_amr->copyData(m_scratchHomo, m_potentialHomo);
1856 m_amr->copyData(m_scratchInho, m_potentialInho);
1857}
1858
1859template <typename P, typename F, typename C>
1860void
1862{
1863 CH_TIME("DischargeInceptionStepper::regrid");
1864 if (m_verbosity > 5) {
1865 pout() << "DischargeInceptionStepper::regrid" << endl;
1866 }
1867
1868 // Regrid tracer particles and field
1869 m_fieldSolver->regrid(a_lmin, a_oldFinestLevel, a_newFinestLevel);
1870 m_tracerParticleSolver->regrid(a_lmin, a_oldFinestLevel, a_newFinestLevel);
1871 m_ionSolver->regrid(a_lmin, a_oldFinestLevel, a_newFinestLevel);
1872
1873 // Regrid electric field and potentials
1874 m_amr->reallocate(m_potential, a_lmin);
1875 m_amr->reallocate(m_potentialHomo, a_lmin);
1876 m_amr->reallocate(m_potentialInho, a_lmin);
1877
1878 m_amr->reallocate(m_electricField, a_lmin);
1879 m_amr->reallocate(m_electricFieldHomo, a_lmin);
1880 m_amr->reallocate(m_electricFieldInho, a_lmin);
1881
1882 m_amr->reallocate(m_gradAlpha, m_phase, a_lmin);
1883
1884 const EBCoarseToFineInterp::Type interpType = EBCoarseToFineInterp::ConservativeMinMod;
1885
1886 m_amr->interpToNewGrids(m_potentialHomo, m_scratchHomo, a_lmin, a_oldFinestLevel, a_newFinestLevel, interpType);
1887 m_amr->interpToNewGrids(m_potentialInho, m_scratchInho, a_lmin, a_oldFinestLevel, a_newFinestLevel, interpType);
1888
1889 switch (m_mode) {
1890 case Mode::Stationary: {
1891 m_amr->reallocate(m_inceptionVoltagePlus, m_phase, a_lmin);
1892 m_amr->reallocate(m_inceptionVoltageMinu, m_phase, a_lmin);
1893 m_amr->reallocate(m_streamerInceptionVoltagePlus, m_phase, a_lmin);
1894 m_amr->reallocate(m_streamerInceptionVoltageMinu, m_phase, a_lmin);
1895 m_amr->reallocate(m_townsendInceptionVoltagePlus, m_phase, a_lmin);
1896 m_amr->reallocate(m_townsendInceptionVoltageMinu, m_phase, a_lmin);
1897
1898 break;
1899 }
1900 case Mode::Transient: {
1901 m_amr->reallocate(m_inceptionIntegral, m_phase, a_lmin);
1902 m_amr->reallocate(m_townsendCriterion, m_phase, a_lmin);
1903 m_amr->reallocate(m_emissionRate, m_phase, a_lmin);
1904 m_amr->reallocate(m_backgroundIonization, m_phase, a_lmin);
1905 m_amr->reallocate(m_detachment, m_phase, a_lmin);
1906
1907 break;
1908 }
1909 default: {
1910 break;
1911 }
1912 }
1913
1914 // Solve for the inhomogeneous and homogeneous Poisson fields again
1915 this->solvePoisson();
1916
1917 // Re-compute the ion velocity and diffusion coefficients
1918 this->computeIonVelocity(m_voltageCurve(m_time));
1919 this->computeIonDiffusion(m_voltageCurve(m_time));
1920}
1921
1922template <typename P, typename F, typename C>
1923void
1925{
1926 CH_TIME("DischargeInceptionStepper::postRegrid");
1927 if (m_verbosity > 5) {
1928 pout() << "DischargeInceptionStepper::postRegrid" << endl;
1929 }
1930
1931 m_scratchHomo.clear();
1932 m_scratchInho.clear();
1933}
1934
1935template <typename P, typename F, typename C>
1936void
1938{
1939 CH_TIME("DischargeInceptionStepper::setVoltageCurve");
1940 if (m_verbosity > 5) {
1941 pout() << "DischargeInceptionStepper::setVoltageCurve" << endl;
1942 }
1943
1944 m_voltageCurve = a_voltageCurve;
1945}
1946
1947template <typename P, typename F, typename C>
1948void
1950{
1951 CH_TIME("DischargeInceptionStepper::setRho");
1952 if (m_verbosity > 5) {
1953 pout() << "DischargeInceptionStepper::setRho" << endl;
1954 }
1955
1956 m_rho = a_rho;
1957}
1958
1959template <typename P, typename F, typename C>
1960void
1962{
1963 CH_TIME("DischargeInceptionStepper::setSigma");
1964 if (m_verbosity > 5) {
1965 pout() << "DischargeInceptionStepper::setSigma" << endl;
1966 }
1967
1968 m_sigma = a_sigma;
1969}
1970
1971template <typename P, typename F, typename C>
1972void
1974{
1975 CH_TIME("DischargeInceptionStepper::setIonDensity");
1976 if (m_verbosity > 5) {
1977 pout() << "DischargeInceptionStepper::setIonDensity" << endl;
1978 }
1979
1980 m_initialIonDensity = a_density;
1981}
1982
1983template <typename P, typename F, typename C>
1984void
1986{
1987 CH_TIME("DischargeInceptionStepper::setIonMobility");
1988 if (m_verbosity > 5) {
1989 pout() << "DischargeInceptionStepper::setIonMobility" << endl;
1990 }
1991
1992 m_ionMobility = a_mobility;
1993}
1994
1995template <typename P, typename F, typename C>
1996void
1998{
1999 CH_TIME("DischargeInceptionStepper::setIonDiffusion");
2000 if (m_verbosity > 5) {
2001 pout() << "DischargeInceptionStepper::setIonDiffusion" << endl;
2002 }
2003
2004 m_ionDiffusion = a_diffCo;
2005}
2006
2007template <typename P, typename F, typename C>
2008void
2010 const std::function<Real(const Real& E, const RealVect& x)>& a_alpha) noexcept
2011{
2012 CH_TIME("DischargeInceptionStepper::setAlpha");
2013 if (m_verbosity > 5) {
2014 pout() << "DischargeInceptionStepper::setAlpha" << endl;
2015 }
2016
2017 m_alpha = a_alpha;
2018}
2019
2020template <typename P, typename F, typename C>
2021void
2023{
2024 CH_TIME("DischargeInceptionStepper::setEta");
2025 if (m_verbosity > 5) {
2026 pout() << "DischargeInceptionStepper::setEta" << endl;
2027 }
2028
2029 m_eta = a_eta;
2030}
2031
2032template <typename P, typename F, typename C>
2033const std::function<Real(const Real& E, const RealVect& x)>&
2038
2039template <typename P, typename F, typename C>
2040const std::function<Real(const Real& E, const RealVect& x)>&
2045
2046template <typename P, typename F, typename C>
2047void
2049 const std::function<Real(const Real& E, const RealVect& x)>& a_backgroundRate) noexcept
2050{
2051 CH_TIME("DischargeInceptionStepper::setBackgroundRate");
2052 if (m_verbosity > 5) {
2053 pout() << "DischargeInceptionStepper::setBackgroundRate" << endl;
2054 }
2055
2056 m_backgroundRate = a_backgroundRate;
2057}
2058
2059template <typename P, typename F, typename C>
2060void
2062 const std::function<Real(const Real& E, const RealVect& x)>& a_detachmentRate) noexcept
2063{
2064 CH_TIME("DischargeInceptionStepper::setDetachmentRate");
2065 if (m_verbosity > 5) {
2066 pout() << "DischargeInceptionStepper::setDetachmentRate" << endl;
2067 }
2068
2069 m_detachmentRate = a_detachmentRate;
2070}
2071
2072template <typename P, typename F, typename C>
2073void
2075 const std::function<Real(const Real& E, const RealVect& x)>& a_currentDensity) noexcept
2076{
2077 CH_TIME("DischargeInceptionStepper::setFieldEmission");
2078 if (m_verbosity > 5) {
2079 pout() << "DischargeInceptionStepper::setFieldEmission" << endl;
2080 }
2081
2082 m_fieldEmission = a_currentDensity;
2083}
2084
2085template <typename P, typename F, typename C>
2086void
2088 const std::function<Real(const Real& E, const RealVect& x)>& a_coefficient) noexcept
2089{
2090 CH_TIME("DischargeInceptionStepper::setSecondaryEmission");
2091 if (m_verbosity > 5) {
2092 pout() << "DischargeInceptionStepper::setSecondaryEmission" << endl;
2093 }
2094
2095 m_secondaryEmission = a_coefficient;
2096}
2097
2098template <typename P, typename F, typename C>
2099Mode
2104
2105template <typename P, typename F, typename C>
2106void
2108{
2109 CH_TIME("DischargeInceptionStepper::seedUniformParticles");
2110 if (m_verbosity > 5) {
2111 pout() << "DischargeInceptionStepper::seedUniformParticles" << endl;
2112 }
2113
2114 ParticleContainer<P>& amrParticles = m_tracerParticleSolver->getParticles();
2115 amrParticles.clearParticles();
2116
2117 for (int lvl = 0; lvl <= m_amr->getFinestLevel(); lvl++) {
2118 const DisjointBoxLayout& dbl = m_amr->getGrids(m_realm)[lvl];
2119 const DataIterator& dit = dbl.dataIterator();
2120 const EBISLayout& ebisl = m_amr->getEBISLayout(m_realm, m_phase)[lvl];
2121
2122 const LevelData<BaseFab<bool>>& validCellsLD = *m_amr->getValidCells(m_realm)[lvl];
2123
2124 const Real dx = m_amr->getDx()[lvl];
2125
2127
2128 const int nbox = dit.size();
2129
2130#pragma omp parallel for schedule(runtime)
2131 for (int mybox = 0; mybox < nbox; mybox++) {
2132 const DataIndex& din = dit[mybox];
2133
2134 const EBISBox& ebisbox = ebisl[din];
2136
2137 List<P>& particles = levelParticles[din].listItems();
2138
2139 auto regularKernel = [&](const IntVect& iv) -> void {
2140 if (validCells(iv, 0) && ebisbox.isRegular(iv)) {
2141 P p;
2142 p.position() = m_amr->getProbLo() + (0.5 * RealVect::Unit + RealVect(iv)) * dx;
2143
2144 particles.add(p);
2145 }
2146 };
2147
2148 auto irregularKernel = [&](const VolIndex& vof) -> void {
2149 if (validCells(vof.gridIndex())) {
2150 P p;
2151 p.position() = m_amr->getProbLo() + Location::position(Location::Cell::Centroid, vof, ebisbox, dx);
2152
2153 particles.add(p);
2154 }
2155 };
2156
2157 // Execute kernels over appropriate regions.
2158 const Box cellBox = dbl[din];
2159 VoFIterator& vofit = (*m_amr->getVofIterator(m_realm, m_phase)[lvl])[din];
2160
2163
2164 for (ListIterator<P> lit(particles); lit.ok(); ++lit) {
2165 P& p = lit();
2166
2167 // Set mass to zero and do a backup of the initial position (we need to rewind particles
2168 // back later on). The scalar<0> flag is the flag we use for start-stop criteria for the
2169 // particle integration.
2170 p.weight() = 0.0;
2171 p.template vect<0>() = p.position();
2172 }
2173 }
2174 }
2175
2176 // Remove particles inside the EB.
2177 m_amr->removeCoveredParticlesIF(amrParticles, m_phase);
2178
2180}
2181
2182template <typename P, typename F, typename C>
2183void
2185{
2186 CH_TIME("DischargeInceptionStepper::seedIonizationParticles");
2187 if (m_verbosity > 5) {
2188 pout() << "DischargeInceptionStepper::seedIonizationParticles" << endl;
2189 }
2190
2191 ParticleContainer<P>& amrParticles = m_tracerParticleSolver->getParticles();
2192 amrParticles.clearParticles();
2193
2194 // Scratch storages
2197
2198 m_amr->allocate(scratch, m_realm, m_phase, SpaceDim);
2199 m_amr->allocate(alphaMesh, m_realm, m_phase, 1);
2200
2201 this->superposition(scratch, a_voltage);
2202
2204
2205 for (int lvl = 0; lvl <= m_amr->getFinestLevel(); lvl++) {
2206 const DisjointBoxLayout& dbl = m_amr->getGrids(m_realm)[lvl];
2207 const DataIterator& dit = dbl.dataIterator();
2208 const EBISLayout& ebisl = m_amr->getEBISLayout(m_realm, m_phase)[lvl];
2209
2210 const LevelData<BaseFab<bool>>& validCellsLD = *m_amr->getValidCells(m_realm)[lvl];
2211
2212 const Real dx = m_amr->getDx()[lvl];
2213 const RealVect probLo = m_amr->getProbLo();
2214
2216
2217 const int nbox = dit.size();
2218
2219#pragma omp parallel for schedule(runtime)
2220 for (int mybox = 0; mybox < nbox; mybox++) {
2221 const DataIndex& din = dit[mybox];
2222
2223 const EBISBox& ebisbox = ebisl[din];
2225
2226 List<P>& particles = levelParticles[din].listItems();
2227
2228 const EBCellFAB& electricField = (*scratch[lvl])[din];
2229 const FArrayBox& electricFieldReg = electricField.getFArrayBox();
2230
2232 FArrayBox& alphaReg = alpha.getFArrayBox();
2233
2234 if (!ebisbox.isAllCovered()) {
2235
2236 auto regularKernel = [&](const IntVect& iv) -> void {
2237 if (validCells(iv, 0) && ebisbox.isRegular(iv)) {
2238
2239 const RealVect x = probLo + dx * (0.5 * RealVect::Unit + RealVect(iv));
2240 const RealVect EE = RealVect(
2242
2243 const Real E = EE.vectorLength();
2244 const Real curAlpha = m_alpha(E, x);
2245 const Real curEta = m_eta(E, x);
2246
2247 if (curAlpha > curEta) {
2248 P p;
2249
2250 p.position() = x;
2251
2252 particles.add(p);
2253
2254 alphaReg(iv, 0) = curAlpha - curEta;
2255 }
2256 }
2257 };
2258
2259 auto irregularKernel = [&](const VolIndex& vof) -> void {
2260 const IntVect iv = vof.gridIndex();
2261
2262 if (validCells(iv, 0) && ebisbox.isIrregular(iv)) {
2263 const RealVect x = probLo + Location::position(Location::Cell::Centroid, vof, ebisbox, dx);
2265 const Real E = EE.vectorLength();
2266
2267 const Real curAlpha = m_alpha(E, x);
2268 const Real curEta = m_eta(E, x);
2269
2270 if (curAlpha > curEta) {
2271 P p;
2272
2273 p.position() = x;
2274
2275 particles.add(p);
2276
2277 alpha(vof, 0) = curAlpha - curEta;
2278 }
2279 }
2280 };
2281
2282 // Execute kernels over appropriate regions.
2283 const Box cellBox = dbl[din];
2284 VoFIterator& vofit = (*m_amr->getVofIterator(m_realm, m_phase)[lvl])[din];
2285
2288 }
2289
2290 for (ListIterator<P> lit(particles); lit.ok(); ++lit) {
2291 P& p = lit();
2292
2293 p.weight() = 0.0;
2294 p.template vect<0>() = p.position();
2295 p.template vect<2>() = RealVect::Zero;
2296 }
2297 }
2298 }
2299
2300 // Update ghost cells
2301 m_amr->arithmeticAverage(alphaMesh, m_realm, m_phase);
2302 m_amr->interpGhost(alphaMesh, m_realm, m_phase);
2303
2304 // Compute gradient
2305 m_amr->computeGradient(m_gradAlpha, alphaMesh, m_realm, m_phase);
2306
2307 // Update ghost cells in the gradient
2308 m_amr->arithmeticAverage(m_gradAlpha, m_realm, m_phase);
2309 m_amr->interpGhost(m_gradAlpha, m_realm, m_phase);
2310 m_amr->interpToCentroids(m_gradAlpha, m_realm, m_phase);
2311}
2312
2313template <typename P, typename F, typename C>
2314void
2316{
2317 CH_TIME("DischargeInceptionStepper::postInitialize");
2318 if (m_verbosity > 5) {
2319 pout() << "DischargeInceptionStepper::postInitialize" << endl;
2320 }
2321
2322 // Initialize the negative ion solver
2323 m_ionSolver->initialData();
2324 this->computeIonVelocity(m_voltageCurve(m_time));
2325 this->computeIonDiffusion(m_voltageCurve(m_time));
2326
2327 switch (m_mode) {
2328 case Mode::Stationary: {
2329 this->computeInceptionIntegralStationary();
2330 this->computeTownsendCriterionStationary();
2331 this->computeCriticalVolumeStationary();
2332 this->computeCriticalAreaStationary();
2333 this->computeIonizationVolumeStationary();
2334 this->computeInceptionVoltageVolume();
2335
2336 // Compute background ionization and field emission on the mesh. Mostly for plotting.
2337 if (m_plotBackgroundIonization) {
2338 this->computeBackgroundIonizationStationary();
2339 }
2340
2341 if (m_plotDetachment) {
2342 this->computeDetachmentStationary();
2343 }
2344
2345 if (m_plotFieldEmission) {
2346 this->computeFieldEmissionStationary();
2347 }
2348
2349 this->writeReportStationary();
2350
2351 break;
2352 }
2353 case Mode::Transient: {
2354
2355 // Seed tracer particles and set velocity
2356 this->seedIonizationParticles(m_voltageCurve(m_time));
2357 this->computeInceptionIntegralTransient(m_voltageCurve(m_time));
2358
2359 // Evaluate the Townsend criterion if the user asks for it.
2360 if (m_evaluateTownsend) {
2361 this->seedIonizationParticles(m_voltageCurve(m_time));
2362 this->computeTownsendCriterionTransient(m_voltageCurve(m_time));
2363 }
2364
2365 // Get the maximum K and T values
2366 Real maxK = -std::numeric_limits<Real>::max();
2367 Real minK = +std::numeric_limits<Real>::max();
2368
2369 Real maxT = -std::numeric_limits<Real>::max();
2370 Real minT = +std::numeric_limits<Real>::max();
2371
2372 DataOps::getMaxMin(maxK, minK, m_inceptionIntegral, 0);
2373 DataOps::getMaxMin(maxT, minT, m_townsendCriterion, 0);
2374 if (!m_fullIntegration) {
2375 maxK = std::min(maxK, m_inceptionK);
2376 maxT = std::min(maxT, 1.0);
2377 }
2378
2379 m_Rdot.emplace_back(m_time, this->computeRdot(m_voltageCurve(m_time)));
2380 m_criticalVolume.emplace_back(m_time, this->computeCriticalVolumeTransient());
2381 m_criticalArea.emplace_back(m_time, this->computeCriticalAreaTransient());
2382 m_ionizationVolumeTransient.emplace_back(m_time, this->computeIonizationVolumeTransient(m_voltageCurve(m_time)));
2383 m_inceptionProbability.emplace_back(m_time, 0.0);
2384 m_maxK.emplace_back(m_time, maxK);
2385 m_maxT.emplace_back(m_time, maxT);
2386
2387 break;
2388 }
2389 default: {
2390 break;
2391 }
2392 }
2393}
2394
2395template <typename P, typename F, typename C>
2396void
2398{
2399 CH_TIME("DischargeInceptionStepper::interpolateGradAlphaToParticles");
2400 if (m_verbosity > 5) {
2401 pout() << "DischargeInceptionStepper::interpolateGradAlphaToParticles" << endl;
2402 }
2403
2404 // Interpolate onto the particle weight structure
2405 m_amr->interpolateParticles<P, RealVect&, &P::template vect<2>>(m_tracerParticleSolver->getParticles(),
2406 m_realm,
2407 m_phase,
2408 m_gradAlpha,
2409 m_tracerParticleSolver->getInterpolationType(),
2410 true);
2411}
2412
2413template <typename P, typename F, typename C>
2414void
2416 const Real a_voltage) noexcept
2417{
2418 CH_TIME("DischargeInceptionStepper::computeInceptionIntegral");
2419 if (m_verbosity > 5) {
2420 pout() << "DischargeInceptionStepper::computeInceptionIntegral" << endl;
2421 }
2422
2423 // TLDR: For this integration we compute the value of the inception integral
2424 //
2425 // K = int alpha_eff(E)dl
2426 //
2427 // The integration runs from the start position of the particle and along a field line until either the
2428 // particle leaves the domain or alpha < 0.0.
2429 //
2430 // For the Euler rule we get
2431 //
2432 // T += alpha_eff(E(x)) * dx
2433 //
2434 // For the trapezoidal rule we get
2435 //
2436 // T += 0.5 * dx * [alpha_eff(E(x)) + alpha_eff(E(x+dx))]
2437
2438 // Reset the particles used for field line integration
2439 this->seedIonizationParticles(a_voltage);
2440 this->resetTracerParticles();
2441
2442 // Switch between algorithms.
2443 switch (m_inceptionAlgorithm) {
2444 case IntegrationAlgorithm::Euler: {
2445 this->inceptionIntegrateEuler(a_voltage);
2446
2447 break;
2448 }
2449 case IntegrationAlgorithm::Trapezoidal: {
2450 this->inceptionIntegrateTrapezoidal(a_voltage);
2451
2452 break;
2453 }
2454 default: {
2455 MayDay::Error("DischargeInceptionStepper::computeInceptionIntegral -- logic bust");
2456
2457 break;
2458 }
2459 }
2460
2461 // Deposit the particles on the mesh and get the max/min values.
2462 m_tracerParticleSolver->deposit(a_inceptionIntegral);
2463
2464 m_amr->conservativeAverage(a_inceptionIntegral, m_realm, m_phase);
2465 m_amr->interpGhost(a_inceptionIntegral, m_realm, m_phase);
2466}
2467
2468template <typename P, typename F, typename C>
2469void
2471{
2472 CH_TIME("DischargeInceptionStepper::computeInceptionIntegralStationary");
2473 if (m_verbosity > 5) {
2474 pout() << "DischargeInceptionStepper::computeInceptionIntegralStationary" << endl;
2475 }
2476
2477 constexpr int maxIter = 50;
2478
2479 // Get the maximum field (in the gas phase) with the electrodes at 1V
2480 Real maxField = 0.0;
2481 Real minField = 0.0;
2482
2483 EBAMRCellData gasField = m_amr->alias(phase::gas, m_electricField);
2484
2485 this->superposition(gasField, 1.0);
2486
2488
2489 // Compute the critical field and the minimum possible inception voltage.
2490 const Real Ecrit = this->getCriticalField();
2491
2492 Real curVoltage = m_relativeDeltaU * Ecrit / maxField;
2493 Real curMaxK = 0.0;
2494 int curIter = 0;
2495
2496 m_inceptionIntegralPlus.resize(0);
2497 m_inceptionIntegralMinu.resize(0);
2498
2499 // We use K1, K2, U2, U1 to extrapolate to a next voltage.
2500 Real K1 = 0.0;
2501 Real K2 = 0.0;
2502 Real U1 = Ecrit / maxField;
2503 Real U2 = curVoltage;
2504
2505 while (curMaxK < m_maxKLimit && curIter < maxIter) {
2508
2511
2514
2515 m_inceptionIntegralPlus.push_back(Kplus);
2516 m_inceptionIntegralMinu.push_back(Kminu);
2517
2518 this->computeInceptionIntegral(Kplus, +curVoltage);
2519 this->computeInceptionIntegral(Kminu, -curVoltage);
2520
2521 // Get the max and min values of K.
2522 Real maxKPlus = -std::numeric_limits<Real>::max();
2523 Real maxKMinu = -std::numeric_limits<Real>::max();
2524
2527
2528 this->getMaxValueAndLocation(maxKPlus, maxKPlusPos, Kplus);
2529 this->getMaxValueAndLocation(maxKMinu, maxKMinuPos, Kminu);
2530
2531 // Populate the relevant data containers.
2532 m_KPlusValues.emplace_back(std::make_tuple(curVoltage, maxKPlus, maxKPlusPos));
2533 m_KMinuValues.emplace_back(std::make_tuple(curVoltage, maxKMinu, maxKMinuPos));
2534
2535 curMaxK = std::max(maxKPlus, maxKMinu);
2536
2537 // Figure out the next voltage. User linear extrapolation near the first point and log-linear otherwise.
2538 K2 = curMaxK;
2539 U2 = curVoltage;
2540
2542 if (K1 <= 0.0) {
2543 const Real dKdU = (K2 - K1) / (U2 - U1);
2544
2545 nextVoltage = U1 + ((K2 - K1) + m_deltaK) / dKdU;
2546 }
2547 else {
2548 const Real dKdU = std::log(K2 / K1) / (U2 - U1);
2549
2550 nextVoltage = U1 + std::log((K2 + m_deltaK) / K1) / dKdU;
2551 }
2552
2553 K1 = K2;
2554 U1 = U2;
2555
2556 // Step to next voltage.
2557 curVoltage = std::min(nextVoltage, curVoltage * m_relativeDeltaU);
2558 curIter = curIter + 1;
2559
2560 if (!m_fullIntegration && (std::abs(curMaxK - m_inceptionK) <= 1E-3)) {
2561 break;
2562 }
2563 }
2564
2565 // Populate the voltage sweep table
2566 m_voltageSweeps.resize(0);
2567 for (int i = 0; i < m_KPlusValues.size(); i++) {
2568 m_voltageSweeps.push_back(std::get<0>(m_KPlusValues[i]));
2569 }
2570}
2571
2572template <typename P, typename F, typename C>
2573void
2575{
2576 CH_TIME("DischargeInceptionStepper::computeInceptionIntegralTransient");
2577 if (m_verbosity > 5) {
2578 pout() << "DischargeInceptionStepper::computeInceptionIntegralTransient" << endl;
2579 }
2580
2581 // Compute K-value on each particle using specified algorithm.
2582 switch (m_inceptionAlgorithm) {
2583 case IntegrationAlgorithm::Euler: {
2584 this->inceptionIntegrateEuler(a_voltage);
2585
2586 break;
2587 }
2588 case IntegrationAlgorithm::Trapezoidal: {
2589 this->inceptionIntegrateTrapezoidal(a_voltage);
2590
2591 break;
2592 }
2593 default: {
2594 MayDay::Error("DischargeInceptionStepper::computeInceptionIntegralTransient - logic bust");
2595
2596 break;
2597 }
2598 }
2599
2600 // Deposit the particles onto m_inceptionIntegral
2601 m_tracerParticleSolver->deposit(m_inceptionIntegral);
2602
2603 m_amr->conservativeAverage(m_inceptionIntegral, m_realm, m_phase);
2604 m_amr->interpGhost(m_inceptionIntegral, m_realm, m_phase);
2605}
2606
2607template <typename P, typename F, typename C>
2608void
2610{
2611 CH_TIME("DischargeInceptionStepper::inceptionIntegrateEuler");
2612 if (m_verbosity > 5) {
2613 pout() << "DischargeInceptionStepper::inceptionIntegrateEuler" << endl;
2614 }
2615
2616 const RealVect probLo = m_amr->getProbLo();
2617 const RealVect probHi = m_amr->getProbHi();
2618
2619 // Allocate a data holder for holding the processed particles. This
2620 // will be faster because then we only have to iterate through the
2621 // particles that are actually still moving.
2623 m_amr->allocate(amrProcessedParticles, m_realm);
2624
2625 ParticleContainer<P>& amrParticles = m_tracerParticleSolver->getParticles();
2626
2627 m_tracerParticleSolver->remap();
2628
2629 size_t particlesBefore = 0;
2630
2631 if (m_debug) {
2632 particlesBefore = amrParticles.getNumberOfValidParticlesGlobal();
2633 }
2634
2635 // Allocate something that holds the velocity of the electrons
2637 m_amr->allocate(scratch, m_realm, m_phase, SpaceDim);
2638 this->superposition(scratch, a_voltage);
2639 DataOps::scale(scratch, -1.0);
2640
2641 m_tracerParticleSolver->setVelocity(scratch);
2642 m_tracerParticleSolver->interpolateVelocities();
2643
2644 while (amrParticles.getNumberOfValidParticlesGlobal() > 0) {
2645
2646 this->interpolateGradAlphaToParticles();
2647
2648 // Euler integration.
2649 for (int lvl = 0; lvl <= m_amr->getFinestLevel(); lvl++) {
2650 const Real dx = m_amr->getDx()[lvl];
2651
2652 const DisjointBoxLayout& dbl = m_amr->getGrids(m_realm)[lvl];
2653 const DataIterator& dit = dbl.dataIterator();
2654
2655 const int nbox = dit.size();
2656
2657#pragma omp parallel for schedule(runtime)
2658 for (int mybox = 0; mybox < nbox; mybox++) {
2659 const DataIndex& din = dit[mybox];
2660
2661 List<P>& solverParticles = amrParticles[lvl][din].listItems();
2663
2664 for (ListIterator<P> lit(solverParticles); lit.ok();) {
2665 P& p = lit();
2666
2667 const RealVect x = p.position();
2668 const RealVect vel = p.velocity();
2669 const Real v = vel.vectorLength();
2670 const Real E = v;
2671 const Real alpha = m_alpha(E, x);
2672 const Real eta = m_eta(E, x);
2673 const Real alphaEff = alpha - eta;
2674 const Real tol = 1E-10;
2675 const Real gradAlpha = tol + (p.template vect<2>()).vectorLength();
2676
2677 // Select a step size equal to the avalance length, but never exceed the physical and grid hardcaps
2678 Real deltaX;
2679 deltaX = std::min(m_alphaDx / (tol + std::abs(alphaEff)), m_gradAlphaDx * std::abs(alphaEff / gradAlpha));
2680 deltaX = std::max(deltaX, m_minGridDx * dx);
2681 deltaX = std::min(deltaX, m_maxGridDx * dx);
2682 deltaX = std::min(deltaX, m_maxPhysDx);
2683 deltaX = std::max(deltaX, m_minPhysDx);
2684
2685 const Real dt = deltaX / v;
2686 const RealVect newPos = p.position() + dt * vel;
2687 const Real delta = (newPos - x).vectorLength();
2688
2689 const bool outsideDomain = this->particleOutsideGrid(newPos, probLo, probHi);
2690 const bool insideEB = this->particleInsideEB(newPos);
2691
2692 Real s = 0.0;
2693
2694 if (alphaEff < 0.0) {
2695 processedParticles.transfer(lit);
2696 }
2697 else if (insideEB) {
2698 // If the particle struck the EB or domain we finish off the integration with a partial step
2699 const RefCountedPtr<BaseIF>& impFunc = m_amr->getBaseImplicitFunction(m_phase);
2700
2701 if (ParticleOps::ebIntersectionBisect(impFunc, x, newPos, m_minGridDx * dx, s)) {
2702 p.weight() = p.weight() + s * delta * alphaEff;
2703 }
2704
2705 processedParticles.transfer(lit);
2706 }
2707 else if (outsideDomain) {
2708 if (ParticleOps::domainIntersection(x, newPos, m_amr->getProbLo(), m_amr->getProbHi(), s)) {
2709 p.weight() = p.weight() + s * delta * alphaEff;
2710 }
2711
2712 processedParticles.transfer(lit);
2713 }
2714 else {
2715 p.position() = newPos;
2716 p.weight() = p.weight() + deltaX * alphaEff;
2717
2718 ++lit;
2719 }
2720 }
2721
2722 // Transfer particles that completed their integration.
2723 if (!m_fullIntegration) {
2724 for (ListIterator<P> lit(solverParticles); lit.ok();) {
2725 P& p = lit();
2726
2727 if (p.weight() >= m_inceptionK) {
2728 processedParticles.transfer(lit);
2729 }
2730 else {
2731 ++lit;
2732 }
2733 }
2734 }
2735 }
2736 }
2737
2738 // Update velocities.
2739 m_tracerParticleSolver->remap();
2740 m_tracerParticleSolver->interpolateVelocities();
2741 }
2742
2744
2745 // Truncate the weights if we didn't run full integration
2746 if (!m_fullIntegration) {
2747 for (int lvl = 0; lvl <= m_amr->getFinestLevel(); lvl++) {
2748 const DisjointBoxLayout& dbl = m_amr->getGrids(m_realm)[lvl];
2749 const DataIterator& dit = dbl.dataIterator();
2750
2751 const int nbox = dit.size();
2752
2753#pragma omp parallel for schedule(runtime)
2754 for (int mybox = 0; mybox < nbox; mybox++) {
2755 const DataIndex& din = dit[mybox];
2756
2757 List<P>& solverParticles = amrParticles[lvl][din].listItems();
2758
2759 for (ListIterator<P> lit(solverParticles); lit.ok(); ++lit) {
2760 lit().weight() = std::min(m_inceptionK, lit().weight());
2761 }
2762 }
2763 }
2764 }
2765
2766 this->rewindTracerParticles();
2767
2768 size_t particlesAfter = 0;
2769
2770 if (m_debug) {
2771 particlesAfter = amrParticles.getNumberOfValidParticlesGlobal();
2772 }
2773
2775 MayDay::Warning("DischargeInceptionStepper::inceptionIntegrateEuler - lost/gained particles!");
2776 }
2777}
2778
2779template <typename P, typename F, typename C>
2780void
2782{
2783 CH_TIME("DischargeInceptionStepper::inceptionIntegrateTrapezoidal");
2784 if (m_verbosity > 5) {
2785 pout() << "DischargeInceptionStepper::inceptionIntegrateTrapezoidal" << endl;
2786 }
2787
2788 // TLDR: We move the particle using Heun's method.
2789 //
2790 // p^(k+1) = p^k + 0.5 * dt * [v(p^k) + v(p^l)]
2791 //
2792 // where p^l = p^k + dt * v(p^k). We will set dt = d/|v(p^k)|. Once we have the
2793 // particle endpoints we can compute the contribution to the inception integral
2794 // by using the trapezoidal (quadrature) rule
2795 //
2796 // T += 0.5 * dx * [alpha_eff(E(p^k)) + alpha_eff(E(p^(k+1)))],
2797 //
2798 // where dx = |p^(k+1) - p^k|.
2799
2800 const RealVect probLo = m_amr->getProbLo();
2801 const RealVect probHi = m_amr->getProbHi();
2802
2803 // Allocate a data holder for holding the processed particles. This
2804 // will be faster because then we only have to iterate through the
2805 // particles that are actually still moving.
2807 m_amr->allocate(amrProcessedParticles, m_realm);
2808
2809 ParticleContainer<P>& amrParticles = m_tracerParticleSolver->getParticles();
2810
2811 m_tracerParticleSolver->remap();
2812
2813 size_t particlesBefore = 0;
2814
2815 if (m_debug) {
2816 particlesBefore = amrParticles.getNumberOfValidParticlesGlobal();
2817 }
2818
2819 // Allocate something that holds the velocity of the electrons
2821 m_amr->allocate(scratch, m_realm, m_phase, SpaceDim);
2822 this->superposition(scratch, a_voltage);
2823 DataOps::scale(scratch, -1.0);
2824
2825 m_tracerParticleSolver->setVelocity(scratch);
2826 m_tracerParticleSolver->interpolateVelocities();
2827
2828 while (amrParticles.getNumberOfValidParticlesGlobal() > 0) {
2829
2830 this->interpolateGradAlphaToParticles();
2831
2832 // Euler stage.
2833 for (int lvl = 0; lvl <= m_amr->getFinestLevel(); lvl++) {
2834 const Real dx = m_amr->getDx()[lvl];
2835
2836 const DisjointBoxLayout& dbl = m_amr->getGrids(m_realm)[lvl];
2837 const DataIterator& dit = dbl.dataIterator();
2838
2839 const int nbox = dit.size();
2840
2841#pragma omp parallel for schedule(runtime)
2842 for (int mybox = 0; mybox < nbox; mybox++) {
2843 const DataIndex& din = dit[mybox];
2844
2845 List<P>& solverParticles = amrParticles[lvl][din].listItems();
2847
2848 for (ListIterator<P> lit(solverParticles); lit.ok();) {
2849 P& p = lit();
2850
2851 const RealVect x = p.position();
2852 const RealVect vel = p.velocity();
2853 const Real v = vel.vectorLength();
2854 const Real E = v;
2855 const Real alpha = m_alpha(E, x);
2856 const Real eta = m_eta(E, x);
2857 const Real alphaEff = alpha - eta;
2858 const Real tol = 1E-10;
2859 const Real gradAlpha = tol + (p.template vect<2>()).vectorLength();
2860
2861 // Select a step size equal to the avalance length, but never exceed the physical and grid hardcaps
2862 Real deltaX;
2863 deltaX = std::min(m_alphaDx / (tol + std::abs(alphaEff)), m_gradAlphaDx * std::abs(alphaEff / gradAlpha));
2864 deltaX = std::max(deltaX, m_minGridDx * dx);
2865 deltaX = std::min(deltaX, m_maxGridDx * dx);
2866 deltaX = std::min(deltaX, m_maxPhysDx);
2867 deltaX = std::max(deltaX, m_minPhysDx);
2868
2869 const Real dt = deltaX / v;
2870 const RealVect newPos = p.position() + dt * vel;
2871 const Real delta = (newPos - x).vectorLength();
2872
2873 const bool outsideDomain = this->particleOutsideGrid(newPos, probLo, probHi);
2874 const bool insideEB = this->particleInsideEB(newPos);
2875
2876 // If particle hit the EB or domain we finish off with a partial Euler step
2877 Real s = 0.0;
2878
2879 if (alphaEff < 0.0) {
2880 processedParticles.transfer(lit);
2881 }
2882 else if (insideEB) {
2883 const RefCountedPtr<BaseIF>& impFunc = m_amr->getBaseImplicitFunction(m_phase);
2884
2885 if (ParticleOps::ebIntersectionBisect(impFunc, x, newPos, m_minGridDx * dx, s)) {
2886 p.weight() = p.weight() + s * delta * alphaEff;
2887 }
2888
2889 processedParticles.transfer(lit);
2890 }
2891 else if (outsideDomain) {
2892 if (ParticleOps::domainIntersection(x, newPos, m_amr->getProbLo(), m_amr->getProbHi(), s)) {
2893 p.weight() = p.weight() + s * delta * alphaEff;
2894 }
2895
2896 processedParticles.transfer(lit);
2897 }
2898 else {
2899 // Do an Euler step, storaging alpha(p^k), v(p^k), and the time step size.
2900 p.template real<0>() = alphaEff;
2901 p.template real<1>() = dt;
2902 p.template vect<1>() = vel;
2903
2904 p.position() = newPos;
2905
2906 ++lit;
2907 }
2908 }
2909 }
2910 }
2911
2912 // Remap and update velocities.
2913 m_tracerParticleSolver->remap();
2914 m_tracerParticleSolver->interpolateVelocities();
2915
2916 // Second stage.
2917 for (int lvl = 0; lvl <= m_amr->getFinestLevel(); lvl++) {
2918 const Real dx = m_amr->getDx()[lvl];
2919
2920 const DisjointBoxLayout& dbl = m_amr->getGrids(m_realm)[lvl];
2921 const DataIterator& dit = dbl.dataIterator();
2922
2923 const int nbox = dit.size();
2924
2925#pragma omp parallel for schedule(runtime)
2926 for (int mybox = 0; mybox < nbox; mybox++) {
2927 const DataIndex& din = dit[mybox];
2928
2929 List<P>& solverParticles = amrParticles[lvl][din].listItems();
2931
2932 for (ListIterator<P> lit(solverParticles); lit.ok();) {
2933
2934 P& p = lit();
2935
2936 const Real dt = p.template real<1>();
2937 const RealVect vk = p.template vect<1>();
2938 const RealVect vk1 = p.velocity();
2939 const RealVect x = p.position();
2940 const Real E = vk1.vectorLength();
2941
2942 // Note the weird subtraction since p.position() was updated
2943 // to p^k + dt * v^k.
2944 const RealVect oldPos = p.position() - dt * vk;
2945 const RealVect newPos = oldPos + 0.5 * dt * (vk + vk1);
2946
2947 // Compute new alpha.
2948 const Real alphak = p.template real<0>();
2949 const Real alphak1 = m_alpha(E, x) - m_eta(E, x);
2950 const Real delta = (newPos - oldPos).vectorLength();
2951
2952 // Stop integration for particles that move into regions alpha < 0.0,
2953 // inside the EB or outside of the domain.
2954 const bool negativeAlpha = (alphak + alphak1) < 0.0;
2955 const bool outsideDomain = this->particleOutsideGrid(newPos, probLo, probHi);
2956 const bool insideEB = this->particleInsideEB(newPos);
2957
2958 // If the particle wound up inside the EB we finish off the integration with a partial Euler step
2959 Real s = 0.0;
2960
2961 if (negativeAlpha) {
2962 processedParticles.transfer(lit);
2963 }
2964 else if (insideEB) {
2965 const RefCountedPtr<BaseIF>& impFunc = m_amr->getBaseImplicitFunction(m_phase);
2966
2967 if (ParticleOps::ebIntersectionBisect(impFunc, oldPos, newPos, m_minGridDx * dx, s)) {
2968 p.weight() = p.weight() + s * delta * alphak;
2969 }
2970
2971 processedParticles.transfer(lit);
2972 }
2973 else if (outsideDomain) {
2974 if (ParticleOps::domainIntersection(oldPos, newPos, m_amr->getProbLo(), m_amr->getProbHi(), s)) {
2975 p.weight() = p.weight() + s * delta * alphak;
2976 }
2977
2978 processedParticles.transfer(lit);
2979 }
2980 else {
2981 p.position() = newPos;
2982 p.weight() = p.weight() + 0.5 * delta * (alphak + alphak1);
2983
2984 ++lit;
2985 }
2986 }
2987
2988 // Transfer particles that completed their integration (if we're doing partial integration)
2989 if (!m_fullIntegration) {
2990 for (ListIterator<P> lit(solverParticles); lit.ok();) {
2991 P& p = lit();
2992
2993 if (p.weight() >= m_inceptionK) {
2994 processedParticles.transfer(lit);
2995 }
2996 else {
2997 ++lit;
2998 }
2999 }
3000 }
3001 }
3002 }
3003
3004 // Remap and update velocities.
3005 m_tracerParticleSolver->remap();
3006 m_tracerParticleSolver->interpolateVelocities();
3007 }
3008
3009 // Copy processed particles over to the solver particles.
3011
3012 // Truncate the weights if we didn't run full integration
3013 if (!m_fullIntegration) {
3014 for (int lvl = 0; lvl <= m_amr->getFinestLevel(); lvl++) {
3015 const DisjointBoxLayout& dbl = m_amr->getGrids(m_realm)[lvl];
3016 const DataIterator& dit = dbl.dataIterator();
3017
3018 const int nbox = dit.size();
3019
3020#pragma omp parallel for schedule(runtime)
3021 for (int mybox = 0; mybox < nbox; mybox++) {
3022 const DataIndex& din = dit[mybox];
3023
3024 List<P>& solverParticles = amrParticles[lvl][din].listItems();
3025
3026 for (ListIterator<P> lit(solverParticles); lit.ok(); ++lit) {
3027 lit().weight() = std::min(m_inceptionK, lit().weight());
3028 }
3029 }
3030 }
3031 }
3032
3033 this->rewindTracerParticles();
3034}
3035
3036template <typename P, typename F, typename C>
3037void
3039{
3040 CH_TIME("DischargeInceptionStepper::computeTownsendCriterionStationary");
3041 if (m_verbosity > 5) {
3042 pout() << "DischargeInceptionStepper::computeTownsendCriterionStationary" << endl;
3043 }
3044
3045 CH_assert(m_inceptionIntegralPlus.size() == m_inceptionIntegralMinu.size());
3046
3047 m_townsendCriterionPlus.resize(0);
3048 m_townsendCriterionMinu.resize(0);
3049
3050 for (size_t i = 0; i < m_voltageSweeps.size(); i++) {
3051 const Real voltage = m_voltageSweeps[i];
3052
3053 const EBAMRCellData KPlus = m_inceptionIntegralPlus[i];
3054 const EBAMRCellData KMinu = m_inceptionIntegralMinu[i];
3055
3058
3061
3064
3067
3070
3073
3074 Real maxTPlus = 0.0;
3075 Real maxTMinu = 0.0;
3076
3079
3080 if (m_evaluateTownsend) {
3081
3082 // Positive polarity.
3083 this->resetTracerParticles();
3084
3085 switch (m_inceptionAlgorithm) {
3086 case IntegrationAlgorithm::Euler: {
3087 this->townsendTrackEuler(voltage);
3088
3089 break;
3090 }
3091 case IntegrationAlgorithm::Trapezoidal: {
3092 this->townsendTrackTrapezoidal(voltage);
3093
3094 break;
3095 }
3096 default: {
3097 MayDay::Error("DischargeInceptionStepper::computeTownsendCriterionStationary -- logic bust");
3098
3099 break;
3100 }
3101 }
3102
3103 m_tracerParticleSolver->deposit(townsendCriterionPlus);
3104
3105 m_amr->conservativeAverage(townsendCriterionPlus, m_realm, m_phase);
3107
3108 // Negative polarity.
3109 this->resetTracerParticles();
3110
3111 switch (m_inceptionAlgorithm) {
3112 case IntegrationAlgorithm::Euler: {
3113 this->townsendTrackEuler(-voltage);
3114
3115 break;
3116 }
3117 case IntegrationAlgorithm::Trapezoidal: {
3118 this->townsendTrackTrapezoidal(-voltage);
3119
3120 break;
3121 }
3122 default: {
3123 MayDay::Error("DischargeInceptionStepper::computeTownsendCriterionStationary -- logic bust");
3124
3125 break;
3126 }
3127 }
3128
3129 m_tracerParticleSolver->deposit(townsendCriterionMinu);
3130
3131 m_amr->conservativeAverage(townsendCriterionMinu, m_realm, m_phase);
3133
3134 // For turning K into exp(K) - 1
3135 auto exponentiate = [](const Real x) -> Real {
3136 return x > 0.0 ? exp(x) - 1 : 0.0;
3137 };
3138
3141
3144
3147
3148 this->getMaxValueAndLocation(maxTPlus, maxTPlusPos, townsendCriterionPlus);
3149 this->getMaxValueAndLocation(maxTMinu, maxTMinuPos, townsendCriterionMinu);
3150
3151 // If we're not doing full integration we truncate the Townsend value to 1.
3152 if (!m_fullIntegration) {
3153 auto truncate = [](const Real x) -> Real {
3154 return std::min(x, 1.0);
3155 };
3156
3159 }
3160 }
3161
3162 m_townsendCriterionPlus.push_back(townsendCriterionPlus);
3163 m_townsendCriterionMinu.push_back(townsendCriterionMinu);
3164
3165 m_TPlusValues.emplace_back(std::make_tuple(voltage, maxTPlus, maxTPlusPos));
3166 m_TMinuValues.emplace_back(std::make_tuple(voltage, maxTMinu, maxTMinuPos));
3167 }
3168}
3169
3170template <typename P, typename F, typename C>
3171void
3173{
3174 CH_TIME("DischargeInceptionStepper::computeTownsendCriterionTransient");
3175 if (m_verbosity > 5) {
3176 pout() << "DischargeInceptionStepper::computeTownsendCriterionTransient" << endl;
3177 }
3178
3179 // Compute T-value on each particle using specified algorithm.
3180 switch (m_inceptionAlgorithm) {
3181 case IntegrationAlgorithm::Euler: {
3182 this->townsendTrackEuler(a_voltage);
3183
3184 break;
3185 }
3186 case IntegrationAlgorithm::Trapezoidal: {
3187 this->townsendTrackTrapezoidal(a_voltage);
3188
3189 break;
3190 }
3191 default: {
3192 MayDay::Error("DischargeInceptionStepper::computeTownsendCriterionTransient - logic bust");
3193
3194 break;
3195 }
3196 }
3197
3198 // Compute gamma
3200 m_amr->allocate(gamma, m_realm, m_phase, 1);
3202 m_tracerParticleSolver->deposit(gamma);
3203
3204 // Turn K into exp(K)-1
3205 auto exponentiate = [](const Real x) -> Real {
3206 return x > 0.0 ? exp(x) - 1 : 0.0;
3207 };
3208 DataOps::copy(m_townsendCriterion, m_inceptionIntegral);
3209 DataOps::compute(m_townsendCriterion, exponentiate);
3210 DataOps::multiply(m_townsendCriterion, gamma);
3211
3212 // If we're running without full integration we truncate the Townsend value to 1.
3213 if (!m_fullIntegration) {
3214 DataOps::compute(m_townsendCriterion, [](const Real x) {
3215 return std::min(x, 1.0);
3216 });
3217 }
3218
3219 m_amr->conservativeAverage(m_townsendCriterion, m_realm, m_phase);
3220 m_amr->interpGhost(m_townsendCriterion, m_realm, m_phase);
3221}
3222
3223template <typename P, typename F, typename C>
3224void
3226{
3227 CH_TIME("DischargeInceptionStepper::townsendTrackEuler");
3228 if (m_verbosity > 5) {
3229 pout() << "DischargeInceptionStepper::townsendTrackEuler" << endl;
3230 }
3231 const RealVect probLo = m_amr->getProbLo();
3232 const RealVect probHi = m_amr->getProbHi();
3233
3234 // Allocate a data holder for holding the processed particles. This
3235 // will be faster because then we only have to iterate through the
3236 // particles that are actually still moving.
3238 m_amr->allocate(amrProcessedParticles, m_realm);
3239
3240 ParticleContainer<P>& amrParticles = m_tracerParticleSolver->getParticles();
3241
3242 m_tracerParticleSolver->remap();
3243
3244 size_t particlesBefore = 0;
3245
3246 if (m_debug) {
3247 particlesBefore = amrParticles.getNumberOfValidParticlesGlobal();
3248 }
3249
3250 // Allocate something that holds the velocity of the ions
3252 m_amr->allocate(scratch, m_realm, m_phase, SpaceDim);
3253 this->superposition(scratch, a_voltage);
3254
3255 m_tracerParticleSolver->setVelocity(scratch);
3256 m_tracerParticleSolver->interpolateVelocities();
3257
3258 while (amrParticles.getNumberOfValidParticlesGlobal() > 0) {
3259
3260 // Euler integration.
3261 for (int lvl = 0; lvl <= m_amr->getFinestLevel(); lvl++) {
3262 const Real dx = m_amr->getDx()[lvl];
3263
3264 // Integrate particles until they leave alpha > 0 or strike a cathode surface.
3265 const DisjointBoxLayout& dbl = m_amr->getGrids(m_realm)[lvl];
3266 const DataIterator& dit = dbl.dataIterator();
3267
3268 const int nbox = dit.size();
3269
3270#pragma omp parallel for schedule(runtime)
3271 for (int mybox = 0; mybox < nbox; mybox++) {
3272 const DataIndex& din = dit[mybox];
3273
3274 List<P>& solverParticles = amrParticles[lvl][din].listItems();
3276
3277 for (ListIterator<P> lit(solverParticles); lit.ok();) {
3278 P& p = lit();
3279
3280 const RealVect x = p.position();
3281 const RealVect vel = p.velocity();
3282 const Real v = vel.vectorLength();
3283 const Real E = v;
3284 const Real deltaX = m_townsendGridDx * dx;
3285 const Real dt = deltaX / v;
3286 const RealVect newPos = p.position() + dt * vel;
3287
3288 const bool outsideDomain = this->particleOutsideGrid(newPos, probLo, probHi);
3289 const bool insideEB = this->particleInsideEB(newPos);
3290
3291 // Stop integration if avalanche phase is over
3292 const Real alpha = m_alpha(E, x);
3293 const Real eta = m_eta(E, x);
3294 const Real alphaEff = alpha - eta;
3295 const bool negativeAlpha = alphaEff <= 0.0;
3296
3297 p.weight() = 0.0;
3298
3299 if (insideEB) {
3300 p.weight() = m_secondaryEmission(E, x);
3301
3302 processedParticles.transfer(lit);
3303 }
3304 else if (outsideDomain || negativeAlpha) {
3305 processedParticles.transfer(lit);
3306 }
3307 else {
3308 p.position() = newPos;
3309
3310 ++lit;
3311 }
3312 }
3313 }
3314 }
3315
3316 // Update velocities.
3317 m_tracerParticleSolver->remap();
3318 m_tracerParticleSolver->interpolateVelocities();
3319 }
3320
3322
3323 this->rewindTracerParticles();
3324
3325 size_t particlesAfter = 0;
3326
3327 if (m_debug) {
3328 particlesAfter = amrParticles.getNumberOfValidParticlesGlobal();
3329 }
3330
3332 MayDay::Warning("DischargeInceptionStepper::townsendTrackEuler - lost/gained particles!");
3333 }
3334}
3335
3336template <typename P, typename F, typename C>
3337void
3339{
3340 CH_TIME("DischargeInceptionStepper::townsendTrackTrapezoidal");
3341 if (m_verbosity > 5) {
3342 pout() << "DischargeInceptionStepper::townsendTrackTrapezoidal" << endl;
3343 }
3344
3345 // TLDR: We move the particle using Heun's method.
3346 //
3347 // p^(k+1) = p^k + 0.5 * dt * [v(p^k) + v(p^l)]
3348 //
3349 // where p^l = p^k + dt * v(p^k). We will set dt = d/|v(p^k)|.
3350
3351 const RealVect probLo = m_amr->getProbLo();
3352 const RealVect probHi = m_amr->getProbHi();
3353
3354 // Allocate a data holder for holding the processed particles. This
3355 // will be faster because then we only have to iterate through the
3356 // particles that are actually still moving.
3358 m_amr->allocate(amrProcessedParticles, m_realm);
3359
3360 ParticleContainer<P>& amrParticles = m_tracerParticleSolver->getParticles();
3361
3362 m_tracerParticleSolver->remap();
3363
3364 size_t particlesBefore = 0;
3365
3366 if (m_debug) {
3367 particlesBefore = amrParticles.getNumberOfValidParticlesGlobal();
3368 }
3369
3370 // Allocate something that holds the velocity of the ions
3372 m_amr->allocate(scratch, m_realm, m_phase, SpaceDim);
3373 this->superposition(scratch, a_voltage);
3374
3375 m_tracerParticleSolver->setVelocity(scratch);
3376 m_tracerParticleSolver->interpolateVelocities();
3377
3378 while (amrParticles.getNumberOfValidParticlesGlobal() > 0) {
3379
3380 // Euler stage.
3381 for (int lvl = 0; lvl <= m_amr->getFinestLevel(); lvl++) {
3382 const Real dx = m_amr->getDx()[lvl];
3383
3384 // First stage
3385 const DisjointBoxLayout& dbl = m_amr->getGrids(m_realm)[lvl];
3386 const DataIterator& dit = dbl.dataIterator();
3387
3388 const int nbox = dit.size();
3389
3390#pragma omp parallel for schedule(runtime)
3391 for (int mybox = 0; mybox < nbox; mybox++) {
3392 const DataIndex& din = dit[mybox];
3393
3394 List<P>& solverParticles = amrParticles[lvl][din].listItems();
3396
3397 for (ListIterator<P> lit(solverParticles); lit.ok();) {
3398 P& p = lit();
3399
3400 const RealVect x = p.position();
3401 const RealVect vel = p.velocity();
3402 const Real v = vel.vectorLength();
3403 const Real E = v;
3404 const Real alpha = m_alpha(E, x);
3405 const Real eta = m_eta(E, x);
3406 const Real alphaEff = alpha - eta;
3407
3408 // Compute a time step that we will use for the integration.
3409 const Real deltaX = m_townsendGridDx * dx;
3410 const Real dt = deltaX / v;
3411 const RealVect newPos = p.position() + vel * dt;
3412
3413 const bool outsideDomain = this->particleOutsideGrid(newPos, probLo, probHi);
3414 const bool insideEB = this->particleInsideEB(newPos);
3415
3416 if (insideEB) {
3417 p.weight() = m_secondaryEmission(E, x);
3418
3419 processedParticles.transfer(lit);
3420 }
3421 else if (alphaEff < 0.0) {
3422 processedParticles.transfer(lit);
3423 }
3424 else if (outsideDomain) {
3425 processedParticles.transfer(lit);
3426 }
3427 else {
3428 // Do an Euler step, storaging alpha(p^k), v(p^k), and the time step size.
3429 p.template real<0>() = alphaEff;
3430 p.template real<1>() = dt;
3431 p.template vect<1>() = vel;
3432
3433 p.position() = newPos;
3434
3435 ++lit;
3436 }
3437 }
3438 }
3439 }
3440
3441 // Remap and update velocities.
3442 m_tracerParticleSolver->remap();
3443 m_tracerParticleSolver->interpolateVelocities();
3444
3445 // Second stage.
3446 for (int lvl = 0; lvl <= m_amr->getFinestLevel(); lvl++) {
3447 const DisjointBoxLayout& dbl = m_amr->getGrids(m_realm)[lvl];
3448 const DataIterator& dit = dbl.dataIterator();
3449
3450 const int nbox = dit.size();
3451
3452#pragma omp parallel for schedule(runtime)
3453 for (int mybox = 0; mybox < nbox; mybox++) {
3454 const DataIndex& din = dit[mybox];
3455
3456 List<P>& solverParticles = amrParticles[lvl][din].listItems();
3458
3459 for (ListIterator<P> lit(solverParticles); lit.ok();) {
3460
3461 P& p = lit();
3462
3463 const Real dt = p.template real<1>();
3464 const RealVect vk = p.template vect<1>();
3465 const RealVect vk1 = p.velocity();
3466 const RealVect x = p.position();
3467 const Real E = vk1.vectorLength();
3468
3469 // Note the weird subtraction since p.position() was updated
3470 // to p^k + dt * v^k.
3471 const RealVect oldPos = p.position() - dt * vk;
3472 const RealVect newPos = p.position() + 0.5 * dt * (vk1 - vk);
3473
3474 // Compute new alpha.
3475 const Real alphak = p.template real<0>();
3476 const Real alphak1 = m_alpha(E, x) - m_eta(E, x);
3477
3478 // Stop integration for particles that move into regions alpha < 0.0,
3479 // inside the EB or outside of the domain.
3480 const bool negativeAlpha = (alphak + alphak1) < 0.0;
3481 const bool outsideDomain = this->particleOutsideGrid(newPos, probLo, probHi);
3482 const bool insideEB = this->particleInsideEB(newPos);
3483
3484 // If the particle wound up inside the EB we finish off the integration with a partial Euler step
3485 Real s = 0.0;
3486
3487 if (insideEB) {
3488 p.weight() = m_secondaryEmission(E, oldPos);
3489
3490 processedParticles.transfer(lit);
3491 }
3492 else if (outsideDomain) {
3493 processedParticles.transfer(lit);
3494 }
3495 else {
3496 p.position() = newPos;
3497
3498 ++lit;
3499 }
3500 }
3501 }
3502 }
3503
3504 // Remap and update velocities.
3505 m_tracerParticleSolver->remap();
3506 m_tracerParticleSolver->interpolateVelocities();
3507 }
3508
3509 // Copy processed particles over to the solver particles.
3511
3512 this->rewindTracerParticles();
3513
3514 size_t particlesAfter = 0;
3515
3516 if (m_debug) {
3517 particlesAfter = amrParticles.getNumberOfValidParticlesGlobal();
3518 }
3519
3521 MayDay::Warning("DischargeInceptionStepper::townsendTrackTrapezoidal - lost/gained particles!");
3522 }
3523}
3524
3525template <typename P, typename F, typename C>
3526Real
3528{
3529 CH_TIME("DischargeInceptionStepper::computeRdot");
3530 if (m_verbosity > 5) {
3531 pout() << "DischargeInceptionStepper::computeRdot" << endl;
3532 }
3533
3534 Real Rdot = 0.0;
3535
3536 // TLDR: We are computing the integral of dne/dt * (1-eta/alpha) over the critical volume and integral(j/e * dS) over the "critical surface"
3537 const RealVect probLo = m_amr->getProbLo();
3538
3539 // Compute the electric field at the input voltage
3541 m_amr->allocate(scratch, m_realm, m_phase, SpaceDim);
3542 this->superposition(scratch, a_voltage);
3543
3544 for (int lvl = 0; lvl <= m_amr->getFinestLevel(); lvl++) {
3545 const DisjointBoxLayout& dbl = m_amr->getGrids(m_realm)[lvl];
3546 const DataIterator& dit = dbl.dataIterator();
3547 const EBISLayout& ebisl = m_amr->getEBISLayout(m_realm, m_phase)[lvl];
3548 const Real dx = m_amr->getDx()[lvl];
3549
3550 const Real vol = std::pow(dx, SpaceDim);
3551 const Real area = std::pow(dx, SpaceDim - 1);
3552
3553 const int nbox = dit.size();
3554
3555#pragma omp parallel for schedule(runtime) reduction(+ : Rdot)
3556 for (int mybox = 0; mybox < nbox; mybox++) {
3557 const DataIndex& din = dit[mybox];
3558
3559 const EBISBox& ebisbox = ebisl[din];
3560 const BaseFab<bool>& validCells = (*m_amr->getValidCells(m_realm)[lvl])[din];
3561
3562 const EBCellFAB& electricField = (*scratch[lvl])[din];
3563 const EBCellFAB& inceptionIntegral = (*m_inceptionIntegral[lvl])[din];
3564 const EBCellFAB& fieldEmission = (*m_emissionRate[lvl])[din];
3565 const EBCellFAB& ionDensity = (*m_ionSolver->getPhi()[lvl])[din];
3566
3567 const FArrayBox& electricFieldReg = electricField.getFArrayBox();
3568 const FArrayBox& inceptionIntegralReg = inceptionIntegral.getFArrayBox();
3569 const FArrayBox& ionDensityReg = ionDensity.getFArrayBox();
3570
3571 // Add contribution from background ionization in regular cells.
3572 auto regularKernel = [&](const IntVect& iv) -> void {
3573 if (ebisbox.isRegular(iv) && validCells(iv, 0)) {
3574 if (inceptionIntegralReg(iv, 0) >= m_inceptionK) {
3575 const RealVect pos = probLo + (0.5 * RealVect::Unit + RealVect(iv)) * dx;
3576 const RealVect EE = RealVect(
3578 const Real E = EE.vectorLength();
3579
3580 const Real alpha = m_alpha(E, pos);
3581 const Real eta = m_eta(E, pos);
3582 const Real k = m_detachmentRate(E, pos);
3583 const Real dndt = m_backgroundRate(E, pos) + k * ionDensityReg(iv, 0);
3584
3585 CH_assert(alpha >= eta);
3586
3587 Rdot += dndt * (1.0 - eta / alpha) * vol;
3588 }
3589 }
3590 };
3591
3592 // Add contribution from background ionization and field emission in cut-cells.
3593 auto irregularKernel = [&](const VolIndex& vof) -> void {
3594 const IntVect iv = vof.gridIndex();
3595 if (ebisbox.isIrregular(iv) && validCells(iv, 0)) {
3596 if (inceptionIntegral(vof, 0) >= m_inceptionK) {
3597 const RealVect pos = probLo + Location::position(Location::Cell::Centroid, vof, ebisbox, dx);
3599 const Real E = EE.vectorLength();
3600
3601 const Real kappa = ebisbox.volFrac(vof);
3602 const Real areaFrac = ebisbox.bndryArea(vof);
3603 const Real alpha = m_alpha(E, pos);
3604 const Real eta = m_eta(E, pos);
3605 const Real k = m_detachmentRate(E, pos);
3606 const Real j = m_fieldEmission(E, pos);
3607 const Real dndt = m_backgroundRate(E, pos) + k * ionDensity(vof, 0);
3608
3609 Rdot += dndt * (1.0 - eta / alpha) * kappa * vol;
3610 Rdot += j / Units::Qe * areaFrac * area;
3611 }
3612 }
3613 };
3614
3615 // Run the kernels.
3616 Box cellBox = dbl[din];
3617 VoFIterator& vofit = (*m_amr->getVofIterator(m_realm, m_phase)[lvl])[din];
3618
3621 }
3622 }
3623
3624 return ParallelOps::sum(Rdot);
3625}
3626
3627template <typename P, typename F, typename C>
3628void
3630{
3631 CH_TIME("DischargeInceptionStepper::rewindTracerParticles");
3632 if (m_verbosity > 5) {
3633 pout() << "DischargeInceptionStepper::rewindTracerParticles" << endl;
3634 }
3635
3636 ParticleContainer<P>& amrParticles = m_tracerParticleSolver->getParticles();
3637
3638 for (int lvl = 0; lvl <= m_amr->getFinestLevel(); lvl++) {
3639 const DisjointBoxLayout& dbl = m_amr->getGrids(m_realm)[lvl];
3640 const DataIterator& dit = dbl.dataIterator();
3641
3642 const int nbox = dit.size();
3643
3644#pragma omp parallel for schedule(runtime)
3645 for (int mybox = 0; mybox < nbox; mybox++) {
3646 const DataIndex& din = dit[mybox];
3647
3648 for (ListIterator<P> lit(amrParticles[lvl][din].listItems()); lit.ok(); ++lit) {
3649 P& p = lit();
3650
3651 p.position() = p.template vect<0>();
3652 }
3653 }
3654 }
3655
3657}
3658
3659template <typename P, typename F, typename C>
3660void
3662{
3663 CH_TIME("DischargeInceptionStepper::resetTracerParticles");
3664 if (m_verbosity > 5) {
3665 pout() << "DischargeInceptionStepper::resetTracerParticles" << endl;
3666 }
3667
3668 ParticleContainer<P>& amrParticles = m_tracerParticleSolver->getParticles();
3669
3670 for (int lvl = 0; lvl <= m_amr->getFinestLevel(); lvl++) {
3671 const DisjointBoxLayout& dbl = m_amr->getGrids(m_realm)[lvl];
3672 const DataIterator& dit = dbl.dataIterator();
3673
3674 const int nbox = dit.size();
3675
3676#pragma omp parallel for schedule(runtime)
3677 for (int mybox = 0; mybox < nbox; mybox++) {
3678 const DataIndex& din = dit[mybox];
3679
3680 for (ListIterator<P> lit(amrParticles[lvl][din].listItems()); lit.ok(); ++lit) {
3681 P& p = lit();
3682
3683 p.weight() = 0.0;
3684 }
3685 }
3686 }
3687}
3688
3689template <typename P, typename F, typename C>
3690void
3692{
3693 CH_TIME("DischargeInceptionStepper::computeBackgroundIonizationStationary");
3694 if (m_verbosity > 5) {
3695 pout() << "DischargeInceptionStepper::computeBackgroundIonizationStationary" << endl;
3696 }
3697
3698 CH_assert(m_inceptionIntegralPlus.size() == m_inceptionIntegralMinu.size());
3699 CH_assert(m_inceptionIntegralPlus.size() == m_KPlusValues.size());
3700 CH_assert(m_inceptionIntegralMinu.size() == m_KMinuValues.size());
3701
3702 m_backgroundIonizationStationary.resize(0);
3703
3704 // Holds the electric field
3707
3708 // Solve ionization volume for each voltage
3709 for (size_t i = 0; i < m_voltageSweeps.size(); i++) {
3710 const Real voltage = m_voltageSweeps[i];
3711
3712 // Compute the electric field into the scratch storage
3713 this->superposition(scratch, voltage);
3714
3716
3718
3719 for (int lvl = 0; lvl <= m_amr->getFinestLevel(); lvl++) {
3720 const DisjointBoxLayout& dbl = m_amr->getGrids(m_realm)[lvl];
3721 const DataIterator& dit = dbl.dataIterator();
3722 const EBISLayout& ebisl = m_amr->getEBISLayout(m_realm, m_phase)[lvl];
3723 const Real dx = m_amr->getDx()[lvl];
3724 const RealVect probLo = m_amr->getProbLo();
3725
3726 const int nbox = dit.size();
3727
3728#pragma omp parallel for schedule(runtime)
3729 for (int mybox = 0; mybox < nbox; mybox++) {
3730 const DataIndex& din = dit[mybox];
3731
3732 const EBISBox& ebisbox = ebisl[din];
3733
3735 FArrayBox& bgIonizationReg = bgIonization.getFArrayBox();
3736
3737 const EBCellFAB& electricField = (*scratch[lvl])[din];
3738 const FArrayBox& electricFieldReg = electricField.getFArrayBox();
3739
3740 auto regularKernel = [&](const IntVect& iv) -> void {
3741 const RealVect pos = probLo + (0.5 * RealVect::Unit + RealVect(iv)) * dx;
3742 const RealVect EE = RealVect(
3744 const Real E = EE.vectorLength();
3745
3746 bgIonizationReg(iv, 0) = m_backgroundRate(E, pos);
3747 };
3748
3749 auto irregularKernel = [&](const VolIndex& vof) -> void {
3750 const RealVect pos = probLo + Location::position(Location::Cell::Centroid, vof, ebisbox, dx);
3752 const Real E = EE.vectorLength();
3753
3754 bgIonization(vof, 0) = m_backgroundRate(E, pos);
3755 };
3756
3757 // Execute kernels
3758 VoFIterator& vofit = (*m_amr->getVofIterator(m_realm, m_phase)[lvl])[din];
3759
3762 }
3763 }
3764
3765 m_backgroundIonizationStationary.push_back(backgroundRate);
3766 }
3767}
3768
3769template <typename P, typename F, typename C>
3770void
3772{
3773 CH_TIME("DischargeInceptionStepper::computeDetachmentStationary");
3774 if (m_verbosity > 5) {
3775 pout() << "DischargeInceptionStepper::computeDetachmentStationary" << endl;
3776 }
3777
3778 CH_assert(m_inceptionIntegralPlus.size() == m_inceptionIntegralMinu.size());
3779 CH_assert(m_inceptionIntegralPlus.size() == m_KPlusValues.size());
3780 CH_assert(m_inceptionIntegralMinu.size() == m_KMinuValues.size());
3781
3782 m_detachmentStationary.resize(0);
3783
3784 // Holds the electric field
3787
3788 // Solve ionization volume for each voltage
3789 for (size_t i = 0; i < m_voltageSweeps.size(); i++) {
3790 const Real voltage = m_voltageSweeps[i];
3791
3792 // Compute the electric field into the scratch storage
3793 this->superposition(scratch, voltage);
3794
3796
3798
3799 for (int lvl = 0; lvl <= m_amr->getFinestLevel(); lvl++) {
3800 const DisjointBoxLayout& dbl = m_amr->getGrids(m_realm)[lvl];
3801 const DataIterator& dit = dbl.dataIterator();
3802 const EBISLayout& ebisl = m_amr->getEBISLayout(m_realm, m_phase)[lvl];
3803 const Real dx = m_amr->getDx()[lvl];
3804 const RealVect probLo = m_amr->getProbLo();
3805
3806 const int nbox = dit.size();
3807
3808#pragma omp parallel for schedule(runtime)
3809 for (int mybox = 0; mybox < nbox; mybox++) {
3810 const DataIndex& din = dit[mybox];
3811
3812 const EBISBox& ebisbox = ebisl[din];
3813
3815 FArrayBox& detachmentReg = detachment.getFArrayBox();
3816
3817 const EBCellFAB& ionDensity = (*m_ionSolver->getPhi()[lvl])[din];
3818 const EBCellFAB& electricField = (*scratch[lvl])[din];
3819
3820 const FArrayBox& ionDensityReg = ionDensity.getFArrayBox();
3821 const FArrayBox& electricFieldReg = electricField.getFArrayBox();
3822
3823 auto regularKernel = [&](const IntVect& iv) -> void {
3824 const RealVect pos = probLo + (0.5 * RealVect::Unit + RealVect(iv)) * dx;
3825 const RealVect EE = RealVect(
3827 const Real E = EE.vectorLength();
3828 const Real phi = ionDensityReg(iv, 0);
3829
3830 detachmentReg(iv, 0) = m_detachmentRate(E, pos) * phi;
3831 };
3832
3833 auto irregularKernel = [&](const VolIndex& vof) -> void {
3834 const RealVect pos = probLo + Location::position(Location::Cell::Centroid, vof, ebisbox, dx);
3836 const Real E = EE.vectorLength();
3837 const Real phi = ionDensity(vof, 0);
3838
3839 detachment(vof, 0) = m_detachmentRate(E, pos) * phi;
3840 };
3841
3842 // Execute kernels
3843 VoFIterator& vofit = (*m_amr->getVofIterator(m_realm, m_phase)[lvl])[din];
3844
3847 }
3848 }
3849
3850 m_amr->conservativeAverage(detachmentRate, m_realm, m_phase);
3851 m_amr->interpGhost(detachmentRate, m_realm, m_phase);
3852
3853 m_detachmentStationary.push_back(detachmentRate);
3854 }
3855}
3856
3857template <typename P, typename F, typename C>
3858void
3860{
3861 CH_TIME("DischargeInceptionStepper::computeFieldEmissionStationary");
3862 if (m_verbosity > 5) {
3863 pout() << "DischargeInceptionStepper::computeFieldEmissionStationary" << endl;
3864 }
3865
3866 CH_assert(m_inceptionIntegralPlus.size() == m_inceptionIntegralMinu.size());
3867 CH_assert(m_inceptionIntegralPlus.size() == m_KPlusValues.size());
3868 CH_assert(m_inceptionIntegralMinu.size() == m_KMinuValues.size());
3869
3870 m_emissionRatesPlus.resize(0);
3871 m_emissionRatesMinu.resize(0);
3872
3873 const int numVoltages = m_inceptionIntegralPlus.size();
3874
3875 // Holds the electric field
3878
3881
3882 for (size_t i = 0; i < m_voltageSweeps.size(); i++) {
3883 const Real voltage = m_voltageSweeps[i];
3884
3885 // Compute the electric field into the scratch storage
3886 this->superposition(scratchPlus, +voltage);
3887 this->superposition(scratchMinu, -voltage);
3888
3891
3894
3897
3898 for (int lvl = 0; lvl <= m_amr->getFinestLevel(); lvl++) {
3899 const DisjointBoxLayout& dbl = m_amr->getGrids(m_realm)[lvl];
3900 const DataIterator& dit = dbl.dataIterator();
3901 const EBISLayout& ebisl = m_amr->getEBISLayout(m_realm, m_phase)[lvl];
3902 const Real& dx = m_amr->getDx()[lvl];
3903 const RealVect probLo = m_amr->getProbLo();
3904
3905 const int nbox = dit.size();
3906
3907#pragma omp parallel for schedule(runtime)
3908 for (int mybox = 0; mybox < nbox; mybox++) {
3909 const DataIndex& din = dit[mybox];
3910
3911 const EBISBox& ebisbox = ebisl[din];
3912
3913 // Here, emissionPlus is when we have the standard voltage (V = 1) and
3914 // emissionMinu is when the voltage is reverted.
3917
3918 emissionPlus.setVal(0.0);
3919 emissionMinu.setVal(0.0);
3920
3923
3924 auto irregularKernel = [&](const VolIndex& vof) -> void {
3925 const RealVect pos = probLo + Location::position(Location::Cell::Centroid, vof, ebisbox, dx);
3926
3927 const RealVect Eplus = RealVect(
3929 const RealVect Eminu = RealVect(
3931
3932 const Real normalEplus = Eplus.dotProduct(ebisbox.normal(vof));
3933 const Real normalEminu = Eminu.dotProduct(ebisbox.normal(vof));
3934
3935 emissionPlus(vof, 0) = 0.0;
3936 emissionMinu(vof, 0) = 0.0;
3937
3938 if (normalEplus < 0.0) {
3939 emissionPlus(vof, 0) = m_fieldEmission(Eplus.vectorLength(), pos);
3940 }
3941 if (normalEminu < 0.0) {
3942 emissionMinu(vof, 0) = m_fieldEmission(Eminu.vectorLength(), pos);
3943 }
3944 };
3945
3946 // Execute kernels
3947 VoFIterator& vofit = (*m_amr->getVofIterator(m_realm, m_phase)[lvl])[din];
3949 }
3950 }
3951
3952 m_emissionRatesPlus.push_back(emissionRatesPlus);
3953 m_emissionRatesMinu.push_back(emissionRatesMinu);
3954 }
3955}
3956
3957template <typename P, typename F, typename C>
3958void
3960 const Real& a_voltage) const noexcept
3961{
3962 CH_TIME("DischargeInceptionStepper::computeFieldEmission");
3963 if (m_verbosity > 5) {
3964 pout() << "DischargeInceptionStepper::computeFieldEmission" << endl;
3965 }
3966
3967 CH_assert(a_emissionRate[0]->nComp() == 1);
3968
3969 const RealVect probLo = m_amr->getProbLo();
3970
3971 // Compute the electric field into the scratch storage
3973 m_amr->allocate(scratch, m_realm, m_phase, SpaceDim);
3974 this->superposition(scratch, a_voltage);
3975
3976 for (int lvl = 0; lvl <= m_amr->getFinestLevel(); lvl++) {
3977 const DisjointBoxLayout& dbl = m_amr->getGrids(m_realm)[lvl];
3978 const DataIterator& dit = dbl.dataIterator();
3979 const EBISLayout& ebisl = m_amr->getEBISLayout(m_realm, m_phase)[lvl];
3980 const Real dx = m_amr->getDx()[lvl];
3981
3982 const int nbox = dit.size();
3983
3984#pragma omp parallel for schedule(runtime)
3985 for (int mybox = 0; mybox < nbox; mybox++) {
3986 const DataIndex& din = dit[mybox];
3987
3988 const EBISBox& ebisbox = ebisl[din];
3989
3990 // Here, emissionPlus is when we have the standard voltage (V = 1) and
3991 // emissionMinu is when the voltage is reverted.
3993
3994 emission.setVal(0.0);
3995
3996 const EBCellFAB& electricField = (*scratch[lvl])[din];
3997 const FArrayBox& electricFieldReg = electricField.getFArrayBox();
3998
3999 auto irregularKernel = [&](const VolIndex& vof) -> void {
4000 const RealVect pos = probLo + Location::position(Location::Cell::Centroid, vof, ebisbox, dx);
4002 const Real E = EE.vectorLength();
4003
4004 if (EE.dotProduct(ebisbox.normal(vof)) > 0.0) {
4005 emission(vof, 0) = m_fieldEmission(E, pos);
4006 }
4007 };
4008
4009 // Execute kernels
4010 VoFIterator& vofit = (*m_amr->getVofIterator(m_realm, m_phase)[lvl])[din];
4012 }
4013 }
4014}
4015
4016template <typename P, typename F, typename C>
4017void
4020 const Real& a_voltage,
4021 const std::function<Real(const Real E, const RealVect x)>& a_func) const noexcept
4022{
4023 CH_TIME("DischargeInceptionStepper::evaluateFunction");
4024 if (m_verbosity > 5) {
4025 pout() << "DischargeInceptionStepper::evaluateFunction" << endl;
4026 }
4027
4028 for (int lvl = 0; lvl <= m_amr->getFinestLevel(); lvl++) {
4029 this->evaluateFunction(*a_data[lvl], a_voltage, a_func, lvl);
4030 }
4031}
4032
4033template <typename P, typename F, typename C>
4034void
4036 const Real& a_voltage,
4037 const std::function<Real(const Real E, const RealVect x)>& a_func,
4038 const int a_level) const noexcept
4039{
4040 CH_TIME("DischargeInceptionStepper::evaluateFunction(level)");
4041 if (m_verbosity > 5) {
4042 pout() << "DischargeInceptionStepper::evaluateFunction(level)" << endl;
4043 }
4044
4045 CH_assert(a_level >= 0);
4046 CH_assert(a_level <= m_amr->getFinestLevel());
4047
4048 // Compute the electric field
4050 m_amr->allocate(scratch, m_realm, m_phase, SpaceDim);
4051 this->superposition(scratch, a_voltage);
4052
4053 const RealVect probLo = m_amr->getProbLo();
4054
4055 const DisjointBoxLayout& dbl = m_amr->getGrids(m_realm)[a_level];
4056 const DataIterator& dit = dbl.dataIterator();
4057 const EBISLayout& ebisl = m_amr->getEBISLayout(m_realm, m_phase)[a_level];
4058 const Real dx = m_amr->getDx()[a_level];
4059
4060 const int nbox = dit.size();
4061
4062#pragma omp parallel for schedule(runtime)
4063 for (int mybox = 0; mybox < nbox; mybox++) {
4064 const DataIndex& din = dit[mybox];
4065
4066 const EBISBox& ebisbox = ebisl[din];
4067
4068 // Here, emissionPlus is when we have the standard voltage (V = 1) and
4069 // emissionMinu is when the voltage is reverted.
4071 FArrayBox& dataReg = data.getFArrayBox();
4072
4073 data.setVal(0.0);
4074
4076 const FArrayBox& electricFieldReg = electricField.getFArrayBox();
4077
4078 auto regularKernel = [&](const IntVect& iv) -> void {
4079 const RealVect pos = probLo + (0.5 * RealVect::Unit + RealVect(iv)) * dx;
4081 const Real E = EE.vectorLength();
4082
4083 dataReg(iv, 0) = a_func(E, pos);
4084 };
4085
4086 auto irregularKernel = [&](const VolIndex& vof) -> void {
4087 const RealVect pos = probLo + Location::position(Location::Cell::Centroid, vof, ebisbox, dx);
4089 const Real E = EE.vectorLength();
4090
4091 data(vof, 0) = a_func(E, pos);
4092 };
4093
4094 // Execute kernels
4095 Box cellBox = dbl[din];
4096 VoFIterator& vofit = (*m_amr->getVofIterator(m_realm, m_phase)[a_level])[din];
4097
4100 }
4101}
4102
4103template <typename P, typename F, typename C>
4104void
4106{
4107 CH_TIME("DischargeInceptionStepper::computeInceptionVoltageVolume");
4108 if (m_verbosity > 5) {
4109 pout() << "DischargeInceptionStepper::computeInceptionVoltageVolume" << endl;
4110 }
4111
4112 // TLDR: This routine runs through all the K-values in each cell and estimates the
4113 // inception voltage using linear interpolation.
4114
4115 CH_assert(m_mode == Mode::Stationary);
4116
4117 if (m_inceptionIntegralPlus.size() < 2) {
4118 DataOps::setValue(m_inceptionVoltagePlus, std::numeric_limits<Real>::quiet_NaN());
4119 DataOps::setValue(m_inceptionVoltageMinu, std::numeric_limits<Real>::quiet_NaN());
4120
4121 MayDay::Warning("DischargeInceptionStepper::computeInceptionVoltageVolume -- not enough voltages for estimating "
4122 "inception voltage");
4123 }
4124 else {
4125 constexpr int comp = 0;
4126
4127 // Function which interpolates the inception voltage if possible. Used in the kernels. This one does interpolation.
4128 auto calcUincInterp = [Kinc = this->m_inceptionK,
4129 &V = this->m_voltageSweeps](const std::vector<Real>& K,
4131 Real streamerInc = std::numeric_limits<Real>::quiet_NaN();
4132 Real townsendInc = std::numeric_limits<Real>::quiet_NaN();
4133
4134 bool foundInception = false;
4135
4136 // Streamer criterion
4137 for (size_t i = 0; i < K.size() - 1; i++) {
4138 if (K[i] <= Kinc && K[i + 1] > Kinc) {
4139 streamerInc = V[i] + (Kinc - K[i]) * (V[i + 1] - V[i]) / (K[i + 1] - K[i]);
4140
4141 break;
4142 }
4143 else if (K[i] == Kinc) {
4144 streamerInc = V[i];
4145
4146 break;
4147 }
4148 }
4149
4150 // Townsend criterion
4151 for (size_t i = 0; i < T.size() - 1; i++) {
4152 if (T[i] <= 1.0 && T[i + 1] > 1) {
4153 townsendInc = V[i] + (1.0 - T[i]) * (V[i + 1] - V[i]) / (T[i + 1] - T[i]);
4154
4155 break;
4156 }
4157 else if (T[i] == 1.0) {
4158 townsendInc = V[i];
4159
4160 break;
4161 }
4162 }
4163
4164 Real Uinc;
4165
4167 Uinc = std::numeric_limits<Real>::quiet_NaN();
4168 }
4170 Uinc = townsendInc;
4171 }
4173 Uinc = streamerInc;
4174 }
4175 else {
4176 Uinc = std::min(streamerInc, townsendInc);
4177 }
4178
4180 };
4181
4182 // Same functionality as above, but without interpolation.
4183 auto calcUincNoInterp = [Kinc = this->m_inceptionK,
4184 &V = this->m_voltageSweeps](const std::vector<Real>& K,
4186 Real streamerInc = std::numeric_limits<Real>::quiet_NaN();
4187 Real townsendInc = std::numeric_limits<Real>::quiet_NaN();
4188
4189 // Streamer criterion
4190 for (size_t i = 0; i < K.size() - 1; i++) {
4191 if (K[i] <= Kinc && K[i + 1] >= Kinc) {
4192 streamerInc = V[i];
4193
4194 break;
4195 }
4196 }
4197
4198 // Townsend criterion
4199 for (size_t i = 0; i < T.size() - 1; i++) {
4200 if (T[i] <= 1.0 && T[i + 1] >= 1) {
4201 townsendInc = V[i];
4202
4203 break;
4204 }
4205 }
4206
4207 Real Uinc;
4208
4210 Uinc = std::numeric_limits<Real>::quiet_NaN();
4211 }
4213 Uinc = townsendInc;
4214 }
4216 Uinc = streamerInc;
4217 }
4218 else {
4219 Uinc = std::min(streamerInc, townsendInc);
4220 }
4221
4223 };
4224
4225 // Iterate through m_inceptionIntegral data and calculate the inception voltage.
4226 for (int lvl = 0; lvl <= m_amr->getFinestLevel(); ++lvl) {
4227 const DisjointBoxLayout& dbl = m_amr->getGrids(m_realm)[lvl];
4228 const DataIterator& dit = dbl.dataIterator();
4229
4230 const int nbox = dit.size();
4231
4232#pragma omp parallel for schedule(runtime)
4233 for (int mybox = 0; mybox < nbox; mybox++) {
4234 const DataIndex& din = dit[mybox];
4235
4236 EBCellFAB& inceptionVoltagePlus = (*m_inceptionVoltagePlus[lvl])[din];
4237 EBCellFAB& inceptionVoltageMinu = (*m_inceptionVoltageMinu[lvl])[din];
4238 EBCellFAB& streamerInceptionVoltagePlus = (*m_streamerInceptionVoltagePlus[lvl])[din];
4239 EBCellFAB& streamerInceptionVoltageMinu = (*m_streamerInceptionVoltageMinu[lvl])[din];
4240 EBCellFAB& townsendInceptionVoltagePlus = (*m_townsendInceptionVoltagePlus[lvl])[din];
4241 EBCellFAB& townsendInceptionVoltageMinu = (*m_townsendInceptionVoltageMinu[lvl])[din];
4242
4249
4250 // Pack the inception integral and townsend criterions into workable vectors.
4255
4260
4261 for (int i = 0; i < m_voltageSweeps.size(); i++) {
4262 inceptionIntegralPlus.push_back(&(*(m_inceptionIntegralPlus[i])[lvl])[din]);
4263 inceptionIntegralMinu.push_back(&(*(m_inceptionIntegralMinu[i])[lvl])[din]);
4264 townsendCriterionPlus.push_back(&(*(m_townsendCriterionPlus[i])[lvl])[din]);
4265 townsendCriterionMinu.push_back(&(*(m_townsendCriterionMinu[i])[lvl])[din]);
4266
4267 inceptionIntegralPlusReg.push_back(&(inceptionIntegralPlus.back()->getFArrayBox()));
4268 inceptionIntegralMinuReg.push_back(&(inceptionIntegralMinu.back()->getFArrayBox()));
4269 townsendCriterionPlusReg.push_back(&(townsendCriterionPlus.back()->getFArrayBox()));
4270 townsendCriterionMinuReg.push_back(&(townsendCriterionMinu.back()->getFArrayBox()));
4271 }
4272
4273 // Regular kernel.
4274 auto regularKernel = [&](const IntVect& iv) -> void {
4277
4280
4281 for (size_t i = 0; i < m_voltageSweeps.size(); i++) {
4282 Kplus.emplace_back((*inceptionIntegralPlusReg[i])(iv, 0));
4283 Kminu.emplace_back((*inceptionIntegralMinuReg[i])(iv, 0));
4284
4285 Tplus.emplace_back((*townsendCriterionPlusReg[i])(iv, 0));
4286 Tminu.emplace_back((*townsendCriterionMinuReg[i])(iv, 0));
4287 }
4288
4291
4292 if (m_fullIntegration) {
4295 }
4296 else {
4299 }
4300
4303
4306
4309 };
4310
4311 // Irregular kernel.
4312 auto irregularKernel = [&](const VolIndex& vof) -> void {
4315
4318
4319 for (size_t i = 0; i < m_voltageSweeps.size(); i++) {
4320 Kplus.emplace_back((*inceptionIntegralPlus[i])(vof, 0));
4321 Kminu.emplace_back((*inceptionIntegralMinu[i])(vof, 0));
4322
4323 Tplus.emplace_back((*townsendCriterionPlus[i])(vof, 0));
4324 Tminu.emplace_back((*townsendCriterionMinu[i])(vof, 0));
4325 }
4326
4329
4330 if (m_fullIntegration) {
4333 }
4334 else {
4337 }
4338
4341
4344
4347 };
4348
4349 // Kernel regions.
4350 const Box& cellBox = dbl[din];
4351 VoFIterator& vofit = (*m_amr->getVofIterator(m_realm, m_phase)[lvl])[din];
4352
4355 }
4356 }
4357
4358 // Coarsen data.
4359 m_amr->conservativeAverage(m_inceptionVoltagePlus, m_realm, m_phase);
4360 m_amr->interpGhost(m_inceptionVoltageMinu, m_realm, m_phase);
4361 }
4362}
4363
4364template <typename P, typename F, typename C>
4367{
4368 CH_TIME("DischargeInceptionStepper::computeMinimumInceptionVoltage");
4369 if (m_verbosity > 5) {
4370 pout() << "DischargeInceptionStepper::computeMinimumInceptionVoltage" << endl;
4371 }
4372
4373 const RealVect probLo = m_amr->getProbLo();
4374
4376
4377 UxInc.first = std::numeric_limits<Real>::max();
4378 UxInc.second = RealVect::Zero;
4379
4380 for (int lvl = 0; lvl <= m_amr->getFinestLevel(); lvl++) {
4381 const DisjointBoxLayout& dbl = m_amr->getGrids(m_realm)[lvl];
4382 const DataIterator& dit = dbl.dataIterator();
4383 const EBISLayout& ebisl = m_amr->getEBISLayout(m_realm, m_phase)[lvl];
4384 const Real& dx = m_amr->getDx()[lvl];
4385
4386 const int nbox = dit.size();
4387
4388#pragma omp parallel for schedule(runtime) reduction(pairmin : UxInc)
4389 for (int mybox = 0; mybox < nbox; mybox++) {
4390 const DataIndex& din = dit[mybox];
4391
4392 const BaseFab<bool>& validCells = (*m_amr->getValidCells(m_realm)[lvl])[din];
4393 const EBISBox& ebisBox = ebisl[din];
4394
4395 const EBCellFAB& voltage = (*a_Uinc[lvl])[din];
4396 const FArrayBox& voltageReg = voltage.getFArrayBox();
4397
4398 auto regularKernel = [&](const IntVect& iv) -> void {
4399 if (validCells(iv, 0) && ebisBox.isRegular(iv)) {
4400 const Real& U = voltageReg(iv, 0);
4401 const RealVect pos = probLo + (0.5 * RealVect::Unit + RealVect(iv)) * dx;
4402
4403 if (!(std::isnan(U))) {
4404 if (std::abs(U) < UxInc.first) {
4405 UxInc.first = std::abs(U);
4406 UxInc.second = pos;
4407 }
4408 }
4409 }
4410 };
4411
4412 auto irregularKernel = [&](const VolIndex& vof) -> void {
4413 const IntVect iv = vof.gridIndex();
4414 if (validCells(iv, 0) && ebisBox.isIrregular(iv)) {
4415 const Real& U = voltage(vof, 0);
4416 const RealVect centroid = ebisBox.centroid(vof);
4417 const RealVect pos = probLo + (0.5 * RealVect::Unit + RealVect(iv) + centroid) * dx;
4418
4419 if (!(std::isnan(U))) {
4420 if (std::abs(U) < UxInc.first) {
4421 UxInc.first = std::abs(U);
4422 UxInc.second = pos;
4423 }
4424 }
4425 }
4426 };
4427
4428 Box cellBox = dbl[din];
4429 VoFIterator& vofit = (*m_amr->getVofIterator(m_realm, m_phase)[lvl])[din];
4430
4433 }
4434 }
4435
4436 return ParallelOps::min(UxInc.first, UxInc.second);
4437}
4438
4439template <typename P, typename F, typename C>
4440void
4442{
4443 CH_TIME("DischargeInceptionStepper::computeCriticalVolumeStationary");
4444 if (m_verbosity > 5) {
4445 pout() << "DischargeInceptionStepper::computeCriticalVolumeStationary" << endl;
4446 }
4447
4448 CH_assert(m_inceptionIntegralPlus.size() == m_inceptionIntegralMinu.size());
4449 CH_assert(m_inceptionIntegralPlus.size() == m_KPlusValues.size());
4450 CH_assert(m_inceptionIntegralMinu.size() == m_KMinuValues.size());
4451
4452 m_criticalVolumePlus.resize(0);
4453 m_criticalVolumeMinu.resize(0);
4454
4455 const int numVoltages = m_inceptionIntegralPlus.size();
4456
4457 // Solve critical volume of K values for each voltage
4458 for (size_t i = 0; i < numVoltages; i++) {
4461
4462 for (int lvl = 0; lvl <= m_amr->getFinestLevel(); ++lvl) {
4463 const DisjointBoxLayout& dbl = m_amr->getGrids(m_realm)[lvl];
4464 const DataIterator& dit = dbl.dataIterator();
4465 const EBISLayout& ebisl = m_amr->getEBISLayout(m_realm, m_phase)[lvl];
4466
4467 const LevelData<BaseFab<bool>>& validCellsLD = *m_amr->getValidCells(m_realm)[lvl];
4468
4469 const Real dx = m_amr->getDx()[lvl];
4470
4471 const int nbox = dit.size();
4472
4473#pragma omp parallel for schedule(runtime) reduction(+ : criticalVolumePlus, criticalVolumeMinu)
4474 for (int mybox = 0; mybox < nbox; mybox++) {
4475 const DataIndex& din = dit[mybox];
4476
4477 const EBISBox& ebisbox = ebisl[din];
4479
4480 const EBCellFAB& inceptionIntegralPlus = (*(m_inceptionIntegralPlus[i])[lvl])[din];
4481 const EBCellFAB& inceptionIntegralMinu = (*(m_inceptionIntegralMinu[i])[lvl])[din];
4482
4485
4486 const EBCellFAB& townsendCritPlus = (*(m_townsendCriterionPlus[i])[lvl])[din];
4487 const EBCellFAB& townsendCritMinu = (*(m_townsendCriterionMinu[i])[lvl])[din];
4488
4489 const FArrayBox& townsendCritPlusReg = townsendCritPlus.getFArrayBox();
4490 const FArrayBox& townsendCritMinuReg = townsendCritMinu.getFArrayBox();
4491
4492 auto regularKernel = [&](const IntVect& iv) -> void {
4493 if (validCells(iv, 0) && ebisbox.isRegular(iv)) {
4494 if (inceptionIntegralPlusReg(iv, 0) >= m_inceptionK || townsendCritPlusReg(iv, 0) >= 1.0) {
4495 criticalVolumePlus += std::pow(dx, SpaceDim);
4496 }
4497 if (inceptionIntegralMinuReg(iv, 0) >= m_inceptionK || townsendCritMinuReg(iv, 0) >= 1.0) {
4498 criticalVolumeMinu += std::pow(dx, SpaceDim);
4499 }
4500 }
4501 };
4502
4503 auto irregularKernel = [&](const VolIndex& vof) -> void {
4504 if (validCells(vof.gridIndex())) {
4505 const Real kappa = ebisbox.volFrac(vof);
4506
4507 if (inceptionIntegralPlus(vof, 0) >= m_inceptionK || townsendCritPlus(vof, 0) >= 1.0) {
4508 criticalVolumePlus += kappa * std::pow(dx, SpaceDim);
4509 }
4510 if (inceptionIntegralMinu(vof, 0) >= m_inceptionK || townsendCritMinu(vof, 0) >= 1.0) {
4511 criticalVolumeMinu += kappa * std::pow(dx, SpaceDim);
4512 }
4513 }
4514 };
4515
4516 // Kernel regions.
4517 const Box cellBox = dbl[din];
4518 VoFIterator& vofit = (*m_amr->getVofIterator(m_realm, m_phase)[lvl])[din];
4519
4522 }
4523 }
4524
4525 m_criticalVolumePlus.push_back(ParallelOps::sum(criticalVolumePlus));
4526 m_criticalVolumeMinu.push_back(ParallelOps::sum(criticalVolumeMinu));
4527 }
4528}
4529
4530template <typename P, typename F, typename C>
4531Real
4533{
4534 CH_TIME("DischargeInceptionStepper::computeCriticalVolumeTransient");
4535 if (m_verbosity > 5) {
4536 pout() << "DischargeInceptionStepper::computeCriticalVolumeTransient" << endl;
4537 }
4538
4539 Real Vcr = 0.0;
4540
4541 for (int lvl = 0; lvl <= m_amr->getFinestLevel(); ++lvl) {
4542 const DisjointBoxLayout& dbl = m_amr->getGrids(m_realm)[lvl];
4543 const DataIterator& dit = dbl.dataIterator();
4544 const EBISLayout& ebisl = m_amr->getEBISLayout(m_realm, m_phase)[lvl];
4545
4546 const LevelData<BaseFab<bool>>& validCellsLD = *m_amr->getValidCells(m_realm)[lvl];
4547
4548 const Real dx = m_amr->getDx()[lvl];
4549 const Real vol = std::pow(dx, SpaceDim);
4550
4551 const int nbox = dit.size();
4552
4553#pragma omp parallel for schedule(runtime) reduction(+ : Vcr)
4554 for (int mybox = 0; mybox < nbox; mybox++) {
4555 const DataIndex& din = dit[mybox];
4556
4557 const EBISBox& ebisbox = ebisl[din];
4559
4560 const EBCellFAB& inceptionIntegral = (*m_inceptionIntegral[lvl])[din];
4561 const FArrayBox& inceptionIntegralReg = inceptionIntegral.getFArrayBox();
4562
4563 const EBCellFAB& townsendCriterion = (*m_townsendCriterion[lvl])[din];
4564 const FArrayBox& townsendCriterionReg = townsendCriterion.getFArrayBox();
4565
4566 auto regularKernel = [&](const IntVect& iv) -> void {
4567 if (validCells(iv, 0) && ebisbox.isRegular(iv)) {
4568 if (inceptionIntegralReg(iv, 0) >= m_inceptionK || townsendCriterionReg(iv, 0) >= 1.0) {
4569 Vcr += vol;
4570 }
4571 }
4572 };
4573
4574 auto irregularKernel = [&](const VolIndex& vof) -> void {
4575 if (validCells(vof.gridIndex())) {
4576 if (inceptionIntegral(vof, 0) >= m_inceptionK || townsendCriterion(vof, 0) >= 1.0) {
4577 Vcr += ebisbox.volFrac(vof) * vol;
4578 }
4579 }
4580 };
4581
4582 // Kernel regions.
4583 const Box cellBox = dbl[din];
4584 VoFIterator& vofit = (*m_amr->getVofIterator(m_realm, m_phase)[lvl])[din];
4585
4588 }
4589 }
4590
4591 return ParallelOps::sum(Vcr);
4592}
4593
4594template <typename P, typename F, typename C>
4595void
4597{
4598 CH_TIME("DischargeInceptionStepper::computeCriticalAreaStationary");
4599 if (m_verbosity > 5) {
4600 pout() << "DischargeInceptionStepper::computeCriticalAreaStationary" << endl;
4601 }
4602
4603 CH_assert(m_inceptionIntegralPlus.size() == m_inceptionIntegralMinu.size());
4604 CH_assert(m_inceptionIntegralPlus.size() == m_KPlusValues.size());
4605 CH_assert(m_inceptionIntegralMinu.size() == m_KMinuValues.size());
4606
4607 m_criticalAreaPlus.resize(0);
4608 m_criticalAreaMinu.resize(0);
4609
4610 const int numVoltages = m_inceptionIntegralPlus.size();
4611
4612 // Solve critical area of K values for each voltage
4613 for (size_t i = 0; i < numVoltages; i++) {
4614 Real criticalAreaPlus = 0.0;
4615 Real criticalAreaMinu = 0.0;
4616
4617 for (int lvl = 0; lvl <= m_amr->getFinestLevel(); ++lvl) {
4618 const DisjointBoxLayout& dbl = m_amr->getGrids(m_realm)[lvl];
4619 const DataIterator& dit = dbl.dataIterator();
4620 const EBISLayout& ebisl = m_amr->getEBISLayout(m_realm, m_phase)[lvl];
4621
4622 const LevelData<BaseFab<bool>>& validCellsLD = *m_amr->getValidCells(m_realm)[lvl];
4623
4624 const Real dx = m_amr->getDx()[lvl];
4625
4626 const int nbox = dit.size();
4627
4628#pragma omp parallel for schedule(runtime) reduction(+ : criticalAreaPlus, criticalAreaMinu)
4629 for (int mybox = 0; mybox < nbox; mybox++) {
4630 const DataIndex& din = dit[mybox];
4631
4632 const EBISBox& ebisbox = ebisl[din];
4634
4635 const EBCellFAB& inceptionIntegralPlus = (*(m_inceptionIntegralPlus[i])[lvl])[din];
4636 const EBCellFAB& inceptionIntegralMinu = (*(m_inceptionIntegralMinu[i])[lvl])[din];
4637
4638 const EBCellFAB& townsendCriterionPlus = (*(m_townsendCriterionPlus[i])[lvl])[din];
4639 const EBCellFAB& townsendCriterionMinu = (*(m_townsendCriterionMinu[i])[lvl])[din];
4640
4641 auto irregularKernel = [&](const VolIndex& vof) -> void {
4642 if (validCells(vof.gridIndex())) {
4643 const Real boundaryArea = ebisbox.bndryArea(vof);
4644
4645 if (inceptionIntegralPlus(vof, 0) >= m_inceptionK || townsendCriterionPlus(vof, 0) >= 1.0) {
4646 criticalAreaPlus += boundaryArea * std::pow(dx, SpaceDim - 1);
4647 }
4648 if (inceptionIntegralMinu(vof, 0) >= m_inceptionK || townsendCriterionMinu(vof, 0) >= 1.0) {
4649 criticalAreaMinu += boundaryArea * std::pow(dx, SpaceDim - 1);
4650 }
4651 }
4652 };
4653
4654 // Kernel regions.
4655 VoFIterator& vofit = (*m_amr->getVofIterator(m_realm, m_phase)[lvl])[din];
4656
4658 }
4659 }
4660
4661 m_criticalAreaPlus.push_back(ParallelOps::sum(criticalAreaPlus));
4662 m_criticalAreaMinu.push_back(ParallelOps::sum(criticalAreaMinu));
4663 }
4664}
4665
4666template <typename P, typename F, typename C>
4667Real
4669{
4670 CH_TIME("DischargeInceptionStepper::computeCriticalAreaTransient");
4671 if (m_verbosity > 5) {
4672 pout() << "DischargeInceptionStepper::computeCriticalAreaTransient" << endl;
4673 }
4674
4675 Real Acr = 0.0;
4676
4677 for (int lvl = 0; lvl <= m_amr->getFinestLevel(); ++lvl) {
4678 const DisjointBoxLayout& dbl = m_amr->getGrids(m_realm)[lvl];
4679 const DataIterator& dit = dbl.dataIterator();
4680 const EBISLayout& ebisl = m_amr->getEBISLayout(m_realm, m_phase)[lvl];
4681
4682 const LevelData<BaseFab<bool>>& validCellsLD = *m_amr->getValidCells(m_realm)[lvl];
4683
4684 const Real dx = m_amr->getDx()[lvl];
4685 const Real area = std::pow(dx, SpaceDim - 1);
4686
4687 const int nbox = dit.size();
4688
4689#pragma omp parallel for schedule(runtime) reduction(+ : Acr)
4690 for (int mybox = 0; mybox < nbox; mybox++) {
4691 const DataIndex& din = dit[mybox];
4692
4693 const EBISBox& ebisbox = ebisl[din];
4695
4696 const EBCellFAB& inceptionIntegral = (*m_inceptionIntegral[lvl])[din];
4697 const EBCellFAB& townsendCriterion = (*m_townsendCriterion[lvl])[din];
4698
4699 auto irregularKernel = [&](const VolIndex& vof) -> void {
4700 if (validCells(vof.gridIndex())) {
4701 if (inceptionIntegral(vof, 0) >= m_inceptionK || townsendCriterion(vof, 0) >= 1.0) {
4702 Acr += ebisbox.bndryArea(vof) * area;
4703 }
4704 }
4705 };
4706
4707 // Kernel regions.
4708 VoFIterator& vofit = (*m_amr->getVofIterator(m_realm, m_phase)[lvl])[din];
4709
4711 }
4712 }
4713
4714 return ParallelOps::sum(Acr);
4715}
4716
4717template <typename P, typename F, typename C>
4718void
4720{
4721 CH_TIME("DischargeInceptionStepper::computeIonizationVolumeStationary");
4722 if (m_verbosity > 5) {
4723 pout() << "DischargeInceptionStepper::computeIonizationVolumeStationary" << endl;
4724 }
4725
4726 CH_assert(m_inceptionIntegralPlus.size() == m_inceptionIntegralMinu.size());
4727 CH_assert(m_inceptionIntegralPlus.size() == m_KPlusValues.size());
4728 CH_assert(m_inceptionIntegralMinu.size() == m_KMinuValues.size());
4729
4730 m_ionizationVolume.resize(0);
4731
4732 const int numVoltages = m_inceptionIntegralPlus.size();
4733
4734 // Storage something that holds the electric filed
4737
4738 // Solve ionization volume for each voltage
4739 for (size_t i = 0; i < m_voltageSweeps.size(); i++) {
4740 const Real voltage = m_voltageSweeps[i];
4741
4742 Real ionizationVolume = 0.0;
4743
4744 this->superposition(scratch, voltage);
4745
4746 for (int lvl = 0; lvl <= m_amr->getFinestLevel(); ++lvl) {
4747 const DisjointBoxLayout& dbl = m_amr->getGrids(m_realm)[lvl];
4748 const DataIterator& dit = dbl.dataIterator();
4749 const EBISLayout& ebisl = m_amr->getEBISLayout(m_realm, m_phase)[lvl];
4750
4751 const LevelData<BaseFab<bool>>& validCellsLD = *m_amr->getValidCells(m_realm)[lvl];
4752
4753 const Real dx = m_amr->getDx()[lvl];
4754 const RealVect probLo = m_amr->getProbLo();
4755
4756 const int nbox = dit.size();
4757
4758#pragma omp parallel for schedule(runtime) reduction(+ : ionizationVolume)
4759 for (int mybox = 0; mybox < nbox; mybox++) {
4760 const DataIndex& din = dit[mybox];
4761
4762 const EBISBox& ebisbox = ebisl[din];
4764
4765 const EBCellFAB& electricField = (*scratch[lvl])[din];
4766 const FArrayBox& electricFieldReg = electricField.getFArrayBox();
4767
4768 auto regularKernel = [&](const IntVect& iv) -> void {
4769 if (validCells(iv, 0) && ebisbox.isRegular(iv)) {
4770 const RealVect x = probLo + dx * (0.5 * RealVect::Unit + RealVect(iv));
4771 const RealVect EE = RealVect(
4773 const Real E = EE.vectorLength();
4774
4775 const Real alpha = m_alpha(E, x);
4776 const Real eta = m_eta(E, x);
4777
4778 if (alpha >= eta) {
4779 ionizationVolume += std::pow(dx, SpaceDim);
4780 }
4781 }
4782 };
4783
4784 auto irregularKernel = [&](const VolIndex& vof) -> void {
4785 if (validCells(vof.gridIndex())) {
4786
4787 const RealVect x = probLo + Location::position(Location::Cell::Center, vof, ebisbox, dx);
4789 const Real E = EE.vectorLength();
4790
4791 const Real alpha = m_alpha(E, x);
4792 const Real eta = m_eta(E, x);
4793
4794 const Real kappa = ebisbox.volFrac(vof);
4795
4796 if (alpha >= eta) {
4797 ionizationVolume += kappa * std::pow(dx, SpaceDim);
4798 }
4799 }
4800 };
4801
4802 // Kernel regions.
4803 const Box cellBox = dbl[din];
4804 VoFIterator& vofit = (*m_amr->getVofIterator(m_realm, m_phase)[lvl])[din];
4805
4808 }
4809 }
4810
4811 m_ionizationVolume.push_back(ParallelOps::sum(ionizationVolume));
4812 }
4813}
4814
4815template <typename P, typename F, typename C>
4816Real
4818{
4819 CH_TIME("DischargeInceptionStepper::computeIonizationVolumeTransient");
4820 if (m_verbosity > 5) {
4821 pout() << "DischargeInceptionStepper::computeIonizationVolumeTransient" << endl;
4822 }
4823
4824 Real Vion = 0.0;
4825
4826 // Calculate electric field
4828 m_amr->allocate(scratch, m_realm, m_phase, SpaceDim);
4829 this->superposition(scratch, a_voltage);
4830
4831 for (int lvl = 0; lvl <= m_amr->getFinestLevel(); ++lvl) {
4832 const DisjointBoxLayout& dbl = m_amr->getGrids(m_realm)[lvl];
4833 const DataIterator& dit = dbl.dataIterator();
4834 const EBISLayout& ebisl = m_amr->getEBISLayout(m_realm, m_phase)[lvl];
4835
4836 const LevelData<BaseFab<bool>>& validCellsLD = *m_amr->getValidCells(m_realm)[lvl];
4837
4838 const Real dx = m_amr->getDx()[lvl];
4839 const Real vol = std::pow(dx, SpaceDim);
4840 const RealVect probLo = m_amr->getProbLo();
4841
4842 const int nbox = dit.size();
4843
4844#pragma omp parallel for schedule(runtime) reduction(+ : Vion)
4845 for (int mybox = 0; mybox < nbox; mybox++) {
4846 const DataIndex& din = dit[mybox];
4847
4848 const EBISBox& ebisbox = ebisl[din];
4850
4851 const EBCellFAB& electricField = (*scratch[lvl])[din];
4852 const FArrayBox& electricFieldReg = electricField.getFArrayBox();
4853
4854 auto regularKernel = [&](const IntVect& iv) -> void {
4855 if (validCells(iv, 0) && ebisbox.isRegular(iv)) {
4856
4857 const RealVect x = probLo + dx * (0.5 * RealVect::Unit + RealVect(iv));
4858 const RealVect EE = RealVect(
4860 const Real E = EE.vectorLength();
4861
4862 const Real alpha = m_alpha(E, x);
4863 const Real eta = m_eta(E, x);
4864 if (alpha >= eta) {
4865 Vion += vol;
4866 }
4867 }
4868 };
4869
4870 auto irregularKernel = [&](const VolIndex& vof) -> void {
4871 if (validCells(vof.gridIndex())) {
4872
4873 const RealVect x = probLo + Location::position(Location::Cell::Centroid, vof, ebisbox, dx);
4875 const Real E = EE.vectorLength();
4876
4877 const Real alpha = m_alpha(E, x);
4878 const Real eta = m_eta(E, x);
4879 if (alpha >= eta) {
4880 Vion += ebisbox.volFrac(vof) * vol;
4881 }
4882 }
4883 };
4884
4885 // Kernel regions.
4886 const Box cellBox = dbl[din];
4887 VoFIterator& vofit = (*m_amr->getVofIterator(m_realm, m_phase)[lvl])[din];
4888
4891 }
4892 }
4893
4894 return ParallelOps::sum(Vion);
4895}
4896
4897template <typename P, typename F, typename C>
4898void
4900{
4901 CH_TIME("DischargeInceptionStepper::writeReportStationary");
4902 if (m_verbosity > 5) {
4903 pout() << "DischargeInceptionStepper::writeReportStationary" << endl;
4904 }
4905
4906 const auto UIncPlus = this->computeMinimumInceptionVoltage(m_inceptionVoltagePlus);
4907 const auto UIncMinu = this->computeMinimumInceptionVoltage(m_inceptionVoltageMinu);
4908
4909 const auto streamerUIncPlus = this->computeMinimumInceptionVoltage(m_streamerInceptionVoltagePlus);
4910 const auto streamerUIncMinu = this->computeMinimumInceptionVoltage(m_streamerInceptionVoltageMinu);
4911
4912 const auto townsendUIncPlus = this->computeMinimumInceptionVoltage(m_townsendInceptionVoltagePlus);
4913 const auto townsendUIncMinu = this->computeMinimumInceptionVoltage(m_townsendInceptionVoltageMinu);
4914
4915#ifdef CH_MPI
4916 if (procID() == 0) {
4917#endif
4918
4919 std::ofstream output(m_outputFile, std::ofstream::out);
4920
4921 const int ww = (SpaceDim == 2) ? 24 : 36;
4922
4923 const std::string lineBreak = "# " + std::string(178 + 4 * ww, '=');
4924
4925 // clang-format off
4926 output << lineBreak << "\n";
4927 if (std::isnan(UIncPlus.first) || std::isnan(UIncMinu.first)) {
4928 output << "# Could not compute inception voltage\n";
4929 }
4930 else {
4931 if(m_fullIntegration){
4932 output << "# Minimum inception voltage(+) = " << UIncPlus.first << ",\t x = " << UIncPlus.second << "\n";
4933 output << "# Minimum inception voltage(-) = " << UIncMinu.first << ",\t x = " << UIncMinu.second << "\n";
4934 output << "# \n";
4935 output << "# Streamer inception voltage(+) = " << streamerUIncPlus.first << ",\t x = " << streamerUIncPlus.second << "\n";
4936 output << "# Streamer inception voltage(-) = " << streamerUIncMinu.first << ",\t x = " << streamerUIncMinu.second << "\n";
4937 output << "# \n";
4938 output << "# Townsend inception voltage(+) = " << townsendUIncPlus.first << ",\t x = " << townsendUIncPlus.second << "\n";
4939 output << "# Townsend inception voltage(-) = " << townsendUIncMinu.first << ",\t x = " << townsendUIncMinu.second << "\n";
4940 }
4941 else{
4942 output << "# Minimum inception voltage(+) >= " << UIncPlus.first << ",\t x = " << UIncPlus.second << "\n";
4943 output << "# Minimum inception voltage(-) >= " << UIncMinu.first << ",\t x = " << UIncMinu.second << "\n";
4944 output << "# \n";
4945 output << "# Streamer inception voltage(+) >= " << streamerUIncPlus.first << ",\t x = " << streamerUIncPlus.second << "\n";
4946 output << "# Streamer inception voltage(-) >= " << streamerUIncMinu.first << ",\t x = " << streamerUIncMinu.second << "\n";
4947 output << "# \n";
4948 output << "# Townsend inception voltage(+) >= " << townsendUIncPlus.first << ",\t x = " << townsendUIncPlus.second << "\n";
4949 output << "# Townsend inception voltage(-) >= " << townsendUIncMinu.first << ",\t x = " << townsendUIncMinu.second << "\n";
4950 }
4951 }
4952
4953 output << lineBreak << "\n";
4954 output << left << setw(15) << setfill(' ') << "# +/- Voltage";
4955 output << left << setw(15) << setfill(' ') << "Max K(+)";
4956 output << left << setw(15) << setfill(' ') << "Max K(-)";
4957 output << left << setw(ww) << setfill(' ') << "Pos. max K(+)";
4958 output << left << setw(ww) << setfill(' ') << "Pos. max K(-)";
4959 output << left << setw(20) << setfill(' ') << "Max T(+)";
4960 output << left << setw(20) << setfill(' ') << "Max T(-)";
4961 output << left << setw(ww) << setfill(' ') << "Pos. max T(+)";
4962 output << left << setw(ww) << setfill(' ') << "Pos. max T(-)";
4963 output << left << setw(20) << setfill(' ') << "Crit. vol(+)";
4964 output << left << setw(20) << setfill(' ') << "Crit. vol(-)";
4965 output << left << setw(20) << setfill(' ') << "Crit. area(+)";
4966 output << left << setw(20) << setfill(' ') << "Crit. area(-)";
4967 output << left << setw(20) << setfill(' ') << "Ionization vol." << "\n";
4968 output << lineBreak << "\n";
4969
4970 auto RealVectToString = [=](const RealVect x) -> std::string {
4971 std::string ret = "(";
4972 for (int dir = 0; dir < SpaceDim; dir++) {
4973 ret += std::to_string(x[dir]);
4974 if(dir < SpaceDim -1) {
4975 ret += ", ";
4976 }
4977 }
4978
4979 ret += ")";
4980
4981 return ret;
4982 };
4983
4984 for (int i = 0; i < m_voltageSweeps.size(); i++) {
4985 output << left << setw(15) << setfill(' ') << m_voltageSweeps[i];
4994 output << left << setw(20) << setfill(' ') << m_criticalVolumePlus[i];
4995 output << left << setw(20) << setfill(' ') << m_criticalVolumeMinu[i];
4996 output << left << setw(20) << setfill(' ') << m_criticalAreaPlus[i];
4997 output << left << setw(20) << setfill(' ') << m_criticalAreaMinu[i];
4998 output << left << setw(20) << setfill(' ') << m_ionizationVolume[i];
4999 output << endl;
5000 }
5001 output << lineBreak << "\n";
5002 // clang-format on
5003#ifdef CH_MPI
5004 }
5005#endif
5006}
5007
5008template <typename P, typename F, typename C>
5009void
5011{
5012 CH_TIME("DischargeInceptionStepper::writeReportTransient");
5013 if (m_verbosity > 5) {
5014 pout() << "DischargeInceptionStepper::writeReportTransient" << endl;
5015 }
5016
5017#ifdef CH_MPI
5018 if (procID() == 0) {
5019#endif
5020 std::ofstream output(m_outputFile, std::ofstream::out);
5021
5022 output << std::left << std::setw(15) << setfill(' ') << "# Time t";
5023 output << std::left << std::setw(15) << setfill(' ') << "V(t)";
5024 output << std::left << std::setw(15) << setfill(' ') << "max K(t)";
5025 output << std::left << std::setw(15) << setfill(' ') << "max T(t)";
5026 output << std::left << std::setw(15) << setfill(' ') << "Vcr(t)";
5027 output << std::left << std::setw(15) << setfill(' ') << "Acr(t)";
5028 output << std::left << std::setw(15) << setfill(' ') << "Vion(t)";
5029 output << std::left << std::setw(15) << setfill(' ') << "lambda(t)";
5030 output << std::left << std::setw(15) << setfill(' ') << "P(t)";
5031 output << std::left << std::setw(15) << setfill(' ') << "dP(t, t+dt)";
5032 output << std::left << std::setw(15) << setfill(' ') << "Time lag";
5033 output << "\n";
5034
5035 // Compute dP(t, t+dt)
5037 for (size_t i = 0; i < m_Rdot.size() - 1; i++) {
5038 const Real t = m_Rdot[i].first;
5039 const Real dt = m_Rdot[i + 1].first - m_Rdot[i].first;
5040 const Real prob = m_inceptionProbability[i].second;
5041 const Real Rdot = m_Rdot[i].second;
5042
5043 dProb.emplace_back((1.0 - prob) * Rdot * dt);
5044 }
5045 dProb.emplace_back(0.0);
5046
5047 // Compute the average waiting time.
5048 std::vector<Real> tau(m_Rdot.size(), 0.0);
5049 for (size_t i = 0; i < m_Rdot.size() - 1; i++) {
5050 const Real t1 = m_Rdot[i].first;
5051 const Real t2 = m_Rdot[i + 1].first;
5052 const Real dt = t2 - t1;
5053 const Real prob = m_inceptionProbability[i].second;
5054 const Real lambda = m_Rdot[i].second;
5055
5056 tau[i + 1] = tau[i] + t1 * (1 - prob) * lambda * dt;
5057 }
5058 for (int i = 0; i < tau.size(); i++) {
5059 const Real prob = m_inceptionProbability[i].second;
5060
5061 tau[i] = prob > 0.0 ? tau[i] / prob : std::numeric_limits<Real>::infinity();
5062 }
5063
5064 for (size_t i = 0; i < m_Rdot.size(); i++) {
5065 const Real time = m_Rdot[i].first;
5066
5067 output << std::left << std::setw(15) << time;
5068 output << std::left << std::setw(15) << m_voltageCurve(time);
5069 output << std::left << std::setw(15) << m_maxK[i].second;
5070 output << std::left << std::setw(15) << m_maxT[i].second;
5071 output << std::left << std::setw(15) << m_criticalVolume[i].second;
5072 output << std::left << std::setw(15) << m_criticalArea[i].second;
5073 output << std::left << std::setw(15) << m_ionizationVolumeTransient[i].second;
5074 output << std::left << std::setw(15) << m_Rdot[i].second;
5075 output << std::left << std::setw(15) << m_inceptionProbability[i].second;
5076 output << std::left << std::setw(15) << dProb[i];
5077 output << std::left << std::setw(15) << tau[i];
5078 output << "\n";
5079 }
5080
5081 output.close();
5082
5083#ifdef CH_MPI
5084 }
5085#endif
5086}
5087
5088template <typename P, typename F, typename C>
5089bool
5091 const RealVect& a_probLo,
5092 const RealVect& a_probHi) const noexcept
5093{
5094#ifndef NDEBUG
5095 CH_TIME("DischargeInceptionStepper::particleOutsideGrid");
5096 if (m_verbosity > 5) {
5097 pout() << "DischargeInceptionStepper::particleOutsideGrid" << endl;
5098 }
5099#endif
5100
5101 bool isOutside = false;
5102
5103 for (int dir = 0; dir < SpaceDim; dir++) {
5104 if (a_pos[dir] <= a_probLo[dir] || a_pos[dir] >= a_probHi[dir]) {
5105 isOutside = true;
5106 }
5107 }
5108
5109 return isOutside;
5110}
5111
5112template <typename P, typename F, typename C>
5113bool
5115{
5116#ifndef NDEBUG
5117 CH_TIME("DischargeInceptionStepper::particleInsideEB");
5118 if (m_verbosity > 5) {
5119 pout() << "DischargeInceptionStepper::particleInsideEB" << endl;
5120 }
5121#endif
5122
5123 const RefCountedPtr<BaseIF>& implicitFunction = m_amr->getBaseImplicitFunction(m_phase);
5124
5125 return (implicitFunction->value(a_pos) >= 0.0) ? true : false;
5126}
5127
5128template <typename P, typename F, typename C>
5129void
5131{
5132 CH_TIME("DischargeInceptionStepper::computeIonVelocity");
5133 if (m_verbosity > 5) {
5134 pout() << "DischargeInceptionStepper::computeIonVelocity" << endl;
5135 }
5136
5137 CH_assert(!(m_ionSolver.isNull()));
5138 CH_assert(m_ionSolver->isMobile());
5139
5140 EBAMRCellData& vel = m_ionSolver->getCellCenteredVelocity();
5141
5142 // Compute electric field at the input voltage and set v = -E
5143 this->superposition(vel, a_voltage);
5144 DataOps::scale(vel, -1.0);
5145
5146 // Allocate mesh data that holds mu and compute it on the mesh.
5148 m_amr->allocate(mu, m_realm, m_phase, 1);
5149
5150 for (int lvl = 0; lvl <= m_amr->getFinestLevel(); lvl++) {
5151 const DisjointBoxLayout& dbl = m_amr->getGrids(m_realm)[lvl];
5152 const DataIterator& dit = dbl.dataIterator();
5153
5154 const int nbox = dit.size();
5155
5156#pragma omp parallel for schedule(runtime)
5157 for (int mybox = 0; mybox < nbox; mybox++) {
5158 const DataIndex& din = dit[mybox];
5159
5160 const EBCellFAB& v = (*vel[lvl])[din];
5161 const FArrayBox& vReg = v.getFArrayBox();
5162
5163 EBCellFAB& MU = (*mu[lvl])[din];
5164 FArrayBox& MUREG = MU.getFArrayBox();
5165
5166 auto regularKernel = [&](const IntVect& iv) -> void {
5167 const RealVect EE = RealVect(D_DECL(vReg(iv, 0), vReg(iv, 1), vReg(iv, 2)));
5168 const Real E = EE.vectorLength();
5169
5170 MUREG(iv, 0) = m_ionMobility(E);
5171 };
5172
5173 auto irregularKernel = [&](const VolIndex& vof) -> void {
5174 const RealVect EE = RealVect(D_DECL(v(vof, 0), v(vof, 1), v(vof, 2)));
5175 const Real E = EE.vectorLength();
5176
5177 MU(vof, 0) = m_ionMobility(E);
5178 };
5179
5180 VoFIterator& vofit = (*m_amr->getVofIterator(m_realm, m_phase)[lvl])[din];
5181 Box cellBox = dbl[din];
5182
5185 }
5186 }
5187
5189
5190 m_amr->arithmeticAverage(vel, m_realm, m_phase);
5191 m_amr->interpGhostPwl(vel, m_realm, m_phase);
5192}
5193
5194template <typename P, typename F, typename C>
5195void
5197{
5198 CH_TIME("DischargeInceptionStepper::computeIonDiffusion");
5199 if (m_verbosity > 5) {
5200 pout() << "DischargeInceptionStepper::computeIonDiffusion" << endl;
5201 }
5202
5203 CH_assert(!(m_ionSolver.isNull()));
5204 CH_assert(m_ionSolver->isMobile());
5205
5206 // Compute the electric field at the input voltage.
5208 m_amr->allocate(scratch, m_realm, m_phase, SpaceDim);
5209 this->superposition(scratch, a_voltage);
5210
5211 // Compute the diffusion coefficient on cell centers.
5213 m_amr->allocate(diffCoCell, m_realm, m_phase, 1);
5214
5215 for (int lvl = 0; lvl <= m_amr->getFinestLevel(); lvl++) {
5216 const DisjointBoxLayout& dbl = m_amr->getGrids(m_realm)[lvl];
5217 const DataIterator& dit = dbl.dataIterator();
5218
5219 const int nbox = dit.size();
5220
5221#pragma omp parallel for schedule(runtime)
5222 for (int mybox = 0; mybox < nbox; mybox++) {
5223 const DataIndex& din = dit[mybox];
5224
5225 const EBCellFAB& electricField = (*scratch[lvl])[din];
5226 const FArrayBox& electricFieldReg = electricField.getFArrayBox();
5227
5228 EBCellFAB& dCo = (*diffCoCell[lvl])[din];
5229 FArrayBox& dCoReg = dCo.getFArrayBox();
5230
5231 auto regularKernel = [&](const IntVect& iv) -> void {
5233 const Real E = EE.vectorLength();
5234
5235 dCoReg(iv, 0) = m_ionDiffusion(E);
5236 };
5237
5238 auto irregularKernel = [&](const VolIndex& vof) -> void {
5240 const Real E = EE.vectorLength();
5241
5242 dCo(vof, 0) = m_ionDiffusion(E);
5243 };
5244
5245 VoFIterator& vofit = (*m_amr->getVofIterator(m_realm, m_phase)[lvl])[din];
5246 Box cellBox = dbl[din];
5247
5250 }
5251 }
5252
5253 // Coarsen and update ghost cells
5254 m_amr->arithmeticAverage(diffCoCell, m_realm, m_phase);
5255 m_amr->interpGhostPwl(diffCoCell, m_realm, m_phase);
5256
5257 // Fetch from solver and average to grid faces.
5258 EBAMRFluxData& diffCoFace = m_ionSolver->getFaceCenteredDiffusionCoefficient();
5259 EBAMRIVData& diffCoEB = m_ionSolver->getEbCenteredDiffusionCoefficient();
5260
5262 DataOps::setValue(diffCoEB, 0.0); // Neumann BC.
5263
5265 diffCoCell,
5266 m_amr->getDomains(),
5267 1,
5268 Interval(0, 0),
5269 Interval(0, 0),
5270 Average::Arithmetic);
5271}
5272
5273template <typename P, typename F, typename C>
5274void
5278 const Real a_voltage) const noexcept
5279{
5280 CH_TIME("DischargeInceptionStepper::superposition(full)");
5281
5282 const EBAMRCellData homogeneousField = m_amr->alias(phase::gas, a_homogeneousField);
5283 const EBAMRCellData inhomogeneousField = m_amr->alias(phase::gas, a_inhomogeneousField);
5284
5288
5289 m_amr->arithmeticAverage(a_sumField, m_realm, m_phase);
5290 m_amr->interpGhostPwl(a_sumField, m_realm, m_phase);
5291 // m_amr->interpToCentroids(a_sumField, m_realm, m_phase);
5292}
5293
5294template <typename P, typename F, typename C>
5295void
5297{
5298 CH_TIME("DischargeInceptionStepper::superposition(short)");
5299
5300 this->superposition(a_sumField, m_electricFieldInho, m_electricFieldHomo, a_voltage);
5301}
5302
5303template <typename P, typename F, typename C>
5304void
5307 const EBAMRCellData& a_data) const noexcept
5308{
5309 CH_TIME("DischargeInceptionStepper::getMaxValueAndLocation");
5310 if (m_verbosity > 5) {
5311 pout() << "DischargeInceptionStepper::getMaxValueAndLocation" << endl;
5312 }
5313
5314 a_maxVal = -std::numeric_limits<Real>::max();
5316
5317 for (int lvl = 0; lvl <= m_amr->getFinestLevel(); lvl++) {
5318 const DisjointBoxLayout& dbl = m_amr->getGrids(m_realm)[lvl];
5319 const DataIterator& dit = dbl.dataIterator();
5320 const EBISLayout& ebisl = m_amr->getEBISLayout(m_realm, m_phase)[lvl];
5321 const Real dx = m_amr->getDx()[lvl];
5322 const RealVect probLo = m_amr->getProbLo();
5323
5324 const LevelData<BaseFab<bool>>& validCellsLD = (*m_amr->getValidCells(m_realm)[lvl]);
5325
5326 const int nbox = dit.size();
5327
5328#pragma omp parallel for schedule(runtime)
5329 for (int mybox = 0; mybox < nbox; mybox++) {
5330 const DataIndex& din = dit[mybox];
5331 const Box& cellbox = dbl[din];
5332 const EBISBox& ebisbox = ebisl[din];
5333
5334 const EBCellFAB& data = (*a_data[lvl])[din];
5335 const FArrayBox& dataReg = data.getFArrayBox();
5337
5338 CH_assert(data.nComp() == 1);
5339
5340 auto regularKernel = [&](const IntVect& iv) -> void {
5341 if (validCells(iv, 0) && ebisbox.isRegular(iv)) {
5342 if (dataReg(iv, 0) > a_maxVal) {
5343 a_maxVal = dataReg(iv, 0);
5344 a_maxPos = probLo + (RealVect(iv) + 0.5 * RealVect::Unit) * dx;
5345 }
5346 }
5347 };
5348
5349 auto irregularKernel = [&](const VolIndex& vof) -> void {
5350 if (validCells(vof.gridIndex(), 0) && ebisbox.isIrregular(vof.gridIndex())) {
5351 if (data(vof, 0) > a_maxVal) {
5352 a_maxVal = data(vof, 0);
5353 a_maxPos = probLo + Location::position(Location::Cell::Centroid, vof, ebisbox, dx);
5354 }
5355 }
5356 };
5357
5358 VoFIterator& vofit = (*m_amr->getVofIterator(m_realm, m_phase)[lvl])[din];
5359
5362 }
5363 }
5364
5366
5369}
5370
5371template <typename P, typename F, typename C>
5372void
5374 int& a_comp,
5375 const EBAMRCellData& a_data,
5377 const int a_level,
5378 const bool a_interpToCentroids,
5379 const bool a_interpGhost) const noexcept
5380{
5381 CH_TIMERS("DischargeInceptionStepper::writeData");
5382 CH_TIMER("DischargeInceptionStepper::writeData::allocate", t1);
5383 CH_TIMER("DischargeInceptionStepper::writeData::local_copy", t2);
5384 CH_TIMER("DischargeInceptionStepper::writeData::interp_ghost", t3);
5385 CH_TIMER("DischargeInceptionStepper::writeData::interp_centroid", t4);
5386 CH_TIMER("DischargeInceptionStepper::writeData::final_copy", t5);
5387 if (m_verbosity > 5) {
5388 pout() << "DischargeInceptionStepper::writeData" << endl;
5389 }
5390
5391 // Number of components we are working with.
5392 const int numComp = a_data[a_level]->nComp();
5393
5394 // Component ranges that we copy to/from.
5395 const Interval srcInterv(0, numComp - 1);
5396 const Interval dstInterv(a_comp, a_comp + numComp - 1);
5397
5398 CH_START(t1);
5400 m_amr->allocate(scratch, m_realm, m_phase, a_level, numComp);
5401 CH_STOP(t1);
5402
5403 CH_START(t2);
5404 m_amr->copyData(scratch, *a_data[a_level], a_level, m_realm, m_realm);
5405 CH_START(t2);
5406
5407 // Interpolate ghost cells
5408 CH_START(t3);
5409 if (a_level > 0 && a_interpGhost) {
5410 m_amr->interpGhost(scratch, *a_data[a_level - 1], a_level, m_realm, m_phase);
5411 }
5412 CH_STOP(t3);
5413
5414 CH_START(t4);
5415 if (a_interpToCentroids) {
5416 m_amr->interpToCentroids(scratch, m_realm, m_phase, a_level);
5417 }
5418 CH_STOP(t4);
5419
5421
5422 CH_START(t5);
5423 m_amr->copyData(a_output, scratch, a_level, a_outputRealm, m_realm, dstInterv, srcInterv);
5424 CH_STOP(t5);
5425
5426 a_comp += numComp;
5427}
5428
5429template <typename P, typename F, typename C>
5430const EBAMRCellData*
5432{
5433 CH_TIMERS("DischargeInceptionStepper::getElectricField");
5434
5435 return &m_homogeneousFieldGas;
5436}
5437
5438template <typename P, typename F, typename C>
5439Real
5441{
5442 CH_TIME("DischargeInceptionStepper::getCriticalField");
5443 if (m_verbosity > 5) {
5444 pout() << "DischargeInceptionStepper::getCriticalField" << endl;
5445 }
5446
5447 Real Emin = -10.0;
5448 Real Emax = +10.0;
5449
5450 // Quick lambda for the effective Townsend ionization coefficient. Note that we solve for the exponent.
5451 auto alpha = [this](const Real E) -> Real {
5452 return m_alpha(std::pow(10.0, E), RealVect::Zero) - m_eta(std::pow(10.0, E), RealVect::Zero);
5453 };
5454
5456
5457 return std::pow(10.0, p);
5458}
5459
5460#include <CD_NamespaceFooter.H>
5461
5462#endif
Simple species for DischargeInception test problem.
TimeStepper class for evaluating the streamer inception criterion.
Mode
For specifying whether the module is run in stationary or transient mode.
Definition CD_DischargeInceptionStepper.H:55
Declaration of various useful OpenMP-related utilities.
Agglomeration of some useful algebraic/polynomial routines.
Declaration of various useful units.
static void scale(MFAMRCellData &a_lhs, const Real &a_scale) noexcept
Scale data by factor.
Definition CD_DataOps.cpp:2398
static void getMaxMinNorm(Real &a_max, Real &a_min, EBAMRCellData &data)
Get maximum and minimum value of normed data.
Definition CD_DataOps.cpp:1797
static void compute(EBAMRCellData &a_data, const std::function< Real(const Real a_cellValue)> &a_func) noexcept
Compute a new value given the old cell value.
Definition CD_DataOps.cpp:480
static void floor(EBAMRCellData &a_lhs, const Real a_value)
Floor values in data holder. This sets all values below a_value to a_value.
Definition CD_DataOps.cpp:1394
static void squareRoot(EBAMRFluxData &a_lhs)
Compute the square root of the input data.
Definition CD_DataOps.cpp:3289
static void getMaxMin(Real &max, Real &min, EBAMRCellData &a_data, const int a_comp)
Get maximum and minimum value of specified component.
Definition CD_DataOps.cpp:1651
static void multiply(EBAMRCellData &a_lhs, const EBAMRCellData &a_rhs)
Multiply data holder by another data holder.
Definition CD_DataOps.cpp:2156
static void kappaScale(EBAMRCellData &a_data) noexcept
Scale data by volume fraction.
Definition CD_DataOps.cpp:2058
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:801
static void setCoveredValue(EBAMRCellData &a_lhs, const int a_comp, const Real a_value)
Set value in covered cells. Does specified component.
Definition CD_DataOps.cpp:2552
static void setValue(LevelData< MFInterfaceFAB< T > > &a_lhs, const T &a_value)
Set value in an MFInterfaceFAB data holder.
Definition CD_DataOpsImplem.H:23
static void dotProduct(MFAMRCellData &a_result, const MFAMRCellData &a_data1, const MFAMRCellData &a_data2)
Compote the cell-wise dot product between two data holders.
Definition CD_DataOps.cpp:533
static void averageCellToFace(EBAMRFluxData &a_faceData, const EBAMRCellData &a_cellData, const Vector< ProblemDomain > &a_domains)
Average all components of the cell-centered data to faces.
Definition CD_DataOps.cpp:153
static void copy(MFAMRCellData &a_dst, const MFAMRCellData &a_src)
Copy data from one data holder to another.
Definition CD_DataOps.cpp:1132
static void multiplyScalar(EBAMRCellData &a_lhs, const EBAMRCellData &a_rhs)
Multiply data holder by another data holder.
Definition CD_DataOps.cpp:2253
Type
Type of interpolation methods supported. PWC = Piecewise constant, ignoring the embedded boundary....
Definition CD_EBCoarseToFineInterp.H:42
static bool ebIntersectionBisect(const RefCountedPtr< BaseIF > &a_impFunc, const RealVect &a_oldPos, const RealVect &a_newPos, const Real &a_bisectStep, Real &a_s)
Compute the intersection point between a particle path and an implicit function using a bisection alg...
Definition CD_ParticleOpsImplem.H:177
static void copyDestructive(ParticleContainer< P > &a_dst, ParticleContainer< P > &a_src) noexcept
Copy all the particles from the a_src to a_dst. This destroys the source particles.
Definition CD_ParticleOpsImplem.H:295
static bool domainIntersection(const RealVect &a_oldPos, const RealVect &a_newPos, const RealVect &a_probLo, const RealVect &a_probHi, Real &a_s)
Compute the intersection point between a particle path and a domain side.
Definition CD_ParticleOpsImplem.H:131
Advection and diffused species for DischargeInceptionStepper.
Definition CD_DischargeInceptionSpecies.H:27
Class for streamer inception integral evaluations.
Definition CD_DischargeInceptionStepper.H:80
virtual const std::function< Real(const Real &E, const RealVect &x)> & getAlpha() const noexcept
Get ionization coefficient.
Definition CD_DischargeInceptionStepperImplem.H:2034
void parseRuntimeOptions() override
Parse runtime options.
Definition CD_DischargeInceptionStepperImplem.H:387
virtual const EBAMRCellData * getElectricField() const noexcept
Get the electric field.
Definition CD_DischargeInceptionStepperImplem.H:5431
void parseMode() noexcept
Parse simulation mode.
Definition CD_DischargeInceptionStepperImplem.H:417
void computeIonVelocity(const Real &a_voltage) noexcept
Set the negative ion velocity. Note.
Definition CD_DischargeInceptionStepperImplem.H:5130
virtual void setIonMobility(const std::function< Real(const Real E)> &a_mobility) noexcept
Set the negative ion mobility (field-dependent)
Definition CD_DischargeInceptionStepperImplem.H:1985
virtual void writeReportStationary() const noexcept
Print report to the terminal.
Definition CD_DischargeInceptionStepperImplem.H:4899
virtual Real advance(const Real a_dt) override
Advancement method. Swaps between various kernels.
Definition CD_DischargeInceptionStepperImplem.H:1572
virtual void townsendTrackTrapezoidal(const Real &a_voltage) noexcept
Track particles (positive ions) using a trapezoidal rule and check if the collide with a cathode.
Definition CD_DischargeInceptionStepperImplem.H:3338
virtual void seedUniformParticles() noexcept
Distribute particles in every grid cell.
Definition CD_DischargeInceptionStepperImplem.H:2107
virtual void resetTracerParticles() noexcept
Reset particles.
Definition CD_DischargeInceptionStepperImplem.H:3661
virtual void setIonDensity(const std::function< Real(const RealVect x)> &a_density) noexcept
Set the negative ion density.
Definition CD_DischargeInceptionStepperImplem.H:1973
virtual void computeCriticalVolumeStationary() noexcept
Compute the critical volume of the K values for each voltage.
Definition CD_DischargeInceptionStepperImplem.H:4441
virtual void postRegrid() override
Perform post-regrid operations.
Definition CD_DischargeInceptionStepperImplem.H:1924
virtual void inceptionIntegrateTrapezoidal(const Real &a_voltage) noexcept
K integral: Add integration parts after particles move.
Definition CD_DischargeInceptionStepperImplem.H:2781
virtual void setBackgroundRate(const std::function< Real(const Real &E, const RealVect &x)> &a_backgroundRate) noexcept
Set the background ionization rate (e.g. from cosmic radiation etc).
Definition CD_DischargeInceptionStepperImplem.H:2048
void computeIonDiffusion(const Real &a_voltage) noexcept
Set the negative ion diffusion coefficient.
Definition CD_DischargeInceptionStepperImplem.H:5196
virtual std::pair< Real, RealVect > computeMinimumInceptionVoltage(const EBAMRCellData &a_Uinc) const noexcept
Compute the minimum inception voltage and the starting electron position.
Definition CD_DischargeInceptionStepperImplem.H:4366
virtual void computeTownsendCriterionStationary() noexcept
Solve for the Townsend criterion for each particle in each voltage.
Definition CD_DischargeInceptionStepperImplem.H:3038
virtual void synchronizeSolverTimes(const int a_step, const Real a_time, const Real a_dt) override
Synchronize solver times and time steps.
Definition CD_DischargeInceptionStepperImplem.H:1770
virtual void inceptionIntegrateEuler(const Real &a_voltage) noexcept
Integrate the inception integral using the Euler rule.
Definition CD_DischargeInceptionStepperImplem.H:2609
virtual void computeInceptionVoltageVolume() noexcept
Interpolate between K values to find voltage giving K_inception and store values in m_inceptionVoltag...
Definition CD_DischargeInceptionStepperImplem.H:4105
virtual Real computeIonizationVolumeTransient(const Real &a_voltage) const noexcept
Compute the ionization volume for each voltage.
Definition CD_DischargeInceptionStepperImplem.H:4817
virtual void computeFieldEmission(EBAMRCellData &a_emissionRate, const Real &a_voltage) const noexcept
Compute field emission rates.
Definition CD_DischargeInceptionStepperImplem.H:3959
virtual int getNumberOfPlotVariables() const override
Get the number of plot variables for this time stepper.
Definition CD_DischargeInceptionStepperImplem.H:670
virtual Real computeCriticalVolumeTransient() const noexcept
Compute the critical volume of the K values for each voltage.
Definition CD_DischargeInceptionStepperImplem.H:4532
void registerOperators() override
Register operators.
Definition CD_DischargeInceptionStepperImplem.H:358
virtual void getMaxValueAndLocation(Real &a_maxVal, RealVect &a_maxPos, const EBAMRCellData &a_data) const noexcept
Get the maximum value and location corresponding to the maximum value in the input data holder.
Definition CD_DischargeInceptionStepperImplem.H:5305
virtual void evaluateFunction(EBAMRCellData &a_data, const Real &a_voltage, const std::function< Real(const Real E, const RealVect x)> &a_func) const noexcept
Evaluate a function f = f(E, x) in a volume.
Definition CD_DischargeInceptionStepperImplem.H:4018
void allocate() override
Allocate storage for solvers and time stepper.
Definition CD_DischargeInceptionStepperImplem.H:171
virtual Vector< std::string > getTransientPlotVariableNames() const noexcept
Get plot variable names for transient mode.
Definition CD_DischargeInceptionStepperImplem.H:1008
virtual Mode getMode() const noexcept
Get the solver mode.
Definition CD_DischargeInceptionStepperImplem.H:2100
void parseInceptionAlgorithm() noexcept
Parse the inception algorithm.
Definition CD_DischargeInceptionStepperImplem.H:476
virtual ~DischargeInceptionStepper()
Destructor.
Definition CD_DischargeInceptionStepperImplem.H:111
void solvePoisson() noexcept
Solve the Poisson equation.
Definition CD_DischargeInceptionStepperImplem.H:249
virtual void setAlpha(const std::function< Real(const Real &E, const RealVect &x)> &a_alpha) noexcept
Set the ionization coefficient.
Definition CD_DischargeInceptionStepperImplem.H:2009
virtual void computeInceptionIntegral(EBAMRCellData &a_inceptionIntegral, const Real a_voltage) noexcept
Compute the inception integral for the input voltage.
Definition CD_DischargeInceptionStepperImplem.H:2415
virtual void regrid(const int a_lmin, const int a_oldFinestLevel, const int a_newFinestLevel) override
Time stepper regrid method.
Definition CD_DischargeInceptionStepperImplem.H:1861
bool particleOutsideGrid(const RealVect &a_pos, const RealVect &a_probLo, const RealVect &a_probHi) const noexcept
Check if particle is outside grid boundaries.
Definition CD_DischargeInceptionStepperImplem.H:5090
virtual void writeReportTransient() const noexcept
Print report to the terminal.
Definition CD_DischargeInceptionStepperImplem.H:5010
virtual void rewindTracerParticles() noexcept
Move particles back to their original position.
Definition CD_DischargeInceptionStepperImplem.H:3629
virtual void townsendTrackEuler(const Real &a_voltage) noexcept
Track particles (positive ions) using an Euler rule and check if the collide with a cathode.
Definition CD_DischargeInceptionStepperImplem.H:3225
virtual void writePlotData(LevelData< EBCellFAB > &a_output, int &a_icomp, const std::string a_outputRealm, const int a_level) const override
Write plot data to output holder.
Definition CD_DischargeInceptionStepperImplem.H:1073
void superposition(EBAMRCellData &a_sumField, const MFAMRCellData &a_inhomogeneousField, const MFAMRCellData &a_homogeneousField, const Real a_voltage) const noexcept
Calculate the total electric field = inhomogeneous + V * homogeneous.
Definition CD_DischargeInceptionStepperImplem.H:5275
void parseOptions()
Parse options.
Definition CD_DischargeInceptionStepperImplem.H:372
void setupSolvers() override
Instantiate the tracer particle solver.
Definition CD_DischargeInceptionStepperImplem.H:121
virtual void setEta(const std::function< Real(const Real &E, const RealVect &x)> &a_eta) noexcept
Set the attachment coefficient.
Definition CD_DischargeInceptionStepperImplem.H:2022
virtual void writePlotDataTransient(LevelData< EBCellFAB > &a_output, int &a_icomp, const std::string a_outputRealm, const int a_level) const noexcept
Write plot data for the 'transient' mode.
Definition CD_DischargeInceptionStepperImplem.H:1315
virtual Vector< std::string > getStationaryPlotVariableNames() const noexcept
Get plot variable names for stationary mode.
Definition CD_DischargeInceptionStepperImplem.H:863
virtual void setDetachmentRate(const std::function< Real(const Real &E, const RealVect &x)> &a_detachmentRate) noexcept
Set the detachment rate for negative ions.
Definition CD_DischargeInceptionStepperImplem.H:2061
virtual void computeTownsendCriterionTransient(const Real &a_voltage) noexcept
Solve for the Townsend criterion for each particle in each voltage.
Definition CD_DischargeInceptionStepperImplem.H:3172
virtual void computeIonizationVolumeStationary() noexcept
Compute the ionization volume for each voltage.
Definition CD_DischargeInceptionStepperImplem.H:4719
virtual void computeFieldEmissionStationary() noexcept
Compute field emission rates.
Definition CD_DischargeInceptionStepperImplem.H:3859
void parseOutput() noexcept
Parse output settings.
Definition CD_DischargeInceptionStepperImplem.H:462
virtual void computeInceptionIntegralStationary() noexcept
Solve streamer inception integral for each particle in each voltage and store K values in m_inception...
Definition CD_DischargeInceptionStepperImplem.H:2470
virtual Real computeCriticalAreaTransient() const noexcept
Compute the critical area of the K values for each voltage.
Definition CD_DischargeInceptionStepperImplem.H:4668
virtual Real getCriticalField() const noexcept
Get the breakdown field.
Definition CD_DischargeInceptionStepperImplem.H:5440
virtual Vector< std::string > getPlotVariableNames() const override
Get plot variable names.
Definition CD_DischargeInceptionStepperImplem.H:807
virtual void computeDetachmentStationary() noexcept
Compute the detachment ionization rate for all voltages.
Definition CD_DischargeInceptionStepperImplem.H:3771
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 used for IO.
Definition CD_DischargeInceptionStepperImplem.H:5373
void parseVoltages() noexcept
Parse voltage levels.
Definition CD_DischargeInceptionStepperImplem.H:442
virtual void interpolateGradAlphaToParticles() noexcept
Interpolate alpha/|grad(alpha)| onto some scratch particle storage.
Definition CD_DischargeInceptionStepperImplem.H:2397
virtual void setIonDiffusion(const std::function< Real(const Real E)> &a_diffCo) noexcept
Set the negative ion diffusion coefficient (field-dependent)
Definition CD_DischargeInceptionStepperImplem.H:1997
void postInitialize() override
Perform any post-initialization steps.
Definition CD_DischargeInceptionStepperImplem.H:2315
virtual void setSigma(const std::function< Real(const RealVect &x)> &a_sigma) noexcept
Set surface charge distribution.
Definition CD_DischargeInceptionStepperImplem.H:1961
void parseTransportAlgorithm() noexcept
Parse the transport algorithm.
Definition CD_DischargeInceptionStepperImplem.H:533
virtual void seedIonizationParticles(const Real a_voltage) noexcept
Add particles to every cell where alpha - eta > 0.0.
Definition CD_DischargeInceptionStepperImplem.H:2184
DischargeInceptionStepper()
Default constructor.
Definition CD_DischargeInceptionStepperImplem.H:37
void parsePlotVariables() noexcept
Parse plot variables.
Definition CD_DischargeInceptionStepperImplem.H:573
void initialData() override
Fill problem with initial data.
Definition CD_DischargeInceptionStepperImplem.H:237
virtual const std::function< Real(const Real &E, const RealVect &x)> & getEta() const noexcept
Get attachment coefficient.
Definition CD_DischargeInceptionStepperImplem.H:2041
virtual void setSecondaryEmission(const std::function< Real(const Real &E, const RealVect &x)> &a_coeff) noexcept
Set the secondary emission coefficient.
Definition CD_DischargeInceptionStepperImplem.H:2087
virtual Real computeDt() override
Compute a time step to be used by Driver.
Definition CD_DischargeInceptionStepperImplem.H:1485
virtual void preRegrid(const int a_lmin, const int a_oldFinestLevel) override
Perform pre-regrid operations.
Definition CD_DischargeInceptionStepperImplem.H:1841
virtual void printStepReport() override
Print a step report. Used in transient simulations.
Definition CD_DischargeInceptionStepperImplem.H:1788
virtual void setRho(const std::function< Real(const RealVect &x)> &a_rho) noexcept
Set space charge distribution.
Definition CD_DischargeInceptionStepperImplem.H:1949
virtual void writePlotDataStationary(LevelData< EBCellFAB > &a_output, int &a_icomp, const std::string a_outputRealm, const int a_level) const noexcept
Write plot data for the 'stationary' mode.
Definition CD_DischargeInceptionStepperImplem.H:1116
void parseVerbosity() noexcept
Parse class verbosity.
Definition CD_DischargeInceptionStepperImplem.H:402
void registerRealms() override
Register realms. Primal is the only realm we need.
Definition CD_DischargeInceptionStepperImplem.H:346
virtual void computeBackgroundIonizationStationary() noexcept
Compute the background ionization rate for all voltages.
Definition CD_DischargeInceptionStepperImplem.H:3691
virtual void computeInceptionIntegralTransient(const Real &a_voltage) noexcept
Solve streamer inception integral.
Definition CD_DischargeInceptionStepperImplem.H:2574
virtual void setVoltageCurve(const std::function< Real(const Real &a_time)> &a_voltageCurve) noexcept
Set the voltage curve (used for transient mode)
Definition CD_DischargeInceptionStepperImplem.H:1937
virtual void computeCriticalAreaStationary() noexcept
Compute the critical area of the K values for each voltage.
Definition CD_DischargeInceptionStepperImplem.H:4596
virtual void advanceIons(const Real a_dt) noexcept
Advance negative ions.
Definition CD_DischargeInceptionStepperImplem.H:1684
bool particleInsideEB(const RealVect a_pos) const noexcept
Check if particle is inside electrode.
Definition CD_DischargeInceptionStepperImplem.H:5114
virtual Real computeRdot(const Real &a_voltage) const noexcept
Compute integral_Vcr(dne/dt * (1 - eta/alpha) dV)
Definition CD_DischargeInceptionStepperImplem.H:3527
virtual void setFieldEmission(const std::function< Real(const Real &E, const RealVect &x)> &a_currentDensity) noexcept
Set the field emission current.
Definition CD_DischargeInceptionStepperImplem.H:2074
static const std::string Primal
Identifier for perimal realm.
Definition CD_Realm.H:38
Class which is used for run-time monitoring of events.
Definition CD_Timer.H:31
Base class for a tracer particle solver. This solver can advance particles in a pre-defined velocity ...
Definition CD_TracerParticleSolver.H:37
virtual void remap()
Remap particles.
Definition CD_TracerParticleSolverImplem.H:338
void parsePlotVariables()
Parse plot variables.
Definition CD_TracerParticleSolverImplem.H:147
phase::which_phase m_phase
Phase where this solver lives.
Definition CD_TracerParticleSolver.H:360
TracerParticleSolver()
Default constructor.
Definition CD_TracerParticleSolverImplem.H:25
virtual void setTime(const int a_step, const Real a_time, const Real a_dt)
Set the time for this solver.
Definition CD_TracerParticleSolverImplem.H:256
std::string m_realm
Realm where this solver lives.
Definition CD_TracerParticleSolver.H:345
virtual ParticleContainer< P > & getParticles()
Get all particles.
Definition CD_TracerParticleSolverImplem.H:662
void parseVerbosity()
Parse solver verbosity.
Definition CD_TracerParticleSolverImplem.H:162
int m_verbosity
Verbosity level.
Definition CD_TracerParticleSolver.H:380
RefCountedPtr< AmrMesh > m_amr
Handle to AMR mesh.
Definition CD_TracerParticleSolver.H:320
virtual Vector< std::string > getPlotVariableNames() const
Get plot variable names.
Definition CD_TracerParticleSolverImplem.H:481
Real m_dt
Time step.
Definition CD_TracerParticleSolver.H:365
virtual void allocate()
Allocate storage for this solver.
Definition CD_TracerParticleSolverImplem.H:194
int m_timeStep
Time step.
Definition CD_TracerParticleSolver.H:375
virtual void interpolateVelocities()
Interpolate particles velocities.
Definition CD_TracerParticleSolverImplem.H:392
Real m_time
Time.
Definition CD_TracerParticleSolver.H:370
virtual int getNumberOfPlotVariables() const
Get the number of plot variables.
Definition CD_TracerParticleSolverImplem.H:460
ALWAYS_INLINE void loop(const Box &a_computeBox, Functor &&kernel, const IntVect &a_stride=IntVect::Unit)
Launch a C++ kernel over a regular grid.
Definition CD_BoxLoopsImplem.H:20
RealVect position(const Location::Cell a_location, const VolIndex &a_vof, const EBISBox &a_ebisbox, const Real &a_dx)
Compute the position (ignoring the "origin) of a Vof.
Definition CD_LocationImplem.H:20
Real max(const Real &a_input) noexcept
Get the maximum of the input, reduced over MPI ranks (in the Chombo communicator)
Definition CD_ParallelOpsImplem.H:176
Real min(const Real &a_input) noexcept
Get the minimum of the input, reduced over MPI ranks (in the Chombo communicator)
Definition CD_ParallelOpsImplem.H:58
Real sum(const Real &a_value) noexcept
Compute the sum across all MPI ranks.
Definition CD_ParallelOpsImplem.H:353
Real brentSolve(const Real a_point1, const Real a_point2, const std::function< Real(const Real x)> a_func) noexcept
Compute the root of a function between two points. This is a 1D problem.
Definition CD_PolyUtils.cpp:168
constexpr Real Qe
Elementary charge.
Definition CD_Units.H:34