void VCS_SOLVE::vcs_switch_elem_pos(size_t ipos, size_t jpos) { if (ipos == jpos) { return; } AssertThrowMsg(ipos < m_nelem && jpos < m_nelem, "vcs_switch_elem_pos", "inappropriate args: {} {}", ipos, jpos); // Change the element Global Index list in each vcs_VolPhase object // to reflect the switch in the element positions. for (size_t iph = 0; iph < m_numPhases; iph++) { vcs_VolPhase* volPhase = m_VolPhaseList[iph].get(); for (size_t e = 0; e < volPhase->nElemConstraints(); e++) { if (volPhase->elemGlobalIndex(e) == ipos) { volPhase->setElemGlobalIndex(e, jpos); } if (volPhase->elemGlobalIndex(e) == jpos) { volPhase->setElemGlobalIndex(e, ipos); } } } std::swap(m_elemAbundancesGoal[ipos], m_elemAbundancesGoal[jpos]); std::swap(m_elemAbundances[ipos], m_elemAbundances[jpos]); std::swap(m_elementMapIndex[ipos], m_elementMapIndex[jpos]); std::swap(m_elType[ipos], m_elType[jpos]); std::swap(m_elementActive[ipos], m_elementActive[jpos]); for (size_t j = 0; j < m_nsp; ++j) { std::swap(m_formulaMatrix(j,ipos), m_formulaMatrix(j,jpos)); } std::swap(m_elementName[ipos], m_elementName[jpos]); }
int VCS_SOLVE::vcs_elem_rearrange(double* const aw, double* const sa, double* const sm, double* const ss) { size_t ncomponents = m_numComponents; if (m_debug_print_lvl >= 2) { plogf(" "); writeline('-', 77); plogf(" --- Subroutine elem_rearrange() called to "); plogf("check stoich. coefficient matrix\n"); plogf(" --- and to rearrange the element ordering once\n"); } // Use a temporary work array for the element numbers // Also make sure the value of test is unique. bool lindep = true; double test = -1.0E10; while (lindep) { lindep = false; for (size_t i = 0; i < m_nelem; ++i) { test -= 1.0; aw[i] = m_elemAbundancesGoal[i]; if (test == aw[i]) { lindep = true; } } } // Top of a loop of some sort based on the index JR. JR is the current // number independent elements found. size_t jr = 0; while (jr < ncomponents) { size_t k; // 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. k = m_nelem; for (size_t ielem = jr; ielem < m_nelem; ielem++) { if (m_elementActive[ielem] && aw[ielem] != test) { k = ielem; break; } } if (k == m_nelem) { throw CanteraError("vcs_elem_rearrange", "Shouldn't be here. Algorithm misfired."); } // Assign a large negative number to the element that we have just // found, in order to take it out of further consideration. aw[k] = test; // CHECK LINEAR INDEPENDENCE OF CURRENT FORMULA MATRIX LINE WITH // PREVIOUS LINES OF THE FORMULA MATRIX // // Modified Gram-Schmidt Method, p. 202 Dalquist QR factorization of // a matrix without row pivoting. size_t jl = jr; // Fill in the row for the current element, k, under consideration // The row will contain the Formula matrix value for that element // from the current component. for (size_t j = 0; j < ncomponents; ++j) { sm[j + jr*ncomponents] = m_formulaMatrix(j,k); } 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 < ncomponents; ++i) { ss[j] += sm[i + jr*ncomponents] * sm[i + j*ncomponents]; } 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 i = 0; i < ncomponents; ++i) { sm[i + jr*ncomponents] -= ss[j] * sm[i + j*ncomponents]; } } } // 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 < ncomponents; ++ml) { sa[jr] += pow(sm[ml + jr*ncomponents], 2); } // IF NORM OF NEW ROW .LT. 1E-6 REJECT if (sa[jr] > 1.0e-6) { break; } } // REARRANGE THE DATA if (jr != k) { if (m_debug_print_lvl >= 2) { plogf(" --- %-2.2s(%9.2g) replaces %-2.2s(%9.2g) as element %3d\n", m_elementName[k], m_elemAbundancesGoal[k], m_elementName[jr], m_elemAbundancesGoal[jr], jr); } vcs_switch_elem_pos(jr, k); std::swap(aw[jr], aw[k]); } // If we haven't found enough components, go back and find some more. jr++; } return VCS_SUCCESS; }
void VCS_SOLVE::vcs_inest(double* const aw, double* const sa, double* const sm, double* const ss, double test) { size_t nspecies = m_numSpeciesTot; size_t nrxn = m_numRxnTot; /* * CALL ROUTINE TO SOLVE MAX(CC*molNum) SUCH THAT AX*molNum = BB * AND molNum(I) .GE. 0.0 * * Note, both of these programs do this. */ vcs_setMolesLinProg(); if (DEBUG_MODE_ENABLED && m_debug_print_lvl >= 2) { plogf("%s Mole Numbers returned from linear programming (vcs_inest initial guess):\n", pprefix); plogf("%s SPECIES MOLE_NUMBER -SS_ChemPotential\n", pprefix); for (size_t kspec = 0; kspec < nspecies; ++kspec) { plogf("%s ", pprefix); plogf("%-12.12s", m_speciesName[kspec].c_str()); plogf(" %15.5g %12.3g\n", m_molNumSpecies_old[kspec], -m_SSfeSpecies[kspec]); } plogf("%s Element Abundance Agreement returned from linear " "programming (vcs_inest initial guess):", pprefix); plogendl(); plogf("%s Element Goal Actual\n", pprefix); for (size_t j = 0; j < m_numElemConstraints; j++) { if (m_elementActive[j]) { double tmp = 0.0; for (size_t kspec = 0; kspec < nspecies; ++kspec) { tmp += m_formulaMatrix(kspec,j) * m_molNumSpecies_old[kspec]; } plogf("%s ", pprefix); plogf(" %-9.9s", (m_elementName[j]).c_str()); plogf(" %12.3g %12.3g\n", m_elemAbundancesGoal[j], tmp); } } plogendl(); } /* * Make sure all species have positive definite mole numbers * Set voltages to zero for now, until we figure out what to do */ m_deltaMolNumSpecies.assign(m_deltaMolNumSpecies.size(), 0.0); for (size_t kspec = 0; kspec < nspecies; ++kspec) { if (m_speciesUnknownType[kspec] != VCS_SPECIES_TYPE_INTERFACIALVOLTAGE) { if (m_molNumSpecies_old[kspec] <= 0.0) { /* * HKM Should eventually include logic here for non SS phases */ if (!m_SSPhase[kspec]) { m_molNumSpecies_old[kspec] = 1.0e-30; } } } else { m_molNumSpecies_old[kspec] = 0.0; } } /* * Now find the optimized basis that spans the stoichiometric * coefficient matrix */ bool conv; (void) vcs_basopt(false, aw, sa, sm, ss, test, &conv); /* ***************************************************************** */ /* **** CALCULATE TOTAL MOLES, ****************** */ /* **** CHEMICAL POTENTIALS OF BASIS ****************** */ /* ***************************************************************** */ /* * Calculate TMoles and m_tPhaseMoles_old[] */ vcs_tmoles(); /* * m_tPhaseMoles_new[] will consist of just the component moles */ for (size_t iph = 0; iph < m_numPhases; iph++) { m_tPhaseMoles_new[iph] = TPhInertMoles[iph] + 1.0E-20; } for (size_t kspec = 0; kspec < m_numComponents; ++kspec) { if (m_speciesUnknownType[kspec] == VCS_SPECIES_TYPE_MOLNUM) { m_tPhaseMoles_new[m_phaseID[kspec]] += m_molNumSpecies_old[kspec]; } } double TMolesMultiphase = 0.0; for (size_t iph = 0; iph < m_numPhases; iph++) { if (! m_VolPhaseList[iph]->m_singleSpecies) { TMolesMultiphase += m_tPhaseMoles_new[iph]; } } m_molNumSpecies_new = m_molNumSpecies_old; for (size_t kspec = 0; kspec < m_numComponents; ++kspec) { if (m_speciesUnknownType[kspec] != VCS_SPECIES_TYPE_MOLNUM) { m_molNumSpecies_new[kspec] = 0.0; } } m_feSpecies_new = m_SSfeSpecies; for (size_t kspec = 0; kspec < m_numComponents; ++kspec) { if (m_speciesUnknownType[kspec] == VCS_SPECIES_TYPE_MOLNUM) { if (! m_SSPhase[kspec]) { size_t iph = m_phaseID[kspec]; m_feSpecies_new[kspec] += log(m_molNumSpecies_new[kspec] / m_tPhaseMoles_old[iph]); } } else { m_molNumSpecies_new[kspec] = 0.0; } } vcs_deltag(0, true, VCS_STATECALC_NEW); if (DEBUG_MODE_ENABLED && m_debug_print_lvl >= 2) { for (size_t kspec = 0; kspec < nspecies; ++kspec) { plogf("%s", pprefix); plogf("%-12.12s", m_speciesName[kspec].c_str()); if (kspec < m_numComponents) plogf("fe* = %15.5g ff = %15.5g\n", m_feSpecies_new[kspec], m_SSfeSpecies[kspec]); else plogf("fe* = %15.5g ff = %15.5g dg* = %15.5g\n", m_feSpecies_new[kspec], m_SSfeSpecies[kspec], m_deltaGRxn_new[kspec-m_numComponents]); } } /* ********************************************************** */ /* **** ESTIMATE REACTION ADJUSTMENTS *********************** */ /* ********************************************************** */ double* xtphMax = VCS_DATA_PTR(m_TmpPhase); double* xtphMin = VCS_DATA_PTR(m_TmpPhase2); m_deltaPhaseMoles.assign(m_deltaPhaseMoles.size(), 0.0); for (size_t iph = 0; iph < m_numPhases; iph++) { xtphMax[iph] = log(m_tPhaseMoles_new[iph] * 1.0E32); xtphMin[iph] = log(m_tPhaseMoles_new[iph] * 1.0E-32); } for (size_t irxn = 0; irxn < nrxn; ++irxn) { size_t kspec = m_indexRxnToSpecies[irxn]; /* * For single species phases, we will not estimate the * mole numbers. If the phase exists, it stays. If it * doesn't exist in the estimate, it doesn't come into * existence here. */ if (! m_SSPhase[kspec]) { size_t iph = m_phaseID[kspec]; if (m_deltaGRxn_new[irxn] > xtphMax[iph]) { m_deltaGRxn_new[irxn] = 0.8 * xtphMax[iph]; } if (m_deltaGRxn_new[irxn] < xtphMin[iph]) { m_deltaGRxn_new[irxn] = 0.8 * xtphMin[iph]; } /* * HKM -> The TMolesMultiphase is a change of mine. * It more evenly distributes the initial moles amongst * multiple multispecies phases according to the * relative values of the standard state free energies. * There is no change for problems with one multispecies * phase. * It cut diamond4.vin iterations down from 62 to 14. */ m_deltaMolNumSpecies[kspec] = 0.5 * (m_tPhaseMoles_new[iph] + TMolesMultiphase) * exp(-m_deltaGRxn_new[irxn]); for (size_t k = 0; k < m_numComponents; ++k) { m_deltaMolNumSpecies[k] += m_stoichCoeffRxnMatrix(k,irxn) * m_deltaMolNumSpecies[kspec]; } for (iph = 0; iph < m_numPhases; iph++) { m_deltaPhaseMoles[iph] += m_deltaMolNumPhase(iph,irxn) * m_deltaMolNumSpecies[kspec]; } } } if (DEBUG_MODE_ENABLED && m_debug_print_lvl >= 2) { for (size_t kspec = 0; kspec < nspecies; ++kspec) { if (m_speciesUnknownType[kspec] != VCS_SPECIES_TYPE_INTERFACIALVOLTAGE) { plogf("%sdirection (", pprefix); plogf("%-12.12s", m_speciesName[kspec].c_str()); plogf(") = %g", m_deltaMolNumSpecies[kspec]); if (m_SSPhase[kspec]) { if (m_molNumSpecies_old[kspec] > 0.0) { plogf(" (ssPhase exists at w = %g moles)", m_molNumSpecies_old[kspec]); } else { plogf(" (ssPhase doesn't exist -> stability not checked)"); } } plogendl(); } } } /* *********************************************************** */ /* **** KEEP COMPONENT SPECIES POSITIVE ********************** */ /* *********************************************************** */ double par = 0.5; for (size_t kspec = 0; kspec < m_numComponents; ++kspec) { if (m_speciesUnknownType[kspec] != VCS_SPECIES_TYPE_INTERFACIALVOLTAGE) { if (par < -m_deltaMolNumSpecies[kspec] / m_molNumSpecies_new[kspec]) { par = -m_deltaMolNumSpecies[kspec] / m_molNumSpecies_new[kspec]; } } } par = 1. / par; if (par <= 1.0 && par > 0.0) { par *= 0.8; } else { par = 1.0; } /* ******************************************** */ /* **** CALCULATE NEW MOLE NUMBERS ************ */ /* ******************************************** */ size_t lt = 0; size_t ikl = 0; double s1 = 0.0; while (true) { for (size_t kspec = 0; kspec < m_numComponents; ++kspec) { if (m_speciesUnknownType[kspec] != VCS_SPECIES_TYPE_INTERFACIALVOLTAGE) { m_molNumSpecies_old[kspec] = m_molNumSpecies_new[kspec] + par * m_deltaMolNumSpecies[kspec]; } else { m_deltaMolNumSpecies[kspec] = 0.0; } } for (size_t kspec = m_numComponents; kspec < nspecies; ++kspec) { if (m_speciesUnknownType[kspec] != VCS_SPECIES_TYPE_INTERFACIALVOLTAGE) { if (m_deltaMolNumSpecies[kspec] != 0.0) { m_molNumSpecies_old[kspec] = m_deltaMolNumSpecies[kspec] * par; } } } /* * We have a new w[] estimate, go get the * TMoles and m_tPhaseMoles_old[] values */ vcs_tmoles(); if (lt > 0) { break; } /* ******************************************* */ /* **** CONVERGENCE FORCING SECTION ********** */ /* ******************************************* */ vcs_setFlagsVolPhases(false, VCS_STATECALC_OLD); vcs_dfe(VCS_STATECALC_OLD, 0, 0, nspecies); double s = 0.0; for (size_t kspec = 0; kspec < nspecies; ++kspec) { s += m_deltaMolNumSpecies[kspec] * m_feSpecies_old[kspec]; } if (s == 0.0) { break; } if (s < 0.0) { if (ikl == 0) { break; } } /* ***************************************** */ /* *** TRY HALF STEP SIZE ****************** */ /* ***************************************** */ if (ikl == 0) { s1 = s; par *= 0.5; ikl = 1; continue; } /* **************************************************** */ /* **** FIT PARABOLA THROUGH HALF AND FULL STEPS ****** */ /* **************************************************** */ double xl = (1.0 - s / (s1 - s)) * 0.5; if (xl < 0.0) { /* *************************************************** */ /* *** POOR DIRECTION, REDUCE STEP SIZE TO 0.2 ******* */ /* *************************************************** */ par *= 0.2; } else { if (xl > 1.0) { /* *************************************************** */ /* **** TOO BIG A STEP, TAKE ORIGINAL FULL STEP ****** */ /* *************************************************** */ par *= 2.0; } else { /* *************************************************** */ /* **** ACCEPT RESULTS OF FORCER ********************* */ /* *************************************************** */ par = par * 2.0 * xl; } } lt = 1; } if (DEBUG_MODE_ENABLED && m_debug_print_lvl >= 2) { plogf("%s Final Mole Numbers produced by inest:\n", pprefix); plogf("%s SPECIES MOLE_NUMBER\n", pprefix); for (size_t kspec = 0; kspec < nspecies; ++kspec) { plogf("%s ", pprefix); plogf("%-12.12s", m_speciesName[kspec].c_str()); plogf(" %g", m_molNumSpecies_old[kspec]); plogendl(); } } }
size_t vcs_VolPhase::transferElementsFM(const ThermoPhase* const tPhase) { size_t nebase = tPhase->nElements(); size_t ne = nebase; size_t ns = tPhase->nSpecies(); /* * Decide whether we need an extra element constraint for charge * neutrality of the phase */ bool cne = chargeNeutralityElement(tPhase); if (cne) { ChargeNeutralityElement = ne; ne++; } /* * Assign and malloc structures */ elemResize(ne); if (ChargeNeutralityElement != npos) { m_elementType[ChargeNeutralityElement] = VCS_ELEM_TYPE_CHARGENEUTRALITY; } size_t eFound = npos; if (hasChargedSpecies(tPhase)) { if (cne) { /* * We need a charge neutrality constraint. * We also have an Electron Element. These are * duplicates of each other. To avoid trouble with * possible range error conflicts, sometimes we eliminate * the Electron condition. Flag that condition for elimination * by toggling the ElActive variable. If we find we need it * later, we will retoggle ElActive to true. */ for (size_t eT = 0; eT < nebase; eT++) { if (tPhase->elementName(eT) == "E") { eFound = eT; m_elementActive[eT] = 0; m_elementType[eT] = VCS_ELEM_TYPE_ELECTRONCHARGE; } } } else { for (size_t eT = 0; eT < nebase; eT++) { if (tPhase->elementName(eT) == "E") { eFound = eT; m_elementType[eT] = VCS_ELEM_TYPE_ELECTRONCHARGE; } } } if (eFound == npos) { eFound = ne; m_elementType[ne] = VCS_ELEM_TYPE_ELECTRONCHARGE; m_elementActive[ne] = 0; std::string ename = "E"; m_elementNames[ne] = ename; ne++; elemResize(ne); } } m_formulaMatrix.resize(ns, ne, 0.0); m_speciesUnknownType.resize(ns, VCS_SPECIES_TYPE_MOLNUM); elemResize(ne); size_t e = 0; for (size_t eT = 0; eT < nebase; eT++) { m_elementNames[e] = tPhase->elementName(eT); m_elementType[e] = tPhase->elementType(eT); e++; } if (cne) { std::string pname = tPhase->id(); if (pname == "") { std::stringstream sss; sss << "phase" << VP_ID_; pname = sss.str(); } e = ChargeNeutralityElement; m_elementNames[e] = "cn_" + pname; } for (size_t k = 0; k < ns; k++) { e = 0; for (size_t eT = 0; eT < nebase; eT++) { m_formulaMatrix(k,e) = tPhase->nAtoms(k, eT); e++; } if (eFound != npos) { m_formulaMatrix(k,eFound) = - tPhase->charge(k); } } if (cne) { for (size_t k = 0; k < ns; k++) { m_formulaMatrix(k,ChargeNeutralityElement) = tPhase->charge(k); } } /* * Here, we figure out what is the species types are * The logic isn't set in stone, and is just for a particular type * of problem that I'm solving first. */ if (ns == 1) { if (tPhase->charge(0) != 0.0) { m_speciesUnknownType[0] = VCS_SPECIES_TYPE_INTERFACIALVOLTAGE; setPhiVarIndex(0); } } return ne; }
int VCS_SOLVE::vcs_elem_rearrange(double* const aw, double* const sa, double* const sm, double* const ss) { size_t ncomponents = m_numComponents; if (DEBUG_MODE_ENABLED && m_debug_print_lvl >= 2) { plogf(" "); for (size_t i=0; i<77; i++) { plogf("-"); } plogf("\n"); plogf(" --- Subroutine elem_rearrange() called to "); plogf("check stoich. coefficient matrix\n"); plogf(" --- and to rearrange the element ordering once"); plogendl(); } /* * Use a temporary work array for the element numbers * Also make sure the value of test is unique. */ bool lindep = false; double test = -1.0E10; do { lindep = false; for (size_t i = 0; i < m_numElemConstraints; ++i) { test -= 1.0; aw[i] = m_elemAbundancesGoal[i]; if (test == aw[i]) { lindep = true; } } } while (lindep); /* * Top of a loop of some sort based on the index JR. JR is the * current number independent elements found. */ size_t jr = npos; do { ++jr; size_t k; /* * Top of another loop point based on finding a linearly * independent species */ do { /* * Search the remaining part of the mole fraction vector, AW, * for the largest remaining species. Return its identity in K. */ k = m_numElemConstraints; for (size_t ielem = jr; ielem < m_numElemConstraints; ielem++) { if (m_elementActive[ielem]) { if (aw[ielem] != test) { k = ielem; break; } } } if (k == m_numElemConstraints) { throw CanteraError("vcs_elem_rearrange", "Shouldn't be here. Algorithm misfired."); } /* * Assign a large negative number to the element that we have * just found, in order to take it out of further consideration. */ aw[k] = test; /* *********************************************************** */ /* **** CHECK LINEAR INDEPENDENCE OF CURRENT FORMULA MATRIX */ /* **** LINE WITH PREVIOUS LINES OF THE FORMULA MATRIX ****** */ /* *********************************************************** */ /* * Modified Gram-Schmidt Method, p. 202 Dalquist * QR factorization of a matrix without row pivoting. */ size_t jl = jr; /* * Fill in the row for the current element, k, under consideration * The row will contain the Formula matrix value for that element * from the current component. */ for (size_t j = 0; j < ncomponents; ++j) { sm[j + jr*ncomponents] = m_formulaMatrix(j,k); } 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 < ncomponents; ++i) { ss[j] += sm[i + jr*ncomponents] * sm[i + j*ncomponents]; } 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 < ncomponents; ++l) { sm[l + jr*ncomponents] -= ss[j] * sm[l + j*ncomponents]; } } } /* * 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 < ncomponents; ++ml) { sa[jr] += pow(sm[ml + jr*ncomponents], 2); } /* **************************************************** */ /* **** IF NORM OF NEW ROW .LT. 1E-6 REJECT ********** */ /* **************************************************** */ if (sa[jr] < 1.0e-6) { lindep = true; } else { lindep = false; } } while (lindep); /* ****************************************** */ /* **** REARRANGE THE DATA ****************** */ /* ****************************************** */ if (jr != k) { if (DEBUG_MODE_ENABLED && m_debug_print_lvl >= 2) { plogf(" --- "); plogf("%-2.2s", (m_elementName[k]).c_str()); plogf("(%9.2g) replaces ", m_elemAbundancesGoal[k]); plogf("%-2.2s", (m_elementName[jr]).c_str()); plogf("(%9.2g) as element %3d", m_elemAbundancesGoal[jr], jr); plogendl(); } vcs_switch_elem_pos(jr, k); std::swap(aw[jr], aw[k]); } /* * If we haven't found enough components, go back * and find some more. (nc -1 is used below, because * jr is counted from 0, via the C convention. */ } while (jr < (ncomponents-1)); return VCS_SUCCESS; }