/** * @file vcs_VolPhase.cpp */ /* $Id$ */ /* * Copywrite (2005) Sandia Corporation. Under the terms of * Contract DE-AC04-94AL85000 with Sandia Corporation, the * U.S. Government retains certain rights in this software. */ #include "vcs_VolPhase.h" #include "vcs_internal.h" #include "vcs_SpeciesProperties.h" #include "vcs_species_thermo.h" #include "vcs_solve.h" #include "ThermoPhase.h" #include "mix_defs.h" #include #include #include namespace VCSnonideal { /* * * vcs_VolPhase(): * * Constructor for the VolPhase object. */ vcs_VolPhase::vcs_VolPhase(VCS_SOLVE * owningSolverObject) : m_owningSolverObject(0), VP_ID(-1), Domain_ID(-1), m_singleSpecies(true), m_gasPhase(false), m_eqnState(VCS_EOS_CONSTANT), ChargeNeutralityElement(-1), p_VCS_UnitsFormat(VCS_UNITS_MKS), p_activityConvention(0), m_numElemConstraints(0), m_elemGlobalIndex(0), m_numSpecies(0), m_totalMolesInert(0.0), m_isIdealSoln(false), m_existence(VCS_PHASE_EXIST_NO), m_MFStartIndex(0), IndSpecies(0), m_useCanteraCalls(false), TP_ptr(0), v_totalMoles(0.0), m_totalVol(0.0), m_vcsStateStatus(VCS_STATECALC_OLD), m_phi(0.0), m_UpToDate(false), m_UpToDate_AC(false), m_UpToDate_VolStar(false), m_UpToDate_VolPM(false), m_UpToDate_GStar(false), m_UpToDate_G0(false), Temp(273.15), Pres(1.01325E5), RefPres(1.01325E5) { m_owningSolverObject = owningSolverObject; } /***************************************************************************/ /* * * ~vcs_VolPhase(): * * Destructor for the VolPhase object. */ vcs_VolPhase::~vcs_VolPhase() { for (int k = 0; k < m_numSpecies; k++) { vcs_SpeciesProperties *sp = ListSpeciesPtr[k]; delete sp; sp = 0; } } /************************************************************************************/ /* * * Copy Constructor(): * * Objects that are owned by this object are deep copied here, except * for the ThermoPhase object. * The assignment operator does most of the work. */ vcs_VolPhase::vcs_VolPhase(const vcs_VolPhase& b) : m_owningSolverObject(b.m_owningSolverObject), VP_ID(b.VP_ID), Domain_ID(b.Domain_ID), m_singleSpecies(b.m_singleSpecies), m_gasPhase(b.m_gasPhase), m_eqnState(b.m_eqnState), ChargeNeutralityElement(b.ChargeNeutralityElement), p_VCS_UnitsFormat(b.p_VCS_UnitsFormat), p_activityConvention(b.p_activityConvention), m_numElemConstraints(b.m_numElemConstraints), m_numSpecies(b.m_numSpecies), m_totalMolesInert(b.m_totalMolesInert), m_isIdealSoln(b.m_isIdealSoln), m_existence(b.m_existence), m_MFStartIndex(b.m_MFStartIndex), m_useCanteraCalls(b.m_useCanteraCalls), TP_ptr(b.TP_ptr), v_totalMoles(b.v_totalMoles), m_phiVarIndex(-1), m_totalVol(b.m_totalVol), m_vcsStateStatus(VCS_STATECALC_OLD), m_phi(b.m_phi), m_UpToDate(false), m_UpToDate_AC(false), m_UpToDate_VolStar(false), m_UpToDate_VolPM(false), m_UpToDate_GStar(false), m_UpToDate_G0(false), Temp(b.Temp), Pres(b.Pres) { /* * Call the Assignment operator to do the heavy * lifting. */ *this = b; } /***************************************************************************/ /* * Assignment operator() * * (note, this is used, so keep it current!) */ vcs_VolPhase& vcs_VolPhase::operator=(const vcs_VolPhase& b) { int k; if (&b != this) { int old_num = m_numSpecies; // Note: we comment this out for the assignment operator // specifically, because it isn't true for the assignment // operator but is true for a copy constructor // m_owningSolverObject = b.m_owningSolverObject; VP_ID = b.VP_ID; Domain_ID = b.Domain_ID; m_singleSpecies = b.m_singleSpecies; m_gasPhase = b.m_gasPhase; m_eqnState = b.m_eqnState; m_numSpecies = b.m_numSpecies; m_numElemConstraints = b.m_numElemConstraints; ChargeNeutralityElement = b.ChargeNeutralityElement; m_elementNames.resize(b.m_numElemConstraints); for (int e = 0; e < b.m_numElemConstraints; e++) { m_elementNames[e] = b.m_elementNames[e]; } m_elementActive = b.m_elementActive; m_elementType = b.m_elementType; m_formulaMatrix.resize(m_numElemConstraints, m_numSpecies, 0.0); for (int e = 0; e < m_numElemConstraints; e++) { for (int k = 0; k < m_numSpecies; k++) { m_formulaMatrix[e][k] = b.m_formulaMatrix[e][k]; } } m_speciesUnknownType = b.m_speciesUnknownType; m_elemGlobalIndex = b.m_elemGlobalIndex; m_numSpecies = b.m_numSpecies; PhaseName = b.PhaseName; m_totalMolesInert = b.m_totalMolesInert; p_activityConvention= b.p_activityConvention; m_isIdealSoln = b.m_isIdealSoln; m_existence = b.m_existence; m_MFStartIndex = b.m_MFStartIndex; /* * Do a shallow copy because we haven' figured this out. */ IndSpecies = b.IndSpecies; //IndSpeciesContig = b.IndSpeciesContig; for (k = 0; k < old_num; k++) { if ( ListSpeciesPtr[k]) { delete ListSpeciesPtr[k]; ListSpeciesPtr[k] = 0; } } ListSpeciesPtr.resize(m_numSpecies, 0); for (k = 0; k < m_numSpecies; k++) { ListSpeciesPtr[k] = new vcs_SpeciesProperties(*(b.ListSpeciesPtr[k])); } p_VCS_UnitsFormat = b.p_VCS_UnitsFormat; m_useCanteraCalls = b.m_useCanteraCalls; /* * Do a shallow copy of the ThermoPhase object pointer. * We don't duplicate the object. * Um, there is no reason we couldn't do a * duplicateMyselfAsThermoPhase() call here. This will * have to be looked into. */ TP_ptr = b.TP_ptr; v_totalMoles = b.v_totalMoles; Xmol = b.Xmol; m_phi = b.m_phi; m_phiVarIndex = b.m_phiVarIndex; SS0ChemicalPotential = b.SS0ChemicalPotential; StarChemicalPotential = b.StarChemicalPotential; StarMolarVol = b.StarMolarVol; PartialMolarVol = b.PartialMolarVol; ActCoeff = b.ActCoeff; dLnActCoeffdMolNumber = b.dLnActCoeffdMolNumber; m_UpToDate = false; m_vcsStateStatus = b.m_vcsStateStatus; m_UpToDate_AC = false; m_UpToDate_VolStar = false; m_UpToDate_VolPM = false; m_UpToDate_GStar = false; m_UpToDate_G0 = false; Temp = b.Temp; Pres = b.Pres; setState_TP(Temp, Pres); _updateMoleFractionDependencies(); } return *this; } /***************************************************************************/ void vcs_VolPhase::resize(const int phaseNum, const int nspecies, const int numElem, const char * const phaseName, const double molesInert) { #ifdef DEBUG_MODE if (nspecies <= 0) { plogf("nspecies Error\n"); std::exit(-1); } if (phaseNum < 0) { plogf("phaseNum should be greater than 0\n"); std::exit(-1); } #endif setTotalMolesInert(molesInert); m_phi = 0.0; m_phiVarIndex = -1; if (phaseNum == VP_ID) { if (strcmp(PhaseName.c_str(), phaseName)) { plogf("Strings are different: %s %s :unknown situation\n", PhaseName.c_str(), phaseName); std::exit(-1); } } else { VP_ID = phaseNum; if (!phaseName) { char itmp[40]; sprintf(itmp, "Phase_%d", VP_ID); PhaseName = itmp; } else { PhaseName = phaseName; } } if (nspecies > 1) { m_singleSpecies = false; } else { m_singleSpecies = true; } if (m_numSpecies == nspecies && numElem == m_numElemConstraints) { return; } m_numSpecies = nspecies; if (nspecies > 1) { m_singleSpecies = false; } IndSpecies.resize(nspecies, -1); if ((int) ListSpeciesPtr.size() >= m_numSpecies) { for (int i = 0; i < m_numSpecies; i++) { if (ListSpeciesPtr[i]) { delete ListSpeciesPtr[i]; ListSpeciesPtr[i] = 0; } } } ListSpeciesPtr.resize(nspecies, 0); for (int i = 0; i < nspecies; i++) { ListSpeciesPtr[i] = new vcs_SpeciesProperties(phaseNum, i, this); } Xmol.resize(nspecies, 0.0); for (int i = 0; i < nspecies; i++) { Xmol[i] = 1.0/nspecies; } SS0ChemicalPotential.resize(nspecies, -1.0); StarChemicalPotential.resize(nspecies, -1.0); StarMolarVol.resize(nspecies, -1.0); PartialMolarVol.resize(nspecies, -1.0); ActCoeff.resize(nspecies, 1.0); dLnActCoeffdMolNumber.resize(nspecies, nspecies, 0.0); m_speciesUnknownType.resize(nspecies, VCS_SPECIES_TYPE_MOLNUM); m_UpToDate = false; m_vcsStateStatus = VCS_STATECALC_OLD; m_UpToDate_AC = false; m_UpToDate_VolStar = false; m_UpToDate_VolPM = false; m_UpToDate_GStar = false; m_UpToDate_G0 = false; elemResize(numElem); } /***************************************************************************/ void vcs_VolPhase::elemResize(const int numElemConstraints) { m_elementNames.resize(numElemConstraints); m_elementActive.resize(numElemConstraints+1, 1); m_elementType.resize(numElemConstraints, VCS_ELEM_TYPE_ABSPOS); m_formulaMatrix.resize(numElemConstraints, m_numSpecies, 0.0); m_elementNames.resize(numElemConstraints, ""); m_elemGlobalIndex.resize(numElemConstraints, -1); m_numElemConstraints = numElemConstraints; } /***************************************************************************/ //! Evaluate activity coefficients /*! * We carry out a calculation whenever UpTODate_AC is false. Specifically * whenever a phase goes zero, we do not carry out calculations on it. * * (private) */ void vcs_VolPhase::_updateActCoeff() const { if (m_isIdealSoln) { m_UpToDate_AC = true; return; } if (m_useCanteraCalls) { TP_ptr->getActivityCoefficients(VCS_DATA_PTR(ActCoeff)); } m_UpToDate_AC = true; } /***************************************************************************/ /* * * Evaluate one activity coefficients. * * return one activity coefficient. Have to recalculate them all to get * one. */ double vcs_VolPhase::AC_calc_one(int kspec) const { if (! m_UpToDate_AC) { _updateActCoeff(); } return(ActCoeff[kspec]); } /***************************************************************************/ // Gibbs free energy calculation at a temperature for the reference state // of each species /* */ void vcs_VolPhase::_updateG0() const { if (m_useCanteraCalls) { TP_ptr->getGibbs_ref(VCS_DATA_PTR(SS0ChemicalPotential)); } else { double R = vcsUtil_gasConstant(p_VCS_UnitsFormat); for (int k = 0; k < m_numSpecies; k++) { int kglob = IndSpecies[k]; vcs_SpeciesProperties *sProp = ListSpeciesPtr[k]; VCS_SPECIES_THERMO *sTherm = sProp->SpeciesThermo; SS0ChemicalPotential[k] = R * (sTherm->G0_R_calc(kglob, Temp)); } } m_UpToDate_G0 = true; } /***************************************************************************/ // Gibbs free energy calculation at a temperature for the reference state // of a species, return a value for one species /* * @param kspec species index * @param TKelvin temperature * * @return return value of the gibbs free energy */ double vcs_VolPhase::G0_calc_one(int kspec) const { if (!m_UpToDate_G0) { _updateG0(); } return SS0ChemicalPotential[kspec]; } /***************************************************************************/ // Gibbs free energy calculation for standard states /* * Calculate the Gibbs free energies for the standard states * The results are held internally within the object. * * @param TKelvin Current temperature * @param pres Current pressure (pascal) */ void vcs_VolPhase::_updateGStar() const { if (m_useCanteraCalls) { TP_ptr->getStandardChemPotentials(VCS_DATA_PTR(StarChemicalPotential)); } else { double R = vcsUtil_gasConstant(p_VCS_UnitsFormat); for (int k = 0; k < m_numSpecies; k++) { int kglob = IndSpecies[k]; vcs_SpeciesProperties *sProp = ListSpeciesPtr[k]; VCS_SPECIES_THERMO *sTherm = sProp->SpeciesThermo; StarChemicalPotential[k] = R * (sTherm->GStar_R_calc(kglob, Temp, Pres)); } } m_UpToDate_GStar = true; } /***************************************************************************/ // Gibbs free energy calculation for standard state of one species /* * Calculate the Gibbs free energies for the standard state * of the kth species. * The results are held internally within the object. * The kth species standard state G is returned * * @param kspec Species number (within the phase) * * @return Gstar[kspec] returns the gibbs free energy for the * standard state of the kspec species. */ double vcs_VolPhase::GStar_calc_one(int kspec) const { if (!m_UpToDate_GStar) { _updateGStar(); } return StarChemicalPotential[kspec]; } /***************************************************************************/ // Set the mole fractions from a conventional mole fraction vector /* * * @param xmol Value of the mole fractions for the species * in the phase. These are contiguous. */ void vcs_VolPhase::setMoleFractions(const double * const xmol) { double sum = -1.0; for (int k = 0; k < m_numSpecies; k++) { Xmol[k] = xmol[k]; sum+= xmol[k]; } if (std::fabs(sum) > 1.0E-13) { for (int k = 0; k < m_numSpecies; k++) { Xmol[k] /= sum; } } _updateMoleFractionDependencies(); m_UpToDate = false; m_vcsStateStatus = VCS_STATECALC_TMP; } /***************************************************************************/ // Updates the mole fractions in subobjects /* * Whenever the mole fractions change, this routine * should be called. */ void vcs_VolPhase::_updateMoleFractionDependencies() { if (m_useCanteraCalls) { if (TP_ptr) { TP_ptr->setState_PX(Pres, &(Xmol[m_MFStartIndex])); } } if (!m_isIdealSoln) { m_UpToDate_AC = false; m_UpToDate_VolPM = false; } } /***************************************************************************/ // Return a const reference to the mole fraction vector in the phase const std::vector & vcs_VolPhase::moleFractions() const { return Xmol; } /***************************************************************************/ // Set the moles and/or mole fractions within the phase /* * * */ void vcs_VolPhase::setMoleFractionsState(double totalMoles, const double * moleFractions, const int vcsStateStatus) { if (totalMoles != 0.0) { if (vcsStateStatus != VCS_STATECALC_TMP) { printf("vcs_VolPhase::setMolesFractionsState: inappropriate usage\n"); std::exit(-1); } m_UpToDate = false; m_vcsStateStatus = VCS_STATECALC_TMP; if (m_existence == -VCS_PHASE_EXIST_ZEROEDPHASE ) { printf("vcs_VolPhase::setMolesFractionsState: inappropriate usage\n"); std::exit(-1); } m_existence = VCS_PHASE_EXIST_YES; } else { m_UpToDate = true; m_vcsStateStatus = vcsStateStatus; if (m_existence > VCS_PHASE_EXIST_NO ) { m_existence = VCS_PHASE_EXIST_NO; } } double sum = 0.0; for (int k = 0; k < m_numSpecies; k++) { Xmol[k] = moleFractions[k]; sum += moleFractions[k]; } if (sum == 0.0) { printf("vcs_VolPhase::setMolesFractionsState: inappropriate usage\n"); std::exit(-1); } if (sum != 1.0) { for (int k = 0; k < m_numSpecies; k++) { Xmol[k] /= sum; } } _updateMoleFractionDependencies(); } /***************************************************************************/ // Set the moles within the phase /* * This function takes as input the mole numbers in vcs format, and * then updates this object with their values. This is essentially * a gather routine. * * * @param molesSpeciesVCS array of mole numbers. Note, the indecises * for species in * this array may not be contiguous. IndSpecies[] is needed * to gather the species into the local contiguous vector * format. */ void vcs_VolPhase::setMolesFromVCS(const int stateCalc, const double * molesSpeciesVCS) { int kglob; double tmp; v_totalMoles = m_totalMolesInert; if (molesSpeciesVCS == 0) { #ifdef DEBUG_MODE if (m_owningSolverObject == 0) { printf("vcs_VolPhase::setMolesFromVCS shouldn't be here\n"); std::exit(-1); } #endif if (stateCalc == VCS_STATECALC_OLD) { molesSpeciesVCS = VCS_DATA_PTR(m_owningSolverObject->m_molNumSpecies_old); } else if (stateCalc == VCS_STATECALC_NEW) { molesSpeciesVCS = VCS_DATA_PTR(m_owningSolverObject->m_molNumSpecies_new); } #ifdef DEBUG_MODE else { printf("vcs_VolPhase::setMolesFromVCS shouldn't be here\n"); std::exit(-1); } #endif } #ifdef DEBUG_MODE else { if (m_owningSolverObject) { if (stateCalc == VCS_STATECALC_OLD) { if (molesSpeciesVCS != VCS_DATA_PTR(m_owningSolverObject->m_molNumSpecies_old)) { printf("vcs_VolPhase::setMolesFromVCS shouldn't be here\n"); std::exit(-1); } } else if (stateCalc == VCS_STATECALC_NEW) { if (molesSpeciesVCS != VCS_DATA_PTR(m_owningSolverObject->m_molNumSpecies_new)) { printf("vcs_VolPhase::setMolesFromVCS shouldn't be here\n"); std::exit(-1); } } } } #endif for (int k = 0; k < m_numSpecies; k++) { if (m_speciesUnknownType[k] != VCS_SPECIES_TYPE_INTERFACIALVOLTAGE) { kglob = IndSpecies[k]; v_totalMoles += MAX(0.0, molesSpeciesVCS[kglob]); } } if (v_totalMoles > 0.0) { for (int k = 0; k < m_numSpecies; k++) { if (m_speciesUnknownType[k] != VCS_SPECIES_TYPE_INTERFACIALVOLTAGE) { kglob = IndSpecies[k]; tmp = MAX(0.0, molesSpeciesVCS[kglob]); Xmol[k] = tmp / v_totalMoles; } } m_existence = VCS_PHASE_EXIST_YES; } else { // This is where we will start to store a better approximation // for the mole fractions, when the phase doesn't exist. // This is currently unimplemented. //for (int k = 0; k < m_numSpecies; k++) { // Xmol[k] = 1.0 / m_numSpecies; //} m_existence = VCS_PHASE_EXIST_NO; } /* * Update the electric potential if it is a solution variable * in the equation system */ if (m_phiVarIndex >= 0) { kglob = IndSpecies[m_phiVarIndex]; if (m_numSpecies == 1) { Xmol[m_phiVarIndex] = 1.0; } else { Xmol[m_phiVarIndex] = 0.0; } double phi = molesSpeciesVCS[kglob]; setElectricPotential(phi); if (m_numSpecies == 1) { m_existence = VCS_PHASE_EXIST_YES; } } _updateMoleFractionDependencies(); if (m_totalMolesInert > 0.0) { m_existence = VCS_PHASE_EXIST_ALWAYS; } /* * Set flags indicating we are up to date with the VCS state vector. */ m_UpToDate = true; m_vcsStateStatus = stateCalc; } /***************************************************************************/ // Set the moles within the phase /* * This function takes as input the mole numbers in vcs format, and * then updates this object with their values. This is essentially * a gather routine. * * * @param molesSpeciesVCS array of mole numbers. Note, * the indecises for species in * this array may not be contiguous. IndSpecies[] is needed * to gather the species into the local contiguous vector * format. */ void vcs_VolPhase::setMolesFromVCSCheck(const int stateCalc, const double * molesSpeciesVCS, const double * const TPhMoles) { setMolesFromVCS(stateCalc, molesSpeciesVCS); /* * Check for consistency with TPhMoles[] */ double Tcheck = TPhMoles[VP_ID]; if (Tcheck != v_totalMoles) { if (vcs_doubleEqual(Tcheck, v_totalMoles)) { Tcheck = v_totalMoles; } else { plogf("vcs_VolPhase::setMolesFromVCSCheck: " "We have a consistency problem: %21.16g %21.16g\n", Tcheck, v_totalMoles); std::exit(-1); } } } /***************************************************************************/ // Update the moles within the phase, if necessary /* * This function takes as input the stateCalc value, which * determines where within VCS_SOLVE to fetch the mole numbers. * It then updates this object with their values. This is essentially * a gather routine. * * @param stateCalc State calc value either VCS_STATECALC_OLD * or VCS_STATECALC_NEW. With any other value * nothing is done. * */ void vcs_VolPhase::updateFromVCS_MoleNumbers(const int stateCalc) { if (!m_UpToDate || (stateCalc != m_vcsStateStatus)) { if (stateCalc == VCS_STATECALC_OLD || stateCalc == VCS_STATECALC_NEW) { if (m_owningSolverObject) { setMolesFromVCS(stateCalc); } } } } /**************************************************************************/ // Fill in an activity coefficients vector within a VCS_SOLVE object /* * This routine will calculate the activity coefficients for the * current phase, and fill in the corresponding entries in the * VCS activity coefficients vector. * * @param AC vector of activity coefficients for all of the species * in all of the phases in a VCS problem. Only the * entries for the current phase are filled in. */ void vcs_VolPhase::sendToVCS_ActCoeff(const int stateCalc, double * const AC) { updateFromVCS_MoleNumbers(stateCalc); if (!m_UpToDate_AC) { _updateActCoeff(); } int kglob; for (int k = 0; k < m_numSpecies; k++) { kglob = IndSpecies[k]; AC[kglob] = ActCoeff[k]; } } /***************************************************************************/ // Fill in the partial molar volume vector for VCS /* * This routine will calculate the partial molar volumes for the * current phase (if needed), and fill in the corresponding entries in the * VCS partial molar volumes vector. * * @param VolPM vector of partial molar volumes for all of the species * in all of the phases in a VCS problem. Only the * entries for the current phase are filled in. */ double vcs_VolPhase::sendToVCS_VolPM(double * const VolPM) const { if (!m_UpToDate_VolPM) { (void) _updateVolPM(); } int kglob; for (int k = 0; k < m_numSpecies; k++) { kglob = IndSpecies[k]; VolPM[kglob] = PartialMolarVol[k]; } return m_totalVol; } /***************************************************************************/ // Fill in the partial molar volume vector for VCS /* * This routine will calculate the partial molar volumes for the * current phase (if needed), and fill in the corresponding entries in the * VCS partial molar volumes vector. * * @param VolPM vector of partial molar volumes for all of the species * in all of the phases in a VCS problem. Only the * entries for the current phase are filled in. */ void vcs_VolPhase::sendToVCS_GStar(double * const gstar) const { if (!m_UpToDate_GStar) { _updateGStar(); } int kglob; for (int k = 0; k < m_numSpecies; k++) { kglob = IndSpecies[k]; gstar[kglob] = StarChemicalPotential[k]; } } /***************************************************************************/ void vcs_VolPhase::setElectricPotential(const double phi) { m_phi = phi; if (m_useCanteraCalls) { TP_ptr->setElectricPotential(m_phi); } // We have changed the state variable. Set uptodate flags to false m_UpToDate_AC = false; m_UpToDate_VolStar = false; m_UpToDate_VolPM = false; m_UpToDate_GStar = false; } /***************************************************************************/ double vcs_VolPhase::electricPotential() const { return m_phi; } /***************************************************************************/ // Sets the temperature and pressure in this object and // underlying objects /* * Sets the temperature and pressure in this object and * underlying objects. The underlying objects refers to the * Cantera's ThermoPhase object for this phase. * * @param temperature_Kelvin (Kelvin) * @param pressure_PA Pressure (MKS units - Pascal) */ void vcs_VolPhase::setState_TP(const double temp, const double pres) { if (Temp == temp) { if (Pres == pres) { return; } } if (m_useCanteraCalls) { TP_ptr->setElectricPotential(m_phi); TP_ptr->setState_TP(temp, pres); } Temp = temp; Pres = pres; m_UpToDate_AC = false; m_UpToDate_VolStar = false; m_UpToDate_VolPM = false; m_UpToDate_GStar = false; m_UpToDate_G0 = false; } /***************************************************************************/ // Sets the temperature in this object and // underlying objects /* * Sets the temperature and pressure in this object and * underlying objects. The underlying objects refers to the * Cantera's ThermoPhase object for this phase. * * @param temperature_Kelvin (Kelvin) */ void vcs_VolPhase::setState_T(const double temp) { setState_TP(temp, Pres); } /***************************************************************************/ // Molar volume calculation for standard states /* * Calculate the molar volume for the standard states * The results are held internally within the object. * * @param TKelvin Current temperature * @param pres Current pressure (pascal) * * Calculations are in m**3 / kmol */ void vcs_VolPhase::_updateVolStar() const { if (m_useCanteraCalls) { TP_ptr->getStandardVolumes(VCS_DATA_PTR(StarMolarVol)); } else { for (int k = 0; k < m_numSpecies; k++) { int kglob = IndSpecies[k]; vcs_SpeciesProperties *sProp = ListSpeciesPtr[k]; VCS_SPECIES_THERMO *sTherm = sProp->SpeciesThermo; StarMolarVol[k] = (sTherm->VolStar_calc(kglob, Temp, Pres)); } } m_UpToDate_VolStar = true; } /***************************************************************************/ // Molar volume calculation for standard state of one species /* * Calculate the molar volume for the standard states * The results are held internally within the object. * Return the molar volume for one species * * @param kspec Species number (within the phase) * @param TKelvin Current temperature * @param pres Current pressure (pascal) * * @return molar volume of the kspec species's standard * state */ double vcs_VolPhase::VolStar_calc_one(int kspec) const { if (!m_UpToDate_VolStar) { _updateVolStar(); } return StarMolarVol[kspec]; } /***************************************************************************/ // Calculate the partial molar volumes of all species and return the // total volume /* * Calculates these quantitites internally and then stores them * * @return total volume (m**3) */ double vcs_VolPhase::_updateVolPM() const { int k, kglob; if (m_useCanteraCalls) { TP_ptr->getPartialMolarVolumes(VCS_DATA_PTR(PartialMolarVol)); } else { for (k = 0; k < m_numSpecies; k++) { kglob = IndSpecies[k]; vcs_SpeciesProperties *sProp = ListSpeciesPtr[k]; VCS_SPECIES_THERMO *sTherm = sProp->SpeciesThermo; StarMolarVol[k] = (sTherm->VolStar_calc(kglob, Temp, Pres)); } for (k = 0; k < m_numSpecies; k++) { PartialMolarVol[k] = StarMolarVol[k]; } } m_totalVol = 0.0; for (k = 0; k < m_numSpecies; k++) { m_totalVol += PartialMolarVol[k] * Xmol[k]; } m_totalVol *= v_totalMoles; if (m_totalMolesInert > 0.0) { if (m_gasPhase) { double volI = m_totalMolesInert * 8314.47215 * Temp / Pres; m_totalVol += volI; } else { printf("unknown situation\n"); std::exit(-1); } } m_UpToDate_VolPM = true; return m_totalVol; } /***************************************************************************/ /* * _updateLnActCoeffJac(): * */ void vcs_VolPhase::_updateLnActCoeffJac() { int k, j; double deltaMoles_j = 0.0; /* * Evaluate the current base activity coefficients if necessary */ if (!m_UpToDate_AC) { _updateActCoeff(); } // Make copies of ActCoeff and Xmol for use in taking differences std::vector ActCoeff_Base(ActCoeff); std::vector Xmol_Base(Xmol); double TMoles_base = v_totalMoles; /* * Loop over the columns species to be deltad */ for (j = 0; j < m_numSpecies; j++) { /* * Calculate a value for the delta moles of species j * -> NOte Xmol[] and Tmoles are always positive or zero * quantities. */ double moles_j_base = v_totalMoles * Xmol_Base[j]; deltaMoles_j = 1.0E-7 * moles_j_base + 1.0E-20 * v_totalMoles + 1.0E-150; /* * Now, update the total moles in the phase and all of the * mole fractions based on this. */ v_totalMoles = TMoles_base + deltaMoles_j; for (k = 0; k < m_numSpecies; k++) { Xmol[k] = Xmol_Base[k] * TMoles_base / v_totalMoles; } Xmol[j] = (moles_j_base + deltaMoles_j) / v_totalMoles; /* * Go get new values for the activity coefficients. * -> Note this calls setState_PX(); */ _updateMoleFractionDependencies(); _updateActCoeff(); /* * Calculate the column of the matrix */ double * const lnActCoeffCol = dLnActCoeffdMolNumber[j]; for (k = 0; k < m_numSpecies; k++) { lnActCoeffCol[k] = (ActCoeff[k] - ActCoeff_Base[k]) / ((ActCoeff[k] + ActCoeff_Base[k]) * 0.5 * deltaMoles_j); } /* * Revert to the base case Xmol, v_totalMoles */ v_totalMoles = TMoles_base; vcs_vdcopy(Xmol, Xmol_Base, m_numSpecies); } /* * Go get base values for the activity coefficients. * -> Note this calls setState_TPX() again; * -> Just wanted to make sure that cantera is in sync * with VolPhase after this call. */ setMoleFractions(VCS_DATA_PTR(Xmol_Base)); _updateMoleFractionDependencies(); _updateActCoeff(); } /***************************************************************************/ // Downloads the ln ActCoeff jacobian into the VCS version of the // ln ActCoeff jacobian. /* * * This is essentially a scatter operation. * * The Jacobians are actually d( lnActCoeff) / d (MolNumber); * dLnActCoeffdMolNumber[j][k] * * j = id of the species mole number * k = id of the species activity coefficient */ void vcs_VolPhase::sendToVCS_LnActCoeffJac(double * const * const LnACJac_VCS) { /* * update the Ln Act Coeff jacobian entries with respect to the * mole number of species in the phase -> we always assume that * they are out of date. */ _updateLnActCoeffJac(); /* * Now copy over the values */ int j, k, jglob, kglob; for (j = 0; j < m_numSpecies; j++) { jglob = IndSpecies[j]; double * const lnACJacVCS_col = LnACJac_VCS[jglob]; const double * const lnACJac_col = dLnActCoeffdMolNumber[j]; for (k = 0; k < m_numSpecies; k++) { kglob = IndSpecies[k]; lnACJacVCS_col[kglob] = lnACJac_col[k]; } } } /***************************************************************************/ // Set the pointer for Cantera's ThermoPhase parameter /* * When we first initialize the ThermoPhase object, we read the * state of the ThermoPhase into vcs_VolPhase object. * * @param tp_ptr Pointer to the ThermoPhase object corresponding * to this phase. */ void vcs_VolPhase::setPtrThermoPhase(Cantera::ThermoPhase *tp_ptr) { TP_ptr = tp_ptr; if (TP_ptr) { m_useCanteraCalls = true; Temp = TP_ptr->temperature(); Pres = TP_ptr->pressure(); setState_TP(Temp, Pres); p_VCS_UnitsFormat = VCS_UNITS_MKS; m_phi = TP_ptr->electricPotential(); int nsp = TP_ptr->nSpecies(); int nelem = TP_ptr->nElements(); if (nsp != m_numSpecies) { if (m_numSpecies != 0) { plogf("Warning Nsp != NVolSpeces: %d %d \n", nsp, m_numSpecies); } resize(VP_ID, nsp, nelem, PhaseName.c_str()); } TP_ptr->getMoleFractions(VCS_DATA_PTR(Xmol)); _updateMoleFractionDependencies(); /* * figure out ideal solution tag */ if (nsp == 1) { m_isIdealSoln = true; } else { int eos = TP_ptr->eosType(); switch (eos) { case Cantera::cIdealGas: case Cantera::cIncompressible: case Cantera::cSurf: case Cantera::cMetal: case Cantera::cStoichSubstance: case Cantera::cSemiconductor: case Cantera::cLatticeSolid: case Cantera::cLattice: case Cantera::cEdge: case Cantera::cIdealSolidSolnPhase: m_isIdealSoln = true; break; default: m_isIdealSoln = false; }; } } else { m_useCanteraCalls = false; } } /***************************************************************************/ // Return a const ThermoPhase pointer corresponding to this phase /* * @return pointer to the ThermoPhase. */ const Cantera::ThermoPhase *vcs_VolPhase::ptrThermoPhase() const { return TP_ptr; } /***************************************************************************/ double vcs_VolPhase::TotalMoles() const { return v_totalMoles; } /***************************************************************************/ double vcs_VolPhase::molefraction(int k) const { return Xmol[k]; } /***************************************************************************/ // Sets the total moles in the phase /* * We don't have to flag the internal state as changing here * because we have just changed the total moles. * * @param totalMols Total moles in the phase (kmol) */ void vcs_VolPhase::setTotalMoles(const double totalMols) { v_totalMoles = totalMols; if (m_totalMolesInert > 0.0) { m_existence = VCS_PHASE_EXIST_ALWAYS; #ifdef DEBUG_MODE if (totalMols < m_totalMolesInert) { printf(" vcs_VolPhase::setTotalMoles:: ERROR totalMoles " "less than inert moles: %g %g\n", totalMols, m_totalMolesInert); std::exit(-1); } #endif } else { if (totalMols > 0.0) { m_existence = VCS_PHASE_EXIST_YES; } else { m_existence = VCS_PHASE_EXIST_NO; } } } /***************************************************************************/ // Sets the mole flag within the object to out of date /* * This will trigger the object to go get the current mole numbers * when it needs it. */ void vcs_VolPhase::setMolesOutOfDate(int stateCalc) { m_UpToDate = false; if (stateCalc != -1) { m_vcsStateStatus = stateCalc; } } /***************************************************************************/ // Sets the mole flag within the object to be current /* * */ void vcs_VolPhase::setMolesCurrent(int stateCalc) { m_UpToDate = true; m_vcsStateStatus = stateCalc; } /***************************************************************************/ // Return a string representing the equation of state /* * The string is no more than 16 characters. * @param EOSType : integer value of the equation of state * * @return returns a string representing the EOS */ std::string string16_EOSType(int EOSType) { char st[32]; st[16] = '\0'; switch (EOSType) { case VCS_EOS_CONSTANT: sprintf(st,"Constant "); break; case VCS_EOS_IDEAL_GAS: sprintf(st,"Ideal Gas "); break; case VCS_EOS_STOICH_SUB: sprintf(st,"Stoich Sub "); break; case VCS_EOS_IDEAL_SOLN: sprintf(st,"Ideal Soln "); break; case VCS_EOS_DEBEYE_HUCKEL: sprintf(st,"Debeye Huckel "); break; case VCS_EOS_REDLICK_KWONG: sprintf(st,"Redlick_Kwong "); break; case VCS_EOS_REGULAR_SOLN: sprintf(st,"Regular Soln "); break; default: sprintf(st,"UnkType: %-7d", EOSType); break; } st[16] = '\0'; std::string sss=st; return sss; } /***************************************************************************/ // Returns whether the phase is an ideal solution phase bool vcs_VolPhase::isIdealSoln() const { return m_isIdealSoln; } /***************************************************************************/ // Returns whether the phase uses Cantera calls bool vcs_VolPhase::usingCanteraCalls() const { return m_useCanteraCalls; } /***************************************************************************/ int vcs_VolPhase::phiVarIndex() const { return m_phiVarIndex; } /***************************************************************************/ void vcs_VolPhase::setPhiVarIndex(int phiVarIndex) { m_phiVarIndex = phiVarIndex; } /***************************************************************************/ // Retrieve the kth Species structure for the species belonging to this phase /* * The index into this vector is the species index within the phase. * * @param kindex kth species index. */ vcs_SpeciesProperties * vcs_VolPhase::speciesProperty(const int kindex) { return ListSpeciesPtr[kindex]; } /***************************************************************************/ // Boolean indicating whether the phase exists or not int vcs_VolPhase::exists() const { return m_existence; } /**********************************************************************/ // Set the existence flag in the object void vcs_VolPhase::setExistence(const int existence) { if (existence == VCS_PHASE_EXIST_NO || existence == VCS_PHASE_EXIST_ZEROEDPHASE) { if (v_totalMoles != 0.0) { #ifdef DEBUG_MODE plogf("vcs_VolPhase::setExistence setting false existence for phase with moles"); plogendl(); exit(-1); #endif v_totalMoles = 0.0; } } #ifdef DEBUG_MODE else { if (m_totalMolesInert == 0.0) { if (v_totalMoles == 0.0) { plogf("vcs_VolPhase::setExistence setting true existence for phase with no moles"); plogendl(); exit(-1); } } } #endif m_existence = existence; } /**********************************************************************/ // Return the Global VCS index of the kth species in the phase /* * @param spIndex local species index (0 to the number of species * in the phase) * * @return Returns the VCS_SOLVE species index of the that species * This changes as rearrangements are carried out. */ int vcs_VolPhase::spGlobalIndexVCS(const int spIndex) const { return IndSpecies[spIndex]; } /**********************************************************************/ //! set the Global VCS index of the kth species in the phase /*! * @param spIndex local species index (0 to the number of species * in the phase) * * @return Returns the VCS_SOLVE species index of the that species * This changes as rearrangements are carried out. */ void vcs_VolPhase::setSpGlobalIndexVCS(const int spIndex, const int spGlobalIndex) { IndSpecies[spIndex] = spGlobalIndex; } /**********************************************************************/ // Sets the total moles of inert in the phase /* * @param tMolesInert Value of the total kmols of inert species in the * phase. */ void vcs_VolPhase::setTotalMolesInert(const double tMolesInert) { if (m_totalMolesInert != tMolesInert) { m_UpToDate = false; m_UpToDate_AC = false; m_UpToDate_VolStar = false; m_UpToDate_VolPM = false; m_UpToDate_GStar = false; m_UpToDate_G0 = false; v_totalMoles += (tMolesInert - m_totalMolesInert); m_totalMolesInert = tMolesInert; } if (m_totalMolesInert > 0.0) { m_existence = VCS_PHASE_EXIST_ALWAYS; } else { if (v_totalMoles > 0.0) { m_existence = VCS_PHASE_EXIST_YES; } else { m_existence = VCS_PHASE_EXIST_NO; } } } /**********************************************************************/ // returns the value of the total kmol of inert in the phase double vcs_VolPhase::totalMolesInert() const { return m_totalMolesInert; } /**********************************************************************/ //! Returns the global index of the local element index for the phase int vcs_VolPhase::elemGlobalIndex(const int e) const { DebugAssertThrowVCS(e >= 0, " vcs_VolPhase::elemGlobalIndex") ; DebugAssertThrowVCS(e < m_numElemConstraints, " vcs_VolPhase::elemGlobalIndex") ; return m_elemGlobalIndex[e]; } //! Returns the global index of the local element index for the phase void vcs_VolPhase::setElemGlobalIndex(const int eLocal, const int eGlobal) { DebugAssertThrowVCS(eLocal >= 0, "vcs_VolPhase::setElemGlobalIndex"); DebugAssertThrowVCS(eLocal < m_numElemConstraints, "vcs_VolPhase::setElemGlobalIndex"); m_elemGlobalIndex[eLocal] = eGlobal; } int vcs_VolPhase::nElemConstraints() const { return m_numElemConstraints; } std::string vcs_VolPhase::elementName(const int e) const { return m_elementNames[e]; } /*! * This function decides whether a phase has charged species * or not. */ static bool hasChargedSpecies(const Cantera::ThermoPhase * const tPhase) { int nSpPhase = tPhase->nSpecies(); for (int k = 0; k < nSpPhase; k++) { if (tPhase->charge(k) != 0.0) { return true; } } return false; } /********************************************************************** * * chargeNeutralityElement(): * * This utility routine decides whether a Cantera ThermoPhase needs * a constraint equation representing the charge neutrality of the * phase. It does this by searching for charged species. If it * finds one, and if the phase needs one, then it returns true. */ static bool chargeNeutralityElement(const Cantera::ThermoPhase * const tPhase) { int hasCharge = hasChargedSpecies(tPhase); if (tPhase->chargeNeutralityNecessary()) { if (hasCharge) { return true; } } return false; } int vcs_VolPhase::transferElementsFM(const Cantera::ThermoPhase * const tPhase) { int e, k, eT; std::string ename; int eFound = -2; /* * */ int nebase = tPhase->nElements(); int ne = nebase; int ns = tPhase->nSpecies(); /* * Decide whether we need an extra element constraint for charge * neutrality of the phase */ bool cne = chargeNeutralityElement(tPhase); if (cne) { ChargeNeutralityElement = ne; ne++; } /* * Assign and malloc structures */ elemResize(ne); if (ChargeNeutralityElement >= 0) { m_elementType[ChargeNeutralityElement] = VCS_ELEM_TYPE_CHARGENEUTRALITY; } if (hasChargedSpecies(tPhase)) { if (cne) { /* * We need a charge neutrality constraint. * We also have an Electron Element. These are * duplicates of each other. To avoid trouble with * possible range error conflicts, sometimes we eliminate * the Electron condition. Flag that condition for elimination * by toggling the ElActive variable. If we find we need it * later, we will retoggle ElActive to true. */ for (eT = 0; eT < nebase; eT++) { ename = tPhase->elementName(eT); if (ename == "E") { eFound = eT; m_elementActive[eT] = 0; m_elementType[eT] = VCS_ELEM_TYPE_ELECTRONCHARGE; } } } else { for (eT = 0; eT < nebase; eT++) { ename = tPhase->elementName(eT); if (ename == "E") { eFound = eT; m_elementType[eT] = VCS_ELEM_TYPE_ELECTRONCHARGE; } } } if (eFound == -2) { eFound = ne; m_elementType[ne] = VCS_ELEM_TYPE_ELECTRONCHARGE; m_elementActive[ne] = 0; std::string ename = "E"; m_elementNames[ne] = ename; ne++; elemResize(ne); } } m_formulaMatrix.resize(ne, ns, 0.0); m_speciesUnknownType.resize(ns, VCS_SPECIES_TYPE_MOLNUM); elemResize(ne); //ElGlobalIndex.resize(ne, -1); e = 0; for (eT = 0; eT < nebase; eT++) { ename = tPhase->elementName(eT); m_elementNames[e] = ename; e++; } if (cne) { std::string pname = tPhase->id(); if (pname == "") { char sss[50]; sprintf(sss, "phase%d", VP_ID); pname = sss; } ename = "cn_" + pname; e = ChargeNeutralityElement; m_elementNames[e] = ename; } double * const * const fm = m_formulaMatrix.baseDataAddr(); for (k = 0; k < ns; k++) { e = 0; for (eT = 0; eT < nebase; eT++) { fm[e][k] = tPhase->nAtoms(k, eT); e++; } if (eFound >= 0) { fm[eFound][k] = - tPhase->charge(k); } } if (cne) { for (k = 0; k < ns; k++) { fm[ChargeNeutralityElement][k] = tPhase->charge(k); } } /* * Here, we figure out what is the species types are * The logic isn't set in stone, and is just for a particular type * of problem that I'm solving first. */ if (ns == 1) { if (tPhase->charge(0) != 0.0) { m_speciesUnknownType[0] = VCS_SPECIES_TYPE_INTERFACIALVOLTAGE; setPhiVarIndex(0); } } return ne; } // Type of the element constraint with index \c e. /* * @param e Element index. */ int vcs_VolPhase::elementType(const int e) const { return m_elementType[e]; } // Set the element Type of the element constraint with index \c e. /* * @param e Element index * @param eType type of the element. */ void vcs_VolPhase::setElementType(const int e, const int eType) { m_elementType[e] = eType; } double const * const * const vcs_VolPhase::getFormulaMatrix() const { double const * const * const fm = m_formulaMatrix.constBaseDataAddr(); return fm; } int vcs_VolPhase::speciesUnknownType(const int k) const { return m_speciesUnknownType[k]; } int vcs_VolPhase::elementActive(const int e) const { return m_elementActive[e]; } //! Return the number of species in the phase int vcs_VolPhase::nSpecies() const { return m_numSpecies; } }