Cantera
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(shared_ptr<ThermoPhase> thermo, int mode)
18{
20 m_nsp = m_thermo->nSpecies();
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 // make a local copy of species charge
27 for (size_t k = 0; k < m_nsp; k++) {
28 m_speciesCharge.push_back(m_thermo->charge(k));
29 }
30
31 // Find the index of electron
32 size_t nElectronSpecies = 0;
33 for (size_t k = 0; k < m_nsp; k++) {
34 if (m_thermo->molecularWeight(k) == getElementWeight("E") &&
35 m_thermo->charge(k) == -1)
36 {
37 m_kElectron = k;
38 nElectronSpecies++;
39 }
40 }
41 if (nElectronSpecies > 1) {
42 throw CanteraError("IonGasTransport::init",
43 "Found multiple electron species.");
44 }
45
46 // Find indices for charge of species
47 for (size_t k = 0; k < m_nsp; k++) {
48 if (m_speciesCharge[k] != 0){
49 if (k != m_kElectron) {
50 m_kIon.push_back(k);
51 }
52 } else {
53 m_kNeutral.push_back(k);
54 }
55 }
56 // set up O2/O2- collision integral [A^2]
57 // Data taken from Prager (2005)
58 const vector<double> temp{300.0, 400.0, 500.0, 600.0, 800.0, 1000.0,
59 1200.0, 1500.0, 2000.0, 2500.0, 3000.0, 4000.0};
60 const vector<double> om11_O2{120.0, 107.0, 98.1, 92.1, 83.0, 77.0,
61 72.6, 67.9, 62.7, 59.3, 56.7, 53.8};
62 int degree = 5;
63 m_om11_O2.resize(degree + 1);
64 polyfit(degree, temp, om11_O2, {}, m_om11_O2);
65 // set up Monchick and Mason parameters
67 // set up n64 parameters
68 setupN64();
69 // setup collision integrals
71 m_molefracs.resize(m_nsp);
72 m_spwork.resize(m_nsp);
73 m_visc.resize(m_nsp);
74 m_sqvisc.resize(m_nsp);
75 m_phi.resize(m_nsp, m_nsp, 0.0);
76 m_bdiff.resize(m_nsp, m_nsp);
77 m_cond.resize(m_nsp);
78
79 // make a local copy of the molecular weights
80 m_mw.resize(m_nsp);
81 m_thermo->getMolecularWeights(m_mw);
82
83 m_wratjk.resize(m_nsp, m_nsp, 0.0);
84 m_wratkj1.resize(m_nsp, m_nsp, 0.0);
85 for (size_t j = 0; j < m_nsp; j++) {
86 for (size_t k = j; k < m_nsp; k++) {
87 m_wratjk(j,k) = sqrt(m_mw[j]/m_mw[k]);
88 m_wratjk(k,j) = sqrt(m_wratjk(j,k));
89 m_wratkj1(j,k) = sqrt(1.0 + m_mw[k]/m_mw[j]);
90 }
91 }
92}
93
95{
96 update_T();
97 update_C();
98
99 if (m_visc_ok) {
100 return m_viscmix;
101 }
102
103 double vismix = 0.0;
104 // update m_visc and m_phi if necessary
105 if (!m_viscwt_ok) {
107 }
108
110
111 for (size_t k : m_kNeutral) {
112 vismix += m_molefracs[k] * m_visc[k]/m_spwork[k]; //denom;
113 }
114 m_viscmix = vismix;
115 return vismix;
116}
117
119{
120 update_T();
121 update_C();
122 if (!m_spcond_ok) {
123 updateCond_T();
124 }
125 if (!m_condmix_ok) {
126 double sum1 = 0.0, sum2 = 0.0;
127 for (size_t k : m_kNeutral) {
128 sum1 += m_molefracs[k] * m_cond[k];
129 sum2 += m_molefracs[k] / m_cond[k];
130 }
131 m_lambda = 0.5*(sum1 + 1.0/sum2);
132 m_condmix_ok = true;
133 }
134 return m_lambda;
135}
136
138{
139 vector<double> mobi(m_nsp);
140 getMobilities(mobi);
141 double p = m_thermo->pressure();
142 double sum = 0.0;
143 for (size_t k : m_kIon) {
144 double ND_k = m_molefracs[k] * p / m_kbt;
145 sum += ND_k * std::abs(m_speciesCharge[k]) * ElectronCharge * mobi[k];
146 }
147 if (m_kElectron != npos) {
148 sum += m_molefracs[m_kElectron] * p / m_kbt *
150 }
151 return sum;
152}
153
155{
157
158 // number of points to use in generating fit data
159 const size_t np = 50;
160 int degree = 4;
161 double dt = (m_thermo->maxTemp() - m_thermo->minTemp())/(np-1);
162 vector<double> tlog(np);
163 vector<double> w(np);
164
165 // generate array of log(t) values
166 for (size_t n = 0; n < np; n++) {
167 double t = m_thermo->minTemp() + dt*n;
168 tlog[n] = log(t);
169 }
170
171 // vector of polynomial coefficients
172 vector<double> c(degree + 1);
173 double err = 0.0, relerr = 0.0,
174 mxerr = 0.0, mxrelerr = 0.0;
175
176 vector<double> diff(np + 1);
177 // The array order still not ideal
178 for (size_t k = 0; k < m_nsp; k++) {
179 for (size_t j = k; j < m_nsp; j++) {
180 if (m_alpha[k] == 0.0 || m_alpha[j] == 0.0 ||
181 k == m_kElectron || j == m_kElectron) {
182 continue;
183 }
184 if (m_speciesCharge[k] == 0) {
185 if (m_speciesCharge[j] == 0) {
186 continue;
187 }
188 } else {
189 if (m_speciesCharge[j] != 0) {
190 continue;
191 }
192 }
193 for (size_t n = 0; n < np; n++) {
194 double t = m_thermo->minTemp() + dt*n;
195 double eps = m_epsilon(j,k);
196 double tstar = Boltzmann * t/eps;
197 double sigma = m_diam(j,k);
198 double om11 = omega11_n64(tstar, m_gamma(j,k))
199 * Pi * sigma * sigma;
200 // Stockmayer-(n,6,4) model is not suitable for collision
201 // between O2/O2- due to resonant charge transfer.
202 // Therefore, the experimental collision data is used instead.
203 if ((k == m_thermo->speciesIndex("O2-", false) ||
204 j == m_thermo->speciesIndex("O2-", false)) &&
205 (k == m_thermo->speciesIndex("O2", false) ||
206 j == m_thermo->speciesIndex("O2", false))) {
207 om11 = poly5(t, m_om11_O2) / 1e20;
208 }
209 double diffcoeff = 3.0/16.0 * sqrt(2.0 * Pi/m_reducedMass(k,j))
210 * pow(Boltzmann * t, 1.5) / om11;
211
212 diff[n] = diffcoeff/pow(t, 1.5);
213 w[n] = 1.0/(diff[n]*diff[n]);
214 }
215 polyfit(degree, tlog, diff, w, c);
216
217 for (size_t n = 0; n < np; n++) {
218 double val, fit;
219 double t = exp(tlog[n]);
220 double pre = pow(t, 1.5);
221 val = pre * diff[n];
222 fit = pre * poly4(tlog[n], c);
223 err = fit - val;
224 relerr = err/val;
225 mxerr = std::max(mxerr, fabs(err));
226 mxrelerr = std::max(mxrelerr, fabs(relerr));
227 }
228 size_t sum = k * (k + 1) / 2;
229 m_diffcoeffs[k*m_nsp+j-sum] = c;
230 }
231 }
232 m_fittingErrors["diff-coeff-max-abs-error"] =
233 std::max(m_fittingErrors.getDouble("diff-coeff-max-abs-error", 0.0),
234 mxerr);
235 m_fittingErrors["diff-coeff-max-rel-error"] =
236 std::max(m_fittingErrors.getDouble("diff-coeff-max-rel-error", 0.0),
237 mxrelerr);
238}
239
241{
242 m_gamma.resize(m_nsp, m_nsp, 0.0);
243 for (size_t i : m_kIon) {
244 for (size_t j : m_kNeutral) {
245 if (m_alpha[j] != 0.0 && m_alpha[i] != 0.0) {
246 double r_alpha = m_alpha[i] / m_alpha[j];
247 // save a copy of polarizability in Angstrom
248 double alphaA_i = m_alpha[i] * 1e30;
249 double alphaA_j = m_alpha[j] * 1e30;
250 // The ratio of dispersion to induction forces
251 double xi = alphaA_i / (m_speciesCharge[i] * m_speciesCharge[i] *
252 (1.0 + pow(2 * r_alpha, 2./3.)) * sqrt(alphaA_j));
253
254 // the collision diameter
255 double K1 = 1.767;
256 double kappa = 0.095;
257 m_diam(i,j) = K1 * (pow(m_alpha[i], 1./3.) + pow(m_alpha[j], 1./3.)) /
258 pow(alphaA_i * alphaA_j * (1.0 + 1.0 / xi), kappa);
259
260 // The original K2 is 0.72, but Han et al. suggested that K2 = 1.44
261 // for better fit.
262 double K2 = 1.44;
263 double epsilon = K2 * ElectronCharge * ElectronCharge *
265 m_alpha[j] * (1.0 + xi) /
266 (8 * Pi * epsilon_0 * pow(m_diam(i,j),4));
267 if (epsilon != 0.0) {
268 m_epsilon(i,j) = epsilon;
269 }
270
271 // Calculate dispersion coefficient and quadrupole polarizability
272 // from curve fitting if not available.
273 // Neutrals
274 if (m_disp[j] == 0.0) {
275 m_disp[j] = exp(1.8846*log(alphaA_j)-0.4737)* 1e-50;
276 }
277 if (m_quad_polar[j] == 0.0) {
278 m_quad_polar[j] = 2.0 * m_disp[j];
279 }
280 // Ions
281 if (m_disp[i] == 0.0) {
282 if (m_speciesCharge[i] > 0) {
283 m_disp[i] = exp(1.8853*log(alphaA_i)+0.2682)* 1e-50;
284 } else {
285 m_disp[i] = exp(3.2246*log(alphaA_i)-3.2397)* 1e-50;
286 }
287 }
288
289 // The binary dispersion coefficient is determined by the combination rule
290 // Reference:
291 // Tang, K. T. "Dynamic polarizabilities and van der Waals coefficients."
292 // Physical Review 177.1 (1969): 108.
293 double C6 = 2.0 * m_disp[i] * m_disp[j] /
294 (1.0/r_alpha * m_disp[i] + r_alpha * m_disp[j]);
295
296 m_gamma(i,j) = (2.0 / pow(m_speciesCharge[i],2) * C6 + m_quad_polar[j]) /
297 (m_alpha[j] * m_diam(i,j) * m_diam(i,j));//Dimensionless
298
299 // properties are symmetric
300 m_diam(j,i) = m_diam(i,j);
301 m_epsilon(j,i) = m_epsilon(i,j);
302 m_gamma(j,i) = m_gamma(i,j);
303 }
304 }
305 }
306}
307
308double IonGasTransport::omega11_n64(const double tstar, const double gamma)
309{
310 double logtstar = log(tstar);
311 double om11 = 0.0;
312 if (tstar < 0.01) {
313 throw CanteraError("IonGasTransport::omega11_n64",
314 "tstar = {} is smaller than 0.01", tstar);
315 } else if (tstar <= 0.04) {
316 // for interval 0.01 to 0.04, SSE = 0.006; R^2 = 1; RMSE = 0.020
317 om11 = 2.97 - 12.0 * gamma
318 - 0.887 * logtstar
319 + 3.86 * gamma * gamma
320 - 6.45 * gamma * logtstar
321 - 0.275 * logtstar * logtstar
322 + 1.20 * gamma * gamma * logtstar
323 - 1.24 * gamma * logtstar * logtstar
324 - 0.164 * pow(logtstar,3);
325 } else if (tstar <= 1000) {
326 // for interval 0.04 to 1000, SSE = 0.282; R^2 = 1; RMSE = 0.033
327 om11 = 1.22 - 0.0343 * gamma
328 + (-0.769 + 0.232 * gamma) * logtstar
329 + (0.306 - 0.165 * gamma) * logtstar * logtstar
330 + (-0.0465 + 0.0388 * gamma) * pow(logtstar,3)
331 + (0.000614 - 0.00285 * gamma) * pow(logtstar,4)
332 + 0.000238 * pow(logtstar,5);
333 } else {
334 throw CanteraError("IonGasTransport::omega11_n64",
335 "tstar = {} is larger than 1000", tstar);
336 }
337 return om11;
338}
339
341{
342 checkArraySize("IonGasTransport::getMixDiffCoeffs", d.size(), m_nsp);
343 update_T();
344 update_C();
345
346 // update the binary diffusion coefficients if necessary
347 if (!m_bindiff_ok) {
348 updateDiff_T();
349 }
350
351 double mmw = m_thermo->meanMolecularWeight();
352 double p = m_thermo->pressure();
353 if (m_nsp == 1) {
354 d[0] = m_bdiff(0,0) / p;
355 } else {
356 for (size_t k = 0; k < m_nsp; k++) {
357 if (k == m_kElectron) {
358 d[k] = 0.4 * m_kbt / ElectronCharge;
359 } else {
360 double sum2 = 0.0;
361 for (size_t j : m_kNeutral) {
362 if (j != k) {
363 sum2 += m_molefracs[j] / m_bdiff(j,k);
364 }
365 }
366 if (sum2 <= 0.0) {
367 d[k] = m_bdiff(k,k) / p;
368 } else {
369 d[k] = (mmw - m_molefracs[k] * m_mw[k])/(p * mmw * sum2);
370 }
371 }
372 }
373 }
374}
375
376void IonGasTransport::getMobilities(span<double> mobi)
377{
378 checkArraySize("IonGasTransport::getMobilities", mobi.size(), m_nsp);
379 update_T();
380 update_C();
381
382 // update the binary diffusion coefficients if necessary
383 if (!m_bindiff_ok) {
384 updateDiff_T();
385 }
386 double p = m_thermo->pressure();
387 for (size_t k = 0; k < m_nsp; k++) {
388 if (k == m_kElectron) {
389 mobi[k] = 0.4;
390 } else {
391 mobi[k] = 0.0;
392 }
393 }
394 for (size_t k : m_kIon) {
395 double sum = 0.0;
396 for (size_t j : m_kNeutral) {
397 double bmobi = m_bdiff(k,j) * ElectronCharge / m_kbt;
398 sum += m_molefracs[j] / bmobi;
399 }
400 mobi[k] = 1.0 / sum / p;
401 }
402}
403
404}
Monchick and Mason collision integrals.
Header file for class ThermoPhase, the base class for phases with thermodynamic properties,...
Base class for exceptions thrown by Cantera classes.
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 normalized by the square of the elementary charge [m⁵].
virtual void updateDiff_T()
Update the binary diffusion coefficients.
vector< double > m_sqvisc
vector of square root of species viscosities.
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 [J] for (i,j) collisions.
DenseMatrix m_diam
hard-sphere diameter [m] for (i,j) collision
vector< double > m_spwork
work space length = m_nsp
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 [Pa·s].
vector< double > m_visc
vector of species viscosities [Pa·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 [J].
DenseMatrix m_reducedMass
This is the reduced mass [kg] of the interaction between species i and j.
bool m_viscwt_ok
Update boolean for the weighting factors for the mixture viscosity.
vector< double > m_alpha
Polarizability [m³] of each species in the phase.
DenseMatrix m_phi
Viscosity weighting function. size = m_nsp * m_nsp.
void setupCollisionIntegral()
Setup range for polynomial fits to collision integrals of Monchick & Mason monchick1961.
void fitDiffCoeffs(MMCollisionInt &integrals) override
Generate polynomial fits to the binary diffusion coefficients.
size_t m_kElectron
index of electron
double thermalConductivity() override
Returns the mixture thermal conductivity [W/m/K].
vector< double > m_om11_O2
polynomial of the collision integral for O2/O2-
void init(shared_ptr< ThermoPhase > thermo, int mode) override
Initialize a transport manager.
double omega11_n64(const double tstar, const double gamma)
Collision integral of omega11 of n64 collision model.
void getMobilities(span< double > mobi) override
The mobilities for ions in gas.
DenseMatrix m_gamma
parameter of omega11 of n64
void setupN64()
setup parameters for n64 model
double viscosity() override
Viscosity [Pa·s] of the mixture.
void getMixDiffCoeffs(span< double > d) override
The mixture transport for ionized gas.
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 [W/m/K].
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.
shared_ptr< ThermoPhase > m_thermo
pointer to the object representing the phase
Definition Transport.h:432
size_t m_nsp
Number of species in the phase.
Definition Transport.h:435
ThermoPhase & thermo()
Phase object.
Definition Transport.h:111
AnyMap m_fittingErrors
Maximum errors associated with fitting pure species transport properties.
Definition Transport.h:438
This file contains definitions for utility functions and text for modules, inputfiles and logging,...
auto poly5(D x, const C &c)
Templated evaluation of a polynomial of order 5.
Definition utilities.h:163
auto poly4(D x, const C &c)
Evaluates a polynomial of order 4.
Definition utilities.h:179
double polyfit(size_t deg, span< const double > x, span< const double > y, span< const double > w, span< double > p)
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:87
const double epsilon_0
Permittivity of free space [F/m].
Definition ct_defs.h:140
const double ElectronCharge
Elementary charge [C].
Definition ct_defs.h:93
const double Pi
Pi.
Definition ct_defs.h:71
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
double getElementWeight(const string &ename)
Get the atomic weight of an element.
Definition Elements.cpp:251
void multiply(const DenseMatrix &A, span< const double > b, span< double > prod)
Multiply A*b and return the result in prod. Uses BLAS routine DGEMV.
void checkArraySize(const char *procedure, size_t available, size_t required)
Wrapper for throwing ArraySizeError.
Contains declarations for string manipulation functions within Cantera.
Various templated functions that carry out common vector and polynomial operations (see Templated Arr...