/** * @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 "cantera/base/stringUtils.h" #include #include #include 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["viscosity"] = 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 &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 & 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 & 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(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((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 &xspecies, XML_Node& log, const std::vector &names, GasTransportParams& tr) { std::string name; int geom; std::map 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 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 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 &xspecies, XML_Node& log, const std::vector &names, LiquidTransportParams& trParam) { std::string name; /* Create a map of species names versus liquid transport data parameters */ std::map datatable; std::map::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(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 &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 ) should read as follows * * * */ 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(trParam.thermo->speciesIndex(velocityBasis)); } else { int linenum = __LINE__; throw TransportDBError(linenum, "Unknown attribute \"" + velocityBasis + "\" for 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; } //==================================================================================================================== }