958 lines
30 KiB
C++
958 lines
30 KiB
C++
/*!
|
|
* @file vcs_solve.cpp Implementation file for the internal class that holds
|
|
* the problem.
|
|
*/
|
|
/*
|
|
* 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_solve.h"
|
|
#include "cantera/base/ctexceptions.h"
|
|
#include "cantera/base/stringUtils.h"
|
|
#include "cantera/equil/vcs_prob.h"
|
|
#include "cantera/equil/vcs_VolPhase.h"
|
|
#include "cantera/equil/vcs_species_thermo.h"
|
|
#include "cantera/base/clockWC.h"
|
|
|
|
using namespace std;
|
|
|
|
namespace Cantera
|
|
{
|
|
|
|
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_numRxnRdc(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(ElectronCharge * Avogadro),
|
|
m_VCount(0),
|
|
m_debug_print_lvl(0),
|
|
m_timing_print_lvl(1),
|
|
m_VCS_UnitsFormat(VCS_UNITS_UNITLESS)
|
|
{
|
|
}
|
|
|
|
void VCS_SOLVE::vcs_initSizes(const size_t nspecies0, const size_t nelements,
|
|
const size_t 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;
|
|
|
|
string ser = "VCS_SOLVE: ERROR:\n\t";
|
|
if (nspecies0 <= 0) {
|
|
plogf("%s Number of species is nonpositive\n", ser);
|
|
throw CanteraError("VCS_SOLVE()", ser +
|
|
" Number of species is nonpositive\n");
|
|
}
|
|
if (nelements <= 0) {
|
|
plogf("%s Number of elements is nonpositive\n", ser);
|
|
throw CanteraError("VCS_SOLVE()", ser +
|
|
" Number of species is nonpositive\n");
|
|
}
|
|
if (nphase0 <= 0) {
|
|
plogf("%s Number of phases is nonpositive\n", ser);
|
|
throw CanteraError("VCS_SOLVE()", ser +
|
|
" Number of species is nonpositive\n");
|
|
}
|
|
|
|
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(nelements, nspecies0, 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(nphase0, nspecies0, 0.0);
|
|
m_phaseParticipation.resize(nphase0, nspecies0, 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_Deficient.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(nspecies0, nelements);
|
|
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_speciesStatus.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 (size_t iph = 0; iph < nphase0; iph++) {
|
|
m_VolPhaseList[iph] = new vcs_VolPhase(this);
|
|
}
|
|
|
|
/*
|
|
* For Future expansion
|
|
*/
|
|
m_useActCoeffJac = true;
|
|
if (m_useActCoeffJac) {
|
|
m_np_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;
|
|
}
|
|
|
|
VCS_SOLVE::~VCS_SOLVE()
|
|
{
|
|
vcs_delete_memory();
|
|
}
|
|
|
|
void VCS_SOLVE::vcs_delete_memory()
|
|
{
|
|
size_t nspecies = m_numSpeciesTot;
|
|
|
|
for (size_t j = 0; j < m_numPhases; j++) {
|
|
delete m_VolPhaseList[j];
|
|
m_VolPhaseList[j] = 0;
|
|
}
|
|
|
|
for (size_t 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;
|
|
}
|
|
|
|
int VCS_SOLVE::vcs(VCS_PROB* vprob, int ifunc, int ipr, int ip1, int maxit)
|
|
{
|
|
int retn = 0, iconv = 0;
|
|
clockWC tickTock;
|
|
|
|
int iprintTime = std::max(ipr, ip1);
|
|
iprintTime = std::min(iprintTime, m_timing_print_lvl);
|
|
|
|
if (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.
|
|
*/
|
|
size_t nspecies0 = vprob->nspecies + 10;
|
|
size_t nelements0 = vprob->ne;
|
|
size_t 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;
|
|
}
|
|
|
|
int VCS_SOLVE::vcs_prob_specifyFully(const VCS_PROB* pub)
|
|
{
|
|
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
|
|
*/
|
|
size_t nspecies = pub->nspecies;
|
|
if (NSPECIES0 < nspecies) {
|
|
plogf("%sPrivate Data is dimensioned too small\n", ser);
|
|
return VCS_PUB_BAD;
|
|
}
|
|
size_t nph = pub->NPhase;
|
|
if (NPHASE0 < nph) {
|
|
plogf("%sPrivate Data is dimensioned too small\n", ser);
|
|
return VCS_PUB_BAD;
|
|
}
|
|
size_t 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
|
|
* Note, it's possible that the number of elements is greater than
|
|
* the number of species. In that case set the number of reactions
|
|
* to zero.
|
|
*/
|
|
if (nelements > nspecies) {
|
|
m_numRxnTot = 0;
|
|
} else {
|
|
m_numRxnTot = nspecies - nelements;
|
|
}
|
|
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 = std::min(2, pub->vcs_debug_print_lvl);
|
|
#endif
|
|
|
|
/*
|
|
* FormulaMatrix[] -> Copy the formula matrix over
|
|
*/
|
|
for (size_t i = 0; i < nspecies; i++) {
|
|
bool nonzero = false;
|
|
for (size_t j = 0; j < nelements; j++) {
|
|
if (pub->FormulaMatrix(i,j) != 0.0) {
|
|
nonzero = true;
|
|
}
|
|
m_formulaMatrix(i,j) = pub->FormulaMatrix(i,j);
|
|
}
|
|
if (!nonzero) {
|
|
plogf("vcs_prob_specifyFully:: species %d %s has a zero formula matrix!\n", i,
|
|
pub->SpName[i]);
|
|
return VCS_PUB_BAD;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Copy over the species molecular weights
|
|
*/
|
|
m_wtSpecies = pub->WtSpecies;
|
|
|
|
/*
|
|
* Copy over the charges
|
|
*/
|
|
m_chargeSpecies = pub->Charge;
|
|
|
|
/*
|
|
* Malloc and Copy the VCS_SPECIES_THERMO structures
|
|
*
|
|
*/
|
|
for (size_t kspec = 0; kspec < nspecies; kspec++) {
|
|
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
|
|
*/
|
|
m_speciesUnknownType = pub->SpeciesUnknownType;
|
|
|
|
/*
|
|
* 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) {
|
|
m_molNumSpecies_old = pub->w;
|
|
} else {
|
|
m_doEstimateEquil = -1;
|
|
m_molNumSpecies_old.assign(m_molNumSpecies_old.size(), 0.0);
|
|
}
|
|
|
|
/*
|
|
* Formulate the Goal Element Abundance Vector
|
|
*/
|
|
if (pub->gai.size() != 0) {
|
|
for (size_t i = 0; i < nelements; i++) {
|
|
m_elemAbundancesGoal[i] = pub->gai[i];
|
|
if (pub->m_elType[i] == VCS_ELEM_TYPE_LATTICERATIO && m_elemAbundancesGoal[i] < 1.0E-10) {
|
|
m_elemAbundancesGoal[i] = 0.0;
|
|
}
|
|
}
|
|
} else {
|
|
if (m_doEstimateEquil == 0) {
|
|
double sum = 0;
|
|
for (size_t j = 0; j < nelements; j++) {
|
|
m_elemAbundancesGoal[j] = 0.0;
|
|
for (size_t kspec = 0; kspec < nspecies; kspec++) {
|
|
if (m_speciesUnknownType[kspec] != VCS_SPECIES_TYPE_INTERFACIALVOLTAGE) {
|
|
sum += m_molNumSpecies_old[kspec];
|
|
m_elemAbundancesGoal[j] += m_formulaMatrix(kspec,j) * m_molNumSpecies_old[kspec];
|
|
}
|
|
}
|
|
if (pub->m_elType[j] == VCS_ELEM_TYPE_LATTICERATIO && m_elemAbundancesGoal[j] < 1.0E-10 * sum) {
|
|
m_elemAbundancesGoal[j] = 0.0;
|
|
}
|
|
}
|
|
} 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 = OneAtm;
|
|
}
|
|
/*
|
|
* TPhInertMoles[] -> must be copied over here
|
|
*/
|
|
for (size_t iph = 0; iph < nph; iph++) {
|
|
vcs_VolPhase* 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 (size_t i = 0; i < nspecies; i++) {
|
|
m_speciesMapIndex[i] = i;
|
|
}
|
|
|
|
/*
|
|
* IndEl[] is an index variable that keep track of element vector
|
|
* rotations.
|
|
*/
|
|
for (size_t i = 0; i < nelements; i++) {
|
|
m_elementMapIndex[i] = i;
|
|
}
|
|
|
|
/*
|
|
* Define all species to be major species, initially.
|
|
*/
|
|
for (size_t i = 0; i < nspecies; i++) {
|
|
m_speciesStatus[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<size_t> numPhSp(nph, 0);
|
|
for (size_t kspec = 0; kspec < nspecies; kspec++) {
|
|
size_t iph = pub->PhaseID[kspec];
|
|
if (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 (size_t iph = 0; iph < nph; iph++) {
|
|
vcs_VolPhase* 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);
|
|
return VCS_PUB_BAD;
|
|
}
|
|
}
|
|
} else {
|
|
if (m_numPhases == 1) {
|
|
for (size_t 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 and types
|
|
*/
|
|
for (size_t 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) {
|
|
throw CanteraError("VCS_SOLVE::vcs_prob_specifyFully",
|
|
"we have an inconsistency!");
|
|
}
|
|
}
|
|
}
|
|
|
|
for (size_t i = 0; i < nelements; i++) {
|
|
if (m_elType[i] == VCS_ELEM_TYPE_CHARGENEUTRALITY) {
|
|
if (m_elemAbundancesGoal[i] != 0.0) {
|
|
if (fabs(m_elemAbundancesGoal[i]) > 1.0E-9) {
|
|
throw CanteraError("VCS_SOLVE::vcs_prob_specifyFully",
|
|
"Charge neutrality condition " + m_elementName[i] +
|
|
" is signicantly nonzero, " + fp2str(m_elemAbundancesGoal[i]) +
|
|
". Giving up");
|
|
} else {
|
|
if (m_debug_print_lvl >= 2) {
|
|
plogf("Charge neutrality condition %s not zero, %g. Setting it zero\n",
|
|
m_elementName[i], m_elemAbundancesGoal[i]);
|
|
}
|
|
m_elemAbundancesGoal[i] = 0.0;
|
|
}
|
|
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Copy over the species names
|
|
*/
|
|
for (size_t 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 (size_t 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.
|
|
*/
|
|
vcs_VolPhase* Vphase = m_VolPhaseList[iph];
|
|
for (size_t k = 0; k < Vphase->nSpecies(); k++) {
|
|
vcs_SpeciesProperties* sProp = Vphase->speciesProperty(k);
|
|
size_t kT = Vphase->spGlobalIndexVCS(k);
|
|
sProp->SpeciesThermo = m_speciesThermoList[kT];
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Specify the Activity Convention information
|
|
*/
|
|
for (size_t iph = 0; iph < nph; iph++) {
|
|
vcs_VolPhase* 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.
|
|
*/
|
|
size_t iSolvent = Vphase->spGlobalIndexVCS(0);
|
|
double mnaught = m_wtSpecies[iSolvent] / 1000.;
|
|
for (size_t k = 1; k < Vphase->nSpecies(); k++) {
|
|
size_t 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) {
|
|
m_PMVolumeSpecies = pub->VolPM;
|
|
}
|
|
|
|
/*
|
|
* Return the success flag
|
|
*/
|
|
return VCS_SUCCESS;
|
|
}
|
|
|
|
int VCS_SOLVE::vcs_prob_specify(const VCS_PROB* pub)
|
|
{
|
|
string yo("vcs_prob_specify ERROR: ");
|
|
int retn = VCS_SUCCESS;
|
|
|
|
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 (size_t kspec = 0; kspec < m_numSpeciesTot; ++kspec) {
|
|
size_t 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 (size_t i = 0; i < m_numElemConstraints; i++) {
|
|
size_t 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.
|
|
*/
|
|
|
|
bool status_change = false;
|
|
for (size_t 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, 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, 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, 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, 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, vPhase->PhaseName, pub_phase_ptr->PhaseName);
|
|
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;
|
|
}
|
|
|
|
int VCS_SOLVE::vcs_prob_update(VCS_PROB* pub)
|
|
{
|
|
size_t k1 = 0;
|
|
vcs_tmoles();
|
|
m_totalVol = vcs_VolTotal(m_temperature, m_pressurePA,
|
|
&m_molNumSpecies_old[0], &m_PMVolumeSpecies[0]);
|
|
|
|
for (size_t i = 0; i < m_numSpeciesTot; ++i) {
|
|
/*
|
|
* Find the index of I in the index vector, m_speciesIndexVector[].
|
|
* Call it K1 and continue.
|
|
*/
|
|
for (size_t j = 0; j < m_numSpeciesTot; ++j) {
|
|
k1 = j;
|
|
if (m_speciesMapIndex[j] == 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;
|
|
}
|
|
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;
|
|
size_t kT = 0;
|
|
for (size_t 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->setMoleFractionsState(vPhase->totalMoles(),
|
|
&vPhase->moleFractions()[0],
|
|
VCS_STATECALC_TMP);
|
|
const vector_fp & mfVector = pubPhase->moleFractions();
|
|
for (size_t k = 0; k < pubPhase->nSpecies(); k++) {
|
|
kT = pubPhase->spGlobalIndexVCS(k);
|
|
pub->mf[kT] = mfVector[k];
|
|
if (pubPhase->phiVarIndex() == k) {
|
|
k1 = vPhase->spGlobalIndexVCS(k);
|
|
double tmp = m_molNumSpecies_old[k1];
|
|
if (! vcs_doubleEqual(pubPhase->electricPotential() , tmp)) {
|
|
throw CanteraError("VCS_SOLVE::vcs_prob_update",
|
|
"We have an inconsistency in voltage, " +
|
|
fp2str(pubPhase->electricPotential()) + " " +
|
|
fp2str(tmp));
|
|
}
|
|
}
|
|
|
|
if (! vcs_doubleEqual(pub->mf[kT], vPhase->molefraction(k))) {
|
|
throw CanteraError("VCS_SOLVE::vcs_prob_update",
|
|
"We have an inconsistency in mole fraction, " +
|
|
fp2str(pub->mf[kT]) + " " + fp2str(vPhase->molefraction(k)));
|
|
}
|
|
if (pubPhase->speciesUnknownType(k) != VCS_SPECIES_TYPE_INTERFACIALVOLTAGE) {
|
|
sumMoles += pub->w[kT];
|
|
}
|
|
}
|
|
if (! vcs_doubleEqual(sumMoles, vPhase->totalMoles())) {
|
|
throw CanteraError("VCS_SOLVE::vcs_prob_update",
|
|
"We have an inconsistency in total moles, " +
|
|
fp2str(sumMoles) + " " + fp2str(pubPhase->totalMoles()));
|
|
}
|
|
}
|
|
|
|
pub->m_Iterations = m_VCount->Its;
|
|
pub->m_NumBasisOptimizations = m_VCount->Basis_Opts;
|
|
return VCS_SUCCESS;
|
|
}
|
|
|
|
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;
|
|
}
|
|
}
|
|
|
|
double VCS_SOLVE::vcs_VolTotal(const double tkelvin, const double pres,
|
|
const double w[], double volPM[])
|
|
{
|
|
double VolTot = 0.0;
|
|
for (size_t 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;
|
|
}
|
|
|
|
}
|