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