/*! * This is done by running * each reaction as far forward or backward as possible, subject * to the constraint that all mole numbers remain * non-negative. Reactions for which \f$ \Delta \mu^0 \f$ are * positive are run in reverse, and ones for which it is negative * are run in the forward direction. The end result is equivalent * to solving the linear programming problem of minimizing the * linear Gibbs function subject to the element and * non-negativity constraints. */ int VCS_SOLVE::vcs_setMolesLinProg() { int ik, irxn; double test = -1.0E-10; #ifdef DEBUG_MODE std::string pprefix(" --- seMolesLinProg "); if (m_debug_print_lvl >= 2) { plogf(" --- call setInitialMoles\n"); } #endif // m_mu are standard state chemical potentials // Boolean on the end specifies standard chem potentials // m_mix->getValidChemPotentials(not_mu, DATA_PTR(m_mu), true); // -> This is already done coming into the routine. double dg_rt; int idir; double nu; double delta_xi, dxi_min = 1.0e10; bool redo = true; int jcomp; int retn; int iter = 0; bool abundancesOK = true; int usedZeroedSpecies; std::vector<double> sm(m_numElemConstraints*m_numElemConstraints, 0.0); std::vector<double> ss(m_numElemConstraints, 0.0); std::vector<double> sa(m_numElemConstraints, 0.0); std::vector<double> wx(m_numElemConstraints, 0.0); std::vector<double> aw(m_numSpeciesTot, 0.0); for (ik = 0; ik < m_numSpeciesTot; ik++) { if (m_speciesUnknownType[ik] != VCS_SPECIES_INTERFACIALVOLTAGE) { m_molNumSpecies_old[ik] = MAX(0.0, m_molNumSpecies_old[ik]); } } #ifdef DEBUG_MODE if (m_debug_print_lvl >= 2) { printProgress(m_speciesName, m_molNumSpecies_old, m_SSfeSpecies); } #endif while (redo) { if (!vcs_elabcheck(0)) { #ifdef DEBUG_MODE if (m_debug_print_lvl >= 2) { plogf("%s Mole numbers failing element abundances\n", pprefix.c_str()); plogf("%sCall vcs_elcorr to attempt fix\n", pprefix.c_str()); } #endif retn = vcs_elcorr(VCS_DATA_PTR(sm), VCS_DATA_PTR(wx)); if (retn >= 2) { abundancesOK = false; } else { abundancesOK = true; } } else { abundancesOK = true; } /* * Now find the optimized basis that spans the stoichiometric * coefficient matrix, based on the current composition, m_molNumSpecies_old[] * We also calculate sc[][], the reaction matrix. */ retn = vcs_basopt(FALSE, VCS_DATA_PTR(aw), VCS_DATA_PTR(sa), VCS_DATA_PTR(sm), VCS_DATA_PTR(ss), test, &usedZeroedSpecies); if (retn != VCS_SUCCESS) return retn; #ifdef DEBUG_MODE if (m_debug_print_lvl >= 2) { plogf("iteration %d\n", iter); } #endif redo = false; iter++; if (iter > 15) break; // loop over all reactions for (irxn = 0; irxn < m_numRxnTot; irxn++) { // dg_rt is the Delta_G / RT value for the reaction ik = m_numComponents + irxn; dg_rt = m_SSfeSpecies[ik]; dxi_min = 1.0e10; const double *sc_irxn = m_stoichCoeffRxnMatrix[irxn]; for (jcomp = 0; jcomp < m_numElemConstraints; jcomp++) { dg_rt += m_SSfeSpecies[jcomp] * sc_irxn[jcomp]; } // fwd or rev direction. // idir > 0 implies increasing the current species // idir < 0 implies decreasing the current species idir = (dg_rt < 0.0 ? 1 : -1); if (idir < 0) { dxi_min = m_molNumSpecies_old[ik]; } for (jcomp = 0; jcomp < m_numComponents; jcomp++) { nu = sc_irxn[jcomp]; // set max change in progress variable by // non-negativity requirement if (nu*idir < 0) { delta_xi = fabs(m_molNumSpecies_old[jcomp]/nu); // if a component has nearly zero moles, redo // with a new set of components if (!redo) { if (delta_xi < 1.0e-10 && (m_molNumSpecies_old[ik] >= 1.0E-10)) { #ifdef DEBUG_MODE if (m_debug_print_lvl >= 2) { plogf(" --- Component too small: %s\n", m_speciesName[jcomp].c_str()); } #endif redo = true; } } if (delta_xi < dxi_min) dxi_min = delta_xi; } } // step the composition by dxi_min, check against zero, since // we are zeroing components and species on every step. // Redo the iteration, if a component went from positive to zero on this step. double dsLocal = idir*dxi_min; m_molNumSpecies_old[ik] += dsLocal; m_molNumSpecies_old[ik] = MAX(0.0, m_molNumSpecies_old[ik]); for (jcomp = 0; jcomp < m_numComponents; jcomp++) { bool full = false; if (m_molNumSpecies_old[jcomp] > 1.0E-15) { full = true; } m_molNumSpecies_old[jcomp] += sc_irxn[jcomp] * dsLocal; m_molNumSpecies_old[jcomp] = MAX(0.0, m_molNumSpecies_old[jcomp]); if (full) { if (m_molNumSpecies_old[jcomp] < 1.0E-60) { redo = true; } } } } // set the moles of the phase objects to match // updateMixMoles(); // Update the phase objects with the contents of the m_molNumSpecies_old vector // vcs_updateVP(0); #ifdef DEBUG_MODE if (m_debug_print_lvl >= 2) { printProgress(m_speciesName, m_molNumSpecies_old, m_SSfeSpecies); } #endif } #ifdef DEBUG_MODE if (m_debug_print_lvl == 1) { printProgress(m_speciesName, m_molNumSpecies_old, m_SSfeSpecies); plogf(" --- setInitialMoles end\n"); } #endif retn = 0; if (!abundancesOK) { retn = -1; } else if (iter > 15) { retn = 1; } return retn; }
/* * This routine is always followed by vcs_prep(). Therefore, tasks * that need to be done for every call to vcsc() should be placed in * vcs_prep() and not in this routine. * * The problem structure refers to: * * the number and identity of the species. * the formula matrix and thus the number of components. * the number and identity of the phases. * the equation of state * the method and parameters for determining the standard state * The method and parameters for determining the activity coefficients. * * Tasks: * 0) Fill in the SSPhase[] array. * 1) Check to see if any multispecies phases actually have only one * species in that phase. If true, reassign that phase and species * to be a single-species phase. * 2) Determine the number of components in the problem if not already * done so. During this process the order of the species is changed * in the private data structure. All references to the species * properties must employ the ind[] index vector. * * @param printLvl Print level of the routine * * @return the return code * VCS_SUCCESS = everything went OK * */ int VCS_SOLVE::vcs_prep_oneTime(int printLvl) { size_t kspec, i; int retn = VCS_SUCCESS; double pres, test; double *aw, *sa, *sm, *ss; bool modifiedSoln = false; bool conv; m_debug_print_lvl = printLvl; /* * Calculate the Single Species status of phases * Also calculate the number of species per phase */ vcs_SSPhase(); /* * Set an initial estimate for the number of noncomponent species * equal to nspecies - nelements. This may be changed below */ m_numRxnTot = m_numSpeciesTot - m_numElemConstraints; m_numRxnRdc = m_numRxnTot; m_numSpeciesRdc = m_numSpeciesTot; for (i = 0; i < m_numRxnRdc; ++i) { m_indexRxnToSpecies[i] = m_numElemConstraints + i; } for (kspec = 0; kspec < m_numSpeciesTot; ++kspec) { size_t pID = m_phaseID[kspec]; size_t spPhIndex = m_speciesLocalPhaseIndex[kspec]; vcs_VolPhase *vPhase = m_VolPhaseList[pID]; vcs_SpeciesProperties *spProp = vPhase->speciesProperty(spPhIndex); double sz = 0.0; size_t eSize = spProp->FormulaMatrixCol.size(); for (size_t e = 0; e < eSize; e++) { sz += fabs(spProp->FormulaMatrixCol[e]); } if (sz > 0.0) { m_spSize[kspec] = sz; } else { m_spSize[kspec] = 1.0; } } /* ***************************************************** */ /* **** DETERMINE THE NUMBER OF COMPONENTS ************* */ /* ***************************************************** */ /* * Obtain a valid estimate of the mole fraction. This will * be used as an initial ordering vector for prioritizing * which species are defined as components. * * If a mole number estimate was supplied from the * input file, use that mole number estimate. * * If a solution estimate wasn't supplied from the input file, * supply an initial estimate for the mole fractions * based on the relative reverse ordering of the * chemical potentials. * * For voltage unknowns, set these to zero for the moment. */ test = -1.0e-10; if (m_doEstimateEquil < 0) { double sum = 0.0; for (kspec = 0; kspec < m_numSpeciesTot; ++kspec) { if (m_speciesUnknownType[kspec] == VCS_SPECIES_TYPE_MOLNUM) { sum += fabs(m_molNumSpecies_old[kspec]); } } if (fabs(sum) < 1.0E-6) { modifiedSoln = true; if (m_pressurePA <= 0.0) pres = 1.01325E5; else pres = m_pressurePA; retn = vcs_evalSS_TP(0, 0, m_temperature, pres); for (kspec = 0; kspec < m_numSpeciesTot; ++kspec) { if (m_speciesUnknownType[kspec] == VCS_SPECIES_TYPE_MOLNUM) { m_molNumSpecies_old[kspec] = - m_SSfeSpecies[kspec]; } else { m_molNumSpecies_old[kspec] = 0.0; } } } test = -1.0e20; } /* * NC = number of components is in the vcs.h common block * This call to BASOPT doesn't calculate the stoichiometric * reaction matrix. */ std::vector<double> awSpace( m_numSpeciesTot + (m_numElemConstraints + 2)*(m_numElemConstraints), 0.0); aw = VCS_DATA_PTR(awSpace); if (aw == NULL) { plogf("vcs_prep_oneTime: failed to get memory: global bailout\n"); return VCS_NOMEMORY; } sa = aw + m_numSpeciesTot; sm = sa + m_numElemConstraints; ss = sm + (m_numElemConstraints)*(m_numElemConstraints); retn = vcs_basopt(true, aw, sa, sm, ss, test, &conv); if (retn != VCS_SUCCESS) { plogf("vcs_prep_oneTime:"); plogf(" Determination of number of components failed: %d\n", retn); plogf(" Global Bailout!\n"); return retn; } if (m_numElemConstraints != m_numComponents) { m_numRxnTot = m_numRxnRdc = m_numSpeciesTot - m_numComponents; for (i = 0; i < m_numRxnRdc; ++i) { m_indexRxnToSpecies[i] = m_numComponents + i; } } /* * The elements might need to be rearranged. */ awSpace.resize(m_numElemConstraints + (m_numElemConstraints + 2)*(m_numElemConstraints), 0.0); aw = VCS_DATA_PTR(awSpace); sa = aw + m_numElemConstraints; sm = sa + m_numElemConstraints; ss = sm + (m_numElemConstraints)*(m_numElemConstraints); retn = vcs_elem_rearrange(aw, sa, sm, ss); if (retn != VCS_SUCCESS) { plogf("vcs_prep_oneTime:"); plogf(" Determination of element reordering failed: %d\n", retn); plogf(" Global Bailout!\n"); return retn; } // If we mucked up the solution unknowns because they were all // zero to start with, set them back to zero here if (modifiedSoln) { for (kspec = 0; kspec < m_numSpeciesTot; ++kspec) { m_molNumSpecies_old[kspec] = 0.0; } } return VCS_SUCCESS; }
int VCS_SOLVE::vcs_prep(int printLvl) { int retn = VCS_SUCCESS; m_debug_print_lvl = printLvl; // Calculate the Single Species status of phases // Also calculate the number of species per phase vcs_SSPhase(); // Set an initial estimate for the number of noncomponent species equal to // nspecies - nelements. This may be changed below if (m_nelem > m_nsp) { m_numRxnTot = 0; } else { m_numRxnTot = m_nsp - m_nelem; } m_numRxnRdc = m_numRxnTot; m_numSpeciesRdc = m_nsp; for (size_t i = 0; i < m_numRxnRdc; ++i) { m_indexRxnToSpecies[i] = m_nelem + i; } for (size_t kspec = 0; kspec < m_nsp; ++kspec) { size_t pID = m_phaseID[kspec]; size_t spPhIndex = m_speciesLocalPhaseIndex[kspec]; vcs_VolPhase* vPhase = m_VolPhaseList[pID].get(); vcs_SpeciesProperties* spProp = vPhase->speciesProperty(spPhIndex); double sz = 0.0; size_t eSize = spProp->FormulaMatrixCol.size(); for (size_t e = 0; e < eSize; e++) { sz += fabs(spProp->FormulaMatrixCol[e]); } if (sz > 0.0) { m_spSize[kspec] = sz; } else { m_spSize[kspec] = 1.0; } } // DETERMINE THE NUMBER OF COMPONENTS // // Obtain a valid estimate of the mole fraction. This will be used as an // initial ordering vector for prioritizing which species are defined as // components. // // If a mole number estimate was supplied from the input file, use that mole // number estimate. // // If a solution estimate wasn't supplied from the input file, supply an // initial estimate for the mole fractions based on the relative reverse // ordering of the chemical potentials. // // For voltage unknowns, set these to zero for the moment. double test = -1.0e-10; bool modifiedSoln = false; if (m_doEstimateEquil < 0) { double sum = 0.0; for (size_t kspec = 0; kspec < m_nsp; ++kspec) { if (m_speciesUnknownType[kspec] == VCS_SPECIES_TYPE_MOLNUM) { sum += fabs(m_molNumSpecies_old[kspec]); } } if (fabs(sum) < 1.0E-6) { modifiedSoln = true; double pres = (m_pressurePA <= 0.0) ? 1.01325E5 : m_pressurePA; retn = vcs_evalSS_TP(0, 0, m_temperature, pres); for (size_t kspec = 0; kspec < m_nsp; ++kspec) { if (m_speciesUnknownType[kspec] == VCS_SPECIES_TYPE_MOLNUM) { m_molNumSpecies_old[kspec] = - m_SSfeSpecies[kspec]; } else { m_molNumSpecies_old[kspec] = 0.0; } } } test = -1.0e20; } // NC = number of components is in the vcs.h common block. This call to // BASOPT doesn't calculate the stoichiometric reaction matrix. vector_fp awSpace(m_nsp + (m_nelem + 2)*(m_nelem), 0.0); double* aw = &awSpace[0]; if (aw == NULL) { plogf("vcs_prep_oneTime: failed to get memory: global bailout\n"); return VCS_NOMEMORY; } double* sa = aw + m_nsp; double* sm = sa + m_nelem; double* ss = sm + m_nelem * m_nelem; bool conv; retn = vcs_basopt(true, aw, sa, sm, ss, test, &conv); if (retn != VCS_SUCCESS) { plogf("vcs_prep_oneTime:"); plogf(" Determination of number of components failed: %d\n", retn); plogf(" Global Bailout!\n"); return retn; } if (m_nsp >= m_numComponents) { m_numRxnTot = m_numRxnRdc = m_nsp - m_numComponents; for (size_t i = 0; i < m_numRxnRdc; ++i) { m_indexRxnToSpecies[i] = m_numComponents + i; } } else { m_numRxnTot = m_numRxnRdc = 0; } // The elements might need to be rearranged. awSpace.resize(m_nelem + (m_nelem + 2)*m_nelem, 0.0); aw = &awSpace[0]; sa = aw + m_nelem; sm = sa + m_nelem; ss = sm + m_nelem * m_nelem; retn = vcs_elem_rearrange(aw, sa, sm, ss); if (retn != VCS_SUCCESS) { plogf("vcs_prep_oneTime:"); plogf(" Determination of element reordering failed: %d\n", retn); plogf(" Global Bailout!\n"); return retn; } // If we mucked up the solution unknowns because they were all // zero to start with, set them back to zero here if (modifiedSoln) { for (size_t kspec = 0; kspec < m_nsp; ++kspec) { m_molNumSpecies_old[kspec] = 0.0; } } // Initialize various arrays in the data to zero m_feSpecies_old.assign(m_feSpecies_old.size(), 0.0); m_feSpecies_new.assign(m_feSpecies_new.size(), 0.0); m_molNumSpecies_new.assign(m_molNumSpecies_new.size(), 0.0); m_deltaMolNumPhase.zero(); m_phaseParticipation.zero(); m_deltaPhaseMoles.assign(m_deltaPhaseMoles.size(), 0.0); m_tPhaseMoles_new.assign(m_tPhaseMoles_new.size(), 0.0); // Calculate the total number of moles in all phases. vcs_tmoles(); // Check to see if the current problem is well posed. double sum = 0.0; for (size_t e = 0; e < m_nelem; e++) { sum += m_mix->elementMoles(e); } if (sum < 1.0E-20) { // Check to see if the current problem is well posed. plogf("vcs has determined the problem is not well posed: Bailing\n"); return VCS_PUB_BAD; } return VCS_SUCCESS; }
int VCS_SOLVE::vcs_setMolesLinProg() { size_t ik, irxn; double test = -1.0E-10; if (DEBUG_MODE_ENABLED && m_debug_print_lvl >= 2) { plogf(" --- call setInitialMoles\n"); } double dg_rt; int idir; double nu; double delta_xi, dxi_min = 1.0e10; bool redo = true; int retn; int iter = 0; bool abundancesOK = true; bool usedZeroedSpecies; vector_fp sm(m_numElemConstraints*m_numElemConstraints, 0.0); vector_fp ss(m_numElemConstraints, 0.0); vector_fp sa(m_numElemConstraints, 0.0); vector_fp wx(m_numElemConstraints, 0.0); vector_fp aw(m_numSpeciesTot, 0.0); for (ik = 0; ik < m_numSpeciesTot; ik++) { if (m_speciesUnknownType[ik] != VCS_SPECIES_INTERFACIALVOLTAGE) { m_molNumSpecies_old[ik] = max(0.0, m_molNumSpecies_old[ik]); } } if (DEBUG_MODE_ENABLED && m_debug_print_lvl >= 2) { printProgress(m_speciesName, m_molNumSpecies_old, m_SSfeSpecies); } while (redo) { if (!vcs_elabcheck(0)) { if (DEBUG_MODE_ENABLED && m_debug_print_lvl >= 2) { plogf(" --- seMolesLinProg Mole numbers failing element abundances\n"); plogf(" --- seMolesLinProg Call vcs_elcorr to attempt fix\n"); } retn = vcs_elcorr(&sm[0], &wx[0]); if (retn >= 2) { abundancesOK = false; } else { abundancesOK = true; } } else { abundancesOK = true; } /* * Now find the optimized basis that spans the stoichiometric * coefficient matrix, based on the current composition, m_molNumSpecies_old[] * We also calculate sc[][], the reaction matrix. */ retn = vcs_basopt(false, &aw[0], &sa[0], &sm[0], &ss[0], test, &usedZeroedSpecies); if (retn != VCS_SUCCESS) { return retn; } if (DEBUG_MODE_ENABLED && m_debug_print_lvl >= 2) { plogf("iteration %d\n", iter); } redo = false; iter++; if (iter > 15) { break; } // loop over all reactions for (irxn = 0; irxn < m_numRxnTot; irxn++) { // dg_rt is the Delta_G / RT value for the reaction ik = m_numComponents + irxn; dg_rt = m_SSfeSpecies[ik]; dxi_min = 1.0e10; const double* sc_irxn = m_stoichCoeffRxnMatrix.ptrColumn(irxn); for (size_t jcomp = 0; jcomp < m_numElemConstraints; jcomp++) { dg_rt += m_SSfeSpecies[jcomp] * sc_irxn[jcomp]; } // fwd or rev direction. // idir > 0 implies increasing the current species // idir < 0 implies decreasing the current species idir = (dg_rt < 0.0 ? 1 : -1); if (idir < 0) { dxi_min = m_molNumSpecies_old[ik]; } for (size_t jcomp = 0; jcomp < m_numComponents; jcomp++) { nu = sc_irxn[jcomp]; // set max change in progress variable by // non-negativity requirement if (nu*idir < 0) { delta_xi = fabs(m_molNumSpecies_old[jcomp]/nu); // if a component has nearly zero moles, redo // with a new set of components if (!redo && delta_xi < 1.0e-10 && (m_molNumSpecies_old[ik] >= 1.0E-10)) { if (DEBUG_MODE_ENABLED && m_debug_print_lvl >= 2) { plogf(" --- Component too small: %s\n", m_speciesName[jcomp]); } redo = true; } dxi_min = std::min(dxi_min, delta_xi); } } // step the composition by dxi_min, check against zero, since // we are zeroing components and species on every step. // Redo the iteration, if a component went from positive to zero on this step. double dsLocal = idir*dxi_min; m_molNumSpecies_old[ik] += dsLocal; m_molNumSpecies_old[ik] = max(0.0, m_molNumSpecies_old[ik]); for (size_t jcomp = 0; jcomp < m_numComponents; jcomp++) { bool full = false; if (m_molNumSpecies_old[jcomp] > 1.0E-15) { full = true; } m_molNumSpecies_old[jcomp] += sc_irxn[jcomp] * dsLocal; m_molNumSpecies_old[jcomp] = max(0.0, m_molNumSpecies_old[jcomp]); if (full && m_molNumSpecies_old[jcomp] < 1.0E-60) { redo = true; } } } if (DEBUG_MODE_ENABLED && m_debug_print_lvl >= 2) { printProgress(m_speciesName, m_molNumSpecies_old, m_SSfeSpecies); } } if (DEBUG_MODE_ENABLED && m_debug_print_lvl == 1) { printProgress(m_speciesName, m_molNumSpecies_old, m_SSfeSpecies); plogf(" --- setInitialMoles end\n"); } retn = 0; if (!abundancesOK) { retn = -1; } else if (iter > 15) { retn = 1; } return retn; }
void VCS_SOLVE::vcs_inest(double* const aw, double* const sa, double* const sm, double* const ss, double test) { size_t nspecies = m_numSpeciesTot; size_t nrxn = m_numRxnTot; /* * CALL ROUTINE TO SOLVE MAX(CC*molNum) SUCH THAT AX*molNum = BB * AND molNum(I) .GE. 0.0 * * Note, both of these programs do this. */ vcs_setMolesLinProg(); if (DEBUG_MODE_ENABLED && m_debug_print_lvl >= 2) { plogf("%s Mole Numbers returned from linear programming (vcs_inest initial guess):\n", pprefix); plogf("%s SPECIES MOLE_NUMBER -SS_ChemPotential\n", pprefix); for (size_t kspec = 0; kspec < nspecies; ++kspec) { plogf("%s ", pprefix); plogf("%-12.12s", m_speciesName[kspec].c_str()); plogf(" %15.5g %12.3g\n", m_molNumSpecies_old[kspec], -m_SSfeSpecies[kspec]); } plogf("%s Element Abundance Agreement returned from linear " "programming (vcs_inest initial guess):", pprefix); plogendl(); plogf("%s Element Goal Actual\n", pprefix); for (size_t j = 0; j < m_numElemConstraints; j++) { if (m_elementActive[j]) { double tmp = 0.0; for (size_t kspec = 0; kspec < nspecies; ++kspec) { tmp += m_formulaMatrix(kspec,j) * m_molNumSpecies_old[kspec]; } plogf("%s ", pprefix); plogf(" %-9.9s", (m_elementName[j]).c_str()); plogf(" %12.3g %12.3g\n", m_elemAbundancesGoal[j], tmp); } } plogendl(); } /* * Make sure all species have positive definite mole numbers * Set voltages to zero for now, until we figure out what to do */ m_deltaMolNumSpecies.assign(m_deltaMolNumSpecies.size(), 0.0); for (size_t kspec = 0; kspec < nspecies; ++kspec) { if (m_speciesUnknownType[kspec] != VCS_SPECIES_TYPE_INTERFACIALVOLTAGE) { if (m_molNumSpecies_old[kspec] <= 0.0) { /* * HKM Should eventually include logic here for non SS phases */ if (!m_SSPhase[kspec]) { m_molNumSpecies_old[kspec] = 1.0e-30; } } } else { m_molNumSpecies_old[kspec] = 0.0; } } /* * Now find the optimized basis that spans the stoichiometric * coefficient matrix */ bool conv; (void) vcs_basopt(false, aw, sa, sm, ss, test, &conv); /* ***************************************************************** */ /* **** CALCULATE TOTAL MOLES, ****************** */ /* **** CHEMICAL POTENTIALS OF BASIS ****************** */ /* ***************************************************************** */ /* * Calculate TMoles and m_tPhaseMoles_old[] */ vcs_tmoles(); /* * m_tPhaseMoles_new[] will consist of just the component moles */ for (size_t iph = 0; iph < m_numPhases; iph++) { m_tPhaseMoles_new[iph] = TPhInertMoles[iph] + 1.0E-20; } for (size_t kspec = 0; kspec < m_numComponents; ++kspec) { if (m_speciesUnknownType[kspec] == VCS_SPECIES_TYPE_MOLNUM) { m_tPhaseMoles_new[m_phaseID[kspec]] += m_molNumSpecies_old[kspec]; } } double TMolesMultiphase = 0.0; for (size_t iph = 0; iph < m_numPhases; iph++) { if (! m_VolPhaseList[iph]->m_singleSpecies) { TMolesMultiphase += m_tPhaseMoles_new[iph]; } } m_molNumSpecies_new = m_molNumSpecies_old; for (size_t kspec = 0; kspec < m_numComponents; ++kspec) { if (m_speciesUnknownType[kspec] != VCS_SPECIES_TYPE_MOLNUM) { m_molNumSpecies_new[kspec] = 0.0; } } m_feSpecies_new = m_SSfeSpecies; for (size_t kspec = 0; kspec < m_numComponents; ++kspec) { if (m_speciesUnknownType[kspec] == VCS_SPECIES_TYPE_MOLNUM) { if (! m_SSPhase[kspec]) { size_t iph = m_phaseID[kspec]; m_feSpecies_new[kspec] += log(m_molNumSpecies_new[kspec] / m_tPhaseMoles_old[iph]); } } else { m_molNumSpecies_new[kspec] = 0.0; } } vcs_deltag(0, true, VCS_STATECALC_NEW); if (DEBUG_MODE_ENABLED && m_debug_print_lvl >= 2) { for (size_t kspec = 0; kspec < nspecies; ++kspec) { plogf("%s", pprefix); plogf("%-12.12s", m_speciesName[kspec].c_str()); if (kspec < m_numComponents) plogf("fe* = %15.5g ff = %15.5g\n", m_feSpecies_new[kspec], m_SSfeSpecies[kspec]); else plogf("fe* = %15.5g ff = %15.5g dg* = %15.5g\n", m_feSpecies_new[kspec], m_SSfeSpecies[kspec], m_deltaGRxn_new[kspec-m_numComponents]); } } /* ********************************************************** */ /* **** ESTIMATE REACTION ADJUSTMENTS *********************** */ /* ********************************************************** */ double* xtphMax = VCS_DATA_PTR(m_TmpPhase); double* xtphMin = VCS_DATA_PTR(m_TmpPhase2); m_deltaPhaseMoles.assign(m_deltaPhaseMoles.size(), 0.0); for (size_t iph = 0; iph < m_numPhases; iph++) { xtphMax[iph] = log(m_tPhaseMoles_new[iph] * 1.0E32); xtphMin[iph] = log(m_tPhaseMoles_new[iph] * 1.0E-32); } for (size_t irxn = 0; irxn < nrxn; ++irxn) { size_t kspec = m_indexRxnToSpecies[irxn]; /* * For single species phases, we will not estimate the * mole numbers. If the phase exists, it stays. If it * doesn't exist in the estimate, it doesn't come into * existence here. */ if (! m_SSPhase[kspec]) { size_t iph = m_phaseID[kspec]; if (m_deltaGRxn_new[irxn] > xtphMax[iph]) { m_deltaGRxn_new[irxn] = 0.8 * xtphMax[iph]; } if (m_deltaGRxn_new[irxn] < xtphMin[iph]) { m_deltaGRxn_new[irxn] = 0.8 * xtphMin[iph]; } /* * HKM -> The TMolesMultiphase is a change of mine. * It more evenly distributes the initial moles amongst * multiple multispecies phases according to the * relative values of the standard state free energies. * There is no change for problems with one multispecies * phase. * It cut diamond4.vin iterations down from 62 to 14. */ m_deltaMolNumSpecies[kspec] = 0.5 * (m_tPhaseMoles_new[iph] + TMolesMultiphase) * exp(-m_deltaGRxn_new[irxn]); for (size_t k = 0; k < m_numComponents; ++k) { m_deltaMolNumSpecies[k] += m_stoichCoeffRxnMatrix(k,irxn) * m_deltaMolNumSpecies[kspec]; } for (iph = 0; iph < m_numPhases; iph++) { m_deltaPhaseMoles[iph] += m_deltaMolNumPhase(iph,irxn) * m_deltaMolNumSpecies[kspec]; } } } if (DEBUG_MODE_ENABLED && m_debug_print_lvl >= 2) { for (size_t kspec = 0; kspec < nspecies; ++kspec) { if (m_speciesUnknownType[kspec] != VCS_SPECIES_TYPE_INTERFACIALVOLTAGE) { plogf("%sdirection (", pprefix); plogf("%-12.12s", m_speciesName[kspec].c_str()); plogf(") = %g", m_deltaMolNumSpecies[kspec]); if (m_SSPhase[kspec]) { if (m_molNumSpecies_old[kspec] > 0.0) { plogf(" (ssPhase exists at w = %g moles)", m_molNumSpecies_old[kspec]); } else { plogf(" (ssPhase doesn't exist -> stability not checked)"); } } plogendl(); } } } /* *********************************************************** */ /* **** KEEP COMPONENT SPECIES POSITIVE ********************** */ /* *********************************************************** */ double par = 0.5; for (size_t kspec = 0; kspec < m_numComponents; ++kspec) { if (m_speciesUnknownType[kspec] != VCS_SPECIES_TYPE_INTERFACIALVOLTAGE) { if (par < -m_deltaMolNumSpecies[kspec] / m_molNumSpecies_new[kspec]) { par = -m_deltaMolNumSpecies[kspec] / m_molNumSpecies_new[kspec]; } } } par = 1. / par; if (par <= 1.0 && par > 0.0) { par *= 0.8; } else { par = 1.0; } /* ******************************************** */ /* **** CALCULATE NEW MOLE NUMBERS ************ */ /* ******************************************** */ size_t lt = 0; size_t ikl = 0; double s1 = 0.0; while (true) { for (size_t kspec = 0; kspec < m_numComponents; ++kspec) { if (m_speciesUnknownType[kspec] != VCS_SPECIES_TYPE_INTERFACIALVOLTAGE) { m_molNumSpecies_old[kspec] = m_molNumSpecies_new[kspec] + par * m_deltaMolNumSpecies[kspec]; } else { m_deltaMolNumSpecies[kspec] = 0.0; } } for (size_t kspec = m_numComponents; kspec < nspecies; ++kspec) { if (m_speciesUnknownType[kspec] != VCS_SPECIES_TYPE_INTERFACIALVOLTAGE) { if (m_deltaMolNumSpecies[kspec] != 0.0) { m_molNumSpecies_old[kspec] = m_deltaMolNumSpecies[kspec] * par; } } } /* * We have a new w[] estimate, go get the * TMoles and m_tPhaseMoles_old[] values */ vcs_tmoles(); if (lt > 0) { break; } /* ******************************************* */ /* **** CONVERGENCE FORCING SECTION ********** */ /* ******************************************* */ vcs_setFlagsVolPhases(false, VCS_STATECALC_OLD); vcs_dfe(VCS_STATECALC_OLD, 0, 0, nspecies); double s = 0.0; for (size_t kspec = 0; kspec < nspecies; ++kspec) { s += m_deltaMolNumSpecies[kspec] * m_feSpecies_old[kspec]; } if (s == 0.0) { break; } if (s < 0.0) { if (ikl == 0) { break; } } /* ***************************************** */ /* *** TRY HALF STEP SIZE ****************** */ /* ***************************************** */ if (ikl == 0) { s1 = s; par *= 0.5; ikl = 1; continue; } /* **************************************************** */ /* **** FIT PARABOLA THROUGH HALF AND FULL STEPS ****** */ /* **************************************************** */ double xl = (1.0 - s / (s1 - s)) * 0.5; if (xl < 0.0) { /* *************************************************** */ /* *** POOR DIRECTION, REDUCE STEP SIZE TO 0.2 ******* */ /* *************************************************** */ par *= 0.2; } else { if (xl > 1.0) { /* *************************************************** */ /* **** TOO BIG A STEP, TAKE ORIGINAL FULL STEP ****** */ /* *************************************************** */ par *= 2.0; } else { /* *************************************************** */ /* **** ACCEPT RESULTS OF FORCER ********************* */ /* *************************************************** */ par = par * 2.0 * xl; } } lt = 1; } if (DEBUG_MODE_ENABLED && m_debug_print_lvl >= 2) { plogf("%s Final Mole Numbers produced by inest:\n", pprefix); plogf("%s SPECIES MOLE_NUMBER\n", pprefix); for (size_t kspec = 0; kspec < nspecies; ++kspec) { plogf("%s ", pprefix); plogf("%-12.12s", m_speciesName[kspec].c_str()); plogf(" %g", m_molNumSpecies_old[kspec]); plogendl(); } } }