cantera/src/transport/MultiTransport.cpp
Ray Speth 54efbaa320 Rewrote exception handling to be more general and more explicit
CanteraError inerits from std:exception, so now it has a what() method
that is used to print a message describing the exception. Adding an
exception to the Cantera error stack now requires explicitly calling
the .save() method.
2012-03-05 20:45:56 +00:00

1074 lines
32 KiB
C++

/**
* @file MultiTransport.cpp
* Implementation file for class MultiTransport
*/
/*
* Copyright 2001 California Institute of Technology
* See file License.txt for licensing information
*/
#include "cantera/thermo/ThermoPhase.h"
#include "cantera/transport/MultiTransport.h"
#include "cantera/numerics/ctlapack.h"
#include "cantera/numerics/DenseMatrix.h"
#include "cantera/base/utilities.h"
#include "cantera/base/utilities.h"
#include "L_matrix.h"
#include "cantera/transport/TransportParams.h"
#include "cantera/thermo/IdealGasPhase.h"
#include "cantera/transport/TransportFactory.h"
#include <iostream>
using namespace std;
/**
* Mole fractions below MIN_X will be set to MIN_X when computing
* transport properties.
*/
#define MIN_X 1.e-20
namespace Cantera
{
/////////////////////////// constants //////////////////////////
// const doublereal ThreeSixteenths = 3.0/16.0;
///////////////////// helper functions /////////////////////////
/**
* @internal
*
* The Parker temperature correction to the rotational collision
* number.
*
* @param tr Reduced temperature \f$ \epsilon/kT \f$
* @param sqtr square root of tr.
*/
inline doublereal Frot(doublereal tr, doublereal sqtr)
{
const doublereal c1 = 0.5*SqrtPi*Pi;
const doublereal c2 = 0.25*Pi*Pi + 2.0;
const doublereal c3 = SqrtPi*Pi;
return 1.0 + c1*sqtr + c2*tr + c3*sqtr*tr;
}
/**
* This method is used by GMRES to multiply the L matrix by a
* vector b. The L matrix has a 3x3 block structure, where each
* block is a K x K matrix. The elements of the upper-right and
* lower-left blocks are all zero. This method is defined so
* that the multiplication only involves the seven non-zero
* blocks.
*/
void L_Matrix::mult(const doublereal* b, doublereal* prod) const
{
integer n = static_cast<int>(nRows())/3;
integer n2 = 2*n;
integer n3 = 3*n;
ct_dgemv(ctlapack::ColMajor, ctlapack::NoTranspose, n, n2, 1.0,
DATA_PTR(data()), static_cast<int>(nRows()), b, 1, 0.0, prod, 1);
ct_dgemv(ctlapack::ColMajor, ctlapack::NoTranspose, n, n3, 1.0,
DATA_PTR(data()) + n, static_cast<int>(nRows()),
b, 1, 0.0, prod+n, 1);
ct_dgemv(ctlapack::ColMajor, ctlapack::NoTranspose, n, n, 1.0,
DATA_PTR(data()) + n*n3 + n2, static_cast<int>(nRows()),
b + n, 1, 0.0, prod+n2, 1);
for (int i = 0; i < n; i++) {
prod[i + n2] += b[i + n2] * value(i + n2, i + n2);
}
}
//////////////////// class MultiTransport methods //////////////
MultiTransport::MultiTransport(thermo_t* thermo)
: Transport(thermo),
m_temp(-1.0)
{
}
MultiTransport::~MultiTransport()
{
}
//====================================================================================================================
bool MultiTransport::initGas(GasTransportParams& tr)
{
// constant mixture attributes
//m_phase = tr.mix;
m_thermo = tr.thermo;
m_nsp = m_thermo->nSpecies();
m_tmin = m_thermo->minTemp();
m_tmax = m_thermo->maxTemp();
// 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_diffcoeffs = tr.diffcoeffs;
m_astar_poly = tr.astar_poly;
m_bstar_poly = tr.bstar_poly;
m_cstar_poly = tr.cstar_poly;
m_om22_poly = tr.omega22_poly;
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 (size_t i = 0; i < m_nsp; i++) {
m_dipoleDiag[i] = tr.dipole(i,i);
}
// the L matrix
m_Lmatrix.resize(3*m_nsp, 3*m_nsp);
m_a.resize(3*m_nsp, 1.0);
m_b.resize(3*m_nsp, 0.0);
m_aa.resize(m_nsp, m_nsp, 0.0);
m_frot_298.resize(m_nsp);
m_rotrelax.resize(m_nsp);
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);
for (size_t j = 0; j < m_nsp; j++)
for (size_t 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_cinternal.resize(m_nsp);
m_polytempvec.resize(5);
m_visc.resize(m_nsp);
m_sqvisc.resize(m_nsp);
m_bdiff.resize(m_nsp, m_nsp);
//m_poly.resize(m_nsp);
m_om22.resize(m_nsp, m_nsp);
m_astar.resize(m_nsp, m_nsp);
m_bstar.resize(m_nsp, m_nsp);
m_cstar.resize(m_nsp, m_nsp);
m_molefracs.resize(m_nsp);
// set flags all false
m_visc_ok = false;
m_spvisc_ok = false;
m_diff_ok = false;
m_abc_ok = false;
m_l0000_ok = false;
m_lmatrix_soln_ok = false;
m_diff_tlast = 0.0;
m_spvisc_tlast = 0.0;
m_visc_tlast = 0.0;
m_thermal_tlast = 0.0;
// use LU decomposition by default
m_gmres = false;
// default GMRES parameters
m_mgmres = 100;
m_eps_gmres = 1.e-4;
// some work space
m_spwork.resize(m_nsp);
m_spwork1.resize(m_nsp);
m_spwork2.resize(m_nsp);
m_spwork3.resize(m_nsp);
// precompute and store log(epsilon_ij/k_B)
m_log_eps_k.resize(m_nsp, m_nsp);
// int j;
for (size_t i = 0; i < m_nsp; i++) {
for (size_t j = i; j < m_nsp; j++) {
m_log_eps_k(i,j) = log(tr.epsilon(i,j)/Boltzmann);
m_log_eps_k(j,i) = m_log_eps_k(i,j);
}
}
// precompute and store constant parts of the Parker rotational
// collision number temperature correction
const doublereal sq298 = sqrt(298.0);
const doublereal kb298 = Boltzmann * 298.0;
m_sqrt_eps_k.resize(m_nsp);
for (size_t k = 0; k < m_nsp; k++) {
m_sqrt_eps_k[k] = sqrt(tr.eps[k]/Boltzmann);
m_frot_298[k] = Frot(tr.eps[k]/kb298,
m_sqrt_eps_k[k]/sq298);
}
// // install updaters
// m_update_transport_T = m_thermo->installUpdater_T(
// new UpdateTransport_T<MultiTransport>(*this));
// m_update_transport_C = m_thermo->installUpdater_C(
// new UpdateTransport_C<MultiTransport>(*this));
// m_update_spvisc_T = m_thermo->installUpdater_T(
// new UpdateSpeciesVisc<MultiTransport>(*this));
// m_update_visc_T = m_thermo->installUpdater_T(
// new UpdateVisc_T<MultiTransport>(*this));
// m_update_diff_T = m_thermo->installUpdater_T(
// new UpdateDiff_T<MultiTransport>(*this));
// m_update_thermal_T = m_thermo->installUpdater_T(
// new UpdateThermal_T<MultiTransport>(*this));
return true;
}
/****************** viscosity ******************************/
doublereal MultiTransport::viscosity()
{
doublereal vismix = 0.0, denom;
// update m_visc if necessary
updateViscosity_T();
// update the mole fractions
updateTransport_C();
for (size_t k = 0; k < m_nsp; k++) {
denom = 0.0;
for (size_t j = 0; j < m_nsp; j++) {
denom += m_phi(k,j) * m_molefracs[j];
}
vismix += m_molefracs[k] * m_visc[k]/denom;
}
return vismix;
}
//====================================================================================================================
/******************* binary diffusion coefficients **************/
void MultiTransport::getBinaryDiffCoeffs(size_t ld, doublereal* d)
{
// if necessary, evaluate the binary diffusion coefficents
// from the polynomial fits
updateDiff_T();
doublereal p = pressure_ig();
doublereal rp = 1.0/p;
for (size_t i = 0; i < m_nsp; i++)
for (size_t j = 0; j < m_nsp; j++) {
d[ld*j + i] = rp * m_bdiff(i,j);
}
}
/****************** thermal conductivity **********************/
/**
* @internal
*/
doublereal MultiTransport::thermalConductivity()
{
solveLMatrixEquation();
doublereal sum = 0.0;
for (size_t k = 0; k < 2*m_nsp; k++) {
sum += m_b[k + m_nsp] * m_a[k + m_nsp];
}
return -4.0*sum;
}
//====================================================================================================================
// Return the thermal diffusion coefficients for the species
/*
*
* @param dt thermal diffusion coefficients
* (length = m_nsp)
*/
void MultiTransport::getThermalDiffCoeffs(doublereal* const dt)
{
solveLMatrixEquation();
const doublereal c = 1.6/GasConstant;
for (size_t k = 0; k < m_nsp; k++) {
dt[k] = c * m_mw[k] * m_molefracs[k] * m_a[k];
}
}
//====================================================================================================================
/**
* @internal
*/
void MultiTransport::solveLMatrixEquation()
{
// if T has changed, update the temperature-dependent
// properties.
updateThermal_T();
updateTransport_C();
// Copy the mole fractions twice into the last two blocks of
// the right-hand-side vector m_b. The first block of m_b was
// set to zero when it was created, and is not modified so
// doesn't need to be reset to zero.
for (size_t k = 0; k < m_nsp; k++) {
m_b[k] = 0.0;
m_b[k + m_nsp] = m_molefracs[k];
m_b[k + 2*m_nsp] = m_molefracs[k];
}
// Set the right-hand side vector to zero in the 3rd block for
// all species with no internal energy modes. The
// corresponding third-block rows and columns will be set to
// zero, except on the diagonal of L01,01, where they are set
// to 1.0. This has the effect of eliminating these equations
// from the system, since the equation becomes: m_a[2*m_nsp +
// k] = 0.0.
// Note that this differs from the Chemkin procedure, where
// all *monatomic* species are excluded. Since monatomic
// radicals can have non-zero internal heat capacities due to
// electronic excitation, they should be retained.
//
// But if CHEMKIN_COMPATIBILITY_MODE is defined, then all
// monatomic species are excluded.
for (size_t k = 0; k < m_nsp; k++) {
if (!hasInternalModes(k)) {
m_b[2*m_nsp + k] = 0.0;
}
}
// evaluate the submatrices of the L matrix
m_Lmatrix.resize(3*m_nsp, 3*m_nsp, 0.0);
eval_L0000(DATA_PTR(m_molefracs));
eval_L0010(DATA_PTR(m_molefracs));
eval_L0001();
eval_L1000();
eval_L1010(DATA_PTR(m_molefracs));
eval_L1001(DATA_PTR(m_molefracs));
eval_L0100();
eval_L0110();
eval_L0101(DATA_PTR(m_molefracs));
// Solve it using GMRES or LU decomposition. The last solution
// in m_a should provide a good starting guess, so convergence
// should be fast.
//if (m_gmres) {
// gmres(m_mgmres, 3*m_nsp, m_Lmatrix, m_b.begin(),
// m_a.begin(), m_eps_gmres);
// m_lmatrix_soln_ok = true;
// m_l0000_ok = true; // L matrix not modified by GMRES
//}
//else {
copy(m_b.begin(), m_b.end(), m_a.begin());
try {
solve(m_Lmatrix, DATA_PTR(m_a));
} catch (CanteraError& err) {
err.save();
//if (info != 0) {
throw CanteraError("MultiTransport::solveLMatrixEquation",
"error in solving L matrix.");
}
m_lmatrix_soln_ok = true;
m_l0000_ok = false;
// L matrix is overwritten with LU decomposition
//}
m_lmatrix_soln_ok = true;
}
//====================================================================================================================
// Get the species diffusive mass fluxes wrt to the mass averaged velocity,
// given the gradients in mole fraction and temperature
/*
* Units for the returned fluxes are kg m-2 s-1.
*
* @param ndim Number of dimensions in the flux expressions
* @param grad_T Gradient of the temperature
* (length = ndim)
* @param ldx Leading dimension of the grad_X array
* (usually equal to m_nsp but not always)
* @param grad_X Gradients of the mole fraction
* Flat vector with the m_nsp in the inner loop.
* length = ldx * ndim
* @param ldf Leading dimension of the fluxes array
* (usually equal to m_nsp but not always)
* @param fluxes Output of the diffusive mass fluxes
* Flat vector with the m_nsp in the inner loop.
* length = ldx * ndim
*/
void MultiTransport::getSpeciesFluxes(size_t ndim, const doublereal* const grad_T, int ldx,
const doublereal* const grad_X,
int ldf, doublereal* const fluxes)
{
// update the binary diffusion coefficients if necessary
updateDiff_T();
doublereal sum;
// If any component of grad_T is non-zero, then get the
// thermal diffusion coefficients
bool addThermalDiffusion = false;
for (size_t i = 0; i < ndim; i++) {
if (grad_T[i] != 0.0) {
addThermalDiffusion = true;
}
}
if (addThermalDiffusion) {
getThermalDiffCoeffs(DATA_PTR(m_spwork));
}
const doublereal* y = m_thermo->massFractions();
doublereal rho = m_thermo->density();
for (size_t i = 0; i < m_nsp; i++) {
sum = 0.0;
for (size_t j = 0; j < m_nsp; j++) {
m_aa(i,j) = m_molefracs[j]*m_molefracs[i]/m_bdiff(i,j);
sum += m_aa(i,j);
}
m_aa(i,i) -= sum;
}
// enforce the condition \sum Y_k V_k = 0. This is done by replacing
// the flux equation with the largest gradx component in the first
// coordinate direction with the flux balance condition.
size_t jmax = 0;
doublereal gradmax = -1.0;
for (size_t j = 0; j < m_nsp; j++) {
if (fabs(grad_X[j]) > gradmax) {
gradmax = fabs(grad_X[j]);
jmax = j;
}
}
// set the matrix elements in this row to the mass fractions,
// and set the entry in gradx to zero
for (size_t j = 0; j < m_nsp; j++) {
m_aa(jmax,j) = y[j];
}
vector_fp gsave(ndim), grx(ldx*m_nsp);
for (size_t n = 0; n < ldx*ndim; n++) {
grx[n] = grad_X[n];
}
//for (n = 0; n < ndim; n++) {
// gsave[n] = grad_X[jmax + n*ldx]; // save the input mole frac gradient
//grad_X[jmax + n*ldx] = 0.0;
// grx[jmax + n*ldx] = 0.0;
// }
// copy grad_X to fluxes
const doublereal* gx;
for (size_t n = 0; n < ndim; n++) {
gx = grad_X + ldx*n;
copy(gx, gx + m_nsp, fluxes + ldf*n);
fluxes[jmax + n*ldf] = 0.0;
}
// use LAPACK to solve the equations
int info=0;
ct_dgetrf(static_cast<int>(m_aa.nRows()),
static_cast<int>(m_aa.nColumns()), m_aa.ptrColumn(0),
static_cast<int>(m_aa.nRows()),
&m_aa.ipiv()[0], info);
if (info == 0) {
ct_dgetrs(ctlapack::NoTranspose,
static_cast<int>(m_aa.nRows()), ndim,
m_aa.ptrColumn(0), static_cast<int>(m_aa.nRows()),
&m_aa.ipiv()[0], fluxes, ldf, info);
if (info != 0) {
info += 100;
}
} else
throw CanteraError("MultiTransport::getSpeciesFluxes",
"Error in DGETRF");
if (info > 50)
throw CanteraError("MultiTransport::getSpeciesFluxes",
"Error in DGETRS");
size_t offset;
doublereal pp = pressure_ig();
// multiply diffusion velocities by rho * V to create
// mass fluxes, and restore the gradx elements that were
// modified
for (size_t n = 0; n < ndim; n++) {
offset = n*ldf;
for (size_t i = 0; i < m_nsp; i++) {
fluxes[i + offset] *= rho * y[i] / pp;
}
//grad_X[jmax + n*ldx] = gsave[n];
}
// thermal diffusion
if (addThermalDiffusion) {
for (size_t n = 0; n < ndim; n++) {
offset = n*ldf;
doublereal grad_logt = grad_T[n]/m_temp;
for (size_t i = 0; i < m_nsp; i++) {
fluxes[i + offset] -= m_spwork[i]*grad_logt;
}
}
}
}
//====================================================================================================================
// Get the mass diffusional fluxes [kg/m^2/s] of the species, given the thermodynamic
// state at two nearby points.
/*
* The specific diffusional fluxes are calculated with reference to the mass averaged
* velocity. This is a one-dimensional vector
*
* @param state1 Array of temperature, density, and mass
* fractions for state 1.
* @param state2 Array of temperature, density, and mass
* fractions for state 2.
* @param delta Distance from state 1 to state 2 (m).
* @param fluxes Output mass fluxes of the species.
* (length = m_nsp)
*/
void MultiTransport::getMassFluxes(const doublereal* state1, const doublereal* state2, doublereal delta,
doublereal* fluxes)
{
double* x1 = DATA_PTR(m_spwork1);
double* x2 = DATA_PTR(m_spwork2);
double* x3 = DATA_PTR(m_spwork3);
size_t n, nsp = m_thermo->nSpecies();
m_thermo->restoreState(nsp+2, state1);
double p1 = m_thermo->pressure();
double t1 = state1[0];
m_thermo->getMoleFractions(x1);
m_thermo->restoreState(nsp+2, state2);
double p2 = m_thermo->pressure();
double t2 = state2[0];
m_thermo->getMoleFractions(x2);
//
double p = 0.5*(p1 + p2);
double t = 0.5*(state1[0] + state2[0]);
for (n = 0; n < nsp; n++) {
x3[n] = 0.5*(x1[n] + x2[n]);
}
m_thermo->setState_TPX(t, p, x3);
m_thermo->getMoleFractions(DATA_PTR(m_molefracs));
// update the binary diffusion coefficients if necessary
updateDiff_T();
// If there is a temperature gadient, then get the
// thermal diffusion coefficients
bool addThermalDiffusion = false;
if (state1[0] != state2[0]) {
addThermalDiffusion = true;
getThermalDiffCoeffs(DATA_PTR(m_spwork));
}
const doublereal* y = m_thermo->massFractions();
doublereal rho = m_thermo->density();
for (size_t i = 0; i < m_nsp; i++) {
doublereal sum = 0.0;
for (size_t j = 0; j < m_nsp; j++) {
m_aa(i,j) = m_molefracs[j]*m_molefracs[i]/m_bdiff(i,j);
sum += m_aa(i,j);
}
m_aa(i,i) -= sum;
}
// enforce the condition \sum Y_k V_k = 0. This is done by
// replacing the flux equation with the largest gradx
// component with the flux balance condition.
size_t jmax = 0;
doublereal gradmax = -1.0;
for (size_t j = 0; j < m_nsp; j++) {
if (fabs(x2[j] - x1[j]) > gradmax) {
gradmax = fabs(x1[j] - x2[j]);
jmax = j;
}
}
// set the matrix elements in this row to the mass fractions,
// and set the entry in gradx to zero
for (size_t j = 0; j < m_nsp; j++) {
m_aa(jmax,j) = y[j];
fluxes[j] = x2[j] - x1[j];
}
fluxes[jmax] = 0.0;
// use LAPACK to solve the equations
int info=0;
size_t nr = m_aa.nRows();
size_t nc = m_aa.nColumns();
ct_dgetrf(nr, nc, m_aa.ptrColumn(0), nr, &m_aa.ipiv()[0], info);
if (info == 0) {
int ndim = 1;
ct_dgetrs(ctlapack::NoTranspose, nr, ndim,
m_aa.ptrColumn(0), nr, &m_aa.ipiv()[0], fluxes, nr, info);
if (info != 0)
throw CanteraError("MultiTransport::getMassFluxes",
"Error in DGETRS. Info = "+int2str(info));
} else
throw CanteraError("MultiTransport::getMassFluxes",
"Error in DGETRF. Info = "+int2str(info));
doublereal pp = pressure_ig();
// multiply diffusion velocities by rho * Y_k to create
// mass fluxes, and divide by pressure
for (size_t i = 0; i < m_nsp; i++) {
fluxes[i] *= rho * y[i] / pp;
}
// thermal diffusion
if (addThermalDiffusion) {
doublereal grad_logt = (t2 - t1)/m_temp;
for (size_t i = 0; i < m_nsp; i++) {
fluxes[i] -= m_spwork[i]*grad_logt;
}
}
}
//====================================================================================================================
void MultiTransport::getMolarFluxes(const doublereal* const state1,
const doublereal* const state2,
const doublereal delta,
doublereal* const fluxes)
{
getMassFluxes(state1, state2, delta, fluxes);
for (size_t k = 0; k < m_thermo->nSpecies(); k++) {
fluxes[k] /= m_mw[k];
}
}
//====================================================================================================================
// Set the solution method for inverting the L matrix
/*
* @param method enum TRANSOLVE_TYPE Either use direct or TRANSOLVE_GMRES
*/
void MultiTransport::setSolutionMethod(TRANSOLVE_TYPE method)
{
if (method == TRANSOLVE_GMRES) {
m_gmres = true;
} else {
m_gmres = false;
}
}
//====================================================================================================================
void MultiTransport::setOptions_GMRES(int m, doublereal eps)
{
if (m > 0) {
m_mgmres = m;
}
if (eps > 0.0) {
m_eps_gmres = eps;
}
}
//====================================================================================================================
void MultiTransport::getMultiDiffCoeffs(const size_t ld, doublereal* const d)
{
doublereal p = pressure_ig();
// update the mole fractions
updateTransport_C();
// update the binary diffusion coefficients
updateDiff_T();
// evaluate L0000 if the temperature or concentrations have
// changed since it was last evaluated.
if (!m_l0000_ok) {
eval_L0000(DATA_PTR(m_molefracs));
}
// invert L00,00
int ierr = invert(m_Lmatrix, m_nsp);
if (ierr != 0) {
throw CanteraError("MultiTransport::getMultiDiffCoeffs",
string(" invert returned ierr = ")+int2str(ierr));
}
m_l0000_ok = false; // matrix is overwritten by inverse
//doublereal pres = m_thermo->pressure();
doublereal prefactor = 16.0 * m_temp
* m_thermo->meanMolecularWeight()/(25.0 * p);
doublereal c;
for (size_t i = 0; i < m_nsp; i++) {
for (size_t j = 0; j < m_nsp; j++) {
c = prefactor/m_mw[j];
d[ld*j + i] = c*m_molefracs[i]*
(m_Lmatrix(i,j) - m_Lmatrix(i,i));
}
}
}
//====================================================================================================================
void MultiTransport::getMixDiffCoeffs(doublereal* const d)
{
// update the mole fractions
updateTransport_C();
// update the binary diffusion coefficients if necessary
updateDiff_T();
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 (size_t k = 0; k < m_nsp; k++) {
sumxw += m_molefracs[k] * m_mw[k];
}
for (size_t k = 0; k < m_nsp; k++) {
sum2 = 0.0;
for (size_t 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 MultiTransport::updateTransport_T()
{
//m_thermo->update_T(m_update_transport_T);
_update_transport_T();
}
void MultiTransport::updateTransport_C()
{
// {m_thermo->update_C(m_update_transport_C);
_update_transport_C();
}
/**
* Update temperature-dependent quantities. This method is called
* by the temperature property updater.
*/
void MultiTransport::_update_transport_T()
{
if (m_temp == m_thermo->temperature()) {
return;
}
m_temp = m_thermo->temperature();
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, and the L matrix reevaluated.
m_visc_ok = false;
m_spvisc_ok = false;
m_diff_ok = false;
m_abc_ok = false;
m_lmatrix_soln_ok = false;
m_l0000_ok = false;
}
/**
* This is called the first time any transport property
* is requested from ThermoSubstance after the concentrations
* have changed.
*/
void MultiTransport::_update_transport_C()
{
// signal that concentration-dependent quantities will need to
// be recomputed before use, and update the local mole
// fraction array.
m_l0000_ok = false;
m_lmatrix_soln_ok = false;
m_thermo->getMoleFractions(DATA_PTR(m_molefracs));
// add an offset to avoid a pure species condition
// (check - this may be unnecessary)
for (size_t k = 0; k < m_nsp; k++) {
m_molefracs[k] = std::max(MIN_X, m_molefracs[k]);
}
}
/*************************************************************************
*
* methods to update temperature-dependent properties
*
*************************************************************************/
/**
* @internal
* Update the binary diffusion coefficients. These are evaluated
* from the polynomial fits at unit pressure (1 Pa).
*/
void MultiTransport::updateDiff_T()
{
if (m_diff_tlast == m_thermo->temperature()) {
return;
}
_update_diff_T();
m_diff_tlast = m_thermo->temperature();
//m_thermo->update_T(m_update_diff_T);
}
void MultiTransport::_update_diff_T()
{
updateTransport_T();
// evaluate binary diffusion coefficients at unit pressure
size_t ic = 0;
if (m_mode == CK_Mode) {
for (size_t i = 0; i < m_nsp; i++) {
for (size_t 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 (size_t i = 0; i < m_nsp; i++) {
for (size_t 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_diff_ok = true;
}
/**
* @internal
* Update the temperature-dependent viscosity terms.
* Updates the array of pure species viscosities, and the
* weighting functions in the viscosity mixture rule.
* The flag m_visc_ok is set to true.
*/
void MultiTransport::updateSpeciesViscosities_T()
{
if (m_spvisc_tlast == m_thermo->temperature()) {
return;
}
_update_species_visc_T();
//m_thermo->update_T(m_update_spvisc_T);
m_spvisc_tlast = m_thermo->temperature();
}
void MultiTransport::_update_species_visc_T()
{
updateTransport_T();
if (m_mode == CK_Mode) {
for (size_t k = 0; k < m_nsp; k++) {
m_visc[k] = exp(dot4(m_polytempvec, m_visccoeffs[k]));
m_sqvisc[k] = sqrt(m_visc[k]);
}
} else {
for (size_t k = 0; k < m_nsp; k++) {
//m_visc[k] = m_sqrt_t*dot5(m_polytempvec, m_visccoeffs[k]);
// the polynomial fit is done for sqrt(visc/sqrt(T))
m_sqvisc[k] = m_t14*dot5(m_polytempvec, m_visccoeffs[k]);
m_visc[k] = (m_sqvisc[k]*m_sqvisc[k]);
}
}
m_spvisc_ok = true;
}
/**
* @internal
*/
void MultiTransport::updateViscosity_T()
{
if (m_visc_tlast == m_thermo->temperature()) {
return;
}
_update_visc_T();
m_visc_tlast = m_thermo->temperature();
}
void MultiTransport::_update_visc_T()
{
doublereal vratiokj, wratiojk, factor1;
updateSpeciesViscosities_T();
// see Eq. (9-5.15) of Reid, Prausnitz, and Poling
for (size_t j = 0; j < m_nsp; j++) {
for (size_t k = j; k < m_nsp; k++) {
vratiokj = m_visc[k]/m_visc[j];
wratiojk = m_mw[j]/m_mw[k];
//rootwjk = sqrt(wratiojk);
//factor1 = 1.0 + sqrt(vratiokj * rootwjk);
//m_phi(k,j) = factor1*factor1 /
// (SqrtEight * sqrt(1.0 + m_mw[k]/m_mw[j]));
//m_phi(j,k) = m_phi(k,j)/(vratiokj * wratiojk);
// 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 /
(SqrtEight * m_wratkj1(j,k));
m_phi(j,k) = m_phi(k,j)/(vratiokj * wratiojk);
}
}
m_visc_ok = true;
}
/**
* @internal
* Update the temperature-dependent terms needed to compute the
* thermal conductivity and thermal diffusion coefficients.
*/
void MultiTransport::updateThermal_T()
{
if (m_thermal_tlast == m_thermo->temperature()) {
return;
}
_update_thermal_T();
// m_thermo->update_T(m_update_thermal_T);
m_thermal_tlast = m_thermo->temperature();
}
void MultiTransport::_update_thermal_T()
{
// we need species viscosities and binary diffusion
// coefficients
updateSpeciesViscosities_T();
updateDiff_T();
// evaluate polynomial fits for A*, B*, C*
doublereal z;
int ipoly;
for (size_t i = 0; i < m_nsp; i++) {
for (size_t j = i; j < m_nsp; j++) {
z = m_logt - m_log_eps_k(i,j);
ipoly = m_poly[i][j];
if (m_mode == CK_Mode) {
m_om22(i,j) = poly6(z, DATA_PTR(m_om22_poly[ipoly]));
m_astar(i,j) = poly6(z, DATA_PTR(m_astar_poly[ipoly]));
m_bstar(i,j) = poly6(z, DATA_PTR(m_bstar_poly[ipoly]));
m_cstar(i,j) = poly6(z, DATA_PTR(m_cstar_poly[ipoly]));
} else {
m_om22(i,j) = poly8(z, DATA_PTR(m_om22_poly[ipoly]));
m_astar(i,j) = poly8(z, DATA_PTR(m_astar_poly[ipoly]));
m_bstar(i,j) = poly8(z, DATA_PTR(m_bstar_poly[ipoly]));
m_cstar(i,j) = poly8(z, DATA_PTR(m_cstar_poly[ipoly]));
}
m_om22(j,i) = m_om22(i,j);
m_astar(j,i) = m_astar(i,j);
m_bstar(j,i) = m_bstar(i,j);
m_cstar(j,i) = m_cstar(i,j);
}
}
m_abc_ok = true;
// evaluate the temperature-dependent rotational relaxation
// rate
doublereal tr, sqtr;
for (size_t k = 0; k < m_nsp; k++) {
tr = m_eps[k]/ m_kbt;
sqtr = m_sqrt_eps_k[k] / m_sqrt_t;
m_rotrelax[k] = std::max(1.0,m_zrot[k]) * m_frot_298[k]/Frot(tr, sqtr);
}
doublereal d;
doublereal c = 1.2*GasConstant*m_temp;
for (size_t k = 0; k < m_nsp; k++) {
d = c * m_visc[k] * m_astar(k,k)/m_mw[k];
m_bdiff(k,k) = d;
}
// Calculate the internal heat capacities by subtracting off the translational contributions
/*
* HKM Exploratory comment:
* The translational component is 1.5
* The rotational component is 1.0 for a linear molecule and 1.5 for a nonlinear molecule
* and zero for a monotomic.
* Chemkin has traditionally subtracted 1.5 here (SAND86-8246).
* The original Dixon-Lewis paper subtracted 1.5 here.
*/
const vector_fp& cp = ((IdealGasPhase*)m_thermo)->cp_R_ref();
for (size_t k = 0; k < m_nsp; k++) {
m_cinternal[k] = cp[k] - 2.5;
}
}
//====================================================================================================================
/*
* This function returns a Transport data object for a given species.
*
*/
struct GasTransportData MultiTransport::
getGasTransportData(int kSpecies) {
struct GasTransportData td;
td.speciesName = m_thermo->speciesName(kSpecies);
td.geometry = 2;
if (m_crot[kSpecies] == 0.0) {
td.geometry = 0;
} else if (m_crot[kSpecies] == 1.0) {
td.geometry = 1;
}
td.wellDepth = m_eps[kSpecies] / Boltzmann;
td.dipoleMoment = m_dipoleDiag[kSpecies] * 1.0E25 / SqrtTen;
td.diameter = m_diam(kSpecies, kSpecies) * 1.0E10;
td.polarizability = m_alpha[kSpecies] * 1.0E30;
td.rotRelaxNumber = m_zrot[kSpecies];
return td;
}
//====================================================================================================================
}