Cantera  4.0.0a2
Loading...
Searching...
No Matches
PlasmaPhase.cpp
Go to the documentation of this file.
1//! @file PlasmaPhase.cpp
2
3// This file is part of Cantera. See License.txt in the top-level directory or
4// at https://cantera.org/license.txt for license and copyright information.
5
8#include <boost/math/special_functions/gamma.hpp>
10#include "cantera/base/global.h"
11#include "cantera/numerics/eigen_dense.h"
15#include <boost/polymorphic_pointer_cast.hpp>
17
18namespace Cantera {
19
20namespace {
21 const double gamma = sqrt(2 * ElectronCharge / ElectronMass);
22}
23
24PlasmaPhase::PlasmaPhase(const string& inputFile, const string& id_)
25{
26 initThermoFile(inputFile, id_);
27
28 // initial electron temperature
30
31 // Initialize the Boltzmann Solver
32 m_eedfSolver = make_unique<EEDFTwoTermApproximation>(this);
33
34 // Set Energy Grid (Hardcoded Defaults for Now)
35 double kTe_max = 60;
36 size_t nGridCells = 301;
37 m_nPoints = nGridCells + 1;
38 m_eedfSolver->setLinearGrid(kTe_max, nGridCells);
41}
42
43PlasmaPhase::~PlasmaPhase()
44{
45 if (shared_ptr<Solution> soln = m_soln.lock()) {
46 soln->removeChangedCallback(this);
47 soln->kinetics()->removeReactionAddedCallback(this);
48 }
49 for (size_t k = 0; k < nCollisions(); k++) {
50 // remove callback
51 m_collisions[k]->removeSetRateCallback(this);
52 }
53}
54
56{
58
59 // Check if there is an electron species in the phase.
61 throw CanteraError("PlasmaPhase::initThermo",
62 "No electron species found.");
63 }
64}
65
67{
68 // Update the heavy species thermodynamic properties
69 // before updating the electron species properties.
71 static const int cacheId = m_cache.getId();
72 CachedScalar cached = m_cache.getScalar(cacheId);
73 double tempNow = temperature();
74 double electronTempNow = electronTemperature();
75 size_t k = m_electronSpeciesIndex;
76 // If the electron temperature has changed since the last time these
77 // properties were computed, recompute them.
78 if (cached.state1 != tempNow || cached.state2 != electronTempNow) {
79 // Evaluate the electron species thermodynamic properties
80 // at the electron temperature.
82 m_cp0_R[k], m_h0_RT[k], m_s0_R[k]);
83 cached.state1 = tempNow;
84 cached.state2 = electronTempNow;
85
86 // Update the electron Gibbs functions, with the electron temperature.
87 m_g0_RT[k] = m_h0_RT[k] - m_s0_R[k];
88 }
89}
90
91// ================================================================= //
92// Overridden from IdealGasPhase or ThermoPhase //
93// ================================================================= //
94
95bool PlasmaPhase::addSpecies(shared_ptr<Species> spec)
96{
97 bool added = IdealGasPhase::addSpecies(spec);
98 size_t k = m_kk - 1;
99
100 if ((spec->name == "e" || spec->name == "Electron") ||
101 (spec->composition.find("E") != spec->composition.end() &&
102 spec->composition.size() == 1 &&
103 spec->composition["E"] == 1)) {
106 } else {
107 throw CanteraError("PlasmaPhase::addSpecies",
108 "Cannot add species, {}. "
109 "Only one electron species is allowed.", spec->name);
110 }
111 }
112 return added;
113}
114
115void PlasmaPhase::setSolution(std::weak_ptr<Solution> soln) {
117 // Register callback function to be executed
118 // when the thermo or kinetics object changed.
119 if (shared_ptr<Solution> soln = m_soln.lock()) {
120 soln->registerChangedCallback(this, [&]() {
122 });
123 }
124}
125
126void PlasmaPhase::getParameters(AnyMap& phaseNode) const
127{
129 AnyMap eedf;
130 eedf["type"] = m_distributionType;
131 vector<double> levels(m_nPoints);
132 Eigen::Map<Eigen::ArrayXd>(levels.data(), m_nPoints) = m_electronEnergyLevels;
133 eedf["energy-levels"] = levels;
134 if (m_distributionType == "isotropic") {
135 eedf["shape-factor"] = m_isotropicShapeFactor;
136 eedf["mean-electron-energy"].setQuantity(meanElectronEnergy(), "eV");
137 } else if (m_distributionType == "discretized") {
138 vector<double> dist(m_nPoints);
139 Eigen::Map<Eigen::ArrayXd>(dist.data(), m_nPoints) = m_electronEnergyDist;
140 eedf["distribution"] = dist;
141 eedf["normalize"] = m_do_normalizeElectronEnergyDist;
142 }
143 phaseNode["electron-energy-distribution"] = std::move(eedf);
144}
145
146void PlasmaPhase::setParameters(const AnyMap& phaseNode, const AnyMap& rootNode)
147{
148 IdealGasPhase::setParameters(phaseNode, rootNode);
149 if (phaseNode.hasKey("electron-energy-distribution")) {
150 const AnyMap eedf = phaseNode["electron-energy-distribution"].as<AnyMap>();
151 m_distributionType = eedf["type"].asString();
152 if (m_distributionType == "isotropic") {
153 if (eedf.hasKey("shape-factor")) {
154 setIsotropicShapeFactor(eedf["shape-factor"].asDouble());
155 } else {
156 throw CanteraError("PlasmaPhase::setParameters",
157 "isotropic type requires shape-factor key.");
158 }
159 if (eedf.hasKey("mean-electron-energy")) {
160 double energy = eedf.convert("mean-electron-energy", "eV");
161 setMeanElectronEnergy(energy);
162 } else {
163 throw CanteraError("PlasmaPhase::setParameters",
164 "isotropic type requires electron-temperature key.");
165 }
166 if (eedf.hasKey("energy-levels")) {
167 auto levels = eedf["energy-levels"].asVector<double>();
169 }
171 } else if (m_distributionType == "discretized") {
172 if (!eedf.hasKey("energy-levels")) {
173 throw CanteraError("PlasmaPhase::setParameters",
174 "Cannot find key energy-levels.");
175 }
176 if (!eedf.hasKey("distribution")) {
177 throw CanteraError("PlasmaPhase::setParameters",
178 "Cannot find key distribution.");
179 }
180 if (eedf.hasKey("normalize")) {
181 enableNormalizeElectronEnergyDist(eedf["normalize"].asBool());
182 }
183 auto levels = eedf["energy-levels"].asVector<double>();
184 auto distribution = eedf["distribution"].asVector<double>(levels.size());
185 setDiscretizedElectronEnergyDist(levels, distribution);
186 }
187 }
188
189 if (rootNode.hasKey("electron-collisions")) {
190 for (const auto& item : rootNode["electron-collisions"].asVector<AnyMap>()) {
191 auto rate = make_shared<ElectronCollisionPlasmaRate>(item);
192 Composition reactants, products;
193 reactants[item["target"].asString()] = 1;
194 reactants[electronSpeciesName()] = 1;
195 if (item.hasKey("product")) {
196 products[item["product"].asString()] = 1;
197 } else {
198 products[item["target"].asString()] = 1;
199 }
200 products[electronSpeciesName()] = 1;
201 if (rate->kind() == "ionization") {
202 products[electronSpeciesName()] += 1;
203 } else if (rate->kind() == "attachment") {
204 products[electronSpeciesName()] -= 1;
205 }
206 auto R = make_shared<Reaction>(reactants, products, rate);
207 addCollision(R);
208 }
209 }
210}
211
212// ================================================================= //
213// Electron Energy Distribution Functions //
214// ================================================================= //
215
217{
218 if (m_distributionType == "discretized") {
219 throw CanteraError("PlasmaPhase::updateElectronEnergyDistribution",
220 "Invalid for discretized electron energy distribution.");
221 } else if (m_distributionType == "isotropic") {
223 } else if (m_distributionType == "Boltzmann-two-term") {
224 auto ierr = m_eedfSolver->calculateDistributionFunction();
225 if (ierr == 0) {
226 auto y = m_eedfSolver->getEEDFEdge();
227 m_electronEnergyDist = Eigen::Map<const Eigen::ArrayXd>(y.data(), m_nPoints);
228 } else {
229 throw CanteraError("PlasmaPhase::updateElectronEnergyDistribution",
230 "Call to calculateDistributionFunction failed.");
231 }
232 bool validEEDF = (
233 static_cast<size_t>(m_electronEnergyDist.size()) == m_nPoints &&
234 m_electronEnergyDist.allFinite() &&
235 m_electronEnergyDist.maxCoeff() > 0.0 &&
236 m_electronEnergyDist.sum() > 0.0
237 );
238
239 if (validEEDF) {
241 } else {
242 writelog("Skipping Te update: EEDF is empty, non-finite, or unnormalized.\n");
243 }
244 } else {
245 throw CanteraError("PlasmaPhase::updateElectronEnergyDistribution",
246 "Unknown method '{}' for determining EEDF", m_distributionType);
247 }
250}
251
253 Eigen::ArrayXd eps32 = m_electronEnergyLevels.pow(3./2.);
254 double norm = 2./3. * numericalQuadrature(m_quadratureMethod,
255 m_electronEnergyDist, eps32);
256 if (norm < 0.0) {
257 throw CanteraError("PlasmaPhase::normalizeElectronEnergyDistribution",
258 "The norm is negative. This might be caused by bad "
259 "electron energy distribution");
260 }
261 m_electronEnergyDist /= norm;
262}
263
265{
266 if (type == "discretized" ||
267 type == "isotropic" ||
268 type == "Boltzmann-two-term") {
270 } else {
271 throw CanteraError("PlasmaPhase::setElectronEnergyDistributionType",
272 "Unknown type for electron energy distribution.");
273 }
274}
275
277{
279 double x = m_isotropicShapeFactor;
280 double gamma1 = boost::math::tgamma(3.0 / 2.0 / x);
281 double gamma2 = boost::math::tgamma(5.0 / 2.0 / x);
282 double c1 = x * std::pow(gamma2, 1.5) / std::pow(gamma1, 2.5);
283 double c2 = std::pow(gamma2 / gamma1, x);
285 c1 / std::pow(meanElectronEnergy(), 1.5) *
286 (-c2 * (m_electronEnergyLevels /
287 meanElectronEnergy()).pow(x)).exp();
289}
290
292 if (Te < 0.0) {
293 throw CanteraError("PlasmaPhase::setElectronTemperature",
294 "Electron temperature cannot be negative.");
295 }
296 m_electronTemp = Te;
298}
299
301{
303
304 if (!m_inEquilibrate) {
305 m_inEquilibrate = true;
306 // Remember current Te and lock Te -> T for the duration
309 }
310}
311
313{
314 if (m_inEquilibrate) {
315 // Restore Te to the pre-equilibrate value
317 m_inEquilibrate = false;
318 }
319
321}
322
324 setElectronTemperature(2.0 / 3.0 * energy * ElectronCharge / Boltzmann);
325}
326
327void PlasmaPhase::setElectronEnergyLevels(span<const double> levels)
328{
329 m_nPoints = levels.size();
330 m_electronEnergyLevels = Eigen::Map<const Eigen::ArrayXd>(levels.data(), m_nPoints);
334}
335
337{
338 m_distNum++;
339}
340
342{
343 m_levelNum++;
344 // Cross sections are interpolated on the energy levels
345 if (m_collisions.size() > 0) {
346 for (shared_ptr<Reaction> collision : m_collisions) {
347 const auto& rate = boost::polymorphic_pointer_downcast
350 }
351 }
352}
353
355{
356 Eigen::ArrayXd h = m_electronEnergyLevels.tail(m_nPoints - 1) -
358 if (m_electronEnergyLevels[0] < 0.0 || (h <= 0.0).any()) {
359 throw CanteraError("PlasmaPhase::checkElectronEnergyLevels",
360 "Values of electron energy levels need to be positive and "
361 "monotonically increasing.");
362 }
363}
364
366{
367 Eigen::ArrayXd h = m_electronEnergyLevels.tail(m_nPoints - 1) -
369 if ((m_electronEnergyDist < 0.0).any()) {
370 throw CanteraError("PlasmaPhase::checkElectronEnergyDistribution",
371 "Values of electron energy distribution cannot be negative.");
372 }
373 if (m_electronEnergyDist[m_nPoints - 1] > 0.01) {
374 warn_user("PlasmaPhase::checkElectronEnergyDistribution",
375 "The value of the last element of electron energy distribution exceed 0.01. "
376 "This indicates that the value of electron energy level is not high enough "
377 "to contain the isotropic distribution at mean electron energy of "
378 "{} eV", meanElectronEnergy());
379 }
380}
381
383 span<const double> dist)
384{
385 m_distributionType = "discretized";
386 m_nPoints = levels.size();
392 }
398}
399
401{
402 // calculate mean electron energy and electron temperature
403 Eigen::ArrayXd eps52 = m_electronEnergyLevels.pow(5./2.);
404 double epsilon_m = 2.0 / 5.0 * numericalQuadrature(m_quadratureMethod,
405 m_electronEnergyDist, eps52);
406 if (epsilon_m < 0.0 && m_quadratureMethod == "simpson") {
407 // try trapezoidal method
408 epsilon_m = 2.0 / 5.0 * numericalQuadrature(
409 "trapezoidal", m_electronEnergyDist, eps52);
410 }
411
412 if (epsilon_m < 0.0) {
413 throw CanteraError("PlasmaPhase::updateElectronTemperatureFromEnergyDist",
414 "The electron energy distribution produces negative electron temperature.");
415 }
416
417 m_electronTemp = 2.0 / 3.0 * epsilon_m * ElectronCharge / Boltzmann;
418}
419
421 m_isotropicShapeFactor = x;
423}
424
426{
427 if (shared_ptr<Solution> soln = m_soln.lock()) {
428 shared_ptr<Kinetics> kin = soln->kinetics();
429 if (!kin) {
430 return;
431 }
432
433 // add collision from the initial list of reactions. Only add reactions we
434 // haven't seen before
435 set<Reaction*> existing;
436 for (auto& R : m_collisions) {
437 existing.insert(R.get());
438 }
439 for (size_t i = 0; i < kin->nReactions(); i++) {
440 shared_ptr<Reaction> R = kin->reaction(i);
441 if (R->rate()->type() != "electron-collision-plasma"
442 || existing.count(R.get())) {
443 continue;
444 }
445 addCollision(R);
446 }
447
448 // Register callback when reaction is added later.
449 // Modifying collision reactions is not supported.
450 kin->registerReactionAddedCallback(this, [this, kin]() {
451 size_t i = kin->nReactions() - 1;
452 if (kin->reaction(i)->type() == "electron-collision-plasma") {
453 addCollision(kin->reaction(i));
454 }
455 });
456 }
457}
458
459void PlasmaPhase::addCollision(shared_ptr<Reaction> collision)
460{
461 size_t i = nCollisions();
462
463 // setup callback to signal updating the cross-section-related
464 // parameters
465 collision->registerSetRateCallback(this, [this, i, collision]() {
466 m_interp_cs_ready[i] = false;
468 std::dynamic_pointer_cast<ElectronCollisionPlasmaRate>(collision->rate());
469 });
470
471 // Identify target species for electron-collision reactions
472 string target;
473 for (const auto& [name, _] : collision->reactants) {
474 // Reactants are expected to be electrons and the target species
475 if (name != electronSpeciesName()) {
476 m_targetSpeciesIndices.emplace_back(speciesIndex(name, true));
477 target = name;
478 break;
479 }
480 }
481 if (target.empty()) {
482 throw CanteraError("PlasmaPhase::addCollision", "Error identifying target for"
483 " collision with equation '{}'", collision->equation());
484 }
485
486 m_collisions.emplace_back(collision);
487 m_collisionRates.emplace_back(
488 std::dynamic_pointer_cast<ElectronCollisionPlasmaRate>(collision->rate()));
489 m_interp_cs_ready.emplace_back(false);
490
491 // resize parameters
494
495 // Set up data used by Boltzmann solver
496 auto& rate = *m_collisionRates.back();
497 string kind = m_collisionRates.back()->kind();
498
499 if ((kind == "effective" || kind == "elastic")) {
500 for (size_t k = 0; k < m_collisions.size() - 1; k++) {
501 if (m_collisions[k]->reactants == collision->reactants &&
502 (m_collisionRates[k]->kind() == "elastic" ||
503 m_collisionRates[k]->kind() == "effective") && !collision->duplicate)
504 {
505 throw CanteraError("PlasmaPhase::addCollision", "Phase already contains"
506 " an effective/elastic cross section for '{}'.", target);
507 }
508 }
509 m_kElastic.push_back(i);
510 } else {
511 m_kInelastic.push_back(i);
512 }
513
514 auto levels = rate.energyLevels();
515 m_energyLevels.emplace_back(levels.begin(), levels.end());
516 auto sections = rate.crossSections();
517 m_crossSections.emplace_back(sections.begin(), sections.end());
518 m_eedfSolver->setGridCache();
519}
520
522{
523 if (m_interp_cs_ready[i]) {
524 return false;
525 }
526 vector<double> levels(m_nPoints);
527 Eigen::Map<Eigen::ArrayXd>(levels.data(), m_nPoints) = m_electronEnergyLevels;
528 m_collisionRates[i]->updateInterpolatedCrossSection(levels);
529 m_interp_cs_ready[i] = true;
530 return true;
531}
532
534{
536 // Forward difference for the first point
540
541 // Central difference for the middle points
542 for (size_t i = 1; i < m_nPoints - 1; i++) {
546 (h1 * h1 - h0 * h0) * m_electronEnergyDist[i] -
547 h1 * h1 * m_electronEnergyDist[i-1]) /
548 (h1 * h0) / (h1 + h0);
549 }
550
551 // Backward difference for the last point
557}
558
560{
561 // cache of cross section plus distribution plus energy-level number
562 static const int cacheId = m_cache.getId();
563 CachedScalar last_stateNum = m_cache.getScalar(cacheId);
564
565 // combine the distribution and energy level number
566 int stateNum = m_distNum + m_levelNum;
567
568 vector<bool> interpChanged(m_collisions.size());
569 for (size_t i = 0; i < m_collisions.size(); i++) {
570 interpChanged[i] = updateInterpolatedCrossSection(i);
571 }
572
573 if (last_stateNum.validate(temperature(), stateNum)) {
574 // check each cross section, and only update coefficients that
575 // the interpolated cross sections change
576 for (size_t i = 0; i < m_collisions.size(); i++) {
577 if (interpChanged[i]) {
579 }
580 }
581 } else {
582 // update every coefficient if distribution, temperature,
583 // or energy levels change.
584 for (size_t i = 0; i < m_collisions.size(); i++) {
586 }
587 }
588}
589
591{
592 // @todo exclude attachment collisions
593 size_t k = m_targetSpeciesIndices[i];
594
595 // Map cross sections to Eigen::ArrayXd
596 auto cs_array = Eigen::Map<const Eigen::ArrayXd>(
597 m_collisionRates[i]->crossSectionInterpolated().data(),
598 m_collisionRates[i]->crossSectionInterpolated().size()
599 );
600
601 // Mass ratio calculation
602 double mass_ratio = ElectronMass / molecularWeight(k) * Avogadro;
603
604 // Calculate the rate using Simpson's rule or trapezoidal rule
605 Eigen::ArrayXd f0_plus = m_electronEnergyDist + Boltzmann * temperature() /
607 m_elasticElectronEnergyLossCoefficients[i] = 2.0 * mass_ratio * gamma *
609 m_quadratureMethod, 1.0 / 3.0 * f0_plus.cwiseProduct(cs_array),
610 m_electronEnergyLevels.pow(3.0));
611}
612
614{
615 if (m_electronEnergyDist.size() != m_nPoints
616 || m_electronEnergyDistDiff.size() != m_nPoints) {
617 throw CanteraError("PlasmaPhase::elasticPowerLoss:",
618 "EEDF not initialized");
619 }
620
622 // The elastic power loss includes the contributions from inelastic
623 // collisions (inelastic recoil effects).
624 double rate = 0.0;
625 for (size_t i = 0; i < nCollisions(); i++) {
628 }
629 const double q_elastic = Avogadro * Avogadro * ElectronCharge *
631
632 if (!std::isfinite(q_elastic)) {
633 throw CanteraError("PlasmaPhase::elasticPowerLoss:",
634 "Non-finite elastic power loss");
635 }
636
637 return q_elastic;
638}
639
641{
642 // Only implemented when using the Boltzmann two-term EEDF
643 if (m_distributionType == "Boltzmann-two-term") {
644 return m_eedfSolver->getElectronMobility();
645 } else {
646 throw NotImplementedError("PlasmaPhase::electronMobility",
647 "Electron mobility is only available for 'Boltzmann-two-term' "
648 "electron energy distributions.");
649 }
650}
651
652// ================================================================= //
653// Molar Thermodynamic Properties of the Solution //
654// ================================================================= //
655
657{
658 m_work.resize(m_kk);
660 double h = 0.0;
661 for (size_t k = 0; k < m_kk; ++k) {
662 h += moleFraction(k) * m_work[k];
663 }
664 return h;
665}
666
668{
669 m_work.resize(m_kk);
671 double u = 0.0;
672 for (size_t k = 0; k < m_kk; ++k) {
673 u += moleFraction(k) * m_work[k];
674 }
675 return u;
676}
677
679{
680 m_work.resize(m_kk);
682 double s = 0.0;
683 for (size_t k = 0; k < m_kk; ++k) {
684 s += moleFraction(k) * m_work[k];
685 }
686 return s;
687}
688
690{
691 m_work.resize(m_kk);
693 double g = 0.0;
694 for (size_t k = 0; k < m_kk; ++k) {
695 g += moleFraction(k) * m_work[k];
696 }
697 return g;
698}
699
700// ================================================================= //
701// Mechanical Equation of State //
702// ================================================================= //
703
705{
706 double T_g = temperature();
707 double T_e = electronTemperature();
709 return T_g + X_e * (T_e - T_g);
710}
711
712double PlasmaPhase::pressure() const {
714}
715
716
717// ================================================================= //
718// Chemical Potentials and Activities //
719// ================================================================= //
720
722{
723 return pressure() / (GasConstant * temperature());
724}
725
726void PlasmaPhase::getActivities(span<double> a) const
727{
728 double tmp = temperature() / meanTemperature();
729 for (size_t k = 0; k < nSpecies(); k++) {
730 a[k] = tmp * moleFraction(k);
731 }
732}
733
734void PlasmaPhase::getActivityCoefficients(span<double> ac) const
735{
736 checkArraySize("PlasmaPhase::getActivityCoefficients", ac.size(), m_kk);
737 double tmp = temperature() / meanTemperature();
738 for (size_t k = 0; k < m_kk; k++) {
739 ac[k] = tmp;
740 }
741}
742
743
744// ================================================================= //
745// Partial Molar Properties of the Solution //
746// ================================================================= //
747
748void PlasmaPhase::getChemPotentials(span<double> mu) const
749{
751 size_t k = m_electronSpeciesIndex;
752 double xx = std::max(SmallNumber, moleFraction(k));
753 mu[k] += (RTe() - RT()) * log(xx);
754}
755
756void PlasmaPhase::getPartialMolarEnthalpies(span<double> hbar) const
757{
758 // Since the `updateThermo` is overriden in `PlasmaPhase`,
759 // `enthalpy_RT_ref` returns \tilde{h}_k(T_k) / (R * T_k).
760 // When calling `IdealGasPhase::getPartialMolarEnthalpies(hbar)`,
761 // the `hbar` array is equal to \tilde{h}_k(T_k) * (R * T) / (R * T_k).
762 // For all heavy species, T_k == T, so we get \tilde{h}_k(T).
763 // For electrons, we need to multiply by T_e/T to get \tilde{h}_k(T_e).
766}
767
768void PlasmaPhase::getPartialMolarEntropies(span<double> sbar) const
769{
770 // Since the `updateThermo` is overriden in `PlasmaPhase`,
771 // `entropy_R_ref` returns s^\text{ref}_k(T_k)/R.
772 // When calling `IdealGasPhase::getPartialMolarEntropies(hbar)`,
773 // the `sbar` array is equal to s^\text{ref}_k(T_k)*R/R - R ln(X_k P/P^ref).
774 // Therefore, there is no need to correct for temperature.
776}
777
778void PlasmaPhase::getPartialMolarIntEnergies(span<double> ubar) const
779{
780 checkArraySize("PlasmaPhase::getPartialMolarIntEnergies", ubar.size(), m_kk);
781 auto _h = enthalpy_RT_ref();
782 for (size_t k = 0; k < m_kk; k++) {
783 ubar[k] = RT() * (_h[k] - 1.0);
784 }
785 // Redefine it for the electron species.
786 size_t k = m_electronSpeciesIndex;
787 ubar[k] = RTe() * (_h[k] - 1.0);
788}
789
790void PlasmaPhase::getPartialMolarVolumes(span<double> vbar) const
791{
792 double vol = RT() / pressure();
793 for (size_t k = 0; k < m_kk; k++) {
794 vbar[k] = vol;
795 }
796 vbar[m_electronSpeciesIndex] = RTe() / pressure();
797}
798
799// ================================================================= //
800// Properties of the Standard State of the Species in the Solution //
801// ================================================================= //
802
803void PlasmaPhase::getStandardChemPotentials(span<double> muStar) const
804{
805 // After calling PlasmaPhase::getGibbs_ref, muStar = mu^\text{ref}_k(T_k)(T_k).
806 // mu^\text{ref} is evaluated at T for heavy species and at Te for electrons.
807 getGibbs_ref(muStar);
808
809 // Then, we need to add R*T_k*ln(P/Pref) to mu^\text{ref}.
810 // .. For heavy species, mu_star = mu^\text{ref}(T) + R*T*ln(P/Pref)
811 double tmp = log(pressure() / refPressure()) * RT();
812 for (size_t k = 0; k < m_kk; k++) {
813 muStar[k] += tmp;
814 }
815 // .. For electrons, mu_star = mu^\text{ref}(Te) + R*T_e*ln(P/Pref)
816 size_t k = m_electronSpeciesIndex;
817 muStar[k] -= log(pressure() / refPressure()) * RT();
818 muStar[k] += log(pressure() / refPressure()) * RTe();
819}
820
821void PlasmaPhase::getStandardVolumes(span<double> vol) const
822{
823 double tmp = RT() / pressure();
824 for (size_t k = 0; k < m_kk; k++) {
825 vol[k] = tmp;
826 }
828}
829
830// ================================================================= //
831// Thermodynamic Values for the Species Reference States //
832// ================================================================= //
833
834void PlasmaPhase::getGibbs_ref(span<double> g) const
835{
836 // Since the `updateThermo` is overriden in `PlasmaPhase`,
837 // `gibbs_RT_ref` returns \mu^\text{ref}_k(T_k) / (R * T_k).
838 // When calling `IdealGasPhase::getGibbs_ref(g)`,
839 // the `g` array is equal to \mu^\text{ref}_k(T_k) * (R * T) / (R * T_k).
840 // For all heavy species, T_k == T, so we get \mu^\text{ref}_k(T).
841 // For electrons, we need to multiply by T_e/T to get \mu^\text{ref}_k(T_e).
844}
845
846void PlasmaPhase::getStandardVolumes_ref(span<double> vol) const
847{
850}
851
852// ================================================================= //
853// Setting the State //
854// ================================================================= //
855
856void PlasmaPhase::setState(const AnyMap& input_state)
857{
858 AnyMap state = input_state;
859
860 // Set electron temperature first.
861 if (state.hasKey("electron-temperature")) {
862 state["Te"] = state["electron-temperature"];
863 }
864
865 if (state.hasKey("Te")) {
866 setElectronTemperature(state.convert("Te", "K"));
867 }
868
869 // Remap allowable synonyms for gas temperature after setting electron temperature,
870 if (state.hasKey("gas-temperature")) {
871 state["T"] = state["gas-temperature"];
872 }
873 if (state.hasKey("Tg")) {
874 state["T"] = state["Tg"];
875 }
876
877 // Call the base class method to set the remaining state variables.
879}
880
882{
883 // sigma = e * n_e * mu_e [S/m]; q_J = sigma * E^2 [W/m^3]
884 const double mu_e = electronMobility(); // m^2 / (V·s)
885 if (mu_e <= 0.0) {
886 return 0.0;
887 }
888 const double ne = concentration(m_electronSpeciesIndex) * Avogadro; // m^-3
889 if (ne <= 0.0) {
890 return 0.0;
891 }
892 const double E = electricField(); // V/m
893 if (E <= 0.0) {
894 return 0.0;
895 }
896 const double sigma = ElectronCharge * ne * mu_e; // S/m
897 return sigma * E * E; // W/m^3
898}
899
901{
902 // Joule heating: sigma * E^2 [W/m^3]
903 const double qJ = jouleHeatingPower();
904 checkFinite(qJ);
905
906 // Elastic + inelastic recoil power loss [W/m^3]
907 double qElastic = elasticPowerLoss();
908 checkFinite(qElastic);
909
910 return qJ + qElastic;
911}
912
913
914}
EEDF Two-Term approximation solver.
Header for plasma reaction rates parameterized by electron collision cross section and electron energ...
Base class for kinetics managers and also contains the kineticsmgr module documentation (see Kinetics...
Header file for class PlasmaPhase.
Declaration for class Cantera::Species.
A map of string keys to values whose type can vary at runtime.
Definition AnyMap.h:431
bool hasKey(const string &key) const
Returns true if the map contains an item named key.
Definition AnyMap.cpp:1477
double convert(const string &key, const string &units) const
Convert the item stored by the given key to the units specified in units.
Definition AnyMap.cpp:1595
Base class for exceptions thrown by Cantera classes.
Electron collision plasma reaction rate type.
void updateInterpolatedCrossSection(span< const double >)
Update the value of m_crossSectionsInterpolated [m2].
void getGibbs_ref(span< double > g) const override
Returns the vector of the Gibbs function of the reference state at the current temperature of the sol...
void getPartialMolarEnthalpies(span< double > hbar) const override
Returns an array of partial molar enthalpies for the species in the mixture.
vector< double > m_g0_RT
Temporary storage for dimensionless reference state Gibbs energies.
vector< double > m_h0_RT
Temporary storage for dimensionless reference state enthalpies.
span< const double > enthalpy_RT_ref() const
Returns a reference to the dimensionless reference state enthalpy vector.
virtual void updateThermo() const
Update the species reference state thermodynamic functions.
vector< double > m_s0_R
Temporary storage for dimensionless reference state entropies.
void getPartialMolarEntropies(span< double > sbar) const override
Returns an array of partial molar entropies of the species in the solution.
vector< double > m_cp0_R
Temporary storage for dimensionless reference state heat capacities.
bool addSpecies(shared_ptr< Species > spec) override
Add a Species to this Phase.
void getChemPotentials(span< double > mu) const override
Get the species chemical potentials. Units: J/kmol.
void getStandardVolumes_ref(span< double > vol) const override
Get the molar volumes of the species reference states at the current T and P_ref of the solution.
virtual void update_single(size_t k, double T, double &cp_R, double &h_RT, double &s_R) const
Get reference-state properties for a single species.
An error indicating that an unimplemented function has been called.
ValueCache m_cache
Cached for saved calculations within each ThermoPhase.
Definition Phase.h:862
size_t nSpecies() const
Returns the number of species in the phase.
Definition Phase.h:247
size_t m_kk
Number of species in the phase.
Definition Phase.h:882
size_t speciesIndex(const string &name, bool raise=true) const
Returns the index of a species named 'name' within the Phase object.
Definition Phase.cpp:127
double temperature() const
Temperature (K).
Definition Phase.h:586
double meanMolecularWeight() const
The mean molecular weight. Units: (kg/kmol)
Definition Phase.h:677
virtual double concentration(const size_t k) const
Concentration of species k.
Definition Phase.cpp:495
double moleFraction(size_t k) const
Return the mole fraction of a single species.
Definition Phase.cpp:457
virtual double density() const
Density (kg/m^3).
Definition Phase.h:611
double molecularWeight(size_t k) const
Molecular weight of species k.
Definition Phase.cpp:398
string name() const
Return the name of the phase.
Definition Phase.cpp:20
void checkElectronEnergyDistribution() const
Check the electron energy distribution.
void getStandardChemPotentials(span< double > muStar) const override
Return the standard chemical potentials of the species. Units: J/kmol.
vector< vector< double > > m_energyLevels
Electron energy levels corresponding to the cross section data.
void setCollisions()
Set collisions.
double meanElectronEnergy() const
Mean electron energy [eV].
void getGibbs_ref(span< double > g) const override
Return the reference chemical potentials of the species. Units: J/kmol.
double m_electronTempEquil
Saved electron temperature during an equilibrium solve.
double enthalpy_mole() const override
Return the Molar enthalpy. Units: J/kmol.
size_t m_nPoints
Number of points of electron energy levels.
void setState(const AnyMap &state) override
Set the state using an AnyMap containing any combination of properties supported by the thermodynamic...
void getActivities(span< double > a) const override
Get the array of non-dimensional activities at the current solution temperature, pressure,...
void addCollision(shared_ptr< Reaction > collision)
Add a collision and record the target species.
virtual void setSolution(std::weak_ptr< Solution > soln) override
Set the link to the Solution object that owns this ThermoPhase.
void normalizeElectronEnergyDistribution()
Electron energy distribution norm.
void updateThermo() const override
Update the species reference state thermodynamic functions.
void getPartialMolarEnthalpies(span< double > hbar) const override
Return the partial molar enthalpies of the species in the solution. Units: J/kmol.
vector< size_t > m_targetSpeciesIndices
The collision-target species indices of m_collisions.
void setElectronTemperature(double Te) override
Set the internally stored electron temperature of the phase [K].
void electronEnergyLevelChanged()
When electron energy level changed, plasma properties such as electron-collision reaction rates need ...
double pressure() const override
Return the pressure of the plasma phase. Units: Pa.
double elasticPowerLoss()
The elastic power loss [J/s/m³].
int m_levelNum
Electron energy level change variable.
bool updateInterpolatedCrossSection(size_t k)
Update interpolated cross section of a collision.
bool m_inEquilibrate
Lock flag (default off)
void electronEnergyDistributionChanged()
When electron energy distribution changed, plasma properties such as electron-collision reaction rate...
size_t nElectronEnergyLevels() const
Number of electron levels.
size_t nCollisions() const
Number of electron collision cross sections.
void endEquilibrate() override
Hook called at the end of an equilibrium calculation on this phase.
Eigen::ArrayXd m_electronEnergyDist
Normalized electron energy distribution vector [-] Length: m_nPoints.
double electricField() const
Get the applied electric field strength [V/m].
Eigen::ArrayXd m_electronEnergyLevels
electron energy levels [ev]. Length: m_nPoints
void getActivityCoefficients(span< double > ac) const override
Get the array of non-dimensional activity coefficients at the current solution temperature,...
double meanTemperature() const
Return the mean temperature of the plasma phase. Units: K.
double intrinsicHeating() override
Intrinsic volumetric heating rate [W/m³].
double electronMobility() const
The electron mobility (m²/V/s)
void getParameters(AnyMap &phaseNode) const override
Store the parameters of a ThermoPhase object such that an identical one could be reconstructed using ...
string type() const override
String indicating the thermodynamic model implemented.
void checkElectronEnergyLevels() const
Check the electron energy levels.
void initThermo() override
Initialize the ThermoPhase object after all species have been set up.
void updateElasticElectronEnergyLossCoefficients()
Update elastic electron energy loss coefficients.
void updateElectronTemperatureFromEnergyDist()
Update electron temperature (K) From energy distribution.
string m_distributionType
Electron energy distribution type. Can be "isotropic", "discretized" or "Boltzmann-two-term".
void updateElectronEnergyDistribution()
Update the electron energy distribution.
vector< double > m_elasticElectronEnergyLossCoefficients
Elastic electron energy loss coefficients (eV m3/s)
string m_quadratureMethod
Numerical quadrature method for electron energy distribution.
PlasmaPhase(const string &inputFile="", const string &id="")
Construct and initialize a PlasmaPhase object directly from an input file.
void beginEquilibrate() override
Hook called at the beginning of an equilibrium calculation on this phase.
void setDiscretizedElectronEnergyDist(span< const double > levels, span< const double > distrb)
Set discretized electron energy distribution.
double m_electronTemp
Electron temperature [K].
double RTe() const
Return the Gas Constant multiplied by the current electron temperature [J/kmol].
double intEnergy_mole() const override
Return the molar internal energy. Units: J/kmol.
double entropy_mole() const override
Return the molar entropy. Units: J/kmol/K.
bool m_do_normalizeElectronEnergyDist
Flag of normalizing electron energy distribution.
void updateElectronEnergyDistDifference()
Update electron energy distribution difference.
void updateElasticElectronEnergyLossCoefficient(size_t i)
Updates the elastic electron energy loss coefficient for collision index i.
vector< size_t > m_kElastic
Indices of elastic collisions in m_crossSections.
unique_ptr< EEDFTwoTermApproximation > m_eedfSolver
Solver used to calculate the EEDF based on electron collision rates.
string electronSpeciesName() const
Electron species name.
void setIsotropicElectronEnergyDistribution()
Set isotropic electron energy distribution.
void getPartialMolarVolumes(span< double > vbar) const override
Return the partial molar volumes of the species in the solution. Units: m³/kmol.
Eigen::ArrayXd m_electronEnergyDistDiff
ionization degree for the electron-electron collisions (tmp is the previous one)
void getStandardVolumes(span< double > vol) const override
Return the standard molar volumes of the species. Units: m³/kmol.
void getPartialMolarEntropies(span< double > sbar) const override
Return the partial molar entropies of the species in the solution. Units: J/kmol/K.
double gibbs_mole() const override
Return the molar Gibbs free energy. Units: J/kmol.
double standardConcentration(size_t k=0) const override
Returns the standard concentration , which is used to normalize the generalized concentration.
std::vector< double > m_work
Work array.
bool addSpecies(shared_ptr< Species > spec) override
Add a Species to this Phase.
const shared_ptr< Reaction > collision(size_t i) const
Get the Reaction object associated with electron collision i.
vector< bool > m_interp_cs_ready
The list of whether the interpolated cross sections is ready.
vector< shared_ptr< ElectronCollisionPlasmaRate > > m_collisionRates
The list of shared pointers of collision rates.
void getChemPotentials(span< double > mu) const override
Return the chemical potentials of the species in the solution. Units: J/kmol.
void setElectronEnergyLevels(span< const double > levels)
Set electron energy levels.
vector< shared_ptr< Reaction > > m_collisions
The list of shared pointers of plasma collision reactions.
void setParameters(const AnyMap &phaseNode, const AnyMap &rootNode=AnyMap()) override
Set equation of state parameters from an AnyMap phase description.
void setMeanElectronEnergy(double energy)
Set mean electron energy [eV].
void getStandardVolumes_ref(span< double > vol) const override
Return the molar volumes of the species reference states. Units: m³/kmol.
size_t m_electronSpeciesIndex
Index of electron species.
vector< vector< double > > m_crossSections
Cross section data.
void setElectronEnergyDistributionType(const string &type)
Set electron energy distribution type.
double jouleHeatingPower() const
The joule heating power (W/m³)
vector< size_t > m_kInelastic
Indices of inelastic collisions in m_crossSections.
double electronTemperature() const override
Electron Temperature [K].
void setIsotropicShapeFactor(double x)
Set the shape factor of isotropic electron energy distribution.
void enableNormalizeElectronEnergyDist(bool enable)
Set flag of automatically normalize electron energy distribution.
void getPartialMolarIntEnergies(span< double > ubar) const override
Return the partial molar internal energies of the species in the solution. Units: J/kmol.
int m_distNum
Electron energy distribution change variable.
virtual void endEquilibrate()
Hook called at the end of an equilibrium calculation on this phase.
virtual void setParameters(const AnyMap &phaseNode, const AnyMap &rootNode=AnyMap())
Set equation of state parameters from an AnyMap phase description.
virtual void getParameters(AnyMap &phaseNode) const
Store the parameters of a ThermoPhase object such that an identical one could be reconstructed using ...
virtual void setState(const AnyMap &state)
Set the state using an AnyMap containing any combination of properties supported by the thermodynamic...
double RT() const
Return the Gas Constant multiplied by the current temperature.
virtual void setSolution(std::weak_ptr< Solution > soln)
Set the link to the Solution object that owns this ThermoPhase.
virtual void initThermo()
Initialize the ThermoPhase object after all species have been set up.
void initThermoFile(const string &inputFile, const string &id)
Initialize a ThermoPhase object using an input file.
std::weak_ptr< Solution > m_soln
reference to Solution
virtual void beginEquilibrate()
Hook called at the beginning of an equilibrium calculation on this phase.
MultiSpeciesThermo m_spthermo
Pointer to the calculation manager for species reference-state thermodynamic properties.
virtual double refPressure() const
Returns the reference pressure in Pa.
CachedScalar getScalar(int id)
Get a reference to a CachedValue object representing a scalar (double) with the given id.
Definition ValueCache.h:161
int getId()
Get a unique id for a cached value.
Header for a file containing miscellaneous numerical functions.
This file contains definitions for utility functions and text for modules, inputfiles and logging,...
void writelog(const string &fmt, const Args &... args)
Write a formatted message to the screen.
Definition global.h:171
double numericalQuadrature(const string &method, const Eigen::ArrayXd &f, const Eigen::ArrayXd &x)
Numerical integration of a function.
Definition funcs.cpp:116
const double Boltzmann
Boltzmann constant [J/K].
Definition ct_defs.h:87
const double Avogadro
Avogadro's Number [number/kmol].
Definition ct_defs.h:84
const double GasConstant
Universal Gas Constant [J/kmol/K].
Definition ct_defs.h:123
const double ElectronCharge
Elementary charge [C].
Definition ct_defs.h:93
const double ElectronMass
Electron Mass [kg].
Definition ct_defs.h:114
void warn_user(const string &method, const string &msg, const Args &... args)
Print a user warning raised from method as CanteraWarning.
Definition global.h:263
Namespace for the Cantera kernel.
Definition AnyMap.cpp:595
const size_t npos
index returned by functions to indicate "no position"
Definition ct_defs.h:183
void checkFinite(const double tmp)
Check to see that a number is finite (not NaN, +Inf or -Inf)
MappedVector asVectorXd(vector< double > &v)
Convenience wrapper for accessing std::vector as an Eigen VectorXd.
Definition eigen_dense.h:60
span< double > asSpan(Eigen::DenseBase< Derived > &v)
Convenience wrapper for accessing Eigen vector/array/map data as a span.
Definition eigen_dense.h:46
const double SmallNumber
smallest number to compare to zero.
Definition ct_defs.h:161
map< string, double > Composition
Map from string names to doubles.
Definition ct_defs.h:180
void checkArraySize(const char *procedure, size_t available, size_t required)
Wrapper for throwing ArraySizeError.
A cached property value and the state at which it was evaluated.
Definition ValueCache.h:33
double state2
Value of the second state variable for the state at which value was evaluated, for example density or...
Definition ValueCache.h:106
bool validate(double state1New)
Check whether the currently cached value is valid based on a single state variable.
Definition ValueCache.h:39
double state1
Value of the first state variable for the state at which value was evaluated, for example temperature...
Definition ValueCache.h:102