/** * @file vcs_solve_TP.cpp Implementation file that contains the * main algorithm for finding an equilibrium */ /* * 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/equil/vcs_VolPhase.h" #include "cantera/base/ctexceptions.h" #include "cantera/base/clockWC.h" #include "cantera/base/stringUtils.h" #include "cantera/numerics/ctlapack.h" #include using namespace std; namespace { enum stages {MAIN, EQUILIB_CHECK, ELEM_ABUND_CHECK, RECHECK_DELETED, RETURN_A, RETURN_B}; } namespace Cantera { void VCS_SOLVE::checkDelta1(double* const dsLocal, double* const delTPhMoles, size_t kspec) { vector_fp dchange(m_numPhases, 0.0); for (size_t k = 0; k < kspec; k++) { if (m_speciesUnknownType[k] != VCS_SPECIES_TYPE_INTERFACIALVOLTAGE) { size_t iph = m_phaseID[k]; dchange[iph] += dsLocal[k]; } } for (size_t iphase = 0; iphase < m_numPhases; iphase++) { double denom = max(m_totalMolNum, 1.0E-4); if (!vcs_doubleEqual(dchange[iphase]/denom, delTPhMoles[iphase]/denom)) { throw CanteraError("VCS_SOLVE::checkDelta1", "we have found a problem"); } } } int VCS_SOLVE::vcs_solve_TP(int print_lvl, int printDetails, int maxit) { int stage = MAIN; int solveFail; bool allMinorZeroedSpecies = false; size_t it1 = 0; size_t npb, iti; int rangeErrorFound = 0; bool giveUpOnElemAbund = false; int finalElemAbundAttempts = 0; bool uptodate_minors = true; int forceComponentCalc = 1; #ifdef DEBUG_MODE char ANOTE[128]; // Set the debug print lvl to the same as the print lvl. m_debug_print_lvl = printDetails; #else char* ANOTE = 0; #endif if (printDetails > 0 && print_lvl == 0) { print_lvl = 1; } // Initialize and set up all counters vcs_counters_init(0); clockWC ticktock; // Malloc temporary space for usage in this routine and in subroutines m_sm.assign(m_numElemConstraints*m_numElemConstraints, 0.0); m_ss.assign(m_numElemConstraints, 0.0); m_sa.assign(m_numElemConstraints, 0.0); m_aw.assign(m_numSpeciesTot, 0.0); m_wx.assign(m_numElemConstraints, 0.0); solveFail = false; // Evaluate the elemental composition vcs_elab(); // Printout the initial conditions for problem if (print_lvl != 0) { plogf("VCS CALCULATION METHOD\n\n "); plogf("%s\n", m_title); plogf("\n\n%5d SPECIES\n%5d ELEMENTS\n", m_numSpeciesTot, m_numElemConstraints); plogf("%5d COMPONENTS\n", m_numComponents); plogf("%5d PHASES\n", m_numPhases); plogf(" PRESSURE%22.8g %3s\n", m_pressurePA, "Pa "); plogf(" TEMPERATURE%19.3f K\n", m_temperature); vcs_VolPhase* Vphase = m_VolPhaseList[0]; if (Vphase->nSpecies() > 0) { plogf(" PHASE1 INERTS%17.3f\n", TPhInertMoles[0]); } if (m_numPhases > 1) { plogf(" PHASE2 INERTS%17.3f\n", TPhInertMoles[1]); } plogf("\n ELEMENTAL ABUNDANCES CORRECT"); plogf(" FROM ESTIMATE Type\n\n"); for (size_t i = 0; i < m_numElemConstraints; ++i) { writeline(' ', 26, false); plogf("%-2.2s", m_elementName[i]); plogf("%20.12E%20.12E %3d\n", m_elemAbundancesGoal[i], m_elemAbundances[i], m_elType[i]); } if (m_doEstimateEquil < 0) { plogf("\n MODIFIED LINEAR PROGRAMMING ESTIMATE OF EQUILIBRIUM - forced\n"); } else if (m_doEstimateEquil > 0) { plogf("\n MODIFIED LINEAR PROGRAMMING ESTIMATE OF EQUILIBRIUM - where necessary\n"); } if (m_doEstimateEquil == 0) { plogf("\n USER ESTIMATE OF EQUILIBRIUM\n"); } if (m_VCS_UnitsFormat == VCS_UNITS_KCALMOL) { plogf(" Stan. Chem. Pot. in kcal/mole\n"); } if (m_VCS_UnitsFormat == VCS_UNITS_UNITLESS) { plogf(" Stan. Chem. Pot. is MU/RT\n"); } if (m_VCS_UnitsFormat == VCS_UNITS_KJMOL) { plogf(" Stan. Chem. Pot. in KJ/mole\n"); } if (m_VCS_UnitsFormat == VCS_UNITS_KELVIN) { plogf(" Stan. Chem. Pot. in Kelvin\n"); } if (m_VCS_UnitsFormat == VCS_UNITS_MKS) { plogf(" Stan. Chem. Pot. in J/kmol\n"); } plogf("\n SPECIES FORMULA VECTOR "); writeline(' ', 41, false); plogf(" STAN_CHEM_POT EQUILIBRIUM_EST. Species_Type\n\n"); writeline(' ', 20, false); for (size_t i = 0; i < m_numElemConstraints; ++i) { plogf("%-4.4s ", m_elementName[i]); } plogf(" PhaseID\n"); double RT = vcs_nondimMult_TP(m_VCS_UnitsFormat, m_temperature); for (size_t i = 0; i < m_numSpeciesTot; ++i) { plogf(" %-18.18s", m_speciesName[i]); for (size_t j = 0; j < m_numElemConstraints; ++j) { plogf("% -7.3g ", m_formulaMatrix(i,j)); } plogf(" %3d ", m_phaseID[i]); writeline(' ', std::max(55-int(m_numElemConstraints)*8, 0), false); plogf("%12.5E %12.5E", RT * m_SSfeSpecies[i], m_molNumSpecies_old[i]); if (m_speciesUnknownType[i] == VCS_SPECIES_TYPE_MOLNUM) { plogf(" Mol_Num"); } else if (m_speciesUnknownType[i] == VCS_SPECIES_TYPE_INTERFACIALVOLTAGE) { plogf(" Voltage"); } else { plogf(" Unknown"); } plogendl(); } } for (size_t i = 0; i < m_numSpeciesTot; ++i) { if (m_molNumSpecies_old[i] < 0.0) { plogf("On Input species %-12s has a negative MF, setting it small", m_speciesName[i]); plogendl(); size_t iph = m_phaseID[i]; double tmp = m_tPhaseMoles_old[iph] * VCS_RELDELETE_SPECIES_CUTOFF * 10; tmp = std::max(tmp, VCS_DELETE_MINORSPECIES_CUTOFF*10.); m_molNumSpecies_old[i] = tmp; } } // Evaluate the total moles of species in the problem vcs_tmoles(); // Evaluate all chemical potentials at the old mole numbers at the outset of // the calculation. vcs_setFlagsVolPhases(false, VCS_STATECALC_OLD); vcs_dfe(VCS_STATECALC_OLD, 0, 0, m_numSpeciesRdc); bool lec; while (true) { if (stage == MAIN) { // DETERMINE BASIS SPECIES, EVALUATE STOICHIOMETRY if (forceComponentCalc) { int retn = solve_tp_component_calc(allMinorZeroedSpecies); if (retn != VCS_SUCCESS) { return retn; } it1 = 1; forceComponentCalc = 0; iti = 0; } // Check on too many iterations. If we have too many iterations, // Clean up and exit code even though we haven't converged. // -> we have run out of iterations! if (m_VCount->Its > maxit) { return -1; } solve_tp_inner(iti, it1, uptodate_minors, allMinorZeroedSpecies, forceComponentCalc, stage, printDetails, ANOTE); lec = false; } else if (stage == EQUILIB_CHECK) { // EQUILIBRIUM CHECK FOR MAJOR SPECIES solve_tp_equilib_check(allMinorZeroedSpecies, uptodate_minors, giveUpOnElemAbund, solveFail, iti, it1, maxit, stage, lec); } else if (stage == ELEM_ABUND_CHECK) { // CORRECT ELEMENTAL ABUNDANCES solve_tp_elem_abund_check(iti, stage, lec, giveUpOnElemAbund, finalElemAbundAttempts, rangeErrorFound); } else if (stage == RECHECK_DELETED) { // RECHECK DELETED SPECIES // // We are here for two reasons. One is if we have achieved // convergence, but some species have been eliminated from the // problem because they were in multispecies phases and their mole // fractions drifted less than VCS_DELETE_SPECIES_CUTOFF. The other // reason why we are here is because all of the non-component // species in the problem have been eliminated for one reason or // another. npb = vcs_recheck_deleted(); // If we haven't found any species that needed adding we are done. if (npb <= 0) { stage = RETURN_B; } else { // If we have found something to add, recalculate everything for // minor species and go back to do a full iteration vcs_setFlagsVolPhases(false, VCS_STATECALC_OLD); vcs_dfe(VCS_STATECALC_OLD, 1, 0, m_numSpeciesRdc); vcs_deltag(0, false, VCS_STATECALC_OLD); iti = 0; stage = MAIN; } } else if (stage == RETURN_A) { // CLEANUP AND RETURN BLOCK npb = vcs_recheck_deleted(); // If we haven't found any species that needed adding we are done. if (npb > 0) { // If we have found something to add, recalculate everything for // minor species and go back to do a full iteration vcs_setFlagsVolPhases(false, VCS_STATECALC_OLD); vcs_dfe(VCS_STATECALC_OLD, 1, 0, m_numSpeciesRdc); vcs_deltag(0, false, VCS_STATECALC_OLD); iti = 0; stage = MAIN; } else { stage = RETURN_B; } } else if (stage == RETURN_B) { // Add back deleted species in non-zeroed phases. Estimate their // mole numbers. npb = vcs_add_all_deleted(); if (npb > 0) { iti = 0; if (DEBUG_MODE_ENABLED && m_debug_print_lvl >= 1) { plogf(" --- add_all_deleted(): some rxns not converged. RETURNING TO LOOP!"); plogendl(); } stage = MAIN; } else { break; } } } // Make sure the volume phase objects hold the same state and information as // the vcs object. This also update the Cantera objects with this // information. vcs_updateVP(VCS_STATECALC_OLD); // Evaluate the final mole fractions storing them in wt[] m_molNumSpecies_new.assign(m_molNumSpecies_new.size(), 0.0); for (size_t kspec = 0; kspec < m_numSpeciesTot; ++kspec) { if (m_SSPhase[kspec]) { m_molNumSpecies_new[kspec] = 1.0; } else { size_t iph = m_phaseID[kspec]; if (m_tPhaseMoles_old[iph] != 0.0) { m_molNumSpecies_new[kspec] = m_molNumSpecies_old[kspec] / m_tPhaseMoles_old[iph]; } else { // For MultiSpecies phases that are zeroed out, return the mole // fraction vector from the VolPhase object. This contains the // mole fraction that would be true if the phase just pops into // existence. size_t i = m_speciesLocalPhaseIndex[kspec]; m_molNumSpecies_new[kspec] = m_VolPhaseList[iph]->molefraction(i); } } } // Return an error code if a Range Space Error is thought to have occurred. if (rangeErrorFound) { solveFail = 1; } // Calculate counters double tsecond = ticktock.secondsWC(); m_VCount->Time_vcs_TP = tsecond; m_VCount->T_Time_vcs_TP += m_VCount->Time_vcs_TP; m_VCount->T_Calls_vcs_TP++; m_VCount->T_Its += m_VCount->Its; m_VCount->T_Basis_Opts += m_VCount->Basis_Opts; m_VCount->T_Time_basopt += m_VCount->Time_basopt; // Return a Flag indicating whether convergence occurred return solveFail; } int VCS_SOLVE::solve_tp_component_calc(bool& allMinorZeroedSpecies) { double test = -1.0e-10; bool usedZeroedSpecies; int retn = vcs_basopt(false, &m_aw[0], &m_sa[0], &m_sm[0], &m_ss[0], test, &usedZeroedSpecies); if (retn != VCS_SUCCESS) { return retn; } // Update the phase objects with the contents of the soln vector vcs_updateVP(VCS_STATECALC_OLD); vcs_deltag(0, false, VCS_STATECALC_OLD); // EVALUATE INITIAL SPECIES STATUS VECTOR allMinorZeroedSpecies = vcs_evaluate_speciesType(); // EVALUATE THE ELELEMT ABUNDANCE CHECK if (! vcs_elabcheck(0)) { if (DEBUG_MODE_ENABLED && m_debug_print_lvl >= 2) { plogf(" --- Element Abundance check failed"); plogendl(); } vcs_elcorr(&m_sm[0], &m_wx[0]); vcs_setFlagsVolPhases(false, VCS_STATECALC_OLD); vcs_dfe(VCS_STATECALC_OLD, 0, 0, m_numSpeciesRdc); // Update the phase objects with the contents of the soln vector vcs_updateVP(VCS_STATECALC_OLD); vcs_deltag(0, false, VCS_STATECALC_OLD); } else if (DEBUG_MODE_ENABLED && m_debug_print_lvl >= 2) { plogf(" --- Element Abundance check passed"); plogendl(); } return retn; } void VCS_SOLVE::solve_tp_inner(size_t& iti, size_t& it1, bool& uptodate_minors, bool& allMinorZeroedSpecies, int& forceComponentCalc, int& stage, bool printDetails, char* ANOTE) { if (iti == 0) { // SET INITIAL VALUES FOR ITERATION // EVALUATE REACTION ADJUSTMENTS // // Evaluate the minor non-component species chemical potentials and // delta G for their formation reactions We have already evaluated the // major non-components if (!uptodate_minors) { vcs_setFlagsVolPhases(false, VCS_STATECALC_OLD); vcs_dfe(VCS_STATECALC_OLD, 1, 0, m_numSpeciesRdc); vcs_deltag(1, false, VCS_STATECALC_OLD); } uptodate_minors = true; } else { uptodate_minors = false; } if (printDetails) { plogf("\n"); writeline('=', 110); plogf(" Iteration = %3d, Iterations since last evaluation of " "optimal basis = %3d", m_VCount->Its, it1 - 1); if (iti == 0) { plogf(" (all species)\n"); } else { plogf(" (only major species)\n"); } } // Calculate the total moles in each phase -> old solution // -> Needed for numerical stability when phases disappear. // -> the phase moles tend to drift off without this step. #ifdef DEBUG_MODE check_tmoles(); #endif vcs_tmoles(); // COPY OLD into NEW and ZERO VECTORS // Copy the old solution into the new solution as an initial guess m_feSpecies_new = m_feSpecies_old; m_actCoeffSpecies_new = m_actCoeffSpecies_old; m_deltaGRxn_new = m_deltaGRxn_old; m_deltaGRxn_Deficient = m_deltaGRxn_old; m_tPhaseMoles_new = m_tPhaseMoles_old; // Zero out the entire vector of updates. We sometimes would query these // values below, and we want to be sure that no information is left from // previous iterations. m_deltaMolNumSpecies.assign(m_deltaMolNumSpecies.size(), 0.0); // DETERMINE IF DEAD PHASES POP INTO EXISTENCE // // First step is a major branch in the algorithm. We first determine if a // phase pops into existence. std::vector phasePopPhaseIDs(0); size_t iphasePop = vcs_popPhaseID(phasePopPhaseIDs); if (iphasePop != npos) { int soldel = vcs_popPhaseRxnStepSizes(iphasePop); if (soldel == 3) { iphasePop = npos; if (DEBUG_MODE_ENABLED && m_debug_print_lvl >= 2) { plogf(" --- vcs_popPhaseRxnStepSizes() was called but stoich " "prevented phase %d popping\n"); } } } // DETERMINE THE REACTION STEP SIZES FOR MAIN STEP AND IF PHASES DIE // // Don't do this step if there is a phase pop size_t iphaseDelete = npos; size_t kspec; if (iphasePop == npos) { // Figure out the new reaction step sizes for the major species (do // minor species in the future too) kspec = npos; iphaseDelete = vcs_RxnStepSizes(forceComponentCalc, kspec); } else if (DEBUG_MODE_ENABLED && m_debug_print_lvl >= 2) { plogf(" --- vcs_RxnStepSizes not called because alternative" "phase creation delta was used instead\n"); } size_t doPhaseDeleteKspec = npos; size_t doPhaseDeleteIph = npos; // Zero out the net change in moles of multispecies phases m_deltaPhaseMoles.assign(m_deltaPhaseMoles.size(), 0.0); // MAIN LOOP IN CALCULATION: LOOP OVER IRXN TO DETERMINE STEP SIZE // // Loop through all of the reactions, irxn, pertaining to the formation // reaction for species kspec in canonical form. // // At the end of this loop, we will have a new estimate for the mole numbers // for all species consistent with an extent of reaction for all // noncomponent species formation reactions. We will have also ensured that // all predicted non-component mole numbers are greater than zero. // // Old_Solution New_Solution Description // ----------------------------------------------------------------------------- // m_molNumSpecies_old[kspec] m_molNumSpecies_new[kspec] Species Mole Numbers // m_deltaMolNumSpecies[kspec] Delta in the Species Mole Numbers if (iphaseDelete != npos) { if (DEBUG_MODE_ENABLED && m_debug_print_lvl >= 2) { plogf(" --- Main Loop Treatment -> Circumvented due to Phase Deletion "); plogendl(); } for (size_t k = 0; k < m_numSpeciesTot; k++) { m_molNumSpecies_new[k] = m_molNumSpecies_old[k] + m_deltaMolNumSpecies[k]; size_t iph = m_phaseID[k]; m_tPhaseMoles_new[iph] += m_deltaMolNumSpecies[k]; } if (kspec >= m_numComponents) { if (m_molNumSpecies_new[m_numSpeciesTot] != 0.0) { throw CanteraError("VCS_SOLVE::solve_tp_inner", "we shouldn't be here!"); } if (m_SSPhase[kspec] == 1) { m_speciesStatus[kspec] = VCS_SPECIES_ZEROEDSS; } else { throw CanteraError("VCS_SOLVE::solve_tp_inner", "we shouldn't be here!"); } ++m_numRxnMinorZeroed; allMinorZeroedSpecies = (m_numRxnMinorZeroed == m_numRxnRdc); } // Set the flags indicating the mole numbers in the vcs_VolPhase objects // are out of date. vcs_setFlagsVolPhases(false, VCS_STATECALC_NEW); // Calculate the new chemical potentials using the tentative solution // values. We only calculate a subset of these, because we have only // updated a subset of the W(). vcs_dfe(VCS_STATECALC_NEW, 0, 0, m_numSpeciesTot); // Evaluate DeltaG for all components if ITI=0, and for major components // only if ITI NE 0 vcs_deltag(0, false, VCS_STATECALC_NEW); } else { if (DEBUG_MODE_ENABLED && m_debug_print_lvl >= 2) { plogf(" --- Main Loop Treatment of each non-component species "); if (iti == 0) { plogf("- Full Calculation:\n"); } else { plogf("- Major Components Calculation:\n"); } plogf(" --- Species IC "); plogf(" KMoles Tent_KMoles Rxn_Adj | Comment \n"); } for (size_t irxn = 0; irxn < m_numRxnRdc; irxn++) { size_t kspec = m_indexRxnToSpecies[irxn]; double* sc_irxn = m_stoichCoeffRxnMatrix.ptrColumn(irxn); size_t iph = m_phaseID[kspec]; vcs_VolPhase* Vphase = m_VolPhaseList[iph]; if (DEBUG_MODE_ENABLED) { ANOTE[0] = '\0'; } double dx; if (iphasePop != npos) { if (iph == iphasePop) { dx = m_deltaMolNumSpecies[kspec]; m_molNumSpecies_new[kspec] = m_molNumSpecies_old[kspec] + m_deltaMolNumSpecies[kspec]; if (DEBUG_MODE_ENABLED) { sprintf(ANOTE, "Phase pop"); } } else { dx = 0.0; m_molNumSpecies_new[kspec] = m_molNumSpecies_old[kspec]; } } else if (m_speciesStatus[kspec] == VCS_SPECIES_INTERFACIALVOLTAGE) { // VOLTAGE SPECIES bool soldel_ret; dx = vcs_minor_alt_calc(kspec, irxn, &soldel_ret, ANOTE); m_deltaMolNumSpecies[kspec] = dx; } else if (m_speciesStatus[kspec] < VCS_SPECIES_MINOR) { // ZEROED OUT SPECIES bool resurrect = (m_deltaMolNumSpecies[kspec] > 0.0); if (DEBUG_MODE_ENABLED && m_debug_print_lvl >= 3) { plogf(" --- %s currently zeroed (SpStatus=%-2d):", m_speciesName[kspec], m_speciesStatus[kspec]); plogf("%3d DG = %11.4E WT = %11.4E W = %11.4E DS = %11.4E\n", irxn, m_deltaGRxn_new[irxn], m_molNumSpecies_new[kspec], m_molNumSpecies_old[kspec], m_deltaMolNumSpecies[kspec]); } if (m_deltaGRxn_new[irxn] >= 0.0 || !resurrect) { m_molNumSpecies_new[kspec] = m_molNumSpecies_old[kspec]; m_deltaMolNumSpecies[kspec] = 0.0; resurrect = false; if (DEBUG_MODE_ENABLED) { sprintf(ANOTE, "Species stays zeroed: DG = %11.4E", m_deltaGRxn_new[irxn]); if (m_deltaGRxn_new[irxn] < 0.0) { if (m_speciesStatus[kspec] == VCS_SPECIES_STOICHZERO) { sprintf(ANOTE, "Species stays zeroed even though dg neg due to " "STOICH/PHASEPOP constraint: DG = %11.4E", m_deltaGRxn_new[irxn]); } else { sprintf(ANOTE, "Species stays zeroed even though dg neg: DG = %11.4E, ds zeroed", m_deltaGRxn_new[irxn]); } } } } else { for (size_t j = 0; j < m_numElemConstraints; ++j) { int elType = m_elType[j]; if (elType == VCS_ELEM_TYPE_ABSPOS) { double atomComp = m_formulaMatrix(kspec,j); if (atomComp > 0.0) { double maxPermissible = m_elemAbundancesGoal[j] / atomComp; if (maxPermissible < VCS_DELETE_MINORSPECIES_CUTOFF) { if (DEBUG_MODE_ENABLED) { sprintf(ANOTE, "Species stays zeroed even though dG " "neg, because of %s elemAbund", m_elementName[j].c_str()); } resurrect = false; break; } } } } } // Resurrect the species if (resurrect) { if (Vphase->exists() == VCS_PHASE_EXIST_NO) { if (DEBUG_MODE_ENABLED && m_debug_print_lvl >= 2) { plogf(" --- Zeroed species changed to major: "); plogf("%-12s\n", m_speciesName[kspec]); } m_speciesStatus[kspec] = VCS_SPECIES_MAJOR; allMinorZeroedSpecies = false; } else { if (DEBUG_MODE_ENABLED && m_debug_print_lvl >= 2) { plogf(" --- Zeroed species changed to minor: "); plogf("%-12s\n", m_speciesName[kspec]); } m_speciesStatus[kspec] = VCS_SPECIES_MINOR; } if (m_deltaMolNumSpecies[kspec] > 0.0) { dx = m_deltaMolNumSpecies[kspec] * 0.01; m_molNumSpecies_new[kspec] = m_molNumSpecies_old[kspec] + dx; } else { m_molNumSpecies_new[kspec] = m_totalMolNum * VCS_DELETE_PHASE_CUTOFF * 10.; dx = m_molNumSpecies_new[kspec] - m_molNumSpecies_old[kspec]; } m_deltaMolNumSpecies[kspec] = dx; if (DEBUG_MODE_ENABLED) { sprintf(ANOTE, "Born:IC=-1 to IC=1:DG=%11.4E", m_deltaGRxn_new[irxn]); } } else { m_molNumSpecies_new[kspec] = m_molNumSpecies_old[kspec]; m_deltaMolNumSpecies[kspec] = 0.0; dx = 0.0; } } else if (m_speciesStatus[kspec] == VCS_SPECIES_MINOR) { // MINOR SPECIES // // Unless ITI isn't equal to zero we zero out changes to minor // species. if (iti != 0) { m_molNumSpecies_new[kspec] = m_molNumSpecies_old[kspec]; m_deltaMolNumSpecies[kspec] = 0.0; dx = 0.0; if (DEBUG_MODE_ENABLED) { sprintf(ANOTE,"minor species not considered"); if (m_debug_print_lvl >= 2) { plogf(" --- "); plogf("%-12s", m_speciesName[kspec]); plogf("%3d%11.4E%11.4E%11.4E | %s", m_speciesStatus[kspec], m_molNumSpecies_old[kspec], m_molNumSpecies_new[kspec], m_deltaMolNumSpecies[kspec], ANOTE); plogendl(); } } continue; } // Minor species alternative calculation // // This is based upon the following approximation: // The mole fraction changes due to these reactions don't affect // the mole numbers of the component species. Therefore the // following approximation is valid for an ideal solution // 0 = DG(I) + log(WT(I)/W(I)) // (DG contains the contribution from FF(I) + log(W(I)/TL) ) // Thus, // WT(I) = W(I) EXP(-DG(I)) // If soldel is true on return, then we branch to the section // that deletes a species from the current set of active species. bool soldel_ret; dx = vcs_minor_alt_calc(kspec, irxn, &soldel_ret, ANOTE); m_deltaMolNumSpecies[kspec] = dx; m_molNumSpecies_new[kspec] = m_molNumSpecies_old[kspec] + dx; if (soldel_ret) { // DELETE MINOR SPECIES LESS THAN VCS_DELETE_SPECIES_CUTOFF // MOLE NUMBER if (DEBUG_MODE_ENABLED && m_debug_print_lvl >= 2) { plogf(" --- Delete minor species in multispec phase: %-12s", m_speciesName[kspec]); plogendl(); } m_deltaMolNumSpecies[kspec] = 0.0; // Delete species, kspec. The alternate return is for the // case where all species become deleted. Then, we need to // branch to the code where we reevaluate the deletion of // all species. size_t lnospec = vcs_delete_species(kspec); if (lnospec) { stage = RECHECK_DELETED; break; } // Go back to consider the next species in the list. Note, // however, that the next species in the list is now in slot // l. In deleting the previous species L, We have exchanged // slot MR with slot l, and then have decremented MR. // Therefore, we will decrement the species counter, here. --irxn; continue; } } else { // MAJOR SPECIES if (DEBUG_MODE_ENABLED) { sprintf(ANOTE, "Normal Major Calc"); } // Check for superconvergence of the formation reaction. Do // nothing if it is superconverged. Skip to the end of the irxn // loop if it is superconverged. if (fabs(m_deltaGRxn_new[irxn]) <= m_tolmaj2) { m_molNumSpecies_new[kspec] = m_molNumSpecies_old[kspec]; m_deltaMolNumSpecies[kspec] = 0.0; dx = 0.0; if (DEBUG_MODE_ENABLED) { sprintf(ANOTE, "major species is converged"); if (m_debug_print_lvl >= 2) { plogf(" --- "); plogf("%-12s", m_speciesName[kspec]); plogf("%3d%11.4E%11.4E%11.4E | %s", m_speciesStatus[kspec], m_molNumSpecies_old[kspec], m_molNumSpecies_new[kspec], m_deltaMolNumSpecies[kspec], ANOTE); plogendl(); } } continue; } // Set the initial step size, dx, equal to the value produced by // the routine, vcs_RxnStepSize(). // // Note the multiplication logic is to make sure that dg[] // didn't change sign due to w[] changing in the middle of the // iteration. (it can if a single species phase goes out of // existence). if ((m_deltaGRxn_new[irxn] * m_deltaMolNumSpecies[kspec]) <= 0.0) { dx = m_deltaMolNumSpecies[kspec]; } else { dx = 0.0; m_deltaMolNumSpecies[kspec] = 0.0; if (DEBUG_MODE_ENABLED) { sprintf(ANOTE, "dx set to 0, DG flipped sign due to " "changed initial point"); } } //Form a tentative value of the new species moles m_molNumSpecies_new[kspec] = m_molNumSpecies_old[kspec] + dx; // Check for non-positive mole fraction of major species. If we // find one, we branch to a section below. Then, depending upon // the outcome, we branch to sections below, or we restart the // entire iteration. if (m_molNumSpecies_new[kspec] <= 0.0) { if (DEBUG_MODE_ENABLED) { sprintf(ANOTE, "initial nonpos kmoles= %11.3E", m_molNumSpecies_new[kspec]); } // NON-POSITIVE MOLES OF MAJOR SPECIES // // We are here when a tentative value of a mole fraction // created by a tentative value of M_DELTAMOLNUMSPECIES(*) // is negative. We branch from here depending upon whether // this species is in a single species phase or in a // multispecies phase. if (!m_SSPhase[kspec]) { // Section for multispecies phases: // - Cut reaction adjustment for positive kmoles of // major species in multispecies phases. Decrease // its concentration by a factor of 10. dx = -0.9 * m_molNumSpecies_old[kspec]; m_deltaMolNumSpecies[kspec] = dx; m_molNumSpecies_new[kspec] = m_molNumSpecies_old[kspec] + dx; } else { // Section for single species phases: // Calculate a dx that will wipe out the // moles in the phase. dx = -m_molNumSpecies_old[kspec]; // Calculate an update that doesn't create a negative // mole number for a component species. Actually, // restrict this a little more so that the component // values can only be reduced by two 99%, for (size_t j = 0; j < m_numComponents; ++j) { if (sc_irxn[j] != 0.0) { m_wx[j] = m_molNumSpecies_old[j] + sc_irxn[j] * dx; if (m_wx[j] <= m_molNumSpecies_old[j] * 0.01 - 1.0E-150) { dx = std::max(dx, m_molNumSpecies_old[j] * -0.99 / sc_irxn[j]); } } else { m_wx[j] = m_molNumSpecies_old[j]; } } m_molNumSpecies_new[kspec] = m_molNumSpecies_old[kspec] + dx; if (m_molNumSpecies_new[kspec] > 0.0) { m_deltaMolNumSpecies[kspec] = dx; if (DEBUG_MODE_ENABLED) { sprintf(ANOTE, "zeroing SS phase created a neg component species " "-> reducing step size instead"); } } else { // We are going to zero the single species phase. // Set the existence flag iph = m_phaseID[kspec]; Vphase = m_VolPhaseList[iph]; if (DEBUG_MODE_ENABLED) { sprintf(ANOTE, "zeroing out SS phase: "); } // Change the base mole numbers for the iteration. // We need to do this here, because we have decided // to eliminate the phase in this special section // outside the main loop. m_molNumSpecies_new[kspec] = 0.0; doPhaseDeleteIph = iph; if (DEBUG_MODE_ENABLED) { doPhaseDeleteKspec = kspec; if (m_debug_print_lvl >= 2 && m_speciesStatus[kspec] >= 0) { plogf(" --- SS species changed to zeroedss: "); plogf("%-12s", m_speciesName[kspec]); plogendl(); } } m_speciesStatus[kspec] = VCS_SPECIES_ZEROEDSS; ++m_numRxnMinorZeroed; allMinorZeroedSpecies = (m_numRxnMinorZeroed == m_numRxnRdc); for (size_t kk = 0; kk < m_numSpeciesTot; kk++) { m_deltaMolNumSpecies[kk] = 0.0; m_molNumSpecies_new[kk] = m_molNumSpecies_old[kk]; } m_deltaMolNumSpecies[kspec] = dx; m_molNumSpecies_new[kspec] = 0.0; for (size_t k = 0; k < m_numComponents; ++k) { m_deltaMolNumSpecies[k] = 0.0; } for (iph = 0; iph < m_numPhases; iph++) { m_deltaPhaseMoles[iph] = 0.0; } } } } } // End of Loop on ic[irxn] -> the type of species // CALCULATE KMOLE NUMBER CHANGE FOR THE COMPONENT BASIS if (dx != 0.0 && (m_speciesUnknownType[kspec] != VCS_SPECIES_TYPE_INTERFACIALVOLTAGE)) { // Change the amount of the component compounds according to the // reaction delta that we just computed. This should keep the // amount of material constant. AssertThrowMsg(fabs(m_deltaMolNumSpecies[kspec] -dx) < 1.0E-14*(fabs(m_deltaMolNumSpecies[kspec]) + fabs(dx) + 1.0E-32), "VCS_SOLVE::solve_tp_inner", "ds[kspec] = {}, dx = {}, kspec = {}\nwe have a problem!", m_deltaMolNumSpecies[kspec], dx, kspec); for (size_t k = 0; k < m_numComponents; ++k) { m_deltaMolNumSpecies[k] += sc_irxn[k] * dx; } // Calculate the tentative change in the total number of moles // in all of the phases for (iph = 0; iph < m_numPhases; iph++) { m_deltaPhaseMoles[iph] += dx * m_deltaMolNumPhase(iph,irxn); } } if (DEBUG_MODE_ENABLED) { checkDelta1(&m_deltaMolNumSpecies[0], &m_deltaPhaseMoles[0], kspec+1); } // Branch point for returning if (m_debug_print_lvl >= 2) { m_molNumSpecies_new[kspec] = m_molNumSpecies_old[kspec] + m_deltaMolNumSpecies[kspec]; plogf(" --- "); plogf("%-12.12s", m_speciesName[kspec]); plogf("%3d%11.4E%11.4E%11.4E | %s", m_speciesStatus[kspec], m_molNumSpecies_old[kspec], m_molNumSpecies_new[kspec], m_deltaMolNumSpecies[kspec], ANOTE); plogendl(); } if (doPhaseDeleteIph != npos) { if (DEBUG_MODE_ENABLED && m_debug_print_lvl >= 2) { plogf(" --- "); plogf("%-12.12s Main Loop Special Case deleting phase with species: ", m_speciesName[doPhaseDeleteKspec]); plogendl(); } break; } } // END OF MAIN LOOP OVER FORMATION REACTIONS if (DEBUG_MODE_ENABLED && m_debug_print_lvl >= 2) { for (size_t k = 0; k < m_numComponents; k++) { plogf(" --- "); plogf("%-12.12s", m_speciesName[k]); plogf(" c%11.4E%11.4E%11.4E |\n", m_molNumSpecies_old[k], m_molNumSpecies_old[k]+m_deltaMolNumSpecies[k], m_deltaMolNumSpecies[k]); } plogf(" "); writeline('-', 80); plogf(" --- Finished Main Loop"); plogendl(); } // LIMIT REDUCTION OF BASIS SPECIES TO 99% // // We have a tentative m_deltaMolNumSpecies[]. Now apply other criteria // to limit its magnitude. double par = 0.5; size_t ll; // only used in DEBUG_MODE for (size_t k = 0; k < m_numComponents; ++k) { if (m_molNumSpecies_old[k] > 0.0) { double xx = -m_deltaMolNumSpecies[k] / m_molNumSpecies_old[k]; if (par < xx) { par = xx; ll = k; } } else if (m_deltaMolNumSpecies[k] < 0.0) { // If we are here, we then do a step which violates element // conservation. size_t iph = m_phaseID[k]; m_deltaPhaseMoles[iph] -= m_deltaMolNumSpecies[k]; m_deltaMolNumSpecies[k] = 0.0; } } par = 1.0 / par; if (par <= 1.01 && par > 0.0) { // Reduce the size of the step by the multiplicative factor, par par *= 0.99; if (DEBUG_MODE_ENABLED && m_debug_print_lvl >= 2) { plogf(" --- Reduction in step size due to component "); plogf("%s", m_speciesName[ll]); plogf(" going negative = %11.3E", par); plogendl(); } for (size_t i = 0; i < m_numSpeciesTot; ++i) { m_deltaMolNumSpecies[i] *= par; } for (size_t iph = 0; iph < m_numPhases; iph++) { m_deltaPhaseMoles[iph] *= par; } } else { par = 1.0; } if (DEBUG_MODE_ENABLED) { checkDelta1(&m_deltaMolNumSpecies[0], &m_deltaPhaseMoles[0], m_numSpeciesTot); } // Now adjust the wt[kspec]'s so that the reflect the decrease in the // overall length of m_deltaMolNumSpecies[kspec] just calculated. At the // end of this section wt[], m_deltaMolNumSpecies[], tPhMoles, and // tPhMoles1 should all be consistent with a new estimate of the state // of the system. for (size_t kspec = 0; kspec < m_numSpeciesTot; ++kspec) { m_molNumSpecies_new[kspec] = m_molNumSpecies_old[kspec] + m_deltaMolNumSpecies[kspec]; if (m_molNumSpecies_new[kspec] < 0.0 && (m_speciesUnknownType[kspec] != VCS_SPECIES_TYPE_INTERFACIALVOLTAGE)) { throw CanteraError("VCS_SOLVE::solve_tp_inner", "vcs_solve_TP: ERROR on step change wt[{}:{}]: {} < 0.0", kspec, m_speciesName[kspec], m_molNumSpecies_new[kspec]); } } // Calculate the tentative total mole numbers for each phase for (size_t iph = 0; iph < m_numPhases; iph++) { m_tPhaseMoles_new[iph] = m_tPhaseMoles_old[iph] + m_deltaPhaseMoles[iph]; } // Set the flags indicating the mole numbers in the vcs_VolPhase objects // are out of date. vcs_setFlagsVolPhases(false, VCS_STATECALC_NEW); // Calculate the new chemical potentials using the tentative solution // values. We only calculate a subset of these, because we have only // updated a subset of the W(). vcs_dfe(VCS_STATECALC_NEW, 0, 0, m_numSpeciesTot); // Evaluate DeltaG for all components if ITI=0, and for major components // only if ITI NE 0 vcs_deltag(0, false, VCS_STATECALC_NEW); // CONVERGENCE FORCER SECTION if (printDetails) { plogf(" --- Total Old Dimensionless Gibbs Free Energy = %20.13E\n", vcs_Total_Gibbs(&m_molNumSpecies_old[0], &m_feSpecies_old[0], &m_tPhaseMoles_old[0])); plogf(" --- Total tentative Dimensionless Gibbs Free Energy = %20.13E", vcs_Total_Gibbs(&m_molNumSpecies_new[0], &m_feSpecies_new[0], &m_tPhaseMoles_new[0])); plogendl(); } bool forced = vcs_globStepDamp(); // Print out the changes to the solution that FORCER produced if (printDetails && forced) { plogf(" -----------------------------------------------------\n"); plogf(" --- FORCER SUBROUTINE changed the solution:\n"); plogf(" --- SPECIES Status INIT MOLES TENT_MOLES"); plogf(" FINAL KMOLES INIT_DEL_G/RT TENT_DEL_G/RT FINAL_DELTA_G/RT\n"); for (size_t i = 0; i < m_numComponents; ++i) { plogf(" --- %-12.12s", m_speciesName[i]); plogf(" %14.6E %14.6E %14.6E\n", m_molNumSpecies_old[i], m_molNumSpecies_old[i] + m_deltaMolNumSpecies[i], m_molNumSpecies_new[i]); } for (size_t kspec = m_numComponents; kspec < m_numSpeciesRdc; ++kspec) { size_t irxn = kspec - m_numComponents; plogf(" --- %-12.12s", m_speciesName[kspec]); plogf(" %2d %14.6E%14.6E%14.6E%14.6E%14.6E%14.6E\n", m_speciesStatus[kspec], m_molNumSpecies_old[kspec], m_molNumSpecies_old[kspec]+m_deltaMolNumSpecies[kspec], m_molNumSpecies_new[kspec], m_deltaGRxn_old[irxn], m_deltaGRxn_tmp[irxn], m_deltaGRxn_new[irxn]); } writeline(' ', 26, false); plogf("Norms of Delta G():%14.6E%14.6E\n", l2normdg(&m_deltaGRxn_old[0]), l2normdg(&m_deltaGRxn_new[0])); plogf(" Total kmoles of gas = %15.7E\n", m_tPhaseMoles_old[0]); if ((m_numPhases > 1) && (!m_VolPhaseList[1]->m_singleSpecies)) { plogf(" Total kmoles of liquid = %15.7E\n", m_tPhaseMoles_old[1]); } else { plogf(" Total kmoles of liquid = %15.7E\n", 0.0); } plogf(" Total New Dimensionless Gibbs Free Energy = %20.13E\n", vcs_Total_Gibbs(&m_molNumSpecies_new[0], &m_feSpecies_new[0], &m_tPhaseMoles_new[0])); plogf(" -----------------------------------------------------"); plogendl(); } } // ITERATION SUMMARY PRINTOUT SECTION if (printDetails) { plogf(" "); writeline('-', 103); plogf(" --- Summary of the Update "); if (iti == 0) { plogf(" (all species):"); } else { plogf(" (only major species):"); } if (m_totalMoleScale != 1.0) { plogf(" (Total Mole Scale = %g)", m_totalMoleScale); } plogf("\n"); plogf(" --- Species Status Initial_KMoles Final_KMoles Initial_Mu/RT"); plogf(" Mu/RT Init_Del_G/RT Delta_G/RT\n"); for (size_t i = 0; i < m_numComponents; ++i) { plogf(" --- %-12.12s", m_speciesName[i]); plogf(" "); plogf("%14.6E%14.6E%14.6E%14.6E\n", m_molNumSpecies_old[i], m_molNumSpecies_new[i], m_feSpecies_old[i], m_feSpecies_new[i]); } for (size_t i = m_numComponents; i < m_numSpeciesRdc; ++i) { size_t l1 = i - m_numComponents; plogf(" --- %-12.12s", m_speciesName[i]); plogf(" %2d %14.6E%14.6E%14.6E%14.6E%14.6E%14.6E\n", m_speciesStatus[i], m_molNumSpecies_old[i], m_molNumSpecies_new[i], m_feSpecies_old[i], m_feSpecies_new[i], m_deltaGRxn_old[l1], m_deltaGRxn_new[l1]); } for (size_t kspec = m_numSpeciesRdc; kspec < m_numSpeciesTot; ++kspec) { size_t l1 = kspec - m_numComponents; plogf(" --- %-12.12s", m_speciesName[kspec]); plogf(" %2d %14.6E%14.6E%14.6E%14.6E%14.6E%14.6E\n", m_speciesStatus[kspec], m_molNumSpecies_old[kspec], m_molNumSpecies_new[kspec], m_feSpecies_old[kspec], m_feSpecies_new[kspec], m_deltaGRxn_old[l1], m_deltaGRxn_new[l1]); } plogf(" ---"); writeline(' ', 56, false); plogf("Norms of Delta G():%14.6E%14.6E", l2normdg(&m_deltaGRxn_old[0]), l2normdg(&m_deltaGRxn_new[0])); plogendl(); plogf(" --- Phase_Name KMoles(after update)\n"); plogf(" --- "); writeline('-', 50); for (size_t iph = 0; iph < m_numPhases; iph++) { vcs_VolPhase* Vphase = m_VolPhaseList[iph]; plogf(" --- %18s = %15.7E\n", Vphase->PhaseName, m_tPhaseMoles_new[iph]); } plogf(" "); writeline('-', 103); plogf(" --- Total Old Dimensionless Gibbs Free Energy = %20.13E\n", vcs_Total_Gibbs(&m_molNumSpecies_old[0], &m_feSpecies_old[0], &m_tPhaseMoles_old[0])); plogf(" --- Total New Dimensionless Gibbs Free Energy = %20.13E", vcs_Total_Gibbs(&m_molNumSpecies_new[0], &m_feSpecies_new[0], &m_tPhaseMoles_new[0])); plogendl(); if (DEBUG_MODE_ENABLED && m_VCount->Its > 550) { plogf(" --- Troublesome solve"); plogendl(); } } // RESET VALUES AT END OF ITERATION // UPDATE MOLE NUMBERS // // If the solution wasn't changed in the forcer routine, then copy the // tentative mole numbers and Phase moles into the actual mole numbers and // phase moles. We will consider this current step to be completed. // // Accept the step. -> the tentative solution now becomes the real solution. // If FORCED is true, then we have already done this inside the FORCED loop. vcs_updateMolNumVolPhases(VCS_STATECALC_NEW); m_tPhaseMoles_old = m_tPhaseMoles_new; m_molNumSpecies_old = m_molNumSpecies_new; m_actCoeffSpecies_old = m_actCoeffSpecies_new; m_deltaGRxn_old = m_deltaGRxn_new; m_feSpecies_old = m_feSpecies_new; vcs_setFlagsVolPhases(true, VCS_STATECALC_OLD); // Increment the iteration counters ++m_VCount->Its; ++it1; if (DEBUG_MODE_ENABLED && m_debug_print_lvl >= 2) { plogf(" --- Increment counter increased, step is accepted: %4d", m_VCount->Its); plogendl(); } // HANDLE DELETION OF MULTISPECIES PHASES // // We delete multiphases, when the total moles in the multiphase is reduced // below a relative threshold. Set microscopic multispecies phases with // total relative number of moles less than VCS_DELETE_PHASE_CUTOFF to // absolute zero. bool justDeletedMultiPhase = false; for (size_t iph = 0; iph < m_numPhases; iph++) { if (!m_VolPhaseList[iph]->m_singleSpecies && m_tPhaseMoles_old[iph] != 0.0 && m_tPhaseMoles_old[iph]/m_totalMolNum <= VCS_DELETE_PHASE_CUTOFF) { if (DEBUG_MODE_ENABLED && m_debug_print_lvl >= 1) { plogf(" --- Setting microscopic phase %d to zero", iph); plogendl(); } justDeletedMultiPhase = true; vcs_delete_multiphase(iph); } } // If we have deleted a multispecies phase because the equilibrium moles // decreased, then we will update all the component basis calculation, and // therefore all of the thermo functions just to be safe. if (justDeletedMultiPhase) { bool usedZeroedSpecies; double test = -1.0e-10; int retn = vcs_basopt(false, &m_aw[0], &m_sa[0], &m_sm[0], &m_ss[0], test, &usedZeroedSpecies); if (retn != VCS_SUCCESS) { throw CanteraError("VCS_SOLVE::solve_tp_inner", "BASOPT returned with an error condition"); } vcs_setFlagsVolPhases(false, VCS_STATECALC_OLD); vcs_dfe(VCS_STATECALC_OLD, 0, 0, m_numSpeciesRdc); vcs_deltag(0, true, VCS_STATECALC_OLD); iti = 0; return; } // CHECK FOR ELEMENT ABUNDANCE if (DEBUG_MODE_ENABLED && m_debug_print_lvl >= 2) { plogf(" --- Normal element abundance check"); } vcs_elab(); if (! vcs_elabcheck(0)) { if (DEBUG_MODE_ENABLED && m_debug_print_lvl >= 2) { plogf(" - failed -> redoing element abundances."); plogendl(); } vcs_elcorr(&m_sm[0], &m_wx[0]); vcs_setFlagsVolPhases(false, VCS_STATECALC_OLD); vcs_dfe(VCS_STATECALC_OLD, 0, 0, m_numSpeciesRdc); vcs_deltag(0, true, VCS_STATECALC_OLD); uptodate_minors = true; } else if (DEBUG_MODE_ENABLED && m_debug_print_lvl >= 2) { plogf(" - passed"); plogendl(); } // CHECK FOR OPTIMUM BASIS for (size_t i = 0; i < m_numRxnRdc; ++i) { size_t l = m_indexRxnToSpecies[i]; if (m_speciesUnknownType[l] == VCS_SPECIES_TYPE_INTERFACIALVOLTAGE) { continue; } for (size_t j = 0; j < m_numComponents; ++j) { bool doSwap = false; if (m_SSPhase[j]) { doSwap = (m_molNumSpecies_old[l] * m_spSize[l]) > (m_molNumSpecies_old[j] * m_spSize[j] * 1.01); if (!m_SSPhase[l] && doSwap) { doSwap = m_molNumSpecies_old[l] > (m_molNumSpecies_old[j] * 1.01); } } else { if (m_SSPhase[l]) { doSwap = (m_molNumSpecies_old[l] * m_spSize[l]) > (m_molNumSpecies_old[j] * m_spSize[j] * 1.01); if (!doSwap) { doSwap = m_molNumSpecies_old[l] > (m_molNumSpecies_old[j] * 1.01); } } else { doSwap = (m_molNumSpecies_old[l] * m_spSize[l]) > (m_molNumSpecies_old[j] * m_spSize[j] * 1.01); } } if (doSwap && m_stoichCoeffRxnMatrix(j,i) != 0.0) { if (DEBUG_MODE_ENABLED && m_debug_print_lvl >= 2) { plogf(" --- Get a new basis because "); plogf("%s", m_speciesName[l]); plogf(" is better than comp "); plogf("%s", m_speciesName[j]); plogf(" and share nonzero stoic: %-9.1f", m_stoichCoeffRxnMatrix(j,i)); plogendl(); } forceComponentCalc = 1; return; } } } if (DEBUG_MODE_ENABLED && m_debug_print_lvl >= 2) { plogf(" --- Check for an optimum basis passed"); plogendl(); } stage = EQUILIB_CHECK; // RE-EVALUATE MAJOR-MINOR VECTOR IF NECESSARY // // Skip this section if we haven't done a full calculation. Go right to the // check equilibrium section if (iti != 0) { return; } if (DEBUG_MODE_ENABLED && m_debug_print_lvl >= 2) { plogf(" --- Reevaluate major-minor status of noncomponents:\n"); } m_numRxnMinorZeroed = 0; for (size_t irxn = 0; irxn < m_numRxnRdc; irxn++) { size_t kspec = m_indexRxnToSpecies[irxn]; int speciesType = vcs_species_type(kspec); if (speciesType < VCS_SPECIES_MINOR) { if (DEBUG_MODE_ENABLED && m_debug_print_lvl >= 2 && m_speciesStatus[kspec] >= VCS_SPECIES_MINOR) { plogf(" --- major/minor species is now zeroed out: %s\n", m_speciesName[kspec]); } ++m_numRxnMinorZeroed; } else if (speciesType == VCS_SPECIES_MINOR) { if (DEBUG_MODE_ENABLED && m_debug_print_lvl >= 2 && m_speciesStatus[kspec] != VCS_SPECIES_MINOR) { if (m_speciesStatus[kspec] == VCS_SPECIES_MAJOR) { plogf(" --- Noncomponent turned from major to minor: "); } else if (kspec < m_numComponents) { plogf(" --- Component turned into a minor species: "); } else { plogf(" --- Zeroed Species turned into a " "minor species: "); } plogf("%s\n", m_speciesName[kspec]); } ++m_numRxnMinorZeroed; } else if (speciesType == VCS_SPECIES_MAJOR) { if (m_speciesStatus[kspec] != VCS_SPECIES_MAJOR) { if (DEBUG_MODE_ENABLED && m_debug_print_lvl >= 2) { if (m_speciesStatus[kspec] == VCS_SPECIES_MINOR) { plogf(" --- Noncomponent turned from minor to major: "); } else if (kspec < m_numComponents) { plogf(" --- Component turned into a major: "); } else { plogf(" --- Noncomponent turned from zeroed to major: "); } plogf("%s\n", m_speciesName[kspec]); } m_speciesStatus[kspec] = VCS_SPECIES_MAJOR; } } m_speciesStatus[kspec] = speciesType; } // This logical variable indicates whether all current non-component species // are minor or nonexistent allMinorZeroedSpecies = (m_numRxnMinorZeroed == m_numRxnRdc); } void VCS_SOLVE::solve_tp_equilib_check(bool& allMinorZeroedSpecies, bool& uptodate_minors, bool& giveUpOnElemAbund, int& solveFail, size_t& iti, size_t& it1, int maxit, int& stage, bool& lec) { if (! allMinorZeroedSpecies) { if (DEBUG_MODE_ENABLED && m_debug_print_lvl >= 2) { plogf(" --- Equilibrium check for major species: "); } for (size_t irxn = 0; irxn < m_numRxnRdc; ++irxn) { size_t kspec = irxn + m_numComponents; if (m_speciesStatus[kspec] == VCS_SPECIES_MAJOR && (fabs(m_deltaGRxn_new[irxn]) > m_tolmaj)) { if (m_VCount->Its >= maxit) { solveFail = -1; // Clean up and exit code even though we haven't converged. // -> we have run out of iterations! stage = RETURN_A; } else { if (DEBUG_MODE_ENABLED && m_debug_print_lvl >= 2) { plogf("%s failed\n", m_speciesName[m_indexRxnToSpecies[irxn]]); } // Convergence amongst major species has not been achieved. // Go back and do another iteration with variable ITI iti = ((it1/4) *4) - it1; stage = MAIN; } return; } } if (DEBUG_MODE_ENABLED && m_debug_print_lvl >= 2) { plogf(" MAJOR SPECIES CONVERGENCE achieved"); plogendl(); } } else if (DEBUG_MODE_ENABLED && m_debug_print_lvl >= 2) { plogf(" MAJOR SPECIES CONVERGENCE achieved " "(because there are no major species)"); plogendl(); } // Convergence amongst major species has been achieved // EQUILIBRIUM CHECK FOR MINOR SPECIES if (m_numRxnMinorZeroed != 0) { // Calculate the chemical potential and reaction DeltaG for minor // species, if needed. if (iti != 0) { vcs_setFlagsVolPhases(false, VCS_STATECALC_OLD); vcs_dfe(VCS_STATECALC_OLD, 1, 0, m_numSpeciesRdc); vcs_deltag(1, false, VCS_STATECALC_OLD); uptodate_minors = true; } if (DEBUG_MODE_ENABLED && m_debug_print_lvl >= 2) { plogf(" --- Equilibrium check for minor species: "); } for (size_t irxn = 0; irxn < m_numRxnRdc; ++irxn) { size_t kspec = irxn + m_numComponents; if (m_speciesStatus[kspec] == VCS_SPECIES_MINOR && (fabs(m_deltaGRxn_new[irxn]) > m_tolmin)) { if (m_VCount->Its >= maxit) { solveFail = -1; // Clean up and exit code. -> Even though we have not // converged, we have run out of iterations ! stage = RETURN_A; return; } if (DEBUG_MODE_ENABLED && m_debug_print_lvl >= 2) { plogf("%s failed\n", m_speciesName[m_indexRxnToSpecies[irxn]]); } // Set iti to zero to force a full calculation, and go back to // the main loop to do another iteration. iti = 0; stage = MAIN; return; } } if (DEBUG_MODE_ENABLED && m_debug_print_lvl >= 2) { plogf(" CONVERGENCE achieved\n"); } } // FINAL ELEMENTAL ABUNDANCE CHECK // Recalculate the element abundance vector again vcs_updateVP(VCS_STATECALC_OLD); vcs_elab(); // LEC is only true when we are near the end game if (lec) { if (!giveUpOnElemAbund) { if (DEBUG_MODE_ENABLED && m_debug_print_lvl >= 2) { plogf(" --- Check the Full Element Abundances: "); } // Final element abundance check: If we fail then we need to go back // and correct the element abundances, and then go do a major step if (! vcs_elabcheck(1)) { if (DEBUG_MODE_ENABLED && m_debug_print_lvl >= 2) { if (! vcs_elabcheck(0)) { plogf(" failed\n"); } else { plogf(" passed for NC but failed for NE: RANGE ERROR\n"); } } // delete? stage = ELEM_ABUND_CHECK; return; } if (DEBUG_MODE_ENABLED && m_debug_print_lvl >= 2) { plogf(" passed\n"); } } // If we have deleted a species then we need to recheck the the deleted // species, before exiting if (m_numSpeciesRdc != m_numSpeciesTot) { stage = RECHECK_DELETED; return; } // Final checks are passed -> go check out stage = RETURN_A; } lec = true; } void VCS_SOLVE::solve_tp_elem_abund_check(size_t& iti, int& stage, bool& lec, bool& giveUpOnElemAbund, int& finalElemAbundAttempts, int& rangeErrorFound) { // HKM - Put in an element abundance check. The element abundances were // being corrected even if they were perfectly OK to start with. This is // actually an expensive operation, so I took it out. Also vcs_dfe() doesn't // need to be called if no changes were made. rangeErrorFound = 0; if (! vcs_elabcheck(1)) { bool ncBefore = vcs_elabcheck(0); vcs_elcorr(&m_sm[0], &m_wx[0]); bool ncAfter = vcs_elabcheck(0); bool neAfter = vcs_elabcheck(1); // Go back to evaluate the total moles of gas and liquid. vcs_setFlagsVolPhases(false, VCS_STATECALC_OLD); vcs_dfe(VCS_STATECALC_OLD, 0, 0, m_numSpeciesRdc); vcs_deltag(0, false, VCS_STATECALC_OLD); if (!ncBefore) { if (ncAfter) { // We have breathed new life into the old problem. Now the // element abundances up to NC agree. Go back and restart the // main loop calculation, resetting the end conditions. lec = false; iti = 0; stage = MAIN; } else { // We are still hosed if (finalElemAbundAttempts >= 3) { giveUpOnElemAbund = true; stage = EQUILIB_CHECK; } else { finalElemAbundAttempts++; lec = false; iti = 0; stage = MAIN; } } return; } else if (ncAfter) { if (!neAfter) { // Probably an unrecoverable range error if (DEBUG_MODE_ENABLED && m_debug_print_lvl >= 2) { plogf(" --- vcs_solve_tp: RANGE SPACE ERROR ENCOUNTERED\n"); plogf(" --- vcs_solve_tp: - Giving up on NE Element Abundance satisfaction \n"); plogf(" --- vcs_solve_tp: - However, NC Element Abundance criteria is satisfied \n"); plogf(" --- vcs_solve_tp: - Returning the calculated equilibrium condition "); plogendl(); } rangeErrorFound = 1; giveUpOnElemAbund = true; } // Recovery of end element abundances -> go do equilibrium check // again and then check out. stage = EQUILIB_CHECK; return; } } // Calculate delta g's vcs_deltag(0, false, VCS_STATECALC_OLD); // Go back to equilibrium check as a prep to eventually checking out stage = EQUILIB_CHECK; } double VCS_SOLVE::vcs_minor_alt_calc(size_t kspec, size_t irxn, bool* do_delete, char* ANOTE) const { double dx = 0.0, a; double w_kspec = m_molNumSpecies_old[kspec]; double molNum_kspec_new; double wTrial, tmp; double dg_irxn = m_deltaGRxn_old[irxn]; doublereal s; size_t iph = m_phaseID[kspec]; *do_delete = false; if (m_speciesUnknownType[kspec] != VCS_SPECIES_TYPE_INTERFACIALVOLTAGE) { if (w_kspec <= 0.0) { w_kspec = VCS_DELETE_MINORSPECIES_CUTOFF; } dg_irxn = std::max(dg_irxn, -200.0); if (DEBUG_MODE_ENABLED && ANOTE) { sprintf(ANOTE,"minor species alternative calc"); } if (dg_irxn >= 23.0) { molNum_kspec_new = w_kspec * 1.0e-10; if (w_kspec < VCS_DELETE_MINORSPECIES_CUTOFF) { goto L_ZERO_SPECIES; } return molNum_kspec_new - w_kspec; } else { if (fabs(dg_irxn) <= m_tolmin2) { molNum_kspec_new = w_kspec; return 0.0; } } // get the diagonal of the activity coefficient Jacobian s = m_np_dLnActCoeffdMolNum(kspec,kspec) / m_tPhaseMoles_old[iph]; // We fit it to a power law approximation of the activity coefficient // // gamma = gamma_0 * ( x / x0)**a // // where a is forced to be a little bit greater than -1. // We do this so that the resulting expression is always nonnegative // We then solve the resulting calculation: // // gamma * x = gamma_0 * x0 exp (-deltaG/RT); a = clip(w_kspec * s, -1.0+1e-8, 100.0); tmp = clip(-dg_irxn / (1.0 + a), -200.0, 200.0); wTrial = w_kspec * exp(tmp); molNum_kspec_new = wTrial; if (wTrial > 100. * w_kspec) { double molNumMax = 0.0001 * m_tPhaseMoles_old[iph]; if (molNumMax < 100. * w_kspec) { molNumMax = 100. * w_kspec; } if (wTrial > molNumMax) { molNum_kspec_new = molNumMax; } else { molNum_kspec_new = wTrial; } } else if (1.0E10 * wTrial < w_kspec) { molNum_kspec_new= 1.0E-10 * w_kspec; } else { molNum_kspec_new = wTrial; } if ((molNum_kspec_new) < VCS_DELETE_MINORSPECIES_CUTOFF) { goto L_ZERO_SPECIES; } return molNum_kspec_new - w_kspec; // Alternate return based for cases where we need to delete the species // from the current list of active species, because its concentration // has gotten too small. L_ZERO_SPECIES: ; *do_delete = true; return - w_kspec; } else { // Voltage calculation // Need to check the sign -> This is good for electrons dx = m_deltaGRxn_old[irxn]/ m_Faraday_dim; if (DEBUG_MODE_ENABLED && ANOTE) { sprintf(ANOTE,"voltage species alternative calc"); } } return dx; } int VCS_SOLVE::delta_species(const size_t kspec, double* const delta_ptr) { size_t irxn = kspec - m_numComponents; int retn = 1; double delta = *delta_ptr; AssertThrowMsg(kspec >= m_numComponents, "VCS_SOLVE::delta_species", "delete_species() ERROR: called for a component {}", kspec); if (m_speciesUnknownType[kspec] != VCS_SPECIES_TYPE_INTERFACIALVOLTAGE) { // Attempt the given dx. If it doesn't work, try to see if a smaller one // would work, double dx = delta; double* sc_irxn = m_stoichCoeffRxnMatrix.ptrColumn(irxn); for (size_t j = 0; j < m_numComponents; ++j) { if (m_molNumSpecies_old[j] > 0.0) { double tmp = sc_irxn[j] * dx; if (-tmp > m_molNumSpecies_old[j]) { retn = 0; dx = std::min(dx, - m_molNumSpecies_old[j] / sc_irxn[j]); } } // If the component has a zero concentration and is a reactant // in the formation reaction, then dx == 0.0, and we just return. if (m_molNumSpecies_old[j] <= 0.0 && sc_irxn[j] < 0.0) { *delta_ptr = 0.0; return 0; } } // ok, we found a positive dx. implement it. *delta_ptr = dx; m_molNumSpecies_old[kspec] += dx; size_t iph = m_phaseID[kspec]; m_tPhaseMoles_old[iph] += dx; vcs_setFlagsVolPhase(iph, false, VCS_STATECALC_OLD); for (size_t j = 0; j < m_numComponents; ++j) { double tmp = sc_irxn[j] * dx; if (tmp != 0.0) { iph = m_phaseID[j]; m_molNumSpecies_old[j] += tmp; m_tPhaseMoles_old[iph] += tmp; vcs_setFlagsVolPhase(iph, false, VCS_STATECALC_OLD); m_molNumSpecies_old[j] = std::max(m_molNumSpecies_old[j], 0.0); } } } return retn; } int VCS_SOLVE::vcs_zero_species(const size_t kspec) { int retn = 1; // Calculate a delta that will eliminate the species. if (m_speciesUnknownType[kspec] != VCS_SPECIES_TYPE_INTERFACIALVOLTAGE) { double dx = -m_molNumSpecies_old[kspec]; if (dx != 0.0) { retn = delta_species(kspec, &dx); if (DEBUG_MODE_ENABLED && !retn && m_debug_print_lvl >= 1) { plogf("vcs_zero_species: Couldn't zero the species %d, " "did delta of %g. orig conc of %g", kspec, dx, m_molNumSpecies_old[kspec] + dx); plogendl(); } } } return retn; } int VCS_SOLVE::vcs_delete_species(const size_t kspec) { const size_t klast = m_numSpeciesRdc - 1; const size_t iph = m_phaseID[kspec]; vcs_VolPhase* const Vphase = m_VolPhaseList[iph]; const size_t irxn = kspec - m_numComponents; // Zero the concentration of the species. // -> This zeroes w[kspec] and modifies m_tPhaseMoles_old[] const int retn = vcs_zero_species(kspec); if (!retn) { throw CanteraError("VCS_SOLVE::vcs_delete_species", "Failed to delete a species!"); } // Decrement the minor species counter if the current species is a minor // species if (m_speciesStatus[kspec] != VCS_SPECIES_MAJOR) { --m_numRxnMinorZeroed; } m_speciesStatus[kspec] = VCS_SPECIES_DELETED; m_deltaGRxn_new[irxn] = 0.0; m_deltaGRxn_old[irxn] = 0.0; m_feSpecies_new[kspec] = 0.0; m_feSpecies_old[kspec] = 0.0; m_molNumSpecies_new[kspec] = m_molNumSpecies_old[kspec]; // Rearrange the data if the current species isn't the last active species. if (kspec != klast) { vcs_switch_pos(true, klast, kspec); } // Adjust the total moles in a phase downwards. Vphase->setMolesFromVCSCheck(VCS_STATECALC_OLD, &m_molNumSpecies_old[0], &m_tPhaseMoles_old[0]); // Adjust the current number of active species and reactions counters --m_numRxnRdc; --m_numSpeciesRdc; // Check to see whether we have just annihilated a multispecies phase. If it // is extinct, call the delete_multiphase() function. if (! m_SSPhase[klast] && Vphase->exists() != VCS_PHASE_EXIST_ALWAYS) { bool stillExists = false; for (size_t k = 0; k < m_numSpeciesRdc; k++) { if (m_speciesUnknownType[k] != VCS_SPECIES_TYPE_INTERFACIALVOLTAGE && m_phaseID[k] == iph && m_molNumSpecies_old[k] > 0.0) { stillExists = true; break; } } if (!stillExists) { vcs_delete_multiphase(iph); } } // When the total number of noncomponent species is zero, we have to signal // the calling code return (m_numRxnRdc == 0); } void VCS_SOLVE::vcs_reinsert_deleted(size_t kspec) { size_t iph = m_phaseID[kspec]; if (DEBUG_MODE_ENABLED && m_debug_print_lvl >= 2) { plogf(" --- Add back a deleted species: %-12s\n", m_speciesName[kspec]); } // Set the species back to minor species status // this adjusts m_molNumSpecies_old[] and m_tPhaseMoles_old[] // HKM -> make this a relative mole number! double dx = m_tPhaseMoles_old[iph] * VCS_RELDELETE_SPECIES_CUTOFF * 10.; delta_species(kspec, &dx); m_speciesStatus[kspec] = VCS_SPECIES_MINOR; if (m_SSPhase[kspec]) { m_speciesStatus[kspec] = VCS_SPECIES_MAJOR; --m_numRxnMinorZeroed; } vcs_VolPhase* Vphase = m_VolPhaseList[iph]; Vphase->setMolesFromVCSCheck(VCS_STATECALC_OLD, &m_molNumSpecies_old[0], &m_tPhaseMoles_old[0]); // We may have popped a multispecies phase back into existence. If we did, // we have to check the other species in that phase. Take care of the // m_speciesStatus[] flag. The value of m_speciesStatus[] must change from // VCS_SPECIES_ZEROEDPHASE to VCS_SPECIES_ZEROEDMS for those other species. if (! m_SSPhase[kspec]) { if (Vphase->exists() == VCS_PHASE_EXIST_NO) { Vphase->setExistence(VCS_PHASE_EXIST_YES); for (size_t k = 0; k < m_numSpeciesTot; k++) { if (m_phaseID[k] == iph && m_speciesStatus[k] != VCS_SPECIES_DELETED) { m_speciesStatus[k] = VCS_SPECIES_MINOR; } } } } else { Vphase->setExistence(VCS_PHASE_EXIST_YES); } ++m_numRxnRdc; ++m_numSpeciesRdc; ++m_numRxnMinorZeroed; if (kspec != (m_numSpeciesRdc - 1)) { // Rearrange both the species and the non-component global data vcs_switch_pos(true, (m_numSpeciesRdc - 1), kspec); } } bool VCS_SOLVE::vcs_delete_multiphase(const size_t iph) { vcs_VolPhase* Vphase = m_VolPhaseList[iph]; bool successful = true; // set the phase existence flag to dead Vphase->setTotalMoles(0.0); if (DEBUG_MODE_ENABLED && m_debug_print_lvl >= 2) { plogf(" --- delete_multiphase %d, %s\n", iph, Vphase->PhaseName); } // Loop over all of the species in the phase. for (size_t kspec = m_numComponents; kspec < m_numSpeciesRdc; ++kspec) { if (m_phaseID[kspec] == iph) { if (m_speciesUnknownType[kspec] != VCS_SPECIES_TYPE_INTERFACIALVOLTAGE) { // calculate an extent of rxn, dx, that zeroes out the species. double dx = - m_molNumSpecies_old[kspec]; double dxTent = dx; int retn = delta_species(kspec, &dxTent); if (retn != 1) { successful = false; if (DEBUG_MODE_ENABLED && m_debug_print_lvl >= 2) { plogf(" --- delete_multiphase %d, %s ERROR problems deleting species %s\n", iph, Vphase->PhaseName, m_speciesName[kspec]); plogf(" --- delta attempted: %g achieved: %g " " Zeroing it manually\n", dx, dxTent); } m_molNumSpecies_old[kspec] = 0.0; m_molNumSpecies_new[kspec] = 0.0; m_deltaMolNumSpecies[kspec] = 0.0; // recover the total phase moles. vcs_tmoles(); } else { // Set the mole number of that species to zero. m_molNumSpecies_old[kspec] = 0.0; m_molNumSpecies_new[kspec] = 0.0; m_deltaMolNumSpecies[kspec] = 0.0; } // Change the status flag of the species to that of an zeroed // phase m_speciesStatus[kspec] = VCS_SPECIES_ZEROEDMS; } } } double dj, dxWant, dxPerm = 0.0, dxPerm2 = 0.0; for (size_t kcomp = 0; kcomp < m_numComponents; ++kcomp) { if (m_phaseID[kcomp] == iph) { if (DEBUG_MODE_ENABLED && m_debug_print_lvl >= 2) { plogf(" --- delete_multiphase One of the species is a component %d - %s with mole number %g\n", kcomp, m_speciesName[kcomp], m_molNumSpecies_old[kcomp]); } if (m_molNumSpecies_old[kcomp] != 0.0) { for (size_t kspec = m_numComponents; kspec < m_numSpeciesRdc; ++kspec) { size_t irxn = kspec - m_numComponents; if (m_phaseID[kspec] != iph) { if (m_stoichCoeffRxnMatrix(kcomp,irxn) != 0.0) { dxWant = -m_molNumSpecies_old[kcomp] / m_stoichCoeffRxnMatrix(kcomp,irxn); if (dxWant + m_molNumSpecies_old[kspec] < 0.0) { dxPerm = -m_molNumSpecies_old[kspec]; } for (size_t jcomp = 0; kcomp < m_numComponents; ++kcomp) { if (jcomp != kcomp) { if (m_phaseID[jcomp] == iph) { dxPerm = 0.0; } else { dj = dxWant * m_stoichCoeffRxnMatrix(jcomp,irxn); if (dj + m_molNumSpecies_old[kcomp] < 0.0) { dxPerm2 = -m_molNumSpecies_old[kcomp] / m_stoichCoeffRxnMatrix(jcomp,irxn); } if (fabs(dxPerm2) < fabs(dxPerm)) { dxPerm = dxPerm2; } } } } } if (dxPerm != 0.0) { delta_species(kspec, &dxPerm); } } } } if (m_molNumSpecies_old[kcomp] != 0.0) { if (DEBUG_MODE_ENABLED && m_debug_print_lvl >= 2) { plogf(" --- delete_multiphase One of the species is a component %d - %s still with mole number %g\n", kcomp, m_speciesName[kcomp], m_molNumSpecies_old[kcomp]); plogf(" --- zeroing it \n"); } m_molNumSpecies_old[kcomp] = 0.0; } m_speciesStatus[kcomp] = VCS_SPECIES_ZEROEDMS; } } // Loop over all of the inactive species in the phase: Right now we // reinstate all species in a deleted multiphase. We may only want to // reinstate the "major ones" in the future. Note, species in phases with // zero mole numbers are still considered active. Whether the phase pops // back into existence or not is checked as part of the main iteration loop. for (size_t kspec = m_numSpeciesRdc; kspec < m_numSpeciesTot; ++kspec) { if (m_phaseID[kspec] == iph) { m_molNumSpecies_old[kspec] = 0.0; m_molNumSpecies_new[kspec] = 0.0; m_deltaMolNumSpecies[kspec] = 0.0; m_speciesStatus[kspec] = VCS_SPECIES_ZEROEDMS; ++m_numRxnRdc; ++m_numSpeciesRdc; if (DEBUG_MODE_ENABLED && m_debug_print_lvl >= 2) { plogf(" --- Make %s", m_speciesName[kspec]); plogf(" an active but zeroed species because its phase " "was zeroed\n"); } if (kspec != (m_numSpeciesRdc - 1)) { // Rearrange both the species and the non-component global data vcs_switch_pos(true, (m_numSpeciesRdc - 1), kspec); } } } // Zero out the total moles counters for the phase m_tPhaseMoles_old[iph] = 0.0; m_tPhaseMoles_new[iph] = 0.0; m_deltaPhaseMoles[iph] = 0.0; // Upload the state to the VP object Vphase->setTotalMoles(0.0); return successful; } int VCS_SOLVE::vcs_recheck_deleted() { vector_fp& xtcutoff = m_TmpPhase; if (DEBUG_MODE_ENABLED && m_debug_print_lvl >= 2) { plogf(" --- Start rechecking deleted species in multispec phases\n"); } if (m_numSpeciesRdc == m_numSpeciesTot) { return 0; } // Use the standard chemical potentials for the chemical potentials of // deleted species. Then, calculate Delta G for for formation reactions. // Note: fe[] here includes everything except for the ln(x[i]) term for (size_t kspec = m_numSpeciesRdc; kspec < m_numSpeciesTot; ++kspec) { size_t iph = m_phaseID[kspec]; m_feSpecies_new[kspec] = (m_SSfeSpecies[kspec] + log(m_actCoeffSpecies_old[kspec]) - m_lnMnaughtSpecies[kspec] + m_chargeSpecies[kspec] * m_Faraday_dim * m_phasePhi[iph]); } // Recalculate the DeltaG's of the formation reactions for the deleted // species in the mechanism vcs_deltag(0, true, VCS_STATECALC_NEW); for (size_t iph = 0; iph < m_numPhases; iph++) { if (m_tPhaseMoles_old[iph] > 0.0) { xtcutoff[iph] = log(1.0 / VCS_RELDELETE_SPECIES_CUTOFF); } else { xtcutoff[iph] = 0.0; } } // We are checking the equation: // // sum_u = sum_j_comp [ sigma_i_j * u_j ] // = u_i_O + log((AC_i * W_i)/m_tPhaseMoles_old) // // by first evaluating: // // DG_i_O = u_i_O - sum_u. // // Then, if TL is zero, the phase pops into existence if DG_i_O < 0. Also, // if the phase exists, then we check to see if the species can have a mole // number larger than VCS_DELETE_SPECIES_CUTOFF (default value = 1.0E-32). // // HKM: This seems to be an inconsistency in the algorithm here that needs // correcting. The requirement above may bypass some multiphases which // should exist. The real requirement for the phase to exist is: // // sum_i_in_phase [ exp(-DG_i_O) ] >= 1.0 // // Thus, we need to amend the code. Also nonideal solutions will tend to // complicate matters severely also. int npb = 0; for (size_t irxn = m_numRxnRdc; irxn < m_numRxnTot; ++irxn) { size_t kspec = m_indexRxnToSpecies[irxn]; size_t iph = m_phaseID[kspec]; if (m_tPhaseMoles_old[iph] == 0.0) { if (m_deltaGRxn_new[irxn] < 0.0) { vcs_reinsert_deleted(kspec); npb++; } else { m_molNumSpecies_old[kspec] = 0.0; } } else if (m_tPhaseMoles_old[iph] > 0.0) { if (m_deltaGRxn_new[irxn] < xtcutoff[iph]) { vcs_reinsert_deleted(kspec); npb++; } } } return npb; } bool VCS_SOLVE::recheck_deleted_phase(const int iphase) { // Check first to see if the phase is in fact deleted const vcs_VolPhase* Vphase = m_VolPhaseList[iphase]; if (Vphase->exists() != VCS_PHASE_EXIST_NO) { return false; } if (Vphase->exists() == VCS_PHASE_EXIST_ZEROEDPHASE) { return false; } if (Vphase->m_singleSpecies) { size_t kspec = Vphase->spGlobalIndexVCS(0); size_t irxn = kspec + m_numComponents; if (m_deltaGRxn_old[irxn] < 0.0) { return true; } return false; } double phaseDG = 1.0; for (size_t kk = 0; kk < Vphase->nSpecies(); kk++) { size_t kspec = Vphase->spGlobalIndexVCS(kk); size_t irxn = kspec + m_numComponents; m_deltaGRxn_old[irxn] = clip(m_deltaGRxn_old[irxn], -50.0, 50.0); phaseDG -= exp(-m_deltaGRxn_old[irxn]); } if (phaseDG < 0.0) { return true; } return false; } size_t VCS_SOLVE::vcs_add_all_deleted() { size_t retn; if (m_numSpeciesRdc == m_numSpeciesTot) { return 0; } // Use the standard chemical potentials for the chemical potentials of // deleted species. Then, calculate Delta G for for formation reactions. We // are relying here on a old saved value of m_actCoeffSpecies_old[kspec] // being sufficiently good. Note, we will recalculate everything at the end // of the routine. m_molNumSpecies_new = m_molNumSpecies_old; for (int cits = 0; cits < 3; cits++) { for (size_t kspec = m_numSpeciesRdc; kspec < m_numSpeciesTot; ++kspec) { size_t iph = m_phaseID[kspec]; vcs_VolPhase* Vphase = m_VolPhaseList[iph]; if (m_molNumSpecies_new[kspec] == 0.0) { m_molNumSpecies_new[kspec] = VCS_DELETE_MINORSPECIES_CUTOFF * 1.0E-10; } if (!Vphase->m_singleSpecies) { Vphase->sendToVCS_ActCoeff(VCS_STATECALC_NEW, &m_actCoeffSpecies_new[0]); } m_feSpecies_new[kspec] = (m_SSfeSpecies[kspec] + log(m_actCoeffSpecies_new[kspec]) - m_lnMnaughtSpecies[kspec] + m_chargeSpecies[kspec] * m_Faraday_dim * m_phasePhi[iph]); } // Recalculate the DeltaG's of the formation reactions for the deleted // species in the mechanism vcs_deltag(0, true, VCS_STATECALC_NEW); for (size_t irxn = m_numRxnRdc; irxn < m_numRxnTot; ++irxn) { size_t kspec = m_indexRxnToSpecies[irxn]; size_t iph = m_phaseID[kspec]; if (m_tPhaseMoles_old[iph] > 0.0) { double maxDG = std::min(m_deltaGRxn_new[irxn], 690.0); double dx = m_tPhaseMoles_old[iph] * exp(- maxDG); m_molNumSpecies_new[kspec] = dx; } } } for (size_t irxn = m_numRxnRdc; irxn < m_numRxnTot; ++irxn) { size_t kspec = m_indexRxnToSpecies[irxn]; size_t iph = m_phaseID[kspec]; if (m_tPhaseMoles_old[iph] > 0.0) { double dx = m_molNumSpecies_new[kspec]; retn = delta_species(kspec, &dx); if (retn == 0) { if (DEBUG_MODE_ENABLED && m_debug_print_lvl) { plogf(" --- add_deleted(): delta_species() failed for species %s (%d) with mol number %g\n", m_speciesName[kspec], kspec, dx); } if (dx > 1.0E-50) { dx = 1.0E-50; retn = delta_species(kspec, &dx); if (DEBUG_MODE_ENABLED && retn == 0 && m_debug_print_lvl) { plogf(" --- add_deleted(): delta_species() failed for species %s (%d) with mol number %g\n", m_speciesName[kspec], kspec, dx); } } } if (DEBUG_MODE_ENABLED && m_debug_print_lvl >= 2) { if (retn != 0) { plogf(" --- add_deleted(): species %s added back in with mol number %g", m_speciesName[kspec], dx); plogendl(); } else { plogf(" --- add_deleted(): species %s failed to be added back in"); plogendl(); } } } } vcs_setFlagsVolPhases(false, VCS_STATECALC_OLD); vcs_dfe(VCS_STATECALC_OLD, 0, 0, m_numSpeciesTot); vcs_deltag(0, true, VCS_STATECALC_OLD); retn = 0; for (size_t irxn = m_numRxnRdc; irxn < m_numRxnTot; ++irxn) { size_t kspec = m_indexRxnToSpecies[irxn]; size_t iph = m_phaseID[kspec]; if (m_tPhaseMoles_old[iph] > 0.0 && fabs(m_deltaGRxn_old[irxn]) > m_tolmin) { if (((m_molNumSpecies_old[kspec] * exp(-m_deltaGRxn_old[irxn])) > VCS_DELETE_MINORSPECIES_CUTOFF) || (m_molNumSpecies_old[kspec] > VCS_DELETE_MINORSPECIES_CUTOFF)) { retn++; if (DEBUG_MODE_ENABLED && m_debug_print_lvl >= 2) { plogf(" --- add_deleted(): species %s with mol number %g not converged: DG = %g", m_speciesName[kspec], m_molNumSpecies_old[kspec], m_deltaGRxn_old[irxn]); plogendl(); } } } } return retn; } bool VCS_SOLVE::vcs_globStepDamp() { double* dptr = &m_deltaGRxn_new[0]; // CALCULATE SLOPE AT END OF THE STEP double s2 = 0.0; for (size_t irxn = 0; irxn < m_numRxnRdc; ++irxn) { size_t kspec = irxn + m_numComponents; if (m_speciesUnknownType[kspec] != VCS_SPECIES_TYPE_INTERFACIALVOLTAGE) { s2 += dptr[irxn] * m_deltaMolNumSpecies[kspec]; } } // CALCULATE ORIGINAL SLOPE double s1 = 0.0; dptr = &m_deltaGRxn_old[0]; for (size_t irxn = 0; irxn < m_numRxnRdc; ++irxn) { size_t kspec = irxn + m_numComponents; if (m_speciesUnknownType[kspec] != VCS_SPECIES_TYPE_INTERFACIALVOLTAGE) { s1 += dptr[irxn] * m_deltaMolNumSpecies[kspec]; } } if (DEBUG_MODE_ENABLED && m_debug_print_lvl >= 2) { plogf(" --- subroutine FORCE: Beginning Slope = %g\n", s1); plogf(" --- subroutine FORCE: End Slope = %g\n", s2); } if (s1 > 0.0) { if (DEBUG_MODE_ENABLED && m_debug_print_lvl >= 2) { plogf(" --- subroutine FORCE produced no adjustments,"); if (s1 < 1.0E-40) { plogf(" s1 positive but really small"); } else { plogf(" failed s1 test"); } plogendl(); } return false; } if (s2 <= 0.0) { if (DEBUG_MODE_ENABLED && m_debug_print_lvl >= 2) { plogf(" --- subroutine FORCE produced no adjustments, s2 < 0"); plogendl(); } return false; } // FIT PARABOLA double al = 1.0; if (fabs(s1 -s2) > 1.0E-200) { al = s1 / (s1 - s2); } if (al >= 0.95 || al < 0.0) { if (DEBUG_MODE_ENABLED && m_debug_print_lvl >= 2) { plogf(" --- subroutine FORCE produced no adjustments (al = %g)\n", al); } return false; } if (DEBUG_MODE_ENABLED && m_debug_print_lvl >= 2) { plogf(" --- subroutine FORCE produced a damping factor = %g\n", al); } // ADJUST MOLE NUMBERS, CHEM. POT if (DEBUG_MODE_ENABLED && m_debug_print_lvl >= 2) { m_deltaGRxn_tmp = m_deltaGRxn_new; } dptr = &m_molNumSpecies_new[0]; for (size_t kspec = 0; kspec < m_numSpeciesRdc; ++kspec) { m_molNumSpecies_new[kspec] = m_molNumSpecies_old[kspec] + al * m_deltaMolNumSpecies[kspec]; } for (size_t iph = 0; iph < m_numPhases; iph++) { m_tPhaseMoles_new[iph] = m_tPhaseMoles_old[iph] + al * m_deltaPhaseMoles[iph]; } vcs_updateVP(VCS_STATECALC_NEW); if (DEBUG_MODE_ENABLED && m_debug_print_lvl >= 2) { plogf(" --- subroutine FORCE adjusted the mole " "numbers, AL = %10.3f\n", al); } // Because we changed the mole numbers, we need to calculate the chemical // potentials again. If a major-only step is being carried out, then we // don't need to update the minor noncomponents. vcs_setFlagsVolPhases(false, VCS_STATECALC_NEW); vcs_dfe(VCS_STATECALC_NEW, 0, 0, m_numSpeciesRdc); // Evaluate DeltaG for all components if ITI=0, and for major components // only if ITI NE 0 vcs_deltag(0, false, VCS_STATECALC_NEW); dptr = &m_deltaGRxn_new[0]; s2 = 0.0; for (size_t irxn = 0; irxn < m_numRxnRdc; ++irxn) { size_t kspec = irxn + m_numComponents; if (m_speciesUnknownType[kspec] != VCS_SPECIES_TYPE_INTERFACIALVOLTAGE) { s2 += dptr[irxn] * m_deltaMolNumSpecies[kspec]; } } if (DEBUG_MODE_ENABLED && m_debug_print_lvl >= 2) { plogf(" --- subroutine FORCE: Adj End Slope = %g", s2); plogendl(); } return true; } int VCS_SOLVE::vcs_basopt(const bool doJustComponents, double aw[], double sa[], double sm[], double ss[], double test, bool* const usedZeroedSpecies) { size_t k; size_t juse = npos; size_t jlose = npos; double* scrxn_ptr; clockWC tickTock; if (DEBUG_MODE_ENABLED && m_debug_print_lvl >= 2) { plogf(" "); for (size_t i=0; i<77; i++) { plogf("-"); } plogf("\n"); plogf(" --- Subroutine BASOPT called to "); if (doJustComponents) { plogf("calculate the number of components\n"); } else { plogf("reevaluate the components\n"); } if (m_debug_print_lvl >= 2) { plogf("\n"); plogf(" --- Formula Matrix used in BASOPT calculation\n"); plogf(" --- Active | "); for (size_t j = 0; j < m_numElemConstraints; j++) { plogf(" %1d ", m_elementActive[j]); } plogf("\n"); plogf(" --- Species | "); for (size_t j = 0; j < m_numElemConstraints; j++) { plogf(" "); vcs_print_stringTrunc(m_elementName[j].c_str(), 8, 1); } plogf("\n"); for (k = 0; k < m_numSpeciesTot; k++) { plogf(" --- "); vcs_print_stringTrunc(m_speciesName[k].c_str(), 11, 1); plogf(" | "); for (size_t j = 0; j < m_numElemConstraints; j++) { plogf(" %8.2g", m_formulaMatrix(k,j)); } plogf("\n"); } plogendl(); } } // Calculate the maximum value of the number of components possible. It's // equal to the minimum of the number of elements and the number of total // species. size_t ncTrial = std::min(m_numElemConstraints, m_numSpeciesTot); m_numComponents = ncTrial; *usedZeroedSpecies = false; vector_int ipiv(ncTrial); int info; // Use a temporary work array for the mole numbers, aw[] std::copy(m_molNumSpecies_old.begin(), m_molNumSpecies_old.begin() + m_numSpeciesTot, aw); // Take out the Voltage unknowns from consideration for (k = 0; k < m_numSpeciesTot; k++) { if (m_speciesUnknownType[k] == VCS_SPECIES_TYPE_INTERFACIALVOLTAGE) { aw[k] = test; } } size_t jr = 0; // Top of a loop of some sort based on the index JR. JR is the current // number of component species found. while (jr < ncTrial) { // Top of another loop point based on finding a linearly independent // species while (true) { // Search the remaining part of the mole fraction vector, AW, for // the largest remaining species. Return its identity in K. The // first search criteria is always the largest positive magnitude of // the mole number. k = vcs_basisOptMax(aw, jr, m_numSpeciesTot); // The fun really starts when you have run out of species that have // a significant concentration. It becomes extremely important to // make a good choice of which species you want to pick to fill out // the basis. Basically, you don't want to use species with elements // abundances which aren't pegged to zero. This means that those // modes will never be allowed to grow. You want to have the best // chance that the component will grow positively. // // Suppose you start with CH4, N2, as the only species with nonzero // compositions. You have the following abundances: // // Abundances: // ---------------- // C 2.0 // N 2.0 // H 4.0 // O 0.0 // // For example, Make the following choice: // // CH4 N2 O choose -> OH // or // CH4 N2 O choose -> H2 // // OH and H2 both fill out the basis. They will pass the algorithm. // However, choosing OH as the next species will create a situation // where H2 can not grow in concentration. This happened in // practice, btw. The reason is that the formation reaction for H2 // will cause one of the component species to go negative. // // The basic idea here is to pick a simple species whose mole number // can grow according to the element compositions. Candidates are // still filtered according to their linear independence. // // Note, if there is electronic charge and the electron species, you // should probably pick the electron as a component, if it linearly // independent. The algorithm below will do this automagically. if ((aw[k] != test) && aw[k] < VCS_DELETE_MINORSPECIES_CUTOFF) { *usedZeroedSpecies = true; double maxConcPossKspec = 0.0; double maxConcPoss = 0.0; size_t kfound = npos; int minNonZeroes = 100000; int nonZeroesKspec = 0; for (size_t kspec = ncTrial; kspec < m_numSpeciesTot; kspec++) { if (aw[kspec] >= 0.0 && m_speciesUnknownType[kspec] != VCS_SPECIES_TYPE_INTERFACIALVOLTAGE) { maxConcPossKspec = 1.0E10; nonZeroesKspec = 0; for (size_t j = 0; j < m_numElemConstraints; ++j) { if (m_elementActive[j] && m_elType[j] == VCS_ELEM_TYPE_ABSPOS) { double nu = m_formulaMatrix(kspec,j); if (nu != 0.0) { nonZeroesKspec++; maxConcPossKspec = std::min(m_elemAbundancesGoal[j] / nu, maxConcPossKspec); } } } if ((maxConcPossKspec >= maxConcPoss) || (maxConcPossKspec > 1.0E-5)) { if (nonZeroesKspec <= minNonZeroes) { if (kfound == npos || nonZeroesKspec < minNonZeroes) { kfound = kspec; } else { // ok we are sitting pretty equal here // decide on the raw ss Gibbs energy if (m_SSfeSpecies[kspec] <= m_SSfeSpecies[kfound]) { kfound = kspec; } } } minNonZeroes = std::min(minNonZeroes, nonZeroesKspec); maxConcPoss = std::max(maxConcPoss, maxConcPossKspec); } } } if (kfound == npos) { double gmin = 0.0; kfound = k; for (size_t kspec = ncTrial; kspec < m_numSpeciesTot; kspec++) { if (aw[kspec] >= 0.0) { size_t irxn = kspec - ncTrial; if (m_deltaGRxn_new[irxn] < gmin) { gmin = m_deltaGRxn_new[irxn]; kfound = kspec; } } } } k = kfound; } if (aw[k] == test) { m_numComponents = jr; ncTrial = m_numComponents; size_t numPreDeleted = m_numRxnTot - m_numRxnRdc; if (numPreDeleted != (m_numSpeciesTot - m_numSpeciesRdc)) { throw CanteraError("VCS_SOLVE::vcs_basopt", "we shouldn't be here"); } m_numRxnTot = m_numSpeciesTot - ncTrial; m_numRxnRdc = m_numRxnTot - numPreDeleted; m_numSpeciesRdc = m_numSpeciesTot - numPreDeleted; for (size_t i = 0; i < m_numSpeciesTot; ++i) { m_indexRxnToSpecies[i] = ncTrial + i; } if (DEBUG_MODE_ENABLED && m_debug_print_lvl >= 2) { plogf(" --- Total number of components found = %3d (ne = %d)\n ", ncTrial, m_numElemConstraints); } goto L_END_LOOP; } // Assign a small negative number to the component that we have just // found, in order to take it out of further consideration. aw[k] = test; // CHECK LINEAR INDEPENDENCE WITH PREVIOUS SPECIES // // Modified Gram-Schmidt Method, p. 202 Dalquist // QR factorization of a matrix without row pivoting. size_t jl = jr; for (size_t j = 0; j < m_numElemConstraints; ++j) { sm[j + jr*m_numElemConstraints] = m_formulaMatrix(k,j); } if (jl > 0) { // Compute the coefficients of JA column of the the upper // triangular R matrix, SS(J) = R_J_JR this is slightly // different than Dalquist) R_JA_JA = 1 for (size_t j = 0; j < jl; ++j) { ss[j] = 0.0; for (size_t i = 0; i < m_numElemConstraints; ++i) { ss[j] += sm[i + jr*m_numElemConstraints] * sm[i + j*m_numElemConstraints]; } ss[j] /= sa[j]; } // Now make the new column, (*,JR), orthogonal to the previous // columns for (size_t j = 0; j < jl; ++j) { for (size_t l = 0; l < m_numElemConstraints; ++l) { sm[l + jr*m_numElemConstraints] -= ss[j] * sm[l + j*m_numElemConstraints]; } } } // Find the new length of the new column in Q. It will be used in // the denominator in future row calcs. sa[jr] = 0.0; for (size_t ml = 0; ml < m_numElemConstraints; ++ml) { sa[jr] += pow(sm[ml + jr*m_numElemConstraints], 2); } // IF NORM OF NEW ROW .LT. 1E-3 REJECT if (sa[jr] > 1.0e-6) { break; } } // REARRANGE THE DATA if (jr != k) { if (DEBUG_MODE_ENABLED && m_debug_print_lvl >= 2) { plogf(" --- %-12.12s", m_speciesName[k]); if (m_speciesUnknownType[k] == VCS_SPECIES_TYPE_INTERFACIALVOLTAGE) { plogf("(Volts = %9.2g)", m_molNumSpecies_old[k]); } else { plogf("(%9.2g)", m_molNumSpecies_old[k]); } plogf(" replaces %-12.12s", m_speciesName[jr]); if (m_speciesUnknownType[jr] == VCS_SPECIES_TYPE_INTERFACIALVOLTAGE) { plogf("(Volts = %9.2g)", m_molNumSpecies_old[jr]); } else { plogf("(%9.2g)", m_molNumSpecies_old[jr]); } plogf(" as component %3d\n", jr); } vcs_switch_pos(false, jr, k); std::swap(aw[jr], aw[k]); } else if (DEBUG_MODE_ENABLED && m_debug_print_lvl >= 2) { plogf(" --- %-12.12s", m_speciesName[k]); if (m_speciesUnknownType[k] == VCS_SPECIES_TYPE_INTERFACIALVOLTAGE) { plogf("(Volts = %9.2g) remains ", m_molNumSpecies_old[k]); } else { plogf("(%9.2g) remains ", m_molNumSpecies_old[k]); } plogf(" as component %3d\n", jr); } // entry point from up above L_END_LOOP: ; // If we haven't found enough components, go back and find some more. jr++; } if (doJustComponents) { goto L_CLEANUP; } // EVALUATE THE STOICHIOMETRY // // Formulate the matrix problem for the stoichiometric // coefficients. CX + B = 0 // // C will be an nc x nc matrix made up of the formula vectors for the // components. n RHS's will be solved for. Thus, B is an nc x n matrix. // // BIG PROBLEM 1/21/99: // // This algorithm makes the assumption that the first nc rows of the formula // matrix aren't rank deficient. However, this might not be the case. For // example, assume that the first element in m_formulaMatrix[] is argon. // Assume that no species in the matrix problem actually includes argon. // Then, the first row in sm[], below will be identically zero. bleh. // // What needs to be done is to perform a rearrangement of the ELEMENTS -> // i.e. rearrange, m_formulaMatrix, sp, and m_elemAbundancesGoal, such that // the first nc elements form in combination with the nc components create // an invertible sm[]. not a small project, but very doable. // // An alternative would be to turn the matrix problem below into an ne x nc // problem, and do QR elimination instead of Gauss-Jordan elimination. Note // the rearrangement of elements need only be done once in the problem. It's // actually very similar to the top of this program with ne being the // species and nc being the elements!! for (size_t j = 0; j < ncTrial; ++j) { for (size_t i = 0; i < ncTrial; ++i) { sm[i + j*m_numElemConstraints] = m_formulaMatrix(j,i); } } for (size_t i = 0; i < m_numRxnTot; ++i) { k = m_indexRxnToSpecies[i]; for (size_t j = 0; j < ncTrial; ++j) { m_stoichCoeffRxnMatrix(j,i) = - m_formulaMatrix(k,j); } } // Solve the linear system to calculate the reaction matrix, // m_stoichCoeffRxnMatrix. ct_dgetrf(ncTrial, ncTrial, sm, m_numElemConstraints, &ipiv[0], info); if (info) { plogf("vcs_solve_TP ERROR: Error factorizing stoichiometric coefficient matrix\n"); return VCS_FAILED_CONVERGENCE; } ct_dgetrs(ctlapack::NoTranspose, ncTrial, m_numRxnTot, sm, m_numElemConstraints, &ipiv[0], m_stoichCoeffRxnMatrix.ptrColumn(0), m_numElemConstraints, info); // NOW, if we have interfacial voltage unknowns, what we did was just wrong // -> hopefully it didn't blow up. Redo the problem. Search for inactive E juse = npos; jlose = npos; for (size_t j = 0; j < m_numElemConstraints; j++) { if (!m_elementActive[j] && !strcmp(m_elementName[j].c_str(), "E")) { juse = j; } } for (size_t j = 0; j < m_numElemConstraints; j++) { if (m_elementActive[j] && !strncmp((m_elementName[j]).c_str(), "cn_", 3)) { jlose = j; } } for (k = 0; k < m_numSpeciesTot; k++) { if (m_speciesUnknownType[k] == VCS_SPECIES_TYPE_INTERFACIALVOLTAGE) { for (size_t j = 0; j < ncTrial; ++j) { for (size_t i = 0; i < ncTrial; ++i) { if (i == jlose) { sm[i + j*m_numElemConstraints] = m_formulaMatrix(j,juse); } else { sm[i + j*m_numElemConstraints] = m_formulaMatrix(j,i); } } } for (size_t i = 0; i < m_numRxnTot; ++i) { k = m_indexRxnToSpecies[i]; for (size_t j = 0; j < ncTrial; ++j) { if (j == jlose) { aw[j] = - m_formulaMatrix(k,juse); } else { aw[j] = - m_formulaMatrix(k,j); } } } ct_dgetrf(ncTrial, ncTrial, sm, m_numElemConstraints, &ipiv[0], info); if (info) { plogf("vcs_solve_TP ERROR: Error factorizing matrix\n"); return VCS_FAILED_CONVERGENCE; } ct_dgetrs(ctlapack::NoTranspose, ncTrial, 1, sm, m_numElemConstraints, &ipiv[0], aw, m_numElemConstraints, info); size_t i = k - ncTrial; for (size_t j = 0; j < ncTrial; j++) { m_stoichCoeffRxnMatrix(j,i) = aw[j]; } } } // Calculate the szTmp array for each formation reaction for (size_t i = 0; i < m_numRxnTot; i++) { double szTmp = 0.0; for (size_t j = 0; j < ncTrial; j++) { szTmp += fabs(m_stoichCoeffRxnMatrix(j,i)); } m_scSize[i] = szTmp; } if (DEBUG_MODE_ENABLED && m_debug_print_lvl >= 2) { plogf(" --- Components:"); for (size_t j = 0; j < ncTrial; j++) { plogf(" %3d", j); } plogf("\n --- Components Moles:"); for (size_t j = 0; j < ncTrial; j++) { if (m_speciesUnknownType[j] == VCS_SPECIES_TYPE_INTERFACIALVOLTAGE) { plogf(" % -10.3E", 0.0); } else { plogf(" % -10.3E", m_molNumSpecies_old[j]); } } plogf("\n --- NonComponent| Moles |"); for (size_t j = 0; j < ncTrial; j++) { plogf(" %10.10s", m_speciesName[j]); } plogf("\n"); for (size_t i = 0; i < m_numRxnTot; i++) { plogf(" --- %3d ", m_indexRxnToSpecies[i]); plogf("%-10.10s", m_speciesName[m_indexRxnToSpecies[i]]); if (m_speciesUnknownType[m_indexRxnToSpecies[i]] == VCS_SPECIES_TYPE_INTERFACIALVOLTAGE) { plogf("|% -10.3E|", 0.0); } else { plogf("|% -10.3E|", m_molNumSpecies_old[m_indexRxnToSpecies[i]]); } for (size_t j = 0; j < ncTrial; j++) { plogf(" %+7.3f", m_stoichCoeffRxnMatrix(j,i)); } plogf("\n"); } // Manual check on the satisfaction of the reaction matrix's ability to // conserve elements double sumMax = -1.0; size_t iMax = npos; size_t jMax = npos; for (size_t i = 0; i < m_numRxnTot; ++i) { k = m_indexRxnToSpecies[i]; double sum; for (size_t j = 0; j < ncTrial; ++j) { if (j == jlose) { sum = m_formulaMatrix(k,juse); for (size_t n = 0; n < ncTrial; n++) { double numElements = m_formulaMatrix(n,juse); double coeff = m_stoichCoeffRxnMatrix(n,i); sum += coeff * numElements; } } else { sum = m_formulaMatrix(k,j); for (size_t n = 0; n < ncTrial; n++) { double numElements = m_formulaMatrix(n,j); double coeff = m_stoichCoeffRxnMatrix(n,i); sum += coeff * numElements; } } if (fabs(sum) > sumMax) { sumMax = fabs(sum); iMax = i; jMax = j; if (j == jlose) { jMax = juse; } } if (fabs(sum) > 1.0E-6) { throw CanteraError("VCS_SOLVE::vcs_basopt", "we have a prob"); } } } plogf(" --- largest error in Stoich coeff = %g at rxn = %d ", sumMax, iMax); plogf("%-10.10s", m_speciesName[m_indexRxnToSpecies[iMax]]); plogf(" element = %d ", jMax); plogf("%-5.5s", m_elementName[jMax]); plogf("\n"); plogf(" "); for (size_t i=0; i<77; i++) { plogf("-"); } plogf("\n"); } // EVALUATE DELTA N VALUES // // Evaluate the change in gas and liquid total moles due to reaction // vectors, DNG and DNL. // Zero out the change of Phase Moles array m_deltaMolNumPhase.zero(); m_phaseParticipation.zero(); // Loop over each reaction, creating the change in Phase Moles array, // m_deltaMolNumPhase(iphase,irxn), and the phase participation array, // PhaseParticipation[irxn][iphase] for (size_t irxn = 0; irxn < m_numRxnTot; ++irxn) { scrxn_ptr = m_stoichCoeffRxnMatrix.ptrColumn(irxn); size_t kspec = m_indexRxnToSpecies[irxn]; size_t iph = m_phaseID[kspec]; m_deltaMolNumPhase(iph,irxn) = 1.0; m_phaseParticipation(iph,irxn)++; for (size_t j = 0; j < ncTrial; ++j) { iph = m_phaseID[j]; if (fabs(scrxn_ptr[j]) <= 1.0e-6) { scrxn_ptr[j] = 0.0; } else { m_deltaMolNumPhase(iph,irxn) += scrxn_ptr[j]; m_phaseParticipation(iph,irxn)++; } } } L_CLEANUP: ; double tsecond = tickTock.secondsWC(); m_VCount->Time_basopt += tsecond; m_VCount->Basis_Opts++; return VCS_SUCCESS; } size_t VCS_SOLVE::vcs_basisOptMax(const double* const molNum, const size_t j, const size_t n) { // The factors of 1.01 and 1.001 are placed in this routine for a purpose. // The purpose is to ensure that roundoff errors don't influence major // decisions. This means that the optimized and non-optimized versions of // the code remain close to each other. // // (we try to avoid the logic: a = b // if (a > b) { do this } // else { do something else } // because roundoff error makes a difference in the inequality evaluation) // // Mole numbers are frequently equal to each other in equilibrium problems // due to constraints. Swaps are only done if there are a 1% difference in // the mole numbers. Of course this logic isn't foolproof. size_t largest = j; double big = molNum[j] * m_spSize[j] * 1.01; if (m_spSize[j] <= 0.0) { throw CanteraError("VCS_SOLVE::vcs_basisOptMax", "spSize is nonpositive"); } for (size_t i = j + 1; i < n; ++i) { if (m_spSize[i] <= 0.0) { throw CanteraError("VCS_SOLVE::vcs_basisOptMax", "spSize is nonpositive"); } bool doSwap = false; if (m_SSPhase[j]) { doSwap = (molNum[i] * m_spSize[i]) > big; if (!m_SSPhase[i] && doSwap) { doSwap = molNum[i] > (molNum[largest] * 1.001); } } else { if (m_SSPhase[i]) { doSwap = (molNum[i] * m_spSize[i]) > big; if (!doSwap) { doSwap = molNum[i] > (molNum[largest] * 1.001); } } else { doSwap = (molNum[i] * m_spSize[i]) > big; } } if (doSwap) { largest = i; big = molNum[i] * m_spSize[i] * 1.01; } } return largest; } int VCS_SOLVE::vcs_species_type(const size_t kspec) const { // Treat special cases first if (m_speciesUnknownType[kspec] == VCS_SPECIES_TYPE_INTERFACIALVOLTAGE) { return VCS_SPECIES_INTERFACIALVOLTAGE; } size_t iph = m_phaseID[kspec]; int irxn = int(kspec) - int(m_numComponents); vcs_VolPhase* VPhase = m_VolPhaseList[iph]; int phaseExist = VPhase->exists(); // Treat zeroed out species first if (m_molNumSpecies_old[kspec] <= 0.0) { if (m_tPhaseMoles_old[iph] <= 0.0 && !m_SSPhase[kspec]) { return VCS_SPECIES_ZEROEDMS; } // see if the species has an element which is so low that species will // always be zero for (size_t j = 0; j < m_numElemConstraints; ++j) { int elType = m_elType[j]; if (elType == VCS_ELEM_TYPE_ABSPOS) { double atomComp = m_formulaMatrix(kspec,j); if (atomComp > 0.0) { double maxPermissible = m_elemAbundancesGoal[j] / atomComp; if (maxPermissible < VCS_DELETE_MINORSPECIES_CUTOFF) { if (DEBUG_MODE_ENABLED && m_debug_print_lvl >= 2) { plogf(" --- %s can not be nonzero because" " needed element %s is zero\n", m_speciesName[kspec], m_elementName[j]); } if (m_SSPhase[kspec]) { return VCS_SPECIES_ZEROEDSS; } else { return VCS_SPECIES_STOICHZERO; } } } } } // The Gibbs free energy for this species is such that it will pop back // into existence. An exception to this is if a needed regular element // is also zeroed out. Then, don't pop the phase or the species back // into existence. if (irxn >= 0) { for (size_t j = 0; j < m_numComponents; ++j) { double stoicC = m_stoichCoeffRxnMatrix(j,irxn); if (stoicC != 0.0) { double negChangeComp = - stoicC; if (negChangeComp > 0.0) { if (m_molNumSpecies_old[j] < 1.0E-60) { if (DEBUG_MODE_ENABLED && m_debug_print_lvl >= 2) { plogf(" --- %s is prevented from popping into existence because" " a needed component to be consumed, %s, has a zero mole number\n", m_speciesName[kspec], m_speciesName[j]); } if (m_SSPhase[kspec]) { return VCS_SPECIES_ZEROEDSS; } else { return VCS_SPECIES_STOICHZERO; } } } else if (negChangeComp < 0.0) { size_t jph = m_phaseID[j]; vcs_VolPhase* jVPhase = m_VolPhaseList[jph]; if (jVPhase->exists() <= 0) { if (DEBUG_MODE_ENABLED && m_debug_print_lvl >= 2) { plogf(" --- %s is prevented from popping into existence because" " a needed component %s is in a zeroed-phase that would be " "popped into existence at the same time\n", m_speciesName[kspec], m_speciesName[j]); } if (m_SSPhase[kspec]) { return VCS_SPECIES_ZEROEDSS; } else { return VCS_SPECIES_STOICHZERO; } } } } } } if (irxn >= 0 && m_deltaGRxn_old[irxn] >= 0.0) { // We are here when the species is or should remain zeroed out if (m_SSPhase[kspec]) { return VCS_SPECIES_ZEROEDSS; } else { if (phaseExist >= VCS_PHASE_EXIST_YES) { return VCS_SPECIES_ACTIVEBUTZERO; } else if (phaseExist == VCS_PHASE_EXIST_ZEROEDPHASE) { return VCS_SPECIES_ZEROEDPHASE; } else { return VCS_SPECIES_ZEROEDMS; } } } // If the current phase already exists, if (m_tPhaseMoles_old[iph] > 0.0) { if (m_SSPhase[kspec]) { return VCS_SPECIES_MAJOR; } else { return VCS_SPECIES_ACTIVEBUTZERO; } } // The Gibbs free energy for this species is such that it will pop back // into existence. Set it to a major species in anticipation. Note, if // we had an estimate for the emerging mole fraction of the species in // the phase, we could do better here. if (m_tPhaseMoles_old[iph] <= 0.0) { if (m_SSPhase[kspec]) { return VCS_SPECIES_MAJOR; } else { return VCS_SPECIES_ZEROEDMS; } } } // Treat species with non-zero mole numbers next // Always treat species in single species phases as majors if the phase // exists. if (m_SSPhase[kspec]) { return VCS_SPECIES_MAJOR; } // Check to see whether the current species is a major component of its // phase. If it is, it is a major component. This is consistent with the // above rule about single species phases. A major component i.e., a species // with a high mole fraction) in any phase is always treated as a major // species if (m_molNumSpecies_old[kspec] > (m_tPhaseMoles_old[iph] * 0.001)) { return VCS_SPECIES_MAJOR; } // Main check in the loop: Check to see if there is a component with a mole // number that is within a factor of 100 of the current species. If there is // and that component is not part of a single species phase and shares a // non-zero stoichiometric coefficient, then the current species is a major // species. if (irxn < 0) { return VCS_SPECIES_MAJOR; } else { double szAdj = m_scSize[irxn] * std::sqrt((double)m_numRxnTot); for (size_t k = 0; k < m_numComponents; ++k) { if (!m_SSPhase[k] && m_stoichCoeffRxnMatrix(k,irxn) != 0.0 && m_molNumSpecies_old[kspec] * szAdj >= m_molNumSpecies_old[k] * 0.01) { return VCS_SPECIES_MAJOR; } } } return VCS_SPECIES_MINOR; } void VCS_SOLVE::vcs_chemPotPhase(const int stateCalc, const size_t iph, const double* const molNum, double* const ac, double* const mu_i, const bool do_deleted) { vcs_VolPhase* Vphase = m_VolPhaseList[iph]; size_t nkk = Vphase->nSpecies(); double tMoles = TPhInertMoles[iph]; for (size_t k = 0; k < nkk; k++) { size_t kspec = Vphase->spGlobalIndexVCS(k); tMoles += molNum[kspec]; } double tlogMoles = 0.0; if (tMoles > 0.0) { tlogMoles = log(tMoles); } Vphase->setMolesFromVCS(stateCalc, molNum); Vphase->sendToVCS_ActCoeff(stateCalc, ac); double phi = Vphase->electricPotential(); double Faraday_phi = m_Faraday_dim * phi; for (size_t k = 0; k < nkk; k++) { size_t kspec = Vphase->spGlobalIndexVCS(k); if (kspec >= m_numComponents) { if (!do_deleted && (m_speciesStatus[kspec] == VCS_SPECIES_DELETED)) { continue; } } if (m_speciesUnknownType[kspec] == VCS_SPECIES_TYPE_INTERFACIALVOLTAGE) { AssertThrowMsg(molNum[kspec] == phi, "VCS_SOLVE::vcs_chemPotPhase", "We have an inconsistency!"); AssertThrowMsg(m_chargeSpecies[kspec] == -1.0, "VCS_SOLVE::vcs_chemPotPhase", "We have an unexpected situation!"); mu_i[kspec] = m_SSfeSpecies[kspec] + m_chargeSpecies[kspec] * Faraday_phi; } else { if (m_SSPhase[kspec]) { mu_i[kspec] = m_SSfeSpecies[kspec] + m_chargeSpecies[kspec] * Faraday_phi; } else if (molNum[kspec] <= VCS_DELETE_MINORSPECIES_CUTOFF) { mu_i[kspec] = m_SSfeSpecies[kspec] + log(ac[kspec] * VCS_DELETE_MINORSPECIES_CUTOFF) - tlogMoles - m_lnMnaughtSpecies[kspec] + m_chargeSpecies[kspec] * Faraday_phi; } else { mu_i[kspec] = m_SSfeSpecies[kspec] + log(ac[kspec] * molNum[kspec]) - tlogMoles - m_lnMnaughtSpecies[kspec] + m_chargeSpecies[kspec] * Faraday_phi; } } } } void VCS_SOLVE::vcs_dfe(const int stateCalc, const int ll, const size_t lbot, const size_t ltop) { double* tPhMoles_ptr=0; double* actCoeff_ptr=0; double* feSpecies=0; double* molNum=0; if (stateCalc == VCS_STATECALC_OLD) { feSpecies = &m_feSpecies_old[0]; tPhMoles_ptr = &m_tPhaseMoles_old[0]; actCoeff_ptr = &m_actCoeffSpecies_old[0]; molNum = &m_molNumSpecies_old[0]; } else if (stateCalc == VCS_STATECALC_NEW) { feSpecies = &m_feSpecies_new[0]; tPhMoles_ptr = &m_tPhaseMoles_new[0]; actCoeff_ptr = &m_actCoeffSpecies_new[0]; molNum = &m_molNumSpecies_new[0]; } else if (DEBUG_MODE_ENABLED) { throw CanteraError("VCS_SOLVE::vcs_dfe", "Subroutine vcs_dfe called with bad stateCalc value: {}", stateCalc); } AssertThrowMsg(m_unitsState != VCS_DIMENSIONAL_G, "VCS_SOLVE::vcs_dfe", "called with wrong units state"); if (DEBUG_MODE_ENABLED && m_debug_print_lvl >= 2) { if (ll == 0) { if (lbot != 0) { plogf(" --- Subroutine vcs_dfe called for one species: "); plogf("%-12.12s", m_speciesName[lbot]); } else { plogf(" --- Subroutine vcs_dfe called for all species"); } } else if (ll > 0) { plogf(" --- Subroutine vcs_dfe called for components and minors"); } else { plogf(" --- Subroutine vcs_dfe called for components and majors"); } if (stateCalc == VCS_STATECALC_NEW) { plogf(" using tentative solution"); } plogendl(); } double* tlogMoles = &m_TmpPhase[0]; // Might as well recalculate the phase mole vector and compare to the stored // one. They should be correct. double* tPhInertMoles = &TPhInertMoles[0]; for (size_t iph = 0; iph < m_numPhases; iph++) { tlogMoles[iph] = tPhInertMoles[iph]; } for (size_t kspec = 0; kspec < m_numSpeciesTot; kspec++) { if (m_speciesUnknownType[kspec] != VCS_SPECIES_TYPE_INTERFACIALVOLTAGE) { size_t iph = m_phaseID[kspec]; tlogMoles[iph] += molNum[kspec]; } } for (size_t iph = 0; iph < m_numPhases; iph++) { AssertThrowMsg(vcs_doubleEqual(tlogMoles[iph], tPhMoles_ptr[iph]), "VCS_SOLVE::vcs_dfe", "phase Moles may be off, iph = {}, {} {}", iph, tlogMoles[iph], tPhMoles_ptr[iph]); } m_TmpPhase.assign(m_TmpPhase.size(), 0.0); for (size_t iph = 0; iph < m_numPhases; iph++) { if (tPhMoles_ptr[iph] > 0.0) { tlogMoles[iph] = log(tPhMoles_ptr[iph]); } } size_t l1, l2; if (ll != 0) { l1 = lbot; l2 = m_numComponents; } else { l1 = lbot; l2 = ltop; } // Calculate activity coefficients for all phases that are not current. Here // we also trigger an update check for each VolPhase to see if its mole // numbers are current with vcs for (size_t iphase = 0; iphase < m_numPhases; iphase++) { vcs_VolPhase* Vphase = m_VolPhaseList[iphase]; Vphase->updateFromVCS_MoleNumbers(stateCalc); if (!Vphase->m_singleSpecies) { Vphase->sendToVCS_ActCoeff(stateCalc, &actCoeff_ptr[0]); } m_phasePhi[iphase] = Vphase->electricPotential(); } // ALL SPECIES, OR COMPONENTS // // Do all of the species when LL = 0. Then we are done for the routine When // LL ne 0., just do the initial components. We will then finish up below // with loops over either the major noncomponent species or the minor // noncomponent species. for (size_t kspec = l1; kspec < l2; ++kspec) { size_t iphase = m_phaseID[kspec]; if (m_speciesUnknownType[kspec] == VCS_SPECIES_TYPE_INTERFACIALVOLTAGE) { AssertThrowMsg(molNum[kspec] == m_phasePhi[iphase], "VCS_SOLVE::vcs_dfe", "We have an inconsistency!"); AssertThrowMsg(m_chargeSpecies[kspec] == -1.0, "VCS_SOLVE::vcs_dfe", "We have an unexpected situation!"); feSpecies[kspec] = m_SSfeSpecies[kspec] + m_chargeSpecies[kspec] * m_Faraday_dim * m_phasePhi[iphase]; } else { if (m_SSPhase[kspec]) { feSpecies[kspec] = m_SSfeSpecies[kspec] + m_chargeSpecies[kspec] * m_Faraday_dim * m_phasePhi[iphase]; } else if ((m_speciesStatus[kspec] == VCS_SPECIES_ZEROEDMS) || (m_speciesStatus[kspec] == VCS_SPECIES_ZEROEDPHASE)) { feSpecies[kspec] = m_SSfeSpecies[kspec] - m_lnMnaughtSpecies[kspec] + m_chargeSpecies[kspec] * m_Faraday_dim * m_phasePhi[iphase]; } else { if (molNum[kspec] <= VCS_DELETE_MINORSPECIES_CUTOFF) { size_t iph = m_phaseID[kspec]; if (tPhMoles_ptr[iph] > 0.0) { feSpecies[kspec] = m_SSfeSpecies[kspec] + log(actCoeff_ptr[kspec] * VCS_DELETE_MINORSPECIES_CUTOFF) - tlogMoles[m_phaseID[kspec]] - m_lnMnaughtSpecies[kspec] + m_chargeSpecies[kspec] * m_Faraday_dim * m_phasePhi[iphase]; } else { feSpecies[kspec] = m_SSfeSpecies[kspec] - m_lnMnaughtSpecies[kspec] + m_chargeSpecies[kspec] * m_Faraday_dim * m_phasePhi[iphase]; } } else { feSpecies[kspec] = m_SSfeSpecies[kspec] + log(actCoeff_ptr[kspec] * molNum[kspec]) - tlogMoles[m_phaseID[kspec]] - m_lnMnaughtSpecies[kspec] + m_chargeSpecies[kspec] * m_Faraday_dim * m_phasePhi[iphase]; } } } } if (ll < 0) { // MAJORS ONLY for (size_t irxn = 0; irxn < m_numRxnRdc; ++irxn) { size_t kspec = m_indexRxnToSpecies[irxn]; if (m_speciesStatus[kspec] != VCS_SPECIES_MINOR) { size_t iphase = m_phaseID[kspec]; if (m_speciesUnknownType[kspec] == VCS_SPECIES_TYPE_INTERFACIALVOLTAGE) { AssertThrowMsg(molNum[kspec] == m_phasePhi[iphase], "VCS_SOLVE::vcs_dfe", "We have an inconsistency!"); AssertThrowMsg(m_chargeSpecies[kspec] == -1.0, "VCS_SOLVE::vcs_dfe", "We have an unexpected situation!"); feSpecies[kspec] = m_SSfeSpecies[kspec] + m_chargeSpecies[kspec] * m_Faraday_dim * m_phasePhi[iphase]; } else { if (m_SSPhase[kspec]) { feSpecies[kspec] = m_SSfeSpecies[kspec] + m_chargeSpecies[kspec] * m_Faraday_dim * m_phasePhi[iphase]; } else if ((m_speciesStatus[kspec] == VCS_SPECIES_ZEROEDMS) || (m_speciesStatus[kspec] == VCS_SPECIES_ZEROEDPHASE)) { feSpecies[kspec] = m_SSfeSpecies[kspec] - m_lnMnaughtSpecies[kspec] + m_chargeSpecies[kspec] * m_Faraday_dim * m_phasePhi[iphase]; } else { if (molNum[kspec] <= VCS_DELETE_MINORSPECIES_CUTOFF) { size_t iph = m_phaseID[kspec]; if (tPhMoles_ptr[iph] > 0.0) { feSpecies[kspec] = m_SSfeSpecies[kspec] + log(actCoeff_ptr[kspec] * VCS_DELETE_MINORSPECIES_CUTOFF) - tlogMoles[m_phaseID[kspec]] - m_lnMnaughtSpecies[kspec] + m_chargeSpecies[kspec] * m_Faraday_dim * m_phasePhi[iphase]; } else { feSpecies[kspec] = m_SSfeSpecies[kspec] - m_lnMnaughtSpecies[kspec] + m_chargeSpecies[kspec] * m_Faraday_dim * m_phasePhi[iphase]; } } else { feSpecies[kspec] = m_SSfeSpecies[kspec] + log(actCoeff_ptr[kspec] * molNum[kspec]) - tlogMoles[m_phaseID[kspec]] - m_lnMnaughtSpecies[kspec] + m_chargeSpecies[kspec] * m_Faraday_dim * m_phasePhi[iphase]; } } } } } } else if (ll > 0) { // MINORS ONLY for (size_t irxn = 0; irxn < m_numRxnRdc; ++irxn) { size_t kspec = m_indexRxnToSpecies[irxn]; if (m_speciesStatus[kspec] == VCS_SPECIES_MINOR) { size_t iphase = m_phaseID[kspec]; if (m_speciesUnknownType[kspec] == VCS_SPECIES_TYPE_INTERFACIALVOLTAGE) { AssertThrowMsg(molNum[kspec] == m_phasePhi[iphase], "VCS_SOLVE::vcs_dfe", "We have an inconsistency!"); AssertThrowMsg(m_chargeSpecies[kspec] == -1.0, "VCS_SOLVE::vcs_dfe", "We have an unexpected situation!"); feSpecies[kspec] = m_SSfeSpecies[kspec] + m_chargeSpecies[kspec] * m_Faraday_dim * m_phasePhi[iphase]; } else { if (m_SSPhase[kspec]) { feSpecies[kspec] = m_SSfeSpecies[kspec] + m_chargeSpecies[kspec] * m_Faraday_dim * m_phasePhi[iphase]; } else if ((m_speciesStatus[kspec] == VCS_SPECIES_ZEROEDMS) || (m_speciesStatus[kspec] == VCS_SPECIES_ZEROEDPHASE)) { feSpecies[kspec] = m_SSfeSpecies[kspec] - m_lnMnaughtSpecies[kspec] + m_chargeSpecies[kspec] * m_Faraday_dim * m_phasePhi[iphase]; } else { if (molNum[kspec] <= VCS_DELETE_MINORSPECIES_CUTOFF) { size_t iph = m_phaseID[kspec]; if (tPhMoles_ptr[iph] > 0.0) { feSpecies[kspec] = m_SSfeSpecies[kspec] + log(actCoeff_ptr[kspec] * VCS_DELETE_MINORSPECIES_CUTOFF) - tlogMoles[m_phaseID[kspec]] - m_lnMnaughtSpecies[kspec] + m_chargeSpecies[kspec] * m_Faraday_dim * m_phasePhi[iphase]; } else { feSpecies[kspec] = m_SSfeSpecies[kspec] - m_lnMnaughtSpecies[kspec] + m_chargeSpecies[kspec] * m_Faraday_dim * m_phasePhi[iphase]; } } else { feSpecies[kspec] = m_SSfeSpecies[kspec] + log(actCoeff_ptr[kspec] * molNum[kspec]) - tlogMoles[m_phaseID[kspec]] - m_lnMnaughtSpecies[kspec] + m_chargeSpecies[kspec] * m_Faraday_dim * m_phasePhi[iphase]; } } } } } } } void VCS_SOLVE::vcs_printSpeciesChemPot(const int stateCalc) const { double mfValue = 1.0; bool zeroedPhase = false; const double* molNum = &m_molNumSpecies_old[0]; const double* actCoeff_ptr = &m_actCoeffSpecies_old[0]; if (stateCalc == VCS_STATECALC_NEW) { actCoeff_ptr = &m_actCoeffSpecies_new[0]; molNum = &m_molNumSpecies_new[0]; } double* tMoles = &m_TmpPhase[0]; const double* tPhInertMoles = &TPhInertMoles[0]; for (size_t iph = 0; iph < m_numPhases; iph++) { tMoles[iph] = tPhInertMoles[iph]; } for (size_t kspec = 0; kspec < m_numSpeciesTot; kspec++) { if (m_speciesUnknownType[kspec] != VCS_SPECIES_TYPE_INTERFACIALVOLTAGE) { size_t iph = m_phaseID[kspec]; tMoles[iph] += molNum[kspec]; } } double RT = m_temperature * GasConstant; writelog(" --- CHEMICAL POT TABLE (J/kmol) Name PhID MolFR ChemoSS " " logMF Gamma Elect extra ElectrChem\n"); writelog(" "); writeline('-', 132); for (size_t kspec = 0; kspec < m_numSpeciesTot; ++kspec) { mfValue = 1.0; size_t iphase = m_phaseID[kspec]; const vcs_VolPhase* Vphase = m_VolPhaseList[iphase]; if ((m_speciesStatus[kspec] == VCS_SPECIES_ZEROEDMS) || (m_speciesStatus[kspec] == VCS_SPECIES_ZEROEDPHASE) || (m_speciesStatus[kspec] == VCS_SPECIES_ZEROEDSS)) { zeroedPhase = true; } else { zeroedPhase = false; } if (tMoles[iphase] > 0.0) { if (molNum[kspec] <= VCS_DELETE_MINORSPECIES_CUTOFF) { mfValue = VCS_DELETE_MINORSPECIES_CUTOFF / tMoles[iphase]; } else { mfValue = molNum[kspec]/tMoles[iphase]; } } else { size_t klocal = m_speciesLocalPhaseIndex[kspec]; mfValue = Vphase->moleFraction(klocal); } double volts = Vphase->electricPotential(); double elect = m_chargeSpecies[kspec] * m_Faraday_dim * volts; double comb = - m_lnMnaughtSpecies[kspec]; double total = (m_SSfeSpecies[kspec] + log(mfValue) + elect + log(actCoeff_ptr[kspec]) + comb); if (zeroedPhase) { writelog(" --- ** zp *** "); } else { writelog(" --- "); } writelogf("%-24.24s", m_speciesName[kspec]); writelogf(" %3d", iphase); writelogf(" % -12.4e", mfValue); writelogf(" % -12.4e", m_SSfeSpecies[kspec] * RT); writelogf(" % -12.4e", log(mfValue) * RT); writelogf(" % -12.4e", log(actCoeff_ptr[kspec]) * RT); writelogf(" % -12.4e", elect * RT); writelogf(" % -12.4e", comb * RT); writelogf(" % -12.4e\n", total *RT); } writelog(" "); writeline('-', 132); } #ifdef DEBUG_MODE void VCS_SOLVE::prneav() const { vector_fp eav(m_numElemConstraints, 0.0); for (size_t j = 0; j < m_numElemConstraints; ++j) { for (size_t i = 0; i < m_numSpeciesTot; ++i) { if (m_speciesUnknownType[i] == VCS_SPECIES_TYPE_INTERFACIALVOLTAGE) { eav[j] += m_formulaMatrix(i,j) * m_molNumSpecies_old[i]; } } } bool kerr = false; plogf("--------------------------------------------------"); plogf("ELEMENT ABUNDANCE VECTOR:\n"); plogf(" Element Now Orignal Deviation Type\n"); for (size_t j = 0; j < m_numElemConstraints; ++j) { plogf(" "); plogf("%-2.2s", m_elementName[j]); plogf(" = %15.6E %15.6E %15.6E %3d\n", eav[j], m_elemAbundancesGoal[j], eav[j] - m_elemAbundancesGoal[j], m_elType[j]); if (m_elemAbundancesGoal[j] != 0.) { if (fabs(eav[j] - m_elemAbundancesGoal[j]) > m_elemAbundancesGoal[j] * 5.0e-9) { kerr = true; } } else { if (fabs(eav[j]) > 1.0e-10) { kerr = true; } } } if (kerr) { plogf("Element abundance check failure\n"); } plogf("--------------------------------------------------"); plogendl(); } #endif double VCS_SOLVE::l2normdg(double dgLocal[]) const { if (m_numRxnRdc <= 0) { return 0.0; } double tmp = 0; for (size_t irxn = 0; irxn < m_numRxnRdc; ++irxn) { size_t kspec = irxn + m_numComponents; if (m_speciesStatus[kspec] == VCS_SPECIES_MAJOR || m_speciesStatus[kspec] == VCS_SPECIES_MINOR || dgLocal[irxn] < 0.0) { if (m_speciesStatus[kspec] != VCS_SPECIES_ZEROEDMS) { tmp += dgLocal[irxn] * dgLocal[irxn]; } } } return std::sqrt(tmp / m_numRxnRdc); } double VCS_SOLVE::vcs_tmoles() { for (size_t i = 0; i < m_numPhases; i++) { m_tPhaseMoles_old[i] = TPhInertMoles[i]; } for (size_t i = 0; i < m_numSpeciesTot; i++) { if (m_speciesUnknownType[i] == VCS_SPECIES_TYPE_MOLNUM) { m_tPhaseMoles_old[m_phaseID[i]] += m_molNumSpecies_old[i]; } } double sum = 0.0; for (size_t i = 0; i < m_numPhases; i++) { sum += m_tPhaseMoles_old[i]; vcs_VolPhase* Vphase = m_VolPhaseList[i]; if (m_tPhaseMoles_old[i] == 0.0) { Vphase->setTotalMoles(0.0); } else { Vphase->setTotalMoles(m_tPhaseMoles_old[i]); } } m_totalMolNum = sum; return m_totalMolNum; } #ifdef DEBUG_MODE void VCS_SOLVE::check_tmoles() const { double sum = 0.0; for (size_t i = 0; i < m_numPhases; i++) { double m_tPhaseMoles_old_a = TPhInertMoles[i]; for (size_t k = 0; k < m_numSpeciesTot; k++) { if (m_speciesUnknownType[k] == VCS_SPECIES_TYPE_MOLNUM && m_phaseID[k] == i) { m_tPhaseMoles_old_a += m_molNumSpecies_old[k]; } } sum += m_tPhaseMoles_old_a; double denom = m_tPhaseMoles_old[i]+ m_tPhaseMoles_old_a + 1.0E-19; if (!vcs_doubleEqual(m_tPhaseMoles_old[i]/denom, m_tPhaseMoles_old_a/denom)) { plogf("check_tmoles: we have found a problem with phase %d: %20.15g, %20.15g\n", i, m_tPhaseMoles_old[i], m_tPhaseMoles_old_a); } } } #endif void VCS_SOLVE::vcs_updateVP(const int vcsState) { for (size_t i = 0; i < m_numPhases; i++) { vcs_VolPhase* Vphase = m_VolPhaseList[i]; if (vcsState == VCS_STATECALC_OLD) { Vphase->setMolesFromVCSCheck(VCS_STATECALC_OLD, &m_molNumSpecies_old[0], &m_tPhaseMoles_old[0]); } else if (vcsState == VCS_STATECALC_NEW) { Vphase->setMolesFromVCSCheck(VCS_STATECALC_NEW, &m_molNumSpecies_new[0], &m_tPhaseMoles_new[0]); } else if (DEBUG_MODE_ENABLED) { throw CanteraError("VCS_SOLVE::vcs_updateVP", "wrong stateCalc value: {}", vcsState); } } } bool VCS_SOLVE::vcs_evaluate_speciesType() { m_numRxnMinorZeroed = 0; if (DEBUG_MODE_ENABLED && m_debug_print_lvl >= 2) { plogf(" --- Species Status decision is reevaluated: All species are minor except for:\n"); } else if (DEBUG_MODE_ENABLED && m_debug_print_lvl >= 5) { plogf(" --- Species Status decision is reevaluated"); plogendl(); } for (size_t kspec = 0; kspec < m_numSpeciesTot; ++kspec) { m_speciesStatus[kspec] = vcs_species_type(kspec); if (DEBUG_MODE_ENABLED && m_debug_print_lvl >= 5) { plogf(" --- %-16s: ", m_speciesName[kspec]); if (kspec < m_numComponents) { plogf("(COMP) "); } else { plogf(" "); } plogf(" %10.3g ", m_molNumSpecies_old[kspec]); const char* sString = vcs_speciesType_string(m_speciesStatus[kspec], 100); plogf("%s\n", sString); } else if (DEBUG_MODE_ENABLED && m_debug_print_lvl >= 2) { if (m_speciesStatus[kspec] != VCS_SPECIES_MINOR) { switch (m_speciesStatus[kspec]) { case VCS_SPECIES_COMPONENT: break; case VCS_SPECIES_MAJOR: plogf(" --- Major Species : %-s\n", m_speciesName[kspec]); break; case VCS_SPECIES_ZEROEDPHASE: plogf(" --- Purposely Zeroed-Phase Species (not in problem): %-s\n", m_speciesName[kspec]); break; case VCS_SPECIES_ZEROEDMS: plogf(" --- Zeroed-MS Phase Species: %-s\n", m_speciesName[kspec]); break; case VCS_SPECIES_ZEROEDSS: plogf(" --- Zeroed-SS Phase Species: %-s\n", m_speciesName[kspec]); break; case VCS_SPECIES_DELETED: plogf(" --- Deleted-Small Species : %-s\n", m_speciesName[kspec]); break; case VCS_SPECIES_ACTIVEBUTZERO: plogf(" --- Zeroed Species in an active MS phase (tmp): %-s\n", m_speciesName[kspec]); break; case VCS_SPECIES_STOICHZERO: plogf(" --- Zeroed Species in an active MS phase (Stoich Constraint): %-s\n", m_speciesName[kspec]); break; case VCS_SPECIES_INTERFACIALVOLTAGE: plogf(" --- InterfaceVoltage Species: %-s\n", m_speciesName[kspec]); break; default: throw CanteraError("VCS_SOLVE::vcs_evaluate_speciesType", "Unknown type: {}", m_speciesStatus[kspec]); } } } if (kspec >= m_numComponents && m_speciesStatus[kspec] != VCS_SPECIES_MAJOR) { ++m_numRxnMinorZeroed; } } if (DEBUG_MODE_ENABLED && m_debug_print_lvl >= 2) { plogf(" ---"); plogendl(); } return (m_numRxnMinorZeroed >= m_numRxnRdc); } void VCS_SOLVE::vcs_deltag(const int l, const bool doDeleted, const int vcsState, const bool alterZeroedPhases) { int icase = 0; size_t irxnl = m_numRxnRdc; if (doDeleted) { irxnl = m_numRxnTot; } double* deltaGRxn; double* feSpecies; double* molNumSpecies; double* actCoeffSpecies; if (vcsState == VCS_STATECALC_NEW) { deltaGRxn = &m_deltaGRxn_new[0]; feSpecies = &m_feSpecies_new[0]; molNumSpecies = &m_molNumSpecies_new[0]; actCoeffSpecies = &m_actCoeffSpecies_new[0]; } else if (vcsState == VCS_STATECALC_OLD) { deltaGRxn = &m_deltaGRxn_old[0]; feSpecies = &m_feSpecies_old[0]; molNumSpecies = &m_molNumSpecies_old[0]; actCoeffSpecies = &m_actCoeffSpecies_old[0]; } else { throw CanteraError("VCS_SOLVE::vcs_deltag", "bad vcsState"); } if (DEBUG_MODE_ENABLED && m_debug_print_lvl >= 2) { plogf(" --- Subroutine vcs_deltag called for "); if (l < 0) { plogf("major noncomponents\n"); } else if (l == 0) { plogf("all noncomponents\n"); } else { plogf("minor noncomponents\n"); } } if (l < 0) { // MAJORS and ZEROED SPECIES ONLY for (size_t irxn = 0; irxn < m_numRxnRdc; ++irxn) { size_t kspec = irxn + m_numComponents; if (m_speciesStatus[kspec] != VCS_SPECIES_MINOR) { icase = 0; deltaGRxn[irxn] = feSpecies[m_indexRxnToSpecies[irxn]]; double* dtmp_ptr = m_stoichCoeffRxnMatrix.ptrColumn(irxn); for (kspec = 0; kspec < m_numComponents; ++kspec) { deltaGRxn[irxn] += dtmp_ptr[kspec] * feSpecies[kspec]; if (molNumSpecies[kspec] < VCS_DELETE_MINORSPECIES_CUTOFF && dtmp_ptr[kspec] < 0.0) { icase = 1; } } if (icase) { deltaGRxn[irxn] = std::max(0.0, deltaGRxn[irxn]); } } } } else if (l == 0) { // ALL REACTIONS for (size_t irxn = 0; irxn < irxnl; ++irxn) { icase = 0; deltaGRxn[irxn] = feSpecies[m_indexRxnToSpecies[irxn]]; double* dtmp_ptr = m_stoichCoeffRxnMatrix.ptrColumn(irxn); for (size_t kspec = 0; kspec < m_numComponents; ++kspec) { deltaGRxn[irxn] += dtmp_ptr[kspec] * feSpecies[kspec]; if (molNumSpecies[kspec] < VCS_DELETE_MINORSPECIES_CUTOFF && dtmp_ptr[kspec] < 0.0) { icase = 1; } } if (icase) { deltaGRxn[irxn] = std::max(0.0, deltaGRxn[irxn]); } } } else { // MINORS AND ZEROED SPECIES for (size_t irxn = 0; irxn < m_numRxnRdc; ++irxn) { size_t kspec = irxn + m_numComponents; if (m_speciesStatus[kspec] <= VCS_SPECIES_MINOR) { icase = 0; deltaGRxn[irxn] = feSpecies[m_indexRxnToSpecies[irxn]]; double* dtmp_ptr = m_stoichCoeffRxnMatrix.ptrColumn(irxn); for (kspec = 0; kspec < m_numComponents; ++kspec) { deltaGRxn[irxn] += dtmp_ptr[kspec] * feSpecies[kspec]; if (m_molNumSpecies_old[kspec] < VCS_DELETE_MINORSPECIES_CUTOFF && dtmp_ptr[kspec] < 0.0) { icase = 1; } } if (icase) { deltaGRxn[irxn] = std::max(0.0, deltaGRxn[irxn]); } } } } /* **** MULTISPECIES PHASES WITH ZERO MOLES ******** */ // // Massage the free energies for species with zero mole fractions in // multispecies phases. This section implements the Equation 3.8-5 in Smith // and Missen, p.59. A multispecies phase will exist iff // // 1 < sum_i(exp(-dg_i)/AC_i) // // If DG is negative then that species wants to be reintroduced into the // calculation. For small dg_i, the expression below becomes: // // 1 - sum_i(exp(-dg_i)/AC_i) ~ sum_i((dg_i-1)/AC_i) + 1 // // So, what we are doing here is equalizing all DG's in a multispecies phase // whose total mole number has already been zeroed out. It must have to do // with the case where a complete multispecies phase is currently zeroed // out. In that case, when one species in that phase has a negative DG, then // the phase should kick in. This code section will cause that to happen, // because a negative DG will dominate the calculation of SDEL. Then, DG(I) // for all species in that phase will be forced to be equal and negative. // Thus, all species in that phase will come into being at the same time. // // HKM -> The ratio of mole fractions at the reinstatement time should be // equal to the normalized weighting of exp(-dg_i) / AC_i. This should be // implemented. // // HKM -> There is circular logic here. ActCoeff depends on the mole // fractions of a phase that does not exist. In actuality the proto-mole // fractions should be selected from the solution of a nonlinear problem // with NsPhase unknowns // // X_i = exp(-dg[irxn]) / ActCoeff_i / denom // // where // // denom = sum_i[ exp(-dg[irxn]) / ActCoeff_i ] // // This can probably be solved by successive iteration. This should be // implemented. if (alterZeroedPhases && false) { for (size_t iph = 0; iph < m_numPhases; iph++) { bool lneed = false; vcs_VolPhase* Vphase = m_VolPhaseList[iph]; if (! Vphase->m_singleSpecies) { double sum = 0.0; for (size_t k = 0; k < Vphase->nSpecies(); k++) { size_t kspec = Vphase->spGlobalIndexVCS(k); if (m_speciesUnknownType[kspec] != VCS_SPECIES_TYPE_INTERFACIALVOLTAGE) { sum += molNumSpecies[kspec]; } if (sum > 0.0) { break; } } if (sum == 0.0) { lneed = true; } } if (lneed) { double poly = 0.0; for (size_t k = 0; k < Vphase->nSpecies(); k++) { size_t kspec = Vphase->spGlobalIndexVCS(k); // We may need to look at deltaGRxn for components! if (kspec >= m_numComponents) { size_t irxn = kspec - m_numComponents; deltaGRxn[irxn] = clip(deltaGRxn[irxn], -50.0, 50.0); poly += exp(-deltaGRxn[irxn])/actCoeffSpecies[kspec]; } } // Calculate deltaGRxn[] for each species in a zeroed // multispecies phase. All of the m_deltaGRxn_new[]'s will be // equal. If deltaGRxn[] is negative, then the phase will come // back into existence. for (size_t k = 0; k < Vphase->nSpecies(); k++) { size_t kspec = Vphase->spGlobalIndexVCS(k); if (kspec >= m_numComponents) { size_t irxn = kspec - m_numComponents; deltaGRxn[irxn] = 1.0 - poly; } } } } } } void VCS_SOLVE::vcs_printDeltaG(const int stateCalc) { double* deltaGRxn = &m_deltaGRxn_old[0]; double* feSpecies = &m_feSpecies_old[0]; double* molNumSpecies = &m_molNumSpecies_old[0]; const double* tPhMoles_ptr = &m_tPhaseMoles_old[0]; const double* actCoeff_ptr = &m_actCoeffSpecies_old[0]; if (stateCalc == VCS_STATECALC_NEW) { deltaGRxn = &m_deltaGRxn_new[0]; feSpecies = &m_feSpecies_new[0]; molNumSpecies = &m_molNumSpecies_new[0]; actCoeff_ptr = &m_actCoeffSpecies_new[0]; tPhMoles_ptr = &m_tPhaseMoles_new[0]; } double RT = m_temperature * GasConstant; bool zeroedPhase = false; if (m_debug_print_lvl >= 2) { plogf(" --- DELTA_G TABLE Components:"); for (size_t j = 0; j < m_numComponents; j++) { plogf(" %3d ", j); } plogf("\n --- Components Moles:"); for (size_t j = 0; j < m_numComponents; j++) { plogf("%10.3g", m_molNumSpecies_old[j]); } plogf("\n --- NonComponent| Moles | "); for (size_t j = 0; j < m_numComponents; j++) { plogf("%-10.10s", m_speciesName[j]); } plogf("\n"); for (size_t i = 0; i < m_numRxnTot; i++) { plogf(" --- %3d ", m_indexRxnToSpecies[i]); plogf("%-10.10s", m_speciesName[m_indexRxnToSpecies[i]]); if (m_speciesUnknownType[m_indexRxnToSpecies[i]] == VCS_SPECIES_TYPE_INTERFACIALVOLTAGE) { plogf("| NA |"); } else { plogf("|%10.3g|", m_molNumSpecies_old[m_indexRxnToSpecies[i]]); } for (size_t j = 0; j < m_numComponents; j++) { plogf(" %6.2f", m_stoichCoeffRxnMatrix(j,i)); } plogf("\n"); } plogf(" "); for (int i=0; i<77; i++) { plogf("-"); } plogf("\n"); } writelog(" --- DeltaG Table (J/kmol) Name PhID MoleNum MolFR " " ElectrChemStar ElectrChem DeltaGStar DeltaG(Pred) Stability\n"); writelog(" "); writeline('-', 132); for (size_t kspec = 0; kspec < m_numSpeciesTot; kspec++) { size_t irxn = npos; if (kspec >= m_numComponents) { irxn = kspec - m_numComponents; } double mfValue = 1.0; size_t iphase = m_phaseID[kspec]; const vcs_VolPhase* Vphase = m_VolPhaseList[iphase]; if ((m_speciesStatus[kspec] == VCS_SPECIES_ZEROEDMS) || (m_speciesStatus[kspec] == VCS_SPECIES_ZEROEDPHASE) || (m_speciesStatus[kspec] == VCS_SPECIES_ZEROEDSS)) { zeroedPhase = true; } else { zeroedPhase = false; } if (tPhMoles_ptr[iphase] > 0.0) { if (molNumSpecies[kspec] <= VCS_DELETE_MINORSPECIES_CUTOFF) { mfValue = VCS_DELETE_MINORSPECIES_CUTOFF / tPhMoles_ptr[iphase]; } else { mfValue = molNumSpecies[kspec] / tPhMoles_ptr[iphase]; } } else { size_t klocal = m_speciesLocalPhaseIndex[kspec]; mfValue = Vphase->moleFraction(klocal); } if (zeroedPhase) { writelog(" --- ** zp *** "); } else { writelog(" --- "); } double feFull = feSpecies[kspec]; if ((m_speciesStatus[kspec] == VCS_SPECIES_ZEROEDMS) || (m_speciesStatus[kspec] == VCS_SPECIES_ZEROEDPHASE)) { feFull += log(actCoeff_ptr[kspec]) + log(mfValue); } writelogf("%-24.24s", m_speciesName[kspec]); writelogf(" %3d", iphase); if (m_speciesUnknownType[kspec] == VCS_SPECIES_TYPE_INTERFACIALVOLTAGE) { writelog(" NA "); } else { writelogf(" % -12.4e", molNumSpecies[kspec]); } writelogf(" % -12.4e", mfValue); writelogf(" % -12.4e", feSpecies[kspec] * RT); writelogf(" % -12.4e", feFull * RT); if (irxn != npos) { writelogf(" % -12.4e", deltaGRxn[irxn] * RT); writelogf(" % -12.4e", (deltaGRxn[irxn] + feFull - feSpecies[kspec]) * RT); if (deltaGRxn[irxn] < 0.0) { if (molNumSpecies[kspec] > 0.0) { writelog(" growing"); } else { writelog(" stable"); } } else if (deltaGRxn[irxn] > 0.0) { if (molNumSpecies[kspec] > 0.0) { writelog(" shrinking"); } else { writelog(" unstable"); } } else { writelog(" balanced"); } } writelog(" \n"); } writelog(" "); writeline('-', 132); } void VCS_SOLVE::vcs_deltag_Phase(const size_t iphase, const bool doDeleted, const int stateCalc, const bool alterZeroedPhases) { double* feSpecies=0; double* deltaGRxn=0; double* actCoeffSpecies=0; if (stateCalc == VCS_STATECALC_NEW) { feSpecies = &m_feSpecies_new[0]; deltaGRxn = &m_deltaGRxn_new[0]; actCoeffSpecies = &m_actCoeffSpecies_new[0]; } else if (stateCalc == VCS_STATECALC_OLD) { feSpecies = &m_feSpecies_old[0]; deltaGRxn = &m_deltaGRxn_old[0]; actCoeffSpecies = &m_actCoeffSpecies_old[0]; } else if (DEBUG_MODE_ENABLED) { throw CanteraError("VCS_SOLVE::vcs_deltag_Phase", "bad stateCalc"); } size_t irxnl = m_numRxnRdc; if (doDeleted) { irxnl = m_numRxnTot; } vcs_VolPhase* vPhase = m_VolPhaseList[iphase]; if (DEBUG_MODE_ENABLED && m_debug_print_lvl >= 2) { plogf(" --- Subroutine vcs_deltag_Phase called for phase %d\n", iphase); } if (vPhase->m_singleSpecies) { // Single species Phase size_t kspec = vPhase->spGlobalIndexVCS(0); AssertThrowMsg(iphase == m_phaseID[kspec], "VCS_SOLVE::vcs_deltag_Phase", "index error"); if (kspec >= m_numComponents) { size_t irxn = kspec - m_numComponents; deltaGRxn[irxn] = feSpecies[kspec]; for (size_t kcomp = 0; kcomp < m_numComponents; ++kcomp) { deltaGRxn[irxn] += m_stoichCoeffRxnMatrix(kcomp,irxn) * feSpecies[kcomp]; } } } else { // Multispecies Phase bool zeroedPhase = true; for (size_t irxn = 0; irxn < irxnl; ++irxn) { size_t kspec = m_indexRxnToSpecies[irxn]; if (m_speciesUnknownType[kspec] != VCS_SPECIES_TYPE_INTERFACIALVOLTAGE && m_phaseID[kspec] == iphase) { if (m_molNumSpecies_old[kspec] > 0.0) { zeroedPhase = false; } deltaGRxn[irxn] = feSpecies[kspec]; for (size_t kcomp = 0; kcomp < m_numComponents; ++kcomp) { deltaGRxn[irxn] += m_stoichCoeffRxnMatrix(kcomp,irxn) * feSpecies[kcomp]; } } } // MULTISPECIES PHASES WITH ZERO MOLES // // Massage the free energies for species with zero mole fractions in // multispecies phases. This section implements the Equation 3.8-5 in // Smith and Missen, p.59. A multispecies phase will exist iff // // 1 < sum_i(exp(-dg_i)/AC_i) // // If DG is negative then that species wants to be reintroduced into the // calculation. For small dg_i, the expression below becomes: // // 1 - sum_i(exp(-dg_i)/AC_i) ~ sum_i((dg_i-1)/AC_i) + 1 // // HKM -> The ratio of mole fractions at the reinstatement time should // be equal to the normalized weighting of exp(-dg_i) / AC_i. This // should be implemented. // // HKM -> There is circular logic here. ActCoeff depends on the mole // fractions of a phase that does not exist. In actuality the proto-mole // fractions should be selected from the solution of a nonlinear problem // with NsPhase unknowns // // X_i = exp(-dg[irxn]) / ActCoeff_i / denom // // where // // denom = sum_i[ exp(-dg[irxn]) / ActCoeff_i ] // // This can probably be solved by successive iteration. This should be // implemented. // // Calculate dg[] for each species in a zeroed multispecies phase. All // of the dg[]'s will be equal. If dg[] is negative, then the phase will // come back into existence. if (alterZeroedPhases && zeroedPhase) { double phaseDG = 1.0; for (size_t irxn = 0; irxn < irxnl; ++irxn) { size_t kspec = m_indexRxnToSpecies[irxn]; if (m_phaseID[kspec] == iphase) { deltaGRxn[irxn] = clip(deltaGRxn[irxn], -50.0, 50.0); phaseDG -= exp(-deltaGRxn[irxn])/actCoeffSpecies[kspec]; } } // Overwrite the individual dg's with the phase DG. for (size_t irxn = 0; irxn < irxnl; ++irxn) { size_t kspec = m_indexRxnToSpecies[irxn]; if (m_phaseID[kspec] == iphase) { deltaGRxn[irxn] = 1.0 - phaseDG; } } } } } void VCS_SOLVE::vcs_switch_pos(const bool ifunc, const size_t k1, const size_t k2) { if (k1 == k2) { return; } if (DEBUG_MODE_ENABLED && (k1 >= m_numSpeciesTot || k2 >= m_numSpeciesTot)) { plogf("vcs_switch_pos: ifunc = 0: inappropriate args: %d %d\n", k1, k2); } // Handle the index pointer in the phase structures first vcs_VolPhase* pv1 = m_VolPhaseList[m_phaseID[k1]]; vcs_VolPhase* pv2 = m_VolPhaseList[m_phaseID[k2]]; size_t kp1 = m_speciesLocalPhaseIndex[k1]; size_t kp2 = m_speciesLocalPhaseIndex[k2]; AssertThrowMsg(pv1->spGlobalIndexVCS(kp1) == k1, "VCS_SOLVE::vcs_switch_pos", "Indexing error"); AssertThrowMsg(pv2->spGlobalIndexVCS(kp2) == k2, "VCS_SOLVE::vcs_switch_pos", "Indexing error"); pv1->setSpGlobalIndexVCS(kp1, k2); pv2->setSpGlobalIndexVCS(kp2, k1); std::swap(m_speciesName[k1], m_speciesName[k2]); std::swap(m_molNumSpecies_old[k1], m_molNumSpecies_old[k2]); std::swap(m_speciesUnknownType[k1], m_speciesUnknownType[k2]); std::swap(m_molNumSpecies_new[k1], m_molNumSpecies_new[k2]); std::swap(m_SSfeSpecies[k1], m_SSfeSpecies[k2]); std::swap(m_spSize[k1], m_spSize[k2]); std::swap(m_deltaMolNumSpecies[k1], m_deltaMolNumSpecies[k2]); std::swap(m_feSpecies_old[k1], m_feSpecies_old[k2]); std::swap(m_feSpecies_new[k1], m_feSpecies_new[k2]); std::swap(m_SSPhase[k1], m_SSPhase[k2]); std::swap(m_phaseID[k1], m_phaseID[k2]); std::swap(m_speciesMapIndex[k1], m_speciesMapIndex[k2]); std::swap(m_speciesLocalPhaseIndex[k1], m_speciesLocalPhaseIndex[k2]); std::swap(m_actConventionSpecies[k1], m_actConventionSpecies[k2]); std::swap(m_lnMnaughtSpecies[k1], m_lnMnaughtSpecies[k2]); std::swap(m_actCoeffSpecies_new[k1], m_actCoeffSpecies_new[k2]); std::swap(m_actCoeffSpecies_old[k1], m_actCoeffSpecies_old[k2]); std::swap(m_wtSpecies[k1], m_wtSpecies[k2]); std::swap(m_chargeSpecies[k1], m_chargeSpecies[k2]); std::swap(m_speciesThermoList[k1], m_speciesThermoList[k2]); std::swap(m_PMVolumeSpecies[k1], m_PMVolumeSpecies[k2]); for (size_t j = 0; j < m_numElemConstraints; ++j) { std::swap(m_formulaMatrix(k1,j), m_formulaMatrix(k2,j)); } if (m_useActCoeffJac && k1 != k2) { for (size_t i = 0; i < m_numSpeciesTot; i++) { std::swap(m_np_dLnActCoeffdMolNum(k1,i), m_np_dLnActCoeffdMolNum(k2,i)); } for (size_t i = 0; i < m_numSpeciesTot; i++) { std::swap(m_np_dLnActCoeffdMolNum(i,k1), m_np_dLnActCoeffdMolNum(i,k2)); } } std::swap(m_speciesStatus[k1], m_speciesStatus[k2]); // Handle the index pointer in the phase structures if (ifunc) { // Find the Rxn indices corresponding to the two species size_t i1 = k1 - m_numComponents; size_t i2 = k2 - m_numComponents; if (DEBUG_MODE_ENABLED && (i1 > m_numRxnTot || i2 >= m_numRxnTot)) { plogf("switch_pos: ifunc = 1: inappropriate noncomp values: %d %d\n", i1 , i2); } for (size_t j = 0; j < m_numComponents; ++j) { std::swap(m_stoichCoeffRxnMatrix(j,i1), m_stoichCoeffRxnMatrix(j,i2)); } std::swap(m_scSize[i1], m_scSize[i2]); for (size_t iph = 0; iph < m_numPhases; iph++) { std::swap(m_deltaMolNumPhase(iph,i1), m_deltaMolNumPhase(iph,i2)); std::swap(m_phaseParticipation(iph,i1), m_phaseParticipation(iph,i2)); } std::swap(m_deltaGRxn_new[i1], m_deltaGRxn_new[i2]); std::swap(m_deltaGRxn_old[i1], m_deltaGRxn_old[i2]); std::swap(m_deltaGRxn_tmp[i1], m_deltaGRxn_tmp[i2]); } } double VCS_SOLVE::vcs_birthGuess(const int kspec) { size_t irxn = kspec - m_numComponents; double dx = 0.0; if (m_speciesUnknownType[kspec] == VCS_SPECIES_TYPE_INTERFACIALVOLTAGE) { return dx; } double w_kspec = VCS_DELETE_MINORSPECIES_CUTOFF; // Check to make sure that species is zero in the solution vector // If it isn't, we don't know what's happening AssertThrowMsg(m_molNumSpecies_old[kspec] == 0.0, "VCS_SOLVE::vcs_birthGuess", "we shouldn't be here"); int ss = m_SSPhase[kspec]; if (!ss) { // Logic to handle species in multiple species phases. We cap the moles // here at 1.0E-15 kmol. bool soldel_ret; double dxm = vcs_minor_alt_calc(kspec, irxn, &soldel_ret); dx = std::min(w_kspec + dxm, 1e-15); } else { // Logic to handle single species phases. There is no real way to // estimate the moles. So we set it to a small number. dx = 1.0E-30; } // Check to see if the current value of the components allow the dx just // estimated. If we are in danger of zeroing a component, only go 1/3 the // way to zeroing the component with this dx. Note, this may mean that dx= 0 // coming back from this routine. This evaluation should be respected. double* sc_irxn = m_stoichCoeffRxnMatrix.ptrColumn(irxn); for (size_t j = 0; j < m_numComponents; ++j) { // Only loop over element constraints that involve positive def. constraints if (m_speciesUnknownType[j] != VCS_SPECIES_TYPE_INTERFACIALVOLTAGE) { if (m_molNumSpecies_old[j] > 0.0) { double tmp = sc_irxn[j] * dx; if (3.0*(-tmp) > m_molNumSpecies_old[j]) { dx = std::min(dx, - 0.3333* m_molNumSpecies_old[j] / sc_irxn[j]); } } if (m_molNumSpecies_old[j] <= 0.0 && sc_irxn[j] < 0.0) { dx = 0.0; } } } return dx; } void VCS_SOLVE::vcs_setFlagsVolPhases(const bool upToDate, const int stateCalc) { if (!upToDate) { for (size_t iph = 0; iph < m_numPhases; iph++) { m_VolPhaseList[iph]->setMolesOutOfDate(stateCalc); } } else { for (size_t iph = 0; iph < m_numPhases; iph++) { m_VolPhaseList[iph]->setMolesCurrent(stateCalc); } } } void VCS_SOLVE::vcs_setFlagsVolPhase(const size_t iph, const bool upToDate, const int stateCalc) { if (!upToDate) { m_VolPhaseList[iph]->setMolesOutOfDate(stateCalc); } else { m_VolPhaseList[iph]->setMolesCurrent(stateCalc); } } void VCS_SOLVE::vcs_updateMolNumVolPhases(const int stateCalc) { for (size_t iph = 0; iph < m_numPhases; iph++) { m_VolPhaseList[iph]->updateFromVCS_MoleNumbers(stateCalc); } } }