cantera/src/transport/TransportFactory.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

1615 lines
59 KiB
C++

/**
* @file TransportFactory.cpp
*
* Implementation file for class TransportFactory.
*/
#include "cantera/thermo/ThermoPhase.h"
// known transport models
#include "cantera/transport/MultiTransport.h"
#include "cantera/transport/MixTransport.h"
#include "cantera/transport/SolidTransport.h"
#include "cantera/transport/DustyGasTransport.h"
#include "cantera/transport/SimpleTransport.h"
#ifdef WITH_IDEAL_SOLUTIONS
#include "cantera/transport/LiquidTransport.h"
#endif
#ifdef WITH_ELECTROLYTES
#include "cantera/transport/AqueousTransport.h"
#endif
#include "cantera/transport/TransportFactory.h"
#include "cantera/numerics/polyfit.h"
#include "MMCollisionInt.h"
#include "cantera/base/xml.h"
#include "cantera/base/XML_Writer.h"
#include "cantera/transport/TransportParams.h"
#include "cantera/transport/LiquidTransportParams.h"
#include "cantera/transport/LiquidTranInteraction.h"
#include "cantera/base/global.h"
#include "cantera/thermo/IdealGasPhase.h"
#include "cantera/base/ctml.h"
#include <cstdio>
#include <cstring>
using namespace std;
//! polynomial degree used for fitting collision integrals
//! except in CK mode, where the degree is 6.
#define COLL_INT_POLY_DEGREE 8
namespace Cantera
{
/////////////////////////// constants //////////////////////////
//@ \cond
const doublereal ThreeSixteenths = 3.0/16.0;
const doublereal TwoOverPi = 2.0/Pi;
const doublereal FiveThirds = 5.0/3.0;
//@ \endcond
//====================================================================================================================
TransportFactory* TransportFactory::s_factory = 0;
#if defined(THREAD_SAFE_CANTERA)
// declaration of static storage for the mutex
boost::mutex TransportFactory::transport_mutex;
#endif
////////////////////////// exceptions /////////////////////////
//====================================================================================================================
//! Exception thrown if an error is encountered while reading the transport database
class TransportDBError : public CanteraError
{
public:
//! Default constructor
/*!
* @param linenum inputs the line number
* @param msg String message to be sent to the user
*/
TransportDBError(int linenum, std::string msg) :
CanteraError("getTransportData", "error reading transport data: " + msg + "\n") {
}
};
//====================================================================================================================
//////////////////// class TransportFactory methods //////////////
//====================================================================================================================
// Second-order correction to the binary diffusion coefficients
/*
* Calculate second-order corrections to binary diffusion
* coefficient pair (dkj, djk). At first order, the binary
* diffusion coefficients are independent of composition, and
* d(k,j) = d(j,k). But at second order, there is a weak
* dependence on composition, with the result that d(k,j) !=
* d(j,k). This method computes the multiplier by which the
* first-order binary diffusion coefficient should be multiplied
* to produce the value correct to second order. The expressions
* here are taken from Marerro and Mason,
* J. Phys. Chem. Ref. Data, vol. 1, p. 3 (1972).
*
* @param t Temperature (K)
* @param tr Transport parameters
* @param k index of first species
* @param j index of second species
* @param xmk mole fraction of species k
* @param xmj mole fraction of species j
* @param fkj multiplier for d(k,j)
* @param fjk multiplier for d(j,k)
*
* @note This method is not used currently.
*/
void TransportFactory::getBinDiffCorrection(doublereal t, const GasTransportParams& tr,
size_t k, size_t j, doublereal xk, doublereal xj,
doublereal& fkj, doublereal& fjk)
{
doublereal w1, w2, wsum, sig1, sig2, sig12, sigratio, sigratio2,
sigratio3, tstar1, tstar2, tstar12,
om22_1, om22_2, om11_12, astar_12, bstar_12, cstar_12,
cnst, wmwp, sqw12, p1, p2, p12, q1, q2, q12;
w1 = tr.mw[k];
w2 = tr.mw[j];
wsum = w1 + w2;
wmwp = (w1 - w2)/wsum;
sqw12 = sqrt(w1*w2);
sig1 = tr.sigma[k];
sig2 = tr.sigma[j];
sig12 = 0.5*(tr.sigma[k] + tr.sigma[j]);
sigratio = sig1*sig1/(sig2*sig2);
sigratio2 = sig1*sig1/(sig12*sig12);
sigratio3 = sig2*sig2/(sig12*sig12);
tstar1 = Boltzmann * t / tr.eps[k];
tstar2 = Boltzmann * t / tr.eps[j];
tstar12 = Boltzmann * t / sqrt(tr.eps[k] * tr.eps[j]);
om22_1 = m_integrals->omega22(tstar1, tr.delta(k,k));
om22_2 = m_integrals->omega22(tstar2, tr.delta(j,j));
om11_12 = m_integrals->omega11(tstar12, tr.delta(k,j));
astar_12 = m_integrals->astar(tstar12, tr.delta(k,j));
bstar_12 = m_integrals->bstar(tstar12, tr.delta(k,j));
cstar_12 = m_integrals->cstar(tstar12, tr.delta(k,j));
cnst = sigratio * sqrt(2.0*w2/wsum) * 2.0 *
w1*w1/(wsum * w2);
p1 = cnst * om22_1 / om11_12;
cnst = (1.0/sigratio) * sqrt(2.0*w1/wsum) * 2.0*w2*w2/(wsum*w1);
p2 = cnst * om22_2 / om11_12;
p12 = 15.0 * wmwp*wmwp + 8.0*w1*w2*astar_12/(wsum*wsum);
cnst = (2.0/(w2*wsum))*sqrt(2.0*w2/wsum)*sigratio2;
q1 = cnst*((2.5 - 1.2*bstar_12)*w1*w1 + 3.0*w2*w2
+ 1.6*w1*w2*astar_12);
cnst = (2.0/(w1*wsum))*sqrt(2.0*w1/wsum)*sigratio3;
q2 = cnst*((2.5 - 1.2*bstar_12)*w2*w2 + 3.0*w1*w1
+ 1.6*w1*w2*astar_12);
q12 = wmwp*wmwp*15.0*(2.5 - 1.2*bstar_12)
+ 4.0*w1*w2*astar_12*(11.0 - 2.4*bstar_12)/(wsum*wsum)
+ 1.6*wsum*om22_1*om22_2/(om11_12*om11_12*sqw12)
* sigratio2 * sigratio3;
cnst = 6.0*cstar_12 - 5.0;
fkj = 1.0 + 0.1*cnst*cnst *
(p1*xk*xk + p2*xj*xj + p12*xk*xj)/
(q1*xk*xk + q2*xj*xj + q12*xk*xj);
fjk = 1.0 + 0.1*cnst*cnst *
(p2*xk*xk + p1*xj*xj + p12*xk*xj)/
(q2*xk*xk + q1*xj*xj + q12*xk*xj);
}
//=============================================================================================================================
// Corrections for polar-nonpolar binary diffusion coefficients
/*
* Calculate corrections to the well depth parameter and the
* diameter for use in computing the binary diffusion coefficient
* of polar-nonpolar pairs. For more information about this
* correction, see Dixon-Lewis, Proc. Royal Society (1968).
*
* @param i Species one - this is a bimolecular correction routine
* @param j species two - this is a bimolecular correction routine
* @param tr Database of species properties read in from the input xml file.
* @param f_eps Multiplicative correction factor to be applied to epsilon(i,j)
* @param f_sigma Multiplicative correction factor to be applied to diam(i,j)
*/
void TransportFactory::makePolarCorrections(size_t i, size_t j,
const GasTransportParams& tr, doublereal& f_eps, doublereal& f_sigma)
{
// no correction if both are nonpolar, or both are polar
if (tr.polar[i] == tr.polar[j]) {
f_eps = 1.0;
f_sigma = 1.0;
return;
}
// corrections to the effective diameter and well depth
// if one is polar and one is non-polar
size_t kp = (tr.polar[i] ? i : j); // the polar one
size_t knp = (i == kp ? j : i); // the nonpolar one
doublereal d3np, d3p, alpha_star, mu_p_star, xi;
d3np = pow(tr.sigma[knp],3);
d3p = pow(tr.sigma[kp],3);
alpha_star = tr.alpha[knp]/d3np;
mu_p_star = tr.dipole(kp,kp)/sqrt(d3p * tr.eps[kp]);
xi = 1.0 + 0.25 * alpha_star * mu_p_star * mu_p_star *
sqrt(tr.eps[kp]/tr.eps[knp]);
f_sigma = pow(xi, -1.0/6.0);
f_eps = xi*xi;
}
//=============================================================================================================================
/*
TransportFactory(): default constructor
The default constructor for this class sets up
m_models[], a mapping between the string name
for a transport model and the integer name.
*/
TransportFactory::TransportFactory() :
m_verbose(false),
m_integrals(0)
{
m_models["Mix"] = cMixtureAveraged;
m_models["Multi"] = cMulticomponent;
m_models["Solid"] = cSolidTransport;
m_models["DustyGas"] = cDustyGasTransport;
m_models["CK_Multi"] = CK_Multicomponent;
m_models["CK_Mix"] = CK_MixtureAveraged;
m_models["Liquid"] = cLiquidTransport;
m_models["Aqueous"] = cAqueousTransport;
m_models["Simple"] = cSimpleTransport;
m_models["User"] = cUserTransport;
m_models["None"] = None;
//m_models["Radiative"] = cRadiative;
m_tranPropMap["viscostiy"] = TP_VISCOSITY;
m_tranPropMap["ionConductivity"] = TP_IONCONDUCTIVITY;
m_tranPropMap["mobilityRatio"] = TP_MOBILITYRATIO;
m_tranPropMap["selfDiffusion"] = TP_SELFDIFFUSION;
m_tranPropMap["thermalConductivity"] = TP_THERMALCOND;
m_tranPropMap["speciesDiffusivity"] = TP_DIFFUSIVITY;
m_tranPropMap["hydrodynamicRadius"] = TP_HYDRORADIUS;
m_tranPropMap["electricalConductivity"] = TP_ELECTCOND;
m_LTRmodelMap[""] = LTP_TD_CONSTANT;
m_LTRmodelMap["constant"] = LTP_TD_CONSTANT;
m_LTRmodelMap["arrhenius"] = LTP_TD_ARRHENIUS;
m_LTRmodelMap["coeffs"] = LTP_TD_POLY;
m_LTRmodelMap["exptemp"] = LTP_TD_EXPT;
m_LTImodelMap[""] = LTI_MODEL_NOTSET;
m_LTImodelMap["none"] = LTI_MODEL_NONE;
m_LTImodelMap["solvent"] = LTI_MODEL_SOLVENT;
m_LTImodelMap["moleFractions"] = LTI_MODEL_MOLEFRACS;
m_LTImodelMap["massFractions"] = LTI_MODEL_MASSFRACS;
m_LTImodelMap["logMoleFractions"] = LTI_MODEL_LOG_MOLEFRACS;
m_LTImodelMap["pairwiseInteraction"] = LTI_MODEL_PAIRWISE_INTERACTION;
m_LTImodelMap["stefanMaxwell_PPN"] = LTI_MODEL_STEFANMAXWELL_PPN;
m_LTImodelMap["moleFractionsExpT"] = LTI_MODEL_MOLEFRACS_EXPT;
}
/*
Destructor
We do not delete statically created single instance of this
class here, because it would create an infinite loop if
destructor is called for that single instance. However, we do
have a pointer to m_integrals that does need to be
explicitly deleted.
*/
TransportFactory::~TransportFactory()
{
if (m_integrals) {
delete m_integrals;
m_integrals = 0;
}
}
// This static function deletes the statically allocated instance.
void TransportFactory::deleteFactory()
{
#if defined(THREAD_SAFE_CANTERA)
boost::mutex::scoped_lock lock(transport_mutex) ;
#endif
if (s_factory) {
delete s_factory;
s_factory = 0;
}
}
/*
make one of several transport models, and return a base class
pointer to it. This method operates at the level of a
single transport property as a function of temperature
and possibly composition.
*/
LTPspecies* TransportFactory::newLTP(const XML_Node& trNode, std::string& name,
TransportPropertyType tp_ind, thermo_t* thermo)
{
LTPspecies* ltps = 0;
std::string model = lowercase(trNode["model"]);
switch (m_LTRmodelMap[model]) {
case LTP_TD_CONSTANT:
ltps = new LTPspecies_Const(trNode, name, tp_ind, thermo);
break;
case LTP_TD_ARRHENIUS:
ltps = new LTPspecies_Arrhenius(trNode, name, tp_ind, thermo);
break;
case LTP_TD_POLY:
ltps = new LTPspecies_Poly(trNode, name, tp_ind, thermo);
break;
case LTP_TD_EXPT:
ltps = new LTPspecies_ExpT(trNode, name, tp_ind, thermo);
break;
default:
throw CanteraError("newLTP","unknown transport model: " + model);
ltps = new LTPspecies(&trNode, name, tp_ind, thermo);
}
return ltps;
}
/*
make one of several transport models, and return a base class
pointer to it. This method operates at the level of a
single mixture transport property. Individual species
transport properties are addressed by the LTPspecies
returned by newLTP
*/
LiquidTranInteraction* TransportFactory::newLTI(const XML_Node& trNode,
TransportPropertyType tp_ind,
LiquidTransportParams& trParam)
{
LiquidTranInteraction* lti = 0;
thermo_t* thermo = trParam.thermo;
std::string model = trNode["model"];
switch (m_LTImodelMap[model]) {
case LTI_MODEL_SOLVENT:
lti = new LTI_Solvent(tp_ind);
lti->init(trNode, thermo);
break;
case LTI_MODEL_MOLEFRACS:
lti = new LTI_MoleFracs(tp_ind);
lti->init(trNode, thermo);
break;
case LTI_MODEL_MASSFRACS:
lti = new LTI_MassFracs(tp_ind);
lti->init(trNode, thermo);
break;
case LTI_MODEL_LOG_MOLEFRACS:
lti = new LTI_Log_MoleFracs(tp_ind);
lti->init(trNode, thermo);
break;
case LTI_MODEL_PAIRWISE_INTERACTION:
lti = new LTI_Pairwise_Interaction(tp_ind);
lti->init(trNode, thermo);
lti->setParameters(trParam);
break;
case LTI_MODEL_STEFANMAXWELL_PPN:
lti = new LTI_StefanMaxwell_PPN(tp_ind);
lti->init(trNode, thermo);
lti->setParameters(trParam);
break;
case LTI_MODEL_STOKES_EINSTEIN:
lti = new LTI_StokesEinstein(tp_ind);
lti->init(trNode, thermo);
lti->setParameters(trParam);
break;
case LTI_MODEL_MOLEFRACS_EXPT:
lti = new LTI_MoleFracs_ExpT(tp_ind);
lti->init(trNode, thermo);
break;
default:
// throw CanteraError("newLTI","unknown transport model: " + model );
lti = new LiquidTranInteraction(tp_ind);
lti->init(trNode, thermo);
}
return lti;
}
/*
make one of several transport models, and return a base class
pointer to it.
*/
Transport* TransportFactory::newTransport(std::string transportModel,
thermo_t* phase, int log_level)
{
if (transportModel == "") {
return new Transport;
}
vector_fp state;
Transport* tr = 0, *gastr = 0;
DustyGasTransport* dtr = 0;
phase->saveState(state);
switch (m_models[transportModel]) {
case None:
tr = new Transport;
break;
case cMulticomponent:
tr = new MultiTransport;
initTransport(tr, phase, 0, log_level);
break;
case CK_Multicomponent:
tr = new MultiTransport;
initTransport(tr, phase, CK_Mode, log_level);
break;
case cMixtureAveraged:
tr = new MixTransport;
initTransport(tr, phase, 0, log_level);
break;
case CK_MixtureAveraged:
tr = new MixTransport;
initTransport(tr, phase, CK_Mode, log_level);
break;
case cSolidTransport:
tr = new SolidTransport;
tr->setThermo(*phase);
break;
case cDustyGasTransport:
tr = new DustyGasTransport;
gastr = new MultiTransport;
initTransport(gastr, phase, 0, log_level);
dtr = (DustyGasTransport*)tr;
dtr->initialize(phase, gastr);
break;
case cSimpleTransport:
tr = new SimpleTransport();
initLiquidTransport(tr, phase, log_level);
tr->setThermo(*phase);
break;
#ifdef WITH_IDEAL_SOLUTIONS
case cLiquidTransport:
tr = new LiquidTransport;
initLiquidTransport(tr, phase, log_level);
tr->setThermo(*phase);
break;
#endif
#ifdef WITH_ELECTROLYTES
case cAqueousTransport:
tr = new AqueousTransport;
initLiquidTransport(tr, phase, log_level);
tr->setThermo(*phase);
break;
#endif
default:
throw CanteraError("newTransport","unknown transport model: " + transportModel);
}
phase->restoreState(state);
return tr;
}
/*
make one of several transport models, and return a base class
pointer to it.
*/
Transport* TransportFactory::newTransport(thermo_t* phase, int log_level)
{
XML_Node& phaseNode=phase->xml();
/*
* Find the Thermo XML node
*/
if (!phaseNode.hasChild("transport")) {
throw CanteraError("TransportFactory::newTransport",
"no transport XML node");
}
XML_Node& transportNode = phaseNode.child("transport");
std::string transportModel = transportNode.attrib("model");
if (transportModel == "") {
throw CanteraError("TransportFactory::newTransport",
"transport XML node doesn't have a model string");
}
return newTransport(transportModel, phase,log_level);
}
//====================================================================================================================
// Prepare to build a new kinetic-theory-based transport manager for low-density gases
/*
* This class fills up the GastransportParams structure for the current phase
*
* Uses polynomial fits to Monchick & Mason collision integrals. store then in tr
*
* @param flog Reference to the ostream for writing log info
* @param transport_database Reference to a vector of pointers containing the
* transport database for each species
* @param thermo Pointer to the %ThermoPhase object
* @param mode Mode -> Either it's CK_Mode, chemkin compatibility mode, or it is not
* We usually run with chemkin compatibility mode turned off.
* @param log_level log level
* @param tr GasTransportParams structure to be filled up with information
*/
void TransportFactory::setupMM(std::ostream& flog, const std::vector<const XML_Node*> &transport_database,
thermo_t* thermo, int mode, int log_level, GasTransportParams& tr)
{
// constant mixture attributes
tr.thermo = thermo;
tr.nsp_ = tr.thermo->nSpecies();
size_t nsp = tr.nsp_;
tr.tmin = thermo->minTemp();
tr.tmax = thermo->maxTemp();
tr.mw.resize(nsp);
tr.log_level = log_level;
copy(tr.thermo->molecularWeights().begin(), tr.thermo->molecularWeights().end(), tr.mw.begin());
tr.mode_ = mode;
tr.epsilon.resize(nsp, nsp, 0.0);
tr.delta.resize(nsp, nsp, 0.0);
tr.reducedMass.resize(nsp, nsp, 0.0);
tr.dipole.resize(nsp, nsp, 0.0);
tr.diam.resize(nsp, nsp, 0.0);
tr.crot.resize(nsp);
tr.zrot.resize(nsp);
tr.polar.resize(nsp, false);
tr.alpha.resize(nsp, 0.0);
tr.poly.resize(nsp);
tr.sigma.resize(nsp);
tr.eps.resize(nsp);
XML_Node root, log;
getTransportData(transport_database, log, tr.thermo->speciesNames(), tr);
for (size_t i = 0; i < nsp; i++) {
tr.poly[i].resize(nsp);
}
doublereal ts1, ts2, tstar_min = 1.e8, tstar_max = 0.0;
doublereal f_eps, f_sigma;
DenseMatrix& diam = tr.diam;
DenseMatrix& epsilon = tr.epsilon;
for (size_t i = 0; i < nsp; i++) {
for (size_t j = i; j < nsp; j++) {
// the reduced mass
tr.reducedMass(i,j) = tr.mw[i] * tr.mw[j] / (Avogadro * (tr.mw[i] + tr.mw[j]));
// hard-sphere diameter for (i,j) collisions
diam(i,j) = 0.5*(tr.sigma[i] + tr.sigma[j]);
// the effective well depth for (i,j) collisions
epsilon(i,j) = sqrt(tr.eps[i]*tr.eps[j]);
// The polynomial fits of collision integrals vs. T*
// will be done for the T* from tstar_min to tstar_max
ts1 = Boltzmann * tr.tmin/epsilon(i,j);
ts2 = Boltzmann * tr.tmax/epsilon(i,j);
if (ts1 < tstar_min) {
tstar_min = ts1;
}
if (ts2 > tstar_max) {
tstar_max = ts2;
}
// the effective dipole moment for (i,j) collisions
tr.dipole(i,j) = sqrt(tr.dipole(i,i)*tr.dipole(j,j));
// reduced dipole moment delta* (nondimensional)
doublereal d = diam(i,j);
tr.delta(i,j) = 0.5 * tr.dipole(i,j)*tr.dipole(i,j)
/ (epsilon(i,j) * d * d * d);
makePolarCorrections(i, j, tr, f_eps, f_sigma);
tr.diam(i,j) *= f_sigma;
epsilon(i,j) *= f_eps;
// properties are symmetric
tr.reducedMass(j,i) = tr.reducedMass(i,j);
diam(j,i) = diam(i,j);
epsilon(j,i) = epsilon(i,j);
tr.dipole(j,i) = tr.dipole(i,j);
tr.delta(j,i) = tr.delta(i,j);
}
}
// Chemkin fits the entire T* range in the Monchick and Mason tables,
// so modify tstar_min and tstar_max if in Chemkin compatibility mode
if (mode == CK_Mode) {
tstar_min = 0.101;
tstar_max = 99.9;
}
// initialize the collision integral calculator for the desired
// T* range
#ifdef DEBUG_MODE
if (m_verbose) {
tr.xml->XML_open(flog, "collision_integrals");
}
#endif
m_integrals = new MMCollisionInt;
m_integrals->init(tr.xml, tstar_min, tstar_max, log_level);
fitCollisionIntegrals(flog, tr);
#ifdef DEBUG_MODE
if (m_verbose) {
tr.xml->XML_close(flog, "collision_integrals");
}
#endif
// make polynomial fits
#ifdef DEBUG_MODE
if (m_verbose) {
tr.xml->XML_open(flog, "property fits");
}
#endif
fitProperties(tr, flog);
#ifdef DEBUG_MODE
if (m_verbose) {
tr.xml->XML_close(flog, "property fits");
}
#endif
}
//====================================================================================================================
// Prepare to build a new transport manager for liquids assuming that
// viscosity transport data is provided in Arrhenius form.
/*
* @param flog Reference to the ostream for writing log info
* @param thermo Pointer to the %ThermoPhase object
* @param log_level log level
* @param trParam LiquidTransportParams structure to be filled up with information
*/
void TransportFactory::setupLiquidTransport(std::ostream& flog, thermo_t* thermo, int log_level,
LiquidTransportParams& trParam)
{
const std::vector<const XML_Node*> & species_database = thermo->speciesData();
const XML_Node* phase_database = &thermo->xml();
// constant mixture attributes
trParam.thermo = thermo;
trParam.nsp_ = trParam.thermo->nSpecies();
size_t nsp = trParam.nsp_;
trParam.tmin = thermo->minTemp();
trParam.tmax = thermo->maxTemp();
trParam.log_level = log_level;
// Get the molecular weights and load them into trParam
trParam.mw.resize(nsp);
copy(trParam.thermo->molecularWeights().begin(),
trParam.thermo->molecularWeights().end(), trParam.mw.begin());
// Resize all other vectors in trParam
trParam.LTData.resize(nsp);
// Need to identify a method to obtain interaction matrices.
// This will fill LiquidTransportParams members visc_Eij, visc_Sij
// trParam.visc_Eij.resize(nsp,nsp);
// trParam.visc_Sij.resize(nsp,nsp);
trParam.thermalCond_Aij.resize(nsp,nsp);
trParam.diff_Dij.resize(nsp,nsp);
trParam.radius_Aij.resize(nsp,nsp);
XML_Node root, log;
// Note that getLiquidSpeciesTransportData just populates the pure species transport data.
getLiquidSpeciesTransportData(species_database, log, trParam.thermo->speciesNames(), trParam);
// getLiquidInteractionsTransportData() populates the
// species-species interaction models parameters
// like visc_Eij
if (phase_database->hasChild("transport")) {
XML_Node& transportNode = phase_database->child("transport");
getLiquidInteractionsTransportData(transportNode, log, trParam.thermo->speciesNames(), trParam);
}
}
//====================================================================================================================
// Initialize an existing transport manager
/*
* This routine sets up an existing gas-phase transport manager.
* It calculates the collision integrals and calls the initGas() function to
* populate the species-dependent data structure.
*
* @param tr Pointer to the Transport manager
* @param thermo Pointer to the ThermoPhase object
* @param mode Chemkin compatible mode or not. This alters the specification of the
* collision integrals. defaults to no.
* @param log_level Defaults to zero, no logging
*
* In DEBUG_MODE, this routine will create the file transport_log.xml
* and write informative information to it.
*/
void TransportFactory::initTransport(Transport* tran,
thermo_t* thermo, int mode, int log_level)
{
const std::vector<const XML_Node*> & transport_database = thermo->speciesData();
GasTransportParams trParam;
#ifdef DEBUG_MODE
if (log_level == 0) {
m_verbose = 0;
}
ofstream flog("transport_log.xml");
trParam.xml = new XML_Writer(flog);
if (m_verbose) {
trParam.xml->XML_open(flog, "transport");
}
#else
// create the object, but don't associate it with a file
std::ostream& flog(std::cout);
#endif
// set up Monchick and Mason collision integrals
setupMM(flog, transport_database, thermo, mode, log_level, trParam);
// do model-specific initialization
tran->initGas(trParam);
#ifdef DEBUG_MODE
if (m_verbose) {
trParam.xml->XML_close(flog, "transport");
}
// finished with log file
flog.close();
#endif
return;
}
//====================================================================================================================
/* Similar to initTransport except uses LiquidTransportParams
class and calls setupLiquidTransport().
*/
void TransportFactory::initLiquidTransport(Transport* tran,
thermo_t* thermo,
int log_level)
{
LiquidTransportParams trParam;
#ifdef DEBUG_MODE
ofstream flog("transport_log.xml");
trParam.xml = new XML_Writer(flog);
if (m_verbose) {
trParam.xml->XML_open(flog, "transport");
}
#else
// create the object, but don't associate it with a file
std::ostream& flog(std::cout);
#endif
setupLiquidTransport(flog, thermo, log_level, trParam);
// do model-specific initialization
tran->initLiquid(trParam);
#ifdef DEBUG_MODE
if (m_verbose) {
trParam.xml->XML_close(flog, "transport");
}
// finished with log file
flog.close();
#endif
return;
}
//====================================================================================================================
// Generate polynomial fits to collision integrals
/*
* @param logfile Reference to an ostream that will contain log information when in
* DEBUG_MODE
* @param tr Reference to the GasTransportParams object that will contain the results.
*/
void TransportFactory::fitCollisionIntegrals(ostream& logfile, GasTransportParams& tr)
{
vector_fp::iterator dptr;
doublereal dstar;
size_t nsp = tr.nsp_;
int mode = tr.mode_;
size_t i, j;
// Chemkin fits to sixth order polynomials
int degree = (mode == CK_Mode ? 6 : COLL_INT_POLY_DEGREE);
#ifdef DEBUG_MODE
if (m_verbose) {
tr.xml->XML_open(logfile, "tstar_fits");
tr.xml->XML_comment(logfile, "fits to A*, B*, and C* vs. log(T*).\n"
"These are done only for the required dstar(j,k) values.");
if (tr.log_level < 3) {
tr.xml->XML_comment(logfile, "*** polynomial coefficients not printed (log_level < 3) ***");
}
}
#endif
for (i = 0; i < nsp; i++) {
for (j = i; j < nsp; j++) {
// Chemkin fits only delta* = 0
if (mode != CK_Mode) {
dstar = tr.delta(i,j);
} else {
dstar = 0.0;
}
// if a fit has already been generated for
// delta* = tr.delta(i,j), then use it. Otherwise,
// make a new fit, and add tr.delta(i,j) to the list
// of delta* values for which fits have been done.
// 'find' returns a pointer to end() if not found
dptr = find(tr.fitlist.begin(), tr.fitlist.end(), dstar);
if (dptr == tr.fitlist.end()) {
vector_fp ca(degree+1), cb(degree+1), cc(degree+1);
vector_fp co22(degree+1);
m_integrals->fit(logfile, degree, dstar,
DATA_PTR(ca), DATA_PTR(cb), DATA_PTR(cc));
m_integrals->fit_omega22(logfile, degree, dstar,
DATA_PTR(co22));
tr.omega22_poly.push_back(co22);
tr.astar_poly.push_back(ca);
tr.bstar_poly.push_back(cb);
tr.cstar_poly.push_back(cc);
tr.poly[i][j] = static_cast<int>(tr.astar_poly.size()) - 1;
tr.fitlist.push_back(dstar);
}
// delta* found in fitlist, so just point to this
// polynomial
else {
tr.poly[i][j] = static_cast<int>((dptr - tr.fitlist.begin()));
}
tr.poly[j][i] = tr.poly[i][j];
}
}
#ifdef DEBUG_MODE
if (m_verbose) {
tr.xml->XML_close(logfile, "tstar_fits");
}
#endif
}
//====================================================================================================================
/*********************************************************
*
* Read Transport Database
*
*********************************************************/
/*
Read transport property data from a file for a list of species.
Given the name of a file containing transport property
parameters and a list of species names, this method returns an
instance of TransportParams containing the transport data for
these species read from the file.
*/
void TransportFactory::getTransportData(const std::vector<const XML_Node*> &xspecies,
XML_Node& log, const std::vector<std::string> &names, GasTransportParams& tr)
{
std::string name;
int geom;
std::map<std::string, GasTransportData> datatable;
doublereal welldepth, diam, dipole, polar, rot;
size_t nsp = xspecies.size();
// read all entries in database into 'datatable' and check for
// errors. Note that this procedure validates all entries, not
// only those for the species listed in 'names'.
std::string val, type;
map<std::string, int> gindx;
gindx["atom"] = 100;
gindx["linear"] = 101;
gindx["nonlinear"] = 102;
int linenum = 0;
for (size_t i = 0; i < nsp; i++) {
const XML_Node& sp = *xspecies[i];
name = sp["name"];
// std::cout << "Processing node for " << name << std::endl;
// put in a try block so that species with no 'transport'
// child are skipped, instead of throwing an exception.
try {
XML_Node& tr = sp.child("transport");
ctml::getString(tr, "geometry", val, type);
geom = gindx[val] - 100;
map<std::string, doublereal> fv;
welldepth = ctml::getFloat(tr, "LJ_welldepth");
diam = ctml::getFloat(tr, "LJ_diameter");
dipole = ctml::getFloat(tr, "dipoleMoment");
polar = ctml::getFloat(tr, "polarizability");
rot = ctml::getFloat(tr, "rotRelax");
GasTransportData data;
data.speciesName = name;
data.geometry = geom;
if (welldepth >= 0.0) {
data.wellDepth = welldepth;
} else throw TransportDBError(linenum,
"negative well depth");
if (diam > 0.0) {
data.diameter = diam;
} else throw TransportDBError(linenum,
"negative or zero diameter");
if (dipole >= 0.0) {
data.dipoleMoment = dipole;
} else throw TransportDBError(linenum,
"negative dipole moment");
if (polar >= 0.0) {
data.polarizability = polar;
} else throw TransportDBError(linenum,
"negative polarizability");
if (rot >= 0.0) {
data.rotRelaxNumber = rot;
} else throw TransportDBError(linenum,
"negative rotation relaxation number");
datatable[name] = data;
} catch (CanteraError& err) {
err.save();
}
}
for (size_t i = 0; i < tr.nsp_; i++) {
GasTransportData& trdat = datatable[names[i]];
// 'datatable' returns a default TransportData object if
// the species name is not one in the transport database.
// This can be detected by examining 'geometry'.
if (trdat.geometry < 0) {
throw TransportDBError(0,"no transport data found for species "
+ names[i]);
}
// parameters are converted to SI units before storing
// rotational heat capacity / R
switch (trdat.geometry) {
case 0:
tr.crot[i] = 0.0; // monatomic
break;
case 1:
tr.crot[i] = 1.0; // linear
break;
default:
tr.crot[i] = 1.5; // nonlinear
}
tr.dipole(i,i) = 1.e-25 * SqrtTen * trdat.dipoleMoment;
if (trdat.dipoleMoment > 0.0) {
tr.polar[i] = true;
} else {
tr.polar[i] = false;
}
// A^3 -> m^3
tr.alpha[i] = 1.e-30 * trdat.polarizability;
tr.sigma[i] = 1.e-10 * trdat.diameter;
tr.eps[i] = Boltzmann * trdat.wellDepth;
tr.zrot[i] = std::max(1.0, trdat.rotRelaxNumber);
}
}
/*
Read transport property data from a file for a list of species.
Given the name of a file containing transport property
parameters and a list of species names, this method returns an
instance of TransportParams containing the transport data for
these species read from the file.
*/
void TransportFactory::getLiquidSpeciesTransportData(const std::vector<const XML_Node*> &xspecies,
XML_Node& log,
const std::vector<std::string> &names,
LiquidTransportParams& trParam)
{
std::string name;
/*
Create a map of species names versus liquid transport data parameters
*/
std::map<std::string, LiquidTransportData> datatable;
std::map<std::string, LiquidTransportData>::iterator it;
// Store the number of species in the phase
size_t nsp = trParam.nsp_;
// Store the number of off-diagonal symmetric interactions between species in the phase
size_t nBinInt = nsp*(nsp-1)/2;
// read all entries in database into 'datatable' and check for
// errors. Note that this procedure validates all entries, not
// only those for the species listed in 'names'.
for (size_t i = 0; i < nsp; i++) {
const XML_Node& sp = *xspecies[i];
name = sp["name"];
vector_fp vCoeff;
// Species with no 'transport' child are skipped. However, if that species is in the list,
// it will throw an exception below.
try {
if (sp.hasChild("transport")) {
XML_Node& trNode = sp.child("transport");
// Fill datatable with LiquidTransportData objects for error checking
// and then insertion into LiquidTransportData objects below.
LiquidTransportData data;
data.speciesName = name;
data.mobilityRatio.resize(nsp*nsp,0);
data.selfDiffusion.resize(nsp,0);
ThermoPhase* temp_thermo = trParam.thermo;
size_t num = trNode.nChildren();
for (size_t iChild = 0; iChild < num; iChild++) {
XML_Node& xmlChild = trNode.child(iChild);
std::string nodeName = xmlChild.name();
switch (m_tranPropMap[nodeName]) {
case TP_VISCOSITY:
data.viscosity = newLTP(xmlChild, name, m_tranPropMap[nodeName], temp_thermo);
break;
case TP_IONCONDUCTIVITY:
data.ionConductivity = newLTP(xmlChild, name, m_tranPropMap[nodeName], temp_thermo);
break;
case TP_MOBILITYRATIO: {
for (size_t iSpec = 0; iSpec< nBinInt; iSpec++) {
XML_Node& propSpecNode = xmlChild.child(iSpec);
std::string specName = propSpecNode.name();
size_t loc = specName.find(":");
std::string firstSpec = specName.substr(0,loc);
std::string secondSpec = specName.substr(loc+1);
size_t index = temp_thermo->speciesIndex(firstSpec.c_str())+nsp*temp_thermo->speciesIndex(secondSpec.c_str());
data.mobilityRatio[index] = newLTP(propSpecNode, name, m_tranPropMap[nodeName], temp_thermo);
};
};
break;
case TP_SELFDIFFUSION: {
for (size_t iSpec = 0; iSpec< nsp; iSpec++) {
XML_Node& propSpecNode = xmlChild.child(iSpec);
std::string specName = propSpecNode.name();
size_t index = temp_thermo->speciesIndex(specName.c_str());
data.selfDiffusion[index] = newLTP(propSpecNode, name, m_tranPropMap[nodeName], temp_thermo);
};
};
break;
case TP_THERMALCOND:
data.thermalCond = newLTP(xmlChild,
name,
m_tranPropMap[nodeName],
temp_thermo);
break;
case TP_DIFFUSIVITY:
data.speciesDiffusivity = newLTP(xmlChild,
name,
m_tranPropMap[nodeName],
temp_thermo);
break;
case TP_HYDRORADIUS:
data.hydroRadius = newLTP(xmlChild,
name,
m_tranPropMap[nodeName],
temp_thermo);
break;
case TP_ELECTCOND:
data.electCond = newLTP(xmlChild,
name,
m_tranPropMap[nodeName],
temp_thermo);
break;
default:
throw CanteraError("getLiquidSpeciesTransportData","unknown transport property: " + nodeName);
}
}
datatable.insert(pair<std::string, LiquidTransportData>(name,data));
}
} catch (CanteraError& err) {
err.save();
throw err;
}
}
trParam.LTData.clear();
for (size_t i = 0; i < trParam.nsp_; i++) {
/*
Check to see that we have a LiquidTransportData object for all of the
species in the phase. If not, throw an error.
*/
it = datatable.find(names[i]);
if (it == datatable.end()) {
throw TransportDBError(0,"No transport data found for species " + names[i]);
}
LiquidTransportData& trdat = it->second;
/*
Now, transfer these objects into LTData in the correct phase index order by
calling the default copy constructor for LiquidTransportData.
*/
trParam.LTData.push_back(trdat);
}
}
/*
Read transport property data from a file for interactions
between species in a liquid.
Given the name of a file containing transport property
parameters and a list of species names, this method returns an
instance of TransportParams containing the transport data for
these species read from the file.
*/
void TransportFactory::getLiquidInteractionsTransportData(const XML_Node& transportNode,
XML_Node& log,
const std::vector<std::string> &names,
LiquidTransportParams& trParam)
{
try {
size_t nsp = trParam.nsp_;
size_t nBinInt = nsp*(nsp-1)/2;
size_t num = transportNode.nChildren();
for (size_t iChild = 0; iChild < num; iChild++) {
//tranTypeNode is a type of transport property like viscosity
XML_Node& tranTypeNode = transportNode.child(iChild);
std::string nodeName = tranTypeNode.name();
trParam.mobilityRatio.resize(nsp*nsp,0);
trParam.selfDiffusion.resize(nsp,0);
ThermoPhase* temp_thermo = trParam.thermo;
if (tranTypeNode.hasChild("compositionDependence")) {
//compDepNode contains the interaction model
XML_Node& compDepNode = tranTypeNode.child("compositionDependence");
switch (m_tranPropMap[nodeName]) {
break;
case TP_VISCOSITY:
trParam.viscosity = newLTI(compDepNode, m_tranPropMap[nodeName], trParam);
break;
case TP_IONCONDUCTIVITY:
trParam.ionConductivity = newLTI(compDepNode,
m_tranPropMap[nodeName],
trParam);
break;
case TP_MOBILITYRATIO: {
for (size_t iSpec = 0; iSpec< nBinInt; iSpec++) {
XML_Node& propSpecNode = compDepNode.child(iSpec);
string specName = propSpecNode.name();
size_t loc = specName.find(":");
string firstSpec = specName.substr(0,loc);
string secondSpec = specName.substr(loc+1);
size_t index = temp_thermo->speciesIndex(firstSpec.c_str())+nsp*temp_thermo->speciesIndex(secondSpec.c_str());
trParam.mobilityRatio[index] = newLTI(propSpecNode,
m_tranPropMap[nodeName],
trParam);
};
};
break;
case TP_SELFDIFFUSION: {
for (size_t iSpec = 0; iSpec< nsp; iSpec++) {
XML_Node& propSpecNode = compDepNode.child(iSpec);
string specName = propSpecNode.name();
size_t index = temp_thermo->speciesIndex(specName.c_str());
trParam.selfDiffusion[index] = newLTI(propSpecNode,
m_tranPropMap[nodeName],
trParam);
};
};
break;
case TP_THERMALCOND:
trParam.thermalCond = newLTI(compDepNode,
m_tranPropMap[nodeName],
trParam);
break;
case TP_DIFFUSIVITY:
trParam.speciesDiffusivity = newLTI(compDepNode,
m_tranPropMap[nodeName],
trParam);
break;
case TP_HYDRORADIUS:
trParam.hydroRadius = newLTI(compDepNode,
m_tranPropMap[nodeName],
trParam);
break;
case TP_ELECTCOND:
trParam.electCond = newLTI(compDepNode,
m_tranPropMap[nodeName],
trParam);
break;
default:
throw CanteraError("getLiquidInteractionsTransportData","unknown transport property: " + nodeName);
}
}
/* Allow a switch between mass-averaged, mole-averaged
* and solvent specified reference velocities.
* XML code within the transportProperty node
* (i.e. within <viscosity>) should read as follows
* <velocityBasis basis="mass"> <!-- mass averaged -->
* <velocityBasis basis="mole"> <!-- mole averaged -->
* <velocityBasis basis="H2O"> <!-- H2O solvent -->
*/
if (tranTypeNode.hasChild("velocityBasis")) {
std::string velocityBasis =
tranTypeNode.child("velocityBasis").attrib("basis");
if (velocityBasis == "mass") {
trParam.velocityBasis_ = VB_MASSAVG;
} else if (velocityBasis == "mole") {
trParam.velocityBasis_ = VB_MOLEAVG;
} else if (trParam.thermo->speciesIndex(velocityBasis) > 0) {
trParam.velocityBasis_ = static_cast<int>(trParam.thermo->speciesIndex(velocityBasis));
} else {
int linenum = __LINE__;
throw TransportDBError(linenum, "Unknown attribute \"" + velocityBasis + "\" for <velocityBasis> node. ");
}
}
}
} catch (CanteraError& err) {
std::cout << err.what() << std::endl;
}
return;
}
/*********************************************************
*
* Polynomial fitting
*
*********************************************************/
//====================================================================================================================
// Generate polynomial fits to the viscosity, conductivity, and
// the binary diffusion coefficients
/*
* If CK_mode, then the fits are of the form
* \f[
* \log(\eta(i)) = \sum_{n = 0}^3 a_n(i) (\log T)^n
* \f]
* and
* \f[
* \log(D(i,j)) = \sum_{n = 0}^3 a_n(i,j) (\log T)^n
* \f]
* Otherwise the fits are of the form
* \f[
* \eta(i)/sqrt(k_BT) = \sum_{n = 0}^4 a_n(i) (\log T)^n
* \f]
* and
* \f[
* D(i,j)/sqrt(k_BT)) = \sum_{n = 0}^4 a_n(i,j) (\log T)^n
* \f]
*
* @param tr Reference to the GasTransportParams object that will contain the results.
* @param logfile Reference to an ostream that will contain log information when in
* DEBUG_MODE
*/
void TransportFactory::fitProperties(GasTransportParams& tr, std::ostream& logfile)
{
doublereal tstar;
int ndeg = 0;
#ifdef DEBUG_MODE
char s[100];
#endif
// number of points to use in generating fit data
const size_t np = 50;
int mode = tr.mode_;
int degree = (mode == CK_Mode ? 3 : 4);
doublereal t, om22;
doublereal dt = (tr.tmax - tr.tmin)/(np-1);
vector_fp tlog(np), spvisc(np), spcond(np);
doublereal val, fit;
vector_fp w(np), w2(np);
// generate array of log(t) values
for (size_t n = 0; n < np; n++) {
t = tr.tmin + dt*n;
tlog[n] = log(t);
}
// vector of polynomial coefficients
vector_fp c(degree + 1), c2(degree + 1);
// fit the pure-species viscosity and thermal conductivity for
// each species
#ifdef DEBUG_MODE
if (tr.log_level < 2 && m_verbose) {
tr.xml->XML_comment(logfile,
"*** polynomial coefficients not printed (log_level < 2) ***");
}
#endif
doublereal sqrt_T, visc, err, relerr,
mxerr = 0.0, mxrelerr = 0.0, mxerr_cond = 0.0, mxrelerr_cond = 0.0;
#ifdef DEBUG_MODE
if (m_verbose) {
tr.xml->XML_open(logfile, "viscosity");
tr.xml->XML_comment(logfile,"Polynomial fits for viscosity");
if (mode == CK_Mode) {
tr.xml->XML_comment(logfile,"log(viscosity) fit to cubic "
"polynomial in log(T)");
} else {
sprintf(s, "viscosity/sqrt(T) fit to "
"polynomial of degree %d in log(T)",degree);
tr.xml->XML_comment(logfile,s);
}
}
#endif
doublereal cp_R, cond, w_RT, f_int, A_factor, B_factor,
c1, cv_rot, cv_int, f_rot, f_trans, om11;
doublereal diffcoeff;
for (size_t k = 0; k < tr.nsp_; k++) {
for (size_t n = 0; n < np; n++) {
t = tr.tmin + dt*n;
tr.thermo->setTemperature(t);
cp_R = ((IdealGasPhase*)tr.thermo)->cp_R_ref()[k];
tstar = Boltzmann * t/ tr.eps[k];
sqrt_T = sqrt(t);
om22 = m_integrals->omega22(tstar, tr.delta(k,k));
om11 = m_integrals->omega11(tstar, tr.delta(k,k));
// self-diffusion coefficient, without polar
// corrections
diffcoeff = ThreeSixteenths *
sqrt(2.0 * Pi/tr.reducedMass(k,k)) *
pow((Boltzmann * t), 1.5)/
(Pi * tr.sigma[k] * tr.sigma[k] * om11);
// viscosity
visc = FiveSixteenths
* sqrt(Pi * tr.mw[k] * Boltzmann * t / Avogadro) /
(om22 * Pi * tr.sigma[k]*tr.sigma[k]);
// thermal conductivity
w_RT = tr.mw[k]/(GasConstant * t);
f_int = w_RT * diffcoeff/visc;
cv_rot = tr.crot[k];
A_factor = 2.5 - f_int;
B_factor = tr.zrot[k] + TwoOverPi
*(FiveThirds * cv_rot + f_int);
c1 = TwoOverPi * A_factor/B_factor;
cv_int = cp_R - 2.5 - cv_rot;
f_rot = f_int * (1.0 + c1);
f_trans = 2.5 * (1.0 - c1 * cv_rot/1.5);
cond = (visc/tr.mw[k])*GasConstant*(f_trans * 1.5
+ f_rot * cv_rot + f_int * cv_int);
if (mode == CK_Mode) {
spvisc[n] = log(visc);
spcond[n] = log(cond);
w[n] = -1.0;
w2[n] = -1.0;
} else {
// the viscosity should be proportional
// approximately to sqrt(T); therefore,
// visc/sqrt(T) should have only a weak
// temperature dependence. And since the mixture
// rule requires the square root of the
// pure-species viscosity, fit the square root of
// (visc/sqrt(T)) to avoid having to compute
// square roots in the mixture rule.
spvisc[n] = sqrt(visc/sqrt_T);
// the pure-species conductivity scales
// approximately with sqrt(T). Unlike the
// viscosity, there is no reason here to fit the
// square root, since a different mixture rule is
// used.
spcond[n] = cond/sqrt_T;
w[n] = 1.0/(spvisc[n]*spvisc[n]);
w2[n] = 1.0/(spcond[n]*spcond[n]);
}
}
polyfit(np, DATA_PTR(tlog), DATA_PTR(spvisc),
DATA_PTR(w), degree, ndeg, 0.0, DATA_PTR(c));
polyfit(np, DATA_PTR(tlog), DATA_PTR(spcond),
DATA_PTR(w), degree, ndeg, 0.0, DATA_PTR(c2));
// evaluate max fit errors for viscosity
for (size_t n = 0; n < np; n++) {
if (mode == CK_Mode) {
val = exp(spvisc[n]);
fit = exp(poly3(tlog[n], DATA_PTR(c)));
} else {
sqrt_T = exp(0.5*tlog[n]);
val = sqrt_T * pow(spvisc[n],2);
fit = sqrt_T * pow(poly4(tlog[n], DATA_PTR(c)),2);
}
err = fit - val;
relerr = err/val;
if (fabs(err) > mxerr) {
mxerr = fabs(err);
}
if (fabs(relerr) > mxrelerr) {
mxrelerr = fabs(relerr);
}
}
// evaluate max fit errors for conductivity
for (size_t n = 0; n < np; n++) {
if (mode == CK_Mode) {
val = exp(spcond[n]);
fit = exp(poly3(tlog[n], DATA_PTR(c2)));
} else {
sqrt_T = exp(0.5*tlog[n]);
val = sqrt_T * spcond[n];
fit = sqrt_T * poly4(tlog[n], DATA_PTR(c2));
}
err = fit - val;
relerr = err/val;
if (fabs(err) > mxerr_cond) {
mxerr_cond = fabs(err);
}
if (fabs(relerr) > mxrelerr_cond) {
mxrelerr_cond = fabs(relerr);
}
}
tr.visccoeffs.push_back(c);
tr.condcoeffs.push_back(c2);
#ifdef DEBUG_MODE
if (tr.log_level >= 2 && m_verbose) {
tr.xml->XML_writeVector(logfile, " ", tr.thermo->speciesName(k),
c.size(), DATA_PTR(c));
}
#endif
}
#ifdef DEBUG_MODE
if (m_verbose) {
sprintf(s, "Maximum viscosity absolute error: %12.6g", mxerr);
tr.xml->XML_comment(logfile,s);
sprintf(s, "Maximum viscosity relative error: %12.6g", mxrelerr);
tr.xml->XML_comment(logfile,s);
tr.xml->XML_close(logfile, "viscosity");
tr.xml->XML_open(logfile, "conductivity");
tr.xml->XML_comment(logfile,"Polynomial fits for conductivity");
if (mode == CK_Mode)
tr.xml->XML_comment(logfile,"log(conductivity) fit to cubic "
"polynomial in log(T)");
else {
sprintf(s, "conductivity/sqrt(T) fit to "
"polynomial of degree %d in log(T)",degree);
tr.xml->XML_comment(logfile,s);
}
if (tr.log_level >= 2)
for (size_t k = 0; k < tr.nsp_; k++) {
tr.xml->XML_writeVector(logfile, " ", tr.thermo->speciesName(k),
degree+1, DATA_PTR(tr.condcoeffs[k]));
}
sprintf(s, "Maximum conductivity absolute error: %12.6g", mxerr_cond);
tr.xml->XML_comment(logfile,s);
sprintf(s, "Maximum conductivity relative error: %12.6g", mxrelerr_cond);
tr.xml->XML_comment(logfile,s);
tr.xml->XML_close(logfile, "conductivity");
// fit the binary diffusion coefficients for each species pair
tr.xml->XML_open(logfile, "binary_diffusion_coefficients");
tr.xml->XML_comment(logfile, "binary diffusion coefficients");
if (mode == CK_Mode)
tr.xml->XML_comment(logfile,"log(D) fit to cubic "
"polynomial in log(T)");
else {
sprintf(s, "D/T**(3/2) fit to "
"polynomial of degree %d in log(T)",degree);
tr.xml->XML_comment(logfile,s);
}
}
#endif
mxerr = 0.0, mxrelerr = 0.0;
vector_fp diff(np + 1);
doublereal eps, sigma;
for (size_t k = 0; k < tr.nsp_; k++) {
for (size_t j = k; j < tr.nsp_; j++) {
for (size_t n = 0; n < np; n++) {
t = tr.tmin + dt*n;
eps = tr.epsilon(j,k);
tstar = Boltzmann * t/eps;
sigma = tr.diam(j,k);
om11 = m_integrals->omega11(tstar, tr.delta(j,k));
diffcoeff = ThreeSixteenths *
sqrt(2.0 * Pi/tr.reducedMass(k,j)) *
pow((Boltzmann * t), 1.5)/
(Pi * sigma * sigma * om11);
// 2nd order correction
// NOTE: THIS CORRECTION IS NOT APPLIED
doublereal fkj, fjk;
getBinDiffCorrection(t, tr, k, j, 1.0, 1.0, fkj, fjk);
//diffcoeff *= fkj;
if (mode == CK_Mode) {
diff[n] = log(diffcoeff);
w[n] = -1.0;
} else {
diff[n] = diffcoeff/pow(t, 1.5);
w[n] = 1.0/(diff[n]*diff[n]);
}
}
polyfit(np, DATA_PTR(tlog), DATA_PTR(diff),
DATA_PTR(w), degree, ndeg, 0.0, DATA_PTR(c));
doublereal pre;
for (size_t n = 0; n < np; n++) {
if (mode == CK_Mode) {
val = exp(diff[n]);
fit = exp(poly3(tlog[n], DATA_PTR(c)));
} else {
t = exp(tlog[n]);
pre = pow(t, 1.5);
val = pre * diff[n];
fit = pre * poly4(tlog[n], DATA_PTR(c));
}
err = fit - val;
relerr = err/val;
if (fabs(err) > mxerr) {
mxerr = fabs(err);
}
if (fabs(relerr) > mxrelerr) {
mxrelerr = fabs(relerr);
}
}
tr.diffcoeffs.push_back(c);
#ifdef DEBUG_MODE
if (tr.log_level >= 2 && m_verbose) {
tr.xml->XML_writeVector(logfile, " ", tr.thermo->speciesName(k)
+ "__"+tr.thermo->speciesName(j), c.size(), DATA_PTR(c));
}
#endif
}
}
#ifdef DEBUG_MODE
if (m_verbose) {
sprintf(s,"Maximum binary diffusion coefficient absolute error:"
" %12.6g", mxerr);
tr.xml->XML_comment(logfile,s);
sprintf(s, "Maximum binary diffusion coefficient relative error:"
"%12.6g", mxrelerr);
tr.xml->XML_comment(logfile,s);
tr.xml->XML_close(logfile, "binary_diffusion_coefficients");
}
#endif
}
//====================================================================================================================
// Create a new transport manager instance.
/*
* @param transportModel String identifying the transport model to be instantiated, defaults to the empty string
* @param thermo ThermoPhase object associated with the phase, defaults to null pointer
* @param loglevel int containing the Loglevel, defaults to zero
* @param f ptr to the TransportFactory object if it's been malloced.
*
* @ingroup transportProps
*/
Transport* newTransportMgr(std::string transportModel, thermo_t* thermo, int loglevel, TransportFactory* f)
{
if (f == 0) {
f = TransportFactory::factory();
}
Transport* ptr = f->newTransport(transportModel, thermo, loglevel);
/*
* Note: We delete the static s_factory instance here, instead of in
* appdelete() in misc.cpp, to avoid linking problems involving
* the need for multiple cantera and transport library statements
* for applications that don't have transport in them.
*/
return ptr;
}
//====================================================================================================================
// Create a new transport manager instance.
/*
* @param thermo ThermoPhase object associated with the phase, defaults to null pointer
* @param loglevel int containing the Loglevel, defaults to zero
* @param f ptr to the TransportFactory object if it's been malloced.
*
* @ingroup transportProps
*/
Transport* newDefaultTransportMgr(thermo_t* thermo, int loglevel, TransportFactory* f)
{
if (f == 0) {
f = TransportFactory::factory();
}
Transport* ptr = f->newTransport(thermo, loglevel);
/*
* Note: We delete the static s_factory instance here, instead of in
* appdelete() in misc.cpp, to avoid linking problems involving
* the need for multiple cantera and transport library statements
* for applications that don't have transport in them.
*/
return ptr;
}
//====================================================================================================================
}