| Cantera 3.0.0

Loading...
Searching...
No Matches
GasTransport.cpp
Go to the documentation of this file.
1//! @file GasTransport.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
6#include "cantera/transport/GasTransport.h"
7#include "MMCollisionInt.h"
8#include "cantera/base/stringUtils.h"
9#include "cantera/numerics/polyfit.h"
10#include "cantera/transport/TransportData.h"
11#include "cantera/thermo/ThermoPhase.h"
12#include "cantera/thermo/Species.h"
13#include "cantera/base/utilities.h"
14#include "cantera/base/global.h"
15
16 namespace Cantera
17{
18
19//! polynomial degree used for fitting collision integrals
20//! except in CK mode, where the degree is 6.
21#define COLL_INT_POLY_DEGREE 8
22
23GasTransport::GasTransport(ThermoPhase* thermo) :
24 Transport(thermo),
25 m_polytempvec(5)
26{
27}
28
29 void GasTransport::update_T()
30{
31 if (m_thermo->nSpecies() != m_nsp) {
32 // Rebuild data structures if number of species has changed
33 init(m_thermo, m_mode, m_log_level);
34 }
35
36 double T = m_thermo->temperature();
37 if (T == m_temp) {
38 return;
39 }
40
41 m_temp = T;
42 m_kbt = Boltzmann * m_temp;
43 m_logt = log(m_temp);
44 m_sqrt_t = sqrt(m_temp);
45 m_t14 = sqrt(m_sqrt_t);
46
47 // compute powers of log(T)
48 m_polytempvec[0] = 1.0;
49 m_polytempvec[1] = m_logt;
50 m_polytempvec[2] = m_logt*m_logt;
51 m_polytempvec[3] = m_logt*m_logt*m_logt;
52 m_polytempvec[4] = m_logt*m_logt*m_logt*m_logt;
53
54 // temperature has changed, so polynomial fits will need to be redone
55 m_visc_ok = false;
56 m_spvisc_ok = false;
57 m_viscwt_ok = false;
58 m_bindiff_ok = false;
59}
60
61 double GasTransport::viscosity()
62{
63 update_T();
64 update_C();
65
66 if (m_visc_ok) {
67 return m_viscmix;
68 }
69
70 double vismix = 0.0;
71 // update m_visc and m_phi if necessary
72 if (!m_viscwt_ok) {
73 updateViscosity_T();
74 }
75
76 multiply(m_phi, m_molefracs.data(), m_spwork.data());
77
78 for (size_t k = 0; k < m_nsp; k++) {
79 vismix += m_molefracs[k] * m_visc[k]/m_spwork[k]; //denom;
80 }
81 m_viscmix = vismix;
82 return vismix;
83}
84
85 void GasTransport::updateViscosity_T()
86{
87 if (!m_spvisc_ok) {
88 updateSpeciesViscosities();
89 }
90
91 // see Eq. (9-5.14) of Poling et al. (2001)
92 for (size_t j = 0; j < m_nsp; j++) {
93 for (size_t k = j; k < m_nsp; k++) {
94 double vratiokj = m_visc[k]/m_visc[j];
95 double wratiojk = m_mw[j]/m_mw[k];
96
97 // Note that m_wratjk(k,j) holds the square root of m_wratjk(j,k)!
98 double factor1 = 1.0 + (m_sqvisc[k]/m_sqvisc[j]) * m_wratjk(k,j);
99 m_phi(k,j) = factor1*factor1 / (sqrt(8.0) * m_wratkj1(j,k));
100 m_phi(j,k) = m_phi(k,j)/(vratiokj * wratiojk);
101 }
102 }
103 m_viscwt_ok = true;
104}
105
106 void GasTransport::updateSpeciesViscosities()
107{
108 update_T();
109 if (m_mode == CK_Mode) {
110 for (size_t k = 0; k < m_nsp; k++) {
111 m_visc[k] = exp(dot4(m_polytempvec, m_visccoeffs[k]));
112 m_sqvisc[k] = sqrt(m_visc[k]);
113 }
114 } else {
115 for (size_t k = 0; k < m_nsp; k++) {
116 // the polynomial fit is done for sqrt(visc/sqrt(T))
117 m_sqvisc[k] = m_t14 * dot5(m_polytempvec, m_visccoeffs[k]);
118 m_visc[k] = (m_sqvisc[k] * m_sqvisc[k]);
119 }
120 }
121 m_spvisc_ok = true;
122}
123
124 void GasTransport::updateDiff_T()
125{
126 update_T();
127 // evaluate binary diffusion coefficients at unit pressure
128 size_t ic = 0;
129 if (m_mode == CK_Mode) {
130 for (size_t i = 0; i < m_nsp; i++) {
131 for (size_t j = i; j < m_nsp; j++) {
132 m_bdiff(i,j) = exp(dot4(m_polytempvec, m_diffcoeffs[ic]));
133 m_bdiff(j,i) = m_bdiff(i,j);
134 ic++;
135 }
136 }
137 } else {
138 for (size_t i = 0; i < m_nsp; i++) {
139 for (size_t j = i; j < m_nsp; j++) {
140 m_bdiff(i,j) = m_temp * m_sqrt_t*dot5(m_polytempvec,
141 m_diffcoeffs[ic]);
142 m_bdiff(j,i) = m_bdiff(i,j);
143 ic++;
144 }
145 }
146 }
147 m_bindiff_ok = true;
148}
149
150 void GasTransport::getBinaryDiffCoeffs(const size_t ld, double* const d)
151{
152 update_T();
153 // if necessary, evaluate the binary diffusion coefficients from the polynomial fits
154 if (!m_bindiff_ok) {
155 updateDiff_T();
156 }
157 if (ld < m_nsp) {
158 throw CanteraError("GasTransport::getBinaryDiffCoeffs", "ld is too small");
159 }
160 double rp = 1.0/m_thermo->pressure();
161 for (size_t i = 0; i < m_nsp; i++) {
162 for (size_t j = 0; j < m_nsp; j++) {
163 d[ld*j + i] = rp * m_bdiff(i,j);
164 }
165 }
166}
167
168 void GasTransport::getMixDiffCoeffs(double* const d)
169{
170 update_T();
171 update_C();
172
173 // update the binary diffusion coefficients if necessary
174 if (!m_bindiff_ok) {
175 updateDiff_T();
176 }
177
178 double mmw = m_thermo->meanMolecularWeight();
179 double p = m_thermo->pressure();
180 if (m_nsp == 1) {
181 d[0] = m_bdiff(0,0) / p;
182 } else {
183 for (size_t k = 0; k < m_nsp; k++) {
184 double sum2 = 0.0;
185 for (size_t j = 0; j < m_nsp; j++) {
186 if (j != k) {
187 sum2 += m_molefracs[j] / m_bdiff(j,k);
188 }
189 }
190 if (sum2 <= 0.0) {
191 d[k] = m_bdiff(k,k) / p;
192 } else {
193 d[k] = (mmw - m_molefracs[k] * m_mw[k])/(p * mmw * sum2);
194 }
195 }
196 }
197}
198
199 void GasTransport::getMixDiffCoeffsMole(double* const d)
200{
201 update_T();
202 update_C();
203
204 // update the binary diffusion coefficients if necessary
205 if (!m_bindiff_ok) {
206 updateDiff_T();
207 }
208
209 double p = m_thermo->pressure();
210 if (m_nsp == 1) {
211 d[0] = m_bdiff(0,0) / p;
212 } else {
213 for (size_t k = 0; k < m_nsp; k++) {
214 double sum2 = 0.0;
215 for (size_t j = 0; j < m_nsp; j++) {
216 if (j != k) {
217 sum2 += m_molefracs[j] / m_bdiff(j,k);
218 }
219 }
220 if (sum2 <= 0.0) {
221 d[k] = m_bdiff(k,k) / p;
222 } else {
223 d[k] = (1 - m_molefracs[k]) / (p * sum2);
224 }
225 }
226 }
227}
228
229 void GasTransport::getMixDiffCoeffsMass(double* const d)
230{
231 update_T();
232 update_C();
233
234 // update the binary diffusion coefficients if necessary
235 if (!m_bindiff_ok) {
236 updateDiff_T();
237 }
238
239 double mmw = m_thermo->meanMolecularWeight();
240 double p = m_thermo->pressure();
241
242 if (m_nsp == 1) {
243 d[0] = m_bdiff(0,0) / p;
244 } else {
245 for (size_t k=0; k<m_nsp; k++) {
246 double sum1 = 0.0;
247 double sum2 = 0.0;
248 for (size_t i=0; i<m_nsp; i++) {
249 if (i==k) {
250 continue;
251 }
252 sum1 += m_molefracs[i] / m_bdiff(k,i);
253 sum2 += m_molefracs[i] * m_mw[i] / m_bdiff(k,i);
254 }
255 sum1 *= p;
256 sum2 *= p * m_molefracs[k] / (mmw - m_mw[k]*m_molefracs[k]);
257 d[k] = 1.0 / (sum1 + sum2);
258 }
259 }
260}
261
262 void GasTransport::init(ThermoPhase* thermo, int mode, int log_level)
263{
264 m_thermo = thermo;
265 m_nsp = m_thermo->nSpecies();
266 m_mode = mode;
267 m_log_level = log_level;
268
269 // set up Monchick and Mason collision integrals
270 setupCollisionParameters();
271 setupCollisionIntegral();
272
273 m_molefracs.resize(m_nsp);
274 m_spwork.resize(m_nsp);
275 m_visc.resize(m_nsp);
276 m_sqvisc.resize(m_nsp);
277 m_phi.resize(m_nsp, m_nsp, 0.0);
278 m_bdiff.resize(m_nsp, m_nsp);
279
280 // make a local copy of the molecular weights
281 m_mw = m_thermo->molecularWeights();
282
283 m_wratjk.resize(m_nsp, m_nsp, 0.0);
284 m_wratkj1.resize(m_nsp, m_nsp, 0.0);
285 for (size_t j = 0; j < m_nsp; j++) {
286 for (size_t k = j; k < m_nsp; k++) {
287 m_wratjk(j,k) = sqrt(m_mw[j]/m_mw[k]);
288 m_wratjk(k,j) = sqrt(m_wratjk(j,k));
289 m_wratkj1(j,k) = sqrt(1.0 + m_mw[k]/m_mw[j]);
290 }
291 }
292}
293
294 void GasTransport::setupCollisionParameters()
295{
296 m_epsilon.resize(m_nsp, m_nsp, 0.0);
297 m_delta.resize(m_nsp, m_nsp, 0.0);
298 m_reducedMass.resize(m_nsp, m_nsp, 0.0);
299 m_dipole.resize(m_nsp, m_nsp, 0.0);
300 m_diam.resize(m_nsp, m_nsp, 0.0);
301 m_crot.resize(m_nsp);
302 m_zrot.resize(m_nsp);
303 m_polar.resize(m_nsp, false);
304 m_alpha.resize(m_nsp, 0.0);
305 m_poly.resize(m_nsp);
306 m_star_poly_uses_actualT.resize(m_nsp);
307 m_sigma.resize(m_nsp);
308 m_eps.resize(m_nsp);
309 m_w_ac.resize(m_nsp);
310 m_disp.resize(m_nsp, 0.0);
311 m_quad_polar.resize(m_nsp, 0.0);
312
313 const vector<double>& mw = m_thermo->molecularWeights();
314 getTransportData();
315
316 for (size_t i = 0; i < m_nsp; i++) {
317 m_poly[i].resize(m_nsp);
318 m_star_poly_uses_actualT[i].resize(m_nsp);
319 }
320
321 double f_eps, f_sigma;
322
323 for (size_t i = 0; i < m_nsp; i++) {
324 for (size_t j = i; j < m_nsp; j++) {
325 // the reduced mass
326 m_reducedMass(i,j) = mw[i] * mw[j] / (Avogadro * (mw[i] + mw[j]));
327
328 // hard-sphere diameter for (i,j) collisions
329 m_diam(i,j) = 0.5*(m_sigma[i] + m_sigma[j]);
330
331 // the effective well depth for (i,j) collisions
332 m_epsilon(i,j) = sqrt(m_eps[i]*m_eps[j]);
333
334 // the effective dipole moment for (i,j) collisions
335 m_dipole(i,j) = sqrt(m_dipole(i,i)*m_dipole(j,j));
336
337 // reduced dipole moment delta* (nondimensional)
338 double d = m_diam(i,j);
339 m_delta(i,j) = 0.5 * m_dipole(i,j)*m_dipole(i,j)
340 / (4 * Pi * epsilon_0 * m_epsilon(i,j) * d * d * d);
341 makePolarCorrections(i, j, f_eps, f_sigma);
342 m_diam(i,j) *= f_sigma;
343 m_epsilon(i,j) *= f_eps;
344
345 // properties are symmetric
346 m_reducedMass(j,i) = m_reducedMass(i,j);
347 m_diam(j,i) = m_diam(i,j);
348 m_epsilon(j,i) = m_epsilon(i,j);
349 m_dipole(j,i) = m_dipole(i,j);
350 m_delta(j,i) = m_delta(i,j);
351 }
352 }
353}
354
355 void GasTransport::setupCollisionIntegral()
356{
357 double tstar_min = 1.e8, tstar_max = 0.0;
358 for (size_t i = 0; i < m_nsp; i++) {
359 for (size_t j = i; j < m_nsp; j++) {
360 // The polynomial fits of collision integrals vs. T*
361 // will be done for the T* from tstar_min to tstar_max
362 tstar_min = std::min(tstar_min, Boltzmann * m_thermo->minTemp()/m_epsilon(i,j));
363 tstar_max = std::max(tstar_max, Boltzmann * m_thermo->maxTemp()/m_epsilon(i,j));
364 }
365 }
366 // Chemkin fits the entire T* range in the Monchick and Mason tables,
367 // so modify tstar_min and tstar_max if in Chemkin compatibility mode
368 if (m_mode == CK_Mode) {
369 tstar_min = 0.101;
370 tstar_max = 99.9;
371 }
372
373 // initialize the collision integral calculator for the desired T* range
374 debuglog("*** collision_integrals ***\n", m_log_level);
375 MMCollisionInt integrals;
376 integrals.init(tstar_min, tstar_max, m_log_level);
377 fitCollisionIntegrals(integrals);
378 debuglog("*** end of collision_integrals ***\n", m_log_level);
379 // make polynomial fits
380 debuglog("*** property fits ***\n", m_log_level);
381 fitProperties(integrals);
382 debuglog("*** end of property fits ***\n", m_log_level);
383}
384
385 void GasTransport::getTransportData()
386{
387 for (size_t k = 0; k < m_thermo->nSpecies(); k++) {
388 shared_ptr<Species> s = m_thermo->species(k);
389 const GasTransportData* sptran =
390 dynamic_cast<GasTransportData*>(s->transport.get());
391 if (!sptran) {
392 throw CanteraError("GasTransport::getTransportData",
393 "Missing gas-phase transport data for species '{}'.", s->name);
394 }
395
396 if (sptran->geometry == "atom") {
397 m_crot[k] = 0.0;
398 } else if (sptran->geometry == "linear") {
399 m_crot[k] = 1.0;
400 } else if (sptran->geometry == "nonlinear") {
401 m_crot[k] = 1.5;
402 }
403
404 m_sigma[k] = sptran->diameter;
405 m_eps[k] = sptran->well_depth;
406 m_dipole(k,k) = sptran->dipole;
407 m_polar[k] = (sptran->dipole > 0);
408 m_alpha[k] = sptran->polarizability;
409 m_zrot[k] = sptran->rotational_relaxation;
410 if (s->input.hasKey("critical-parameters") &&
411 s->input["critical-parameters"].hasKey("acentric-factor"))
412 {
413 m_w_ac[k] = s->input["critical-parameters"]["acentric-factor"].asDouble();
414 } else {
415 m_w_ac[k] = sptran->acentric_factor;
416 }
417 m_disp[k] = sptran->dispersion_coefficient;
418 m_quad_polar[k] = sptran->quadrupole_polarizability;
419 }
420}
421
422 void GasTransport::makePolarCorrections(size_t i, size_t j,
423 double& f_eps, double& f_sigma)
424{
425 // no correction if both are nonpolar, or both are polar
426 if (m_polar[i] == m_polar[j]) {
427 f_eps = 1.0;
428 f_sigma = 1.0;
429 return;
430 }
431
432 // corrections to the effective diameter and well depth
433 // if one is polar and one is non-polar
434 size_t kp = (m_polar[i] ? i : j); // the polar one
435 size_t knp = (i == kp ? j : i); // the nonpolar one
436 double d3np, d3p, alpha_star, mu_p_star, xi;
437 d3np = pow(m_sigma[knp],3);
438 d3p = pow(m_sigma[kp],3);
439 alpha_star = m_alpha[knp]/d3np;
440 mu_p_star = m_dipole(kp,kp)/sqrt(4 * Pi * epsilon_0 * d3p * m_eps[kp]);
441 xi = 1.0 + 0.25 * alpha_star * mu_p_star * mu_p_star *
442 sqrt(m_eps[kp]/m_eps[knp]);
443 f_sigma = pow(xi, -1.0/6.0);
444 f_eps = xi*xi;
445}
446
447 void GasTransport::fitCollisionIntegrals(MMCollisionInt& integrals)
448{
449 // Chemkin fits to sixth order polynomials
450 int degree = (m_mode == CK_Mode ? 6 : COLL_INT_POLY_DEGREE);
451 if (m_log_level) {
452 writelog("tstar_fits\n"
453 "fits to A*, B*, and C* vs. log(T*).\n"
454 "These are done only for the required dstar(j,k) values.\n\n");
455 if (m_log_level < 3) {
456 writelog("*** polynomial coefficients not printed (log_level < 3) ***\n");
457 }
458 }
459 vector<double> fitlist;
460 m_omega22_poly.clear();
461 m_astar_poly.clear();
462 m_bstar_poly.clear();
463 m_cstar_poly.clear();
464 for (size_t i = 0; i < m_nsp; i++) {
465 for (size_t j = i; j < m_nsp; j++) {
466 // Chemkin fits only delta* = 0
467 double dstar = (m_mode != CK_Mode) ? m_delta(i,j) : 0.0;
468
469 // if a fit has already been generated for delta* = m_delta(i,j),
470 // then use it. Otherwise, make a new fit, and add m_delta(i,j) to
471 // the list of delta* values for which fits have been done.
472
473 // 'find' returns a pointer to end() if not found
474 auto dptr = find(fitlist.begin(), fitlist.end(), dstar);
475 if (dptr == fitlist.end()) {
476 vector<double> ca(degree+1), cb(degree+1), cc(degree+1);
477 vector<double> co22(degree+1);
478 integrals.fit(degree, dstar, ca.data(), cb.data(), cc.data());
479 integrals.fit_omega22(degree, dstar, co22.data());
480 m_omega22_poly.push_back(co22);
481 m_astar_poly.push_back(ca);
482 m_bstar_poly.push_back(cb);
483 m_cstar_poly.push_back(cc);
484 m_poly[i][j] = static_cast<int>(m_astar_poly.size()) - 1;
485 m_star_poly_uses_actualT[i][j] = 0;
486 fitlist.push_back(dstar);
487 } else {
488 // delta* found in fitlist, so just point to this polynomial
489 m_poly[i][j] = static_cast<int>((dptr - fitlist.begin()));
490 m_star_poly_uses_actualT[i][j] = 0;
491 }
492 m_poly[j][i] = m_poly[i][j];
493 m_star_poly_uses_actualT[j][i] = m_star_poly_uses_actualT[i][j];
494 }
495 }
496}
497
498 void GasTransport::fitProperties(MMCollisionInt& integrals)
499{
500 // number of points to use in generating fit data
501 const size_t np = 50;
502 int degree = (m_mode == CK_Mode ? 3 : 4);
503 double dt = (m_thermo->maxTemp() - m_thermo->minTemp())/(np-1);
504 vector<double> tlog(np), spvisc(np), spcond(np);
505 vector<double> w(np), w2(np);
506
507 m_visccoeffs.clear();
508 m_condcoeffs.clear();
509
510 // generate array of log(t) values
511 for (size_t n = 0; n < np; n++) {
512 double t = m_thermo->minTemp() + dt*n;
513 tlog[n] = log(t);
514 }
515
516 // vector of polynomial coefficients
517 vector<double> c(degree + 1), c2(degree + 1);
518
519 // fit the pure-species viscosity and thermal conductivity for each species
520 if (m_log_level && m_log_level < 2) {
521 writelog("*** polynomial coefficients not printed (log_level < 2) ***\n");
522 }
523 double visc, err, relerr,
524 mxerr = 0.0, mxrelerr = 0.0, mxerr_cond = 0.0, mxrelerr_cond = 0.0;
525
526 if (m_log_level) {
527 writelog("Polynomial fits for viscosity:\n");
528 if (m_mode == CK_Mode) {
529 writelog("log(viscosity) fit to cubic polynomial in log(T)\n");
530 } else {
531 writelogf("viscosity/sqrt(T) fit to polynomial of degree "
532 "%d in log(T)", degree);
533 }
534 }
535
536 double T_save = m_thermo->temperature();
537 const vector<double>& mw = m_thermo->molecularWeights();
538 for (size_t k = 0; k < m_nsp; k++) {
539 double tstar = Boltzmann * 298.0 / m_eps[k];
540 // Scaling factor for temperature dependence of z_rot. [Kee2003] Eq.
541 // 12.112 or [Kee2017] Eq. 11.115
542 double fz_298 = 1.0 + pow(Pi, 1.5) / sqrt(tstar) * (0.5 + 1.0 / tstar) +
543 (0.25 * Pi * Pi + 2) / tstar;
544
545 for (size_t n = 0; n < np; n++) {
546 double t = m_thermo->minTemp() + dt*n;
547 m_thermo->setTemperature(t);
548 vector<double> cp_R_all(m_thermo->nSpecies());
549 m_thermo->getCp_R_ref(&cp_R_all[0]);
550 double cp_R = cp_R_all[k];
551 tstar = Boltzmann * t / m_eps[k];
552 double sqrt_T = sqrt(t);
553 double om22 = integrals.omega22(tstar, m_delta(k,k));
554 double om11 = integrals.omega11(tstar, m_delta(k,k));
555
556 // self-diffusion coefficient, without polar corrections
557 double diffcoeff = 3.0/16.0 * sqrt(2.0 * Pi/m_reducedMass(k,k)) *
558 pow((Boltzmann * t), 1.5)/
559 (Pi * m_sigma[k] * m_sigma[k] * om11);
560
561 // viscosity
562 visc = 5.0/16.0 * sqrt(Pi * mw[k] * Boltzmann * t / Avogadro) /
563 (om22 * Pi * m_sigma[k]*m_sigma[k]);
564
565 // thermal conductivity
566 double f_int = mw[k]/(GasConstant * t) * diffcoeff/visc;
567 double cv_rot = m_crot[k];
568 double A_factor = 2.5 - f_int;
569 double fz_tstar = 1.0 + pow(Pi, 1.5) / sqrt(tstar) * (0.5 + 1.0 / tstar) +
570 (0.25 * Pi * Pi + 2) / tstar;
571 double B_factor = m_zrot[k] * fz_298 / fz_tstar + 2.0/Pi * (5.0/3.0 * cv_rot + f_int);
572 double c1 = 2.0/Pi * A_factor/B_factor;
573 double cv_int = cp_R - 2.5 - cv_rot;
574 double f_rot = f_int * (1.0 + c1);
575 double f_trans = 2.5 * (1.0 - c1 * cv_rot/1.5);
576 double cond = (visc/mw[k])*GasConstant*(f_trans * 1.5
577 + f_rot * cv_rot + f_int * cv_int);
578
579 if (m_mode == CK_Mode) {
580 spvisc[n] = log(visc);
581 spcond[n] = log(cond);
582 w[n] = -1.0;
583 w2[n] = -1.0;
584 } else {
585 // the viscosity should be proportional approximately to
586 // sqrt(T); therefore, visc/sqrt(T) should have only a weak
587 // temperature dependence. And since the mixture rule requires
588 // the square root of the pure-species viscosity, fit the square
589 // root of (visc/sqrt(T)) to avoid having to compute square
590 // roots in the mixture rule.
591 spvisc[n] = sqrt(visc/sqrt_T);
592
593 // the pure-species conductivity scales approximately with
594 // sqrt(T). Unlike the viscosity, there is no reason here to fit
595 // the square root, since a different mixture rule is used.
596 spcond[n] = cond/sqrt_T;
597 w[n] = 1.0/(spvisc[n]*spvisc[n]);
598 w2[n] = 1.0/(spcond[n]*spcond[n]);
599 }
600 }
601 polyfit(np, degree, tlog.data(), spvisc.data(), w.data(), c.data());
602 polyfit(np, degree, tlog.data(), spcond.data(), w2.data(), c2.data());
603
604 // evaluate max fit errors for viscosity
605 for (size_t n = 0; n < np; n++) {
606 double val, fit;
607 if (m_mode == CK_Mode) {
608 val = exp(spvisc[n]);
609 fit = exp(poly3(tlog[n], c.data()));
610 } else {
611 double sqrt_T = exp(0.5*tlog[n]);
612 val = sqrt_T * pow(spvisc[n],2);
613 fit = sqrt_T * pow(poly4(tlog[n], c.data()),2);
614 }
615 err = fit - val;
616 relerr = err/val;
617 mxerr = std::max(mxerr, fabs(err));
618 mxrelerr = std::max(mxrelerr, fabs(relerr));
619 }
620
621 // evaluate max fit errors for conductivity
622 for (size_t n = 0; n < np; n++) {
623 double val, fit;
624 if (m_mode == CK_Mode) {
625 val = exp(spcond[n]);
626 fit = exp(poly3(tlog[n], c2.data()));
627 } else {
628 double sqrt_T = exp(0.5*tlog[n]);
629 val = sqrt_T * spcond[n];
630 fit = sqrt_T * poly4(tlog[n], c2.data());
631 }
632 err = fit - val;
633 relerr = err/val;
634 mxerr_cond = std::max(mxerr_cond, fabs(err));
635 mxrelerr_cond = std::max(mxrelerr_cond, fabs(relerr));
636 }
637 m_visccoeffs.push_back(c);
638 m_condcoeffs.push_back(c2);
639
640 if (m_log_level >= 2) {
641 writelog(m_thermo->speciesName(k) + ": [" + vec2str(c) + "]\n");
642 }
643 }
644 m_thermo->setTemperature(T_save);
645
646 if (m_log_level) {
647 writelogf("Maximum viscosity absolute error: %12.6g\n", mxerr);
648 writelogf("Maximum viscosity relative error: %12.6g\n", mxrelerr);
649 writelog("\nPolynomial fits for conductivity:\n");
650 if (m_mode == CK_Mode) {
651 writelog("log(conductivity) fit to cubic polynomial in log(T)");
652 } else {
653 writelogf("conductivity/sqrt(T) fit to "
654 "polynomial of degree %d in log(T)", degree);
655 }
656 if (m_log_level >= 2) {
657 for (size_t k = 0; k < m_nsp; k++) {
658 writelog(m_thermo->speciesName(k) + ": [" +
659 vec2str(m_condcoeffs[k]) + "]\n");
660 }
661 }
662 writelogf("Maximum conductivity absolute error: %12.6g\n", mxerr_cond);
663 writelogf("Maximum conductivity relative error: %12.6g\n", mxrelerr_cond);
664
665 // fit the binary diffusion coefficients for each species pair
666 writelogf("\nbinary diffusion coefficients:\n");
667 if (m_mode == CK_Mode) {
668 writelog("log(D) fit to cubic polynomial in log(T)");
669 } else {
670 writelogf("D/T**(3/2) fit to polynomial of degree %d in log(T)",degree);
671 }
672 }
673
674 fitDiffCoeffs(integrals);
675}
676
677 void GasTransport::fitDiffCoeffs(MMCollisionInt& integrals)
678{
679 // number of points to use in generating fit data
680 const size_t np = 50;
681 int degree = (m_mode == CK_Mode ? 3 : 4);
682 double dt = (m_thermo->maxTemp() - m_thermo->minTemp())/(np-1);
683 vector<double> tlog(np);
684 vector<double> w(np), w2(np);
685
686 // generate array of log(t) values
687 for (size_t n = 0; n < np; n++) {
688 double t = m_thermo->minTemp() + dt*n;
689 tlog[n] = log(t);
690 }
691
692 // vector of polynomial coefficients
693 vector<double> c(degree + 1), c2(degree + 1);
694 double err, relerr,
695 mxerr = 0.0, mxrelerr = 0.0;
696
697 vector<double> diff(np + 1);
698 m_diffcoeffs.clear();
699 for (size_t k = 0; k < m_nsp; k++) {
700 for (size_t j = k; j < m_nsp; j++) {
701 for (size_t n = 0; n < np; n++) {
702 double t = m_thermo->minTemp() + dt*n;
703 double eps = m_epsilon(j,k);
704 double tstar = Boltzmann * t/eps;
705 double sigma = m_diam(j,k);
706 double om11 = integrals.omega11(tstar, m_delta(j,k));
707 double diffcoeff = 3.0/16.0 * sqrt(2.0 * Pi/m_reducedMass(k,j))
708 * pow(Boltzmann * t, 1.5) / (Pi * sigma * sigma * om11);
709
710 // 2nd order correction
711 // NOTE: THIS CORRECTION IS NOT APPLIED
712 double fkj, fjk;
713 getBinDiffCorrection(t, integrals, k, j, 1.0, 1.0, fkj, fjk);
714
715 if (m_mode == CK_Mode) {
716 diff[n] = log(diffcoeff);
717 w[n] = -1.0;
718 } else {
719 diff[n] = diffcoeff/pow(t, 1.5);
720 w[n] = 1.0/(diff[n]*diff[n]);
721 }
722 }
723 polyfit(np, degree, tlog.data(), diff.data(), w.data(), c.data());
724
725 for (size_t n = 0; n < np; n++) {
726 double val, fit;
727 if (m_mode == CK_Mode) {
728 val = exp(diff[n]);
729 fit = exp(poly3(tlog[n], c.data()));
730 } else {
731 double t = exp(tlog[n]);
732 double pre = pow(t, 1.5);
733 val = pre * diff[n];
734 fit = pre * poly4(tlog[n], c.data());
735 }
736 err = fit - val;
737 relerr = err/val;
738 mxerr = std::max(mxerr, fabs(err));
739 mxrelerr = std::max(mxrelerr, fabs(relerr));
740 }
741 m_diffcoeffs.push_back(c);
742 if (m_log_level >= 2) {
743 writelog(m_thermo->speciesName(k) + "__" +
744 m_thermo->speciesName(j) + ": [" + vec2str(c) + "]\n");
745 }
746 }
747 }
748 if (m_log_level) {
749 writelogf("Maximum binary diffusion coefficient absolute error:"
750 " %12.6g\n", mxerr);
751 writelogf("Maximum binary diffusion coefficient relative error:"
752 "%12.6g", mxrelerr);
753 }
754}
755
756 void GasTransport::getBinDiffCorrection(double t, MMCollisionInt& integrals,
757 size_t k, size_t j, double xk, double xj, double& fkj, double& fjk)
758{
759 double w1 = m_thermo->molecularWeight(k);
760 double w2 = m_thermo->molecularWeight(j);
761 double wsum = w1 + w2;
762 double wmwp = (w1 - w2)/wsum;
763 double sqw12 = sqrt(w1*w2);
764 double sig1 = m_sigma[k];
765 double sig2 = m_sigma[j];
766 double sig12 = 0.5*(m_sigma[k] + m_sigma[j]);
767 double sigratio = sig1*sig1/(sig2*sig2);
768 double sigratio2 = sig1*sig1/(sig12*sig12);
769 double sigratio3 = sig2*sig2/(sig12*sig12);
770 double tstar1 = Boltzmann * t / m_eps[k];
771 double tstar2 = Boltzmann * t / m_eps[j];
772 double tstar12 = Boltzmann * t / sqrt(m_eps[k] * m_eps[j]);
773 double om22_1 = integrals.omega22(tstar1, m_delta(k,k));
774 double om22_2 = integrals.omega22(tstar2, m_delta(j,j));
775 double om11_12 = integrals.omega11(tstar12, m_delta(k,j));
776 double astar_12 = integrals.astar(tstar12, m_delta(k,j));
777 double bstar_12 = integrals.bstar(tstar12, m_delta(k,j));
778 double cstar_12 = integrals.cstar(tstar12, m_delta(k,j));
779
780 double cnst = sigratio * sqrt(2.0*w2/wsum) * 2.0 * w1*w1/(wsum * w2);
781 double p1 = cnst * om22_1 / om11_12;
782
783 cnst = (1.0/sigratio) * sqrt(2.0*w1/wsum) * 2.0*w2*w2/(wsum*w1);
784 double p2 = cnst * om22_2 / om11_12;
785 double p12 = 15.0 * wmwp*wmwp + 8.0*w1*w2*astar_12/(wsum*wsum);
786
787 cnst = (2.0/(w2*wsum))*sqrt(2.0*w2/wsum)*sigratio2;
788 double q1 = cnst*((2.5 - 1.2*bstar_12)*w1*w1 + 3.0*w2*w2
789 + 1.6*w1*w2*astar_12);
790
791 cnst = (2.0/(w1*wsum))*sqrt(2.0*w1/wsum)*sigratio3;
792 double q2 = cnst*((2.5 - 1.2*bstar_12)*w2*w2 + 3.0*w1*w1
793 + 1.6*w1*w2*astar_12);
794 double q12 = wmwp*wmwp*15.0*(2.5 - 1.2*bstar_12)
795 + 4.0*w1*w2*astar_12*(11.0 - 2.4*bstar_12)/(wsum*wsum)
796 + 1.6*wsum*om22_1*om22_2/(om11_12*om11_12*sqw12)
797 * sigratio2 * sigratio3;
798
799 cnst = 6.0*cstar_12 - 5.0;
800 fkj = 1.0 + 0.1*cnst*cnst *
801 (p1*xk*xk + p2*xj*xj + p12*xk*xj)/
802 (q1*xk*xk + q2*xj*xj + q12*xk*xj);
803 fjk = 1.0 + 0.1*cnst*cnst *
804 (p2*xk*xk + p1*xj*xj + p12*xk*xj)/
805 (q2*xk*xk + q1*xj*xj + q12*xk*xj);
806}
807
808 void GasTransport::getViscosityPolynomial(size_t i, double* coeffs) const
809{
810 for (int k = 0; k < (m_mode == CK_Mode ? 4 : 5); k++) {
811 coeffs[k] = m_visccoeffs[i][k];
812 }
813}
814
815 void GasTransport::getConductivityPolynomial(size_t i, double* coeffs) const
816{
817 for (int k = 0; k < (m_mode == CK_Mode ? 4 : 5); k++) {
818 coeffs[k] = m_condcoeffs[i][k];
819 }
820}
821
822 void GasTransport::getBinDiffusivityPolynomial(size_t i, size_t j, double* coeffs) const
823{
824 size_t mi = (j >= i? i : j);
825 size_t mj = (j >= i? j : i);
826 size_t ic = 0;
827 for (size_t ii = 0; ii < mi; ii++) {
828 ic += m_nsp - ii;
829 }
830 ic += mj - mi;
831
832 for (int k = 0; k < (m_mode == CK_Mode ? 4 : 5); k++) {
833 coeffs[k] = m_diffcoeffs[ic][k];
834 }
835}
836
837 void GasTransport::getCollisionIntegralPolynomial(size_t i, size_t j,
838 double* astar_coeffs,
839 double* bstar_coeffs,
840 double* cstar_coeffs) const
841{
842 for (int k = 0; k < (m_mode == CK_Mode ? 6 : COLL_INT_POLY_DEGREE) + 1; k++) {
843 astar_coeffs[k] = m_astar_poly[m_poly[i][j]][k];
844 bstar_coeffs[k] = m_bstar_poly[m_poly[i][j]][k];
845 cstar_coeffs[k] = m_cstar_poly[m_poly[i][j]][k];
846 }
847}
848
849 void GasTransport::setViscosityPolynomial(size_t i, double* coeffs)
850{
851 for (int k = 0; k < (m_mode == CK_Mode ? 4 : 5); k++) {
852 m_visccoeffs[i][k] = coeffs[k];
853 }
854
855 m_visc_ok = false;
856 m_spvisc_ok = false;
857 m_viscwt_ok = false;
858 m_bindiff_ok = false;
859 m_temp = -1;
860}
861
862 void GasTransport::setConductivityPolynomial(size_t i, double* coeffs)
863{
864 for (int k = 0; k < (m_mode == CK_Mode ? 4 : 5); k++) {
865 m_condcoeffs[i][k] = coeffs[k];
866 }
867
868 m_visc_ok = false;
869 m_spvisc_ok = false;
870 m_viscwt_ok = false;
871 m_bindiff_ok = false;
872 m_temp = -1;
873}
874
875 void GasTransport::setBinDiffusivityPolynomial(size_t i, size_t j, double* coeffs)
876{
877 size_t mi = (j >= i? i : j);
878 size_t mj = (j >= i? j : i);
879 size_t ic = 0;
880 for (size_t ii = 0; ii < mi; ii++) {
881 ic += m_nsp - ii;
882 }
883 ic += mj - mi;
884
885 for (int k = 0; k < (m_mode == CK_Mode ? 4 : 5); k++) {
886 m_diffcoeffs[ic][k] = coeffs[k];
887 }
888
889 m_visc_ok = false;
890 m_spvisc_ok = false;
891 m_viscwt_ok = false;
892 m_bindiff_ok = false;
893 m_temp = -1;
894}
895
896 void GasTransport::setCollisionIntegralPolynomial(size_t i, size_t j,
897 double* astar_coeffs,
898 double* bstar_coeffs,
899 double* cstar_coeffs, bool actualT)
900{
901 size_t degree = (m_mode == CK_Mode ? 6 : COLL_INT_POLY_DEGREE);
902 vector<double> ca(degree+1), cb(degree+1), cc(degree+1);
903
904 for (size_t k = 0; k < degree+1; k++) {
905 ca[k] = astar_coeffs[k];
906 cb[k] = bstar_coeffs[k];
907 cc[k] = cstar_coeffs[k];
908 }
909
910 m_astar_poly.push_back(ca);
911 m_bstar_poly.push_back(cb);
912 m_cstar_poly.push_back(cc);
913 m_poly[i][j] = static_cast<int>(m_astar_poly.size()) - 1;
914 m_poly[j][i] = m_poly[i][j];
915 if (actualT) {
916 m_star_poly_uses_actualT[i][j] = 1;
917 m_star_poly_uses_actualT[j][i] = m_star_poly_uses_actualT[i][j];
918 }
919
920 m_visc_ok = false;
921 m_spvisc_ok = false;
922 m_viscwt_ok = false;
923 m_bindiff_ok = false;
924 m_temp = -1;
925}
926
927}
COLL_INT_POLY_DEGREE
#define COLL_INT_POLY_DEGREE
polynomial degree used for fitting collision integrals except in CK mode, where the degree is 6.
Definition GasTransport.cpp:21
GasTransport.h
MMCollisionInt.h
Monchick and Mason collision integrals.
Species.h
Declaration for class Cantera::Species.
ThermoPhase.h
Header file for class ThermoPhase, the base class for phases with thermodynamic properties,...
TransportData.h
Cantera::CanteraError
Base class for exceptions thrown by Cantera classes.
Definition ctexceptions.h:66
Cantera::GasTransportData
Transport data for a single gas-phase species which can be used in mixture-averaged or multicomponent...
Definition TransportData.h:46
Cantera::GasTransportData::polarizability
double polarizability
The polarizability of the molecule [m^3]. Default 0.0.
Definition TransportData.h:88
Cantera::GasTransportData::diameter
double diameter
The Lennard-Jones collision diameter [m].
Definition TransportData.h:79
Cantera::GasTransportData::acentric_factor
double acentric_factor
Pitzer's acentric factor [dimensionless]. Default 0.0.
Definition TransportData.h:96
Cantera::GasTransportData::quadrupole_polarizability
double quadrupole_polarizability
quadrupole. Default 0.0.
Definition TransportData.h:102
Cantera::GasTransportData::rotational_relaxation
double rotational_relaxation
The rotational relaxation number (the number of collisions it takes to equilibrate the rotational deg...
Definition TransportData.h:93
Cantera::GasTransportData::dispersion_coefficient
double dispersion_coefficient
dispersion normalized by e^2. [m^5] Default 0.0.
Definition TransportData.h:99
Cantera::GasTransportData::dipole
double dipole
The permanent dipole moment of the molecule [Coulomb-m]. Default 0.0.
Definition TransportData.h:85
Cantera::GasTransportData::well_depth
double well_depth
The Lennard-Jones well depth [J].
Definition TransportData.h:82
Cantera::GasTransportData::geometry
string geometry
A string specifying the molecular geometry.
Definition TransportData.h:76
Cantera::MMCollisionInt
Calculation of Collision integrals.
Definition MMCollisionInt.h:25
Cantera::MMCollisionInt::init
void init(double tsmin, double tsmax, int loglevel=0)
Initialize the object for calculation.
Definition MMCollisionInt.cpp:216
Cantera::Phase::nSpecies
size_t nSpecies() const
Returns the number of species in the phase.
Definition Phase.h:245
Cantera::ThermoPhase
Base class for a phase with thermodynamic properties.
Definition ThermoPhase.h:390
global.h
This file contains definitions for utility functions and text for modules, inputfiles and logging,...
Cantera::vec2str
string vec2str(const vector< double > &v, const string &fmt, const string &sep)
Convert a vector to a string (separated by commas)
Definition stringUtils.cpp:33
Cantera::debuglog
void debuglog(const string &msg, int loglevel)
Write a message to the log only if loglevel > 0.
Definition global.h:158
Cantera::writelogf
void writelogf(const char *fmt, const Args &... args)
Write a formatted message to the screen.
Definition global.h:195
Cantera::writelog
void writelog(const string &fmt, const Args &... args)
Write a formatted message to the screen.
Definition global.h:175
Cantera::dot5
double dot5(const V &x, const V &y)
Templated Inner product of two vectors of length 5.
Definition utilities.h:55
Cantera::poly4
R poly4(D x, R *c)
Evaluates a polynomial of order 4.
Definition utilities.h:153
Cantera::poly3
R poly3(D x, R *c)
Templated evaluation of a polynomial of order 3.
Definition utilities.h:165
Cantera::dot4
double dot4(const V &x, const V &y)
Templated Inner product of two vectors of length 4.
Definition utilities.h:40
Cantera::polyfit
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
Cantera::Boltzmann
const double Boltzmann
Boltzmann constant [J/K].
Definition ct_defs.h:84
Cantera::Avogadro
const double Avogadro
Avogadro's Number [number/kmol].
Definition ct_defs.h:81
Cantera::epsilon_0
const double epsilon_0
Permittivity of free space [F/m].
Definition ct_defs.h:137
Cantera::GasConstant
const double GasConstant
Universal Gas Constant [J/kmol/K].
Definition ct_defs.h:120
Cantera::Pi
const double Pi
Pi.
Definition ct_defs.h:68
Cantera
Namespace for the Cantera kernel.
Definition AnyMap.cpp:564
Cantera::multiply
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.
Definition DenseMatrix.cpp:207
polyfit.h
stringUtils.h
Contains declarations for string manipulation functions within Cantera.
utilities.h
Various templated functions that carry out common vector and polynomial operations (see Templated Arr...