547 lines
14 KiB
C++
Executable file
547 lines
14 KiB
C++
Executable file
/**
|
|
* @file PecosTransport.cpp
|
|
* Mixture-averaged transport properties.
|
|
*/
|
|
|
|
#include "cantera/transport/PecosTransport.h"
|
|
#include "cantera/transport/TransportParams.h"
|
|
#include "cantera/base/stringUtils.h"
|
|
#include "cantera/thermo/IdealGasPhase.h"
|
|
|
|
#include <sstream>
|
|
|
|
using namespace std;
|
|
|
|
namespace Cantera
|
|
{
|
|
|
|
PecosTransport::PecosTransport() :
|
|
m_nsp(0),
|
|
m_temp(-1.0),
|
|
m_logt(0.0)
|
|
{
|
|
warn_deprecated("class PecosTransport", "To be removed after Cantera 2.2");
|
|
}
|
|
|
|
bool PecosTransport::initGas(GasTransportParams& tr)
|
|
{
|
|
// constant substance attributes
|
|
m_thermo = tr.thermo;
|
|
m_nsp = static_cast<int>(m_thermo->nSpecies());
|
|
|
|
// make a local copy of the molecular weights
|
|
m_mw.resize(m_nsp);
|
|
copy(m_thermo->molecularWeights().begin(),
|
|
m_thermo->molecularWeights().end(), m_mw.begin());
|
|
|
|
// copy polynomials and parameters into local storage
|
|
m_poly = tr.poly;
|
|
m_visccoeffs = tr.visccoeffs;
|
|
m_condcoeffs = tr.condcoeffs;
|
|
m_diffcoeffs = tr.diffcoeffs;
|
|
|
|
m_zrot = tr.zrot;
|
|
m_crot = tr.crot;
|
|
m_epsilon = tr.epsilon;
|
|
m_mode = tr.mode_;
|
|
m_diam = tr.diam;
|
|
m_eps = tr.eps;
|
|
m_alpha = tr.alpha;
|
|
m_dipoleDiag.resize(m_nsp);
|
|
for (int i = 0; i < m_nsp; i++) {
|
|
m_dipoleDiag[i] = tr.dipole(i,i);
|
|
}
|
|
|
|
m_phi.resize(m_nsp, m_nsp, 0.0);
|
|
m_wratjk.resize(m_nsp, m_nsp, 0.0);
|
|
m_wratkj1.resize(m_nsp, m_nsp, 0.0);
|
|
int j, k;
|
|
for (j = 0; j < m_nsp; j++)
|
|
for (k = j; k < m_nsp; k++) {
|
|
m_wratjk(j,k) = sqrt(m_mw[j]/m_mw[k]);
|
|
m_wratjk(k,j) = sqrt(m_wratjk(j,k));
|
|
m_wratkj1(j,k) = sqrt(1.0 + m_mw[k]/m_mw[j]);
|
|
}
|
|
|
|
m_polytempvec.resize(5);
|
|
m_visc.resize(m_nsp);
|
|
m_sqvisc.resize(m_nsp);
|
|
m_cond.resize(m_nsp);
|
|
m_bdiff.resize(m_nsp, m_nsp);
|
|
|
|
m_molefracs.resize(m_nsp);
|
|
m_spwork.resize(m_nsp);
|
|
|
|
// set flags all false
|
|
m_viscmix_ok = false;
|
|
m_viscwt_ok = false;
|
|
m_spvisc_ok = false;
|
|
m_spcond_ok = false;
|
|
m_condmix_ok = false;
|
|
m_spcond_ok = false;
|
|
m_diffmix_ok = false;
|
|
m_abc_ok = false;
|
|
|
|
// read blottner fit parameters (A,B,C)
|
|
read_blottner_transport_table();
|
|
|
|
// set specific heats
|
|
cv_rot.resize(m_nsp);
|
|
cp_R.resize(m_nsp);
|
|
cv_int.resize(m_nsp);
|
|
|
|
for (k = 0; k < m_nsp; k++) {
|
|
cv_rot[k] = tr.crot[k];
|
|
cp_R[k] = ((IdealGasPhase*)tr.thermo)->cp_R_ref()[k];
|
|
cv_int[k] = cp_R[k] - 2.5 - cv_rot[k];
|
|
}
|
|
return true;
|
|
}
|
|
|
|
doublereal PecosTransport::viscosity()
|
|
{
|
|
update_T();
|
|
update_C();
|
|
|
|
if (m_viscmix_ok) {
|
|
return m_viscmix;
|
|
}
|
|
|
|
doublereal vismix = 0.0;
|
|
int k;
|
|
// update m_visc and m_phi if necessary
|
|
if (!m_viscwt_ok) {
|
|
updateViscosity_T();
|
|
}
|
|
|
|
multiply(m_phi, DATA_PTR(m_molefracs), DATA_PTR(m_spwork));
|
|
|
|
for (k = 0; k < m_nsp; k++) {
|
|
vismix += m_molefracs[k] * m_visc[k]/m_spwork[k]; //denom;
|
|
}
|
|
m_viscmix = vismix;
|
|
return vismix;
|
|
}
|
|
|
|
void PecosTransport::getBinaryDiffCoeffs(const size_t ld, doublereal* const d)
|
|
{
|
|
int i,j;
|
|
|
|
update_T();
|
|
|
|
// if necessary, evaluate the binary diffusion coefficents
|
|
if (!m_bindiff_ok) {
|
|
updateDiff_T();
|
|
}
|
|
|
|
doublereal rp = 1.0/pressure_ig();
|
|
for (i = 0; i < m_nsp; i++)
|
|
for (j = 0; j < m_nsp; j++) {
|
|
d[ld*j + i] = rp * m_bdiff(i,j);
|
|
}
|
|
}
|
|
|
|
void PecosTransport::getMobilities(doublereal* const mobil)
|
|
{
|
|
int k;
|
|
getMixDiffCoeffs(DATA_PTR(m_spwork));
|
|
doublereal c1 = ElectronCharge / (Boltzmann * m_temp);
|
|
for (k = 0; k < m_nsp; k++) {
|
|
mobil[k] = c1 * m_spwork[k] * m_thermo->charge(k);
|
|
}
|
|
}
|
|
|
|
doublereal PecosTransport::thermalConductivity()
|
|
{
|
|
int k;
|
|
doublereal lambda = 0.0;
|
|
|
|
update_T();
|
|
update_C();
|
|
|
|
// update m_cond and m_phi if necessary
|
|
if (!m_spcond_ok) {
|
|
updateCond_T();
|
|
}
|
|
if (!m_condmix_ok) {
|
|
|
|
multiply(m_phi, DATA_PTR(m_molefracs), DATA_PTR(m_spwork));
|
|
|
|
for (k = 0; k < m_nsp; k++) {
|
|
lambda += m_molefracs[k] * m_cond[k]/m_spwork[k]; //denom;
|
|
}
|
|
|
|
}
|
|
m_lambda = lambda;
|
|
return m_lambda;
|
|
|
|
}
|
|
|
|
void PecosTransport::getThermalDiffCoeffs(doublereal* const dt)
|
|
{
|
|
int k;
|
|
for (k = 0; k < m_nsp; k++) {
|
|
dt[k] = 0.0;
|
|
}
|
|
}
|
|
|
|
void PecosTransport::getSpeciesFluxes(size_t ndim,
|
|
const doublereal* const grad_T,
|
|
size_t ldx, const doublereal* const grad_X,
|
|
size_t ldf, doublereal* const fluxes)
|
|
{
|
|
size_t n = 0;
|
|
int k;
|
|
|
|
update_T();
|
|
update_C();
|
|
|
|
getMixDiffCoeffs(DATA_PTR(m_spwork));
|
|
|
|
const vector_fp& mw = m_thermo->molecularWeights();
|
|
const doublereal* y = m_thermo->massFractions();
|
|
doublereal rhon = m_thermo->molarDensity();
|
|
|
|
vector_fp sum(ndim,0.0);
|
|
|
|
doublereal correction=0.0;
|
|
// grab 2nd (summation) term -- still need to multiply by mass fraction (\rho_s / \rho)
|
|
for (k = 0; k < m_nsp; k++) {
|
|
correction += rhon * mw[k] * m_spwork[k] * grad_X[n*ldx + k];
|
|
}
|
|
|
|
for (n = 0; n < ndim; n++) {
|
|
for (k = 0; k < m_nsp; k++) {
|
|
fluxes[n*ldf + k] = -rhon * mw[k] * m_spwork[k] * grad_X[n*ldx + k] + y[k]*correction;
|
|
sum[n] += fluxes[n*ldf + k];
|
|
}
|
|
}
|
|
// add correction flux to enforce sum to zero
|
|
for (n = 0; n < ndim; n++) {
|
|
for (k = 0; k < m_nsp; k++) {
|
|
fluxes[n*ldf + k] -= y[k]*sum[n];
|
|
}
|
|
}
|
|
}
|
|
|
|
void PecosTransport::getMixDiffCoeffs(doublereal* const d)
|
|
{
|
|
update_T();
|
|
update_C();
|
|
|
|
// update the binary diffusion coefficients if necessary
|
|
if (!m_bindiff_ok) {
|
|
updateDiff_T();
|
|
}
|
|
|
|
int k, j;
|
|
doublereal mmw = m_thermo->meanMolecularWeight();
|
|
doublereal sumxw = 0.0, sum2;
|
|
doublereal p = pressure_ig();
|
|
if (m_nsp == 1) {
|
|
d[0] = m_bdiff(0,0) / p;
|
|
} else {
|
|
for (k = 0; k < m_nsp; k++) {
|
|
sumxw += m_molefracs[k] * m_mw[k];
|
|
}
|
|
for (k = 0; k < m_nsp; k++) {
|
|
sum2 = 0.0;
|
|
for (j = 0; j < m_nsp; j++) {
|
|
if (j != k) {
|
|
sum2 += m_molefracs[j] / m_bdiff(j,k);
|
|
}
|
|
}
|
|
if (sum2 <= 0.0) {
|
|
d[k] = m_bdiff(k,k) / p;
|
|
} else {
|
|
d[k] = (sumxw - m_molefracs[k] * m_mw[k])/(p * mmw * sum2);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void PecosTransport::getMixDiffCoeffsMole(doublereal* const d)
|
|
{
|
|
update_T();
|
|
update_C();
|
|
|
|
// update the binary diffusion coefficients if necessary
|
|
if (!m_bindiff_ok) {
|
|
updateDiff_T();
|
|
}
|
|
|
|
doublereal p = m_thermo->pressure();
|
|
if (m_nsp == 1) {
|
|
d[0] = m_bdiff(0,0) / p;
|
|
} else {
|
|
for (int k = 0; k < m_nsp; k++) {
|
|
double sum2 = 0.0;
|
|
for (int j = 0; j < m_nsp; j++) {
|
|
if (j != k) {
|
|
sum2 += m_molefracs[j] / m_bdiff(j,k);
|
|
}
|
|
}
|
|
if (sum2 <= 0.0) {
|
|
d[k] = m_bdiff(k,k) / p;
|
|
} else {
|
|
d[k] = (1 - m_molefracs[k]) / (p * sum2);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void PecosTransport::getMixDiffCoeffsMass(doublereal* const d)
|
|
{
|
|
update_T();
|
|
update_C();
|
|
|
|
// update the binary diffusion coefficients if necessary
|
|
if (!m_bindiff_ok) {
|
|
updateDiff_T();
|
|
}
|
|
|
|
doublereal mmw = m_thermo->meanMolecularWeight();
|
|
doublereal p = m_thermo->pressure();
|
|
|
|
if (m_nsp == 1) {
|
|
d[0] = m_bdiff(0,0) / p;
|
|
} else {
|
|
for (int k=0; k<m_nsp; k++) {
|
|
double sum1 = 0.0;
|
|
double sum2 = 0.0;
|
|
for (int i=0; i<m_nsp; i++) {
|
|
if (i==k) {
|
|
continue;
|
|
}
|
|
sum1 += m_molefracs[i] / m_bdiff(k,i);
|
|
sum2 += m_molefracs[i] * m_mw[i] / m_bdiff(k,i);
|
|
}
|
|
sum1 *= p;
|
|
sum2 *= p * m_molefracs[k] / (mmw - m_mw[k]*m_molefracs[k]);
|
|
d[k] = 1.0 / (sum1 + sum2);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @internal This is called whenever a transport property is
|
|
* requested from ThermoSubstance if the temperature has changed
|
|
* since the last call to update_T.
|
|
*/
|
|
void PecosTransport::update_T()
|
|
{
|
|
doublereal t = m_thermo->temperature();
|
|
if (t == m_temp) {
|
|
return;
|
|
}
|
|
if (t <= 0.0) {
|
|
throw CanteraError("PecosTransport::update_T",
|
|
"negative temperature "+fp2str(t));
|
|
}
|
|
m_temp = t;
|
|
m_logt = log(m_temp);
|
|
m_kbt = Boltzmann * m_temp;
|
|
m_sqrt_t = sqrt(m_temp);
|
|
m_t14 = sqrt(m_sqrt_t);
|
|
m_t32 = m_temp * m_sqrt_t;
|
|
m_sqrt_kbt = sqrt(Boltzmann*m_temp);
|
|
|
|
// compute powers of log(T)
|
|
m_polytempvec[0] = 1.0;
|
|
m_polytempvec[1] = m_logt;
|
|
m_polytempvec[2] = m_logt*m_logt;
|
|
m_polytempvec[3] = m_logt*m_logt*m_logt;
|
|
m_polytempvec[4] = m_logt*m_logt*m_logt*m_logt;
|
|
|
|
// temperature has changed, so polynomial fits will need to be redone.
|
|
m_viscmix_ok = false;
|
|
m_spvisc_ok = false;
|
|
m_viscwt_ok = false;
|
|
m_spcond_ok = false;
|
|
m_diffmix_ok = false;
|
|
m_bindiff_ok = false;
|
|
m_abc_ok = false;
|
|
m_condmix_ok = false;
|
|
}
|
|
|
|
void PecosTransport::update_C()
|
|
{
|
|
// signal that concentration-dependent quantities will need to
|
|
// be recomputed before use, and update the local mole
|
|
// fractions.
|
|
|
|
m_viscmix_ok = false;
|
|
m_diffmix_ok = false;
|
|
m_condmix_ok = false;
|
|
|
|
m_thermo->getMoleFractions(DATA_PTR(m_molefracs));
|
|
|
|
// add an offset to avoid a pure species condition
|
|
int k;
|
|
for (k = 0; k < m_nsp; k++) {
|
|
m_molefracs[k] = std::max(Tiny, m_molefracs[k]);
|
|
}
|
|
}
|
|
|
|
void PecosTransport::updateCond_T()
|
|
{
|
|
int k;
|
|
doublereal fivehalves = 5/2;
|
|
for (k = 0; k < m_nsp; k++) {
|
|
// need to add cv_elec in the future
|
|
m_cond[k] = m_visc[k] * (fivehalves * cv_int[k] + cv_rot[k] + m_thermo->cv_vib(k,m_temp));
|
|
}
|
|
m_spcond_ok = true;
|
|
m_condmix_ok = false;
|
|
}
|
|
|
|
void PecosTransport::updateDiff_T()
|
|
{
|
|
// evaluate binary diffusion coefficients at unit pressure
|
|
int i,j;
|
|
int ic = 0;
|
|
if (m_mode == CK_Mode) {
|
|
for (i = 0; i < m_nsp; i++) {
|
|
for (j = i; j < m_nsp; j++) {
|
|
m_bdiff(i,j) = exp(dot4(m_polytempvec, m_diffcoeffs[ic]));
|
|
m_bdiff(j,i) = m_bdiff(i,j);
|
|
ic++;
|
|
}
|
|
}
|
|
} else {
|
|
for (i = 0; i < m_nsp; i++) {
|
|
for (j = i; j < m_nsp; j++) {
|
|
m_bdiff(i,j) = m_temp * m_sqrt_t*dot5(m_polytempvec,
|
|
m_diffcoeffs[ic]);
|
|
m_bdiff(j,i) = m_bdiff(i,j);
|
|
ic++;
|
|
}
|
|
}
|
|
}
|
|
|
|
m_bindiff_ok = true;
|
|
m_diffmix_ok = false;
|
|
}
|
|
|
|
void PecosTransport::updateSpeciesViscosities()
|
|
{
|
|
int k;
|
|
// iterate over species, update pure-species viscosity
|
|
for (k = 0; k < m_nsp; k++) {
|
|
m_visc[k] = 0.10*std::exp(a[k]*(m_logt*m_logt) + b[k]*m_logt + c[k]);
|
|
m_sqvisc[k] = sqrt(m_visc[k]);
|
|
}
|
|
|
|
// time to update mixing
|
|
m_spvisc_ok = true;
|
|
}
|
|
|
|
void PecosTransport::read_blottner_transport_table()
|
|
{
|
|
// from: AIAA-1997-2474 and Sandia Report SC-RR-70-754
|
|
//
|
|
// # Air -- Identical to N2 fit
|
|
// # N -- Sandia Report SC-RR-70-754
|
|
// # N2 -- Sandia Report SC-RR-70-754
|
|
// # CPN2 -- Identical to N2 fit
|
|
// # NO -- Sandia Report SC-RR-70-754
|
|
// # O -- Sandia Report SC-RR-70-754
|
|
// # O2 -- Sandia Report SC-RR-70-754
|
|
// # C -- AIAA-1997-2474
|
|
// # C2 -- AIAA-1997-2474
|
|
// # C3 -- AIAA-1997-2474
|
|
// # C2H -- wild-ass guess: identical to HCN fit
|
|
// # CN -- AIAA-1997-2474
|
|
// # CO -- AIAA-1997-2474
|
|
// # CO2 -- AIAA-1997-2474
|
|
// # HCN -- AIAA-1997-2474
|
|
// # H -- AIAA-1997-2474
|
|
// # H2 -- AIAA-1997-2474
|
|
// # e -- Sandia Report SC-RR-70-754
|
|
|
|
istringstream blot
|
|
("Air 2.68142000000e-02 3.17783800000e-01 -1.13155513000e+01\n"
|
|
"CPAir 2.68142000000e-02 3.17783800000e-01 -1.13155513000e+01\n"
|
|
"N 1.15572000000e-02 6.03167900000e-01 -1.24327495000e+01\n"
|
|
"N2 2.68142000000e-02 3.17783800000e-01 -1.13155513000e+01\n"
|
|
"CPN2 2.68142000000e-02 3.17783800000e-01 -1.13155513000e+01\n"
|
|
"NO 4.36378000000e-02 -3.35511000000e-02 -9.57674300000e+00\n"
|
|
"O 2.03144000000e-02 4.29440400000e-01 -1.16031403000e+01\n"
|
|
"O2 4.49290000000e-02 -8.26158000000e-02 -9.20194750000e+00\n"
|
|
"C -8.3285e-3 0.7703240 -12.7378000\n"
|
|
"C2 -8.4311e-3 0.7876060 -13.0268000\n"
|
|
"C3 -8.4312e-3 0.7876090 -12.8240000\n"
|
|
"C2H -2.4241e-2 1.0946550 -14.5835500\n"
|
|
"CN -8.3811e-3 0.7860330 -12.9406000\n"
|
|
"CO -0.019527394 1.013295 -13.97873\n"
|
|
"CO2 -0.019527387 1.047818 -14.32212\n"
|
|
"HCN -2.4241e-2 1.0946550 -14.5835500\n"
|
|
"H -8.3912e-3 0.7743270 -13.6653000\n"
|
|
"H2 -8.3346e-3 0.7815380 -13.5351000\n"
|
|
"e 0.00000000000e+00 0.00000000000e+00 -1.16031403000e+01\n");
|
|
|
|
string line;
|
|
string name;
|
|
string ss1,ss2,ss3,ss4,sss;
|
|
int k;
|
|
int i = 0;
|
|
|
|
while (std::getline(blot, line)) {
|
|
|
|
istringstream ss(line);
|
|
std::getline(ss, ss1, ' ');
|
|
std::getline(ss, ss2, ' ');
|
|
std::getline(ss, ss3, ' ');
|
|
std::getline(ss, ss4, ' ');
|
|
name = ss1;
|
|
|
|
// now put coefficients in correct species
|
|
for (k = 0; k < m_nsp; k++) {
|
|
string sss = m_thermo->speciesName(k);
|
|
|
|
// this is the right species index
|
|
if (sss.compare(ss1) == 0) {
|
|
a[k] = fpValue(ss2);
|
|
b[k] = fpValue(ss3);
|
|
c[k] = fpValue(ss4);
|
|
|
|
// index
|
|
i++;
|
|
} else { // default to air
|
|
|
|
a[k] = 0.026;
|
|
b[k] = 0.3;
|
|
c[k] = -11.3;
|
|
}
|
|
|
|
} // done with for loop
|
|
}
|
|
}
|
|
|
|
void PecosTransport::updateViscosity_T()
|
|
{
|
|
doublereal vratiokj, wratiojk, factor1;
|
|
|
|
if (!m_spvisc_ok) {
|
|
updateSpeciesViscosities();
|
|
}
|
|
|
|
// see Eq. (9-5.15) of Reid, Prausnitz, and Poling
|
|
int j, k;
|
|
for (j = 0; j < m_nsp; j++) {
|
|
for (k = j; k < m_nsp; k++) {
|
|
vratiokj = m_visc[k]/m_visc[j];
|
|
wratiojk = m_mw[j]/m_mw[k];
|
|
|
|
// Note that m_wratjk(k,j) holds the square root of
|
|
// m_wratjk(j,k)!
|
|
factor1 = 1.0 + (m_sqvisc[k]/m_sqvisc[j]) * m_wratjk(k,j);
|
|
m_phi(k,j) = factor1*factor1 /
|
|
(sqrt(8.0) * m_wratkj1(j,k));
|
|
m_phi(j,k) = m_phi(k,j)/(vratiokj * wratiojk);
|
|
}
|
|
}
|
|
m_viscwt_ok = true;
|
|
}
|
|
|
|
}
|