/** * @file VPStandardStateTP.cpp * Definition file for a derived class of ThermoPhase that handles * variable pressure standard state methods for calculating * thermodynamic properties (see \ref thermoprops and * class \link Cantera::VPStandardStateTP VPStandardStateTP\endlink). */ // This file is part of Cantera. See License.txt in the top-level directory or // at http://www.cantera.org/license.txt for license and copyright information. #include "cantera/thermo/VPStandardStateTP.h" #include "cantera/thermo/PDSS.h" #include "cantera/thermo/PDSS_IdealGas.h" #include "cantera/thermo/PDSS_Water.h" #include "cantera/thermo/PDSS_ConstVol.h" #include "cantera/thermo/PDSS_SSVol.h" #include "cantera/thermo/PDSS_HKFT.h" #include "cantera/thermo/PDSS_IonsFromNeutral.h" #include "cantera/thermo/SpeciesThermoFactory.h" #include "cantera/base/utilities.h" #include "cantera/base/ctml.h" using namespace std; namespace Cantera { VPStandardStateTP::VPStandardStateTP() : m_Pcurrent(OneAtm), m_Tlast_ss(-1.0), m_Plast_ss(-1.0), m_useTmpRefStateStorage(true) { } int VPStandardStateTP::standardStateConvention() const { return cSS_CONVENTION_VPSS; } void VPStandardStateTP::getChemPotentials_RT(doublereal* muRT) const { getChemPotentials(muRT); for (size_t k = 0; k < m_kk; k++) { muRT[k] *= 1.0 / RT(); } } // ----- Thermodynamic Values for the Species Standard States States ---- void VPStandardStateTP::getStandardChemPotentials(doublereal* g) const { getGibbs_RT(g); for (size_t k = 0; k < m_kk; k++) { g[k] *= RT(); } } void VPStandardStateTP::getEnthalpy_RT(doublereal* hrt) const { updateStandardStateThermo(); std::copy(m_hss_RT.begin(), m_hss_RT.end(), hrt); } void VPStandardStateTP::getEntropy_R(doublereal* sr) const { updateStandardStateThermo(); std::copy(m_sss_R.begin(), m_sss_R.end(), sr); } void VPStandardStateTP::getGibbs_RT(doublereal* grt) const { updateStandardStateThermo(); std::copy(m_gss_RT.begin(), m_gss_RT.end(), grt); } void VPStandardStateTP::getPureGibbs(doublereal* g) const { updateStandardStateThermo(); std::copy(m_gss_RT.begin(), m_gss_RT.end(), g); scale(g, g+m_kk, g, RT()); } void VPStandardStateTP::getIntEnergy_RT(doublereal* urt) const { updateStandardStateThermo(); std::copy(m_hss_RT.begin(), m_hss_RT.end(), urt); for (size_t k = 0; k < m_kk; k++) { urt[k] -= m_Plast_ss / RT() * m_Vss[k]; } } void VPStandardStateTP::getCp_R(doublereal* cpr) const { updateStandardStateThermo(); std::copy(m_cpss_R.begin(), m_cpss_R.end(), cpr); } void VPStandardStateTP::getStandardVolumes(doublereal* vol) const { updateStandardStateThermo(); std::copy(m_Vss.begin(), m_Vss.end(), vol); } const vector_fp& VPStandardStateTP::getStandardVolumes() const { updateStandardStateThermo(); return m_Vss; } // ----- Thermodynamic Values for the Species Reference States ---- void VPStandardStateTP::getEnthalpy_RT_ref(doublereal* hrt) const { updateStandardStateThermo(); if (m_useTmpRefStateStorage) { std::copy(m_h0_RT.begin(), m_h0_RT.end(), hrt); } else { throw NotImplementedError("VPStandardStateTP::getEnthalpy_RT_ref"); } } void VPStandardStateTP::getGibbs_RT_ref(doublereal* grt) const { updateStandardStateThermo(); if (m_useTmpRefStateStorage) { std::copy(m_g0_RT.begin(), m_g0_RT.end(), grt); } else { throw NotImplementedError("VPStandardStateTP::getGibbs_RT_ref"); } } void VPStandardStateTP::getGibbs_ref(doublereal* g) const { updateStandardStateThermo(); if (m_useTmpRefStateStorage) { std::copy(m_g0_RT.begin(), m_g0_RT.end(), g); scale(g, g+m_kk, g, RT()); } else { for (size_t k = 0; k < m_kk; k++) { PDSS* kPDSS = m_PDSS_storage[k].get(); kPDSS->setState_TP(m_tlast, m_Plast_ss); double h0_RT = kPDSS->enthalpy_RT_ref(); double s0_R = kPDSS->entropy_R_ref(); g[k] = RT() * (h0_RT - s0_R); } } } const vector_fp& VPStandardStateTP::Gibbs_RT_ref() const { updateStandardStateThermo(); if (m_useTmpRefStateStorage) { return m_g0_RT; } else { throw NotImplementedError("VPStandardStateTP::getGibbs_RT_ref"); } } void VPStandardStateTP::getEntropy_R_ref(doublereal* sr) const { updateStandardStateThermo(); if (m_useTmpRefStateStorage) { std::copy(m_s0_R.begin(), m_s0_R.end(), sr); } else { throw NotImplementedError("VPStandardStateTP::getEntropy_R_ref"); } } void VPStandardStateTP::getCp_R_ref(doublereal* cpr) const { updateStandardStateThermo(); if (m_useTmpRefStateStorage) { std::copy(m_cp0_R.begin(), m_cp0_R.end(), cpr); } else { throw NotImplementedError("VPStandardStateTP::getCp_R_ref"); } } void VPStandardStateTP::getStandardVolumes_ref(doublereal* vol) const { updateStandardStateThermo(); std::copy(m_Vss.begin(), m_Vss.end(), vol); } void VPStandardStateTP::initThermo() { ThermoPhase::initThermo(); for (size_t k = 0; k < m_kk; k++) { PDSS* kPDSS = m_PDSS_storage[k].get(); if (kPDSS) { kPDSS->initThermo(); } } } bool VPStandardStateTP::addSpecies(shared_ptr spec) { // Specifically skip ThermoPhase::addSpecies since the Species object // doesn't have an associated SpeciesThermoInterpType object bool added = Phase::addSpecies(spec); if (!added) { return false; } m_h0_RT.push_back(0.0); m_cp0_R.push_back(0.0); m_g0_RT.push_back(0.0); m_s0_R.push_back(0.0); m_V0.push_back(0.0); m_hss_RT.push_back(0.0); m_cpss_R.push_back(0.0); m_gss_RT.push_back(0.0); m_sss_R.push_back(0.0); m_Vss.push_back(0.0); return true; } void VPStandardStateTP::setTemperature(const doublereal temp) { setState_TP(temp, m_Pcurrent); updateStandardStateThermo(); } void VPStandardStateTP::setPressure(doublereal p) { setState_TP(temperature(), p); updateStandardStateThermo(); } void VPStandardStateTP::calcDensity() { throw NotImplementedError("VPStandardStateTP::calcDensity() called, " "but EOS for phase is not known"); } void VPStandardStateTP::setState_TP(doublereal t, doublereal pres) { // A pretty tricky algorithm is needed here, due to problems involving // standard states of real fluids. For those cases you need to combine the T // and P specification for the standard state, or else you may venture into // the forbidden zone, especially when nearing the triple point. Therefore, // we need to do the standard state thermo calc with the (t, pres) combo. Phase::setTemperature(t); m_Pcurrent = pres; updateStandardStateThermo(); // Now, we still need to do the calculations for general ThermoPhase // objects. So, we switch back to a virtual function call, setTemperature, // and setPressure to recalculate stuff for child ThermoPhase objects of the // VPStandardStateTP object. At this point, we haven't touched m_tlast or // m_plast, so some calculations may still need to be done at the // ThermoPhase object level. calcDensity(); } void VPStandardStateTP::createInstallPDSS(size_t k, const XML_Node& s, const XML_Node* phaseNode) { if (m_PDSS_storage.size() < k+1) { m_PDSS_storage.resize(k+1); } PDSS* kPDSS = nullptr; bool use_STITbyPDSS; const XML_Node* const ss = s.findByName("standardState"); if (!ss) { use_STITbyPDSS = false; kPDSS = new PDSS_IdealGas(this, k, s, *phaseNode, true); } else { std::string model = ss->attrib("model"); if (model == "constant_incompressible") { kPDSS = new PDSS_ConstVol(this, k, s, *phaseNode, true); use_STITbyPDSS = false; } else if (model == "waterIAPWS" || model == "waterPDSS") { kPDSS = new PDSS_Water(this, 0); use_STITbyPDSS = true; m_useTmpRefStateStorage = false; } else if (model == "HKFT") { kPDSS = new PDSS_HKFT(this, k, s, *phaseNode, true); use_STITbyPDSS = true; } else if (model == "IonFromNeutral") { kPDSS = new PDSS_IonsFromNeutral(this, k, s, *phaseNode, true); use_STITbyPDSS = true; } else if (model == "constant" || model == "temperature_polynomial" || model == "density_temperature_polynomial") { kPDSS = new PDSS_SSVol(this, k, s, *phaseNode, true); use_STITbyPDSS = false; } else { throw CanteraError("VPStandardStateTP::createInstallPDSS", "unknown standard state formulation: " + model); } } if (use_STITbyPDSS) { auto stit = make_shared(kPDSS); m_spthermo->install_STIT(k, stit); } else { shared_ptr stit( newSpeciesThermoInterpType(s.child("thermo"))); stit->validate(s["name"]); m_spthermo->install_STIT(k, stit); } m_PDSS_storage[k].reset(kPDSS); } PDSS* VPStandardStateTP::providePDSS(size_t k) { return m_PDSS_storage[k].get(); } const PDSS* VPStandardStateTP::providePDSS(size_t k) const { return m_PDSS_storage[k].get(); } void VPStandardStateTP::invalidateCache() { ThermoPhase::invalidateCache(); m_Tlast_ss += 0.0001234; } void VPStandardStateTP::initThermoXML(XML_Node& phaseNode, const std::string& id) { for (size_t k = 0; k < m_kk; k++) { PDSS* kPDSS = m_PDSS_storage[k].get(); AssertTrace(kPDSS != 0); if (kPDSS) { kPDSS->initThermoXML(phaseNode, id); } } ThermoPhase::initThermoXML(phaseNode, id); } void VPStandardStateTP::_updateStandardStateThermo() const { double Tnow = temperature(); for (size_t k = 0; k < m_kk; k++) { PDSS* kPDSS = m_PDSS_storage[k].get(); kPDSS->setState_TP(Tnow, m_Pcurrent); // reference state thermo if (Tnow != m_tlast && m_useTmpRefStateStorage) { m_h0_RT[k] = kPDSS->enthalpy_RT_ref(); m_s0_R[k] = kPDSS->entropy_R_ref(); m_g0_RT[k] = m_h0_RT[k] - m_s0_R[k]; m_cp0_R[k] = kPDSS->cp_R_ref(); m_V0[k] = kPDSS->molarVolume_ref(); } // standard state thermo m_hss_RT[k] = kPDSS->enthalpy_RT(); m_sss_R[k] = kPDSS->entropy_R(); m_gss_RT[k] = m_hss_RT[k] - m_sss_R[k]; m_cpss_R[k] = kPDSS->cp_R(); m_Vss[k] = kPDSS->molarVolume(); } m_Plast_ss = m_Pcurrent; m_Tlast_ss = Tnow; m_tlast = Tnow; } void VPStandardStateTP::updateStandardStateThermo() const { double Tnow = temperature(); if (Tnow != m_Tlast_ss || Tnow != m_tlast || m_Pcurrent != m_Plast_ss) { _updateStandardStateThermo(); } } }