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()
58 for (
size_t j = 0; j <
m_points; j++) {
63 throw CanteraError(
"EEDFTwoTermApproximation::calculateDistributionFunction",
64 " unknown EEDF first guess");
73 for (
size_t i = 0; i <
m_points + 1; i++) {
92 "m_maxn is zero; no iterations will occur.");
96 "m_points is zero; the EEDF grid is empty.");
98 if (isnan(delta) || delta == 0.0) {
100 "m_delta0 is NaN or zero; solver cannot update.");
103 for (
size_t n = 0; n <
m_maxn; n++) {
104 if (0.0 < err1 && err1 < err0) {
105 delta *= log(
m_factorM) / (log(err0) - log(err1));
108 Eigen::VectorXd f0_old = f0;
110 checkFinite(
"EEDFTwoTermApproximation::converge: f0", f0.data(), f0.size());
113 Eigen::VectorXd Df0 = (f0_old - f0).cwiseAbs();
117 }
else if (n ==
m_maxn - 1) {
118 throw CanteraError(
"WeaklyIonizedGas::converge",
"Convergence failed");
140 for (
size_t i = 0; i <
m_points; i++) {
148 Eigen::SparseLU<SparseMat> solver(A);
149 if (solver.info() == Eigen::NumericalIssue) {
151 "Error SparseLU solver: NumericalIssue");
152 }
else if (solver.info() == Eigen::InvalidInput) {
154 "Error SparseLU solver: InvalidInput");
156 if (solver.info() != Eigen::Success) {
158 "Error SparseLU solver",
"Decomposition failed");
163 Eigen::VectorXd f1 = solver.solve(f0);
164 if(solver.info() != Eigen::Success) {
165 throw CanteraError(
"EEDFTwoTermApproximation::iterate",
"Solving failed");
169 checkFinite(
"EEDFTwoTermApproximation::converge: f0", f1.data(), f1.size());
180 double expm1a = expm1(g * (-a + x0));
181 double expm1b = expm1(g * (-b + x0));
186 A1 = (expm1a * ag1 + ag - expm1b * bg1 - bg) / (g*g);
187 A2 = (expm1a * (2 * ag1 + ag * ag) + ag * (ag + 2) -
188 expm1b * (2 * bg1 + bg * bg) - bg * (bg + 2)) / (g*g*g);
190 A1 = 0.5 * (b*b - a*a);
191 A2 = 1.0 / 3.0 * (b*b*b - a*a*a);
195 double c0 = (a * u1 - b * u0) / (a - b);
196 double c1 = (u0 - u1) / (a - b);
198 return c0 * A1 + c1 * A2;
204 const double f_min = 1e-300;
207 double f1 = std::max(f0(1), f_min);
208 double f0_ = std::max(f0(0), f_min);
213 double fN = std::max(f0(N), f_min);
214 double fNm1 = std::max(f0(N - 1), f_min);
218 for (
size_t i = 1; i < N; ++i) {
219 double f_up = std::max(f0(i + 1), f_min);
220 double f_down = std::max(f0(i - 1), f_min);
228 SparseTriplets tripletList;
229 for (
size_t n = 0; n <
m_eps[k].size(); n++) {
230 double eps_a =
m_eps[k][n][0];
231 double eps_b =
m_eps[k][n][1];
232 double sigma_a =
m_sigma[k][n][0];
233 double sigma_b =
m_sigma[k][n][1];
234 auto j =
static_cast<SparseMat::StorageIndex
>(
m_j[k][n]);
236 double p = m_gamma * r;
238 tripletList.emplace_back(j, j, p);
241 P.setFromTriplets(tripletList.begin(), tripletList.end());
247 SparseTriplets tripletList;
248 for (
size_t n = 0; n <
m_eps[k].size(); n++) {
249 double eps_a =
m_eps[k][n][0];
250 double eps_b =
m_eps[k][n][1];
251 double sigma_a =
m_sigma[k][n][0];
252 double sigma_b =
m_sigma[k][n][1];
253 auto i =
static_cast<SparseMat::StorageIndex
>(
m_i[k][n]);
254 auto j =
static_cast<SparseMat::StorageIndex
>(
m_j[k][n]);
258 tripletList.emplace_back(i, j, q);
261 Q.setFromTriplets(tripletList.begin(), tripletList.end());
283 alpha = (mu * E - sqrt(pow(mu * E, 2) - 4 * D * nu * nDensity)) / 2.0 / D / nDensity;
290 for (
size_t j = 1; j <
m_points; j++) {
296 double q = omega / (nDensity * m_gamma * pow(
m_gridEdge[j], 0.5));
298 double F = sigma_tilde * sigma_tilde / (sigma_tilde * sigma_tilde + q * q);
299 double DA = m_gamma / 3.0 * pow(E / nDensity, 2.0) *
m_gridEdge[j];
301 double D = DA / sigma_tilde * F + DB;
303 W -= m_gamma / 3.0 * 2 * alpha * E / nDensity *
m_gridEdge[j] / sigma_tilde;
307 if (!std::isfinite(z)) {
308 throw CanteraError(
"matrix_A",
"Non-finite Peclet number encountered");
310 if (std::abs(z) > 500) {
311 warn_user(
"EEDFTwoTermApproximation::matrix_A",
312 "Large Peclet number z = {:.3e} at j = {}. "
313 "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.
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].
void warn_user(const string &method, const string &msg, const Args &... args)
Print a user warning raised from method as CanteraWarning.
Namespace for the Cantera kernel.
void checkFinite(const double tmp)
Check to see that a number is finite (not NaN, +Inf or -Inf)