/** * @file vcs_inest.cpp * Methods for obtaining a good initial guess */ /* $Author$ * $Date$ * $Revision$ */ /* * Copywrite (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 #include #include #include "vcs_solve.h" #include "vcs_internal.h" #include "vcs_VolPhase.h" namespace VCSnonideal { static char pprefix[20] = " --- vcs_inest: "; /*****************************************************************************/ /*****************************************************************************/ /*****************************************************************************/ void VCS_SOLVE::inest(double *aw, double *sa, double *sm, double *ss, double test) /************************************************************************** * * inest: * * Estimates equilibrium compositions. * Algorithm covered in a section of Smith and Missen's Book. * * Linear programming module is based on using dbolm. ***************************************************************************/ { int conv, k, lt, ikl, kspec, iph, irxn; double s, s1, xl, par; int finished; int nspecies = m_numSpeciesTot; int nrxn = m_numRxnTot; vcs_VolPhase *Vphase = 0; double *molNum = VCS_DATA_PTR(soln); double TMolesMultiphase; double *xtphMax = VCS_DATA_PTR(TmpPhase); double *xtphMin = VCS_DATA_PTR(TmpPhase2); ikl = 0; lt = 0; /* * 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. */ #ifdef ALTLINPROG vcs_setMolesLinProg(); #else int j, jj; std::vector ax(m_numElemConstraints*nspecies, 0.0); std::vector bb(m_numElemConstraints, 0.0); std::vector cc(nspecies, 0.0); int neActive = 0; jj = 0; for (j = 0; j < m_numElemConstraints; j++) { if (ElActive[j]) { neActive++; bb[jj] = gai[j]; jj++; } } for (kspec = 0; kspec < nspecies; ++kspec) { cc[kspec] = -ff[kspec]; jj = 0; for (j = 0; j < m_numElemConstraints; ++j) { if (ElActive[j]) { ax[jj + kspec * neActive] = FormulaMatrix[j][kspec]; jj++; } } } linprogmax(molNum, VCS_DATA_PTR(cc), VCS_DATA_PTR(ax), VCS_DATA_PTR(bb), neActive, nspecies, neActive); #endif #ifdef DEBUG_MODE if (vcs_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 (kspec = 0; kspec < nspecies; ++kspec) { plogf("%s ", pprefix); plogf("%-12.12s", SpName[kspec].c_str()); plogf(" %15.5g %12.3g\n", molNum[kspec], -ff[kspec]); } plogf("%s Element Abundance Agreement returned from linear " "programming (vcs_inest initial guess):\n", pprefix); plogf("%s Element Goal Actual\n", pprefix); int jj = 0; for (int j = 0; j < m_numElemConstraints; j++) { if (ElActive[j]) { double tmp = 0.0; for (kspec = 0; kspec < nspecies; ++kspec) { tmp += FormulaMatrix[j][kspec] * molNum[kspec]; } plogf("%s ", pprefix); plogf(" %-9.9s", (ElName[j]).c_str()); plogf(" %12.3g %12.3g\n", gai[j], tmp); jj++; } } } #endif /* * Make sure all species have positive definite mole numbers * Set voltages to zero for now, until we figure out what to do */ vcs_dzero(VCS_DATA_PTR(ds), nspecies); for (kspec = 0; kspec < nspecies; ++kspec) { iph = PhaseID[kspec]; Vphase = VPhaseList[iph]; if (SpeciesUnknownType[kspec] != VCS_SPECIES_TYPE_INTERFACIALVOLTAGE) { if (molNum[kspec] <= 0.0) { /* * HKM Should eventually include logic here for non SS phases */ if (!SSPhase[kspec]) { molNum[kspec] = 1.0e-30; } } } else { molNum[kspec] = 0.0; } if (molNum[kspec] > 0.0) { if (Vphase->Existence == 0) { Vphase->Existence = 1; } } else if (SSPhase[kspec]) { Vphase->Existence = 0; } } /* * Now find the optimized basis that spans the stoichiometric * coefficient matrix */ (void) vcs_basopt(FALSE, aw, sa, sm, ss, test, &conv); /* ***************************************************************** */ /* **** CALCULATE TOTAL GASEOUS AND LIQUID MOLES, ****************** */ /* **** CHEMICAL POTENTIALS OF BASIS ****************** */ /* ***************************************************************** */ /* * Calculate TMoles and TPhMoles[] */ vcs_tmoles(); /* * TPhMoles1[] will consist of just the component moles */ for (iph = 0; iph < NPhase; iph++) { TPhMoles1[iph] = TPhInertMoles[iph] + 1.0E-20; } for (kspec = 0; kspec < m_numComponents; ++kspec) { if (SpeciesUnknownType[kspec] == VCS_SPECIES_TYPE_MOLNUM) { TPhMoles1[PhaseID[kspec]] += molNum[kspec]; } } TMolesMultiphase = 0.0; for (iph = 0; iph < NPhase; iph++) { if (! VPhaseList[iph]->SingleSpecies) { TMolesMultiphase += TPhMoles1[iph]; } } vcs_dcopy(VCS_DATA_PTR(wt), molNum, nspecies); for (kspec = 0; kspec < m_numComponents; ++kspec) { if (SpeciesUnknownType[kspec] != VCS_SPECIES_TYPE_MOLNUM) { wt[kspec] = 0.0; } } vcs_dcopy(VCS_DATA_PTR(m_gibbsSpecies), VCS_DATA_PTR(ff), nspecies); for (kspec = 0; kspec < m_numComponents; ++kspec) { if (SpeciesUnknownType[kspec] == VCS_SPECIES_TYPE_MOLNUM) { if (! SSPhase[kspec]) { iph = PhaseID[kspec]; m_gibbsSpecies[kspec] += log(wt[kspec] / TPhMoles[iph]); } } else { wt[kspec] = 0.0; } } vcs_deltag(0, true); #ifdef DEBUG_MODE if (vcs_debug_print_lvl >= 2) { for (kspec = 0; kspec < nspecies; ++kspec) { plogf("%s", pprefix); plogf("%-12.12s", SpName[kspec].c_str()); if (kspec < m_numComponents) plogf("fe* = %15.5g ff = %15.5g\n", m_gibbsSpecies[kspec], ff[kspec]); else plogf("fe* = %15.5g ff = %15.5g dg* = %15.5g\n", m_gibbsSpecies[kspec], ff[kspec], dg[kspec-m_numComponents]); } } #endif /* ********************************************************** */ /* **** ESTIMATE REACTION ADJUSTMENTS *********************** */ /* ********************************************************** */ vcs_dzero(VCS_DATA_PTR(DelTPhMoles), NPhase); for (iph = 0; iph < NPhase; iph++) { xtphMax[iph] = log(TPhMoles1[iph] * 1.0E32); xtphMin[iph] = log(TPhMoles1[iph] * 1.0E-32); } for (irxn = 0; irxn < nrxn; ++irxn) { kspec = ir[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 (! SSPhase[kspec]) { iph = PhaseID[kspec]; if (dg[irxn] > xtphMax[iph]) dg[irxn] = 0.8 * xtphMax[iph]; if (dg[irxn] < xtphMin[iph]) dg[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. */ ds[kspec] = 0.5 * (TPhMoles1[iph] + TMolesMultiphase) * exp(-dg[irxn]); for (k = 0; k < m_numComponents; ++k) { ds[k] += sc[irxn][k] * ds[kspec]; } for (iph = 0; iph < NPhase; iph++) { DelTPhMoles[iph] += DnPhase[irxn][iph] * ds[kspec]; } } } #ifdef DEBUG_MODE if (vcs_debug_print_lvl >= 2) { for (kspec = 0; kspec < nspecies; ++kspec) { if (SpeciesUnknownType[kspec] != VCS_SPECIES_TYPE_INTERFACIALVOLTAGE) { plogf("%sdirection (", pprefix); plogf("%-12.12s", SpName[kspec].c_str()); plogf(") = %g", ds[kspec]); if (SSPhase[kspec]) { if (molNum[kspec] > 0.0) { plogf(" (ssPhase exists at w = %g moles)", molNum[kspec]); } else { plogf(" (ssPhase doesn't exist -> stability not checked)"); } } plogf("\n"); } } } #endif /* *********************************************************** */ /* **** KEEP COMPONENT SPECIES POSITIVE ********************** */ /* *********************************************************** */ par = 0.5; for (kspec = 0; kspec < m_numComponents; ++kspec) { if (SpeciesUnknownType[kspec] != VCS_SPECIES_TYPE_INTERFACIALVOLTAGE) { if (par < -ds[kspec] / wt[kspec]) par = -ds[kspec] / wt[kspec]; } } par = 1. / par; if (par <= 1.0 && par > 0.0) { par *= 0.8; } else { par = 1.0; } /* ******************************************** */ /* **** CALCULATE NEW MOLE NUMBERS ************ */ /* ******************************************** */ finished = FALSE; do { for (kspec = 0; kspec < m_numComponents; ++kspec) { if (SpeciesUnknownType[kspec] != VCS_SPECIES_TYPE_INTERFACIALVOLTAGE) { molNum[kspec] = wt[kspec] + par * ds[kspec]; } else { ds[kspec] = 0.0; } } for (kspec = m_numComponents; kspec < nspecies; ++kspec) { if (SpeciesUnknownType[kspec] != VCS_SPECIES_TYPE_INTERFACIALVOLTAGE) { if (ds[kspec] != 0.0) molNum[kspec] = ds[kspec] * par; } } /* * We have a new w[] estimate, go get the * TMoles and TPhMoles[] values */ vcs_tmoles(); if (lt > 0) goto finished; /* ******************************************* */ /* **** CONVERGENCE FORCING SECTION ********** */ /* ******************************************* */ vcs_dfe(molNum, 0, 0, 0, nspecies); for (kspec = 0, s = 0.0; kspec < nspecies; ++kspec) { s += ds[kspec] * m_gibbsSpecies[kspec]; } if (s == 0.0) { finished = TRUE; continue; } if (s < 0.0) { if (ikl <= 0) { finished = TRUE; continue; } } /* ***************************************** */ /* *** TRY HALF STEP SIZE ****************** */ /* ***************************************** */ if (ikl <= 0) { s1 = s; par *= 0.5; ikl = 1; continue; } /* **************************************************** */ /* **** FIT PARABOLA THROUGH HALF AND FULL STEPS ****** */ /* **************************************************** */ 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; } while (!finished); finished: ; #ifdef DEBUG_MODE if (vcs_debug_print_lvl >= 2) { plogf("%s Final Mole Numbers produced by inest:\n", pprefix); plogf("%s SPECIES MOLE_NUMBER\n", pprefix); for (kspec = 0; kspec < nspecies; ++kspec) { plogf("%s ", pprefix); plogf("%-12.12s", SpName[kspec].c_str()); plogf(" %g\n", molNum[kspec]); } } #endif } /* inest() *****************************************************************/ /*****************************************************************************/ /*****************************************************************************/ /*****************************************************************************/ int VCS_SOLVE::vcs_inest_TP(void) /************************************************************************** * * vcs_inest_TP: * * Create an initial estimate of the solution to the thermodynamic * equilibrium problem. * * Return value: * * 0: successful initial guess * -1: Unsuccessful initial guess, the elemental abundances aren't * satisfied. ***************************************************************************/ { int retn = 0; double test; double tsecond = vcs_second(); test = -1.0E20; /* * Malloc temporary space for usage in this routine and in * subroutines * sm[ne*ne] * ss[ne] * sa[ne] * aw[m] */ std::vector sm(m_numElemConstraints*m_numElemConstraints, 0.0); std::vector ss(m_numElemConstraints, 0.0); std::vector sa(m_numElemConstraints, 0.0); std::vector aw(m_numSpeciesTot+ m_numElemConstraints, 0.0); /* * Go get the estimate of the solution */ #ifdef DEBUG_MODE if (vcs_debug_print_lvl >= 2) { plogf("%sGo find an initial estimate for the equilibrium problem\n", pprefix); } #endif inest(VCS_DATA_PTR(aw), VCS_DATA_PTR(sa), VCS_DATA_PTR(sm), VCS_DATA_PTR(ss), test); /* * Calculate the elemental abundances */ vcs_elab(); /* * If we still fail to achieve the correct elemental abundances, * try to fix the problem again by calling the main elemental abundances * fixer routine, used in the main program. What this does, is that it * attempts to tweak the mole numbers of the component species to * satisfy the element abundance constraints. * * Note: We won't do this unless we have to since it involves inverting * a matrix. */ int rangeCheck = vcs_elabcheck(1); if (!vcs_elabcheck(0)) { #ifdef DEBUG_MODE if (vcs_debug_print_lvl >= 2) { plogf("%sInitial guess failed element abundances\n", pprefix); plogf("%sCall vcs_elcorr to attempt fix\n", pprefix); } #endif vcs_elcorr(VCS_DATA_PTR(sm), VCS_DATA_PTR(aw)); rangeCheck = vcs_elabcheck(1); if (!vcs_elabcheck(0)) { plogf("%sInitial guess still fails element abundance equations\n", pprefix); plogf("%s - Inability to ever satisfy element abundance " "constraints is probable\n", pprefix); retn = -1; } else { #ifdef DEBUG_MODE if (vcs_debug_print_lvl >= 2) { if (rangeCheck) { plogf("%sInitial guess now satisfies element abundances\n", pprefix); } else { plogf("%sElement Abundances RANGE ERROR\n", pprefix); plogf("%s - Initial guess satisfies NC=%d element abundances, " "BUT not NE=%d element abundances\n", pprefix, m_numComponents, m_numElemConstraints); } } #endif } } else { #ifdef DEBUG_MODE if (vcs_debug_print_lvl >= 2) { if (rangeCheck) { plogf("%sInitial guess satisfies element abundances\n", pprefix); } else { plogf("%sElement Abundances RANGE ERROR\n", pprefix); plogf("%s - Initial guess satisfies NC=%d element abundances, " "BUT not NE=%d element abundances\n", pprefix, m_numComponents, m_numElemConstraints); } } #endif } #ifdef DEBUG_MODE if (vcs_debug_print_lvl >= 2) { plogf("%sTotal Dimensionless Gibbs Free Energy = %15.7E\n", pprefix, vcs_Total_Gibbs(VCS_DATA_PTR(soln), VCS_DATA_PTR(m_gibbsSpecies), VCS_DATA_PTR(TPhMoles))); } #endif /* * Free malloced memory */ tsecond = vcs_second() - tsecond; m_VCount->T_Time_inest += tsecond; (m_VCount->T_Calls_Inest)++; return retn; }/**** vcs_inest() ***********************************************************/ }