13 IonGasTransport::IonGasTransport() :
18 void IonGasTransport::init(
thermo_t* thermo,
int mode,
int log_level)
23 if (m_mode == CK_Mode) {
25 "mode = CK_Mode, which is an outdated lower-order fit.");
27 m_log_level = log_level;
29 for (
size_t k = 0; k < m_nsp; k++) {
30 m_speciesCharge.push_back(m_thermo->charge(k));
34 if (m_thermo->speciesIndex(
"E") !=
npos ) {
35 m_kElectron = m_thermo->speciesIndex(
"E");
39 for (
size_t k = 0; k < m_nsp; k++) {
40 if (m_speciesCharge[k] != 0){
41 if (k != m_kElectron) {
45 m_kNeutral.push_back(k);
50 const vector_fp temp{300.0, 400.0, 500.0, 600.0, 800.0, 1000.0,
51 1200.0, 1500.0, 2000.0, 2500.0, 3000.0, 4000.0};
52 const vector_fp om11_O2{120.0, 107.0, 98.1, 92.1, 83.0, 77.0,
53 72.6, 67.9, 62.7, 59.3, 56.7, 53.8};
56 m_om11_O2.resize(degree + 1);
57 polyfit(temp.size(), degree, temp.data(), om11_O2.data(),
58 w.data(), m_om11_O2.data());
60 setupCollisionParameters();
64 setupCollisionIntegral();
65 m_molefracs.resize(m_nsp);
66 m_spwork.resize(m_nsp);
68 m_sqvisc.resize(m_nsp);
69 m_phi.resize(m_nsp, m_nsp, 0.0);
70 m_bdiff.resize(m_nsp, m_nsp);
74 m_mw = m_thermo->molecularWeights();
76 m_wratjk.resize(m_nsp, m_nsp, 0.0);
77 m_wratkj1.resize(m_nsp, m_nsp, 0.0);
78 for (
size_t j = 0; j < m_nsp; j++) {
79 for (
size_t k = j; k < m_nsp; k++) {
80 m_wratjk(j,k) = sqrt(m_mw[j]/m_mw[k]);
81 m_wratjk(k,j) = sqrt(m_wratjk(j,k));
82 m_wratkj1(j,k) = sqrt(1.0 + m_mw[k]/m_mw[j]);
87 double IonGasTransport::viscosity()
102 multiply(m_phi, m_molefracs.data(), m_spwork.data());
104 for (
size_t k : m_kNeutral) {
105 vismix += m_molefracs[k] * m_visc[k]/m_spwork[k];
111 double IonGasTransport::thermalConductivity()
119 doublereal sum1 = 0.0, sum2 = 0.0;
120 for (
size_t k : m_kNeutral) {
121 sum1 += m_molefracs[k] * m_cond[k];
122 sum2 += m_molefracs[k] / m_cond[k];
124 m_lambda = 0.5*(sum1 + 1.0/sum2);
130 double IonGasTransport::electricalConductivity()
133 getMobilities(&mobi[0]);
134 double p = m_thermo->pressure();
136 for (
size_t k : m_kIon) {
137 double ND_k = m_molefracs[k] * p / m_kbt;
138 sum += ND_k * std::abs(m_speciesCharge[k]) *
ElectronCharge * mobi[k];
140 if (m_kElectron !=
npos) {
141 sum += m_molefracs[m_kElectron] * p / m_kbt *
149 GasTransport::fitDiffCoeffs(integrals);
152 const size_t np = 50;
154 double dt = (m_thermo->maxTemp() - m_thermo->minTemp())/(np-1);
159 for (
size_t n = 0; n < np; n++) {
160 double t = m_thermo->minTemp() + dt*n;
166 double err = 0.0, relerr = 0.0,
167 mxerr = 0.0, mxrelerr = 0.0;
171 for (
size_t k = 0; k < m_nsp; k++) {
172 for (
size_t j = k; j < m_nsp; j++) {
173 if (m_alpha[k] == 0.0 || m_alpha[j] == 0.0 ||
174 k == m_kElectron || j == m_kElectron) {
177 if (m_speciesCharge[k] == 0) {
178 if (m_speciesCharge[j] == 0) {
182 if (m_speciesCharge[j] != 0) {
186 for (
size_t n = 0; n < np; n++) {
187 double t = m_thermo->minTemp() + dt*n;
188 double eps = m_epsilon(j,k);
190 double sigma = m_diam(j,k);
191 double om11 = omega11_n64(tstar, m_gamma(j,k))
192 *
Pi * sigma * sigma;
196 if ((k == m_thermo->speciesIndex(
"O2-") ||
197 j == m_thermo->speciesIndex(
"O2-")) &&
198 (k == m_thermo->speciesIndex(
"O2") ||
199 j == m_thermo->speciesIndex(
"O2"))) {
200 om11 =
poly5(t, m_om11_O2.data()) / 1e20;
202 double diffcoeff = 3.0/16.0 * sqrt(2.0 *
Pi/m_reducedMass(k,j))
205 diff[n] = diffcoeff/pow(t, 1.5);
206 w[n] = 1.0/(diff[n]*diff[n]);
208 polyfit(np, degree, tlog.data(), diff.data(), w.data(), c.data());
210 for (
size_t n = 0; n < np; n++) {
212 double t = exp(tlog[n]);
213 double pre = pow(t, 1.5);
215 fit = pre *
poly4(tlog[n], c.data());
218 mxerr = std::max(mxerr, fabs(err));
219 mxrelerr = std::max(mxrelerr, fabs(relerr));
221 size_t sum = k * (k + 1) / 2;
222 m_diffcoeffs[k*m_nsp+j-sum] = c;
223 if (m_log_level >= 2) {
224 writelog(m_thermo->speciesName(k) +
"__" +
225 m_thermo->speciesName(j) +
": [" +
vec2str(c) +
"]\n");
231 writelogf(
"Maximum binary diffusion coefficient absolute error:"
233 writelogf(
"Maximum binary diffusion coefficient relative error:"
238 void IonGasTransport::setupN64()
240 m_gamma.resize(m_nsp, m_nsp, 0.0);
241 for (
size_t i : m_kIon) {
242 for (
size_t j : m_kNeutral) {
243 if (m_alpha[j] != 0.0 && m_alpha[i] != 0.0) {
244 double r_alpha = m_alpha[i] / m_alpha[j];
246 double alphaA_i = m_alpha[i] * 1e30;
247 double alphaA_j = m_alpha[j] * 1e30;
249 double xi = alphaA_i / (m_speciesCharge[i] * m_speciesCharge[i] *
250 (1.0 + pow(2 * r_alpha, 2./3.)) * sqrt(alphaA_j));
254 double kappa = 0.095;
255 m_diam(i,j) = K1 * (pow(m_alpha[i], 1./3.) + pow(m_alpha[j], 1./3.)) /
256 pow(alphaA_i * alphaA_j * (1.0 + 1.0 / xi), kappa);
262 m_speciesCharge[i] * m_speciesCharge[i] *
263 m_alpha[j] * (1.0 + xi) /
265 if (epsilon != 0.0) {
266 m_epsilon(i,j) = epsilon;
272 if (m_disp[j] == 0.0) {
273 m_disp[j] = exp(1.8846*log(alphaA_j)-0.4737)* 1e-50;
275 if (m_quad_polar[j] == 0.0) {
276 m_quad_polar[j] = 2.0 * m_disp[j];
279 if (m_disp[i] == 0.0) {
280 if (m_speciesCharge[i] > 0) {
281 m_disp[i] = exp(1.8853*log(alphaA_i)+0.2682)* 1e-50;
283 m_disp[i] = exp(3.2246*log(alphaA_i)-3.2397)* 1e-50;
291 double C6 = 2.0 * m_disp[i] * m_disp[j] /
292 (1.0/r_alpha * m_disp[i] + r_alpha * m_disp[j]);
294 m_gamma(i,j) = (2.0 / pow(m_speciesCharge[i],2) * C6 + m_quad_polar[j]) /
295 (m_alpha[j] * m_diam(i,j) * m_diam(i,j));
298 m_diam(j,i) = m_diam(i,j);
299 m_epsilon(j,i) = m_epsilon(i,j);
300 m_gamma(j,i) = m_gamma(i,j);
306 double IonGasTransport::omega11_n64(
const double tstar,
const double gamma)
308 double logtstar = log(tstar);
312 "tstar = {} is smaller than 0.01", tstar);
313 }
else if (tstar <= 0.04) {
315 om11 = 2.97 - 12.0 * gamma
317 + 3.86 * gamma * gamma
318 - 6.45 * gamma * logtstar
319 - 0.275 * logtstar * logtstar
320 + 1.20 * gamma * gamma * logtstar
321 - 1.24 * gamma * logtstar * logtstar
322 - 0.164 * pow(logtstar,3);
323 }
else if (tstar <= 1000) {
325 om11 = 1.22 - 0.0343 * gamma
326 + (-0.769 + 0.232 * gamma) * logtstar
327 + (0.306 - 0.165 * gamma) * logtstar * logtstar
328 + (-0.0465 + 0.0388 * gamma) * pow(logtstar,3)
329 + (0.000614 - 0.00285 * gamma) * pow(logtstar,4)
330 + 0.000238 * pow(logtstar,5);
333 "tstar = {} is larger than 1000", tstar);
338 void IonGasTransport::getMixDiffCoeffs(
double*
const d)
348 double mmw = m_thermo->meanMolecularWeight();
349 double p = m_thermo->pressure();
351 d[0] = m_bdiff(0,0) / p;
353 for (
size_t k = 0; k < m_nsp; k++) {
354 if (k == m_kElectron) {
358 for (
size_t j : m_kNeutral) {
360 sum2 += m_molefracs[j] / m_bdiff(j,k);
364 d[k] = m_bdiff(k,k) / p;
366 d[k] = (mmw - m_molefracs[k] * m_mw[k])/(p * mmw * sum2);
373 void IonGasTransport::getMobilities(
double*
const mobi)
382 double p = m_thermo->pressure();
383 for (
size_t k = 0; k < m_nsp; k++) {
384 if (k == m_kElectron) {
390 for (
size_t k : m_kIon) {
392 for (
size_t j : m_kNeutral) {
394 sum += m_molefracs[j] / bmobi;
396 mobi[k] = 1.0 / sum / p;
Monk and Monchick collision integrals.
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.
const size_t npos
index returned by functions to indicate "no position"
const double Boltzmann
Boltzmann constant [J/K].
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].
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.
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, 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.
R poly4(D x, R *c)
Evaluates a polynomial of order 4.
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.
Contains declarations for string manipulation functions within Cantera.