/*! * @file vcs_solve_TP.cpp Implementation file that contains the * main algorithm for finding an equilibrium */ /* * $Id$ */ /* * 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" #include "vcs_species_thermo.h" namespace VCSnonideal { /*****************************************************************************/ /************ Prototypes for static functions ********************************/ static void print_space(int num); #ifdef DEBUG //static double minor_alt_calc(int, int, int *, char *); #else //static double minor_alt_calc(int, int, int *); #endif #ifdef DEBUG # ifdef DEBUG_MORE static void prneav(void); static int prnfm(void); # endif #endif /*****************************************************************************/ /*****************************************************************************/ /*****************************************************************************/ #ifdef DEBUG void VCS_SOLVE::checkDelta1(double * const ds, double * const delTPhMoles, int kspec) { std::vector dchange(NPhase, 0.0); for (int k = 0; k < kspec; k++) { if (SpeciesUnknownType[k] != VCS_SPECIES_TYPE_INTERFACIALVOLTAGE) { int iph = PhaseID[k]; dchange[iph] += ds[k]; } } for (int iphase = 0; iphase < NPhase; iphase++) { double denom = MAX(TMoles, 1.0E-4); if (!vcs_doubleEqual(dchange[iphase]/denom, delTPhMoles[iphase]/denom)) { plogf("checkDelta1: we have found a problem\n"); exit(-1); } } } #endif /*****************************************************************************/ /*****************************************************************************/ /*****************************************************************************/ int VCS_SOLVE::vcs_solve_TP(int print_lvl, int printDetails, int maxit) /************************************************************************** * * NONIDEAL SYSTEM STOICHIOMETRIC EQUILBRIUM ALGORITHM USING VCS METHOD * ---------------------------------------------------------------------- * * Any number of single-species phases and two multi-species phases * can be handled by the present version (the latter is readily * modified). Phase 1 is nominally a gas, since alog(P) is added to the * standard chemical potential data. This can be overridden by * setting p = 1. Phase 2 is nominally a liquid, or any phase for * which the standard chemical potential data is independent of P. * Multi-species phases is deemed to be absent if nt .lt. 1.0E-10. * If multi-species phase is absent at equilibrium, dgRT value refers * to 1 - sigma(x(I)), where x(I) are virtual mole fractions at the * current equilibrium. * A linear programming routine must be provided for the initial * estimate of the equilibrium composition * * Input * print_lvl = 1 -> Print results to standard output * 0 -> don't report on anything * printDetails = 1 -> Print intermediate results. * MAXIT -> Maximum number of iterations for the algorithm * * Return Value * * solveFail = TRUE -> Failure to solve the current problem * FALSE -> Normal successful return. * * Some definitions of variables * * NL = Number of species in multiphase non-gaseous phases * M = Number of species * NC = Number of components. * NE = Number of elements * * E(J) = Char*2 name for the Jth element in the mechanism * * IT = Running count on the number of iterations of the algorithm. * ITL = Controls whether the FORCER subroutine is called. TRUE means * that FORCER is not called. * MajorSpeciesHaveConverged = Indicates convergence amongst * major species. * -> Also controls whether a new reaction adjustment is requested. * IM = IM is true if all noncomponent species are minor or nonexistent * NRUNS = number of problems to run * M = Number of species * NE = Number of elements * NS1 = number of single-species phases * NL1 = Number of phase2 species * IF = Type of chemical potential data: -1 kcal/mol * 0 MU/RT * 1 kJ/mol * IEST = Initial estimate: 0 user estimate * -1 machine estimate * For each Species: * SP = Species name * BM = formula vector * SI = Type of phase, 0 single-species * 1 multi-species gas * 2 multi-species liquid * FF = Input standard chemical potential * * E(J) = Char*2 name for the Jth element in the mechanism * * Return Codes * ------------------ * 0 = Equilibrium Achieved * 1 = Range space error encountered. The element abundance criteria are * only partially satisfied. Specifically, the first NC= (number of * components) conditions are satisfied. However, the full NE * (number of elements) conditions are not satisfied. The equilibrirum * condition is returned. * -1 = Maximum number of iterations is exceeded. Convergence was not * found. * *************************************************************************/ { int conv = FALSE, retn = VCS_SUCCESS; double test, RT; int j, k, l, solveFail, l1, kspec, irxn, im, forced, iph; // double *ss, *sm, *sa, *aw, *wx, double dx, xx, par, tsecond; int liqphase = FALSE, numSpecliquid = 0; int dofast, soldel, ll, it1; int lec, npb, iti, i, lnospec; int rangeErrorFound = 0; bool giveUpOnElemAbund = false; int finalElemAbundAttempts = 0; bool MajorSpeciesHaveConverged = false; int uptodate_minors = TRUE; bool justDeletedMultiPhase = FALSE; int usedZeroedSpecies; /* return flag from basopt indicating that one of the components had a zero concentration */ vcs_VolPhase *Vphase; double *sc_irxn = NULL; /* Stoichiometric coefficients for cur rxn */ double *dnPhase_irxn; #ifdef DEBUG char ANOTE[128]; /* * Set the debug print lvl to the same as the print lvl. */ vcs_debug_print_lvl = printDetails; #endif if (printDetails > 0 && print_lvl == 0) { print_lvl = 1; } /* * Initialize and set up all counters */ vcs_counters_init(0); tsecond = vcs_second(); /* * Malloc temporary space for usage in this routine and in * subroutines * sm[ne*ne] * ss[ne] * sa[ne] * aw[m] * wx[ne] * xy[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, 0.0); std::vector wx(m_numElemConstraints, 0.0); solveFail = FALSE; im = FALSE; /* ****************************************************** */ /* **** Evaluate the elemental composition ****** */ /* ****************************************************** */ vcs_elab(); /* ******************************************************* */ /* **** Printout the initial conditions for problem ****** */ /* ******************************************************* */ if (NPhase > 1) { if (! VPhaseList[1]->SingleSpecies) { liqphase = TRUE; numSpecliquid = VPhaseList[1]->NVolSpecies; } } if (print_lvl != 0) { plogf("VCS CALCULATION METHOD\n\n "); plogf("%s\n", Title.c_str()); plogf("\n\n%5d SPECIES%8d ELEMENTS", m_numSpeciesTot, m_numElemConstraints); plogf("%16d COMPONENTS\n%5d PHASE1 SPECIES", m_numComponents, ((VPhaseList[0])->NVolSpecies)); plogf("%10d PHASE2 SPECIES%8d SINGLE SPECIES PHASES\n\n", numSpecliquid, m_numSpeciesTot - (VPhaseList[0])->NVolSpecies - numSpecliquid); plogf(" PRESSURE%22.3f ATM\n TEMPERATURE%19.3f K\n", Pres, T); Vphase = VPhaseList[0]; if (Vphase->NVolSpecies > 0) { plogf(" PHASE1 INERTS%17.3f\n", TPhInertMoles[0]); } if (liqphase) { plogf(" PHASE2 INERTS%17.3f\n", TPhInertMoles[1]); } plogf("\n ELEMENTAL ABUNDANCES CORRECT"); plogf(" FROM ESTIMATE Type\n\n"); for (i = 0; i < m_numElemConstraints; ++i) { print_space(26); plogf("%-2.2s", (ElName[i]).c_str()); plogf("%20.12E%20.12E %3d\n", gai[i], ga[i], m_elType[i]); } if (iest < 0) { plogf("\n MODIFIED LINEAR PROGRAMMING ESTIMATE OF EQUILIBRIUM\n"); } if (iest >= 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"); print_space(29); plogf(" STAN_CHEM_POT EQUILIBRIUM_EST. Species_Type\n\n"); print_space(14); for (i = 0; i < m_numElemConstraints; ++i) plogf(" %-2.2s", ElName[i].c_str()); plogf(" SI(I)\n"); RT = vcs_nondimMult_TP(m_VCS_UnitsFormat, T); for (i = 0; i < m_numSpeciesTot; ++i) { plogf(" %-12s", SpName[i].c_str()); for (j = 0; j < m_numElemConstraints; ++j) { plogf("%3g", FormulaMatrix[j][i]); } if (PhaseID[i] == 0) { plogf(" 1"); } else if (PhaseID[i] == 1) { if (liqphase) plogf(" 2"); else plogf(" 0"); } else { plogf(" 0"); } print_space(47-m_numElemConstraints*3); plogf("%12.5E %12.5E", RT * ff[i], soln[i]); if (SpeciesUnknownType[i] == VCS_SPECIES_TYPE_MOLNUM) { plogf(" Mol_Num"); } else if (SpeciesUnknownType[i] == VCS_SPECIES_TYPE_INTERFACIALVOLTAGE) { plogf(" Voltage"); } else { plogf(" Unknown"); } plogf(" \n"); } } for (i = 0; i < m_numSpeciesTot; ++i) { if (soln[i] < 0.0) { plogf("On Input species %-12s has a " "negative MF, setting it small\n", SpName[i].c_str()); soln[i] = VCS_DELETE_SPECIES_CUTOFF; } } /* *********************************************** */ /* **** EVALUATE TOTAL MOLES, GAS AND LIQUID ***** */ /* *********************************************** */ /* - Evaluate the total moles of gas and liquid */ /* - These quantities are storred in the global variables */ vcs_tmoles(); /* ******************************************* */ /* **** EVALUATE ALL CHEMICAL POTENTIALS ***** */ /* ******************************************* */ vcs_dfe(VCS_DATA_PTR(soln), 0, 0, 0, m_numSpeciesRdc); /* * HKM -> If there was a machine estimate, we used to branch * to the code segment which determined whether we needed a * new component basis. If we did, we would go to L429. * If we didn't, we would go to a point below basopt() below. * I have taken this section out of the code for simplicity's * sake. It's not need for speed, since in any recursive * call to this subroutine we would have an initial estimate * of the solution. And, we don't need to optimize the * startup of nonrecursive calls to this subroutine. */ /* *********************************************************** */ /* **** DETERMINE BASIS SPECIES, EVALUATE STOICHIOMETRY ****** */ /* *********************************************************** */ /* * This is an entry point for later in the calculation */ L_COMPONENT_CALC: ; test = -1.0e-10; retn = vcs_basopt(FALSE, VCS_DATA_PTR(aw), VCS_DATA_PTR(sa), VCS_DATA_PTR(sm), VCS_DATA_PTR(ss), test, &usedZeroedSpecies); if (retn != VCS_SUCCESS) return retn; if (conv) { goto L_RETURN_BLOCK; } it1 = 1; MajorSpeciesHaveConverged = false; /*************************************************************************/ /************** EVALUATE INITIAL MAJOR-MINOR VECTOR **********************/ /*************************************************************************/ m_numRxnMinorZeroed = 0; for (irxn = 0; irxn < m_numRxnRdc; ++irxn) { kspec = ir[irxn]; spStatus[irxn] = vcs_species_type(kspec); if (spStatus[irxn] == VCS_SPECIES_MINOR) { spStatus[irxn] = VCS_SPECIES_MAJOR; #ifdef DEBUG if (vcs_debug_print_lvl >= 2) { plogf(" --- Minor species changed to major: "); plogf("%-12s\n", SpName[kspec].c_str()); } #endif } if (spStatus[irxn] != VCS_SPECIES_MAJOR) { ++m_numRxnMinorZeroed; } } im = (m_numRxnMinorZeroed == m_numRxnRdc); lec = FALSE; if (! vcs_elabcheck(0)) { #ifdef DEBUG if (vcs_debug_print_lvl >= 2) { plogf(" --- Element Abundance check failed\n"); } #endif vcs_elcorr(VCS_DATA_PTR(sm), VCS_DATA_PTR(wx)); vcs_dfe(VCS_DATA_PTR(soln), 0, 0, 0, m_numSpeciesRdc); } #ifdef DEBUG else { if (vcs_debug_print_lvl >= 2) { plogf(" --- Element Abundance check passed\n"); } } #endif // Update the phase objects with the contents of the soln vector vcs_updateVP(0); vcs_deltag(0, false); iti = 0; goto L_MAINLOOP_ALL_SPECIES; /* ********************************************************* */ /* **** SET INITIAL VALUES FOR ITERATION ******************* */ /* **** EVALUATE REACTION ADJUSTMENTS ******************* */ /* ********************************************************* */ /* * This is the top of the loop ---------------------------------------- * Every 4th iteration ITI = 0. Else, It's equal to a negative number */ L_MAINLOOP_MM4_SPECIES: ; iti = ((it1/4) *4) - it1; /* * Entry point when the code wants to force an ITI=0 calculation */ L_MAINLOOP_ALL_SPECIES: ; if (iti == 0) { /* * Evaluate the minor non-componenent species chemical * potentials and delta G for their formation reactions * We have already evaluated the major non-components */ if (uptodate_minors == FALSE) { vcs_dfe(VCS_DATA_PTR(soln), 0, 1, 0, m_numSpeciesRdc); vcs_deltag(1, false); } uptodate_minors = TRUE; } else { uptodate_minors = FALSE; } if (printDetails) { plogf("\n"); vcs_print_line("=", 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"); } } vcs_dcopy(VCS_DATA_PTR(fel), VCS_DATA_PTR(m_gibbsSpecies), m_numSpeciesRdc); vcs_dcopy(VCS_DATA_PTR(feTrial), VCS_DATA_PTR(m_gibbsSpecies), m_numSpeciesRdc); vcs_dcopy(VCS_DATA_PTR(ActCoeff0), VCS_DATA_PTR(ActCoeff), m_numSpeciesRdc); vcs_dcopy(VCS_DATA_PTR(dgl), VCS_DATA_PTR(dg), m_numRxnRdc); /* Go find a new reaction adjustment -> * i.e., change in extent of reaction for each reaction. * * 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. */ vcs_dzero(VCS_DATA_PTR(ds), m_numSpeciesTot); /* * Figure out whether we will calculate new reaction step sizes * for the major species. * -> We won't if all species are minors (im), OR * all major species have already converged */ if (!(MajorSpeciesHaveConverged) && ! im) { soldel = vcs_RxnStepSizes(); /* - If SOLDEL is true then we encountered a reaction between */ /* - single-species-phase species, only, and have adjusted */ /* - the mole number vector, W(), directly. In this case, */ /* - we should immediately go back and recompute a new */ /* - component basis, if the species that was zeroed was */ /* - a component. SOLDEL is true when this is so. */ if (soldel > 0) { /* - We have changed the base mole number amongst single- */ /* - species-phase species. However, we don't need to */ /* - recaculate their chemical potentials because they */ /* - are constant, anyway! */ if (soldel == 2) { goto L_COMPONENT_CALC; } /* - We have not changed the actual DG values for */ /* - any species, even the one we deleted. Thus, */ /* - we don't need to start over. */ } } else { #ifdef DEBUG if (vcs_debug_print_lvl >= 2) { if (im) { plogf(" --- vcs_RxnStepSizes not called because all" "species are minors\n"); } else { plogf(" --- vcs_RxnStepSizes not called because " "all majors have converged\n"); } } #endif } lec = FALSE; /* * Zero out the net change in moles of multispecies phases */ vcs_dzero(VCS_DATA_PTR(DelTPhMoles), NPhase); /* **************************************************************** */ /* ***************** MAIN LOOP IN CALCULATION ******************** */ /* **************************************************************** */ /* * 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 wt[kspec] for all species consistent with an extent * of reaction, ds[kspec] for all noncomponent species formation * reactions. We will have also ensured that all predicted * non-component mole numbers are greater than zero. */ if (m_VCount->Its > maxit) { solveFail = -1; /* * Clean up and exit code even though we haven't * converged. -> we have run out of iterations! */ goto L_RETURN_BLOCK; } #ifdef DEBUG if (vcs_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(" Moles Tent_Moles Rxn_Adj | Comment \n"); } #endif for (irxn = 0; irxn < m_numRxnRdc; irxn++) { kspec = ir[irxn]; sc_irxn = sc[irxn]; iph = PhaseID[kspec]; Vphase = VPhaseList[iph]; #ifdef DEBUG ANOTE[0] = '\0'; #endif /********************************************************************/ /********************** VOLTAGE SPECIES **************************/ /********************************************************************/ if (spStatus[irxn] == VCS_SPECIES_INTERFACIALVOLTAGE) { #ifdef DEBUG dx = minor_alt_calc(kspec, irxn, &soldel, ANOTE); #else dx = minor_alt_calc(kspec, irxn, &soldel); #endif ds[kspec] = dx; } else if (spStatus[irxn] < VCS_SPECIES_MINOR) { /********************************************************************/ /********************** ZEROED OUT SPECIES **************************/ /********************************************************************/ bool resurrect = true; #ifdef DEBUG if (vcs_debug_print_lvl >= 3) { plogf(" --- %s currently zeroed (SpStatus=%-2d):", SpName[kspec].c_str(), spStatus[irxn]); plogf("%3d DG = %11.4E WT = %11.4E W = %11.4E DS = %11.4E\n", irxn, dg[irxn], wt[kspec], soln[kspec], ds[kspec]); } #endif // HKM Alternative is to not allow ds[] = 0.0 phases // to pop back into existence. For esthetics, I'm allowing this. // so that dg < 0.0 phases with zero mole numbers become components. // This is also better, because that component will be the first // one to pop into existence if there is a minute quantity of the element. // This could change in the future. //if (dg[irxn] >= 0.0 || ds[kspec] <= 0.0) { if (dg[irxn] >= 0.0 ) { wt[kspec] = soln[kspec]; ds[kspec] = 0.0; resurrect = false; #ifdef DEBUG sprintf(ANOTE, "Species stays zeroed: DG = %11.4E", dg[irxn]); if (dg[irxn] < 0.0) { sprintf(ANOTE, "Species stays zeroed even though dg neg:DG = %11.4E, ds zeroed ", dg[irxn]); } //if (vcs_debug_print_lvl >= 2) { //plogf(" --- "); plogf("%-12s", SpName[kspec]); //plogf("%3d%11.4E%11.4E%11.4E | %s\n", // spStatus[irxn], w[kspec], wt[kspec], // ds[kspec], ANOTE); //} #endif } else { for (int j = 0; j < m_numElemConstraints; ++j) { int elType = m_elType[j]; if (elType == VCS_ELEM_TYPE_ABSPOS) { double atomComp = FormulaMatrix[j][kspec]; if (atomComp > 0.0) { double maxPermissible = gai[j] / atomComp; if (maxPermissible < VCS_DELETE_MINORSPECIES_CUTOFF) { #ifdef DEBUG sprintf(ANOTE, "Species stays zeroed even though dG neg, because of %s elemAbund", ElName[j].c_str()); #endif resurrect = false; break; } } } } } /* * Resurrect the species */ if (resurrect) { if (Vphase->Existence == 0) Vphase->Existence = 1; --m_numRxnMinorZeroed; #ifdef DEBUG if (vcs_debug_print_lvl >= 2) { plogf(" --- Zeroed species changed to major: "); plogf("%-12s\n", SpName[kspec].c_str()); } #endif spStatus[irxn] = VCS_SPECIES_MAJOR; im = FALSE; MajorSpeciesHaveConverged = false; if (ds[kspec] > 0.0) { dx = ds[kspec] * 0.01; wt[kspec] = soln[kspec] + dx; } else { wt[kspec] = TMoles * VCS_DELETE_PHASE_CUTOFF * 10.; dx = wt[kspec] - soln[kspec]; } ds[kspec] = dx; #ifdef DEBUG sprintf(ANOTE, "Born:IC=-1 to IC=1:DG=%11.4E", dg[irxn]); #endif } else { wt[kspec] = soln[kspec]; ds[kspec] = 0.0; dx = 0.0; } } else if (spStatus[irxn] == VCS_SPECIES_MINOR) { /********************************************************************/ /***************************** MINOR SPECIES ************************/ /********************************************************************/ /* * Unless ITI isn't equal to zero we zero out changes * to minor species. */ if (iti != 0) { wt[kspec] = soln[kspec]; ds[kspec] = 0.0; dx = 0.0; #ifdef DEBUG sprintf(ANOTE,"minor species not considered"); if (vcs_debug_print_lvl >= 2) { plogf(" --- "); plogf("%-12s", SpName[kspec].c_str()); plogf("%3d%11.4E%11.4E%11.4E | %s\n", spStatus[irxn], soln[kspec], wt[kspec], ds[kspec], ANOTE); } #endif 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. */ #ifdef DEBUG dx = minor_alt_calc(kspec, irxn, &soldel, ANOTE); #else dx = minor_alt_calc(kspec, irxn, &soldel); #endif ds[kspec] = dx; if (soldel) { /*******************************************************************/ /***** DELETE MINOR SPECIES LESS THAN VCS_DELETE_SPECIES_CUTOFF */ /***** MOLE NUMBER */ /*******************************************************************/ #ifdef DEBUG if (vcs_debug_print_lvl >= 2) { plogf(" --- Delete minor species in multispec phase: %-12s\n", SpName[kspec].c_str()); } #endif ds[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. */ lnospec = delete_species(kspec); if (lnospec) goto L_RECHECK_DELETED; /* * 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; #ifdef DEBUG goto L_MAIN_LOOP_END_NO_PRINT; #else goto L_MAIN_LOOP_END; #endif } } else { /********************************************************************/ /*********************** MAJOR SPECIES ******************************/ /********************************************************************/ #ifdef DEBUG sprintf(ANOTE, "Normal Major Calc"); #endif /* * 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(dg[irxn]) <= tolmaj2) { wt[kspec] = soln[kspec]; ds[kspec] = 0.0; dx = 0.0; #ifdef DEBUG sprintf(ANOTE, "major species is converged"); if (vcs_debug_print_lvl >= 2) { plogf(" --- "); plogf("%-12s", SpName[kspec].c_str()); plogf("%3d%11.4E%11.4E%11.4E | %s\n", spStatus[irxn], soln[kspec], wt[kspec], ds[kspec], ANOTE); } #endif continue; } /* * Set the initial step size, dx, equal to the value produced * by the routine, vcs_RxnStepSize(). * * Note the multiplition 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 ((dg[irxn] * ds[kspec]) <= 0.0) { dx = ds[kspec]; } else { dx = 0.0; ds[kspec] = 0.0; #ifdef DEBUG sprintf(ANOTE, "dx set to 0, DG flipped sign due to " "changed initial point"); #endif } /* * Form a tentative value of the new species moles */ wt[kspec] = soln[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 (wt[kspec] <= 0.0) { #ifdef DEBUG sprintf(ANOTE, "initial nonpos moles= %11.3E", wt[kspec]); #endif /* ************************************************* */ /* *** NON-POSITIVE MOLES OF MAJOR SPECIES ********* */ /* ************************************************* */ /* * We are here when a tentative value of a mole fraction * created by a tentative value of DS(*) is negative. * We branch from here depending upon whether this * species is in a single species phase or in * a multispecies phase. */ if (! (SSPhase[kspec])) { /* * Section for multispecies phases: * - Cut reaction adjustment for positive moles of * major species in multispecies phases. * Decrease its concentration by a factor of 10. */ dx = -0.9 * soln[kspec]; ds[kspec] = dx; wt[kspec] = soln[kspec] + dx; /* * Change major to minor if the current species * has a mole number that is less than 1/100 of the * total moles in the problem. * However, it also has to be a small species within its * own phase as well. * we can't call vcs_species_type() because the phase moles * would be wrong. */ if (wt[kspec] < 0.005 * TMoles) { iph = PhaseID[kspec]; if (wt[kspec] < (TPhMoles[iph] * 0.01)) { #ifdef DEBUG if (vcs_debug_print_lvl >= 2) { plogf(" --- Major species changed to minor: "); plogf("%-12s\n", SpName[kspec].c_str()); } #endif spStatus[irxn] = VCS_SPECIES_MINOR; ++m_numRxnMinorZeroed; im = (m_numRxnMinorZeroed == m_numRxnRdc); } } } else { /* * Section for single species phases: * Calculate a dx that will wipe out the * moles in the phase. */ dx = -soln[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 (j = 0; j < m_numComponents; ++j) { if (sc_irxn[j] != 0.0) { wx[j] = soln[j] + sc_irxn[j] * dx; if (wx[j] <= soln[j] * 0.01 - 1.0E-150) { dx = MAX(dx, soln[j] * -0.99 / sc_irxn[j]); } } else { wx[j] = soln[j]; } } wt[kspec] = soln[kspec] + dx; if (wt[kspec] > 0.0) { ds[kspec] = dx; #ifdef DEBUG sprintf(ANOTE, "zeroing SS phase created a neg component species " "-> reducing step size instead"); #endif } else { /* * We are going to zero the single species phase. * Set the existence flag */ iph = PhaseID[kspec]; Vphase = VPhaseList[iph]; Vphase->Existence = 0; #ifdef DEBUG sprintf(ANOTE, "zero SS phase: moles went neg"); #endif /* * 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. */ soln[kspec] = 0.0; for (j = 0; j < m_numComponents; ++j) { soln[j] = wx[j]; } /* * Change the total number of moles in all phases due to * the reaction that wil be zeroing out the pure species * phase. Make sure the moles in the current ss phase is * identically zero. */ dnPhase_irxn = DnPhase[irxn]; for (int iphase = 0; iphase < NPhase; iphase++) { TPhMoles[iphase] += dnPhase_irxn[iphase] * dx; } TPhMoles[iph] = 0.0; vcs_updateVP(0); /* * Recalcuate the chemical potentials, FE(), and the * reaction free energy changes, DG(), for the current * set of reactions being considered. The set of reactions * is determined by the value of iti. */ vcs_dfe(VCS_DATA_PTR(soln), 0, iti, 0, m_numSpeciesRdc); vcs_deltag(iti, false); /* * Redefine the starting conditions for noncomponents * which have yet to be processed in the main loop */ for (ll = kspec+1; ll < m_numSpeciesRdc; ++ll) { fel[ll] = m_gibbsSpecies[ll]; } for (ll = irxn+1; ll < m_numRxnRdc; ++ll) { dgl[ll] = dg[ll]; } #ifdef DEBUG if (vcs_debug_print_lvl >= 2) { if (spStatus[irxn] >= 0) { plogf(" --- SS species changed to zeroedss: "); plogf("%-12s\n", SpName[kspec].c_str()); } } #endif spStatus[irxn] = VCS_SPECIES_ZEROEDSS; ++m_numRxnMinorZeroed; im = (m_numRxnMinorZeroed == m_numRxnRdc); if (im && iti != 0) { goto L_EQUILIB_CHECK; } wt[kspec] = soln[kspec]; ds[kspec] = 0.0; dx = 0.0; } } } /*********************************************************************/ /*** LINE SEARCH ALGORITHM FOR MAJOR SPECIES IN NON-IDEAL PHASES *****/ /*********************************************************************/ /* * Skip the line search if we are birthing a species */ if (dx != 0.0 && (soln[kspec] > 0.0) && (SpeciesUnknownType[kspec] != VCS_SPECIES_TYPE_INTERFACIALVOLTAGE)) { double dx_old = dx; #ifdef DEBUG dx = vcs_line_search(irxn, dx_old, ANOTE); #else dx = vcs_line_search(irxn, dx_old); #endif } ds[kspec] = dx; } /* End of Loop on ic[irxn] -> the type of species */ /***********************************************************************/ /****** CALCULATE MOLE NUMBER CHANGE FOR THE COMPONENT BASIS ***********/ /***********************************************************************/ if (dx != 0.0 && (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. */ #ifdef DEBUG if (ds[kspec] != dx) { plogf("we have a problem!\n"); exit(-1); } #endif for (k = 0; k < m_numComponents; ++k) { ds[k] += sc_irxn[k] * dx; } /* * Calculate the tentative change in the total number of * moles in all of the phases */ dnPhase_irxn = DnPhase[irxn]; for (iph = 0; iph < NPhase; iph++) { DelTPhMoles[iph] += dx * dnPhase_irxn[iph]; } } #ifdef DEBUG checkDelta1(VCS_DATA_PTR(ds), VCS_DATA_PTR(DelTPhMoles), kspec+1); #endif /* * Branch point for returning - */ #ifndef DEBUG L_MAIN_LOOP_END: ; #endif #ifdef DEBUG if (vcs_debug_print_lvl >= 2) { wt[kspec] = soln[kspec] + ds[kspec]; plogf(" --- "); plogf("%-12.12s", SpName[kspec].c_str()); plogf("%3d%11.4E%11.4E%11.4E | %s\n", spStatus[irxn], soln[kspec], wt[kspec], ds[kspec], ANOTE); } L_MAIN_LOOP_END_NO_PRINT: ; #endif /**************** END OF MAIN LOOP OVER FORMATION REACTIONS ************/ } #ifdef DEBUG if (vcs_debug_print_lvl >= 2) { for (k = 0; k < m_numComponents; k++) { plogf(" --- "); plogf("%-12.12s", SpName[k].c_str()); plogf(" c%11.4E%11.4E%11.4E |\n", soln[k], soln[k]+ds[k], ds[k]); } plogf(" "); vcs_print_line("-", 80); plogf(" --- Finished Main Loop\n"); } #endif /*************************************************************************/ /*********** LIMIT REDUCTION OF BASIS SPECIES TO 99% *********************/ /*************************************************************************/ /* * We have a tentative DS(L=1,MR). Now apply other criteria * to limit it's magnitude. */ par = 0.5; for (k = 0; k < m_numComponents; ++k) { if (soln[k] > 0.0) { xx = -ds[k] / soln[k]; if (par < xx) { par = xx; #ifdef DEBUG ll = k; #endif } } else { if (ds[k] < 0.0) { /* * If we are here, we then do a step which violates element * conservation. */ iph = PhaseID[k]; DelTPhMoles[iph] -= ds[k]; ds[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; #ifdef DEBUG if (vcs_debug_print_lvl >= 2) { plogf(" --- Reduction in step size due to component "); plogf("%s", SpName[ll].c_str()); plogf(" going negative = %11.3E\n", par); } #endif for (i = 0; i < m_numSpeciesTot; ++i) { ds[i] *= par; } for (iph = 0; iph < NPhase; iph++) { DelTPhMoles[iph] *= par; } } else { par = 1.0; } #ifdef DEBUG checkDelta1(VCS_DATA_PTR(ds), VCS_DATA_PTR(DelTPhMoles), m_numSpeciesTot); #endif /* * Now adjust the wt[kspec]'s so that the reflect the decrease in * the overall length of ds[kspec] just calculated. At the end * of this section wt[], ds[], tPhMoles, and tPhMoles1 should all be * consistent with a new estimate of the state of the system. */ for (kspec = 0; kspec < m_numSpeciesTot; ++kspec) { wt[kspec] = soln[kspec] + ds[kspec]; if (wt[kspec] < 0.0 && (SpeciesUnknownType[kspec] != VCS_SPECIES_TYPE_INTERFACIALVOLTAGE)) { plogf("vcs_solve_TP: ERROR on step change wt[%d:%s]: %g < 0.0\n", kspec, SpName[kspec].c_str(), wt[kspec]); exit(-1); } } /* * Calculate the tentative total mole numbers for each phase */ for (iph = 0; iph < NPhase; iph++) { TPhMoles1[iph] = TPhMoles[iph] + DelTPhMoles[iph]; } /* * 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_updateVP(1); vcs_dfe(VCS_DATA_PTR(wt), 1, iti, 0, m_numSpeciesTot); /* * Evaluate DeltaG for all components if ITI=0, and for * major components only if ITI NE 0 */ if (iti == 0) vcs_deltag(0, false); else vcs_deltag(-1, false); /* * Print Intermediate results */ // HKM Actually always need to calculate this // or else nonprintouts get different results and sometimes // fail in the line search algorithm -> Why is this? vcs_dfe(VCS_DATA_PTR(wt), 1, 1, 0, m_numSpeciesRdc); if (printDetails) { if (iti != 0) { #ifdef DEBUG if (vcs_debug_print_lvl >= 2) { plogf(" *** vcs_dfe for printout only:"); } #endif vcs_dfe(VCS_DATA_PTR(wt), 1, 1, 0, m_numSpeciesRdc); #ifdef DEBUG if (vcs_debug_print_lvl >= 2) { plogf(" *** vcs_deltag for printout only:"); } #endif vcs_deltag(1, false); } plogf(" "); vcs_print_line("-", 103); plogf(" --- Summary of the Update "); if (iti == 0) { plogf(" (all species):\n"); } else { plogf(" (only major species):\n"); } plogf(" --- Species Status Initial_Moles Final_Moles Initial_Mu/RT"); plogf(" Mu/RT Init_Del_G/RT Delta_G/RT\n"); for (i = 0; i < m_numComponents; ++i) { plogf(" --- %-12.12s", SpName[i].c_str()); plogf(" "); plogf("%14.6E%14.6E%14.6E%14.6E\n", soln[i], wt[i], fel[i], m_gibbsSpecies[i]); } for (i = m_numComponents; i < m_numSpeciesRdc; ++i) { l1 = i - m_numComponents; plogf(" --- %-12.12s", SpName[i].c_str()); plogf(" %2d %14.6E%14.6E%14.6E%14.6E%14.6E%14.6E\n", spStatus[l1], soln[i], wt[i], fel[i], m_gibbsSpecies[i], dgl[l1], dg[l1]); } for (kspec = m_numSpeciesRdc; kspec < m_numSpeciesTot; ++kspec) { l1 = kspec - m_numComponents; plogf(" --- %-12.12s", SpName[kspec].c_str()); plogf(" %2d %14.6E%14.6E%14.6E%14.6E%14.6E%14.6E\n", spStatus[l1], soln[kspec], wt[kspec], fel[kspec], m_gibbsSpecies[kspec], dgl[l1], dg[l1]); } plogf(" ---"); print_space(56); plogf("Norms of Delta G():%14.6E%14.6E\n", l2normdg(VCS_DATA_PTR(dgl)), l2normdg(VCS_DATA_PTR(dg))); plogf(" --- Phase_Name Moles(after update)\n"); plogf(" --- "); vcs_print_line("-", 50); for (iph = 0; iph < NPhase; iph++) { Vphase = VPhaseList[iph]; plogf(" --- %18s = %15.7E\n", Vphase->PhaseName.c_str(), TPhMoles1[iph]); } plogf(" "); vcs_print_line("-", 103); plogf(" --- Total Dimensionless Gibbs Free Energy = %15.7E\n", vcs_Total_Gibbs(VCS_DATA_PTR(wt), VCS_DATA_PTR(m_gibbsSpecies), VCS_DATA_PTR(TPhMoles1))); if (m_VCount->Its > 150) { plogf(" --- Troublesome solve\n"); } #ifdef DEBUG #ifdef DEBUG_MORE if (vcs_debug_print_lvl >= 3) { prneav(); } #endif #endif } /* *************************************************************** */ /* **** CONVERGENCE FORCER SECTION ******************************* */ /* *************************************************************** */ /* * Save the previous delta G in the old vector for * printout purposes */ if (printDetails) { vcs_dcopy(VCS_DATA_PTR(dgl), VCS_DATA_PTR(dg), m_numRxnRdc); } forced = FALSE; // if (! im && ! MajorSpeciesHaveConverged) { forced = force(iti); //} /* * Print out the changes to the solution that FORCER produced */ if (printDetails && forced) { if (iti != 0) { #ifdef DEBUG if (vcs_debug_print_lvl >= 3) { plogf(" *** vcs_dfe for printout only:"); } #endif vcs_updateVP(0); vcs_dfe(VCS_DATA_PTR(soln), 0, 1, 0, m_numSpeciesRdc); #ifdef DEBUG if (vcs_debug_print_lvl >= 3) { plogf(" *** vcs_deltag call for printouts only;"); } #endif vcs_deltag(1, false); } plogf(" -----------------------------------------------------\n"); plogf(" --- FORCER SUBROUTINE changed the solution:\n"); plogf(" --- SPECIES Status TENT MOLES"); plogf(" FINAL MOLES TENT_DEL_G/RT FINAL_DELTA_G/RT\n"); for (i = 0; i < m_numComponents; ++i) { plogf(" --- %-12.12s", SpName[i].c_str()); plogf(" %14.6E%14.6E\n", wt[i], soln[i]); } for (kspec = m_numComponents; kspec < m_numSpeciesRdc; ++kspec) { irxn = kspec - m_numComponents; plogf(" --- %-12.12s", SpName[kspec].c_str()); plogf(" %2d %14.6E%14.6E%14.6E%14.6E\n", spStatus[irxn], wt[kspec], soln[kspec], dgl[irxn], dg[irxn]); } print_space(26); plogf("Norms of Delta G():%14.6E%14.6E\n", l2normdg(VCS_DATA_PTR(dgl)), l2normdg(VCS_DATA_PTR(dg))); plogf(" Total moles of gas = %15.7E\n", TPhMoles[0]); if ((NPhase > 1) && (! (VPhaseList[1])->SingleSpecies)) { plogf(" Total moles of liquid = %15.7E\n", TPhMoles[1]); } else { plogf(" Total moles of liquid = %15.7E\n", 0.0); } plogf(" Total Dimensionless Gibbs Free Energy = %15.7E\n", vcs_Total_Gibbs(VCS_DATA_PTR(soln), VCS_DATA_PTR(m_gibbsSpecies), VCS_DATA_PTR(TPhMoles))); plogf(" -----------------------------------------------------\n"); } /*************************************************************************/ /******************* 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. */ if (! forced) { vcs_dcopy(VCS_DATA_PTR(TPhMoles), VCS_DATA_PTR(TPhMoles1), NPhase); vcs_dcopy(VCS_DATA_PTR(soln), VCS_DATA_PTR(wt), m_numSpeciesRdc); } vcs_updateVP(0); /* * Increment the iteration counters */ ++(m_VCount->Its); ++it1; #ifdef DEBUG if (vcs_debug_print_lvl >= 2) { plogf(" --- Increment counter increased, step is accepted: %4d\n", m_VCount->Its); } #endif /*************************************************************************/ /******************* 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. */ justDeletedMultiPhase = FALSE; for (iph = 0; iph < NPhase; iph++) { Vphase = VPhaseList[iph]; if (!(Vphase->SingleSpecies)) { if (TPhMoles[iph] != 0.0 && TPhMoles[iph]/TMoles <= VCS_DELETE_PHASE_CUTOFF) { soldel = 1; for (kspec = 0; kspec < m_numSpeciesRdc; kspec++) { if (PhaseID[kspec] == iph && soln[kspec] > 0.0) { irxn = kspec - m_numComponents; if (kspec < m_numComponents) { if (soln[kspec] > VCS_DELETE_SPECIES_CUTOFF) { soldel = 0; break; } } else { for (k = 0; k < m_numComponents; k++) { if (sc[irxn][k] != 0.0) { if (soln[kspec]/soln[k] > VCS_DELETE_PHASE_CUTOFF) { soldel = 0; break; } } } } } } if (soldel) { #ifdef DEBUG if (vcs_debug_print_lvl >= 1) { plogf(" --- Setting microscopic phase %d to zero\n", iph); } #endif justDeletedMultiPhase = TRUE; 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) { justDeletedMultiPhase = FALSE; retn = vcs_basopt(FALSE, VCS_DATA_PTR(aw), VCS_DATA_PTR(sa), VCS_DATA_PTR(sm), VCS_DATA_PTR(ss), test, &usedZeroedSpecies); if (retn != VCS_SUCCESS) return retn; vcs_dfe(VCS_DATA_PTR(soln), 0, 0, 0, m_numSpeciesRdc); vcs_deltag(0, true); uptodate_minors = TRUE; if (conv) { /* * HKM -> I don't understand why the code would just give * up here in some cases. * This should probably be taken out */ plogf(" DELETION OF MULTISPECIES PHASE. "); plogf("Convergence to number of positive n(i) less than C.\n"); plogf("Check results to follow carefully. \n\n"); goto L_RETURN_BLOCK; } } /*************************************************************************/ /***************** CHECK FOR ELEMENT ABUNDANCE****************************/ /*************************************************************************/ #ifdef DEBUG if (vcs_debug_print_lvl >= 2) { plogf(" --- Normal element abundance check"); } #endif vcs_elab(); if (! vcs_elabcheck(0)) { #ifdef DEBUG if (vcs_debug_print_lvl >= 2) { plogf(" - failed -> redoing element abundances.\n"); } #endif vcs_elcorr(VCS_DATA_PTR(sm), VCS_DATA_PTR(wx)); vcs_dfe(VCS_DATA_PTR(soln), 0, 0, 0, m_numSpeciesRdc); vcs_deltag(0, true); uptodate_minors = TRUE; } #ifdef DEBUG else { if (vcs_debug_print_lvl >= 2) { plogf(" - passed\n"); } } #endif /*************************************************************************/ /***************** CHECK FOR OPTIMUM BASIS *******************************/ /*************************************************************************/ /* * HKM -> We first evaluate whether the components species are * ordered according to their mole numbers. If they are, * then we can essential do an order(NR) operation instead * of an order(NR*NC) operation to determine whether * a new basis is needed. * * HKM -> This section used to be branched to initially if * there was a machine estimate. I took it out to simplify * the code logic. */ dofast = (m_numComponents != 1); for (i = 1; i < m_numComponents; ++i) { if (soln[i - 1] < soln[i]) { dofast = FALSE; break; } } dofast = false; if (dofast) { for (i = 0; i < m_numRxnRdc; ++i) { l = ir[i]; for (j = m_numComponents - 1; j >= 0; j--) { if (soln[l] > soln[j]) { if (sc[i][j] != 0.0) { #ifdef DEBUG if (vcs_debug_print_lvl >= 2) { plogf(" --- Get a new basis because %s", SpName[l].c_str()); plogf(" is larger than comp %s", SpName[j].c_str()); plogf(" and share nonzero stoic: %-9.1f\n", sc[i][j]); } #endif goto L_COMPONENT_CALC; } } else { break; } #ifdef DEBUG_HKM if (spStatus[i] == VCS_SPECIES_ZEROEDMS) { if (soln[j] == 0.0) { if (sc[i][j] != 0.0) { if (dg[i] < 0.0) { #ifdef DEBUG if (vcs_debug_print_lvl >= 2) { plogf(" --- Get a new basis because %s", SpName[l].c_str()); plogf(" has dg < 0.0 and comp %s has zero mole num", SpName[j].c_str()); plogf(" and share nonzero stoic: %-9.1f\n", sc[i][j]); } #endif goto L_COMPONENT_CALC; } } } } #endif } } } else { for (i = 0; i < m_numRxnRdc; ++i) { l = ir[i]; for (j = 0; j < m_numComponents; ++j) { if (soln[l] > soln[j]) { if (sc[i][j] != 0.0) { #ifdef DEBUG if (vcs_debug_print_lvl >= 2) { plogf(" --- Get a new basis because "); plogf("%s", SpName[l].c_str()); plogf(" is larger than comp "); plogf("%s", SpName[j].c_str()); plogf(" and share nonzero stoic: %-9.1f\n", sc[i][j]); } #endif goto L_COMPONENT_CALC; } } #ifdef DEBUG_HKM if (spStatus[i] == VCS_SPECIES_ZEROEDMS) { if (soln[j] == 0.0) { if (sc[i][j] != 0.0) { if (dg[i] < 0.0) { #ifdef DEBUG if (vcs_debug_print_lvl >= 2) { plogf(" --- Get a new basis because %s", SpName[l].c_str()); plogf(" has dg < 0.0 and comp %s has zero mole num", SpName[j].c_str()); plogf(" and share nonzero stoic: %-9.1f\n", sc[i][j]); } #endif goto L_COMPONENT_CALC; } } } } #endif } } } #ifdef DEBUG if (vcs_debug_print_lvl >= 2) { plogf(" --- Check for an optimum basis passed\n"); } #endif /*************************************************************************/ /********************** 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) { #ifdef DEBUG if (vcs_debug_print_lvl >= 2) { plogf(" --- Reevaluate major-minor status of noncomponents:\n"); } #endif m_numRxnMinorZeroed = 0; for (irxn = 0; irxn < m_numRxnRdc; irxn++) { kspec = ir[irxn]; int speciesType = vcs_species_type(kspec); if (speciesType < VCS_SPECIES_MINOR) { #ifdef DEBUG if (vcs_debug_print_lvl >= 2) { if (spStatus[irxn] >= VCS_SPECIES_MINOR) { plogf(" --- major/minor species is now zeroed out: %s\n", SpName[kspec].c_str()); } } #endif ++m_numRxnMinorZeroed; } else if (speciesType == VCS_SPECIES_MINOR) { #ifdef DEBUG if (vcs_debug_print_lvl >= 2) { if (spStatus[irxn] != VCS_SPECIES_MINOR) { if (spStatus[irxn] == VCS_SPECIES_MAJOR) { plogf(" --- Noncomponent turned from major to minor: "); } else if (spStatus[irxn] == VCS_SPECIES_COMPONENT) { plogf(" --- Component turned into a minor species: "); } else { plogf(" --- Zeroed Species turned into a " "minor species: "); } plogf("%s\n", SpName[kspec].c_str()); } } #endif ++m_numRxnMinorZeroed; } else if (speciesType == VCS_SPECIES_MAJOR) { if (spStatus[irxn] != VCS_SPECIES_MAJOR) { #ifdef DEBUG if (vcs_debug_print_lvl >= 2) { if (spStatus[irxn] == VCS_SPECIES_MINOR) { plogf(" --- Noncomponent turned from minor to major: "); } else if (spStatus[irxn] == VCS_SPECIES_COMPONENT) { plogf(" --- Component turned into a major: "); } else { plogf(" --- Noncomponent turned from zeroed to major: "); } plogf("%s\n", SpName[kspec].c_str()); } #endif spStatus[irxn] = VCS_SPECIES_MAJOR; /* * For this special case, we must reevaluate thermo functions */ if (iti != 0) { vcs_dfe(VCS_DATA_PTR(soln), 0, 0, kspec, kspec+1); vcs_deltag(0, false); } } } spStatus[irxn] = speciesType; } /* * This logical variable indicates whether all current * non-component species are minor or nonexistent */ im = (m_numRxnMinorZeroed == m_numRxnRdc); } /*************************************************************************/ /***************** EQUILIBRIUM CHECK FOR MAJOR SPECIES *******************/ /*************************************************************************/ L_EQUILIB_CHECK: ; if (! im) { #ifdef DEBUG if (vcs_debug_print_lvl >= 2) { plogf(" --- Equilibrium check for major species: "); } #endif for (irxn = 0; irxn < m_numRxnRdc; ++irxn) { if (spStatus[irxn] == VCS_SPECIES_MAJOR && (fabs(dg[irxn]) > 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! */ goto L_RETURN_BLOCK; } else { #ifdef DEBUG if (vcs_debug_print_lvl >= 2) { plogf("%s failed\n", SpName[ir[irxn]].c_str()); } #endif /* * Set MajorSpeciesHaveConverged to false to indicate that * convergence amongst * major species has not been achieved */ MajorSpeciesHaveConverged = false; /* * Go back and do another iteration with variable ITI */ goto L_MAINLOOP_MM4_SPECIES; } } } #ifdef DEBUG if (vcs_debug_print_lvl >= 2) { plogf(" MAJOR SPECIES CONVERGENCE achieved\n"); } #endif } #ifdef DEBUG else { if (vcs_debug_print_lvl >= 2) { plogf(" MAJOR SPECIES CONVERGENCE achieved " "(because there are no major species)\n"); } } #endif /* * Set MajorSpeciesHaveConverged to true to indicate * that convergence amongst major species has been achieved */ MajorSpeciesHaveConverged = true; /*************************************************************************/ /*************** 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_dfe(VCS_DATA_PTR(soln), 0, 1, 0, m_numSpeciesRdc); vcs_deltag(1, false); uptodate_minors = TRUE; } #ifdef DEBUG if (vcs_debug_print_lvl >= 2) { plogf(" --- Equilibrium check for minor species: "); } #endif for (irxn = 0; irxn < m_numRxnRdc; ++irxn) { if (spStatus[irxn] == VCS_SPECIES_MINOR && (fabs(dg[irxn]) > 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 ! */ goto L_RETURN_BLOCK; } #ifdef DEBUG if (vcs_debug_print_lvl >= 2) { plogf("%s failed\n", SpName[ir[irxn]].c_str()); } #endif /* * Set iti to zero to force a full calculation, and go back * to the main loop to do another iteration. */ iti = 0; goto L_MAINLOOP_ALL_SPECIES; } } #ifdef DEBUG if (vcs_debug_print_lvl >= 2) { plogf(" CONVERGENCE achieved\n"); } #endif } /*************************************************************************/ /*********************** FINAL ELEMENTAL ABUNDANCE CHECK *****************/ /*************************************************************************/ /* * Recalculate the element abundance vector again */ vcs_updateVP(0); vcs_elab(); /* LEC is only true when we are near the end game */ if (lec) { if (!giveUpOnElemAbund) { #ifdef DEBUG if (vcs_debug_print_lvl >= 2) { plogf(" --- Check the Full Element Abundances: "); } #endif /* * 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) ) { #ifdef DEBUG if (vcs_debug_print_lvl >= 2) { if (! vcs_elabcheck(0)) { plogf(" failed\n"); } else { plogf(" passed for NC but failed for NE: RANGE ERROR\n"); } } #endif // delete? goto L_ELEM_ABUND_CHECK; } #ifdef DEBUG if (vcs_debug_print_lvl >= 2) { plogf(" passed\n"); } #endif } /* * If we have deleted a species then we need to recheck the * the deleted species, before exiting */ if (m_numSpeciesRdc != m_numSpeciesTot) { goto L_RECHECK_DELETED; } /* - Final checks are passed -> go check out */ goto L_RETURN_BLOCK; } lec = TRUE; /* *************************************************** */ /* **** CORRECT ELEMENTAL ABUNDANCES ***************** */ /* *************************************************** */ L_ELEM_ABUND_CHECK: ; /* * 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)) { int ncBefore = vcs_elabcheck(0); vcs_elcorr(VCS_DATA_PTR(sm), VCS_DATA_PTR(wx)); int ncAfter = vcs_elabcheck(0); int neAfter = vcs_elabcheck(1); /* * Go back to evaluate the total moles of gas and liquid. */ vcs_dfe(VCS_DATA_PTR(soln), 0, 0, 0, m_numSpeciesRdc); vcs_deltag(0, false); /* * */ 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; goto L_MAINLOOP_ALL_SPECIES; } else { /* * We are still hosed */ if (finalElemAbundAttempts >= 3) { giveUpOnElemAbund = true; goto L_EQUILIB_CHECK; } else { finalElemAbundAttempts++; lec = FALSE; iti = 0; goto L_MAINLOOP_ALL_SPECIES; } } } else { if (ncAfter) { if (neAfter) { /* * Recovery of end element abundances * -> go do equilibrium check again and then * check out. */ goto L_EQUILIB_CHECK; } else { /* * Probably an unrecoverable range error */ #ifdef DEBUG if (vcs_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 \n"); } #endif rangeErrorFound = 1; giveUpOnElemAbund = true; goto L_EQUILIB_CHECK; } } } } // Calculate delta g's vcs_deltag(0, false); // Go back to equilibrium check as a prep to eventually checking out goto L_EQUILIB_CHECK; /* *************************************************** */ /* **** 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. */ L_RECHECK_DELETED: ; npb = recheck_deleted(); /* * If we haven't found any species that needed adding we are done. */ if (npb <= 0) { goto L_RETURN_BLOCK_B; } /* * If we have found something to add, recalculate everything * for minor species and go back to do a full iteration */ MajorSpeciesHaveConverged = true; vcs_dfe(VCS_DATA_PTR(soln), 0, 1, 0, m_numSpeciesRdc); vcs_deltag(0, false); iti = 0; goto L_MAINLOOP_ALL_SPECIES; /*************************************************************************/ /******************** CLEANUP AND RETURN BLOCK ***************************/ /*************************************************************************/ L_RETURN_BLOCK: ; npb = 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 */ MajorSpeciesHaveConverged = true; vcs_dfe(VCS_DATA_PTR(soln), 0, 1, 0, m_numSpeciesRdc); vcs_deltag(0, false); iti = 0; goto L_MAINLOOP_ALL_SPECIES; } L_RETURN_BLOCK_B: ; /* * Add back deleted species in non-zeroed phases. Estimate their * mole numbers. */ add_deleted(); /* * 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(0); /* * Store the final Delta G values for each non-component species * in the species slot rather than the reaction slot */ kspec = m_numSpeciesTot; i = m_numRxnTot; for (irxn = 0; irxn < m_numRxnTot; ++irxn) { --kspec; --i; dg[kspec] = dg[i]; } vcs_dzero(VCS_DATA_PTR(dg), m_numComponents); /* * Evaluate the final mole fractions * storring them in wt[] */ vcs_vdzero(wt, m_numSpeciesTot); for (kspec = 0; kspec < m_numSpeciesTot; ++kspec) { if (SSPhase[kspec]) { wt[kspec] = 1.0; } else { iph = PhaseID[kspec]; if (TPhMoles[iph] != 0.0) { wt[kspec] = soln[kspec] / TPhMoles[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. */ i = indPhSp[kspec]; Vphase = VPhaseList[iph]; wt[kspec] = Vphase->molefraction(i); } } } // Return an error code if a Range Space Error is thought to have occurred. if (rangeErrorFound) { solveFail = 1; } /* * Free temporary storage used in this routine * and increment counters */ /* * Calculate counters */ tsecond = vcs_second() - tsecond; 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; } /* vcs_solve_TP() **********************************************************/ /*****************************************************************************/ /*****************************************************************************/ /*****************************************************************************/ double VCS_SOLVE::minor_alt_calc(int kspec, int irxn, int *do_delete #ifdef DEBUG , char *ANOTE #endif ) /************************************************************************** * * minor_alt_calc: * * 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 phase: * 0 = DG(I) + log(WT(I)/W(I)) * * W(i) = Old mole number of species i in the phase * WT(i) = Trial new mole number of species i in the pahse * * (DG contains the contribution from * FF(I) + log(ActCoeff[i] * W(I)/Total_Moles) ) * Thus, * WT(I) = W(I) EXP(-DG(I)) * * Most of this section is mainly restricting the update to reasonable * values. * * * Note: This routine was generalized to incorporate * nonideal phases. * * Input: * ------ * kspec, irxn = the current species and corresponding formation * reaction number. * Output: * --------- * return value: dx = the change in mole number * do_delete: BOOLEAN which if true on return, then we branch * to the section that deletes a species from the * current set of active species. *************************************************************************/ { double dx; double w_kspec = soln[kspec]; double *wt_kspec = VCS_DATA_PTR(wt) + kspec; double wTrial; double *ds_kspec = VCS_DATA_PTR(ds) + kspec; double dg_irxn = dg[irxn]; int iphase = PhaseID[kspec]; vcs_VolPhase *Vphase = VPhaseList[iphase]; *do_delete = FALSE; if (SpeciesUnknownType[kspec] != VCS_SPECIES_TYPE_INTERFACIALVOLTAGE) { if (w_kspec <= 0.0) { w_kspec = VCS_DELETE_MINORSPECIES_CUTOFF; } if (dg_irxn < -20.) { dg_irxn = -20.; } #ifdef DEBUG sprintf(ANOTE,"minor species alternative calc"); #endif if (dg_irxn >= 82.0) { (*wt_kspec) = w_kspec * 1.0e-6; if (w_kspec < VCS_DELETE_MINORSPECIES_CUTOFF) { goto L_ZERO_SPECIES; } } else { if (fabs(dg_irxn) <= tolmin2) { (*wt_kspec) = w_kspec; (*ds_kspec) = 0.0; return 0.0; } // c = log(ActCoeff[kspec] * w_kspec) - dg_irxn; } if (dg_irxn > 10.0) { (*wt_kspec) = w_kspec * 1.0e-5; if (w_kspec < VCS_DELETE_MINORSPECIES_CUTOFF) { goto L_ZERO_SPECIES; } } else { double ac0 = ActCoeff[kspec]; double ac = ac0; double w0 = w_kspec; double dd = exp(-dg_irxn); wTrial = w0 * ac0 / ac * dd; *wt_kspec = wTrial; Vphase->setMolesFromVCS(VCS_DATA_PTR(wt)); Vphase->sendToVCSActCoeff(VCS_DATA_PTR(ActCoeff)); double ac1 = ActCoeff[kspec]; double acprime = 0.0; if (fabs(wTrial - w0) > 1.0E-8 * w0) { acprime = (ac1 - ac0) / (wTrial - w0); } double jac = acprime * wTrial + ac1; double fTrial = ac1 * wTrial - ac0*w0*dd; double w2 = wTrial - fTrial / jac; if (w2 > 100.*w0) { *wt_kspec = 100.0 * w0; } else if (100. * w2 < w0) { *wt_kspec = 0.01 * w0; } else { *wt_kspec = w2; } } if ((*wt_kspec) < VCS_DELETE_MINORSPECIES_CUTOFF) { goto L_ZERO_SPECIES; } dx = (*wt_kspec) - w_kspec; (*ds_kspec) = dx; return dx; /* * * 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; dx = - w_kspec; (*ds_kspec) = dx; return dx; } else { /* * Voltage calculation * HKM -> Need to check the sign */ dx = dg[irxn]/ Faraday_dim; #ifdef DEBUG sprintf(ANOTE,"voltage species alternative calc"); #endif } return dx; } /*****************************************************************************/ /*****************************************************************************/ /*****************************************************************************/ int VCS_SOLVE::delta_species(int kspec, double *delta_ptr) /************************************************************************ * * delta_species(): * * Change the concentration of a species by delta moles. * Make sure to conserve * elements and keep track of the total moles in all phases. * * return: * 1: succeeded * 0: failed. ************************************************************************/ { int irxn = kspec - m_numComponents; int retn = 1; int j; double tmp; double delta = *delta_ptr; if (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 = sc[irxn]; for (j = 0; j < m_numComponents; ++j) { if (soln[j] > 0.0) { tmp = sc_irxn[j] * dx; if (-tmp > soln[j]) { retn = 0; dx = MIN(dx, - soln[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 (soln[j] <= 0.0) { if (sc_irxn[j] < 0.0) { *delta_ptr = 0.0; return 0; } } } /* * ok, we found a positive dx. implement it. */ *delta_ptr = dx; soln[kspec] += dx; int iph = PhaseID[kspec]; TPhMoles[iph] += dx; for (j = 0; j < m_numComponents; ++j) { iph = PhaseID[j]; tmp = sc_irxn[j] * dx; soln[j] += tmp; TPhMoles[iph] += tmp; if (soln[j] < 0.0) { soln[j] = 0.0; } } } return retn; } /*****************************************************************************/ /*****************************************************************************/ /*****************************************************************************/ int VCS_SOLVE::zero_species(int kspec) /************************************************************************ * * zero_species: * * Zero out the concentration of a species. Make sure to conserve * elements and keep track of the total moles in all phases. * w[] * TPhMoles[] * * return: * 1: succeeded * 0: failed. ************************************************************************/ { int retn = 1; /* * Calculate a delta that will eliminate the species. */ if (SpeciesUnknownType[kspec] != VCS_SPECIES_TYPE_INTERFACIALVOLTAGE) { double dx = -(soln[kspec]); if (dx != 0.0) { retn = delta_species(kspec, &dx); if (!retn) { plogf("zero_species: Couldn't zero the species %d, " "did delta of %g. orig conc of %g\n", kspec, dx, soln[kspec] + dx); } } } return retn; } /*****************************************************************************/ /*****************************************************************************/ /*****************************************************************************/ int VCS_SOLVE::delete_species(int kspec) /************************************************************************ * * delete_species: * * Rearrange data when species is added or removed. The Lth species is * moved to the back of the species vector. The back of the species * vector is indicated by the value of MR, the current number of * active species in the mechanism. * * Input * kspec = species number * Return value * The return is true when the current number of * noncomponent species is equal to zero. A recheck of deleted species * is carried out in the main code. *************************************************************************/ { int klast = m_numSpeciesRdc - 1; int iph = PhaseID[kspec]; vcs_VolPhase *Vphase = VPhaseList[iph]; int irxn = kspec - m_numComponents; /* This is the noncomponent rxn index */ /* * Zero the concentration of the species. * -> This zeroes w[kspec] and modifies TPhMoles[] */ int retn = zero_species(kspec); if (! retn) { plogf("Failed to delete a species!\n"); exit(-1); } /* * Decrement the minor species counter if the current species is * a minor species */ if (spStatus[irxn] != VCS_SPECIES_MAJOR) --(m_numRxnMinorZeroed); spStatus[irxn] = VCS_SPECIES_DELETED; dg[irxn] = 0.0; dgl[irxn] = 0.0; m_gibbsSpecies[kspec] = 0.0; fel[kspec] = 0.0; wt[kspec] = 0.0; /* * 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_DATA_PTR(soln), VCS_DATA_PTR(TPhMoles)); /* * 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 (! SSPhase[klast]) { if (Vphase->Existence != 2) { Vphase->Existence = 0; for (kspec = 0; kspec < m_numSpeciesRdc; kspec++) { if (SpeciesUnknownType[kspec] != VCS_SPECIES_TYPE_INTERFACIALVOLTAGE) { if (PhaseID[kspec] == iph) { if (soln[kspec] > 0.0) { Vphase->Existence = 1; break; } } } } if (Vphase->Existence == 0) { delete_multiphase(iph); } } } /* * When the total number of noncomponent species is zero, we * have to signal the calling code */ return (m_numRxnRdc == 0); } /* delete_species() ********************************************************/ /**************************************************************************** * * reinsert_deleted(): * * irxn = id of the noncomponent species formation reaction for the * species to be added in. * * We make decisions on the initial mole number, and major-minor status * here. We also fix up the total moles in a phase. * * The algorithm proceeds to implement these decisions in the previous * position of the species. Then, vcs_switch_pos is called to move the * species into the last active species slot, incrementing the number * of active species at the same time. * * This routine is responsible for the global data manipulation only. */ void VCS_SOLVE::vcs_reinsert_deleted(int kspec) { int i, k, irxn = kspec - m_numComponents; int *phaseID = VCS_DATA_PTR(PhaseID); double dx; #ifdef DEBUG if (vcs_debug_print_lvl >= 2) { plogf(" --- Add back a deleted species: %-12s\n", SpName[kspec].c_str()); } #endif /* * Set the species back to minor species status * this adjusts soln[] and TPhMoles[] * HKM -> make this a relative mole number! */ dx = VCS_DELETE_SPECIES_CUTOFF * 10.; delta_species(kspec, &dx); spStatus[irxn] = VCS_SPECIES_MINOR; if (SSPhase[kspec]) { spStatus[irxn] = VCS_SPECIES_MAJOR; --(m_numRxnMinorZeroed); } int iph = PhaseID[kspec]; vcs_VolPhase *Vphase = VPhaseList[iph]; Vphase->setMolesFromVCSCheck(VCS_DATA_PTR(soln), VCS_DATA_PTR(TPhMoles)); /* * 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 spStatus[] flag. * The value of spStatus[] must change from * VCS_SPECIES_ZEROEDPHASE to VCS_SPECIES_ZEROEDMS * for those other species. */ if (! SSPhase[kspec]) { if (Vphase->Existence == 0) { Vphase->Existence = 1; for (k = 0; k < m_numSpeciesTot; k++) { if (phaseID[k] == iph) { i = k - m_numComponents; if (spStatus[i] == VCS_SPECIES_ZEROEDPHASE) spStatus[i] = VCS_SPECIES_ZEROEDMS; } } } } else { Vphase->Existence = 1; } ++(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); } } /* vcs_reinsert_deleted() */ /**************************************************************************** * * delete_multiphase(): * * This routine handles the bookkeepking involved with the * deletion of multiphase phases from the * problem. When they are deleted, all of their species become active * species, even though their mole numbers are set to zero. * The routine does not make the decision to eliminate multiphases. * * 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. */ void VCS_SOLVE::delete_multiphase(int iph) { int kspec, j, irxn; double dx; vcs_VolPhase *Vphase = VPhaseList[iph]; /* * set the phase existence flag to dead */ Vphase->Existence = 0; #ifdef DEBUG if (vcs_debug_print_lvl >= 2) { plogf(" --- delete_multiphase %d, %s\n", iph, Vphase->PhaseName.c_str()); } #endif /* * Zero out the total moles counters for the phase */ TPhMoles[iph] = 0.0; TPhMoles1[iph] = 0.0; DelTPhMoles[iph] = 0.0; /* * Loop over all of the active species in the phase. */ for (kspec = 0; kspec < m_numSpeciesRdc; ++kspec) { if (PhaseID[kspec] == iph) { if (SpeciesUnknownType[kspec] != VCS_SPECIES_TYPE_INTERFACIALVOLTAGE) { irxn = kspec - m_numComponents; /* * calculate an extent of rxn, dx, that zeroes out the species. */ dx = - (soln[kspec]); /* * Set the mole numbers of that species to zero. */ soln[kspec] = 0.0; wt[kspec] = 0.0; ds[kspec] = 0.0; /* * Change the status flag of the species to that of an * zeroed phase */ spStatus[irxn] = VCS_SPECIES_ZEROEDPHASE; /* * changed the component mole numbers to account for the * final extent of reaction. Make sure to keep component * mole numbers constant. * HKM -> note, this will cause a loss of moles! */ for (j = 0; j < m_numComponents; ++j) { soln[j] += sc[irxn][j] * dx; if (soln[j] < 0.0) { soln[j] = 0.0; } } } } } /* * 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 (kspec = m_numSpeciesRdc; kspec < m_numSpeciesTot; ++kspec) { if (PhaseID[kspec] == iph) { irxn = kspec - m_numComponents; soln[kspec] = 0.0; wt[kspec] = 0.0; ds[kspec] = 0.0; spStatus[irxn] = VCS_SPECIES_ZEROEDPHASE; ++(m_numRxnRdc); ++(m_numSpeciesRdc); #ifdef DEBUG if (vcs_debug_print_lvl >= 2) { plogf(" --- Make %s", SpName[kspec].c_str()); plogf(" an active but zeroed species because its phase " "was zeroed\n"); } #endif if (kspec != (m_numSpeciesRdc - 1)) { /* * Rearrange both the species and the non-component global data */ vcs_switch_pos(TRUE, (m_numSpeciesRdc - 1), kspec); } } } /* * Upload the state to the VP object */ Vphase->setMolesFromVCSCheck(VCS_DATA_PTR(soln), VCS_DATA_PTR(TPhMoles), iph); } /* delete_multiphase() *****************************************************/ /***************************************************************************** * * recheck_deleted: * * Recheck deleted species in multispecies phases. * * HKM -> This algorithm needs to be updated for activity coefficients */ int VCS_SOLVE::recheck_deleted(void) { int iph, kspec, irxn, npb; double *xtcutoff = VCS_DATA_PTR(TmpPhase); #ifdef DEBUG if (vcs_debug_print_lvl >= 2) { plogf(" --- Start rechecking deleted species in multispec phases\n"); } #endif 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 */ for (kspec = m_numSpeciesRdc; kspec < m_numSpeciesTot; ++kspec) { m_gibbsSpecies[kspec] = ff[kspec]; } /* * Recalculate the DeltaG's of the formation reactions for the * deleted species in the mechanism */ vcs_deltag(0, true); for (iph = 0; iph < NPhase; iph++) { if (TPhMoles[iph] > 0.0) xtcutoff[iph] = log (TPhMoles[iph] / VCS_DELETE_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)/TPhMoles) * * 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 th code. Also nonideal solutions will tend to * complicate matters severely also. */ npb = 0; for (irxn = m_numRxnRdc; irxn < m_numRxnTot; ++irxn) { kspec = ir[irxn]; iph = PhaseID[kspec]; if (TPhMoles[iph] == 0.0) { if (dg[irxn] < 0.0) { vcs_reinsert_deleted(kspec); npb++; } else { soln[kspec] = 0.0; } } else if (TPhMoles[iph] > 0.0) { if (dg[irxn] < xtcutoff[iph]) { vcs_reinsert_deleted(kspec); npb++; } } } return npb; } /* recheck_deleted() *******************************************************/ /*****************************************************************************/ /*****************************************************************************/ /*****************************************************************************/ void VCS_SOLVE::add_deleted(void) /************************************************************************* * * Provide an estimate for the deleted species in phases that * are not zeroed out * *************************************************************************/ { int iph, kspec, retn; if (m_numSpeciesRdc == m_numSpeciesTot) return; /* * Use the standard chemical potentials for the chemical potentials * of deleted species. Then, calculate Delta G for * for formation reactions * * HKM Note: We need to update this step for nonunity activity * coefficients. * The formula will be fe = ff + RT * ln(actCoeff) * where the activity coefficient is evaluated at * ~ infinite dilution. */ for (kspec = m_numSpeciesRdc; kspec < m_numSpeciesTot; ++kspec) { m_gibbsSpecies[kspec] = ff[kspec]; } /* * Recalculate the DeltaG's of the formation reactions for the * deleted species in the mechanism */ vcs_deltag(0, true); for (int irxn = m_numRxnRdc; irxn < m_numRxnTot; ++irxn) { kspec = ir[irxn]; iph = PhaseID[kspec]; if (TPhMoles[iph] > 0.0) { double maxDG = MIN(dg[irxn], 300); double dx = TPhMoles[iph] * exp(- maxDG); retn = delta_species(kspec, &dx); } } vcs_dfe(VCS_DATA_PTR(soln), 0, 0, 0, m_numSpeciesTot); vcs_deltag(0, true); } /*****************************************************************************/ /*****************************************************************************/ /*****************************************************************************/ int VCS_SOLVE::force(int iti) /************************************************************************** * * force: * * Convergence Forcer: * * This routine optimizes the minimization of the total gibbs free * energy: * Gibbs = sum_k( fe_k * w_k ) * along the current direction ds[], by choosing a value, al: (0 0), * does this code section kick in. It finds the point on the parabola * where the slope is equal to zero. * * NOTE: The algorithm used to find the slope is not quite accurate. * The term, sum_k( (fe_k_n - fe_k_n-1) * w_k_n-1 ) * is dropped from s1, and, the term, * sum_k( (fe_k_n - fe_k_n-1) * w_k_n ), is dropped from s2 *************************************************************************/ { double s1, s2, al; int i, iph; double *dptr = VCS_DATA_PTR(m_gibbsSpecies); //int numSpeciesRdc = m_numSpeciesRdc; /* *************************************************** */ /* **** CALCULATE SLOPE AT END OF THE STEP ********** */ /* *************************************************** */ s2 = 0.0; for (i = 0; i < m_numSpeciesRdc; ++i) { s2 += dptr[i] * ds[i]; } #ifdef DEBUG_NOT if (s2 <= 0.0) { #ifdef DEBUG_NOT if (vcs_debug_print_lvl >= 2) { plogf(" --- subroutine FORCE produced no adjustments,"); plogf(" failed s2 test\n"); } #endif return FALSE; } #endif #ifdef DEBUG if (vcs_debug_print_lvl >= 2) { plogf(" --- subroutine FORCE: End Slope = %g\n", s2); } #endif /* *************************************************** */ /* **** CALCULATE ORIGINAL SLOPE ********************* */ /* ************************************************** */ s1 = 0.0; dptr = VCS_DATA_PTR(fel); for (i = 0; i < m_numSpeciesRdc; ++i) { s1 += dptr[i] * ds[i]; } #ifdef DEBUG_NOT if (s1 >= 0.0) { #ifdef DEBUG if (vcs_debug_print_lvl >= 2) { plogf(" --- subroutine FORCE produced no adjustments,"); plogf(" failed s1 test -PROBLEM!!\n"); } #endif return FALSE; } #endif #ifdef DEBUG if (vcs_debug_print_lvl >= 2) { plogf(" --- subroutine FORCE: Beginning Slope = %g\n", s1); } #endif /* *************************************************** */ /* **** FIT PARABOLA ********************************* */ /* *************************************************** */ al = 1.0; if (fabs(s1 -s2) > 1.0E-200) { al = s1 / (s1 - s2); } if (al >= 0.95 || al < 0.0) { #ifdef DEBUG if (vcs_debug_print_lvl >= 2) { plogf(" --- subroutine FORCE produced no adjustments (al = %g)\n", al); } #endif return FALSE; } #ifdef DEBUG if (vcs_debug_print_lvl >= 2) { plogf(" --- subroutine FORCE produced a damping factor = %g\n", al); } #endif /* *************************************************** */ /* **** ADJUST MOLE NUMBERS, CHEM. POT *************** */ /* *************************************************** */ dptr = VCS_DATA_PTR(soln); for (i = 0; i < m_numSpeciesRdc; ++i) { dptr[i] += al * ds[i]; } for (iph = 0; iph < NPhase; iph++) { TPhMoles[iph] += al * DelTPhMoles[iph]; } vcs_updateVP(0); #ifdef DEBUG if (vcs_debug_print_lvl >= 2) { plogf(" --- subroutine FORCE adjusted the mole " "numbers, AL = %10.3f\n", al); } #endif /* * 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_dfe(dptr, 0, iti, 0, m_numSpeciesRdc); /* * Evaluate DeltaG for all components if ITI=0, and for * major components only if ITI NE 0 */ vcs_deltag(iti, false); return TRUE; } /* force() *****************************************************************/ /*****************************************************************************/ /*****************************************************************************/ /*****************************************************************************/ /* * vcs_RxnStepSizes(): * * Calculates formation reaction step sizes. * This is equation 6.4-16, p. 143 in Smith and Missen. * * Output * ------- * ds(I) : reaction adjustments, where I refers to the Ith species * formation reaction. This is adjustment is for species * i + M, where M is the number of components. * Special branching occurs sometimes. This causes the component basis * to be reevaluated * return = 0 : normal return * 1 : A single species phase species has been zeroed out * in this routine. The species is a noncomponent * 2 : Same as one but, the zeroed species is a component. */ int VCS_SOLVE::vcs_RxnStepSizes() { int j, k, irxn, kspec, soldel = 0, iph; double s, xx, dss; vcs_VolPhase *Vphase = 0; double *dnPhase_irxn; #ifdef DEBUG char ANOTE[128]; if (vcs_debug_print_lvl >= 2) { plogf(" "); for (j = 0; j < 82; j++) plogf("-"); plogf("\n"); plogf(" --- Subroutine vcs_RxnStepSizes called - Details:\n"); plogf(" "); for (j = 0; j < 82; j++) plogf("-"); plogf("\n"); plogf(" --- Species Moles Rxn_Adjustment DeltaG" " | Comment\n"); } #endif /* * We update the matrix dlnActCoeffdmolNumber[][] at the * top of the loop, when necessary */ if (UseActCoeffJac) { vcs_CalcLnActCoeffJac(VCS_DATA_PTR(soln)); } /************************************************************************ ******** LOOP OVER THE FORMATION REACTIONS ***************************** ************************************************************************/ for (irxn = 0; irxn < m_numRxnRdc; ++irxn) { #ifdef DEBUG sprintf(ANOTE,"Normal Calc"); #endif kspec = ir[irxn]; if (SpeciesUnknownType[kspec] != VCS_SPECIES_TYPE_INTERFACIALVOLTAGE) { dnPhase_irxn = DnPhase[irxn]; if (soln[kspec] == 0.0 && (! SSPhase[kspec])) { /********************************************************************/ /******* MULTISPECIES PHASE WITH total moles equal to zero *********/ /*******************************************************************/ /* * If dg[irxn] is negative, then the multispecies phase should * come alive again. Add a small positive step size to * make it come alive. */ if (dg[irxn] < -1.0e-4) { /* * First decide if this species is part of a multiphase that * is nontrivial in size. */ iph = PhaseID[kspec]; double tphmoles = TPhMoles[iph]; double trphmoles = tphmoles / TMoles; if (trphmoles > VCS_DELETE_PHASE_CUTOFF) { ds[kspec] = TMoles * VCS_SMALL_MULTIPHASE_SPECIES; #ifdef DEBUG sprintf(ANOTE, "MultSpec: small species born again DG = %11.3E", dg[irxn]); #endif } else { #ifdef DEBUG sprintf(ANOTE, "MultSpec: phase come alive DG = %11.3E", dg[irxn]); #endif Vphase = VPhaseList[iph]; int numSpPhase = Vphase->NVolSpecies; ds[kspec] = TMoles * 10.0 * VCS_DELETE_PHASE_CUTOFF / numSpPhase; } --(m_numRxnMinorZeroed); } else { #ifdef DEBUG sprintf(ANOTE, "MultSpec: still dead DG = %11.3E", dg[irxn]); #endif ds[kspec] = 0.0; } } else { /********************************************************************/ /************************* REGULAR PROCESSING ************/ /********************************************************************/ /* * First take care of cases where we want to bail out * * * Don't bother if superconvergence has already been achieved * in this mode. */ if (fabs(dg[irxn]) <= tolmaj2) { #ifdef DEBUG sprintf(ANOTE,"Skipped: superconverged DG = %11.3E", dg[irxn]); if (vcs_debug_print_lvl >= 2) { plogf(" --- %-12.12s", SpName[kspec].c_str()); plogf(" %12.4E %12.4E %12.4E | %s\n", soln[kspec], ds[kspec], dg[irxn], ANOTE); } #endif continue; } /* * Don't calculate for minor or nonexistent species if * their values are to be decreasing anyway. */ if ((spStatus[irxn] != VCS_SPECIES_MAJOR) && (dg[irxn] >= 0.0)) { #ifdef DEBUG sprintf(ANOTE,"Skipped: IC = %3d and DG >0: %11.3E", spStatus[irxn], dg[irxn]); if (vcs_debug_print_lvl >= 2) { plogf(" --- %-12.12s", SpName[kspec].c_str()); plogf(" %12.4E %12.4E %12.4E | %s\n", soln[kspec], ds[kspec], dg[irxn], ANOTE); } #endif continue; } /* * Start of the regular processing */ if (SSPhase[kspec]) { s = 0.0; } else { s = 1.0 / soln[kspec] ; } for (j = 0; j < m_numComponents; ++j) { if (!SSPhase[j]) { if (soln[j] > 0.0) { s += SQUARE(sc[irxn][j]) / soln[j]; } } } for (j = 0; j < NPhase; j++) { Vphase = VPhaseList[j]; if (! Vphase->SingleSpecies) { if (TPhMoles[j] > 0.0) s -= SQUARE(dnPhase_irxn[j]) / TPhMoles[j]; } } if (s != 0.0) { /* * Take into account of the * derivatives of the activity coefficients with respect to the * mole numbers, even in our diagonal approximation. */ if (UseActCoeffJac) { double s_old = s; s = vcs_Hessian_diag_adj(irxn, s_old); #ifdef DEBUG if (s_old != s) { sprintf(ANOTE, "Normal calc: diag adjusted from %g " "to %g due to act coeff", s_old, s); } #endif } ds[kspec] = -dg[irxn] / s; // New section to do damping of the ds[] /* * */ for (j = 0; j < m_numComponents; ++j) { double stoicC = sc[irxn][j]; if (stoicC != 0.0) { double negChangeComp = - stoicC * ds[kspec]; if (negChangeComp > soln[j]) { if (soln[j] > 0.0) { #ifdef DEBUG sprintf(ANOTE, "Delta damped from %g " "to %g due to component %d (%10s) going neg", ds[kspec], -soln[j]/stoicC, j, SpName[j].c_str()); #endif ds[kspec] = - soln[j] / stoicC; } else { #ifdef DEBUG sprintf(ANOTE, "Delta damped from %g " "to %g due to component %d (%10s) zero", ds[kspec], -soln[j]/stoicC, j, SpName[j].c_str()); #endif ds[kspec] = 0.0; } } } } // Implement a damping term that limits ds to the size of the mole number if (-ds[kspec] > soln[kspec]) { #ifdef DEBUG sprintf(ANOTE, "Delta damped from %g " "to %g due to %s going negative", ds[kspec], -soln[kspec], SpName[kspec].c_str()); #endif ds[kspec] = -soln[kspec]; } } else { /* ************************************************************ */ /* **** REACTION IS ENTIRELY AMONGST SINGLE SPECIES PHASES **** */ /* **** DELETE ONE OF THE PHASES AND RECOMPUTE BASIS ********* */ /* ************************************************************ */ /* * Either the species L will disappear or one of the * component single species phases will disappear. The sign * of DG(I) will indicate which way the reaction will go. * Then, we need to follow the reaction to see which species * will zero out first. * -> The species to be zeroed out will be "k". */ if (dg[irxn] > 0.0) { dss = soln[kspec]; k = kspec; for (j = 0; j < m_numComponents; ++j) { if (sc[irxn][j] > 0.0) { xx = soln[j] / sc[irxn][j]; if (xx < dss) { dss = xx; k = j; } } } dss = -dss; } else { dss = 1.0e10; for (j = 0; j < m_numComponents; ++j) { if (sc[irxn][j] < 0.0) { xx = -soln[j] / sc[irxn][j]; if (xx < dss) { dss = xx; k = j; } } } } /* * Here we adjust the mole fractions * according to DSS and the stoichiometric array * to take into account that we are eliminating * the kth species. DSS contains the amount * of moles of the kth species that needs to be * added back into the component species. */ if (dss != 0.0) { soln[kspec] += dss; TPhMoles[PhaseID[kspec]] += dss; for (j = 0; j < m_numComponents; ++j) { soln[j] += dss * sc[irxn][j]; TPhMoles[PhaseID[j]] += dss * sc[irxn][j]; } soln[k] = 0.0; iph = PhaseID[k]; Vphase = VPhaseList[iph]; Vphase->Existence = 0; TPhMoles[iph] = 0.0; #ifdef DEBUG if (vcs_debug_print_lvl >= 2) { plogf(" --- vcs_RxnStepSizes Special section to delete %s\n", SpName[k].c_str()); plogf(" --- Immediate return - Restart iteration\n"); } #endif /* * We need to immediately recompute the * component basis, because we just zeroed * it out. */ if (k != kspec) soldel = 2; else soldel = 1; return soldel; } } } /* End of regular processing */ #ifdef DEBUG if (vcs_debug_print_lvl >= 2) { plogf(" --- %-12.12s", SpName[kspec].c_str()); plogf(" %12.4E %12.4E %12.4E | %s\n", soln[kspec], ds[kspec], dg[irxn], ANOTE); } #endif } /* End of loop over SpeciesUnknownType */ } /* End of loop over non-component stoichiometric formation reactions */ #ifdef DEBUG if (vcs_debug_print_lvl >= 2) { plogf(" "); vcs_print_line("-", 82); } #endif return soldel; } /*****************************************************************************/ /************************************************************************** * * vcs_deltag: * * This subroutine calculates reaction free energy changes for * all noncomponent formation reactions. Formation reactions are * reactions which create each noncomponent species from the component * species. SC(J,I) are the stoichiometric coefficients for these * reactions. A stoichiometric coefficient of one is assumed for * species I in this reaction. * * INPUT * L = < 0 : Calculate reactions corresponding to * major noncomponent and zeroed species only * L = 0 : Do all noncomponent reactions, i, between * 0 <= i < irxnl * L > 0 : Calculate reactions corresponding to * minor noncomponent and zeroed species only * irxnl : used with L = 0 to indicate upper limit. * * Note we special case one important issue. * If the component has zero moles, then we do not * allow deltaG < 0.0 for formation reactions which * would lead to the loss of more of the component. * This dG < 0.0 feeds back into the algorithm in several * places, and leads to a infinite loop in at least one case. */ void VCS_SOLVE::vcs_deltag(int l, bool doDeleted) { int iph; int lneed, irxn, kspec; double *dtmp_ptr; int icase = 0; int irxnl = m_numRxnRdc; if (doDeleted) { irxnl = m_numRxnTot; } #ifdef DEBUG if (vcs_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"); } } #endif /* ************************************************* */ /* **** MAJORS and ZEREOD SPECIES ONLY ************* */ /* ************************************************* */ if (l < 0) { for (irxn = 0; irxn < m_numRxnRdc; ++irxn) { if (spStatus[irxn] != VCS_SPECIES_MINOR) { icase = 0; dg[irxn] = m_gibbsSpecies[ir[irxn]]; dtmp_ptr = sc[irxn]; for (kspec = 0; kspec < m_numComponents; ++kspec) { dg[irxn] += dtmp_ptr[kspec] * m_gibbsSpecies[kspec]; if (soln[kspec] < VCS_DELETE_MINORSPECIES_CUTOFF && dtmp_ptr[kspec] < 0.0) { icase = 1; } } if (icase) { dg[irxn] = MAX(0.0, dg[irxn]); } } } } else if (l == 0) { /* ************************************************* */ /* **** ALL REACTIONS ****************************** */ /* ************************************************* */ for (irxn = 0; irxn < irxnl; ++irxn) { icase = 0; dg[irxn] = m_gibbsSpecies[ir[irxn]]; dtmp_ptr = sc[irxn]; for (kspec = 0; kspec < m_numComponents; ++kspec) { dg[irxn] += dtmp_ptr[kspec] * m_gibbsSpecies[kspec]; if (soln[kspec] < VCS_DELETE_MINORSPECIES_CUTOFF && dtmp_ptr[kspec] < 0.0) { icase = 1; } } if (icase) { dg[irxn] = MAX(0.0, dg[irxn]); } } } else { /* ************************************************* */ /* **** MINORS AND ZEROED SPECIES ****************** */ /* ************************************************* */ for (irxn = 0; irxn < m_numRxnRdc; ++irxn) { if (spStatus[irxn] <= VCS_SPECIES_MINOR) { icase = 0; dg[irxn] = m_gibbsSpecies[ir[irxn]]; dtmp_ptr = sc[irxn]; for (kspec = 0; kspec < m_numComponents; ++kspec) { dg[irxn] += dtmp_ptr[kspec] * m_gibbsSpecies[kspec]; if (soln[kspec] < VCS_DELETE_MINORSPECIES_CUTOFF && dtmp_ptr[kspec] < 0.0) { icase = 1; } } if (icase) { dg[irxn] = MAX(0.0, dg[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. */ int k; for (iph = 0; iph < NPhase; iph++) { lneed = FALSE; vcs_VolPhase *Vphase = VPhaseList[iph]; if (! Vphase->SingleSpecies) { double sum = 0.0; for (k = 0; k < Vphase->NVolSpecies; k++) { kspec = Vphase->IndSpecies[k]; if (SpeciesUnknownType[kspec] != VCS_SPECIES_TYPE_INTERFACIALVOLTAGE) { sum += soln[kspec]; } if (sum > 0.0) break; } if (sum == 0.0) { lneed = TRUE; } } if (lneed) { double poly = 0.0; for (k = 0; k < Vphase->NVolSpecies; k++) { kspec = Vphase->IndSpecies[k]; irxn = kspec - m_numComponents; if (dg[irxn] > 50.0) dg[irxn] = 50.0; if (dg[irxn] < -50.0) dg[irxn] = -50.0; poly += exp(-dg[irxn])/ActCoeff[kspec]; } /* * 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. */ for (k = 0; k < Vphase->NVolSpecies; k++) { kspec = Vphase->IndSpecies[k]; irxn = kspec - m_numComponents; dg[irxn] = 1.0 - poly; } } } #ifdef DEBUG_NOT for (irxn = 0; irxn < m_numRxnRdc; ++irxn) { checkFinite(dg[irxn]); } #endif } /* vcs_deltag() ************************************************************/ /*****************************************************************************/ /*****************************************************************************/ /*****************************************************************************/ int VCS_SOLVE::vcs_basopt(int ifirst, double aw[], double sa[], double sm[], double ss[], double test, int *usedZeroedSpecies) /************************************************************************** * Choose the optimum basis for the calculations. This is done by * choosing the species with the largest mole fraction * not currently a linear combination of the previous components. * Then, calculate the stoichiometric coefficient matrix for that * basis. * * Calculates the identity of the component species in the mechanism. * Rearranges the solution data to put the component data at the * front of the species list. * * Then, calculates SC(J,I) the formation reactions for all noncomponent * * species in the mechanism. * Also calculates DNG(I) and DNL(I), the net mole change for each * formation reaction. * Also, initializes IR(I) to the default state. * * Input * --------- * IFIRST = If true, the SC, DNG, and DNL are not calculated. * TEST = This is a small negative number dependent upon whether * an estimate is supplied or not. * W(I) = Mole fractions which will be used to construct an * optimal basis from. * * Output * --------- * usedZeroedSpecies = If true, then a species with a zero concentration * was used as a component. The problem may be * converged. * * Other Variables * aw[i] = Mole fraction work space (# species in length) * sa[j] = Gramm-Schmidt orthog work space (nc in length) * ss[j] = Gramm-Schmidt orthog work space (nc in length) * sm[i+j*ne] = QR matrix work space (nc*ne in length) * *************************************************************************/ { int j, k, l, i, jl, ml, jr, lindep, irxn, kspec; int ncTrial; int juse = -1; int jlose = -1; double *dptr, *scrxn_ptr; double tsecond = vcs_second(); #ifdef DEBUG if (vcs_debug_print_lvl >= 2) { plogf(" "); for(i=0; i<77; i++) plogf("-"); plogf("\n"); plogf(" --- Subroutine BASOPT called to "); if (ifirst) plogf("calculate the number of components\n"); else plogf("reevaluate the components\n"); if (vcs_debug_print_lvl >= 2) { plogf("\n"); plogf(" --- Formula Matrix used in BASOPT calculation\n"); plogf(" --- Active | "); for (j = 0; j < m_numElemConstraints; j++) { plogf(" %1d ", ElActive[j]); } plogf("\n"); plogf(" --- Species | "); for (j = 0; j < m_numElemConstraints; j++) { plogf(" "); vcs_print_stringTrunc(ElName[j].c_str(), 4, 1); } plogf("\n"); for (k = 0; k < m_numSpeciesTot; k++) { plogf(" --- "); vcs_print_stringTrunc(SpName[k].c_str(), 11, 1); plogf(" | "); for (j = 0; j < m_numElemConstraints; j++) { plogf("%5.1g", FormulaMatrix[j][k]); } plogf("\n"); } plogf("\n"); } } #endif /* * 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. */ ncTrial = MIN(m_numElemConstraints, m_numSpeciesTot); m_numComponents = ncTrial; *usedZeroedSpecies = FALSE; /* * Use a temporary work array for the mole numbers, aw[] */ vcs_dcopy(aw, VCS_DATA_PTR(soln), m_numSpeciesTot); /* * Take out the Voltage unknowns from consideration */ for (k = 0; k < m_numSpeciesTot; k++) { if (SpeciesUnknownType[k] == VCS_SPECIES_TYPE_INTERFACIALVOLTAGE) { aw[k] = test; } } jr = -1; /* * Top of a loop of some sort based on the index JR. JR is the * current number of component species found. */ do { ++jr; /* - 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. * The first search criteria is always the largest positive * magnitude of the mole number. */ k = vcs_amax(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; int kfound = -1; int minNonZeroes = 100000; int nonZeroesKspec = 0; for (kspec = ncTrial; kspec < m_numSpeciesTot; kspec++) { if (aw[kspec] >= 0.0) { if (SpeciesUnknownType[kspec] != VCS_SPECIES_TYPE_INTERFACIALVOLTAGE) { maxConcPossKspec = 1.0E10; nonZeroesKspec = 0; for (int j = 0; j < m_numElemConstraints; ++j) { if (ElActive[j]) { if (m_elType[j] == VCS_ELEM_TYPE_ABSPOS) { double nu = FormulaMatrix[j][kspec]; if (nu != 0.0) { nonZeroesKspec++; maxConcPossKspec = MIN(gai[j] / nu, maxConcPossKspec); } } } } if ((maxConcPossKspec >= maxConcPoss) || (maxConcPossKspec > 1.0E-5)) { if (nonZeroesKspec <= minNonZeroes) { if (kfound < 0 || nonZeroesKspec < minNonZeroes) { kfound = kspec; } else { // ok we are sitting pretty equal here decide on the raw ss Gibbs energy if (ff[kspec] <= ff[kfound]) { kfound = kspec; } } } if (nonZeroesKspec < minNonZeroes) { minNonZeroes = nonZeroesKspec; } if (maxConcPossKspec > maxConcPoss) { maxConcPoss = maxConcPossKspec; } } } } } if (kfound == -1) { double gmin = 0.0; kfound = k; for (kspec = ncTrial; kspec < m_numSpeciesTot; kspec++) { if (aw[kspec] >= 0.0) { irxn = kspec - ncTrial; if (dg[irxn] < gmin) { gmin = dg[irxn]; kfound = kspec; } } } } k = kfound; } if (aw[k] == test) { m_numComponents = jr; ncTrial = m_numComponents; int numPreDeleted = m_numRxnTot - m_numRxnRdc; if (numPreDeleted != (m_numSpeciesTot - m_numSpeciesRdc)) { plogf("we shouldn't be here\n"); exit(-1); } m_numRxnTot = m_numSpeciesTot - ncTrial; m_numRxnRdc = m_numRxnTot - numPreDeleted; m_numSpeciesRdc = m_numSpeciesTot - numPreDeleted; for (i = 0; i < m_numSpeciesTot; ++i) { ir[i] = ncTrial + i; } #ifdef DEBUG if (vcs_debug_print_lvl >= 2) { plogf(" --- Total number of components found = %3d (ne = %d)\n ", ncTrial, m_numElemConstraints); } #endif 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. */ jl = jr; for (j = 0; j < m_numElemConstraints; ++j) { sm[j + jr*m_numElemConstraints] = 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 (j = 0; j < jl; ++j) { ss[j] = 0.0; for (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 (j = 0; j < jl; ++j) { for (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 (ml = 0; ml < m_numElemConstraints; ++ml) { sa[jr] += SQUARE(sm[ml + jr*m_numElemConstraints]); } /* **************************************************** */ /* **** IF NORM OF NEW ROW .LT. 1E-3 REJECT ********** */ /* **************************************************** */ if (sa[jr] < 1.0e-6) lindep = TRUE; else lindep = FALSE; } while(lindep); /* ****************************************** */ /* **** REARRANGE THE DATA ****************** */ /* ****************************************** */ if (jr != k) { #ifdef DEBUG if (vcs_debug_print_lvl >= 2) { plogf(" --- %-12.12s", (SpName[k]).c_str()); plogf("(%9.2g) replaces %-12.12s", soln[k], SpName[jr].c_str()); plogf("(%9.2g) as component %3d\n", soln[jr], jr); } #endif vcs_switch_pos(FALSE, jr, k); vcsUtil_dsw(aw, jr, k); } #ifdef DEBUG else { if (vcs_debug_print_lvl >= 2) { plogf(" --- %-12.12s", SpName[k].c_str()); plogf("(%9.2g) remains ", soln[k]); plogf(" as component %3d\n", jr); } } #endif /* - entry point from up above */ L_END_LOOP: ; /* * 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 < (ncTrial-1)); if (ifirst) 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 FormulaMatrix[] is argon. Assume that * no species in the matrix problem actually includes argon. * Then, the first row in sm[], below will be indentically * zero. bleh. * What needs to be done is to perform a rearrangement * of the ELEMENTS -> i.e. rearrange, FormulaMatrix, sp, and gai, 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-Jordon 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 (j = 0; j < ncTrial; ++j) { for (i = 0; i < ncTrial; ++i) { sm[i + j*m_numElemConstraints] = FormulaMatrix[i][j]; } } for (i = 0; i < m_numRxnTot; ++i) { k = ir[i]; for (j = 0; j < ncTrial; ++j) { sc[i][j] = FormulaMatrix[j][k]; } } /* * Use Gauss-Jordon block elimination to calculate * the reaction matrix, sc[][]. */ j = vcsUtil_mlequ(sm, m_numElemConstraints, ncTrial, sc[0], m_numRxnTot); if (j == 1) { plogf("vcs_solve_TP ERROR: mlequ returned an error condition\n"); return VCS_FAILED_CONVERGENCE; } /* * 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 = -1; jlose = -1; for (j = 0; j < m_numElemConstraints; j++) { if (! (ElActive[j])) { if (!strcmp((ElName[j]).c_str(), "E")) { juse = j; } } } for (j = 0; j < m_numElemConstraints; j++) { if (ElActive[j]) { if (!strncmp((ElName[j]).c_str(), "cn_", 3)) { jlose = j; } } } for (k = 0; k < m_numSpeciesTot; k++) { if (SpeciesUnknownType[k] == VCS_SPECIES_TYPE_INTERFACIALVOLTAGE) { for (j = 0; j < ncTrial; ++j) { for (i = 0; i < ncTrial; ++i) { if (i == jlose) { sm[i + j*m_numElemConstraints] = FormulaMatrix[juse][j]; } else { sm[i + j*m_numElemConstraints] = FormulaMatrix[i][j]; } } } for (i = 0; i < m_numRxnTot; ++i) { k = ir[i]; for (j = 0; j < ncTrial; ++j) { if (j == jlose) { aw[j] = FormulaMatrix[juse][k]; } else { aw[j] = FormulaMatrix[j][k]; } } } j = vcsUtil_mlequ(sm, m_numElemConstraints, ncTrial, aw, 1); if (j == 1) { plogf("vcs_solve_TP ERROR: mlequ returned an error condition\n"); return VCS_FAILED_CONVERGENCE; } i = k - ncTrial; for (j = 0; j < ncTrial; j++) { sc[i][j] = aw[j]; } } } /* * Calculate the szTmp array for each formation reaction */ for (i = 0; i < m_numRxnTot; i++) { double szTmp = 0.0; for (j = 0; j < ncTrial; j++) { szTmp += fabs(sc[i][j]); } scSize[i] = szTmp; } #ifdef DEBUG if (vcs_debug_print_lvl >= 2) { plogf(" --- Components:"); for (j = 0; j < ncTrial; j++) { plogf(" %3d ", j); } plogf("\n --- Components Moles:"); for (j = 0; j < ncTrial; j++) { plogf("%10.3g", soln[j]); } plogf("\n --- NonComponent| Moles | "); for (j = 0; j < ncTrial; j++) { plogf("%-10.10s", SpName[j].c_str()); } //plogf("| scSize"); plogf("\n"); for (i = 0; i < m_numRxnTot; i++) { plogf(" --- %3d ", ir[i]); plogf("%-10.10s", SpName[ir[i]].c_str()); plogf("|%10.3g|", soln[ir[i]]); for (j = 0; j < ncTrial; j++) { plogf(" %6.2f", sc[i][j]); } //plogf(" | %6.2f", scSize[i]); plogf("\n"); } plogf(" "); for(i=0; i<77; i++) plogf("-"); plogf("\n"); } #endif /* **************************************************** */ /* **** 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 */ vcs_dzero(DnPhase[0], (NSPECIES0)*(NPHASE0)); vcs_izero(PhaseParticipation[0], (NSPECIES0)*(NPHASE0)); /* * Loop over each reaction, creating the change in Phase Moles * array, DnPhase[irxn][iphase], * and the phase participation array, PhaseParticipation[irxn][iphase] */ for (irxn = 0; irxn < m_numRxnTot; ++irxn) { scrxn_ptr = sc[irxn]; dptr = DnPhase[irxn]; kspec = ir[irxn]; int iph = PhaseID[kspec]; int *pp_ptr = PhaseParticipation[irxn]; dptr[iph] = 1.0; pp_ptr[iph]++; for (j = 0; j < ncTrial; ++j) { iph = PhaseID[j]; if (fabs(scrxn_ptr[j]) <= 1.0e-6) { scrxn_ptr[j] = 0.0; } else { dptr[iph] += scrxn_ptr[j]; pp_ptr[iph]++; } } } L_CLEANUP: ; tsecond = vcs_second() - tsecond; m_VCount->Time_basopt += tsecond; (m_VCount->Basis_Opts)++; return VCS_SUCCESS; } /* vcs_basopt() ************************************************************/ /*****************************************************************************/ /*****************************************************************************/ /*****************************************************************************/ int VCS_SOLVE::vcs_species_type(int kspec) /************************************************************************* * * vcs_species_type: * * Evaluate the species category for the input species * return the type in the return variable *************************************************************************/ { int irxn = kspec - m_numComponents; int iph, k; if (kspec < m_numComponents) return VCS_SPECIES_COMPONENT; if (SpeciesUnknownType[kspec] == VCS_SPECIES_TYPE_INTERFACIALVOLTAGE) { return VCS_SPECIES_INTERFACIALVOLTAGE; } iph = PhaseID[kspec]; if (soln[kspec] <= 0.0) { if (dg[irxn] >= 0.0) { /* * We are here when the species is or should be zeroed out */ if (SSPhase[kspec]) { return VCS_SPECIES_ZEROEDSS; } else { if (TPhMoles[iph] == 0.0) return VCS_SPECIES_ZEROEDPHASE; else return VCS_SPECIES_ZEROEDMS; } } /* * The Gibbs free energy for this species is such that * it will pop back into existence. * -> Set it to a major species in anticipation. * -> One exception to this is if a needed component * is also zeroed out. Then, don't pop the phase back into * existence. * -> Another 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. */ for (int j = 0; j < m_numComponents; ++j) { double stoicC = sc[irxn][j]; if (stoicC != 0.0) { double negChangeComp = - stoicC; if (negChangeComp > 0.0) { if (soln[j] < 1.0E-60) { #ifdef DEBUG if (vcs_debug_print_lvl >= 2) { plogf(" --- %s would have popped back into existance but" " needed component %s is zero\n", SpName[kspec].c_str(), SpName[j].c_str()); } #endif if (SSPhase[kspec]) { return VCS_SPECIES_ZEROEDSS; } else { return VCS_SPECIES_ZEROEDMS; } } } } } for (int j = 0; j < m_numElemConstraints; ++j) { int elType = m_elType[j]; if (elType == VCS_ELEM_TYPE_ABSPOS) { double atomComp = FormulaMatrix[j][kspec]; if (atomComp > 0.0) { double maxPermissible = gai[j] / atomComp; if (maxPermissible < VCS_DELETE_MINORSPECIES_CUTOFF) { #ifdef DEBUG if (vcs_debug_print_lvl >= 2) { plogf(" --- %s would have popped back into existance but" " needed element %s is zero\n", SpName[kspec].c_str(), (ElName[j]).c_str()); } #endif if (SSPhase[kspec]) { return VCS_SPECIES_ZEROEDSS; } else { return VCS_SPECIES_ZEROEDMS; } } } } } return VCS_SPECIES_MAJOR; } /* * Always treat species in single species phases as majors */ if (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 */ if (soln[kspec] > (TPhMoles[iph] * 0.1)) 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. */ double szAdj = scSize[irxn] * sqrt(m_numRxnTot); for (k = 0; k < m_numComponents; ++k) { if (!(SSPhase[k])) { if (sc[irxn][k] != 0.0) { if (soln[kspec] * szAdj >= soln[k] * 0.01) { return VCS_SPECIES_MAJOR; } } } } return VCS_SPECIES_MINOR; } /*****************************************************************************/ /*****************************************************************************/ /*****************************************************************************/ void VCS_SOLVE::vcs_chemPotPhase(int iph, const double *const molNum, double * const ac, double * const mu_i, bool do_deleted) /************************************************************************** * * vcs_chemPotPhase: * * We calculate the dimensionless chemical potentials of all species * in a single phase. * * Formula: * --------------- * * Ideal Mixtures: * * fe(I) = ff(I) + ln(z(I)) - ln(tPhMoles_ptr[iph]) * * ( This is equivalent to the adding the log of the * mole fraction onto the standard chemical * potential. ) * * Non-Ideal Mixtures: * ActivityConvention = 0: * fe(I) = ff(I) + ln(ActCoeff[i]z(I)) - ln(tPhMoles_ptr[iph]) * * ( This is equivalent to the adding the log of the * mole fraction multiplied by the activity coefficient * onto the standard chemical potential. ) * * ActivityConvention = 1: -> molality activity formulation * fe(I) = ff(I) + ln(ActCoeff[i]z(I)) - ln(tPhMoles_ptr[iph]) * - ln(Mnaught * m_units) * * note: z(I)/tPhMoles_ptr[iph] = Xmol[i] is the mole fraction * of i in the phase. * * NOTE: * As per the discussion in vcs_dfe(), for small species where the mole * fraction * z(i) < VCS_DELETE_MINORSPECIES_CUTOFF * The chemical potential is calculated as: * fe(I) = ff(I) + ln(ActCoeff[i](VCS_DELETE_MINORSPECIES_CUTOFF)) * * Input * -------- * iph : Phase to be calculated * molNum(i) : Number of moles of species i * (VCS species order) * ff : standard state chemical potentials. These are the * chemical potentials of the standard states at * the same T and P as the solution. * (VCS species order) * Output * ------- * ac[] : Activity coefficients for species in phase * (VCS species order) * mu_i[] : Dimensionless chemical potentials for phase species * (VCS species order) * *************************************************************************/ { vcs_VolPhase *Vphase = VPhaseList[iph]; int nkk = Vphase->NVolSpecies; int k, kspec; #ifdef DEBUG //if (vcs_debug_print_lvl >= 2) { // plogf(" --- Subroutine vcs_chemPotPhase called for phase %d\n", // iph); //} #endif double tMoles = TPhInertMoles[iph]; for (k = 0; k < nkk; k++) { kspec = Vphase->IndSpecies[k]; tMoles += molNum[kspec]; } double tlogMoles = 0.0; if (tMoles > 0.0) { tlogMoles = log(tMoles); } Vphase->setMolesFromVCS(molNum); Vphase->sendToVCSActCoeff(ac); double phi = Vphase->electricPotential(); double Faraday_phi = Faraday_dim * phi; for (k = 0; k < nkk; k++) { kspec = Vphase->IndSpecies[k]; if (kspec >= m_numComponents) { int irxn = kspec - m_numComponents; if (!do_deleted && (spStatus[irxn] == VCS_SPECIES_DELETED)) { continue; } } if (SpeciesUnknownType[kspec] == VCS_SPECIES_TYPE_INTERFACIALVOLTAGE) { #ifdef DEBUG if (molNum[kspec] != phi) { plogf("We have an inconsistency!\n"); exit(-1); } if (Charge[kspec] != -1.0) { plogf("We have an unexpected situation!\n"); exit(-1); } #endif mu_i[kspec] = ff[kspec] + Charge[kspec] * Faraday_phi; } else { if (SSPhase[kspec]) { mu_i[kspec] = ff[kspec] + Charge[kspec] * Faraday_phi; } else if (molNum[kspec] <= VCS_DELETE_MINORSPECIES_CUTOFF) { mu_i[kspec] = ff[kspec] + log(ac[kspec] * VCS_DELETE_MINORSPECIES_CUTOFF) - tlogMoles - SpecLnMnaught[kspec] + Charge[kspec] * Faraday_phi; } else { mu_i[kspec] = ff[kspec] + log(ac[kspec] * molNum[kspec]) - tlogMoles - SpecLnMnaught[kspec] + Charge[kspec] * Faraday_phi; } } } } /*****************************************************************************/ /*****************************************************************************/ /*****************************************************************************/ void VCS_SOLVE::vcs_dfe(double *z, int kk, int ll, int lbot, int ltop) /************************************************************************** * * vcs_dfe: * * We calculate the dimensionless chemical potentials of all species * or certain groups of species here, at a fixed temperature and pressure, * for the input mole vector z[] in the parameter list. * Nondimensionalization is achieved by division by RT. * * Note, for multispecies phases which are currently zeroed out, * the chemical potential is filled out with the standard chemical * potential. * * For species in multispecies phases whose concentration is zero, * we need to set the mole fraction to a very low value. * It's chemical potential * is then calculated using the VCS_DELETE_MINORSPECIES_CUTOFF concentration * to keep numbers positive. * * * Formula: * --------------- * * Ideal Mixtures: * * fe(I) = ff(I) + ln(z(I)) - ln(tPhMoles_ptr[iph]) * * ( This is equivalent to the adding the log of the * mole fraction onto the standard chemical * potential. ) * * Non-Ideal Mixtures: * ActivityConvention = 0: * fe(I) = ff(I) + ln(ActCoeff[i]z(I)) - ln(tPhMoles_ptr[iph]) * * ( This is equivalent to the adding the log of the * mole fraction multiplied by the activity coefficient * onto the standard chemical potential. ) * * ActivityConvention = 1: -> molality activity formulation * fe(I) = ff(I) + ln(ActCoeff[i]z(I)) - ln(tPhMoles_ptr[iph]) * - ln(Mnaught * m_units) * * note: z(I)/tPhMoles_ptr[iph] = Xmol[i] is the mole fraction * of i in the phase. * * NOTE: * As per the discussion above, for small species where the mole * fraction * z(i) < VCS_DELETE_MINORSPECIES_CUTOFF * The chemical potential is calculated as: * fe(I) = ff(I) + ln(ActCoeff[i](VCS_DELETE_MINORSPECIES_CUTOFF)) * * VCS_SPECIES_TYPE_INTERFACIALVOLTAGE * * These chemical potentials refer to electrons in * metal electrodes. They have the following formula * * fe(I) = ff(I) - F V / RT * * F is Faraday's constant. * R = gas constant * T = temperature * V = potential of the interface = phi_electrode - phi_solution * * For these species, the solution vector is V in volts. * * Input * -------- * ll = 0: Calculate for all species * -1: calculate for components and for major non-components * 1: calculate for components and for minor non-components * lbot : restricts the calculation of the chemical potential * ltop to the species between LBOT <= i < LTOP. Usually * LBOT and LTOP will be equal to 0 and MR, respectively. * z(i) : Number of moles of species i * -> This can either be the current solution vector WT() * or the actual solution vector W() * kk 1: Use the tentative values for the total number of * moles in the phases, i.e., use TG1 instead of TG etc. * 0: Use the base values of the total number of * moles in each system. * ff : standard state chemical potentials. These are the * chemical potentials of the standard states at * the same T and P as the solution. * tg : Total Number of moles in the phase. * * *************************************************************************/ { int l1, l2, iph, kspec, irxn; int iphase; double *tPhMoles_ptr; double *tlogMoles; vcs_VolPhase *Vphase; VCS_SPECIES_THERMO *st_ptr; #ifdef DEBUG if (vcs_debug_print_lvl >= 2) { if (ll == 0) { if (lbot != 0) { plogf(" --- Subroutine vcs_dfe called for one species: "); plogf("%-12.12s", SpName[lbot].c_str()); } 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 (kk == 1) plogf(" using tentative solution\n"); else plogf("\n"); } #endif if (kk <= 0) { tPhMoles_ptr = VCS_DATA_PTR(TPhMoles); } else { tPhMoles_ptr = VCS_DATA_PTR(TPhMoles1); } tlogMoles = VCS_DATA_PTR(TmpPhase); /* * Might as well recalculate the phase mole vector * and compare to the storred one. They should be correct. */ double *tPhInertMoles = VCS_DATA_PTR(TPhInertMoles); for (iph = 0; iph < NPhase; iph++) { tlogMoles[iph] = tPhInertMoles[iph]; } for (kspec = 0; kspec < m_numSpeciesTot; kspec++) { if(SpeciesUnknownType[kspec] != VCS_SPECIES_TYPE_INTERFACIALVOLTAGE) { iph = PhaseID[kspec]; tlogMoles[iph] += z[kspec]; } } #ifdef DEBUG for (iph = 0; iph < NPhase; iph++) { if (! vcs_doubleEqual(tlogMoles[iph], tPhMoles_ptr[iph])) { plogf("phase Moles may be off, iph = %d, %20.14g %20.14g \n", iph, tlogMoles[iph], tPhMoles_ptr[iph]); exit(0); } } #endif vcs_dzero(tlogMoles, NPhase); for (iph = 0; iph < NPhase; iph++) { if (tPhMoles_ptr[iph] > 0.0) { tlogMoles[iph] = log(tPhMoles_ptr[iph]); } } /* * Zero the indicator that that tells us the activity coefficients * are current */ vcs_izero(VCS_DATA_PTR(CurrPhAC), NPhase); if (ll != 0) { l1 = lbot; l2 = m_numComponents; } else { l1 = lbot; l2 = ltop; } /* * Calculate activity coefficients for all phases that are * not current */ for (iphase = 0; iphase < NPhase; iphase++) { if (!CurrPhAC[iphase]) { Vphase = VPhaseList[iphase]; if (!Vphase->SingleSpecies) { Vphase->setMolesFromVCS(z); Vphase->sendToVCSActCoeff(VCS_DATA_PTR(ActCoeff)); } phasePhi[iphase] = Vphase->electricPotential(); CurrPhAC[iphase] = 1; } } /* ************************************************************** */ /* **** 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 (kspec = l1; kspec < l2; ++kspec) { iphase = PhaseID[kspec]; if (SpeciesUnknownType[kspec] == VCS_SPECIES_TYPE_INTERFACIALVOLTAGE) { #ifdef DEBUG if (z[kspec] != phasePhi[iphase]) { plogf("We have an inconsistency!\n"); exit(-1); } if (Charge[kspec] != -1.0) { plogf("We have an unexpected situation!\n"); exit(-1); } #endif m_gibbsSpecies[kspec] = ff[kspec] + Charge[kspec] * Faraday_dim * phasePhi[iphase]; } else { if (SSPhase[kspec]) { m_gibbsSpecies[kspec] = ff[kspec]; } else { if (z[kspec] <= VCS_DELETE_MINORSPECIES_CUTOFF) { iph = PhaseID[kspec]; if (tPhMoles_ptr[iph] > 0.0) { m_gibbsSpecies[kspec] = ff[kspec] + log(ActCoeff[kspec] * VCS_DELETE_MINORSPECIES_CUTOFF) - tlogMoles[PhaseID[kspec]] - SpecLnMnaught[kspec] + Charge[kspec] * Faraday_dim * phasePhi[iphase]; } else { m_gibbsSpecies[kspec] = ff[kspec]; } } else { m_gibbsSpecies[kspec] = ff[kspec] + log(ActCoeff[kspec] * z[kspec]) - tlogMoles[PhaseID[kspec]] - SpecLnMnaught[kspec] + Charge[kspec] * Faraday_dim * phasePhi[iphase]; } } } } /* ************************************************ */ /* **** MAJORS ONLY ******************************* */ /* ************************************************ */ if (ll < 0) { for (irxn = 0; irxn < m_numRxnRdc; ++irxn) { if (spStatus[irxn] != VCS_SPECIES_MINOR) { kspec = ir[irxn]; iphase = PhaseID[kspec]; if (SpeciesUnknownType[kspec] == VCS_SPECIES_TYPE_INTERFACIALVOLTAGE) { #ifdef DEBUG if (z[kspec] != phasePhi[iphase]) { plogf("We have an inconsistency!\n"); exit(-1); } if (Charge[kspec] != -1.0) { plogf("We have an unexpected situation!\n"); exit(-1); } #endif m_gibbsSpecies[kspec] = ff[kspec] + Charge[kspec] * Faraday_dim * phasePhi[iphase]; } else { if (SSPhase[kspec]) { m_gibbsSpecies[kspec] = ff[kspec]; } else { if (z[kspec] <= VCS_DELETE_MINORSPECIES_CUTOFF) { iph = PhaseID[kspec]; if (tPhMoles_ptr[iph] > 0.0) { m_gibbsSpecies[kspec] = ff[kspec] + log(ActCoeff[kspec] * VCS_DELETE_MINORSPECIES_CUTOFF) - tlogMoles[PhaseID[kspec]] - SpecLnMnaught[kspec] + Charge[kspec] * Faraday_dim * phasePhi[iphase]; ; } else { m_gibbsSpecies[kspec] = ff[kspec]; } } else { m_gibbsSpecies[kspec] = ff[kspec] + log(ActCoeff[kspec] * z[kspec]) - tlogMoles[PhaseID[kspec]] - SpecLnMnaught[kspec] + Charge[kspec] * Faraday_dim * phasePhi[iphase]; } } } } } /* ************************************************ */ /* **** MINORS ONLY ******************************* */ /* ************************************************ */ } else if (ll > 0) { for (irxn = 0; irxn < m_numRxnRdc; ++irxn) { if (spStatus[irxn] == VCS_SPECIES_MINOR) { kspec = ir[irxn]; iphase = PhaseID[kspec]; if (SpeciesUnknownType[kspec] == VCS_SPECIES_TYPE_INTERFACIALVOLTAGE) { #ifdef DEBUG if (z[kspec] != phasePhi[iphase]) { plogf("We have an inconsistency!\n"); exit(-1); } if (Charge[kspec] != -1.0) { plogf("We have an unexpected situation!\n"); exit(-1); } #endif m_gibbsSpecies[kspec] = ff[kspec] + Charge[kspec] * Faraday_dim * phasePhi[iphase]; ; } else { if (SSPhase[kspec]) { m_gibbsSpecies[kspec] = ff[kspec]; } else { if (z[kspec] <= VCS_DELETE_MINORSPECIES_CUTOFF) { iph = PhaseID[kspec]; if (tPhMoles_ptr[iph] > 0.0) { m_gibbsSpecies[kspec] = ff[kspec] + log(ActCoeff[kspec] * VCS_DELETE_MINORSPECIES_CUTOFF) - tlogMoles[PhaseID[kspec]] - SpecLnMnaught[kspec]; } else { m_gibbsSpecies[kspec] = ff[kspec]; } } else { st_ptr = SpeciesThermo[kspec]; m_gibbsSpecies[kspec] = ff[kspec] + log(ActCoeff[kspec] * z[kspec]) - tlogMoles[PhaseID[kspec]] - SpecLnMnaught[kspec]; } } } } } } #ifdef DEBUG_NOT for (kspec = 0; kspec < m_numSpeciesRdc; kspec++) { checkFinite(fe[kspec]); } #endif } /* vcs_dfe() ***************************************************************/ /*****************************************************************************/ /*****************************************************************************/ /*****************************************************************************/ int vcsUtil_mlequ(double *c, int idem, int n, double *b, int m) /************************************************************************* * * vcs_mlequ: * * Invert an nxn matrix and solve m rhs's * * Solve C X + B = 0; * * This routine uses Gauss elimination and is optimized for the solution * of lots of rhs's. * A crude form of row pivoting is used here. * * * c[i+j*idem] = c_i_j = Matrix to be inverted: i = row number * j = column number * b[i+j*idem] = b_i_j = vectors of rhs's: i = row number * j = column number * (each column is a new rhs) * n = number of rows and columns in the matrix * m = number of rhs to be solved for * idem = first dimension in the fortran calling routine * idem >= n must be true * * Return Value * 1 : Matrix is singluar * 0 : solution is OK * * The solution is returned in the matrix b. *************************************************************************/ { int i, j, k, l; double R; /* * Loop over the rows * -> At the end of each loop, the only nonzero entry in the column * will be on the diagonal. We can therfore just invert the * diagonal at the end of the program to solve the equation system. */ for (i = 0; i < n; ++i) { if (c[i + i * idem] == 0.0) { /* * Do a simple form of row pivoting to find a non-zero pivot */ for (k = i + 1; k < n; ++k) { if (c[k + i * idem] != 0.0) goto FOUND_PIVOT; } plogf("vcs_mlequ ERROR: Encountered a zero column: %d\n", i); return 1; FOUND_PIVOT: ; for (j = 0; j < n; ++j) c[i + j * idem] += c[k + j * idem]; for (j = 0; j < m; ++j) b[i + j * idem] += b[k + j * idem]; } for (l = 0; l < n; ++l) { if (l != i && c[l + i * idem] != 0.0) { R = c[l + i * idem] / c[i + i * idem]; c[l + i * idem] = 0.0; for (j = i+1; j < n; ++j) c[l + j * idem] -= c[i + j * idem] * R; for (j = 0; j < m; ++j) b[l + j * idem] -= b[i + j * idem] * R; } } } /* * The negative in the last expression is due to the form of B upon * input */ for (i = 0; i < n; ++i) { for (j = 0; j < m; ++j) b[i + j * idem] = -b[i + j * idem] / c[i + i*idem]; } return VCS_SUCCESS; } /* vcs_mlequ() *************************************************************/ /*****************************************************************************/ /*****************************************************************************/ /*****************************************************************************/ void vcsUtil_isw(int x[], int i1, int i2) /************************************************************************** * * vcs_isw: * * Switches the value of X(i1) with X(i2) *************************************************************************/ { int t; t = x[i1]; x[i1] = x[i2]; x[i2] = t; } /* vcs_isw() ***************************************************************/ /*****************************************************************************/ /*****************************************************************************/ /*****************************************************************************/ void vcsUtil_dsw(double *x, int i1, int i2) /************************************************************************** * * vcs_dsw: * * Switches the value of X(i1) with X(i2) *************************************************************************/ { double t; t = x[i1]; x[i1] = x[i2]; x[i2] = t; } /* vcs_dsw() ***************************************************************/ /*****************************************************************************/ /*****************************************************************************/ /*****************************************************************************/ void vcsUtil_ssw(char **vstr, int i1, int i2) /************************************************************************** * * vcs_ssw: * * Switches the place of two strings in an array of strings. * (Limited to strings of length less than 24 characters). *************************************************************************/ { char tmp[24]; (void) strncpy(tmp, vstr[i2], (size_t) 24); (void) strncpy(vstr[i2], vstr[i1], (size_t) 24); (void) strncpy(vstr[i1], tmp, (size_t) 24); } /* * * vcs_stsw: * * Switches the place of two strings in a vector of strings. */ void vcsUtil_stsw(std::vector & vstr, int i1, int i2) { std::string tmp(vstr[i2]); vstr[i2] = vstr[i1]; vstr[i1] = tmp; } /*****************************************************************************/ /*****************************************************************************/ /*****************************************************************************/ #ifdef DEBUG void VCS_SOLVE::prneav(void) /************************************************************************* * * Print out and check the elemental abundance vector * *************************************************************************/ { int kerr, i, j; std::vector eav(m_numElemConstraints, 0.0); for (j = 0; j < m_numElemConstraints; ++j) { for (i = 0; i < m_numSpeciesTot; ++i) { if (SpeciesUnknownType[i] == VCS_SPECIES_TYPE_INTERFACIALVOLTAGE) { eav[j] += FormulaMatrix[j][i] * soln[i]; } } } kerr = FALSE; plogf( "--------------------------------------------------"); plogf("ELEMENT ABUNDANCE VECTOR:\n"); plogf(" Element Now Orignal Deviation Type\n"); for (j = 0; j < m_numElemConstraints; ++j) { plogf(" "); plogf("%-2.2s", (ElName[j]).c_str()); plogf(" = %15.6E %15.6E %15.6E %3d\n", eav[j], gai[j], eav[j] - gai[j], m_elType[j]); if (gai[j] != 0.) { if (fabs(eav[j] - gai[j]) > gai[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("--------------------------------------------------\n"); } #endif /*****************************************************************************/ /*****************************************************************************/ /*****************************************************************************/ double VCS_SOLVE::l2normdg(double dg[]) /************************************************************************* * * l2normdg: * * Calculate the norm of the DG vector. * Positive DG for species which don't exist are ignored. ************************************************************************/ { double tmp; int irxn; if (m_numRxnRdc <= 0) return 0.0; for (irxn = 0, tmp = 0.0; irxn < m_numRxnRdc; ++irxn) { if (spStatus[irxn] == VCS_SPECIES_MAJOR || spStatus[irxn] == VCS_SPECIES_MINOR || dg[irxn] < 0.0) { if (spStatus[irxn] != VCS_SPECIES_ZEROEDMS) { tmp += dg[irxn] * dg[irxn]; } } } return (sqrt(tmp / m_numRxnRdc)); } /*****************************************************************************/ /*****************************************************************************/ /*****************************************************************************/ void VCS_SOLVE::vcs_tmoles(void) /************************************************************************** * * vcs_tmoles: * * Calculates the total number of moles of species in all phases. * Calculates the total number of moles in all phases. * Reconciles Phase existence flags with total moles in each phase. *************************************************************************/ { int i; double sum; vcs_VolPhase *Vphase; for (i = 0; i < NPhase; i++) { TPhMoles[i] = TPhInertMoles[i]; } for (i = 0; i < m_numSpeciesTot; i++) { if (SpeciesUnknownType[i] == VCS_SPECIES_TYPE_MOLNUM) { TPhMoles[PhaseID[i]] += soln[i]; } } sum = 0.0; for (i = 0; i < NPhase; i++) { sum += TPhMoles[i]; Vphase = VPhaseList[i]; // Took out because we aren't updating mole fractions in Vphase // Vphase->TMoles = TPhMoles[i]; if (TPhMoles[i] == 0.0) { Vphase->Existence = 0; } else { if (TPhInertMoles[i] > 0.0) { Vphase->Existence = 2; } else { Vphase->Existence = 1; } } } TMoles = sum; } /* vcs_tmoles() ************************************************************/ /*****************************************************************************/ /*****************************************************************************/ /*****************************************************************************/ void VCS_SOLVE::vcs_updateVP (int place) /************************************************************************* * vcs_updateVP() * * This routine uploads the state of the system into all of the * VolumePhase objects in the current problem. * place * 0 -> from soln * 1 -> from wt *************************************************************************/ { vcs_VolPhase *Vphase; for (int i = 0; i < NPhase; i++) { Vphase = VPhaseList[i]; if (place == 0) { Vphase->setMolesFromVCSCheck(VCS_DATA_PTR(soln), VCS_DATA_PTR(TPhMoles), i); } else if (place == 1) { Vphase->setMolesFromVCSCheck(VCS_DATA_PTR(wt), VCS_DATA_PTR(TPhMoles1), i); } else { plogf("we shouldn't be here\n"); exit(-1); } } } /*****************************************************************************/ /*****************************************************************************/ /*****************************************************************************/ void VCS_SOLVE::vcs_switch2D(double * const * const Jac, int k1, int k2) /************************************************************************** * vcs_switch2D: * * Switch rows and columns of a square matrix *************************************************************************/ { int i; register double dtmp; for (i = 0; i < m_numSpeciesTot; i++) { SWAP(Jac[k1][i], Jac[k2][i], dtmp); } for (i = 0; i < m_numSpeciesTot; i++) { SWAP(Jac[i][k1], Jac[i][k2], dtmp); } } /*****************************************************************************/ /*****************************************************************************/ /*****************************************************************************/ void VCS_SOLVE::vcs_switch_pos(int ifunc, int k1, int k2) /************************************************************************** * * vcs_switch_pos: * * Swaps the indecises for all of the global data for two species, k1 * and k2. * * ifunc: If true, switch the species data and the noncomponent reaction * data. This must be called for a non-component species only. * * If false, switch the species data only. Typically, we use this * option when determining the component species and at the * end of the calculation, when we want to return unscrambled * results. *************************************************************************/ { register int j; register double t1 = 0.0; int i1, i2, iph, kp1, kp2; vcs_VolPhase *pv1, *pv2; VCS_SPECIES_THERMO *st_tmp; if (k1 == k2) return; #ifdef DEBUG if (k1 < 0 || k1 > (m_numSpeciesTot - 1) || k2 < 0 || k2 > (m_numSpeciesTot - 1) ) { plogf("vcs_switch_pos: ifunc = 0: inappropriate args: %d %d\n", k1, k2); } #endif /* * Handle the index pointer in the phase structures first */ pv1 = VPhaseList[PhaseID[k1]]; pv2 = VPhaseList[PhaseID[k2]]; kp1 = indPhSp[k1]; kp2 = indPhSp[k2]; #ifdef DEBUG if (pv1->IndSpecies[kp1] != k1) { plogf("Indexing error in program\n"); exit(-1); } if (pv2->IndSpecies[kp2] != k2) { plogf("Indexing error in program\n"); exit(-1); } #endif pv1->IndSpecies[kp1] = k2; pv2->IndSpecies[kp2] = k1; vcsUtil_stsw(SpName, k1, k2); SWAP(soln[k1], soln[k2], t1); SWAP(SpeciesUnknownType[k1], SpeciesUnknownType[k2], j); SWAP(wt[k1], wt[k2], t1); SWAP(ff[k1], ff[k2], t1); SWAP(m_gibbsSpecies[k1], m_gibbsSpecies[k2], t1); SWAP(ds[k1], ds[k2], t1); SWAP(fel[k1], fel[k2], t1); SWAP(feTrial[k1], feTrial[k2], t1); SWAP(SSPhase[k1], SSPhase[k2], j); SWAP(PhaseID[k1], PhaseID[k2], j); SWAP(ind[k1], ind[k2], j); SWAP(indPhSp[k1], indPhSp[k2], j); SWAP(SpecActConvention[k1], SpecActConvention[k2], j); SWAP(SpecLnMnaught[k1], SpecLnMnaught[k2], t1); SWAP(ActCoeff[k1], ActCoeff[k2], t1); SWAP(ActCoeff0[k1], ActCoeff0[k2], t1); SWAP(WtSpecies[k1], WtSpecies[k2], t1); SWAP(Charge[k1], Charge[k2], t1); SWAP(SpeciesThermo[k1], SpeciesThermo[k2], st_tmp); SWAP(VolPM[k1], VolPM[k2], t1); for (j = 0; j < m_numElemConstraints; ++j) { SWAP(FormulaMatrix[j][k1], FormulaMatrix[j][k2], t1); } if (UseActCoeffJac) { vcs_switch2D(dLnActCoeffdMolNum.baseDataAddr(), k1, k2); } /* * Handle the index pointer in the phase structures */ if (ifunc) { /* * Find the noncomponent indecises for the two species */ i1 = k1 - m_numComponents; i2 = k2 - m_numComponents; #ifdef DEBUG if (i1 < 0 || i1 > (m_numRxnTot - 1) || i2 < 0 || i2 > (m_numRxnTot - 1) ) { plogf("switch_pos: ifunc = 1: inappropriate noncomp values: %d %d\n", i1 , i2); } #endif for (j = 0; j < m_numComponents; ++j) { SWAP(sc[i1][j], sc[i2][j], t1); } SWAP(scSize[i1], scSize[i2], t1); for (iph = 0; iph < NPhase; iph++) { SWAP(DnPhase[i1][iph], DnPhase[i2][iph], t1); SWAP(PhaseParticipation[i1][iph], PhaseParticipation[i2][iph], j); } SWAP(dg[i1], dg[i2], t1); SWAP(dgl[i1], dgl[i2], t1); SWAP(spStatus[i1], spStatus[i2], j); /* * We don't want to swap ir[], because the values of ir should * stay the same after the swap * * vcs_isw(ir, i1, i2); */ } } /* vcs_switch_pos() ********************************************************/ /*****************************************************************************/ /*****************************************************************************/ /*****************************************************************************/ static void print_space(int num) { int j; for (j = 0; j < num; j++) plogf(" "); } /**************************************************************************** * * vcs_deltag_Phase(): * * Calculate deltag of formation for all species in a single * phase. It is assumed that the fe[] is up to date for all species. * Howevever, if the phase is currently zereoed out, a subproblem * is calculated to solve for AC[i] and pseudo-X[i] for that * phase. */ void VCS_SOLVE::vcs_deltag_Phase(int iphase, bool doDeleted) { int iph; int irxn, kspec, kcomp; double *dtmp_ptr; int irxnl = m_numRxnRdc; if (doDeleted) irxnl = m_numRxnTot; vcs_VolPhase *vPhase = VPhaseList[iphase]; #ifdef DEBUG if (vcs_debug_print_lvl >= 2) { plogf(" --- Subroutine vcs_deltag_Phase called for phase %d\n", iphase); } #endif /* * Single species Phase */ if (vPhase->SingleSpecies) { kspec = vPhase->IndSpecies[0]; #ifdef DEBUG if (iphase != PhaseID[kspec]) { plogf("vcs_deltag_Phase index error\n"); exit(-1); } #endif if (kspec >= m_numComponents) { irxn = kspec - m_numComponents; dg[irxn] = m_gibbsSpecies[kspec]; dtmp_ptr = sc[irxn]; for (kcomp = 0; kcomp < m_numComponents; ++kcomp) { dg[irxn] += dtmp_ptr[kcomp] * m_gibbsSpecies[kcomp]; } } } /* * Multispecies Phase */ else { bool zeroedPhase = TRUE; for (irxn = 0; irxn < irxnl; ++irxn) { kspec = ir[irxn]; if (SpeciesUnknownType[kspec] != VCS_SPECIES_TYPE_INTERFACIALVOLTAGE) { iph = PhaseID[kspec]; if (iph == iphase ) { if (soln[kspec] > 0.0) zeroedPhase = FALSE; dg[irxn] = m_gibbsSpecies[kspec]; dtmp_ptr = sc[irxn]; for (kcomp = 0; kcomp < m_numComponents; ++kcomp) { dg[irxn] += dtmp_ptr[kcomp] * m_gibbsSpecies[kcomp]; } } } } /* * special section for zeroed phases */ /* ************************************************* */ /* **** 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 (zeroedPhase) { double phaseDG = 1.0; for (irxn = 0; irxn < irxnl; ++irxn) { kspec = ir[irxn]; iph = PhaseID[kspec]; if (iph == iphase) { if (dg[irxn] > 50.0) dg[irxn] = 50.0; if (dg[irxn] < -50.0) dg[irxn] = -50.0; phaseDG -= exp(-dg[irxn])/ActCoeff[kspec]; } } /* * Overwrite the individual dg's with the phase DG. */ for (irxn = 0; irxn < irxnl; ++irxn) { kspec = ir[irxn]; iph = PhaseID[kspec]; if (iph == iphase) { dg[irxn] = 1.0 - phaseDG; } } } } } /**************************************************************************** * * vcs_birthGuess * * Birth guess returns the number of moles of a species * that is coming back to life. or -> whose concentration has * been forced to zero by a constraint for some reason, and needs * to be reinitialized. */ double VCS_SOLVE::vcs_birthGuess(int kspec) { int irxn = kspec - m_numComponents; int soldel = false; double dx = 0.0; if (SpeciesUnknownType[kspec] == VCS_SPECIES_TYPE_INTERFACIALVOLTAGE) { return dx; } double w_kspec = VCS_DELETE_SPECIES_CUTOFF; // Check to make sure that species is zero in the solution vector // If it isn't, we don't know what's happening if (soln[kspec] != 0.0) { w_kspec = 0.0; plogf("we shouldn't be here\n"); exit(-1); } int ss = SSPhase[kspec]; if (!ss) { /* * Logic to handle species in multiple species phases */ #ifdef DEBUG char ANOTE[32]; double dxm = minor_alt_calc(kspec, irxn, &soldel, ANOTE); #else double dxm = minor_alt_calc(kspec, irxn, &soldel); #endif dx = w_kspec + dxm; if (dx > 1.0E-15) { dx = 1.0E-15; } } else { /* * Logic to handle single species phases */ dx = VCS_DELETE_SPECIES_CUTOFF * 100.; } /* * Check to see if the current value of the components * allow the dx. * 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 = sc[irxn]; for (int j = 0; j < m_numComponents; ++j) { // Only loop over element contraints that involve positive def. constraints if (SpeciesUnknownType[j] != VCS_SPECIES_TYPE_INTERFACIALVOLTAGE) { if (soln[j] > 0.0) { double tmp = sc_irxn[j] * dx; if (3.0*(-tmp) > soln[j]) { dx = MIN(dx, - 0.3333* soln[j] / sc_irxn[j]); } } if (soln[j] <= 0.0) { if (sc_irxn[j] < 0.0) { dx = 0.0; } } } } return dx; } /*****************************************************************/ }