19vcs_VolPhase::vcs_VolPhase(VCS_SOLVE* owningSolverObject,
20 ThermoPhase* thermoPhase,
size_t phaseNum)
21 : m_owningSolverObject(owningSolverObject)
23 if (!m_owningSolverObject) {
24 throw CanteraError(
"vcs_VolPhase::vcs_VolPhase",
25 "owningSolverObject must not be null");
28 throw CanteraError(
"vcs_VolPhase::vcs_VolPhase",
29 "thermoPhase must not be null");
33 PhaseName = thermoPhase->name().empty() ?
34 fmt::format(
"Phase_{}", VP_ID_) : thermoPhase->name();
36 m_numSpecies = thermoPhase->nSpecies();
39 m_singleSpecies = (m_numSpecies == 1);
42 p_activityConvention = thermoPhase->activityConvention();
44 IndSpecies.assign(m_numSpecies, npos);
45 Xmol_.assign(m_numSpecies, 0.0);
46 creationMoleNumbers_.assign(m_numSpecies, 0.0);
47 creationGlobalRxnNumbers_.assign(m_numSpecies, npos);
48 for (
size_t k = 0; k < m_numSpecies; k++) {
49 double defaultFrac = 1.0 / m_numSpecies;
50 Xmol_[k] = defaultFrac;
51 creationMoleNumbers_[k] = defaultFrac;
54 SS0ChemicalPotential.assign(m_numSpecies, -1.0);
55 StarChemicalPotential.assign(m_numSpecies, -1.0);
56 StarMolarVol.assign(m_numSpecies, -1.0);
57 PartialMolarVol.assign(m_numSpecies, -1.0);
58 ActCoeff.assign(m_numSpecies, 1.0);
59 np_dLnActCoeffdMolNumber.resize(m_numSpecies, m_numSpecies, 0.0);
64 m_UpToDate_AC =
false;
65 m_UpToDate_VolStar =
false;
66 m_UpToDate_VolPM =
false;
67 m_UpToDate_GStar =
false;
68 m_UpToDate_G0 =
false;
71 Temp_ = TP_ptr->temperature();
72 Pres_ = TP_ptr->pressure();
73 m_phi = TP_ptr->electricPotential();
75 transferElementsFM(TP_ptr);
79 size_t neVP = nElemConstraints();
80 for (
size_t eVP = 0; eVP < neVP; eVP++) {
81 string enVP = elementName(eVP);
82 size_t foundPos = m_owningSolverObject->elementIndex(enVP);
83 if (foundPos == npos) {
84 int elType = elementType(eVP);
85 int elactive = elementActive(eVP);
86 foundPos = m_owningSolverObject->addElement(enVP.c_str(), elType, elactive);
88 setElemGlobalIndex(eVP, foundPos);
91 setState_TP(Temp_, Pres_);
92 TP_ptr->getMoleFractions(Xmol_);
93 creationMoleNumbers_ = Xmol_;
94 _updateMoleFractionDependencies();
96 m_isIdealSoln = m_singleSpecies ? true : TP_ptr->isIdeal();
99void vcs_VolPhase::elemResize(
const size_t numElemConstraints)
101 m_elementNames.resize(numElemConstraints);
102 m_elementActive.resize(numElemConstraints+1, 1);
104 m_formulaMatrix.resize(m_numSpecies, numElemConstraints, 0.0);
105 m_elementNames.resize(numElemConstraints,
"");
106 m_elemGlobalIndex.resize(numElemConstraints, npos);
107 m_numElemConstraints = numElemConstraints;
110void vcs_VolPhase::_updateActCoeff()
const
113 m_UpToDate_AC =
true;
116 TP_ptr->getActivityCoefficients(ActCoeff);
117 m_UpToDate_AC =
true;
120void vcs_VolPhase::_updateG0()
const
122 TP_ptr->getGibbs_ref(SS0ChemicalPotential);
123 m_UpToDate_G0 =
true;
126double vcs_VolPhase::G0_calc_one(
size_t kspec)
const
128 if (!m_UpToDate_G0) {
131 return SS0ChemicalPotential[kspec];
134void vcs_VolPhase::_updateGStar()
const
136 TP_ptr->getStandardChemPotentials(StarChemicalPotential);
137 m_UpToDate_GStar =
true;
140double vcs_VolPhase::GStar_calc_one(
size_t kspec)
const
142 if (!m_UpToDate_GStar) {
145 return StarChemicalPotential[kspec];
148void vcs_VolPhase::setMoleFractions(span<const double> xmol)
150 checkArraySize(
"vcs_VolPhase::setMoleFractions", xmol.size(), m_numSpecies);
152 for (
size_t k = 0; k < m_numSpecies; k++) {
156 if (std::fabs(sum) > 1.0E-13) {
157 for (
size_t k = 0; k < m_numSpecies; k++) {
161 _updateMoleFractionDependencies();
166void vcs_VolPhase::_updateMoleFractionDependencies()
169 TP_ptr->setMoleFractions(span<const double>(
170 &Xmol_[m_MFStartIndex], m_numSpecies));
171 TP_ptr->setPressure(Pres_);
173 if (!m_isIdealSoln) {
174 m_UpToDate_AC =
false;
175 m_UpToDate_VolPM =
false;
179span<const double> vcs_VolPhase::moleFractions()
const
184double vcs_VolPhase::moleFraction(
size_t k)
const
189void vcs_VolPhase::setMoleFractionsState(
const double totalMoles,
190 span<const double> moleFractions,
const int vcsStateStatus)
192 checkArraySize(
"vcs_VolPhase::setMoleFractionsState", moleFractions.size(),
194 if (totalMoles != 0.0) {
198 throw CanteraError(
"vcs_VolPhase::setMolesFractionsState",
199 "inappropriate usage");
204 throw CanteraError(
"vcs_VolPhase::setMolesFractionsState",
205 "inappropriate usage");
210 m_vcsStateStatus = vcsStateStatus;
213 double fractotal = 1.0;
214 v_totalMoles = totalMoles;
216 for (
size_t k = 0; k < m_numSpecies; k++) {
217 Xmol_[k] = moleFractions[k];
218 sum += moleFractions[k];
221 throw CanteraError(
"vcs_VolPhase::setMolesFractionsState",
222 "inappropriate usage");
224 if (sum != fractotal) {
225 for (
size_t k = 0; k < m_numSpecies; k++) {
226 Xmol_[k] *= (fractotal /sum);
229 _updateMoleFractionDependencies();
232void vcs_VolPhase::setMolesFromVCS(
const int stateCalc,
233 span<const double> molesSpeciesVCS)
237 if (molesSpeciesVCS.empty()) {
239 molesSpeciesVCS = span<const double>(m_owningSolverObject->m_molNumSpecies_old);
241 molesSpeciesVCS = span<const double>(m_owningSolverObject->m_molNumSpecies_new);
243 throw CanteraError(
"vcs_VolPhase::setMolesFromVCS",
"shouldn't be here");
247 for (
size_t k = 0; k < m_numSpecies; k++) {
249 size_t kglob = IndSpecies[k];
250 v_totalMoles += std::max(0.0, molesSpeciesVCS[kglob]);
253 if (v_totalMoles > 0.0) {
254 for (
size_t k = 0; k < m_numSpecies; k++) {
256 size_t kglob = IndSpecies[k];
257 double tmp = std::max(0.0, molesSpeciesVCS[kglob]);
258 Xmol_[k] = tmp / v_totalMoles;
271 if (m_phiVarIndex !=
npos) {
272 size_t kglob = IndSpecies[m_phiVarIndex];
273 if (m_numSpecies == 1) {
274 Xmol_[m_phiVarIndex] = 1.0;
276 Xmol_[m_phiVarIndex] = 0.0;
278 double phi = molesSpeciesVCS[kglob];
279 setElectricPotential(phi);
280 if (m_numSpecies == 1) {
284 _updateMoleFractionDependencies();
290 creationMoleNumbers_ = Xmol_;
295 m_vcsStateStatus = stateCalc;
298void vcs_VolPhase::setMolesFromVCSCheck(
const int vcsStateStatus,
299 span<const double> molesSpeciesVCS,
300 span<const double> TPhMoles)
302 setMolesFromVCS(vcsStateStatus, molesSpeciesVCS);
305 double Tcheck = TPhMoles[VP_ID_];
306 if (Tcheck != v_totalMoles) {
308 Tcheck = v_totalMoles;
310 throw CanteraError(
"vcs_VolPhase::setMolesFromVCSCheck",
311 "We have a consistency problem: {} {}", Tcheck, v_totalMoles);
316void vcs_VolPhase::updateFromVCS_MoleNumbers(
const int vcsStateStatus)
318 if ((!m_UpToDate || vcsStateStatus != m_vcsStateStatus) &&
320 setMolesFromVCS(vcsStateStatus);
324void vcs_VolPhase::sendToVCS_ActCoeff(
const int vcsStateStatus, span<double> AC)
326 updateFromVCS_MoleNumbers(vcsStateStatus);
327 if (!m_UpToDate_AC) {
330 for (
size_t k = 0; k < m_numSpecies; k++) {
331 size_t kglob = IndSpecies[k];
332 AC[kglob] = ActCoeff[k];
336double vcs_VolPhase::sendToVCS_VolPM(span<double> VolPM)
const
338 if (!m_UpToDate_VolPM) {
341 for (
size_t k = 0; k < m_numSpecies; k++) {
342 size_t kglob = IndSpecies[k];
343 VolPM[kglob] = PartialMolarVol[k];
348void vcs_VolPhase::sendToVCS_GStar(span<double> gstar)
const
350 if (!m_UpToDate_GStar) {
353 for (
size_t k = 0; k < m_numSpecies; k++) {
354 size_t kglob = IndSpecies[k];
355 gstar[kglob] = StarChemicalPotential[k];
359void vcs_VolPhase::setElectricPotential(
const double phi)
362 TP_ptr->setElectricPotential(m_phi);
364 m_UpToDate_AC =
false;
365 m_UpToDate_VolStar =
false;
366 m_UpToDate_VolPM =
false;
367 m_UpToDate_GStar =
false;
370double vcs_VolPhase::electricPotential()
const
375void vcs_VolPhase::setState_TP(
const double temp,
const double pres)
377 if (Temp_ == temp && Pres_ == pres) {
380 TP_ptr->setElectricPotential(m_phi);
381 TP_ptr->setState_TP(temp, pres);
384 m_UpToDate_AC =
false;
385 m_UpToDate_VolStar =
false;
386 m_UpToDate_VolPM =
false;
387 m_UpToDate_GStar =
false;
388 m_UpToDate_G0 =
false;
391void vcs_VolPhase::_updateVolStar()
const
393 TP_ptr->getStandardVolumes(StarMolarVol);
394 m_UpToDate_VolStar =
true;
397double vcs_VolPhase::_updateVolPM()
const
399 TP_ptr->getPartialMolarVolumes(PartialMolarVol);
401 for (
size_t k = 0; k < m_numSpecies; k++) {
402 m_totalVol += PartialMolarVol[k] * Xmol_[k];
404 m_totalVol *= v_totalMoles;
405 m_UpToDate_VolPM =
true;
409void vcs_VolPhase::_updateLnActCoeffJac()
411 double phaseTotalMoles = v_totalMoles;
412 if (phaseTotalMoles < 1.0E-14) {
413 phaseTotalMoles = 1.0;
417 if (!m_UpToDate_AC) {
423 TP_ptr->getdlnActCoeffdlnN(m_numSpecies,
424 span<double>(&np_dLnActCoeffdMolNumber(0, 0), m_numSpecies * m_numSpecies));
425 for (
size_t j = 0; j < m_numSpecies; j++) {
426 double moles_j_base = phaseTotalMoles * Xmol_[j];
427 auto np_lnActCoeffCol = np_dLnActCoeffdMolNumber.col(j);
428 if (moles_j_base < 1.0E-200) {
429 moles_j_base = 1.0E-7 * moles_j_base + 1.0E-13 * phaseTotalMoles + 1.0E-150;
431 for (
size_t k = 0; k < m_numSpecies; k++) {
432 np_lnActCoeffCol[k] = np_lnActCoeffCol[k] * phaseTotalMoles / moles_j_base;
436 double deltaMoles_j = 0.0;
438 vector<double> ActCoeff_Base(ActCoeff);
439 vector<double> Xmol_Base(Xmol_);
440 double TMoles_base = phaseTotalMoles;
443 for (
size_t j = 0; j < m_numSpecies; j++) {
446 double moles_j_base = phaseTotalMoles * Xmol_Base[j];
447 deltaMoles_j = 1.0E-7 * moles_j_base + 1.0E-13 * phaseTotalMoles + 1.0E-150;
451 phaseTotalMoles = TMoles_base + deltaMoles_j;
452 for (
size_t k = 0; k < m_numSpecies; k++) {
453 Xmol_[k] = Xmol_Base[k] * TMoles_base / phaseTotalMoles;
455 Xmol_[j] = (moles_j_base + deltaMoles_j) / phaseTotalMoles;
459 _updateMoleFractionDependencies();
463 v_totalMoles = TMoles_base;
470 setMoleFractions(Xmol_Base);
471 _updateMoleFractionDependencies();
475void vcs_VolPhase::sendToVCS_LnActCoeffJac(
Array2D& np_LnACJac_VCS)
479 _updateLnActCoeffJac();
482 for (
size_t j = 0; j < m_numSpecies; j++) {
483 size_t jglob = IndSpecies[j];
484 for (
size_t k = 0; k < m_numSpecies; k++) {
485 size_t kglob = IndSpecies[k];
486 np_LnACJac_VCS(kglob,jglob) = np_dLnActCoeffdMolNumber(k,j);
491double vcs_VolPhase::totalMoles()
const
496double vcs_VolPhase::molefraction(
size_t k)
const
501void vcs_VolPhase::setCreationMoleNumbers(span<const double> n_k,
502 span<const size_t> creationGlobalRxnNumbers)
504 checkArraySize(
"vcs_VolPhase::setCreationMoleNumbers", n_k.size(), m_numSpecies);
505 checkArraySize(
"vcs_VolPhase::setCreationMoleNumbers", creationGlobalRxnNumbers.size(), m_numSpecies);
506 creationMoleNumbers_.assign(n_k.begin(), n_k.end());
507 for (
size_t k = 0; k < m_numSpecies; k++) {
508 creationGlobalRxnNumbers_[k] = creationGlobalRxnNumbers[k];
512span<const double> vcs_VolPhase::creationMoleNumbers(
513 span<size_t> creationGlobalRxnNumbers)
const
515 checkArraySize(
"vcs_VolPhase::creationMoleNumbers", creationGlobalRxnNumbers.size(), m_numSpecies);
516 copy(creationGlobalRxnNumbers_.begin(), creationGlobalRxnNumbers_.end(),
517 creationGlobalRxnNumbers.begin());
518 return creationMoleNumbers_;
521void vcs_VolPhase::setTotalMoles(
const double totalMols)
523 v_totalMoles = totalMols;
524 if (m_singleSpecies && (m_phiVarIndex == 0)) {
527 if (totalMols > 0.0) {
535void vcs_VolPhase::setMolesOutOfDate(
int stateCalc)
538 if (stateCalc != -1) {
539 m_vcsStateStatus = stateCalc;
543void vcs_VolPhase::setMolesCurrent(
int stateCalc)
546 m_vcsStateStatus = stateCalc;
549bool vcs_VolPhase::isIdealSoln()
const
551 return m_isIdealSoln;
554size_t vcs_VolPhase::phiVarIndex()
const
556 return m_phiVarIndex;
559void vcs_VolPhase::setPhiVarIndex(
size_t phiVarIndex)
561 m_phiVarIndex = phiVarIndex;
563 if (m_singleSpecies && m_phiVarIndex == 0) {
568int vcs_VolPhase::exists()
const
573void vcs_VolPhase::setExistence(
const int existence)
576 if (v_totalMoles != 0.0) {
578 "setting false existence for phase with moles");
580 }
else if (v_totalMoles == 0.0 && (!m_singleSpecies || m_phiVarIndex != 0)) {
582 "setting true existence for phase with no moles");
586 "Trying to set existence of an electron phase to false");
588 m_existence = existence;
591size_t vcs_VolPhase::spGlobalIndexVCS(
const size_t spIndex)
const
593 return IndSpecies[spIndex];
596void vcs_VolPhase::setSpGlobalIndexVCS(
const size_t spIndex,
597 const size_t spGlobalIndex)
599 IndSpecies[spIndex] = spGlobalIndex;
600 if (spGlobalIndex >= m_numElemConstraints) {
601 creationGlobalRxnNumbers_[spIndex] = spGlobalIndex - m_numElemConstraints;
605size_t vcs_VolPhase::elemGlobalIndex(
const size_t e)
const
607 AssertThrow(e < m_numElemConstraints,
" vcs_VolPhase::elemGlobalIndex");
608 return m_elemGlobalIndex[e];
611void vcs_VolPhase::setElemGlobalIndex(
const size_t eLocal,
const size_t eGlobal)
614 "vcs_VolPhase::setElemGlobalIndex");
615 m_elemGlobalIndex[eLocal] = eGlobal;
618size_t vcs_VolPhase::nElemConstraints()
const
620 return m_numElemConstraints;
623string vcs_VolPhase::elementName(
size_t e)
const
626 if (e < m_elementNames.size()) {
627 return m_elementNames[e];
629 throw IndexError(
"vcs_VolPhase::elementName",
"element", e, m_elementNames.size());
635 for (
size_t k = 0; k < tPhase->
nSpecies(); k++) {
636 if (tPhase->
charge(k) != 0.0) {
656size_t vcs_VolPhase::transferElementsFM(
const ThermoPhase*
const tPhase)
666 ChargeNeutralityElement = ne;
673 if (ChargeNeutralityElement !=
npos) {
677 size_t eFound =
npos;
686 for (
size_t eT = 0; eT < nebase; eT++) {
689 m_elementActive[eT] = 0;
694 for (
size_t eT = 0; eT < nebase; eT++) {
701 if (eFound ==
npos) {
704 m_elementActive[ne] = 0;
706 m_elementNames[ne] = ename;
712 m_formulaMatrix.resize(ns, ne, 0.0);
717 for (
size_t eT = 0; eT < nebase; eT++) {
724 string pname = tPhase->
name();
726 pname = fmt::format(
"phase{}", VP_ID_);
728 e = ChargeNeutralityElement;
729 m_elementNames[e] =
"cn_" + pname;
732 for (
size_t k = 0; k < ns; k++) {
734 for (
size_t eT = 0; eT < nebase; eT++) {
735 m_formulaMatrix(k,e) = tPhase->
nAtoms(k, eT);
738 if (eFound !=
npos) {
739 m_formulaMatrix(k,eFound) = - tPhase->
charge(k);
744 for (
size_t k = 0; k < ns; k++) {
745 m_formulaMatrix(k,ChargeNeutralityElement) = tPhase->
charge(k);
752 if (ns == 1 && tPhase->
charge(0) != 0.0) {
760int vcs_VolPhase::elementType(
const size_t e)
const
762 return m_elementType[e];
765const Array2D& vcs_VolPhase::getFormulaMatrix()
const
767 return m_formulaMatrix;
770int vcs_VolPhase::speciesUnknownType(
const size_t k)
const
772 return m_speciesUnknownType[k];
775int vcs_VolPhase::elementActive(
const size_t e)
const
777 return m_elementActive[e];
780size_t vcs_VolPhase::nSpecies()
const
785string vcs_VolPhase::eos_name()
const
787 return TP_ptr->type();
Header file for class ThermoPhase, the base class for phases with thermodynamic properties,...
A class for 2D arrays stored in column-major (Fortran-compatible) form.
Base class for exceptions thrown by Cantera classes.
An array index is out of range.
size_t nSpecies() const
Returns the number of species in the phase.
int elementType(size_t m) const
Return the element constraint type Possible types include:
double nAtoms(size_t k, size_t m) const
Number of atoms of element m in species k.
size_t nElements() const
Number of elements.
string elementName(size_t m) const
Name of the element with index m.
double charge(size_t k) const
Dimensionless electrical charge of a single molecule of species k The charge is normalized by the the...
string name() const
Return the name of the phase.
Base class for a phase with thermodynamic properties.
bool chargeNeutralityNecessary() const
Returns the chargeNeutralityNecessity boolean.
#define AssertThrow(expr, procedure)
Assertion must be true or an error is thrown.
#define AssertThrowMsg(expr, procedure,...)
Assertion must be true or an error is thrown.
Namespace for the Cantera kernel.
const size_t npos
index returned by functions to indicate "no position"
static bool chargeNeutralityElement(const ThermoPhase *const tPhase)
This utility routine decides whether a Cantera ThermoPhase needs a constraint equation representing t...
static bool hasChargedSpecies(const ThermoPhase *const tPhase)
This function decides whether a phase has charged species or not.
bool vcs_doubleEqual(double d1, double d2)
Simple routine to check whether two doubles are equal up to roundoff error.
void checkArraySize(const char *procedure, size_t available, size_t required)
Wrapper for throwing ArraySizeError.
Contains declarations for string manipulation functions within Cantera.
Header for the object representing each phase within vcs.
#define VCS_SPECIES_TYPE_INTERFACIALVOLTAGE
Unknown refers to the voltage level of a phase.
#define VCS_STATECALC_OLD
State Calculation based on the old or base mole numbers.
#define VCS_STATECALC_TMP
State Calculation based on a temporary set of mole numbers.
#define VCS_PHASE_EXIST_NO
Phase doesn't currently exist in the mixture.
#define VCS_PHASE_EXIST_ALWAYS
These defines are valid values for the phase existence flag.
#define VCS_ELEM_TYPE_ABSPOS
Normal element constraint consisting of positive coefficients for the formula matrix.
#define VCS_STATECALC_NEW
State Calculation based on the new or tentative mole numbers.
#define VCS_SPECIES_TYPE_MOLNUM
Unknown refers to mole number of a single species.
#define VCS_ELEM_TYPE_ELECTRONCHARGE
This refers to conservation of electrons.
#define VCS_ELEM_TYPE_CHARGENEUTRALITY
This refers to a charge neutrality of a single phase.
#define VCS_PHASE_EXIST_YES
Phase is a normal phase that currently exists.
#define VCS_PHASE_EXIST_ZEROEDPHASE
Phase currently is zeroed due to a programmatic issue.
Header file for the internal object that holds the vcs equilibrium problem (see Class VCS_SOLVE and C...