Cantera  4.0.0a1
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);
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
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,...
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 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 .
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...