12#include "cantera/numerics/eigen_dense.h"
20typedef Eigen::SparseMatrix<double> SparseMat;
22EEDFTwoTermApproximation::EEDFTwoTermApproximation(
PlasmaPhase* s)
31void EEDFTwoTermApproximation::setLinearGrid(
double& kTe_max,
size_t& ncell)
38 for (
size_t j = 0; j <
m_points; j++) {
46int EEDFTwoTermApproximation::calculateDistributionFunction()
59 writelog(
"First guess EEDF maxwell\n");
60 for (
size_t j = 0; j <
m_points; j++) {
65 throw CanteraError(
"EEDFTwoTermApproximation::calculateDistributionFunction",
66 " unknown EEDF first guess");
75 for (
size_t i = 0; i <
m_points + 1; i++) {
94 "m_maxn is zero; no iterations will occur.");
98 "m_points is zero; the EEDF grid is empty.");
100 if (isnan(delta) || delta == 0.0) {
101 throw CanteraError(
"EEDFTwoTermApproximation::converge",
102 "m_delta0 is NaN or zero; solver cannot update.");
105 for (
size_t n = 0; n <
m_maxn; n++) {
106 if (0.0 < err1 && err1 < err0) {
107 delta *= log(
m_factorM) / (log(err0) - log(err1));
110 Eigen::VectorXd f0_old = f0;
112 checkFinite(
"EEDFTwoTermApproximation::converge: f0", f0.data(), f0.size());
115 Eigen::VectorXd Df0 = (f0_old - f0).cwiseAbs();
119 }
else if (n ==
m_maxn - 1) {
120 throw CanteraError(
"WeaklyIonizedGas::converge",
"Convergence failed");
142 for (
size_t i = 0; i <
m_points; i++) {
150 Eigen::SparseLU<SparseMat> solver(A);
151 if (solver.info() == Eigen::NumericalIssue) {
153 "Error SparseLU solver: NumericalIssue");
154 }
else if (solver.info() == Eigen::InvalidInput) {
156 "Error SparseLU solver: InvalidInput");
158 if (solver.info() != Eigen::Success) {
160 "Error SparseLU solver",
"Decomposition failed");
165 Eigen::VectorXd f1 = solver.solve(f0);
166 if(solver.info() != Eigen::Success) {
167 throw CanteraError(
"EEDFTwoTermApproximation::iterate",
"Solving failed");
171 checkFinite(
"EEDFTwoTermApproximation::converge: f0", f1.data(), f1.size());
182 double expm1a = expm1(g * (-a + x0));
183 double expm1b = expm1(g * (-b + x0));
188 A1 = (expm1a * ag1 + ag - expm1b * bg1 - bg) / (g*g);
189 A2 = (expm1a * (2 * ag1 + ag * ag) + ag * (ag + 2) -
190 expm1b * (2 * bg1 + bg * bg) - bg * (bg + 2)) / (g*g*g);
192 A1 = 0.5 * (b*b - a*a);
193 A2 = 1.0 / 3.0 * (b*b*b - a*a*a);
197 double c0 = (a * u1 - b * u0) / (a - b);
198 double c1 = (u0 - u1) / (a - b);
200 return c0 * A1 + c1 * A2;
206 const double f_min = 1e-300;
209 double f1 = std::max(f0(1), f_min);
210 double f0_ = std::max(f0(0), f_min);
215 double fN = std::max(f0(N), f_min);
216 double fNm1 = std::max(f0(N - 1), f_min);
220 for (
size_t i = 1; i < N; ++i) {
221 double f_up = std::max(f0(i + 1), f_min);
222 double f_down = std::max(f0(i - 1), f_min);
230 SparseTriplets tripletList;
231 for (
size_t n = 0; n <
m_eps[k].size(); n++) {
232 double eps_a =
m_eps[k][n][0];
233 double eps_b =
m_eps[k][n][1];
234 double sigma_a =
m_sigma[k][n][0];
235 double sigma_b =
m_sigma[k][n][1];
236 size_t j =
m_j[k][n];
238 double p = m_gamma * r;
240 tripletList.emplace_back(j, j, p);
243 P.setFromTriplets(tripletList.begin(), tripletList.end());
249 SparseTriplets tripletList;
250 for (
size_t n = 0; n <
m_eps[k].size(); n++) {
251 double eps_a =
m_eps[k][n][0];
252 double eps_b =
m_eps[k][n][1];
253 double sigma_a =
m_sigma[k][n][0];
254 double sigma_b =
m_sigma[k][n][1];
255 size_t i =
m_i[k][n];
256 size_t j =
m_j[k][n];
260 tripletList.emplace_back(i, j, q);
263 Q.setFromTriplets(tripletList.begin(), tripletList.end());
285 alpha = (mu * E - sqrt(pow(mu * E, 2) - 4 * D * nu * nDensity)) / 2.0 / D / nDensity;
292 for (
size_t j = 1; j <
m_points; j++) {
298 double q = omega / (nDensity * m_gamma * pow(
m_gridEdge[j], 0.5));
300 double F = sigma_tilde * sigma_tilde / (sigma_tilde * sigma_tilde + q * q);
301 double DA = m_gamma / 3.0 * pow(E / nDensity, 2.0) *
m_gridEdge[j];
303 double D = DA / sigma_tilde * F + DB;
305 W -= m_gamma / 3.0 * 2 * alpha * E / nDensity *
m_gridEdge[j] / sigma_tilde;
309 if (!std::isfinite(z)) {
310 throw CanteraError(
"matrix_A",
"Non-finite Peclet number encountered");
312 if (std::abs(z) > 500) {
313 writelog(
"Warning: Large Peclet number z = {:.3e} at j = {}. W = {:.3e}, D = {:.3e}, E/N = {:.3e}\n",
314 z, j, W, D, E / nDensity);
316 a0[j] = W / (1 - std::exp(-z));
317 a1[j] = W / (1 - std::exp(z));
320 SparseTriplets tripletList;
323 tripletList.emplace_back(0, 0, a0[1]);
325 for (
size_t j = 1; j <
m_points - 1; j++) {
326 tripletList.emplace_back(j, j, a0[j+1] - a1[j]);
330 for (
size_t j = 0; j <
m_points - 1; j++) {
331 tripletList.emplace_back(j, j+1, a1[j+1]);
335 for (
size_t j = 1; j <
m_points; j++) {
336 tripletList.emplace_back(j, j-1, -a0[j]);
340 tripletList.emplace_back(N, N, -a1[N]);
343 A.setFromTriplets(tripletList.begin(), tripletList.end());
348 for (
size_t i = 0; i <
m_points; i++) {
353 for (
size_t i = 0; i <
m_points; i++) {
355 G.insert(i, i) = - alpha * m_gamma / 3 * (alpha * (pow(
m_gridEdge[i + 1], 2) - pow(
m_gridEdge[i], 2)) / sigma_c / 2
372 Eigen::VectorXd s = PQ * f0;
373 checkFinite(
"EEDFTwoTermApproximation::netProductionFrequency: s",
385 for (
size_t i = 0; i <
m_points; i++) {
392 auto f = Eigen::Map<const Eigen::ArrayXd>(y.data(), y.size());
394 return 1./3. * m_gamma *
simpson(f, x) / nDensity;
400 vector<double> y(
m_points + 1, 0.0);
401 for (
size_t i = 1; i <
m_points; i++) {
410 auto f = ConstMappedVector(y.data(), y.size());
412 return -1./3. * m_gamma *
simpson(f, x) / nDensity;
435 if (kind ==
"ionization") {
437 }
else if (kind ==
"attachment") {
471 double tmp_sum = 0.0;
491 for (
size_t i = 0; i <
m_points; i++) {
495 for (
size_t i = 0; i <
m_points + 1; i++) {
512 for (
size_t i = 0; i <
m_points; i++) {
519void EEDFTwoTermApproximation::setGridCache()
531 auto& x = collision->energyLevels();
532 auto& y = collision->crossSections();
534 int shiftFactor = (collision->kind() ==
"ionization") ? 2 : 1;
536 for (
size_t i = 0; i <
m_points + 1; i++) {
537 eps1[i] =
clip(shiftFactor *
m_gridEdge[i] + collision->threshold(),
540 vector<double> nodes = eps1;
541 for (
size_t i = 0; i <
m_points + 1; i++) {
546 for (
size_t i = 0; i < x.size(); i++) {
547 if (x[i] >= eps1[0] && x[i] <= eps1[
m_points]) {
548 nodes.push_back(x[i]);
552 std::sort(nodes.begin(), nodes.end());
553 auto last = std::unique(nodes.begin(), nodes.end());
554 nodes.resize(std::distance(nodes.begin(), last));
555 vector<double> sigma0(nodes.size());
556 for (
size_t i = 0; i < nodes.size(); i++) {
561 for (
size_t i = 1; i < nodes.size(); i++) {
567 for (
size_t i = 1; i < nodes.size(); i++) {
568 auto low = std::lower_bound(eps1.begin(), eps1.end(), nodes[i]);
569 m_i[k].push_back(low - eps1.begin() - 1);
573 for (
size_t i = 0; i < nodes.size() - 1; i++) {
574 m_sigma[k].push_back({sigma0[i], sigma0[i+1]});
578 for (
size_t i = 0; i < nodes.size() - 1; i++) {
579 m_eps[k].push_back({nodes[i], nodes[i+1]});
583 auto x_offset = collision->energyLevels();
584 for (
auto& element : x_offset) {
585 element -= collision->threshold();
592 string m_quadratureMethod =
"simpson";
593 Eigen::VectorXd p(f.size());
594 for (
int i = 0; i < f.size(); i++) {
595 p[i] = f(i) * pow(grid[i], 0.5);
EEDF Two-Term approximation solver.
Header for plasma reaction rates parameterized by electron collision cross section and electron energ...
Header file for class PlasmaPhase.
Base class for exceptions thrown by Cantera classes.
double m_rtol
Error tolerance for convergence.
Eigen::VectorXd m_f0
normalized electron energy distribution function
vector< double > m_gridEdge
Grid of electron energy (cell boundary i-1/2) [eV].
vector< vector< size_t > > m_i
Location of cell i for grid cache.
void calculateTotalCrossSection()
Compute the total (elastic + inelastic) cross section.
vector< vector< size_t > > m_j
Location of cell j for grid cache.
vector< double > m_X_targets_prev
Previous mole fraction of targets used to compute eedf.
vector< vector< vector< double > > > m_eps
The energy boundaries of the overlap of cell i and j.
vector< vector< vector< double > > > m_sigma
Cross section at the boundaries of the overlap of cell i and j.
vector< size_t > m_k_lg_Targets
Local to global indices.
Eigen::SparseMatrix< double > matrix_Q(const vector< double > &g, size_t k)
The matrix of scattering-in.
bool m_first_call
First call to calculateDistributionFunction.
void converge(Eigen::VectorXd &f0)
Iterate f0 (EEDF) until convergence.
double m_delta0
Formerly options for the EEDF solver.
double electronDiffusivity(const Eigen::VectorXd &f0)
Diffusivity.
PlasmaPhase * m_phase
Pointer to the PlasmaPhase object used to initialize this object.
double norm(const Eigen::VectorXd &f, const Eigen::VectorXd &grid)
Compute the L1 norm of a function f defined over a given energy grid.
void updateMoleFractions()
Update the vector of species mole fractions.
vector< size_t > m_kTargets
list of target species indices in global Cantera numbering (1 index per cs)
bool m_has_EEDF
flag of having an EEDF
double electronMobility(const Eigen::VectorXd &f0)
Mobility.
Eigen::VectorXd m_gridCenter
Grid of electron energy (cell center) [eV].
size_t m_maxn
Maximum number of iterations.
Eigen::SparseMatrix< double > matrix_A(const Eigen::VectorXd &f0)
Matrix A (Ax = b) of the equation of EEDF, which is discretized by the exponential scheme of Scharfet...
Eigen::VectorXd iterate(const Eigen::VectorXd &f0, double delta)
An iteration of solving electron energy distribution function.
double m_electronMobility
Electron mobility [m²/V·s].
size_t m_points
The number of points in the EEDF grid.
vector< size_t > m_kOthers
Indices of species which has no cross-section data.
double m_factorM
The factor for step size change.
void calculateTotalElasticCrossSection()
Compute the total elastic collision cross section.
void updateCrossSections()
Update the total cross sections based on the current state.
void initSpeciesIndexCrossSections()
Initialize species indices associated with cross-section data.
double m_init_kTe
The initial electron temperature [eV].
Eigen::SparseMatrix< double > matrix_P(const vector< double > &g, size_t k)
The matrix of scattering-out.
double netProductionFrequency(const Eigen::VectorXd &f0)
Reduced net production frequency.
std::string m_firstguess
The first guess for the EEDF.
std::string m_growth
The growth model of EEDF.
vector< double > m_totalCrossSectionEdge
Total electron cross section on the cell boundary (i-1/2) of energy grid.
vector< double > m_f0_edge
EEDF at grid edges (cell boundaries)
vector< size_t > m_klocTargets
list of target species indices in local X EEDF numbering (1 index per cs)
vector< double > m_X_targets
Mole fraction of targets.
double integralPQ(double a, double b, double u0, double u1, double g, double x0)
The integral in [a, b] of assuming that u is linear with u(a) = u0 and u(b) = u1.
vector< double > m_totalCrossSectionCenter
Total electron cross section on the cell center of energy grid.
vector< double > vector_g(const Eigen::VectorXd &f0)
Vector g is used by matrix_P() and matrix_Q().
vector< int > m_inFactor
in factor.
vector< double > m_sigmaElastic
vector of total elastic cross section weighted with mass ratio
virtual double molarDensity() const
Molar density (kmol/m^3).
size_t nSpecies() const
Returns the number of species in the phase.
double temperature() const
Temperature (K).
double moleFraction(size_t k) const
Return the mole fraction of a single species.
double molecularWeight(size_t k) const
Molecular weight of species k.
Base class for handling plasma properties, specifically focusing on the electron energy distribution.
double electricFieldFrequency() const
Get the frequency of the applied electric field [Hz].
size_t nCollisions() const
Number of electron collision cross sections.
double electricField() const
Get the applied electric field strength [V/m].
const vector< size_t > & kElastic() const
Get the indices for elastic electron collisions.
const shared_ptr< ElectronCollisionPlasmaRate > collisionRate(size_t i) const
Get the ElectronCollisionPlasmaRate object associated with electron collision i.
size_t targetIndex(size_t i) const
target of a specific process
const vector< size_t > & kInelastic() const
Get the indicies for inelastic electron collisions.
Definitions for the classes that are thrown when Cantera experiences an error condition (also contain...
Header for a file containing miscellaneous numerical functions.
void writelog(const string &fmt, const Args &... args)
Write a formatted message to the screen.
T clip(const T &value, const T &lower, const T &upper)
Clip value such that lower <= value <= upper.
double linearInterp(double x, const vector< double > &xpts, const vector< double > &fpts)
Linearly interpolate a function defined on a discrete grid.
double numericalQuadrature(const string &method, const Eigen::ArrayXd &f, const Eigen::ArrayXd &x)
Numerical integration of a function.
double simpson(const Eigen::ArrayXd &f, const Eigen::ArrayXd &x)
Numerical integration of a function using Simpson's rule with flexibility of taking odd and even numb...
const double Boltzmann
Boltzmann constant [J/K].
const double Avogadro
Avogadro's Number [number/kmol].
const double ElectronCharge
Elementary charge [C].
const double ElectronMass
Electron Mass [kg].
Namespace for the Cantera kernel.
void checkFinite(const double tmp)
Check to see that a number is finite (not NaN, +Inf or -Inf)