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 = (1.0 + 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.
2538 K2 = curMaxK;
2539 U2 = curVoltage;
2540
2541 const Real dKdU = (K2 - K1) / (U2 - U1);
2542 const Real nextVoltage = U1 + ((K2 - K1) + m_deltaK) / dKdU;
2543
2544 K1 = K2;
2545 U1 = U2;
2546
2547 // Step to next voltage.
2548 curVoltage = std::min(nextVoltage, curVoltage * m_relativeDeltaU);
2549 curIter = curIter + 1;
2550
2551 if (!m_fullIntegration && (std::abs(curMaxK - m_inceptionK) <= 1E-3)) {
2552 break;
2553 }
2554 }
2555
2556 // Populate the voltage sweep table
2557 m_voltageSweeps.resize(0);
2558 for (int i = 0; i < m_KPlusValues.size(); i++) {
2559 m_voltageSweeps.push_back(std::get<0>(m_KPlusValues[i]));
2560 }
2561}
2562
2563template <typename P, typename F, typename C>
2564void
2566{
2567 CH_TIME("DischargeInceptionStepper::computeInceptionIntegralTransient");
2568 if (m_verbosity > 5) {
2569 pout() << "DischargeInceptionStepper::computeInceptionIntegralTransient" << endl;
2570 }
2571
2572 // Compute K-value on each particle using specified algorithm.
2573 switch (m_inceptionAlgorithm) {
2574 case IntegrationAlgorithm::Euler: {
2575 this->inceptionIntegrateEuler(a_voltage);
2576
2577 break;
2578 }
2579 case IntegrationAlgorithm::Trapezoidal: {
2580 this->inceptionIntegrateTrapezoidal(a_voltage);
2581
2582 break;
2583 }
2584 default: {
2585 MayDay::Error("DischargeInceptionStepper::computeInceptionIntegralTransient - logic bust");
2586
2587 break;
2588 }
2589 }
2590
2591 // Deposit the particles onto m_inceptionIntegral
2592 m_tracerParticleSolver->deposit(m_inceptionIntegral);
2593
2594 m_amr->conservativeAverage(m_inceptionIntegral, m_realm, m_phase);
2595 m_amr->interpGhost(m_inceptionIntegral, m_realm, m_phase);
2596}
2597
2598template <typename P, typename F, typename C>
2599void
2601{
2602 CH_TIME("DischargeInceptionStepper::inceptionIntegrateEuler");
2603 if (m_verbosity > 5) {
2604 pout() << "DischargeInceptionStepper::inceptionIntegrateEuler" << endl;
2605 }
2606
2607 const RealVect probLo = m_amr->getProbLo();
2608 const RealVect probHi = m_amr->getProbHi();
2609
2610 // Allocate a data holder for holding the processed particles. This
2611 // will be faster because then we only have to iterate through the
2612 // particles that are actually still moving.
2614 m_amr->allocate(amrProcessedParticles, m_realm);
2615
2616 ParticleContainer<P>& amrParticles = m_tracerParticleSolver->getParticles();
2617
2618 m_tracerParticleSolver->remap();
2619
2620 size_t particlesBefore = 0;
2621
2622 if (m_debug) {
2623 particlesBefore = amrParticles.getNumberOfValidParticlesGlobal();
2624 }
2625
2626 // Allocate something that holds the velocity of the electrons
2628 m_amr->allocate(scratch, m_realm, m_phase, SpaceDim);
2629 this->superposition(scratch, a_voltage);
2630 DataOps::scale(scratch, -1.0);
2631
2632 m_tracerParticleSolver->setVelocity(scratch);
2633 m_tracerParticleSolver->interpolateVelocities();
2634
2635 while (amrParticles.getNumberOfValidParticlesGlobal() > 0) {
2636
2637 this->interpolateGradAlphaToParticles();
2638
2639 // Euler integration.
2640 for (int lvl = 0; lvl <= m_amr->getFinestLevel(); lvl++) {
2641 const Real dx = m_amr->getDx()[lvl];
2642
2643 const DisjointBoxLayout& dbl = m_amr->getGrids(m_realm)[lvl];
2644 const DataIterator& dit = dbl.dataIterator();
2645
2646 const int nbox = dit.size();
2647
2648#pragma omp parallel for schedule(runtime)
2649 for (int mybox = 0; mybox < nbox; mybox++) {
2650 const DataIndex& din = dit[mybox];
2651
2652 List<P>& solverParticles = amrParticles[lvl][din].listItems();
2654
2655 for (ListIterator<P> lit(solverParticles); lit.ok();) {
2656 P& p = lit();
2657
2658 const RealVect x = p.position();
2659 const RealVect vel = p.velocity();
2660 const Real v = vel.vectorLength();
2661 const Real E = v;
2662 const Real alpha = m_alpha(E, x);
2663 const Real eta = m_eta(E, x);
2664 const Real alphaEff = alpha - eta;
2665 const Real tol = 1E-10;
2666 const Real gradAlpha = tol + (p.template vect<2>()).vectorLength();
2667
2668 // Select a step size equal to the avalance length, but never exceed the physical and grid hardcaps
2669 Real deltaX;
2670 deltaX = std::min(m_alphaDx / (tol + std::abs(alphaEff)), m_gradAlphaDx * std::abs(alphaEff / gradAlpha));
2671 deltaX = std::max(deltaX, m_minGridDx * dx);
2672 deltaX = std::min(deltaX, m_maxGridDx * dx);
2673 deltaX = std::min(deltaX, m_maxPhysDx);
2674 deltaX = std::max(deltaX, m_minPhysDx);
2675
2676 const Real dt = deltaX / v;
2677 const RealVect newPos = p.position() + dt * vel;
2678 const Real delta = (newPos - x).vectorLength();
2679
2680 const bool outsideDomain = this->particleOutsideGrid(newPos, probLo, probHi);
2681 const bool insideEB = this->particleInsideEB(newPos);
2682
2683 Real s = 0.0;
2684
2685 if (alphaEff < 0.0) {
2686 processedParticles.transfer(lit);
2687 }
2688 else if (insideEB) {
2689 // If the particle struck the EB or domain we finish off the integration with a partial step
2690 const RefCountedPtr<BaseIF>& impFunc = m_amr->getBaseImplicitFunction(m_phase);
2691
2692 if (ParticleOps::ebIntersectionBisect(impFunc, x, newPos, m_minGridDx * dx, s)) {
2693 p.weight() = p.weight() + s * delta * alphaEff;
2694 }
2695
2696 processedParticles.transfer(lit);
2697 }
2698 else if (outsideDomain) {
2699 if (ParticleOps::domainIntersection(x, newPos, m_amr->getProbLo(), m_amr->getProbHi(), s)) {
2700 p.weight() = p.weight() + s * delta * alphaEff;
2701 }
2702
2703 processedParticles.transfer(lit);
2704 }
2705 else {
2706 p.position() = newPos;
2707 p.weight() = p.weight() + deltaX * alphaEff;
2708
2709 ++lit;
2710 }
2711 }
2712
2713 // Transfer particles that completed their integration.
2714 if (!m_fullIntegration) {
2715 for (ListIterator<P> lit(solverParticles); lit.ok();) {
2716 P& p = lit();
2717
2718 if (p.weight() >= m_inceptionK) {
2719 processedParticles.transfer(lit);
2720 }
2721 else {
2722 ++lit;
2723 }
2724 }
2725 }
2726 }
2727 }
2728
2729 // Update velocities.
2730 m_tracerParticleSolver->remap();
2731 m_tracerParticleSolver->interpolateVelocities();
2732 }
2733
2735
2736 // Truncate the weights if we didn't run full integration
2737 if (!m_fullIntegration) {
2738 for (int lvl = 0; lvl <= m_amr->getFinestLevel(); lvl++) {
2739 const DisjointBoxLayout& dbl = m_amr->getGrids(m_realm)[lvl];
2740 const DataIterator& dit = dbl.dataIterator();
2741
2742 const int nbox = dit.size();
2743
2744#pragma omp parallel for schedule(runtime)
2745 for (int mybox = 0; mybox < nbox; mybox++) {
2746 const DataIndex& din = dit[mybox];
2747
2748 List<P>& solverParticles = amrParticles[lvl][din].listItems();
2749
2750 for (ListIterator<P> lit(solverParticles); lit.ok(); ++lit) {
2751 lit().weight() = std::min(m_inceptionK, lit().weight());
2752 }
2753 }
2754 }
2755 }
2756
2757 this->rewindTracerParticles();
2758
2759 size_t particlesAfter = 0;
2760
2761 if (m_debug) {
2762 particlesAfter = amrParticles.getNumberOfValidParticlesGlobal();
2763 }
2764
2766 MayDay::Warning("DischargeInceptionStepper::inceptionIntegrateEuler - lost/gained particles!");
2767 }
2768}
2769
2770template <typename P, typename F, typename C>
2771void
2773{
2774 CH_TIME("DischargeInceptionStepper::inceptionIntegrateTrapezoidal");
2775 if (m_verbosity > 5) {
2776 pout() << "DischargeInceptionStepper::inceptionIntegrateTrapezoidal" << endl;
2777 }
2778
2779 // TLDR: We move the particle using Heun's method.
2780 //
2781 // p^(k+1) = p^k + 0.5 * dt * [v(p^k) + v(p^l)]
2782 //
2783 // where p^l = p^k + dt * v(p^k). We will set dt = d/|v(p^k)|. Once we have the
2784 // particle endpoints we can compute the contribution to the inception integral
2785 // by using the trapezoidal (quadrature) rule
2786 //
2787 // T += 0.5 * dx * [alpha_eff(E(p^k)) + alpha_eff(E(p^(k+1)))],
2788 //
2789 // where dx = |p^(k+1) - p^k|.
2790
2791 const RealVect probLo = m_amr->getProbLo();
2792 const RealVect probHi = m_amr->getProbHi();
2793
2794 // Allocate a data holder for holding the processed particles. This
2795 // will be faster because then we only have to iterate through the
2796 // particles that are actually still moving.
2798 m_amr->allocate(amrProcessedParticles, m_realm);
2799
2800 ParticleContainer<P>& amrParticles = m_tracerParticleSolver->getParticles();
2801
2802 m_tracerParticleSolver->remap();
2803
2804 size_t particlesBefore = 0;
2805
2806 if (m_debug) {
2807 particlesBefore = amrParticles.getNumberOfValidParticlesGlobal();
2808 }
2809
2810 // Allocate something that holds the velocity of the electrons
2812 m_amr->allocate(scratch, m_realm, m_phase, SpaceDim);
2813 this->superposition(scratch, a_voltage);
2814 DataOps::scale(scratch, -1.0);
2815
2816 m_tracerParticleSolver->setVelocity(scratch);
2817 m_tracerParticleSolver->interpolateVelocities();
2818
2819 while (amrParticles.getNumberOfValidParticlesGlobal() > 0) {
2820
2821 this->interpolateGradAlphaToParticles();
2822
2823 // Euler stage.
2824 for (int lvl = 0; lvl <= m_amr->getFinestLevel(); lvl++) {
2825 const Real dx = m_amr->getDx()[lvl];
2826
2827 const DisjointBoxLayout& dbl = m_amr->getGrids(m_realm)[lvl];
2828 const DataIterator& dit = dbl.dataIterator();
2829
2830 const int nbox = dit.size();
2831
2832#pragma omp parallel for schedule(runtime)
2833 for (int mybox = 0; mybox < nbox; mybox++) {
2834 const DataIndex& din = dit[mybox];
2835
2836 List<P>& solverParticles = amrParticles[lvl][din].listItems();
2838
2839 for (ListIterator<P> lit(solverParticles); lit.ok();) {
2840 P& p = lit();
2841
2842 const RealVect x = p.position();
2843 const RealVect vel = p.velocity();
2844 const Real v = vel.vectorLength();
2845 const Real E = v;
2846 const Real alpha = m_alpha(E, x);
2847 const Real eta = m_eta(E, x);
2848 const Real alphaEff = alpha - eta;
2849 const Real tol = 1E-10;
2850 const Real gradAlpha = tol + (p.template vect<2>()).vectorLength();
2851
2852 // Select a step size equal to the avalance length, but never exceed the physical and grid hardcaps
2853 Real deltaX;
2854 deltaX = std::min(m_alphaDx / (tol + std::abs(alphaEff)), m_gradAlphaDx * std::abs(alphaEff / gradAlpha));
2855 deltaX = std::max(deltaX, m_minGridDx * dx);
2856 deltaX = std::min(deltaX, m_maxGridDx * dx);
2857 deltaX = std::min(deltaX, m_maxPhysDx);
2858 deltaX = std::max(deltaX, m_minPhysDx);
2859
2860 const Real dt = deltaX / v;
2861 const RealVect newPos = p.position() + dt * vel;
2862 const Real delta = (newPos - x).vectorLength();
2863
2864 const bool outsideDomain = this->particleOutsideGrid(newPos, probLo, probHi);
2865 const bool insideEB = this->particleInsideEB(newPos);
2866
2867 // If particle hit the EB or domain we finish off with a partial Euler step
2868 Real s = 0.0;
2869
2870 if (alphaEff < 0.0) {
2871 processedParticles.transfer(lit);
2872 }
2873 else if (insideEB) {
2874 const RefCountedPtr<BaseIF>& impFunc = m_amr->getBaseImplicitFunction(m_phase);
2875
2876 if (ParticleOps::ebIntersectionBisect(impFunc, x, newPos, m_minGridDx * dx, s)) {
2877 p.weight() = p.weight() + s * delta * alphaEff;
2878 }
2879
2880 processedParticles.transfer(lit);
2881 }
2882 else if (outsideDomain) {
2883 if (ParticleOps::domainIntersection(x, newPos, m_amr->getProbLo(), m_amr->getProbHi(), s)) {
2884 p.weight() = p.weight() + s * delta * alphaEff;
2885 }
2886
2887 processedParticles.transfer(lit);
2888 }
2889 else {
2890 // Do an Euler step, storaging alpha(p^k), v(p^k), and the time step size.
2891 p.template real<0>() = alphaEff;
2892 p.template real<1>() = dt;
2893 p.template vect<1>() = vel;
2894
2895 p.position() = newPos;
2896
2897 ++lit;
2898 }
2899 }
2900 }
2901 }
2902
2903 // Remap and update velocities.
2904 m_tracerParticleSolver->remap();
2905 m_tracerParticleSolver->interpolateVelocities();
2906
2907 // Second stage.
2908 for (int lvl = 0; lvl <= m_amr->getFinestLevel(); lvl++) {
2909 const Real dx = m_amr->getDx()[lvl];
2910
2911 const DisjointBoxLayout& dbl = m_amr->getGrids(m_realm)[lvl];
2912 const DataIterator& dit = dbl.dataIterator();
2913
2914 const int nbox = dit.size();
2915
2916#pragma omp parallel for schedule(runtime)
2917 for (int mybox = 0; mybox < nbox; mybox++) {
2918 const DataIndex& din = dit[mybox];
2919
2920 List<P>& solverParticles = amrParticles[lvl][din].listItems();
2922
2923 for (ListIterator<P> lit(solverParticles); lit.ok();) {
2924
2925 P& p = lit();
2926
2927 const Real dt = p.template real<1>();
2928 const RealVect vk = p.template vect<1>();
2929 const RealVect vk1 = p.velocity();
2930 const RealVect x = p.position();
2931 const Real E = vk1.vectorLength();
2932
2933 // Note the weird subtraction since p.position() was updated
2934 // to p^k + dt * v^k.
2935 const RealVect oldPos = p.position() - dt * vk;
2936 const RealVect newPos = oldPos + 0.5 * dt * (vk + vk1);
2937
2938 // Compute new alpha.
2939 const Real alphak = p.template real<0>();
2940 const Real alphak1 = m_alpha(E, x) - m_eta(E, x);
2941 const Real delta = (newPos - oldPos).vectorLength();
2942
2943 // Stop integration for particles that move into regions alpha < 0.0,
2944 // inside the EB or outside of the domain.
2945 const bool negativeAlpha = (alphak + alphak1) < 0.0;
2946 const bool outsideDomain = this->particleOutsideGrid(newPos, probLo, probHi);
2947 const bool insideEB = this->particleInsideEB(newPos);
2948
2949 // If the particle wound up inside the EB we finish off the integration with a partial Euler step
2950 Real s = 0.0;
2951
2952 if (negativeAlpha) {
2953 processedParticles.transfer(lit);
2954 }
2955 else if (insideEB) {
2956 const RefCountedPtr<BaseIF>& impFunc = m_amr->getBaseImplicitFunction(m_phase);
2957
2958 if (ParticleOps::ebIntersectionBisect(impFunc, oldPos, newPos, m_minGridDx * dx, s)) {
2959 p.weight() = p.weight() + s * delta * alphak;
2960 }
2961
2962 processedParticles.transfer(lit);
2963 }
2964 else if (outsideDomain) {
2965 if (ParticleOps::domainIntersection(oldPos, newPos, m_amr->getProbLo(), m_amr->getProbHi(), s)) {
2966 p.weight() = p.weight() + s * delta * alphak;
2967 }
2968
2969 processedParticles.transfer(lit);
2970 }
2971 else {
2972 p.position() = newPos;
2973 p.weight() = p.weight() + 0.5 * delta * (alphak + alphak1);
2974
2975 ++lit;
2976 }
2977 }
2978
2979 // Transfer particles that completed their integration (if we're doing partial integration)
2980 if (!m_fullIntegration) {
2981 for (ListIterator<P> lit(solverParticles); lit.ok();) {
2982 P& p = lit();
2983
2984 if (p.weight() >= m_inceptionK) {
2985 processedParticles.transfer(lit);
2986 }
2987 else {
2988 ++lit;
2989 }
2990 }
2991 }
2992 }
2993 }
2994
2995 // Remap and update velocities.
2996 m_tracerParticleSolver->remap();
2997 m_tracerParticleSolver->interpolateVelocities();
2998 }
2999
3000 // Copy processed particles over to the solver particles.
3002
3003 // Truncate the weights if we didn't run full integration
3004 if (!m_fullIntegration) {
3005 for (int lvl = 0; lvl <= m_amr->getFinestLevel(); lvl++) {
3006 const DisjointBoxLayout& dbl = m_amr->getGrids(m_realm)[lvl];
3007 const DataIterator& dit = dbl.dataIterator();
3008
3009 const int nbox = dit.size();
3010
3011#pragma omp parallel for schedule(runtime)
3012 for (int mybox = 0; mybox < nbox; mybox++) {
3013 const DataIndex& din = dit[mybox];
3014
3015 List<P>& solverParticles = amrParticles[lvl][din].listItems();
3016
3017 for (ListIterator<P> lit(solverParticles); lit.ok(); ++lit) {
3018 lit().weight() = std::min(m_inceptionK, lit().weight());
3019 }
3020 }
3021 }
3022 }
3023
3024 this->rewindTracerParticles();
3025}
3026
3027template <typename P, typename F, typename C>
3028void
3030{
3031 CH_TIME("DischargeInceptionStepper::computeTownsendCriterionStationary");
3032 if (m_verbosity > 5) {
3033 pout() << "DischargeInceptionStepper::computeTownsendCriterionStationary" << endl;
3034 }
3035
3036 CH_assert(m_inceptionIntegralPlus.size() == m_inceptionIntegralMinu.size());
3037
3038 m_townsendCriterionPlus.resize(0);
3039 m_townsendCriterionMinu.resize(0);
3040
3041 for (size_t i = 0; i < m_voltageSweeps.size(); i++) {
3042 const Real voltage = m_voltageSweeps[i];
3043
3044 const EBAMRCellData KPlus = m_inceptionIntegralPlus[i];
3045 const EBAMRCellData KMinu = m_inceptionIntegralMinu[i];
3046
3049
3052
3055
3058
3061
3064
3065 Real maxTPlus = 0.0;
3066 Real maxTMinu = 0.0;
3067
3070
3071 if (m_evaluateTownsend) {
3072
3073 // Positive polarity.
3074 this->resetTracerParticles();
3075
3076 switch (m_inceptionAlgorithm) {
3077 case IntegrationAlgorithm::Euler: {
3078 this->townsendTrackEuler(voltage);
3079
3080 break;
3081 }
3082 case IntegrationAlgorithm::Trapezoidal: {
3083 this->townsendTrackTrapezoidal(voltage);
3084
3085 break;
3086 }
3087 default: {
3088 MayDay::Error("DischargeInceptionStepper::computeTownsendCriterionStationary -- logic bust");
3089
3090 break;
3091 }
3092 }
3093
3094 m_tracerParticleSolver->deposit(townsendCriterionPlus);
3095
3096 m_amr->conservativeAverage(townsendCriterionPlus, m_realm, m_phase);
3098
3099 // Negative polarity.
3100 this->resetTracerParticles();
3101
3102 switch (m_inceptionAlgorithm) {
3103 case IntegrationAlgorithm::Euler: {
3104 this->townsendTrackEuler(-voltage);
3105
3106 break;
3107 }
3108 case IntegrationAlgorithm::Trapezoidal: {
3109 this->townsendTrackTrapezoidal(-voltage);
3110
3111 break;
3112 }
3113 default: {
3114 MayDay::Error("DischargeInceptionStepper::computeTownsendCriterionStationary -- logic bust");
3115
3116 break;
3117 }
3118 }
3119
3120 m_tracerParticleSolver->deposit(townsendCriterionMinu);
3121
3122 m_amr->conservativeAverage(townsendCriterionMinu, m_realm, m_phase);
3124
3125 // For turning K into exp(K) - 1
3126 auto exponentiate = [](const Real x) -> Real {
3127 return x > 0.0 ? exp(x) - 1 : 0.0;
3128 };
3129
3132
3135
3138
3139 this->getMaxValueAndLocation(maxTPlus, maxTPlusPos, townsendCriterionPlus);
3140 this->getMaxValueAndLocation(maxTMinu, maxTMinuPos, townsendCriterionMinu);
3141
3142 // If we're not doing full integration we truncate the Townsend value to 1.
3143 if (!m_fullIntegration) {
3144 auto truncate = [](const Real x) -> Real {
3145 return std::min(x, 1.0);
3146 };
3147
3150 }
3151 }
3152
3153 m_townsendCriterionPlus.push_back(townsendCriterionPlus);
3154 m_townsendCriterionMinu.push_back(townsendCriterionMinu);
3155
3156 m_TPlusValues.emplace_back(std::make_tuple(voltage, maxTPlus, maxTPlusPos));
3157 m_TMinuValues.emplace_back(std::make_tuple(voltage, maxTMinu, maxTMinuPos));
3158 }
3159}
3160
3161template <typename P, typename F, typename C>
3162void
3164{
3165 CH_TIME("DischargeInceptionStepper::computeTownsendCriterionTransient");
3166 if (m_verbosity > 5) {
3167 pout() << "DischargeInceptionStepper::computeTownsendCriterionTransient" << endl;
3168 }
3169
3170 // Compute T-value on each particle using specified algorithm.
3171 switch (m_inceptionAlgorithm) {
3172 case IntegrationAlgorithm::Euler: {
3173 this->townsendTrackEuler(a_voltage);
3174
3175 break;
3176 }
3177 case IntegrationAlgorithm::Trapezoidal: {
3178 this->townsendTrackTrapezoidal(a_voltage);
3179
3180 break;
3181 }
3182 default: {
3183 MayDay::Error("DischargeInceptionStepper::computeTownsendCriterionTransient - logic bust");
3184
3185 break;
3186 }
3187 }
3188
3189 // Compute gamma
3191 m_amr->allocate(gamma, m_realm, m_phase, 1);
3193 m_tracerParticleSolver->deposit(gamma);
3194
3195 // Turn K into exp(K)-1
3196 auto exponentiate = [](const Real x) -> Real {
3197 return x > 0.0 ? exp(x) - 1 : 0.0;
3198 };
3199 DataOps::copy(m_townsendCriterion, m_inceptionIntegral);
3200 DataOps::compute(m_townsendCriterion, exponentiate);
3201 DataOps::multiply(m_townsendCriterion, gamma);
3202
3203 // If we're running without full integration we truncate the Townsend value to 1.
3204 if (!m_fullIntegration) {
3205 DataOps::compute(m_townsendCriterion, [](const Real x) {
3206 return std::min(x, 1.0);
3207 });
3208 }
3209
3210 m_amr->conservativeAverage(m_townsendCriterion, m_realm, m_phase);
3211 m_amr->interpGhost(m_townsendCriterion, m_realm, m_phase);
3212}
3213
3214template <typename P, typename F, typename C>
3215void
3217{
3218 CH_TIME("DischargeInceptionStepper::townsendTrackEuler");
3219 if (m_verbosity > 5) {
3220 pout() << "DischargeInceptionStepper::townsendTrackEuler" << endl;
3221 }
3222 const RealVect probLo = m_amr->getProbLo();
3223 const RealVect probHi = m_amr->getProbHi();
3224
3225 // Allocate a data holder for holding the processed particles. This
3226 // will be faster because then we only have to iterate through the
3227 // particles that are actually still moving.
3229 m_amr->allocate(amrProcessedParticles, m_realm);
3230
3231 ParticleContainer<P>& amrParticles = m_tracerParticleSolver->getParticles();
3232
3233 m_tracerParticleSolver->remap();
3234
3235 size_t particlesBefore = 0;
3236
3237 if (m_debug) {
3238 particlesBefore = amrParticles.getNumberOfValidParticlesGlobal();
3239 }
3240
3241 // Allocate something that holds the velocity of the ions
3243 m_amr->allocate(scratch, m_realm, m_phase, SpaceDim);
3244 this->superposition(scratch, a_voltage);
3245
3246 m_tracerParticleSolver->setVelocity(scratch);
3247 m_tracerParticleSolver->interpolateVelocities();
3248
3249 while (amrParticles.getNumberOfValidParticlesGlobal() > 0) {
3250
3251 // Euler integration.
3252 for (int lvl = 0; lvl <= m_amr->getFinestLevel(); lvl++) {
3253 const Real dx = m_amr->getDx()[lvl];
3254
3255 // Integrate particles until they leave alpha > 0 or strike a cathode surface.
3256 const DisjointBoxLayout& dbl = m_amr->getGrids(m_realm)[lvl];
3257 const DataIterator& dit = dbl.dataIterator();
3258
3259 const int nbox = dit.size();
3260
3261#pragma omp parallel for schedule(runtime)
3262 for (int mybox = 0; mybox < nbox; mybox++) {
3263 const DataIndex& din = dit[mybox];
3264
3265 List<P>& solverParticles = amrParticles[lvl][din].listItems();
3267
3268 for (ListIterator<P> lit(solverParticles); lit.ok();) {
3269 P& p = lit();
3270
3271 const RealVect x = p.position();
3272 const RealVect vel = p.velocity();
3273 const Real v = vel.vectorLength();
3274 const Real E = v;
3275 const Real deltaX = m_townsendGridDx * dx;
3276 const Real dt = deltaX / v;
3277 const RealVect newPos = p.position() + dt * vel;
3278
3279 const bool outsideDomain = this->particleOutsideGrid(newPos, probLo, probHi);
3280 const bool insideEB = this->particleInsideEB(newPos);
3281
3282 // Stop integration if avalanche phase is over
3283 const Real alpha = m_alpha(E, x);
3284 const Real eta = m_eta(E, x);
3285 const Real alphaEff = alpha - eta;
3286 const bool negativeAlpha = alphaEff <= 0.0;
3287
3288 p.weight() = 0.0;
3289
3290 if (insideEB) {
3291 p.weight() = m_secondaryEmission(E, x);
3292
3293 processedParticles.transfer(lit);
3294 }
3295 else if (outsideDomain || negativeAlpha) {
3296 processedParticles.transfer(lit);
3297 }
3298 else {
3299 p.position() = newPos;
3300
3301 ++lit;
3302 }
3303 }
3304 }
3305 }
3306
3307 // Update velocities.
3308 m_tracerParticleSolver->remap();
3309 m_tracerParticleSolver->interpolateVelocities();
3310 }
3311
3313
3314 this->rewindTracerParticles();
3315
3316 size_t particlesAfter = 0;
3317
3318 if (m_debug) {
3319 particlesAfter = amrParticles.getNumberOfValidParticlesGlobal();
3320 }
3321
3323 MayDay::Warning("DischargeInceptionStepper::townsendTrackEuler - lost/gained particles!");
3324 }
3325}
3326
3327template <typename P, typename F, typename C>
3328void
3330{
3331 CH_TIME("DischargeInceptionStepper::townsendTrackTrapezoidal");
3332 if (m_verbosity > 5) {
3333 pout() << "DischargeInceptionStepper::townsendTrackTrapezoidal" << endl;
3334 }
3335
3336 // TLDR: We move the particle using Heun's method.
3337 //
3338 // p^(k+1) = p^k + 0.5 * dt * [v(p^k) + v(p^l)]
3339 //
3340 // where p^l = p^k + dt * v(p^k). We will set dt = d/|v(p^k)|.
3341
3342 const RealVect probLo = m_amr->getProbLo();
3343 const RealVect probHi = m_amr->getProbHi();
3344
3345 // Allocate a data holder for holding the processed particles. This
3346 // will be faster because then we only have to iterate through the
3347 // particles that are actually still moving.
3349 m_amr->allocate(amrProcessedParticles, m_realm);
3350
3351 ParticleContainer<P>& amrParticles = m_tracerParticleSolver->getParticles();
3352
3353 m_tracerParticleSolver->remap();
3354
3355 size_t particlesBefore = 0;
3356
3357 if (m_debug) {
3358 particlesBefore = amrParticles.getNumberOfValidParticlesGlobal();
3359 }
3360
3361 // Allocate something that holds the velocity of the ions
3363 m_amr->allocate(scratch, m_realm, m_phase, SpaceDim);
3364 this->superposition(scratch, a_voltage);
3365
3366 m_tracerParticleSolver->setVelocity(scratch);
3367 m_tracerParticleSolver->interpolateVelocities();
3368
3369 while (amrParticles.getNumberOfValidParticlesGlobal() > 0) {
3370
3371 // Euler stage.
3372 for (int lvl = 0; lvl <= m_amr->getFinestLevel(); lvl++) {
3373 const Real dx = m_amr->getDx()[lvl];
3374
3375 // First stage
3376 const DisjointBoxLayout& dbl = m_amr->getGrids(m_realm)[lvl];
3377 const DataIterator& dit = dbl.dataIterator();
3378
3379 const int nbox = dit.size();
3380
3381#pragma omp parallel for schedule(runtime)
3382 for (int mybox = 0; mybox < nbox; mybox++) {
3383 const DataIndex& din = dit[mybox];
3384
3385 List<P>& solverParticles = amrParticles[lvl][din].listItems();
3387
3388 for (ListIterator<P> lit(solverParticles); lit.ok();) {
3389 P& p = lit();
3390
3391 const RealVect x = p.position();
3392 const RealVect vel = p.velocity();
3393 const Real v = vel.vectorLength();
3394 const Real E = v;
3395 const Real alpha = m_alpha(E, x);
3396 const Real eta = m_eta(E, x);
3397 const Real alphaEff = alpha - eta;
3398
3399 // Compute a time step that we will use for the integration.
3400 const Real deltaX = m_townsendGridDx * dx;
3401 const Real dt = deltaX / v;
3402 const RealVect newPos = p.position() + vel * dt;
3403
3404 const bool outsideDomain = this->particleOutsideGrid(newPos, probLo, probHi);
3405 const bool insideEB = this->particleInsideEB(newPos);
3406
3407 if (insideEB) {
3408 p.weight() = m_secondaryEmission(E, x);
3409
3410 processedParticles.transfer(lit);
3411 }
3412 else if (alphaEff < 0.0) {
3413 processedParticles.transfer(lit);
3414 }
3415 else if (outsideDomain) {
3416 processedParticles.transfer(lit);
3417 }
3418 else {
3419 // Do an Euler step, storaging alpha(p^k), v(p^k), and the time step size.
3420 p.template real<0>() = alphaEff;
3421 p.template real<1>() = dt;
3422 p.template vect<1>() = vel;
3423
3424 p.position() = newPos;
3425
3426 ++lit;
3427 }
3428 }
3429 }
3430 }
3431
3432 // Remap and update velocities.
3433 m_tracerParticleSolver->remap();
3434 m_tracerParticleSolver->interpolateVelocities();
3435
3436 // Second stage.
3437 for (int lvl = 0; lvl <= m_amr->getFinestLevel(); lvl++) {
3438 const DisjointBoxLayout& dbl = m_amr->getGrids(m_realm)[lvl];
3439 const DataIterator& dit = dbl.dataIterator();
3440
3441 const int nbox = dit.size();
3442
3443#pragma omp parallel for schedule(runtime)
3444 for (int mybox = 0; mybox < nbox; mybox++) {
3445 const DataIndex& din = dit[mybox];
3446
3447 List<P>& solverParticles = amrParticles[lvl][din].listItems();
3449
3450 for (ListIterator<P> lit(solverParticles); lit.ok();) {
3451
3452 P& p = lit();
3453
3454 const Real dt = p.template real<1>();
3455 const RealVect vk = p.template vect<1>();
3456 const RealVect vk1 = p.velocity();
3457 const RealVect x = p.position();
3458 const Real E = vk1.vectorLength();
3459
3460 // Note the weird subtraction since p.position() was updated
3461 // to p^k + dt * v^k.
3462 const RealVect oldPos = p.position() - dt * vk;
3463 const RealVect newPos = p.position() + 0.5 * dt * (vk1 - vk);
3464
3465 // Compute new alpha.
3466 const Real alphak = p.template real<0>();
3467 const Real alphak1 = m_alpha(E, x) - m_eta(E, x);
3468
3469 // Stop integration for particles that move into regions alpha < 0.0,
3470 // inside the EB or outside of the domain.
3471 const bool negativeAlpha = (alphak + alphak1) < 0.0;
3472 const bool outsideDomain = this->particleOutsideGrid(newPos, probLo, probHi);
3473 const bool insideEB = this->particleInsideEB(newPos);
3474
3475 // If the particle wound up inside the EB we finish off the integration with a partial Euler step
3476 Real s = 0.0;
3477
3478 if (insideEB) {
3479 p.weight() = m_secondaryEmission(E, oldPos);
3480
3481 processedParticles.transfer(lit);
3482 }
3483 else if (outsideDomain) {
3484 processedParticles.transfer(lit);
3485 }
3486 else {
3487 p.position() = newPos;
3488
3489 ++lit;
3490 }
3491 }
3492 }
3493 }
3494
3495 // Remap and update velocities.
3496 m_tracerParticleSolver->remap();
3497 m_tracerParticleSolver->interpolateVelocities();
3498 }
3499
3500 // Copy processed particles over to the solver particles.
3502
3503 this->rewindTracerParticles();
3504
3505 size_t particlesAfter = 0;
3506
3507 if (m_debug) {
3508 particlesAfter = amrParticles.getNumberOfValidParticlesGlobal();
3509 }
3510
3512 MayDay::Warning("DischargeInceptionStepper::townsendTrackTrapezoidal - lost/gained particles!");
3513 }
3514}
3515
3516template <typename P, typename F, typename C>
3517Real
3519{
3520 CH_TIME("DischargeInceptionStepper::computeRdot");
3521 if (m_verbosity > 5) {
3522 pout() << "DischargeInceptionStepper::computeRdot" << endl;
3523 }
3524
3525 Real Rdot = 0.0;
3526
3527 // 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"
3528 const RealVect probLo = m_amr->getProbLo();
3529
3530 // Compute the electric field at the input voltage
3532 m_amr->allocate(scratch, m_realm, m_phase, SpaceDim);
3533 this->superposition(scratch, a_voltage);
3534
3535 for (int lvl = 0; lvl <= m_amr->getFinestLevel(); lvl++) {
3536 const DisjointBoxLayout& dbl = m_amr->getGrids(m_realm)[lvl];
3537 const DataIterator& dit = dbl.dataIterator();
3538 const EBISLayout& ebisl = m_amr->getEBISLayout(m_realm, m_phase)[lvl];
3539 const Real dx = m_amr->getDx()[lvl];
3540
3541 const Real vol = std::pow(dx, SpaceDim);
3542 const Real area = std::pow(dx, SpaceDim - 1);
3543
3544 const int nbox = dit.size();
3545
3546#pragma omp parallel for schedule(runtime) reduction(+ : Rdot)
3547 for (int mybox = 0; mybox < nbox; mybox++) {
3548 const DataIndex& din = dit[mybox];
3549
3550 const EBISBox& ebisbox = ebisl[din];
3551 const BaseFab<bool>& validCells = (*m_amr->getValidCells(m_realm)[lvl])[din];
3552
3553 const EBCellFAB& electricField = (*scratch[lvl])[din];
3554 const EBCellFAB& inceptionIntegral = (*m_inceptionIntegral[lvl])[din];
3555 const EBCellFAB& fieldEmission = (*m_emissionRate[lvl])[din];
3556 const EBCellFAB& ionDensity = (*m_ionSolver->getPhi()[lvl])[din];
3557
3558 const FArrayBox& electricFieldReg = electricField.getFArrayBox();
3559 const FArrayBox& inceptionIntegralReg = inceptionIntegral.getFArrayBox();
3560 const FArrayBox& ionDensityReg = ionDensity.getFArrayBox();
3561
3562 // Add contribution from background ionization in regular cells.
3563 auto regularKernel = [&](const IntVect& iv) -> void {
3564 if (ebisbox.isRegular(iv) && validCells(iv, 0)) {
3565 if (inceptionIntegralReg(iv, 0) >= m_inceptionK) {
3566 const RealVect pos = probLo + (0.5 * RealVect::Unit + RealVect(iv)) * dx;
3567 const RealVect EE = RealVect(
3569 const Real E = EE.vectorLength();
3570
3571 const Real alpha = m_alpha(E, pos);
3572 const Real eta = m_eta(E, pos);
3573 const Real k = m_detachmentRate(E, pos);
3574 const Real dndt = m_backgroundRate(E, pos) + k * ionDensityReg(iv, 0);
3575
3576 CH_assert(alpha >= eta);
3577
3578 Rdot += dndt * (1.0 - eta / alpha) * vol;
3579 }
3580 }
3581 };
3582
3583 // Add contribution from background ionization and field emission in cut-cells.
3584 auto irregularKernel = [&](const VolIndex& vof) -> void {
3585 const IntVect iv = vof.gridIndex();
3586 if (ebisbox.isIrregular(iv) && validCells(iv, 0)) {
3587 if (inceptionIntegral(vof, 0) >= m_inceptionK) {
3588 const RealVect pos = probLo + Location::position(Location::Cell::Centroid, vof, ebisbox, dx);
3590 const Real E = EE.vectorLength();
3591
3592 const Real kappa = ebisbox.volFrac(vof);
3593 const Real areaFrac = ebisbox.bndryArea(vof);
3594 const Real alpha = m_alpha(E, pos);
3595 const Real eta = m_eta(E, pos);
3596 const Real k = m_detachmentRate(E, pos);
3597 const Real j = m_fieldEmission(E, pos);
3598 const Real dndt = m_backgroundRate(E, pos) + k * ionDensity(vof, 0);
3599
3600 Rdot += dndt * (1.0 - eta / alpha) * kappa * vol;
3601 Rdot += j / Units::Qe * areaFrac * area;
3602 }
3603 }
3604 };
3605
3606 // Run the kernels.
3607 Box cellBox = dbl[din];
3608 VoFIterator& vofit = (*m_amr->getVofIterator(m_realm, m_phase)[lvl])[din];
3609
3612 }
3613 }
3614
3615 return ParallelOps::sum(Rdot);
3616}
3617
3618template <typename P, typename F, typename C>
3619void
3621{
3622 CH_TIME("DischargeInceptionStepper::rewindTracerParticles");
3623 if (m_verbosity > 5) {
3624 pout() << "DischargeInceptionStepper::rewindTracerParticles" << endl;
3625 }
3626
3627 ParticleContainer<P>& amrParticles = m_tracerParticleSolver->getParticles();
3628
3629 for (int lvl = 0; lvl <= m_amr->getFinestLevel(); lvl++) {
3630 const DisjointBoxLayout& dbl = m_amr->getGrids(m_realm)[lvl];
3631 const DataIterator& dit = dbl.dataIterator();
3632
3633 const int nbox = dit.size();
3634
3635#pragma omp parallel for schedule(runtime)
3636 for (int mybox = 0; mybox < nbox; mybox++) {
3637 const DataIndex& din = dit[mybox];
3638
3639 for (ListIterator<P> lit(amrParticles[lvl][din].listItems()); lit.ok(); ++lit) {
3640 P& p = lit();
3641
3642 p.position() = p.template vect<0>();
3643 }
3644 }
3645 }
3646
3648}
3649
3650template <typename P, typename F, typename C>
3651void
3653{
3654 CH_TIME("DischargeInceptionStepper::resetTracerParticles");
3655 if (m_verbosity > 5) {
3656 pout() << "DischargeInceptionStepper::resetTracerParticles" << endl;
3657 }
3658
3659 ParticleContainer<P>& amrParticles = m_tracerParticleSolver->getParticles();
3660
3661 for (int lvl = 0; lvl <= m_amr->getFinestLevel(); lvl++) {
3662 const DisjointBoxLayout& dbl = m_amr->getGrids(m_realm)[lvl];
3663 const DataIterator& dit = dbl.dataIterator();
3664
3665 const int nbox = dit.size();
3666
3667#pragma omp parallel for schedule(runtime)
3668 for (int mybox = 0; mybox < nbox; mybox++) {
3669 const DataIndex& din = dit[mybox];
3670
3671 for (ListIterator<P> lit(amrParticles[lvl][din].listItems()); lit.ok(); ++lit) {
3672 P& p = lit();
3673
3674 p.weight() = 0.0;
3675 }
3676 }
3677 }
3678}
3679
3680template <typename P, typename F, typename C>
3681void
3683{
3684 CH_TIME("DischargeInceptionStepper::computeBackgroundIonizationStationary");
3685 if (m_verbosity > 5) {
3686 pout() << "DischargeInceptionStepper::computeBackgroundIonizationStationary" << endl;
3687 }
3688
3689 CH_assert(m_inceptionIntegralPlus.size() == m_inceptionIntegralMinu.size());
3690 CH_assert(m_inceptionIntegralPlus.size() == m_KPlusValues.size());
3691 CH_assert(m_inceptionIntegralMinu.size() == m_KMinuValues.size());
3692
3693 m_backgroundIonizationStationary.resize(0);
3694
3695 // Holds the electric field
3698
3699 // Solve ionization volume for each voltage
3700 for (size_t i = 0; i < m_voltageSweeps.size(); i++) {
3701 const Real voltage = m_voltageSweeps[i];
3702
3703 // Compute the electric field into the scratch storage
3704 this->superposition(scratch, voltage);
3705
3707
3709
3710 for (int lvl = 0; lvl <= m_amr->getFinestLevel(); lvl++) {
3711 const DisjointBoxLayout& dbl = m_amr->getGrids(m_realm)[lvl];
3712 const DataIterator& dit = dbl.dataIterator();
3713 const EBISLayout& ebisl = m_amr->getEBISLayout(m_realm, m_phase)[lvl];
3714 const Real dx = m_amr->getDx()[lvl];
3715 const RealVect probLo = m_amr->getProbLo();
3716
3717 const int nbox = dit.size();
3718
3719#pragma omp parallel for schedule(runtime)
3720 for (int mybox = 0; mybox < nbox; mybox++) {
3721 const DataIndex& din = dit[mybox];
3722
3723 const EBISBox& ebisbox = ebisl[din];
3724
3726 FArrayBox& bgIonizationReg = bgIonization.getFArrayBox();
3727
3728 const EBCellFAB& electricField = (*scratch[lvl])[din];
3729 const FArrayBox& electricFieldReg = electricField.getFArrayBox();
3730
3731 auto regularKernel = [&](const IntVect& iv) -> void {
3732 const RealVect pos = probLo + (0.5 * RealVect::Unit + RealVect(iv)) * dx;
3733 const RealVect EE = RealVect(
3735 const Real E = EE.vectorLength();
3736
3737 bgIonizationReg(iv, 0) = m_backgroundRate(E, pos);
3738 };
3739
3740 auto irregularKernel = [&](const VolIndex& vof) -> void {
3741 const RealVect pos = probLo + Location::position(Location::Cell::Centroid, vof, ebisbox, dx);
3743 const Real E = EE.vectorLength();
3744
3745 bgIonization(vof, 0) = m_backgroundRate(E, pos);
3746 };
3747
3748 // Execute kernels
3749 VoFIterator& vofit = (*m_amr->getVofIterator(m_realm, m_phase)[lvl])[din];
3750
3753 }
3754 }
3755
3756 m_backgroundIonizationStationary.push_back(backgroundRate);
3757 }
3758}
3759
3760template <typename P, typename F, typename C>
3761void
3763{
3764 CH_TIME("DischargeInceptionStepper::computeDetachmentStationary");
3765 if (m_verbosity > 5) {
3766 pout() << "DischargeInceptionStepper::computeDetachmentStationary" << endl;
3767 }
3768
3769 CH_assert(m_inceptionIntegralPlus.size() == m_inceptionIntegralMinu.size());
3770 CH_assert(m_inceptionIntegralPlus.size() == m_KPlusValues.size());
3771 CH_assert(m_inceptionIntegralMinu.size() == m_KMinuValues.size());
3772
3773 m_detachmentStationary.resize(0);
3774
3775 // Holds the electric field
3778
3779 // Solve ionization volume for each voltage
3780 for (size_t i = 0; i < m_voltageSweeps.size(); i++) {
3781 const Real voltage = m_voltageSweeps[i];
3782
3783 // Compute the electric field into the scratch storage
3784 this->superposition(scratch, voltage);
3785
3787
3789
3790 for (int lvl = 0; lvl <= m_amr->getFinestLevel(); lvl++) {
3791 const DisjointBoxLayout& dbl = m_amr->getGrids(m_realm)[lvl];
3792 const DataIterator& dit = dbl.dataIterator();
3793 const EBISLayout& ebisl = m_amr->getEBISLayout(m_realm, m_phase)[lvl];
3794 const Real dx = m_amr->getDx()[lvl];
3795 const RealVect probLo = m_amr->getProbLo();
3796
3797 const int nbox = dit.size();
3798
3799#pragma omp parallel for schedule(runtime)
3800 for (int mybox = 0; mybox < nbox; mybox++) {
3801 const DataIndex& din = dit[mybox];
3802
3803 const EBISBox& ebisbox = ebisl[din];
3804
3806 FArrayBox& detachmentReg = detachment.getFArrayBox();
3807
3808 const EBCellFAB& ionDensity = (*m_ionSolver->getPhi()[lvl])[din];
3809 const EBCellFAB& electricField = (*scratch[lvl])[din];
3810
3811 const FArrayBox& ionDensityReg = ionDensity.getFArrayBox();
3812 const FArrayBox& electricFieldReg = electricField.getFArrayBox();
3813
3814 auto regularKernel = [&](const IntVect& iv) -> void {
3815 const RealVect pos = probLo + (0.5 * RealVect::Unit + RealVect(iv)) * dx;
3816 const RealVect EE = RealVect(
3818 const Real E = EE.vectorLength();
3819 const Real phi = ionDensityReg(iv, 0);
3820
3821 detachmentReg(iv, 0) = m_detachmentRate(E, pos) * phi;
3822 };
3823
3824 auto irregularKernel = [&](const VolIndex& vof) -> void {
3825 const RealVect pos = probLo + Location::position(Location::Cell::Centroid, vof, ebisbox, dx);
3827 const Real E = EE.vectorLength();
3828 const Real phi = ionDensity(vof, 0);
3829
3830 detachment(vof, 0) = m_detachmentRate(E, pos) * phi;
3831 };
3832
3833 // Execute kernels
3834 VoFIterator& vofit = (*m_amr->getVofIterator(m_realm, m_phase)[lvl])[din];
3835
3838 }
3839 }
3840
3841 m_amr->conservativeAverage(detachmentRate, m_realm, m_phase);
3842 m_amr->interpGhost(detachmentRate, m_realm, m_phase);
3843
3844 m_detachmentStationary.push_back(detachmentRate);
3845 }
3846}
3847
3848template <typename P, typename F, typename C>
3849void
3851{
3852 CH_TIME("DischargeInceptionStepper::computeFieldEmissionStationary");
3853 if (m_verbosity > 5) {
3854 pout() << "DischargeInceptionStepper::computeFieldEmissionStationary" << endl;
3855 }
3856
3857 CH_assert(m_inceptionIntegralPlus.size() == m_inceptionIntegralMinu.size());
3858 CH_assert(m_inceptionIntegralPlus.size() == m_KPlusValues.size());
3859 CH_assert(m_inceptionIntegralMinu.size() == m_KMinuValues.size());
3860
3861 m_emissionRatesPlus.resize(0);
3862 m_emissionRatesMinu.resize(0);
3863
3864 const int numVoltages = m_inceptionIntegralPlus.size();
3865
3866 // Holds the electric field
3869
3872
3873 for (size_t i = 0; i < m_voltageSweeps.size(); i++) {
3874 const Real voltage = m_voltageSweeps[i];
3875
3876 // Compute the electric field into the scratch storage
3877 this->superposition(scratchPlus, +voltage);
3878 this->superposition(scratchMinu, -voltage);
3879
3882
3885
3888
3889 for (int lvl = 0; lvl <= m_amr->getFinestLevel(); lvl++) {
3890 const DisjointBoxLayout& dbl = m_amr->getGrids(m_realm)[lvl];
3891 const DataIterator& dit = dbl.dataIterator();
3892 const EBISLayout& ebisl = m_amr->getEBISLayout(m_realm, m_phase)[lvl];
3893 const Real& dx = m_amr->getDx()[lvl];
3894 const RealVect probLo = m_amr->getProbLo();
3895
3896 const int nbox = dit.size();
3897
3898#pragma omp parallel for schedule(runtime)
3899 for (int mybox = 0; mybox < nbox; mybox++) {
3900 const DataIndex& din = dit[mybox];
3901
3902 const EBISBox& ebisbox = ebisl[din];
3903
3904 // Here, emissionPlus is when we have the standard voltage (V = 1) and
3905 // emissionMinu is when the voltage is reverted.
3908
3909 emissionPlus.setVal(0.0);
3910 emissionMinu.setVal(0.0);
3911
3914
3915 auto irregularKernel = [&](const VolIndex& vof) -> void {
3916 const RealVect pos = probLo + Location::position(Location::Cell::Centroid, vof, ebisbox, dx);
3917
3918 const RealVect Eplus = RealVect(
3920 const RealVect Eminu = RealVect(
3922
3923 const Real normalEplus = Eplus.dotProduct(ebisbox.normal(vof));
3924 const Real normalEminu = Eminu.dotProduct(ebisbox.normal(vof));
3925
3926 emissionPlus(vof, 0) = 0.0;
3927 emissionMinu(vof, 0) = 0.0;
3928
3929 if (normalEplus < 0.0) {
3930 emissionPlus(vof, 0) = m_fieldEmission(Eplus.vectorLength(), pos);
3931 }
3932 if (normalEminu < 0.0) {
3933 emissionMinu(vof, 0) = m_fieldEmission(Eminu.vectorLength(), pos);
3934 }
3935 };
3936
3937 // Execute kernels
3938 VoFIterator& vofit = (*m_amr->getVofIterator(m_realm, m_phase)[lvl])[din];
3940 }
3941 }
3942
3943 m_emissionRatesPlus.push_back(emissionRatesPlus);
3944 m_emissionRatesMinu.push_back(emissionRatesMinu);
3945 }
3946}
3947
3948template <typename P, typename F, typename C>
3949void
3951 const Real& a_voltage) const noexcept
3952{
3953 CH_TIME("DischargeInceptionStepper::computeFieldEmission");
3954 if (m_verbosity > 5) {
3955 pout() << "DischargeInceptionStepper::computeFieldEmission" << endl;
3956 }
3957
3958 CH_assert(a_emissionRate[0]->nComp() == 1);
3959
3960 const RealVect probLo = m_amr->getProbLo();
3961
3962 // Compute the electric field into the scratch storage
3964 m_amr->allocate(scratch, m_realm, m_phase, SpaceDim);
3965 this->superposition(scratch, a_voltage);
3966
3967 for (int lvl = 0; lvl <= m_amr->getFinestLevel(); lvl++) {
3968 const DisjointBoxLayout& dbl = m_amr->getGrids(m_realm)[lvl];
3969 const DataIterator& dit = dbl.dataIterator();
3970 const EBISLayout& ebisl = m_amr->getEBISLayout(m_realm, m_phase)[lvl];
3971 const Real dx = m_amr->getDx()[lvl];
3972
3973 const int nbox = dit.size();
3974
3975#pragma omp parallel for schedule(runtime)
3976 for (int mybox = 0; mybox < nbox; mybox++) {
3977 const DataIndex& din = dit[mybox];
3978
3979 const EBISBox& ebisbox = ebisl[din];
3980
3981 // Here, emissionPlus is when we have the standard voltage (V = 1) and
3982 // emissionMinu is when the voltage is reverted.
3984
3985 emission.setVal(0.0);
3986
3987 const EBCellFAB& electricField = (*scratch[lvl])[din];
3988 const FArrayBox& electricFieldReg = electricField.getFArrayBox();
3989
3990 auto irregularKernel = [&](const VolIndex& vof) -> void {
3991 const RealVect pos = probLo + Location::position(Location::Cell::Centroid, vof, ebisbox, dx);
3993 const Real E = EE.vectorLength();
3994
3995 if (EE.dotProduct(ebisbox.normal(vof)) > 0.0) {
3996 emission(vof, 0) = m_fieldEmission(E, pos);
3997 }
3998 };
3999
4000 // Execute kernels
4001 VoFIterator& vofit = (*m_amr->getVofIterator(m_realm, m_phase)[lvl])[din];
4003 }
4004 }
4005}
4006
4007template <typename P, typename F, typename C>
4008void
4011 const Real& a_voltage,
4012 const std::function<Real(const Real E, const RealVect x)>& a_func) const noexcept
4013{
4014 CH_TIME("DischargeInceptionStepper::evaluateFunction");
4015 if (m_verbosity > 5) {
4016 pout() << "DischargeInceptionStepper::evaluateFunction" << endl;
4017 }
4018
4019 for (int lvl = 0; lvl <= m_amr->getFinestLevel(); lvl++) {
4020 this->evaluateFunction(*a_data[lvl], a_voltage, a_func, lvl);
4021 }
4022}
4023
4024template <typename P, typename F, typename C>
4025void
4027 const Real& a_voltage,
4028 const std::function<Real(const Real E, const RealVect x)>& a_func,
4029 const int a_level) const noexcept
4030{
4031 CH_TIME("DischargeInceptionStepper::evaluateFunction(level)");
4032 if (m_verbosity > 5) {
4033 pout() << "DischargeInceptionStepper::evaluateFunction(level)" << endl;
4034 }
4035
4036 CH_assert(a_level >= 0);
4037 CH_assert(a_level <= m_amr->getFinestLevel());
4038
4039 // Compute the electric field
4041 m_amr->allocate(scratch, m_realm, m_phase, SpaceDim);
4042 this->superposition(scratch, a_voltage);
4043
4044 const RealVect probLo = m_amr->getProbLo();
4045
4046 const DisjointBoxLayout& dbl = m_amr->getGrids(m_realm)[a_level];
4047 const DataIterator& dit = dbl.dataIterator();
4048 const EBISLayout& ebisl = m_amr->getEBISLayout(m_realm, m_phase)[a_level];
4049 const Real dx = m_amr->getDx()[a_level];
4050
4051 const int nbox = dit.size();
4052
4053#pragma omp parallel for schedule(runtime)
4054 for (int mybox = 0; mybox < nbox; mybox++) {
4055 const DataIndex& din = dit[mybox];
4056
4057 const EBISBox& ebisbox = ebisl[din];
4058
4059 // Here, emissionPlus is when we have the standard voltage (V = 1) and
4060 // emissionMinu is when the voltage is reverted.
4062 FArrayBox& dataReg = data.getFArrayBox();
4063
4064 data.setVal(0.0);
4065
4067 const FArrayBox& electricFieldReg = electricField.getFArrayBox();
4068
4069 auto regularKernel = [&](const IntVect& iv) -> void {
4070 const RealVect pos = probLo + (0.5 * RealVect::Unit + RealVect(iv)) * dx;
4072 const Real E = EE.vectorLength();
4073
4074 dataReg(iv, 0) = a_func(E, pos);
4075 };
4076
4077 auto irregularKernel = [&](const VolIndex& vof) -> void {
4078 const RealVect pos = probLo + Location::position(Location::Cell::Centroid, vof, ebisbox, dx);
4080 const Real E = EE.vectorLength();
4081
4082 data(vof, 0) = a_func(E, pos);
4083 };
4084
4085 // Execute kernels
4086 Box cellBox = dbl[din];
4087 VoFIterator& vofit = (*m_amr->getVofIterator(m_realm, m_phase)[a_level])[din];
4088
4091 }
4092}
4093
4094template <typename P, typename F, typename C>
4095void
4097{
4098 CH_TIME("DischargeInceptionStepper::computeInceptionVoltageVolume");
4099 if (m_verbosity > 5) {
4100 pout() << "DischargeInceptionStepper::computeInceptionVoltageVolume" << endl;
4101 }
4102
4103 // TLDR: This routine runs through all the K-values in each cell and estimates the
4104 // inception voltage using linear interpolation.
4105
4106 CH_assert(m_mode == Mode::Stationary);
4107
4108 if (m_inceptionIntegralPlus.size() < 2) {
4109 DataOps::setValue(m_inceptionVoltagePlus, std::numeric_limits<Real>::quiet_NaN());
4110 DataOps::setValue(m_inceptionVoltageMinu, std::numeric_limits<Real>::quiet_NaN());
4111
4112 MayDay::Warning("DischargeInceptionStepper::computeInceptionVoltageVolume -- not enough voltages for estimating "
4113 "inception voltage");
4114 }
4115 else {
4116 constexpr int comp = 0;
4117
4118 // Function which interpolates the inception voltage if possible. Used in the kernels. This one does interpolation.
4119 auto calcUincInterp = [Kinc = this->m_inceptionK,
4120 &V = this->m_voltageSweeps](const std::vector<Real>& K,
4122 Real streamerInc = std::numeric_limits<Real>::quiet_NaN();
4123 Real townsendInc = std::numeric_limits<Real>::quiet_NaN();
4124
4125 bool foundInception = false;
4126
4127 // Streamer criterion
4128 for (size_t i = 0; i < K.size() - 1; i++) {
4129 if (K[i] <= Kinc && K[i + 1] > Kinc) {
4130 streamerInc = V[i] + (Kinc - K[i]) * (V[i + 1] - V[i]) / (K[i + 1] - K[i]);
4131
4132 break;
4133 }
4134 else if (K[i] == Kinc) {
4135 streamerInc = V[i];
4136
4137 break;
4138 }
4139 }
4140
4141 // Townsend criterion
4142 for (size_t i = 0; i < T.size() - 1; i++) {
4143 if (T[i] <= 1.0 && T[i + 1] > 1) {
4144 townsendInc = V[i] + (1.0 - T[i]) * (V[i + 1] - V[i]) / (T[i + 1] - T[i]);
4145
4146 break;
4147 }
4148 else if (T[i] == 1.0) {
4149 townsendInc = V[i];
4150
4151 break;
4152 }
4153 }
4154
4155 Real Uinc;
4156
4158 Uinc = std::numeric_limits<Real>::quiet_NaN();
4159 }
4161 Uinc = townsendInc;
4162 }
4164 Uinc = streamerInc;
4165 }
4166 else {
4167 Uinc = std::min(streamerInc, townsendInc);
4168 }
4169
4171 };
4172
4173 // Same functionality as above, but without interpolation.
4174 auto calcUincNoInterp = [Kinc = this->m_inceptionK,
4175 &V = this->m_voltageSweeps](const std::vector<Real>& K,
4177 Real streamerInc = std::numeric_limits<Real>::quiet_NaN();
4178 Real townsendInc = std::numeric_limits<Real>::quiet_NaN();
4179
4180 // Streamer criterion
4181 for (size_t i = 0; i < K.size() - 1; i++) {
4182 if (K[i] <= Kinc && K[i + 1] >= Kinc) {
4183 streamerInc = V[i];
4184
4185 break;
4186 }
4187 }
4188
4189 // Townsend criterion
4190 for (size_t i = 0; i < T.size() - 1; i++) {
4191 if (T[i] <= 1.0 && T[i + 1] >= 1) {
4192 townsendInc = V[i];
4193
4194 break;
4195 }
4196 }
4197
4198 Real Uinc;
4199
4201 Uinc = std::numeric_limits<Real>::quiet_NaN();
4202 }
4204 Uinc = townsendInc;
4205 }
4207 Uinc = streamerInc;
4208 }
4209 else {
4210 Uinc = std::min(streamerInc, townsendInc);
4211 }
4212
4214 };
4215
4216 // Iterate through m_inceptionIntegral data and calculate the inception voltage.
4217 for (int lvl = 0; lvl <= m_amr->getFinestLevel(); ++lvl) {
4218 const DisjointBoxLayout& dbl = m_amr->getGrids(m_realm)[lvl];
4219 const DataIterator& dit = dbl.dataIterator();
4220
4221 const int nbox = dit.size();
4222
4223#pragma omp parallel for schedule(runtime)
4224 for (int mybox = 0; mybox < nbox; mybox++) {
4225 const DataIndex& din = dit[mybox];
4226
4227 EBCellFAB& inceptionVoltagePlus = (*m_inceptionVoltagePlus[lvl])[din];
4228 EBCellFAB& inceptionVoltageMinu = (*m_inceptionVoltageMinu[lvl])[din];
4229 EBCellFAB& streamerInceptionVoltagePlus = (*m_streamerInceptionVoltagePlus[lvl])[din];
4230 EBCellFAB& streamerInceptionVoltageMinu = (*m_streamerInceptionVoltageMinu[lvl])[din];
4231 EBCellFAB& townsendInceptionVoltagePlus = (*m_townsendInceptionVoltagePlus[lvl])[din];
4232 EBCellFAB& townsendInceptionVoltageMinu = (*m_townsendInceptionVoltageMinu[lvl])[din];
4233
4240
4241 // Pack the inception integral and townsend criterions into workable vectors.
4246
4251
4252 for (int i = 0; i < m_voltageSweeps.size(); i++) {
4253 inceptionIntegralPlus.push_back(&(*(m_inceptionIntegralPlus[i])[lvl])[din]);
4254 inceptionIntegralMinu.push_back(&(*(m_inceptionIntegralMinu[i])[lvl])[din]);
4255 townsendCriterionPlus.push_back(&(*(m_townsendCriterionPlus[i])[lvl])[din]);
4256 townsendCriterionMinu.push_back(&(*(m_townsendCriterionMinu[i])[lvl])[din]);
4257
4258 inceptionIntegralPlusReg.push_back(&(inceptionIntegralPlus.back()->getFArrayBox()));
4259 inceptionIntegralMinuReg.push_back(&(inceptionIntegralMinu.back()->getFArrayBox()));
4260 townsendCriterionPlusReg.push_back(&(townsendCriterionPlus.back()->getFArrayBox()));
4261 townsendCriterionMinuReg.push_back(&(townsendCriterionMinu.back()->getFArrayBox()));
4262 }
4263
4264 // Regular kernel.
4265 auto regularKernel = [&](const IntVect& iv) -> void {
4268
4271
4272 for (size_t i = 0; i < m_voltageSweeps.size(); i++) {
4273 Kplus.emplace_back((*inceptionIntegralPlusReg[i])(iv, 0));
4274 Kminu.emplace_back((*inceptionIntegralMinuReg[i])(iv, 0));
4275
4276 Tplus.emplace_back((*townsendCriterionPlusReg[i])(iv, 0));
4277 Tminu.emplace_back((*townsendCriterionMinuReg[i])(iv, 0));
4278 }
4279
4282
4283 if (m_fullIntegration) {
4286 }
4287 else {
4290 }
4291
4294
4297
4300 };
4301
4302 // Irregular kernel.
4303 auto irregularKernel = [&](const VolIndex& vof) -> void {
4306
4309
4310 for (size_t i = 0; i < m_voltageSweeps.size(); i++) {
4311 Kplus.emplace_back((*inceptionIntegralPlus[i])(vof, 0));
4312 Kminu.emplace_back((*inceptionIntegralMinu[i])(vof, 0));
4313
4314 Tplus.emplace_back((*townsendCriterionPlus[i])(vof, 0));
4315 Tminu.emplace_back((*townsendCriterionMinu[i])(vof, 0));
4316 }
4317
4320
4321 if (m_fullIntegration) {
4324 }
4325 else {
4328 }
4329
4332
4335
4338 };
4339
4340 // Kernel regions.
4341 const Box& cellBox = dbl[din];
4342 VoFIterator& vofit = (*m_amr->getVofIterator(m_realm, m_phase)[lvl])[din];
4343
4346 }
4347 }
4348
4349 // Coarsen data.
4350 m_amr->conservativeAverage(m_inceptionVoltagePlus, m_realm, m_phase);
4351 m_amr->interpGhost(m_inceptionVoltageMinu, m_realm, m_phase);
4352 }
4353}
4354
4355template <typename P, typename F, typename C>
4358{
4359 CH_TIME("DischargeInceptionStepper::computeMinimumInceptionVoltage");
4360 if (m_verbosity > 5) {
4361 pout() << "DischargeInceptionStepper::computeMinimumInceptionVoltage" << endl;
4362 }
4363
4364 const RealVect probLo = m_amr->getProbLo();
4365
4367
4368 UxInc.first = std::numeric_limits<Real>::max();
4369 UxInc.second = RealVect::Zero;
4370
4371 for (int lvl = 0; lvl <= m_amr->getFinestLevel(); lvl++) {
4372 const DisjointBoxLayout& dbl = m_amr->getGrids(m_realm)[lvl];
4373 const DataIterator& dit = dbl.dataIterator();
4374 const EBISLayout& ebisl = m_amr->getEBISLayout(m_realm, m_phase)[lvl];
4375 const Real& dx = m_amr->getDx()[lvl];
4376
4377 const int nbox = dit.size();
4378
4379#pragma omp parallel for schedule(runtime) reduction(pairmin : UxInc)
4380 for (int mybox = 0; mybox < nbox; mybox++) {
4381 const DataIndex& din = dit[mybox];
4382
4383 const BaseFab<bool>& validCells = (*m_amr->getValidCells(m_realm)[lvl])[din];
4384 const EBISBox& ebisBox = ebisl[din];
4385
4386 const EBCellFAB& voltage = (*a_Uinc[lvl])[din];
4387 const FArrayBox& voltageReg = voltage.getFArrayBox();
4388
4389 auto regularKernel = [&](const IntVect& iv) -> void {
4390 if (validCells(iv, 0) && ebisBox.isRegular(iv)) {
4391 const Real& U = voltageReg(iv, 0);
4392 const RealVect pos = probLo + (0.5 * RealVect::Unit + RealVect(iv)) * dx;
4393
4394 if (!(std::isnan(U))) {
4395 if (std::abs(U) < UxInc.first) {
4396 UxInc.first = std::abs(U);
4397 UxInc.second = pos;
4398 }
4399 }
4400 }
4401 };
4402
4403 auto irregularKernel = [&](const VolIndex& vof) -> void {
4404 const IntVect iv = vof.gridIndex();
4405 if (validCells(iv, 0) && ebisBox.isIrregular(iv)) {
4406 const Real& U = voltage(vof, 0);
4407 const RealVect centroid = ebisBox.centroid(vof);
4408 const RealVect pos = probLo + (0.5 * RealVect::Unit + RealVect(iv) + centroid) * dx;
4409
4410 if (!(std::isnan(U))) {
4411 if (std::abs(U) < UxInc.first) {
4412 UxInc.first = std::abs(U);
4413 UxInc.second = pos;
4414 }
4415 }
4416 }
4417 };
4418
4419 Box cellBox = dbl[din];
4420 VoFIterator& vofit = (*m_amr->getVofIterator(m_realm, m_phase)[lvl])[din];
4421
4424 }
4425 }
4426
4427 return ParallelOps::min(UxInc.first, UxInc.second);
4428}
4429
4430template <typename P, typename F, typename C>
4431void
4433{
4434 CH_TIME("DischargeInceptionStepper::computeCriticalVolumeStationary");
4435 if (m_verbosity > 5) {
4436 pout() << "DischargeInceptionStepper::computeCriticalVolumeStationary" << endl;
4437 }
4438
4439 CH_assert(m_inceptionIntegralPlus.size() == m_inceptionIntegralMinu.size());
4440 CH_assert(m_inceptionIntegralPlus.size() == m_KPlusValues.size());
4441 CH_assert(m_inceptionIntegralMinu.size() == m_KMinuValues.size());
4442
4443 m_criticalVolumePlus.resize(0);
4444 m_criticalVolumeMinu.resize(0);
4445
4446 const int numVoltages = m_inceptionIntegralPlus.size();
4447
4448 // Solve critical volume of K values for each voltage
4449 for (size_t i = 0; i < numVoltages; i++) {
4452
4453 for (int lvl = 0; lvl <= m_amr->getFinestLevel(); ++lvl) {
4454 const DisjointBoxLayout& dbl = m_amr->getGrids(m_realm)[lvl];
4455 const DataIterator& dit = dbl.dataIterator();
4456 const EBISLayout& ebisl = m_amr->getEBISLayout(m_realm, m_phase)[lvl];
4457
4458 const LevelData<BaseFab<bool>>& validCellsLD = *m_amr->getValidCells(m_realm)[lvl];
4459
4460 const Real dx = m_amr->getDx()[lvl];
4461
4462 const int nbox = dit.size();
4463
4464#pragma omp parallel for schedule(runtime) reduction(+ : criticalVolumePlus, criticalVolumeMinu)
4465 for (int mybox = 0; mybox < nbox; mybox++) {
4466 const DataIndex& din = dit[mybox];
4467
4468 const EBISBox& ebisbox = ebisl[din];
4470
4471 const EBCellFAB& inceptionIntegralPlus = (*(m_inceptionIntegralPlus[i])[lvl])[din];
4472 const EBCellFAB& inceptionIntegralMinu = (*(m_inceptionIntegralMinu[i])[lvl])[din];
4473
4476
4477 const EBCellFAB& townsendCritPlus = (*(m_townsendCriterionPlus[i])[lvl])[din];
4478 const EBCellFAB& townsendCritMinu = (*(m_townsendCriterionMinu[i])[lvl])[din];
4479
4480 const FArrayBox& townsendCritPlusReg = townsendCritPlus.getFArrayBox();
4481 const FArrayBox& townsendCritMinuReg = townsendCritMinu.getFArrayBox();
4482
4483 auto regularKernel = [&](const IntVect& iv) -> void {
4484 if (validCells(iv, 0) && ebisbox.isRegular(iv)) {
4485 if (inceptionIntegralPlusReg(iv, 0) >= m_inceptionK || townsendCritPlusReg(iv, 0) >= 1.0) {
4486 criticalVolumePlus += std::pow(dx, SpaceDim);
4487 }
4488 if (inceptionIntegralMinuReg(iv, 0) >= m_inceptionK || townsendCritMinuReg(iv, 0) >= 1.0) {
4489 criticalVolumeMinu += std::pow(dx, SpaceDim);
4490 }
4491 }
4492 };
4493
4494 auto irregularKernel = [&](const VolIndex& vof) -> void {
4495 if (validCells(vof.gridIndex())) {
4496 const Real kappa = ebisbox.volFrac(vof);
4497
4498 if (inceptionIntegralPlus(vof, 0) >= m_inceptionK || townsendCritPlus(vof, 0) >= 1.0) {
4499 criticalVolumePlus += kappa * std::pow(dx, SpaceDim);
4500 }
4501 if (inceptionIntegralMinu(vof, 0) >= m_inceptionK || townsendCritMinu(vof, 0) >= 1.0) {
4502 criticalVolumeMinu += kappa * std::pow(dx, SpaceDim);
4503 }
4504 }
4505 };
4506
4507 // Kernel regions.
4508 const Box cellBox = dbl[din];
4509 VoFIterator& vofit = (*m_amr->getVofIterator(m_realm, m_phase)[lvl])[din];
4510
4513 }
4514 }
4515
4516 m_criticalVolumePlus.push_back(ParallelOps::sum(criticalVolumePlus));
4517 m_criticalVolumeMinu.push_back(ParallelOps::sum(criticalVolumeMinu));
4518 }
4519}
4520
4521template <typename P, typename F, typename C>
4522Real
4524{
4525 CH_TIME("DischargeInceptionStepper::computeCriticalVolumeTransient");
4526 if (m_verbosity > 5) {
4527 pout() << "DischargeInceptionStepper::computeCriticalVolumeTransient" << endl;
4528 }
4529
4530 Real Vcr = 0.0;
4531
4532 for (int lvl = 0; lvl <= m_amr->getFinestLevel(); ++lvl) {
4533 const DisjointBoxLayout& dbl = m_amr->getGrids(m_realm)[lvl];
4534 const DataIterator& dit = dbl.dataIterator();
4535 const EBISLayout& ebisl = m_amr->getEBISLayout(m_realm, m_phase)[lvl];
4536
4537 const LevelData<BaseFab<bool>>& validCellsLD = *m_amr->getValidCells(m_realm)[lvl];
4538
4539 const Real dx = m_amr->getDx()[lvl];
4540 const Real vol = std::pow(dx, SpaceDim);
4541
4542 const int nbox = dit.size();
4543
4544#pragma omp parallel for schedule(runtime) reduction(+ : Vcr)
4545 for (int mybox = 0; mybox < nbox; mybox++) {
4546 const DataIndex& din = dit[mybox];
4547
4548 const EBISBox& ebisbox = ebisl[din];
4550
4551 const EBCellFAB& inceptionIntegral = (*m_inceptionIntegral[lvl])[din];
4552 const FArrayBox& inceptionIntegralReg = inceptionIntegral.getFArrayBox();
4553
4554 const EBCellFAB& townsendCriterion = (*m_townsendCriterion[lvl])[din];
4555 const FArrayBox& townsendCriterionReg = townsendCriterion.getFArrayBox();
4556
4557 auto regularKernel = [&](const IntVect& iv) -> void {
4558 if (validCells(iv, 0) && ebisbox.isRegular(iv)) {
4559 if (inceptionIntegralReg(iv, 0) >= m_inceptionK || townsendCriterionReg(iv, 0) >= 1.0) {
4560 Vcr += vol;
4561 }
4562 }
4563 };
4564
4565 auto irregularKernel = [&](const VolIndex& vof) -> void {
4566 if (validCells(vof.gridIndex())) {
4567 if (inceptionIntegral(vof, 0) >= m_inceptionK || townsendCriterion(vof, 0) >= 1.0) {
4568 Vcr += ebisbox.volFrac(vof) * vol;
4569 }
4570 }
4571 };
4572
4573 // Kernel regions.
4574 const Box cellBox = dbl[din];
4575 VoFIterator& vofit = (*m_amr->getVofIterator(m_realm, m_phase)[lvl])[din];
4576
4579 }
4580 }
4581
4582 return ParallelOps::sum(Vcr);
4583}
4584
4585template <typename P, typename F, typename C>
4586void
4588{
4589 CH_TIME("DischargeInceptionStepper::computeCriticalAreaStationary");
4590 if (m_verbosity > 5) {
4591 pout() << "DischargeInceptionStepper::computeCriticalAreaStationary" << endl;
4592 }
4593
4594 CH_assert(m_inceptionIntegralPlus.size() == m_inceptionIntegralMinu.size());
4595 CH_assert(m_inceptionIntegralPlus.size() == m_KPlusValues.size());
4596 CH_assert(m_inceptionIntegralMinu.size() == m_KMinuValues.size());
4597
4598 m_criticalAreaPlus.resize(0);
4599 m_criticalAreaMinu.resize(0);
4600
4601 const int numVoltages = m_inceptionIntegralPlus.size();
4602
4603 // Solve critical area of K values for each voltage
4604 for (size_t i = 0; i < numVoltages; i++) {
4605 Real criticalAreaPlus = 0.0;
4606 Real criticalAreaMinu = 0.0;
4607
4608 for (int lvl = 0; lvl <= m_amr->getFinestLevel(); ++lvl) {
4609 const DisjointBoxLayout& dbl = m_amr->getGrids(m_realm)[lvl];
4610 const DataIterator& dit = dbl.dataIterator();
4611 const EBISLayout& ebisl = m_amr->getEBISLayout(m_realm, m_phase)[lvl];
4612
4613 const LevelData<BaseFab<bool>>& validCellsLD = *m_amr->getValidCells(m_realm)[lvl];
4614
4615 const Real dx = m_amr->getDx()[lvl];
4616
4617 const int nbox = dit.size();
4618
4619#pragma omp parallel for schedule(runtime) reduction(+ : criticalAreaPlus, criticalAreaMinu)
4620 for (int mybox = 0; mybox < nbox; mybox++) {
4621 const DataIndex& din = dit[mybox];
4622
4623 const EBISBox& ebisbox = ebisl[din];
4625
4626 const EBCellFAB& inceptionIntegralPlus = (*(m_inceptionIntegralPlus[i])[lvl])[din];
4627 const EBCellFAB& inceptionIntegralMinu = (*(m_inceptionIntegralMinu[i])[lvl])[din];
4628
4629 const EBCellFAB& townsendCriterionPlus = (*(m_townsendCriterionPlus[i])[lvl])[din];
4630 const EBCellFAB& townsendCriterionMinu = (*(m_townsendCriterionMinu[i])[lvl])[din];
4631
4632 auto irregularKernel = [&](const VolIndex& vof) -> void {
4633 if (validCells(vof.gridIndex())) {
4634 const Real boundaryArea = ebisbox.bndryArea(vof);
4635
4636 if (inceptionIntegralPlus(vof, 0) >= m_inceptionK || townsendCriterionPlus(vof, 0) >= 1.0) {
4637 criticalAreaPlus += boundaryArea * std::pow(dx, SpaceDim - 1);
4638 }
4639 if (inceptionIntegralMinu(vof, 0) >= m_inceptionK || townsendCriterionMinu(vof, 0) >= 1.0) {
4640 criticalAreaMinu += boundaryArea * std::pow(dx, SpaceDim - 1);
4641 }
4642 }
4643 };
4644
4645 // Kernel regions.
4646 VoFIterator& vofit = (*m_amr->getVofIterator(m_realm, m_phase)[lvl])[din];
4647
4649 }
4650 }
4651
4652 m_criticalAreaPlus.push_back(ParallelOps::sum(criticalAreaPlus));
4653 m_criticalAreaMinu.push_back(ParallelOps::sum(criticalAreaMinu));
4654 }
4655}
4656
4657template <typename P, typename F, typename C>
4658Real
4660{
4661 CH_TIME("DischargeInceptionStepper::computeCriticalAreaTransient");
4662 if (m_verbosity > 5) {
4663 pout() << "DischargeInceptionStepper::computeCriticalAreaTransient" << endl;
4664 }
4665
4666 Real Acr = 0.0;
4667
4668 for (int lvl = 0; lvl <= m_amr->getFinestLevel(); ++lvl) {
4669 const DisjointBoxLayout& dbl = m_amr->getGrids(m_realm)[lvl];
4670 const DataIterator& dit = dbl.dataIterator();
4671 const EBISLayout& ebisl = m_amr->getEBISLayout(m_realm, m_phase)[lvl];
4672
4673 const LevelData<BaseFab<bool>>& validCellsLD = *m_amr->getValidCells(m_realm)[lvl];
4674
4675 const Real dx = m_amr->getDx()[lvl];
4676 const Real area = std::pow(dx, SpaceDim - 1);
4677
4678 const int nbox = dit.size();
4679
4680#pragma omp parallel for schedule(runtime) reduction(+ : Acr)
4681 for (int mybox = 0; mybox < nbox; mybox++) {
4682 const DataIndex& din = dit[mybox];
4683
4684 const EBISBox& ebisbox = ebisl[din];
4686
4687 const EBCellFAB& inceptionIntegral = (*m_inceptionIntegral[lvl])[din];
4688 const EBCellFAB& townsendCriterion = (*m_townsendCriterion[lvl])[din];
4689
4690 auto irregularKernel = [&](const VolIndex& vof) -> void {
4691 if (validCells(vof.gridIndex())) {
4692 if (inceptionIntegral(vof, 0) >= m_inceptionK || townsendCriterion(vof, 0) >= 1.0) {
4693 Acr += ebisbox.bndryArea(vof) * area;
4694 }
4695 }
4696 };
4697
4698 // Kernel regions.
4699 VoFIterator& vofit = (*m_amr->getVofIterator(m_realm, m_phase)[lvl])[din];
4700
4702 }
4703 }
4704
4705 return ParallelOps::sum(Acr);
4706}
4707
4708template <typename P, typename F, typename C>
4709void
4711{
4712 CH_TIME("DischargeInceptionStepper::computeIonizationVolumeStationary");
4713 if (m_verbosity > 5) {
4714 pout() << "DischargeInceptionStepper::computeIonizationVolumeStationary" << endl;
4715 }
4716
4717 CH_assert(m_inceptionIntegralPlus.size() == m_inceptionIntegralMinu.size());
4718 CH_assert(m_inceptionIntegralPlus.size() == m_KPlusValues.size());
4719 CH_assert(m_inceptionIntegralMinu.size() == m_KMinuValues.size());
4720
4721 m_ionizationVolume.resize(0);
4722
4723 const int numVoltages = m_inceptionIntegralPlus.size();
4724
4725 // Storage something that holds the electric filed
4728
4729 // Solve ionization volume for each voltage
4730 for (size_t i = 0; i < m_voltageSweeps.size(); i++) {
4731 const Real voltage = m_voltageSweeps[i];
4732
4733 Real ionizationVolume = 0.0;
4734
4735 this->superposition(scratch, voltage);
4736
4737 for (int lvl = 0; lvl <= m_amr->getFinestLevel(); ++lvl) {
4738 const DisjointBoxLayout& dbl = m_amr->getGrids(m_realm)[lvl];
4739 const DataIterator& dit = dbl.dataIterator();
4740 const EBISLayout& ebisl = m_amr->getEBISLayout(m_realm, m_phase)[lvl];
4741
4742 const LevelData<BaseFab<bool>>& validCellsLD = *m_amr->getValidCells(m_realm)[lvl];
4743
4744 const Real dx = m_amr->getDx()[lvl];
4745 const RealVect probLo = m_amr->getProbLo();
4746
4747 const int nbox = dit.size();
4748
4749#pragma omp parallel for schedule(runtime) reduction(+ : ionizationVolume)
4750 for (int mybox = 0; mybox < nbox; mybox++) {
4751 const DataIndex& din = dit[mybox];
4752
4753 const EBISBox& ebisbox = ebisl[din];
4755
4756 const EBCellFAB& electricField = (*scratch[lvl])[din];
4757 const FArrayBox& electricFieldReg = electricField.getFArrayBox();
4758
4759 auto regularKernel = [&](const IntVect& iv) -> void {
4760 if (validCells(iv, 0) && ebisbox.isRegular(iv)) {
4761 const RealVect x = probLo + dx * (0.5 * RealVect::Unit + RealVect(iv));
4762 const RealVect EE = RealVect(
4764 const Real E = EE.vectorLength();
4765
4766 const Real alpha = m_alpha(E, x);
4767 const Real eta = m_eta(E, x);
4768
4769 if (alpha >= eta) {
4770 ionizationVolume += std::pow(dx, SpaceDim);
4771 }
4772 }
4773 };
4774
4775 auto irregularKernel = [&](const VolIndex& vof) -> void {
4776 if (validCells(vof.gridIndex())) {
4777
4778 const RealVect x = probLo + Location::position(Location::Cell::Center, vof, ebisbox, dx);
4780 const Real E = EE.vectorLength();
4781
4782 const Real alpha = m_alpha(E, x);
4783 const Real eta = m_eta(E, x);
4784
4785 const Real kappa = ebisbox.volFrac(vof);
4786
4787 if (alpha >= eta) {
4788 ionizationVolume += kappa * std::pow(dx, SpaceDim);
4789 }
4790 }
4791 };
4792
4793 // Kernel regions.
4794 const Box cellBox = dbl[din];
4795 VoFIterator& vofit = (*m_amr->getVofIterator(m_realm, m_phase)[lvl])[din];
4796
4799 }
4800 }
4801
4802 m_ionizationVolume.push_back(ParallelOps::sum(ionizationVolume));
4803 }
4804}
4805
4806template <typename P, typename F, typename C>
4807Real
4809{
4810 CH_TIME("DischargeInceptionStepper::computeIonizationVolumeTransient");
4811 if (m_verbosity > 5) {
4812 pout() << "DischargeInceptionStepper::computeIonizationVolumeTransient" << endl;
4813 }
4814
4815 Real Vion = 0.0;
4816
4817 // Calculate electric field
4819 m_amr->allocate(scratch, m_realm, m_phase, SpaceDim);
4820 this->superposition(scratch, a_voltage);
4821
4822 for (int lvl = 0; lvl <= m_amr->getFinestLevel(); ++lvl) {
4823 const DisjointBoxLayout& dbl = m_amr->getGrids(m_realm)[lvl];
4824 const DataIterator& dit = dbl.dataIterator();
4825 const EBISLayout& ebisl = m_amr->getEBISLayout(m_realm, m_phase)[lvl];
4826
4827 const LevelData<BaseFab<bool>>& validCellsLD = *m_amr->getValidCells(m_realm)[lvl];
4828
4829 const Real dx = m_amr->getDx()[lvl];
4830 const Real vol = std::pow(dx, SpaceDim);
4831 const RealVect probLo = m_amr->getProbLo();
4832
4833 const int nbox = dit.size();
4834
4835#pragma omp parallel for schedule(runtime) reduction(+ : Vion)
4836 for (int mybox = 0; mybox < nbox; mybox++) {
4837 const DataIndex& din = dit[mybox];
4838
4839 const EBISBox& ebisbox = ebisl[din];
4841
4842 const EBCellFAB& electricField = (*scratch[lvl])[din];
4843 const FArrayBox& electricFieldReg = electricField.getFArrayBox();
4844
4845 auto regularKernel = [&](const IntVect& iv) -> void {
4846 if (validCells(iv, 0) && ebisbox.isRegular(iv)) {
4847
4848 const RealVect x = probLo + dx * (0.5 * RealVect::Unit + RealVect(iv));
4849 const RealVect EE = RealVect(
4851 const Real E = EE.vectorLength();
4852
4853 const Real alpha = m_alpha(E, x);
4854 const Real eta = m_eta(E, x);
4855 if (alpha >= eta) {
4856 Vion += vol;
4857 }
4858 }
4859 };
4860
4861 auto irregularKernel = [&](const VolIndex& vof) -> void {
4862 if (validCells(vof.gridIndex())) {
4863
4864 const RealVect x = probLo + Location::position(Location::Cell::Centroid, vof, ebisbox, dx);
4866 const Real E = EE.vectorLength();
4867
4868 const Real alpha = m_alpha(E, x);
4869 const Real eta = m_eta(E, x);
4870 if (alpha >= eta) {
4871 Vion += ebisbox.volFrac(vof) * vol;
4872 }
4873 }
4874 };
4875
4876 // Kernel regions.
4877 const Box cellBox = dbl[din];
4878 VoFIterator& vofit = (*m_amr->getVofIterator(m_realm, m_phase)[lvl])[din];
4879
4882 }
4883 }
4884
4885 return ParallelOps::sum(Vion);
4886}
4887
4888template <typename P, typename F, typename C>
4889void
4891{
4892 CH_TIME("DischargeInceptionStepper::writeReportStationary");
4893 if (m_verbosity > 5) {
4894 pout() << "DischargeInceptionStepper::writeReportStationary" << endl;
4895 }
4896
4897 const auto UIncPlus = this->computeMinimumInceptionVoltage(m_inceptionVoltagePlus);
4898 const auto UIncMinu = this->computeMinimumInceptionVoltage(m_inceptionVoltageMinu);
4899
4900 const auto streamerUIncPlus = this->computeMinimumInceptionVoltage(m_streamerInceptionVoltagePlus);
4901 const auto streamerUIncMinu = this->computeMinimumInceptionVoltage(m_streamerInceptionVoltageMinu);
4902
4903 const auto townsendUIncPlus = this->computeMinimumInceptionVoltage(m_townsendInceptionVoltagePlus);
4904 const auto townsendUIncMinu = this->computeMinimumInceptionVoltage(m_townsendInceptionVoltageMinu);
4905
4906#ifdef CH_MPI
4907 if (procID() == 0) {
4908#endif
4909
4910 std::ofstream output(m_outputFile, std::ofstream::out);
4911
4912 const int ww = (SpaceDim == 2) ? 24 : 36;
4913
4914 const std::string lineBreak = "# " + std::string(178 + 4 * ww, '=');
4915
4916 // clang-format off
4917 output << lineBreak << "\n";
4918 if (std::isnan(UIncPlus.first) || std::isnan(UIncMinu.first)) {
4919 output << "# Could not compute inception voltage\n";
4920 }
4921 else {
4922 if(m_fullIntegration){
4923 output << "# Minimum inception voltage(+) = " << UIncPlus.first << ",\t x = " << UIncPlus.second << "\n";
4924 output << "# Minimum inception voltage(-) = " << UIncMinu.first << ",\t x = " << UIncMinu.second << "\n";
4925 output << "# \n";
4926 output << "# Streamer inception voltage(+) = " << streamerUIncPlus.first << ",\t x = " << streamerUIncPlus.second << "\n";
4927 output << "# Streamer inception voltage(-) = " << streamerUIncMinu.first << ",\t x = " << streamerUIncMinu.second << "\n";
4928 output << "# \n";
4929 output << "# Townsend inception voltage(+) = " << townsendUIncPlus.first << ",\t x = " << townsendUIncPlus.second << "\n";
4930 output << "# Townsend inception voltage(-) = " << townsendUIncMinu.first << ",\t x = " << townsendUIncMinu.second << "\n";
4931 }
4932 else{
4933 output << "# Minimum inception voltage(+) >= " << UIncPlus.first << ",\t x = " << UIncPlus.second << "\n";
4934 output << "# Minimum inception voltage(-) >= " << UIncMinu.first << ",\t x = " << UIncMinu.second << "\n";
4935 output << "# \n";
4936 output << "# Streamer inception voltage(+) >= " << streamerUIncPlus.first << ",\t x = " << streamerUIncPlus.second << "\n";
4937 output << "# Streamer inception voltage(-) >= " << streamerUIncMinu.first << ",\t x = " << streamerUIncMinu.second << "\n";
4938 output << "# \n";
4939 output << "# Townsend inception voltage(+) >= " << townsendUIncPlus.first << ",\t x = " << townsendUIncPlus.second << "\n";
4940 output << "# Townsend inception voltage(-) >= " << townsendUIncMinu.first << ",\t x = " << townsendUIncMinu.second << "\n";
4941 }
4942 }
4943
4944 output << lineBreak << "\n";
4945 output << left << setw(15) << setfill(' ') << "# +/- Voltage";
4946 output << left << setw(15) << setfill(' ') << "Max K(+)";
4947 output << left << setw(15) << setfill(' ') << "Max K(-)";
4948 output << left << setw(ww) << setfill(' ') << "Pos. max K(+)";
4949 output << left << setw(ww) << setfill(' ') << "Pos. max K(-)";
4950 output << left << setw(20) << setfill(' ') << "Max T(+)";
4951 output << left << setw(20) << setfill(' ') << "Max T(-)";
4952 output << left << setw(ww) << setfill(' ') << "Pos. max T(+)";
4953 output << left << setw(ww) << setfill(' ') << "Pos. max T(-)";
4954 output << left << setw(20) << setfill(' ') << "Crit. vol(+)";
4955 output << left << setw(20) << setfill(' ') << "Crit. vol(-)";
4956 output << left << setw(20) << setfill(' ') << "Crit. area(+)";
4957 output << left << setw(20) << setfill(' ') << "Crit. area(-)";
4958 output << left << setw(20) << setfill(' ') << "Ionization vol." << "\n";
4959 output << lineBreak << "\n";
4960
4961 auto RealVectToString = [=](const RealVect x) -> std::string {
4962 std::string ret = "(";
4963 for (int dir = 0; dir < SpaceDim; dir++) {
4964 ret += std::to_string(x[dir]);
4965 if(dir < SpaceDim -1) {
4966 ret += ", ";
4967 }
4968 }
4969
4970 ret += ")";
4971
4972 return ret;
4973 };
4974
4975 for (int i = 0; i < m_voltageSweeps.size(); i++) {
4976 output << left << setw(15) << setfill(' ') << m_voltageSweeps[i];
4985 output << left << setw(20) << setfill(' ') << m_criticalVolumePlus[i];
4986 output << left << setw(20) << setfill(' ') << m_criticalVolumeMinu[i];
4987 output << left << setw(20) << setfill(' ') << m_criticalAreaPlus[i];
4988 output << left << setw(20) << setfill(' ') << m_criticalAreaMinu[i];
4989 output << left << setw(20) << setfill(' ') << m_ionizationVolume[i];
4990 output << endl;
4991 }
4992 output << lineBreak << "\n";
4993 // clang-format on
4994#ifdef CH_MPI
4995 }
4996#endif
4997}
4998
4999template <typename P, typename F, typename C>
5000void
5002{
5003 CH_TIME("DischargeInceptionStepper::writeReportTransient");
5004 if (m_verbosity > 5) {
5005 pout() << "DischargeInceptionStepper::writeReportTransient" << endl;
5006 }
5007
5008#ifdef CH_MPI
5009 if (procID() == 0) {
5010#endif
5011 std::ofstream output(m_outputFile, std::ofstream::out);
5012
5013 output << std::left << std::setw(15) << setfill(' ') << "# Time t";
5014 output << std::left << std::setw(15) << setfill(' ') << "V(t)";
5015 output << std::left << std::setw(15) << setfill(' ') << "max K(t)";
5016 output << std::left << std::setw(15) << setfill(' ') << "max T(t)";
5017 output << std::left << std::setw(15) << setfill(' ') << "Vcr(t)";
5018 output << std::left << std::setw(15) << setfill(' ') << "Acr(t)";
5019 output << std::left << std::setw(15) << setfill(' ') << "Vion(t)";
5020 output << std::left << std::setw(15) << setfill(' ') << "lambda(t)";
5021 output << std::left << std::setw(15) << setfill(' ') << "P(t)";
5022 output << std::left << std::setw(15) << setfill(' ') << "dP(t, t+dt)";
5023 output << std::left << std::setw(15) << setfill(' ') << "Time lag";
5024 output << "\n";
5025
5026 // Compute dP(t, t+dt)
5028 for (size_t i = 0; i < m_Rdot.size() - 1; i++) {
5029 const Real t = m_Rdot[i].first;
5030 const Real dt = m_Rdot[i + 1].first - m_Rdot[i].first;
5031 const Real prob = m_inceptionProbability[i].second;
5032 const Real Rdot = m_Rdot[i].second;
5033
5034 dProb.emplace_back((1.0 - prob) * Rdot * dt);
5035 }
5036 dProb.emplace_back(0.0);
5037
5038 // Compute the average waiting time.
5039 std::vector<Real> tau(m_Rdot.size(), 0.0);
5040 for (size_t i = 0; i < m_Rdot.size() - 1; i++) {
5041 const Real t1 = m_Rdot[i].first;
5042 const Real t2 = m_Rdot[i + 1].first;
5043 const Real dt = t2 - t1;
5044 const Real prob = m_inceptionProbability[i].second;
5045 const Real lambda = m_Rdot[i].second;
5046
5047 tau[i + 1] = tau[i] + t1 * (1 - prob) * lambda * dt;
5048 }
5049 for (int i = 0; i < tau.size(); i++) {
5050 const Real prob = m_inceptionProbability[i].second;
5051
5052 tau[i] = prob > 0.0 ? tau[i] / prob : std::numeric_limits<Real>::infinity();
5053 }
5054
5055 for (size_t i = 0; i < m_Rdot.size(); i++) {
5056 const Real time = m_Rdot[i].first;
5057
5058 output << std::left << std::setw(15) << time;
5059 output << std::left << std::setw(15) << m_voltageCurve(time);
5060 output << std::left << std::setw(15) << m_maxK[i].second;
5061 output << std::left << std::setw(15) << m_maxT[i].second;
5062 output << std::left << std::setw(15) << m_criticalVolume[i].second;
5063 output << std::left << std::setw(15) << m_criticalArea[i].second;
5064 output << std::left << std::setw(15) << m_ionizationVolumeTransient[i].second;
5065 output << std::left << std::setw(15) << m_Rdot[i].second;
5066 output << std::left << std::setw(15) << m_inceptionProbability[i].second;
5067 output << std::left << std::setw(15) << dProb[i];
5068 output << std::left << std::setw(15) << tau[i];
5069 output << "\n";
5070 }
5071
5072 output.close();
5073
5074#ifdef CH_MPI
5075 }
5076#endif
5077}
5078
5079template <typename P, typename F, typename C>
5080bool
5082 const RealVect& a_probLo,
5083 const RealVect& a_probHi) const noexcept
5084{
5085#ifndef NDEBUG
5086 CH_TIME("DischargeInceptionStepper::particleOutsideGrid");
5087 if (m_verbosity > 5) {
5088 pout() << "DischargeInceptionStepper::particleOutsideGrid" << endl;
5089 }
5090#endif
5091
5092 bool isOutside = false;
5093
5094 for (int dir = 0; dir < SpaceDim; dir++) {
5095 if (a_pos[dir] <= a_probLo[dir] || a_pos[dir] >= a_probHi[dir]) {
5096 isOutside = true;
5097 }
5098 }
5099
5100 return isOutside;
5101}
5102
5103template <typename P, typename F, typename C>
5104bool
5106{
5107#ifndef NDEBUG
5108 CH_TIME("DischargeInceptionStepper::particleInsideEB");
5109 if (m_verbosity > 5) {
5110 pout() << "DischargeInceptionStepper::particleInsideEB" << endl;
5111 }
5112#endif
5113
5114 const RefCountedPtr<BaseIF>& implicitFunction = m_amr->getBaseImplicitFunction(m_phase);
5115
5116 return (implicitFunction->value(a_pos) >= 0.0) ? true : false;
5117}
5118
5119template <typename P, typename F, typename C>
5120void
5122{
5123 CH_TIME("DischargeInceptionStepper::computeIonVelocity");
5124 if (m_verbosity > 5) {
5125 pout() << "DischargeInceptionStepper::computeIonVelocity" << endl;
5126 }
5127
5128 CH_assert(!(m_ionSolver.isNull()));
5129 CH_assert(m_ionSolver->isMobile());
5130
5131 EBAMRCellData& vel = m_ionSolver->getCellCenteredVelocity();
5132
5133 // Compute electric field at the input voltage and set v = -E
5134 this->superposition(vel, a_voltage);
5135 DataOps::scale(vel, -1.0);
5136
5137 // Allocate mesh data that holds mu and compute it on the mesh.
5139 m_amr->allocate(mu, m_realm, m_phase, 1);
5140
5141 for (int lvl = 0; lvl <= m_amr->getFinestLevel(); lvl++) {
5142 const DisjointBoxLayout& dbl = m_amr->getGrids(m_realm)[lvl];
5143 const DataIterator& dit = dbl.dataIterator();
5144
5145 const int nbox = dit.size();
5146
5147#pragma omp parallel for schedule(runtime)
5148 for (int mybox = 0; mybox < nbox; mybox++) {
5149 const DataIndex& din = dit[mybox];
5150
5151 const EBCellFAB& v = (*vel[lvl])[din];
5152 const FArrayBox& vReg = v.getFArrayBox();
5153
5154 EBCellFAB& MU = (*mu[lvl])[din];
5155 FArrayBox& MUREG = MU.getFArrayBox();
5156
5157 auto regularKernel = [&](const IntVect& iv) -> void {
5158 const RealVect EE = RealVect(D_DECL(vReg(iv, 0), vReg(iv, 1), vReg(iv, 2)));
5159 const Real E = EE.vectorLength();
5160
5161 MUREG(iv, 0) = m_ionMobility(E);
5162 };
5163
5164 auto irregularKernel = [&](const VolIndex& vof) -> void {
5165 const RealVect EE = RealVect(D_DECL(v(vof, 0), v(vof, 1), v(vof, 2)));
5166 const Real E = EE.vectorLength();
5167
5168 MU(vof, 0) = m_ionMobility(E);
5169 };
5170
5171 VoFIterator& vofit = (*m_amr->getVofIterator(m_realm, m_phase)[lvl])[din];
5172 Box cellBox = dbl[din];
5173
5176 }
5177 }
5178
5180
5181 m_amr->arithmeticAverage(vel, m_realm, m_phase);
5182 m_amr->interpGhostPwl(vel, m_realm, m_phase);
5183}
5184
5185template <typename P, typename F, typename C>
5186void
5188{
5189 CH_TIME("DischargeInceptionStepper::computeIonDiffusion");
5190 if (m_verbosity > 5) {
5191 pout() << "DischargeInceptionStepper::computeIonDiffusion" << endl;
5192 }
5193
5194 CH_assert(!(m_ionSolver.isNull()));
5195 CH_assert(m_ionSolver->isMobile());
5196
5197 // Compute the electric field at the input voltage.
5199 m_amr->allocate(scratch, m_realm, m_phase, SpaceDim);
5200 this->superposition(scratch, a_voltage);
5201
5202 // Compute the diffusion coefficient on cell centers.
5204 m_amr->allocate(diffCoCell, m_realm, m_phase, 1);
5205
5206 for (int lvl = 0; lvl <= m_amr->getFinestLevel(); lvl++) {
5207 const DisjointBoxLayout& dbl = m_amr->getGrids(m_realm)[lvl];
5208 const DataIterator& dit = dbl.dataIterator();
5209
5210 const int nbox = dit.size();
5211
5212#pragma omp parallel for schedule(runtime)
5213 for (int mybox = 0; mybox < nbox; mybox++) {
5214 const DataIndex& din = dit[mybox];
5215
5216 const EBCellFAB& electricField = (*scratch[lvl])[din];
5217 const FArrayBox& electricFieldReg = electricField.getFArrayBox();
5218
5219 EBCellFAB& dCo = (*diffCoCell[lvl])[din];
5220 FArrayBox& dCoReg = dCo.getFArrayBox();
5221
5222 auto regularKernel = [&](const IntVect& iv) -> void {
5224 const Real E = EE.vectorLength();
5225
5226 dCoReg(iv, 0) = m_ionDiffusion(E);
5227 };
5228
5229 auto irregularKernel = [&](const VolIndex& vof) -> void {
5231 const Real E = EE.vectorLength();
5232
5233 dCo(vof, 0) = m_ionDiffusion(E);
5234 };
5235
5236 VoFIterator& vofit = (*m_amr->getVofIterator(m_realm, m_phase)[lvl])[din];
5237 Box cellBox = dbl[din];
5238
5241 }
5242 }
5243
5244 // Coarsen and update ghost cells
5245 m_amr->arithmeticAverage(diffCoCell, m_realm, m_phase);
5246 m_amr->interpGhostPwl(diffCoCell, m_realm, m_phase);
5247
5248 // Fetch from solver and average to grid faces.
5249 EBAMRFluxData& diffCoFace = m_ionSolver->getFaceCenteredDiffusionCoefficient();
5250 EBAMRIVData& diffCoEB = m_ionSolver->getEbCenteredDiffusionCoefficient();
5251
5253 DataOps::setValue(diffCoEB, 0.0); // Neumann BC.
5254
5256 diffCoCell,
5257 m_amr->getDomains(),
5258 1,
5259 Interval(0, 0),
5260 Interval(0, 0),
5261 Average::Arithmetic);
5262}
5263
5264template <typename P, typename F, typename C>
5265void
5269 const Real a_voltage) const noexcept
5270{
5271 CH_TIME("DischargeInceptionStepper::superposition(full)");
5272
5273 const EBAMRCellData homogeneousField = m_amr->alias(phase::gas, a_homogeneousField);
5274 const EBAMRCellData inhomogeneousField = m_amr->alias(phase::gas, a_inhomogeneousField);
5275
5279
5280 m_amr->arithmeticAverage(a_sumField, m_realm, m_phase);
5281 m_amr->interpGhostPwl(a_sumField, m_realm, m_phase);
5282 // m_amr->interpToCentroids(a_sumField, m_realm, m_phase);
5283}
5284
5285template <typename P, typename F, typename C>
5286void
5288{
5289 CH_TIME("DischargeInceptionStepper::superposition(short)");
5290
5291 this->superposition(a_sumField, m_electricFieldInho, m_electricFieldHomo, a_voltage);
5292}
5293
5294template <typename P, typename F, typename C>
5295void
5298 const EBAMRCellData& a_data) const noexcept
5299{
5300 CH_TIME("DischargeInceptionStepper::getMaxValueAndLocation");
5301 if (m_verbosity > 5) {
5302 pout() << "DischargeInceptionStepper::getMaxValueAndLocation" << endl;
5303 }
5304
5305 a_maxVal = -std::numeric_limits<Real>::max();
5307
5308 for (int lvl = 0; lvl <= m_amr->getFinestLevel(); lvl++) {
5309 const DisjointBoxLayout& dbl = m_amr->getGrids(m_realm)[lvl];
5310 const DataIterator& dit = dbl.dataIterator();
5311 const EBISLayout& ebisl = m_amr->getEBISLayout(m_realm, m_phase)[lvl];
5312 const Real dx = m_amr->getDx()[lvl];
5313 const RealVect probLo = m_amr->getProbLo();
5314
5315 const LevelData<BaseFab<bool>>& validCellsLD = (*m_amr->getValidCells(m_realm)[lvl]);
5316
5317 const int nbox = dit.size();
5318
5319#pragma omp parallel for schedule(runtime)
5320 for (int mybox = 0; mybox < nbox; mybox++) {
5321 const DataIndex& din = dit[mybox];
5322 const Box& cellbox = dbl[din];
5323 const EBISBox& ebisbox = ebisl[din];
5324
5325 const EBCellFAB& data = (*a_data[lvl])[din];
5326 const FArrayBox& dataReg = data.getFArrayBox();
5328
5329 CH_assert(data.nComp() == 1);
5330
5331 auto regularKernel = [&](const IntVect& iv) -> void {
5332 if (validCells(iv, 0) && ebisbox.isRegular(iv)) {
5333 if (dataReg(iv, 0) > a_maxVal) {
5334 a_maxVal = dataReg(iv, 0);
5335 a_maxPos = probLo + (RealVect(iv) + 0.5 * RealVect::Unit) * dx;
5336 }
5337 }
5338 };
5339
5340 auto irregularKernel = [&](const VolIndex& vof) -> void {
5341 if (validCells(vof.gridIndex(), 0) && ebisbox.isIrregular(vof.gridIndex())) {
5342 if (data(vof, 0) > a_maxVal) {
5343 a_maxVal = data(vof, 0);
5344 a_maxPos = probLo + Location::position(Location::Cell::Centroid, vof, ebisbox, dx);
5345 }
5346 }
5347 };
5348
5349 VoFIterator& vofit = (*m_amr->getVofIterator(m_realm, m_phase)[lvl])[din];
5350
5353 }
5354 }
5355
5357
5360}
5361
5362template <typename P, typename F, typename C>
5363void
5365 int& a_comp,
5366 const EBAMRCellData& a_data,
5368 const int a_level,
5369 const bool a_interpToCentroids,
5370 const bool a_interpGhost) const noexcept
5371{
5372 CH_TIMERS("DischargeInceptionStepper::writeData");
5373 CH_TIMER("DischargeInceptionStepper::writeData::allocate", t1);
5374 CH_TIMER("DischargeInceptionStepper::writeData::local_copy", t2);
5375 CH_TIMER("DischargeInceptionStepper::writeData::interp_ghost", t3);
5376 CH_TIMER("DischargeInceptionStepper::writeData::interp_centroid", t4);
5377 CH_TIMER("DischargeInceptionStepper::writeData::final_copy", t5);
5378 if (m_verbosity > 5) {
5379 pout() << "DischargeInceptionStepper::writeData" << endl;
5380 }
5381
5382 // Number of components we are working with.
5383 const int numComp = a_data[a_level]->nComp();
5384
5385 // Component ranges that we copy to/from.
5386 const Interval srcInterv(0, numComp - 1);
5387 const Interval dstInterv(a_comp, a_comp + numComp - 1);
5388
5389 CH_START(t1);
5391 m_amr->allocate(scratch, m_realm, m_phase, a_level, numComp);
5392 CH_STOP(t1);
5393
5394 CH_START(t2);
5395 m_amr->copyData(scratch, *a_data[a_level], a_level, m_realm, m_realm);
5396 CH_START(t2);
5397
5398 // Interpolate ghost cells
5399 CH_START(t3);
5400 if (a_level > 0 && a_interpGhost) {
5401 m_amr->interpGhost(scratch, *a_data[a_level - 1], a_level, m_realm, m_phase);
5402 }
5403 CH_STOP(t3);
5404
5405 CH_START(t4);
5406 if (a_interpToCentroids) {
5407 m_amr->interpToCentroids(scratch, m_realm, m_phase, a_level);
5408 }
5409 CH_STOP(t4);
5410
5412
5413 CH_START(t5);
5414 m_amr->copyData(a_output, scratch, a_level, a_outputRealm, m_realm, dstInterv, srcInterv);
5415 CH_STOP(t5);
5416
5417 a_comp += numComp;
5418}
5419
5420template <typename P, typename F, typename C>
5421const EBAMRCellData*
5423{
5424 CH_TIMERS("DischargeInceptionStepper::getElectricField");
5425
5426 return &m_homogeneousFieldGas;
5427}
5428
5429template <typename P, typename F, typename C>
5430Real
5432{
5433 CH_TIME("DischargeInceptionStepper::getCriticalField");
5434 if (m_verbosity > 5) {
5435 pout() << "DischargeInceptionStepper::getCriticalField" << endl;
5436 }
5437
5438 Real Emin = -10.0;
5439 Real Emax = +10.0;
5440
5441 // Quick lambda for the effective Townsend ionization coefficient. Note that we solve for the exponent.
5442 auto alpha = [this](const Real E) -> Real {
5443 return m_alpha(std::pow(10.0, E), RealVect::Zero) - m_eta(std::pow(10.0, E), RealVect::Zero);
5444 };
5445
5447
5448 return std::pow(10.0, p);
5449}
5450
5451#include <CD_NamespaceFooter.H>
5452
5453#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:5422
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:5121
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:4890
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:3329
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:3652
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:4432
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:2772
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:5187
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:4357
virtual void computeTownsendCriterionStationary() noexcept
Solve for the Townsend criterion for each particle in each voltage.
Definition CD_DischargeInceptionStepperImplem.H:3029
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:2600
virtual void computeInceptionVoltageVolume() noexcept
Interpolate between K values to find voltage giving K_inception and store values in m_inceptionVoltag...
Definition CD_DischargeInceptionStepperImplem.H:4096
virtual Real computeIonizationVolumeTransient(const Real &a_voltage) const noexcept
Compute the ionization volume for each voltage.
Definition CD_DischargeInceptionStepperImplem.H:4808
virtual void computeFieldEmission(EBAMRCellData &a_emissionRate, const Real &a_voltage) const noexcept
Compute field emission rates.
Definition CD_DischargeInceptionStepperImplem.H:3950
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:4523
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:5296
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:4009
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:5081
virtual void writeReportTransient() const noexcept
Print report to the terminal.
Definition CD_DischargeInceptionStepperImplem.H:5001
virtual void rewindTracerParticles() noexcept
Move particles back to their original position.
Definition CD_DischargeInceptionStepperImplem.H:3620
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:3216
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:5266
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:3163
virtual void computeIonizationVolumeStationary() noexcept
Compute the ionization volume for each voltage.
Definition CD_DischargeInceptionStepperImplem.H:4710
virtual void computeFieldEmissionStationary() noexcept
Compute field emission rates.
Definition CD_DischargeInceptionStepperImplem.H:3850
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:4659
virtual Real getCriticalField() const noexcept
Get the breakdown field.
Definition CD_DischargeInceptionStepperImplem.H:5431
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:3762
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:5364
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:3682
virtual void computeInceptionIntegralTransient(const Real &a_voltage) noexcept
Solve streamer inception integral.
Definition CD_DischargeInceptionStepperImplem.H:2565
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:4587
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:5105
virtual Real computeRdot(const Real &a_voltage) const noexcept
Compute integral_Vcr(dne/dt * (1 - eta/alpha) dV)
Definition CD_DischargeInceptionStepperImplem.H:3518
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