/** * @file vcs_prob.cpp * Implementation for the Interface class for the vcs thermo * equilibrium solver package, */ /* * Copyright (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 "cantera/equil/vcs_prob.h" #include "cantera/equil/vcs_VolPhase.h" #include "vcs_species_thermo.h" #include "cantera/equil/vcs_internal.h" #include "cantera/thermo/ThermoPhase.h" #include "cantera/thermo/MolalityVPSSTP.h" #include #include #include using namespace std; namespace VCSnonideal { /* * VCS_PROB: constructor * * We initialize the arrays in the structure to the appropriate sizes. * And, we initialize all of the elements of the arrays to defaults. */ VCS_PROB::VCS_PROB(size_t nsp, size_t nel, size_t nph) : prob_type(VCS_PROBTYPE_TP), nspecies(nsp), NSPECIES0(0), ne(nel), NE0(0), NPhase(nph), NPHASE0(0), T(298.15), PresPA(1.0), Vol(0.0), m_VCS_UnitsFormat(VCS_UNITS_UNITLESS), /* Set the units for the chemical potential data to be * unitless */ iest(-1), /* The default is to not expect an initial estimate * of the species concentrations */ tolmaj(1.0E-8), tolmin(1.0E-6), m_Iterations(0), m_NumBasisOptimizations(0), m_printLvl(0), vcs_debug_print_lvl(0) { NSPECIES0 = nspecies; if (nspecies <= 0) { plogf("number of species is zero or neg\n"); exit(EXIT_FAILURE); } NE0 = ne; if (ne <= 0) { plogf("number of elements is zero or neg\n"); exit(EXIT_FAILURE); } NPHASE0 = NPhase; if (NPhase <= 0) { plogf("number of phases is zero or neg\n"); exit(EXIT_FAILURE); } if (nspecies < NPhase) { plogf("number of species is less than number of phases\n"); exit(EXIT_FAILURE); } m_gibbsSpecies.resize(nspecies, 0.0); w.resize(nspecies, 0.0); mf.resize(nspecies, 0.0); gai.resize(ne, 0.0); FormulaMatrix.resize(ne, nspecies, 0.0); SpeciesUnknownType.resize(nspecies, VCS_SPECIES_TYPE_MOLNUM); VolPM.resize(nspecies, 0.0); PhaseID.resize(nspecies, npos); SpName.resize(nspecies, ""); ElName.resize(ne, ""); m_elType.resize(ne, VCS_ELEM_TYPE_ABSPOS); ElActive.resize(ne, 1); WtSpecies.resize(nspecies, 0.0); Charge.resize(nspecies, 0.0); SpeciesThermo.resize(nspecies,0); for (size_t kspec = 0; kspec < nspecies; kspec++) { VCS_SPECIES_THERMO* ts_tmp = new VCS_SPECIES_THERMO(0, 0); if (ts_tmp == 0) { plogf("Failed to init a ts struct\n"); exit(EXIT_FAILURE); } SpeciesThermo[kspec] = ts_tmp; } VPhaseList.resize(nph, 0); for (size_t iphase = 0; iphase < NPhase; iphase++) { VPhaseList[iphase] = new vcs_VolPhase(); } } /**************************************************************************/ /**************************************************************************/ /**************************************************************************/ /* * VCS_PROB_INPUT:destructor * * We need to manually free all of the arrays. */ VCS_PROB::~VCS_PROB() { for (size_t i = 0; i < nspecies; i++) { delete SpeciesThermo[i]; SpeciesThermo[i] = 0; } for (size_t iph = 0; iph < NPhase; iph++) { delete VPhaseList[iph]; VPhaseList[iph] = 0; } } // Resizes all of the phase lists within the structure /* * Note, this doesn't change the number of phases in the problem. * It will change NPHASE0 if nsp is greater than NPHASE0. * * @param nPhase size to dimension all the phase lists to * @param force If true, this will dimension the size to be equal to nPhase * even if nPhase is less than the current value of NPHASE0 */ void VCS_PROB::resizePhase(size_t nPhase, int force) { if (force || nPhase > NPHASE0) { NPHASE0 = nPhase; } } // Resizes all of the species lists within the structure /* * Note, this doesn't change the number of species in the problem. * It will change NSPECIES0 if nsp is greater than NSPECIES0. * * @param nsp size to dimension all the species to * @param force If true, this will dimension the size to be equal to nsp * even if nsp is less than the current value of NSPECIES0 */ void VCS_PROB::resizeSpecies(size_t nsp, int force) { if (force || nsp > NSPECIES0) { m_gibbsSpecies.resize(nsp, 0.0); w.resize(nsp, 0.0); mf.resize(nsp, 0.0); FormulaMatrix.resize(NE0, nsp, 0.0); SpeciesUnknownType.resize(nsp, VCS_SPECIES_TYPE_MOLNUM); VolPM.resize(nsp, 0.0); PhaseID.resize(nsp, 0); SpName.resize(nsp, ""); WtSpecies.resize(nsp, 0.0); Charge.resize(nsp, 0.0); NSPECIES0 = nsp; if (nspecies > NSPECIES0) { nspecies = NSPECIES0; plogf("shouldn't be here\n"); exit(EXIT_FAILURE); } } } // Resizes all of the element lists within the structure /* * Note, this doesn't change the number of element constraints * in the problem. * It will change NE0 if nel is greater than NE0. * * @param nel size to dimension all the elements lists * @param force If true, this will dimension the size to be equal to nel * even if nel is less than the current value of NEL0 */ void VCS_PROB::resizeElements(size_t nel, int force) { if (force || nel > NE0) { gai.resize(nel, 0.0); FormulaMatrix.resize(nel, NSPECIES0, 0.0); ElName.resize(nel, ""); m_elType.resize(nel, VCS_ELEM_TYPE_ABSPOS); ElActive.resize(nel, 1); NE0 = nel; if (ne > NE0) { ne = NE0; } } } // Calculate the element abundance vector /* * Calculates the element abundance vectors from the mole * numbers */ void VCS_PROB::set_gai() { double* ElemAbund = VCS_DATA_PTR(gai); double* const* const fm = FormulaMatrix.baseDataAddr(); vcs_dzero(ElemAbund, ne); for (size_t j = 0; j < ne; j++) { for (size_t kspec = 0; kspec < nspecies; kspec++) { ElemAbund[j] += fm[j][kspec] * w[kspec]; } } } /*****************************************************************************/ static void print_space(int num) { for (int j = 0; j < num; j++) { (void) plogf(" "); } } /*****************************************************************************/ static void print_char(const char letter, const int num) { for (int i = 0; i < num; i++) { plogf("%c", letter); } } /***************************************************************************** * prob_report(): * * Print out the problem specification in all generality * as it currently exists in the VCS_PROB object * */ void VCS_PROB::prob_report(int print_lvl) { m_printLvl = print_lvl; vcs_VolPhase* Vphase = 0; /* * Printout the species information: PhaseID's and mole nums */ if (m_printLvl > 0) { plogf("\n"); print_char('=', 80); plogf("\n"); print_char('=', 20); plogf(" VCS_PROB: PROBLEM STATEMENT "); print_char('=', 31); plogf("\n"); print_char('=', 80); plogf("\n"); plogf("\n"); if (prob_type == 0) { plogf("\tSolve a constant T, P problem:\n"); plogf("\t\tT = %g K\n", T); double pres_atm = PresPA / 1.01325E5; plogf("\t\tPres = %g atm\n", pres_atm); } else { plogf("\tUnknown problem type\n"); exit(EXIT_FAILURE); } plogf("\n"); plogf(" Phase IDs of species\n"); plogf(" species phaseID phaseName "); plogf(" Initial_Estimated_Moles Species_Type\n"); for (size_t i = 0; i < nspecies; i++) { Vphase = VPhaseList[PhaseID[i]]; plogf("%16s %5d %16s", SpName[i].c_str(), PhaseID[i], Vphase->PhaseName.c_str()); if (iest >= 0) { plogf(" %-10.5g", w[i]); } else { plogf(" N/A"); } if (SpeciesUnknownType[i] == VCS_SPECIES_TYPE_MOLNUM) { plogf(" Mol_Num"); } else if (SpeciesUnknownType[i] == VCS_SPECIES_TYPE_INTERFACIALVOLTAGE) { plogf(" Voltage"); } else { plogf(" "); } plogf("\n"); } /* * Printout of the Phase structure information */ plogf("\n"); print_char('-', 80); plogf("\n"); plogf(" Information about phases\n"); plogf(" PhaseName PhaseNum SingSpec GasPhase " " EqnState NumSpec"); plogf(" TMolesInert TKmoles\n"); for (size_t iphase = 0; iphase < NPhase; iphase++) { Vphase = VPhaseList[iphase]; std::string EOS_cstr = string16_EOSType(Vphase->m_eqnState); plogf("%16s %5d %5d %8d ", Vphase->PhaseName.c_str(), Vphase->VP_ID_, Vphase->m_singleSpecies, Vphase->m_gasPhase); plogf("%16s %8d %16e ", EOS_cstr.c_str(), Vphase->nSpecies(), Vphase->totalMolesInert()); if (iest >= 0) { plogf("%16e\n", Vphase->totalMoles()); } else { plogf(" N/A\n"); } } plogf("\nElemental Abundances: "); plogf(" Target_kmol ElemType ElActive\n"); double fac = 1.0; if (m_VCS_UnitsFormat == VCS_UNITS_MKS) { //fac = 1.0E3; fac = 1.0; } for (size_t i = 0; i < ne; ++i) { print_space(26); plogf("%-2.2s", ElName[i].c_str()); plogf("%20.12E ", fac * gai[i]); plogf("%3d %3d\n", m_elType[i], ElActive[i]); } plogf("\nChemical Potentials: "); if (m_VCS_UnitsFormat == VCS_UNITS_UNITLESS) { plogf("(unitless)"); } else if (m_VCS_UnitsFormat == VCS_UNITS_KCALMOL) { plogf("(kcal/gmol)"); } else if (m_VCS_UnitsFormat == VCS_UNITS_KJMOL) { plogf("(kJ/gmol)"); } else if (m_VCS_UnitsFormat == VCS_UNITS_KELVIN) { plogf("(Kelvin)"); } else if (m_VCS_UnitsFormat == VCS_UNITS_MKS) { plogf("(J/kmol)"); } plogf("\n"); plogf(" Species (phase) " " SS0ChemPot StarChemPot\n"); for (size_t iphase = 0; iphase < NPhase; iphase++) { Vphase = VPhaseList[iphase]; Vphase->setState_TP(T, PresPA); for (size_t kindex = 0; kindex < Vphase->nSpecies(); kindex++) { size_t kglob = Vphase->spGlobalIndexVCS(kindex); plogf("%16s ", SpName[kglob].c_str()); if (kindex == 0) { plogf("%16s", Vphase->PhaseName.c_str()); } else { plogf(" "); } plogf("%16g %16g\n", Vphase->G0_calc_one(kindex), Vphase->GStar_calc_one(kindex)); } } plogf("\n"); print_char('=', 80); plogf("\n"); print_char('=', 20); plogf(" VCS_PROB: END OF PROBLEM STATEMENT "); print_char('=', 24); plogf("\n"); print_char('=', 80); plogf("\n\n"); } } // Add elements to the local element list /* * This routine sorts through the elements defined in the * vcs_VolPhase object. It then adds the new elements to * the VCS_PROB object, and creates a global map, which is * stored in the vcs_VolPhase object. * Id and matching of elements is done strictly via the element name, * with case not mattering. * * The routine also fills in the position of the element * in the vcs_VolPhase object's ElGlobalIndex field. * * @param volPhase Object containing the phase to be added. * The elements in this phase are parsed for * addition to the global element list */ void VCS_PROB::addPhaseElements(vcs_VolPhase* volPhase) { size_t e, eVP; size_t foundPos = npos; size_t neVP = volPhase->nElemConstraints(); std::string en; std::string enVP; /* * Loop through the elements in the vol phase object */ for (eVP = 0; eVP < neVP; eVP++) { foundPos = npos; enVP = volPhase->elementName(eVP); /* * Search for matches with the existing elements. * If found, then fill in the entry in the global * mapping array. */ for (e = 0; e < ne; e++) { en = ElName[e]; if (!strcmp(enVP.c_str(), en.c_str())) { volPhase->setElemGlobalIndex(eVP, e); foundPos = e; } } if (foundPos == npos) { int elType = volPhase->elementType(eVP); int elactive = volPhase->elementActive(eVP); e = addElement(enVP.c_str(), elType, elactive); volPhase->setElemGlobalIndex(eVP, e); } } } // This routine resizes the number of elements in the VCS_PROB object by // adding a new element to the end of the element list /* * The element name is added. Formula vector entries ang element * abundances for the new element are set to zero. * * Returns the index number of the new element. * * @param elNameNew New name of the element * @param elType Type of the element * @param elactive boolean indicating whether the element is active * * @return returns the index number of the new element */ size_t VCS_PROB::addElement(const char* elNameNew, int elType, int elactive) { if (!elNameNew) { plogf("error: element must have a name\n"); exit(EXIT_FAILURE); } size_t nel = ne + 1; resizeElements(nel, 1); ne = nel; ElName[ne-1] = elNameNew; m_elType[ne-1] = elType; ElActive[ne-1] = elactive; return (ne - 1); } // This routines adds entries for the formula matrix for one species /* * This routines adds entries for the formula matrix for this object * for one species * * This object also fills in the index filed, IndSpecies, within * the volPhase object. * * @param volPhase object containing the species * @param k Species number within the volPhase k * @param kT global Species number within this object * */ size_t VCS_PROB::addOnePhaseSpecies(vcs_VolPhase* volPhase, size_t k, size_t kT) { size_t e, eVP; if (kT > nspecies) { /* * Need to expand the number of species here */ plogf("Shouldn't be here\n"); exit(EXIT_FAILURE); } double const* const* const fm = volPhase->getFormulaMatrix(); for (eVP = 0; eVP < volPhase->nElemConstraints(); eVP++) { e = volPhase->elemGlobalIndex(eVP); #ifdef DEBUG_MODE if (e == npos) { exit(EXIT_FAILURE); } #endif FormulaMatrix[e][kT] = fm[eVP][k]; } /* * Tell the phase object about the current position of the * species within the global species vector */ volPhase->setSpGlobalIndexVCS(k, kT); return kT; } void VCS_PROB::reportCSV(const std::string& reportFile) { size_t k; size_t istart; double vol = 0.0; string sName; FILE* FP = fopen(reportFile.c_str(), "w"); if (!FP) { plogf("Failure to open file\n"); exit(EXIT_FAILURE); } double Temp = T; std::vector volPM(nspecies, 0.0); std::vector activity(nspecies, 0.0); std::vector ac(nspecies, 0.0); std::vector mu(nspecies, 0.0); std::vector mu0(nspecies, 0.0); std::vector molalities(nspecies, 0.0); vol = 0.0; size_t iK = 0; for (size_t iphase = 0; iphase < NPhase; iphase++) { istart = iK; vcs_VolPhase* volP = VPhaseList[iphase]; //const Cantera::ThermoPhase *tptr = volP->ptrThermoPhase(); size_t nSpeciesPhase = volP->nSpecies(); volPM.resize(nSpeciesPhase, 0.0); volP->sendToVCS_VolPM(VCS_DATA_PTR(volPM)); double TMolesPhase = volP->totalMoles(); double VolPhaseVolumes = 0.0; for (k = 0; k < nSpeciesPhase; k++) { iK++; VolPhaseVolumes += volPM[istart + k] * mf[istart + k]; } VolPhaseVolumes *= TMolesPhase; vol += VolPhaseVolumes; } fprintf(FP,"--------------------- VCS_MULTIPHASE_EQUIL FINAL REPORT" " -----------------------------\n"); fprintf(FP,"Temperature = %11.5g kelvin\n", Temp); fprintf(FP,"Pressure = %11.5g Pascal\n", PresPA); fprintf(FP,"Total Volume = %11.5g m**3\n", vol); fprintf(FP,"Number Basis optimizations = %d\n", m_NumBasisOptimizations); fprintf(FP,"Number VCS iterations = %d\n", m_Iterations); iK = 0; for (size_t iphase = 0; iphase < NPhase; iphase++) { istart = iK; vcs_VolPhase* volP = VPhaseList[iphase]; const Cantera::ThermoPhase* tp = volP->ptrThermoPhase(); string phaseName = volP->PhaseName; size_t nSpeciesPhase = volP->nSpecies(); volP->sendToVCS_VolPM(VCS_DATA_PTR(volPM)); double TMolesPhase = volP->totalMoles(); //AssertTrace(TMolesPhase == m_mix->phaseMoles(iphase)); activity.resize(nSpeciesPhase, 0.0); ac.resize(nSpeciesPhase, 0.0); mu0.resize(nSpeciesPhase, 0.0); mu.resize(nSpeciesPhase, 0.0); volPM.resize(nSpeciesPhase, 0.0); molalities.resize(nSpeciesPhase, 0.0); int actConvention = tp->activityConvention(); tp->getActivities(VCS_DATA_PTR(activity)); tp->getActivityCoefficients(VCS_DATA_PTR(ac)); tp->getStandardChemPotentials(VCS_DATA_PTR(mu0)); tp->getPartialMolarVolumes(VCS_DATA_PTR(volPM)); tp->getChemPotentials(VCS_DATA_PTR(mu)); double VolPhaseVolumes = 0.0; for (k = 0; k < nSpeciesPhase; k++) { VolPhaseVolumes += volPM[k] * mf[istart + k]; } VolPhaseVolumes *= TMolesPhase; vol += VolPhaseVolumes; if (actConvention == 1) { const Cantera::MolalityVPSSTP* mTP = static_cast(tp); tp->getChemPotentials(VCS_DATA_PTR(mu)); mTP->getMolalities(VCS_DATA_PTR(molalities)); tp->getChemPotentials(VCS_DATA_PTR(mu)); if (iphase == 0) { fprintf(FP," Name, Phase, PhaseMoles, Mole_Fract, " "Molalities, ActCoeff, Activity," "ChemPot_SS0, ChemPot, mole_num, PMVol, Phase_Volume\n"); fprintf(FP," , , (kmol), , " " , , ," " (J/kmol), (J/kmol), (kmol), (m**3/kmol), (m**3)\n"); } for (k = 0; k < nSpeciesPhase; k++) { sName = tp->speciesName(k); fprintf(FP,"%12s, %11s, %11.3e, %11.3e, %11.3e, %11.3e, %11.3e," "%11.3e, %11.3e, %11.3e, %11.3e, %11.3e\n", sName.c_str(), phaseName.c_str(), TMolesPhase, mf[istart + k], molalities[k], ac[k], activity[k], mu0[k]*1.0E-6, mu[k]*1.0E-6, mf[istart + k] * TMolesPhase, volPM[k], VolPhaseVolumes); } } else { if (iphase == 0) { fprintf(FP," Name, Phase, PhaseMoles, Mole_Fract, " "Molalities, ActCoeff, Activity," " ChemPotSS0, ChemPot, mole_num, PMVol, Phase_Volume\n"); fprintf(FP," , , (kmol), , " " , , ," " (J/kmol), (J/kmol), (kmol), (m**3/kmol), (m**3)\n"); } for (k = 0; k < nSpeciesPhase; k++) { molalities[k] = 0.0; } for (k = 0; k < nSpeciesPhase; k++) { sName = tp->speciesName(k); fprintf(FP,"%12s, %11s, %11.3e, %11.3e, %11.3e, %11.3e, %11.3e, " "%11.3e, %11.3e,% 11.3e, %11.3e, %11.3e\n", sName.c_str(), phaseName.c_str(), TMolesPhase, mf[istart + k], molalities[k], ac[k], activity[k], mu0[k]*1.0E-6, mu[k]*1.0E-6, mf[istart + k] * TMolesPhase, volPM[k], VolPhaseVolumes); } } #ifdef DEBUG_MODE /* * Check consistency: These should be equal */ tp->getChemPotentials(VCS_DATA_PTR(m_gibbsSpecies)+istart); for (k = 0; k < nSpeciesPhase; k++) { if (!vcs_doubleEqual(m_gibbsSpecies[istart+k], mu[k])) { fprintf(FP,"ERROR: incompatibility!\n"); fclose(FP); plogf("ERROR: incompatibility!\n"); exit(EXIT_FAILURE); } } #endif iK += nSpeciesPhase; } fclose(FP); } void VCS_PROB::setDebugPrintLvl(int lvl) { vcs_debug_print_lvl = lvl; } }