Cantera  3.1.0
Loading...
Searching...
No Matches
IonGasTransport.cpp
Go to the documentation of this file.
1//! @file IonGasTransport.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
11#include "cantera/base/global.h"
12#include "MMCollisionInt.h"
13
14namespace Cantera
15{
16
17void IonGasTransport::init(ThermoPhase* thermo, int mode, int log_level)
18{
21 m_mode = mode;
22 if (m_mode == CK_Mode) {
23 throw CanteraError("IonGasTransport::init",
24 "mode = CK_Mode, which is an outdated lower-order fit.");
25 }
26 if (log_level == -7) {
27 log_level = 0;
28 } else {
29 warn_deprecated("Transport::init", "The log_level parameter "
30 "is deprecated and will be removed after Cantera 3.1.");
31 }
32 m_log_level = log_level;
33 // make a local copy of species charge
34 for (size_t k = 0; k < m_nsp; k++) {
35 m_speciesCharge.push_back(m_thermo->charge(k));
36 }
37
38 // Find the index of electron
39 size_t nElectronSpecies = 0;
40 for (size_t k = 0; k < m_nsp; k++) {
42 m_thermo->charge(k) == -1)
43 {
44 m_kElectron = k;
45 nElectronSpecies++;
46 }
47 }
48 if (nElectronSpecies > 1) {
49 throw CanteraError("IonGasTransport::init",
50 "Found multiple electron species.");
51 }
52
53 // Find indices for charge of species
54 for (size_t k = 0; k < m_nsp; k++) {
55 if (m_speciesCharge[k] != 0){
56 if (k != m_kElectron) {
57 m_kIon.push_back(k);
58 }
59 } else {
60 m_kNeutral.push_back(k);
61 }
62 }
63 // set up O2/O2- collision integral [A^2]
64 // Data taken from Prager (2005)
65 const vector<double> temp{300.0, 400.0, 500.0, 600.0, 800.0, 1000.0,
66 1200.0, 1500.0, 2000.0, 2500.0, 3000.0, 4000.0};
67 const vector<double> om11_O2{120.0, 107.0, 98.1, 92.1, 83.0, 77.0,
68 72.6, 67.9, 62.7, 59.3, 56.7, 53.8};
69 vector<double> w(temp.size(),-1);
70 int degree = 5;
71 m_om11_O2.resize(degree + 1);
72 polyfit(temp.size(), degree, temp.data(), om11_O2.data(),
73 w.data(), m_om11_O2.data());
74 // set up Monchick and Mason parameters
76 // set up n64 parameters
77 setupN64();
78 // setup collision integrals
80 m_molefracs.resize(m_nsp);
81 m_spwork.resize(m_nsp);
82 m_visc.resize(m_nsp);
83 m_sqvisc.resize(m_nsp);
84 m_phi.resize(m_nsp, m_nsp, 0.0);
86 m_cond.resize(m_nsp);
87
88 // make a local copy of the molecular weights
90
93 for (size_t j = 0; j < m_nsp; j++) {
94 for (size_t k = j; k < m_nsp; k++) {
95 m_wratjk(j,k) = sqrt(m_mw[j]/m_mw[k]);
96 m_wratjk(k,j) = sqrt(m_wratjk(j,k));
97 m_wratkj1(j,k) = sqrt(1.0 + m_mw[k]/m_mw[j]);
98 }
99 }
100}
101
103{
104 update_T();
105 update_C();
106
107 if (m_visc_ok) {
108 return m_viscmix;
109 }
110
111 double vismix = 0.0;
112 // update m_visc and m_phi if necessary
113 if (!m_viscwt_ok) {
115 }
116
117 multiply(m_phi, m_molefracs.data(), m_spwork.data());
118
119 for (size_t k : m_kNeutral) {
120 vismix += m_molefracs[k] * m_visc[k]/m_spwork[k]; //denom;
121 }
122 m_viscmix = vismix;
123 return vismix;
124}
125
127{
128 update_T();
129 update_C();
130 if (!m_spcond_ok) {
131 updateCond_T();
132 }
133 if (!m_condmix_ok) {
134 double sum1 = 0.0, sum2 = 0.0;
135 for (size_t k : m_kNeutral) {
136 sum1 += m_molefracs[k] * m_cond[k];
137 sum2 += m_molefracs[k] / m_cond[k];
138 }
139 m_lambda = 0.5*(sum1 + 1.0/sum2);
140 m_condmix_ok = true;
141 }
142 return m_lambda;
143}
144
146{
147 vector<double> mobi(m_nsp);
148 getMobilities(&mobi[0]);
149 double p = m_thermo->pressure();
150 double sum = 0.0;
151 for (size_t k : m_kIon) {
152 double ND_k = m_molefracs[k] * p / m_kbt;
153 sum += ND_k * std::abs(m_speciesCharge[k]) * ElectronCharge * mobi[k];
154 }
155 if (m_kElectron != npos) {
156 sum += m_molefracs[m_kElectron] * p / m_kbt *
158 }
159 return sum;
160}
161
163{
165
166 // number of points to use in generating fit data
167 const size_t np = 50;
168 int degree = 4;
169 double dt = (m_thermo->maxTemp() - m_thermo->minTemp())/(np-1);
170 vector<double> tlog(np);
171 vector<double> w(np);
172
173 // generate array of log(t) values
174 for (size_t n = 0; n < np; n++) {
175 double t = m_thermo->minTemp() + dt*n;
176 tlog[n] = log(t);
177 }
178
179 // vector of polynomial coefficients
180 vector<double> c(degree + 1);
181 double err = 0.0, relerr = 0.0,
182 mxerr = 0.0, mxrelerr = 0.0;
183
184 vector<double> diff(np + 1);
185 // The array order still not ideal
186 for (size_t k = 0; k < m_nsp; k++) {
187 for (size_t j = k; j < m_nsp; j++) {
188 if (m_alpha[k] == 0.0 || m_alpha[j] == 0.0 ||
189 k == m_kElectron || j == m_kElectron) {
190 continue;
191 }
192 if (m_speciesCharge[k] == 0) {
193 if (m_speciesCharge[j] == 0) {
194 continue;
195 }
196 } else {
197 if (m_speciesCharge[j] != 0) {
198 continue;
199 }
200 }
201 for (size_t n = 0; n < np; n++) {
202 double t = m_thermo->minTemp() + dt*n;
203 double eps = m_epsilon(j,k);
204 double tstar = Boltzmann * t/eps;
205 double sigma = m_diam(j,k);
206 double om11 = omega11_n64(tstar, m_gamma(j,k))
207 * Pi * sigma * sigma;
208 // Stockmayer-(n,6,4) model is not suitable for collision
209 // between O2/O2- due to resonant charge transfer.
210 // Therefore, the experimental collision data is used instead.
211 if ((k == m_thermo->speciesIndex("O2-") ||
212 j == m_thermo->speciesIndex("O2-")) &&
213 (k == m_thermo->speciesIndex("O2") ||
214 j == m_thermo->speciesIndex("O2"))) {
215 om11 = poly5(t, m_om11_O2.data()) / 1e20;
216 }
217 double diffcoeff = 3.0/16.0 * sqrt(2.0 * Pi/m_reducedMass(k,j))
218 * pow(Boltzmann * t, 1.5) / om11;
219
220 diff[n] = diffcoeff/pow(t, 1.5);
221 w[n] = 1.0/(diff[n]*diff[n]);
222 }
223 polyfit(np, degree, tlog.data(), diff.data(), w.data(), c.data());
224
225 for (size_t n = 0; n < np; n++) {
226 double val, fit;
227 double t = exp(tlog[n]);
228 double pre = pow(t, 1.5);
229 val = pre * diff[n];
230 fit = pre * poly4(tlog[n], c.data());
231 err = fit - val;
232 relerr = err/val;
233 mxerr = std::max(mxerr, fabs(err));
234 mxrelerr = std::max(mxrelerr, fabs(relerr));
235 }
236 size_t sum = k * (k + 1) / 2;
237 m_diffcoeffs[k*m_nsp+j-sum] = c;
238 if (m_log_level >= 2) {
239 writelog(m_thermo->speciesName(k) + "__" +
240 m_thermo->speciesName(j) + ": [" + vec2str(c) + "]\n");
241 }
242 }
243 }
244 m_fittingErrors["diff-coeff-max-abs-error"] =
245 std::max(m_fittingErrors.getDouble("diff-coeff-max-abs-error", 0.0),
246 mxerr);
247 m_fittingErrors["diff-coeff-max-rel-error"] =
248 std::max(m_fittingErrors.getDouble("diff-coeff-max-rel-error", 0.0),
249 mxrelerr);
250
251 if (m_log_level) {
252 writelogf("Maximum binary diffusion coefficient absolute error:"
253 " %12.6g\n", mxerr);
254 writelogf("Maximum binary diffusion coefficient relative error:"
255 "%12.6g", mxrelerr);
256 }
257}
258
260{
261 m_gamma.resize(m_nsp, m_nsp, 0.0);
262 for (size_t i : m_kIon) {
263 for (size_t j : m_kNeutral) {
264 if (m_alpha[j] != 0.0 && m_alpha[i] != 0.0) {
265 double r_alpha = m_alpha[i] / m_alpha[j];
266 // save a copy of polarizability in Angstrom
267 double alphaA_i = m_alpha[i] * 1e30;
268 double alphaA_j = m_alpha[j] * 1e30;
269 // The ratio of dispersion to induction forces
270 double xi = alphaA_i / (m_speciesCharge[i] * m_speciesCharge[i] *
271 (1.0 + pow(2 * r_alpha, 2./3.)) * sqrt(alphaA_j));
272
273 // the collision diameter
274 double K1 = 1.767;
275 double kappa = 0.095;
276 m_diam(i,j) = K1 * (pow(m_alpha[i], 1./3.) + pow(m_alpha[j], 1./3.)) /
277 pow(alphaA_i * alphaA_j * (1.0 + 1.0 / xi), kappa);
278
279 // The original K2 is 0.72, but Han et al. suggested that K2 = 1.44
280 // for better fit.
281 double K2 = 1.44;
282 double epsilon = K2 * ElectronCharge * ElectronCharge *
284 m_alpha[j] * (1.0 + xi) /
285 (8 * Pi * epsilon_0 * pow(m_diam(i,j),4));
286 if (epsilon != 0.0) {
287 m_epsilon(i,j) = epsilon;
288 }
289
290 // Calculate dispersion coefficient and quadrupole polarizability
291 // from curve fitting if not available.
292 // Neutrals
293 if (m_disp[j] == 0.0) {
294 m_disp[j] = exp(1.8846*log(alphaA_j)-0.4737)* 1e-50;
295 }
296 if (m_quad_polar[j] == 0.0) {
297 m_quad_polar[j] = 2.0 * m_disp[j];
298 }
299 // Ions
300 if (m_disp[i] == 0.0) {
301 if (m_speciesCharge[i] > 0) {
302 m_disp[i] = exp(1.8853*log(alphaA_i)+0.2682)* 1e-50;
303 } else {
304 m_disp[i] = exp(3.2246*log(alphaA_i)-3.2397)* 1e-50;
305 }
306 }
307
308 // The binary dispersion coefficient is determined by the combination rule
309 // Reference:
310 // Tang, K. T. "Dynamic polarizabilities and van der Waals coefficients."
311 // Physical Review 177.1 (1969): 108.
312 double C6 = 2.0 * m_disp[i] * m_disp[j] /
313 (1.0/r_alpha * m_disp[i] + r_alpha * m_disp[j]);
314
315 m_gamma(i,j) = (2.0 / pow(m_speciesCharge[i],2) * C6 + m_quad_polar[j]) /
316 (m_alpha[j] * m_diam(i,j) * m_diam(i,j));//Dimensionless
317
318 // properties are symmetric
319 m_diam(j,i) = m_diam(i,j);
320 m_epsilon(j,i) = m_epsilon(i,j);
321 m_gamma(j,i) = m_gamma(i,j);
322 }
323 }
324 }
325}
326
327double IonGasTransport::omega11_n64(const double tstar, const double gamma)
328{
329 double logtstar = log(tstar);
330 double om11 = 0.0;
331 if (tstar < 0.01) {
332 throw CanteraError("IonGasTransport::omega11_n64",
333 "tstar = {} is smaller than 0.01", tstar);
334 } else if (tstar <= 0.04) {
335 // for interval 0.01 to 0.04, SSE = 0.006; R^2 = 1; RMSE = 0.020
336 om11 = 2.97 - 12.0 * gamma
337 - 0.887 * logtstar
338 + 3.86 * gamma * gamma
339 - 6.45 * gamma * logtstar
340 - 0.275 * logtstar * logtstar
341 + 1.20 * gamma * gamma * logtstar
342 - 1.24 * gamma * logtstar * logtstar
343 - 0.164 * pow(logtstar,3);
344 } else if (tstar <= 1000) {
345 // for interval 0.04 to 1000, SSE = 0.282; R^2 = 1; RMSE = 0.033
346 om11 = 1.22 - 0.0343 * gamma
347 + (-0.769 + 0.232 * gamma) * logtstar
348 + (0.306 - 0.165 * gamma) * logtstar * logtstar
349 + (-0.0465 + 0.0388 * gamma) * pow(logtstar,3)
350 + (0.000614 - 0.00285 * gamma) * pow(logtstar,4)
351 + 0.000238 * pow(logtstar,5);
352 } else {
353 throw CanteraError("IonGasTransport::omega11_n64",
354 "tstar = {} is larger than 1000", tstar);
355 }
356 return om11;
357}
358
360{
361 update_T();
362 update_C();
363
364 // update the binary diffusion coefficients if necessary
365 if (!m_bindiff_ok) {
366 updateDiff_T();
367 }
368
369 double mmw = m_thermo->meanMolecularWeight();
370 double p = m_thermo->pressure();
371 if (m_nsp == 1) {
372 d[0] = m_bdiff(0,0) / p;
373 } else {
374 for (size_t k = 0; k < m_nsp; k++) {
375 if (k == m_kElectron) {
376 d[k] = 0.4 * m_kbt / ElectronCharge;
377 } else {
378 double sum2 = 0.0;
379 for (size_t j : m_kNeutral) {
380 if (j != k) {
381 sum2 += m_molefracs[j] / m_bdiff(j,k);
382 }
383 }
384 if (sum2 <= 0.0) {
385 d[k] = m_bdiff(k,k) / p;
386 } else {
387 d[k] = (mmw - m_molefracs[k] * m_mw[k])/(p * mmw * sum2);
388 }
389 }
390 }
391 }
392}
393
394void IonGasTransport::getMobilities(double* const mobi)
395{
396 update_T();
397 update_C();
398
399 // update the binary diffusion coefficients if necessary
400 if (!m_bindiff_ok) {
401 updateDiff_T();
402 }
403 double p = m_thermo->pressure();
404 for (size_t k = 0; k < m_nsp; k++) {
405 if (k == m_kElectron) {
406 mobi[k] = 0.4;
407 } else {
408 mobi[k] = 0.0;
409 }
410 }
411 for (size_t k : m_kIon) {
412 double sum = 0.0;
413 for (size_t j : m_kNeutral) {
414 double bmobi = m_bdiff(k,j) * ElectronCharge / m_kbt;
415 sum += m_molefracs[j] / bmobi;
416 }
417 mobi[k] = 1.0 / sum / p;
418 }
419}
420
421}
Monchick and Mason collision integrals.
Header file for class ThermoPhase, the base class for phases with thermodynamic properties,...
double getDouble(const string &key, double default_) const
If key exists, return it as a double, otherwise return default_.
Definition AnyMap.cpp:1580
Base class for exceptions thrown by Cantera classes.
void resize(size_t n, size_t m, double v=0.0) override
Resize the matrix.
virtual void setupCollisionParameters()
Setup parameters for a new kinetic-theory-based transport manager for low-density gases.
vector< double > m_mw
Local copy of the species molecular weights.
vector< double > m_molefracs
Vector of species mole fractions.
vector< double > m_quad_polar
Quadrupole polarizability.
bool m_visc_ok
Update boolean for mixture rule for the mixture viscosity.
DenseMatrix m_wratkj1
Holds square roots of molecular weight ratios.
virtual void fitDiffCoeffs(MMCollisionInt &integrals)
Generate polynomial fits to the binary diffusion coefficients.
vector< double > m_disp
Dispersion coefficient.
virtual void updateDiff_T()
Update the binary diffusion coefficients.
vector< double > m_sqvisc
vector of square root of species viscosities sqrt(kg /m /s).
DenseMatrix m_wratjk
Holds square roots of molecular weight ratios.
bool m_bindiff_ok
Update boolean for the binary diffusivities at unit pressure.
DenseMatrix m_epsilon
The effective well depth for (i,j) collisions.
DenseMatrix m_diam
hard-sphere diameter for (i,j) collision
vector< double > m_spwork
work space length = m_kk
int m_mode
Type of the polynomial fits to temperature.
virtual void updateViscosity_T()
Update the temperature-dependent viscosity terms.
double m_viscmix
Internal storage for the viscosity of the mixture (kg /m /s)
vector< double > m_visc
vector of species viscosities (kg /m /s).
DenseMatrix m_bdiff
Matrix of binary diffusion coefficients at the reference pressure and the current temperature Size is...
vector< vector< double > > m_diffcoeffs
Polynomial fits to the binary diffusivity of each species.
double m_kbt
Current value of Boltzmann constant times the temperature (Joules)
DenseMatrix m_reducedMass
This is the reduced mass of the interaction between species i and j.
int m_log_level
Level of verbose printing during initialization.
bool m_viscwt_ok
Update boolean for the weighting factors for the mixture viscosity.
vector< double > m_alpha
Polarizability of each species in the phase.
DenseMatrix m_phi
m_phi is a Viscosity Weighting Function. size = m_nsp * n_nsp
void setupCollisionIntegral()
Setup range for polynomial fits to collision integrals of Monchick & Mason .
void fitDiffCoeffs(MMCollisionInt &integrals) override
Generate polynomial fits to the binary diffusion coefficients.
void getMobilities(double *const mobi) override
The mobilities for ions in gas.
size_t m_kElectron
index of electron
double thermalConductivity() override
Returns the mixture thermal conductivity (W/m/K).
void getMixDiffCoeffs(double *const d) override
The mixture transport for ionized gas.
vector< double > m_om11_O2
polynomial of the collision integral for O2/O2-
double omega11_n64(const double tstar, const double gamma)
Collision integral of omega11 of n64 collision model.
void init(ThermoPhase *thermo, int mode, int log_level=-7) override
Initialize a transport manager.
DenseMatrix m_gamma
parameter of omega11 of n64
void setupN64()
setup parameters for n64 model
double viscosity() override
Viscosity of the mixture (kg/m/s).
vector< size_t > m_kIon
index of ions (exclude electron.)
double electricalConductivity() override
The electrical conductivity (Siemens/m).
vector< size_t > m_kNeutral
index of neutral species
vector< double > m_speciesCharge
electrical properties
Calculation of Collision integrals.
void update_T() override
Update the internal parameters whenever the temperature has changed.
bool m_spcond_ok
Update boolean for the species thermal conductivities.
double m_lambda
Internal storage for the calculated mixture thermal conductivity.
void updateCond_T()
Update the temperature dependent parts of the species thermal conductivities.
void update_C() override
Update the internal parameters whenever the concentrations have changed.
vector< double > m_cond
vector of species thermal conductivities (W/m /K)
bool m_condmix_ok
Update boolean for the mixture rule for the mixture thermal conductivity.
size_t nSpecies() const
Returns the number of species in the phase.
Definition Phase.h:231
double meanMolecularWeight() const
The mean molecular weight. Units: (kg/kmol)
Definition Phase.h:655
string speciesName(size_t k) const
Name of the species with index k.
Definition Phase.cpp:142
const vector< double > & molecularWeights() const
Return a const reference to the internal vector of molecular weights.
Definition Phase.cpp:395
size_t speciesIndex(const string &name) const
Returns the index of a species named 'name' within the Phase object.
Definition Phase.cpp:129
double molecularWeight(size_t k) const
Molecular weight of species k.
Definition Phase.cpp:383
virtual double pressure() const
Return the thermodynamic pressure (Pa).
Definition Phase.h:580
double charge(size_t k) const
Dimensionless electrical charge of a single molecule of species k The charge is normalized by the the...
Definition Phase.h:538
Base class for a phase with thermodynamic properties.
virtual double minTemp(size_t k=npos) const
Minimum temperature for which the thermodynamic data for the species or phase are valid.
virtual double maxTemp(size_t k=npos) const
Maximum temperature for which the thermodynamic data for the species are valid.
ThermoPhase * m_thermo
pointer to the object representing the phase
Definition Transport.h:437
size_t m_nsp
Number of species.
Definition Transport.h:440
ThermoPhase & thermo()
Phase object.
Definition Transport.h:103
AnyMap m_fittingErrors
Maximum errors associated with fitting pure species transport properties.
Definition Transport.h:443
This file contains definitions for utility functions and text for modules, inputfiles and logging,...
string vec2str(const vector< double > &v, const string &fmt, const string &sep)
Convert a vector to a string (separated by commas)
void writelogf(const char *fmt, const Args &... args)
Write a formatted message to the screen.
Definition global.h:191
void writelog(const string &fmt, const Args &... args)
Write a formatted message to the screen.
Definition global.h:171
R poly4(D x, R *c)
Evaluates a polynomial of order 4.
Definition utilities.h:153
R poly5(D x, R *c)
Templated evaluation of a polynomial of order 5.
Definition utilities.h:141
double polyfit(size_t n, size_t deg, const double *xp, const double *yp, const double *wp, double *pp)
Fits a polynomial function to a set of data points.
Definition polyfit.cpp:14
const double Boltzmann
Boltzmann constant [J/K].
Definition ct_defs.h:84
const double epsilon_0
Permittivity of free space [F/m].
Definition ct_defs.h:137
const double ElectronCharge
Elementary charge [C].
Definition ct_defs.h:90
const double Pi
Pi.
Definition ct_defs.h:68
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:180
double getElementWeight(const string &ename)
Get the atomic weight of an element.
Definition Elements.cpp:251
void multiply(const DenseMatrix &A, const double *const b, double *const prod)
Multiply A*b and return the result in prod. Uses BLAS routine DGEMV.
void warn_deprecated(const string &source, const AnyBase &node, const string &message)
A deprecation warning for syntax in an input file.
Definition AnyMap.cpp:1997
Contains declarations for string manipulation functions within Cantera.
Various templated functions that carry out common vector and polynomial operations (see Templated Arr...