1069 lines
31 KiB
C++
1069 lines
31 KiB
C++
/*!
|
|
* @file vcs_solve.h
|
|
* Header file for the internal class that holds the problem.
|
|
*/
|
|
/*
|
|
* $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_solve.h"
|
|
#include "vcs_Exception.h"
|
|
#include "vcs_internal.h"
|
|
#include "vcs_prob.h"
|
|
|
|
#include "vcs_VolPhase.h"
|
|
#include "vcs_SpeciesProperties.h"
|
|
#include "vcs_species_thermo.h"
|
|
|
|
#include "clockWC.h"
|
|
|
|
#include <string>
|
|
#include "math.h"
|
|
using namespace std;
|
|
|
|
namespace VCSnonideal {
|
|
|
|
int vcs_timing_print_lvl = 1;
|
|
|
|
VCS_SOLVE::VCS_SOLVE() :
|
|
NSPECIES0(0),
|
|
NPHASE0(0),
|
|
m_numSpeciesTot(0),
|
|
m_numElemConstraints(0),
|
|
m_numComponents(0),
|
|
m_numRxnTot(0),
|
|
m_numSpeciesRdc(0),
|
|
m_numRxnMinorZeroed(0),
|
|
m_numPhases(0),
|
|
m_doEstimateEquil(0),
|
|
m_totalMolNum(0.0),
|
|
m_temperature(0.0),
|
|
m_pressurePA(0.0),
|
|
m_tolmaj(0.0),
|
|
m_tolmin(0.0),
|
|
m_tolmaj2(0.0),
|
|
m_tolmin2(0.0),
|
|
m_unitsState(VCS_DIMENSIONAL_G),
|
|
m_totalMoleScale(1.0),
|
|
m_useActCoeffJac(0),
|
|
m_totalVol(0.0),
|
|
m_Faraday_dim(1.602e-19 * 6.022136736e26),
|
|
m_VCount(0),
|
|
m_debug_print_lvl(0),
|
|
m_timing_print_lvl(1),
|
|
m_VCS_UnitsFormat(VCS_UNITS_UNITLESS)
|
|
{
|
|
}
|
|
|
|
// Initialize the sizes within the VCS_SOLVE object
|
|
/*
|
|
* This resizes all of the internal arrays within the object. This routine
|
|
* operates in two modes. If all of the parameters are the same as it
|
|
* currently exists in the object, nothing is done by this routine; a quick
|
|
* exit is carried out and all of the data in the object persists.
|
|
*
|
|
* IF any of the parameters are different than currently exists in the
|
|
* object, then all of the data in the object must be redone. It may not
|
|
* be zeroed, but it must be redone.
|
|
*
|
|
* @param nspecies0 Number of species within the object
|
|
* @param nelements Number of element constraints within the problem
|
|
* @param nphase0 Number of phases defined within the problem.
|
|
*
|
|
*/
|
|
void VCS_SOLVE::vcs_initSizes(const int nspecies0, const int nelements,
|
|
const int nphase0) {
|
|
|
|
if (NSPECIES0 != 0) {
|
|
if ((nspecies0 != NSPECIES0) || (nelements != m_numElemConstraints) || (nphase0 != NPHASE0)){
|
|
vcs_delete_memory();
|
|
} else {
|
|
return;
|
|
}
|
|
}
|
|
|
|
NSPECIES0 = nspecies0;
|
|
NPHASE0 = nphase0;
|
|
m_numSpeciesTot = nspecies0;
|
|
m_numElemConstraints = nelements;
|
|
m_numComponents = nelements;
|
|
|
|
int iph;
|
|
string ser = "VCS_SOLVE: ERROR:\n\t";
|
|
if (nspecies0 <= 0) {
|
|
plogf("%s Number of species is nonpositive\n", ser.c_str());
|
|
throw vcsError("VCS_SOLVE()",
|
|
ser + " Number of species is nonpositive\n",
|
|
VCS_PUB_BAD);
|
|
}
|
|
if (nelements <= 0) {
|
|
plogf("%s Number of elements is nonpositive\n", ser.c_str());
|
|
throw vcsError("VCS_SOLVE()",
|
|
ser + " Number of species is nonpositive\n",
|
|
VCS_PUB_BAD);
|
|
}
|
|
if (nphase0 <= 0) {
|
|
plogf("%s Number of phases is nonpositive\n", ser.c_str());
|
|
throw vcsError("VCS_SOLVE()",
|
|
ser + " Number of species is nonpositive\n",
|
|
VCS_PUB_BAD);
|
|
}
|
|
|
|
//vcs_priv_init(this);
|
|
m_VCS_UnitsFormat = VCS_UNITS_UNITLESS;
|
|
|
|
/*
|
|
* We will initialize sc[] to note the fact that it needs to be
|
|
* filled with meaningful information.
|
|
*/
|
|
m_stoichCoeffRxnMatrix.resize(nspecies0, nelements, 0.0);
|
|
|
|
m_scSize.resize(nspecies0, 0.0);
|
|
m_spSize.resize(nspecies0, 1.0);
|
|
|
|
m_SSfeSpecies.resize(nspecies0, 0.0);
|
|
m_feSpecies_new.resize(nspecies0, 0.0);
|
|
m_molNumSpecies_old.resize(nspecies0, 0.0);
|
|
|
|
m_speciesUnknownType.resize(nspecies0, VCS_SPECIES_TYPE_MOLNUM);
|
|
|
|
m_deltaMolNumPhase.resize(nspecies0, nphase0, 0.0);
|
|
m_phaseParticipation.resize(nspecies0, nphase0, 0);
|
|
m_phasePhi.resize(nphase0, 0.0);
|
|
|
|
m_molNumSpecies_new.resize(nspecies0, 0.0);
|
|
|
|
m_deltaGRxn_new.resize(nspecies0, 0.0);
|
|
m_deltaGRxn_old.resize(nspecies0, 0.0);
|
|
m_deltaGRxn_tmp.resize(nspecies0, 0.0);
|
|
m_deltaMolNumSpecies.resize(nspecies0, 0.0);
|
|
|
|
m_feSpecies_old.resize(nspecies0, 0.0);
|
|
m_elemAbundances.resize(nelements, 0.0);
|
|
m_elemAbundancesGoal.resize(nelements, 0.0);
|
|
|
|
m_tPhaseMoles_old.resize(nphase0, 0.0);
|
|
m_tPhaseMoles_new.resize(nphase0, 0.0);
|
|
m_deltaPhaseMoles.resize(nphase0, 0.0);
|
|
m_TmpPhase.resize(nphase0, 0.0);
|
|
m_TmpPhase2.resize(nphase0, 0.0);
|
|
|
|
m_formulaMatrix.resize(nelements, nspecies0);
|
|
|
|
TPhInertMoles.resize(nphase0, 0.0);
|
|
|
|
/*
|
|
* ind[] is an index variable that keep track of solution vector
|
|
* rotations.
|
|
*/
|
|
m_speciesMapIndex.resize(nspecies0, 0);
|
|
m_speciesLocalPhaseIndex.resize(nspecies0, 0);
|
|
/*
|
|
* IndEl[] is an index variable that keep track of element vector
|
|
* rotations.
|
|
*/
|
|
m_elementMapIndex.resize(nelements, 0);
|
|
|
|
/*
|
|
* ir[] is an index vector that keeps track of the irxn to species
|
|
* mapping. We can't fill it in until we know the number of c
|
|
* components in the problem
|
|
*/
|
|
m_indexRxnToSpecies.resize(nspecies0, 0);
|
|
|
|
/* Initialize all species to be major species */
|
|
m_rxnStatus.resize(nspecies0, 1);
|
|
|
|
m_SSPhase.resize(2*nspecies0, 0);
|
|
m_phaseID.resize(nspecies0, 0);
|
|
|
|
m_numElemConstraints = nelements;
|
|
|
|
m_elementName.resize(nelements, std::string(""));
|
|
m_speciesName.resize(nspecies0, std::string(""));
|
|
|
|
m_elType.resize(nelements, VCS_ELEM_TYPE_ABSPOS);
|
|
|
|
m_elementActive.resize(nelements, 1);
|
|
/*
|
|
* Malloc space for activity coefficients for all species
|
|
* -> Set it equal to one.
|
|
*/
|
|
m_actConventionSpecies.resize(nspecies0, 0);
|
|
m_phaseActConvention.resize(nphase0, 0);
|
|
m_lnMnaughtSpecies.resize(nspecies0, 0.0);
|
|
m_actCoeffSpecies_new.resize(nspecies0, 1.0);
|
|
m_actCoeffSpecies_old.resize(nspecies0, 1.0);
|
|
m_wtSpecies.resize(nspecies0, 0.0);
|
|
m_chargeSpecies.resize(nspecies0, 0.0);
|
|
m_speciesThermoList.resize(nspecies0, (VCS_SPECIES_THERMO *)0);
|
|
|
|
/*
|
|
* Malloc Phase Info
|
|
*/
|
|
m_VolPhaseList.resize(nphase0, 0);
|
|
for (iph = 0; iph < nphase0; iph++) {
|
|
m_VolPhaseList[iph] = new vcs_VolPhase(this);
|
|
}
|
|
|
|
/*
|
|
* For Future expansion
|
|
*/
|
|
m_useActCoeffJac = true;
|
|
if (m_useActCoeffJac) {
|
|
m_dLnActCoeffdMolNum.resize(nspecies0, nspecies0, 0.0);
|
|
}
|
|
|
|
m_PMVolumeSpecies.resize(nspecies0, 0.0);
|
|
|
|
/*
|
|
* Malloc space for counters kept within vcs
|
|
*
|
|
*/
|
|
m_VCount = new VCS_COUNTERS();
|
|
vcs_counters_init(1);
|
|
|
|
if (vcs_timing_print_lvl == 0) {
|
|
m_timing_print_lvl = 0;
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
|
/****************************************************************************/
|
|
|
|
// Destructor
|
|
/*
|
|
*
|
|
*/
|
|
VCS_SOLVE::~VCS_SOLVE()
|
|
{
|
|
vcs_delete_memory();
|
|
}
|
|
/*****************************************************************************/
|
|
|
|
// Delete memory that isn't just resizeable STL containers
|
|
/*
|
|
* This gets called by the destructor or by InitSizes().
|
|
*/
|
|
void VCS_SOLVE::vcs_delete_memory() {
|
|
int j;
|
|
int nspecies = m_numSpeciesTot;
|
|
|
|
for (j = 0; j < m_numPhases; j++) {
|
|
delete m_VolPhaseList[j];
|
|
m_VolPhaseList[j] = 0;
|
|
}
|
|
|
|
for (j = 0; j < nspecies; j++) {
|
|
delete m_speciesThermoList[j];
|
|
m_speciesThermoList[j] = 0;
|
|
}
|
|
|
|
delete m_VCount;
|
|
m_VCount = 0;
|
|
|
|
NSPECIES0 = 0;
|
|
NPHASE0 = 0;
|
|
m_numElemConstraints = 0;
|
|
m_numComponents = 0;
|
|
}
|
|
/*****************************************************************************/
|
|
|
|
// Solve an equilibrium problem
|
|
/*
|
|
* This is the main interface routine to the equilibrium solver
|
|
*
|
|
* Input:
|
|
* @param vprob Object containing the equilibrium Problem statement
|
|
*
|
|
* @param ifunc Determines the operation to be done: Valid values:
|
|
* 0 -> Solve a new problem by initializing structures
|
|
* first. An initial estimate may or may not have
|
|
* been already determined. This is indicated in the
|
|
* VCS_PROB structure.
|
|
* 1 -> The problem has already been initialized and
|
|
* set up. We call this routine to resolve it
|
|
* using the problem statement and
|
|
* solution estimate contained in
|
|
* the VCS_PROB structure.
|
|
* 2 -> Don't solve a problem. Destroy all the private
|
|
* structures.
|
|
*
|
|
* @param ipr Printing of results
|
|
* ipr = 1 -> Print problem statement and final results to
|
|
* standard output
|
|
* 0 -> don't report on anything
|
|
* @param ip1 Printing of intermediate results
|
|
* ip1 = 1 -> Print intermediate results.
|
|
* = 0 -> No intermediate results printing
|
|
*
|
|
* @param maxit Maximum number of iterations for the algorithm
|
|
*
|
|
* Output:
|
|
*
|
|
* @return
|
|
* nonzero value: failure to solve the problem at hand.
|
|
* zero : success
|
|
*/
|
|
int VCS_SOLVE::vcs(VCS_PROB *vprob, int ifunc, int ipr, int ip1, int maxit) {
|
|
int retn = 0;
|
|
int iconv = 0, nspecies0, nelements0, nphase0;
|
|
Cantera::clockWC tickTock;
|
|
|
|
int iprintTime = MAX(ipr, ip1);
|
|
if (m_timing_print_lvl < iprintTime) {
|
|
iprintTime = m_timing_print_lvl ;
|
|
}
|
|
|
|
if (ifunc < 0 || ifunc > 2) {
|
|
plogf("vcs: Unrecognized value of ifunc, %d: bailing!\n",
|
|
ifunc);
|
|
return VCS_PUB_BAD;
|
|
}
|
|
|
|
if (ifunc == 0) {
|
|
/*
|
|
* This function is called to create the private data
|
|
* using the public data.
|
|
*/
|
|
nspecies0 = vprob->nspecies + 10;
|
|
nelements0 = vprob->ne;
|
|
nphase0 = vprob->NPhase;
|
|
|
|
vcs_initSizes(nspecies0, nelements0, nphase0);
|
|
|
|
if (retn != 0) {
|
|
plogf("vcs_priv_alloc returned a bad status, %d: bailing!\n",
|
|
retn);
|
|
return retn;
|
|
}
|
|
/*
|
|
* This function is called to copy the public data
|
|
* and the current problem specification
|
|
* into the current object's data structure.
|
|
*/
|
|
retn = vcs_prob_specifyFully(vprob);
|
|
if (retn != 0) {
|
|
plogf("vcs_pub_to_priv returned a bad status, %d: bailing!\n",
|
|
retn);
|
|
return retn;
|
|
}
|
|
/*
|
|
* Prep the problem data
|
|
* - adjust the identity of any phases
|
|
* - determine the number of components in the problem
|
|
*/
|
|
retn = vcs_prep_oneTime(ip1);
|
|
if (retn != 0) {
|
|
plogf("vcs_prep_oneTime returned a bad status, %d: bailing!\n",
|
|
retn);
|
|
return retn;
|
|
}
|
|
}
|
|
if (ifunc == 1) {
|
|
/*
|
|
* This function is called to copy the current problem
|
|
* into the current object's data structure.
|
|
*/
|
|
retn = vcs_prob_specify(vprob);
|
|
if (retn != 0) {
|
|
plogf("vcs_prob_specify returned a bad status, %d: bailing!\n",
|
|
retn);
|
|
return retn;
|
|
}
|
|
}
|
|
if (ifunc != 2) {
|
|
/*
|
|
* Prep the problem data for this particular instantiation of
|
|
* the problem
|
|
*/
|
|
retn = vcs_prep();
|
|
if (retn != VCS_SUCCESS) {
|
|
plogf("vcs_prep returned a bad status, %d: bailing!\n", retn);
|
|
return retn;
|
|
}
|
|
|
|
/*
|
|
* Check to see if the current problem is well posed.
|
|
*/
|
|
if (!vcs_wellPosed(vprob)) {
|
|
plogf("vcs has determined the problem is not well posed: Bailing\n");
|
|
return VCS_PUB_BAD;
|
|
}
|
|
|
|
/*
|
|
* Once we have defined the global internal data structure defining
|
|
* the problem, then we go ahead and solve the problem.
|
|
*
|
|
* (right now, all we do is solve fixed T, P problems.
|
|
* Methods for other problem types will go in at this level.
|
|
* For example, solving for fixed T, V problems will involve
|
|
* a 2x2 Newton's method, using loops over vcs_TP() to
|
|
* calculate the residual and Jacobian)
|
|
*/
|
|
|
|
iconv = vcs_TP(ipr, ip1, maxit, vprob->T, vprob->PresPA);
|
|
|
|
|
|
/*
|
|
* If requested to print anything out, go ahead and do so;
|
|
*/
|
|
if (ipr > 0) vcs_report(iconv);
|
|
/*
|
|
* Copy the results of the run back to the VCS_PROB structure,
|
|
* which is returned to the user.
|
|
*/
|
|
vcs_prob_update(vprob);
|
|
}
|
|
|
|
/*
|
|
* Report on the time if requested to do so
|
|
*/
|
|
double te = tickTock.secondsWC();
|
|
m_VCount->T_Time_vcs += te;
|
|
if (iprintTime > 0) {
|
|
vcs_TCounters_report(m_timing_print_lvl);
|
|
}
|
|
/*
|
|
* Now, destroy the private data, if requested to do so
|
|
*
|
|
* FILL IN
|
|
*/
|
|
|
|
if (iconv < 0) {
|
|
plogf("ERROR: FAILURE its = %d!\n", m_VCount->Its);
|
|
} else if (iconv == 1) {
|
|
plogf("WARNING: RANGE SPACE ERROR encountered\n");
|
|
}
|
|
return iconv;
|
|
}
|
|
/*****************************************************************************/
|
|
|
|
// Fully specify the problem to be solved using VCS_PROB
|
|
/*
|
|
* Use the contents of the VCS_PROB to specify the contents of the
|
|
* private data, VCS_SOLVE.
|
|
*
|
|
* @param pub Pointer to VCS_PROB that will be used to
|
|
* initialize the current equilibrium problem
|
|
*/
|
|
int VCS_SOLVE::vcs_prob_specifyFully(const VCS_PROB *pub) {
|
|
int i, j, kspec;
|
|
int iph;
|
|
vcs_VolPhase *Vphase = 0;
|
|
const char *ser =
|
|
"vcs_pub_to_priv ERROR :ill defined interface -> bailout:\n\t";
|
|
|
|
/*
|
|
* First Check to see whether we have room for the current problem
|
|
* size
|
|
*/
|
|
int nspecies = pub->nspecies;
|
|
if (NSPECIES0 < nspecies) {
|
|
plogf("%sPrivate Data is dimensioned too small\n", ser);
|
|
return VCS_PUB_BAD;
|
|
}
|
|
int nph = pub->NPhase;
|
|
if (NPHASE0 < nph) {
|
|
plogf("%sPrivate Data is dimensioned too small\n", ser);
|
|
return VCS_PUB_BAD;
|
|
}
|
|
int nelements = pub->ne;
|
|
if (m_numElemConstraints < nelements) {
|
|
plogf("%sPrivate Data is dimensioned too small\n", ser);
|
|
return VCS_PUB_BAD;
|
|
}
|
|
|
|
/*
|
|
* OK, We have room. Now, transfer the integer numbers
|
|
*/
|
|
m_numElemConstraints = nelements;
|
|
m_numSpeciesTot = nspecies;
|
|
m_numSpeciesRdc = m_numSpeciesTot;
|
|
/*
|
|
* nc = number of components -> will be determined later.
|
|
* but set it to its maximum possible value here.
|
|
*/
|
|
m_numComponents = nelements;
|
|
/*
|
|
* m_numRxnTot = number of noncomponents, also equal to the
|
|
* number of reactions
|
|
*/
|
|
m_numRxnTot = MAX(nspecies - nelements, 0);
|
|
m_numRxnRdc = m_numRxnTot;
|
|
/*
|
|
* number of minor species rxn -> all species rxn are major at the start.
|
|
*/
|
|
m_numRxnMinorZeroed = 0;
|
|
/*
|
|
* NPhase = number of phases
|
|
*/
|
|
m_numPhases = nph;
|
|
|
|
#ifdef DEBUG_MODE
|
|
m_debug_print_lvl = pub->vcs_debug_print_lvl;
|
|
#else
|
|
m_debug_print_lvl = MIN(2, pub->vcs_debug_print_lvl);
|
|
#endif
|
|
|
|
/*
|
|
* FormulaMatrix[] -> Copy the formula matrix over
|
|
*/
|
|
for (i = 0; i < nspecies; i++) {
|
|
for (j = 0; j < nelements; j++) {
|
|
m_formulaMatrix[j][i] = pub->FormulaMatrix[j][i];
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Copy over the species molecular weights
|
|
*/
|
|
vcs_vdcopy(m_wtSpecies, pub->WtSpecies, nspecies);
|
|
|
|
/*
|
|
* Copy over the charges
|
|
*/
|
|
vcs_vdcopy(m_chargeSpecies, pub->Charge, nspecies);
|
|
|
|
/*
|
|
* Malloc and Copy the VCS_SPECIES_THERMO structures
|
|
*
|
|
*/
|
|
for (kspec = 0; kspec < nspecies; kspec++) {
|
|
if (m_speciesThermoList[kspec] != NULL) {
|
|
delete m_speciesThermoList[kspec];
|
|
}
|
|
VCS_SPECIES_THERMO *spf = pub->SpeciesThermo[kspec];
|
|
m_speciesThermoList[kspec] = spf->duplMyselfAsVCS_SPECIES_THERMO();
|
|
if (m_speciesThermoList[kspec] == NULL) {
|
|
plogf(" duplMyselfAsVCS_SPECIES_THERMO returned an error!\n");
|
|
return VCS_PUB_BAD;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Copy the species unknown type
|
|
*/
|
|
vcs_icopy(VCS_DATA_PTR(m_speciesUnknownType),
|
|
VCS_DATA_PTR(pub->SpeciesUnknownType), nspecies);
|
|
|
|
/*
|
|
* iest => Do we have an initial estimate of the species mole numbers ?
|
|
*/
|
|
m_doEstimateEquil = pub->iest;
|
|
|
|
/*
|
|
* w[] -> Copy the equilibrium mole number estimate if it exists.
|
|
*/
|
|
if (pub->w.size() != 0) {
|
|
vcs_vdcopy(m_molNumSpecies_old, pub->w, nspecies);
|
|
} else {
|
|
m_doEstimateEquil = -1;
|
|
vcs_dzero(VCS_DATA_PTR(m_molNumSpecies_old), nspecies);
|
|
}
|
|
|
|
/*
|
|
* Formulate the Goal Element Abundance Vector
|
|
*/
|
|
if (pub->gai.size() != 0) {
|
|
for (i = 0; i < nelements; i++) m_elemAbundancesGoal[i] = pub->gai[i];
|
|
} else {
|
|
if (m_doEstimateEquil == 0) {
|
|
for (j = 0; j < nelements; j++) {
|
|
m_elemAbundancesGoal[j] = 0.0;
|
|
for (kspec = 0; kspec < nspecies; kspec++) {
|
|
if (m_speciesUnknownType[kspec] != VCS_SPECIES_TYPE_INTERFACIALVOLTAGE) {
|
|
m_elemAbundancesGoal[j] += m_formulaMatrix[j][kspec] * m_molNumSpecies_old[kspec];
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
plogf("%sElement Abundances, m_elemAbundancesGoal[], not specified\n", ser);
|
|
return VCS_PUB_BAD;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* zero out values that will be filled in later
|
|
*/
|
|
/*
|
|
* TPhMoles[] -> Untouched here. These will be filled in vcs_prep.c
|
|
* TPhMoles1[]
|
|
* DelTPhMoles[]
|
|
*
|
|
*
|
|
* T, Pres, copy over here
|
|
*/
|
|
if (pub->T > 0.0) m_temperature = pub->T;
|
|
else m_temperature = 293.15;
|
|
if (pub->PresPA > 0.0) m_pressurePA = pub->PresPA;
|
|
else m_pressurePA = Cantera::OneAtm;
|
|
/*
|
|
* TPhInertMoles[] -> must be copied over here
|
|
*/
|
|
for (iph = 0; iph < nph; iph++) {
|
|
Vphase = pub->VPhaseList[iph];
|
|
TPhInertMoles[iph] = Vphase->totalMolesInert();
|
|
}
|
|
|
|
/*
|
|
* if__ : Copy over the units for the chemical potential
|
|
*/
|
|
m_VCS_UnitsFormat = pub->m_VCS_UnitsFormat;
|
|
|
|
/*
|
|
* tolerance requirements -> copy them over here and later
|
|
*/
|
|
m_tolmaj = pub->tolmaj;
|
|
m_tolmin = pub->tolmin;
|
|
m_tolmaj2 = 0.01 * m_tolmaj;
|
|
m_tolmin2 = 0.01 * m_tolmin;
|
|
|
|
/*
|
|
* m_speciesIndexVector[] is an index variable that keep track
|
|
* of solution vector rotations.
|
|
*/
|
|
for (i = 0; i < nspecies; i++) {
|
|
m_speciesMapIndex[i] = i;
|
|
}
|
|
|
|
/*
|
|
* IndEl[] is an index variable that keep track of element vector
|
|
* rotations.
|
|
*/
|
|
for (i = 0; i < nelements; i++) m_elementMapIndex[i] = i;
|
|
|
|
/*
|
|
* Define all species to be major species, initially.
|
|
*/
|
|
for (i = 0; i < nspecies; i++) m_rxnStatus[i] = VCS_SPECIES_MAJOR;
|
|
|
|
/*
|
|
* PhaseID: Fill in the species to phase mapping
|
|
* -> Check for bad values at the same time.
|
|
*/
|
|
if (pub->PhaseID.size() != 0) {
|
|
std::vector<int> numPhSp(nph, 0);
|
|
for (kspec = 0; kspec < nspecies; kspec++) {
|
|
iph = pub->PhaseID[kspec];
|
|
if (iph < 0 || iph >= nph) {
|
|
plogf("%sSpecies to Phase Mapping, PhaseID, has a bad value\n",
|
|
ser);
|
|
plogf("\tPhaseID[%d] = %d\n", kspec, iph);
|
|
plogf("\tAllowed values: 0 to %d\n", nph - 1);
|
|
return VCS_PUB_BAD;
|
|
}
|
|
m_phaseID[kspec] = pub->PhaseID[kspec];
|
|
m_speciesLocalPhaseIndex[kspec] = numPhSp[iph];
|
|
numPhSp[iph]++;
|
|
}
|
|
for (iph = 0; iph < nph; iph++) {
|
|
Vphase = pub->VPhaseList[iph];
|
|
if (numPhSp[iph] != Vphase->nSpecies()) {
|
|
plogf("%sNumber of species in phase %d, %s, doesn't match\n",
|
|
ser, iph, Vphase->PhaseName.c_str());
|
|
return VCS_PUB_BAD;
|
|
}
|
|
}
|
|
} else {
|
|
if (m_numPhases == 1) {
|
|
for (kspec = 0; kspec < nspecies; kspec++) {
|
|
m_phaseID[kspec] = 0;
|
|
m_speciesLocalPhaseIndex[kspec] = kspec;
|
|
}
|
|
} else {
|
|
plogf("%sSpecies to Phase Mapping, PhaseID, is not defined\n", ser);
|
|
return VCS_PUB_BAD;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Copy over the element types
|
|
*/
|
|
m_elType.resize(nelements, VCS_ELEM_TYPE_ABSPOS);
|
|
m_elementActive.resize(nelements, 1);
|
|
|
|
/*
|
|
* Copy over the element names
|
|
*/
|
|
for (i = 0; i < nelements; i++) {
|
|
m_elementName[i] = pub->ElName[i];
|
|
m_elType[i] = pub->m_elType[i];
|
|
m_elementActive[i] = pub->ElActive[i];
|
|
if (!strncmp(m_elementName[i].c_str(), "cn_", 3)) {
|
|
m_elType[i] = VCS_ELEM_TYPE_CHARGENEUTRALITY;
|
|
if (pub->m_elType[i] != VCS_ELEM_TYPE_CHARGENEUTRALITY) {
|
|
plogf("we have an inconsistency!\n");
|
|
exit(-1);
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Copy over the species names
|
|
*/
|
|
for (i = 0; i < nspecies; i++) {
|
|
m_speciesName[i] = pub->SpName[i];
|
|
}
|
|
/*
|
|
* Copy over all of the phase information
|
|
* Use the object's assignment operator
|
|
*/
|
|
for (iph = 0; iph < nph; iph++) {
|
|
*(m_VolPhaseList[iph]) = *(pub->VPhaseList[iph]);
|
|
/*
|
|
* Fix up the species thermo pointer in the vcs_SpeciesThermo object
|
|
* It should point to the species thermo pointer in the private
|
|
* data space.
|
|
*/
|
|
Vphase = m_VolPhaseList[iph];
|
|
for (int k = 0; k < Vphase->nSpecies(); k++) {
|
|
vcs_SpeciesProperties *sProp = Vphase->speciesProperty(k);
|
|
int kT = Vphase->spGlobalIndexVCS(k);
|
|
sProp->SpeciesThermo = m_speciesThermoList[kT];
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Specify the Activity Convention information
|
|
*/
|
|
for (iph = 0; iph < nph; iph++) {
|
|
Vphase = m_VolPhaseList[iph];
|
|
m_phaseActConvention[iph] = Vphase->p_activityConvention;
|
|
if (Vphase->p_activityConvention != 0) {
|
|
/*
|
|
* We assume here that species 0 is the solvent.
|
|
* The solvent isn't on a unity activity basis
|
|
* The activity for the solvent assumes that the
|
|
* it goes to one as the species mole fraction goes to
|
|
* one; i.e., it's really on a molarity framework.
|
|
* So SpecLnMnaught[iSolvent] = 0.0, and the
|
|
* loop below starts at 1, not 0.
|
|
*/
|
|
int iSolvent = Vphase->spGlobalIndexVCS(0);
|
|
double mnaught = m_wtSpecies[iSolvent] / 1000.;
|
|
for (int k = 1; k < Vphase->nSpecies(); k++) {
|
|
int kspec = Vphase->spGlobalIndexVCS(k);
|
|
m_actConventionSpecies[kspec] = Vphase->p_activityConvention;
|
|
m_lnMnaughtSpecies[kspec] = log(mnaught);
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Copy the title info
|
|
*/
|
|
if (pub->Title.size() == 0) {
|
|
m_title = "Unspecified Problem Title";
|
|
} else {
|
|
m_title = pub->Title;
|
|
}
|
|
|
|
/*
|
|
* Copy the volume info
|
|
*/
|
|
m_totalVol = pub->Vol;
|
|
if (m_PMVolumeSpecies.size() != 0) {
|
|
vcs_dcopy(VCS_DATA_PTR(m_PMVolumeSpecies), VCS_DATA_PTR(pub->VolPM), nspecies);
|
|
}
|
|
|
|
/*
|
|
* Return the success flag
|
|
*/
|
|
return VCS_SUCCESS;
|
|
}
|
|
/*****************************************************************************/
|
|
|
|
// Specify the problem to be solved using VCS_PROB, incrementally
|
|
/*
|
|
* Use the contents of the VCS_PROB to specify the contents of the
|
|
* private data, VCS_SOLVE.
|
|
*
|
|
* It's assumed we are solving the same problem.
|
|
*
|
|
* @param pub Pointer to VCS_PROdB that will be used to
|
|
* initialize the current equilibrium problem
|
|
*/
|
|
int VCS_SOLVE::vcs_prob_specify(const VCS_PROB *pub) {
|
|
int kspec, k, i, j, iph;
|
|
string yo("vcs_prob_specify ERROR: ");
|
|
int retn = VCS_SUCCESS;
|
|
bool status_change = false;
|
|
|
|
m_temperature = pub->T;
|
|
m_pressurePA = pub->PresPA;
|
|
m_VCS_UnitsFormat = pub->m_VCS_UnitsFormat;
|
|
m_doEstimateEquil = pub->iest;
|
|
|
|
m_totalVol = pub->Vol;
|
|
|
|
m_tolmaj = pub->tolmaj;
|
|
m_tolmin = pub->tolmin;
|
|
m_tolmaj2 = 0.01 * m_tolmaj;
|
|
m_tolmin2 = 0.01 * m_tolmin;
|
|
|
|
for (kspec = 0; kspec < m_numSpeciesTot; ++kspec) {
|
|
k = m_speciesMapIndex[kspec];
|
|
m_molNumSpecies_old[kspec] = pub->w[k];
|
|
m_molNumSpecies_new[kspec] = pub->mf[k];
|
|
m_feSpecies_old[kspec] = pub->m_gibbsSpecies[k];
|
|
}
|
|
|
|
/*
|
|
* Transfer the element abundance goals to the solve object
|
|
*/
|
|
for (i = 0; i < m_numElemConstraints; i++) {
|
|
j = m_elementMapIndex[i];
|
|
m_elemAbundancesGoal[i] = pub->gai[j];
|
|
}
|
|
|
|
/*
|
|
* Try to do the best job at guessing at the title
|
|
*/
|
|
if (pub->Title.size() == 0) {
|
|
if (m_title.size() == 0) {
|
|
m_title = "Unspecified Problem Title";
|
|
}
|
|
} else {
|
|
m_title = pub->Title;
|
|
}
|
|
|
|
/*
|
|
* Copy over the phase information.
|
|
* -> For each entry in the phase structure, determine
|
|
* if that entry can change from its initial value
|
|
* Either copy over the new value or create an error
|
|
* condition.
|
|
*/
|
|
|
|
for (iph = 0; iph < m_numPhases; iph++) {
|
|
vcs_VolPhase *vPhase = m_VolPhaseList[iph];
|
|
vcs_VolPhase *pub_phase_ptr = pub->VPhaseList[iph];
|
|
|
|
if (vPhase->VP_ID != pub_phase_ptr->VP_ID) {
|
|
plogf("%sPhase numbers have changed:%d %d\n", yo.c_str(),
|
|
vPhase->VP_ID, pub_phase_ptr->VP_ID);
|
|
retn = VCS_PUB_BAD;
|
|
}
|
|
|
|
if (vPhase->m_singleSpecies != pub_phase_ptr->m_singleSpecies) {
|
|
plogf("%sSingleSpecies value have changed:%d %d\n", yo.c_str(),
|
|
vPhase->m_singleSpecies,
|
|
pub_phase_ptr->m_singleSpecies);
|
|
retn = VCS_PUB_BAD;
|
|
}
|
|
|
|
if (vPhase->m_gasPhase != pub_phase_ptr->m_gasPhase) {
|
|
plogf("%sGasPhase value have changed:%d %d\n", yo.c_str(),
|
|
vPhase->m_gasPhase,
|
|
pub_phase_ptr->m_gasPhase);
|
|
retn = VCS_PUB_BAD;
|
|
}
|
|
|
|
vPhase->m_eqnState = pub_phase_ptr->m_eqnState;
|
|
|
|
if (vPhase->nSpecies() != pub_phase_ptr->nSpecies()) {
|
|
plogf("%sNVolSpecies value have changed:%d %d\n", yo.c_str(),
|
|
vPhase->nSpecies(),
|
|
pub_phase_ptr->nSpecies());
|
|
retn = VCS_PUB_BAD;
|
|
}
|
|
|
|
if (vPhase->PhaseName == pub_phase_ptr->PhaseName) {
|
|
plogf("%sPhaseName value have changed:%s %s\n", yo.c_str(),
|
|
vPhase->PhaseName.c_str(),
|
|
pub_phase_ptr->PhaseName.c_str());
|
|
retn = VCS_PUB_BAD;
|
|
}
|
|
|
|
if (vPhase->totalMolesInert() != pub_phase_ptr->totalMolesInert()) {
|
|
status_change = true;
|
|
}
|
|
/*
|
|
* Copy over the number of inert moles if it has changed.
|
|
*/
|
|
TPhInertMoles[iph] = pub_phase_ptr->totalMolesInert();
|
|
vPhase->setTotalMolesInert(pub_phase_ptr->totalMolesInert());
|
|
if (TPhInertMoles[iph] > 0.0) {
|
|
vPhase->setExistence(2);
|
|
vPhase->m_singleSpecies = FALSE;
|
|
}
|
|
|
|
/*
|
|
* Copy over the interfacial potential
|
|
*/
|
|
double phi = pub_phase_ptr->electricPotential();
|
|
vPhase->setElectricPotential(phi);
|
|
}
|
|
|
|
|
|
if (status_change) vcs_SSPhase();
|
|
/*
|
|
* Calculate the total number of moles in all phases.
|
|
*/
|
|
vcs_tmoles();
|
|
|
|
return retn;
|
|
}
|
|
/*****************************************************************************/
|
|
|
|
// Transfer the results of the equilibrium calculation back to VCS_PROB
|
|
/*
|
|
* The VCS_PUB structure is returned to the user.
|
|
*
|
|
* @param pub Pointer to VCS_PROB that will get the results of the
|
|
* equilibrium calculation transfered to it.
|
|
*/
|
|
int VCS_SOLVE::vcs_prob_update(VCS_PROB *pub) {
|
|
int i, j, l;
|
|
int k1 = 0;
|
|
|
|
vcs_tmoles();
|
|
m_totalVol = vcs_VolTotal(m_temperature, m_pressurePA,
|
|
VCS_DATA_PTR(m_molNumSpecies_old), VCS_DATA_PTR(m_PMVolumeSpecies));
|
|
|
|
for (i = 0; i < m_numSpeciesTot; ++i) {
|
|
/*
|
|
* Find the index of I in the index vector, m_speciesIndexVector[].
|
|
* Call it K1 and continue.
|
|
*/
|
|
for (j = 0; j < m_numSpeciesTot; ++j) {
|
|
l = m_speciesMapIndex[j];
|
|
k1 = j;
|
|
if (l == i) break;
|
|
}
|
|
/*
|
|
* - Switch the species data back from K1 into I
|
|
*/
|
|
if (pub->SpeciesUnknownType[i] != VCS_SPECIES_TYPE_INTERFACIALVOLTAGE) {
|
|
pub->w[i] = m_molNumSpecies_old[k1];
|
|
} else {
|
|
pub->w[i] = 0.0;
|
|
plogf("voltage species = %g\n", m_molNumSpecies_old[k1]);
|
|
}
|
|
pub->mf[i] = m_molNumSpecies_new[k1];
|
|
pub->m_gibbsSpecies[i] = m_feSpecies_old[k1];
|
|
pub->VolPM[i] = m_PMVolumeSpecies[k1];
|
|
}
|
|
|
|
pub->T = m_temperature;
|
|
pub->PresPA = m_pressurePA;
|
|
pub->Vol = m_totalVol;
|
|
int kT = 0;
|
|
for (int iph = 0; iph < pub->NPhase; iph++) {
|
|
vcs_VolPhase *pubPhase = pub->VPhaseList[iph];
|
|
vcs_VolPhase *vPhase = m_VolPhaseList[iph];
|
|
pubPhase->setTotalMolesInert(vPhase->totalMolesInert());
|
|
pubPhase->setTotalMoles(vPhase->TotalMoles());
|
|
pubPhase->setElectricPotential(vPhase->electricPotential());
|
|
double sumMoles = pubPhase->totalMolesInert();
|
|
pubPhase->setMoleFractions(VCS_DATA_PTR(vPhase->moleFractions()));
|
|
for (int k = 0; k < pubPhase->nSpecies(); k++) {
|
|
kT = pubPhase->spGlobalIndexVCS(k);
|
|
|
|
if (pubPhase->phiVarIndex() == k) {
|
|
k1 = vPhase->spGlobalIndexVCS(k);
|
|
double tmp = m_molNumSpecies_old[k1];
|
|
if (! vcs_doubleEqual( pubPhase->electricPotential() , tmp)) {
|
|
plogf("We have an inconsistency in voltage, %g, %g\n",
|
|
pubPhase->electricPotential(), tmp);
|
|
exit(-1);
|
|
}
|
|
}
|
|
|
|
if (! vcs_doubleEqual( pub->mf[kT], vPhase->molefraction(k))) {
|
|
plogf("We have an inconsistency in mole fraction, %g, %g\n",
|
|
pub->mf[kT], vPhase->molefraction(k));
|
|
exit(-1);
|
|
}
|
|
if (pubPhase->speciesUnknownType(k) != VCS_SPECIES_TYPE_INTERFACIALVOLTAGE) {
|
|
sumMoles += pub->w[kT];
|
|
}
|
|
}
|
|
if (! vcs_doubleEqual(sumMoles, vPhase->TotalMoles())) {
|
|
plogf("We have an inconsistency in total moles, %g %g\n",
|
|
sumMoles, pubPhase->TotalMoles());
|
|
exit(-1);
|
|
}
|
|
|
|
}
|
|
|
|
pub->m_Iterations = m_VCount->Its;
|
|
pub->m_NumBasisOptimizations = m_VCount->Basis_Opts;
|
|
|
|
return VCS_SUCCESS;
|
|
}
|
|
/*****************************************************************************/
|
|
|
|
// Initialize the internal counters
|
|
/*
|
|
* Initialize the internal counters containing the subroutine call
|
|
* values and times spent in the subroutines.
|
|
*
|
|
* ifunc = 0 Initialize only those counters appropriate for the top of
|
|
* vcs_solve_TP().
|
|
* = 1 Initialize all counters.
|
|
*/
|
|
void VCS_SOLVE::vcs_counters_init(int ifunc) {
|
|
m_VCount->Its = 0;
|
|
m_VCount->Basis_Opts = 0;
|
|
m_VCount->Time_vcs_TP = 0.0;
|
|
m_VCount->Time_basopt = 0.0;
|
|
if (ifunc) {
|
|
m_VCount->T_Its = 0;
|
|
m_VCount->T_Basis_Opts = 0;
|
|
m_VCount->T_Calls_Inest = 0;
|
|
m_VCount->T_Calls_vcs_TP = 0;
|
|
m_VCount->T_Time_vcs_TP = 0.0;
|
|
m_VCount->T_Time_basopt = 0.0;
|
|
m_VCount->T_Time_inest = 0.0;
|
|
m_VCount->T_Time_vcs = 0.0;
|
|
}
|
|
}
|
|
/**************************************************************************/
|
|
|
|
// Calculation of the total volume and the partial molar volumes
|
|
/*
|
|
* This function calculates the partial molar volume
|
|
* for all species, kspec, in the thermo problem
|
|
* at the temperature TKelvin and pressure, Pres, pres is in atm.
|
|
* And, it calculates the total volume of the combined system.
|
|
*
|
|
* Input
|
|
* ---------------
|
|
* @param tkelvin Temperature in kelvin()
|
|
* @param pres Pressure in Pascal
|
|
* @param w w[] is thevector containing the current mole numbers
|
|
* in units of kmol.
|
|
*
|
|
* Output
|
|
* ----------------
|
|
* @param volPM[] For species in all phase, the entries are the
|
|
* partial molar volumes units of M**3 / kmol.
|
|
*
|
|
* @return The return value is the total volume of
|
|
* the entire system in units of m**3.
|
|
*/
|
|
double VCS_SOLVE::vcs_VolTotal(const double tkelvin, const double pres,
|
|
const double w[], double volPM[]) {
|
|
double VolTot = 0.0;
|
|
for (int iphase = 0; iphase < m_numPhases; iphase++) {
|
|
vcs_VolPhase *Vphase = m_VolPhaseList[iphase];
|
|
Vphase->setState_TP(tkelvin, pres);
|
|
Vphase->setMolesFromVCS(VCS_STATECALC_OLD, w);
|
|
double Volp = Vphase->sendToVCS_VolPM(volPM);
|
|
VolTot += Volp;
|
|
}
|
|
return VolTot;
|
|
}
|
|
/****************************************************************************/
|
|
|
|
|
|
}
|