16IonGasTransport::IonGasTransport() :
21void IonGasTransport::init(
ThermoPhase* thermo,
int mode,
int log_level)
26 if (m_mode == CK_Mode) {
28 "mode = CK_Mode, which is an outdated lower-order fit.");
30 m_log_level = log_level;
32 for (
size_t k = 0; k < m_nsp; k++) {
33 m_speciesCharge.push_back(m_thermo->charge(k));
37 size_t nElectronSpecies = 0;
38 for (
size_t k = 0; k < m_nsp; k++) {
39 if (m_thermo->molecularWeight(k) ==
40 m_thermo->atomicWeight(m_thermo->elementIndex(
"E")) &&
41 m_thermo->charge(k) == -1) {
46 if (nElectronSpecies > 1) {
48 "Found multiple electron species.");
52 for (
size_t k = 0; k < m_nsp; k++) {
53 if (m_speciesCharge[k] != 0){
54 if (k != m_kElectron) {
58 m_kNeutral.push_back(k);
63 const vector_fp temp{300.0, 400.0, 500.0, 600.0, 800.0, 1000.0,
64 1200.0, 1500.0, 2000.0, 2500.0, 3000.0, 4000.0};
65 const vector_fp om11_O2{120.0, 107.0, 98.1, 92.1, 83.0, 77.0,
66 72.6, 67.9, 62.7, 59.3, 56.7, 53.8};
69 m_om11_O2.resize(degree + 1);
70 polyfit(temp.size(), degree, temp.data(), om11_O2.data(),
71 w.data(), m_om11_O2.data());
73 setupCollisionParameters();
77 setupCollisionIntegral();
78 m_molefracs.resize(m_nsp);
79 m_spwork.resize(m_nsp);
81 m_sqvisc.resize(m_nsp);
82 m_phi.resize(m_nsp, m_nsp, 0.0);
83 m_bdiff.resize(m_nsp, m_nsp);
87 m_mw = m_thermo->molecularWeights();
89 m_wratjk.resize(m_nsp, m_nsp, 0.0);
90 m_wratkj1.resize(m_nsp, m_nsp, 0.0);
91 for (
size_t j = 0; j < m_nsp; j++) {
92 for (
size_t k = j; k < m_nsp; k++) {
93 m_wratjk(j,k) = sqrt(m_mw[j]/m_mw[k]);
94 m_wratjk(k,j) = sqrt(m_wratjk(j,k));
95 m_wratkj1(j,k) = sqrt(1.0 + m_mw[k]/m_mw[j]);
100double IonGasTransport::viscosity()
115 multiply(m_phi, m_molefracs.data(), m_spwork.data());
117 for (
size_t k : m_kNeutral) {
118 vismix += m_molefracs[k] * m_visc[k]/m_spwork[k];
124double IonGasTransport::thermalConductivity()
132 doublereal sum1 = 0.0, sum2 = 0.0;
133 for (
size_t k : m_kNeutral) {
134 sum1 += m_molefracs[k] * m_cond[k];
135 sum2 += m_molefracs[k] / m_cond[k];
137 m_lambda = 0.5*(sum1 + 1.0/sum2);
143double IonGasTransport::electricalConductivity()
146 getMobilities(&mobi[0]);
147 double p = m_thermo->pressure();
149 for (
size_t k : m_kIon) {
150 double ND_k = m_molefracs[k] * p / m_kbt;
151 sum += ND_k * std::abs(m_speciesCharge[k]) *
ElectronCharge * mobi[k];
153 if (m_kElectron !=
npos) {
154 sum += m_molefracs[m_kElectron] * p / m_kbt *
162 GasTransport::fitDiffCoeffs(integrals);
165 const size_t np = 50;
167 double dt = (m_thermo->maxTemp() - m_thermo->minTemp())/(np-1);
172 for (
size_t n = 0; n < np; n++) {
173 double t = m_thermo->minTemp() + dt*n;
179 double err = 0.0, relerr = 0.0,
180 mxerr = 0.0, mxrelerr = 0.0;
184 for (
size_t k = 0; k < m_nsp; k++) {
185 for (
size_t j = k; j < m_nsp; j++) {
186 if (m_alpha[k] == 0.0 || m_alpha[j] == 0.0 ||
187 k == m_kElectron || j == m_kElectron) {
190 if (m_speciesCharge[k] == 0) {
191 if (m_speciesCharge[j] == 0) {
195 if (m_speciesCharge[j] != 0) {
199 for (
size_t n = 0; n < np; n++) {
200 double t = m_thermo->minTemp() + dt*n;
201 double eps = m_epsilon(j,k);
203 double sigma = m_diam(j,k);
204 double om11 = omega11_n64(tstar, m_gamma(j,k))
205 *
Pi * sigma * sigma;
209 if ((k == m_thermo->speciesIndex(
"O2-") ||
210 j == m_thermo->speciesIndex(
"O2-")) &&
211 (k == m_thermo->speciesIndex(
"O2") ||
212 j == m_thermo->speciesIndex(
"O2"))) {
213 om11 =
poly5(t, m_om11_O2.data()) / 1e20;
215 double diffcoeff = 3.0/16.0 * sqrt(2.0 *
Pi/m_reducedMass(k,j))
218 diff[n] = diffcoeff/pow(t, 1.5);
219 w[n] = 1.0/(diff[n]*diff[n]);
221 polyfit(np, degree, tlog.data(), diff.data(), w.data(), c.data());
223 for (
size_t n = 0; n < np; n++) {
225 double t = exp(tlog[n]);
226 double pre = pow(t, 1.5);
228 fit = pre *
poly4(tlog[n], c.data());
231 mxerr = std::max(mxerr, fabs(err));
232 mxrelerr = std::max(mxrelerr, fabs(relerr));
234 size_t sum = k * (k + 1) / 2;
235 m_diffcoeffs[k*m_nsp+j-sum] = c;
236 if (m_log_level >= 2) {
237 writelog(m_thermo->speciesName(k) +
"__" +
238 m_thermo->speciesName(j) +
": [" +
vec2str(c) +
"]\n");
244 writelogf(
"Maximum binary diffusion coefficient absolute error:"
246 writelogf(
"Maximum binary diffusion coefficient relative error:"
251void IonGasTransport::setupN64()
253 m_gamma.resize(m_nsp, m_nsp, 0.0);
254 for (
size_t i : m_kIon) {
255 for (
size_t j : m_kNeutral) {
256 if (m_alpha[j] != 0.0 && m_alpha[i] != 0.0) {
257 double r_alpha = m_alpha[i] / m_alpha[j];
259 double alphaA_i = m_alpha[i] * 1e30;
260 double alphaA_j = m_alpha[j] * 1e30;
262 double xi = alphaA_i / (m_speciesCharge[i] * m_speciesCharge[i] *
263 (1.0 + pow(2 * r_alpha, 2./3.)) * sqrt(alphaA_j));
267 double kappa = 0.095;
268 m_diam(i,j) = K1 * (pow(m_alpha[i], 1./3.) + pow(m_alpha[j], 1./3.)) /
269 pow(alphaA_i * alphaA_j * (1.0 + 1.0 / xi), kappa);
275 m_speciesCharge[i] * m_speciesCharge[i] *
276 m_alpha[j] * (1.0 + xi) /
278 if (epsilon != 0.0) {
279 m_epsilon(i,j) = epsilon;
285 if (m_disp[j] == 0.0) {
286 m_disp[j] = exp(1.8846*log(alphaA_j)-0.4737)* 1e-50;
288 if (m_quad_polar[j] == 0.0) {
289 m_quad_polar[j] = 2.0 * m_disp[j];
292 if (m_disp[i] == 0.0) {
293 if (m_speciesCharge[i] > 0) {
294 m_disp[i] = exp(1.8853*log(alphaA_i)+0.2682)* 1e-50;
296 m_disp[i] = exp(3.2246*log(alphaA_i)-3.2397)* 1e-50;
304 double C6 = 2.0 * m_disp[i] * m_disp[j] /
305 (1.0/r_alpha * m_disp[i] + r_alpha * m_disp[j]);
307 m_gamma(i,j) = (2.0 / pow(m_speciesCharge[i],2) * C6 + m_quad_polar[j]) /
308 (m_alpha[j] * m_diam(i,j) * m_diam(i,j));
311 m_diam(j,i) = m_diam(i,j);
312 m_epsilon(j,i) = m_epsilon(i,j);
313 m_gamma(j,i) = m_gamma(i,j);
319double IonGasTransport::omega11_n64(
const double tstar,
const double gamma)
321 double logtstar = log(tstar);
325 "tstar = {} is smaller than 0.01", tstar);
326 }
else if (tstar <= 0.04) {
328 om11 = 2.97 - 12.0 * gamma
330 + 3.86 * gamma * gamma
331 - 6.45 * gamma * logtstar
332 - 0.275 * logtstar * logtstar
333 + 1.20 * gamma * gamma * logtstar
334 - 1.24 * gamma * logtstar * logtstar
335 - 0.164 * pow(logtstar,3);
336 }
else if (tstar <= 1000) {
338 om11 = 1.22 - 0.0343 * gamma
339 + (-0.769 + 0.232 * gamma) * logtstar
340 + (0.306 - 0.165 * gamma) * logtstar * logtstar
341 + (-0.0465 + 0.0388 * gamma) * pow(logtstar,3)
342 + (0.000614 - 0.00285 * gamma) * pow(logtstar,4)
343 + 0.000238 * pow(logtstar,5);
346 "tstar = {} is larger than 1000", tstar);
351void IonGasTransport::getMixDiffCoeffs(
double*
const d)
361 double mmw = m_thermo->meanMolecularWeight();
362 double p = m_thermo->pressure();
364 d[0] = m_bdiff(0,0) / p;
366 for (
size_t k = 0; k < m_nsp; k++) {
367 if (k == m_kElectron) {
371 for (
size_t j : m_kNeutral) {
373 sum2 += m_molefracs[j] / m_bdiff(j,k);
377 d[k] = m_bdiff(k,k) / p;
379 d[k] = (mmw - m_molefracs[k] * m_mw[k])/(p * mmw * sum2);
386void IonGasTransport::getMobilities(
double*
const mobi)
395 double p = m_thermo->pressure();
396 for (
size_t k = 0; k < m_nsp; k++) {
397 if (k == m_kElectron) {
403 for (
size_t k : m_kIon) {
405 for (
size_t j : m_kNeutral) {
407 sum += m_molefracs[j] / bmobi;
409 mobi[k] = 1.0 / sum / p;
Monk and Monchick collision integrals.
Header file for class ThermoPhase, the base class for phases with thermodynamic properties,...
Base class for exceptions thrown by Cantera classes.
Calculation of Collision integrals.
size_t nSpecies() const
Returns the number of species in the phase.
Base class for a phase with thermodynamic properties.
This file contains definitions for utility functions and text for modules, inputfiles,...
void writelog(const std::string &fmt, const Args &... args)
Write a formatted message to the screen.
void writelogf(const char *fmt, const Args &... args)
Write a formatted message to the screen.
Namespace for the Cantera kernel.
const size_t npos
index returned by functions to indicate "no position"
const double Boltzmann
Boltzmann constant [J/K].
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.
std::string vec2str(const vector_fp &v, const std::string &fmt="%g", const std::string &sep=", ")
Convert a vector to a string (separated by commas)
R poly5(D x, R *c)
Templated evaluation of a polynomial of order 5.
std::vector< double > vector_fp
Turn on the use of stl vectors for the basic array type within cantera Vector of doubles.
const double epsilon_0
Permittivity of free space [F/m].
const double ElectronCharge
Elementary charge [C].
R poly4(D x, R *c)
Evaluates a polynomial of order 4.
double polyfit(size_t n, size_t deg, const double *x, const double *y, const double *w, double *p)
Fits a polynomial function to a set of data points.
Contains declarations for string manipulation functions within Cantera.
Various templated functions that carry out common vector operations (see Templated Utility Functions)...