/* * This routine is mostly concerned with changing the private data * to be consistent with that needed for solution. It is called for * every invocation of the vcs_solve() except for the cleanup invocation. * * Tasks: * 1) Initialization of arrays to zero. * 2) Calculate total number of moles in all phases * * return code * VCS_SUCCESS = everything went OK * VCS_PUB_BAD = There is an irreconcilable difference in the * public data structure from when the problem was * initially set up. */ int VCS_SOLVE::vcs_prep() { /* * Initialize various arrays in the data to zero */ vcs_vdzero(m_feSpecies_old, m_numSpeciesTot); vcs_vdzero(m_feSpecies_new, m_numSpeciesTot); vcs_vdzero(m_molNumSpecies_new, m_numSpeciesTot); vcs_dzero(&(m_deltaMolNumPhase[0][0]), m_numSpeciesTot * m_numPhases); vcs_izero(&(m_phaseParticipation[0][0]), m_numSpeciesTot * m_numPhases); vcs_dzero(VCS_DATA_PTR(m_deltaPhaseMoles), m_numPhases); vcs_dzero(VCS_DATA_PTR(m_tPhaseMoles_new), m_numPhases); /* * Calculate the total number of moles in all phases. */ vcs_tmoles(); return VCS_SUCCESS; }
/* * Redimensionalize the free energies using the multiplier R * T * * Essentially the internal data can either be in dimensional form * or in nondimensional form. This routine switches the data from * nondimensional form into dimensional form. * * What we do is to multiply by RT. */ void VCS_SOLVE::vcs_redim_TP(void) { double tf; if (m_unitsState != VCS_DIMENSIONAL_G) { m_unitsState = VCS_DIMENSIONAL_G; tf = vcs_nondimMult_TP(m_VCS_UnitsFormat, m_temperature); for (size_t i = 0; i < m_numSpeciesTot; ++i) { /* * Modify the standard state and total chemical potential data, * FF(I), to make it have units, i.e. mu = RT * mu_star */ m_SSfeSpecies[i] *= tf; m_deltaGRxn_new[i] *= tf; m_deltaGRxn_old[i] *= tf; m_feSpecies_old[i] *= tf; } m_Faraday_dim *= tf; } if (m_totalMoleScale != 1.0) { if (m_VCS_UnitsFormat == VCS_UNITS_MKS) { #ifdef DEBUG_MODE if (m_debug_print_lvl >= 2) { plogf(" --- vcs_redim_TP() called: getting rid of mole scale of %g", m_totalMoleScale); plogendl(); } #endif for (size_t i = 0; i < m_numSpeciesTot; ++i) { if (m_speciesUnknownType[i] != VCS_SPECIES_TYPE_INTERFACIALVOLTAGE) { m_molNumSpecies_old[i] *= m_totalMoleScale; } } for (size_t i = 0; i < m_numElemConstraints; ++i) { m_elemAbundancesGoal[i] *= m_totalMoleScale; } for (size_t iph = 0; iph < m_numPhases; iph++) { TPhInertMoles[iph] *= m_totalMoleScale; if (TPhInertMoles[iph] != 0.0) { vcs_VolPhase *vphase = m_VolPhaseList[iph]; vphase->setTotalMolesInert(TPhInertMoles[iph]); } } vcs_tmoles(); } } }
int VCS_SOLVE::vcs_prep() { /* * Initialize various arrays in the data to zero */ m_feSpecies_old.assign(m_feSpecies_old.size(), 0.0); m_feSpecies_new.assign(m_feSpecies_new.size(), 0.0); m_molNumSpecies_new.assign(m_molNumSpecies_new.size(), 0.0); m_deltaMolNumPhase.zero(); m_phaseParticipation.zero(); m_deltaPhaseMoles.assign(m_deltaPhaseMoles.size(), 0.0); m_tPhaseMoles_new.assign(m_tPhaseMoles_new.size(), 0.0); /* * Calculate the total number of moles in all phases. */ vcs_tmoles(); return VCS_SUCCESS; }
int VCS_SOLVE::vcs_elcorr(double aa[], double x[]) /************************************************************************** * * vcs_elcorr: * * This subroutine corrects for element abundances. At the end of the * surbroutine, the total moles in all phases are recalculated again, * because we have changed the number of moles in this routine. * * Input * -> temporary work vectors: * aa[ne*ne] * x[ne] * * Return Values: * 0 = Nothing of significance happened, * Element abundances were and still are good. * 1 = The solution changed significantly; * The element abundances are now good. * 2 = The solution changed significantly, * The element abundances are still bad. * 3 = The solution changed significantly, * The element abundances are still bad and a component * species got zeroed out. * * Internal data to be worked on:: * * ga Current element abundances * m_elemAbundancesGoal Required elemental abundances * m_molNumSpecies_old Current mole number of species. * m_formulaMatrix[][] Formular matrix of the species * ne Number of elements * nc Number of components. * * NOTES: * This routine is turning out to be very problematic. There are * lots of special cases and problems with zeroing out species. * * Still need to check out when we do loops over nc vs. ne. * *************************************************************************/ { int i, j, retn = 0, kspec, goodSpec, its; double xx, par, saveDir, dir; #ifdef DEBUG_MODE double l2before = 0.0, l2after = 0.0; std::vector<double> ga_save(m_numElemConstraints, 0.0); vcs_dcopy(VCS_DATA_PTR(ga_save), VCS_DATA_PTR(m_elemAbundances), m_numElemConstraints); if (m_debug_print_lvl >= 2) { plogf(" --- vcsc_elcorr: Element abundances correction routine"); if (m_numElemConstraints != m_numComponents) { plogf(" (m_numComponents != m_numElemConstraints)"); } plogf("\n"); } for (i = 0; i < m_numElemConstraints; ++i) { x[i] = m_elemAbundances[i] - m_elemAbundancesGoal[i]; } l2before = 0.0; for (i = 0; i < m_numElemConstraints; ++i) { l2before += x[i] * x[i]; } l2before = sqrt(l2before/m_numElemConstraints); #endif /* * Special section to take out single species, single component, * moles. These are species which have non-zero entries in the * formula matrix, and no other species have zero values either. * */ int numNonZero = 0; bool changed = false; bool multisign = false; for (i = 0; i < m_numElemConstraints; ++i) { numNonZero = 0; multisign = false; for (kspec = 0; kspec < m_numSpeciesTot; kspec++) { if (m_speciesUnknownType[kspec] != VCS_SPECIES_TYPE_INTERFACIALVOLTAGE) { double eval = m_formulaMatrix[i][kspec]; if (eval < 0.0) { multisign = true; } if (eval != 0.0) { numNonZero++; } } } if (!multisign) { if (numNonZero < 2) { for (kspec = 0; kspec < m_numSpeciesTot; kspec++) { if (m_speciesUnknownType[kspec] != VCS_SPECIES_TYPE_INTERFACIALVOLTAGE) { double eval = m_formulaMatrix[i][kspec]; if (eval > 0.0) { m_molNumSpecies_old[kspec] = m_elemAbundancesGoal[i] / eval; changed = true; } } } } else { int numCompNonZero = 0; int compID = -1; for (kspec = 0; kspec < m_numComponents; kspec++) { if (m_speciesUnknownType[kspec] != VCS_SPECIES_TYPE_INTERFACIALVOLTAGE) { double eval = m_formulaMatrix[i][kspec]; if (eval > 0.0) { compID = kspec; numCompNonZero++; } } } if (numCompNonZero == 1) { double diff = m_elemAbundancesGoal[i]; for (kspec = m_numComponents; kspec < m_numSpeciesTot; kspec++) { if (m_speciesUnknownType[kspec] != VCS_SPECIES_TYPE_INTERFACIALVOLTAGE) { double eval = m_formulaMatrix[i][kspec]; diff -= eval * m_molNumSpecies_old[kspec]; } m_molNumSpecies_old[compID] = MAX(0.0,diff/m_formulaMatrix[i][compID]); changed = true; } } } } } if (changed) { vcs_elab(); } /* * Section to check for maximum bounds errors on all species * due to elements. * This may only be tried on element types which are VCS_ELEM_TYPE_ABSPOS. * This is because no other species may have a negative number of these. * * Note, also we can do this over ne, the number of elements, not just * the number of components. */ changed = false; for (i = 0; i < m_numElemConstraints; ++i) { int elType = m_elType[i]; if (elType == VCS_ELEM_TYPE_ABSPOS) { for (kspec = 0; kspec < m_numSpeciesTot; kspec++) { if (m_speciesUnknownType[kspec] != VCS_SPECIES_TYPE_INTERFACIALVOLTAGE) { double atomComp = m_formulaMatrix[i][kspec]; if (atomComp > 0.0) { double maxPermissible = m_elemAbundancesGoal[i] / atomComp; if (m_molNumSpecies_old[kspec] > maxPermissible) { #ifdef DEBUG_MODE if (m_debug_print_lvl >= 3) { plogf(" --- vcs_elcorr: Reduced species %s from %g to %g " "due to %s max bounds constraint\n", m_speciesName[kspec].c_str(), m_molNumSpecies_old[kspec], maxPermissible, m_elementName[i].c_str()); } #endif m_molNumSpecies_old[kspec] = maxPermissible; changed = true; if (m_molNumSpecies_old[kspec] < VCS_DELETE_MINORSPECIES_CUTOFF) { m_molNumSpecies_old[kspec] = 0.0; if (m_SSPhase[kspec]) { m_speciesStatus[kspec] = VCS_SPECIES_ZEROEDSS; } else { m_speciesStatus[kspec] = VCS_SPECIES_ACTIVEBUTZERO; } #ifdef DEBUG_MODE if (m_debug_print_lvl >= 2) { plogf(" --- vcs_elcorr: Zeroed species %s and changed " "status to %d due to max bounds constraint\n", m_speciesName[kspec].c_str(), m_speciesStatus[kspec]); } #endif } } } } } } } // Recalculate the element abundances if something has changed. if (changed) { vcs_elab(); } /* * Ok, do the general case. Linear algebra problem is * of length nc, not ne, as there may be degenerate rows when * nc .ne. ne. */ for (i = 0; i < m_numComponents; ++i) { x[i] = m_elemAbundances[i] - m_elemAbundancesGoal[i]; if (fabs(x[i]) > 1.0E-13) retn = 1; for (j = 0; j < m_numComponents; ++j) { aa[j + i*m_numElemConstraints] = m_formulaMatrix[j][i]; } } i = vcsUtil_mlequ(aa, m_numElemConstraints, m_numComponents, x, 1); if (i == 1) { plogf("vcs_elcorr ERROR: mlequ returned error condition\n"); return VCS_FAILED_CONVERGENCE; } /* * Now apply the new direction without creating negative species. */ par = 0.5; for (i = 0; i < m_numComponents; ++i) { if (m_molNumSpecies_old[i] > 0.0) { xx = -x[i] / m_molNumSpecies_old[i]; if (par < xx) par = xx; } } if (par > 100.0) { par = 100.0; } par = 1.0 / par; if (par < 1.0 && par > 0.0) { retn = 2; par *= 0.9999; for (i = 0; i < m_numComponents; ++i) { double tmp = m_molNumSpecies_old[i] + par * x[i]; if (tmp > 0.0) { m_molNumSpecies_old[i] = tmp; } else { if (m_SSPhase[i]) { m_molNumSpecies_old[i] = 0.0; } else { m_molNumSpecies_old[i] = m_molNumSpecies_old[i] * 0.0001; } } } } else { for (i = 0; i < m_numComponents; ++i) { double tmp = m_molNumSpecies_old[i] + x[i]; if (tmp > 0.0) { m_molNumSpecies_old[i] = tmp; } else { if (m_SSPhase[i]) { m_molNumSpecies_old[i] = 0.0; } else { m_molNumSpecies_old[i] = m_molNumSpecies_old[i] * 0.0001; } } } } /* * We have changed the element abundances. Calculate them again */ vcs_elab(); /* * We have changed the total moles in each phase. Calculate them again */ vcs_tmoles(); /* * Try some ad hoc procedures for fixing the problem */ if (retn >= 2) { /* * First find a species whose adjustment is a win-win * situation. */ for (kspec = 0; kspec < m_numSpeciesTot; kspec++) { if (m_speciesUnknownType[kspec] == VCS_SPECIES_TYPE_INTERFACIALVOLTAGE) { continue; } saveDir = 0.0; goodSpec = TRUE; for (i = 0; i < m_numComponents; ++i) { dir = m_formulaMatrix[i][kspec] * (m_elemAbundancesGoal[i] - m_elemAbundances[i]); if (fabs(dir) > 1.0E-10) { if (dir > 0.0) { if (saveDir < 0.0) { goodSpec = FALSE; break; } } else { if (saveDir > 0.0) { goodSpec = FALSE; break; } } saveDir = dir; } else { if (m_formulaMatrix[i][kspec] != 0.) { goodSpec = FALSE; break; } } } if (goodSpec) { its = 0; xx = 0.0; for (i = 0; i < m_numComponents; ++i) { if (m_formulaMatrix[i][kspec] != 0.0) { xx += (m_elemAbundancesGoal[i] - m_elemAbundances[i]) / m_formulaMatrix[i][kspec]; its++; } } if (its > 0) xx /= its; m_molNumSpecies_old[kspec] += xx; m_molNumSpecies_old[kspec] = MAX(m_molNumSpecies_old[kspec], 1.0E-10); /* * If we are dealing with a deleted species, then * we need to reinsert it into the active list. */ if (kspec >= m_numSpeciesRdc) { vcs_reinsert_deleted(kspec); m_molNumSpecies_old[m_numSpeciesRdc - 1] = xx; vcs_elab(); goto L_CLEANUP; } vcs_elab(); } } } if (vcs_elabcheck(0)) { retn = 1; goto L_CLEANUP; } for (i = 0; i < m_numElemConstraints; ++i) { if (m_elType[i] == VCS_ELEM_TYPE_CHARGENEUTRALITY || (m_elType[i] == VCS_ELEM_TYPE_ABSPOS && m_elemAbundancesGoal[i] == 0.0)) { for (kspec = 0; kspec < m_numSpeciesRdc; kspec++) { if (m_elemAbundances[i] > 0.0) { if (m_formulaMatrix[i][kspec] < 0.0) { m_molNumSpecies_old[kspec] -= m_elemAbundances[i] / m_formulaMatrix[i][kspec] ; if (m_molNumSpecies_old[kspec] < 0.0) { m_molNumSpecies_old[kspec] = 0.0; } vcs_elab(); break; } } if (m_elemAbundances[i] < 0.0) { if (m_formulaMatrix[i][kspec] > 0.0) { m_molNumSpecies_old[kspec] -= m_elemAbundances[i] / m_formulaMatrix[i][kspec]; if (m_molNumSpecies_old[kspec] < 0.0) { m_molNumSpecies_old[kspec] = 0.0; } vcs_elab(); break; } } } } } if (vcs_elabcheck(1)) { retn = 1; goto L_CLEANUP; } /* * For electron charges element types, we try positive deltas * in the species concentrations to match the desired * electron charge exactly. */ for (i = 0; i < m_numElemConstraints; ++i) { double dev = m_elemAbundancesGoal[i] - m_elemAbundances[i]; if (m_elType[i] == VCS_ELEM_TYPE_ELECTRONCHARGE && (fabs(dev) > 1.0E-300)) { bool useZeroed = true; for (kspec = 0; kspec < m_numSpeciesRdc; kspec++) { if (dev < 0.0) { if (m_formulaMatrix[i][kspec] < 0.0) { if (m_molNumSpecies_old[kspec] > 0.0) { useZeroed = false; } } } else { if (m_formulaMatrix[i][kspec] > 0.0) { if (m_molNumSpecies_old[kspec] > 0.0) { useZeroed = false; } } } } for (kspec = 0; kspec < m_numSpeciesRdc; kspec++) { if (m_molNumSpecies_old[kspec] > 0.0 || useZeroed) { if (dev < 0.0) { if (m_formulaMatrix[i][kspec] < 0.0) { double delta = dev / m_formulaMatrix[i][kspec] ; m_molNumSpecies_old[kspec] += delta; if (m_molNumSpecies_old[kspec] < 0.0) { m_molNumSpecies_old[kspec] = 0.0; } vcs_elab(); break; } } if (dev > 0.0) { if (m_formulaMatrix[i][kspec] > 0.0) { double delta = dev / m_formulaMatrix[i][kspec] ; m_molNumSpecies_old[kspec] += delta; if (m_molNumSpecies_old[kspec] < 0.0) { m_molNumSpecies_old[kspec] = 0.0; } vcs_elab(); break; } } } } } } if (vcs_elabcheck(1)) { retn = 1; goto L_CLEANUP; } L_CLEANUP: ; vcs_tmoles(); #ifdef DEBUG_MODE l2after = 0.0; for (i = 0; i < m_numElemConstraints; ++i) { l2after += SQUARE(m_elemAbundances[i] - m_elemAbundancesGoal[i]); } l2after = sqrt(l2after/m_numElemConstraints); if (m_debug_print_lvl >= 2) { plogf(" --- Elem_Abund: Correct Initial " " Final\n"); for (i = 0; i < m_numElemConstraints; ++i) { plogf(" --- "); plogf("%-2.2s", m_elementName[i].c_str()); plogf(" %20.12E %20.12E %20.12E\n", m_elemAbundancesGoal[i], ga_save[i], m_elemAbundances[i]); } plogf(" --- Diff_Norm: %20.12E %20.12E\n", l2before, l2after); } #endif return retn; }
/* * Nondimensionalize the free energies using the divisor, R * T * * Essentially the internal data can either be in dimensional form * or in nondimensional form. This routine switches the data from * dimensional form into nondimensional form. * * What we do is to divide by RT. * * @todo Add a scale factor based on the total mole numbers. * The algorithm contains hard coded numbers based on the * total mole number. If we ever were faced with a problem * with significantly different total kmol numbers than one * the algorithm would have problems. */ void VCS_SOLVE::vcs_nondim_TP() { double tf; if (m_unitsState == VCS_DIMENSIONAL_G) { m_unitsState = VCS_NONDIMENSIONAL_G; tf = 1.0 / vcs_nondimMult_TP(m_VCS_UnitsFormat, m_temperature); for (size_t i = 0; i < m_numSpeciesTot; ++i) { /* * Modify the standard state and total chemical potential data, * FF(I), to make it dimensionless, i.e., mu / RT. * Thus, we may divide it by the temperature. */ m_SSfeSpecies[i] *= tf; m_deltaGRxn_new[i] *= tf; m_deltaGRxn_old[i] *= tf; m_feSpecies_old[i] *= tf; } m_Faraday_dim = vcs_nondim_Farad(m_VCS_UnitsFormat, m_temperature); /* * Scale the total moles if necessary: * First find out the total moles */ double tmole_orig = vcs_tmoles(); /* * Then add in the total moles of elements that are goals. Either one * or the other is specified here. */ double esum = 0.0; for (size_t i = 0; i < m_numElemConstraints; ++i) { if (m_elType[i] == VCS_ELEM_TYPE_ABSPOS) { esum += fabs(m_elemAbundancesGoal[i]); } } tmole_orig += esum; /* * Ok now test out the bounds on the total moles that this program can * handle. These are a bit arbitrary. However, it would seem that any * reasonable input would be between these two numbers below. */ if (tmole_orig < 1.0E-200 || tmole_orig > 1.0E200) { plogf(" VCS_SOLVE::vcs_nondim_TP ERROR: Total input moles , %g, is outside the range handled by vcs. exit", tmole_orig); plogendl(); throw vcsError("VCS_SOLVE::vcs_nondim_TP", " Total input moles ," + Cantera::fp2str(tmole_orig) + "is outside the range handled by vcs.\n"); } // Determine the scale of the problem if (tmole_orig > 1.0E4) { m_totalMoleScale = tmole_orig / 1.0E4; } else if (tmole_orig < 1.0E-4) { m_totalMoleScale = tmole_orig / 1.0E-4; } else { m_totalMoleScale = 1.0; } if (m_totalMoleScale != 1.0) { if (m_VCS_UnitsFormat == VCS_UNITS_MKS) { #ifdef DEBUG_MODE if (m_debug_print_lvl >= 2) { plogf(" --- vcs_nondim_TP() called: USING A MOLE SCALE OF %g until further notice", m_totalMoleScale); plogendl(); } #endif for (size_t i = 0; i < m_numSpeciesTot; ++i) { if (m_speciesUnknownType[i] != VCS_SPECIES_TYPE_INTERFACIALVOLTAGE) { m_molNumSpecies_old[i] *= (1.0 / m_totalMoleScale); } } for (size_t i = 0; i < m_numElemConstraints; ++i) { m_elemAbundancesGoal[i] *= (1.0 / m_totalMoleScale); } for (size_t iph = 0; iph < m_numPhases; iph++) { TPhInertMoles[iph] *= (1.0 / m_totalMoleScale); if (TPhInertMoles[iph] != 0.0) { vcs_VolPhase *vphase = m_VolPhaseList[iph]; vphase->setTotalMolesInert(TPhInertMoles[iph]); } } } vcs_tmoles(); } } }
int VCS_SOLVE::vcs_prep(int printLvl) { int retn = VCS_SUCCESS; m_debug_print_lvl = printLvl; // Calculate the Single Species status of phases // Also calculate the number of species per phase vcs_SSPhase(); // Set an initial estimate for the number of noncomponent species equal to // nspecies - nelements. This may be changed below if (m_nelem > m_nsp) { m_numRxnTot = 0; } else { m_numRxnTot = m_nsp - m_nelem; } m_numRxnRdc = m_numRxnTot; m_numSpeciesRdc = m_nsp; for (size_t i = 0; i < m_numRxnRdc; ++i) { m_indexRxnToSpecies[i] = m_nelem + i; } for (size_t kspec = 0; kspec < m_nsp; ++kspec) { size_t pID = m_phaseID[kspec]; size_t spPhIndex = m_speciesLocalPhaseIndex[kspec]; vcs_VolPhase* vPhase = m_VolPhaseList[pID].get(); vcs_SpeciesProperties* spProp = vPhase->speciesProperty(spPhIndex); double sz = 0.0; size_t eSize = spProp->FormulaMatrixCol.size(); for (size_t e = 0; e < eSize; e++) { sz += fabs(spProp->FormulaMatrixCol[e]); } if (sz > 0.0) { m_spSize[kspec] = sz; } else { m_spSize[kspec] = 1.0; } } // DETERMINE THE NUMBER OF COMPONENTS // // Obtain a valid estimate of the mole fraction. This will be used as an // initial ordering vector for prioritizing which species are defined as // components. // // If a mole number estimate was supplied from the input file, use that mole // number estimate. // // If a solution estimate wasn't supplied from the input file, supply an // initial estimate for the mole fractions based on the relative reverse // ordering of the chemical potentials. // // For voltage unknowns, set these to zero for the moment. double test = -1.0e-10; bool modifiedSoln = false; if (m_doEstimateEquil < 0) { double sum = 0.0; for (size_t kspec = 0; kspec < m_nsp; ++kspec) { if (m_speciesUnknownType[kspec] == VCS_SPECIES_TYPE_MOLNUM) { sum += fabs(m_molNumSpecies_old[kspec]); } } if (fabs(sum) < 1.0E-6) { modifiedSoln = true; double pres = (m_pressurePA <= 0.0) ? 1.01325E5 : m_pressurePA; retn = vcs_evalSS_TP(0, 0, m_temperature, pres); for (size_t kspec = 0; kspec < m_nsp; ++kspec) { if (m_speciesUnknownType[kspec] == VCS_SPECIES_TYPE_MOLNUM) { m_molNumSpecies_old[kspec] = - m_SSfeSpecies[kspec]; } else { m_molNumSpecies_old[kspec] = 0.0; } } } test = -1.0e20; } // NC = number of components is in the vcs.h common block. This call to // BASOPT doesn't calculate the stoichiometric reaction matrix. vector_fp awSpace(m_nsp + (m_nelem + 2)*(m_nelem), 0.0); double* aw = &awSpace[0]; if (aw == NULL) { plogf("vcs_prep_oneTime: failed to get memory: global bailout\n"); return VCS_NOMEMORY; } double* sa = aw + m_nsp; double* sm = sa + m_nelem; double* ss = sm + m_nelem * m_nelem; bool conv; retn = vcs_basopt(true, aw, sa, sm, ss, test, &conv); if (retn != VCS_SUCCESS) { plogf("vcs_prep_oneTime:"); plogf(" Determination of number of components failed: %d\n", retn); plogf(" Global Bailout!\n"); return retn; } if (m_nsp >= m_numComponents) { m_numRxnTot = m_numRxnRdc = m_nsp - m_numComponents; for (size_t i = 0; i < m_numRxnRdc; ++i) { m_indexRxnToSpecies[i] = m_numComponents + i; } } else { m_numRxnTot = m_numRxnRdc = 0; } // The elements might need to be rearranged. awSpace.resize(m_nelem + (m_nelem + 2)*m_nelem, 0.0); aw = &awSpace[0]; sa = aw + m_nelem; sm = sa + m_nelem; ss = sm + m_nelem * m_nelem; retn = vcs_elem_rearrange(aw, sa, sm, ss); if (retn != VCS_SUCCESS) { plogf("vcs_prep_oneTime:"); plogf(" Determination of element reordering failed: %d\n", retn); plogf(" Global Bailout!\n"); return retn; } // If we mucked up the solution unknowns because they were all // zero to start with, set them back to zero here if (modifiedSoln) { for (size_t kspec = 0; kspec < m_nsp; ++kspec) { m_molNumSpecies_old[kspec] = 0.0; } } // Initialize various arrays in the data to zero m_feSpecies_old.assign(m_feSpecies_old.size(), 0.0); m_feSpecies_new.assign(m_feSpecies_new.size(), 0.0); m_molNumSpecies_new.assign(m_molNumSpecies_new.size(), 0.0); m_deltaMolNumPhase.zero(); m_phaseParticipation.zero(); m_deltaPhaseMoles.assign(m_deltaPhaseMoles.size(), 0.0); m_tPhaseMoles_new.assign(m_tPhaseMoles_new.size(), 0.0); // Calculate the total number of moles in all phases. vcs_tmoles(); // Check to see if the current problem is well posed. double sum = 0.0; for (size_t e = 0; e < m_nelem; e++) { sum += m_mix->elementMoles(e); } if (sum < 1.0E-20) { // Check to see if the current problem is well posed. plogf("vcs has determined the problem is not well posed: Bailing\n"); return VCS_PUB_BAD; } return VCS_SUCCESS; }
/************************************************************************** * * vcs_report: * * Print out a report on the state of the equilibrium problem to * standard output. * This prints out the current contents of the VCS_SOLVE class, V. * The "old" solution vector is printed out. ***************************************************************************/ int VCS_SOLVE::vcs_report(int iconv) { bool printActualMoles = true, inertYes = false; size_t i, j, l, k, kspec; size_t nspecies = m_numSpeciesTot; double g; char originalUnitsState = m_unitsState; std::vector<size_t> sortindex(nspecies,0); std::vector<double> xy(nspecies,0.0); /* ************************************************************** */ /* **** SORT DEPENDENT SPECIES IN DECREASING ORDER OF MOLES ***** */ /* ************************************************************** */ for (i = 0; i < nspecies; ++i) { sortindex[i] = i; xy[i] = m_molNumSpecies_old[i]; } /* * Sort the XY vector, the mole fraction vector, * and the sort index vector, sortindex, according to * the magnitude of the mole fraction vector. */ for (l = m_numComponents; l < m_numSpeciesRdc; ++l) { k = vcs_optMax(VCS_DATA_PTR(xy), 0, l, m_numSpeciesRdc); if (k != l) { vcsUtil_dsw(VCS_DATA_PTR(xy), k, l); vcsUtil_ssw(VCS_DATA_PTR(sortindex), k, l); } } /* * Decide whether we have to nondimensionalize the equations. * -> For the printouts from this routine, we will use nondimensional * representations. This may be expanded in the future. */ if (m_unitsState == VCS_DIMENSIONAL_G) { vcs_nondim_TP(); } double molScale = 1.0; if (printActualMoles) { molScale = m_totalMoleScale; } vcs_setFlagsVolPhases(false, VCS_STATECALC_OLD); vcs_dfe(VCS_STATECALC_OLD, 0, 0, m_numSpeciesTot); /* ******************************************************** */ /* *** PRINT OUT RESULTS ********************************** */ /* ******************************************************** */ plogf("\n\n\n\n"); print_line("-", 80); print_line("-", 80); plogf("\t\t VCS_TP REPORT\n"); print_line("-", 80); print_line("-", 80); if (iconv < 0) { plogf(" ERROR: CONVERGENCE CRITERION NOT SATISFIED.\n"); } else if (iconv == 1) { plogf(" RANGE SPACE ERROR: Equilibrium Found but not all Element Abundances are Satisfied\n"); } /* * Calculate some quantities that may need updating */ vcs_tmoles(); m_totalVol = vcs_VolTotal(m_temperature, m_pressurePA, VCS_DATA_PTR(m_molNumSpecies_old), VCS_DATA_PTR(m_PMVolumeSpecies)); plogf("\t\tTemperature = %15.2g Kelvin\n", m_temperature); plogf("\t\tPressure = %15.5g Pa \n", m_pressurePA); plogf("\t\ttotal Volume = %15.5g m**3\n", m_totalVol * molScale); if (!printActualMoles) { plogf("\t\tMole Scale = %15.5g kmol (all mole numbers and volumes are scaled by this value)\n", molScale); } /* * -------- TABLE OF SPECIES IN DECREASING MOLE NUMBERS -------------- */ plogf("\n\n"); print_line("-", 80); plogf(" Species Equilibrium kmoles "); plogf("Mole Fraction ChemPot/RT SpecUnkType\n"); print_line("-", 80); for (i = 0; i < m_numComponents; ++i) { plogf(" %-12.12s", m_speciesName[i].c_str()); print_space(13); plogf("%14.7E %14.7E %12.4E", m_molNumSpecies_old[i] * molScale, m_molNumSpecies_new[i] * molScale, m_feSpecies_old[i]); plogf(" %3d", m_speciesUnknownType[i]); plogf("\n"); } for (i = m_numComponents; i < m_numSpeciesRdc; ++i) { l = sortindex[i]; plogf(" %-12.12s", m_speciesName[l].c_str()); print_space(13); if (m_speciesUnknownType[l] == VCS_SPECIES_TYPE_MOLNUM) { plogf("%14.7E %14.7E %12.4E", m_molNumSpecies_old[l] * molScale, m_molNumSpecies_new[l] * molScale, m_feSpecies_old[l]); plogf(" KMolNum "); } else if (m_speciesUnknownType[l] == VCS_SPECIES_TYPE_INTERFACIALVOLTAGE) { plogf(" NA %14.7E %12.4E", 1.0, m_feSpecies_old[l]); plogf(" Voltage = %14.7E", m_molNumSpecies_old[l] * molScale); } else { plogf("we have a problem\n"); exit(EXIT_FAILURE); } plogf("\n"); } for (i = 0; i < m_numPhases; i++) { if (TPhInertMoles[i] > 0.0) { inertYes = true; if (i == 0) { plogf(" Inert Gas Species "); } else { plogf(" Inert Species in phase %16s ", (m_VolPhaseList[i])->PhaseName.c_str()); } plogf("%14.7E %14.7E %12.4E\n", TPhInertMoles[i] * molScale, TPhInertMoles[i] / m_tPhaseMoles_old[i], 0.0); } } if (m_numSpeciesRdc != nspecies) { plogf("\n SPECIES WITH LESS THAN 1.0E-32 KMOLES:\n\n"); for (kspec = m_numSpeciesRdc; kspec < nspecies; ++kspec) { plogf(" %-12.12s", m_speciesName[kspec].c_str()); // Note m_deltaGRxn_new[] stores in kspec slot not irxn slot, after solve plogf(" %14.7E %14.7E %12.4E", m_molNumSpecies_old[kspec]*molScale, m_molNumSpecies_new[kspec]*molScale, m_deltaGRxn_new[kspec]); if (m_speciesUnknownType[i] == VCS_SPECIES_TYPE_MOLNUM) { plogf(" KMol_Num"); } else if (m_speciesUnknownType[i] == VCS_SPECIES_TYPE_INTERFACIALVOLTAGE) { plogf(" Voltage"); } else { plogf(" Unknown"); } plogf("\n"); } } print_line("-", 80); plogf("\n"); /* * ---------- TABLE OF SPECIES FORMATION REACTIONS ------------------ */ plogf("\n"); print_line("-", m_numComponents*10 + 45); plogf(" |ComponentID|"); for (j = 0; j < m_numComponents; j++) { plogf(" %3d", j); } plogf(" | |\n"); plogf(" | Components|"); for (j = 0; j < m_numComponents; j++) { plogf(" %10.10s", m_speciesName[j].c_str()); } plogf(" | |\n"); plogf(" NonComponent | Moles |"); for (j = 0; j < m_numComponents; j++) { plogf(" %10.3g", m_molNumSpecies_old[j] * molScale); } plogf(" | DG/RT Rxn |\n"); print_line("-", m_numComponents*10 + 45); for (size_t irxn = 0; irxn < m_numRxnTot; irxn++) { size_t kspec = m_indexRxnToSpecies[irxn]; plogf(" %3d ", kspec); plogf("%-10.10s", m_speciesName[kspec].c_str()); plogf("|%10.3g |", m_molNumSpecies_old[kspec]*molScale); for (j = 0; j < m_numComponents; j++) { plogf(" %6.2f", m_stoichCoeffRxnMatrix[irxn][j]); } plogf(" |%10.3g |", m_deltaGRxn_new[irxn]); plogf("\n"); } print_line("-", m_numComponents*10 + 45); plogf("\n"); /* * ------------------ TABLE OF PHASE INFORMATION --------------------- */ std::vector<double> gaPhase(m_numElemConstraints, 0.0); std::vector<double> gaTPhase(m_numElemConstraints, 0.0); double totalMoles = 0.0; double gibbsPhase = 0.0; double gibbsTotal = 0.0; plogf("\n\n"); plogf("\n"); print_line("-", m_numElemConstraints*10 + 58); plogf(" | ElementID |"); for (j = 0; j < m_numElemConstraints; j++) { plogf(" %3d", j); } plogf(" | |\n"); plogf(" | Element |"); for (j = 0; j < m_numElemConstraints; j++) { plogf(" %10.10s", (m_elementName[j]).c_str()); } plogf(" | |\n"); plogf(" PhaseName |KMolTarget |"); for (j = 0; j < m_numElemConstraints; j++) { plogf(" %10.3g", m_elemAbundancesGoal[j]); } plogf(" | Gibbs Total |\n"); print_line("-", m_numElemConstraints*10 + 58); for (size_t iphase = 0; iphase < m_numPhases; iphase++) { plogf(" %3d ", iphase); vcs_VolPhase *VPhase = m_VolPhaseList[iphase]; plogf("%-12.12s |",VPhase->PhaseName.c_str()); plogf("%10.3e |", m_tPhaseMoles_old[iphase]*molScale); totalMoles += m_tPhaseMoles_old[iphase]; if (m_tPhaseMoles_old[iphase] != VPhase->totalMoles()) { if (! vcs_doubleEqual(m_tPhaseMoles_old[iphase], VPhase->totalMoles())) { plogf("We have a problem\n"); exit(EXIT_FAILURE); } } vcs_elabPhase(iphase, VCS_DATA_PTR(gaPhase)); for (j = 0; j < m_numElemConstraints; j++) { plogf(" %10.3g", gaPhase[j]); gaTPhase[j] += gaPhase[j]; } gibbsPhase = vcs_GibbsPhase(iphase, VCS_DATA_PTR(m_molNumSpecies_old), VCS_DATA_PTR(m_feSpecies_old)); gibbsTotal += gibbsPhase; plogf(" | %18.11E |\n", gibbsPhase); } print_line("-", m_numElemConstraints*10 + 58); plogf(" TOTAL |%10.3e |", totalMoles); for (j = 0; j < m_numElemConstraints; j++) { plogf(" %10.3g", gaTPhase[j]); } plogf(" | %18.11E |\n", gibbsTotal); print_line("-", m_numElemConstraints*10 + 58); plogf("\n"); /* * ----------- GLOBAL SATISFACTION INFORMATION ----------------------- */ /* * Calculate the total dimensionless Gibbs Free Energy * -> Inert species are handled as if they had a standard free * energy of zero */ g = vcs_Total_Gibbs(VCS_DATA_PTR(m_molNumSpecies_old), VCS_DATA_PTR(m_feSpecies_old), VCS_DATA_PTR(m_tPhaseMoles_old)); plogf("\n\tTotal Dimensionless Gibbs Free Energy = G/RT = %15.7E\n", g); if (inertYes) plogf("\t\t(Inert species have standard free energy of zero)\n"); plogf("\nElemental Abundances (kmol): "); plogf(" Actual Target Type ElActive\n"); for (i = 0; i < m_numElemConstraints; ++i) { print_space(26); plogf("%-2.2s", (m_elementName[i]).c_str()); plogf("%20.12E %20.12E", m_elemAbundances[i]*molScale, m_elemAbundancesGoal[i]*molScale); plogf(" %3d %3d\n", m_elType[i], m_elementActive[i]); } plogf("\n"); /* * ------------------ TABLE OF SPECIES CHEM POTS --------------------- */ plogf("\n"); print_line("-", 93); plogf("Chemical Potentials of the Species: (dimensionless)\n"); double rt = vcs_nondimMult_TP(m_VCS_UnitsFormat, m_temperature); plogf("\t\t(RT = %g ", rt); vcs_printChemPotUnits(m_VCS_UnitsFormat); plogf(")\n"); plogf(" Name TKMoles StandStateChemPot " " ln(AC) ln(X_i) | F z_i phi | ChemPot | (-lnMnaught)"); plogf("| (MolNum ChemPot)|"); plogf("\n"); print_line("-", 147); for (i = 0; i < nspecies; ++i) { l = sortindex[i]; size_t pid = m_phaseID[l]; plogf(" %-12.12s", m_speciesName[l].c_str()); plogf(" %14.7E ", m_molNumSpecies_old[l]*molScale); plogf("%14.7E ", m_SSfeSpecies[l]); plogf("%14.7E ", log(m_actCoeffSpecies_old[l])); double tpmoles = m_tPhaseMoles_old[pid]; double phi = m_phasePhi[pid]; double eContrib = phi * m_chargeSpecies[l] * m_Faraday_dim; double lx = 0.0; if (m_speciesUnknownType[l] == VCS_SPECIES_TYPE_INTERFACIALVOLTAGE) { lx = 0.0; } else { if (tpmoles > 0.0 && m_molNumSpecies_old[l] > 0.0) { double tmp = MAX(VCS_DELETE_MINORSPECIES_CUTOFF, m_molNumSpecies_old[l]); lx = log(tmp) - log(tpmoles); } else { lx = m_feSpecies_old[l] - m_SSfeSpecies[l] - log(m_actCoeffSpecies_old[l]) + m_lnMnaughtSpecies[l]; } } plogf("%14.7E |", lx); plogf("%14.7E | ", eContrib); double tmp = m_SSfeSpecies[l] + log(m_actCoeffSpecies_old[l]) + lx - m_lnMnaughtSpecies[l] + eContrib; if (fabs(m_feSpecies_old[l] - tmp) > 1.0E-7) { plogf("\n\t\twe have a problem - doesn't add up\n"); exit(EXIT_FAILURE); } plogf(" %12.4E |", m_feSpecies_old[l]); if( m_lnMnaughtSpecies[l] != 0.0) { plogf("(%11.5E)", - m_lnMnaughtSpecies[l]); } else { plogf(" "); } plogf("| %20.9E |", m_feSpecies_old[l] * m_molNumSpecies_old[l] * molScale); plogf("\n"); } for (i = 0; i < 125; i++) plogf(" "); plogf(" %20.9E\n", g); print_line("-", 147); /* * ------------- TABLE OF SOLUTION COUNTERS -------------------------- */ plogf("\n"); plogf("\nCounters: Iterations Time (seconds)\n"); if (m_timing_print_lvl > 0) { plogf(" vcs_basopt: %5d %11.5E\n", m_VCount->Basis_Opts, m_VCount->Time_basopt); plogf(" vcs_TP: %5d %11.5E\n", m_VCount->Its, m_VCount->Time_vcs_TP); } else { plogf(" vcs_basopt: %5d %11s\n", m_VCount->Basis_Opts," NA "); plogf(" vcs_TP: %5d %11s\n", m_VCount->Its," NA " ); } print_line("-", 80); print_line("-", 80); /* * Set the Units state of the system back to where it was when we * entered the program. */ if (originalUnitsState != m_unitsState) { if (originalUnitsState == VCS_DIMENSIONAL_G ) vcs_redim_TP(); else vcs_nondim_TP(); } /* * Return a successful completion flag */ return VCS_SUCCESS; } /* vcs_report() ************************************************************/
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(); } } }
/* * The VCS_PUB structure is returned to the user. * * @param pub Pointer to VCS_PROB that will get the results of the * equilibrium calculation transfered to it. */ int VCS_SOLVE::vcs_prob_update(VCS_PROB* pub) { size_t k1 = 0; vcs_tmoles(); m_totalVol = vcs_VolTotal(m_temperature, m_pressurePA, VCS_DATA_PTR(m_molNumSpecies_old), VCS_DATA_PTR(m_PMVolumeSpecies)); for (size_t i = 0; i < m_numSpeciesTot; ++i) { /* * Find the index of I in the index vector, m_speciesIndexVector[]. * Call it K1 and continue. */ for (size_t j = 0; j < m_numSpeciesTot; ++j) { k1 = j; if (m_speciesMapIndex[j] == i) { break; } } /* * - Switch the species data back from K1 into I */ if (pub->SpeciesUnknownType[i] != VCS_SPECIES_TYPE_INTERFACIALVOLTAGE) { pub->w[i] = m_molNumSpecies_old[k1]; } else { pub->w[i] = 0.0; // plogf("voltage species = %g\n", m_molNumSpecies_old[k1]); } //pub->mf[i] = m_molNumSpecies_new[k1]; pub->m_gibbsSpecies[i] = m_feSpecies_old[k1]; pub->VolPM[i] = m_PMVolumeSpecies[k1]; } pub->T = m_temperature; pub->PresPA = m_pressurePA; pub->Vol = m_totalVol; size_t kT = 0; for (size_t iph = 0; iph < pub->NPhase; iph++) { vcs_VolPhase* pubPhase = pub->VPhaseList[iph]; vcs_VolPhase* vPhase = m_VolPhaseList[iph]; pubPhase->setTotalMolesInert(vPhase->totalMolesInert()); pubPhase->setTotalMoles(vPhase->totalMoles()); pubPhase->setElectricPotential(vPhase->electricPotential()); double sumMoles = pubPhase->totalMolesInert(); pubPhase->setMoleFractionsState(vPhase->totalMoles(), VCS_DATA_PTR(vPhase->moleFractions()), VCS_STATECALC_TMP); const std::vector<double> & mfVector = pubPhase->moleFractions(); for (size_t k = 0; k < pubPhase->nSpecies(); k++) { kT = pubPhase->spGlobalIndexVCS(k); pub->mf[kT] = mfVector[k]; if (pubPhase->phiVarIndex() == k) { k1 = vPhase->spGlobalIndexVCS(k); double tmp = m_molNumSpecies_old[k1]; if (! vcs_doubleEqual(pubPhase->electricPotential() , tmp)) { plogf("We have an inconsistency in voltage, %g, %g\n", pubPhase->electricPotential(), tmp); exit(EXIT_FAILURE); } } if (! vcs_doubleEqual(pub->mf[kT], vPhase->molefraction(k))) { plogf("We have an inconsistency in mole fraction, %g, %g\n", pub->mf[kT], vPhase->molefraction(k)); exit(EXIT_FAILURE); } if (pubPhase->speciesUnknownType(k) != VCS_SPECIES_TYPE_INTERFACIALVOLTAGE) { sumMoles += pub->w[kT]; } } if (! vcs_doubleEqual(sumMoles, vPhase->totalMoles())) { plogf("We have an inconsistency in total moles, %g %g\n", sumMoles, pubPhase->totalMoles()); exit(EXIT_FAILURE); } } pub->m_Iterations = m_VCount->Its; pub->m_NumBasisOptimizations = m_VCount->Basis_Opts; return VCS_SUCCESS; }
/* * Use the contents of the VCS_PROB to specify the contents of the * private data, VCS_SOLVE. * * It's assumed we are solving the same problem. * * @param pub Pointer to VCS_PROdB that will be used to * initialize the current equilibrium problem */ int VCS_SOLVE::vcs_prob_specify(const VCS_PROB* pub) { size_t kspec, k, i, j, iph; string yo("vcs_prob_specify ERROR: "); int retn = VCS_SUCCESS; bool status_change = false; m_temperature = pub->T; m_pressurePA = pub->PresPA; m_VCS_UnitsFormat = pub->m_VCS_UnitsFormat; m_doEstimateEquil = pub->iest; m_totalVol = pub->Vol; m_tolmaj = pub->tolmaj; m_tolmin = pub->tolmin; m_tolmaj2 = 0.01 * m_tolmaj; m_tolmin2 = 0.01 * m_tolmin; for (kspec = 0; kspec < m_numSpeciesTot; ++kspec) { k = m_speciesMapIndex[kspec]; m_molNumSpecies_old[kspec] = pub->w[k]; m_molNumSpecies_new[kspec] = pub->mf[k]; m_feSpecies_old[kspec] = pub->m_gibbsSpecies[k]; } /* * Transfer the element abundance goals to the solve object */ for (i = 0; i < m_numElemConstraints; i++) { j = m_elementMapIndex[i]; m_elemAbundancesGoal[i] = pub->gai[j]; } /* * Try to do the best job at guessing at the title */ if (pub->Title.size() == 0) { if (m_title.size() == 0) { m_title = "Unspecified Problem Title"; } } else { m_title = pub->Title; } /* * Copy over the phase information. * -> For each entry in the phase structure, determine * if that entry can change from its initial value * Either copy over the new value or create an error * condition. */ for (iph = 0; iph < m_numPhases; iph++) { vcs_VolPhase* vPhase = m_VolPhaseList[iph]; vcs_VolPhase* pub_phase_ptr = pub->VPhaseList[iph]; if (vPhase->VP_ID_ != pub_phase_ptr->VP_ID_) { plogf("%sPhase numbers have changed:%d %d\n", yo.c_str(), vPhase->VP_ID_, pub_phase_ptr->VP_ID_); retn = VCS_PUB_BAD; } if (vPhase->m_singleSpecies != pub_phase_ptr->m_singleSpecies) { plogf("%sSingleSpecies value have changed:%d %d\n", yo.c_str(), vPhase->m_singleSpecies, pub_phase_ptr->m_singleSpecies); retn = VCS_PUB_BAD; } if (vPhase->m_gasPhase != pub_phase_ptr->m_gasPhase) { plogf("%sGasPhase value have changed:%d %d\n", yo.c_str(), vPhase->m_gasPhase, pub_phase_ptr->m_gasPhase); retn = VCS_PUB_BAD; } vPhase->m_eqnState = pub_phase_ptr->m_eqnState; if (vPhase->nSpecies() != pub_phase_ptr->nSpecies()) { plogf("%sNVolSpecies value have changed:%d %d\n", yo.c_str(), vPhase->nSpecies(), pub_phase_ptr->nSpecies()); retn = VCS_PUB_BAD; } if (vPhase->PhaseName != pub_phase_ptr->PhaseName) { plogf("%sPhaseName value have changed:%s %s\n", yo.c_str(), vPhase->PhaseName.c_str(), pub_phase_ptr->PhaseName.c_str()); retn = VCS_PUB_BAD; } if (vPhase->totalMolesInert() != pub_phase_ptr->totalMolesInert()) { status_change = true; } /* * Copy over the number of inert moles if it has changed. */ TPhInertMoles[iph] = pub_phase_ptr->totalMolesInert(); vPhase->setTotalMolesInert(pub_phase_ptr->totalMolesInert()); if (TPhInertMoles[iph] > 0.0) { vPhase->setExistence(2); vPhase->m_singleSpecies = false; } /* * Copy over the interfacial potential */ double phi = pub_phase_ptr->electricPotential(); vPhase->setElectricPotential(phi); } if (status_change) { vcs_SSPhase(); } /* * Calculate the total number of moles in all phases. */ vcs_tmoles(); return retn; }