Moved the external libraries to separate library files so that libcantera.a just contains its own namespace externals. Fixed several errors in the equilibrium program that occurred during the port. (int to size_t issues). Moved some equilibrium program headers to the include file system, so that it can link with equilibrium program. Worked on Cantera.mak. Needs more work. Fixed an issue with the Residual virtual base classes within numerics. They didn't inherit due to int to size_t migration. This caused numerous test problems to fail (issue with backwards compatibility - do we want it and how much do we want?). Added csvdiff back so that it's available for shell environment runtests.
951 lines
36 KiB
C++
951 lines
36 KiB
C++
/**
|
|
* @file vcs_phaseStability.cpp
|
|
* Implementation class for functions associated with determining the stability of a phase
|
|
* (see Class \link Cantera::VCS_SOLVE VCS_SOLVE\endlink and \ref equilfunctions ).
|
|
*/
|
|
#include "cantera/equil/vcs_solve.h"
|
|
#include "cantera/equil/vcs_internal.h"
|
|
#include "vcs_species_thermo.h"
|
|
#include "cantera/equil/vcs_VolPhase.h"
|
|
#include "vcs_Exception.h"
|
|
|
|
#include <cstdio>
|
|
#include <cstdlib>
|
|
#include <cmath>
|
|
#include <vector>
|
|
#include <cstring>
|
|
#include <algorithm>
|
|
|
|
using namespace std;
|
|
|
|
namespace VCSnonideal
|
|
{
|
|
|
|
//====================================================================================================================
|
|
// Utility function that evaluates whether a phase can be popped into existence
|
|
/*
|
|
* A phase can be popped iff the stoichiometric coefficients for the
|
|
* component species, whose concentrations will be lowered during the
|
|
* process, are positive by at least a small degree.
|
|
*
|
|
* If one of the phase species is a zeroed component, then the phase can
|
|
* be popped if the component increases in mole number as the phase moles
|
|
* are increased.
|
|
*
|
|
* @param iphasePop id of the phase, which is currently zeroed,
|
|
*
|
|
* @return Returns true if the phase can come into existence
|
|
* and false otherwise.
|
|
*/
|
|
bool VCS_SOLVE::vcs_popPhasePossible(const size_t iphasePop) const
|
|
{
|
|
|
|
vcs_VolPhase* Vphase = m_VolPhaseList[iphasePop];
|
|
|
|
#ifdef DEBUG_MODE
|
|
int existence = Vphase->exists();
|
|
if (existence > 0) {
|
|
printf("ERROR vcs_popPhasePossible called for a phase that exists!");
|
|
std::exit(-1);
|
|
}
|
|
#endif
|
|
|
|
/*
|
|
* Loop through all of the species in the phase. We say the phase
|
|
* can be popped, if there is one species in the phase that can be
|
|
* popped.
|
|
*/
|
|
for (size_t k = 0; k < Vphase->nSpecies(); k++) {
|
|
size_t kspec = Vphase->spGlobalIndexVCS(k);
|
|
#ifdef DEBUG_MODE
|
|
if (m_molNumSpecies_old[kspec] > 0.0) {
|
|
printf("ERROR vcs_popPhasePossible we shouldn't be here %lu %g > 0.0",
|
|
kspec, m_molNumSpecies_old[kspec]);
|
|
exit(-1);
|
|
}
|
|
#endif
|
|
size_t irxn = kspec - m_numComponents;
|
|
if (kspec >= m_numComponents) {
|
|
bool iPopPossible = true;
|
|
for (size_t j = 0; j < m_numComponents; ++j) {
|
|
if (m_elType[j] == VCS_ELEM_TYPE_ABSPOS) {
|
|
double stoicC = m_stoichCoeffRxnMatrix[irxn][j];
|
|
if (stoicC != 0.0) {
|
|
double negChangeComp = - stoicC * 1.0;
|
|
if (negChangeComp > 0.0) {
|
|
// TODO: We may have to come up with a tolerance here
|
|
if (m_molNumSpecies_old[j] <= VCS_DELETE_ELEMENTABS_CUTOFF*0.5) {
|
|
iPopPossible = false;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (iPopPossible) {
|
|
return true;
|
|
}
|
|
} else {
|
|
/*
|
|
* We are here when the species in the phase is a component. Its mole number is zero.
|
|
* We loop through the regular reaction looking for a reaction that can pop the
|
|
* component.
|
|
*/
|
|
//printf("WE are here at new logic - CHECK\n");
|
|
for (size_t jrxn = 0; jrxn < m_numRxnRdc; jrxn++) {
|
|
bool foundJrxn = false;
|
|
// First, if the component is a product of the reaction
|
|
if (m_stoichCoeffRxnMatrix[jrxn][kspec] > 0.0) {
|
|
foundJrxn = true;
|
|
for (size_t kcomp = 0; kcomp < m_numComponents; kcomp++) {
|
|
if (m_stoichCoeffRxnMatrix[jrxn][kcomp] < 0.0) {
|
|
if (m_molNumSpecies_old[kcomp] <= VCS_DELETE_ELEMENTABS_CUTOFF*0.5) {
|
|
foundJrxn = false;
|
|
}
|
|
}
|
|
}
|
|
if (foundJrxn) {
|
|
//printf("We have found a component phase pop! CHECK1 \n");
|
|
return true;
|
|
}
|
|
}
|
|
// Second we are here if the component is a reactant in the reaction, and the reaction goes backwards.
|
|
else if (m_stoichCoeffRxnMatrix[jrxn][kspec] < 0.0) {
|
|
foundJrxn = true;
|
|
size_t jspec = jrxn + m_numComponents;
|
|
if (m_molNumSpecies_old[jspec] <= VCS_DELETE_ELEMENTABS_CUTOFF*0.5) {
|
|
foundJrxn = false;
|
|
continue;
|
|
}
|
|
for (size_t kcomp = 0; kcomp < m_numComponents; kcomp++) {
|
|
if (m_stoichCoeffRxnMatrix[jrxn][kcomp] > 0.0) {
|
|
if (m_molNumSpecies_old[kcomp] <= VCS_DELETE_ELEMENTABS_CUTOFF*0.5) {
|
|
foundJrxn = false;
|
|
}
|
|
}
|
|
}
|
|
if (foundJrxn) {
|
|
//printf("We have found a component phase pop! CHECK2 \n");
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
//====================================================================================================================
|
|
// Determine the list of problems that need to be checked to see if there are any phases pops
|
|
/*
|
|
* This routine evaluates and fills in the following quantities
|
|
* phasePopProblemLists_
|
|
*
|
|
* Need to work in species that are zeroed by element constraints
|
|
*
|
|
* @return Returns the number of problems that must be checked.
|
|
*/
|
|
int VCS_SOLVE::vcs_phasePopDeterminePossibleList()
|
|
{
|
|
int nfound = 0;
|
|
vcs_VolPhase* Vphase = 0;
|
|
double stoicC;
|
|
double molComp;
|
|
std::vector<int> linkedPhases;
|
|
phasePopProblemLists_.clear();
|
|
|
|
/*
|
|
* This is a vector over each component.
|
|
* For zeroed components it lists the phases, which are currently zeroed,
|
|
* which have a species with a positive stoichiometric value wrt the component.
|
|
* Therefore, we could pop the component species and pop that phase at the same time
|
|
* if we considered no other factors than keeping the component mole number positve.
|
|
*
|
|
* It does not count species with positive stoichiometric values if that species
|
|
* already has a positive mole number. The phase is already popped.
|
|
*/
|
|
std::vector< std::vector<size_t> > zeroedComponentLinkedPhasePops(m_numComponents);
|
|
/*
|
|
* The logic below calculates zeroedComponentLinkedPhasePops
|
|
*/
|
|
for (size_t j = 0; j < m_numComponents; j++) {
|
|
if (m_elType[j] == VCS_ELEM_TYPE_ABSPOS) {
|
|
molComp = m_molNumSpecies_old[j];
|
|
if (molComp <= 0.0) {
|
|
std::vector<size_t> &jList = zeroedComponentLinkedPhasePops[j];
|
|
size_t iph = m_phaseID[j];
|
|
jList.push_back(iph);
|
|
for (size_t irxn = 0; irxn < m_numRxnTot; irxn++) {
|
|
size_t kspec = irxn + m_numComponents;
|
|
iph = m_phaseID[kspec];
|
|
Vphase = m_VolPhaseList[iph];
|
|
int existence = Vphase->exists();
|
|
if (existence < 0) {
|
|
stoicC = m_stoichCoeffRxnMatrix[irxn][j];
|
|
if (stoicC > 0.0) {
|
|
if (std::find(jList.begin(), jList.end(), iph) != jList.end()) {
|
|
jList.push_back(iph);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
/*
|
|
* This is a vector over each zeroed phase
|
|
* For zeroed phases, it lists the components, which are currently zereoed,
|
|
* which have a species with a negative stoichiometric value wrt one or more species in the phase.
|
|
* Cut out components which have a pos stoichiometric value with another species in the phase.
|
|
*/
|
|
std::vector< std::vector<size_t> > zeroedPhaseLinkedZeroComponents(m_numPhases);
|
|
/*
|
|
* The logic below calculates zeroedPhaseLinkedZeroComponents
|
|
*/
|
|
for (size_t iph = 0; iph < m_numPhases; iph++) {
|
|
std::vector<size_t> &iphList = zeroedPhaseLinkedZeroComponents[iph];
|
|
iphList.clear();
|
|
Vphase = m_VolPhaseList[iph];
|
|
int existence = Vphase->exists();
|
|
if (existence < 0) {
|
|
|
|
linkedPhases.clear();
|
|
size_t nsp = Vphase->nSpecies();
|
|
for (size_t k = 0; k < nsp; k++) {
|
|
size_t kspec = Vphase->spGlobalIndexVCS(k);
|
|
size_t irxn = kspec - m_numComponents;
|
|
|
|
for (size_t j = 0; j < m_numComponents; j++) {
|
|
if (m_elType[j] == VCS_ELEM_TYPE_ABSPOS) {
|
|
molComp = m_molNumSpecies_old[j];
|
|
if (molComp <= 0.0) {
|
|
stoicC = m_stoichCoeffRxnMatrix[irxn][j];
|
|
if (stoicC < 0.0) {
|
|
bool foundPos = false;
|
|
for (size_t kk = 0; kk < nsp; kk++) {
|
|
size_t kkspec = Vphase->spGlobalIndexVCS(kk);
|
|
if (kkspec >= m_numComponents) {
|
|
size_t iirxn = kkspec - m_numComponents;
|
|
if (m_stoichCoeffRxnMatrix[iirxn][j] > 0.0) {
|
|
foundPos = true;
|
|
}
|
|
}
|
|
}
|
|
if (!foundPos) {
|
|
if (std::find(iphList.begin(), iphList.end(), j) != iphList.end()) {
|
|
iphList.push_back(j);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Now fill in the phasePopProblemLists_ list.
|
|
*
|
|
*/
|
|
for (size_t iph = 0; iph < m_numPhases; iph++) {
|
|
Vphase = m_VolPhaseList[iph];
|
|
int existence = Vphase->exists();
|
|
if (existence < 0) {
|
|
std::vector<size_t> &iphList = zeroedPhaseLinkedZeroComponents[iph];
|
|
std::vector<size_t> popProblem(0);
|
|
popProblem.push_back(iph);
|
|
for (size_t i = 0; i < iphList.size(); i++) {
|
|
size_t j = iphList[i];
|
|
std::vector<size_t> &jList = zeroedComponentLinkedPhasePops[j];
|
|
for (size_t jjl = 0; jjl < jList.size(); jjl++) {
|
|
size_t jph = jList[jjl];
|
|
if (std::find(popProblem.begin(), popProblem.end(), jph) != popProblem.end()) {
|
|
popProblem.push_back(jph);
|
|
}
|
|
}
|
|
}
|
|
phasePopProblemLists_.push_back(popProblem);
|
|
}
|
|
}
|
|
|
|
return nfound;
|
|
}
|
|
|
|
|
|
//====================================================================================================================
|
|
// Decision as to whether a phase pops back into existence
|
|
/*
|
|
* @return returns the phase id of the phases that pops back into
|
|
* existence. Returns npos if there are no phases
|
|
*/
|
|
size_t VCS_SOLVE::vcs_popPhaseID(std::vector<size_t> & phasePopPhaseIDs)
|
|
{
|
|
size_t iphasePop = npos;
|
|
size_t irxn, kspec;
|
|
doublereal FephaseMax = -1.0E30;
|
|
doublereal Fephase = -1.0E30;
|
|
vcs_VolPhase* Vphase = 0;
|
|
|
|
|
|
#ifdef DEBUG_MODE
|
|
char anote[128];
|
|
if (m_debug_print_lvl >= 2) {
|
|
plogf(" --- vcs_popPhaseID() called\n");
|
|
plogf(" --- Phase Status F_e MoleNum\n");
|
|
plogf(" --------------------------------------------------------------------------\n");
|
|
}
|
|
#endif
|
|
for (size_t iph = 0; iph < m_numPhases; iph++) {
|
|
Vphase = m_VolPhaseList[iph];
|
|
int existence = Vphase->exists();
|
|
#ifdef DEBUG_MODE
|
|
strcpy(anote, "");
|
|
#endif
|
|
if (existence > 0) {
|
|
|
|
#ifdef DEBUG_MODE
|
|
if (m_debug_print_lvl >= 2) {
|
|
plogf(" --- %18s %5d NA %11.3e\n",
|
|
Vphase->PhaseName.c_str(),
|
|
existence,
|
|
m_tPhaseMoles_old[iph]);
|
|
}
|
|
#endif
|
|
} else {
|
|
if (Vphase->m_singleSpecies) {
|
|
/***********************************************************************
|
|
*
|
|
* Single Phase Stability Resolution
|
|
*
|
|
***********************************************************************/
|
|
kspec = Vphase->spGlobalIndexVCS(0);
|
|
irxn = kspec - m_numComponents;
|
|
doublereal deltaGRxn = m_deltaGRxn_old[irxn];
|
|
Fephase = exp(-deltaGRxn) - 1.0;
|
|
if (Fephase > 0.0) {
|
|
#ifdef DEBUG_MODE
|
|
strcpy(anote," (ready to be birthed)");
|
|
#endif
|
|
if (Fephase > FephaseMax) {
|
|
iphasePop = iph;
|
|
FephaseMax = Fephase;
|
|
#ifdef DEBUG_MODE
|
|
strcpy(anote," (chosen to be birthed)");
|
|
#endif
|
|
}
|
|
}
|
|
#ifdef DEBUG_MODE
|
|
if (Fephase < 0.0) {
|
|
strcpy(anote," (not stable)");
|
|
if (m_tPhaseMoles_old[iph] > 0.0) {
|
|
printf("shouldn't be here\n");
|
|
exit(-1);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
#ifdef DEBUG_MODE
|
|
if (m_debug_print_lvl >= 2) {
|
|
plogf(" --- %18s %5d %10.3g %10.3g %s\n",
|
|
Vphase->PhaseName.c_str(),
|
|
existence, Fephase,
|
|
m_tPhaseMoles_old[iph], anote);
|
|
}
|
|
#endif
|
|
|
|
} else {
|
|
/***********************************************************************
|
|
*
|
|
* MultiSpecies Phase Stability Resolution
|
|
*
|
|
***********************************************************************/
|
|
if (vcs_popPhasePossible(iph)) {
|
|
Fephase = vcs_phaseStabilityTest(iph);
|
|
if (Fephase > 0.0) {
|
|
if (Fephase > FephaseMax) {
|
|
iphasePop = iph;
|
|
FephaseMax = Fephase;
|
|
}
|
|
} else {
|
|
if (Fephase > FephaseMax) {
|
|
FephaseMax = Fephase;
|
|
}
|
|
}
|
|
#ifdef DEBUG_MODE
|
|
if (m_debug_print_lvl >= 2) {
|
|
plogf(" --- %18s %5d %11.3g %11.3g\n",
|
|
Vphase->PhaseName.c_str(),
|
|
existence, Fephase,
|
|
m_tPhaseMoles_old[iph]);
|
|
}
|
|
#endif
|
|
} else {
|
|
#ifdef DEBUG_MODE
|
|
if (m_debug_print_lvl >= 2) {
|
|
plogf(" --- %18s %5d blocked %11.3g\n",
|
|
Vphase->PhaseName.c_str(),
|
|
existence, m_tPhaseMoles_old[iph]);
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
}
|
|
}
|
|
phasePopPhaseIDs.resize(0);
|
|
if (iphasePop != npos) {
|
|
phasePopPhaseIDs.push_back(iphasePop);
|
|
}
|
|
|
|
/*
|
|
* Insert logic here to figure out if phase pops are linked together. Only do one linked
|
|
* pop at a time.
|
|
*/
|
|
|
|
#ifdef DEBUG_MODE
|
|
if (m_debug_print_lvl >= 2) {
|
|
plogf(" ---------------------------------------------------------------------\n");
|
|
}
|
|
#endif
|
|
return iphasePop;
|
|
}
|
|
//====================================================================================================================
|
|
// Calculates the deltas of the reactions due to phases popping
|
|
// into existence
|
|
/*
|
|
* @param iphasePop Phase id of the phase that will come into existence
|
|
*
|
|
* Output
|
|
* -------
|
|
* m_deltaMolNumSpecies(irxn) : reaction adjustments, where irxn refers
|
|
* to the irxn'th species
|
|
* formation reaction. This adjustment
|
|
* is for species
|
|
* irxn + M, where M is the number
|
|
* of components.
|
|
*
|
|
* @return Returns an int representing the status of the step
|
|
* - 0 : normal return
|
|
* - 1 : A single species phase species has been zeroed out
|
|
* in this routine. The species is a noncomponent
|
|
* - 2 : Same as one but, the zeroed species is a component.
|
|
* - 3 : Nothing was done because the phase couldn't be birthed
|
|
* because a needed component is zero.
|
|
*/
|
|
int VCS_SOLVE::vcs_popPhaseRxnStepSizes(const size_t iphasePop)
|
|
{
|
|
vcs_VolPhase* Vphase = m_VolPhaseList[iphasePop];
|
|
// Identify the first species in the phase
|
|
size_t kspec = Vphase->spGlobalIndexVCS(0);
|
|
// Identify the formation reaction for that species
|
|
size_t irxn = kspec - m_numComponents;
|
|
std::vector<size_t> creationGlobalRxnNumbers;
|
|
|
|
doublereal s;
|
|
// Calculate the initial moles of the phase being born.
|
|
// Here we set it to 10x of the value which would cause the phase to be
|
|
// zeroed out within the algorithm. We may later adjust the value.
|
|
doublereal tPhaseMoles = 10. * m_totalMolNum * VCS_DELETE_PHASE_CUTOFF;
|
|
|
|
|
|
#ifdef DEBUG_MODE
|
|
int existence = Vphase->exists();
|
|
if (existence > 0) {
|
|
printf("ERROR vcs_popPhaseRxnStepSizes called for a phase that exists!");
|
|
exit(-1);
|
|
}
|
|
char anote[256];
|
|
if (m_debug_print_lvl >= 2) {
|
|
plogf(" --- vcs_popPhaseRxnStepSizes() called to pop phase %s %d into existence\n",
|
|
Vphase->PhaseName.c_str(), iphasePop);
|
|
}
|
|
#endif
|
|
// Section for a single-species phase
|
|
if (Vphase->m_singleSpecies) {
|
|
s = 0.0;
|
|
double* dnPhase_irxn = m_deltaMolNumPhase[irxn];
|
|
for (size_t j = 0; j < m_numComponents; ++j) {
|
|
if (!m_SSPhase[j]) {
|
|
if (m_molNumSpecies_old[j] > 0.0) {
|
|
s += SQUARE(m_stoichCoeffRxnMatrix[irxn][j]) / m_molNumSpecies_old[j];
|
|
}
|
|
}
|
|
}
|
|
for (size_t j = 0; j < m_numPhases; j++) {
|
|
Vphase = m_VolPhaseList[j];
|
|
if (! Vphase->m_singleSpecies) {
|
|
if (m_tPhaseMoles_old[j] > 0.0) {
|
|
s -= SQUARE(dnPhase_irxn[j]) / m_tPhaseMoles_old[j];
|
|
}
|
|
}
|
|
}
|
|
if (s != 0.0) {
|
|
double s_old = s;
|
|
s = vcs_Hessian_diag_adj(irxn, s_old);
|
|
#ifdef DEBUG_MODE
|
|
if (s_old != s) {
|
|
sprintf(anote, "Normal calc: diag adjusted from %g "
|
|
"to %g due to act coeff", s_old, s);
|
|
}
|
|
#endif
|
|
m_deltaMolNumSpecies[kspec] = -m_deltaGRxn_new[irxn] / s;
|
|
} else {
|
|
// Ok, s is equal to zero. We can not apply a sophisticated theory
|
|
// to birth the phase. Just pick a small delta and go with it.
|
|
m_deltaMolNumSpecies[kspec] = tPhaseMoles;
|
|
}
|
|
|
|
/*
|
|
* section to do damping of the m_deltaMolNumSpecies[]
|
|
*/
|
|
for (size_t j = 0; j < m_numComponents; ++j) {
|
|
double stoicC = m_stoichCoeffRxnMatrix[irxn][j];
|
|
if (stoicC != 0.0) {
|
|
if (m_elType[j] == VCS_ELEM_TYPE_ABSPOS) {
|
|
double negChangeComp = - stoicC * m_deltaMolNumSpecies[kspec];
|
|
if (negChangeComp > m_molNumSpecies_old[j]) {
|
|
if (m_molNumSpecies_old[j] > 0.0) {
|
|
#ifdef DEBUG_MODE
|
|
sprintf(anote, "Delta damped from %g "
|
|
"to %g due to component %lu (%10s) going neg", m_deltaMolNumSpecies[kspec],
|
|
-m_molNumSpecies_old[j]/stoicC, j, m_speciesName[j].c_str());
|
|
#endif
|
|
m_deltaMolNumSpecies[kspec] = - 0.5 * m_molNumSpecies_old[j] / stoicC;
|
|
} else {
|
|
#ifdef DEBUG_MODE
|
|
sprintf(anote, "Delta damped from %g "
|
|
"to %g due to component %lu (%10s) zero", m_deltaMolNumSpecies[kspec],
|
|
-m_molNumSpecies_old[j]/stoicC, j, m_speciesName[j].c_str());
|
|
#endif
|
|
m_deltaMolNumSpecies[kspec] = 0.0;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
// Implement a damping term that limits m_deltaMolNumSpecies to the size of the mole number
|
|
if (-m_deltaMolNumSpecies[kspec] > m_molNumSpecies_old[kspec]) {
|
|
#ifdef DEBUG_MODE
|
|
sprintf(anote, "Delta damped from %g "
|
|
"to %g due to %s going negative", m_deltaMolNumSpecies[kspec],
|
|
-m_molNumSpecies_old[kspec], m_speciesName[kspec].c_str());
|
|
#endif
|
|
m_deltaMolNumSpecies[kspec] = -m_molNumSpecies_old[kspec];
|
|
}
|
|
|
|
|
|
} else {
|
|
vector<doublereal> fracDelta(Vphase->nSpecies());
|
|
vector<doublereal> X_est(Vphase->nSpecies());
|
|
fracDelta = Vphase->creationMoleNumbers(creationGlobalRxnNumbers);
|
|
|
|
double sumFrac = 0.0;
|
|
for (size_t k = 0; k < Vphase->nSpecies(); k++) {
|
|
sumFrac += fracDelta[k];
|
|
}
|
|
for (size_t k = 0; k < Vphase->nSpecies(); k++) {
|
|
X_est[k] = fracDelta[k] / sumFrac;
|
|
}
|
|
|
|
doublereal deltaMolNumPhase = tPhaseMoles;
|
|
doublereal damp = 1.0;
|
|
m_deltaGRxn_tmp = m_molNumSpecies_old;
|
|
double* molNumSpecies_tmp = DATA_PTR(m_deltaGRxn_tmp);
|
|
|
|
|
|
for (size_t k = 0; k < Vphase->nSpecies(); k++) {
|
|
kspec = Vphase->spGlobalIndexVCS(k);
|
|
double delmol = deltaMolNumPhase * X_est[k];
|
|
if (kspec >= m_numComponents) {
|
|
irxn = kspec - m_numComponents;
|
|
for (size_t j = 0; j < m_numComponents; ++j) {
|
|
double stoicC = m_stoichCoeffRxnMatrix[irxn][j];
|
|
if (stoicC != 0.0) {
|
|
if (m_elType[j] == VCS_ELEM_TYPE_ABSPOS) {
|
|
molNumSpecies_tmp[j] += stoicC * delmol;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
doublereal ratioComp = 0.0;
|
|
for (size_t j = 0; j < m_numComponents; ++j) {
|
|
double deltaJ = m_molNumSpecies_old[j] - molNumSpecies_tmp[j];
|
|
if (molNumSpecies_tmp[j] < 0.0) {
|
|
ratioComp = 1.0;
|
|
if (deltaJ > 0.0) {
|
|
double delta0 = m_molNumSpecies_old[j];
|
|
double dampj = delta0 / deltaJ * 0.9;
|
|
if (dampj < damp) {
|
|
damp = dampj;
|
|
}
|
|
}
|
|
} else {
|
|
if (m_elType[j] == VCS_ELEM_TYPE_ABSPOS) {
|
|
size_t jph = m_phaseID[j];
|
|
if ((jph != iphasePop) && (!m_SSPhase[j])) {
|
|
double fdeltaJ = fabs(deltaJ);
|
|
if (m_molNumSpecies_old[j] > 0.0) {
|
|
ratioComp = std::max(ratioComp, fdeltaJ/ m_molNumSpecies_old[j]);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// We may have greatly underestimated the deltaMoles for the phase pop
|
|
// Here we create a damp > 1 to account for this possibility.
|
|
// We adjust upwards to make sure that a component in an existing multispecies
|
|
// phase is modified by a factor of 1/1000.
|
|
if (ratioComp > 1.0E-30) {
|
|
if (ratioComp < 0.001) {
|
|
damp = 0.001 / ratioComp;
|
|
}
|
|
}
|
|
|
|
|
|
if (damp <= 1.0E-6) {
|
|
return 3;
|
|
}
|
|
|
|
for (size_t k = 0; k < Vphase->nSpecies(); k++) {
|
|
kspec = Vphase->spGlobalIndexVCS(k);
|
|
if (kspec < m_numComponents) {
|
|
m_speciesStatus[kspec] = VCS_SPECIES_COMPONENT;
|
|
} else {
|
|
m_deltaMolNumSpecies[kspec] = deltaMolNumPhase * X_est[k] * damp;
|
|
if (X_est[k] > 1.0E-3) {
|
|
m_speciesStatus[kspec] = VCS_SPECIES_MAJOR;
|
|
} else {
|
|
m_speciesStatus[kspec] = VCS_SPECIES_MINOR;
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
double VCS_SOLVE::vcs_phaseStabilityTest(const size_t iph)
|
|
{
|
|
|
|
/*
|
|
* We will use the _new state calc here
|
|
*/
|
|
size_t kspec, irxn, k, i, kc, kc_spec;
|
|
vcs_VolPhase* Vphase = m_VolPhaseList[iph];
|
|
doublereal deltaGRxn;
|
|
|
|
// We will do a full newton calculation later, but for now, ...
|
|
bool doSuccessiveSubstitution = true;
|
|
double funcPhaseStability;
|
|
vector<doublereal> X_est(Vphase->nSpecies(), 0.0);
|
|
vector<doublereal> delFrac(Vphase->nSpecies(), 0.0);
|
|
vector<doublereal> E_phi(Vphase->nSpecies(), 0.0);
|
|
vector<doublereal> fracDelta_new(Vphase->nSpecies(), 0.0);
|
|
vector<doublereal> fracDelta_old(Vphase->nSpecies(), 0.0);
|
|
vector<doublereal> fracDelta_raw(Vphase->nSpecies(), 0.0);
|
|
vector<size_t> creationGlobalRxnNumbers(Vphase->nSpecies(), npos);
|
|
vcs_dcopy(VCS_DATA_PTR(m_deltaGRxn_Deficient), VCS_DATA_PTR(m_deltaGRxn_old), m_numRxnRdc);
|
|
|
|
vector<doublereal> m_feSpecies_Deficient(m_numComponents, 0.0);
|
|
doublereal damp = 1.0;
|
|
doublereal dampOld = 1.0;
|
|
doublereal normUpdate = 1.0;
|
|
doublereal normUpdateOld = 1.0;
|
|
doublereal sum = 0.0;
|
|
doublereal dirProd = 0.0;
|
|
doublereal dirProdOld = 0.0;
|
|
|
|
// get the activity coefficients
|
|
Vphase->sendToVCS_ActCoeff(VCS_STATECALC_OLD, VCS_DATA_PTR(m_actCoeffSpecies_new));
|
|
|
|
// Get the stored estimate for the composition of the phase if
|
|
// it gets created
|
|
fracDelta_new = Vphase->creationMoleNumbers(creationGlobalRxnNumbers);
|
|
|
|
|
|
std::vector<size_t> componentList;
|
|
|
|
for (k = 0; k < Vphase->nSpecies(); k++) {
|
|
kspec = Vphase->spGlobalIndexVCS(k);
|
|
if (kspec < m_numComponents) {
|
|
componentList.push_back(k);
|
|
}
|
|
}
|
|
|
|
for (k = 0; k < m_numComponents; k++) {
|
|
m_feSpecies_Deficient[k] = m_feSpecies_old[k];
|
|
}
|
|
normUpdate = 0.1 * vcs_l2norm(fracDelta_new);
|
|
damp = 1.0E-2;
|
|
|
|
if (doSuccessiveSubstitution) {
|
|
|
|
#ifdef DEBUG_MODE
|
|
int KP = 0;
|
|
if (m_debug_print_lvl >= 2) {
|
|
plogf(" --- vcs_phaseStabilityTest() called\n");
|
|
plogf(" --- Its X_old[%2d] FracDel_old[%2d] deltaF[%2d] FracDel_new[%2d]"
|
|
" normUpdate damp FuncPhaseStability\n", KP, KP, KP, KP);
|
|
plogf(" --------------------------------------------------------------"
|
|
"--------------------------------------------------------\n");
|
|
} else if (m_debug_print_lvl == 1) {
|
|
plogf(" --- vcs_phaseStabilityTest() called for phase %d\n", iph);
|
|
}
|
|
#endif
|
|
|
|
for (k = 0; k < Vphase->nSpecies(); k++) {
|
|
if (fracDelta_new[k] < 1.0E-13) {
|
|
fracDelta_new[k] = 1.0E-13;
|
|
}
|
|
}
|
|
bool converged = false;
|
|
for (int its = 0; its < 200 && (!converged); its++) {
|
|
|
|
dampOld = damp;
|
|
normUpdateOld = normUpdate;
|
|
fracDelta_old = fracDelta_new;
|
|
dirProdOld = dirProd;
|
|
|
|
// Given a set of fracDelta's, we calculate the fracDelta's
|
|
// for the component species, if any
|
|
for (i = 0; i < componentList.size(); i++) {
|
|
kc = componentList[i];
|
|
kc_spec = Vphase->spGlobalIndexVCS(kc);
|
|
fracDelta_old[kc] = 0.0;
|
|
for (k = 0; k < Vphase->nSpecies(); k++) {
|
|
kspec = Vphase->spGlobalIndexVCS(k);
|
|
if (kspec >= m_numComponents) {
|
|
irxn = kspec - m_numComponents;
|
|
fracDelta_old[kc] += m_stoichCoeffRxnMatrix[irxn][kc_spec] * fracDelta_old[k];
|
|
}
|
|
}
|
|
}
|
|
|
|
// Now, calculate the predicted mole fractions, X_est[k]
|
|
double sumFrac = 0.0;
|
|
for (k = 0; k < Vphase->nSpecies(); k++) {
|
|
sumFrac += fracDelta_old[k];
|
|
}
|
|
// Necessary because this can be identically zero. -> we need to fix this algorithm!
|
|
if (sumFrac <= 0.0) {
|
|
sumFrac = 1.0;
|
|
}
|
|
double sum_Xcomp = 0.0;
|
|
for (k = 0; k < Vphase->nSpecies(); k++) {
|
|
X_est[k] = fracDelta_old[k] / sumFrac;
|
|
kc_spec = Vphase->spGlobalIndexVCS(k);
|
|
if (kc_spec < m_numComponents) {
|
|
sum_Xcomp += X_est[k];
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* Feed the newly formed estimate of the mole fractions back into the
|
|
* ThermoPhase object
|
|
*/
|
|
Vphase->setMoleFractionsState(0.0, VCS_DATA_PTR(X_est), VCS_STATECALC_PHASESTABILITY);
|
|
|
|
/*
|
|
* get the activity coefficients
|
|
*/
|
|
Vphase->sendToVCS_ActCoeff(VCS_STATECALC_OLD, VCS_DATA_PTR(m_actCoeffSpecies_new));
|
|
|
|
/*
|
|
* First calculate altered chemical potentials for component species
|
|
* belonging to this phase.
|
|
*/
|
|
for (i = 0; i < componentList.size(); i++) {
|
|
kc = componentList[i];
|
|
kc_spec = Vphase->spGlobalIndexVCS(kc);
|
|
if (X_est[kc] > VCS_DELETE_MINORSPECIES_CUTOFF) {
|
|
m_feSpecies_Deficient[kc_spec] = m_feSpecies_old[kc_spec]
|
|
+ log(m_actCoeffSpecies_new[kc_spec] * X_est[kc]);
|
|
} else {
|
|
m_feSpecies_Deficient[kc_spec] = m_feSpecies_old[kc_spec]
|
|
+ log(m_actCoeffSpecies_new[kc_spec] * VCS_DELETE_MINORSPECIES_CUTOFF);
|
|
}
|
|
}
|
|
|
|
for (i = 0; i < componentList.size(); i++) {
|
|
kc = componentList[i];
|
|
kc_spec = Vphase->spGlobalIndexVCS(kc);
|
|
|
|
for (k = 0; k < Vphase->nSpecies(); k++) {
|
|
kspec = Vphase->spGlobalIndexVCS(k);
|
|
if (kspec >= m_numComponents) {
|
|
irxn = kspec - m_numComponents;
|
|
if (i == 0) {
|
|
m_deltaGRxn_Deficient[irxn] = m_deltaGRxn_old[irxn];
|
|
}
|
|
double* dtmp_ptr = m_stoichCoeffRxnMatrix[irxn];
|
|
if (dtmp_ptr[kc_spec] != 0.0) {
|
|
m_deltaGRxn_Deficient[irxn] +=
|
|
dtmp_ptr[kc_spec] * (m_feSpecies_Deficient[kc_spec]- m_feSpecies_old[kc_spec]);
|
|
}
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Calculate the E_phi's
|
|
*/
|
|
sum = 0.0;
|
|
funcPhaseStability = sum_Xcomp - 1.0;
|
|
for (k = 0; k < Vphase->nSpecies(); k++) {
|
|
kspec = Vphase->spGlobalIndexVCS(k);
|
|
if (kspec >= m_numComponents) {
|
|
irxn = kspec - m_numComponents;
|
|
deltaGRxn = m_deltaGRxn_Deficient[irxn];
|
|
if (deltaGRxn > 50.0) {
|
|
deltaGRxn = 50.0;
|
|
}
|
|
if (deltaGRxn < -50.0) {
|
|
deltaGRxn = -50.0;
|
|
}
|
|
E_phi[k] = std::exp(-deltaGRxn) / m_actCoeffSpecies_new[kspec];
|
|
sum += E_phi[k];
|
|
funcPhaseStability += E_phi[k];
|
|
} else {
|
|
E_phi[k] = 0.0;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Calculate the raw estimate of the new fracs
|
|
*/
|
|
for (k = 0; k < Vphase->nSpecies(); k++) {
|
|
kspec = Vphase->spGlobalIndexVCS(k);
|
|
double b = E_phi[k] / sum * (1.0 - sum_Xcomp);
|
|
if (kspec >= m_numComponents) {
|
|
irxn = kspec - m_numComponents;
|
|
fracDelta_raw[k] = b;
|
|
}
|
|
}
|
|
|
|
|
|
// Given a set of fracDelta's, we calculate the fracDelta's
|
|
// for the component species, if any
|
|
for (i = 0; i < componentList.size(); i++) {
|
|
kc = componentList[i];
|
|
kc_spec = Vphase->spGlobalIndexVCS(kc);
|
|
fracDelta_raw[kc] = 0.0;
|
|
for (k = 0; k < Vphase->nSpecies(); k++) {
|
|
kspec = Vphase->spGlobalIndexVCS(k);
|
|
if (kspec >= m_numComponents) {
|
|
irxn = kspec - m_numComponents;
|
|
fracDelta_raw[kc] += m_stoichCoeffRxnMatrix[irxn][kc_spec] * fracDelta_raw[k];
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
* Now possibly dampen the estimate.
|
|
*/
|
|
doublereal sumADel = 0.0;
|
|
for (k = 0; k < Vphase->nSpecies(); k++) {
|
|
delFrac[k] = fracDelta_raw[k] - fracDelta_old[k];
|
|
sumADel += fabs(delFrac[k]);
|
|
}
|
|
normUpdate = vcs_l2norm(delFrac);
|
|
|
|
dirProd = 0.0;
|
|
for (k = 0; k < Vphase->nSpecies(); k++) {
|
|
dirProd += fracDelta_old[k] * delFrac[k];
|
|
}
|
|
bool crossedSign = false;
|
|
if (dirProd * dirProdOld < 0.0) {
|
|
crossedSign = true;
|
|
}
|
|
|
|
|
|
damp = 0.5;
|
|
if (dampOld < 0.25) {
|
|
damp = 2.0 * dampOld;
|
|
}
|
|
if (crossedSign) {
|
|
if (normUpdate *1.5 > normUpdateOld) {
|
|
damp = 0.5 * dampOld;
|
|
} else if (normUpdate *2.0 > normUpdateOld) {
|
|
damp = 0.8 * dampOld;
|
|
}
|
|
} else {
|
|
if (normUpdate > normUpdateOld * 2.0) {
|
|
damp = 0.6 * dampOld;
|
|
} else if (normUpdate > normUpdateOld * 1.2) {
|
|
damp = 0.9 * dampOld;
|
|
}
|
|
}
|
|
|
|
for (k = 0; k < Vphase->nSpecies(); k++) {
|
|
if (fabs(damp * delFrac[k]) > 0.3*fabs(fracDelta_old[k])) {
|
|
damp = std::max(0.3*fabs(fracDelta_old[k]) / fabs(delFrac[k]),
|
|
1.0E-8/fabs(delFrac[k]));
|
|
}
|
|
if (delFrac[k] < 0.0) {
|
|
if (2.0 * damp * (-delFrac[k]) > fracDelta_old[k]) {
|
|
damp = fracDelta_old[k] / (2.0 * (-delFrac[k]));
|
|
}
|
|
}
|
|
if (delFrac[k] > 0.0) {
|
|
if (2.0 * damp * delFrac[k] > fracDelta_old[k]) {
|
|
damp = fracDelta_old[k] / (2.0 * delFrac[k]);
|
|
}
|
|
}
|
|
}
|
|
if (damp < 0.000001) {
|
|
damp = 0.000001;
|
|
}
|
|
|
|
for (k = 0; k < Vphase->nSpecies(); k++) {
|
|
fracDelta_new[k] = fracDelta_old[k] + damp * (delFrac[k]);
|
|
}
|
|
|
|
#ifdef DEBUG_MODE
|
|
if (m_debug_print_lvl >= 2) {
|
|
plogf(" --- %3d %12g %12g %12g %12g %12g %12g %12g\n", its, X_est[KP], fracDelta_old[KP],
|
|
delFrac[KP], fracDelta_new[KP], normUpdate, damp, funcPhaseStability);
|
|
}
|
|
#endif
|
|
|
|
if (normUpdate < 1.0E-5) {
|
|
converged = true;
|
|
}
|
|
|
|
}
|
|
|
|
if (converged) {
|
|
Vphase->setMoleFractionsState(0.0, VCS_DATA_PTR(X_est),
|
|
VCS_STATECALC_PHASESTABILITY);
|
|
Vphase->setCreationMoleNumbers(VCS_DATA_PTR(fracDelta_new), creationGlobalRxnNumbers);
|
|
}
|
|
|
|
|
|
} else {
|
|
printf("not done yet\n");
|
|
exit(-1);
|
|
}
|
|
#ifdef DEBUG_MODE
|
|
if (m_debug_print_lvl >= 2) {
|
|
plogf(" ------------------------------------------------------------"
|
|
"-------------------------------------------------------------\n");
|
|
} else if (m_debug_print_lvl == 1) {
|
|
if (funcPhaseStability > 0.0) {
|
|
plogf(" --- phase %d with func = %g is to be born\n", iph, funcPhaseStability);
|
|
} else {
|
|
plogf(" --- phase %d with func = %g stays dead\n", iph, funcPhaseStability);
|
|
}
|
|
}
|
|
#endif
|
|
return funcPhaseStability;
|
|
}
|
|
|
|
}
|
|
|