diff --git a/Cantera/src/equil/Makefile.in b/Cantera/src/equil/Makefile.in index 0935c7ecc..05ff7bcd3 100644 --- a/Cantera/src/equil/Makefile.in +++ b/Cantera/src/equil/Makefile.in @@ -15,6 +15,10 @@ INCDIR = ../../../build/include/cantera/kernel INSTALL_TSC = ../../../bin/install_tsc do_ranlib = @DO_RANLIB@ +# do_VCSnonideal = @DO_VCSNONIDEAL@ +do_VCSnonideal = 1 +# do_VCSnonideal = 0 + debug_mode = @CANTERA_DEBUG_MODE@ ifeq ($(debug_mode), 1) DEBUG_FLAG=-DDEBUG_MODE @@ -32,7 +36,7 @@ endif # #LOCAL_DEFS=-DDEBUG_BASISOPTIMIZE # -#LOCAL_DEFS=-DDEBUG_BASISOPTIMIZE -DDEBUG_CHEMEQUIL +LOCAL_DEFS=-DDEBUG_BASISOPTIMIZE -DDEBUG_CHEMEQUIL -DCANTERA_SRC_TREE -DALTLINPROG -DDEBUG # PIC_FLAG=@PIC@ @@ -43,28 +47,56 @@ EQUIL_OBJ = BasisOptimize.o ChemEquil.o MultiPhase.o MultiPhaseEquil.o equilibra EQUIL_H = ChemEquil.h MultiPhase.h MultiPhaseEquil.h equil.h PropertyCalculator.h +ifeq ($(do_VCSnonideal), 1) +VCSNONIDEAL_OBJ = vcs_solve_TP.o vcs_VolPhase.o vcs_solve.o vcs_prob.o \ + vcs_TP.o vcs_TV.o vcs_report.o vcs_util.o \ + vcs_IntStarStar.o vcs_DoubleStarStar.o vcs_elem.o \ + vcs_elem_rearrange.o vcs_MultiPhaseEquil.o \ + vcs_nasa_poly.o vcs_nondim.o vcs_Exception.o \ + vcs_funcVtot.o vcs_inest.o vcs_rearrange.o \ + vcs_root1d.o vcs_rxnadj.o vcs_timer_generic.o \ + vcs_SpeciesProperties.o vcs_setMolesLinProg.o \ + vcs_prep.o vcs_species_thermo.o vcs_Gibbs.o +VCSNONIDEAL_H = vcs_internal.h vcs_VolPhase.h vcs_solve.h vcs_prob.h \ + vcs_IntStarStar.h vcs_DoubleStarStar.h vcs_defs.h \ + vcs_MultiPhaseEquil.h vcs_nasa_poly.h vcs_Exception.h \ + vcs_SpeciesProperties.h vcs_species_thermo.h +endif + CXX_INCLUDES = -I../base -I../thermo -I../numerics @CXX_INCLUDES@ LIB = @buildlib@/libequil.a -DEPENDS = $(EQUIL_OBJ:.o=.d) +ifeq ($(do_VCSnonideal), 1) + VLIB=@buildlib@/libVCSnonideal.a +endif -all: $(LIB) .depends +DEPENDS = $(EQUIL_OBJ:.o=.d) $(VCSNONIDEAL_OBJ:.o=.d) + +all: $(LIB) $(VLIB) @(@INSTALL@ -d $(INCDIR)) - @(for lh in $(EQUIL_H) ; do \ + @(for lh in $(EQUIL_H) $(VCSNONIDEAL_H); do \ $(INSTALL_TSC) "$${lh}" $(INCDIR) ; \ done) %.d: - @CXX_DEPENDS@ $(CXX_INCLUDES) $*.cpp > $*.d + @CXX_DEPENDS@ $(CXX_FLAGS) $(CXX_INCLUDES) $*.cpp > $*.d .cpp.o: @CXX@ -c $< $(CXX_FLAGS) $(CXX_INCLUDES) -$(LIB): $(EQUIL_OBJ) $(EQUIL_H) - @ARCHIVE@ $(LIB) $(EQUIL_OBJ) > /dev/null +$(LIB): $(EQUIL_OBJ) $(EQUIL_H) $(VCSNONIDEAL_OBJ) $(VCSNONIDEAL_H) + @ARCHIVE@ $(LIB) $(EQUIL_OBJ) $(VCSNONIDEAL_OBJ) > /dev/null ifeq ($(do_ranlib),1) @RANLIB@ $(LIB) endif +$(VLIB): $(VCSNONIDEAL_OBJ) $(VCS_NONIDEAL_H) + @ARCHIVE@ $(VLIB) $(VCSNONIDEAL_OBJ) > /dev/null +ifeq ($(do_ranlib),1) + @RANLIB@ $(VLIB) +endif + + + clean: @(for lh in dummy.h $(EQUIL_H) ; do \ th=$(INCDIR)/"$${lh}" ; \ diff --git a/Cantera/src/equil/MultiPhaseEquil.cpp b/Cantera/src/equil/MultiPhaseEquil.cpp index 0ec39e84b..86ddcb6f3 100644 --- a/Cantera/src/equil/MultiPhaseEquil.cpp +++ b/Cantera/src/equil/MultiPhaseEquil.cpp @@ -1,5 +1,6 @@ #include "MultiPhaseEquil.h" #include "MultiPhase.h" +#include "MolalityVPSSTP.h" #include "sort.h" #include "global.h" @@ -851,4 +852,172 @@ namespace Cantera { } return maxerr; } + + double MultiPhaseEquil::phaseMoles(index_t iph) { + return m_mix->phaseMoles(iph); + } + +#include + /* + * + */ + void MultiPhaseEquil::reportCSV(const std::string &reportFile) { + int k; + int istart; + int nSpecies; + + double vol = 0.0; + string sName; + int nphase = m_np; + + FILE * FP = fopen(reportFile.c_str(), "w"); + if (!FP) { + printf("Failure to open file\n"); + exit(-1); + } + double Temp = m_mix->temperature(); + double pres = m_mix->pressure(); + vector mf( m_nsp_mix, 1.0); + vector fe(m_nsp_mix, 0.0); + + std::vector VolPM; + std::vector activity; + std::vector ac; + std::vector mu; + std::vector mu0; + std::vector molalities; + + + vol = 0.0; + for (int iphase = 0; iphase < nphase; iphase++) { + istart = m_mix->speciesIndex(0, iphase); + ThermoPhase &tref = m_mix->phase(iphase); + nSpecies = tref.nSpecies(); + VolPM.resize(nSpecies, 0.0); + tref.getMoleFractions(&mf[istart]); + tref.getPartialMolarVolumes(DATA_PTR(VolPM)); + //vcs_VolPhase *volP = m_vprob->VPhaseList[iphase]; + + double TMolesPhase = phaseMoles(iphase); + double VolPhaseVolumes = 0.0; + for (k = 0; k < nSpecies; k++) { + VolPhaseVolumes += VolPM[k] * mf[istart + k]; + } + VolPhaseVolumes *= TMolesPhase; + vol += VolPhaseVolumes; + } + fprintf(FP,"--------------------- VCS_MULTIPHASE_EQUIL FINAL REPORT" + " -----------------------------\n"); + fprintf(FP,"Temperature = %11.5g kelvin\n", Temp); + fprintf(FP,"Pressure = %11.5g Pascal\n", pres); + fprintf(FP,"Total Volume = %11.5g m**3\n", vol); + // fprintf(FP,"Number Basis optimizations = %d\n", m_vprob->m_NumBasisOptimizations); + // fprintf(FP,"Number VCS iterations = %d\n", m_vprob->m_Iterations); + + for (int iphase = 0; iphase < nphase; iphase++) { + istart = m_mix->speciesIndex(0, iphase); + + ThermoPhase &tref = m_mix->phase(iphase); + ThermoPhase *tp = &tref; + tp->getMoleFractions(&mf[istart]); + string phaseName = tref.name(); + // vcs_VolPhase *volP = m_vprob->VPhaseList[iphase]; + double TMolesPhase = phaseMoles(iphase); + //AssertTrace(TMolesPhase == m_mix->phaseMoles(iphase)); + nSpecies = tref.nSpecies(); + activity.resize(nSpecies, 0.0); + ac.resize(nSpecies, 0.0); + + mu0.resize(nSpecies, 0.0); + mu.resize(nSpecies, 0.0); + VolPM.resize(nSpecies, 0.0); + molalities.resize(nSpecies, 0.0); + + int actConvention = tp->activityConvention(); + tp->getActivities(DATA_PTR(activity)); + tp->getActivityCoefficients(DATA_PTR(ac)); + tp->getStandardChemPotentials(DATA_PTR(mu0)); + + tp->getPartialMolarVolumes(DATA_PTR(VolPM)); + tp->getChemPotentials(DATA_PTR(mu)); + double VolPhaseVolumes = 0.0; + for (k = 0; k < nSpecies; k++) { + VolPhaseVolumes += VolPM[k] * mf[istart + k]; + } + VolPhaseVolumes *= TMolesPhase; + vol += VolPhaseVolumes; + if (actConvention == 1) { + MolalityVPSSTP *mTP = static_cast(tp); + tp->getChemPotentials(DATA_PTR(mu)); + mTP->getMolalities(DATA_PTR(molalities)); + tp->getChemPotentials(DATA_PTR(mu)); + + if (iphase == 0) { + fprintf(FP," Name, Phase, PhaseMoles, Mole_Fract, " + "Molalities, ActCoeff, Activity," + "ChemPot_SS0, ChemPot, mole_num, PMVol, Phase_Volume\n"); + + fprintf(FP," , , (kmol), , " + ", , ," + " (kJ/gmol), (kJ/gmol), (kmol), (m**3/kmol), (m**3)\n"); + } + for (k = 0; k < nSpecies; k++) { + sName = tp->speciesName(k); + fprintf(FP,"%12s, %11s, %11.3e, %11.3e, %11.3e, %11.3e, %11.3e," + "%11.3e, %11.3e, %11.3e, %11.3e, %11.3e\n", + sName.c_str(), + phaseName.c_str(), TMolesPhase, + mf[istart + k], molalities[k], ac[k], activity[k], + mu0[k]*1.0E-6, mu[k]*1.0E-6, + mf[istart + k] * TMolesPhase, + VolPM[k], VolPhaseVolumes ); + } + + } else { + if (iphase == 0) { + fprintf(FP," Name, Phase, PhaseMoles, Mole_Fract, " + "Molalities, ActCoeff, Activity," + " ChemPotSS0, ChemPot, mole_num, PMVol, Phase_Volume\n"); + + fprintf(FP," , , (kmol), , " + ", , ," + " (kJ/gmol), (kJ/gmol), (kmol), (m**3/kmol), (m**3)\n"); + } + for (k = 0; k < nSpecies; k++) { + molalities[k] = 0.0; + } + for (k = 0; k < nSpecies; k++) { + sName = tp->speciesName(k); + fprintf(FP,"%12s, %11s, %11.3e, %11.3e, %11.3e, %11.3e, %11.3e, " + "%11.3e, %11.3e,% 11.3e, %11.3e, %11.3e\n", + sName.c_str(), + phaseName.c_str(), TMolesPhase, + mf[istart + k], molalities[k], ac[k], + activity[k], mu0[k]*1.0E-6, mu[k]*1.0E-6, + mf[istart + k] * TMolesPhase, + VolPM[k], VolPhaseVolumes ); + } + } +#ifdef DEBUG + /* + * Check consistency: These should be equal + */ + tp->getChemPotentials(&(fe[istart])); + for (k = 0; k < nSpecies; k++) { + //if (!vcs_doubleEqual(fe[istart+k], mu[k])) { + // fprintf(FP,"ERROR: incompatibility!\n"); + // fclose(FP); + // printf("ERROR: incompatibility!\n"); + // exit(-1); + // } + } +#endif + + } + fclose(FP); + } + + + + } diff --git a/Cantera/src/equil/MultiPhaseEquil.h b/Cantera/src/equil/MultiPhaseEquil.h index 52f4fba4e..0cead38c4 100644 --- a/Cantera/src/equil/MultiPhaseEquil.h +++ b/Cantera/src/equil/MultiPhaseEquil.h @@ -75,6 +75,10 @@ namespace Cantera { index_t componentIndex(index_t n) { return m_species[m_order[n]]; } + void reportCSV(const std::string &reportFile); + + double phaseMoles(index_t iph); + protected: void getComponents(const vector_int& order); diff --git a/Cantera/src/equil/equilibrate.cpp b/Cantera/src/equil/equilibrate.cpp index 55274725d..e26b4bb17 100644 --- a/Cantera/src/equil/equilibrate.cpp +++ b/Cantera/src/equil/equilibrate.cpp @@ -7,6 +7,7 @@ #include "equil.h" #include "ChemEquil.h" #include "MultiPhaseEquil.h" +#include "vcs_MultiPhaseEquil.h" namespace Cantera { @@ -23,6 +24,7 @@ namespace Cantera { doublereal equilibrate(MultiPhase& s, const char* XY, doublereal tol, int maxsteps, int maxiter, int loglevel) { + if (loglevel > 0) { beginLogGroup("equilibrate",loglevel); addLogEntry("multiphase equilibrate function"); @@ -62,6 +64,7 @@ namespace Cantera { throw CanteraError("equilibrate","unsupported option"); return -1.0; } + } /* @@ -103,6 +106,8 @@ namespace Cantera { int retn = -1; int nAttempts = 0; int retnSub = 0; + bool estimateEquil = false; + int printLvlSub = 0; if (loglevel > 0) { beginLogGroup("equilibrate", loglevel); @@ -120,7 +125,37 @@ namespace Cantera { } } while (redo) { - if (solver > 0) { + + if (solver >= 2) { + m = new MultiPhase; + try { + m->addPhase(&s, 1.0); + m->init(); + nAttempts++; + (void) vcs_equilibrate(*m, XY, estimateEquil, printLvlSub, + rtol, maxsteps, maxiter, loglevel-1); + redo = false; + if (loglevel > 0) + addLogEntry("VCSnonideal solver succeeded."); + delete m; + retn = nAttempts; + } + catch (CanteraError err) { + if (loglevel > 0) + addLogEntry("VCSnonideal solver failed."); + delete m; + if (nAttempts < 2) { + if (loglevel > 0) + addLogEntry("Trying single phase ChemEquil solver."); + solver = -1; + } + else { + if (loglevel > 0) + endLogGroup("equilibrate"); + throw err; + } + } + } else if (solver == 1) { m = new MultiPhase; try { m->addPhase(&s, 1.0); diff --git a/Cantera/src/equil/vcs_DoubleStarStar.cpp b/Cantera/src/equil/vcs_DoubleStarStar.cpp new file mode 100644 index 000000000..3d2034d59 --- /dev/null +++ b/Cantera/src/equil/vcs_DoubleStarStar.cpp @@ -0,0 +1,140 @@ +/** + * @file DoubleStarStar.cpp + * + * Header file for class DoubleStarStar + */ + +/* + * $Author$ + * $Revision$ + * $Date$ + */ + +#include "vcs_DoubleStarStar.h" + +namespace VCSnonideal { + +//!Default constructor. Create an empty array. +DoubleStarStar::DoubleStarStar() : + m_nrows(0), + m_ncols(0) +{ + m_data.clear(); + m_colAddr.clear(); +} + +/* + * Constructor. Create an \c m by \c n array, and initialize + * all elements to \c v. + */ +DoubleStarStar::DoubleStarStar(int m, int n, double v) : + m_nrows(n), + m_ncols(m) +{ + m_data.resize(n*m); + std::fill(m_data.begin(), m_data.end(), v); + m_colAddr.resize(m); + for (int jcol = 0; jcol < m_ncols; jcol++) { + m_colAddr[jcol] = &(m_data[jcol*m_nrows]); + } +} + +// copy constructor +DoubleStarStar::DoubleStarStar(const DoubleStarStar& y) { + m_nrows = y.m_nrows; + m_ncols = y.m_ncols; + m_data.resize(m_nrows*m_ncols); + m_data = y.m_data; + m_colAddr.resize(m_ncols); + for (int jcol = 0; jcol < m_ncols; jcol++) { + m_colAddr[jcol] = &(m_data[jcol*m_nrows]); + } +} + +// assignment operator +DoubleStarStar& DoubleStarStar::operator=(const DoubleStarStar& y) { + if (&y == this) return *this; + m_nrows = y.m_nrows; + m_ncols = y.m_ncols; + m_data.resize(m_nrows*m_ncols); + m_data = y.m_data; + m_colAddr.resize(m_ncols); + for (int jcol = 0; jcol < m_ncols; jcol++) { + m_colAddr[jcol] = &(m_data[jcol*m_nrows]); + } + return *this; +} + + +// resize the array, and fill the new entries with 'v' +/* + * @param n This is the number of rows + * @param m This is the number of columns in the new matrix + * @param v Default fill value -> defaults to zero. + */ +void DoubleStarStar::resize(int m, int n, double v) { + std::vector old_data; + bool doCopy = false; + if (m_nrows > 0 && m_ncols > 0) { + if (m_nrows != n) { + doCopy = true; + old_data = m_data; + } + } + m_data.resize(n*m, v); + if (doCopy) { + if (n >= m_nrows && m >= m_ncols) { + for (int jcol = 0; jcol < m_ncols; jcol++) { + for (int irow = 0; irow < m_nrows; irow++) { + m_data[jcol*n + irow] = old_data[jcol*m_nrows + irow]; + } + for (int irow = m_nrows; irow < n; irow++) { + m_data[jcol*n + irow] = v; + } + } + for (int jcol = m_ncols; jcol < m; jcol++) { + for (int irow = 0; irow < n; irow++) { + m_data[jcol*n + irow] = v; + } + } + } else { + std::fill(m_data.begin(), m_data.end(), v); + for (int jcol = 0; jcol < m_ncols; jcol++) { + for (int irow = 0; irow < m_nrows; irow++) { + m_data[jcol*n + irow] = old_data[jcol*m_nrows + irow]; + } + } + } + } + m_nrows = n; + m_ncols = m; + m_colAddr.resize(m_ncols); + for (int jcol = 0; jcol < m_ncols; jcol++) { + m_colAddr[jcol] = &(m_data[jcol*m_nrows]); + } +} + +double * const DoubleStarStar::operator[](int jcol) { + return m_colAddr[jcol]; +} + +const double * const DoubleStarStar::operator[](int jcol) const { + return (const double * const) m_colAddr[jcol]; +} + +double * const * const DoubleStarStar::baseDataAddr() { + return (double * const * const) &(m_colAddr[0]); +} + +// Number of rows +int DoubleStarStar::nRows() const { + return m_nrows; +} + +// Number of columns +int DoubleStarStar::nColumns() const { + return m_ncols; +} + +} + diff --git a/Cantera/src/equil/vcs_DoubleStarStar.h b/Cantera/src/equil/vcs_DoubleStarStar.h new file mode 100644 index 000000000..bb4f55545 --- /dev/null +++ b/Cantera/src/equil/vcs_DoubleStarStar.h @@ -0,0 +1,124 @@ +/** + * @file DoubleStarStar.h + * + * Header file for class DoubleStarStar + */ + +/* + * $Author$ + * $Revision$ + * $Date$ + */ + +#ifndef VCS_DOUBLESTARSTAR_H +#define VCS_DOUBLESTARSTAR_H + +#include + +namespace VCSnonideal { + +//! A class for 2D double arrays storred in column-major +//! (Fortran-compatible) form. +/*! + * In this form, the data entry for an n row, m col + * matrix is + * index = i + (n-1) * j + * where + * Matrix[j][i] + * i = row + * j = column + * The way this is instantiated is via the constructor: + * DoubleStarStar Dmatrix(mcol, mrow); + * + * The way this is referenced is via the notation: + * Dmatrix[icol][irow] + */ +class DoubleStarStar { + +public: + + //! Default constructor. Create an empty array. + DoubleStarStar(); + + //! Constructor. + /*! + * Create an \c nrow by \c mcol double array, and initialize + * all elements to \c v. + * + * @param mcol Number of columns + * @param nrow Number of rows + */ + DoubleStarStar(int mcol, int nrow, double v = 0.0); + + //! copy constructor + /*! + * @param y object to be copied + */ + DoubleStarStar(const DoubleStarStar& y); + + /// assignment operator + /*! + * @param y object to be copied + */ + DoubleStarStar& operator=(const DoubleStarStar& y); + + //! Resize the array, and fill the new entries with 'v' + /*! + * @param mrow This is the number of columns in the new matrix + * @param ncol This is the number of rows + * @param v Default fill value -> defaults to zero. + */ + void resize(int mcol, int nrow, double v = 0.0); + + //! Pointer to the top of the column + /*! + * @param jcol This is the jth column + * + * @return returns the pointer to the top of the jth column + */ + double * const operator[](int jcol); + + //! Returns a const Pointer to the top of the jth column + /*! + * @param jcol This is the jth column + * + * @return returns the pointer to the top of the jth column + */ + const double * const operator[](int jcol) const; + + //! Returns a double ** pointer to the base address + /*! + * This is the second way to get to the data + * This returns a double ** which can later be used in + * Dmatrix[icol][irow] notation to get to the data + */ + double * const * const baseDataAddr(); + + //! Number of rows + int nRows() const; + + //! Number of columns + int nColumns() const; + +private: + //! Storage area + std::vector m_data; + + //! Vector of addresses for the top of the columns + /*! + * Length = mcol + */ + std::vector m_colAddr; + + //! number of rows + int m_nrows; + + //! number of columns + int m_ncols; +}; + +} + +#endif + + diff --git a/Cantera/src/equil/vcs_Exception.cpp b/Cantera/src/equil/vcs_Exception.cpp new file mode 100644 index 000000000..b93ba34b4 --- /dev/null +++ b/Cantera/src/equil/vcs_Exception.cpp @@ -0,0 +1,14 @@ + +#include "vcs_Exception.h" + +namespace VCSnonideal { + +vcsError::vcsError(std::string proc, std::string msg, int errorCode) : + m_proc(proc), + m_msg(msg), + m_errorCode(errorCode) +{ + +} + +} diff --git a/Cantera/src/equil/vcs_Exception.h b/Cantera/src/equil/vcs_Exception.h new file mode 100644 index 000000000..ec0104ef5 --- /dev/null +++ b/Cantera/src/equil/vcs_Exception.h @@ -0,0 +1,45 @@ +/** + * @file vcs_Exception.h + */ +/* + * $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. + */ +#ifndef VCS_EXCEPTION_H +#define VCS_EXCEPTION_H + +#include + +namespace VCSnonideal { + +class vcsError { +public: + vcsError(std::string proc, std::string msg, int errorCode=-1); + virtual ~vcsError(){} +protected: + std::string m_proc; + std::string m_msg; + int m_errorCode; +}; + + +//! Assertion must be true or an error is thrown +/*! + * Assertion must be true or else a vcsError is thrown. A diagnostic + * string indicating where the error + * occured is added to the thrown object. + * + * @param expr Boolean expression that must be true + * @param proc Character string or std:string expression indicating the procedure + * where the assertion failed + * @ingroup errorhandling + */ +#define AssertThrowVCS(expr, proc) ((expr) ? (void) 0 : throw vcsError(proc, std::string("failed assert: ") + #expr,-1)) + +} + +#endif diff --git a/Cantera/src/equil/vcs_Gibbs.cpp b/Cantera/src/equil/vcs_Gibbs.cpp new file mode 100644 index 000000000..b30597037 --- /dev/null +++ b/Cantera/src/equil/vcs_Gibbs.cpp @@ -0,0 +1,101 @@ +/** + * @file vcs_Gibbs.cpp + * Functions which calculate the extrinsic Gibbs Free energies + */ +/* + * $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 "vcs_solve.h" +#include "vcs_internal.h" +#include "vcs_VolPhase.h" +#include "math.h" + +namespace VCSnonideal { + +/*****************************************************************************/ +/*****************************************************************************/ +/*****************************************************************************/ + +double VCS_SOLVE::vcs_Total_Gibbs(double *w, double *fe, double *tPhMoles) + + /************************************************************************* + * + * vcs_Total_Gibbs: + * + * Calculate the total dimensionless Gibbs free energy + * -> Inert species are handled as if they had a standard free + * energy of zero. + * Note, for this algorithm this function should be MONOTONICALLY + * DECREASING. + *************************************************************************/ +{ + double g = 0.0; + int kspec; + int iph; + for (iph = 0; iph < NPhase; iph++) { + vcs_VolPhase *Vphase = VPhaseList[iph]; + if ((TPhInertMoles[iph] > 0.0) && (tPhMoles[iph] > 0.0)) { + g += TPhInertMoles[iph] * + log(TPhInertMoles[iph] / tPhMoles[iph]); + if (Vphase->GasPhase) { + g += TPhInertMoles[iph] * log(Pres); + } + } + } + for (kspec = 0; kspec < m_numSpeciesRdc; ++kspec) { + g += w[kspec] * fe[kspec]; + } + + return g; +} + +/*****************************************************************************/ +/*****************************************************************************/ +/*****************************************************************************/ + +double VCS_SOLVE::vcs_GibbsPhase(int iphase, double *w, double *fe) + + /************************************************************************* + * + * vcs_Total_Gibbs: + * + * Calculate the total dimensionless Gibbs free energy + * -> Inert species are handled as if they had a standard free + * energy of zero. + * Note, for this algorithm this function should be MONOTONICALLY + * DECREASING. + *************************************************************************/ +{ + double g = 0.0; + + vcs_VolPhase *Vphase = VPhaseList[iphase]; + if ((TPhInertMoles[iphase] > 0.0) && (TPhMoles[iphase] > 0.0)) { + g += TPhInertMoles[iphase] * + log(TPhInertMoles[iphase] / TPhMoles[iphase]); + if (Vphase->GasPhase == iphase) { + g += TPhInertMoles[iphase] * log(Pres); + } + } + + for (int kspec = 0; kspec < m_numSpeciesRdc; ++kspec) { + if (PhaseID[kspec] == iphase) { + g += w[kspec] * fe[kspec]; + } + } + + return g; +} + +/*****************************************************************************/ +/*****************************************************************************/ +/*****************************************************************************/ +} + + diff --git a/Cantera/src/equil/vcs_IntStarStar.cpp b/Cantera/src/equil/vcs_IntStarStar.cpp new file mode 100644 index 000000000..772ade325 --- /dev/null +++ b/Cantera/src/equil/vcs_IntStarStar.cpp @@ -0,0 +1,141 @@ +/** + * @file IntStarStar.cpp + * + * Header file for class IntStarStar + */ + +/* + * $Author$ + * $Revision$ + * $Date$ + */ + +#include "vcs_IntStarStar.h" + +namespace VCSnonideal { + +//!Default constructor. Create an empty array. +IntStarStar::IntStarStar() : + m_nrows(0), + m_ncols(0) +{ + m_data.clear(); + m_colAddr.clear(); +} + +/* + * Constructor. Create an \c m by \c n array, and initialize + * all elements to \c v. + */ +IntStarStar::IntStarStar(int m, int n, int v) : + m_nrows(n), + m_ncols(m) +{ + m_data.resize(n*m); + std::fill(m_data.begin(), m_data.end(), v); + m_colAddr.resize(m); + for (int jcol = 0; jcol < m_ncols; jcol++) { + m_colAddr[jcol] = &(m_data[jcol*m_nrows]); + } +} + +// copy constructor +IntStarStar::IntStarStar(const IntStarStar& y) { + m_nrows = y.m_nrows; + m_ncols = y.m_ncols; + m_data.resize(m_nrows*m_ncols); + m_data = y.m_data; + m_colAddr.resize(m_ncols); + for (int jcol = 0; jcol < m_ncols; jcol++) { + m_colAddr[jcol] = &(m_data[jcol*m_nrows]); + } + } + +// assignment operator +IntStarStar& IntStarStar::operator=(const IntStarStar& y) { + if (&y == this) return *this; + m_nrows = y.m_nrows; + m_ncols = y.m_ncols; + m_data.resize(m_nrows*m_ncols); + m_data = y.m_data; + m_colAddr.resize(m_ncols); + for (int jcol = 0; jcol < m_ncols; jcol++) { + m_colAddr[jcol] = &(m_data[jcol*m_nrows]); + } + return *this; +} + + +//! resize the array, and fill the new entries with 'v' +/*! + * @param n This is the number of rows + * @param m This is the number of columns in the new matrix + * @param v Default fill value -> defaults to zero. + */ +void IntStarStar::resize(int m, int n, int v) { + std::vector old_data; + bool doCopy = false; + if (m_nrows > 0 && m_ncols > 0) { + if (m_ncols != m) { + doCopy = true; + old_data = m_data; + } + } + m_data.resize(n*m, v); + if (doCopy) { + if (n >= m_nrows && m >= m_ncols) { + for (int jcol = 0; jcol < m_ncols; jcol++) { + for (int irow = 0; irow < m_nrows; irow++) { + m_data[jcol*m + irow] = old_data[jcol*m_ncols + irow]; + } + for (int irow = m_nrows; irow < n; irow++) { + m_data[jcol*m + irow] = v; + } + } + for (int jcol = m_ncols; jcol < m; jcol++) { + for (int irow = 0; irow < n; irow++) { + m_data[jcol*m + irow] = v; + } + } + } else { + std::fill(m_data.begin(), m_data.end(), v); + for (int jcol = 0; jcol < m_ncols; jcol++) { + for (int irow = 0; irow < m_nrows; irow++) { + m_data[jcol*m + irow] = old_data[jcol*m_ncols + irow]; + } + } + } + } + m_nrows = n; + m_ncols = m; + m_colAddr.resize(m_ncols); + for (int jcol = 0; jcol < m_ncols; jcol++) { + m_colAddr[jcol] = &(m_data[jcol*m_nrows]); + } +} + +int * const IntStarStar::operator[](int jcol) { + return m_colAddr[jcol]; +} + +const int * const IntStarStar::operator[](int jcol) const { + return (const int * const) m_colAddr[jcol]; +} + +int * const * const IntStarStar::baseDataAddr() { + return (int * const * const) &(m_colAddr[0]); +} + +/// Number of rows +int IntStarStar::nRows() const { + return m_nrows; +} + +/// Number of columns +int IntStarStar::nColumns() const { + return m_ncols; +} + +} + + diff --git a/Cantera/src/equil/vcs_IntStarStar.h b/Cantera/src/equil/vcs_IntStarStar.h new file mode 100644 index 000000000..653f42bb7 --- /dev/null +++ b/Cantera/src/equil/vcs_IntStarStar.h @@ -0,0 +1,106 @@ +/** + * @file IntStarStar.h + * + * Header file for class IntStarStar + */ + +/* + * $Author$ + * $Revision$ + * $Date$ + */ + +#ifndef VCS_INTSTARSTAR_H +#define VCS_INTSTARSTAR_H + +#include + +namespace VCSnonideal { + +//! A class for 2D int arrays storred in column-major +//! (Fortran-compatible) form. +/*! + * In this form, the data entry for an n row, m col + * matrix is + * index = i + (n-1) * j + * where + * Matrix[j][i] + * i = row + * j = column + */ +class IntStarStar { + +public: + + //! Default constructor. Create an empty array. + IntStarStar(); + + //! Constructor. + /*! + * Create an \c nrow by \c mcol int array, and initialize + * all elements to \c v. + * + * @param mcol Number of columns + * @param nrow Number of rows + */ + IntStarStar(int mcol, int nrow, int v = 0); + + //! copy constructor + IntStarStar(const IntStarStar& y); + + /// assignment operator + IntStarStar& operator=(const IntStarStar& y); + + + //! Resize the array, and fill the new entries with 'v' + /*! + * @param mcol This is the number of columns in the new matrix + * @param nrow This is the number of rows + * @param v Default fill value -> defaults to zero. + */ + void resize(int mcol, int nrow, int v = 0); + + //! Pointer to the top of the column + /*! + * @param jcol Pointer to the top of the jth column + */ + int * const operator[](int jcol); + + //! Pointer to the top of the column + /*! + * @param j Pointer to the top of the jth column + */ + const int * const operator[](int jcol) const; + + //! Returns a int ** pointer to the base address + /*! + * This is the second way to get to the data + * This returns a int ** which can later be used in + * Imatrix[icol][irow] notation to get to the data + */ + int * const * const baseDataAddr(); + + //! Number of rows + int nRows() const; + + //! Number of columns + int nColumns() const; + +private: + //! Storage area + std::vector m_data; + + std::vector m_colAddr; + + //! number of rows + int m_nrows; + + //! number of columns + int m_ncols; +}; + +} + +#endif + + diff --git a/Cantera/src/equil/vcs_MultiPhaseEquil.cpp b/Cantera/src/equil/vcs_MultiPhaseEquil.cpp new file mode 100644 index 000000000..99ad32569 --- /dev/null +++ b/Cantera/src/equil/vcs_MultiPhaseEquil.cpp @@ -0,0 +1,1774 @@ +/** + * @file vcs_MultiPhaseEquil.cpp + * + */ +/* + * + * $Id$ + */ +#include "vcs_MultiPhaseEquil.h" +#include "vcs_prob.h" +#include "vcs_internal.h" +#include "vcs_VolPhase.h" +#include "vcs_species_thermo.h" +#include "vcs_SpeciesProperties.h" +#include "vcs_VolPhase.h" +#include "vcs_nasa_poly.h" +#include "vcs_solve.h" + +#ifdef CANTERA_SRC_TREE +#include "speciesThermoTypes.h" +#include "IdealSolidSolnPhase.h" +#include "IdealMolalSoln.h" +#include "ChemEquil.h" +#else +#include "cantera/Cantera.h" +#include "cantera/thermo.h" +#include "cantera/kernel/speciesThermoTypes.h" +#include "cantera/kernel/IdealSolidSolnPhase.h" +#include "cantera/kernel/IdealMolalSoln.h" +#include "cantera/kernel/ChemEquil.h" +#endif + + +#include +#include + +using namespace Cantera; +using namespace std; +using namespace VCSnonideal; + +namespace Cantera { + + vcs_MultiPhaseEquil::vcs_MultiPhaseEquil() : + m_vprob(0), + m_mix(0), + m_printLvl(0), + m_vsolvePtr(0) + { + } + + vcs_MultiPhaseEquil::vcs_MultiPhaseEquil(mix_t* mix, int printLvl, + bool start) : + m_vprob(0), + m_mix(0), + m_printLvl(printLvl), + m_vsolvePtr(0) + { + // Debugging level + + int nsp = mix->nSpecies(); + int nel = mix->nElements(); + int nph = mix->nPhases(); + + /* + * Create a VCS_PROB object that describes the equilibrium problem. + * The constructor just mallocs the necessary objects and sizes them. + */ + m_vprob = new VCS_PROB(nsp, nel, nph); + m_mix = mix; + m_vprob->m_printLvl = m_printLvl; + /* + * Work out the details of the VCS_VPROB construction and + * Transfer the current problem to VCS_PROB object + */ + int res = vcs_Cantera_to_vprob(mix, m_vprob); + if (res != 0) { + plogf("problems\n"); + } + } + + vcs_MultiPhaseEquil::~vcs_MultiPhaseEquil() { + delete m_vprob; + m_vprob = 0; + if (m_vsolvePtr) { + delete m_vsolvePtr; + m_vsolvePtr = 0; + } + } + + int vcs_MultiPhaseEquil::equilibrate_TV(int XY, doublereal xtarget, + bool estimateEquil, + int printLvl, doublereal err, + int maxsteps, int loglevel) { + + addLogEntry("problem type","fixed T, V"); + // doublereal dt = 1.0e3; + doublereal Vtarget = m_mix->volume(); + doublereal dVdP; + if ((XY != TV) && (XY != HV) && (XY != UV) && (XY != SV)) { + throw CanteraError("vcs_MultiPhaseEquil::equilibrate_TV", + "Wrong XY flag:" + int2str(XY)); + } + int maxiter = 100; + int iSuccess; + int innerXY; + double Pnow; + if (XY == TV) { + m_mix->setTemperature(xtarget); + } + double Pnew; + bool strt = estimateEquil; + double P1, V1, V2, P2; + doublereal Tlow = 0.5 * m_mix->minTemp();; + doublereal Thigh = 2.0 * m_mix->maxTemp(); + doublereal Vnow, Verr; + int printLvlSub = MAX(0, printLvl - 1); + for (int n = 0; n < maxiter; n++) { + Pnow = m_mix->pressure(); + + beginLogGroup("iteration "+int2str(n)); + switch(XY) { + case TV: + iSuccess = equilibrate_TP(strt, printLvlSub, err, maxsteps, loglevel); + break; + case HV: + innerXY = HP; + iSuccess = equilibrate_HP(xtarget, innerXY, Tlow, Thigh, strt, + printLvlSub, err, maxsteps, loglevel); + break; + case UV: + innerXY = UP; + iSuccess = equilibrate_HP(xtarget, innerXY, Tlow, Thigh, strt, + printLvlSub, err, maxsteps, loglevel); + break; + case SV: + innerXY = SP; + iSuccess = equilibrate_SP(xtarget, Tlow, Thigh, strt, + printLvlSub, err, maxsteps, loglevel); + break; + default: + break; + } + strt = false; + Vnow = m_mix->volume(); + if (n == 0) { + V2 = Vnow; + P2 = Pnow; + } else if (n == 1) { + V1 = Vnow; + P1 = Pnow; + } else { + P2 = P1; + V2 = V1; + P1 = Pnow; + V1 = Vnow; + } + + Verr = fabs((Vtarget - Vnow)/Vtarget); + addLogEntry("P",fp2str(Pnow)); + addLogEntry("V rel error",fp2str(Verr)); + endLogGroup(); + + if (Verr < err) { + addLogEntry("P iterations",int2str(n)); + addLogEntry("Final P",fp2str(Pnow)); + addLogEntry("V rel error",fp2str(Verr)); + goto done; + } + // find dV/dP + if (n > 1) { + dVdP = (V2 - V1) / (P2 - P1); + if (dVdP == 0.0) { + throw CanteraError("vcs_MultiPhase::equilibrate_TV", + "dVdP == 0.0"); + } else { + Pnew = Pnow + (Vtarget - Vnow) / dVdP; + if (Pnew < 0.2 * Pnow) { + Pnew = 0.2 * Pnow; + } + if (Pnew > 3.0 * Pnow) { + Pnew = 3.0 * Pnow; + } + } + + } else { + m_mix->setPressure(Pnow*1.01); + dVdP = (m_mix->volume() - Vnow)/(0.01*Pnow); + Pnew = Pnow + 0.5*(Vtarget - Vnow)/dVdP; + if (Pnew < 0.5* Pnow) { + Pnew = 0.5 * Pnow; + } + if (Pnew > 1.7 * Pnow) { + Pnew = 1.7 * Pnow; + } + + } + m_mix->setPressure(Pnew); + } + throw CanteraError("vcs_MultiPhase::equilibrate_TV", + "No convergence for V"); + + done:; + return iSuccess; + } + + + int vcs_MultiPhaseEquil::equilibrate_HP(doublereal Htarget, + int XY, double Tlow, double Thigh, + bool estimateEquil, + int printLvl, doublereal err, + int maxsteps, int loglevel) { + int maxiter = 100; + int iSuccess; + if (XY != HP && XY != UP) { + throw CanteraError("vcs_MultiPhaseEquil::equilibrate_HP", + "Wrong XP" + XY); + } + bool strt = estimateEquil; + + // Lower bound on T. This will change as we progress in the calculation + if (Tlow <= 0.0) { + Tlow = 0.5 * m_mix->minTemp(); + } + // Upper bound on T. This will change as we progress in the calculation + if (Thigh <= 0.0 || Thigh > 1.0E6) { + Thigh = 2.0 * m_mix->maxTemp(); + } + addLogEntry("problem type","fixed H,P"); + addLogEntry("H target",fp2str(Htarget)); + + doublereal cpb = 1.0, dT, dTa, dTmax, Tnew; + doublereal Hnow; + doublereal Hlow = Undef; + doublereal Hhigh = Undef; + doublereal Herr, HConvErr; + doublereal Tnow = m_mix->temperature(); + int printLvlSub = MAX(printLvl - 1, 0); + + for (int n = 0; n < maxiter; n++) { + + + // start with a loose error tolerance, but tighten it as we get + // close to the final temperature + beginLogGroup("iteration "+int2str(n)); + + try { + Tnow = m_mix->temperature(); + iSuccess = equilibrate_TP(strt, printLvlSub, err, maxsteps, loglevel); + strt = false; + if (XY == UP) { + Hnow = m_mix->IntEnergy(); + } else { + Hnow = m_mix->enthalpy(); + } + double pmoles[10]; + pmoles[0] = m_mix->phaseMoles(0); + double Tmoles = pmoles[0]; + double HperMole = Hnow/Tmoles; + plogf("T = %g, Hnow = %g ,Tmoles = %g, HperMole = %g\n", + Tnow, Hnow, Tmoles, HperMole); + // the equilibrium enthalpy monotonically increases with T; + // if the current value is below the target, then we know the + // current temperature is too low. Set the lower bounds. + + + if (Hnow < Htarget) { + if (Tnow > Tlow) { + Tlow = Tnow; + Hlow = Hnow; + } + } + // the current enthalpy is greater than the target; therefore the + // current temperature is too high. Set the high bounds. + else { + if (Tnow < Thigh) { + Thigh = Tnow; + Hhigh = Hnow; + } + } + if (Hlow != Undef && Hhigh != Undef) { + cpb = (Hhigh - Hlow)/(Thigh - Tlow); + dT = (Htarget - Hnow)/cpb; + dTa = fabs(dT); + dTmax = 0.5*fabs(Thigh - Tlow); + if (dTa > dTmax) dT *= dTmax/dTa; + } + else { + Tnew = sqrt(Tlow*Thigh); + dT = Tnew - Tnow; + if (dT < -200.) dT = 200; + if (dT > 200.) dT = 200.; + } + double acpb = MAX(fabs(cpb), 1.0E-6); + double denom = MAX(fabs(Htarget), acpb); + Herr = Htarget - Hnow; + HConvErr = fabs((Herr)/denom); + addLogEntry("T",fp2str(m_mix->temperature())); + addLogEntry("H",fp2str(Hnow)); + addLogEntry("Herr",fp2str(Herr)); + addLogEntry("H rel error",fp2str(HConvErr)); + addLogEntry("lower T bound",fp2str(Tlow)); + addLogEntry("upper T bound",fp2str(Thigh)); + endLogGroup(); // iteration + if (printLvl > 0) { + plogf(" equilibrate_HP: It = %d, Tcurr = %g Hcurr = %g, Htarget = %g\n", + n, Tnow, Hnow, Htarget); + plogf(" H rel error = %g, cp = %g, HConvErr = %g\n", + Herr, cpb, HConvErr); + } + + if (HConvErr < err) { // || dTa < 1.0e-4) { + addLogEntry("T iterations",int2str(n)); + addLogEntry("Final T",fp2str(m_mix->temperature())); + addLogEntry("H rel error",fp2str(Herr)); + if (printLvl > 0) { + plogf(" equilibrate_HP: CONVERGENCE: Hfinal = %g Tfinal = %g, Its = %d \n", + Hnow, Tnow, n); + plogf(" H rel error = %g, cp = %g, HConvErr = %g\n", + Herr, cpb, HConvErr); + } + goto done; + } + Tnew = Tnow + dT; + if (Tnew < 0.0) Tnew = 0.5*Tnow; + m_mix->setTemperature(Tnew); + + } + catch (CanteraError err) { + if (!estimateEquil) { + addLogEntry("no convergence", + "try estimating composition at the start"); + strt = true; + } + else { + Tnew = 0.5*(Tnow + Thigh); + if (fabs(Tnew - Tnow) < 1.0) Tnew = Tnow + 1.0; + m_mix->setTemperature(Tnew); + addLogEntry("no convergence", + "trying T = "+fp2str(Tnow)); + } + endLogGroup(); + } + + } + addLogEntry("reached max number of T iterations",int2str(maxiter)); + endLogGroup(); + throw CanteraError("MultiPhase::equilibrate_HP", + "No convergence for T"); + done:; + return iSuccess; + } + + int vcs_MultiPhaseEquil::equilibrate_SP(doublereal Starget, + double Tlow, double Thigh, + bool estimateEquil, + int printLvl, doublereal err, + int maxsteps, int loglevel) { + int maxiter = 100; + int iSuccess; + bool strt = estimateEquil; + + // Lower bound on T. This will change as we progress in the calculation + if (Tlow <= 0.0) { + Tlow = 0.5 * m_mix->minTemp(); + } + // Upper bound on T. This will change as we progress in the calculation + if (Thigh <= 0.0 || Thigh > 1.0E6) { + Thigh = 2.0 * m_mix->maxTemp(); + } + addLogEntry("problem type","fixed S,P"); + addLogEntry("S target",fp2str(Starget)); + + doublereal cpb = 1.0, dT, dTa, dTmax, Tnew; + doublereal Snow; + doublereal Slow = Undef; + doublereal Shigh = Undef; + doublereal Serr, SConvErr; + doublereal Tnow = m_mix->temperature(); + if (Tnow < Tlow) { + Tlow = Tnow; + } + if (Tnow > Thigh) { + Thigh = Tnow; + } + int printLvlSub = MAX(printLvl - 1, 0); + + for (int n = 0; n < maxiter; n++) { + + // start with a loose error tolerance, but tighten it as we get + // close to the final temperature + beginLogGroup("iteration "+int2str(n)); + + try { + Tnow = m_mix->temperature(); + iSuccess = equilibrate_TP(strt, printLvlSub, err, maxsteps, loglevel); + strt = false; + Snow = m_mix->entropy(); + double pmoles[10]; + pmoles[0] = m_mix->phaseMoles(0); + double Tmoles = pmoles[0]; + double SperMole = Snow/Tmoles; + plogf("T = %g, Snow = %g ,Tmoles = %g, SperMole = %g\n", + Tnow, Snow, Tmoles, SperMole); + + + // the equilibrium entropy monotonically increases with T; + // if the current value is below the target, then we know the + // current temperature is too low. Set the lower bounds to the + // current condition. + if (Snow < Starget) { + if (Tnow > Tlow) { + Tlow = Tnow; + Slow = Snow; + } else { + if (Slow > Starget) { + if (Snow < Slow) { + Thigh = Tlow; + Shigh = Slow; + Tlow = Tnow; + Slow = Snow; + } + } + } + } + // the current enthalpy is greater than the target; therefore the + // current temperature is too high. Set the high bounds. + else { + if (Tnow < Thigh) { + Thigh = Tnow; + Shigh = Snow; + } + } + if (Slow != Undef && Shigh != Undef) { + cpb = (Shigh - Slow)/(Thigh - Tlow); + dT = (Starget - Snow)/cpb; + Tnew = Tnow + dT; + dTa = fabs(dT); + dTmax = 0.5*fabs(Thigh - Tlow); + if (Tnew > Thigh || Tnew < Tlow) { + dTmax = 1.5*fabs(Thigh - Tlow); + } + dTmax = MIN(dTmax, 300.); + if (dTa > dTmax) dT *= dTmax/dTa; + } else { + Tnew = sqrt(Tlow*Thigh); + dT = Tnew - Tnow; + } + + double acpb = MAX(fabs(cpb), 1.0E-6); + double denom = MAX(fabs(Starget), acpb); + Serr = Starget - Snow; + SConvErr = fabs((Serr)/denom); + addLogEntry("T",fp2str(m_mix->temperature())); + addLogEntry("S",fp2str(Snow)); + addLogEntry("Serr",fp2str(Serr)); + addLogEntry("S rel error",fp2str(SConvErr)); + addLogEntry("lower T bound",fp2str(Tlow)); + addLogEntry("upper T bound",fp2str(Thigh)); + endLogGroup(); // iteration + if (printLvl > 0) { + plogf(" equilibrate_SP: It = %d, Tcurr = %g Scurr = %g, Starget = %g\n", + n, Tnow, Snow, Starget); + plogf(" S rel error = %g, cp = %g, SConvErr = %g\n", + Serr, cpb, SConvErr); + } + + if (SConvErr < err) { // || dTa < 1.0e-4) { + addLogEntry("T iterations",int2str(n)); + addLogEntry("Final T",fp2str(m_mix->temperature())); + addLogEntry("S rel error",fp2str(Serr)); + if (printLvl > 0) { + plogf(" equilibrate_SP: CONVERGENCE: Sfinal = %g Tfinal = %g, Its = %d \n", + Snow, Tnow, n); + plogf(" S rel error = %g, cp = %g, HConvErr = %g\n", + Serr, cpb, SConvErr); + } + goto done; + } + Tnew = Tnow + dT; + if (Tnew < 0.0) Tnew = 0.5*Tnow; + m_mix->setTemperature(Tnew); + + } + catch (CanteraError err) { + if (!estimateEquil) { + addLogEntry("no convergence", + "try estimating composition at the start"); + strt = true; + } + else { + Tnew = 0.5*(Tnow + Thigh); + if (fabs(Tnew - Tnow) < 1.0) Tnew = Tnow + 1.0; + m_mix->setTemperature(Tnew); + addLogEntry("no convergence", + "trying T = "+fp2str(Tnow)); + } + endLogGroup(); + } + + } + addLogEntry("reached max number of T iterations",int2str(maxiter)); + endLogGroup(); + throw CanteraError("MultiPhase::equilibrate_SP", + "No convergence for T"); + done:; + return iSuccess; + } + + + /* + * Equilibrate the solution using the current element abundances + */ + int vcs_MultiPhaseEquil::equilibrate(int XY, bool estimateEquil, + int printLvl, doublereal err, + int maxsteps, int loglevel) { + int iSuccess; + doublereal xtarget; + if (XY == TP) { + iSuccess = equilibrate_TP(estimateEquil, printLvl, err, maxsteps, loglevel); + } else if (XY == HP || XY == UP) { + if (XY == HP) { + xtarget = m_mix->enthalpy(); + } else { + xtarget = m_mix->IntEnergy(); + } + double Tlow = 0.5 * m_mix->minTemp(); + double Thigh = 2.0 * m_mix->maxTemp(); + iSuccess = equilibrate_HP(xtarget, XY, Tlow, Thigh, + estimateEquil, printLvl, err, maxsteps, loglevel); + } else if (XY == SP) { + xtarget = m_mix->entropy(); + double Tlow = 0.5 * m_mix->minTemp(); + double Thigh = 2.0 * m_mix->maxTemp(); + iSuccess = equilibrate_SP(xtarget, Tlow, Thigh, + estimateEquil, printLvl, err, maxsteps, loglevel); + + } else if (XY == TV) { + xtarget = m_mix->temperature(); + iSuccess = equilibrate_TV(XY, xtarget, + estimateEquil, printLvl, err, maxsteps, loglevel); + } else if (XY == HV) { + xtarget = m_mix->enthalpy(); + iSuccess = equilibrate_TV(XY, xtarget, + estimateEquil, printLvl, err, maxsteps, loglevel); + } else if (XY == UV) { + xtarget = m_mix->IntEnergy(); + iSuccess = equilibrate_TV(XY, xtarget, + estimateEquil, printLvl, err, maxsteps, loglevel); + } else if (XY == SV) { + xtarget = m_mix->entropy(); + iSuccess = equilibrate_TV(XY, xtarget, estimateEquil, + printLvl, err, maxsteps, loglevel); + } else { + throw CanteraError(" vcs_MultiPhaseEquil::equilibrate", + "Unsupported Option"); + } + return iSuccess; + } + + /* + * Equilibrate the solution using the current element abundances + */ + int vcs_MultiPhaseEquil::equilibrate_TP(bool estimateEquil, + int printLvl, doublereal err, + int maxsteps, int loglevel) { + // Debugging level + + int maxit = maxsteps;; + double ts = vcs_second(); + int nsp = m_mix->nSpecies(); + int nel = m_mix->nElements(); + int nph = m_mix->nPhases(); + if (m_vprob == 0) { + m_vprob = new VCS_PROB(nsp, nel, nph); + } + m_printLvl = printLvl; + m_vprob->m_printLvl = printLvl; + + /* + * Extract the current state information + * from the MultiPhase object and + * Transfer it to VCS_PROB object. + */ + int res = vcs_Cantera_update_vprob(m_mix, m_vprob); + if (res != 0) { + plogf("problems\n"); + } + + // Set the estimation technique + if (estimateEquil) { + m_vprob->iest = 0; + } else { + m_vprob->iest = -1; + } + + // Check obvious bounds on the temperature and pressure + // NOTE, we may want to do more here with the real bounds + // given by the ThermoPhase objects. + double T = m_mix->temperature(); + if (T <= 0.0) { + throw CanteraError("vcs_MultiPhaseEquil::equilibrate", + "Temperature less than zero on input"); + } + double pres = m_mix->pressure(); + if (pres <= 0.0) { + throw CanteraError("vcs_MultiPhaseEquil::equilibrate", + "Pressure less than zero on input"); + } + + + /* + * Print out the problem specification from the point of + * view of the vprob object. + */ + m_vprob->prob_report(m_printLvl); + + /* + * Call the thermo Program + */ + int ip1 = m_printLvl; + int ipr = m_printLvl; + if (m_printLvl >= 2) { + ip1 = m_printLvl - 1; + } else { + ip1 = 0; + } + if (!m_vsolvePtr) { + m_vsolvePtr = new VCS_SOLVE(); + } + int iSuccess = m_vsolvePtr->vcs(m_vprob, 0, ipr, ip1, maxit); + + /* + * Transfer the information back to the MultiPhase object. + * Note we don't just call setMoles, because some multispecies + * solution phases may be zeroed out, and that would cause a problem + * for that routine. Also, the mole fractions of such zereod out + * phases actually contain information about likely reemergent + * states. + */ + m_mix->updateMoleFractions(); + int kGlob = 0; + for (int ip = 0; ip < m_vprob->NPhase; ip++) { + double phaseMole = 0.0; + ThermoPhase &tref = m_mix->phase(ip); + int nspPhase = tref.nSpecies(); + for (int k = 0; k < nspPhase; k++, kGlob++) { + phaseMole += m_vprob->w[kGlob]; + } + //phaseMole *= 1.0E-3; + m_mix->setPhaseMoles(ip, phaseMole); + } + + double te = vcs_second(); + if (printLvl > 0) { + plogf("\n Results from vcs:\n"); + if (iSuccess != 0) { + plogf("\nVCS FAILED TO CONVERGE!\n"); + } + plogf("\n"); + plogf("Temperature = %g Kelvin\n", m_vprob->T); + plogf("Pressure = %g ", m_vprob->Pres); + if (m_vprob->m_VCS_UnitsFormat == VCS_UNITS_MKS) { + plogf("Pa\n"); + } else { + plogf("atm\n"); + } + plogf("\n"); + plogf("----------------------------------------" + "---------------------\n"); + plogf(" Name Mole_Number"); + if (m_vprob->m_VCS_UnitsFormat == VCS_UNITS_MKS) { + plogf("(kmol)"); + } else { + plogf("(gmol)"); + } + plogf(" Mole_Fraction Chem_Potential"); + if (m_vprob->m_VCS_UnitsFormat == VCS_UNITS_KCALMOL) + plogf(" (kcal/mol)\n"); + else if (m_vprob->m_VCS_UnitsFormat == VCS_UNITS_UNITLESS) + plogf(" (Dimensionless)\n"); + else if (m_vprob->m_VCS_UnitsFormat == VCS_UNITS_KJMOL) + plogf(" (kJ/mol)\n"); + else if (m_vprob->m_VCS_UnitsFormat == VCS_UNITS_KELVIN) + plogf(" (Kelvin)\n"); + else if (m_vprob->m_VCS_UnitsFormat == VCS_UNITS_MKS) + plogf(" (J/kmol)\n"); + plogf("--------------------------------------------------" + "-----------\n"); + for (int i = 0; i < m_vprob->nspecies; i++) { + plogf("%-12s", m_vprob->SpName[i].c_str()); + if (m_vprob->SpeciesUnknownType[i] == VCS_SPECIES_TYPE_INTERFACIALVOLTAGE) { + plogf(" %15.3e %15.3e ", 0.0, m_vprob->mf[i]); + plogf("%15.3e\n", m_vprob->m_gibbsSpecies[i]); + } else { + plogf(" %15.3e %15.3e ", m_vprob->w[i], m_vprob->mf[i]); + if (m_vprob->w[i] <= 0.0) { + int iph = m_vprob->PhaseID[i]; + vcs_VolPhase *VPhase = m_vprob->VPhaseList[iph]; + if (VPhase->NVolSpecies > 1) { + plogf(" -1.000e+300\n"); + } else { + plogf("%15.3e\n", m_vprob->m_gibbsSpecies[i]); + } + } else { + plogf("%15.3e\n", m_vprob->m_gibbsSpecies[i]); + } + } + } + plogf("------------------------------------------" + "-------------------\n"); + + plogf("Total time = %12.6e seconds\n", te - ts); + } + return iSuccess; + } + + + + /************************************************************************** + * + * + */ + void vcs_MultiPhaseEquil::reportCSV(const std::string &reportFile) { + int k; + int istart; + int nSpecies; + + double vol = 0.0; + string sName; + int nphase = m_vprob->NPhase; + + FILE * FP = fopen(reportFile.c_str(), "w"); + if (!FP) { + plogf("Failure to open file\n"); + exit(-1); + } + double Temp = m_mix->temperature(); + double pres = m_mix->pressure(); + double *mf = VCS_DATA_PTR(m_vprob->mf); +#ifdef DEBUG + double *fe = VCS_DATA_PTR(m_vprob->m_gibbsSpecies); +#endif + std::vector VolPM; + std::vector activity; + std::vector ac; + std::vector mu; + std::vector mu0; + std::vector molalities; + + + vol = 0.0; + for (int iphase = 0; iphase < nphase; iphase++) { + istart = m_mix->speciesIndex(0, iphase); + ThermoPhase &tref = m_mix->phase(iphase); + nSpecies = tref.nSpecies(); + VolPM.resize(nSpecies, 0.0); + tref.getPartialMolarVolumes(VCS_DATA_PTR(VolPM)); + vcs_VolPhase *volP = m_vprob->VPhaseList[iphase]; + + double TMolesPhase = volP->TotalMoles(); + double VolPhaseVolumes = 0.0; + for (k = 0; k < nSpecies; k++) { + VolPhaseVolumes += VolPM[k] * mf[istart + k]; + } + VolPhaseVolumes *= TMolesPhase; + vol += VolPhaseVolumes; + } + + fprintf(FP,"--------------------- VCS_MULTIPHASE_EQUIL FINAL REPORT" + " -----------------------------\n"); + fprintf(FP,"Temperature = %11.5g kelvin\n", Temp); + fprintf(FP,"Pressure = %11.5g Pascal\n", pres); + fprintf(FP,"Total Volume = %11.5g m**3\n", vol); + fprintf(FP,"Number Basis optimizations = %d\n", m_vprob->m_NumBasisOptimizations); + fprintf(FP,"Number VCS iterations = %d\n", m_vprob->m_Iterations); + + for (int iphase = 0; iphase < nphase; iphase++) { + istart = m_mix->speciesIndex(0, iphase); + ThermoPhase &tref = m_mix->phase(iphase); + ThermoPhase *tp = &tref; + string phaseName = tref.name(); + vcs_VolPhase *volP = m_vprob->VPhaseList[iphase]; + double TMolesPhase = volP->TotalMoles(); + //AssertTrace(TMolesPhase == m_mix->phaseMoles(iphase)); + nSpecies = tref.nSpecies(); + activity.resize(nSpecies, 0.0); + ac.resize(nSpecies, 0.0); + + mu0.resize(nSpecies, 0.0); + mu.resize(nSpecies, 0.0); + VolPM.resize(nSpecies, 0.0); + molalities.resize(nSpecies, 0.0); + + int actConvention = tp->activityConvention(); + tp->getActivities(VCS_DATA_PTR(activity)); + tp->getActivityCoefficients(VCS_DATA_PTR(ac)); + tp->getStandardChemPotentials(VCS_DATA_PTR(mu0)); + + tp->getPartialMolarVolumes(VCS_DATA_PTR(VolPM)); + tp->getChemPotentials(VCS_DATA_PTR(mu)); + double VolPhaseVolumes = 0.0; + for (k = 0; k < nSpecies; k++) { + VolPhaseVolumes += VolPM[k] * mf[istart + k]; + } + VolPhaseVolumes *= TMolesPhase; + vol += VolPhaseVolumes; + + + if (actConvention == 1) { + MolalityVPSSTP *mTP = static_cast(tp); + tp->getChemPotentials(VCS_DATA_PTR(mu)); + mTP->getMolalities(VCS_DATA_PTR(molalities)); + tp->getChemPotentials(VCS_DATA_PTR(mu)); + + if (iphase == 0) { + fprintf(FP," Name, Phase, PhaseMoles, Mole_Fract, " + "Molalities, ActCoeff, Activity," + "ChemPot_SS0, ChemPot, mole_num, PMVol, Phase_Volume\n"); + + fprintf(FP," , , (kmol), , " + " , , ," + " (J/kmol), (J/kmol), (kmol), (m**3/kmol), (m**3)\n"); + } + for (k = 0; k < nSpecies; k++) { + sName = tp->speciesName(k); + fprintf(FP,"%12s, %11s, %11.3e, %11.3e, %11.3e, %11.3e, %11.3e," + "%11.3e, %11.3e, %11.3e, %11.3e, %11.3e\n", + sName.c_str(), + phaseName.c_str(), TMolesPhase, + mf[istart + k], molalities[k], ac[k], activity[k], + mu0[k]*1.0E-6, mu[k]*1.0E-6, + mf[istart + k] * TMolesPhase, + VolPM[k], VolPhaseVolumes ); + } + + } else { + if (iphase == 0) { + fprintf(FP," Name, Phase, PhaseMoles, Mole_Fract, " + "Molalities, ActCoeff, Activity," + " ChemPotSS0, ChemPot, mole_num, PMVol, Phase_Volume\n"); + + fprintf(FP," , , (kmol), , " + " , , ," + " (J/kmol), (J/kmol), (kmol), (m**3/kmol), (m**3)\n"); + } + for (k = 0; k < nSpecies; k++) { + molalities[k] = 0.0; + } + for (k = 0; k < nSpecies; k++) { + sName = tp->speciesName(k); + fprintf(FP,"%12s, %11s, %11.3e, %11.3e, %11.3e, %11.3e, %11.3e, " + "%11.3e, %11.3e,% 11.3e, %11.3e, %11.3e\n", + sName.c_str(), + phaseName.c_str(), TMolesPhase, + mf[istart + k], molalities[k], ac[k], + activity[k], mu0[k]*1.0E-6, mu[k]*1.0E-6, + mf[istart + k] * TMolesPhase, + VolPM[k], VolPhaseVolumes ); + } + } + +#ifdef DEBUG + /* + * Check consistency: These should be equal + */ + tp->getChemPotentials(fe+istart); + for (k = 0; k < nSpecies; k++) { + if (!vcs_doubleEqual(fe[istart+k], mu[k])) { + fprintf(FP,"ERROR: incompatibility!\n"); + fclose(FP); + plogf("ERROR: incompatibility!\n"); + exit(-1); + } + } +#endif + + } + fclose(FP); + } + + /*! + * This function decides whether a phase has charged species + * or not. + */ + static bool hasChargedSpecies(ThermoPhase *tPhase) { + int nSpPhase = tPhase->nSpecies(); + for (int k = 0; k < nSpPhase; k++) { + if (tPhase->charge(k) != 0.0) { + return true; + } + } + return false; + } + + /********************************************************************** + * + * chargeNeutralityElement(): + * + * This utility routine decides whether a Cantera ThermoPhase needs + * a constraint equation representing the charge neutrality of the + * phase. It does this by searching for charged species. If it + * finds one, and if the phase needs one, then it returns true. + */ + static bool chargeNeutralityElement(ThermoPhase *tPhase) { + int hasCharge = hasChargedSpecies(tPhase); + if (tPhase->chargeNeutralityNecessary()) { + if (hasCharge) { + return true; + } + } + return false; + } + + /************************************************************************* + * + * + * + */ + static int setUpElements(vcs_VolPhase *VolPhase, ThermoPhase *tPhase) { + int e, k, eT; + string ename; + int eFound = -2; + /* + * + */ + int nebase = tPhase->nElements(); + int ne = nebase; + int ns = tPhase->nSpecies(); + + /* + * Decide whether we need an extra element constraint for charge + * neutrality of the phase + */ + bool cne = chargeNeutralityElement(tPhase); + if (cne) { + VolPhase->ChargeNeutralityElement = ne; + ne++; + } + + + + /* + * Assign and malloc structures + */ + VolPhase->nElemConstraints = ne; + + VolPhase->ElName.resize(ne); + + VolPhase->ElActive.resize(ne+1, 1); + VolPhase->m_elType.resize(ne, VCS_ELEM_TYPE_ABSPOS); + + if (VolPhase->ChargeNeutralityElement >= 0) { + VolPhase->m_elType[VolPhase->ChargeNeutralityElement] = + VCS_ELEM_TYPE_CHARGENEUTRALITY; + } + + if (hasChargedSpecies(tPhase)) { + if (cne) { + /* + * We need a charge neutrality constraint. + * We also have an Electron Element. These are + * duplicates of each other. To avoid trouble with + * possible range error conflicts, sometimes we eliminate + * the Electron condition. Flag that condition for elimination + * by toggling the ElActive variable. If we find we need it + * later, we will retoggle ElActive to true. + */ + for (eT = 0; eT < nebase; eT++) { + ename = tPhase->elementName(eT); + if (ename == "E") { + eFound = eT; + VolPhase->ElActive[eT] = 0; + VolPhase->m_elType[eT] = VCS_ELEM_TYPE_ELECTRONCHARGE; + } + } + } else { + for (eT = 0; eT < nebase; eT++) { + ename = tPhase->elementName(eT); + if (ename == "E") { + eFound = eT; + VolPhase->m_elType[eT] = VCS_ELEM_TYPE_ELECTRONCHARGE; + } + } + } + if (eFound == -2) { + eFound = ne; + VolPhase->m_elType[ne] = VCS_ELEM_TYPE_ELECTRONCHARGE; + VolPhase->ElActive[ne] = 0; + string ename = "E"; + VolPhase->ElName[ne] = ename; + ne++; + VolPhase->nElemConstraints = ne; + } + + } + + VolPhase->FormulaMatrix.resize(ne, ns, 0.0); + + VolPhase->SpeciesUnknownType.resize(ns, VCS_SPECIES_TYPE_MOLNUM); + + VolPhase->ElGlobalIndex.resize(ne, -1); + + + e = 0; + for (eT = 0; eT < nebase; eT++) { + ename = tPhase->elementName(eT); + VolPhase->ElName[e] = ename; + e++; + } + + if (cne) { + string pname = tPhase->id(); + if (pname == "") { + char sss[50]; + sprintf(sss, "phase%d", VolPhase->VP_ID); + pname = sss; + } + ename = "cn_" + pname; + e = VolPhase->ChargeNeutralityElement; + VolPhase->ElName[e] = ename; + } + + double * const * const fm = VolPhase->FormulaMatrix.baseDataAddr(); + for (k = 0; k < ns; k++) { + e = 0; + for (eT = 0; eT < nebase; eT++) { + fm[e][k] = tPhase->nAtoms(k, eT); + e++; + } + if (eFound >= 0) { + fm[eFound][k] = - tPhase->charge(k); + } + } + + if (cne) { + for (k = 0; k < ns; k++) { + fm[VolPhase->ChargeNeutralityElement][k] = tPhase->charge(k); + } + } + + + /* + * Here, we figure out what is the species types are + * The logic isn't set in stone, and is just for a particular type + * of problem that I'm solving first. + */ + if (ns == 1) { + if (tPhase->charge(0) != 0.0) { + VolPhase->SpeciesUnknownType[0] = VCS_SPECIES_TYPE_INTERFACIALVOLTAGE; + VolPhase->m_phiVarIndex = 0; + } + } + + return ne; + } + + static void print_char(const char letter, const int num) { + for (int i = 0; i < num; i++) plogf("%c", letter); + } + + /* + * --------------------------------------------------------------------------------------- + * + * HKM -> Work on transfering the current value of the voltages into the + * equilibrium problem. + */ + int vcs_Cantera_to_vprob(MultiPhase *mphase, VCS_PROB *vprob) { + int k; + VCS_SPECIES_THERMO *ts_ptr = 0; + + /* + * Calculate the total number of species and phases in the problem + */ + int totNumPhases = mphase->nPhases(); + int totNumSpecies = mphase->nSpecies(); + + // Problem type has yet to be worked out. + vprob->prob_type = 0; + vprob->nspecies = totNumSpecies; + vprob->ne = 0; + vprob->NPhase = totNumPhases; + vprob->m_VCS_UnitsFormat = VCS_UNITS_MKS; + // Set the initial estimate to a machine generated estimate for now + // We will work out the details later. + vprob->iest = 0; + vprob->T = mphase->temperature(); + vprob->Pres = mphase->pressure(); + vprob->Vol = mphase->volume(); + vprob->Title = "MultiPhase Object"; + + ThermoPhase *tPhase = 0; + + int iSurPhase = -1; + int gasPhase; + + /* + * Loop over the phases, transfering pertinent information + */ + int kT = 0; + for (int iphase = 0; iphase < totNumPhases; iphase++) { + + /* + * Get the thermophase object - assume volume phase + */ + iSurPhase = -1; + tPhase = &(mphase->phase(iphase)); + + /* + * Query Cantera for the equation of state type of the + * current phase. + */ + int eos = tPhase->eosType(); + if (eos == cIdealGas) gasPhase = 1; + else gasPhase = 0; + + /* + * Find out the number of species in the phase + */ + int nSpPhase = tPhase->nSpecies(); + /* + * Find out the name of the phase + */ + string phaseName = tPhase->name(); + + /* + * Call the basic vcs_VolPhase creation routine. + * Properties set here: + * ->PhaseNum = phase number in the thermo problem + * ->GasPhase = Boolean indicating whether it is a gas phase + * ->NumSpecies = number of species in the phase + * ->TMolesInert = Inerts in the phase = 0.0 for cantera + * ->PhaseName = Name of the phase + */ + + vcs_VolPhase *VolPhase = vprob->VPhaseList[iphase]; + VolPhase->resize(iphase, nSpPhase, phaseName.c_str(), 0.0); + VolPhase->GasPhase = gasPhase; + /* + * Tell the vcs_VolPhase pointer about cantera + */ + VolPhase->UseCanteraCalls = true; + VolPhase->m_VCS_UnitsFormat = vprob->m_VCS_UnitsFormat; + VolPhase->setPtrThermoPhase(tPhase); + VolPhase->setTotalMoles(0.0); + /* + * Set the electric potential of the volume phase from the + * ThermoPhase object's value. + */ + VolPhase->setElectricPotential(tPhase->electricPotential()); + /* + * Query the ThermoPhase object to find out what convention + * it uses for the specification of activity and Standard State. + */ + VolPhase->ActivityConvention = tPhase->activityConvention(); + /* + * Assign the value of eqn of state + * -> Handle conflicts here. + */ + switch (eos) { + case cIdealGas: + VolPhase->EqnState = VCS_EOS_IDEAL_GAS; + break; + case cIncompressible: + VolPhase->EqnState = VCS_EOS_CONSTANT; + break; + case cSurf: + plogf("cSurf not handled yet\n"); + exit(-1); + break; + case cStoichSubstance: + VolPhase->EqnState = VCS_EOS_STOICH_SUB; + break; + case cPureFluid: + plogf("cPureFluid not handled yet\n"); + exit(-1); + break; + case cEdge: + plogf("cEdge not handled yet\n"); + exit(-1); + break; + case cIdealSolidSolnPhase0: + case cIdealSolidSolnPhase1: + case cIdealSolidSolnPhase2: + VolPhase->EqnState = VCS_EOS_IDEAL_SOLN; + break; + default: + plogf("Unknown Cantera EOS: %d\n", eos); + VolPhase->EqnState = VCS_EOS_UNK_CANTERA; + if (!VolPhase->UseCanteraCalls) { + plogf("vcs functions asked for, but unimplemented\n"); + exit(-1); + } + break; + } + + /* + * Transfer all of the element information from the + * ThermoPhase object to the vcs_VolPhase object. + * Also decide whether we need a new charge neutrality + * element in the phase to enforce a charge neutrality + * constraint. + */ + setUpElements(VolPhase, tPhase); + + /* + * Combine the element information in the vcs_VolPhase + * object into the vprob object. + */ + vprob->addPhaseElements(VolPhase); + + VolPhase->setState_TP(vprob->T, vprob->Pres); + vector muPhase(tPhase->nSpecies(),0.0); + tPhase->getChemPotentials(&muPhase[0]); + double tMoles = 0.0; + /* + * Loop through each species in the current phase + */ + for (k = 0; k < nSpPhase; k++) { + /* + * Obtain the molecular weight of the species from the + * ThermoPhase object + */ + vprob->WtSpecies[kT] = tPhase->molecularWeight(k); + + /* + * Obtain the charges of the species from the + * ThermoPhase object + */ + vprob->Charge[kT] = tPhase->charge(k); + + /* + * Set the phaseid of the species + */ + vprob->PhaseID[kT] = iphase; + + /* + * Transfer the Species name + */ + string stmp = mphase->speciesName(kT); + vprob->SpName[kT] = stmp; + + /* + * Set the initial estimate of the number of kmoles of the species + * and the mole fraction vector. translate from + * kmol to gmol. + */ + vprob->w[kT] = mphase->speciesMoles(kT); + tMoles += vprob->w[kT]; + vprob->mf[kT] = mphase->moleFraction(kT); + + /* + * transfer chemical potential vector + */ + vprob->m_gibbsSpecies[kT] = muPhase[k]; + /* + * Transfer the type of unknown + */ + vprob->SpeciesUnknownType[kT] = VolPhase->SpeciesUnknownType[k]; + /* + * Transfer the species information from the + * volPhase structure to the VPROB structure + * This includes: + * FormulaMatrix[][] + * VolPhase->IndSpecies[] + */ + vprob->addOnePhaseSpecies(VolPhase, k, kT); + + /* + * Get a pointer to the thermo object + */ + ts_ptr = vprob->SpeciesThermo[kT]; + /* + * Fill in the vcs_SpeciesProperty structure + */ + vcs_SpeciesProperties *sProp = VolPhase->ListSpeciesPtr[k]; + sProp->NumElements = vprob->ne; + sProp->SpName = vprob->SpName[kT]; + sProp->SpeciesThermo = ts_ptr; + sProp->WtSpecies = tPhase->molecularWeight(k); + sProp->FormulaMatrixCol.resize(vprob->ne, 0.0); + for (int e = 0; e < vprob->ne; e++) { + sProp->FormulaMatrixCol[e] = vprob->FormulaMatrix[e][kT]; + } + sProp->Charge = tPhase->charge(k); + sProp->SurfaceSpecies = false; + sProp->VolPM = 0.0; + + /* + * Transfer the thermo specification of the species + * vprob->SpeciesThermo[] + */ + ts_ptr->UseCanteraCalls = VolPhase->UseCanteraCalls; + ts_ptr->m_VCS_UnitsFormat = VolPhase->m_VCS_UnitsFormat; + /* + * Add lookback connectivity into the thermo object first + */ + ts_ptr->IndexPhase = iphase; + ts_ptr->IndexSpeciesPhase = k; + ts_ptr->OwningPhase = VolPhase; + /* + * get a reference to the Cantera species thermo. + */ + SpeciesThermo &sp = tPhase->speciesThermo(); + + int spType; + double c[150]; + double minTemp, maxTemp, refPressure; + sp.reportParams(k, spType, c, minTemp, maxTemp, refPressure); + if (spType == NASA) { + if (ts_ptr->SS0_Params) { + if (ts_ptr->SS0_Model == VCS_SS0_NASA_POLY) { + vcs_nasa_poly_destroy((VCS_NASA_POLY **) &(ts_ptr->SS0_Params)); + ts_ptr->SS0_Params = 0; + } + } + ts_ptr->SS0_Model = VCS_SS0_NASA_POLY; + + ts_ptr->SS0_Params = (void *) + vcs_nasa_poly_create(2, vprob->ne); + ts_ptr->SS0_feSave = 0.0; + ts_ptr->SS0_TSave = -90.; + ts_ptr->SS0_Pref = sp.refPressure(); + if (gasPhase) { + ts_ptr->SSStar_Model = VCS_SSSTAR_IDEAL_GAS; + ts_ptr->SSStar_Vol_Model = VCS_SSVOL_IDEALGAS; + } else { + ts_ptr->SSStar_Model = VCS_SSSTAR_CONSTANT; + ts_ptr->SSStar_Vol_Model = VCS_SSVOL_CONSTANT; + } + ts_ptr->Activity_Coeff_Model = VCS_AC_CONSTANT; + ts_ptr->Activity_Coeff_Params = NULL; + VCS_NASA_POLY * poly_ptr = (VCS_NASA_POLY *)ts_ptr->SS0_Params; + + poly_ptr->Tlimits[0] = minTemp; + poly_ptr->Tlimits[1] = c[0]; + poly_ptr->Tlimits[2] = maxTemp; + + /* + * Cantera takes coefficients A5 and A6 and puts them into + * the first and second spots in the polynomial vector. + * Here, we reverse this operation. + */ + poly_ptr->Acoeff[0][0] = c[3]; + poly_ptr->Acoeff[0][1] = c[4]; + poly_ptr->Acoeff[0][2] = c[5]; + poly_ptr->Acoeff[0][3] = c[6]; + poly_ptr->Acoeff[0][4] = c[7]; + poly_ptr->Acoeff[0][5] = c[1]; + poly_ptr->Acoeff[0][6] = c[2]; + + poly_ptr->Acoeff[1][0] = c[10]; + poly_ptr->Acoeff[1][1] = c[11]; + poly_ptr->Acoeff[1][2] = c[12]; + poly_ptr->Acoeff[1][3] = c[13]; + poly_ptr->Acoeff[1][4] = c[14]; + poly_ptr->Acoeff[1][5] = c[8]; + poly_ptr->Acoeff[1][6] = c[9]; + + } else if (spType == SIMPLE) { + ts_ptr->SS0_Model = VCS_SS0_CONSTANT; + ts_ptr->SS0_T0 = c[0]; + ts_ptr->SS0_H0 = c[1]; + ts_ptr->SS0_S0 = c[2]; + ts_ptr->SS0_Cp0 = c[3]; + if (gasPhase) { + ts_ptr->SSStar_Model = VCS_SSSTAR_IDEAL_GAS; + ts_ptr->SSStar_Vol_Model = VCS_SSVOL_IDEALGAS; + } else { + ts_ptr->SSStar_Model = VCS_SSSTAR_CONSTANT; + ts_ptr->SSStar_Vol_Model = VCS_SSVOL_CONSTANT; + } + ts_ptr->Activity_Coeff_Model = VCS_AC_CONSTANT; + ts_ptr->Activity_Coeff_Params = NULL; + } else { + plogf("vcs_Cantera_convert: Species Type %d not handled\n", + spType); + ts_ptr->SS0_Model = VCS_SS0_NOTHANDLED; + ts_ptr->SSStar_Model = VCS_SSSTAR_NOTHANDLED; + if (!(ts_ptr->UseCanteraCalls )) { + plogf("Cantera calls not being used -> exiting\n"); + exit(-1); + } + } + + /* + * Transfer the Volume Information -> NEEDS WORK + */ + if (gasPhase) { + ts_ptr->SSStar_Vol_Model = VCS_SSVOL_IDEALGAS; + ts_ptr->SSStar_Vol_Params = NULL; + ts_ptr->SSStar_Vol0 = 82.05 * 273.15 / 1.0; + + } else { + std::vector phaseTermCoeff(nSpPhase, 0.0); + int nCoeff; + tPhase->getParameters(nCoeff, VCS_DATA_PTR(phaseTermCoeff)); + ts_ptr->SSStar_Vol_Model = VCS_SSVOL_CONSTANT; + ts_ptr->SSStar_Vol0 = phaseTermCoeff[k]; + } + kT++; + } + + /* + * Now go back through the species in the phase and assign + * a valid mole fraction to all phases, even if the initial + * estimate of the total number of moles is zero. + */ + if (tMoles > 0.0) { + for (k = 0; k < nSpPhase; k++) { + int kTa = VolPhase->IndSpecies[k]; + vprob->mf[kTa] = vprob->w[kTa] / tMoles; + } + } else { + /* + * Perhaps, we could do a more sophisticated treatment below. + * But, will start with this. + */ + for (k = 0; k < nSpPhase; k++) { + int kTa = VolPhase->IndSpecies[k]; + vprob->mf[kTa]= 1.0 / (double) nSpPhase; + } + } + + VolPhase->setMolesFromVCS(VCS_DATA_PTR(vprob->w)); + /* + * Now, calculate a sample naught gibbs free energy calculation + * at the specified temperature. + */ + VolPhase->G0_calc(vprob->T); + double R = vcsUtil_gasConstant(vprob->m_VCS_UnitsFormat); + for (k = 0; k < nSpPhase; k++) { + vcs_SpeciesProperties *sProp = VolPhase->ListSpeciesPtr[k]; + ts_ptr = sProp->SpeciesThermo; + ts_ptr->SS0_feSave = VolPhase->SS0ChemicalPotential[k] / R; + ts_ptr->SS0_TSave = vprob->T; + } + + } + + /* + * Transfer initial element abundances to the vprob object. + * We have to find the mapping index from one to the other + * + */ + vprob->gai.resize(vprob->ne, 0.0); + vprob->set_gai(); + + /* + * Printout the species information: PhaseID's and mole nums + */ + if (vprob->m_printLvl > 0) { + plogf("\n"); print_char('-', 80); plogf("\n"); + plogf(" Phase IDs of species\n"); + plogf(" species phaseID phaseName "); + plogf(" Initial_Estimated_gMols\n"); + for (int i = 0; i < vprob->nspecies; i++) { + int iphase = vprob->PhaseID[i]; + + vcs_VolPhase *VolPhase = vprob->VPhaseList[iphase]; + plogf("%16s %5d %16s", vprob->SpName[i].c_str(), iphase, + VolPhase->PhaseName.c_str()); + plogf(" %-10.5g\n", 1.0E3 * vprob->w[i]); + } + + /* + * Printout of the Phase structure information + */ + plogf("\n"); print_char('-', 80); plogf("\n"); + plogf(" Information about phases\n"); + plogf(" PhaseName PhaseNum SingSpec GasPhase EqnState NumSpec"); + plogf(" TMolesInert Tmoles(gmol)\n"); + + for (int iphase = 0; iphase < vprob->NPhase; iphase++) { + vcs_VolPhase *VolPhase = vprob->VPhaseList[iphase]; + std::string sEOS = string16_EOSType(VolPhase->EqnState); + plogf("%16s %5d %5d %8d %16s %8d %16e ", VolPhase->PhaseName.c_str(), + VolPhase->VP_ID, VolPhase->SingleSpecies, + VolPhase->GasPhase, sEOS.c_str(), + VolPhase->NVolSpecies, VolPhase->TMolesInert * 1.0E3); + plogf("%16e\n", VolPhase->TotalMoles() * 1.0E3); + } + + plogf("\n"); print_char('=', 80); plogf("\n"); + print_char('=', 20); + plogf(" Cantera_to_vprob: END OF PROBLEM STATEMENT "); + print_char('=', 23); plogf("\n"); + print_char('=', 80); plogf("\n\n"); + } + + return VCS_SUCCESS; + } + + // Transfer the current state of mphase into the VCS_PROB object + /* + * The basic problem has already been set up. + */ + int vcs_Cantera_update_vprob(MultiPhase *mphase, VCS_PROB *vprob) { + int totNumPhases = mphase->nPhases(); + int kT = 0; + std::vector tmpMoles; + // Problem type has yet to be worked out. + vprob->prob_type = 0; + // Whether we have an estimate or not gets overwritten on + // the call to the equilibrium solver. + vprob->iest = 0; + vprob->T = mphase->temperature(); + vprob->Pres = mphase->pressure(); + vprob->Vol = mphase->volume(); + ThermoPhase *tPhase = 0; + + for (int iphase = 0; iphase < totNumPhases; iphase++) { + tPhase = &(mphase->phase(iphase)); + vcs_VolPhase *volPhase = vprob->VPhaseList[iphase]; + /* + * Set the electric potential of the volume phase from the + * ThermoPhase object's value. + */ + volPhase->setElectricPotential(tPhase->electricPotential()); + + volPhase->setState_TP(vprob->T, vprob->Pres); + vector muPhase(tPhase->nSpecies(),0.0); + tPhase->getChemPotentials(&muPhase[0]); + /* + * Loop through each species in the current phase + */ + int nSpPhase = tPhase->nSpecies(); + // volPhase->TMoles = 0.0; + tmpMoles.resize(nSpPhase); + for (int k = 0; k < nSpPhase; k++) { + tmpMoles[k] = mphase->speciesMoles(kT); + vprob->w[kT] = mphase->speciesMoles(kT); + vprob->mf[kT] = mphase->moleFraction(kT); + + /* + * transfer chemical potential vector + */ + vprob->m_gibbsSpecies[kT] = muPhase[k]; + + kT++; + } + if (volPhase->m_phiVarIndex >= 0) { + int kglob = volPhase->IndSpecies[volPhase->m_phiVarIndex]; + vprob->w[kglob] = tPhase->electricPotential(); + } + volPhase->setMolesFromVCS(VCS_DATA_PTR(vprob->w)); + if (volPhase->TotalMoles() > 0.0) { + volPhase->Existence = 1; + } else { + volPhase->Existence = 0; + } + } + /* + * Transfer initial element abundances to the vprob object. + * Put them in the front of the object. There may be + * more constraints than there are elements. But, we + * know the element abundances are in the front of the + * vector. + */ + vprob->set_gai(); + + /* + * Printout the species information: PhaseID's and mole nums + */ + if (vprob->m_printLvl > 0) { + plogf("\n"); print_char('-', 80); plogf("\n"); + plogf(" Phase IDs of species\n"); + plogf(" species phaseID phaseName "); + plogf(" Initial_Estimated_gMols\n"); + for (int i = 0; i < vprob->nspecies; i++) { + int iphase = vprob->PhaseID[i]; + + vcs_VolPhase *VolPhase = vprob->VPhaseList[iphase]; + plogf("%16s %5d %16s", vprob->SpName[i].c_str(), iphase, + VolPhase->PhaseName.c_str()); + plogf(" %-10.5g\n", 1.0E3 * vprob->w[i]); + } + + /* + * Printout of the Phase structure information + */ + plogf("\n"); print_char('-', 80); plogf("\n"); + plogf(" Information about phases\n"); + plogf(" PhaseName PhaseNum SingSpec GasPhase EqnState NumSpec"); + plogf(" TMolesInert Tmoles(gmol)\n"); + + for (int iphase = 0; iphase < vprob->NPhase; iphase++) { + vcs_VolPhase *VolPhase = vprob->VPhaseList[iphase]; + std::string sEOS = string16_EOSType(VolPhase->EqnState); + plogf("%16s %5d %5d %8d %16s %8d %16e ", VolPhase->PhaseName.c_str(), + VolPhase->VP_ID, VolPhase->SingleSpecies, + VolPhase->GasPhase, sEOS.c_str(), + VolPhase->NVolSpecies, VolPhase->TMolesInert * 1.0E3); + plogf("%16e\n", VolPhase->TotalMoles() * 1.0E3); + } + + plogf("\n"); print_char('=', 80); plogf("\n"); + print_char('=', 20); + plogf(" Cantera_to_vprob: END OF PROBLEM STATEMENT "); + print_char('=', 23); plogf("\n"); + print_char('=', 80); plogf("\n\n"); + } + + return VCS_SUCCESS; + } + + /* + * Set a single-phase chemical solution to chemical equilibrium. + * This is a convenience function that uses one or the other of + * the two chemical equilibrium solvers. + * + * @param s The object to set to an equilibrium state + * + * @param XY An integer specifying the two properties to be held + * constant. + * + * @param estimateEquil Boolean indicating whether the solver + * should estimate its own initial condition. + * If false, the initial mole fraction vector + * in the %ThermoPhase object is used as the + * initial condition. + * + * @param printLvl Determines the amount of printing that + * gets sent to stdout from the vcs package + * (Note, you may have to compile with debug + * flags to get some printing). + * + * @param solver The equilibrium solver to use. If solver = 0, + * the ChemEquil solver will be used, and if + * solver = 1, the vcs_MultiPhaseEquil solver will + * be used (slower than ChemEquil, + * but more stable). If solver < 0 (default, then + * ChemEquil will be tried first, and if it fails + * vcs_MultiPhaseEquil will be tried. + * + * @param maxsteps The maximum number of steps to take to find + * the solution. + * + * @param maxiter For the MultiPhaseEquil solver only, this is + * the maximum number of outer temperature or + * pressure iterations to take when T and/or P is + * not held fixed. + * + * @param loglevel Controls amount of diagnostic output. loglevel + * = 0 suppresses diagnostics, and increasingly-verbose + * messages are written as loglevel increases. The + * messages are written to a file in HTML format for viewing + * in a web browser. @see HTML_logs + */ + int vcs_equilibrate(thermo_t& s, const char* XY, + bool estimateEquil, int printLvl, + int solver, + doublereal rtol, int maxsteps, int maxiter, + int loglevel) { + MultiPhase* m = 0; + bool redo = true; + int retn = 1; + + beginLogGroup("equilibrate", loglevel); + addLogEntry("Single-phase equilibrate function"); + { + beginLogGroup("arguments"); + addLogEntry("phase",s.id()); + addLogEntry("XY",XY); + addLogEntry("solver",solver); + addLogEntry("rtol",rtol); + addLogEntry("maxsteps",maxsteps); + addLogEntry("maxiter",maxiter); + addLogEntry("loglevel",loglevel); + endLogGroup("arguments"); + } + + if (solver > 0) { + m = new MultiPhase; + try { + /* + * Set the kmoles of the phase to 1.0, arbitrarily. + * It actually doesn't matter. + */ + m->addPhase(&s, 1.0); + m->init(); + + retn = vcs_equilibrate(*m, XY, estimateEquil, printLvl, + rtol, maxsteps, maxiter, loglevel); + redo = false; + addLogEntry("MultiPhaseEquil solver succeeded."); + delete m; + } + catch (CanteraError err) { + addLogEntry("MultiPhaseEquil solver failed."); + delete m; + throw err; + } + } else { + throw CanteraError("vcs_equilibrate", + "ChemEquil not implemented in this interface yet"); + } + + /* + * We are here only for a success + */ + endLogGroup("equilibrate"); + return retn; + } + + int vcs_equilibrate(MultiPhase& s, const char* XY, + bool estimateEquil, int printLvl, + doublereal tol, int maxsteps, int maxiter, + int loglevel) { + int ixy = _equilflag(XY); + int retn = vcs_equilibrate_1(s, ixy, estimateEquil, printLvl, + tol, maxsteps, maxiter, loglevel); + return retn; + }; + + /* + * Set a multiphase mixture to a state of chemical equilibrium. + * This is the top-level driver for multiphase equilibrium. It + * doesn't do much more than call the equilibrate method of class + * MultiPhase, except that it adds some messages to the logfile, + * if loglevel is set > 0. + * + * @return Returns the value of 1 if successful. + * + * @ingroup equil + */ + int vcs_equilibrate_1(MultiPhase& s, int ixy, + bool estimateEquil, int printLvl, + doublereal tol, int maxsteps, int maxiter, int loglevel) { + static int counter = 0; + int retn = 1; + beginLogGroup("equilibrate",loglevel); + addLogEntry("multiphase equilibrate function"); + beginLogGroup("arguments"); + addLogEntry("XY",ixy); + addLogEntry("tol",tol); + addLogEntry("maxsteps",maxsteps); + addLogEntry("maxiter",maxiter); + addLogEntry("loglevel",loglevel); + endLogGroup("arguments"); + int printLvlSub = MAX(0, printLvl); + + s.init(); + + try { + vcs_MultiPhaseEquil *eqsolve = new vcs_MultiPhaseEquil(&s, printLvlSub); + int err = eqsolve->equilibrate(ixy, estimateEquil, printLvlSub, + tol, maxsteps, maxiter); + if (err != 0) { + retn = 0; + } + addLogEntry("Success. Error", err); + endLogGroup("equilibrate"); + // hard code a csv output file. + if (printLvl > 0) { + string reportFile = "vcs_equilibrate_res.csv"; + if (counter > 0) { + reportFile = "vcs_equilibrate_res_" + int2str(counter) + ".csv"; + } + eqsolve->reportCSV(reportFile); + counter++; + } + delete eqsolve; + return retn; + } + catch (CanteraError e) { + addLogEntry("Failure.", lastErrorMessage()); + endLogGroup("equilibrate"); + throw e; + } + + return retn; + } + +} diff --git a/Cantera/src/equil/vcs_MultiPhaseEquil.h b/Cantera/src/equil/vcs_MultiPhaseEquil.h new file mode 100644 index 000000000..481717de6 --- /dev/null +++ b/Cantera/src/equil/vcs_MultiPhaseEquil.h @@ -0,0 +1,362 @@ +/** + * @file vcs_MultiPhase.h + * Interface class for the vcsnonlinear solver + */ + +/* + * $Author$ + * $Revision$ + * $Date$ + */ + + +#ifndef VCS_MULTIPHASEEQUIL_H +#define VCS_MULTIPHASEEQUIL_H + + +#ifdef CANTERA_NOTIN_SRC_TREE +#include "cantera/kernel/ct_defs.h" +#include "cantera/kernel/MultiPhase.h" +#else +#include "ct_defs.h" +#include "MultiPhase.h" +#endif +/* + * VCS_PROB is outside of Cantera namespace + */ +namespace VCSnonideal { + class VCS_PROB; + class VCS_SOLVE; +} + +namespace Cantera { + + int vcs_Cantera_to_vprob(MultiPhase *mphase, VCSnonideal::VCS_PROB *vprob); + + int vcs_Cantera_update_vprob(MultiPhase *mphase, + VCSnonideal::VCS_PROB *vprob); + + //! Set a single-phase chemical solution to chemical equilibrium. + /*! + * The function uses the element abundance vector that is + * currently consistent with the composition within the phase + * itself. Two other thermodynamic quantities, determined by the + * XY string, are held constant during the equilibration. + * This is a convenience function that uses one or the other of + * the two chemical equilibrium solvers. + * + * @param s The object to set to an equilibrium state + * + * @param XY An integer specifying the two properties to be held + * constant. + * + * @param estimateEquil Boolean indicating whether the solver + * should estimate its own initial condition. + * If false, the initial mole fraction vector + * in the %ThermoPhase object is used as the + * initial condition. + * + * @param printLvl Determines the amount of printing that + * gets sent to stdout from the vcs package + * (Note, you may have to compile with debug + * flags to get some printing). + * + * @param solver The equilibrium solver to use. If solver = 0, + * the ChemEquil solver will be used, and if + * solver = 1, the vcs_MultiPhaseEquil solver will + * be used (slower than ChemEquil, + * but more stable). If solver < 0 (default, then + * ChemEquil will be tried first, and if it fails + * vcs_MultiPhaseEquil will be tried. + * + * @param maxsteps The maximum number of steps to take to find + * the solution. + * + * @param maxiter For the MultiPhaseEquil solver only, this is + * the maximum number of outer temperature or + * pressure iterations to take when T and/or P is + * not held fixed. + * + * @param loglevel Controls amount of diagnostic output. loglevel + * = 0 suppresses diagnostics, and increasingly-verbose + * messages are written as loglevel increases. The + * messages are written to a file in HTML format for viewing + * in a web browser. @see HTML_logs + * + * @ingroup equilfunctions + */ + int vcs_equilibrate(thermo_t& s, const char* XY, + bool estimateEquil = false, int printLvl = 0, + int solver = -1, doublereal rtol = 1.0e-9, + int maxsteps = 1000, + int maxiter = 100, int loglevel = -99); + + + //! Set a multi-phase chemical solution to chemical equilibrium. + /*! + * This function uses the vcs_MultiPhaseEquil interface to the + * vcs solver. + * The function uses the element abundance vector that is + * currently consistent with the composition within the phases + * themselves. Two other thermodynamic quantities, determined by the + * XY string, are held constant during the equilibration. + * + * @param s The object to set to an equilibrium state + * + * @param XY A character string representing the unknowns + * to be held constant + * + * @param estimateEquil Boolean indicating whether the solver + * should estimate its own initial condition. + * If false, the initial mole fraction vector + * in the %ThermoPhase object is used as the + * initial condition. + * + * @param printLvl Determines the amount of printing that + * gets sent to stdout from the vcs package + * (Note, you may have to compile with debug + * flags to get some printing). + * + * @param maxsteps The maximum number of steps to take to find + * the solution. + * + * @param maxiter For the MultiPhaseEquil solver only, this is + * the maximum number of outer temperature or + * pressure iterations to take when T and/or P is + * not held fixed. + * + * @param loglevel Controls amount of diagnostic output. loglevel + * = 0 suppresses diagnostics, and increasingly-verbose + * messages are written as loglevel increases. The + * messages are written to a file in HTML format for viewing + * in a web browser. @see HTML_logs + * + * @ingroup equilfunctions + */ + int vcs_equilibrate(MultiPhase& s, const char* XY, + bool estimateEquil = false, int printLvl = 0, + doublereal rtol = 1.0e-9, int maxsteps = 1000, + int maxiter = 100, int loglevel = -99); + + //! Set a multi-phase chemical solution to chemical equilibrium. + /*! + * This function uses the vcs_MultiPhaseEquil interface to the + * vcs solver. + * The function uses the element abundance vector that is + * currently consistent with the composition within the phases + * themselves. Two other thermodynamic quantities, determined by the + * XY string, are held constant during the equilibration. + * + * @param s The object to set to an equilibrium state + * + * @param XY An integer specifying the two properties to be held + * constant. + * + * @param estimateEquil Boolean indicating whether the solver + * should estimate its own initial condition. + * If false, the initial mole fraction vector + * in the %ThermoPhase object is used as the + * initial condition. + * + * @param printLvl Determines the amount of printing that + * gets sent to stdout from the vcs package + * (Note, you may have to compile with debug + * flags to get some printing). + * + * @param maxsteps The maximum number of steps to take to find + * the solution. + * + * @param maxiter For the MultiPhaseEquil solver only, this is + * the maximum number of outer temperature or + * pressure iterations to take when T and/or P is + * not held fixed. + * + * @param loglevel Controls amount of diagnostic output. loglevel + * = 0 suppresses diagnostics, and increasingly-verbose + * messages are written as loglevel increases. The + * messages are written to a file in HTML format for viewing + * in a web browser. @see HTML_logs + * + * @ingroup equilfunctions + */ + int vcs_equilibrate_1(MultiPhase& s, int ixy, + bool estimateEquil = false, int printLvl = 0, + doublereal rtol = 1.0e-9, int maxsteps = 1000, + int maxiter = 100, int loglevel = -99); + + //! Cantera's Interface to the Multiphase chemical equilibrium solver. + /*! + * Class MultiPhaseEquil is designed to be used to set a mixture + * containing one or more phases to a state of chemical equilibrium. + * + * @ingroup equilfunctions + */ + class vcs_MultiPhaseEquil { + public: + //! Shorthand for the MultiPhase mixture object used by Cantera + //! to store information about multiple phases + typedef MultiPhase mix_t; + typedef size_t index_t; + typedef DenseMatrix matrix_t; + + vcs_MultiPhaseEquil(); + vcs_MultiPhaseEquil(mix_t* mix, int printLvl, bool start=true); + + virtual ~vcs_MultiPhaseEquil(); + + int constituent(index_t m) { + if (m < m_nel) return m_order[m]; + else return -1; + } + + void getStoichVector(index_t rxn, vector_fp& nu) { + index_t k; + nu.resize(m_nsp, 0.0); + if (rxn > m_nsp - m_nel) return; + for (k = 0; k < m_nsp; k++) { + nu[m_order[k]] = m_N(k, rxn); + } + } + + int iterations() { return m_iter; } + + //! Equilibrate the solution using the current element abundances + //! storred in the MultiPhase object + /*! + * Use the vcs algorithm to equilibrate the current multiphase + * mixture. + * + * @param XY Integer representing what two thermo quantities + * are held constant during the equilibration + * + * @param estimateEquil Boolean indicating whether the solver + * should estimate its own initial condition. + * If false, the initial mole fraction vector + * in the %ThermoPhase object is used as the + * initial condition. + * + * @param printLvl Determines the amount of printing that + * gets sent to stdout from the vcs package + * (Note, you may have to compile with debug + * flags to get some printing). + * @param err Internal error level + * @param maxsteps max steps allowed. + * @param loglevel for + */ + int equilibrate(int XY, bool estimateEquil = false, + int printLvl= 0, doublereal err = 1.0e-6, + int maxsteps = 1000, int loglevel=-99); + + //! Equilibrate the solution using the current element abundances + //! storred in the MultiPhase object using constant T and P + /*! + * Use the vcs algorithm to equilibrate the current multiphase + * mixture. + * + * @param estimateEquil Boolean indicating whether the solver + * should estimate its own initial condition. + * If false, the initial mole fraction vector + * in the %ThermoPhase object is used as the + * initial condition. + * + * @param printLvl Determines the amount of printing that + * gets sent to stdout from the vcs package + * (Note, you may have to compile with debug + * flags to get some printing). + * @param err Internal error level + * @param maxsteps max steps allowed. + * @param loglevel for + */ + int equilibrate_TP(bool estimateEquil = false, + int printLvl= 0, doublereal err = 1.0e-6, + int maxsteps = 1000, int loglevel=-99); + + //! Equilibrate the solution using the current element abundances + //! storred in the MultiPhase object using constant H and P + /*! + * Use the vcs algorithm to equilibrate the current multiphase + * mixture. + * + * @param estimateEquil Boolean indicating whether the solver + * should estimate its own initial condition. + * If false, the initial mole fraction vector + * in the %ThermoPhase object is used as the + * initial condition. + * + * @param printLvl Determines the amount of printing that + * gets sent to stdout from the vcs package + * (Note, you may have to compile with debug + * flags to get some printing). + * @param err Internal error level + * @param maxsteps max steps allowed. + * @param loglevel for + */ + int equilibrate_HP(doublereal Htarget, int XY, double Tlow, double Thigh, + bool estimateEquil = false, + int printLvl = 0, doublereal err = 1.0E-6, + int maxsteps = 1000, int loglevel=-99); + + + int equilibrate_SP(doublereal Starget, double Tlow, double Thigh, + bool estimateEquil = false, + int printLvl = 0, doublereal err = 1.0E-6, + int maxsteps = 1000, int loglevel=-99); + + int equilibrate_TV(int XY, doublereal xtarget, + bool estimateEquil, + int printLvl, doublereal err, + int maxsteps, int loglevel); + + void reportCSV(const std::string &reportFile); + + index_t componentIndex(index_t n) { return m_species[m_order[n]]; } + + protected: + + + //! Number of elements in the combined element object describing all of the + //! phases. + index_t m_nel; + + //! Number of species in the combined multiphase object + index_t m_nsp; + + //! Vector that takes into account of the current sorting of the species + /*! + * The index of m_order is the original k value of the species in the + * multiphase. The value of m_order, k_sorted, is the current value of the + * species index. + * + * m_order[korig] = k_sorted + */ + vector_int m_order; + + VCSnonideal::VCS_PROB *m_vprob; + + //! Pointer to the MultiPhase mixture that will be equilibrated. + /*! + * Solutions will be returned in this variable. + */ + mix_t *m_mix; + int m_printLvl; + + matrix_t m_N; + + int m_iter; + + // Vector of indices for species that are included in the + // calculation. This is used to exclude pure-phase species + // with invalid thermo data + vector_int m_species; + + //! Pointer to the object that does all of the equilibration work. + /*! + * This object owns the pointer. + */ + VCSnonideal::VCS_SOLVE *m_vsolvePtr; + }; + +} + + +#endif diff --git a/Cantera/src/equil/vcs_SpeciesProperties.cpp b/Cantera/src/equil/vcs_SpeciesProperties.cpp new file mode 100644 index 000000000..e04122200 --- /dev/null +++ b/Cantera/src/equil/vcs_SpeciesProperties.cpp @@ -0,0 +1,93 @@ +/** + * @file vcs_SpeciesProperties.cpp + */ +/* + * $Id $ + */ +#include +#include +#include + +#include "vcs_defs.h" +#include "vcs_SpeciesProperties.h" +#include "vcs_VolPhase.h" +#include "vcs_species_thermo.h" +#include "vcs_internal.h" + +using namespace std; + +namespace VCSnonideal { + +/***************************************************************************** + * + * constructor(): + */ +vcs_SpeciesProperties::vcs_SpeciesProperties(int indexPhase, + int indexSpeciesPhase, + vcs_VolPhase *owning) : + IndexPhase(indexPhase), + IndexSpeciesPhase(indexSpeciesPhase), + OwningPhase(owning), + SpeciesThermo(0), + WtSpecies(0.0), + Charge(0.0), + SurfaceSpecies(0), + VolPM(0.0), + ReferenceMoleFraction(1.0E-6) +{ +} + +/****************************************************************************** + * + * destructor + */ +vcs_SpeciesProperties::~vcs_SpeciesProperties() +{ +} + +/***************************************************************************** + * + * Copy Constructor vcs_SpeciesProperties + */ +vcs_SpeciesProperties::vcs_SpeciesProperties(const vcs_SpeciesProperties& b) : + IndexPhase(b.IndexPhase), + IndexSpeciesPhase(b.IndexSpeciesPhase), + OwningPhase(b.OwningPhase), + NumElements(b.NumElements), + SpeciesThermo(b.SpeciesThermo), + WtSpecies(b.WtSpecies), + Charge(b.Charge), + SurfaceSpecies(b.SurfaceSpecies), + VolPM(b.VolPM), + ReferenceMoleFraction(b.ReferenceMoleFraction) +{ + SpName = b.SpName; + FormulaMatrixCol = b.FormulaMatrixCol; +} + +/***************************************************************************** + * + * Assignment operator for vcs_SpeciesProperties + */ +vcs_SpeciesProperties& +vcs_SpeciesProperties::operator=(const vcs_SpeciesProperties& b) +{ + if (&b != this) { + IndexPhase = b.IndexPhase; + IndexSpeciesPhase = b.IndexSpeciesPhase; + OwningPhase = b.OwningPhase; + NumElements = b.NumElements; + SpName = b.SpName; + WtSpecies = b.WtSpecies; + FormulaMatrixCol = b.FormulaMatrixCol; + Charge = b.Charge; + SurfaceSpecies = b.SurfaceSpecies; + VolPM = b.VolPM; + ReferenceMoleFraction = b.ReferenceMoleFraction; + } + return *this; +} + +/*****************************************************************************/ +} + diff --git a/Cantera/src/equil/vcs_SpeciesProperties.h b/Cantera/src/equil/vcs_SpeciesProperties.h new file mode 100644 index 000000000..7a3457a6d --- /dev/null +++ b/Cantera/src/equil/vcs_SpeciesProperties.h @@ -0,0 +1,66 @@ + +/* $Id$ */ + +#ifndef VCS_SPECIES_PROPERTIES_H +#define VCS_SPECIES_PROPERTIES_H + +#include +#include + +namespace VCSnonideal { + +class VCS_SPECIES_THERMO; +class vcs_VolPhase; + +class vcs_SpeciesProperties { + +public: + int IndexPhase; + int IndexSpeciesPhase; + vcs_VolPhase *OwningPhase; + int NumElements; + + //! Name of the species + std::string SpName; + + VCS_SPECIES_THERMO *SpeciesThermo; /* Pointer to the thermo + structure for this species */ + double WtSpecies; /* Molecular Weight of the species (gm/mol) */ + + //! Column of the formula matrix, comprising the + //! element composition of the species */ + std::vector FormulaMatrixCol; + + double Charge; /* Charge state of the species -> This may + be duplication of what's in the + FormulaMatrixCol entries. However, it's prudent + to separate it out. */ + int SurfaceSpecies; /* True if this species belongs to a surface phase + */ + /* + * Various Calculated Quantities that are appropriate to + * keep copies of at this level. + */ + double VolPM; /* Partial molar volume of the species */ + double ReferenceMoleFraction; /* Representative value of the mole + fraction of this species in a phase. + This value is used for convergence issues + and for calculation of numerical derivs */ + + /* + * constructor and destructor + */ + vcs_SpeciesProperties(int indexPhase, int indexSpeciesPhase, + vcs_VolPhase *owning); + virtual ~vcs_SpeciesProperties(); + + /* + * Copy constructor and assignment operator + */ + vcs_SpeciesProperties(const vcs_SpeciesProperties& b); + vcs_SpeciesProperties& operator=(const vcs_SpeciesProperties& b); +}; + +} + +#endif diff --git a/Cantera/src/equil/vcs_TP.cpp b/Cantera/src/equil/vcs_TP.cpp new file mode 100644 index 000000000..34203c60f --- /dev/null +++ b/Cantera/src/equil/vcs_TP.cpp @@ -0,0 +1,196 @@ +/* ======================================================================= */ +/* -------------------------------------------------- */ +/* | RCS Head Information on zuzax.pchem.sandia.gov | */ +/* -------------------------------------------------- */ +/* $RCSfile$ */ +/* $Author$ */ +/* $Date$ */ +/* $Revision$ */ +/* ======================================================================= */ +#include +#include +#include + +#include "vcs_solve.h" +#include "vcs_internal.h" +#include "vcs_species_thermo.h" +#include "vcs_VolPhase.h" + +namespace VCSnonideal { + +/*****************************************************************************/ +/*****************************************************************************/ +/*****************************************************************************/ + +int VCS_SOLVE::vcs_TP(int ipr, int ip1, int maxit, double T_arg, double pres_arg) + + /************************************************************************** + * + * vcs_TP: + * + * Solve an equilibrium problem at a particular fixed temperature + * and pressure + * + * ipr = 1 -> Print results to standard output + * 0 -> don't report on anything + * ip1 = 1 -> Print intermediate results. + * maxit -> Maximum number of iterations for the algorithm + * T = Temperature (Kelvin) + * pres = Pressure (units given by if__ variable) + * + * 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 retn, iconv; + /* + * Store the temperature and pressure in the private global variables + */ + T = T_arg; + Pres = pres_arg; + /* + * Evaluate the standard state free energies + * at the current temperatures and pressures. + */ + iconv = vcs_evalSS_TP(ipr, ip1, T, pres_arg); + + /* + * Prepare the problem data: + * ->nondimensionalize the free energies using + * the divisor, R * T + */ + vcs_nondim_TP(); + /* + * Prep the fe field + */ + vcs_fePrep_TP(); + /* + * Decide whether we need an initial estimate of the solution + * If so, go get one. If not, then + */ + if (iest) { + retn = vcs_inest_TP(); + if (retn != VCS_SUCCESS) { + plogf("vcs_inest_TP returned a failure flag\n"); + } + } + /* + * Solve the problem at a fixed Temperature and Pressure + * (all information concerning Temperature and Pressure has already + * been derived. The free energies are now in dimensionless form.) + */ + iconv = vcs_solve_TP(ipr, ip1, maxit); + + /* + * Redimensionalize the free energies using + * the reverse of vcs_nondim to add back units. + */ + vcs_redim_TP(); + /* + * Return the convergence success flag. + */ + return iconv; +} +/*****************************************************************************/ +/*****************************************************************************/ +/*****************************************************************************/ +/*ARGSUSED*/ +int VCS_SOLVE::vcs_evalSS_TP(int ipr, int ip1, double Temp, double pres) + + /************************************************************************** + * + * vcs_evalSS_TP: + * + * IPR = 1 -> Print results to standard output + * 0 -> don't report on anything + * IP1 = 1 -> Print intermediate results. + * T = Temperature (Kelvin) + * Pres = Pressure (units of if__ variable) + * + * Evaluate the standard state free energies at the current temperature + * and pressure. Ideal gas pressure contribution is added in here. + * + ***************************************************************************/ +{ + // int i; + //double R; + /* + * At this level of the program, we are still using values + * for the free energies that have units. + */ + // R = vcsUtil_gasConstant(m_VCS_UnitsFormat); + + /* + * We need to special case VCS_UNITS_UNITLESS, here. + * cpc_ts_GStar_calc() returns units of Kelvin. Also, the temperature + * comes into play in calculating the ideal equation of state + * contributions, and other equations of state also. Therefore, + * we will emulate the VCS_UNITS_KELVIN case, here by chaning + * the initial gibbs free energy units to Kelvin before feeding + * them to the cpc_ts_GStar_calc() routine. Then, we will revert + * them back to unitless at the end of this routine. + */ + + + /* + * Loop over the species calculating the standard state Gibbs free + * energies. -> These are energies that only depend upon the Temperature + * and possibly on the pressure (i.e., ideal gas, etc). + */ + // HKM -> We can change this to looks over phases, calling the vcs_VolPhase + // object. Working to get rid of VCS_SPECIES_THERMO object + //for (i = 0; i < m_numSpeciesTot; ++i) { + // VCS_SPECIES_THERMO *spt = SpeciesThermo[i]; + // ff[i] = R * spt->GStar_R_calc(i, Temp, pres); + //} + + for (int iph = 0; iph < NPhase; iph++) { + vcs_VolPhase* vph = VPhaseList[iph]; + vph->setState_TP(T, Pres); + vph->sendToVCSGStar(VCS_DATA_PTR(ff)); + } + + if (m_VCS_UnitsFormat == VCS_UNITS_UNITLESS) { + for (int i = 0; i < m_numSpeciesTot; ++i) { + ff[i] /= Temp; + } + } + return VCS_SUCCESS; +} /***************************************************************************/ + +/*****************************************************************************/ +/*****************************************************************************/ +/*****************************************************************************/ + +void VCS_SOLVE::vcs_fePrep_TP(void) + + /************************************************************************** + * + * + ***************************************************************************/ +{ + int i; + for (i = 0; i < m_numSpeciesTot; ++i) { + /* + * For single species phases, initialize the chemical + * potential with the value of the standard state chemical + * potential. This value doesn't change during the calculation + */ + if (SSPhase[i]) { + m_gibbsSpecies[i] = ff[i]; + } + } +} /* vcs_fePrep_TP() ********************************************************/ +/*****************************************************************************/ +/*****************************************************************************/ +/*****************************************************************************/ +} + diff --git a/Cantera/src/equil/vcs_TV.cpp b/Cantera/src/equil/vcs_TV.cpp new file mode 100644 index 000000000..ea72fa7c0 --- /dev/null +++ b/Cantera/src/equil/vcs_TV.cpp @@ -0,0 +1,112 @@ +/* ======================================================================= */ +/* -------------------------------------------------- */ +/* | RCS Head Information on zuzax.pchem.sandia.gov | */ +/* -------------------------------------------------- */ +/* $RCSfile$ */ +/* $Author$ */ +/* $Date$ */ +/* $Revision$ */ +/* ======================================================================= */ +#include +#include +#include + +#include "vcs_solve.h" +#include "vcs_internal.h" +#include "vcs_VolPhase.h" +#include "vcs_species_thermo.h" + +namespace VCSnonideal { + +/************************************************************************** + * + * vcs_TV: + * + * Solve an equilibrium problem at a particular fixed temperature + * and volume. + * This is done as a root finder problem, solving repetative + * calls to solve_TP. + * + * ipr = 1 -> Print results to standard output + * 0 -> don't report on anything + * ip1 = 1 -> Print intermediate results. + * maxit -> Maximum number of iterations for the algorithm + * T = Temperature (Kelvin) + * Pres = Pressure (units specififed by if__ variable) + */ +int VCS_SOLVE::vcs_TV(int ipr, int ip1, int maxit, double T_arg, double VolRequest) +{ + int iconv, varID; + double Pmin, Pmax, Preturn; + VCS_FUNC_PTR func; + + /* + * Store the temperature in the private global variables + */ + T = T_arg; + + /* + * Set the unknown variable to the pressure + */ + varID = 1; + + /* + * Set the function to the volume function + */ + func = vcs_funcVtot; + + /* + * Set max and min Pressures + */ + Pmin = 0.0; + Pmax = 1.0E30; + Preturn = 1.0; + + iconv = vcsUtil_root1d(Pmin, Pmax, maxit, func, (void *) this, + VolRequest, varID, &Preturn); + + /* + * Return the convergence success flag. + */ + return iconv; +} + + /************************************************************************** + * + * vcs_VolTotal + * + * This function calculates the partial molar volume + * for all species, kspec, in the thermo problem + * at the temperature TKelvin and pressure, Pres, pres is in atm. + * And, it calculates the total volume of the combined system. + * + * Input + * iphase + * TKelvin + * pres + * w[] => vector containing the current mole numbers. + * + * Output + * VolPM[] => For species in all phase, the entries are the + * partial molar volumes + * return value = Total volume of the phase in L**3 / MOL_UNITS + * + * (L and MOL_UNITS determined from global units value if__) + */ +double VCS_SOLVE::vcs_VolTotal(double tkelvin, double pres, double w[], + double VolPM[]) +{ + double volTot = 0.0; + for (int iphase = 0; iphase < NPhase; iphase++) { + vcs_VolPhase *Vphase = VPhaseList[iphase]; + Vphase->setState_TP(tkelvin, pres); + Vphase->setMolesFromVCS(w); + double volp = Vphase->VolPM_calc(); + (void) Vphase->sendToVCSVolPM(VolPM); + volTot += volp; + } + return volTot; +} +/**************************************************************************/ +} + diff --git a/Cantera/src/equil/vcs_VolPhase.cpp b/Cantera/src/equil/vcs_VolPhase.cpp new file mode 100644 index 000000000..0b70c005a --- /dev/null +++ b/Cantera/src/equil/vcs_VolPhase.cpp @@ -0,0 +1,978 @@ +/** + * @file vcs_VolPhase.cpp + */ + +/* $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 "vcs_VolPhase.h" +#include "vcs_internal.h" +#include "vcs_SpeciesProperties.h" +#include "vcs_species_thermo.h" + +#ifdef CANTERA_SRC_TREE +#include "ThermoPhase.h" +#else +#include "cantera/Cantera.h" +#include "cantera/kernel/ThermoPhase.h" +#endif + +#include +#include + +namespace VCSnonideal { + +/***************************************************************************** + * + * vcs_VolPhase(): + * + * Constructor for the VolPhase object. + */ +vcs_VolPhase::vcs_VolPhase() : + VP_ID(-1), + Domain_ID(-1), + SingleSpecies(true), + GasPhase(false), + LiqPhase(false), + EqnState(VCS_EOS_CONSTANT), + nElemConstraints(0), + ChargeNeutralityElement(-1), + ElGlobalIndex(0), + NVolSpecies(0), + TMolesInert(0.0), + ActivityConvention(0), + Existence(0), + IndexSpecialSpecies(-1), + Activity_Coeff_Model(VCS_AC_CONSTANT), + Activity_Coeff_Params(0), + IndSpecies(0), + IndSpeciesContig(true), + UseCanteraCalls(false), + m_VCS_UnitsFormat(VCS_UNITS_MKS), + TP_ptr(0), + TMoles(0.0), + Vol(0.0), + m_phi(0.0), + m_UpToDate_AC(false), + m_UpToDate_VolStar(false), + m_UpToDate_VolPM(false), + m_UpToDate_GStar(false), + Temp(273.15), + Pres(1.01325E5), + RefPres(1.01325E5) +{ +} + +/* + * + * ~vcs_VolPhase(): + * + * Destructor for the VolPhase object. + */ +vcs_VolPhase::~vcs_VolPhase() { + for (int k = 0; k < NVolSpecies; k++) { + vcs_SpeciesProperties *sp = ListSpeciesPtr[k]; + delete sp; + sp = 0; + } +} + +/* + * + * Copy Constructor(): + * + * Objects that are owned by this object are deep copied here, except + * for the ThermoPhase object. + * The assignment operator does most of the work. + */ +vcs_VolPhase::vcs_VolPhase(const vcs_VolPhase& b) : + VP_ID(b.VP_ID), + Domain_ID(b.Domain_ID), + SingleSpecies(b.SingleSpecies), + GasPhase(b.GasPhase), + LiqPhase(b.LiqPhase), + EqnState(b.EqnState), + nElemConstraints(b.nElemConstraints), + ChargeNeutralityElement(b.ChargeNeutralityElement), + NVolSpecies(b.NVolSpecies), + TMolesInert(b.TMolesInert), + ActivityConvention(b.ActivityConvention), + Existence(b.Existence), + IndexSpecialSpecies(b.IndexSpecialSpecies), + Activity_Coeff_Model(b.Activity_Coeff_Model), + Activity_Coeff_Params(b.Activity_Coeff_Params), + IndSpeciesContig(b.IndSpeciesContig), + UseCanteraCalls(b.UseCanteraCalls), + m_VCS_UnitsFormat(b.m_VCS_UnitsFormat), + TP_ptr(b.TP_ptr), + TMoles(b.TMoles), + m_phiVarIndex(-1), + Vol(b.Vol), + m_phi(b.m_phi), + m_UpToDate_AC(false), + m_UpToDate_VolStar(false), + m_UpToDate_VolPM(false), + m_UpToDate_GStar(false), + Temp(b.Temp), + Pres(b.Pres) +{ + /* + * Call the Assignment operator to do the heavy + * lifting. + */ + *this = b; +} + +/***************************************************************************** + * Assignment operator() + * + * (note, this is used, so keep it current!) + */ +vcs_VolPhase& vcs_VolPhase::operator=(const vcs_VolPhase& b) +{ + int k; + if (&b != this) { + int old_num = NVolSpecies; + + VP_ID = b.VP_ID; + Domain_ID = b.Domain_ID; + SingleSpecies = b.SingleSpecies; + GasPhase = b.GasPhase; + LiqPhase = b.LiqPhase; + EqnState = b.EqnState; + + NVolSpecies = b.NVolSpecies; + nElemConstraints = b.nElemConstraints; + ChargeNeutralityElement = b.ChargeNeutralityElement; + + + ElName.resize(b.nElemConstraints); + for (int e = 0; e < b.nElemConstraints; e++) { + ElName[e] = b.ElName[e]; + } + + ElActive = b.ElActive; + m_elType = b.m_elType; + + FormulaMatrix.resize(nElemConstraints, NVolSpecies, 0.0); + for (int e = 0; e < nElemConstraints; e++) { + for (int k = 0; k < NVolSpecies; k++) { + FormulaMatrix[e][k] = b.FormulaMatrix[e][k]; + } + } + + SpeciesUnknownType = b.SpeciesUnknownType; + ElGlobalIndex = b.ElGlobalIndex; + NVolSpecies = b.NVolSpecies; + PhaseName = b.PhaseName; + TMolesInert = b.TMolesInert; + ActivityConvention = b.ActivityConvention; + Existence = b.Existence; + IndexSpecialSpecies = b.IndexSpecialSpecies; + Activity_Coeff_Model = b.Activity_Coeff_Model; + + /* + * Do a shallow copy because we haven' figured this out. + */ + Activity_Coeff_Params = b.Activity_Coeff_Params; + IndSpecies = b.IndSpecies; + IndSpeciesContig = b.IndSpeciesContig; + + for (k = 0; k < old_num; k++) { + if ( ListSpeciesPtr[k]) { + delete ListSpeciesPtr[k]; + ListSpeciesPtr[k] = 0; + } + } + ListSpeciesPtr.resize(NVolSpecies, 0); + for (k = 0; k < NVolSpecies; k++) { + ListSpeciesPtr[k] = + new vcs_SpeciesProperties(*(b.ListSpeciesPtr[k])); + } + + UseCanteraCalls = b.UseCanteraCalls; + m_VCS_UnitsFormat = b.m_VCS_UnitsFormat; + /* + * Do a shallow copy of the ThermoPhase object pointer. + * We don't duplicate the object. + * Um, there is no reason we couldn't do a + * duplicateMyselfAsThermoPhase() call here. This will + * have to be looked into. + */ + TP_ptr = b.TP_ptr; + TMoles = b.TMoles; + + Xmol = b.Xmol; + + m_phi = b.m_phi; + m_phiVarIndex = b.m_phiVarIndex; + + SS0ChemicalPotential = b.SS0ChemicalPotential; + StarChemicalPotential = b.StarChemicalPotential; + + StarMolarVol = b.StarMolarVol; + PartialMolarVol = b.PartialMolarVol; + ActCoeff = b.ActCoeff; + + dLnActCoeffdMolNumber = b.dLnActCoeffdMolNumber; + + m_UpToDate_AC = false; + m_UpToDate_VolStar = false; + m_UpToDate_VolPM = false; + m_UpToDate_GStar = false; + Temp = b.Temp; + Pres = b.Pres; + setState_TP(Temp, Pres); + _updateMoleFractionDependencies(); + } + return *this; +} + + +void vcs_VolPhase::resize(int phaseNum, int nspecies, const char *phaseName, + double molesInert) { + if (nspecies <= 0) { + plogf("nspecies Error\n"); + exit(-1); + } + if (phaseNum < 0) { + plogf("phaseNum should be greater than 0\n"); + exit(-1); + } + + TMolesInert = molesInert; + if (TMolesInert > 0.0) { + Existence = 2; + } + + m_phi = 0.0; + m_phiVarIndex = -1; + + if (phaseNum == VP_ID) { + if (strcmp(PhaseName.c_str(), phaseName)) { + plogf("Strings are different: %s %s :unknown situation\n", + PhaseName.c_str(), phaseName); + exit(-1); + } + } else { + VP_ID = phaseNum; + if (!phaseName) { + char itmp[40]; + sprintf(itmp, "Phase_%d", VP_ID); + PhaseName = itmp; + } else { + PhaseName = phaseName; + } + } + if (nspecies > 1) { + SingleSpecies = false; + } else { + SingleSpecies = true; + } + + if (NVolSpecies == nspecies) { + return; + } + + NVolSpecies = nspecies; + if (nspecies > 1) { + SingleSpecies = false; + } + + IndSpecies.resize(nspecies,-1); + + if ((int) ListSpeciesPtr.size() >= NVolSpecies) { + for (int i = 0; i < NVolSpecies; i++) { + if (ListSpeciesPtr[i]) { + delete ListSpeciesPtr[i]; + ListSpeciesPtr[i] = 0; + } + } + } + ListSpeciesPtr.resize(nspecies, 0); + for (int i = 0; i < nspecies; i++) { + ListSpeciesPtr[i] = new vcs_SpeciesProperties(phaseNum, i, this); + } + + Xmol.resize(nspecies, 0.0); + for (int i = 0; i < nspecies; i++) { + Xmol[i] = 1.0/nspecies; + } + + SS0ChemicalPotential.resize(nspecies, -1.0); + StarChemicalPotential.resize(nspecies, -1.0); + StarMolarVol.resize(nspecies, -1.0); + PartialMolarVol.resize(nspecies, -1.0); + ActCoeff.resize(nspecies, 1.0); + dLnActCoeffdMolNumber.resize(nspecies, nspecies, 0.0); + + + SpeciesUnknownType.resize(nspecies, VCS_SPECIES_TYPE_MOLNUM); + m_UpToDate_AC = false; + m_UpToDate_VolStar = false; + m_UpToDate_VolPM = false; + m_UpToDate_GStar = false; +} + + +//! Evaluate activity coefficients +/*! + * We carry out a calculation whenever UpTODate_AC is false. Specifically + * whenever a phase goes zero, we do not carry out calculations on it. + */ +void vcs_VolPhase::evaluateActCoeff() const { + char yo[] = "cpc_eval_ac "; + if (m_UpToDate_AC == true) return; + if (UseCanteraCalls) { + TP_ptr->getActivityCoefficients(VCS_DATA_PTR(ActCoeff)); + } else { + switch (Activity_Coeff_Model) { + case VCS_AC_CONSTANT: + /* + * Don't need to do anything since ActCoeff[] is initialized to + * the value of one, and never changed for this model. + */ + break; + case VCS_AC_DEBYE_HUCKEL: + plogf("Not implemented Yet\n"); + exit(-1); + break; + case VCS_AC_REGULAR_SOLN: + plogf("Not implemented Yet\n"); + exit(-1); + break; + case VCS_AC_MARGULES: + plogf("Not implemented Yet\n"); + exit(-1); + break; + default: + plogf("%sERROR: unknown model\n", yo); + exit(-1); + } + } + m_UpToDate_AC = true; +} + +/****************************************************************************** + * + * Evaluate one activity coefficients. + * + * return one activity coefficient. Have to recalculate them all to get + * one. + */ +double vcs_VolPhase::AC_calc_one(int kspec) const { + evaluateActCoeff(); + return(ActCoeff[kspec]); +} + +// Gibbs free energy calculation at a temperature for the reference state +// of each species +/* + * @param TKelvin temperature + */ +void vcs_VolPhase::G0_calc(double tkelvin) { + bool lsame = false; + if (Temp == tkelvin) { + lsame = true; + } + + bool doit = !lsame; + setState_TP(tkelvin, Pres); + if (SS0ChemicalPotential[0] == -1) doit = true; + if (doit) { + if (UseCanteraCalls) { + TP_ptr->getGibbs_ref(VCS_DATA_PTR(SS0ChemicalPotential)); + } else { + double R = vcsUtil_gasConstant(m_VCS_UnitsFormat); + for (int k = 0; k < NVolSpecies; k++) { + int kglob = IndSpecies[k]; + vcs_SpeciesProperties *sProp = ListSpeciesPtr[k]; + VCS_SPECIES_THERMO *sTherm = sProp->SpeciesThermo; + SS0ChemicalPotential[k] = + R * (sTherm->G0_R_calc(kglob, tkelvin)); + } + } + } +} + +// Gibbs free energy calculation at a temperature for the reference state +// of a species, return a value for one species +/* + * @param kspec species index + * @param TKelvin temperature + * + * @return return value of the gibbs free energy + */ +double vcs_VolPhase::G0_calc_one(int kspec, double tkelvin) { + G0_calc(tkelvin); + return SS0ChemicalPotential[kspec]; +} + +// Gibbs free energy calculation for standard states +/* + * Calculate the Gibbs free energies for the standard states + * The results are held internally within the object. + * + * @param TKelvin Current temperature + * @param pres Current pressure + */ +void vcs_VolPhase::GStar_calc(double tkelvin, double pres) { + setState_TP(tkelvin, pres); + if (!m_UpToDate_GStar) { + if (UseCanteraCalls) { + TP_ptr->getStandardChemPotentials(VCS_DATA_PTR(StarChemicalPotential)); + } else { + double R = vcsUtil_gasConstant(m_VCS_UnitsFormat); + for (int k = 0; k < NVolSpecies; k++) { + int kglob = IndSpecies[k]; + vcs_SpeciesProperties *sProp = ListSpeciesPtr[k]; + VCS_SPECIES_THERMO *sTherm = sProp->SpeciesThermo; + StarChemicalPotential[k] = + R * (sTherm->GStar_R_calc(kglob, tkelvin, pres)); + } + } + m_UpToDate_GStar = true; + } +} + +// Gibbs free energy calculation for standard state of one species +/* + * Calculate the Gibbs free energies for the standard state + * of the kth species. + * The results are held internally within the object. + * The kth species standard state G is returned + * + * @param kspec Species number (within the phase) + * @param TKelvin Current temperature + * @param pres Current pressure + * + * @return Gstar[kspec] returns the gibbs free energy for the + * standard state of the kth species. + */ +double vcs_VolPhase::GStar_calc_one(int kspec, double tkelvin, + double pres) { + GStar_calc(tkelvin, pres); + return StarChemicalPotential[kspec]; +} + +// Set the moles within the phase +/* + * This function takes as input the mole numbers in vcs format, and + * then updates this object with their values. This is essentially + * a gather routine. + * + * + * @param molesSpeciesVCS array of mole numbers. Note, the indecises for species in + * this array may not be contiguous. IndSpecies[] is needed + * to gather the species into the local contiguous vector + * format. + */ +void vcs_VolPhase::setMolesFromVCS(const double * const molesSpeciesVCS) { + int kglob; + double tmp; + TMoles = TMolesInert; + for (int k = 0; k < NVolSpecies; k++) { + if (SpeciesUnknownType[k] != VCS_SPECIES_TYPE_INTERFACIALVOLTAGE) { + kglob = IndSpecies[k]; + tmp = MAX(0.0, molesSpeciesVCS[kglob]); + Xmol[k] = tmp; + TMoles += tmp; + } + } + if (TMoles > 0.0) { + for (int k = 0; k < NVolSpecies; k++) { + Xmol[k] /= TMoles; + } + Existence = 1; + } else { + // This is where we will start to store a better approximation + // for the mole fractions, when the phase doesn't exist. + // This is currently unimplemented. + for (int k = 0; k < NVolSpecies; k++) { + Xmol[k] = 1.0 / NVolSpecies; + } + Existence = 0; + } + /* + * Update the electric potential if it is a solution variable + * in the equation system + */ + if (m_phiVarIndex >= 0) { + kglob = IndSpecies[m_phiVarIndex]; + if (NVolSpecies == 1) { + Xmol[m_phiVarIndex] = 1.0; + } else { + Xmol[m_phiVarIndex] = 0.0; + } + double phi = molesSpeciesVCS[kglob]; + setElectricPotential(phi); + if (NVolSpecies == 1) { + Existence = 1; + } + } + _updateMoleFractionDependencies(); + if (TMolesInert > 0.0) { + Existence = 2; + } +} + +// Set the mole fractions from a conventional mole fraction vector +/* + * + * @param xmol Value of the mole fractions for the species + * in the phase. These are contiguous. + */ +void vcs_VolPhase::setMoleFractions(const double * const xmol) { + double sum = -1.0; + for (int k = 0; k < NVolSpecies; k++) { + Xmol[k] = xmol[k]; + sum+= xmol[k]; + } + if (fabs(sum) > 1.0E-13) { + for (int k = 0; k < NVolSpecies; k++) { + Xmol[k] /= sum; + } + } + _updateMoleFractionDependencies(); +} + +// Updates the mole fractions in subobjects +/* + * Whenever the mole fractions change, this routine + * should be called. + */ +void vcs_VolPhase::_updateMoleFractionDependencies() { + if (UseCanteraCalls) { + if (TP_ptr) { + TP_ptr->setState_PX(Pres, VCS_DATA_PTR(Xmol)); + } + } + m_UpToDate_AC = false; + m_UpToDate_VolPM = false; +} + +// Return a const reference to the mole fraction vector in the phase +const std::vector & vcs_VolPhase::moleFractions() const { + return Xmol; +} + + +//! Set the moles within the phase +/*! + * This function takes as input the mole numbers in vcs format, and + * then updates this object with their values. This is essentially + * a gather routine. + * + * + * @param molesSpeciesVCS array of mole numbers. Note, the indecises for species in + * this array may not be contiguous. IndSpecies[] is needed + * to gather the species into the local contiguous vector + * format. + */ +void vcs_VolPhase::setMolesFromVCSCheck(const double * const molesSpeciesVCS, + const double * const TPhMoles, + int iphase) { + setMolesFromVCS(molesSpeciesVCS); + /* + * Check for consistency with TPhMoles[] + */ + double Tcheck = TPhMoles[VP_ID]; + if (Tcheck != TMoles) { + if (vcs_doubleEqual(Tcheck, TMoles)) { + Tcheck = TMoles; + } else { + plogf("We have a consistency problem: %21.16g %21.16g\n", + Tcheck, TMoles); + exit(-1); + } + } +} + +// Fill in an activity coefficients vector for VCS +/* + * This routine will calculate the activity coefficients for the + * current phase, and fill in the corresponding entries in the + * VCS activity coefficients vector. + * + * @param AC vector of activity coefficients for all of the species + * in all of the phases in a VCS problem. Only the + * entries for the current phase are filled in. + */ +void vcs_VolPhase::sendToVCSActCoeff(double * const AC) const { + if (!m_UpToDate_AC) { + evaluateActCoeff(); + } + int kglob; + for (int k = 0; k < NVolSpecies; k++) { + kglob = IndSpecies[k]; + AC[kglob] = ActCoeff[k]; + } +} + +// Fill in the partial molar volume vector for VCS +/* + * This routine will calculate the partial molar volumes for the + * current phase (if needed), and fill in the corresponding entries in the + * VCS partial molar volumes vector. + * + * @param VolPM vector of partial molar volumes for all of the species + * in all of the phases in a VCS problem. Only the + * entries for the current phase are filled in. + */ +double vcs_VolPhase::sendToVCSVolPM(double * const VolPM) const { + if (!m_UpToDate_VolPM) { + (void) VolPM_calc(); + } + int kglob; + for (int k = 0; k < NVolSpecies; k++) { + kglob = IndSpecies[k]; + VolPM[kglob] = PartialMolarVol[k]; + } + return Vol; +} + +// Fill in the partial molar volume vector for VCS +/* + * This routine will calculate the partial molar volumes for the + * current phase (if needed), and fill in the corresponding entries in the + * VCS partial molar volumes vector. + * + * @param VolPM vector of partial molar volumes for all of the species + * in all of the phases in a VCS problem. Only the + * entries for the current phase are filled in. + */ +void vcs_VolPhase::sendToVCSGStar(double * const gstar){ + if (!m_UpToDate_GStar) { + GStar_calc(Temp, Pres); + } + int kglob; + for (int k = 0; k < NVolSpecies; k++) { + kglob = IndSpecies[k]; + gstar[kglob] = StarChemicalPotential[k]; + } +} + + + +void vcs_VolPhase::setElectricPotential(double phi) { + m_phi = phi; + if (UseCanteraCalls) { + TP_ptr->setElectricPotential(m_phi); + } + // We have changed the state variable. Set uptodate flags to false + m_UpToDate_AC = false; + m_UpToDate_VolStar = false; + m_UpToDate_VolPM = false; + m_UpToDate_GStar = false; +} + +double vcs_VolPhase::electricPotential() const { + return m_phi; +} + +// Sets the temperature and pressure in this object and +// underlying objects +/* + * Sets the temperature and pressure in this object and + * underlying objects. The underlying objects refers to the + * Cantera's ThermoPhase object for this phase. + * + * @param temperature_Kelvin (Kelvin) + * @param pressure_PA Pressure (MKS units - Pascal) + */ +void vcs_VolPhase::setState_TP(double temp, double pres) +{ + if (Temp == temp) { + if (Pres == pres) { + return; + } + } + if (UseCanteraCalls) { + TP_ptr->setElectricPotential(m_phi); + TP_ptr->setState_TP(temp, pres); + } + Temp = temp; + Pres = pres; + m_UpToDate_AC = false; + m_UpToDate_VolStar = false; + m_UpToDate_VolPM = false; + m_UpToDate_GStar = false; +} + + +// Molar volume calculation for standard states +/* + * Calculate the molar volume for the standard states + * The results are held internally within the object. + * + * @param TKelvin Current temperature + * @param pres Current pressure + */ +void vcs_VolPhase::VolStar_calc(double tkelvin, double pres) { + setState_TP(tkelvin, pres); + if (!m_UpToDate_VolStar) { + if (UseCanteraCalls) { + TP_ptr->getStandardVolumes(VCS_DATA_PTR(StarMolarVol)); + } else { + for (int k = 0; k < NVolSpecies; k++) { + int kglob = IndSpecies[k]; + vcs_SpeciesProperties *sProp = ListSpeciesPtr[k]; + VCS_SPECIES_THERMO *sTherm = sProp->SpeciesThermo; + StarMolarVol[k] = + (sTherm->VolStar_calc(kglob, tkelvin, pres)); + } + } + m_UpToDate_VolStar = true; + } +} + +// Molar volume calculation for standard state of one species +/* + * Calculate the molar volume for the standard states + * The results are held internally within the object. + * Return the molar volume for one species + * + * @param kspec Species number (within the phase) + * @param TKelvin Current temperature + * @param pres Current pressure + * + * @return molar volume of the kspec species's standard + * state + */ +double vcs_VolPhase::VolStar_calc_one(int kspec, double tkelvin, double pres) +{ + VolStar_calc(tkelvin, pres); + return StarMolarVol[kspec]; +} + +/****************************************************************************** + * + * VolPM_calc + */ +double vcs_VolPhase::VolPM_calc() const { + int k, kglob; + if (!m_UpToDate_VolPM) { + if (UseCanteraCalls) { + TP_ptr->getPartialMolarVolumes(VCS_DATA_PTR(PartialMolarVol)); + } else { + for (k = 0; k < NVolSpecies; k++) { + kglob = IndSpecies[k]; + vcs_SpeciesProperties *sProp = ListSpeciesPtr[k]; + VCS_SPECIES_THERMO *sTherm = sProp->SpeciesThermo; + StarMolarVol[k] = + (sTherm->VolStar_calc(kglob, Temp, Pres)); + } + for (k = 0; k < NVolSpecies; k++) { + PartialMolarVol[k] = StarMolarVol[k]; + } + } + + Vol = 0.0; + for (k = 0; k < NVolSpecies; k++) { + Vol += PartialMolarVol[k] * Xmol[k]; + } + Vol *= TMoles; + } + m_UpToDate_VolPM = true; + return Vol; +} + +/* + * updateLnActCoeffJac(): + * + */ +void vcs_VolPhase::updateLnActCoeffJac(const double * const moleNumbersVCS) { + int k, j; + double deltaMoles_j = 0.0; + /* + * Make sure the base state of this object is fully up to date. + * with the current values of the mole numbers. + * -> This sets TMoles and Xmol[] + */ + setMolesFromVCS(moleNumbersVCS); + + /* + * Evaluate the current base activity coefficients. + */ + evaluateActCoeff(); + + // Make copies of ActCoeff and Xmol for use in taking differences + std::vector ActCoeff_Base(ActCoeff); + std::vector Xmol_Base(Xmol); + double TMoles_base = TMoles; + + /* + * Loop over the columns species to be deltad + */ + for (j = 0; j < NVolSpecies; j++) { + /* + * Calculate a value for the delta moles of species j + * -> NOte Xmol[] and Tmoles are always positive or zero + * quantities. + */ + double moles_j_base = TMoles * Xmol_Base[j]; + deltaMoles_j = 1.0E-7 * moles_j_base + 1.0E-20 * TMoles + 1.0E-150; + /* + * Now, update the total moles in the phase and all of the + * mole fractions based on this. + */ + TMoles = TMoles_base + deltaMoles_j; + for (k = 0; k < NVolSpecies; k++) { + Xmol[k] = Xmol_Base[k] * TMoles_base / TMoles; + } + Xmol[j] = (moles_j_base + deltaMoles_j) / TMoles; + + /* + * Go get new values for the activity coefficients. + * -> Note this calls setState_PX(); + */ + _updateMoleFractionDependencies(); + evaluateActCoeff(); + /* + * Calculate the column of the matrix + */ + double * const lnActCoeffCol = dLnActCoeffdMolNumber[j]; + for (k = 0; k < NVolSpecies; k++) { + lnActCoeffCol[k] = (ActCoeff[k] - ActCoeff_Base[k]) / + ((ActCoeff[k] + ActCoeff_Base[k]) * 0.5 * deltaMoles_j); + } + /* + * Revert to the base case Xmol, TMoles + */ + TMoles = TMoles_base; + vcs_vdcopy(Xmol, Xmol_Base, NVolSpecies); + } + /* + * Go get base values for the activity coefficients. + * -> Note this calls setState_TPX() again; + * -> Just wanted to make sure that cantera is in sync + * with VolPhase after this call. + */ + setMoleFractions(VCS_DATA_PTR(Xmol_Base)); + _updateMoleFractionDependencies(); + evaluateActCoeff(); +} + +// Downloads the ln ActCoeff jacobian into the VCS version of the +// ln ActCoeff jacobian. +/* + * + * This is essentially a scatter operation. + * + * The Jacobians are actually d( lnActCoeff) / d (MolNumber); + * dLnActCoeffdMolNumber[j][k] + * + * j = id of the species mole number + * k = id of the species activity coefficient + */ +void vcs_VolPhase::sendToVCSLnActCoeffJac(double * const * const LnACJac_VCS) const { + int j, k, jglob, kglob; + for (j = 0; j < NVolSpecies; j++) { + jglob = IndSpecies[j]; + double * const lnACJacVCS_col = LnACJac_VCS[jglob]; + const double * const lnACJac_col = dLnActCoeffdMolNumber[j]; + for (k = 0; k < NVolSpecies; k++) { + kglob = IndSpecies[k]; + lnACJacVCS_col[kglob] = lnACJac_col[k]; + } + } +} + +// Set the pointer for Cantera's ThermoPhase parameter +/* + * When we first initialize the ThermoPhase object, we read the + * state of the ThermoPhase into vcs_VolPhase object. + * + * @param tp_ptr Pointer to the ThermoPhase object corresponding + * to this phase. + */ +void vcs_VolPhase::setPtrThermoPhase(Cantera::ThermoPhase *tp_ptr) { + TP_ptr = tp_ptr; + if (TP_ptr) { + UseCanteraCalls = true; + Temp = TP_ptr->temperature(); + Pres = TP_ptr->pressure(); + setState_TP(Temp, Pres); + m_VCS_UnitsFormat = VCS_UNITS_MKS; + m_phi = TP_ptr->electricPotential(); + int nsp = TP_ptr->nSpecies(); + if (nsp != NVolSpecies) { + if (NVolSpecies != 0) { + plogf("Warning Nsp != NVolSpeces: %d %d \n", nsp, NVolSpecies); + } + resize(VP_ID, nsp, PhaseName.c_str()); + } + TP_ptr->getMoleFractions(VCS_DATA_PTR(Xmol)); + _updateMoleFractionDependencies(); + } else { + UseCanteraCalls = false; + } +} + +// Return a const ThermoPhase pointer corresponding to this phase +/* + * @return pointer to the ThermoPhase. + */ +const Cantera::ThermoPhase *vcs_VolPhase::ptrThermoPhase() const { + return TP_ptr; +} + +double vcs_VolPhase::TotalMoles() const { + return TMoles; +} + +double vcs_VolPhase::molefraction(int k) const { + return Xmol[k]; +} + +void vcs_VolPhase::setTotalMoles(double tmols) { + TMoles = tmols; +} + +// Return a string representing the equation of state +/* + * The string is no more than 16 characters. + * @param EOSType : integer value of the equation of state + * + * @return returns a string representing the EOS + */ +std::string string16_EOSType(int EOSType) { + char st[32]; + st[16] = '\0'; + switch (EOSType) { + case VCS_EOS_CONSTANT: + sprintf(st,"Constant "); + break; + case VCS_EOS_IDEAL_GAS: + sprintf(st,"Ideal Gas "); + break; + case VCS_EOS_STOICH_SUB: + sprintf(st,"Stoich Sub "); + break; + case VCS_EOS_IDEAL_SOLN: + sprintf(st,"Ideal Soln "); + break; + case VCS_EOS_DEBEYE_HUCKEL: + sprintf(st,"Debeye Huckel "); + break; + case VCS_EOS_REDLICK_KWONG: + sprintf(st,"Redlick_Kwong "); + break; + case VCS_EOS_REGULAR_SOLN: + sprintf(st,"Regular Soln "); + break; + default: + sprintf(st,"UnkType: %-7d", EOSType); + break; + } + st[16] = '\0'; + std::string sss=st; + return sss; +} + +} + diff --git a/Cantera/src/equil/vcs_VolPhase.h b/Cantera/src/equil/vcs_VolPhase.h new file mode 100644 index 000000000..0cf79bd49 --- /dev/null +++ b/Cantera/src/equil/vcs_VolPhase.h @@ -0,0 +1,750 @@ +/** + * @file vcs_VolPhase.h + * Header for the object representing each phase within vcs + */ +/* + * $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. + */ + +#ifndef VCS_VOLPHASE_H +#define VCS_VOLPHASE_H + +#include "vcs_DoubleStarStar.h" + +#include +#include + +/* + * Forward references + */ +// Forward reference for ThermoPhase object within the Cantera namespace +namespace Cantera { + class ThermoPhase; +} + +namespace VCSnonideal { +/* + * Models for the species activity coefficients + * + */ +#define VCS_AC_CONSTANT 0 +#define VCS_AC_DEBYE_HUCKEL 23 +#define VCS_AC_REGULAR_SOLN 25 +#define VCS_AC_MARGULES 300 +#define VCS_AC_UNK_CANTERA -1 +#define VCS_AC_UNK -2 +/* + * + * Models for the standard state volume of each species + */ +#define VCS_SSVOL_IDEALGAS 0 +#define VCS_SSVOL_CONSTANT 1 + +/* + * DEFINITIONS FOR THE vcs_VolPhase structure + * + * + * Equation of State Types + * - Permissible values for the EqnState variable in CPC_PHASE structure + */ +#define VCS_EOS_CONSTANT 0 +#define VCS_EOS_IDEAL_GAS 1 +#define VCS_EOS_STOICH_SUB 5 +#define VCS_EOS_IDEAL_SOLN 22 +#define VCS_EOS_DEBEYE_HUCKEL 23 +#define VCS_EOS_REDLICK_KWONG 24 +#define VCS_EOS_REGULAR_SOLN 25 +#define VCS_EOS_UNK_CANTERA -1 + + +struct VCS_SPECIES; +class vcs_SpeciesProperties; + + +//! Phase information and Phase calculations for vcs. +/*! + * Each phase in a vcs calculation has a vcs_VolPhase object associated + * with it. This object helps to coordinate property evaluations for + * species within the phase. Usually these evaluations must be carried + * out on a per phase basis. However, vcs frequently needs per species + * quantitites. Therefore, we need an interface layer between vcs + * and Cantera's ThermoPhase. + * + * The species stay in the same ordering within this structure. + * The vcs algorithm will change the ordering of species in + * the global species list. However, the indexing of species in this + * list stays the same. This structure contains structures that + * point to the species belonging to this phase in the global + * vcs species list. + * + * This object is considered not to own the underlying Cantera ThermoPhase + * object for the phase. + * + * This object contains an idea of the temperature and pressure. + * It checks to see if if the temperature and pressure has changed before calling + * underlying property evalulation routines. + * + * The object contains values for the electric potential of a phase. + * It coordinates the evalulation of properties wrt when the electric + * potential of a phase has changed. + * + * The object knows about the mole fractions of the phase. It controls + * the values of mole fractions, and coordinates the property evalulation + * wrt to changes in the mole fractions. It also will keep track of the + * likely values of mole fractions in multicomponent phases even when + * the phase doesn't actually exist within the thermo program. + * + * The object knows about the total moles of a phase. It checkes to + * see if the phase currently exists or not, and modifies its behavior + * accordingly. + * + * + * Activity coefficients and volume calculations are lagged. They are only + * called when they are needed (and when the state has changed so that they + * need to be recalculated). + */ +class vcs_VolPhase { +public: + + //! Original ID of the phase in the problem. + /*! + * If a non-ideal phase splits into two due to a + * miscibility gap, these numbers will stay the + * same after the split. + */ + int VP_ID; + + //! ID of the surface or volume domain in which the + //! this phase exists + /*! + * This ventures into the idea of installing a physical location + * into a thermodynamics program. This unknown is currently not + * being used. + */ + int Domain_ID; + + //! If true, this phase consists of a single species + int SingleSpecies; + + //! If true, this phase is a gas-phase like phase + /*! + * A RTlog(p/1atm) term is added onto the chemical potential + */ + int GasPhase; + + //! If true, this phase is a liquid-phase like phase*/ + int LiqPhase; + + //! Type of the equation of state + /*! + * The known types are listed at the top of this file. + */ + int EqnState; + + //! Number of element constraints within the problem + /*! + * This is usually equal to the number of elements. + * + */ + int nElemConstraints; + + //! This is the element number for the charge neutrality condition of the phase + /*! + * If it has one. If it does not have a charge neutrality + * constraint, then this value is equal to -1 + */ + int ChargeNeutralityElement; + + //! vector of strings containing the element names + /*! + * Length = nElemConstraints + */ + std::vector ElName; + + //! boolean indicating whether element constraint is active + //! for the current problem + std::vector ElActive; + + //! Type of the element + /*! + * m_elType[j] = type of the element + * 0 VCS_ELEM_TYPE_ABSPOS Normal element that is positive + * or zero in all species. + * 1 VCS_ELEM_TPYE_ELECTRONCHARGE element dof that corresponds + * to the charge DOF. + * 2 VCS_ELEM_TYPE_OTHERCONSTRAINT Other constraint which may + * mean that a species has neg 0 or pos value + * of that constraint (other than charge) + */ + std::vector m_elType; + + //! Formula Matrix for the phase + /*! + * FormulaMatrix[j][kspec] + * = Formula Matrix for the species + * Number of elements, j, + * in the kspec species + */ + DoubleStarStar FormulaMatrix; + + //! Type of the species unknown + /*! + * SpeciesUnknownType[k] = type of species + * Normal -> VCS_SPECIES_TYPE_MOLUNK + * ( unknown is the mole number in the phase) + * metal electron -> VCS_SPECIES_INTERFACIALVOLTAGE + * ( unknown is the interfacial voltage (volts) + */ + std::vector SpeciesUnknownType; + + //! Index of the element number in the global list of elements + //! storred in VCS_PROB or VCS_SOLVE + std::vector ElGlobalIndex; + + //! Number of species in the phase + int NVolSpecies; + + //! String name for the phase + std::string PhaseName; + + //! Total moles of inert in the phase + double TMolesInert; + + //! Convention for the activity formulation + /*! + * 0 = molar based activities (default) + * 1 = Molality based activities + * mu = mu_0 + ln a_molality + * standard state is based on unity molality + */ + int ActivityConvention; + + //! Current state of existence: + /*! + * 0 : Doesn't exist currently + * 1 : Does exist currently + * 2 : Always exists because it contains + * inerts which can't exist in any other + * phase + */ + int Existence; + + //! Index of the species which is special in + //! with respect to the thermo treatment. + /*! + * For water models this index will point to + * the index for water. + * defaults to 0 + */ + int IndexSpecialSpecies; + + //! Integer representing the activity coefficient model + /*! + * The known models are listed at the top of this page + */ + int Activity_Coeff_Model; + + //! General pointer for hanging stuff off of + /*! + * Currently, not implemented very well + */ + void *Activity_Coeff_Params; + + //! Index into the species vectors + /*! + * Maps the phase species number into the global species number. + * Note, as part of the vcs algorithm, the order of the species + * vector is changed during the algorithm + */ + std::vector IndSpecies; + + //! Boolean indicating whether IndSpecies is contiguous + bool IndSpeciesContig; + + //! Vector of Species structures for the species belonging to this phase + /*! + * The index into this vector is the species index within the phase. + */ + std::vector ListSpeciesPtr; + + //! If this is true, then calculations are actually performed within + //! Cantera + bool UseCanteraCalls; + + //! Units for the chemical potential data, pressure data, volume, + //! and species amounts + /*! + * All internally storred quantities will have these units. Also, printed + * quantitities will display in these units. Input quantities are expected + * in these units. + * + * Chem_Pot Pres vol moles + * ---------------------------------------------------------------------- + * -1 VCS_UNITS_KCALMOL = kcal/mol atm cm**3 gmol + * 0 VCS_UNITS_UNITLESS = MU / RT -> no units atm cm**3 gmol + * 1 VCS_UNITS_KJMOL = kJ / mol atm cm**3 gmol + * 2 VCS_UNITS_KELVIN = KELVIN -> MU / R atm cm**3 gmol + * 3 VCS_UNITS_MKS = Joules / Kmol (Cantera) Pa m**3 kmol + * ---------------------------------------------------------------------- + * + * see vcs_defs.h for more information. + * + * Currently, this value should be the same as the owning VCS_PROB or + * VCS_SOLVE object. There is no code for handling anything else atm. + */ + int m_VCS_UnitsFormat; + + +private: + /** + * If we are using Cantera, this is the + * pointer to the ThermoPhase object. If not, this is null. + */ + Cantera::ThermoPhase * TP_ptr; + + /** + * Variables Having to do with Calculated States + */ + + //! Total mols in the phase + /*! + * units vary + */ + double TMoles; + + //! Vector of the current mole fractions for species + //! in the phase + std::vector Xmol; + +public: + + //! If the potential is a solution variable in VCS, it acts as a species. + //! This is the species index in the phase for the potential + int m_phiVarIndex; + + //! Total Volume of the phase + /*! + * units are m**3 + */ + mutable double Vol; + + //! Vector of calculated SS0 chemical potentials for the + //! current Temperature. + /*! + * Note, This is the chemical potential derived strictly from the polynomial + * in temperature. Pressure effects have to be added in to + * get to the standard state. + * + * Units -> depends on VCS_UnitsFormat variable + * Cantera -> J/kmol + */ + mutable std::vector SS0ChemicalPotential; + + //! Vector of calculated Star chemical potentials for the + //! current Temperature and pressure. + /*! + * Note, This is the chemical potential at unit activity. Thus, we can call + * it the standard state chemical potential as well. + * + * Units -> depends on VCS_UnitsFormat variable + * Cantera -> J/kmol + */ + mutable std::vector StarChemicalPotential; + + //! Vector of the Star molar Volumes of the species. + /*! + * units depends on VCS_UnitsFormat variable + * Cantera -> m3 / kmol + */ + mutable std::vector StarMolarVol; + + //! Vector of the Partial molar Volumes of the species. + /*! + * units depends on VCS_UnitsFormat variable + * Cantera -> m3 / kmol + */ + mutable std::vector PartialMolarVol; + + /** + * Vector of calculated activity coefficients for the current + * state. + */ + mutable std::vector ActCoeff; + + //! Vector of the derivatives of the ln activity coefficient wrt to the + //! current mole number + /*! + * dLnActCoeffdMolNumber[j][k]; + * j = id of the species mole number + * k = id of the species activity coefficient + */ + mutable DoubleStarStar dLnActCoeffdMolNumber; + +private: + + //! Value of the potential for the phase (Volts) + double m_phi; + + //! Boolean indicating whether activity coefficients are uptodate. + /*! + * Activity coefficients and volume calculations are lagged. They are only + * called when they are needed (and when the state has changed so that they + * need to be recalculated). + */ + mutable bool m_UpToDate_AC; + + //! Boolean indicating whether Star volumes are uptodate. + /*! + * Activity coefficients and volume calculations are lagged. They are only + * called when they are needed (and when the state has changed so that they + * need to be recalculated). + * Star volumes are sensitive to temperature and pressure + */ + mutable bool m_UpToDate_VolStar; + + //! Boolean indicating whether partial molar volumes are uptodate. + /*! + * Activity coefficients and volume calculations are lagged. They are only + * called when they are needed (and when the state has changed so that they + * need to be recalculated). + * partial molar volumes are sensitive to everything + */ + mutable bool m_UpToDate_VolPM; + + //! Boolean indicating whether GStar is uptodate. + /*! + * GStar is sensitive to the temperature and the pressure, only + */ + mutable bool m_UpToDate_GStar; + + //! Current value of the temperature for this object, and underlying objects + double Temp; + + //! Current value of the pressure for this object, and underlying objects + double Pres; +public: + + //! Reference pressure for the phase + double RefPres; + + /************************************************************************* + * FUNCTIONS * + ************************************************************************/ + + //! Base constructor for the class + vcs_VolPhase(); + + //! Copy constructor + /*! + * @param b object to be copied + */ + vcs_VolPhase(const vcs_VolPhase& b); + + //! Assignment operator + /*! + * @param b object to be copied + */ + vcs_VolPhase& operator=(const vcs_VolPhase& b); + + //! Destructor + ~vcs_VolPhase(); + + /** + * The resize() function fills in all of the initial information if it + * is not given in the constructor. + */ + void resize(int phaseNum, int numSpecies, const char *phaseName, + double molesInert = 0.0); + + + //! Evaluate activity coefficients + /*! + * We carry out a calculation whenever UpTODate_AC is false. Specifically + * whenever a phase goes zero, we do not carry out calculations on it. + */ + void evaluateActCoeff() const; + + //! Evaluate activity coefficients and return the kspec coefficient + /*! + * We carry out a calculation whenever UpTODate_AC is false. Specifically + * whenever a phase goes zero, we do not carry out calculations on it. + * + * @param kspec species number + */ + double AC_calc_one(int kspec) const; + + + //! Set the moles within the phase + /*! + * This function takes as input the mole numbers in vcs format, and + * then updates this object with their values. This is essentially + * a gather routine. + * + * @param molesSpeciesVCS array of mole numbers. Note, the indecises for species in + * this array may not be contiguous. IndSpecies[] is needed + * to gather the species into the local contiguous vector + * format. + */ + void setMolesFromVCS(const double * const molesSpeciesVCS); + + //! Set the moles within the phase + /*! + * This function takes as input the mole numbers in vcs format, and + * then updates this object with their values. This is essentially + * a gather routine. + * Additionally it checks to see that the total moles value in + * TPhMoles[iplace] is equal to the internally computed value. + * If this isn't the case, an error exit is carried out. + * + * + * @param molesSpeciesVCS array of mole numbers. Note, the indecises + * for species in + * this array may not be contiguous. IndSpecies[] is needed + * to gather the species into the local contiguous vector + * format. + * @param TPhMoles VCS's array containing the number of moles + * in each phase. + * @param iphase index of the current phase. + * + */ + void setMolesFromVCSCheck(const double * const molesSpeciesVCS, + const double * const TPhMoles, + int iphase = -1); + + //! Fill in an activity coefficients vector for VCS + /*! + * This routine will calculate the activity coefficients for the + * current phase, and fill in the corresponding entries in the + * VCS activity coefficients vector. + * + * @param AC vector of activity coefficients for all of the species + * in all of the phases in a VCS problem. Only the + * entries for the current phase are filled in. + */ + void sendToVCSActCoeff(double * const AC) const; + + //! set the electric potential of the phase + /*! + * @param phi electric potential (volts) + */ + void setElectricPotential(double phi); + + //! Returns the electric field of the phase + /*! + * Units are potential + */ + double electricPotential() const; + + //! Gibbs free energy calculation for standard states + /*! + * Calculate the Gibbs free energies for the standard states + * The results are held internally within the object. + * + * @param TKelvin Current temperature + * @param pres Current pressure + */ + void GStar_calc(double TKelvin, double pres); + + //! Gibbs free energy calculation for standard state of one species + /*! + * Calculate the Gibbs free energies for the standard state + * of the kth species. + * The results are held internally within the object. + * The kth species standard state G is returned + * + * @param kspec Species number (within the phase) + * @param TKelvin Current temperature + * @param pres Current pressure + * + * @return Gstar[kspec] returns the gibbs free energy for the + * standard state of the kth species. + */ + double GStar_calc_one(int kspec, double TKelvin, double pres); + + //! Gibbs free energy calculation at a temperature for the reference state + //! of each species + /*! + * @param TKelvin temperature + */ + void G0_calc(double TKelvin); + + //! Gibbs free energy calculation at a temperature for the reference state + //! of a species, return a value for one species + /*! + * @param kspec species index + * @param TKelvin temperature + * + * @return return value of the gibbs free energy + */ + double G0_calc_one(int kspec, double TKelvin); + + + //! Molar volume calculation for standard states + /*! + * Calculate the molar volume for the standard states + * The results are held internally within the object. + * + * @param TKelvin Current temperature + * @param pres Current pressure + */ + void VolStar_calc(double TKelvin, double pres); + + //! Molar volume calculation for standard state of one species + /*! + * Calculate the molar volume for the standard states + * The results are held internally within the object. + * Return the molar volume for one species + * + * @param kspec Species number (within the phase) + * @param TKelvin Current temperature + * @param pres Current pressure + * + * @return molar volume of the kspec species's standard + * state + */ + double VolStar_calc_one(int kglob, double TKelvin, double pres); + + //! Calculate the partial molar volumes of all species and return the + //! total volume + /*! + * Calculates these quantitites internally + * + * @return total volume + */ + double VolPM_calc() const; + + //! Fill in the partial molar volume vector for VCS + /*! + * This routine will calculate the partial molar volumes for the + * current phase (if needed), and fill in the corresponding entries in the + * VCS partial molar volumes vector. + * + * @param VolPM vector of partial molar volumes for all of the species + * in all of the phases in a VCS problem. Only the + * entries for the current phase are filled in. + */ + double sendToVCSVolPM(double * const VolPM) const; + + //! Fill in the partial molar volume vector for VCS + /*! + * This routine will calculate the partial molar volumes for the + * current phase (if needed), and fill in the corresponding entries in the + * VCS partial molar volumes vector. + * + * @param VolPM vector of partial molar volumes for all of the species + * in all of the phases in a VCS problem. Only the + * entries for the current phase are filled in. + */ + void sendToVCSGStar(double * const gstar); + + //! Sets the temperature and pressure in this object and + //! underlying objects + /*! + * Sets the temperature and pressure in this object and + * underlying objects. The underlying objects refers to the + * Cantera's ThermoPhase object for this phase. + * + * @param temperature_Kelvin (Kelvin) + * @param pressure_PA Pressure (MKS units - Pascal) + */ + void setState_TP(double temperature_Kelvin, double pressure_PA); + + //! Evaluation of Activity Coefficient Jacobians + /*! + * This is the derivative of the ln of the activity coefficient + * with respect to mole number of jth species. + * (temp, pressure, and other mole numbers held constant + * + * @param moleNumbers Mole numbers are input. + */ + void updateLnActCoeffJac(const double * const moleNumbers); + + // Downloads the ln ActCoeff jacobian into the VCS version of the + // ln ActCoeff jacobian. + /* + * + * This is essentially a scatter operation. + * + * @param LnAcJac_VCS jacobian parameter + * The Jacobians are actually d( lnActCoeff) / d (MolNumber); + * dLnActCoeffdMolNumber[j][k] + * + * j = id of the species mole number + * k = id of the species activity coefficient + */ + void sendToVCSLnActCoeffJac(double * const * const LnACJac_VCS) const; + + //! Set the pointer for Cantera's ThermoPhase parameter + /*! + * When we first initialize the ThermoPhase object, we read the + * state of the ThermoPhase into vcs_VolPhase object. + * + * @param tp_ptr Pointer to the ThermoPhase object corresponding + * to this phase. + */ + void setPtrThermoPhase(Cantera::ThermoPhase *tp_ptr); + + //! Return a const ThermoPhase pointer corresponding to this phase + /*! + * @return pointer to the ThermoPhase. + */ + const Cantera::ThermoPhase *ptrThermoPhase() const; + + //! Return the total moles in the phase + /*! + * + * Units -> depends on VCS_UnitsFormat variable + * Cantera -> J/kmol + */ + double TotalMoles() const; + + //! Returns the mole fraction of the kspec species + /*! + * Returns the mole fraction of the kspec species + * + */ + double molefraction(int kspec) const; + + //! Sets the total moles in the phase + /*! + * + */ + void setTotalMoles(double tmols); + + //! Set the mole fractions from a conventional mole fraction vector + /*! + * + * @param xmol Value of the mole fractions for the species + * in the phase. These are contiguous. + */ + void setMoleFractions (const double * const xmol); + + //! Return a const reference to the mole fractions + const std::vector & moleFractions() const; + +private: + + //! Updates the mole fractions in subobjects + /*! + * Whenever the mole fractions change, this routine + * should be called. + */ + void _updateMoleFractionDependencies(); +}; + +//! Return a string representing the equation of state +/*! + * @param EOSType : integer value of the equation of state + * + * @return returns a string representing the EOS + */ +std::string string16_EOSType(int EOSType); + +} + +#endif diff --git a/Cantera/src/equil/vcs_dbocls.c b/Cantera/src/equil/vcs_dbocls.c new file mode 100644 index 000000000..2ab550e45 --- /dev/null +++ b/Cantera/src/equil/vcs_dbocls.c @@ -0,0 +1,1325 @@ +/* dbocls.f -- translated by f2c (version 20031025). + You must link the resulting object file with libf2c: + on Microsoft Windows system, link with libf2c.lib; + on Linux or Unix systems, link with .../path/to/libf2c.a -lm + or, if you install libf2c.a in a standard place, with -lf2c -lm + -- in that order, at the end of the command line, as in + cc *.o -lf2c -lm + Source for libf2c is in /netlib/f2c/libf2c.zip, e.g., + + http://www.netlib.org/f2c/libf2c.zip +*/ + +#include "f2c.h" + +/* Table of constant values */ + +static integer c__1 = 1; +static integer c__0 = 0; +static integer c__2 = 2; +static integer c__4 = 4; + +/* DECK DBOCLS */ +/* Subroutine */ int dbocls_(doublereal *w, integer *mdw, integer *mcon, + integer *mrows, integer *ncols, doublereal *bl, doublereal *bu, + integer *ind, integer *iopt, doublereal *x, doublereal *rnormc, + doublereal *rnorm, integer *mode, doublereal *rw, integer *iw) +{ + /* Initialized data */ + + static integer igo = 0; + + /* System generated locals */ + integer w_dim1, w_offset, i__1, i__2; + doublereal d__1, d__2; + + /* Local variables */ + static integer i__, j; + static doublereal t, t1, t2; + static integer ip, jp, lp; + static doublereal wt; + static integer llb; + static doublereal one; + static integer lds, iiw, liw, llx, irw, lrw; + extern doublereal ddot_(integer *, doublereal *, integer *, doublereal *, + integer *); + static integer idum, lbou, lmdw, lndw, mdwl, nerr; + static real rdum; + static integer lenx, lliw, mnew, jopt[5], lopt; + static doublereal zero; + static integer mopt, llrw, mout; + extern doublereal dnrm2_(integer *, doublereal *, integer *); + static real rdum2; + static integer icase; + extern /* Subroutine */ int dscal_(integer *, doublereal *, doublereal *, + integer *); + static integer modec; + static logical accum; + static integer nchar; + extern /* Subroutine */ int dbols_(doublereal *, integer *, integer *, + integer *, doublereal *, doublereal *, integer *, integer *, + doublereal *, doublereal *, integer *, doublereal *, integer *); + static integer level; + extern doublereal dasum_(integer *, doublereal *, integer *); + static doublereal anorm, cnorm; + static integer lboum; + extern /* Subroutine */ int dcopy_(integer *, doublereal *, integer *, + doublereal *, integer *); + static integer liopt; + extern doublereal d1mach_(integer *); + static integer locacc; + static logical checkl; + static integer iscale, locdim; + static logical filter; + static doublereal drelpr; + static logical pretri; + static integer inrows; + extern /* Subroutine */ int xerrwv_(char *, integer *, integer *, integer + *, integer *, integer *, integer *, integer *, real *, real *, + ftnlen); + +/* ***BEGIN PROLOGUE DBOCLS */ +/* ***DATE WRITTEN 821220 (YYMMDD) */ +/* ***REVISION DATE 870803 (YYMMDD) */ +/* ***CATEGORY NO. K1A2A,G2E,G2H1,G2H2 */ +/* ***KEYWORDS LIBRARY=SLATEC,TYPE=DOUBLE PRECISION(SBOCLS-S DBOCLS-D), */ +/* BOUNDS,CONSTRAINTS,INEQUALITY,LEAST SQUARES,LINEAR */ +/* ***AUTHOR HANSON, R. J., SNLA */ +/* ***PURPOSE Solve the bounded and constrained least squares */ +/* problem consisting of solving the equation */ +/* E*X = F (in the least squares sense) */ +/* subject to the linear constraints */ +/* C*X = Y. */ +/* ***DESCRIPTION */ + +/* **** Double Precision Version of SBOCLS **** */ +/* **** All INPUT and OUTPUT real variables are DOUBLE PRECISION **** */ + +/* This subprogram solves the bounded and constrained least squares */ +/* problem. The problem statement is: */ + +/* Solve E*X = F (least squares sense), subject to constraints */ +/* C*X=Y. */ + +/* In this formulation both X and Y are unknowns, and both may */ +/* have bounds on any of their components. This formulation */ +/* of the problem allows the user to have equality and inequality */ +/* constraints as well as simple bounds on the solution components. */ + +/* This constrained linear least squares subprogram solves E*X=F */ +/* subject to C*X=Y, where E is MROWS by NCOLS, C is MCON by NCOLS. */ + +/* The user must have dimension statements of the form */ + +/* DIMENSION W(MDW,NCOLS+MCON+1), BL(NCOLS+MCON), BU(NCOLS+MCON), */ +/* * X(2*(NCOLS+MCON)+2+NX), RW(6*NCOLS+5*MCON) */ +/* INTEGER IND(NCOLS+MCON), IOPT(17+NI), IW(2*(NCOLS+MCON)) */ + +/* (here NX=number of extra locations required for the options; NX=0 */ +/* if no options are in use. Also NI=number of extra locations */ +/* for options 1-9.) */ + +/* INPUT */ +/* ----- */ + +/* ------------------------- */ +/* W(MDW,*),MCON,MROWS,NCOLS */ +/* ------------------------- */ +/* The array W contains the (possibly null) matrix [C:*] followed by */ +/* [E:F]. This must be placed in W as follows: */ +/* [C : *] */ +/* W = [ ] */ +/* [E : F] */ +/* The (*) after C indicates that this data can be undefined. The */ +/* matrix [E:F] has MROWS rows and NCOLS+1 columns. The matrix C is */ +/* placed in the first MCON rows of W(*,*) while [E:F] */ +/* follows in rows MCON+1 through MCON+MROWS of W(*,*). The vector F */ +/* is placed in rows MCON+1 through MCON+MROWS, column NCOLS+1. The */ +/* values of MDW and NCOLS must be positive; the value of MCON must */ +/* be nonnegative. An exception to this occurs when using option 1 */ +/* for accumulation of blocks of equations. In that case MROWS is an */ +/* OUTPUT variable only, and the matrix data for [E:F] is placed in */ +/* W(*,*), one block of rows at a time. See IOPT(*) contents, option */ +/* number 1, for further details. The row dimension, MDW, of the */ +/* array W(*,*) must satisfy the inequality: */ + +/* If using option 1, */ +/* MDW .ge. MCON + max(max. number of */ +/* rows accumulated, NCOLS) + 1. */ +/* If using option 8, */ +/* MDW .ge. MCON + MROWS. */ +/* Else */ +/* MDW .ge. MCON + max(MROWS, NCOLS). */ + +/* Other values are errors, but this is checked only when using */ +/* option=2. The value of MROWS is an output parameter when */ +/* using option number 1 for accumulating large blocks of least */ +/* squares equations before solving the problem. */ +/* See IOPT(*) contents for details about option 1. */ + +/* ------------------ */ +/* BL(*),BU(*),IND(*) */ +/* ------------------ */ +/* These arrays contain the information about the bounds that the */ +/* solution values are to satisfy. The value of IND(J) tells the */ +/* type of bound and BL(J) and BU(J) give the explicit values for */ +/* the respective upper and lower bounds on the unknowns X and Y. */ +/* The first NVARS entries of IND(*), BL(*) and BU(*) specify */ +/* bounds on X; the next MCON entries specify bounds on Y. */ + +/* 1. For IND(J)=1, require X(J) .ge. BL(J); */ +/* IF J.gt.NCOLS, Y(J-NCOLS) .ge. BL(J). */ +/* (the value of BU(J) is not used.) */ +/* 2. For IND(J)=2, require X(J) .le. BU(J); */ +/* IF J.gt.NCOLS, Y(J-NCOLS) .le. BU(J). */ +/* (the value of BL(J) is not used.) */ +/* 3. For IND(J)=3, require X(J) .ge. BL(J) and */ +/* X(J) .le. BU(J); */ +/* IF J.gt.NCOLS, Y(J-NCOLS) .ge. BL(J) and */ +/* Y(J-NCOLS) .le. BU(J). */ +/* (to impose equality constraints have BL(J)=BU(J)= */ +/* constraining value.) */ +/* 4. For IND(J)=4, no bounds on X(J) or Y(J-NCOLS) are required. */ +/* (the values of BL(J) and BU(J) are not used.) */ + +/* Values other than 1,2,3 or 4 for IND(J) are errors. In the case */ +/* IND(J)=3 (upper and lower bounds) the condition BL(J) .gt. BU(J) */ +/* is an error. The values BL(J), BU(J), J .gt. NCOLS, will be */ +/* changed. Significant changes mean that the constraints are */ +/* infeasible. (Users must make this decision themselves.) */ +/* The new values for BL(J), BU(J), J .gt. NCOLS, define a */ +/* region such that the perturbed problem is feasible. If users */ +/* know that their problem is feasible, this step can be skipped */ +/* by using option number 8 described below. */ +/* See IOPT(*) description. */ + + +/* ------- */ +/* IOPT(*) */ +/* ------- */ +/* This is the array where the user can specify nonstandard options */ +/* for DBOCLS( ). Most of the time this feature can be ignored by */ +/* setting the input value IOPT(1)=99. Occasionally users may have */ +/* needs that require use of the following subprogram options. For */ +/* details about how to use the options see below: IOPT(*) CONTENTS. */ + +/* Option Number Brief Statement of Purpose */ +/* ------ ------ ----- --------- -- ------- */ +/* 1 Return to user for accumulation of blocks */ +/* of least squares equations. The values */ +/* of IOPT(*) are changed with this option. */ +/* The changes are updates to pointers for */ +/* placing the rows of equations into position */ +/* for processing. */ +/* 2 Check lengths of all arrays used in the */ +/* subprogram. */ +/* 3 Column scaling of the data matrix, [C]. */ +/* [E] */ +/* 4 User provides column scaling for matrix [C]. */ +/* [E] */ +/* 5 Provide option array to the low-level */ +/* subprogram SBOLS( ). */ +/* 6 Provide option array to the low-level */ +/* subprogram SBOLSM( ). */ +/* 7 Move the IOPT(*) processing pointer. */ +/* 8 Do not preprocess the constraints to */ +/* resolve infeasibilities. */ +/* 9 Do not pretriangularize the least squares matrix. */ +/* 99 No more options to change. */ + +/* ---- */ +/* X(*) */ +/* ---- */ +/* This array is used to pass data associated with options 4,5 and */ +/* 6. Ignore this parameter (on input) if no options are used. */ +/* Otherwise see below: IOPT(*) CONTENTS. */ + + +/* OUTPUT */ +/* ------ */ + +/* ----------------- */ +/* X(*),RNORMC,RNORM */ +/* ----------------- */ +/* The array X(*) contains a solution (if MODE .ge.0 or .eq.-22) for */ +/* the constrained least squares problem. The value RNORMC is the */ +/* minimum residual vector length for the constraints C*X - Y = 0. */ +/* The value RNORM is the minimum residual vector length for the */ +/* least squares equations. Normally RNORMC=0, but in the case of */ +/* inconsistent constraints this value will be nonzero. */ +/* The values of X are returned in the first NVARS entries of X(*). */ +/* The values of Y are returned in the last MCON entries of X(*). */ + +/* ---- */ +/* MODE */ +/* ---- */ +/* The sign of MODE determines whether the subprogram has completed */ +/* normally, or encountered an error condition or abnormal status. A */ +/* value of MODE .ge. 0 signifies that the subprogram has completed */ +/* normally. The value of mode (.ge. 0) is the number of variables */ +/* in an active status: not at a bound nor at the value zero, for */ +/* the case of free variables. A negative value of MODE will be one */ +/* of the cases (-57)-(-41), (-37)-(-22), (-19)-(-2). Values .lt. -1 */ +/* correspond to an abnormal completion of the subprogram. These */ +/* error messages are in groups for the subprograms DBOCLS(), */ +/* SBOLSM(), and SBOLS(). An approximate solution will be returned */ +/* to the user only when max. iterations is reached, MODE=-22. */ + +/* ----------- */ +/* RW(*),IW(*) */ +/* ----------- */ +/* These are working arrays. (normally the user can ignore the */ +/* contents of these arrays.) */ + +/* IOPT(*) CONTENTS */ +/* ------- -------- */ +/* The option array allows a user to modify some internal variables */ +/* in the subprogram without recompiling the source code. A central */ +/* goal of the initial software design was to do a good job for most */ +/* people. Thus the use of options will be restricted to a select */ +/* group of users. The processing of the option array proceeds as */ +/* follows: a pointer, here called LP, is initially set to the value */ +/* 1. At the pointer position the option number is extracted and */ +/* used for locating other information that allows for options to be */ +/* changed. The portion of the array IOPT(*) that is used for each */ +/* option is fixed; the user and the subprogram both know how many */ +/* locations are needed for each option. The value of LP is updated */ +/* for each option based on the amount of storage in IOPT(*) that is */ +/* required. A great deal of error checking is done by the */ +/* subprogram on the contents of the option array. Nevertheless it */ +/* is still possible to give the subprogram optional input that is */ +/* meaningless. For example option 4 uses the locations */ +/* X(NCOLS+IOFF),...,X(NCOLS+IOFF+NCOLS-1) for passing scaling data. */ +/* The user must manage the allocation of these locations. */ + +/* 1 */ +/* - */ +/* This option allows the user to solve problems with a large number */ +/* of rows compared to the number of variables. The idea is that the */ +/* subprogram returns to the user (perhaps many times) and receives */ +/* new least squares equations from the calling program unit. */ +/* Eventually the user signals "that's all" and a solution is then */ +/* computed. The value of MROWS is an output variable when this */ +/* option is used. Its value is always in the range 0 .le. MROWS */ +/* .le. NCOLS+1. It is the number of rows after the */ +/* triangularization of the entire set of equations. If LP is the */ +/* processing pointer for IOPT(*), the usage for the sequential */ +/* processing of blocks of equations is */ + + +/* IOPT(LP)=1 */ +/* Move block of equations to W(*,*) starting at */ +/* the first row of W(*,*). */ +/* IOPT(LP+3)=# of rows in the block; user defined */ + +/* The user now calls DBOCLS( ) in a loop. The value of IOPT(LP+1) */ +/* directs the user's action. The value of IOPT(LP+2) points to */ +/* where the subsequent rows are to be placed in W(*,*). Both of */ +/* these values are first defined in the subprogram. The user */ +/* changes the value of IOPT(LP+1) (to 2) as a signal that all of */ +/* the rows have been processed. */ + + +/* . 4) { + nerr = 56; + nchar = 46; + xerrwv_("DBOCLS(). FOR J=(I1), IND(J)=(I2) MUST BE 1-4.", & + nchar, &nerr, &level, &c__2, &j, &ind[j], &c__0, & + rdum, &rdum, (ftnlen)46); +/* DO(RETURN TO USER PROGRAM UNIT) */ + goto L260; + } +/* L10: */ + } + +/* SEE THAT BOUNDS ARE CONSISTENT. */ + i__1 = *ncols + *mcon; + for (j = 1; j <= i__1; ++j) { + if (ind[j] == 3) { + if (bl[j] > bu[j]) { + nerr = 57; + nchar = 58; + rdum2 = bl[j]; + rdum = bu[j]; + xerrwv_("DBOCLS(). FOR J=(I1), BOUND BL(J)=(R1) IS .GT. " + "BU(J)=(R2).", &nchar, &nerr, &level, &c__1, &j, & + idum, &c__2, &rdum2, &rdum, (ftnlen)58); +/* DO(RETURN TO USER PROGRAM UNIT) */ + goto L260; + } + } +/* L20: */ + } +/* END PROCEDURE */ +/* DO(PROCESS OPTION ARRAY) */ +/* PROCEDURE(PROCESS OPTION ARRAY) */ + zero = 0.; + one = 1.; + drelpr = d1mach_(&c__4); + checkl = FALSE_; + filter = TRUE_; + lenx = (*ncols + *mcon << 1) + 2; + iscale = 1; + igo = 1; + accum = FALSE_; + pretri = TRUE_; + lopt = 0; + mopt = 0; + lp = 0; + lds = 0; +/* DO FOREVER */ +L30: + lp += lds; + ip = iopt[lp + 1]; + jp = abs(ip); + +/* TEST FOR NO MORE OPTIIONS TO CHANGE. */ + if (ip == 99) { + if (lopt == 0) { + lopt = -(lp + 2); + } + if (mopt == 0) { + mopt = -(abs(lopt) + 7); + } + if (lopt < 0) { + lbou = abs(lopt); + } else { + lbou = lopt - 15; + } + +/* SEND COL. SCALING TO DBOLS(). */ + iopt[lbou] = 4; + iopt[lbou + 1] = 1; + +/* PASS AN OPTION ARRAY FOR DBOLSM(). */ + iopt[lbou + 2] = 5; + +/* LOC. OF OPTION ARRAY FOR DBOLSM( ). */ + iopt[lbou + 3] = 8; + +/* SKIP TO START OF USER-GIVEN OPTION ARRAY FOR DBOLS(). */ + iopt[lbou + 4] = 6; + iopt[lbou + 6] = 99; + if (lopt > 0) { + iopt[lbou + 5] = lopt - lbou + 1; + } else { + iopt[lbou + 4] = -iopt[lbou + 4]; + } + if (mopt < 0) { + lboum = abs(mopt); + } else { + lboum = mopt - 8; + } + +/* CHANGE PRETRIANGULARIZATION FACTOR IN DBOLSM(). */ + iopt[lboum] = 5; + iopt[lboum + 1] = *ncols + *mcon + 1; + +/* PASS WEIGHT TO DBOLSM() FOR RANK TEST. */ + iopt[lboum + 2] = 6; + iopt[lboum + 3] = *ncols + *mcon + 2; + iopt[lboum + 4] = *mcon; + +/* SKIP TO USER-GIVEN OPTION ARRAY FOR DBOLSM( ). */ + iopt[lboum + 5] = 1; + iopt[lboum + 7] = 99; + if (mopt > 0) { + iopt[lboum + 6] = mopt - lboum + 1; + } else { + iopt[lboum + 5] = -iopt[lboum + 5]; + } +/* EXIT FOREVER */ + goto L50; + } else if (jp == 99) { + lds = 1; +/* CYCLE FOREVER */ + goto L50; + } else if (jp == 1) { + if (ip > 0) { + +/* SET UP DIRECTION FLAG LOCATION, ROW STACKING POINTER */ +/* LOCATION, AND LOCATION FOR NUMBER OF NEW ROWS. */ + locacc = lp + 2; + +/* IOPT(LOCACC-1)=OPTION NUMBER FOR SEQ. ACCUMULATION. */ +/* CONTENTS.. IOPT(LOCACC )=USER DIRECTION FLAG, 1 OR 2. */ +/* IOPT(LOCACC+1)=ROW STACKING POINTER. */ +/* IOPT(LOCACC+2)=NUMBER OF NEW ROWS TO PROCESS. */ +/* USER ACTION WITH THIS OPTION.. */ +/* (SET UP OPTION DATA FOR SEQ. ACCUMULATION IN IOPT(*).) */ +/* (MOVE BLOCK OF EQUATIONS INTO W(*,*) STARTING AT FIRST */ +/* ROW OF W(*,*) BELOW THE ROWS FOR THE CONSTRAINT MATRIX C. */ +/* SET IOPT(LOCACC+2)=NO. OF LEAST SQUARES EQUATIONS IN BLOCK. */ +/* LOOP */ +/* CALL DBOCLS() */ + +/* IF(IOPT(LOCACC) .EQ. 1) THEN */ +/* STACK EQUAS. INTO W(*,*), STARTING AT */ +/* ROW IOPT(LOCACC+1). */ +/* INTO W(*,*). */ +/* SET IOPT(LOCACC+2)=NO. OF EQUAS. */ +/* IF LAST BLOCK OF EQUAS., SET IOPT(LOCACC)=2. */ +/* ELSE IF IOPT(LOCACC) .EQ. 2) THEN */ +/* (PROCESS IS OVER. EXIT LOOP.) */ +/* ELSE */ +/* (ERROR CONDITION. SHOULD NOT HAPPEN.) */ +/* END IF */ +/* END LOOP */ + iopt[locacc + 1] = *mcon + 1; + accum = TRUE_; + iopt[locacc] = igo; + } + lds = 4; +/* CYCLE FOREVER */ + goto L30; + } else if (jp == 2) { + if (ip > 0) { + +/* GET ACTUAL LENGTHS OF ARRAYS FOR CHECKING AGAINST NEEDS. */ + locdim = lp + 2; + +/* LMDW.GE.MCON+MAX(MOUT,NCOLS), IF MCON.GT.0 .AND FILTER */ +/* LMDW.GE.MCON+MOUT, OTHERWISE */ + +/* LNDW.GE.NCOLS+MCON+1 */ +/* LLB .GE.NCOLS+MCON */ +/* LLX .GE.2*(NCOLS+MCON)+2+EXTRA REQD. IN OPTIONS. */ +/* LLRW.GE.6*NCOLS+5*MCON */ +/* LLIW.GE.2*(NCOLS+MCON) */ +/* LIOP.GE. AMOUNT REQD. FOR OPTION ARRAY. */ + lmdw = iopt[locdim]; + lndw = iopt[locdim + 1]; + llb = iopt[locdim + 2]; + llx = iopt[locdim + 3]; + llrw = iopt[locdim + 4]; + lliw = iopt[locdim + 5]; + liopt = iopt[locdim + 6]; + checkl = TRUE_; + } + lds = 8; +/* CYCLE FOREVER */ + goto L30; + +/* OPTION TO MODIFY THE COLUMN SCALING. */ + } else if (jp == 3) { + if (ip > 0) { + iscale = iopt[lp + 2]; + +/* SEE THAT ISCALE IS 1 THRU 3. */ + if (iscale < 1 || iscale > 3) { + nerr = 48; + nchar = 41; + xerrwv_("DBOCLS(). ISCALE OPTION=(I1) MUST BE 1-3.", & + nchar, &nerr, &level, &c__1, &iscale, &idum, & + c__0, &rdum, &rdum, (ftnlen)41); +/* DO(RETURN TO USER PROGRAM UNIT) */ + goto L260; + } + } + lds = 2; +/* CYCLE FOREVER */ + goto L30; + +/* IN THIS OPTION THE USER HAS PROVIDED SCALING. THE */ +/* SCALE FACTORS FOR THE COLUMNS BEGIN IN X(NCOLS+IOPT(LP+2)). */ + } else if (jp == 4) { + if (ip > 0) { + iscale = 4; + if (iopt[lp + 2] <= 0) { + nerr = 49; + nchar = 86; + xerrwv_("DBOCLS(). OFFSET PAST X(NCOLS) (I1) FOR USER-PR" + "OVIDED COLUMN SCALING MUST BE POSITIVE.", &nchar, + &nerr, &level, &c__1, &iopt[lp + 2], &idum, &c__0, + &rdum, &rdum, (ftnlen)86); +/* DO(RETURN TO USER PROGRAM UNIT) */ + goto L260; + } + dcopy_(ncols, &x[*ncols + iopt[lp + 2]], &c__1, &rw[1], &c__1) + ; + lenx += *ncols; + i__1 = *ncols; + for (j = 1; j <= i__1; ++j) { + if (rw[j] <= zero) { + nerr = 50; + nchar = 84; + rdum2 = rw[j]; + xerrwv_("DBOCLS(). EACH PROVIDED COL. SCALE FACTOR M" + "UST BE POSITIVE. COMP. (I1) NOW = (R1).", & + nchar, &nerr, &level, &c__1, &j, &idum, &c__1, + &rdum2, &rdum, (ftnlen)84); +/* DO(RETURN TO USER PROGRAM UNIT) */ + goto L260; + } +/* L40: */ + } + } + lds = 2; +/* CYCLE FOREVER */ + goto L30; + +/* IN THIS OPTION AN OPTION ARRAY IS PROVIDED TO DBOLS(). */ + } else if (jp == 5) { + if (ip > 0) { + lopt = iopt[lp + 2]; + } + lds = 2; +/* CYCLE FOREVER */ + goto L30; + +/* IN THIS OPTION AN OPTION ARRAY IS PROVIDED TO DBOLSM(). */ + } else if (jp == 6) { + if (ip > 0) { + mopt = iopt[lp + 2]; + } + lds = 2; +/* CYCLE FOREVER */ + goto L30; + +/* THIS OPTION USES THE NEXT LOC OF IOPT(*) AS A */ +/* POINTER VALUE TO SKIP TO NEXT. */ + } else if (jp == 7) { + if (ip > 0) { + lp = iopt[lp + 2] - 1; + lds = 0; + } else { + lds = 2; + } +/* CYCLE FOREVER */ + goto L30; + +/* THIS OPTION AVOIDS THE CONSTRAINT RESOLVING PHASE FOR */ +/* THE LINEAR CONSTRAINTS C*X=Y. */ + } else if (jp == 8) { + filter = ! (ip > 0); + lds = 1; +/* CYCLE FOREVER */ + goto L30; + +/* THIS OPTION SUPPRESSES PRETIRANGULARIZATION OF THE LEAST */ +/* SQUARES EQATIONS. */ + } else if (jp == 9) { + pretri = ! (ip > 0); + lds = 1; +/* CYCLE FOREVER */ + goto L30; + +/* NO VALID OPTION NUMBER WAS NOTED. THIS IS AN ERROR CONDITION. */ + } else { + nerr = 51; + nchar = 48; + rdum = (real) idum; + xerrwv_("DBOCLS(). THE OPTION NUMBER=(I1) IS NOT DEFINED.", & + nchar, &nerr, &level, &c__1, &jp, &idum, &c__0, &rdum, & + rdum, (ftnlen)48); +/* DO(RETURN TO USER PROGRAM UNIT) */ + goto L260; + } +/* END FOREVER */ +/* END PROCEDURE */ +L50: + if (checkl) { +/* DO(CHECK LENGTHS OF ARRAYS) */ +/* PROCEDURE(CHECK LENGTHS OF ARRAYS) */ + +/* THIS FEATURE ALLOWS THE USER TO MAKE SURE THAT THE */ +/* ARRAYS ARE LONG ENOUGH FOR THE INTENDED PROBLEM SIZE AND USE. */ + if (filter && ! accum) { + mdwl = *mcon + max(*mrows,*ncols); + } else { + mdwl = *mcon + *ncols + 1; + } + if (lmdw < mdwl) { + nerr = 41; + nchar = 88; + xerrwv_("DBOCLS(). THE ROW DIMENSION OF W(,)=(I1) MUST BE .G" + "E. THE NUMBER OF EFFECTIVE ROWS=(I2).", &nchar, &nerr, + &level, &c__2, &lmdw, &mdwl, &c__0, &rdum, &rdum, ( + ftnlen)88); +/* DO(RETURN TO USER PROGRAM UNIT) */ + goto L260; + } + if (lndw < *ncols + *mcon + 1) { + nerr = 42; + nchar = 75; + i__1 = *ncols + *mcon + 1; + xerrwv_("DBOCLS(). THE COLUMN DIMENSION OF W(,)=(I1) MUST BE" + " .GE. NCOLS+MCON+1=(I2).", &nchar, &nerr, &level, & + c__2, &lndw, &i__1, &c__0, &rdum, &rdum, (ftnlen)75); +/* DO(RETURN TO USER PROGRAM UNIT) */ + goto L260; + } + if (llb < *ncols + *mcon) { + nerr = 43; + nchar = 94; + i__1 = *ncols + *mcon; + xerrwv_("DBOCLS(). THE DIMENSIONS OF THE ARRAYS BL(),BU(), A" + "ND IND()=(I1) MUST BE .GE. NCOLS+MCON=(I2).", &nchar, + &nerr, &level, &c__2, &llb, &i__1, &c__0, &rdum, & + rdum, (ftnlen)94); +/* DO(RETURN TO USER PROGRAM UNIT) */ + goto L260; + } + if (llx < lenx) { + nerr = 44; + nchar = 71; + xerrwv_("DBOCLS(). THE DIMENSION OF X()=(I1) MUST BE .GE. TH" + "E REQD. LENGTH=(I2).", &nchar, &nerr, &level, &c__2, & + llx, &lenx, &c__0, &rdum, &rdum, (ftnlen)71); +/* DO(RETURN TO USER PROGRAM UNIT) */ + goto L260; + } + if (llrw < *ncols * 6 + *mcon * 5) { + nerr = 45; + nchar = 70; + i__1 = *ncols * 6 + *mcon * 5; + xerrwv_("DBOCLS(). THE DIMENSION OF RW()=(I1) MUST BE .GE. 6" + "*NCOLS+5*MCON=(I2).", &nchar, &nerr, &level, &c__2, & + llrw, &i__1, &c__0, &rdum, &rdum, (ftnlen)70); +/* DO(RETURN TO USER PROGRAM UNIT) */ + goto L260; + } + if (lliw < (*ncols << 1) + (*mcon << 1)) { + nerr = 46; + nchar = 69; + i__1 = (*ncols << 1) + (*mcon << 1); + xerrwv_("DBOCLS() THE DIMENSION OF IW()=(I1) MUST BE .GE. 2*" + "NCOLS+2*MCON=(I2).", &nchar, &nerr, &level, &c__2, & + lliw, &i__1, &c__0, &rdum, &rdum, (ftnlen)69); +/* DO(RETURN TO USER PROGRAM UNIT) */ + goto L260; + } + if (liopt < lp + 17) { + nerr = 47; + nchar = 72; + i__1 = lp + 17; + xerrwv_("DBOCLS(). THE DIMENSION OF IOPT()=(I1) MUST BE .GE." + " THE REQD. LEN.=(I2).", &nchar, &nerr, &level, &c__2, + &liopt, &i__1, &c__0, &rdum, &rdum, (ftnlen)72); +/* DO(RETURN TO USER PROGRAM UNIT) */ + goto L260; + } +/* END PROCEDURE */ + } + } + +/* OPTIONALLY GO BACK TO THE USER FOR ACCUMULATION OF LEAST SQUARES */ +/* EQUATIONS AND DIRECTIONS FOR PROCESSING THESE EQUATIONS. */ +/* DO(ACCUMULATE LEAST SQUARES EQUATIONS) */ +/* PROCEDURE(ACCUMULATE LEAST SQUARES EQUATIONS) */ + if (accum) { + *mrows = iopt[locacc + 1] - 1 - *mcon; + inrows = iopt[locacc + 2]; + mnew = *mrows + inrows; + if (mnew < 0 || mnew + *mcon > *mdw) { + nerr = 52; + nchar = 66; + i__1 = *mdw - *mcon; + xerrwv_("DBOCLS(). NO. OF ROWS=(I1) MUST BE .GE. 0 .AND. .LE.MDW" + "-MCON=(I2)", &nchar, &nerr, &level, &c__2, &mnew, &i__1, & + c__0, &rdum, &rdum, (ftnlen)65); +/* (RETURN TO USER PROGRAM UNIT) */ + goto L260; + } + } + +/* USE THE SOFTWARE OF DBOLS( ) FOR THE TRIANGULARIZATION OF THE */ +/* LEAST SQUARES MATRIX. THIS MAY INVOLVE A SYSTALTIC INTERCHANGE */ +/* OF PROCESSING POINTERS BETWEEN THE CALLING AND CALLED (DBOLS()) */ +/* PROGRAM UNITS. */ + jopt[0] = 1; + jopt[1] = 2; + jopt[3] = *mrows; + jopt[4] = 99; + irw = *ncols + 1; + iiw = 1; + if (accum || pretri) { + dbols_(&w[*mcon + 1 + w_dim1], mdw, &mout, ncols, &bl[1], &bu[1], & + ind[1], jopt, &x[1], rnorm, mode, &rw[irw], &iw[iiw]); + } else { + mout = *mrows; + } + if (accum) { + accum = iopt[locacc] == 1; + iopt[locacc + 1] = jopt[2] + *mcon; +/* Computing MIN */ + i__1 = *ncols + 1; + *mrows = min(i__1,mnew); + } +/* END PROCEDURE */ + if (accum) { + return 0; + } +/* DO(SOLVE CONSTRAINED AND BOUNDED LEAST SQUARES PROBLEM) */ +/* PROCEDURE(SOLVE CONSTRAINED AND BOUNDED LEAST SQUARES PROBLEM) */ + +/* MOVE RIGHT HAND SIDE OF LEAST SQUARES EQUATIONS. */ + dcopy_(&mout, &w[*mcon + 1 + (*ncols + 1) * w_dim1], &c__1, &w[*mcon + 1 + + (*ncols + *mcon + 1) * w_dim1], &c__1); + if (*mcon > 0 && filter) { + +/* PROJECT THE LINEAR CONSTRAINTS INTO A REACHABLE SET. */ + i__1 = *mcon; + for (i__ = 1; i__ <= i__1; ++i__) { + dcopy_(ncols, &w[i__ + w_dim1], mdw, &w[*mcon + 1 + (*ncols + i__) + * w_dim1], &c__1); +/* L60: */ + } + +/* PLACE (-)IDENTITY MATRIX AFTER CONSTRAINT DATA. */ + i__1 = *ncols + *mcon + 1; + for (j = *ncols + 1; j <= i__1; ++j) { + w[j * w_dim1 + 1] = zero; + dcopy_(mcon, &w[j * w_dim1 + 1], &c__0, &w[j * w_dim1 + 1], &c__1) + ; +/* L70: */ + } + w[(*ncols + 1) * w_dim1 + 1] = -one; + i__1 = *mdw + 1; + dcopy_(mcon, &w[(*ncols + 1) * w_dim1 + 1], &c__0, &w[(*ncols + 1) * + w_dim1 + 1], &i__1); + +/* OBTAIN A 'FEASIBLE POINT' FOR THE LINEAR CONSTRAINTS. */ + jopt[0] = 99; + irw = *ncols + 1; + iiw = 1; + i__1 = *ncols + *mcon; + dbols_(&w[w_offset], mdw, mcon, &i__1, &bl[1], &bu[1], &ind[1], jopt, + &x[1], rnormc, &modec, &rw[irw], &iw[iiw]); + +/* ENLARGE THE BOUNDS SET, IF REQUIRED, TO INCLUDE POINTS THAT */ +/* CAN BE REACHED. */ + i__1 = *ncols + *mcon; + for (j = *ncols + 1; j <= i__1; ++j) { + icase = ind[j]; + if (icase < 4) { + t = ddot_(ncols, &w[*mcon + 1 + j * w_dim1], &c__1, &x[1], & + c__1); + } + switch (icase) { + case 1: goto L80; + case 2: goto L90; + case 3: goto L100; + case 4: goto L110; + } + goto L120; +/* CASE 1 */ +L80: +/* Computing MIN */ + d__1 = t, d__2 = bl[j]; + bl[j] = min(d__1,d__2); + goto L120; +/* CASE 2 */ +L90: +/* Computing MAX */ + d__1 = t, d__2 = bu[j]; + bu[j] = max(d__1,d__2); + goto L120; +/* CASE 3 */ +L100: +/* Computing MIN */ + d__1 = t, d__2 = bl[j]; + bl[j] = min(d__1,d__2); +/* Computing MAX */ + d__1 = t, d__2 = bu[j]; + bu[j] = max(d__1,d__2); + goto L120; +/* CASE 4 */ +L110: +L120: +/* L130: */ + ; + } + +/* MOVE CONSTRAINT DATA BACK TO THE ORIGINAL AREA. */ + i__1 = *ncols + *mcon; + for (j = *ncols + 1; j <= i__1; ++j) { + dcopy_(ncols, &w[*mcon + 1 + j * w_dim1], &c__1, &w[j - *ncols + + w_dim1], mdw); +/* L140: */ + } + } + if (*mcon > 0) { + i__1 = *ncols + *mcon; + for (j = *ncols + 1; j <= i__1; ++j) { + w[*mcon + 1 + j * w_dim1] = zero; + dcopy_(&mout, &w[*mcon + 1 + j * w_dim1], &c__0, &w[*mcon + 1 + j + * w_dim1], &c__1); +/* L150: */ + } + +/* PUT IN (-)IDENTITY MATRIX (POSSIBLY) ONCE AGAIN. */ + i__1 = *ncols + *mcon + 1; + for (j = *ncols + 1; j <= i__1; ++j) { + w[j * w_dim1 + 1] = zero; + dcopy_(mcon, &w[j * w_dim1 + 1], &c__0, &w[j * w_dim1 + 1], &c__1) + ; +/* L160: */ + } + w[(*ncols + 1) * w_dim1 + 1] = -one; + i__1 = *mdw + 1; + dcopy_(mcon, &w[(*ncols + 1) * w_dim1 + 1], &c__0, &w[(*ncols + 1) * + w_dim1 + 1], &i__1); + } + +/* COMPUTE NOMINAL COLUMN SCALING FOR THE UNWEIGHTED MATRIX. */ + cnorm = zero; + anorm = zero; + i__1 = *ncols; + for (j = 1; j <= i__1; ++j) { + t1 = dasum_(mcon, &w[j * w_dim1 + 1], &c__1); + t2 = dasum_(&mout, &w[*mcon + 1 + w_dim1], &c__1); + t = t1 + t2; + if (t == zero) { + t = one; + } + cnorm = max(cnorm,t1); + anorm = max(anorm,t2); + x[*ncols + *mcon + j] = one / t; +/* L170: */ + } + switch (iscale) { + case 1: goto L180; + case 2: goto L190; + case 3: goto L210; + case 4: goto L220; + } + goto L230; +/* CASE 1 */ +L180: + goto L230; +/* CASE 2 */ + +/* SCALE COLS. (BEFORE WEIGHTING) TO HAVE LENGTH ONE. */ +L190: + i__1 = *ncols; + for (j = 1; j <= i__1; ++j) { + i__2 = *mcon + mout; + t = dnrm2_(&i__2, &w[j * w_dim1 + 1], &c__1); + if (t == zero) { + t = one; + } + x[*ncols + *mcon + j] = one / t; +/* L200: */ + } + goto L230; +/* CASE 3 */ + +/* SUPPRESS SCALING (USE UNIT MATRIX). */ +L210: + x[*ncols + *mcon + 1] = one; + dcopy_(ncols, &x[*ncols + *mcon + 1], &c__0, &x[*ncols + *mcon + 1], & + c__1); + goto L230; +/* CASE 4 */ + +/* THE USER HAS PROVIDED SCALING. */ +L220: + dcopy_(ncols, &rw[1], &c__1, &x[*ncols + *mcon + 1], &c__1); +L230: + i__1 = *ncols + *mcon; + for (j = *ncols + 1; j <= i__1; ++j) { + x[*ncols + *mcon + j] = one; +/* L240: */ + } + +/* WEIGHT THE LEAST SQUARES EQUATIONS. */ + wt = drelpr; + if (anorm > zero) { + wt /= anorm; + } + if (cnorm > zero) { + wt *= cnorm; + } + i__1 = mout; + for (i__ = 1; i__ <= i__1; ++i__) { + dscal_(ncols, &wt, &w[i__ + *mcon + w_dim1], mdw); +/* L250: */ + } + dscal_(&mout, &wt, &w[*mcon + 1 + (*mcon + *ncols + 1) * w_dim1], &c__1); + lrw = 1; + liw = 1; + +/* SET THE NEW TRIANGULARIZATION FACTOR. */ + x[(*ncols + *mcon << 1) + 1] = zero; + +/* SET THE WEIGHT TO USE IN COMPONENTS .GT. MCON, */ +/* WHEN MAKING LINEAR INDEPENDENCE TEST. */ + x[(*ncols + *mcon << 1) + 2] = one / wt; + i__1 = mout + *mcon; + i__2 = *ncols + *mcon; + dbols_(&w[w_offset], mdw, &i__1, &i__2, &bl[1], &bu[1], &ind[1], &iopt[ + lbou], &x[1], rnorm, mode, &rw[lrw], &iw[liw]); + *rnorm /= wt; +/* END PROCEDURE */ +/* PROCEDURE(RETURN TO USER PROGRAM UNIT) */ +L260: + if (*mode >= 0) { + *mode = -nerr; + } + igo = 0; + return 0; +/* END PROGRAM */ +} /* dbocls_ */ + diff --git a/Cantera/src/equil/vcs_dbols.c b/Cantera/src/equil/vcs_dbols.c new file mode 100644 index 000000000..8d0e2ce56 --- /dev/null +++ b/Cantera/src/equil/vcs_dbols.c @@ -0,0 +1,957 @@ +/* dbols.f -- translated by f2c (version 20031025). + You must link the resulting object file with libf2c: + on Microsoft Windows system, link with libf2c.lib; + on Linux or Unix systems, link with .../path/to/libf2c.a -lm + or, if you install libf2c.a in a standard place, with -lf2c -lm + -- in that order, at the end of the command line, as in + cc *.o -lf2c -lm + Source for libf2c is in /netlib/f2c/libf2c.zip, e.g., + + http://www.netlib.org/f2c/libf2c.zip +*/ + +#include "f2c.h" + +/* Table of constant values */ + +static integer c__1 = 1; +static integer c__0 = 0; +static integer c__2 = 2; + +/* DECK DBOLS */ +/* Subroutine */ int dbols_(doublereal *w, integer *mdw, integer *mrows, + integer *ncols, doublereal *bl, doublereal *bu, integer *ind, integer + *iopt, doublereal *x, doublereal *rnorm, integer *mode, doublereal * + rw, integer *iw) +{ + /* Initialized data */ + + static integer igo = 0; + + /* System generated locals */ + integer w_dim1, w_offset, i__1, i__2, i__3; + doublereal d__1; + + /* Local variables */ + static integer i__, j; + static doublereal sc; + static integer ip, jp, lp; + static doublereal ss; + static integer llb; + static doublereal one; + static integer lds, llx, ibig, idum, lmdw, lndw, nerr; + static real rdum; + static integer lenx, lliw, mnew; + extern /* Subroutine */ int drot_(integer *, doublereal *, integer *, + doublereal *, integer *, doublereal *, doublereal *); + static integer lopt; + static doublereal zero; + static integer llrw; + extern doublereal dnrm2_(integer *, doublereal *, integer *); + static real rdum2; + static integer nchar, level; + extern /* Subroutine */ int dcopy_(integer *, doublereal *, integer *, + doublereal *, integer *), drotg_(doublereal *, doublereal *, + doublereal *, doublereal *); + static integer liopt, locacc; + static logical checkl; + static integer iscale; + extern integer idamax_(integer *, doublereal *, integer *); + static integer locdim; + extern /* Subroutine */ int dbolsm_(doublereal *, integer *, integer *, + integer *, doublereal *, doublereal *, integer *, integer *, + doublereal *, doublereal *, integer *, doublereal *, doublereal *, + doublereal *, integer *, integer *); + static integer inrows; + extern /* Subroutine */ int xerrwv_(char *, integer *, integer *, integer + *, integer *, integer *, integer *, integer *, real *, real *, + ftnlen); + +/* ***BEGIN PROLOGUE DBOLS */ +/* ***DATE WRITTEN 821220 (YYMMDD) */ +/* ***REVISION DATE 861211 (YYMMDD) */ +/* ***CATEGORY NO. K1A2A,G2E,G2H1,G2H2 */ +/* ***KEYWORDS LIBRARY=SLATEC,TYPE=DOUBLE PRECISION(SBOLS-S DBOLS-D), */ +/* BOUNDS,CONSTRAINTS,INEQUALITY,LEAST SQUARES,LINEAR */ +/* ***AUTHOR HANSON, R. J., SNLA */ +/* ***PURPOSE Solve the problem */ +/* E*X = F (in the least squares sense) */ +/* with bounds on selected X values. */ +/* ***DESCRIPTION */ + +/* **** Double Precision Version of SBOLS **** */ +/* **** All INPUT and OUTPUT real variables are DOUBLE PRECISION **** */ + +/* The user must have dimension statements of the form: */ + +/* DIMENSION W(MDW,NCOLS+1), BL(NCOLS), BU(NCOLS), */ +/* * X(NCOLS+NX), RW(5*NCOLS) */ +/* INTEGER IND(NCOLS), IOPT(1+NI), IW(2*NCOLS) */ + +/* (here NX=number of extra locations required for option 4; NX=0 */ +/* for no options; NX=NCOLS if this option is in use. Here NI=number */ +/* of extra locations required for options 1-6; NI=0 for no */ +/* options.) */ + +/* INPUT */ +/* ----- */ + +/* -------------------- */ +/* W(MDW,*),MROWS,NCOLS */ +/* -------------------- */ +/* The array W(*,*) contains the matrix [E:F] on entry. The matrix */ +/* [E:F] has MROWS rows and NCOLS+1 columns. This data is placed in */ +/* the array W(*,*) with E occupying the first NCOLS columns and the */ +/* right side vector F in column NCOLS+1. The row dimension, MDW, of */ +/* the array W(*,*) must satisfy the inequality MDW .ge. MROWS. */ +/* Other values of MDW are errrors. The values of MROWS and NCOLS */ +/* must be positive. Other values are errors. There is an exception */ +/* to this when using option 1 for accumulation of blocks of */ +/* equations. In that case MROWS is an OUTPUT variable ONLY, and the */ +/* matrix data for [E:F] is placed in W(*,*), one block of rows at a */ +/* time. MROWS contains the number of rows in the matrix after */ +/* triangularizing several blocks of equations. This is an OUTPUT */ +/* parameter ONLY when option 1 is used. See IOPT(*) CONTENTS */ +/* for details about option 1. */ + +/* ------------------ */ +/* BL(*),BU(*),IND(*) */ +/* ------------------ */ +/* These arrays contain the information about the bounds that the */ +/* solution values are to satisfy. The value of IND(J) tells the */ +/* type of bound and BL(J) and BU(J) give the explicit values for */ +/* the respective upper and lower bounds. */ + +/* 1. For IND(J)=1, require X(J) .ge. BL(J). */ +/* (the value of BU(J) is not used.) */ +/* 2. For IND(J)=2, require X(J) .le. BU(J). */ +/* (the value of BL(J) is not used.) */ +/* 3. For IND(J)=3, require X(J) .ge. BL(J) and */ +/* X(J) .le. BU(J). */ +/* 4. For IND(J)=4, no bounds on X(J) are required. */ +/* (the values of BL(J) and BU(J) are not used.) */ + +/* Values other than 1,2,3 or 4 for IND(J) are errors. In the case */ +/* IND(J)=3 (upper and lower bounds) the condition BL(J) .gt. BU(J) */ +/* is an error. */ + +/* ------- */ +/* IOPT(*) */ +/* ------- */ +/* This is the array where the user can specify nonstandard options */ +/* for DBOLSM( ). Most of the time this feature can be ignored by */ +/* setting the input value IOPT(1)=99. Occasionally users may have */ +/* needs that require use of the following subprogram options. For */ +/* details about how to use the options see below: IOPT(*) CONTENTS. */ + +/* Option Number Brief Statement of Purpose */ +/* ------ ------ ----- --------- -- ------- */ +/* 1 Return to user for accumulation of blocks */ +/* of least squares equations. */ +/* 2 Check lengths of all arrays used in the */ +/* subprogram. */ +/* 3 Standard scaling of the data matrix, E. */ +/* 4 User provides column scaling for matrix E. */ +/* 5 Provide option array to the low-level */ +/* subprogram DBOLSM( ). */ +/* 6 Move the IOPT(*) processing pointer. */ +/* 99 No more options to change. */ + +/* ---- */ +/* X(*) */ +/* ---- */ +/* This array is used to pass data associated with option 4. Ignore */ +/* this parameter if this option is not used. Otherwise see below: */ +/* IOPT(*) CONTENTS. */ + +/* OUTPUT */ +/* ------ */ + +/* ---------- */ +/* X(*),RNORM */ +/* ---------- */ +/* The array X(*) contains a solution (if MODE .ge.0 or .eq.-22) for */ +/* the constrained least squares problem. The value RNORM is the */ +/* minimum residual vector length. */ + +/* ---- */ +/* MODE */ +/* ---- */ +/* The sign of MODE determines whether the subprogram has completed */ +/* normally, or encountered an error condition or abnormal status. A */ +/* value of MODE .ge. 0 signifies that the subprogram has completed */ +/* normally. The value of MODE (.GE. 0) is the number of variables */ +/* in an active status: not at a bound nor at the value ZERO, for */ +/* the case of free variables. A negative value of MODE will be one */ +/* of the cases -37,-36,...,-22, or -17,...,-2. Values .lt. -1 */ +/* correspond to an abnormal completion of the subprogram. To */ +/* understand the abnormal completion codes see below: ERROR */ +/* MESSAGES for DBOLS( ). AN approximate solution will be returned */ +/* to the user only when max. iterations is reached, MODE=-22. */ +/* Values for MODE=-37,...,-22 come from the low-level subprogram */ +/* DBOLSM(). See the section ERROR MESSAGES for DBOLSM() in the */ +/* documentation for DBOLSM(). */ + +/* ----------- */ +/* RW(*),IW(*) */ +/* ----------- */ +/* These are working arrays with 5*NCOLS and 2*NCOLS entries. */ +/* (normally the user can ignore the contents of these arrays, */ +/* but they must be dimensioned properly.) */ + +/* IOPT(*) CONTENTS */ +/* ------- -------- */ +/* The option array allows a user to modify internal variables in */ +/* the subprogram without recompiling the source code. A central */ +/* goal of the initial software design was to do a good job for most */ +/* people. Thus the use of options will be restricted to a select */ +/* group of users. The processing of the option array proceeds as */ +/* follows: a pointer, here called LP, is initially set to the value */ +/* 1. This value is updated as each option is processed. At the */ +/* pointer position the option number is extracted and used for */ +/* locating other information that allows for options to be changed. */ +/* The portion of the array IOPT(*) that is used for each option is */ +/* fixed; the user and the subprogram both know how many locations */ +/* are needed for each option. A great deal of error checking is */ +/* done by the subprogram on the contents of the option array. */ +/* Nevertheless it is still possible to give the subprogram optional */ +/* input that is meaningless. For example option 4 uses the */ +/* locations X(NCOLS+IOFF),...,X(NCOLS+IOFF+NCOLS-1) for passing */ +/* scaling data. The user must manage the allocation of these */ +/* locations. */ + +/* 1 */ +/* - */ +/* This option allows the user to solve problems with a large number */ +/* of rows compared to the number of variables. The idea is that the */ +/* subprogram returns to the user (perhaps many times) and receives */ +/* new least squares equations from the calling program unit. */ +/* Eventually the user signals "that's all" and then computes the */ +/* solution with one final call to subprogram DBOLS( ). The value of */ +/* MROWS is an OUTPUT variable when this option is used. Its value */ +/* is always in the range 0 .le. MROWS .le. NCOLS+1. It is equal to */ +/* the number of rows after the triangularization of the entire set */ +/* of equations. If LP is the processing pointer for IOPT(*), the */ +/* usage for the sequential processing of blocks of equations is */ + +/* IOPT(LP)=1 */ +/* Move block of equations to W(*,*) starting at */ +/* the first row of W(*,*). */ +/* IOPT(LP+3)=# of rows in the block; user defined */ + +/* The user now calls DBOLS( ) in a loop. The value of IOPT(LP+1) */ +/* directs the user's action. The value of IOPT(LP+2) points to */ +/* where the subsequent rows are to be placed in W(*,*). */ + +/* . 4) { + nerr = 4; + nchar = 45; + xerrwv_("DBOLS(). FOR J=(I1), IND(J)=(I2) MUST BE 1-4.", & + nchar, &nerr, &level, &c__2, &j, &ind[j], &c__0, & + rdum, &rdum, (ftnlen)45); +/* DO(RETURN TO USER PROGRAM UNIT) */ + goto L190; + } +/* L10: */ + } + +/* SEE THAT BOUNDS ARE CONSISTENT. */ + i__1 = *ncols; + for (j = 1; j <= i__1; ++j) { + if (ind[j] == 3) { + if (bl[j] > bu[j]) { + nerr = 5; + nchar = 57; + rdum2 = bl[j]; + rdum = bu[j]; + xerrwv_("DBOLS(). FOR J=(I1), BOUND BL(J)=(R1) IS .GT. B" + "U(J)=(R2).", &nchar, &nerr, &level, &c__1, &j, & + idum, &c__2, &rdum2, &rdum, (ftnlen)57); +/* DO(RETURN TO USER PROGRAM UNIT) */ + goto L190; + } + } +/* L20: */ + } +/* END PROCEDURE */ +/* DO(PROCESS OPTION ARRAY) */ +/* PROCEDURE(PROCESS OPTION ARRAY) */ + zero = 0.; + one = 1.; + checkl = FALSE_; + lenx = *ncols; + iscale = 1; + igo = 2; + lopt = 0; + lp = 0; + lds = 0; +L30: + lp += lds; + ip = iopt[lp + 1]; + jp = abs(ip); + +/* TEST FOR NO MORE OPTIONS. */ + if (ip == 99) { + if (lopt == 0) { + lopt = lp + 1; + } + goto L50; + } else if (jp == 99) { + lds = 1; + goto L30; + } else if (jp == 1) { + if (ip > 0) { + +/* SET UP DIRECTION FLAG, ROW STACKING POINTER */ +/* LOCATION, AND LOCATION FOR NUMBER OF NEW ROWS. */ + locacc = lp + 2; + +/* IOPT(LOCACC-1)=OPTION NUMBER FOR SEQ. ACCUMULATION. */ +/* CONTENTS.. IOPT(LOCACC )=USER DIRECTION FLAG, 1 OR 2. */ +/* IOPT(LOCACC+1)=ROW STACKING POINTER. */ +/* IOPT(LOCACC+2)=NUMBER OF NEW ROWS TO PROCESS. */ +/* USER ACTION WITH THIS OPTION.. */ +/* (SET UP OPTION DATA FOR SEQ. ACCUMULATION IN IOPT(*). */ +/* MUST ALSO START PROCESS WITH IOPT(LOCACC)=1.) */ +/* (MOVE BLOCK OF EQUATIONS INTO W(*,*) STARTING AT FIRST */ +/* ROW OF W(*,*). SET IOPT(LOCACC+2)=NO. OF ROWS IN BLOCK.) */ +/* LOOP */ +/* CALL DBOLS() */ + +/* IF(IOPT(LOCACC) .EQ. 1) THEN */ +/* STACK EQUAS., STARTING AT ROW IOPT(LOCACC+1), */ +/* INTO W(*,*). */ +/* SET IOPT(LOCACC+2)=NO. OF EQUAS. */ +/* IF LAST BLOCK OF EQUAS., SET IOPT(LOCACC)=2. */ +/* ELSE IF IOPT(LOCACC) .EQ. 2) THEN */ +/* (PROCESS IS OVER. EXIT LOOP.) */ +/* ELSE */ +/* (ERROR CONDITION. SHOULD NOT HAPPEN.) */ +/* END IF */ +/* END LOOP */ +/* SET IOPT(LOCACC-1)=-OPTION NUMBER FOR SEQ. ACCUMULATION. */ +/* CALL DBOLS( ) */ + iopt[locacc + 1] = 1; + igo = 1; + } + lds = 4; + goto L30; + } else if (jp == 2) { + if (ip > 0) { + +/* GET ACTUAL LENGTHS OF ARRAYS FOR CHECKING AGAINST NEEDS. */ + locdim = lp + 2; + +/* LMDW.GE.MROWS */ +/* LNDW.GE.NCOLS+1 */ +/* LLB .GE.NCOLS */ +/* LLX .GE.NCOLS+EXTRA REQD. IN OPTIONS. */ +/* LLRW.GE.5*NCOLS */ +/* LLIW.GE.2*NCOLS */ +/* LIOP.GE. AMOUNT REQD. FOR IOPTION ARRAY. */ + lmdw = iopt[locdim]; + lndw = iopt[locdim + 1]; + llb = iopt[locdim + 2]; + llx = iopt[locdim + 3]; + llrw = iopt[locdim + 4]; + lliw = iopt[locdim + 5]; + liopt = iopt[locdim + 6]; + checkl = TRUE_; + } + lds = 8; + goto L30; + +/* OPTION TO MODIFY THE COLUMN SCALING. */ + } else if (jp == 3) { + if (ip > 0) { + iscale = iopt[lp + 2]; + +/* SEE THAT ISCALE IS 1 THRU 3. */ + if (iscale < 1 || iscale > 3) { + nerr = 7; + nchar = 40; + xerrwv_("DBOLS(). ISCALE OPTION=(I1) MUST BE 1-3.", & + nchar, &nerr, &level, &c__1, &iscale, &idum, & + c__0, &rdum, &rdum, (ftnlen)40); +/* DO(RETURN TO USER PROGRAM UNIT) */ + goto L190; + } + } + lds = 2; +/* CYCLE FOREVER */ + goto L30; + +/* IN THIS OPTION THE USER HAS PROVIDED SCALING. THE */ +/* SCALE FACTORS FOR THE COLUMNS BEGIN IN X(NCOLS+IOPT(LP+2)). */ + } else if (jp == 4) { + if (ip > 0) { + iscale = 4; + if (iopt[lp + 2] <= 0) { + nerr = 8; + nchar = 85; + xerrwv_("DBOLS(). OFFSET PAST X(NCOLS) (I1) FOR USER-PRO" + "VIDED COLUMN SCALING MUST BE POSITIVE.", &nchar, & + nerr, &level, &c__1, &iopt[lp + 2], &idum, &c__0, + &rdum, &rdum, (ftnlen)85); +/* DO(RETURN TO USER PROGRAM UNIT) */ + goto L190; + } + dcopy_(ncols, &x[*ncols + iopt[lp + 2]], &c__1, &rw[1], &c__1) + ; + lenx += *ncols; + i__1 = *ncols; + for (j = 1; j <= i__1; ++j) { + if (rw[j] <= zero) { + nerr = 9; + nchar = 85; + rdum2 = rw[j]; + xerrwv_("DBOLS(). EACH PROVIDED COL. SCALE FACTOR MU" + "ST BE POSITIVE. COMPONENT (I1) NOW = (R1).", & + nchar, &nerr, &level, &c__1, &j, &idum, &c__1, + &rdum2, &rdum, (ftnlen)85); +/* DO(RETURN TO USER PROGRAM UNIT) */ + goto L190; + } +/* L40: */ + } + } + lds = 2; +/* CYCLE FOREVER */ + goto L30; + +/* IN THIS OPTION AN OPTION ARRAY IS PROVIDED TO DBOLSM(). */ + } else if (jp == 5) { + if (ip > 0) { + lopt = iopt[lp + 2]; + } + lds = 2; +/* CYCLE FOREVER */ + goto L30; + +/* THIS OPTION USES THE NEXT LOC OF IOPT(*) AS AN */ +/* INCREMENT TO SKIP. */ + } else if (jp == 6) { + if (ip > 0) { + lp = iopt[lp + 2] - 1; + lds = 0; + } else { + lds = 2; + } +/* CYCLE FOREVER */ + goto L30; + +/* NO VALID OPTION NUMBER WAS NOTED. THIS IS AN ERROR CONDITION. */ + } else { + nerr = 6; + nchar = 47; + rdum2 = (real) idum; + xerrwv_("DBOLS(). THE OPTION NUMBER=(I1) IS NOT DEFINED.", &nchar, + &nerr, &level, &c__1, &jp, &idum, &c__0, &rdum2, &rdum2, + (ftnlen)47); +/* DO(RETURN TO USER PROGRAM UNIT) */ + goto L190; + } +L50: +/* END PROCEDURE */ + if (checkl) { +/* DO(CHECK LENGTHS OF ARRAYS) */ +/* PROCEDURE(CHECK LENGTHS OF ARRAYS) */ + +/* THIS FEATURE ALLOWS THE USER TO MAKE SURE THAT THE */ +/* ARRAYS ARE LONG ENOUGH FOR THE INTENDED PROBLEM SIZE AND USE. */ + if (lmdw < *mrows) { + nerr = 11; + nchar = 76; + xerrwv_("DBOLS(). THE ROW DIMENSION OF W(,)=(I1) MUST BE .GE" + ".THE NUMBER OF ROWS=(I2).", &nchar, &nerr, &level, & + c__2, &lmdw, mrows, &c__0, &rdum, &rdum, (ftnlen)76); +/* DO(RETURN TO USER PROGRAM UNIT) */ + goto L190; + } + if (lndw < *ncols + 1) { + nerr = 12; + nchar = 69; + i__1 = *ncols + 1; + xerrwv_("DBOLS(). THE COLUMN DIMENSION OF W(,)=(I1) MUST BE " + ".GE. NCOLS+1=(I2).", &nchar, &nerr, &level, &c__2, & + lndw, &i__1, &c__0, &rdum, &rdum, (ftnlen)69); +/* DO(RETURN TO USER PROGRAM UNIT) */ + goto L190; + } + if (llb < *ncols) { + nerr = 13; + nchar = 88; + xerrwv_("DBOLS(). THE DIMENSIONS OF THE ARRAYS BL(),BU(), AN" + "D IND()=(I1) MUST BE .GE. NCOLS=(I2).", &nchar, &nerr, + &level, &c__2, &llb, ncols, &c__0, &rdum, &rdum, ( + ftnlen)88); +/* DO(RETURN TO USER PROGRAM UNIT) */ + goto L190; + } + if (llx < lenx) { + nerr = 14; + nchar = 70; + xerrwv_("DBOLS(). THE DIMENSION OF X()=(I1) MUST BE .GE. THE" + " REQD. LENGTH=(I2).", &nchar, &nerr, &level, &c__2, & + llx, &lenx, &c__0, &rdum, &rdum, (ftnlen)70); +/* DO(RETURN TO USER PROGRAM UNIT) */ + goto L190; + } + if (llrw < *ncols * 5) { + nerr = 15; + nchar = 62; + i__1 = *ncols * 5; + xerrwv_("DBOLS(). THE DIMENSION OF RW()=(I1) MUST BE .GE. 5*" + "NCOLS=(I2).", &nchar, &nerr, &level, &c__2, &llrw, & + i__1, &c__0, &rdum, &rdum, (ftnlen)62); +/* DO(RETURN TO USER PROGRAM UNIT) */ + goto L190; + } + if (lliw < *ncols << 1) { + nerr = 16; + nchar = 61; + i__1 = *ncols << 1; + xerrwv_("DBOLS() THE DIMENSION OF IW()=(I1) MUST BE .GE. 2*N" + "COLS=(I2).", &nchar, &nerr, &level, &c__2, &lliw, & + i__1, &c__0, &rdum, &rdum, (ftnlen)61); +/* DO(RETURN TO USER PROGRAM UNIT) */ + goto L190; + } + if (liopt < lp + 1) { + nerr = 17; + nchar = 71; + i__1 = lp + 1; + xerrwv_("DBOLS(). THE DIMENSION OF IOPT()=(I1) MUST BE .GE. " + "THE REQD. LEN.=(I2).", &nchar, &nerr, &level, &c__2, & + liopt, &i__1, &c__0, &rdum, &rdum, (ftnlen)71); +/* DO(RETURN TO USER PROGRAM UNIT) */ + goto L190; + } +/* END PROCEDURE */ + } + } + switch (igo) { + case 1: goto L60; + case 2: goto L90; + } + goto L180; + +/* GO BACK TO THE USER FOR ACCUMULATION OF LEAST SQUARES */ +/* EQUATIONS AND DIRECTIONS TO QUIT PROCESSING. */ +/* CASE 1 */ +L60: +/* DO(ACCUMULATE LEAST SQUARES EQUATIONS) */ +/* PROCEDURE(ACCUMULATE LEAST SQUARES EQUATIONS) */ + *mrows = iopt[locacc + 1] - 1; + inrows = iopt[locacc + 2]; + mnew = *mrows + inrows; + if (mnew < 0 || mnew > *mdw) { + nerr = 10; + nchar = 61; + xerrwv_("DBOLS(). NO. OF ROWS=(I1) MUST BE .GE. 0 .AND. .LE. MDW=(I2" + ").", &nchar, &nerr, &level, &c__2, &mnew, mdw, &c__0, &rdum, & + rdum, (ftnlen)61); +/* DO(RETURN TO USER PROGRAM UNIT) */ + goto L190; + } +/* Computing MIN */ + i__2 = *ncols + 1; + i__1 = min(i__2,mnew); + for (j = 1; j <= i__1; ++j) { + i__2 = max(*mrows,j) + 1; + for (i__ = mnew; i__ >= i__2; --i__) { + i__3 = i__ - j; + ibig = idamax_(&i__3, &w[j + j * w_dim1], &c__1) + j - 1; + +/* PIVOT FOR INCREASED STABILITY. */ + drotg_(&w[ibig + j * w_dim1], &w[i__ + j * w_dim1], &sc, &ss); + i__3 = *ncols + 1 - j; + drot_(&i__3, &w[ibig + (j + 1) * w_dim1], mdw, &w[i__ + (j + 1) * + w_dim1], mdw, &sc, &ss); + w[i__ + j * w_dim1] = zero; +/* L70: */ + } +/* L80: */ + } +/* Computing MIN */ + i__1 = *ncols + 1; + *mrows = min(i__1,mnew); + iopt[locacc + 1] = *mrows + 1; + igo = iopt[locacc]; +/* END PROCEDURE */ + if (igo == 2) { + igo = 0; + } + goto L180; +/* CASE 2 */ +L90: +/* DO(INITIALIZE VARIABLES AND DATA VALUES) */ +/* PROCEDURE(INITIALIZE VARIABLES AND DATA VALUES) */ + i__1 = *ncols; + for (j = 1; j <= i__1; ++j) { + switch (iscale) { + case 1: goto L100; + case 2: goto L110; + case 3: goto L120; + case 4: goto L130; + } + goto L140; +L100: +/* CASE 1 */ + +/* THIS IS THE NOMINAL SCALING. EACH NONZERO */ +/* COL. HAS MAX. NORM EQUAL TO ONE. */ + ibig = idamax_(mrows, &w[j * w_dim1 + 1], &c__1); + rw[j] = (d__1 = w[ibig + j * w_dim1], abs(d__1)); + if (rw[j] == zero) { + rw[j] = one; + } else { + rw[j] = one / rw[j]; + } + goto L140; +L110: +/* CASE 2 */ + +/* THIS CHOICE OF SCALING MAKES EACH NONZERO COLUMN */ +/* HAVE EUCLIDEAN LENGTH EQUAL TO ONE. */ + rw[j] = dnrm2_(mrows, &w[j * w_dim1 + 1], &c__1); + if (rw[j] == zero) { + rw[j] = one; + } else { + rw[j] = one / rw[j]; + } + goto L140; +L120: +/* CASE 3 */ + +/* THIS CASE EFFECTIVELY SUPPRESSES SCALING BY SETTING */ +/* THE SCALING MATRIX TO THE IDENTITY MATRIX. */ + rw[1] = one; + dcopy_(ncols, &rw[1], &c__0, &rw[1], &c__1); + goto L160; +L130: +/* CASE 4 */ + goto L160; +L140: +/* L150: */ + ; + } +L160: +/* END PROCEDURE */ +/* DO(SOLVE BOUNDED LEAST SQUARES PROBLEM) */ +/* PROCEDURE(SOLVE BOUNDED LEAST SQUARES PROBLEM) */ + +/* INITIALIZE IBASIS(*), J=1,NCOLS, AND IBB(*), J=1,NCOLS, */ +/* TO =J,AND =1, FOR USE IN DBOLSM( ). */ + i__1 = *ncols; + for (j = 1; j <= i__1; ++j) { + iw[j] = j; + iw[j + *ncols] = 1; + rw[*ncols * 3 + j] = bl[j]; + rw[(*ncols << 2) + j] = bu[j]; +/* L170: */ + } + dbolsm_(&w[w_offset], mdw, mrows, ncols, &rw[*ncols * 3 + 1], &rw[(*ncols + << 2) + 1], &ind[1], &iopt[lopt], &x[1], rnorm, mode, &rw[*ncols + + 1], &rw[(*ncols << 1) + 1], &rw[1], &iw[1], &iw[*ncols + 1]); +/* END PROCEDURE */ + igo = 0; +L180: + return 0; +/* PROCEDURE(RETURN TO USER PROGRAM UNIT) */ +L190: + if (*mode >= 0) { + *mode = -nerr; + } + igo = 0; + return 0; +/* END PROCEDURE */ +} /* dbols_ */ + diff --git a/Cantera/src/equil/vcs_defs.h b/Cantera/src/equil/vcs_defs.h new file mode 100644 index 000000000..b3c2f8c83 --- /dev/null +++ b/Cantera/src/equil/vcs_defs.h @@ -0,0 +1,262 @@ +/** + * @file vcs_defs.h + * Defines and definitions within the vcs package + */ +/* + * $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. + */ + +#ifndef VCS_DEFS_H +#define VCS_DEFS_H + +namespace VCSnonideal { + +/*****************************************************************************/ +/*****************************************************************************/ +/*****************************************************************************/ +/* + * + * COMMON DEFINITIONS -> Protect them against redefinitions + */ + +#ifndef TRUE +# define TRUE 1 +#endif + +#ifndef FALSE +# define FALSE 0 +#endif + +#ifndef BOOLEAN +# define BOOLEAN int +#endif + +#ifndef MAX +# define MAX(x,y) (( (x) > (y) ) ? (x) : (y)) +#endif + +#ifndef MIN +# define MIN(x,y) (( (x) < (y) ) ? (x) : (y)) +#endif + +#ifndef SWAP +# define SWAP(x1, x2, temp) ((temp) = (x1), (x1) = (x2), (x2) = (temp)) +#endif + +#ifndef SQUARE +# define SQUARE(x) ((x) * (x)) +#endif + +#ifndef DSIGN +# define DSIGN(x) (( (x) == (0.0) ) ? (0.0) : ( ((x) > 0.0) ? 1.0 : -1.0 )) +#endif + +/*****************************************************************************/ +/*****************************************************************************/ +/*****************************************************************************/ +/* + * ERROR CODES + * + */ + +#define VCS_SUCCESS 0 +#define VCS_NOMEMORY 1 +#define VCS_FAILED_CONVERGENCE -1 +#define VCS_SHOULDNT_BE_HERE -2 +#define VCS_PUB_BAD -3 +#define VCS_THERMO_OUTOFRANGE -4 +#define VCS_FAILED_LOOKUP -5 +#define VCS_MP_FAIL -6 + +#define VCS_ERR_NOVALUE -1.1234E15 + +/*****************************************************************************/ +/*****************************************************************************/ +/*****************************************************************************/ +/* + * Problem Types + */ +#define VCS_PROBTYPE_TP 0 +#define VCS_PROBTYPE_TV 1 + +/*****************************************************************************/ +/*****************************************************************************/ +/*****************************************************************************/ +/* + * Maximum Length of any name in this package + */ +#define VCS_MAX_NAME_LEN 31 + +#define VCS_MAX_NAME_LEN_P1 32 + +/*****************************************************************************/ +/*****************************************************************************/ +/*****************************************************************************/ +/* + * SIZES OF PHASES AND MOLE NUMBER CUTOFFS + * + * VCS_DELETE_SPECIES_CUTOFF: Cutoff relative mole number value, + * below which species are deleted + * from the equilibrium problem. + */ +#ifndef VCS_DELETE_SPECIES_CUTOFF +#define VCS_DELETE_SPECIES_CUTOFF 1.0e-32 +#endif +#ifndef VCS_DELETE_MINORSPECIES_CUTOFF +#define VCS_DELETE_MINORSPECIES_CUTOFF 1.0e-140 +#endif + +/* +* VCS_SMALL_MULTIPHASE_SPECIES: +* Relative value of multiphase +* species mole number for a multiphase +* species which is small. +*/ +#define VCS_SMALL_MULTIPHASE_SPECIES 1.0e-25 + +/* +* VCS_DELETE_PHASE_CUTOFF: Cutoff relative moles below +* which a phase is deleted +* from the equilibrium problem. +*/ +#ifndef VCS_DELETE_PHASE_CUTOFF +#define VCS_DELETE_PHASE_CUTOFF 1.0e-11 +#endif + +/*****************************************************************************/ +/*****************************************************************************/ +/*****************************************************************************/ +/* + * State of Dimensional Units for Gibbs free energies + */ +#define VCS_NONDIMENSIONAL_G 1 +#define VCS_DIMENSIONAL_G 0 + +/*****************************************************************************/ +/*****************************************************************************/ +/*****************************************************************************/ +/* + * SPECIES CATEGORIES USED IN VCS_SOLVE_TP + */ +//! @name Species Types during the iteration +//! valid values for spStatus() +//@{ +//! Species is a component +#define VCS_SPECIES_COMPONENT 2 + +//! Species is a major species +/*! + * A major species is either a species in a multicomponent phase with + * significant concentration or its a Stoich Phase + */ +#define VCS_SPECIES_MAJOR 1 + +//! Species is a major species +/*! + * A major species is either a species in a multicomponent phase with + * significant concentration or its a Stoich Phase + */ +#define VCS_SPECIES_MINOR 0 + +//! Species lies in a multicomponent phase that is zeroed atm +/*! + * The species lies in a multicomponent phase that is currently + * deleted. + */ +#define VCS_SPECIES_ZEROEDPHASE -1 + +//! Species lies in a multicomponent phase, with concentration zero +/*! + * The species lies in a multicomponent phase that exists. + * It concentration is currently zero, even though it may + * or may not actually have a low mole fraction in the phase + * this situation occurs when phases pop back into life. + */ +#define VCS_SPECIES_ZEROEDMS -2 + +//! Species is a SS phase, that is currently zeroed out. +/*! + * The species lies in a single-species phase which + * is currently zereod out. + */ +#define VCS_SPECIES_ZEROEDSS -3 + +//! Species has such a small mole fraction it is deleted. +/*! + * The species is believed to have such a small mole fraction + * that it best to throw the calculation of it out. + * It will be aded back in at the end of the calculation. + */ +#define VCS_SPECIES_DELETED -4 + +//! Species refers to an electron in the metal +/*! + * The unknown is equal to the interfacial voltage + * drop across the interface on the SHE (standard + * hyrdogen electrode) scale (volts). + */ +#define VCS_SPECIES_INTERFACIALVOLTAGE -5 + +/*****************************************************************************/ +/*****************************************************************************/ +/*****************************************************************************/ +/* + * Units for the chemical potential data and pressure variables: + * + * Chem_Pot Pres vol moles + * ------------------------------------------------- + * VCS_UNITS_KCALMOL = kcal/mol atm cm**3 gmol + * VCS_UNITS_UNITLESS = MU / RT -> no units atm cm**3 gmol + * VCS_UNITS_KJMOL = kJ / mol atm cm**3 gmol + * VCS_UNITS_KELVIN = KELVIN -> MU / R atm cm**3 gmol + * VCS_UNITS_MKS = Joules / Kmol (Cantera) Pa m**3 kmol + * + * Energy: + * VCS_UNITS_KCALMOL = kcal/mol + * VCS_UNITS_UNITLESS = MU / RT -> no units + * VCS_UNITS_KJMOL = kJ / mol + * VCS_UNITS_KELVIN = KELVIN -> MU / R + * VCS_UNITS_MKS = J / kmol + * + * Pressure: (Pref and Pres) + * VCS_UNITS_KCALMOL = atm + * VCS_UNITS_UNITLESS = no units + * VCS_UNITS_KJMOL = atm + * VCS_UNITS_KELVIN = atm + * VCS_UNITS_MKS = Pa = kg / m s2 + */ +#define VCS_UNITS_KCALMOL -1 +#define VCS_UNITS_UNITLESS 0 +#define VCS_UNITS_KJMOL 1 +#define VCS_UNITS_KELVIN 2 +#define VCS_UNITS_MKS 3 + +/*****************************************************************************/ +/*****************************************************************************/ +/*****************************************************************************/ +/* + * Element type Defines + */ +#define VCS_ELEM_TYPE_ABSPOS 0 +#define VCS_ELEM_TYPE_ELECTRONCHARGE 1 +#define VCS_ELEM_TYPE_CHARGENEUTRALITY 2 +#define VCS_ELEM_TYPE_OTHERCONSTRAINT 3 +/*****************************************************************************/ +/*****************************************************************************/ +/*****************************************************************************/ +/* + * Species type Defines + */ +#define VCS_SPECIES_TYPE_MOLNUM 0 +#define VCS_SPECIES_TYPE_INTERFACIALVOLTAGE -5 + +/****************************************************************************/ +} + +#endif diff --git a/Cantera/src/equil/vcs_dvout.c b/Cantera/src/equil/vcs_dvout.c new file mode 100644 index 000000000..a8d328205 --- /dev/null +++ b/Cantera/src/equil/vcs_dvout.c @@ -0,0 +1,294 @@ +/* dvout.f -- translated by f2c (version 20031025). + You must link the resulting object file with libf2c: + on Microsoft Windows system, link with libf2c.lib; + on Linux or Unix systems, link with .../path/to/libf2c.a -lm + or, if you install libf2c.a in a standard place, with -lf2c -lm + -- in that order, at the end of the command line, as in + cc *.o -lf2c -lm + Source for libf2c is in /netlib/f2c/libf2c.zip, e.g., + + http://www.netlib.org/f2c/libf2c.zip +*/ + +#include "f2c.h" + +/* Table of constant values */ + +static integer c__2 = 2; +static integer c__1 = 1; + +/* DECK DVOUT */ +/* Subroutine */ int dvout_(integer *n, doublereal *dx, char *ifmt, integer * + idigit, ftnlen ifmt_len) +{ + /* Format strings */ + static char fmt_1000[] = "(1x,i4,\002 - \002,i4,1x,1p8d14.5)"; + static char fmt_1001[] = "(1x,i4,\002 - \002,i4,1x,1p5d22.13)"; + static char fmt_1002[] = "(1x,i4,\002 - \002,i4,1x,1p4d28.19)"; + static char fmt_1003[] = "(1x,i4,\002 - \002,i4,1x,1p3d36.27)"; + + /* System generated locals */ + integer i__1, i__2, i__3; + cilist ci__1; + + /* Builtin functions */ + integer s_wsfe(cilist *), e_wsfe(void), do_fio(integer *, char *, ftnlen); + + /* Local variables */ + static integer i__, k1, k2, lout; + extern integer i1mach_(integer *); + static integer ndigit; + + /* Fortran I/O blocks */ + static cilist io___5 = { 0, 0, 0, fmt_1000, 0 }; + static cilist io___7 = { 0, 0, 0, fmt_1001, 0 }; + static cilist io___8 = { 0, 0, 0, fmt_1002, 0 }; + static cilist io___9 = { 0, 0, 0, fmt_1003, 0 }; + static cilist io___10 = { 0, 0, 0, fmt_1000, 0 }; + static cilist io___11 = { 0, 0, 0, fmt_1001, 0 }; + static cilist io___12 = { 0, 0, 0, fmt_1002, 0 }; + static cilist io___13 = { 0, 0, 0, fmt_1003, 0 }; + + +/* ***BEGIN PROLOGUE DVOUT */ +/* ***REFER TO DSPLP */ +/* ***ROUTINES CALLED I1MACH */ +/* ***DESCRIPTION */ + +/* REVISED FEB. 27, 1981. */ + +/* DOUBLE PRECISION VECTOR OUTPUT ROUTINE. */ + +/* INPUT.. */ + +/* N,DX(*) PRINT THE DOUBLE PRECISION ARRAY DX(I),I=1,...,N, ON */ +/* OUTPUT UNIT LOUT. THE HEADING IN THE FORTRAN FORMAT */ +/* STATEMENT IFMT(*), DESCRIBED BELOW, IS PRINTED AS A FIRST */ +/* STEP. THE COMPONENTS DX(I) ARE INDEXED, ON OUTPUT, */ +/* IN A PLEASANT FORMAT. */ +/* IFMT(*) A FORTRAN FORMAT STATEMENT. THIS IS PRINTED ON OUTPUT */ +/* UNIT LOUT WITH THE VARIABLE FORMAT FORTRAN STATEMENT */ +/* WRITE(LOUT,IFMT) */ +/* IDIGIT PRINT AT LEAST IABS(IDIGIT) DECIMAL DIGITS PER NUMBER. */ +/* THE SUBPROGRAM WILL CHOOSE THAT INTEGER 4,6,10 OR 14 */ +/* WHICH WILL PRINT AT LEAST IABS(IDIGIT) NUMBER OF */ +/* PLACES. IF IDIGIT.LT.0, 72 PRINTING COLUMNS ARE UTILIZED */ +/* TO WRITE EACH LINE OF OUTPUT OF THE ARRAY DX(*). (THIS */ +/* CAN BE USED ON MOST TIME-SHARING TERMINALS). IF */ +/* IDIGIT.GE.0, 133 PRINTING COLUMNS ARE UTILIZED. (THIS CAN */ +/* BE USED ON MOST LINE PRINTERS). */ + +/* EXAMPLE.. */ + +/* PRINT AN ARRAY CALLED (COSTS OF PURCHASES) OF LENGTH 100 SHOWING */ +/* 6 DECIMAL DIGITS PER NUMBER. THE USER IS RUNNING ON A TIME-SHARING */ +/* SYSTEM WITH A 72 COLUMN OUTPUT DEVICE. */ + +/* DOUBLE PRECISION COSTS(100) */ +/* N = 100 */ +/* IDIGIT = -6 */ +/* CALL DVOUT(N,COSTS,'(''1COSTS OF PURCHASES'')',IDIGIT) */ + + + +/* AUTHORS JOHN A. WISNIEWSKI SANDIA LABS ALBUQUERQUE. */ +/* RICHARD J. HANSON SANDIA LABS ALBUQUERQUE. */ +/* DATE JULY 27,1978. */ +/* ***END PROLOGUE DVOUT */ +/* ***FIRST EXECUTABLE STATEMENT DVOUT */ + /* Parameter adjustments */ + --dx; + + /* Function Body */ + lout = i1mach_(&c__2); + ci__1.cierr = 0; + ci__1.ciunit = lout; + ci__1.cifmt = ifmt; + s_wsfe(&ci__1); + e_wsfe(); + if (*n <= 0) { + return 0; + } + ndigit = *idigit; + if (*idigit == 0) { + ndigit = 6; + } + if (*idigit >= 0) { + goto L80; + } + + ndigit = -(*idigit); + if (ndigit > 6) { + goto L20; + } + + i__1 = *n; + for (k1 = 1; k1 <= i__1; k1 += 4) { +/* Computing MIN */ + i__2 = *n, i__3 = k1 + 3; + k2 = min(i__2,i__3); + io___5.ciunit = lout; + s_wsfe(&io___5); + do_fio(&c__1, (char *)&k1, (ftnlen)sizeof(integer)); + do_fio(&c__1, (char *)&k2, (ftnlen)sizeof(integer)); + i__2 = k2; + for (i__ = k1; i__ <= i__2; ++i__) { + do_fio(&c__1, (char *)&dx[i__], (ftnlen)sizeof(doublereal)); + } + e_wsfe(); +/* L10: */ + } + return 0; + +L20: + if (ndigit > 14) { + goto L40; + } + + i__1 = *n; + for (k1 = 1; k1 <= i__1; k1 += 2) { +/* Computing MIN */ + i__2 = *n, i__3 = k1 + 1; + k2 = min(i__2,i__3); + io___7.ciunit = lout; + s_wsfe(&io___7); + do_fio(&c__1, (char *)&k1, (ftnlen)sizeof(integer)); + do_fio(&c__1, (char *)&k2, (ftnlen)sizeof(integer)); + i__2 = k2; + for (i__ = k1; i__ <= i__2; ++i__) { + do_fio(&c__1, (char *)&dx[i__], (ftnlen)sizeof(doublereal)); + } + e_wsfe(); +/* L30: */ + } + return 0; + +L40: + if (ndigit > 20) { + goto L60; + } + + i__1 = *n; + for (k1 = 1; k1 <= i__1; k1 += 2) { +/* Computing MIN */ + i__2 = *n, i__3 = k1 + 1; + k2 = min(i__2,i__3); + io___8.ciunit = lout; + s_wsfe(&io___8); + do_fio(&c__1, (char *)&k1, (ftnlen)sizeof(integer)); + do_fio(&c__1, (char *)&k2, (ftnlen)sizeof(integer)); + i__2 = k2; + for (i__ = k1; i__ <= i__2; ++i__) { + do_fio(&c__1, (char *)&dx[i__], (ftnlen)sizeof(doublereal)); + } + e_wsfe(); +/* L50: */ + } + return 0; + +L60: + i__1 = *n; + for (k1 = 1; k1 <= i__1; ++k1) { + k2 = k1; + io___9.ciunit = lout; + s_wsfe(&io___9); + do_fio(&c__1, (char *)&k1, (ftnlen)sizeof(integer)); + do_fio(&c__1, (char *)&k2, (ftnlen)sizeof(integer)); + i__2 = k2; + for (i__ = k1; i__ <= i__2; ++i__) { + do_fio(&c__1, (char *)&dx[i__], (ftnlen)sizeof(doublereal)); + } + e_wsfe(); +/* L70: */ + } + return 0; + +L80: + if (ndigit > 6) { + goto L100; + } + + i__1 = *n; + for (k1 = 1; k1 <= i__1; k1 += 8) { +/* Computing MIN */ + i__2 = *n, i__3 = k1 + 7; + k2 = min(i__2,i__3); + io___10.ciunit = lout; + s_wsfe(&io___10); + do_fio(&c__1, (char *)&k1, (ftnlen)sizeof(integer)); + do_fio(&c__1, (char *)&k2, (ftnlen)sizeof(integer)); + i__2 = k2; + for (i__ = k1; i__ <= i__2; ++i__) { + do_fio(&c__1, (char *)&dx[i__], (ftnlen)sizeof(doublereal)); + } + e_wsfe(); +/* L90: */ + } + return 0; + +L100: + if (ndigit > 14) { + goto L120; + } + + i__1 = *n; + for (k1 = 1; k1 <= i__1; k1 += 5) { +/* Computing MIN */ + i__2 = *n, i__3 = k1 + 4; + k2 = min(i__2,i__3); + io___11.ciunit = lout; + s_wsfe(&io___11); + do_fio(&c__1, (char *)&k1, (ftnlen)sizeof(integer)); + do_fio(&c__1, (char *)&k2, (ftnlen)sizeof(integer)); + i__2 = k2; + for (i__ = k1; i__ <= i__2; ++i__) { + do_fio(&c__1, (char *)&dx[i__], (ftnlen)sizeof(doublereal)); + } + e_wsfe(); +/* L110: */ + } + return 0; + +L120: + if (ndigit > 20) { + goto L140; + } + + i__1 = *n; + for (k1 = 1; k1 <= i__1; k1 += 4) { +/* Computing MIN */ + i__2 = *n, i__3 = k1 + 3; + k2 = min(i__2,i__3); + io___12.ciunit = lout; + s_wsfe(&io___12); + do_fio(&c__1, (char *)&k1, (ftnlen)sizeof(integer)); + do_fio(&c__1, (char *)&k2, (ftnlen)sizeof(integer)); + i__2 = k2; + for (i__ = k1; i__ <= i__2; ++i__) { + do_fio(&c__1, (char *)&dx[i__], (ftnlen)sizeof(doublereal)); + } + e_wsfe(); +/* L130: */ + } + return 0; + +L140: + i__1 = *n; + for (k1 = 1; k1 <= i__1; k1 += 3) { +/* Computing MIN */ + i__2 = *n, i__3 = k1 + 2; + k2 = min(i__2,i__3); + io___13.ciunit = lout; + s_wsfe(&io___13); + do_fio(&c__1, (char *)&k1, (ftnlen)sizeof(integer)); + do_fio(&c__1, (char *)&k2, (ftnlen)sizeof(integer)); + i__2 = k2; + for (i__ = k1; i__ <= i__2; ++i__) { + do_fio(&c__1, (char *)&dx[i__], (ftnlen)sizeof(doublereal)); + } + e_wsfe(); +/* L150: */ + } + return 0; +} /* dvout_ */ + diff --git a/Cantera/src/equil/vcs_elem.cpp b/Cantera/src/equil/vcs_elem.cpp new file mode 100644 index 000000000..e3d6d6168 --- /dev/null +++ b/Cantera/src/equil/vcs_elem.cpp @@ -0,0 +1,602 @@ +/** + * @file vcs_elem.cpp + */ +/* + * $Id$ + */ + +#include "vcs_solve.h" +#include "vcs_internal.h" +#include "math.h" + +namespace VCSnonideal { + +/*****************************************************************************/ +/*****************************************************************************/ +/*****************************************************************************/ + +void VCS_SOLVE::vcs_elab(void) + + /************************************************************************* + * + * vcs_elab: + * + * Computes the elemental abundances vector, ga[], and stores it + * back into the global structure + *************************************************************************/ +{ + for (int j = 0; j < m_numElemConstraints; ++j) { + ga[j] = 0.0; + for (int i = 0; i < m_numSpeciesTot; ++i) { + if (SpeciesUnknownType[i] != VCS_SPECIES_TYPE_INTERFACIALVOLTAGE) { + ga[j] += FormulaMatrix[j][i] * soln[i]; + } + } + } +} +/*****************************************************************************/ +/*****************************************************************************/ +/*****************************************************************************/ +/* + * + * vcs_elabcheck: + * + * This function checks to see if the element abundances are in + * compliance. If they are, then TRUE is returned. If not, + * FALSE is returned. Note the number of constraints checked is + * usually equal to the number of components in the problem. This + * routine can check satisfaction of all of the constraints in the + * problem, which is equal to ne. However, the solver can't fix + * breakage of constraints above nc, because that nc is the + * range space by definition. Satisfaction of extra constraints would + * have had to occur in the problem specification. + * + * The constraints should be broken up into 2 sections. If + * a constraint involves a formula matrix with positive and + * negative signs, and eaSet = 0.0, then you can't expect that the + * sum will be zero. There may be roundoff that inhibits this. + * However, if the formula matrix is all of one sign, then + * this requires that all species with nonzero entries in the + * formula matrix be identically zero. We put this into + * the logic below. + * + * Input + * ------- + * ibound = 1 : Checks constraints up to the number of elements + * 0 : Checks constraints up to the number of components. + * + */ +int VCS_SOLVE::vcs_elabcheck(int ibound) { + int i; + int top = m_numComponents; + double eval, scale; + int numNonZero; + bool multisign = false; + if (ibound) { + top = m_numElemConstraints; + } + /* + * Require 12 digits of accuracy on non-zero constraints. + */ + for (i = 0; i < top; ++i) { + if (fabs(ga[i] - gai[i]) > (fabs(gai[i]) * 1.0e-12)) { + /* + * This logic is for charge neutrality condition + */ + if (m_elType[i] == VCS_ELEM_TYPE_CHARGENEUTRALITY) { + AssertThrowVCS(gai[i] == 0.0, "vcs_elabcheck"); + } + if (gai[i] == 0.0 || (m_elType[i] == VCS_ELEM_TYPE_ELECTRONCHARGE)) { + scale = VCS_DELETE_MINORSPECIES_CUTOFF; + /* + * Find out if the constraint is a multisign constraint. + * If it is, then we have to worry about roundoff error + * in the addition of terms. We are limited to 13 + * digits of finite arithmetic accuracy. + */ + numNonZero = 0; + multisign = false; + for (int kspec = 0; kspec < m_numSpeciesTot; kspec++) { + eval = FormulaMatrix[i][kspec]; + if (eval < 0.0) { + multisign = true; + } + if (eval != 0.0) { + scale = MAX(scale, fabs(eval * soln[kspec])); + numNonZero++; + } + } + if (multisign) { + if (fabs(ga[i] - gai[i]) > 1e-11 * scale) { + return FALSE; + } + } else { + if (fabs(ga[i] - gai[i]) > VCS_DELETE_MINORSPECIES_CUTOFF) { + return FALSE; + } + } + } else { + /* + * For normal element balances, we require absolute compliance + * even for rediculously small numbers. + */ + if (m_elType[i] == VCS_ELEM_TYPE_ABSPOS) { + return FALSE; + } else { + return FALSE; + } + } + } + } + return TRUE; +} /* vcs_elabcheck() *********************************************************/ + +/*****************************************************************************/ +/*****************************************************************************/ +/*****************************************************************************/ + +void VCS_SOLVE::vcs_elabPhase(int iphase, double * const elemAbundPhase) + + /************************************************************************* + * + * vcs_elabPhase: + * + * Computes the elemental abundances vector for a single phase, + * elemAbundPhase[], and returns it through the argument list. + * The mole numbers of species are taken from the current value + * in soln[]. + *************************************************************************/ +{ + int i, j; + for (j = 0; j < m_numElemConstraints; ++j) { + elemAbundPhase[j] = 0.0; + for (i = 0; i < m_numSpeciesTot; ++i) { + if (SpeciesUnknownType[i] != VCS_SPECIES_TYPE_INTERFACIALVOLTAGE) { + if (PhaseID[i] == iphase) { + elemAbundPhase[j] += FormulaMatrix[j][i] * soln[i]; + } + } + } + } +} + +/*****************************************************************************/ +/*****************************************************************************/ +/*****************************************************************************/ + +int VCS_SOLVE::vcs_elcorr(double aa[], double x[]) + + /************************************************************************** + * + * vcs_elcorr: + * + * This subroutine corrects for element abundances. At the end of the + * surbroutine, the total moles in all phases are recalculated again, + * because we have changed the number of moles in this routine. + * + * Input + * -> temporary work vectors: + * aa[ne*ne] + * x[ne] + * + * Return Values: + * 0 = Nothing of significance happened, + * Element abundances were and still are good. + * 1 = The solution changed significantly; + * The element abundances are now good. + * 2 = The solution changed significantly, + * The element abundances are still bad. + * 3 = The solution changed significantly, + * The element abundances are still bad and a component + * species got zeroed out. + * + * Internal data to be worked on:: + * + * ga Current element abundances + * gai Required elemental abundances + * soln Current mole number of species. + * FormulaMatrix[][] Formular matrix of the species + * ne Number of elements + * nc Number of components. + * + * NOTES: + * This routine is turning out to be very problematic. There are + * lots of special cases and problems with zeroing out species. + * + * Still need to check out when we do loops over nc vs. ne. + * + *************************************************************************/ +{ + int i, j, retn = 0, kspec, goodSpec, its; + double xx, par, saveDir, dir; + +#ifdef DEBUG + double l2before = 0.0, l2after = 0.0; + std::vector ga_save(m_numElemConstraints, 0.0); + vcs_dcopy(VCS_DATA_PTR(ga_save), VCS_DATA_PTR(ga), m_numElemConstraints); + if (vcs_debug_print_lvl >= 2) { + plogf(" --- vcsc_elcorr: Element abundances correction routine"); + if (m_numElemConstraints != m_numComponents) { + plogf(" (m_numComponents != m_numElemConstraints)"); + } + plogf("\n"); + } + + for (i = 0; i < m_numElemConstraints; ++i) { + x[i] = ga[i] - gai[i]; + } + l2before = 0.0; + for (i = 0; i < m_numElemConstraints; ++i) { + l2before += x[i] * x[i]; + } + l2before = sqrt(l2before/m_numElemConstraints); +#endif + + /* + * Special section to take out single species, single component, + * moles. These are species which have non-zero entries in the + * formula matrix, and no other species have zero values either. + * + */ + int numNonZero = 0; + bool changed = false; + bool multisign = false; + for (i = 0; i < m_numElemConstraints; ++i) { + numNonZero = 0; + multisign = false; + for (kspec = 0; kspec < m_numSpeciesTot; kspec++) { + if (SpeciesUnknownType[kspec] != VCS_SPECIES_TYPE_INTERFACIALVOLTAGE) { + double eval = FormulaMatrix[i][kspec]; + if (eval < 0.0) { + multisign = true; + } + if (eval != 0.0) { + numNonZero++; + } + } + } + if (!multisign) { + if (numNonZero < 2) { + for (kspec = 0; kspec < m_numSpeciesTot; kspec++) { + if (SpeciesUnknownType[kspec] != VCS_SPECIES_TYPE_INTERFACIALVOLTAGE) { + double eval = FormulaMatrix[i][kspec]; + if (eval > 0.0) { + soln[kspec] = gai[i] / eval; + changed = true; + } + } + } + } else { + int numCompNonZero = 0; + int compID = -1; + for (kspec = 0; kspec < m_numComponents; kspec++) { + if (SpeciesUnknownType[kspec] != VCS_SPECIES_TYPE_INTERFACIALVOLTAGE) { + double eval = FormulaMatrix[i][kspec]; + if (eval > 0.0) { + compID = kspec; + numCompNonZero++; + } + } + } + if (numCompNonZero == 1) { + double diff = gai[i]; + for (kspec = m_numComponents; kspec < m_numSpeciesTot; kspec++) { + if (SpeciesUnknownType[kspec] != VCS_SPECIES_TYPE_INTERFACIALVOLTAGE) { + double eval = FormulaMatrix[i][kspec]; + diff -= eval * soln[kspec]; + } + soln[compID] = MAX(0.0,diff/FormulaMatrix[i][compID]); + changed = true; + } + } + } + } + } + if (changed) { + vcs_elab(); + } + + /* + * Section to check for maximum bounds errors on all species + * due to elements. + * This may only be tried on element types which are VCS_ELEM_TYPE_ABSPOS. + * This is because no other species may have a negative number of these. + * + * Note, also we can do this over ne, the number of elements, not just + * the number of components. + */ + changed = false; + for (i = 0; i < m_numElemConstraints; ++i) { + int elType = m_elType[i]; + if (elType == VCS_ELEM_TYPE_ABSPOS) { + for (kspec = 0; kspec < m_numSpeciesTot; kspec++) { + if (SpeciesUnknownType[kspec] != VCS_SPECIES_TYPE_INTERFACIALVOLTAGE) { + double atomComp = FormulaMatrix[i][kspec]; + if (atomComp > 0.0) { + double maxPermissible = gai[i] / atomComp; + if (soln[kspec] > maxPermissible) { + +#ifdef DEBUG + if (vcs_debug_print_lvl >= 3) { + plogf(" --- vcs_elcorr: Reduced species %s from %g to %g due to %s max bounds constraint\n", + SpName[kspec].c_str(), soln[kspec], maxPermissible, ElName[i].c_str()); + } +#endif + soln[kspec] = maxPermissible; + changed = true; + if (soln[kspec] < VCS_DELETE_MINORSPECIES_CUTOFF) { + soln[kspec] = 0.0; + if (SSPhase[kspec]) { + spStatus[kspec] = VCS_SPECIES_ZEROEDSS; + } else { + spStatus[kspec] = VCS_SPECIES_ZEROEDMS; + } +#ifdef DEBUG + if (vcs_debug_print_lvl >= 2) { + plogf(" --- vcs_elcorr: Zeroed species %s and changed status to %d due to max bounds constraint\n", + SpName[kspec].c_str(), spStatus[kspec]); + } +#endif + } + } + } + } + } + } + } + + // Recalculate the element abundances if something has changed. + if (changed) { + vcs_elab(); + } + + /* + * Ok, do the general case. Linear algebra problem is + * of length nc, not ne, as there may be degenerate rows when + * nc .ne. ne. + */ + for (i = 0; i < m_numComponents; ++i) { + x[i] = ga[i] - gai[i]; + if (fabs(x[i]) > 1.0E-13) retn = 1; + for (j = 0; j < m_numComponents; ++j) { + aa[j + i*m_numElemConstraints] = FormulaMatrix[j][i]; + } + } + i = vcsUtil_mlequ(aa, m_numElemConstraints, m_numComponents, x, 1); + if (i == 1) { + plogf("vcs_elcorr ERROR: mlequ returned error condition\n"); + return VCS_FAILED_CONVERGENCE; + } + /* + * Now apply the new direction without creating negative species. + */ + par = 0.5; + for (i = 0; i < m_numComponents; ++i) { + if (soln[i] > 0.0) { + xx = -x[i] / soln[i]; + if (par < xx) par = xx; + } + } + if (par > 100.0) { + par = 100.0; + } + par = 1.0 / par; + if (par < 1.0 && par > 0.0) { + retn = 2; + par *= 0.9999; + for (i = 0; i < m_numComponents; ++i) { + double tmp = soln[i] + par * x[i]; + if (tmp > 0.0) { + soln[i] = tmp; + } else { + if (SSPhase[i]) { + soln[i] = 0.0; + } else { + soln[i] = soln[i] * 0.0001; + } + } + } + } else { + for (i = 0; i < m_numComponents; ++i) { + double tmp = soln[i] + x[i]; + if (tmp > 0.0) { + soln[i] = tmp; + } else { + if (SSPhase[i]) { + soln[i] = 0.0; + } else { + soln[i] = soln[i] * 0.0001; + } + } + } + } + + /* + * We have changed the element abundances. Calculate them again + */ + vcs_elab(); + /* + * We have changed the total moles in each phase. Calculate them again + */ + vcs_tmoles(); + + /* + * Try some ad hoc procedures for fixing the problem + */ + if (retn >= 2) { + /* + * First find a species whose adjustment is a win-win + * situation. + */ + for (kspec = 0; kspec < m_numSpeciesTot; kspec++) { + if (SpeciesUnknownType[kspec] == VCS_SPECIES_TYPE_INTERFACIALVOLTAGE) { + continue; + } + saveDir = 0.0; + goodSpec = TRUE; + for (i = 0; i < m_numComponents; ++i) { + dir = FormulaMatrix[i][kspec] * (gai[i] - ga[i]); + if (fabs(dir) > 1.0E-10) { + if (dir > 0.0) { + if (saveDir < 0.0) { + goodSpec = FALSE; + break; + } + } else { + if (saveDir > 0.0) { + goodSpec = FALSE; + break; + } + } + saveDir = dir; + } else { + if (FormulaMatrix[i][kspec] != 0.) { + goodSpec = FALSE; + break; + } + } + } + if (goodSpec) { + its = 0; + xx = 0.0; + for (i = 0; i < m_numComponents; ++i) { + if (FormulaMatrix[i][kspec] != 0.0) { + xx += (gai[i] - ga[i]) / FormulaMatrix[i][kspec]; + its++; + } + } + if (its > 0) xx /= its; + soln[kspec] += xx; + soln[kspec] = MAX(soln[kspec], 1.0E-10); + /* + * If we are dealing with a deleted species, then + * we need to reinsert it into the active list. + */ + if (kspec >= m_numSpeciesRdc) { + vcs_reinsert_deleted(kspec); + soln[m_numSpeciesRdc - 1] = xx; + vcs_elab(); + goto L_CLEANUP; + } + vcs_elab(); + } + } + } + if (vcs_elabcheck(0)) { + retn = 1; + goto L_CLEANUP; + } + + for (i = 0; i < m_numElemConstraints; ++i) { + if (m_elType[i] == VCS_ELEM_TYPE_CHARGENEUTRALITY || + (m_elType[i] == VCS_ELEM_TYPE_ABSPOS && gai[i] == 0.0)) { + for (kspec = 0; kspec < m_numSpeciesRdc; kspec++) { + if (ga[i] > 0.0) { + if (FormulaMatrix[i][kspec] < 0.0) { + soln[kspec] -= ga[i] / FormulaMatrix[i][kspec] ; + if (soln[kspec] < 0.0) { + soln[kspec] = 0.0; + } + vcs_elab(); + break; + } + } + if (ga[i] < 0.0) { + if (FormulaMatrix[i][kspec] > 0.0) { + soln[kspec] -= ga[i] / FormulaMatrix[i][kspec]; + if (soln[kspec] < 0.0) { + soln[kspec] = 0.0; + } + vcs_elab(); + break; + } + } + } + } + } + if (vcs_elabcheck(1)) { + retn = 1; + goto L_CLEANUP; + } + + /* + * For electron charges element types, we try positive deltas + * in the species concentrations to match the desired + * electron charge exactly. + */ + for (i = 0; i < m_numElemConstraints; ++i) { + double dev = gai[i] - ga[i]; + if (m_elType[i] == VCS_ELEM_TYPE_ELECTRONCHARGE && (fabs(dev) > 1.0E-300)) { + bool useZeroed = true; + for (kspec = 0; kspec < m_numSpeciesRdc; kspec++) { + if (dev < 0.0) { + if (FormulaMatrix[i][kspec] < 0.0) { + if (soln[kspec] > 0.0) { + useZeroed = false; + } + } + } else { + if (FormulaMatrix[i][kspec] > 0.0) { + if (soln[kspec] > 0.0) { + useZeroed = false; + } + } + } + } + for (kspec = 0; kspec < m_numSpeciesRdc; kspec++) { + if (soln[kspec] > 0.0 || useZeroed) { + if (dev < 0.0) { + if (FormulaMatrix[i][kspec] < 0.0) { + double delta = dev / FormulaMatrix[i][kspec] ; + soln[kspec] += delta; + if (soln[kspec] < 0.0) { + soln[kspec] = 0.0; + } + vcs_elab(); + break; + } + } + if (dev > 0.0) { + if (FormulaMatrix[i][kspec] > 0.0) { + double delta = dev / FormulaMatrix[i][kspec] ; + soln[kspec] += delta; + if (soln[kspec] < 0.0) { + soln[kspec] = 0.0; + } + vcs_elab(); + break; + } + } + } + } + } + } + if (vcs_elabcheck(1)) { + retn = 1; + goto L_CLEANUP; + } + + L_CLEANUP: ; + vcs_tmoles(); +#ifdef DEBUG + l2after = 0.0; + for (i = 0; i < m_numElemConstraints; ++i) { + l2after += SQUARE(ga[i] - gai[i]); + } + l2after = sqrt(l2after/m_numElemConstraints); + if (vcs_debug_print_lvl >= 2) { + plogf(" --- Elem_Abund: Correct Initial " + " Final\n"); + for (i = 0; i < m_numElemConstraints; ++i) { + plogf(" --- "); plogf("%-2.2s", ElName[i].c_str()); + plogf(" %20.12E %20.12E %20.12E\n", gai[i], ga_save[i], ga[i]); + } + plogf(" --- Diff_Norm: %20.12E %20.12E\n", + l2before, l2after); + } +#endif + return retn; +} /* vcs_elcorr() ************************************************************/ + +} + diff --git a/Cantera/src/equil/vcs_elem_rearrange.cpp b/Cantera/src/equil/vcs_elem_rearrange.cpp new file mode 100644 index 000000000..c2adbbca2 --- /dev/null +++ b/Cantera/src/equil/vcs_elem_rearrange.cpp @@ -0,0 +1,250 @@ +/* $Author$ + * $Date$ + * $Revision$ + */ + +/* + * Copywrite (2005) Sandia Corporation. Under the terms of + * Contract DE-AC04-94AL85000 with Sandia Corporation, the + * U.S. Government retains certain rights in this software. + */ + +#include +#include +#include + +#include "vcs_solve.h" +#include "vcs_internal.h" +#include "vcs_VolPhase.h" + +namespace VCSnonideal { + + /* + * vcs_elem_rearrange: + * + * This subroutine handles the rearrangement of the constraint + * equations represented by the Formula Matrix. Rearrangement is only + * necessary when the number of components is less than the number of + * elements. For this case, some constraints can never be satisfied + * exactly, because the range space represented by the Formula + * Matrix of the components can't span the extra space. These + * constraints, which are out of the range space of the component + * Formula matrix entries, are migrated to the back of the Formula + * matrix. + * + * A prototypical example is an extra element column in + * FormulaMatrix[], + * which is identically zero. For example, let's say that argon is + * has an element column in FormulaMatrix[], but no species in the + * mechanism + * actually contains argon. Then, nc < ne. Also, without perturbation + * of FormulaMatrix[] vcs_basopt[] would produce a zero pivot + * because the matrix + * would be singular (unless the argon element column was already the + * last column of FormulaMatrix[]. + * This routine borrows heavily from vcs_basopt's algorithm. It + * finds nc constraints which span the range space of the Component + * Formula matrix, and assigns them as the first nc components in the + * formular matrix. This guarrantees that vcs_basopt[] has a + * nonsingular matrix to invert. + * + * Other Variables + * aw[i] = Mole fraction work space (ne in length) + * sa[j] = Gramm-Schmidt orthog work space (ne in length) + * ss[j] = Gramm-Schmidt orthog work space (ne in length) + * sm[i+j*ne] = QR matrix work space (ne*ne in length) + * + */ +int VCS_SOLVE::vcs_elem_rearrange(double *aw, double *sa, double *sm, + double *ss) { + int j, k, l, i, jl, ml, jr, lindep, ielem; + int ncomponents = m_numComponents; + double test = -1.0E10; +#ifdef DEBUG + if (vcs_debug_print_lvl >= 2) { + plogf(" "); for(i=0; i<77; i++) plogf("-"); plogf("\n"); + plogf(" --- Subroutine elem_rearrange() called to "); + plogf("check stoich. coefficent matrix\n"); + plogf(" --- and to rearrange the element ordering once\n"); + } +#endif + + /* + * Use a temporary work array for the element numbers + * Also make sure the value of test is unique. + */ + lindep = FALSE; + do { + lindep = FALSE; + for (i = 0; i < m_numElemConstraints; ++i) { + test -= 1.0; + aw[i] = gai[i]; + if (test == aw[i]) lindep = TRUE; + } + } while (lindep); + + /* + * Top of a loop of some sort based on the index JR. JR is the + * current number independent elements found. + */ + jr = -1; + 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. + */ + k = m_numElemConstraints; + for (ielem = jr; ielem < m_numElemConstraints; ielem++) { + if (ElActive[ielem]) { + if (aw[ielem] != test) { + k = ielem; + break; + } + } + } + if (k == m_numElemConstraints) { + plogf("Shouldn't be here\n"); + exit(-1); + } + + /* + * Assign a large negative number to the element that we have + * just found, in order to take it out of further consideration. + */ + aw[k] = test; + + /* *********************************************************** */ + /* **** CHECK LINEAR INDEPENDENCE OF CURRENT FORMULA MATRIX */ + /* **** LINE WITH PREVIOUS LINES OF THE FORMULA MATRIX ****** */ + /* *********************************************************** */ + /* + * Modified Gram-Schmidt Method, p. 202 Dalquist + * QR factorization of a matrix without row pivoting. + */ + jl = jr; + /* + * Fill in the row for the current element, k, under consideration + * The row will contain the Formula matrix value for that element + * from the current component. + */ + for (j = 0; j < ncomponents; ++j) { + sm[j + jr*ncomponents] = FormulaMatrix[k][j]; + } + if (jl > 0) { + /* + * Compute the coefficients of JA column of the + * the upper triangular R matrix, SS(J) = R_J_JR + * (this is slightly different than Dalquist) + * R_JA_JA = 1 + */ + for (j = 0; j < jl; ++j) { + ss[j] = 0.0; + for (i = 0; i < ncomponents; ++i) { + ss[j] += sm[i + jr*ncomponents] * sm[i + j*ncomponents]; + } + ss[j] /= sa[j]; + } + /* + * Now make the new column, (*,JR), orthogonal to the + * previous columns + */ + for (j = 0; j < jl; ++j) { + for (l = 0; l < ncomponents; ++l) { + sm[l + jr*ncomponents] -= ss[j] * sm[l + j*ncomponents]; + } + } + } + + /* + * Find the new length of the new column in Q. + * It will be used in the denominator in future row calcs. + */ + sa[jr] = 0.0; + for (ml = 0; ml < ncomponents; ++ml) { + sa[jr] += SQUARE(sm[ml + jr*ncomponents]); + } + /* **************************************************** */ + /* **** IF NORM OF NEW ROW .LT. 1E-6 REJECT ********** */ + /* **************************************************** */ + if (sa[jr] < 1.0e-6) lindep = TRUE; + else lindep = FALSE; + } while(lindep); + /* ****************************************** */ + /* **** REARRANGE THE DATA ****************** */ + /* ****************************************** */ + if (jr != k) { +#ifdef DEBUG + if (vcs_debug_print_lvl >= 2) { + plogf(" --- "); plogf("%-2.2s", (ElName[k]).c_str()); + plogf("(%9.2g) replaces ", gai[k]); + plogf("%-2.2s", (ElName[jr]).c_str()); + plogf("(%9.2g) as element %3d\n", gai[jr], jr); + } +#endif + vcs_switch_elem_pos(jr, k); + vcsUtil_dsw(aw, jr, k); + } + + /* + * If we haven't found enough components, go back + * and find some more. (nc -1 is used below, because + * jr is counted from 0, via the C convention. + */ + } while (jr < (ncomponents-1)); + return VCS_SUCCESS; +} /* vcs_elem_rearrange() ****************************************************/ + +// Swaps the indecises for all of the global data for two elements, ipos +// and jpos. +/* + * This function knows all of the element information with VCS_SOLVE, and + * can therefore switch element positions + * + * @param ipos first global element index + * @param jpos second global element index + */ +void VCS_SOLVE::vcs_switch_elem_pos(int ipos, int jpos) { + if (ipos == jpos) return; + int j; + double dtmp; + vcs_VolPhase *volPhase; +#ifdef DEBUG + if (ipos < 0 || ipos > (m_numElemConstraints - 1) || + jpos < 0 || jpos > (m_numElemConstraints - 1) ) { + plogf("vcs_switch_elem_pos: ifunc = 0: inappropriate args: %d %d\n", + ipos, jpos); + } +#endif + /* + * Change the element Global Index list in each phase object + * to reflect the switch in the element positions. + */ + for (int iph = 0; iph < NPhase; iph++) { + volPhase = VPhaseList[iph]; + for (int e = 0; e < volPhase->nElemConstraints; e++) { + if (volPhase->ElGlobalIndex[e] == ipos) { + volPhase->ElGlobalIndex[e] = jpos; + } + if (volPhase->ElGlobalIndex[e] == jpos) { + volPhase->ElGlobalIndex[e] =ipos; + } + } + } + vcsUtil_dsw(VCS_DATA_PTR(gai), ipos, jpos); + vcsUtil_dsw(VCS_DATA_PTR(ga), ipos, jpos); + vcsUtil_isw(VCS_DATA_PTR(IndEl), ipos, jpos); + vcsUtil_isw(VCS_DATA_PTR(m_elType), ipos, jpos); + vcsUtil_isw(VCS_DATA_PTR(ElActive), ipos, jpos); + for (j = 0; j < m_numSpeciesTot; ++j) { + SWAP(FormulaMatrix[ipos][j], FormulaMatrix[jpos][j], dtmp); + } + vcsUtil_stsw(ElName, ipos, jpos); +} /* vcs_switch_elem_pos() ***************************************************/ +} + diff --git a/Cantera/src/equil/vcs_funcVtot.cpp b/Cantera/src/equil/vcs_funcVtot.cpp new file mode 100644 index 000000000..cdebb5756 --- /dev/null +++ b/Cantera/src/equil/vcs_funcVtot.cpp @@ -0,0 +1,64 @@ +/* ======================================================================= */ +/* -------------------------------------------------- */ +/* | RCS Head Information on zuzax.pchem.sandia.gov | */ +/* -------------------------------------------------- */ +/* $RCSfile$ */ +/* $Author$ */ +/* $Date$ */ +/* $Revision$ */ +/* ======================================================================= */ +#include +#include +#include + +#include "vcs_solve.h" +#include "vcs_internal.h" + +#define TOL_CONV 1.0E-5 + +namespace VCSnonideal { + +/************************************************************************** + * + * vcs_funcVtot: + * + * This is the rootfinder function call for the function vcs_TV(). + * + */ +double vcs_funcVtot(double xval, double Vtarget, int varID, void *fptrPassthrough, int *err) +{ + VCS_SOLVE *vptr = (VCS_SOLVE *) fptrPassthrough; + static int first_time = TRUE; + int retn; + double vol; + if (varID == 0) { + vptr->T = xval; + } else if (varID == 1) { + vptr->Pres = xval; + } +#ifdef DEBUG + retn = vptr->vcs_TP(1, 1, 10000, vptr->T, vptr->Pres); +#else + retn = vptr->vcs_TP(0, 0, 10000, vptr->T, vptr->Pres); +#endif + if (retn != VCS_SUCCESS) { + plogf("vcs_funcVtot ERROR: vcs_TP returned error condition, %d\n", + retn); + *err = retn; + } + vol = vptr->vcs_VolTotal(vptr->T, vptr->Pres, VCS_DATA_PTR(vptr->soln), + VCS_DATA_PTR(vptr->VolPM)); +#ifdef DEBUG + vptr->vcs_report(retn); +#endif + + + if (first_time) { + first_time = FALSE; + vptr->iest = FALSE; + } + return (vol - Vtarget); +} +/*****************************************************************************/ +} + diff --git a/Cantera/src/equil/vcs_inest.cpp b/Cantera/src/equil/vcs_inest.cpp new file mode 100644 index 000000000..b1857811c --- /dev/null +++ b/Cantera/src/equil/vcs_inest.cpp @@ -0,0 +1,509 @@ +/** + * @file vcs_inest.cpp + * Methods for obtaining a good initial guess + */ +/* $Author$ + * $Date$ + * $Revision$ + */ + +/* + * Copywrite (2005) Sandia Corporation. Under the terms of + * Contract DE-AC04-94AL85000 with Sandia Corporation, the + * U.S. Government retains certain rights in this software. + */ + +#include +#include +#include + +#include "vcs_solve.h" +#include "vcs_internal.h" +#include "vcs_VolPhase.h" + +namespace VCSnonideal { + + static char pprefix[20] = " --- vcs_inest: "; + +#ifdef ALTLINPROG +#else + extern int linprogmax(double *, double *, double *, double *, int, int, int); +#endif + /*****************************************************************************/ + /*****************************************************************************/ + /*****************************************************************************/ + + void VCS_SOLVE::inest(double *aw, double *sa, double *sm, + double *ss, double test) + + /************************************************************************** + * + * inest: + * + * Estimates equilibrium compositions. + * Algorithm covered in a section of Smith and Missen's Book. + * + * Linear programming module is based on using dbolm. + ***************************************************************************/ + { + int conv, j, k, lt, ikl, kspec, iph, irxn, jj; + double s, s1, xl, par; + int finished; + int nspecies = m_numSpeciesTot; + int nrxn = m_numRxnTot; + vcs_VolPhase *Vphase = 0; + + double *molNum = VCS_DATA_PTR(soln); + double TMolesMultiphase; + double *xtphMax = VCS_DATA_PTR(TmpPhase); + double *xtphMin = VCS_DATA_PTR(TmpPhase2); + + ikl = 0; + lt = 0; + + + /* + * CALL ROUTINE TO SOLVE MAX(CC*molNum) SUCH THAT AX*molNum = BB + * AND molNum(I) .GE. 0.0 + * + * Note, both of these programs do this. + */ +#ifdef ALTLINPROG + vcs_setMolesLinProg(); +#else + + std::vector ax(ne*nspecies, 0.0); + std::vector bb(ne, 0.0); + std::vector cc(nspecies, 0.0); + + int neActive = 0; + jj = 0; + for (j = 0; j < ne; j++) { + if (ElActive[j]) { + neActive++; + bb[jj] = gai[j]; + jj++; + } + } + for (kspec = 0; kspec < nspecies; ++kspec) { + cc[kspec] = -ff[kspec]; + jj = 0; + for (j = 0; j < ne; ++j) { + if (ElActive[j]) { + ax[jj + kspec * neActive] = FormulaMatrix[j][kspec]; + jj++; + } + } + } + linprogmax(molNum, VCS_DATA_PTR(cc), VCS_DATA_PTR(ax), + VCS_DATA_PTR(bb), neActive, nspecies, neActive); +#endif + +#ifdef DEBUG + if (vcs_debug_print_lvl >= 2) { + plogf("%s Mole Numbers returned from linear programming (vcs_inest initial guess):\n", + pprefix); + plogf("%s SPECIES MOLE_NUMBER -SS_ChemPotential\n", pprefix); + for (kspec = 0; kspec < nspecies; ++kspec) { + plogf("%s ", pprefix); plogf("%-12.12s", SpName[kspec].c_str()); + plogf(" %15.5g %12.3g\n", molNum[kspec], -ff[kspec]); + } + plogf("%s Element Abundance Agreement returned from linear " + "programming (vcs_inest initial guess):\n", + pprefix); + plogf("%s Element Goal Actual\n", pprefix); + jj = 0; + for (j = 0; j < m_numElemConstraints; j++) { + if (ElActive[j]) { + double tmp = 0.0; + for (kspec = 0; kspec < nspecies; ++kspec) { + tmp += FormulaMatrix[j][kspec] * molNum[kspec]; + } + plogf("%s ", pprefix); plogf(" %-9.9s", (ElName[j]).c_str()); + plogf(" %12.3g %12.3g\n", gai[j], tmp); + jj++; + } + } + } +#endif + + + /* + * Make sure all species have positive definite mole numbers + * Set voltages to zero for now, until we figure out what to do + */ + vcs_dzero(VCS_DATA_PTR(ds), nspecies); + for (kspec = 0; kspec < nspecies; ++kspec) { + iph = PhaseID[kspec]; + Vphase = VPhaseList[iph]; + if (SpeciesUnknownType[kspec] != VCS_SPECIES_TYPE_INTERFACIALVOLTAGE) { + if (molNum[kspec] <= 0.0) { + /* + * HKM Should eventually include logic here for non SS phases + */ + if (!SSPhase[kspec]) { + molNum[kspec] = 1.0e-30; + } + } + } else { + molNum[kspec] = 0.0; + } + if (molNum[kspec] > 0.0) { + if (Vphase->Existence == 0) { + Vphase->Existence = 1; + } + } else if (SSPhase[kspec]) { + Vphase->Existence = 0; + } + } + + /* + * Now find the optimized basis that spans the stoichiometric + * coefficient matrix + */ + (void) vcs_basopt(FALSE, aw, sa, sm, ss, test, &conv); + + /* ***************************************************************** */ + /* **** CALCULATE TOTAL GASEOUS AND LIQUID MOLES, ****************** */ + /* **** CHEMICAL POTENTIALS OF BASIS ****************** */ + /* ***************************************************************** */ + /* + * Calculate TMoles and TPhMoles[] + */ + vcs_tmoles(); + /* + * TPhMoles1[] will consist of just the component moles + */ + for (iph = 0; iph < NPhase; iph++) { + TPhMoles1[iph] = TPhInertMoles[iph] + 1.0E-20; + } + for (kspec = 0; kspec < m_numComponents; ++kspec) { + if (SpeciesUnknownType[kspec] == VCS_SPECIES_TYPE_MOLNUM) { + TPhMoles1[PhaseID[kspec]] += molNum[kspec]; + } + } + TMolesMultiphase = 0.0; + for (iph = 0; iph < NPhase; iph++) { + if (! VPhaseList[iph]->SingleSpecies) { + TMolesMultiphase += TPhMoles1[iph]; + } + } + vcs_dcopy(VCS_DATA_PTR(wt), molNum, nspecies); + for (kspec = 0; kspec < m_numComponents; ++kspec) { + if (SpeciesUnknownType[kspec] != VCS_SPECIES_TYPE_MOLNUM) { + wt[kspec] = 0.0; + } + } + vcs_dcopy(VCS_DATA_PTR(m_gibbsSpecies), VCS_DATA_PTR(ff), nspecies); + + for (kspec = 0; kspec < m_numComponents; ++kspec) { + if (SpeciesUnknownType[kspec] == VCS_SPECIES_TYPE_MOLNUM) { + if (! SSPhase[kspec]) { + iph = PhaseID[kspec]; + m_gibbsSpecies[kspec] += log(wt[kspec] / TPhMoles[iph]); + } + } else { + wt[kspec] = 0.0; + } + } + vcs_deltag(0, true); +#ifdef DEBUG + if (vcs_debug_print_lvl >= 2) { + for (kspec = 0; kspec < nspecies; ++kspec) { + plogf("%s", pprefix); plogf("%-12.12s", SpName[kspec].c_str()); + if (kspec < m_numComponents) + plogf("fe* = %15.5g ff = %15.5g\n", m_gibbsSpecies[kspec], ff[kspec]); + else + plogf("fe* = %15.5g ff = %15.5g dg* = %15.5g\n", + m_gibbsSpecies[kspec], ff[kspec], dg[kspec-m_numComponents]); + } + } +#endif + /* ********************************************************** */ + /* **** ESTIMATE REACTION ADJUSTMENTS *********************** */ + /* ********************************************************** */ + vcs_dzero(VCS_DATA_PTR(DelTPhMoles), NPhase); + for (iph = 0; iph < NPhase; iph++) { + xtphMax[iph] = log(TPhMoles1[iph] * 1.0E32); + xtphMin[iph] = log(TPhMoles1[iph] * 1.0E-32); + } + for (irxn = 0; irxn < nrxn; ++irxn) { + kspec = ir[irxn]; + /* + * For single species phases, we will not estimate the + * mole numbers. If the phase exists, it stays. If it + * doesn't exist in the estimate, it doesn't come into + * existence here. + */ + if (! SSPhase[kspec]) { + iph = PhaseID[kspec]; + if (dg[irxn] > xtphMax[iph]) dg[irxn] = 0.8 * xtphMax[iph]; + if (dg[irxn] < xtphMin[iph]) dg[irxn] = 0.8 * xtphMin[iph]; + /* + * HKM -> The TMolesMultiphase is a change of mine. + * It more evenly distributes the initial moles amongst + * multiple multispecies phases according to the + * relative values of the standard state free energies. + * There is no change for problems with one multispecies + * phase. + * It cut diamond4.vin iterations down from 62 to 14. + */ + ds[kspec] = 0.5 * (TPhMoles1[iph] + TMolesMultiphase) + * exp(-dg[irxn]); + + for (k = 0; k < m_numComponents; ++k) { + ds[k] += sc[irxn][k] * ds[kspec]; + } + + for (iph = 0; iph < NPhase; iph++) { + DelTPhMoles[iph] += DnPhase[irxn][iph] * ds[kspec]; + } + } + } +#ifdef DEBUG + if (vcs_debug_print_lvl >= 2) { + for (kspec = 0; kspec < nspecies; ++kspec) { + if (SpeciesUnknownType[kspec] != VCS_SPECIES_TYPE_INTERFACIALVOLTAGE) { + plogf("%sdirection (", pprefix); plogf("%-12.12s", SpName[kspec].c_str()); + plogf(") = %g", ds[kspec]); + if (SSPhase[kspec]) { + if (molNum[kspec] > 0.0) { + plogf(" (ssPhase exists at w = %g moles)", molNum[kspec]); + } else { + plogf(" (ssPhase doesn't exist -> stability not checked)"); + } + } + plogf("\n"); + } + } + } +#endif + /* *********************************************************** */ + /* **** KEEP COMPONENT SPECIES POSITIVE ********************** */ + /* *********************************************************** */ + par = 0.5; + for (kspec = 0; kspec < m_numComponents; ++kspec) { + if (SpeciesUnknownType[kspec] != VCS_SPECIES_TYPE_INTERFACIALVOLTAGE) { + if (par < -ds[kspec] / wt[kspec]) par = -ds[kspec] / wt[kspec]; + } + } + par = 1. / par; + if (par <= 1.0 && par > 0.0) { + par *= 0.8; + } else { + par = 1.0; + } + /* ******************************************** */ + /* **** CALCULATE NEW MOLE NUMBERS ************ */ + /* ******************************************** */ + finished = FALSE; + do { + for (kspec = 0; kspec < m_numComponents; ++kspec) { + if (SpeciesUnknownType[kspec] != VCS_SPECIES_TYPE_INTERFACIALVOLTAGE) { + molNum[kspec] = wt[kspec] + par * ds[kspec]; + } else { + ds[kspec] = 0.0; + } + } + for (kspec = m_numComponents; kspec < nspecies; ++kspec) { + if (SpeciesUnknownType[kspec] != VCS_SPECIES_TYPE_INTERFACIALVOLTAGE) { + if (ds[kspec] != 0.0) molNum[kspec] = ds[kspec] * par; + } + } + /* + * We have a new w[] estimate, go get the + * TMoles and TPhMoles[] values + */ + vcs_tmoles(); + if (lt > 0) goto finished; + /* ******************************************* */ + /* **** CONVERGENCE FORCING SECTION ********** */ + /* ******************************************* */ + vcs_dfe(molNum, 0, 0, 0, nspecies); + for (kspec = 0, s = 0.0; kspec < nspecies; ++kspec) { + s += ds[kspec] * m_gibbsSpecies[kspec]; + } + if (s == 0.0) { + finished = TRUE; continue; + } + if (s < 0.0) { + if (ikl <= 0) { + finished = TRUE; continue; + } + } + /* ***************************************** */ + /* *** TRY HALF STEP SIZE ****************** */ + /* ***************************************** */ + if (ikl <= 0) { + s1 = s; + par *= 0.5; + ikl = 1; + continue; + } + /* **************************************************** */ + /* **** FIT PARABOLA THROUGH HALF AND FULL STEPS ****** */ + /* **************************************************** */ + xl = (1.0 - s / (s1 - s)) * 0.5; + if (xl < 0.0) { + /* *************************************************** */ + /* *** POOR DIRECTION, REDUCE STEP SIZE TO 0.2 ******* */ + /* *************************************************** */ + par *= 0.2; + } else { + if (xl > 1.0) { + /* *************************************************** */ + /* **** TOO BIG A STEP, TAKE ORIGINAL FULL STEP ****** */ + /* *************************************************** */ + par *= 2.0; + } else { + /* *************************************************** */ + /* **** ACCEPT RESULTS OF FORCER ********************* */ + /* *************************************************** */ + par = par * 2.0 * xl; + } + } + lt = 1; + } while (!finished); + finished: + ; +#ifdef DEBUG + if (vcs_debug_print_lvl >= 2) { + plogf("%s Final Mole Numbers produced by inest:\n", + pprefix); + plogf("%s SPECIES MOLE_NUMBER\n", pprefix); + for (kspec = 0; kspec < nspecies; ++kspec) { + plogf("%s ", pprefix); plogf("%-12.12s", SpName[kspec].c_str()); + plogf(" %g\n", molNum[kspec]); + } + } +#endif + } /* inest() *****************************************************************/ + + /*****************************************************************************/ + /*****************************************************************************/ + /*****************************************************************************/ + + int VCS_SOLVE::vcs_inest_TP(void) + + /************************************************************************** + * + * vcs_inest_TP: + * + * Create an initial estimate of the solution to the thermodynamic + * equilibrium problem. + * + * Return value: + * + * 0: successful initial guess + * -1: Unsuccessful initial guess, the elemental abundances aren't + * satisfied. + ***************************************************************************/ + { + int retn = 0; + double test; + double tsecond = vcs_second(); + test = -1.0E20; + /* + * Malloc temporary space for usage in this routine and in + * subroutines + * sm[ne*ne] + * ss[ne] + * sa[ne] + * aw[m] + */ + + + std::vector sm(m_numElemConstraints*m_numElemConstraints, 0.0); + std::vector ss(m_numElemConstraints, 0.0); + std::vector sa(m_numElemConstraints, 0.0); + std::vector aw(m_numSpeciesTot+ m_numElemConstraints, 0.0); + /* + * Go get the estimate of the solution + */ +#ifdef DEBUG + if (vcs_debug_print_lvl >= 2) { + plogf("%sGo find an initial estimate for the equilibrium problem\n", + pprefix); + } +#endif + inest(VCS_DATA_PTR(aw), VCS_DATA_PTR(sa), VCS_DATA_PTR(sm), + VCS_DATA_PTR(ss), test); + /* + * Calculate the elemental abundances + */ + vcs_elab(); + /* + * If we still fail to achieve the correct elemental abundances, + * try to fix the problem again by calling the main elemental abundances + * fixer routine, used in the main program. What this does, is that it + * attempts to tweak the mole numbers of the component species to + * satisfy the element abundance constraints. + * + * Note: We won't do this unless we have to since it involves inverting + * a matrix. + */ + int rangeCheck = vcs_elabcheck(1); + if (!vcs_elabcheck(0)) { +#ifdef DEBUG + if (vcs_debug_print_lvl >= 2) { + plogf("%sInitial guess failed element abundances\n", pprefix); + plogf("%sCall vcs_elcorr to attempt fix\n", pprefix); + } +#endif + vcs_elcorr(VCS_DATA_PTR(sm), VCS_DATA_PTR(aw)); + rangeCheck = vcs_elabcheck(1); + if (!vcs_elabcheck(0)) { + plogf("%sInitial guess still fails element abundance equations\n", + pprefix); + plogf("%s - Inability to ever satisfy element abundance " + "constraints is probable\n", pprefix); + retn = -1; + } else { +#ifdef DEBUG + if (vcs_debug_print_lvl >= 2) { + if (rangeCheck) { + plogf("%sInitial guess now satisfies element abundances\n", pprefix); + } else { + plogf("%sElement Abundances RANGE ERROR\n", pprefix); + plogf("%s - Initial guess satisfies NC=%d element abundances, " + "BUT not NE=%d element abundances\n", pprefix, + m_numComponents, m_numElemConstraints); + } + } +#endif + } + } + else { +#ifdef DEBUG + if (vcs_debug_print_lvl >= 2) { + if (rangeCheck) { + plogf("%sInitial guess satisfies element abundances\n", pprefix); + } else { + plogf("%sElement Abundances RANGE ERROR\n", pprefix); + plogf("%s - Initial guess satisfies NC=%d element abundances, " + "BUT not NE=%d element abundances\n", pprefix, + m_numComponents, m_numElemConstraints); + } + } +#endif + } + +#ifdef DEBUG + if (vcs_debug_print_lvl >= 2) { + plogf("%sTotal Dimensionless Gibbs Free Energy = %15.7E\n", pprefix, + vcs_Total_Gibbs(VCS_DATA_PTR(soln), VCS_DATA_PTR(m_gibbsSpecies), + VCS_DATA_PTR(TPhMoles))); + } +#endif + + /* + * Free malloced memory + */ + tsecond = vcs_second() - tsecond; + m_VCount->T_Time_inest += tsecond; + (m_VCount->T_Calls_Inest)++; + return retn; + }/**** vcs_inest() ***********************************************************/ + +} + diff --git a/Cantera/src/equil/vcs_internal.h b/Cantera/src/equil/vcs_internal.h new file mode 100644 index 000000000..43979cf74 --- /dev/null +++ b/Cantera/src/equil/vcs_internal.h @@ -0,0 +1,183 @@ +/* + * $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. + */ + +#ifndef _VCS_INTERNAL_H +#define _VCS_INTERNAL_H + +#include "vcs_defs.h" +#include "vcs_DoubleStarStar.h" +#include "vcs_Exception.h" + + +#include "global.h" + +namespace VCSnonideal { + +//! Points to the data in a std::vector<> object +#define VCS_DATA_PTR(vvv) (&(vvv[0])) + +//! define this Cantera function to replace printf +/*! + * We can replace this with printf easily + */ +#define plogf Cantera::writelogf + + +/*****************************************************************************/ +/*****************************************************************************/ +/*****************************************************************************/ + +/************ ERROR HANDLING *****************/ + +struct VCS_ERR { + int Flag; + int Species1; + int Species2; + int PrintLevel; + double Value1; + double Value2; + char Mess[120]; +}; +typedef struct VCS_ERR VCS_ERR_STRUCT; + + +extern int vcsUtil_err_check(VCS_ERR_STRUCT &vcsE, char *string1, int ival); +extern void vcsUtil_err_reset(VCS_ERR_STRUCT &vcsE); + + +/*********************************/ +/* Function Pointer Typedefs */ +/*********************************/ + +typedef double (*VCS_FUNC_PTR)(double, double, int, void *, int *); + +/* + * Forward references + */ +class VCS_SPECIES_THERMO; +class VCS_PROB; + + +/****************************************************************************/ +/****************************************************************************/ +/****************************************************************************/ + +//! Amount of extra printing that is done while in debug mode. +/*! + * 0 -> none + * 1 -> some + * 2 -> alot (default) + * 3 -> everything + */ + +class VCS_COUNTERS { +public: + int T_Its; /* Total number of iterations in the main loop + of vcs_TP() to solve for thermo equilibrium */ + int Its; /* Current number of iterations in the main loop + of vcs_TP() to solve for thermo equilibrium */ + int T_Basis_Opts; /* Total number of optimizations of the + components basis set done */ + int Basis_Opts; /* Total number of optimizations of the + components basis set done */ + int T_Calls_Inest; /* Current number of times the initial thermo + equilibrium estimator has been called */ + int T_Calls_vcs_TP; /* Current number of calls to vcs_TP */ + double T_Time_vcs_TP; /* Total time spent in vcs_TP */ + double Time_vcs_TP; /* Current time spent in vcs_TP */ + double T_Time_basopt; /* Total Time spent in basopt () */ + double Time_basopt; /* Current Time spent in basopt () */ + double T_Time_inest; /* Time spent in initial estimator */ + double T_Time_vcs; /* Time spent in the vcs suite of programs */ +}; + +/*****************************************************************************/ +/**************** Prototypes *************************************************/ +/*****************************************************************************/ + +/* Externals for vcs_funcVtot.c */ + +extern double vcs_funcVtot(double, double, int, void *, int *); + +/* Externals defined in vcs_nondim.c */ + +extern double vcsUtil_gasConstant(int); + +/* Externals in vcs_solve_TP.c */ + +extern int vcsUtil_mlequ(double *, int, int, double *, int); +extern void vcsUtil_dsw(double *, int ,int); +extern void vcsUtil_isw(int [], int, int); +extern void vcsUtil_ssw(char **, int, int); +extern void vcsUtil_stsw(std::vector & vecStrings, int, int); + +/* Externals for vcs_root1d.c */ + +extern int vcsUtil_root1d(double, double, int, VCS_FUNC_PTR , void *, + double , int, double *); + +/* Externals defined in vcs_timer_generic.c */ + +extern double vcs_second(void); + + +/* Externals defined in vcs_util.c */ + +#define USE_MEMSET +#ifdef USE_MEMSET +#include +# define vcs_dzero(vector, length) (void) memset((void *) (vector), 0, \ + (length) * sizeof(double)) +# define vcs_izero(vector, length) (void) memset((void *) (vector), 0, \ + (length) * sizeof(int)) +# define vcs_dcopy(vec_to, vec_from, length) \ + (void) memcpy((void *) (vec_to), (const void *) (vec_from), \ + (length) * sizeof(double)) +# define vcs_icopy(vec_to, vec_from, length) \ + (void) memcpy((void *) (vec_to), (const void *) (vec_from), \ + (length) * sizeof(int)) +# define vcs_vdzero(vector, length) (void) memset(VCS_DATA_PTR(vector), 0, \ + (length) * sizeof(double)) +# define vcs_vizero(vector, length) (void) memset(VCS_DATA_PTR(vector), 0, \ + (length) * sizeof(int)) + +inline void vcs_vdcopy(std::vector & vec_to, + const std::vector & vec_from, int length) { + (void) memcpy((void *)&(vec_to[0]), (const void *) &(vec_from[0]), + (length) * sizeof(double)); +} +inline void vcs_vicopy(std::vector & vec_to, + const std::vector & vec_from, int length) { + (void) memcpy((void *)&(vec_to[0]), (const void *) &(vec_from[0]), + (length) * sizeof(int)); +} +#else +extern void vcs_dzero(double *, int); +extern void vcs_izero(int *, int); +extern void vcs_dcopy(double *, double *, int); +extern void vcs_icopy(int *, int *, int); +extern void vcs_vdzero(std::vector &vvv, int len = -1); +extern void vcs_vizero(std::vector &vvv, int len = -1); +void vcs_vdcopy(std::vector &vec_to, + const std::vector vec_from, int len = -1); +void vcs_vicopy(std::vector &vec_to, + const std::vector vec_from, int len = -1); +#endif +extern int vcs_amax(double *, int, int); +extern int vcs_max_int(int *, int); +extern void vcs_print_line(const char *, int); +extern void vcs_print_stringTrunc(const char *, int, int); +extern bool vcs_doubleEqual(double, double); + +/****************************************************************************/ +} + +#endif +/****************************************************************************/ diff --git a/Cantera/src/equil/vcs_linmaxc.cpp b/Cantera/src/equil/vcs_linmaxc.cpp new file mode 100644 index 000000000..904f710c9 --- /dev/null +++ b/Cantera/src/equil/vcs_linmaxc.cpp @@ -0,0 +1,146 @@ +/* $Author$ + * $Date$ + * $Revision$ + */ + +/* + * Copywrite (2005) Sandia Corporation. Under the terms of + * Contract DE-AC04-94AL85000 with Sandia Corporation, the + * U.S. Government retains certain rights in this software. + */ + +#include +#include +#include + +#ifdef hpux +#define dbocls_ dbocls +#endif +#ifdef DEBUG +//extern int vcs_debug_print_lvl; +#endif + + +extern "C" void dbocls_(double *W, int *MDW, int *MCON, int *MROWS, + int *NCOLS, + double *BL, double *BU, int *IND, int *IOPT, + double *X, double *RNORMC, double *RNORM, + int *MODE, double *RW, int *IW); + +/***************************************************************************** +* This whole program is a wrapper for the slatec routine, DBOCLS() +* DBOCLS solves a bounded and constrained least squares problem. +******************************************************************************/ + +int linprogmax(double *XMOLES, double *CC, double *AX, double *BB, + int NE, int M, int NE0) + + /*----------------------------------------------------------------------- + * Find XMOLES(I), i = 1, M such that + * Maximize CC dot W, subject to the NE constraints: + * + * [AX] [XMOLES] = [BB] + * and XMOLES(i) > 0 + * + * Input + * --------- + * AX(NE, M) - matrix of constraints AX(I,J) = ax(i + j*ne0) + * BB(NE) - contraint values + * CC(M) - Vector of "Good Values" to maximize + * + * Output + * --------- + * XMOLES(M) - optimal value of XMOLES() + *----------------------------------------------------------------------*/ +{ + int MROWS, MCON, NCOLS, NX, NI, MDW, i, j, MODE; + double sum, F[1], RNORMC, RNORM, *W, *BL, *BU, *RW, *X; + int *IND, *IW, *IOPT; + + MROWS = 1; + MCON = NE; + NCOLS = M; + MDW = MCON + NCOLS; + NX = 0; + NI = 0; + + sum = 0.0; + for (i = 0; i < M; i++) { + sum += fabs(CC[i]); + } + F[0] = sum * 1000.; + if (F[0] <= 0.0) F[0] = 1000.; + + BL = (double *) malloc(2*(NCOLS+MCON) * sizeof(double)); + BU = BL + (NCOLS+MCON); + IND = (int *) malloc((NCOLS+MCON) * sizeof(int)); + RW = (double *) malloc((6*NCOLS + 5*MCON) * sizeof(double)); + IW = (int *) malloc((2*NCOLS + 2*MCON) * sizeof(int)); + IOPT = (int *) malloc((17 + NI) * sizeof(int)); + X = (double *) malloc((2*(NCOLS+MCON) + 2 + NX) * sizeof(double)); + W = (double *) malloc((MDW*(NCOLS+MCON+1)) * sizeof(double)); + if (W == NULL) { + plogf("linproxmax ERROR: can not malloc memory of size %d bytes\n", + (int) ((MDW*(NCOLS+MCON+1)) * sizeof(double))); + if (BL != NULL) free((void *) BL); + if (IND != NULL) free((void *) IND); + if (RW != NULL) free((void *) RW); + if (IW != NULL) free((void *) IW); + if (IOPT != NULL) free((void *) IOPT); + if (W != NULL) free((void *) W); + return -1; + } + for (j = 0; j < MCON; j++) { + for (i = 0; i < NCOLS; i++) { + W[j + i*MDW] = AX[j + i*NE0]; + } + } + for (i = 0; i < NCOLS; i++) { + W[MCON + i*MDW] = CC[i]; + } + W[MCON + (NCOLS)*MDW] = F[0]; + IOPT[0] = 99; + + for (j = 0; j < NCOLS; j++) { + IND[j] = 1; + BL[j] = 0.0; + BU[j] = 1.0e200; + } + for (j = 0; j < MCON; j++) { + IND[j + NCOLS] = 3; + BL[j + NCOLS] = BB[j]; + BU[j + NCOLS] = BL[j + NCOLS]; + } + + + dbocls_(W, &MDW, &MCON, &MROWS, &NCOLS, BL, BU, IND, IOPT, + X, &RNORMC, &RNORM, &MODE, RW, IW); + if (MODE != 0) { + plogf("Return from DBOCLS was not normal, MODE = %d\n", MODE); + plogf(" refer to subroutine DBOCLS for resolution\n"); + plogf(" RNORMC = %g\n", RNORMC); + } + + for (j = 0; j < NCOLS; j++) { + XMOLES[j] = X[j]; + } +#ifdef DEBUG + //sum = 0.0; + //for (j = 0; j < NCOLS; j++) { + // sum += XMOLES[j] * CC[j]; + //} + //if (vcs_debug_print_lvl >= 2) { + // plogf(" -- linmaxc: Final Maximized Value = %g\n", sum); + //} +#endif + + free((void *)W); + free((void *)BL); + free((void *)IND); + free((void *)RW); + free((void *)IW); + free((void *)IOPT); + free((void *)X); + + return 0; +} diff --git a/Cantera/src/equil/vcs_nasa_poly.cpp b/Cantera/src/equil/vcs_nasa_poly.cpp new file mode 100644 index 000000000..da83dc5f0 --- /dev/null +++ b/Cantera/src/equil/vcs_nasa_poly.cpp @@ -0,0 +1,412 @@ +/* + * $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 + +#include "vcs_defs.h" +#include "vcs_nasa_poly.h" +#include "vcs_species_thermo.h" +#include "vcs_internal.h" + +namespace VCSnonideal { + +/****************************************************************************** + * + * Constructor + */ +VCS_NASA_POLY::VCS_NASA_POLY(int numTempRegions, int numEl) : + NumTempRegions(numTempRegions), + NumEl(numEl), + PhType(' ') +{ + Date[0] = '\0'; + SpName[0] = '\0'; + PhName[0] = '\0'; + ElName.resize(numEl, ""); + ElComp.resize(numEl, 0.0); + + if (NumTempRegions < 1) NumTempRegions = 1; + Tlimits.resize(NumTempRegions+1, 0.0); + Acoeff.resize(NumTempRegions, 7, 0.0); +} +/*****************************************************************************/ +/*****************************************************************************/ +/*****************************************************************************/ + +VCS_NASA_POLY *vcs_nasa_poly_create(int numTempRegions, int numEl) + + /************************************************************************** + * + * vcs_nasa_poly_create: + * + * Constructor routine for the nasa polynomial structure. + * It initializes all data to zero. The number of temperature regions + * malloced is storred within the structure itself. + * + * Input + * numTempRegions: Number of temperature regions + * + * Return + * Pointer to the newly malloced structure. + * If NULL, then an out of memory condition occurred + ***************************************************************************/ + +{ + VCS_NASA_POLY *poly_ptr; + poly_ptr = new VCS_NASA_POLY(numTempRegions, numEl); + return poly_ptr; +} + +/*************************************************************************** + * Copy Constructor + */ +VCS_NASA_POLY::VCS_NASA_POLY(const VCS_NASA_POLY &b) : + NumTempRegions(0), + NumEl(0) +{ + *this = b; +} +/****************************************************************************** + * + * operator=() + * + */ +VCS_NASA_POLY& VCS_NASA_POLY::operator=(const VCS_NASA_POLY &b) { + if (&b != this) { + NumTempRegions = b.NumTempRegions; + Tlimits = b.Tlimits; + Acoeff = b.Acoeff; + NumEl = b.NumEl; + ElComp = b.ElComp; + ElName = b.ElName; + strcpy(Date, b.Date); + PhType = b.PhType; + strcpy(SpName, b.SpName); + strcpy(PhName, b.PhName); + } + return *this; +} + + +/***************************************************************************** + * + * ~VCS_NASA_POLY(): + * + * Destructor for class + */ +VCS_NASA_POLY::~VCS_NASA_POLY() { +} +/*****************************************************************************/ +/*****************************************************************************/ +/*****************************************************************************/ + +void vcs_nasa_poly_destroy(VCS_NASA_POLY **poly_hdl) + + /************************************************************************** + * + * vcs_nasa_poly_destroy: + * + * Destructor routine for the nasa polynomial structure. + *************************************************************************/ +{ + VCS_NASA_POLY *poly_ptr = *poly_hdl; + if (poly_ptr) { + delete poly_ptr; + poly_ptr = 0; + } +} + +/*****************************************************************************/ +/*****************************************************************************/ +/*****************************************************************************/ + +double vcs_G0_NASA(double TKelvin, VCS_NASA_POLY *poly_ptr) + + /************************************************************************** + * + * vcs_GibbsFE_NASA: + * + * Calculate the Gibbs free energy (in Kelvin) for a single species + * using the Nasa polynomial format. + * + * Input + * TKelvin = Temperature in Kelvin. + * poly_ptr = Pointer to structure containing the NASA Polynomial + * coefficients + * + * Return + * gibbsFE = Gibbs free energy / R -> units of kelvin + * + * Error Conditions + * VCS_THERMO_OUTOFRANGE: + * If the input temperature, is out of range of the polynomials, + * an error Flag is set, and the temperature is storred in the + * error structure. + ***************************************************************************/ +{ + int iRegion; + double *a, gibbsFE; + double *Tlim = VCS_DATA_PTR(poly_ptr->Tlimits); + static double Tsave = -10., C0, C1, C2, C3, C4, C5; + //extern CPC_ERR_STRUCT cpcE; + /* + * Find the temperature region + */ + if (TKelvin <= *Tlim) { +#ifdef DEBUG + plogf("vcs_G0_NASA error: TKelvin below lowest bounds %g\n", *Tlim); +#endif + iRegion = 0; + + if (TKelvin <= 0.0) { + gibbsFE = poly_ptr->Acoeff[0][5]; + return gibbsFE; + } + goto L_FOUNDREGION; + } + for (iRegion = 0; iRegion < poly_ptr->NumTempRegions; iRegion++) { + Tlim++; + if (TKelvin <= *Tlim) goto L_FOUNDREGION; + } +#ifdef DEBUG + plogf("vcs_G0_NASA error: TKelvin above highest bounds %g\n", *(Tlim)); +#endif + + iRegion--; + L_FOUNDREGION:; + a = poly_ptr->Acoeff[iRegion]; + if (Tsave != TKelvin) { + Tsave = TKelvin; + C0 = 1.0 - log(TKelvin); + C1 = TKelvin * 0.5; + C2 = TKelvin * TKelvin; + C3 = C2 * TKelvin; + C4 = C3 * TKelvin; + C2 /= 6.0; + C3 /= 12.0; + C4 /= 20.0; + C5 = 1.0 / TKelvin; + } + gibbsFE = a[0]*C0 - a[1]*C1 - a[2]*C2 - a[3]*C3 - a[4]*C4 + a[5]*C5 - a[6]; + gibbsFE *= TKelvin; + return gibbsFE; +} /***************************************************************************/ + +double vcs_H0_NASA(double TKelvin, VCS_NASA_POLY *poly_ptr) + + /************************************************************************** + * + * vcs_H0_NASA: + * + * Calculate the standard state Enthalpy (in Kelvin) for a single species + * using the Nasa polynomial format. + * + * Input + * TKelvin = Temperature in Kelvin. + * poly_ptr = Pointer to structure containing the NASA Polynomial + * coefficients + * + * Return + * H0 = Standard State Enthalpy / R -> units of kelvin + * + * Error Conditions + * VCS_THERMO_OUTOFRANGE: + * If the input temperature, is out of range of the polynomials, + * an error Flag is set, and the temperature is storred in the + * error structure. + ***************************************************************************/ +{ + int iRegion; + double *a, H0; + double *Tlim = VCS_DATA_PTR(poly_ptr->Tlimits); + static double Tsave = -10., C1, C2, C3, C4, C5; + /* + * Find the temperature region + */ + if (TKelvin <= *Tlim) { +#ifdef DEBUG + plogf("vcs_H0_NASA error: TKelvin below lowest bounds\n"); +#endif + iRegion = 0; + if (TKelvin <= 0.0) { + H0 = poly_ptr->Acoeff[0][6]; + return H0; + } + goto L_FOUNDREGION; + } + for (iRegion = 0; iRegion < poly_ptr->NumTempRegions; iRegion++) { + Tlim++; + if (TKelvin <= *Tlim) goto L_FOUNDREGION; + } +#ifdef DEBUG + plogf("vcs_H0_NASA error: TKelvin above highest bounds\n"); +#endif + iRegion--; + L_FOUNDREGION:; + a = poly_ptr->Acoeff[iRegion]; + if (Tsave != TKelvin) { + Tsave = TKelvin; + C1 = TKelvin * 0.5; + C2 = TKelvin * TKelvin; + C3 = C2 * TKelvin; + C4 = C3 * TKelvin; + C2 /= 3.0; + C3 /= 4.0; + C4 /= 5.0; + C5 = 1.0 / TKelvin; + } + H0 = a[0] + a[1]*C1 + a[2]*C2 + a[3]*C3 + a[4]*C4 + a[5]*C5; + return H0; +} /***************************************************************************/ + +/*****************************************************************************/ +/*****************************************************************************/ +/*****************************************************************************/ + +double vcs_Cp0_NASA(double TKelvin, VCS_NASA_POLY *poly_ptr) + + /************************************************************************** + * + * vcs_Cp0_NASA: + * + * Calculate the standard state Heat Capacity at constant pressure + * (in Kelvin) for a single species using the Nasa polynomial format. + * + * Input + * TKelvin = Temperature in Kelvin. + * poly_ptr = Pointer to structure containing the NASA Polynomial + * coefficients + * + * Return + * Cp0 = Heat Capacity at constant Pressure / R -> dimensionless + * + * Error Conditions + * VCS_THERMO_OUTOFRANGE: + * If the input temperature, is out of range of the polynomials, + * an error Flag is set, and the temperature is storred in the + * error structure. + ***************************************************************************/ +{ + int iRegion; + double *a, Cp0; + double *Tlim = VCS_DATA_PTR(poly_ptr->Tlimits); + static double Tsave = -10., C2, C3, C4; + /* + * Find the temperature region + */ + if (TKelvin <= *Tlim) { +#ifdef DEBUG + plogf("vcs_Cp0_NASA error: TKelvin below lowest bounds\n"); +#endif + iRegion = 0; + if (TKelvin <= 0.0) { + Cp0 = poly_ptr->Acoeff[0][0]; + return Cp0; + } + goto L_FOUNDREGION; + } + for (iRegion = 0; iRegion < poly_ptr->NumTempRegions; iRegion++) { + Tlim++; + if (TKelvin <= *Tlim) goto L_FOUNDREGION; + } +#ifdef DEBUG + plogf("vcs_Cp0_NASA error: TKelvin above highest bounds\n"); +#endif + + iRegion--; + L_FOUNDREGION:; + a = poly_ptr->Acoeff[iRegion]; + if (Tsave != TKelvin) { + Tsave = TKelvin; + C2 = TKelvin * TKelvin; + C3 = C2 * TKelvin; + C4 = C3 * TKelvin; + } + Cp0 = a[0] + a[1]*TKelvin + a[2]*C2 + a[3]*C3 + a[4]*C4; + return Cp0; +} /***************************************************************************/ + +/*****************************************************************************/ +/*****************************************************************************/ +/*****************************************************************************/ + +double vcs_S0_NASA(double TKelvin, VCS_NASA_POLY *poly_ptr) + + /************************************************************************** + * + * vcs_S0_NASA: + * + * Calculates the standard state Entropy (in Kelvin) for a single species + * using the Nasa polynomial format. + * + * Input + * TKelvin = Temperature in Kelvin. + * poly_ptr = Pointer to structure containing the NASA Polynomial + * coefficients + * + * Return + * S0 = Standard State Entropy / R -> unitless + * + * Error Conditions + * VCS_THERMO_OUTOFRANGE: + * If the input temperature, is out of range of the polynomials, + * an error Flag is set, and the temperature is storred in the + * error structure. + ***************************************************************************/ +{ + int iRegion; + double *a, S0; + double *Tlim = VCS_DATA_PTR(poly_ptr->Tlimits); + static double Tsave = -10., C0, C2, C3, C4; + /* + * Find the temperature region + */ + if (TKelvin <= *Tlim) { +#ifdef DEBUG + plogf("vcs_S0_NASA error: TKelvin below lowest bounds\n"); +#endif + iRegion = 0; + if (TKelvin <= 0.0) { + S0 = 0.0; + return S0; + } + goto L_FOUNDREGION; + } + for (iRegion = 0; iRegion < poly_ptr->NumTempRegions; iRegion++) { + Tlim++; + if (TKelvin <= *Tlim) goto L_FOUNDREGION; + } +#ifdef DEBUG + plogf("vcs_S0_NASA error: TKelvin above highest bounds\n"); +#endif + iRegion--; + + + L_FOUNDREGION:; + a = poly_ptr->Acoeff[iRegion]; + if (Tsave != TKelvin) { + Tsave = TKelvin; + C0 = log(TKelvin); + C2 = TKelvin * TKelvin; + C3 = C2 * TKelvin; + C4 = C3 * TKelvin; + C2 /= 2.0; + C3 /= 3.0; + C4 /= 4.0; + } + S0 = a[0]*C0 + a[1]*TKelvin + a[2]*C2 + a[3]*C3 + a[4]*C4 + a[7]; + return S0; +} /***************************************************************************/ + +} + diff --git a/Cantera/src/equil/vcs_nasa_poly.h b/Cantera/src/equil/vcs_nasa_poly.h new file mode 100644 index 000000000..338e353ac --- /dev/null +++ b/Cantera/src/equil/vcs_nasa_poly.h @@ -0,0 +1,89 @@ +/* + * $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. + */ + +#ifndef VCS_NASA_POLY_H +#define VCS_NASA_POLY_H + +#include +#include +#include "vcs_DoubleStarStar.h" + +namespace VCSnonideal { + +/* +* NASA Polynomial Form for Standard state Thermo Functions. +* +* +* NumberTempRegions +* Number of temperature regions in the fits: +* Must be greater or equal to one. +* +* Tlimits[NumberTempRegions+1]: +* Temperature limits of the regions. At the intersection of +* the regions, the polynomial formulas are suppose to be +* C1 continuous. +* To Locate Region i for current temperature, TKelvin: +* Tlimits[i] <= TKelvin < Tlimits[i+1] +* +* Acoeff[NumberTempRegions][7] +* Coefficients for calculation of the standard state thermodynamic +* functions. +* +* double *a; +* for i such that Tlimits[i] <= T < Tlimits[i+1]: +* a = Acoeff[i]; +* +* C_p/R = a[0] + a[1]*T + a[2] * T^2 + a[3] * T^3 + a[4] * T^4 +* +* H/RT = a[0] + a[1]/2*T + a[2]/3 * T^2 + a[3]/4 * T^3 + a[4]/5 * T^4 +* + a[5]/T +* +* S/R = a[0] * log(T) + a[1] * T + a[2]/2 * T^2 + a[3]/3 * T^3 +* + a[4]/4 * T^4 + a[6] +* +*/ +class VCS_NASA_POLY { +public: + VCS_NASA_POLY(int, int); + VCS_NASA_POLY(const VCS_NASA_POLY &b); + VCS_NASA_POLY& operator=(const VCS_NASA_POLY &); + + ~VCS_NASA_POLY(); + int NumTempRegions; +/* Vector Of Temperature Limits -> One More Than + The Number Of Regions */ + std::vector Tlimits; + + DoubleStarStar Acoeff; + + int NumEl; + std::vector ElComp; + std::vector ElName; + char Date[12]; + char PhType; + char SpName[24]; + char PhName[24]; +}; + + +/* Externals for vcs_nasa_poly.c */ + +extern VCS_NASA_POLY *vcs_nasa_poly_create(int, int); +extern void vcs_nasa_poly_free(VCS_NASA_POLY *); +extern void vcs_nasa_poly_destroy(VCS_NASA_POLY **);; + +extern double vcs_G0_NASA(double, VCS_NASA_POLY *); +extern double vcs_H0_NASA(double, VCS_NASA_POLY *); +extern double vcs_Cp0_NASA(double, VCS_NASA_POLY *); +extern double vcs_S0_NASA(double, VCS_NASA_POLY *); + +} + +#endif diff --git a/Cantera/src/equil/vcs_nondim.cpp b/Cantera/src/equil/vcs_nondim.cpp new file mode 100644 index 000000000..89873c302 --- /dev/null +++ b/Cantera/src/equil/vcs_nondim.cpp @@ -0,0 +1,222 @@ +/*======================================================================= +* -------------------------------------------------- +* | CVS Head Information on zuzax.pchem.sandia.gov | +* -------------------------------------------------- +* $RCSfile$ +* $Author$ +* $Date$ +* $Revision$ +*======================================================================= */ +#include +#include +#include + +#include "vcs_solve.h" +#include "vcs_internal.h" + +namespace VCSnonideal { + +/************************************************************************** + * + * vcs_nondimMult: + * + * Returns the multiplier for the nondimensionalization of the equations + * (this is basically equal to RT) + **************************************************************************/ +double VCS_SOLVE::vcs_nondim_Farad(int mu_units, double TKelvin) +{ + double Farad; + if (TKelvin <= 0.0) TKelvin = 293.15; + switch (mu_units) { + case VCS_UNITS_MKS: + case VCS_UNITS_KJMOL: + case VCS_UNITS_KCALMOL: + Farad = 1.602E-19 * 6.022136736e26/ (TKelvin * 8.314472E3); + break; + case VCS_UNITS_UNITLESS: + Farad = 1.602E-19 * 6.022136736e26; + break; + case VCS_UNITS_KELVIN: + Farad = 1.602E-19 * 6.022136736e26/ (TKelvin); + break; + default: + plogf("vcs_nondim_Farad error: unknown units: %d\n", mu_units); + exit(-1); + } + return Farad; +} + +double VCS_SOLVE::vcs_nondimMult_TP(int mu_units, double TKelvin) +{ + double rt; + if (TKelvin <= 0.0) TKelvin = 293.15; + switch (mu_units) { + case VCS_UNITS_KCALMOL: + rt = TKelvin * 8.314472E-3 / 4.184; + break; + case VCS_UNITS_UNITLESS: + rt = 1.0; + break; + case VCS_UNITS_KJMOL: + rt = TKelvin * 0.008314472; + break; + case VCS_UNITS_KELVIN: + rt = TKelvin; + break; + case VCS_UNITS_MKS: + rt = TKelvin * 8.314472E3; + break; + default: + plogf("vcs_nondimMult_TP error: unknown units: %d\n", mu_units); + exit(-1); + } + return rt; +} + +/************************************************************************** + * + * vcs_gasConstant: + * + * Returns the value of the gas constant in the units specified by + * if__. + ***************************************************************************/ +double vcsUtil_gasConstant(int mu_units) +{ + double r; + switch (mu_units) { + case VCS_UNITS_KCALMOL: + r = 0.008314472/4.184; + break; + case VCS_UNITS_UNITLESS: + r = 1.0; + break; + case VCS_UNITS_KJMOL: + r = 0.008314472; + break; + case VCS_UNITS_KELVIN: + r = 1.0; + break; + case VCS_UNITS_MKS: + /* joules / kg-mol K = kg m2 / s2 kg-mol K */ + r = 8.314472E3; + break; + default: + plogf("vcs_gasConstant error: uknown units: %d\n", + mu_units); + exit(-1); + } + return r; +} + +/************************************************************************** + * + * vcs_nondim_TP: + * Nondimensionalize the problem data: + * ->nondimensionalize the free energies using + * the divisor, R * T + * + * + * HKM -> I don't think we need to modify the mole nubmers by 1E3 for the + * case of MKS units. However, what we need to do is to add a scale + * factor so that the number of moles or kmoles is ~ 1.0. Many of the + * algorithms rely on this I think in a subtle way. This is the perfect + * place to add this in. + **************************************************************************/ +void VCS_SOLVE::vcs_nondim_TP(void) { + int i; + double tf; + if (UnitsState == VCS_DIMENSIONAL_G) { + UnitsState = VCS_NONDIMENSIONAL_G; + tf = 1.0 / vcs_nondimMult_TP(m_VCS_UnitsFormat, T); + for (i = 0; i < m_numSpeciesTot; ++i) { + /* + * Modify the standard state and total chemical potential data, + * FF(I), to make it dimensionless, i.e., mu / RT. + * Thus, we may divide it by the temperature. + */ + ff[i] *= tf; + m_gibbsSpecies[i] *= tf; + dg[i] *= tf; + dgl[i] *= tf; + fel[i] *= tf; + } + + Faraday_dim = vcs_nondim_Farad(m_VCS_UnitsFormat, T); + if (m_VCS_UnitsFormat == VCS_UNITS_MKS) { + for (i = 0; i < m_numSpeciesTot; ++i) { + if (SpeciesUnknownType[i] != VCS_SPECIES_TYPE_INTERFACIALVOLTAGE) { + soln[i] *= 1.0E3; + } + } + for (i = 0; i < m_numElemConstraints; ++i) { + gai[i] *= 1.0E3; + } + } + } +} /* vcs_nondim_TP() *********************************************************/ + +/************************************************************************** + * + * vcs_nondim_TP: + * Redimensionalize the problem data: + * ->redimensionalize the free energies using the reverse + * of vcs_nondim_TP + **************************************************************************/ +void VCS_SOLVE::vcs_redim_TP(void) +{ + int i; + double tf; + if (UnitsState != VCS_DIMENSIONAL_G) { + UnitsState = VCS_DIMENSIONAL_G; + tf = vcs_nondimMult_TP(m_VCS_UnitsFormat, T); + for (i = 0; i < m_numSpeciesTot; ++i) { + /* + * Modify the standard state and total chemical potential data, + * FF(I), to make it have units, i.e. mu = RT * mu_star + */ + ff[i] *= tf; + m_gibbsSpecies[i] *= tf; + dg[i] *= tf; + dgl[i] *= tf; + fel[i] *= tf; + } + Faraday_dim *= tf; + } + if (m_VCS_UnitsFormat == VCS_UNITS_MKS) { + for (i = 0; i < m_numSpeciesTot; ++i) { + if (SpeciesUnknownType[i] != VCS_SPECIES_TYPE_INTERFACIALVOLTAGE) { + soln[i] /= 1.0E3; + } + } + for (i = 0; i < m_numElemConstraints; ++i) { + gai[i] /= 1.0E3; + } + } + +} /* vcs_redim_TP() **********************************************************/ + +void VCS_SOLVE::vcs_printChemPotUnits(int unitsFormat) { + switch(unitsFormat) { + case VCS_UNITS_KCALMOL: + plogf("kcal/gmol"); + break; + case VCS_UNITS_UNITLESS: + plogf("dimensionless"); + break; + case VCS_UNITS_KJMOL: + plogf("kJ/gmol"); + break; + case VCS_UNITS_KELVIN: + plogf("Kelvin"); + break; + case VCS_UNITS_MKS: + plogf("J/kmol"); + break; + default: + plogf("unknown units!"); + exit(-1); + } +} + +} + diff --git a/Cantera/src/equil/vcs_prep.cpp b/Cantera/src/equil/vcs_prep.cpp new file mode 100644 index 000000000..71b7849c7 --- /dev/null +++ b/Cantera/src/equil/vcs_prep.cpp @@ -0,0 +1,308 @@ +/** + * @file vcs_prep.cpp + * This file contains some prepatory functions. + */ + +/* $RCSfile$ */ +/* $Author$ */ +/* $Date$ */ +/* $Revision$ */ +/* + * Copywrite (2005) Sandia Corporation. Under the terms of + * Contract DE-AC04-94AL85000 with Sandia Corporation, the + * U.S. Government retains certain rights in this software. + */ +#include +#include +#include + +#include "vcs_solve.h" +#include "vcs_internal.h" +#include "vcs_prob.h" +#include "vcs_VolPhase.h" + +namespace VCSnonideal { + +/*****************************************************************************/ +/*****************************************************************************/ +/*****************************************************************************/ + +void VCS_SOLVE::vcs_SSPhase(void) + /************************************************************************** + * + * vcs_SSPhase: + * + * Calculate the status of single species phases. + * + *************************************************************************/ +{ + int kspec, iph; + vcs_VolPhase *Vphase; + + std::vector numPhSpecies(NPhase, 0); + + for (kspec = 0; kspec < m_numSpeciesTot; ++kspec) { + numPhSpecies[PhaseID[kspec]]++; + } + /* + * Handle the special case of a single species in a phase that + * has been earmarked as a multispecies phase. + * Treat that species as a single-species phase + */ + for (iph = 0; iph < NPhase; iph++) { + Vphase = VPhaseList[iph]; + Vphase->SingleSpecies = false; + if (TPhInertMoles[iph] > 0.0) { + Vphase->Existence = 2; + } + if (numPhSpecies[iph] <= 1) { + if (TPhInertMoles[iph] == 0.0) { + Vphase->SingleSpecies = true; + } + } + Vphase->NVolSpecies = numPhSpecies[iph]; + } + + /* + * Fill in some useful arrays here that have to do with the + * static information concerning the phase ID of species. + * SSPhase = Boolean indicating whether a species is in a + * single species phase or not. + */ + for (kspec = 0; kspec < m_numSpeciesTot; kspec++) { + iph = PhaseID[kspec]; + Vphase = VPhaseList[iph]; + if (Vphase->SingleSpecies) SSPhase[kspec] = TRUE; + else SSPhase[kspec] = FALSE; + } +} +/*****************************************************************************/ +/*****************************************************************************/ +/*****************************************************************************/ + +int VCS_SOLVE::vcs_prep_oneTime(int printLvl) + + /************************************************************************** + * + * vcs_prep_oneTime: + * + * This routine is mostly concerned with changing the private data + * to be consistent with what's needed for solution. It is called one + * time for each new problem structure definition. + * + * This routine is always followed by vcs_prep(). Therefore, tasks + * that need to be done for every call to vcsc() should be placed in + * vcs_prep() and not in this routine. + * + * The problem structure refers to: + * + * the number and identity of the species. + * the formula matrix and thus the number of components. + * the number and identity of the phases. + * the equation of state + * the method and parameters for determining the standard state + * The method and parameters for determining the activity coefficients. + * + * Tasks: + * 0) Fill in the SSPhase[] array. + * 1) Check to see if any multispecies phases actually have only one + * species in that phase. If true, reassign that phase and species + * to be a single-species phase. + * 2) Determine the number of components in the problem if not already + * done so. During this process the order of the species is changed + * in the private data structure. All references to the species + * properties must employ the ind[] index vector. + * + * return code + * VCS_SUCCESS = everything went OK + * + **************************************************************************/ +{ + int kspec, i, conv, retn = VCS_SUCCESS; + double pres, test; + double *aw, *sa, *sm, *ss; + bool modifiedSoln = false; + +#ifdef DEBUG + vcs_debug_print_lvl = printLvl; +#endif + + /* + * Calculate the Single Species status of phases + * Also calculate the number of species per phase + */ + vcs_SSPhase(); + + /* + * Set an initial estimate for the number of noncomponent species + * equal to nspecies - nelements. This may be changed below + */ + m_numRxnTot = m_numSpeciesTot - m_numElemConstraints; + m_numRxnRdc = m_numRxnTot; + m_numSpeciesRdc = m_numSpeciesTot; + for (i = 0; i < m_numRxnRdc; ++i) { + ir[i] = m_numElemConstraints + i; + } + + /* ***************************************************** */ + /* **** DETERMINE THE NUMBER OF COMPONENTS ************* */ + /* ***************************************************** */ + + /* + * Obtain a valid estimate of the mole fraction. This will + * be used as an initial ordering vector for prioritizing + * which species are defined as components. + * + * If a mole number estimate was supplied from the + * input file, use that mole number estimate. + * + * If a solution estimate wasn't supplied from the input file, + * supply an initial estimate for the mole fractions + * based on the relative reverse ordering of the + * chemical potentials. + * + * For voltage unknowns, set these to zero for the moment. + */ + test = -1.0e-10; + if (iest < 0) { + double sum = 0.0; + for (kspec = 0; kspec < m_numSpeciesTot; ++kspec) { + if (SpeciesUnknownType[kspec] == VCS_SPECIES_TYPE_MOLNUM) { + sum += fabs(soln[kspec]); + } + } + if (fabs(sum) < 1.0E-6) { + modifiedSoln = true; + if (Pres <= 0.0) pres = 1.0; + else pres = Pres; + retn = vcs_evalSS_TP(0, 0, T, pres); + for (kspec = 0; kspec < m_numSpeciesTot; ++kspec) { + if (SpeciesUnknownType[kspec] == VCS_SPECIES_TYPE_MOLNUM) { + soln[kspec] = - ff[kspec]; + } else { + soln[kspec] = 0.0; + } + } + } + test = -1.0e20; + } + + /* + * NC = number of components is in the vcs.h common block + * This call to BASOPT doesn't calculate the stoichiometric + * reaction matrix. + */ + std::vector awSpace( m_numSpeciesTot + (m_numElemConstraints + 2)*(m_numElemConstraints), 0.0); + aw = VCS_DATA_PTR(awSpace); + if (aw == NULL) { + plogf("vcs_prep_oneTime: failed to get memory: global bailout\n"); + return VCS_NOMEMORY; + } + sa = aw + m_numSpeciesTot; + sm = sa + m_numElemConstraints; + ss = sm + (m_numElemConstraints)*(m_numElemConstraints); + retn = vcs_basopt(TRUE, aw, sa, sm, ss, test, &conv); + if (retn != VCS_SUCCESS) { + plogf("vcs_prep_oneTime:"); + plogf(" Determination of number of components failed: %d\n", + retn); + plogf(" Global Bailout!\n"); + return retn; + } + + if (m_numElemConstraints != m_numComponents) { + m_numRxnTot = m_numRxnRdc = m_numSpeciesTot - m_numComponents; + for (i = 0; i < m_numRxnRdc; ++i) { + ir[i] = m_numComponents + i; + } + } + + /* + * The elements might need to be rearranged. + */ + awSpace.resize(m_numElemConstraints + (m_numElemConstraints + 2)*(m_numElemConstraints), 0.0); + aw = VCS_DATA_PTR(awSpace); + sa = aw + m_numElemConstraints; + sm = sa + m_numElemConstraints; + ss = sm + (m_numElemConstraints)*(m_numElemConstraints); + retn = vcs_elem_rearrange(aw, sa, sm, ss); + if (retn != VCS_SUCCESS) { + plogf("vcs_prep_oneTime:"); + plogf(" Determination of element reordering failed: %d\n", + retn); + plogf(" Global Bailout!\n"); + return retn; + } + + // If we mucked up the solution unknowns because they were all + // zero to start with, set them back to zero here + if (modifiedSoln) { + for (kspec = 0; kspec < m_numSpeciesTot; ++kspec) { + soln[kspec] = 0.0; + } + } + return VCS_SUCCESS; +} +/*****************************************************************************/ +/*****************************************************************************/ +/*****************************************************************************/ +// Prepare the object for re-solution +/* + * This routine is mostly concerned with changing the private data + * to be consistent with that needed for solution. It is called for + * every invocation of the vcs_solve() except for the cleanup invocation. + * + * Tasks: + * 1) Initialization of arrays to zero. + * 2) Calculate total number of moles in all phases + * + * return code + * VCS_SUCCESS = everything went OK + * VCS_PUB_BAD = There is an irreconcilable difference in the + * public data structure from when the problem was + * initially set up. + */ +int VCS_SOLVE::vcs_prep(void) { + /* + * Initialize various arrays in the data to zero + */ + vcs_dzero(VCS_DATA_PTR(m_gibbsSpecies), m_numSpeciesTot); + vcs_vdzero(fel, m_numSpeciesTot); + vcs_vdzero(wt, m_numSpeciesTot); + vcs_dzero(&(DnPhase[0][0]), m_numSpeciesTot*NPhase); + vcs_izero(&(PhaseParticipation[0][0]), m_numSpeciesTot*NPhase); + vcs_dzero(VCS_DATA_PTR(DelTPhMoles), NPhase); + vcs_dzero(VCS_DATA_PTR(TPhMoles1), NPhase); + /* + * Calculate the total number of moles in all phases. + */ + vcs_tmoles(); + return VCS_SUCCESS; +} +/*****************************************************************************/ +/*****************************************************************************/ +/*****************************************************************************/ + +bool VCS_SOLVE::vcs_wellPosed(VCS_PROB *vprob) + + /************************************************************************** + * + * vcs_wellPosed: + * + * In this routine, we check for things that will cause the algorithm + * to fail. + * + **************************************************************************/ +{ + double sum = 0.0; + for (int e = 0; e < vprob->ne; e++) { + sum = sum + vprob->gai[e]; + } + if (sum < 1.0E-20) { + plogf("vcs_wellPosed: Element abundance is close to zero\n"); + return false; + } + return true; +} +/*****************************************************************************/ +} diff --git a/Cantera/src/equil/vcs_prob.cpp b/Cantera/src/equil/vcs_prob.cpp new file mode 100644 index 000000000..6eafc9e8b --- /dev/null +++ b/Cantera/src/equil/vcs_prob.cpp @@ -0,0 +1,638 @@ +/** + * @file vcs_prob.cpp + * Implementation for the Interface class for the vcs thermo + * equilibrium solver package, + */ + +/* + * $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 "vcs_prob.h" +#include "vcs_VolPhase.h" +#include "vcs_species_thermo.h" +#include "vcs_internal.h" + +#ifdef CANTERA_SRC_TREE +#include "ThermoPhase.h" +#include "MolalityVPSSTP.h" +#else +#include "cantera/Cantera.h" +#include "cantera/kernel/ThermoPhase.h" +#include "cantera/kernel/MolalityVPSSTP.h" +#endif +#include +using namespace std; + +namespace VCSnonideal { + +/* + * VCS_PROB: constructor + * + * We initialize the arrays in the structure to the appropriate sizes. + * And, we initialize all of the elements of the arrays to defaults. + */ +VCS_PROB::VCS_PROB(int nsp, int nel, int nph) : + prob_type(VCS_PROBTYPE_TP), + nspecies(nsp), + NSPECIES0(0), + ne(nel), + NE0(0), + NPhase(nph), + NPHASE0(0), + T(298.15), + Pres(1.0), + Vol(0.0), + m_VCS_UnitsFormat(VCS_UNITS_UNITLESS), + /* Set the units for the chemical potential data to be + * unitless */ + iest(-1), /* The default is to not expect an initial estimate + * of the species concentrations */ + tolmaj(1.0E-8), + tolmin(1.0E-6), + m_Iterations(0), + m_NumBasisOptimizations(0), + m_printLvl(0) +#ifdef DEBUG + , + vcs_debug_print_lvl(0) +#endif +{ + NSPECIES0 = nspecies; + if (nspecies <= 0) { + plogf("number of species is zero or neg\n"); + exit(-1); + } + NE0 = ne; + if (ne <= 0) { + plogf("number of elements is zero or neg\n"); + exit(-1); + } + NPHASE0 = NPhase; + if (NPhase <= 0) { + plogf("number of phases is zero or neg\n"); + exit(-1); + } + if (nspecies < NPhase) { + plogf("number of species is less than number of phases\n"); + exit(-1); + } + + m_gibbsSpecies.resize(nspecies, 0.0); + w.resize(nspecies, 0.0); + mf.resize(nspecies, 0.0); + gai.resize(ne, 0.0); + FormulaMatrix.resize(ne, nspecies, 0.0); + SpeciesUnknownType.resize(nspecies, VCS_SPECIES_TYPE_MOLNUM); + VolPM.resize(nspecies, 0.0); + PhaseID.resize(nspecies, -1); + SpName.resize(nspecies, ""); + ElName.resize(ne, ""); + m_elType.resize(ne, VCS_ELEM_TYPE_ABSPOS); + ElActive.resize(ne, 1); + WtSpecies.resize(nspecies, 0.0); + Charge.resize(nspecies, 0.0); + SpeciesThermo.resize(nspecies,0); + for (int kspec = 0; kspec < nspecies; kspec++) { + VCS_SPECIES_THERMO *ts_tmp = new VCS_SPECIES_THERMO(0, 0); + if (ts_tmp == 0) { + plogf("Failed to init a ts struct\n"); + exit(-1); + } + SpeciesThermo[kspec] = ts_tmp; + } + VPhaseList.resize(nph, 0); + for (int iphase = 0; iphase < NPhase; iphase++) { + VPhaseList[iphase] = new vcs_VolPhase(); + } +} +/**************************************************************************/ +/**************************************************************************/ +/**************************************************************************/ +/* + * VCS_PROB_INPUT:destructor + * + * We need to manually free all of the arrays. + */ +VCS_PROB::~VCS_PROB() { + for (int i = 0; i < nspecies; i++) { + delete SpeciesThermo[i]; + SpeciesThermo[i] = 0; + } + for (int iph = 0; iph < NPhase; iph++) { + delete VPhaseList[iph]; + VPhaseList[iph] = 0; + } +} + +// Resizes all of the phase lists within the structure +/* + * Note, this doesn't change the number of phases in the problem. + * It will change NPHASE0 if nsp is greater than NPHASE0. + * + * @param nPhase size to dimension all the phase lists to + * @param force If true, this will dimension the size to be equal to nPhase + * even if nPhase is less than the current value of NPHASE0 + */ +void VCS_PROB::resizePhase(int nPhase, int force) { + if (force || nPhase > NPHASE0) { + NPHASE0 = nPhase; + } +} + +// Resizes all of the species lists within the structure +/* + * Note, this doesn't change the number of species in the problem. + * It will change NSPECIES0 if nsp is greater than NSPECIES0. + * + * @param nsp size to dimension all the species to + * @param force If true, this will dimension the size to be equal to nsp + * even if nsp is less than the current value of NSPECIES0 + */ +void VCS_PROB::resizeSpecies(int nsp, int force) { + if (force || nsp > NSPECIES0) { + m_gibbsSpecies.resize(nsp, 0.0); + w.resize(nsp, 0.0); + mf.resize(nsp, 0.0); + FormulaMatrix.resize(NE0, nsp, 0.0); + SpeciesUnknownType.resize(nsp, VCS_SPECIES_TYPE_MOLNUM); + VolPM.resize(nsp, 0.0); + PhaseID.resize(nsp, 0); + SpName.resize(nsp, ""); + WtSpecies.resize(nsp, 0.0); + Charge.resize(nsp, 0.0); + NSPECIES0 = nsp; + if (nspecies > NSPECIES0) { + nspecies = NSPECIES0; + plogf("shouldn't be here\n"); + exit(-1); + } + } +} + +// Resizes all of the element lists within the structure +/* + * Note, this doesn't change the number of element constraints + * in the problem. + * It will change NE0 if nel is greater than NE0. + * + * @param nel size to dimension all the elements lists + * @param force If true, this will dimension the size to be equal to nel + * even if nel is less than the current value of NEL0 + */ +void VCS_PROB::resizeElements(int nel, int force) { + if (force || nel > NE0) { + gai.resize(nel, 0.0); + FormulaMatrix.resize(nel, NSPECIES0, 0.0); + ElName.resize(nel, ""); + m_elType.resize(nel, VCS_ELEM_TYPE_ABSPOS); + ElActive.resize(nel, 1); + NE0 = nel; + if (ne > NE0) ne = NE0; + } +} + +// Calculate the element abundance vector +/* + * Calculates the element abundance vectors from the mole + * numbers + */ +void VCS_PROB::set_gai () +{ + int kspec, j; + double *ElemAbund = VCS_DATA_PTR(gai); + double *const *const fm = FormulaMatrix.baseDataAddr(); + vcs_dzero(ElemAbund, ne); + + for (j = 0; j < ne; j++) { + for (kspec = 0; kspec < nspecies; kspec++) { + ElemAbund[j] += fm[j][kspec] * w[kspec]; + } + } +} + +/*****************************************************************************/ +static void print_space(int num) { + for (int j = 0; j < num; j++) (void) plogf(" "); +} +/*****************************************************************************/ +static void print_char(const char letter, const int num) { + for (int i = 0; i < num; i++) plogf("%c", letter); +} + +/***************************************************************************** + * prob_report(): + * + * Print out the problem specification in all generality + * as it currently exists in the VCS_PROB object + * + */ +void VCS_PROB::prob_report(int print_lvl) { + m_printLvl = print_lvl; + int i, iphase; + vcs_VolPhase *Vphase = 0; + /* + * Printout the species information: PhaseID's and mole nums + */ + if (m_printLvl > 0) { + plogf("\n"); print_char('=', 80); plogf("\n"); + print_char('=', 20); + plogf(" VCS_PROB: PROBLEM STATEMENT "); + print_char('=', 31); plogf("\n"); + print_char('=', 80); plogf("\n"); + + plogf("\n"); + if (prob_type == 0) { + plogf("\tSolve a constant T, P problem:\n"); + plogf("\t\tT = %g K\n", T); + double pres_atm = Pres; + if (m_VCS_UnitsFormat == VCS_UNITS_MKS) { + pres_atm = Pres / 1.0133E5; + } + plogf("\t\tPres = %g atm\n", pres_atm); + } else { + plogf("\tUnknown problem type\n"); + exit(-1); + } + plogf("\n"); + plogf(" Phase IDs of species\n"); + plogf(" species phaseID phaseName "); + plogf(" Initial_Estimated_Moles Species_Type\n"); + for (i = 0; i < nspecies; i++) { + Vphase = VPhaseList[PhaseID[i]]; + plogf("%16s %5d %16s", SpName[i].c_str(), PhaseID[i], + Vphase->PhaseName.c_str()); + if (iest >= 0) plogf(" %-10.5g", w[i]); + else plogf(" N/A"); + if (SpeciesUnknownType[i] == VCS_SPECIES_TYPE_MOLNUM) { + plogf(" Mol_Num"); + } else if (SpeciesUnknownType[i] == VCS_SPECIES_TYPE_INTERFACIALVOLTAGE) { + plogf(" Voltage"); + } else { + plogf(" "); + } + plogf("\n"); + } + + /* + * Printout of the Phase structure information + */ + plogf("\n"); print_char('-', 80); plogf("\n"); + plogf(" Information about phases\n"); + plogf(" PhaseName PhaseNum SingSpec GasPhase " + " EqnState NumSpec"); + plogf(" TMolesInert Tmoles\n"); + + for (iphase = 0; iphase < NPhase; iphase++) { + Vphase = VPhaseList[iphase]; + std::string EOS_cstr = string16_EOSType(Vphase->EqnState); + plogf("%16s %5d %5d %8d ", Vphase->PhaseName.c_str(), + Vphase->VP_ID, Vphase->SingleSpecies, Vphase->GasPhase); + plogf("%16s %8d %16e ", EOS_cstr.c_str(), + Vphase->NVolSpecies, Vphase->TMolesInert); + if (iest >= 0) plogf("%16e\n", Vphase->TotalMoles()); + else plogf(" N/A\n"); + } + + plogf("\nElemental Abundances: "); + plogf(" Target_gmol ElemType ElActive\n"); + double fac = 1.0; + if (m_VCS_UnitsFormat == VCS_UNITS_MKS) { + fac = 1.0E3; + } + for (i = 0; i < ne; ++i) { + print_space(26); plogf("%-2.2s", ElName[i].c_str()); + plogf("%20.12E ", fac * gai[i]); + plogf("%3d %3d\n", m_elType[i], ElActive[i]); + } + + plogf("\nChemical Potentials: "); + if (m_VCS_UnitsFormat == VCS_UNITS_UNITLESS) { + plogf("(unitless)"); + } else if (m_VCS_UnitsFormat == VCS_UNITS_KCALMOL) { + plogf("(kcal/gmol)"); + } else if (m_VCS_UnitsFormat == VCS_UNITS_KJMOL) { + plogf("(kJ/gmol)"); + } else if (m_VCS_UnitsFormat == VCS_UNITS_KELVIN) { + plogf("(Kelvin)"); + } else if (m_VCS_UnitsFormat == VCS_UNITS_MKS) { + plogf("(J/kmol)"); + } + plogf("\n"); + plogf(" Species (phase) " + " SS0ChemPot StarChemPot\n"); + for (iphase = 0; iphase < NPhase; iphase++) { + Vphase = VPhaseList[iphase]; + Vphase->G0_calc(T); + Vphase->GStar_calc(T, Pres); + for (int kindex = 0; kindex < Vphase->NVolSpecies; kindex++) { + int kglob = Vphase->IndSpecies[kindex]; + plogf("%16s ", SpName[kglob].c_str()); + if (kindex == 0) { + plogf("%16s", Vphase->PhaseName.c_str()); + } else { + plogf(" "); + } + + plogf("%16g %16g\n", + Vphase->SS0ChemicalPotential[kindex], + Vphase->StarChemicalPotential[kindex] ); + } + } + plogf("\n"); print_char('=', 80); plogf("\n"); + print_char('=', 20); plogf(" VCS_PROB: END OF PROBLEM STATEMENT "); + print_char('=', 24); plogf("\n"); + print_char('=', 80); plogf("\n\n"); + } +} + + +// Add elements to the local element list +/* + * This routine sorts through the elements defined in the + * vcs_VolPhase object. It then adds the new elements to + * the VCS_PROB object, and creates a global map, which is + * storred in the vcs_VolPhase object. + * Id and matching of elements is done strictly via the element name, + * with case not mattering. + * + * The routine also fills in the position of the element + * in the vcs_VolPhase object's ElGlobalIndex field. + * + * @param volPhase Object containing the phase to be added. + * The elements in this phase are parsed for + * addition to the global element list + */ +void VCS_PROB::addPhaseElements(vcs_VolPhase *volPhase) { + int e, eVP; + int foundPos = -1; + int neVP = volPhase->nElemConstraints; + std::string en; + std::string enVP; + /* + * Loop through the elements in the vol phase object + */ + for (eVP = 0; eVP < neVP; eVP++) { + foundPos = -1; + enVP = volPhase->ElName[eVP]; + /* + * Search for matches with the existing elements. + * If found, then fill in the entry in the global + * mapping array. + */ + for (e = 0; e < ne; e++) { + en = ElName[e]; + if (!strcasecmp(enVP.c_str(), en.c_str())) { + volPhase->ElGlobalIndex[eVP] = e; + foundPos = e; + } + } + if (foundPos == -1) { + int elType = volPhase->m_elType[eVP]; + int elactive = volPhase->ElActive[eVP]; + e = addElement(enVP.c_str(), elType, elactive); + volPhase->ElGlobalIndex[eVP] = e; + } + } +} + + +// This routine resizes the number of elements in the VCS_PROB object by +// adding a new element to the end of the element list +/* + * The element name is added. Formula vector entries ang element + * abundances for the new element are set to zero. + * + * Returns the index number of the new element. + * + * @param elNameNew New name of the element + * @param elType Type of the element + * @param elactive boolean indicating whether the element is active + * + * @return returns the index number of the new element + */ +int VCS_PROB::addElement(const char *elNameNew, int elType, int elactive) { + if (!elNameNew) { + plogf("error: element must have a name\n"); + exit(-1); + } + int nel = ne + 1; + resizeElements(nel, 1); + ne = nel; + ElName[ne-1] = elNameNew; + m_elType[ne-1] = elType; + ElActive[ne-1] = elactive; + return (ne - 1); +} + +// This routines adds entries for the formula matrix for one species +/* + * This routines adds entries for the formula matrix for this object + * for one species + * + * This object also fills in the index filed, IndSpecies, within + * the volPhase object. + * + * @param volPhase object containing the species + * @param k Species number within the volPhase k + * @param kT global Species number within this object + * + */ +int VCS_PROB::addOnePhaseSpecies(vcs_VolPhase *volPhase, int k, int kT) { + int e, eVP; + if (kT > nspecies) { + /* + * Need to expand the number of species here + */ + plogf("Shouldn't be here\n"); + exit(-1); + } + double *const *const fm = volPhase->FormulaMatrix.baseDataAddr(); + for (eVP = 0; eVP < volPhase->nElemConstraints; eVP++) { + e = volPhase->ElGlobalIndex[eVP]; + if (e < 0) { + exit(-1); + } + FormulaMatrix[e][kT] = fm[eVP][k]; + } + /* + * Tell the phase object about the current position of the + * species within the global species vector + */ + volPhase->IndSpecies[k] = kT; + return kT; +} + +void VCS_PROB::reportCSV(const std::string &reportFile) { + int k; + int istart; + + double vol = 0.0; + string sName; + int nphase = NPhase; + + FILE * FP = fopen(reportFile.c_str(), "w"); + if (!FP) { + plogf("Failure to open file\n"); + exit(-1); + } + double Temp = T; + double pres = Pres; + + std::vector VolPM(nspecies, 0.0); + std::vector activity(nspecies, 0.0);; + std::vector ac(nspecies, 0.0);; + std::vector mu(nspecies, 0.0);; + std::vector mu0(nspecies, 0.0);; + std::vector molalities(nspecies, 0.0);; + + + vol = 0.0; + int iK = 0; + for (int iphase = 0; iphase < nphase; iphase++) { + istart = iK; + vcs_VolPhase *volP = VPhaseList[iphase]; + //const Cantera::ThermoPhase *tptr = volP->ptrThermoPhase(); + int nSpeciesPhase = volP->NVolSpecies; + VolPM.resize(nSpeciesPhase, 0.0); + volP->sendToVCSVolPM(VCS_DATA_PTR(VolPM)); + + double TMolesPhase = volP->TotalMoles(); + double VolPhaseVolumes = 0.0; + for (k = 0; k < nSpeciesPhase; k++) { + iK++; + VolPhaseVolumes += VolPM[istart + k] * mf[istart + k]; + } + VolPhaseVolumes *= TMolesPhase; + vol += VolPhaseVolumes; + } + + fprintf(FP,"--------------------- VCS_MULTIPHASE_EQUIL FINAL REPORT" + " -----------------------------\n"); + fprintf(FP,"Temperature = %11.5g kelvin\n", Temp); + fprintf(FP,"Pressure = %11.5g Pascal\n", pres); + fprintf(FP,"Total Volume = %11.5g m**3\n", vol); + fprintf(FP,"Number Basis optimizations = %d\n", m_NumBasisOptimizations); + fprintf(FP,"Number VCS iterations = %d\n", m_Iterations); + + iK = 0; + for (int iphase = 0; iphase < nphase; iphase++) { + istart = iK; + + vcs_VolPhase *volP = VPhaseList[iphase]; + const Cantera::ThermoPhase *tp = volP->ptrThermoPhase(); + string phaseName = volP->PhaseName; + int nSpeciesPhase = volP->NVolSpecies; + volP->sendToVCSVolPM(VCS_DATA_PTR(VolPM)); + double TMolesPhase = volP->TotalMoles(); + //AssertTrace(TMolesPhase == m_mix->phaseMoles(iphase)); + activity.resize(nSpeciesPhase, 0.0); + ac.resize(nSpeciesPhase, 0.0); + + mu0.resize(nSpeciesPhase, 0.0); + mu.resize(nSpeciesPhase, 0.0); + VolPM.resize(nSpeciesPhase, 0.0); + molalities.resize(nSpeciesPhase, 0.0); + + int actConvention = tp->activityConvention(); + tp->getActivities(VCS_DATA_PTR(activity)); + tp->getActivityCoefficients(VCS_DATA_PTR(ac)); + tp->getStandardChemPotentials(VCS_DATA_PTR(mu0)); + + tp->getPartialMolarVolumes(VCS_DATA_PTR(VolPM)); + tp->getChemPotentials(VCS_DATA_PTR(mu)); + double VolPhaseVolumes = 0.0; + for (k = 0; k < nSpeciesPhase; k++) { + VolPhaseVolumes += VolPM[k] * mf[istart + k]; + } + VolPhaseVolumes *= TMolesPhase; + vol += VolPhaseVolumes; + + + if (actConvention == 1) { + const Cantera::MolalityVPSSTP *mTP = static_cast(tp); + tp->getChemPotentials(VCS_DATA_PTR(mu)); + mTP->getMolalities(VCS_DATA_PTR(molalities)); + tp->getChemPotentials(VCS_DATA_PTR(mu)); + + if (iphase == 0) { + fprintf(FP," Name, Phase, PhaseMoles, Mole_Fract, " + "Molalities, ActCoeff, Activity," + "ChemPot_SS0, ChemPot, mole_num, PMVol, Phase_Volume\n"); + + fprintf(FP," , , (kmol), , " + " , , ," + " (J/kmol), (J/kmol), (kmol), (m**3/kmol), (m**3)\n"); + } + for (k = 0; k < nSpeciesPhase; k++) { + sName = tp->speciesName(k); + fprintf(FP,"%12s, %11s, %11.3e, %11.3e, %11.3e, %11.3e, %11.3e," + "%11.3e, %11.3e, %11.3e, %11.3e, %11.3e\n", + sName.c_str(), + phaseName.c_str(), TMolesPhase, + mf[istart + k], molalities[k], ac[k], activity[k], + mu0[k]*1.0E-6, mu[k]*1.0E-6, + mf[istart + k] * TMolesPhase, + VolPM[k], VolPhaseVolumes ); + } + + } else { + if (iphase == 0) { + fprintf(FP," Name, Phase, PhaseMoles, Mole_Fract, " + "Molalities, ActCoeff, Activity," + " ChemPotSS0, ChemPot, mole_num, PMVol, Phase_Volume\n"); + + fprintf(FP," , , (kmol), , " + " , , ," + " (J/kmol), (J/kmol), (kmol), (m**3/kmol), (m**3)\n"); + } + for (k = 0; k < nSpeciesPhase; k++) { + molalities[k] = 0.0; + } + for (k = 0; k < nSpeciesPhase; k++) { + sName = tp->speciesName(k); + fprintf(FP,"%12s, %11s, %11.3e, %11.3e, %11.3e, %11.3e, %11.3e, " + "%11.3e, %11.3e,% 11.3e, %11.3e, %11.3e\n", + sName.c_str(), + phaseName.c_str(), TMolesPhase, + mf[istart + k], molalities[k], ac[k], + activity[k], mu0[k]*1.0E-6, mu[k]*1.0E-6, + mf[istart + k] * TMolesPhase, + VolPM[k], VolPhaseVolumes ); + } + } + +#ifdef DEBUG + /* + * Check consistency: These should be equal + */ + tp->getChemPotentials(VCS_DATA_PTR(m_gibbsSpecies)+istart); + for (k = 0; k < nSpeciesPhase; k++) { + if (!vcs_doubleEqual(m_gibbsSpecies[istart+k], mu[k])) { + fprintf(FP,"ERROR: incompatibility!\n"); + fclose(FP); + plogf("ERROR: incompatibility!\n"); + exit(-1); + } + } +#endif + iK += nSpeciesPhase; + } + fclose(FP); +} + + +#ifdef DEBUG +void VCS_PROB::setDebugPrintLvl(int lvl) { + vcs_debug_print_lvl = lvl; +} + +#endif + +} diff --git a/Cantera/src/equil/vcs_prob.h b/Cantera/src/equil/vcs_prob.h new file mode 100644 index 000000000..3a9bf419b --- /dev/null +++ b/Cantera/src/equil/vcs_prob.h @@ -0,0 +1,375 @@ +/** + * @file vcs_prob.h + * Header for the Interface class for the vcs thermo equilibrium solver package, + */ + +/* + * $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. + */ + +#ifndef _VCS_PROB_H +#define _VCS_PROB_H + +#include "vcs_DoubleStarStar.h" +#include "vcs_IntStarStar.h" +#include "vcs_defs.h" +#include +#include + +namespace VCSnonideal { + + class vcs_VolPhase; + class VCS_SPECIES_THERMO; + + //! Interface class for the vcs thermo equilibrium solver package, + //! which generally describes the problem to be solved. + /*! + * HKM add: + * HaveEstimate -> 0 no estimate, or estimate that doesn' satisfy elem + * abundances + * 1 have an estimate that satisfies elem_abund. + * 2 Have an estimate that minimizes a subproblem + * and satisfies elem abund. + * solnFound -> True, soln to current problem found and included here + * False, soln has not been found. + */ + class VCS_PROB { + public: + + //! Problem type. I.e., the identity of what is held constant. + /*! + * Currently, T and P are held constant, and this input + * is ignored + */ + int prob_type; + + //! Total number of species in the problems + int nspecies; + + //! Species number used to malloc data structures + int NSPECIES0; + + //! Number of element contraints in the equilibrium problem + int ne; + + //! Number of element constrints used to malloc data structures + //! involving elements + int NE0; + + //! Number of phases in the problem + int NPhase; + + //! Number of phases used to malloc data structures + int NPHASE0; + + //! Vector of chemical potentials of the species + /*! + * This is a calculated output quantity + * length = number of species + * units = m_VCS_UnitsFormat; + */ + std::vector m_gibbsSpecies; + + //! Total number of moles of the kth species. + /*! + * This is both an input and an output variable. + * On input, this is an estimate of the mole numbers. + * The actual element abundance vector contains the problem specification. + * + * On output, this contains the solution for the total number of moles + * of the kth species. + * + * units = m_VCS_UnitsFormat + */ + std::vector w; + + //! Mole fraction vector + /*! + * This is a calculated vector, calculated from w[] + * length number of species. + * -> Take out? -> No, useful for storage of a quantity often needed + */ + std::vector mf; + + //! Element abundances for jth element + /*! + * This is input from the input file and is considered a constant from + * thereon within the vcs_solve_TP(). + * units = m_VCS_UnitsFormat + */ + std::vector gai; + + //! Formula Matrix for the problem + /*! + * FormulaMatrix[j][kspec] = Number of elements, j, in the kspec + * species + */ + DoubleStarStar FormulaMatrix; + + //! Specifies the species unknown type + /*! + * There are two types. One is the straightforward + * species, with the mole number w[k], as the + * unknown. The second is the an interfacial + * voltage where w[k] refers to the interfacial + * voltage in volts. + * These species types correspond to metalic + * electrons corresponding to electrodes. + * The voltage and other interfacial conditions + * sets up an interfacial current, which is + * set to zero in this initial treatment. + * Later we may have non-zero interfacial currents. + */ + std::vector SpeciesUnknownType; + + //! Temperature (Kelvin) + /*! + * Specification of the temperature for the equilibrium problem + */ + double T; + + //! Pressure + /*! + * units given by m_VCS_UnitsFormat + */ + double Pres; + + //! Volume of the entire system + /*! + * units given by m_VCS_UnitsFormat + * Note, this is an output variable atm + */ + double Vol; + + //! Partial Molar Volumes of species + /*! + * This is a calculated vector, calculated from w[] + * length number of species. + * -> Take out? -> No, useful for storage of a quantity often needed + */ + std::vector VolPM; + + //! Units for the chemical potential data, pressure data, volume, + //! and species amounts + /*! + * All internally storred quantities will have these units. Also, printed + * quantitities will display in these units. + * + * Chem_Pot Pres vol moles + * ---------------------------------------------------------------------- + * -1 VCS_UNITS_KCALMOL = kcal/mol atm cm**3 gmol + * 0 VCS_UNITS_UNITLESS = MU / RT -> no units atm cm**3 gmol + * 1 VCS_UNITS_KJMOL = kJ / mol atm cm**3 gmol + * 2 VCS_UNITS_KELVIN = KELVIN -> MU / R atm cm**3 gmol + * 3 VCS_UNITS_MKS = Joules / Kmol (Cantera) Pa m**3 kmol + * ---------------------------------------------------------------------- + * + * see vcs_defs.h for more information + */ + int m_VCS_UnitsFormat; + + //! Specification of the initial estimate method + /*! + * iest = Initial estimate: 0 user estimate + * -1 machine estimate + */ + int iest; + + //! Tolerance requirement for major species + double tolmaj; + + //! Tolerance requirement for minor species + double tolmin; + + //! Mapping between the species and the phases + std::vector PhaseID; + + //! Vector of strings containing the species names + std::vector SpName; + + //! vector of strings containing the element names + std::vector ElName; + + //! vector of Element types + std::vector m_elType; + + //! Specifies whether an element constraint is active + /*! + * The default is true + * Length = nelements + */ + std::vector ElActive; + + //! Molecular weight of species + /*! + * WtSpecies[k] = molecular weight of species in gm/mol + */ + std::vector WtSpecies; + + //! Charge of each species + std::vector Charge; + + //! Array of phase structures + std::vector VPhaseList; + + // String containing the title of the run + std::string Title; + + //! Vector of pointers to thermo structures which identify the model + //! and parameters for evaluating the thermodynamic + //! functions for that particular species + std::vector SpeciesThermo; + + //! Number of iterations + /*! + * This is an output variable + */ + int m_Iterations; + + //! Number of basis optimizations used + /*! + * This is an output variable + */ + int m_NumBasisOptimizations; + + //! Print level for print routines + int m_printLvl; +#ifdef DEBUG + //! Debug print lvl + int vcs_debug_print_lvl; +#endif + + //! Constructor + /*! + * This constructor initializes the sizes within the object + * to parameter values. + * + * @param nsp number of species + * @param nel number of elements + * @param nph number of phases + */ + VCS_PROB(int nsp, int nel, int nph); + + //! Destructor + ~VCS_PROB(); + + //! Resizes all of the phase lists within the structure + /*! + * Note, this doesn't change the number of phases in the problem. + * It will change NPHASE0 if nsp is greater than NPHASE0. + * + * @param nPhase size to dimension all the phase lists to + * @param force If true, this will dimension the size to be equal to nPhase + * even if nPhase is less than the current value of NPHASE0 + */ + void resizePhase(int nPhase, int force); + + //! Resizes all of the species lists within the structure + /*! + * Note, this doesn't change the number of species in the problem. + * It will change NSPECIES0 if nsp is greater than NSPECIES0. + * + * @param nsp size to dimension all the species lists to + * @param force If true, this will dimension the size to be equal to nsp + * even if nsp is less than the current value of NSPECIES0 + */ + void resizeSpecies(int nsp, int force); + + //! Resizes all of the element lists within the structure + /*! + * Note, this doesn't change the number of element constraints in the problem. + * It will change NE0 if nel is greater than NE0. + * + * @param nel size to dimension all the elements lists + * @param force If true, this will dimension the size to be equal to nel + * even if nel is less than the current value of NEL0 + */ + void resizeElements(int nel, int force); + + + //! Calculate the element abundance vector + /*! + * Calculates the element abundance vectors from the mole + * numbers + */ + void set_gai(); + + //! Print out the problem specification in all generality + //! as it currently exists in the VCS_PROB object + /*! + * @param print_lvl Parameter lvl for printing + * 0 - no printing + * 1 - all printing + */ + void prob_report(int print_lvl); + + //! Add elements to the local element list + /*! + * This routine sorts through the elements defined in the + * vcs_VolPhase object. It then adds the new elements to + * the VCS_PROB object, and creates a global map, which is + * storred in the vcs_VolPhase object. + * Id and matching of elements is done strictly via the element name, + * with case not mattering. + * + * The routine also fills in the position of the element + * in the vcs_VolPhase object's ElGlobalIndex field. + * + * @param volPhase Object containing the phase to be added. + * The elements in this phase are parsed for + * addition to the global element list + */ + void addPhaseElements(vcs_VolPhase *volPhase); + + + //! This routine resizes the number of elements in the VCS_PROB object by + //! adding a new element to the end of the element list + /*! + * The element name is added. Formula vector entries ang element + * abundances for the new element are set to zero. + * + * Returns the index number of the new element. + * + * @param elNameNew New name of the element + * @param elType Type of the element + * @param elactive boolean indicating whether the element is active + * + * @return returns the index number of the new element + */ + int addElement(const char *elNameNew, int elType, int elactive); + + + //! This routines adds entries for the formula matrix for one species + /*! + * This routines adds entries for the formula matrix for this object + * for one species + * + * This object also fills in the index filed, IndSpecies, within + * the volPhase object. + * + * @param volPhase object containing the species + * @param k Species number within the volPhase k + * @param kT global Species number within this object + * + */ + int addOnePhaseSpecies(vcs_VolPhase *volPhase, int k, int kT); + + void reportCSV(const std::string &reportFile); + +#ifdef DEBUG + //! Set the debug level + /*! + * @param vcs_debug_print_lvl input debug level + */ + void setDebugPrintLvl(int vcs_debug_print_lvl); +#endif + }; + +} + +#endif diff --git a/Cantera/src/equil/vcs_rearrange.cpp b/Cantera/src/equil/vcs_rearrange.cpp new file mode 100644 index 000000000..ab56cab7e --- /dev/null +++ b/Cantera/src/equil/vcs_rearrange.cpp @@ -0,0 +1,58 @@ +/* ======================================================================= */ +/* -------------------------------------------------- */ +/* | RCS Head Information on zuzax.pchem.sandia.gov | */ +/* -------------------------------------------------- */ +/* $RCSfile$ */ +/* $Author$ */ +/* $Date$ */ +/* $Revision$ */ +/* ======================================================================= */ +#include +#include +#include + +#include "vcs_solve.h" +#include "vcs_internal.h" + +namespace VCSnonideal { + +/*****************************************************************************/ +/*****************************************************************************/ +/*****************************************************************************/ + +int VCS_SOLVE::vcs_rearrange(void) + /************************************************************************** + * + * vcs_rearrange: + * + * Switch all species data back to the original order. This destroys + * the data based on reaction ordering. + **************************************************************************/ +{ + int i, l, j, k1; + /* ********************************************************* */ + /* **** RE-ARRANGE INPUT DATA ****************************** */ + /* ********************************************************* */ + /* - Loop over all of the species */ + for (i = 0; i < m_numSpeciesTot; ++i) { + /* + * Find the index of I in the index vector IND. + * Call it K1 and continue. + */ + for (j = 0; j < m_numSpeciesTot; ++j) { + l = ind[j]; + k1 = j; + if (l == i) break; + } + /* + * - Switch the species data back from K1 into I + * -> because we loop over all species, reaction data + * are now permanently hosed. + */ + vcs_switch_pos(FALSE, i, k1); + } + return 0; +} /* vcs_rearrange() *********************************************************/ + +} + diff --git a/Cantera/src/equil/vcs_report.cpp b/Cantera/src/equil/vcs_report.cpp new file mode 100644 index 000000000..555b06bad --- /dev/null +++ b/Cantera/src/equil/vcs_report.cpp @@ -0,0 +1,400 @@ +/* + * $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" + +namespace VCSnonideal { + +/*****************************************************************************/ +static void print_space(int num) +{ + for (int j = 0; j < num; j++) { + plogf(" "); + } +} + +static void print_line(char *schar, int num) { + for (int j = 0; j < num; j++) plogf("%s", schar); + plogf("\n"); +} + +/*****************************************************************************/ +/*****************************************************************************/ +/*****************************************************************************/ + +int VCS_SOLVE::vcs_report(int iconv) + + /************************************************************************** + * + * vcs_report: + * + * Print out a report on the state of the equilibrium problem to + * standard output. + * This prints out the current contents of the VCS_SOLVE class, V. + ***************************************************************************/ +{ + int i, j, l, k, inertYes = FALSE, kspec; + int nspecies = m_numSpeciesTot; + double g; + + char originalUnitsState = UnitsState; + + + std::vector sortindex(nspecies,0); + std::vector xy(nspecies,0.0); + + /* ************************************************************** */ + /* **** SORT DEPENDENT SPECIES IN DECREASING ORDER OF MOLES ***** */ + /* ************************************************************** */ + + for (i = 0; i < nspecies; ++i) { + sortindex[i] = i; + xy[i] = soln[i]; + } + /* + * Sort the XY vector, the mole fraction vector, + * and the sort index vector, sortindex, according to + * the magnitude of the mole fraction vector. + */ + for (l = m_numComponents; l < m_numSpeciesRdc; ++l) { + k = vcs_amax(VCS_DATA_PTR(xy), l, m_numSpeciesRdc); + if (k != l) { + vcsUtil_dsw(VCS_DATA_PTR(xy), k, l); + vcsUtil_isw(VCS_DATA_PTR(sortindex), k, l); + } + } + + /* + * Decide whether we have to nondimensionalize the equations. + * -> For the printouts from this routine, we will use nondimensional + * representations. This may be expanded in the future. + */ + if (UnitsState == VCS_DIMENSIONAL_G) { + vcs_nondim_TP(); + } + + /* ******************************************************** */ + /* *** PRINT OUT RESULTS ********************************** */ + /* ******************************************************** */ + + plogf("\n\n\n\n"); + print_line("-", 80); + print_line("-", 80); + plogf("\t\t VCS_TP REPORT\n"); + print_line("-", 80); + print_line("-", 80); + if (iconv < 0) { + plogf(" ERROR: CONVERGENCE CRITERION NOT SATISFIED.\n"); + } else if (iconv == 1) { + plogf(" RANGE SPACE ERROR: Equilibrium Found but not all Element Abundances are Satisfied\n"); + } + /* + * Calculate some quantities that may need updating + */ + vcs_tmoles(); + Vol = vcs_VolTotal(T, Pres, VCS_DATA_PTR(soln), VCS_DATA_PTR(VolPM)); + + plogf("\t\tTemperature = %15.2g Kelvin\n", T); + plogf("\t\tPressure = %15.5g Atmos\n", Pres); + plogf("\t\tVolume = %15.5g cm**3\n", Vol); + + /* + * -------- TABLE OF SPECIES IN DECREASING MOLE NUMBERS -------------- + */ + plogf("\n\n"); + print_line("-", 80); + plogf(" Species Equilibrium moles "); + plogf("Mole Fraction ChemPot/RT SpecUnkType\n"); + print_line("-", 80); + for (i = 0; i < m_numComponents; ++i) { + plogf(" %-12.12s", SpName[i].c_str()); + print_space(13); + plogf("%14.7E %14.7E %12.4E", soln[i], wt[i], m_gibbsSpecies[i]); + plogf(" %3d", SpeciesUnknownType[i]); + plogf("\n"); + } + for (i = m_numComponents; i < m_numSpeciesRdc; ++i) { + l = sortindex[i]; + plogf(" %-12.12s", SpName[l].c_str()); + print_space(13); + + if (SpeciesUnknownType[l] == VCS_SPECIES_TYPE_MOLNUM) { + plogf("%14.7E %14.7E %12.4E", soln[l], wt[l], m_gibbsSpecies[l]); + plogf(" MolNum "); + } else if (SpeciesUnknownType[l] == VCS_SPECIES_TYPE_INTERFACIALVOLTAGE) { + plogf(" NA %14.7E %12.4E", 1.0, m_gibbsSpecies[l]); + plogf(" Voltage = %14.7E", soln[l]); + } else { + plogf("we have a problem\n"); + exit(-1); + } + plogf("\n"); + } + for (i = 0; i < NPhase; i++) { + if (TPhInertMoles[i] > 0.0) { + inertYes = TRUE; + if (i == 0) { + plogf(" Inert Gas Species "); + } else { + plogf(" Inert Species in phase %16s ", + (VPhaseList[i])->PhaseName.c_str()); + } + plogf("%14.7E %14.7E %12.4E\n", TPhInertMoles[i], + TPhInertMoles[i] / TPhMoles[i], 0.0); + } + } + if (m_numSpeciesRdc != nspecies) { + plogf("\n SPECIES WITH LESS THAN 1.0E-32 MOLES:\n\n"); + for (kspec = m_numSpeciesRdc; kspec < nspecies; ++kspec) { + plogf(" %-12.12s", SpName[kspec].c_str()); + plogf(" %14.7E %14.7E %12.4E", + soln[kspec], wt[kspec], dg[kspec]); + 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"); + } + } + print_line("-", 80); + plogf("\n"); + + /* + * ---------- TABLE OF SPECIES FORMATION REACTIONS ------------------ + */ + plogf("\n"); + print_line("-", m_numComponents*10 + 45); + plogf(" |ComponentID|"); + for (j = 0; j < m_numComponents; j++) { + plogf(" %3d", j); + } + plogf(" | |\n"); + plogf(" | Components|"); + for (j = 0; j < m_numComponents; j++) { + plogf(" %10.10s", SpName[j].c_str()); + } + plogf(" | |\n"); + plogf(" NonComponent | Moles |"); + for (j = 0; j < m_numComponents; j++) { + plogf(" %10.3g", soln[j]); + } + plogf(" | DG/RT Rxn |\n"); + print_line("-", m_numComponents*10 + 45); + for (i = 0; i < m_numRxnTot; i++) { + int irxn = ir[i]; + plogf(" %3d ", irxn); + plogf("%-10.10s", SpName[irxn].c_str()); + plogf("|%10.3g |", soln[irxn]); + for (j = 0; j < m_numComponents; j++) { + plogf(" %6.2f", sc[i][j]); + } + plogf(" |%10.3g |", dg[irxn]); + plogf("\n"); + } + print_line("-", m_numComponents*10 + 45); + plogf("\n"); + + /* + * ------------------ TABLE OF PHASE INFORMATION --------------------- + */ + std::vector gaPhase(m_numElemConstraints, 0.0); + std::vector gaTPhase(m_numElemConstraints, 0.0); + double totalMoles = 0.0; + double gibbsPhase = 0.0; + double gibbsTotal = 0.0; + plogf("\n\n"); + plogf("\n"); + print_line("-", m_numElemConstraints*10 + 58); + plogf(" | ElementID |"); + for (j = 0; j < m_numElemConstraints; j++) { + plogf(" %3d", j); + } + plogf(" | |\n"); + plogf(" | Element |"); + for (j = 0; j < m_numElemConstraints; j++) { + plogf(" %10.10s", (ElName[j]).c_str()); + } + plogf(" | |\n"); + plogf(" PhaseName | MolTarget |"); + for (j = 0; j < m_numElemConstraints; j++) { + plogf(" %10.3g", gai[j]); + } + plogf(" | Gibbs Total |\n"); + print_line("-", m_numElemConstraints*10 + 58); + for (int iphase = 0; iphase < NPhase; iphase++) { + plogf(" %3d ", iphase); + vcs_VolPhase *VPhase = VPhaseList[iphase]; + plogf("%-12.12s |",VPhase->PhaseName.c_str()); + plogf("%10.3e |", TPhMoles[iphase]); + totalMoles += TPhMoles[iphase]; + if (TPhMoles[iphase] != VPhase->TotalMoles()) { + if (! vcs_doubleEqual(TPhMoles[iphase], VPhase->TotalMoles())) { + plogf("We have a problem\n"); + exit(-1); + } + } + vcs_elabPhase(iphase, VCS_DATA_PTR(gaPhase)); + for (j = 0; j < m_numElemConstraints; j++) { + plogf(" %10.3g", gaPhase[j]); + gaTPhase[j] += gaPhase[j]; + } + gibbsPhase = vcs_GibbsPhase(iphase, VCS_DATA_PTR(soln), + VCS_DATA_PTR(m_gibbsSpecies)); + gibbsTotal += gibbsPhase; + plogf(" | %18.11E |\n", gibbsPhase); + } + print_line("-", m_numElemConstraints*10 + 58); + plogf(" TOTAL |%10.3e |", totalMoles); + for (j = 0; j < m_numElemConstraints; j++) { + plogf(" %10.3g", gaTPhase[j]); + } + plogf(" | %18.11E |\n", gibbsTotal); + + print_line("-", m_numElemConstraints*10 + 58); + plogf("\n"); + + /* + * ----------- GLOBAL SATISFACTION INFORMATION ----------------------- + */ + + /* + * Calculate the total dimensionless Gibbs Free Energy + * -> Inert species are handled as if they had a standard free + * energy of zero + */ + + g = vcs_Total_Gibbs(VCS_DATA_PTR(soln), VCS_DATA_PTR(m_gibbsSpecies), + VCS_DATA_PTR(TPhMoles)); + plogf("\n\tTotal Dimensionless Gibbs Free Energy = G/RT = %15.7E\n", g); + if (inertYes) + plogf("\t\t(Inert species have standard free energy of zero)\n"); + + plogf("\nElemental Abundances: "); + plogf(" Actual Target Type ElActive\n"); + for (i = 0; i < m_numElemConstraints; ++i) { + print_space(26); plogf("%-2.2s", (ElName[i]).c_str()); + plogf("%20.12E %20.12E", ga[i], gai[i]); + plogf(" %3d %3d\n", m_elType[i], ElActive[i]); + } + plogf("\n"); + + /* + * ------------------ TABLE OF SPECIES CHEM POTS --------------------- + */ + plogf("\n"); print_line("-", 93); + plogf("Chemical Potentials of the Species: (dimensionless)\n"); + + double rt = vcs_nondimMult_TP(m_VCS_UnitsFormat, T); + plogf("\t\t(RT = %g ", rt); + vcs_printChemPotUnits(m_VCS_UnitsFormat); + plogf(")\n"); + plogf(" Name TMoles StandStateChemPot " + " ln(AC) ln(X_i) | F z_i phi | ChemPot | (-lnMnaught)\n"); + print_line("-", 115); + for (i = 0; i < nspecies; ++i) { + l = sortindex[i]; + int pid = PhaseID[l]; + plogf(" %-12.12s", SpName[l].c_str()); + plogf(" %14.7E ", soln[l]); + plogf("%14.7E ", ff[l]); + plogf("%14.7E ", log(ActCoeff[l])); + double tpmoles = TPhMoles[pid]; + double phi = phasePhi[pid]; + double eContrib = phi * Charge[l] * Faraday_dim; + double lx = 0.0; + if (SpeciesUnknownType[l] == VCS_SPECIES_TYPE_INTERFACIALVOLTAGE) { + lx = 0.0; + } else { + if (tpmoles > 0.0 && soln[l] > 0.0) { + lx = log(soln[l]) - log(tpmoles); + } else { + lx = m_gibbsSpecies[l] - ff[l] - log(ActCoeff[l]) + SpecLnMnaught[l]; + } + } + plogf("%14.7E |", lx); + plogf("%14.7E | ", eContrib); + double tmp = ff[l] + log(ActCoeff[l]) + lx - SpecLnMnaught[l] + eContrib; + if (fabs(m_gibbsSpecies[l] - tmp) > 1.0E-8) { + plogf("\n\t\twe have a problem - doesn't add up\n"); + exit(-1); + } + plogf(" %12.4E |", m_gibbsSpecies[l]); + if (SpecLnMnaught[l] != 0.0) { + plogf(" (%14.7E)", - SpecLnMnaught[l]); + } + plogf("\n"); + } + print_line("-", 115); + + /* + * ------------- TABLE OF SOLUTION COUNTERS -------------------------- + */ + plogf("\n"); + plogf("\nCounters: Iterations Time (seconds)\n"); + plogf(" vcs_basopt: %5d %11.5E\n", + m_VCount->Basis_Opts, m_VCount->Time_basopt); + plogf(" vcs_TP: %5d %11.5E\n", + m_VCount->Its, m_VCount->Time_vcs_TP); + + print_line("-", 80); + print_line("-", 80); + + /* + * Set the Units state of the system back to where it was when we + * entered the program. + */ + if (originalUnitsState != UnitsState) { + if (originalUnitsState == VCS_DIMENSIONAL_G ) vcs_redim_TP(); + else vcs_nondim_TP(); + } + /* + * Return a successful completion flag + */ + return VCS_SUCCESS; +} /* vcs_report() ************************************************************/ +/*****************************************************************************/ +/*****************************************************************************/ +/*****************************************************************************/ + +void VCS_SOLVE::vcs_TCounters_report(void) + + /************************************************************************** + * + * vcs_TCounters_report: + * + * Print out the total Its and time counters to standard output + ***************************************************************************/ +{ + plogf("\nTCounters: Num_Calls Total_Its Total_Time (seconds)\n"); + plogf(" vcs_basopt: %5d %5d %11.5E\n", + m_VCount->T_Basis_Opts, m_VCount->T_Basis_Opts, m_VCount->T_Time_basopt); + plogf(" vcs_TP: %5d %5d %11.5E\n", + m_VCount->T_Calls_vcs_TP, m_VCount->T_Its, m_VCount->T_Time_vcs_TP); + plogf(" vcs_inest: %5d %11.5E\n", + m_VCount->T_Calls_Inest, m_VCount->T_Time_inest); + plogf(" vcs_TotalTime: %11.5E\n", + m_VCount->T_Time_vcs); +} + +/*****************************************************************************/ +/*****************************************************************************/ +/*****************************************************************************/ +} + diff --git a/Cantera/src/equil/vcs_root1d.cpp b/Cantera/src/equil/vcs_root1d.cpp new file mode 100644 index 000000000..f3ca72d0a --- /dev/null +++ b/Cantera/src/equil/vcs_root1d.cpp @@ -0,0 +1,349 @@ +/* ======================================================================= */ +/* -------------------------------------------------- */ +/* | RCS Head Information on zuzax.pchem.sandia.gov | */ +/* -------------------------------------------------- */ +/* $RCSfile$ */ +/* $Author$ */ +/* $Date$ */ +/* $Revision$ */ +/* ======================================================================= */ +#include +#include +#include + +#include "vcs_internal.h" + +namespace VCSnonideal { + +#define TOL_CONV 1.0E-5 +/*****************************************************************************/ +/*****************************************************************************/ +/*****************************************************************************/ +#ifdef DEBUG_ROOT1D +static void print_funcEval(FILE *fp, double xval, double fval, int its) + +{ + fprintf(fp,"\n"); + fprintf(fp,"...............................................................\n"); + fprintf(fp,".................. vcs_root1d Function Evaluation .............\n"); + fprintf(fp,".................. iteration = %5d ........................\n", its); + fprintf(fp,".................. value = %12.5g ......................\n", xval); + fprintf(fp,".................. funct = %12.5g ......................\n", fval); + fprintf(fp,"...............................................................\n"); + fprintf(fp,"\n"); +} +#endif +/*****************************************************************************/ +/*****************************************************************************/ +/*****************************************************************************/ + +int vcsUtil_root1d(double xmin, double xmax, int itmax, + VCS_FUNC_PTR func, void *fptrPassthrough, + double FuncTargVal, int varID, + double *xbest) + + /************************************************************************** + * + * vcs_root1d: + * + * Driver for solving a 1D function. + ***************************************************************************/ +{ + static int callNum = 0; + const char *stre = "vcs_root1d ERROR: "; + const char *strw = "vcs_root1d WARNING: "; + int converged = FALSE, err = FALSE; +#ifdef DEBUG_ROOT1D + char fileName[80]; + FILE *fp; +#endif + double x1, x2, xnew, f1, f2, fnew, slope; + int its = 0, posStraddle, retn = VCS_SUCCESS; + int foundPosF = FALSE; + int foundNegF = FALSE; + int foundStraddle = FALSE; + double xPosF, xNegF; + double fnorm; /* A valid norm for the making the function value + * dimensionless */ + double c[9], f[3], xn1, xn2, x0 = 0.0, f0 = 0.0, root, theta, xquad; + + callNum++; +#ifdef DEBUG_ROOT1D + sprintf(fileName, "rootfd_%d.log", callNum); + fp = fopen(fileName, "w"); + fprintf(fp, " Iter TP_its xval Func_val | Reasoning\n"); + fprintf(fp, "-----------------------------------------------------" + "-------------------------------\n"); +#endif + if (xmax <= xmin) { + plogf("%sxmin and xmax are bad: %g %g\n", stre, xmin, xmax); + return VCS_PUB_BAD; + } + x1 = *xbest; + if (x1 < xmin || x1 > xmax) { + x1 = (xmin + xmax) / 2.0; + } + f1 = func(x1, FuncTargVal, varID, fptrPassthrough, &err); +#ifdef DEBUG_ROOT1D + print_funcEval(x1, f1, its); + + fprintf(fp, "%-5d %-5d %-15.5E %-15.5E\n", -2, VCount->Its, x1, f1); +#endif + if (f1 == 0.0) { + *xbest = x1; + return VCS_SUCCESS; + } + else if (f1 > 0.0) { + foundPosF = TRUE; + xPosF = x1; + } else { + foundNegF = TRUE; + xNegF = x1; + } + x2 = x1 * 1.1; + if (x2 > xmax) x2 = x1 - (xmax - xmin) / 100.; + f2 = func(x2, FuncTargVal, varID, fptrPassthrough, &err); +#ifdef DEBUG_ROOT1D + print_funcEval(x2, f2, its); + fprintf(fp, "%-5d %-5d %-15.5E %-15.5E", -1, VCount->Its, x2, f2); +#endif + + if (FuncTargVal != 0.0) { + fnorm = fabs(FuncTargVal) + 1.0E-13; + } else { + fnorm = 0.5*(fabs(f1) + fabs(f2)) + fabs(FuncTargVal); + } + + if (f2 == 0.0) + return retn; + else if (f2 > 0.0) { + if (!foundPosF) { + foundPosF = TRUE; + xPosF = x2; + } + } else { + if (!foundNegF) { + foundNegF = TRUE; + xNegF = x2; + } + } + foundStraddle = foundPosF && foundNegF; + if (foundStraddle) { + if (xPosF > xNegF) posStraddle = TRUE; + else posStraddle = FALSE; + } + + do { + /* + * Find an estimate of the next point to try based on + * a linear approximation. + */ + slope = (f2 - f1) / (x2 - x1); + if (slope == 0.0) { + plogf("%s functions evals produced the same result, %g, at %g and %g\n", + strw, f2, x1, x2); + xnew = 2*x2 - x1 + 1.0E-3; + } else { + xnew = x2 - f2 / slope; + } +#ifdef DEBUG_ROOT1D + fprintf(fp, " | xlin = %-9.4g", xnew); +#endif + + /* + * Do a quadratic fit -> Note this algorithm seems + * to work OK. The quadratic approximation doesn't kick in until + * the end of the run, when it becomes reliable. + */ + if (its > 0) { + c[0] = 1.; c[1] = 1.; c[2] = 1.; + c[3] = x0; c[4] = x1; c[5] = x2; + c[6] = SQUARE(x0); c[7] = SQUARE(x1); c[8] = SQUARE(x2); + f[0] = - f0; f[1] = - f1; f[2] = - f2; + retn = vcsUtil_mlequ(c, 3, 3, f, 1); + if (retn == 1) goto QUAD_BAIL; + root = f[1]* f[1] - 4.0 * f[0] * f[2]; + if (root >= 0.0) { + xn1 = (- f[1] + sqrt(root)) / (2.0 * f[2]); + xn2 = (- f[1] - sqrt(root)) / (2.0 * f[2]); + if (fabs(xn2 - x2) < fabs(xn1 - x2) && xn2 > 0.0 ) xquad = xn2; + else xquad = xn1; + theta = fabs(xquad - xnew) / fabs(xnew - x2); + theta = MIN(1.0, theta); + xnew = theta * xnew + (1.0 - theta) * xquad; +#ifdef DEBUG_ROOT1D + if (theta != 1.0) { + fprintf(fp, " | xquad = %-9.4g", xnew); + } +#endif + } else { + /* + * Pick out situations where the convergence may be + * accelerated. + */ + if ((DSIGN(xnew - x2) == DSIGN(x2 - x1)) && + (DSIGN(x2 - x1) == DSIGN(x1 - x0)) ) { + xnew += xnew - x2; +#ifdef DEBUG_ROOT1D + fprintf(fp, " | xquada = %-9.4g", xnew); +#endif + } + } + } + QUAD_BAIL: ; + + + /* + * + * Put heuristic bounds on the step jump + */ + if ( (xnew > x1 && xnew < x2) || (xnew < x1 && xnew > x2)) { + /* + * + * If we are doing a jump inbetween two points, make sure + * the new trial is between 10% and 90% of the distance + * between the old points. + */ + slope = fabs(x2 - x1) / 10.; + if (fabs(xnew - x1) < slope) { + xnew = x1 + DSIGN(xnew-x1) * slope; +#ifdef DEBUG_ROOT1D + fprintf(fp, " | x10%% = %-9.4g", xnew); +#endif + } + if (fabs(xnew - x2) < slope) { + xnew = x2 + DSIGN(xnew-x2) * slope; +#ifdef DEBUG_ROOT1D + fprintf(fp, " | x10%% = %-9.4g", xnew); +#endif + } + } else { + /* + * If we are venturing into new ground, only allow the step jump + * to increase by 100% at each interation + */ + slope = 2.0 * fabs(x2 - x1); + if (fabs(slope) < fabs(xnew - x2)) { + xnew = x2 + DSIGN(xnew-x2) * slope; +#ifdef DEBUG_ROOT1D + fprintf(fp, " | xlimitsize = %-9.4g", xnew); +#endif + } + } + + + if (xnew > xmax) { + xnew = x2 + (xmax - x2) / 2.0; +#ifdef DEBUG_ROOT1D + fprintf(fp, " | xlimitmax = %-9.4g", xnew); +#endif + } + if (xnew < xmin) { + xnew = x2 + (x2 - xmin) / 2.0; +#ifdef DEBUG_ROOT1D + fprintf(fp, " | xlimitmin = %-9.4g", xnew); +#endif + } + if (foundStraddle) { +#ifdef DEBUG_ROOT1D + slope = xnew; +#endif + if (posStraddle) { + if (f2 > 0.0) { + if (xnew > x2) xnew = (xNegF + x2)/2; + if (xnew < xNegF) xnew = (xNegF + x2)/2; + } else { + if (xnew < x2) xnew = (xPosF + x2)/2; + if (xnew > xPosF) xnew = (xPosF + x2)/2; + } + } else { + if (f2 > 0.0) { + if (xnew < x2) xnew = (xNegF + x2)/2; + if (xnew > xNegF) xnew = (xNegF + x2)/2; + } else { + if (xnew > x2) xnew = (xPosF + x2)/2; + if (xnew < xPosF) xnew = (xPosF + x2)/2; + } + } +#ifdef DEBUG_ROOT1D + if (slope != xnew) { + fprintf(fp, " | xstraddle = %-9.4g", xnew); + } +#endif + } + + fnew = func(xnew, FuncTargVal, varID, fptrPassthrough, &err); +#ifdef DEBUG_ROOT1D + fprintf(fp,"\n"); + print_funcEval(xnew, fnew, its); + fprintf(fp, "%-5d %-5d %-15.5E %-15.5E", its, VCount->Its, xnew, fnew); +#endif + + if (foundStraddle) { + if (posStraddle) { + if (fnew > 0.0) { + if (xnew < xPosF) xPosF = xnew; + } else { + if (xnew > xNegF) xNegF = xnew; + } + } else { + if (fnew > 0.0) { + if (xnew > xPosF) xPosF = xnew; + } else { + if (xnew < xNegF) xNegF = xnew; + } + } + } + + if (! foundStraddle) { + if (fnew > 0.0) { + if (!foundPosF) { + foundPosF = TRUE; + xPosF = xnew; + foundStraddle = TRUE; + if (xPosF > xNegF) posStraddle = TRUE; + else posStraddle = FALSE; + } + } else { + if (!foundNegF) { + foundNegF = TRUE; + xNegF = xnew; + foundStraddle = TRUE; + if (xPosF > xNegF) posStraddle = TRUE; + else posStraddle = FALSE; + } + } + } + + x0 = x1; + f0 = f1; + x1 = x2; + f1 = f2; + x2 = xnew; + f2 = fnew; + if (fabs(fnew / fnorm) < 1.0E-5) { + converged = TRUE; + } + its++; + } while (! converged && its < itmax); + if (converged) { +#ifdef DEBUG_ROOT1D + plogf("vcs_root1d success: convergence achieved\n"); + fprintf(fp, " | vcs_root1d success in %d its, fnorm = %g\n", its, fnorm); +#endif + } else { + retn = VCS_FAILED_CONVERGENCE; + plogf("vcs_root1d ERROR: maximum iterations exceeded without convergence\n"); +#ifdef DEBUG_ROOT1D + fprintf(fp, "\nvcs_root1d failure in %d its\n", its); +#endif + } + *xbest = x2; +#ifdef DEBUG_ROOT1D + fclose(fp); +#endif + return retn; +} +/*****************************************************************************/ +} + diff --git a/Cantera/src/equil/vcs_rxnadj.cpp b/Cantera/src/equil/vcs_rxnadj.cpp new file mode 100644 index 000000000..c538afb12 --- /dev/null +++ b/Cantera/src/equil/vcs_rxnadj.cpp @@ -0,0 +1,533 @@ +/* ======================================================================= */ +/* -------------------------------------------------- */ +/* | RCS Head Information on zuzax.pchem.sandia.gov | */ +/* -------------------------------------------------- */ +/* $RCSfile$ */ +/* $Author$ */ +/* $Date$ */ +/* $Revision$ */ +/* ======================================================================= */ +#include +#include +#include + +#include "vcs_solve.h" +#include "vcs_internal.h" +#include "vcs_VolPhase.h" + +namespace VCSnonideal { + +/*****************************************************************************/ +/*****************************************************************************/ +/*****************************************************************************/ + +int VCS_SOLVE::vcs_rxn_adj_cg(void) + + /************************************************************************** + * + * vcs_rxn_adj_cg: + * + * Calculates reaction adjustments. This does what equation 6.4-16, p. 143 + * in Smith and Missen is suppose to do. However, a full matrix is + * formed and then solved via a conjugate gradient algorithm. No + * preconditioning is done. + * + * If special branching is warranted, then the program bails out. + * + * Output + * ------- + * DS(I) : reaction adjustment, where I refers to the Ith species + * 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. + * + * Special attention is taken to flag cases where the direction of the + * update is contrary to the steepest descent rule. This is an important + * attribute of the regular vcs algorithm. We don't want to violate this + ***************************************************************************/ +{ + int irxn, j, k, kspec, soldel = 0; + double s, xx, dss; + double *dnPhase_irxn; +#ifdef DEBUG + char ANOTE[128]; + plogf(" "); for (j = 0; j < 77; j++) plogf("-"); + plogf("\n --- Subroutine rxn_adj_cg() called\n"); + plogf(" --- Species Moles Rxn_Adjustment | Comment\n"); +#endif + + /* + * Precalculation loop -> we calculate quantities based on + * loops over the number of species. + * We also evaluate whether the matrix is appropriate for + * this algorithm. If not, we bail out. + */ + for (irxn = 0; irxn < m_numRxnRdc; ++irxn) { +#ifdef DEBUG + sprintf(ANOTE,"Normal Calc"); +#endif + + kspec = ir[irxn]; + dnPhase_irxn = DnPhase[irxn]; + + if (soln[kspec] == 0.0 && (! SSPhase[kspec])) { + /* *******************************************************************/ + /* **** MULTISPECIES PHASE WITH total moles equal to zero ************/ + /* *******************************************************************/ + /* + * HKM -> the statment below presupposes units in dg[]. It probably + * should be replaced with something more relativistic + */ + if (dg[irxn] < -1.0e-4) { +#ifdef DEBUG + (void) sprintf(ANOTE, "MultSpec: come alive DG = %11.3E", dg[irxn]); +#endif + ds[kspec] = 1.0e-10; + spStatus[irxn] = VCS_SPECIES_MAJOR; + --(m_numRxnMinorZeroed); + } else { +#ifdef DEBUG + (void) 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: converged DG = %11.3E\n", dg[irxn]); + plogf(" --- "); plogf("%-12.12s", SpName[kspec].c_str()); + plogf(" %12.4E %12.4E | %s\n", soln[kspec], ds[kspec], ANOTE); +#endif + continue; + } + /* + * Don't calculate for minor or nonexistent species if + * their values are to be decreasing anyway. + */ + if (spStatus[irxn] <= VCS_SPECIES_MINOR && dg[irxn] >= 0.0) { +#ifdef DEBUG + sprintf(ANOTE,"Skipped: IC = %3d and DG >0: %11.3E\n", + spStatus[irxn], dg[irxn]); + plogf(" --- "); plogf("%-12.12s", SpName[kspec].c_str()); + plogf(" %12.4E %12.4E | %s\n", soln[kspec], ds[kspec], 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]) s += SQUARE(sc[irxn][j]) / soln[j]; + } + for (j = 0; j < NPhase; j++) { + if (! (VPhaseList[j])->SingleSpecies) { + if (TPhMoles[j] > 0.0) + s -= SQUARE(dnPhase_irxn[j]) / TPhMoles[j]; + } + } + if (s != 0.0) { + ds[kspec] = -dg[irxn] / s; + } else { + /* ************************************************************ */ + /* **** REACTION IS ENTIRELY AMONGST SINGLE SPECIES PHASES **** */ + /* **** DELETE ONE SOLID 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. + */ + 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; + TPhMoles[PhaseID[k]] = 0.0; +#ifdef DEBUG + plogf(" --- vcs_st2 Special section to delete "); + plogf("%-12.12s", SpName[k].c_str()); + plogf("\n --- 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 + plogf(" --- "); plogf("%-12.12s", SpName[kspec].c_str()); + plogf(" %12.4E %12.4E | %s\n", soln[kspec], ds[kspec], ANOTE); +#endif + } /* End of loop over non-component stoichiometric formation reactions */ + + + + + /* + * + * When we form the Hessian we must be careful to ensure that it + * is a symmetric positive definate matrix, still. This means zeroing + * out columns when we zero out rows as well. + * -> I suggest writing a small program to make sure of this + * property. + */ + + +#ifdef DEBUG + plogf(" "); for (j = 0; j < 77; j++) plogf("-"); plogf("\n"); +#endif + return soldel; +} /* vcs_rxn_adj_cg() ********************************************************/ +/*****************************************************************************/ +/*****************************************************************************/ +/*****************************************************************************/ + +double VCS_SOLVE::vcs_Hessian_diag_adj(int irxn, double hessianDiag_Ideal) + + /************************************************************************** + * + * vcs_actCoeff_diag_adj(irxn): + * + * Calculates the diagonal contribution to the Hessian due to + * the dependence of the activity coefficients on the mole numbers. + * + * (See framemaker notes, Eqn. 20 - VCS Equations document) + * + * We allow the diagonal to be increased positively to any degree. + * We allow the diagonal to be decreased to 1/3 of the ideal solution + * value, but no more -> it must remain positive. + **************************************************************************/ +{ + double diag = hessianDiag_Ideal; + double hessActCoef = vcs_Hessian_actCoeff_diag(irxn); + if (hessianDiag_Ideal <= 0.0) { + plogf("We shouldn't be here\n"); + exit(-1); + } + if (hessActCoef >= 0.0) { + diag += hessActCoef; + } else if (fabs(hessActCoef) < 0.6666 * hessianDiag_Ideal) { + diag += hessActCoef; + } else { + diag -= 0.6666 * hessianDiag_Ideal; + } + return diag; +} +/*****************************************************************************/ +/*****************************************************************************/ +/*****************************************************************************/ + +double VCS_SOLVE::vcs_Hessian_actCoeff_diag(int irxn) + + /************************************************************************** + * + * vcs_Hessian_actCoeff_diag(irxn): + * + * Calculates the diagonal contribution to the Hessian due to + * the dependence of the activity coefficients on the mole numbers. + * (See framemaker notes, Eqn. 20 - VCS Equations document) + **************************************************************************/ +{ + int kspec, k, l, kph; + double s; + double *sc_irxn; + kspec = ir[irxn]; + kph = PhaseID[kspec]; + sc_irxn = sc[irxn]; + /* + * First the diagonal term of the Jacobian + */ + s = dLnActCoeffdMolNum[kspec][kspec]; + /* + * Next, the other terms. Note this only a loop over the components + * So, it's not too expensive to calculate. + */ + for (l = 0; l < m_numComponents; l++) { + if (!SSPhase[l]) { + for (k = 0; k < m_numComponents; ++k) { + if (PhaseID[k] == PhaseID[l]) { + s += sc_irxn[k] * sc_irxn[l] * dLnActCoeffdMolNum[k][l]; + } + } + if (kph == PhaseID[l]) { + s += sc_irxn[l] * (dLnActCoeffdMolNum[kspec][l] + dLnActCoeffdMolNum[l][kspec]); + } + } + } + return s; +} +/*****************************************************************************/ +/*****************************************************************************/ +/*****************************************************************************/ + + +void VCS_SOLVE::vcs_CalcLnActCoeffJac(const double * const moleSpeciesVCS) + + /************************************************************************* + * + * + * + * + *************************************************************************/ +{ + /* + * Loop over all of the phases in the problem + */ + for (int iphase = 0; iphase < NPhase; iphase++) { + vcs_VolPhase *Vphase = VPhaseList[iphase]; + /* + * We don't need to call single species phases; + */ + if (!Vphase->SingleSpecies) { + /* + * update the Ln Act Coeff jacobian entries with respect to the + * mole number of species in the phase + */ + Vphase->updateLnActCoeffJac(moleSpeciesVCS); + /* + * Download the resulting calculation into the full matrix + * -> This scatter calculation is carried out in the + * volume object. + */ + Vphase->sendToVCSLnActCoeffJac(dLnActCoeffdMolNum.baseDataAddr()); + } + } +} + +/*****************************************************************************/ +/*****************************************************************************/ +/*****************************************************************************/ + +double VCS_SOLVE::deltaG_Recalc_Rxn(int irxn, const double *const molNum, + double * const ac, double * const mu_i) + + /************************************************************************* + * + * deltaG_Recalc_Rxn + * This function recalculates the deltaG for reaction irxn, + * given the mole numbers in molNum. It uses the temporary + * space mu_i, to hold the chemical potentials + *************************************************************************/ +{ + int kspec = irxn + m_numComponents; + int *pp_ptr = PhaseParticipation[irxn]; + for (int iphase = 0; iphase < NPhase; iphase++) { + if (pp_ptr[iphase]) { + vcs_chemPotPhase(iphase, molNum, ac, mu_i); + } + } + double deltaG = mu_i[kspec]; + double *sc_irxn = sc[irxn]; + for (int k = 0; k < m_numComponents; k++) { + deltaG += sc_irxn[k] * mu_i[k]; + } + return deltaG; +} + +/*****************************************************************************/ +/*****************************************************************************/ +/*****************************************************************************/ +#ifdef DEBUG +double VCS_SOLVE::vcs_line_search(int irxn, double dx_orig, char *ANOTE) +#else +double VCS_SOLVE::vcs_line_search(int irxn, double dx_orig) +#endif + /************************************************************************* + * + * In this routine we carry out a rough line search algorithm + * to make sure that the dG doesn't switch signs prematurely. + * + * + *************************************************************************/ +{ + int its = 0; + int k; + int kspec = ir[irxn]; + const int MAXITS = 10; + double dx = dx_orig; + double *sc_irxn = sc[irxn]; + double *molNumBase = VCS_DATA_PTR(soln); + double *acBase = VCS_DATA_PTR(ActCoeff0); + double *ac = VCS_DATA_PTR(ActCoeff); + double *molNum = VCS_DATA_PTR(wt); + double molSum = 0.0; + double slope; + /* + * Calculate the deltaG value at the dx = 0.0 point + */ + double deltaGOrig = deltaG_Recalc_Rxn(irxn, molNumBase, acBase, VCS_DATA_PTR(fel)); + double forig = fabs(deltaGOrig) + 1.0E-15; + if (deltaGOrig > 0.0) { + if (dx_orig > 0.0) { + dx = 0.0; +#ifdef DEBUG + if (vcs_debug_print_lvl >= 2) { + //plogf(" --- %s :Warning possible error dx>0 dg > 0\n", SpName[kspec]); + } + sprintf(ANOTE,"Rxn reduced to zero step size in line search: dx>0 dg > 0"); +#endif + return dx; + } + } else if (deltaGOrig < 0.0) { + if (dx_orig < 0.0) { + dx = 0.0; +#ifdef DEBUG + if (vcs_debug_print_lvl >= 2) { + //plogf(" --- %s :Warning possible error dx<0 dg < 0\n", SpName[kspec]); + } + sprintf(ANOTE,"Rxn reduced to zero step size in line search: dx<0 dg < 0"); +#endif + return dx; + } + } else if (deltaGOrig == 0.0) { + return 0.0; + } + if (dx_orig == 0.0) return 0.0; + + vcs_dcopy(molNum, molNumBase, m_numSpeciesRdc); + molSum = molNumBase[kspec]; + molNum[kspec] = molNumBase[kspec] + dx_orig; + for (k = 0; k < m_numComponents; k++) { + molNum[k] = molNumBase[k] + sc_irxn[k] * dx_orig; + molSum += molNumBase[k]; + } + + double deltaG1 = deltaG_Recalc_Rxn(irxn, molNum, ac, VCS_DATA_PTR(feTrial)); + + /* + * If deltaG hasn't switched signs when going the full distance + * then we are heading in the appropriate direction, and + * we should accept the current full step size + */ + if (deltaG1 * deltaGOrig > 0.0) { + dx = dx_orig; + goto finalize; + } + /* + * If we have decreased somewhat, the deltaG return after finding + * a better estimate for the line search. + */ + if (fabs(deltaG1) < 0.8*forig) { + if (deltaG1 * deltaGOrig < 0.0) { + slope = (deltaG1 - deltaGOrig) / dx_orig; + dx = -deltaGOrig / slope; + } else { + dx = dx_orig; + } + goto finalize; + } + + dx = dx_orig; + + for (its = 0; its < MAXITS; its++) { + /* + * Calculate the approximation to the total Gibbs free energy at + * the dx *= 0.5 point + */ + dx *= 0.5; + molNum[kspec] = molNumBase[kspec] + dx; + for (k = 0; k < m_numComponents; k++) { + molNum[k] = molNumBase[k] + sc_irxn[k] * dx; + } + double deltaG = deltaG_Recalc_Rxn(irxn, molNum, ac, VCS_DATA_PTR(feTrial)); + /* + * If deltaG hasn't switched signs when going the full distance + * then we are heading in the appropriate direction, and + * we should accept the current step + */ + if (deltaG * deltaGOrig > 0.0) { + goto finalize; + } + /* + * If we have decreased somewhat, the deltaG return after finding + * a better estimate for the line search. + */ + if (fabs(deltaG) / forig < (1.0 - 0.1 * dx / dx_orig)) { + if (deltaG * deltaGOrig < 0.0) { + slope = (deltaG - deltaGOrig) / dx; + dx = -deltaGOrig / slope; + } + goto finalize; + } + } + + finalize: + if (its >= MAXITS) { +#ifdef DEBUG + sprintf(ANOTE,"Rxn reduced to zero step size from %g to %g (MAXITS)", + dx_orig, dx); + return dx; +#endif + } +#ifdef DEBUG + if (dx != dx_orig) { + sprintf(ANOTE,"Line Search reduced step size from %g to %g", + dx_orig, dx); + } +#endif + + return dx; +} +/*****************************************************************************/ +/*****************************************************************************/ +/*****************************************************************************/ +} + diff --git a/Cantera/src/equil/vcs_setMolesLinProg.cpp b/Cantera/src/equil/vcs_setMolesLinProg.cpp new file mode 100644 index 000000000..4a6e14c56 --- /dev/null +++ b/Cantera/src/equil/vcs_setMolesLinProg.cpp @@ -0,0 +1,231 @@ +/*! + * @file vcs_setMolesLinProg.cpp + * + */ +/* + * $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_internal.h" +#include "vcs_VolPhase.h" +#include "vcs_species_thermo.h" +#include "vcs_solve.h" + +#include +#include +using namespace std; + +namespace VCSnonideal { + +#ifdef DEBUG +static void printProgress(const vector &spName, + const vector &soln, + const vector &ff) { + int nsp = soln.size(); + double sum = 0.0; + plogf(" --- Summary of current progress:\n"); + plogf(" --- Name Moles - SSGibbs \n"); + plogf(" -------------------------------------------------------------------------------------\n"); + for (int k = 0; k < nsp; k++) { + plogf(" --- %20s %12.4g - %12.4g\n", spName[k].c_str(), soln[k], ff[k]); + sum += soln[k] * ff[k]; + } + plogf(" --- Total sum to be minimized = %g\n", sum); +} +#endif + +//! Estimate the initial mole numbers. +/*! + * This is done by running + * each reaction as far forward or backward as possible, subject + * to the constraint that all mole numbers remain + * non-negative. Reactions for which \f$ \Delta \mu^0 \f$ are + * positive are run in reverse, and ones for which it is negative + * are run in the forward direction. The end result is equivalent + * to solving the linear programming problem of minimizing the + * linear Gibbs function subject to the element and + * non-negativity constraints. + */ +int VCS_SOLVE::vcs_setMolesLinProg() { + int ik, irxn; + double test = -1.0E-10; + +#ifdef DEBUG + char *pprefix = " --- seMolesLinProg "; + if (vcs_debug_print_lvl >= 2) { + plogf(" --- call setInitialMoles\n"); + } +#endif + + + // m_mu are standard state chemical potentials + // Boolean on the end specifies standard chem potentials + // m_mix->getValidChemPotentials(not_mu, DATA_PTR(m_mu), true); + // -> This is already done coming into the routine. + double dg_rt; + + int idir; + double nu; + double delta_xi, dxi_min = 1.0e10; + bool redo = true; + int jcomp; + int retn; + int iter = 0; + bool abundancesOK = true; + int usedZeroedSpecies; + + 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 wx(m_numElemConstraints, 0.0); + std::vector aw(m_numSpeciesTot, 0.0); + + for (ik = 0; ik < m_numSpeciesTot; ik++) { + if (SpeciesUnknownType[ik] != VCS_SPECIES_INTERFACIALVOLTAGE) { + soln[ik] = MAX(0.0, soln[ik]); + } + } + +#ifdef DEBUG + if (vcs_debug_print_lvl >= 2) { + printProgress(SpName, soln, ff); + } +#endif + + while (redo) { + + if (!vcs_elabcheck(0)) { +#ifdef DEBUG + if (vcs_debug_print_lvl >= 2) { + plogf("%s Mole numbers failing element abundances\n", pprefix); + plogf("%sCall vcs_elcorr to attempt fix\n", pprefix); + } +#endif + retn = vcs_elcorr(VCS_DATA_PTR(sm), VCS_DATA_PTR(wx)); + if (retn >= 2) { + abundancesOK = false; + } else { + abundancesOK = true; + } + } else { + abundancesOK = true; + } + /* + * Now find the optimized basis that spans the stoichiometric + * coefficient matrix, based on the current composition, soln[] + * We also calculate sc[][], the reaction matrix. + */ + 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; + +#ifdef DEBUG + if (vcs_debug_print_lvl >= 2) { + plogf("iteration %d\n", iter); + } +#endif + redo = false; + iter++; + if (iter > 15) break; + + // loop over all reactions + for (irxn = 0; irxn < m_numRxnTot; irxn++) { + + // dg_rt is the Delta_G / RT value for the reaction + ik = m_numComponents + irxn; + dg_rt = ff[ik]; + dxi_min = 1.0e10; + const double *sc_irxn = sc[irxn]; + for (jcomp = 0; jcomp < m_numElemConstraints; jcomp++) { + dg_rt += ff[jcomp] * sc_irxn[jcomp]; + } + // fwd or rev direction. + // idir > 0 implies increasing the current species + // idir < 0 implies decreasing the current species + idir = (dg_rt < 0.0 ? 1 : -1); + if (idir < 0) { + dxi_min = soln[ik]; + } + + for (jcomp = 0; jcomp < m_numComponents; jcomp++) { + nu = sc_irxn[jcomp]; + + // set max change in progress variable by + // non-negativity requirement + if (nu*idir < 0) { + delta_xi = fabs(soln[jcomp]/nu); + // if a component has nearly zero moles, redo + // with a new set of components + if (!redo) { + if (delta_xi < 1.0e-10 && (soln[ik] >= 1.0E-10)) { +#ifdef DEBUG + if (vcs_debug_print_lvl >= 2) { + plogf(" --- Component too small: %s\n", SpName[jcomp].c_str()); + } +#endif + redo = true; + } + } + if (delta_xi < dxi_min) dxi_min = delta_xi; + } + } + + // step the composition by dxi_min, check against zero, since + // we are zeroing components and species on every step. + // Redo the iteration, if a component went from positive to zero on this step. + double ds = idir*dxi_min; + soln[ik] += ds; + soln[ik] = MAX(0.0, soln[ik]); + for (jcomp = 0; jcomp < m_numComponents; jcomp++) { + bool full = false; + if (soln[jcomp] > 1.0E-15) { + full = true; + } + soln[jcomp] += sc_irxn[jcomp] * ds; + soln[jcomp] = MAX(0.0, soln[jcomp]); + if (full) { + if (soln[jcomp] < 1.0E-60) { + redo = true; + } + } + } + } + + // set the moles of the phase objects to match + // updateMixMoles(); + // Update the phase objects with the contents of the soln vector + // vcs_updateVP(0); +#ifdef DEBUG + if (vcs_debug_print_lvl >= 2) { + printProgress(SpName, soln, ff); + } +#endif + } + +#ifdef DEBUG + if (vcs_debug_print_lvl == 1) { + printProgress(SpName, soln, ff); + plogf(" --- setInitialMoles end\n"); + } +#endif + retn = 0; + if (!abundancesOK) { + retn = -1; + } else if (iter > 15) { + retn = 1; + } + return retn; +} + +} + diff --git a/Cantera/src/equil/vcs_solve.cpp b/Cantera/src/equil/vcs_solve.cpp new file mode 100644 index 000000000..ff524c690 --- /dev/null +++ b/Cantera/src/equil/vcs_solve.cpp @@ -0,0 +1,1024 @@ +/*! + * @file vcs_solve.h + * Header file for the internal class that holds the problem. + */ +/* + * $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 "vcs_solve.h" +#include "vcs_Exception.h" +#include "vcs_internal.h" +#include "vcs_prob.h" + +#include "vcs_VolPhase.h" +#include "vcs_SpeciesProperties.h" +#include "vcs_species_thermo.h" + +#include +#include "math.h" +using namespace std; + +namespace VCSnonideal { + + VCS_SOLVE::VCS_SOLVE() : + NSPECIES0(0), + NPHASE0(0), + m_numSpeciesTot(0), + m_numElemConstraints(0), + m_numComponents(0), + m_numRxnTot(0), + m_numSpeciesRdc(0), + m_numRxnMinorZeroed(0), + NPhase(0), + iest(0), + TMoles(0.0), + T(0.0), + Pres(0.0), + tolmaj(0.0), + tolmin(0.0), + tolmaj2(0.0), + tolmin2(0.0), + UnitsState(VCS_DIMENSIONAL_G), + UseActCoeffJac(0), + Vol(0.0), + Faraday_dim(1.602e-19 * 6.022136736e26), + m_VCount(0), +#ifdef DEBUG + vcs_debug_print_lvl(0), +#endif + m_VCS_UnitsFormat(VCS_UNITS_UNITLESS) + { + } + + void VCS_SOLVE::InitSizes(int nspecies0, int nelements, int nphase0) + { + if (NSPECIES0 != 0) { + if ((nspecies0 != NSPECIES0) || (nelements != m_numElemConstraints) || (nphase0 != NPHASE0)){ + delete_memory(); + } else { + return; + } + } + + NSPECIES0 = nspecies0; + NPHASE0 = nphase0; + m_numSpeciesTot = nspecies0; + m_numElemConstraints = nelements; + m_numComponents = nelements; + + int iph; + string ser = "VCS_SOLVE: ERROR:\n\t"; + if (nspecies0 <= 0) { + plogf("%s Number of species is nonpositive\n", ser.c_str()); + throw vcsError("VCS_SOLVE()", + ser + " Number of species is nonpositive\n", + VCS_PUB_BAD); + } + if (nelements <= 0) { + plogf("%s Number of elements is nonpositive\n", ser.c_str()); + throw vcsError("VCS_SOLVE()", + ser + " Number of species is nonpositive\n", + VCS_PUB_BAD); + } + if (nphase0 <= 0) { + plogf("%s Number of phases is nonpositive\n", ser.c_str()); + throw vcsError("VCS_SOLVE()", + ser + " Number of species is nonpositive\n", + VCS_PUB_BAD); + } + + //vcs_priv_init(this); + m_VCS_UnitsFormat = VCS_UNITS_UNITLESS; + + /* + * We will initialize sc[] to note the fact that it needs to be + * filled with meaningful information. + */ + sc.resize(nspecies0, nelements, 0.0); + + scSize.resize(nspecies0, 0.0); + + m_gibbsSpecies.resize(nspecies0, 0.0); + ff.resize(nspecies0, 0.0); + feTrial.resize(nspecies0, 0.0); + soln.resize(nspecies0, 0.0); + + SpeciesUnknownType.resize(nspecies0, VCS_SPECIES_TYPE_MOLNUM); + + DnPhase.resize(nspecies0, nphase0, 0.0); + PhaseParticipation.resize(nspecies0, nphase0, 0); + phasePhi.resize(nphase0, 0.0); + + wt.resize(nspecies0, 0.0); + + dg.resize(nspecies0, 0.0); + dgl.resize(nspecies0, 0.0); + ds.resize(nspecies0, 0.0); + + fel.resize(nspecies0, 0.0); + ga.resize(nelements, 0.0); + gai.resize(nelements, 0.0); + + + TPhMoles.resize(nphase0, 0.0); + TPhMoles1.resize(nphase0, 0.0); + DelTPhMoles.resize(nphase0, 0.0); + TmpPhase.resize(nphase0, 0.0); + TmpPhase2.resize(nphase0, 0.0); + + FormulaMatrix.resize(nelements, nspecies0); + + TPhInertMoles.resize(nphase0, 0.0); + + /* + * ind[] is an index variable that keep track of solution vector + * rotations. + */ + ind.resize(nspecies0, 0); + indPhSp.resize(nspecies0, 0); + /* + * IndEl[] is an index variable that keep track of element vector + * rotations. + */ + IndEl.resize(nelements, 0); + + /* + * ir[] is an index vector that keeps track of the irxn to species + * mapping. We can't fill it in until we know the number of c + * components in the problem + */ + ir.resize(nspecies0, 0); + + /* Initialize all species to be major species */ + spStatus.resize(nspecies0, 1); + + SSPhase.resize(2*nspecies0, 0); + PhaseID.resize(nspecies0, 0); + + m_numElemConstraints = nelements; + + ElName.resize(nelements, std::string("")); + SpName.resize(nspecies0, std::string("")); + + m_elType.resize(nelements, VCS_ELEM_TYPE_ABSPOS); + + ElActive.resize(nelements, 1); + /* + * Malloc space for activity coefficients for all species + * -> Set it equal to one. + */ + SpecActConvention.resize(nspecies0, 0); + PhaseActConvention.resize(nphase0, 0); + SpecLnMnaught.resize(nspecies0, 0.0); + ActCoeff.resize(nspecies0, 1.0); + ActCoeff0.resize(nspecies0, 1.0); + CurrPhAC.resize(nphase0, 0); + WtSpecies.resize(nspecies0, 0.0); + Charge.resize(nspecies0, 0.0); + SpeciesThermo.resize(nspecies0, (VCS_SPECIES_THERMO *)0); + + /* + * Malloc Phase Info + */ + VPhaseList.resize(nphase0, 0); + for (iph = 0; iph < nphase0; iph++) { + VPhaseList[iph] = new vcs_VolPhase(); + } + + /* + * For Future expansion + */ + UseActCoeffJac = true; + if (UseActCoeffJac ) { + dLnActCoeffdMolNum.resize(nspecies0, nspecies0, 0.0); + } + + VolPM.resize(nspecies0, 0.0); + + /* + * Malloc space for counters kept within vcs + * + */ + m_VCount = new VCS_COUNTERS(); + vcs_counters_init(1); + + + return; + + } + + + /** + * + */ + VCS_SOLVE::~VCS_SOLVE() + { + delete_memory(); + } + + void VCS_SOLVE::delete_memory(void) + { + int j, nph = NPhase; + int nspecies = m_numSpeciesTot; + + for (j = 0; j < nph; j++) { + delete VPhaseList[j]; + VPhaseList[j] = 0; + } + + for (j = 0; j < nspecies; j++) { + delete SpeciesThermo[j]; + SpeciesThermo[j] = 0; + } + + delete m_VCount; m_VCount = 0; + + NSPECIES0 = 0; + NPHASE0 = 0; + m_numElemConstraints = 0; + m_numComponents = 0; + } + + /*****************************************************************************/ + /*****************************************************************************/ + /*****************************************************************************/ + // Solve an equilibrium problem + /* + * This is the main interface routine to the equilibrium solver + * + * Input: + * @param vprob Object containing the equilibrium Problem statement + * + * @param ifunc Determines the operation to be done: Valid values: + * 0 -> Solve a new problem by initializing structures + * first. An initial estimate may or may not have + * been already determined. This is indicated in the + * VCS_PROB structure. + * 1 -> The problem has already been initialized and + * set up. We call this routine to resolve it + * using the problem statement and + * solution estimate contained in + * the VCS_PROB structure. + * 2 -> Don't solve a problem. Destroy all the private + * structures. + * + * @param ipr Printing of results + * ipr = 1 -> Print problem statement and final results to + * standard output + * 0 -> don't report on anything + * @param ip1 Printing of intermediate results + * ip1 = 1 -> Print intermediate results. + * = 0 -> No intermediate results printing + * + * @param maxit Maximum number of iterations for the algorithm + * + * @param iprintTime Printing of time information. Default = -1, + * implies printing if other printing is turned on. + * + * Output: + * + * @return + * nonzero value: failure to solve the problem at hand. + * zero : success + */ + int VCS_SOLVE::vcs(VCS_PROB *vprob, int ifunc, int ipr, int ip1, int maxit, + int iprintTime) { + int retn = 0; + int iconv = 0, nspecies0, nelements0, nphase0; + double te, ts = vcs_second(); + if (iprintTime == -1) { + iprintTime = MAX(ipr, ip1); + } + if (ifunc < 0 || ifunc > 2) { + plogf("vcs: Unrecognized value of ifunc, %d: bailing!\n", + ifunc); + return VCS_PUB_BAD; + } + + if (ifunc == 0) { + /* + * This function is called to create the private data + * using the public data. + */ + nspecies0 = vprob->nspecies + 10; + nelements0 = vprob->ne; + nphase0 = vprob->NPhase; + + InitSizes(nspecies0, nelements0, nphase0); + + if (retn != 0) { + plogf("vcs_priv_alloc returned a bad status, %d: bailing!\n", + retn); + return retn; + } + /* + * This function is called to copy the public data + * and the current problem specification + * into the current object's data structure. + */ + retn = vcs_prob_specifyFully(vprob); + if (retn != 0) { + plogf("vcs_pub_to_priv returned a bad status, %d: bailing!\n", + retn); + return retn; + } + /* + * Prep the problem data + * - adjust the identity of any phases + * - determine the number of components in the problem + */ + retn = vcs_prep_oneTime(ip1); + if (retn != 0) { + plogf("vcs_prep_oneTime returned a bad status, %d: bailing!\n", + retn); + return retn; + } + } + if (ifunc == 1) { + /* + * This function is called to copy the current problem + * into the current object's data structure. + */ + retn = vcs_prob_specify(vprob); + if (retn != 0) { + plogf("vcs_prob_specify returned a bad status, %d: bailing!\n", + retn); + return retn; + } + } + if (ifunc != 2) { + /* + * Prep the problem data for this particular instantiation of + * the problem + */ + retn = vcs_prep(); + if (retn != VCS_SUCCESS) { + plogf("vcs_prep returned a bad status, %d: bailing!\n", retn); + return retn; + } + + /* + * Check to see if the current problem is well posed. + */ + if (!vcs_wellPosed(vprob)) { + plogf("vcs has determined the problem is not well posed: Bailing\n"); + return VCS_PUB_BAD; + } + + /* + * Once we have defined the global internal data structure defining + * the problem, then we go ahead and solve the problem. + * + * (right now, all we do is solve fixed T, P problems. + * Methods for other problem types will go in at this level. + * For example, solving for fixed T, V problems will involve + * a 2x2 Newton's method, using loops over vcs_TP() to + * calculate the residual and Jacobian) + */ + switch (vprob->prob_type) { + case VCS_PROBTYPE_TP: + iconv = vcs_TP(ipr, ip1, maxit, vprob->T, vprob->Pres); + break; + case VCS_PROBTYPE_TV: + iconv = vcs_TV(ipr, ip1, maxit, vprob->T, vprob->Vol); + break; + default: + plogf("Unknown or unimplemented problem type: %d\n", + vprob->prob_type); + return VCS_PUB_BAD; + } + + /* + * If requested to print anything out, go ahead and do so; + */ + if (ipr) vcs_report(iconv); + /* + * Copy the results of the run back to the VCS_PROB structure, + * which is returned to the user. + */ + vcs_prob_update(vprob); + } + + /* + * Report on the time if requested to do so + */ + te = vcs_second(); + m_VCount->T_Time_vcs += te - ts; + if (iprintTime > 0) { + vcs_TCounters_report(); + } + /* + * Now, destroy the private data, if requested to do so + * + * FILL IN + */ + + if (iconv < 0) { + plogf("ERROR: FAILURE its = %d!\n", m_VCount->Its); + } else if (iconv == 1) { + plogf("WARNING: RANGE SPACE ERROR encountered\n"); + } + return iconv; + } + /*****************************************************************************/ + /*****************************************************************************/ + /*****************************************************************************/ + // Fully specify the problem to be solved using VCS_PROB + /* + * Use the contents of the VCS_PROB to specify the contents of the + * private data, VCS_SOLVE. + * + * @param pub Pointer to VCS_PROB that will be used to + * initialize the current equilibrium problem + */ + int VCS_SOLVE::vcs_prob_specifyFully(const VCS_PROB *pub) { + int i, j, kspec; + int iph; + vcs_VolPhase *Vphase = 0; + const char *ser = + "vcs_pub_to_priv ERROR :ill defined interface -> bailout:\n\t"; + + /* + * First Check to see whether we have room for the current problem + * size + */ + int nspecies = pub->nspecies; + if (NSPECIES0 < nspecies) { + plogf("%sPrivate Data is dimensioned too small\n", ser); + return VCS_PUB_BAD; + } + int nph = pub->NPhase; + if (NPHASE0 < nph) { + plogf("%sPrivate Data is dimensioned too small\n", ser); + return VCS_PUB_BAD; + } + int nelements = pub->ne; + if (m_numElemConstraints < nelements) { + plogf("%sPrivate Data is dimensioned too small\n", ser); + return VCS_PUB_BAD; + } + + /* + * OK, We have room. Now, transfer the integer numbers + */ + m_numElemConstraints = nelements; + m_numSpeciesTot = nspecies; + m_numSpeciesRdc = m_numSpeciesTot; + /* + * nc = number of components -> will be determined later. + * but set it to its maximum possible value here. + */ + m_numComponents = nelements; + /* + * m_numRxnTot = number of noncomponents, also equal to the + * number of reactions + */ + m_numRxnTot = MAX(nspecies - nelements, 0); + m_numRxnRdc = m_numRxnTot; + /* + * number of minor species rxn -> all species rxn are major at the start. + */ + m_numRxnMinorZeroed = 0; + /* + * NPhase = number of phases + */ + NPhase = nph; + +#ifdef DEBUG + vcs_debug_print_lvl = pub->vcs_debug_print_lvl; +#endif + + /* + * FormulaMatrix[] -> Copy the formula matrix over + */ + for (i = 0; i < nspecies; i++) { + for (j = 0; j < nelements; j++) { + FormulaMatrix[j][i] = pub->FormulaMatrix[j][i]; + } + } + + /* + * Copy over the species molecular weights + */ + vcs_vdcopy(WtSpecies, pub->WtSpecies, nspecies); + + /* + * Copy over the charges + */ + vcs_vdcopy(Charge, pub->Charge, nspecies); + + /* + * Malloc and Copy the VCS_SPECIES_THERMO structures + * + */ + for (kspec = 0; kspec < nspecies; kspec++) { + if (SpeciesThermo[kspec] != NULL) { + delete SpeciesThermo[kspec]; + } + VCS_SPECIES_THERMO *spf = pub->SpeciesThermo[kspec]; + SpeciesThermo[kspec] = spf->duplMyselfAsVCS_SPECIES_THERMO(); + if (SpeciesThermo[kspec] == NULL) { + plogf(" duplMyselfAsVCS_SPECIES_THERMO returned an error!\n"); + return VCS_PUB_BAD; + } + } + + /* + * Copy the species unknown type + */ + vcs_icopy(VCS_DATA_PTR(SpeciesUnknownType), + VCS_DATA_PTR(pub->SpeciesUnknownType), nspecies); + + /* + * iest => Do we have an initial estimate of the species mole numbers ? + */ + iest = pub->iest; + + /* + * w[] -> Copy the equilibrium mole number estimate if it exists. + */ + if (pub->w.size() != 0) { + vcs_vdcopy(soln, pub->w, nspecies); + } else { + iest = -1; + vcs_dzero(VCS_DATA_PTR(soln), nspecies); + } + + /* + * Formulate the Goal Element Abundance Vector, gai[] + */ + if (pub->gai.size() != 0) { + for (i = 0; i < nelements; i++) gai[i] = pub->gai[i]; + } else { + if (iest == 0) { + for (j = 0; j < nelements; j++) { + gai[j] = 0.0; + for (kspec = 0; kspec < nspecies; kspec++) { + if (SpeciesUnknownType[kspec] != VCS_SPECIES_TYPE_INTERFACIALVOLTAGE) { + gai[j] += FormulaMatrix[j][kspec] * soln[kspec]; + } + } + } + } else { + plogf("%sElement Abundances, gai[], not specified\n", ser); + return VCS_PUB_BAD; + } + } + + /* + * zero out values that will be filled in later + */ + /* + * TPhMoles[] -> Untouched here. These will be filled in vcs_prep.c + * TPhMoles1[] + * DelTPhMoles[] + * + * + * T, Pres, copy over here + */ + if (pub->T > 0.0) T = pub->T; + else T = 293.15; + if (pub->Pres > 0.0) Pres = pub->Pres; + else Pres = 1.0; + /* + * TPhInertMoles[] -> must be copied over here + */ + for (iph = 0; iph < nph; iph++) { + Vphase = pub->VPhaseList[iph]; + TPhInertMoles[iph] = Vphase->TMolesInert; + } + + /* + * if__ : Copy over the units for the chemical potential + */ + m_VCS_UnitsFormat = pub->m_VCS_UnitsFormat; + + /* + * tolerance requirements -> copy them over here and later + */ + tolmaj = pub->tolmaj; + tolmin = pub->tolmin; + tolmaj2 = 0.01 * tolmaj; + tolmin2 = 0.01 * tolmin; + /* + * ind[] is an index variable that keep track of solution vector + * rotations. + */ + for (i = 0; i < nspecies; i++) ind[i] = i; + /* + * IndEl[] is an index variable that keep track of element vector + * rotations. + */ + for (i = 0; i < nelements; i++) IndEl[i] = i; + /* + * ir[] -> will be done below once nc is defined. + * ic[] -> Define all species to be major species, initially. + */ + for (i = 0; i < nspecies; i++) spStatus[i] = VCS_SPECIES_MAJOR; + /* + * PhaseID: Fill in the species to phase mapping + * -> Check for bad values at the same time. + */ + if (pub->PhaseID.size() != 0) { + std::vector numPhSp(nph, 0); + for (kspec = 0; kspec < nspecies; kspec++) { + iph = pub->PhaseID[kspec]; + if (iph < 0 || iph >= nph) { + plogf("%sSpecies to Phase Mapping, PhaseID, has a bad value\n", + ser); + plogf("\tPhaseID[%d] = %d\n", kspec, iph); + plogf("\tAllowed values: 0 to %d\n", nph - 1); + return VCS_PUB_BAD; + } + PhaseID[kspec] = pub->PhaseID[kspec]; + indPhSp[kspec] = numPhSp[iph]; + numPhSp[iph]++; + } + for (iph = 0; iph < nph; iph++) { + Vphase = pub->VPhaseList[iph]; + if (numPhSp[iph] != Vphase->NVolSpecies) { + plogf("%sNumber of species in phase %d, %s, doesn't match\n", + ser, iph, Vphase->PhaseName.c_str()); + return VCS_PUB_BAD; + } + } + } else { + if (NPhase == 1) { + for (kspec = 0; kspec < nspecies; kspec++) { + PhaseID[kspec] = 0; + indPhSp[kspec] = kspec; + } + } else { + plogf("%sSpecies to Phase Mapping, PhaseID, is not defined\n", ser); + return VCS_PUB_BAD; + } + } + + /* + * Copy over the element types + */ + m_elType.resize(nelements, VCS_ELEM_TYPE_ABSPOS); + ElActive.resize(nelements, 1); + + /* + * Copy over the element names + */ + for (i = 0; i < nelements; i++) { + ElName[i] = pub->ElName[i]; + m_elType[i] = pub->m_elType[i]; + ElActive[i] = pub->ElActive[i]; + if (!strncmp(ElName[i].c_str(), "cn_", 3)) { + m_elType[i] = VCS_ELEM_TYPE_CHARGENEUTRALITY; + if (pub->m_elType[i] != VCS_ELEM_TYPE_CHARGENEUTRALITY) { + plogf("we have an inconsistency!\n"); + exit(-1); + } + } + } + + /* + * Copy over the species names + */ + for (i = 0; i < nspecies; i++) { + SpName[i] = pub->SpName[i]; + } + /* + * Copy over all of the phase information + * Use the object's assignment operator + */ + for (iph = 0; iph < nph; iph++) { + *(VPhaseList[iph]) = *(pub->VPhaseList[iph]); + /* + * Fix up the species thermo pointer in the vcs_SpeciesThermo object + * It should point to the species thermo pointer in the private + * data space. + */ + Vphase = VPhaseList[iph]; + for (int k = 0; k < Vphase->NVolSpecies; k++) { + vcs_SpeciesProperties *sProp = Vphase->ListSpeciesPtr[k]; + int kT = Vphase->IndSpecies[k]; + sProp->SpeciesThermo = SpeciesThermo[kT]; + } + } + + /* + * Specify the Activity Convention information + */ + for (iph = 0; iph < nph; iph++) { + Vphase = VPhaseList[iph]; + PhaseActConvention[iph] = Vphase->ActivityConvention; + if (Vphase->ActivityConvention != 0) { + /* + * We assume here that species 0 is the solvent. + * The solvent isn't on a unity activity basis + * The activity for the solvent assumes that the + * it goes to one as the species mole fraction goes to + * one; i.e., it's really on a molarity framework. + * So SpecLnMnaught[iSolvent] = 0.0, and the + * loop below starts at 1, not 0. + */ + int iSolvent = Vphase->IndSpecies[0]; + double mnaught = WtSpecies[iSolvent] / 1000.; + for (int k = 1; k < Vphase->NVolSpecies; k++) { + int kspec = Vphase->IndSpecies[k]; + SpecActConvention[kspec] = Vphase->ActivityConvention; + SpecLnMnaught[kspec] = log(mnaught); + } + } + } + + /* + * Copy the title info + */ + if (pub->Title.size() == 0) { + Title = "Unspecified Problem Title"; + } else { + Title = pub->Title; + } + + /* + * Copy the volume info + */ + Vol = pub->Vol; + if (VolPM.size() != 0) { + vcs_dcopy(VCS_DATA_PTR(VolPM), VCS_DATA_PTR(pub->VolPM), nspecies); + } + + /* + * Return the success flag + */ + return VCS_SUCCESS; + } + /*****************************************************************************/ + /*****************************************************************************/ + /*****************************************************************************/ + // Specify the problem to be solved using VCS_PROB, incrementally + /* + * Use the contents of the VCS_PROB to specify the contents of the + * private data, VCS_SOLVE. + * + * It's assumed we are solving the same problem. + * + * @param pub Pointer to VCS_PROB that will be used to + * initialize the current equilibrium problem + */ + int VCS_SOLVE::vcs_prob_specify(const VCS_PROB *pub) { + int kspec, k, i, j, iph; + char *yo = "vcs_prob_specify ERROR: "; + int retn = VCS_SUCCESS; + bool status_change = false; + + T = pub->T; + Pres = pub->Pres; + m_VCS_UnitsFormat = pub->m_VCS_UnitsFormat; + iest = pub->iest; + + Vol = pub->Vol; + + tolmaj = pub->tolmaj; + tolmin = pub->tolmin; + tolmaj2 = 0.01 * tolmaj; + tolmin2 = 0.01 * tolmin; + + for (kspec = 0; kspec < m_numSpeciesTot; ++kspec) { + k = ind[kspec]; + soln[kspec] = pub->w[k]; + wt[kspec] = pub->mf[k]; + m_gibbsSpecies[kspec] = pub->m_gibbsSpecies[k]; + } + + /* + * Transfer the element abundance goals to the solve object + */ + for (i = 0; i < m_numElemConstraints; i++) { + j = IndEl[i]; + gai[i] = pub->gai[j]; + } + + /* + * Try to do the best job at guessing at the title + */ + if (pub->Title.size() == 0) { + if (Title.size() == 0) { + Title = "Unspecified Problem Title"; + } + } else { + Title = pub->Title; + } + + /* + * Copy over the phase information. + * -> For each entry in the phase structure, determine + * if that entry can change from its initial value + * Either copy over the new value or create an error + * condition. + */ + + for (iph = 0; iph < NPhase; iph++) { + vcs_VolPhase *vPhase = VPhaseList[iph]; + vcs_VolPhase *pub_phase_ptr = pub->VPhaseList[iph]; + + if (vPhase->VP_ID != pub_phase_ptr->VP_ID) { + plogf("%sPhase numbers have changed:%d %d\n", yo, + vPhase->VP_ID, pub_phase_ptr->VP_ID); + retn = VCS_PUB_BAD; + } + + if (vPhase->SingleSpecies != pub_phase_ptr->SingleSpecies) { + plogf("%sSingleSpecies value have changed:%d %d\n", yo, + vPhase->SingleSpecies, + pub_phase_ptr->SingleSpecies); + retn = VCS_PUB_BAD; + } + + if (vPhase->GasPhase != pub_phase_ptr->GasPhase) { + plogf("%sGasPhase value have changed:%d %d\n", yo, + vPhase->GasPhase, + pub_phase_ptr->GasPhase); + retn = VCS_PUB_BAD; + } + + vPhase->EqnState = pub_phase_ptr->EqnState; + + if (vPhase->NVolSpecies != pub_phase_ptr->NVolSpecies) { + plogf("%sNVolSpecies value have changed:%d %d\n", yo, + vPhase->NVolSpecies, + pub_phase_ptr->NVolSpecies); + retn = VCS_PUB_BAD; + } + + if (vPhase->PhaseName == pub_phase_ptr->PhaseName) { + plogf("%sPhaseName value have changed:%s %s\n", yo, + vPhase->PhaseName.c_str(), + pub_phase_ptr->PhaseName.c_str()); + retn = VCS_PUB_BAD; + } + + if (vPhase->TMolesInert != pub_phase_ptr->TMolesInert) { + status_change = true; + } + /* + * Copy over the number of inert moles if it has changed. + */ + TPhInertMoles[iph] = pub_phase_ptr->TMolesInert; + vPhase->TMolesInert = pub_phase_ptr->TMolesInert; + if (TPhInertMoles[iph] > 0.0) { + vPhase->Existence = 2; + vPhase->SingleSpecies = FALSE; + } + + /* + * Copy over the interfacial potential + */ + double phi = pub_phase_ptr->electricPotential(); + vPhase->setElectricPotential(phi); + } + + + if (status_change) vcs_SSPhase(); + /* + * Calculate the total number of moles in all phases. + */ + vcs_tmoles(); + + return retn; + } + /*****************************************************************************/ + /*****************************************************************************/ + /*****************************************************************************/ + // Transfer the results of the equilibrium calculation back to VCS_PROB + /* + * The VCS_PUB structure is returned to the user. + * + * @param pub Pointer to VCS_PROB that will get the results of the + * equilibrium calculation transfered to it. + */ + int VCS_SOLVE::vcs_prob_update(VCS_PROB *pub) { + int i, j, k1, l; + + + vcs_tmoles(); + Vol = vcs_VolTotal(T, Pres, VCS_DATA_PTR(soln), VCS_DATA_PTR(VolPM)); + + for (i = 0; i < m_numSpeciesTot; ++i) { + /* + * Find the index of I in the index vector, ind[]. + * Call it K1 and continue. + */ + for (j = 0; j < m_numSpeciesTot; ++j) { + l = ind[j]; + k1 = j; + if (l == i) break; + } + /* + * - Switch the species data back from K1 into I + */ + if (pub->SpeciesUnknownType[i] != VCS_SPECIES_TYPE_INTERFACIALVOLTAGE) { + pub->w[i] = soln[k1]; + } else { + pub->w[i] = 0.0; + plogf("voltage species = %g\n", soln[k1]); + } + pub->mf[i] = wt[k1]; + pub->m_gibbsSpecies[i] = m_gibbsSpecies[k1]; + pub->VolPM[i] = VolPM[k1]; + } + + pub->T = T; + pub->Pres = Pres; + pub->Vol = Vol; + int kT = 0; + for (int iph = 0; iph < pub->NPhase; iph++) { + vcs_VolPhase *pubPhase = pub->VPhaseList[iph]; + vcs_VolPhase *vPhase = VPhaseList[iph]; + pubPhase->Existence = vPhase->Existence; + // Note pubPhase is not the same as vPhase, since they contain + // different indexing into the solution vector. + // pubPhase->TMoles = vPhase->TMoles; + pubPhase->TMolesInert = vPhase->TMolesInert; + pubPhase->setTotalMoles(vPhase->TotalMoles()); + pubPhase->setElectricPotential(vPhase->electricPotential()); + double sumMoles = pubPhase->TMolesInert; + pubPhase->setMoleFractions(VCS_DATA_PTR(vPhase->moleFractions())); + for (int k = 0; k < pubPhase->NVolSpecies; k++) { + kT = pubPhase->IndSpecies[k]; + //pubPhase->Xmol[k] = vPhase->Xmol[k]; + pubPhase->SS0ChemicalPotential[k] = vPhase->SS0ChemicalPotential[k]; + pubPhase->StarChemicalPotential[k] = vPhase->StarChemicalPotential[k]; + pubPhase->StarMolarVol[k] = vPhase->StarMolarVol[k]; + pubPhase->PartialMolarVol[k] = vPhase->PartialMolarVol[k]; + pubPhase->ActCoeff[k] = vPhase->ActCoeff[k]; + + if (pubPhase->m_phiVarIndex == k) { + k1 = vPhase->IndSpecies[k]; + double tmp = soln[k1]; + if (! vcs_doubleEqual( pubPhase->electricPotential() , tmp)) { + plogf("We have an inconsistency in voltage, %g, %g\n", + pubPhase->electricPotential(), tmp); + exit(-1); + } + } + + + if (! vcs_doubleEqual( pub->mf[kT], vPhase->molefraction(k))) { + plogf("We have an inconsistency in mole fraction, %g, %g\n", + pub->mf[kT], vPhase->molefraction(k)); + exit(-1); + } + if (pubPhase->SpeciesUnknownType[k] != VCS_SPECIES_TYPE_INTERFACIALVOLTAGE) { + sumMoles += pub->w[kT]; + } + } + if (! vcs_doubleEqual(sumMoles, vPhase->TotalMoles())) { + plogf("We have an inconsistency in total moles, %g %g\n", + sumMoles, pubPhase->TotalMoles()); + exit(-1); + } + + } + + pub->m_Iterations = m_VCount->Its; + pub->m_NumBasisOptimizations = m_VCount->Basis_Opts; + + return VCS_SUCCESS; + } + /*****************************************************************************/ + /*****************************************************************************/ + /*****************************************************************************/ + // Initialize the internal counters + /* + * Initialize the internal counters containing the subroutine call + * values and times spent in the subroutines. + * + * ifunc = 0 Initialize only those counters appropriate for the top of + * vcs_solve_TP(). + * = 1 Initialize all counters. + */ + void VCS_SOLVE::vcs_counters_init(int ifunc) { + m_VCount->Its = 0; + m_VCount->Basis_Opts = 0; + m_VCount->Time_vcs_TP = 0.0; + m_VCount->Time_basopt = 0.0; + if (ifunc) { + m_VCount->T_Its = 0; + m_VCount->T_Basis_Opts = 0; + m_VCount->T_Calls_Inest = 0; + m_VCount->T_Calls_vcs_TP = 0; + m_VCount->T_Time_vcs_TP = 0.0; + m_VCount->T_Time_basopt = 0.0; + m_VCount->T_Time_inest = 0.0; + m_VCount->T_Time_vcs = 0.0; + } + } + + + +} + diff --git a/Cantera/src/equil/vcs_solve.h b/Cantera/src/equil/vcs_solve.h new file mode 100644 index 000000000..04c1ebc06 --- /dev/null +++ b/Cantera/src/equil/vcs_solve.h @@ -0,0 +1,807 @@ +/** + * @file vcs_solve.h + * Header file for the internal object that holds the problem + */ +/* + * $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. + */ + + +#ifndef _VCS_SOLVE_H +#define _VCS_SOLVE_H + +/* +* Index of Symbols +* ------------------- +* irxn -> refers to the species or rxn between the species and +* the components in the problem +* k -> refers to the species +* j -> refers to the element or component +* +* ### -> to be eliminated +*/ +#include +#include + +#include "vcs_defs.h" +#include "vcs_DoubleStarStar.h" +#include "vcs_IntStarStar.h" + +namespace VCSnonideal { +/* + * Forward references + */ +class vcs_VolPhase; +class VCS_SPECIES_THERMO; +class VCS_PROB; +class VCS_COUNTERS; + + +//! This is the main structure used to hold the internal data +//! used in vcs_solve_TP(), and to solve TP systems. +/*! + * The indecises of information in this + * structure may change when the species basis changes or when + * phases pop in and out of existence. Both of these operations + * change the species ordering. + * + */ +class VCS_SOLVE { +public: + //! Constructor for the VCS_SOLVE class + VCS_SOLVE(); + + //! Destructor + ~VCS_SOLVE(); + + void InitSizes(int nspecies0, int nelements, int nphase0); + + //! Solve an equilibrium problem + /*! + * This is the main interface routine to the equilibrium solver + * + * Input: + * @param vprob Object containing the equilibrium Problem statement + * + * @param ifunc Determines the operation to be done: Valid values: + * 0 -> Solve a new problem by initializing structures + * first. An initial estimate may or may not have + * been already determined. This is indicated in the + * VCS_PROB structure. + * 1 -> The problem has already been initialized and + * set up. We call this routine to resolve it + * using the problem statement and + * solution estimate contained in + * the VCS_PROB structure. + * 2 -> Don't solve a problem. Destroy all the private + * structures. + * + * @param ipr Printing of results + * ipr = 1 -> Print problem statement and final results to + * standard output + * 0 -> don't report on anything + * @param ip1 Printing of intermediate results + * IP1 = 1 -> Print intermediate results. + * + * @param maxit Maximum number of iterations for the algorithm + * + * @param iprintTime Printing of time information. Default = -1, + * implies printing if other printing is turned + * on. + * + * Output: + * + * @return + * nonzero value: failure to solve the problem at hand. + * zero : success + */ + int vcs(VCS_PROB *vprob, int ifunc, int ipr, int ip1, int maxit, + int iprintTime = -1); + + int vcs_solve_TP(int, int, int); + + void vcs_reinsert_deleted(int kspec); + int vcs_basopt(int ifirst, double aw[], double sa[], double sm[], + double ss[], double test, int *usedZeroedSpecies); + int vcs_species_type(int kspec); + void vcs_chemPotPhase(int iph, const double *const molNum, + double * const ac, double * const mu_i, + bool do_deleted = false); + void vcs_dfe(double *z, int kk, int ll, int lbot, int ltop); + void vcs_updateVP(int place); + int vcs_RxnStepSizes(void); + void vcs_tmoles(void); + void vcs_deltag(int l, bool doDeleted); + void vcs_switch_pos(int ifunc, int k1, int k2); + void vcs_deltag_Phase(int iphase, bool doDeleted); + + //! birthGuess 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. + /*! + * Do a minor alt calculation. But, cap the mole numbers at + * 1.0E-15. + * For SS phases use VCS_DELETE_SPECIES_CUTOFF * 100. + * + * The routine makes sure the guess doesn't reduce the concentration + * of a component by more than 1/3. Note this may mean that + * the vlaue coming back from this routine is zero or a + * very small number. + * + * @param kspec Species number that is coming back to life + * @return number of moles of the species + */ + double vcs_birthGuess(int kspec); + + //! Solve an equilibrium problem at a particular fixed temperature + //! and pressure + /*! + * The actual problem statement is assumed to be in the structure + * already. This is a wrapper around the solve_TP() function. + * In this wrapper, we nondimensionalize the system + * we calculate the standard state gibbs free energies of the + * species, and we decide whether to we need to use the + * initial guess algorithm. + * + * @param ipr = 1 -> Print results to standard output + * 0 -> don't report on anything + * @param ip1 = 1 -> Print intermediate results. + * 0 -> Dont print any intermediate results + * @param maxit Maximum number of iterations for the algorithm + * @param T Value of the Temperature (Kelvin) + * Param pres Value of the Pressure (units given by m_VCS_UnitsFormat variable + * + * @return Returns an integer representing the success of the algorithm + * 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 vcs_TP(int ipr, int ip1, int maxit, double T, double pres); + + int vcs_evalSS_TP(int ipr, int ip1, double Temp, double pres); + void vcs_fePrep_TP(void); + int vcs_TV(int ipr, int ip1, int maxit, double T, double VolRequest); + double vcs_VolTotal(double, double, double [], double []); + + int vcs_prep_oneTime(int printLvl); + + //! Prepare the object for resolution + /*! + * This routine is mostly concerned with changing the private data + * to be consistent with that needed for solution. It is called for + * every invocation of the vcs_solve() except for the cleanup invocation. + * + * Tasks: + * 1) Initialization of arrays to zero. + * + * return code + * VCS_SUCCESS = everything went OK + * VCS_PUB_BAD = There is an irreconcilable difference in the + * public data structure from when the problem was + * initially set up. + */ + int vcs_prep(void); + + bool vcs_wellPosed(VCS_PROB *vprob); + + int vcs_elem_rearrange(double *aw, double *sa, double *sm, double *ss); + void vcs_switch_elem_pos(int ipos, int jpos); + + int vcs_rxn_adj_cg(void); + double vcs_Hessian_diag_adj(int, double); + double vcs_Hessian_actCoeff_diag(int irxn); + void vcs_CalcLnActCoeffJac(const double * const moleSpeciesVCS); +#ifdef DEBUG + double vcs_line_search(int irxn, double dx_orig, char *ANOTE); +#else + double vcs_line_search(int irxn, double dx_orig); +#endif + + int vcs_report(int); + + int vcs_rearrange(void); + + + double vcs_nondim_Farad(int mu_units, double TKelvin); + double vcs_nondimMult_TP(int mu_units, double TKelvin); + void vcs_nondim_TP(void); + void vcs_redim_TP(void); + void vcs_printChemPotUnits(int unitsFormat); + + void vcs_elab(void); + int vcs_elabcheck(int ibound); + void vcs_elabPhase(int iphase, double * const elemAbundPhase); + int vcs_elcorr(double aa[], double x[]); + + int vcs_inest_TP(void); + +#ifdef ALTLINPROG + //! Extimate the initial mole numbers by constrained linear programming + /*! + * This is done by running + * each reaction as far forward or backward as possible, subject + * to the constraint that all mole numbers remain + * non-negative. Reactions for which \f$ \Delta \mu^0 \f$ are + * positive are run in reverse, and ones for which it is negative + * are run in the forward direction. The end result is equivalent + * to solving the linear programming problem of minimizing the + * linear Gibbs function subject to the element and + * non-negativity constraints. + */ + int vcs_setMolesLinProg(); +#endif + + double vcs_Total_Gibbs(double *w, double *fe, double *tPhMoles); + double vcs_GibbsPhase(int iphase, double *w, double *fe); + + double vcs_Gxs_phase_calc(vcs_VolPhase *Vphase, double *mf_PO); + double vcs_Gxs_calc(int iphase); + + //! Transfer the results of the equilibrium calculation back to VCS_PROB + /*! + * The VCS_PUB structure is returned to the user. + * + * @param pub Pointer to VCS_PROB object that will get the results of the + * equilibrium calculation transfered to it. + */ + int vcs_prob_update(VCS_PROB *pub); + + //! Fully specify the problem to be solved using VCS_PROB + /*! + * Use the contents of the VCS_PROB to specify the contents of the + * private data, VCS_SOLVE. + * + * @param pub Pointer to VCS_PROB that will be used to + * initialize the current equilibrium problem + */ + int vcs_prob_specifyFully(const VCS_PROB *pub); + + //! Specify the problem to be solved using VCS_PROB, incrementally + /*! + * Use the contents of the VCS_PROB to specify the contents of the + * private data, VCS_SOLVE. + * + * It's assumed we are solving the same problem. + * + * @param pub Pointer to VCS_PROB that will be used to + * initialize the current equilibrium problem + */ + int VCS_SOLVE::vcs_prob_specify(const VCS_PROB *pub); + +private: + int zero_species(int kspec); + int delete_species(int kspec); + void delete_multiphase(int iph); + int delta_species(int kspec, double *delta_ptr); + void add_deleted(void); + int recheck_deleted(void); + + //! Alternative treatment for the update of a minor species + /*! + * @param kspec Species index of the minor species + * @param irxn Rxn index of the same minor species + * @param do_delete + */ + double minor_alt_calc(int kspec, int irxn, int *do_delete +#ifdef DEBUG + , char *ANOTE +#endif + ); + + int force(int iti); + void vcs_switch2D(double * const * const Jac, int k1, int k2); + double l2normdg(double dg[]); +#ifdef DEBUG + void prneav(void); + void checkDelta1(double * const ds, double * const delTPhMoles, int kspec); +#endif + void inest(double *aw, double *sa, double *sm, + double *ss, double test); + void vcs_SSPhase(void); + double deltaG_Recalc_Rxn(int irxn, const double *const molNum, + double * const ac, double * const mu_i); + void delete_memory(); + + //! Initialize the internal counters + /*! + * Initialize the internal counters containing the subroutine call + * values and times spent in the subroutines. + * + * ifunc = 0 Initialize only those counters appropriate for the top of + * vcs_solve_TP(). + * = 1 Initialize all counters. + */ + void vcs_counters_init(int ifunc); + + void vcs_TCounters_report(void); + +public: + //! value of the number of species used to malloc data structures + int NSPECIES0; + + //! value of the number of phases used to malloc data structures + int NPHASE0; + + //! Total number of species in the problems + int m_numSpeciesTot; + + //! Number of element constraints in the problem + /*! + * This is typically equal to the number of elements in the problem + */ + int m_numElemConstraints; + + //! Number of components calculated for the problem + int m_numComponents; + + //! Total number of non-component species in the problem + int m_numRxnTot; + + //! Current number of species in the problems + /*! + * Species can be deleted if they aren't + * stable under the current conditions + */ + int m_numSpeciesRdc; + + //! Current number of non-component species in the problem + /*! + * Species can be deleted if they aren't + * stable under the current conditions + */ + int m_numRxnRdc; + + //! Number of active species which are currently either zeroed out or + //! are minor species + int m_numRxnMinorZeroed; + + //! Number of Phases in the problem + int NPhase; + + //! Formula matrix for the problem + /*! + * FormulaMatrix[j][kspec] = Number of elements, j, in the kspec species + * + * Both element and species indecies are swapped. + */ + DoubleStarStar FormulaMatrix; + + //! Stoichiometric coefficient matrix for for the reaction mechanism + //! expressed in Reduced Canonical Form. + /*! + * This is the stoichiometric coefficient matrix for the + * reaction which forms species K from the component species. A + * stoichiometric coefficient of one is assumed for the + * species K in this mechanism. + * + * NOTE: K = IRXN + NC + * + * sc[irxn][j] : + * j refers to the component number, and irxn + * refers to the irxn_th non-component species. + * + * + * length = [nspecies0][nelements0] + */ + DoubleStarStar sc; + + //! Absolute size of the stoichiometric coefficients + /*! + * scSize[irxn] = abs(Size) of the stoichiometric + * coefficients. These are used to determine + * whether a given species should be + * handled by the alt_min treatment or + * should be handled as a major species. + */ + std::vector scSize; + + //! Dimensionless/Dimensional free energy for all the species in the mechanism at the + //! current T, P, and mole numbers. + /*! + * The first NC entries are for components. The following + * NR entries are for the current non-component species in the mechanism. + * The dimension of this vector is specified by the m_VCS_UnitsFormat variable. + */ + std::vector m_gibbsSpecies; + + //! Standard state chemical potentials for species K at the current + //! temperature and pressure. + /*! + * The first NC entries are for components. The following NR entries are + * for the current non-component species in the mechanism. + */ + std::vector ff; + + //! Dimensionless trial free energy for all the species in the mechanism + //! at the current T, P, and mole numbers. + /*! + * The first NC entries are for components. The following + * NR entries are for the current non-component species in the mechanism. + */ + std::vector feTrial; + + //! Setting for the initial estimate + /*! + * Initial estimate: 0 user estimate + * -1 machine estimate + */ + int iest; + + //! Total moles of the species + /*! + * soln[k] = Total number of moles of the kth species. + * Length = Total number of species = m + */ + std::vector soln; + + //! Specifies the species unknown type + /*! + * There are two types. One is the straightforward + * species, with the mole number w[k], as the + * unknown. The second is the an interfacial + * voltage where w[k] refers to the interfacial + * voltage in volts. + * These species types correspond to metalic + * electrons corresponding to electrodes. + * The voltage and other interfacial conditions + * sets up an interfacial current, which is + * set to zero in this initial treatment. + * Later we may have non-zero interfacial currents. + */ + std::vector SpeciesUnknownType; + + //! Change in the number of moles of phase, iphase, due to the noncomponent formation + //! reaction, irxn, for species, k: + /*! + * DnPhase[irxn][iphase] = k = nc + irxn + */ + DoubleStarStar DnPhase; + + //! This is 1 if the phase, iphase, participates in the formation reaction + //! irxn, and zero otherwise. PhaseParticipation[irxn][iphase] + IntStarStar PhaseParticipation; + + //! electric potential of the iph phase + std::vector phasePhi; + + //! Tentative value of the mole number vector. It's also used to store the + //! mole fraction vector. + std::vector wt; + + //! Delta G(I) for the noncomponent species in the mechanism. + /*! + * Computed by the subroutine DELTAG. DG is the free + * energy change for the reaction which + * forms species K from the + * component species. This vector has length + * equal to the number of noncomponent + * species in the mechanism. It starts with + * the first current noncomponent species + * in the mechanism. + */ + std::vector dg; + + //! Last deltag[irxn] from the previous step + std::vector dgl; + + //! Reaction Adjustments for each species + /*! + * Length = number of species + */ + std::vector ds; + + std::vector fel; /* fel[k] = Old Free Energy vector from the previous + * iteration. fe[] is copied into fel[] */ + std::vector ga; /* ga[j] = Element abundances for jth element from + * estimate + * -> this is calculated from the current mole + * fraction vector and BM, the formula + * vector. + * units = gmoles */ + std::vector gai; /* gai[j] = Element abundances for jth element + * -> corrected + * -> this is input from the input file and + * is considered a constant from thereon. + * units = gmoles */ + double TMoles; /* TMoles = Total number of moles in all phases + * This number includes the inerts. + * -> Don't use this except for scaling + * purposes only */ + + //! total gmols of species in each phase + /*! + * This contains the total number of moles of species in each phase + * + * Length = number of phases + */ + std::vector TPhMoles; + + //! total gmols of species in each phase in the tentative soln vector + /*! + * This contains the total number of moles of species in each phase + * in the tentative solution vector + * + * Length = number of phases + */ + std::vector TPhMoles1; + + //! Temporary vector of length NPhase + std::vector TmpPhase; + + //! Temporary vector of length NPhase + std::vector TmpPhase2; + + //! Change in the total moles in each phase + /*! + * Length number of phases. + */ + std::vector DelTPhMoles; + + //! Temperature (Kelvin) + double T; + + //! Pressure (units are Pascals) + double Pres; + + //! Total kmoles of inert to add to each phase + /*! + * TPhInertMoles[iph] = Total gmoles of inert to add to each phase + * length = number of phases + */ + std::vector TPhInertMoles; + + double tolmaj; /* tolmaj = Tolerance requirement for major species */ + double tolmin; /* tolmin = Tolerance requirement for minor species */ + double tolmaj2; /* tolmaj2 = Below this, major species aren't refined + * any more */ + double tolmin2; /* tolmin2 = Below this, minor species aren't refined + * any more */ + std::vector ind; /* ind[k] = Index vector that keeps track of the + * rearrangement + * of the species vector within the problem. + * -> At the end of each run, the species + * vector and associated data gets put back + * in the original order. */ + + //! Index that keeps track of the index of the species according + //! to the phase + /*! + * indPhSp[k] = Index that keeps track of the index of the species according + * to the phase + * Length = number of species + */ + std::vector indPhSp; + + //! Index vector that keeps track of the rearrangement of the elements + /*! + * IndEl[j] + */ + std::vector IndEl; + + //! Mapping between the species index for noncomponent species and the + //! full species index. + /*! + * ir[irxn] = Mapping between the species index for + * noncomponent species and the full species + * index. + * - Initially set to a value of K = NC + I + * This vector has length equal to number + * of noncomponent species in the mechanism. + * It starts with the first current + * noncomponent species in the mechanism. + */ + std::vector ir; + + //! Major - Minor status Vector for the noncomponent + /*! + * species irxn : 1 -> Major player VCS_SPECIES_MAJOR + * 0 -> Minor player VCS_SPECIES_MINOR + * -1 -> Mole number is zero + * in inactive phase VCS_SPECIES_ZEROEDPHASE + * -2 -> Deleted species in an + * active multicom phase VCS_SPECIES_ZEROEDMS + * -3 -> Mole number is zero + * in a stoich phase - VCS_SPECIES_ZEREODSS + * -4 -> Species is deleted + * - VCS_SPECIES_DELETED + * -> Length equal to number of + * non-components*/ + std::vector spStatus; + + //! Mapping from the species number to the phase number + std::vector PhaseID; + + //! Boolean indicating whether a species to single species phase + std::vector SSPhase; + + + //! Species string name for the kth species + /*! + * SpName[k] = Species string name for the kth species + */ + std::vector SpName; + + //! Vector of strings containing the element names + /*! + * ElName[j] = String containing element names + */ + std::vector ElName; + + //! Type of the element constraint + /*! + * m_elType[j] = type of the element + * 0 VCS_ELEM_TYPE_ABSPOS Normal element that is positive + * or zero in all species. + * 1 VCS_ELEM_TPYE_ELECTRONCHARGE element dof that corresponds + * to the electronic charge DOF. + * 2 VCS_ELEM_TYPE_CHARGENEUTRALITY element dof that + * corresponds to a required charge + * neutrality constraint on the phase. + * The element abundance is always exactly zero. + * 3 VCS_ELEM_TYPE_OTHERCONSTRAINT Other constraint which may + * mean that a species has neg 0 or pos value + * of that constraint (other than charge) + */ + std::vector m_elType; + + //! Specifies whether an element constraint is active + /*! + * The default is true + * Length = nelements + */ + std::vector ElActive; + + //! Array of Phase Structures + /*! + * Length = number of phases + */ + std::vector VPhaseList; + + //! String containing the title of the run + std::string Title; + + //! This specifies the current state of units for the Gibbs free energy + //! properties in the program. + /*! + *. The default is to have this unitless + */ + char UnitsState; + + //! specifies the activity convention of the phase containing the species + /*! + * SpecActConvention[kspec] + * 0 = molar based + * 1 = molality based + * length = number of species + */ + std::vector SpecActConvention; + + //! specifies the activity convention of the phase. + /*! + * 0 = molar based + * 1 = molality based + * length = number of phases + */ + std::vector PhaseActConvention; + + //! specifies the ln(Mnaught) used to calculate the chemical potentials + /*! + * For molar based activity conventions + * this will be equal to 0.0 + * length = number of species + */ + std::vector SpecLnMnaught; + + //! Activity Coefficients for Species + /*! + * + * Length = number of species + */ + std::vector ActCoeff; + + //! Activity Coefficients for Species + /*! + * + * Length = number of species + */ + std::vector ActCoeff0; + + //! Change in activity coefficient with mole number + /*! + * length = [nspecies][nspecies] + * + * (This is a temporary array that + * gets regenerated every time it's + * needed. It is not swapped wrt species + * (unused atm) + */ + DoubleStarStar dLnActCoeffdMolNum; + + //! This boolean indicates whether the activity coefficients for a phase + //! are current. + std::vector CurrPhAC; + + //! Molecular weight of each species + /*! + * units = gm/gmol + * length = number of species + */ + std::vector WtSpecies; + + //! Charge of each species + /*! + * Length = number of species + */ + std::vector Charge; + + //! Vector of pointers to thermostructures which identify the model + //! and parameters for evaluating the thermodynamic functions for that + //! particular species. + /*! + * SpeciesThermo[k] pointer to the thermo information for the kth species + */ + std::vector SpeciesThermo; + + //! Choice of Hessians + /*! + * If this is true, then we will use a better approximation to the + * Hessian based on Jacobian of the ln(ActCoeff) with respect to mole + * numbers + */ + int UseActCoeffJac; + + double Vol; /* Vol = Volume (cm^3) */ + + //! Partialm molar volumes of the species + /*! + * units = mks (m^3/kmol) -determined by m_VCS_UnitsFormat + * Length = number of species + */ + std::vector VolPM; + + //! dimensionless value of Faraday's constant + /*! + * F / RT (1/volt) + */ + double Faraday_dim; + + + VCS_COUNTERS *m_VCount; + +#ifdef DEBUG + int vcs_debug_print_lvl; +#endif + + //! Units for the chemical potential data: + /*! + * VCS_UnitsFormat = Units for the chemical potential data: + * -1: kcal/mol + * 0: MU/RT + * 1: kJ/mol + * 2: Kelvin + * 3: J / kmol + * and pressure data: + * -1: atm + * 0: atm + * 1: atm + * 2: atm + * 3: Pa + */ + int m_VCS_UnitsFormat; + +}; + +} +#endif + diff --git a/Cantera/src/equil/vcs_solve_TP.cpp b/Cantera/src/equil/vcs_solve_TP.cpp new file mode 100644 index 000000000..fed624b18 --- /dev/null +++ b/Cantera/src/equil/vcs_solve_TP.cpp @@ -0,0 +1,4984 @@ +/*! + * @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; +} +/*****************************************************************/ +} + diff --git a/Cantera/src/equil/vcs_species_thermo.cpp b/Cantera/src/equil/vcs_species_thermo.cpp new file mode 100644 index 000000000..6f783a78c --- /dev/null +++ b/Cantera/src/equil/vcs_species_thermo.cpp @@ -0,0 +1,494 @@ +/* + * $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_species_thermo.h" +#include "vcs_defs.h" +#include "vcs_VolPhase.h" +#include "vcs_nasa_poly.h" +#include "vcs_Exception.h" +#include "vcs_internal.h" + +using namespace std; + +namespace VCSnonideal { + +/***************************************************************************** + * + * constructor(): + */ + +VCS_SPECIES_THERMO::VCS_SPECIES_THERMO(int indexPhase, + int indexSpeciesPhase) : + + IndexPhase(indexPhase), + IndexSpeciesPhase(indexSpeciesPhase), + OwningPhase(0), + SS0_Model(VCS_SS0_CONSTANT), + SS0_feSave(0.0), + SS0_TSave(-90.0), + SS0_T0(273.15), + SS0_H0(0.0), + SS0_S0(0.0), + SS0_Cp0(0.0), + SS0_Pref(1.0), + SS0_Params(0), + SSStar_Model(VCS_SSSTAR_CONSTANT), + SSStar_Params(0), + Activity_Coeff_Model(VCS_AC_CONSTANT), + Activity_Coeff_Params(0), + SSStar_Vol_Model(VCS_SSVOL_IDEALGAS), + SSStar_Vol_Params(0), + SSStar_Vol0(-1.0), + UseCanteraCalls(false), + m_VCS_UnitsFormat(VCS_UNITS_UNITLESS) +{ + /* + * Set up the numerical value for P_reference, based on the current + * global units choice. + */ + if (m_VCS_UnitsFormat == VCS_UNITS_MKS) { + SS0_Pref = 1.0133E5; + } else { + SS0_Pref = 1.0; + } +} + + +/****************************************************************************** + * + * destructor + */ +VCS_SPECIES_THERMO::~VCS_SPECIES_THERMO() +{ + if (SS0_Model == VCS_SS0_NASA_POLY) { + vcs_nasa_poly_destroy((VCS_NASA_POLY **) &(this->SS0_Params)); + SS0_Params = 0; + } +} + +/***************************************************************************** + * + * Copy Constructor VCS_SPECIES_THERMO + */ +VCS_SPECIES_THERMO::VCS_SPECIES_THERMO(const VCS_SPECIES_THERMO& b) : + IndexPhase(b.IndexPhase), + IndexSpeciesPhase(b.IndexSpeciesPhase), + OwningPhase(b.OwningPhase), + SS0_Model(b.SS0_Model), + SS0_feSave(b.SS0_feSave), + SS0_TSave(b.SS0_TSave), + SS0_T0(b.SS0_T0), + SS0_H0(b.SS0_H0), + SS0_S0(b.SS0_S0), + SS0_Cp0(b.SS0_Cp0), + SS0_Pref(b.SS0_Pref), + SS0_Params(0), + SSStar_Model(b.SSStar_Model), + SSStar_Params(0), + Activity_Coeff_Model(b.Activity_Coeff_Model), + Activity_Coeff_Params(0), + SSStar_Vol_Model(b.SSStar_Vol_Model), + SSStar_Vol_Params(0), + SSStar_Vol0(b.SSStar_Vol0), + UseCanteraCalls(b.UseCanteraCalls), + m_VCS_UnitsFormat(b.m_VCS_UnitsFormat) +{ + switch (SS0_Model) { + case VCS_SS0_NASA_POLY: + VCS_NASA_POLY *ppp = (VCS_NASA_POLY *) b.SS0_Params; + SS0_Params = (void *) new VCS_NASA_POLY(*ppp); + break; + default: + break; + } +} + +/***************************************************************************** + * + * Assignment operator for VCS_SPECIES_THERMO + */ +VCS_SPECIES_THERMO& +VCS_SPECIES_THERMO::operator=(const VCS_SPECIES_THERMO& b) +{ + if (&b != this) { + IndexPhase = b.IndexPhase; + IndexSpeciesPhase = b.IndexSpeciesPhase; + OwningPhase = b.OwningPhase; + SS0_Model = b.SS0_Model; + SS0_feSave = b.SS0_feSave; + SS0_TSave = b.SS0_TSave; + SS0_T0 = b.SS0_T0; + SS0_H0 = b.SS0_H0; + SS0_S0 = b.SS0_S0; + SS0_Cp0 = b.SS0_Cp0; + SS0_Pref = b.SS0_Pref; + + switch (SS0_Model) { + case VCS_SS0_NASA_POLY: + VCS_NASA_POLY *ppp = (VCS_NASA_POLY *) b.SS0_Params; + SS0_Params = (void *) new VCS_NASA_POLY(*ppp); + break; + default: + break; + } + + SSStar_Model = b.SSStar_Model; + /* + * shallow copy because function is undeveloped. + */ + SSStar_Params = b.SSStar_Params; + Activity_Coeff_Model = b.Activity_Coeff_Model; + /* + * shallow copy because function is undeveloped. + */ + Activity_Coeff_Params = b.Activity_Coeff_Params; + SSStar_Vol_Model = b.SSStar_Vol_Model; + /* + * shallow copy because function is undeveloped. + */ + SSStar_Vol_Params = b.SSStar_Vol_Params; + SSStar_Vol0 = b.SSStar_Vol0; + UseCanteraCalls = b.UseCanteraCalls; + m_VCS_UnitsFormat = b.m_VCS_UnitsFormat; + } + return *this; +} + +/****************************************************************************** + * + * duplMyselfAsVCS_SPECIES_THERMO(): (virtual) + * + * This routine can duplicate inherited objects given a base class + * pointer. It relies on valid copy constructors. + */ + +VCS_SPECIES_THERMO* VCS_SPECIES_THERMO::duplMyselfAsVCS_SPECIES_THERMO() { + VCS_SPECIES_THERMO* ptr = new VCS_SPECIES_THERMO(*this); + return ptr; +} + + +/************************************************************************** + * + * GStar_R_calc(); + * + * This function calculates the standard state Gibbs free energy + * for species, kspec, at the solution temperature TKelvin and + * solution pressure, Pres. + * + * + * Input + * kglob = species global index. + * TKelvin = Temperature in Kelvin + * pres = pressure is given in units specified by if__ variable. + * + * + * Output + * return value = standard state free energy in units of Kelvin. + */ +double VCS_SPECIES_THERMO::GStar_R_calc(int kglob, double TKelvin, + double pres) +{ + char yo[] = "VCS_SPECIES_THERMO::GStar_R_calc "; + double fe, T; + fe = G0_R_calc(kglob, TKelvin); + T = TKelvin; + if (UseCanteraCalls) { + AssertThrowVCS(m_VCS_UnitsFormat == VCS_UNITS_MKS, "Possible inconsistency"); + int kspec = IndexSpeciesPhase; + fe = OwningPhase->GStar_calc_one(kspec, TKelvin, pres); + double R = vcsUtil_gasConstant(m_VCS_UnitsFormat); + fe /= R; + } else { + double pref = SS0_Pref; + switch(SSStar_Model) { + case VCS_SSSTAR_CONSTANT: + break; + case VCS_SSSTAR_IDEAL_GAS: + fe += T * log( pres/ pref ); + break; + default: + plogf("%sERROR: unknown SSStar model\n", yo); + exit(-1); + } + } + return fe; +} + +/************************************************************************** + * + * VolStar_calc: + * + * This function calculates the standard state molar volume + * for species, kspec, at the temperature TKelvin and pressure, Pres, + * + * Input + * + * Output + * return value = standard state volume in cm**3 per mol. + * (VCS_UNITS_MKS) m**3 / kmol + */ +double VCS_SPECIES_THERMO:: +VolStar_calc(int kglob, double TKelvin, double pres) +{ + char yo[] = "VCS_SPECIES_THERMO::VStar_calc "; + double vol, T; + + T = TKelvin; + if (UseCanteraCalls) { + AssertThrowVCS(m_VCS_UnitsFormat == VCS_UNITS_MKS, "Possible inconsistency"); + int kspec = IndexSpeciesPhase; + vol = OwningPhase->VolStar_calc_one(kspec, TKelvin, pres); + } else { + switch(SSStar_Vol_Model) { + case VCS_SSVOL_CONSTANT: + vol = SSStar_Vol0; + break; + case VCS_SSVOL_IDEALGAS: + if (m_VCS_UnitsFormat == VCS_UNITS_MKS) { + vol = 8.31451E3 * T / pres; + } else { + vol= 83.14510 / 1.01325 * T / pres; + } + break; + default: + plogf("%sERROR: unknown SSVol model\n", yo); + exit(-1); + } + } + return vol; +} + +/************************************************************************** + * + * G0_R_calc: + * + * This function calculates the naught state Gibbs free energy + * for species, kspec, at the temperature TKelvin + * + * Input + * kglob = species global index. + * TKelvin = Temperature in Kelvin + * + * Output + * return value = naught state free energy in Kelvin. + */ +double VCS_SPECIES_THERMO::G0_R_calc(int kglob, double TKelvin) +{ +#ifdef DEBUG + char yo[] = "VS_SPECIES_THERMO::G0_R_calc "; +#endif + double fe, H, S; + if (SS0_Model == VCS_SS0_CONSTANT) { + fe = SS0_feSave; + return fe; + } + if (TKelvin == SS0_TSave) { + fe = SS0_feSave; + return fe; + } + if (UseCanteraCalls) { + AssertThrowVCS(m_VCS_UnitsFormat == VCS_UNITS_MKS, "Possible inconsistency"); + int kspec = IndexSpeciesPhase; + fe = OwningPhase->G0_calc_one(kspec, TKelvin); + double R = vcsUtil_gasConstant(m_VCS_UnitsFormat); + fe /= R; + } else { + switch (SS0_Model) { + case VCS_SS0_CONSTANT: + fe = SS0_feSave; + break; + case VCS_SS0_CONSTANT_CP: + H = SS0_H0 + (TKelvin - SS0_T0) * SS0_Cp0; + S = SS0_Cp0 + SS0_Cp0 * log((TKelvin / SS0_T0)); + fe = H - TKelvin * S; + break; + case VCS_SS0_NASA_POLY: + fe = vcs_G0_NASA(TKelvin, (VCS_NASA_POLY *) SS0_Params); + break; + default: +#ifdef DEBUG + plogf("%sERROR: unknown model\n", yo); +#endif + exit(-1); + } + } + SS0_feSave = fe; + SS0_TSave = TKelvin; + return fe; +} + +/************************************************************************** + * + * eval_ac: + * + * This function evaluates the activity coefficient + * for species, kspec + * + * Input + * kglob -> integer value of the species in the global + * species list within VCS_GLOB. Phase and local species id + * can be looked up within object. + * + * Note, T, P and mole fractions are obtained from the + * single private instance of VCS_GLOB + * + * + * Output + * return value = activity coefficient for species kspec + */ +double VCS_SPECIES_THERMO::eval_ac(int kglob) +{ +#ifdef DEBUG + char yo[] = "VCS_SPECIES_THERMO::eval_ac "; +#endif + double ac; + /* + * Activity coefficients are frequently evaluated on a per phase + * basis. If they are, then the currPhAC[] boolean may be used + * to reduce repeated work. Just set currPhAC[iph], when the + * activity coefficients for all species in the phase are reevaluated. + */ + if (UseCanteraCalls) { + int kspec = IndexSpeciesPhase; + ac = OwningPhase->AC_calc_one(kspec); + } else { + switch (Activity_Coeff_Model) { + case VCS_AC_CONSTANT: + ac = 1.0; + break; + case VCS_AC_DEBYE_HUCKEL: + + plogf("Not implemented Yet\n"); + exit(-1); + break; + + case VCS_AC_REGULAR_SOLN: + + plogf("Not implemented Yet\n"); + exit(-1); + break; + + case VCS_AC_MARGULES: + + plogf("Not implemented Yet\n"); + exit(-1); + break; + default: +#ifdef DEBUG + plogf("%sERROR: unknown model\n", yo); +#endif + exit(-1); + } + } + return ac; +} +/*****************************************************************************/ +/*****************************************************************************/ +/*****************************************************************************/ + +double VCS_SOLVE::vcs_Gxs_phase_calc(vcs_VolPhase *Vphase, double *mf_PO) + + /************************************************************************** + * + * vcs_Gxs_calc: + * + * This function evaluates the Gibbs Excess free energy function for + * the phase pointed to by Vphase. + * + * There are two ways. They may be evaluated from the + * activity coefficients themselves + * + * Gxs/RT = sum_i_inphase( X_i * ln (ActCoeff_i)) + * + * Or, the actual formulas for the excess Gibbs free energy may + * be used (which the activity coefficients probably came from anyway. + * + * Input + * phase_ptr => Pointer to the phase that we want to calculate + * the + * mf_PO => Vector of mole fractions in the phase + * in "Phase Order" order. This must sum to one. However + * this condition is not checked. + * + * Output + * return value = activity coefficient for species kspec + ***************************************************************************/ +{ + int kspec, kglob; + double Gxs = 0.0, ac; + VCS_SPECIES_THERMO *ts_ptr; + if (Vphase->Activity_Coeff_Model != VCS_AC_CONSTANT) { + for (kspec = 0; kspec < Vphase->NVolSpecies; kspec++) { + kglob = Vphase->IndSpecies[kspec]; + ts_ptr = SpeciesThermo[kglob]; + ac = ts_ptr->eval_ac(kspec); + Gxs += mf_PO[kspec] * log(ac); + } + } + return Gxs; +} + +/*****************************************************************************/ +/*****************************************************************************/ +/*****************************************************************************/ + +double VCS_SOLVE::vcs_Gxs_calc(int iphase) + + /************************************************************************** + * + * vcs_Gxs_calc: + * + * This function evaluates the Gibbs Excess free energy function. + * + * There are two ways. They may be evaluated from the + * activity coefficients themselves + * + * Gxs/RT = sum_i_inphase( X_i * ln (ActCoeff_i)) + * + * Or, the actual formulas for the excess Gibbs free energy may + * be used (which the activity coefficients probably came from anyway. + * + * Input + * + * + * Output + * return value = activity coefficient for species kspec + ***************************************************************************/ +{ + int kspec; + double Gxs = 0.0, ac; + double totmol = TPhMoles[iphase]; + vcs_VolPhase *Vphase = VPhaseList[iphase]; + VCS_SPECIES_THERMO *ts_ptr; + + if (totmol != 0.0 && Vphase->Activity_Coeff_Model != VCS_AC_CONSTANT) { + for (kspec = 0; kspec < m_numSpeciesRdc; kspec++) { + if (PhaseID[kspec] == iphase) { + if (SpeciesUnknownType[kspec] != VCS_SPECIES_TYPE_INTERFACIALVOLTAGE) { + ts_ptr = SpeciesThermo[kspec]; + ac = ts_ptr->eval_ac(kspec); + Gxs += soln[kspec]/totmol * log(ac); + } else { + plogf("FILL IN\n"); + exit(-1); + } + } + } + } + return Gxs; +} +/*****************************************************************************/ +} diff --git a/Cantera/src/equil/vcs_species_thermo.h b/Cantera/src/equil/vcs_species_thermo.h new file mode 100644 index 000000000..e9a9cd5dc --- /dev/null +++ b/Cantera/src/equil/vcs_species_thermo.h @@ -0,0 +1,264 @@ +/* + * $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. + */ + +#ifndef VCS_SPECIES_THERMO_H +#define VCS_SPECIES_THERMO_H + +//#include + +namespace VCSnonideal { + +class vcs_VolPhase; + +/*****************************************************************************/ +/*****************************************************************************/ +/*****************************************************************************/ +/* + * Models for the species standard state Naught temperature + * dependence + */ +#define VCS_SS0_NOTHANDLED -1 +#define VCS_SS0_CONSTANT 0 +#define VCS_SS0_NASA_POLY 1 +#define VCS_SS0_CONSTANT_CP 2 + + +/* + * Models for the species standard state extra pressure dependence + * + */ +#define VCS_SSSTAR_NOTHANDLED -1 +#define VCS_SSSTAR_CONSTANT 0 +#define VCS_SSSTAR_IDEAL_GAS 1 + +/* + * Identifies the thermo model for the species + * This structure is shared by volumetric and surface species. However, + * each will have its own types of thermodynamic models. These + * quantities all have appropriate units. The units are specified by + * VCS_UnitsFormat. + */ +class VCS_SPECIES_THERMO { + /* + * All objects are public for ease of development + */ +public: + /** + * Index of the phase that this species belongs to. + */ + int IndexPhase; + + /** + * Index of this species in the current phase. + */ + int IndexSpeciesPhase; + + /** + * Pointer to the owning phase object. + */ + vcs_VolPhase *OwningPhase; + + /** + * Integer representing the models for the species standard state + * Naught temperature dependence. They are listed above and start + * with VCS_SS0_... + */ + int SS0_Model; + + /** + * Internal storage of the last calculation of the reference + * naught Gibbs free energy at SS0_TSave. + * (always in units of Kelvin) + */ + double SS0_feSave; + + /** + * Internal storage of the last temperature used in the + * calculation of the reference naught Gibbs free energy. + * units = kelvin + */ + double SS0_TSave; + + /** + * Base temperature used in the VCS_SS0_CONSTANT_CP + * model + */ + double SS0_T0; + + /** + * Base enthalpy used in the VCS_SS0_CONSTANT_CP + * model + */ + double SS0_H0; + + /** + * Base entropy used in the VCS_SS0_CONSTANT_CP + * model + */ + double SS0_S0; + + /** + * Base heat capacity used in the VCS_SS0_CONSTANT_CP + * model + */ + double SS0_Cp0; + + /** + * Value of the pressure for the reference state. + */ + double SS0_Pref; + /** + * Pointer to a list of parameters that is malloced for + * complicated reference state calculation. + */ + void *SS0_Params; + /** + * Integer value representing the star state model. + */ + int SSStar_Model; + + /** + * Pointer to a list of parameters that is malloced for + * complicated reference star state calculation. + */ + void *SSStar_Params; + + /** + * Integer value representing the activity coefficient model + * These are defined in vcs_VolPhase.h and start with + * VCS_AC_... + */ + int Activity_Coeff_Model; + + /** + * Pointer to a list of parameters that is malloced for + * activity coefficient models. + */ + void *Activity_Coeff_Params; + + /** + * Models for the standard state volume of each species + */ + int SSStar_Vol_Model; + + /** + * Pointer to a list of parameters that is malloced for + * volume models + */ + void *SSStar_Vol_Params; + + /** + * parameter that is used int eh VCS_SSVOL_CONSTANT model. + */ + double SSStar_Vol0; + + /** + * If true, this object will call Cantera to do its member + * calculations. + */ + bool UseCanteraCalls; + + int m_VCS_UnitsFormat; + /* + * constructor and destructor + */ + VCS_SPECIES_THERMO(int indexPhase, int indexSpeciesPhase); + virtual ~VCS_SPECIES_THERMO(); + + /* + * Copy constructor and assignment operator + */ + VCS_SPECIES_THERMO(const VCS_SPECIES_THERMO& b); + VCS_SPECIES_THERMO& operator=(const VCS_SPECIES_THERMO& b); + + /* + * Duplication function for inherited classes. + */ + virtual VCS_SPECIES_THERMO* duplMyselfAsVCS_SPECIES_THERMO(); + + /** + * This function calculates the standard state Gibbs free energy + * for species, kspec, at the temperature TKelvin and pressure, Pres. + * + * + * Input + * TKelvin = Temperature in Kelvin + * pres = pressure is given in units specified by if__ variable. + * + * + * Output + * return value = standard state free energy in units of Kelvin. + */ + virtual double GStar_R_calc(int kspec, double TKelvin, double pres); + + /** + * + * G0_calc: + * + * This function calculates the standard state Gibbs free energy + * for species, kspec, at the temperature TKelvin + * + * Input + * + * + * Output + * return value = standard state free energy in Kelvin. + */ + virtual double G0_R_calc(int kspec, double TKelvin); + + /** + * cpc_ts_VStar_calc: + * + * This function calculates the standard state molar volume + * for species, kspec, at the temperature TKelvin and pressure, Pres, + * + * + * Input + * + * + * Output + * return value = standard state volume in cm**3 per mol. + * (if__=3) m**3 / kmol + */ + virtual double VolStar_calc(int kglob, double TKelvin, double Pres); + + /** + * This function evaluates the activity coefficient + * for species, kspec + * + * Input + * kspec -> integer value of the species in the global + * species list within VCS_SOLVE. Phase and local species id + * can be looked up within object. + * + * Note, T, P and mole fractions are obtained from the + * single private instance of VCS_SOLVE + * + * + * + * Output + * return value = activity coefficient for species kspec + */ + virtual double eval_ac(int kspec); + + /** + * Get the pointer to the vcs_VolPhase object for this species. + */ + +}; + +/* Externals for vcs_species_thermo.c */ + +//extern double vcs_Gxs_phase_calc(vcs_VolPhase *, double *); +//extern double vcs_Gxs_calc(int iphase); + +} + +#endif diff --git a/Cantera/src/equil/vcs_timer_generic.cpp b/Cantera/src/equil/vcs_timer_generic.cpp new file mode 100644 index 000000000..a0894b466 --- /dev/null +++ b/Cantera/src/equil/vcs_timer_generic.cpp @@ -0,0 +1,67 @@ +/*==================================================================== + * ------------------------ + * | CVS File Information | + * ------------------------ + * $RCSfile$ + * $Author$ + * $Date$ + * $Revision$ + * $Name$ + *====================================================================*/ + +#include +#include "vcs_internal.h" + +#ifdef COUGAR +#define HAVE_MD_TIMER +#endif +namespace VCSnonideal { +/**************************************************************************/ +/**************************************************************************/ +/**************************************************************************/ +#ifndef HAVE_MD_TIMER +double vcs_second(void) + + /************************************************************************* + * + * vcs_second() + * + * Returns system cpu and wall clock time in seconds. This + * is a strictly Ansi C timer, since clock() is defined as an + * Ansi C function. On some machines clock() returns type + * unsigned long (HP) and on others (SUN) it returns type long. + * An attempt to recover the actual time for clocks which have + * rolled over is made also. However, it only works if this + * function is called fairly regularily during + * the solution procedure. + * + * clock() -> returns the time in microseconds. Division by + * the macro CLOCKS_PER_SEC recovers the time in seconds. + *************************************************************************/ +{ + static clock_t last_num_ticks = 0; + static double inv_clocks_per_sec = 1./(double)CLOCKS_PER_SEC; + static double clock_width = + (double)(1L<<((int)sizeof(clock_t)*8-2))*4./(double)CLOCKS_PER_SEC; + static int clock_rollovers = 0; + clock_t num_ticks = clock(); + if (num_ticks < last_num_ticks) { + clock_rollovers++; + } + double value = num_ticks * inv_clocks_per_sec; + if (clock_rollovers) { + value += clock_rollovers * clock_width; + } + last_num_ticks = num_ticks; + return(value); +} +#else +#include +double vcs_second(void) +{ + return dclock(); +} +#endif + +} + diff --git a/Cantera/src/equil/vcs_util.cpp b/Cantera/src/equil/vcs_util.cpp new file mode 100644 index 000000000..78fc45fa3 --- /dev/null +++ b/Cantera/src/equil/vcs_util.cpp @@ -0,0 +1,445 @@ +/* ======================================================================= */ +/* -------------------------------------------------- */ +/* | RCS Head Information on zuzax.pchem.sandia.gov | */ +/* -------------------------------------------------- */ +/* $RCSfile$ */ +/* $Author$ */ +/* $Date$ */ +/* $Revision$ */ +/* ======================================================================= */ + +#include +#include + +#include "vcs_internal.h" + +namespace VCSnonideal { + +/*****************************************************************************/ +/*****************************************************************************/ +/*****************************************************************************/ +#ifndef USE_MEMSET +void vcs_dzero(double *vector, int length) + + /************************************************************************** + * + * vcs_dzero: + * + * Zeroes a double vector + ***************************************************************************/ +{ + int i; + for (i = 0; i < length; i++) vector[i] = 0.0; +} /* vcs_dzero() *************************************************************/ +#endif +/*****************************************************************************/ +/*****************************************************************************/ +/*****************************************************************************/ +#ifndef USE_MEMSET +void vcs_izero(int *vector, int length) + + /************************************************************************** + * + * vcs_izero: + * + * Zeroes an int vector + ***************************************************************************/ +{ + int i; + for (i = 0; i < length; i++) vector[i] = 0; +} /* vcs_izero() *************************************************************/ +#endif +/*****************************************************************************/ +/*****************************************************************************/ +/*****************************************************************************/ + +#ifndef USE_MEMSET +void vcs_dcopy(double *vec_to, double *vec_from, int length) + + /************************************************************************** + * + * vcs_dcopy: + * + * Copies a double vector + ***************************************************************************/ +{ + int i; + for (i = 0; i < length; i++) vec_to[i] = vec_from[i]; +} /* vcs_dzero() *************************************************************/ +#endif +/*****************************************************************************/ +/*****************************************************************************/ +/*****************************************************************************/ + +#ifndef USE_MEMSET +void vcs_icopy(int *vec_to, int *vec_from, int length) + + /************************************************************************** + * + * vcs_icopy: + * + * copies an int vector + ***************************************************************************/ +{ + int i; + for (i = 0; i < length; i++) vec_to[i] = vec_from[i]; +} /* vcs_dzero() *************************************************************/ +#endif + +/*****************************************************************************/ +/*****************************************************************************/ +/*****************************************************************************/ + +#ifndef USE_MEMSET +/* + * vcs_vdzero + * + * zeroes a double vector + */ +void vcs_vdzero(std::vector &vvv, int len) { + if (len < 0) { + std::fill(vvv.begin(), vvv.end(), 0.0); + } else { + std::fill_n(vvv.begin(), len, 0.0); + } +} +#endif + +/*****************************************************************************/ +/*****************************************************************************/ +/*****************************************************************************/ + +#ifndef USE_MEMSET +/* + * vcs_vizero + * + * zeroes a double vector + */ +void vcs_vizero(std::vector &vvv, int len) { + if (len < 0) { + std::fill(vvv.begin(), vvv.end(), 0.0); + } else { + std::fill_n(vvv.begin(), len, 0.0); + } +} +#endif + + + +#ifndef USE_MEMSET +/* + * vcs_vdcopy + * + * copies a vector of doubles to another vector of doubles + * + * @param vec_to Vector to be copied to + * @param vec_from Vector to be copied from + * @param length Length of the copy + */ +void vcs_vdcopy(std::vector &vec_to, + const std::vector & vec_from, int length) { + std::copy(vec_from.begin(), vec_from.begin() + length, vec_to.begin()); +} +#endif + + +#ifndef USE_MEMSET +/* + * vcs_vicopy + * + * copies a vector to another vector + * + * @param vec_to Vector to be copied to + * @param vec_from Vector to be copied from + * @param length Length of the copy + */ +void vcs_vicopy(std::vector &vec_to, + const std::vector & vec_from, int length) { + std::copy(vec_from.begin(), vec_from.begin() + length, vec_to.begin()); +} +#endif +/*****************************************************************************/ +/*****************************************************************************/ +/*****************************************************************************/ + +int vcs_amax(double *x, int j, int n) + + /************************************************************************** + * + * vcs_amax: + * + * Finds the location of the maximum component in a double vector + * INPUT + * x(*) - Vector to search + * j <= i < n : i is the range of indecises to search in X(*) + * + * RETURN + * return index of the greatest value on X(*) searched + ***************************************************************************/ +{ + int i; + int largest = j; + double big = x[j]; + for (i = j + 1; i < n; ++i) { + if (x[i] > big) { + largest = i; + big = x[i]; + } + } + return largest; +} /* vcs_amax() **************************************************************/ +/*****************************************************************************/ +/*****************************************************************************/ +/*****************************************************************************/ + +int vcs_max_int(int *vector, int length) + + /************************************************************************** + * + * vcs_max_int: + * + * returns the maximum integer in a list. + ***************************************************************************/ +{ + int i, retn; + if (vector == NULL || length <= 0) return 0; + retn = vector[0]; + for (i = 1; i < length; i++) { + retn = MAX( retn, vector[i]); + } + return retn; +} +/*****************************************************************************/ +/*****************************************************************************/ +/*****************************************************************************/ + +static void printSev(int severity) +{ + switch (severity) { + case 1: + plogf(" VCS NOTE : "); break; + case 2: + plogf(" VCS WARNING : "); break; + case 3: + plogf(" VCS ERROR : "); break; + case 4: + plogf(" VCS FATAL ERROR : "); break; + } +} +/*****************************************************************************/ +/*****************************************************************************/ +/*****************************************************************************/ + +int vcsUtil_err_check(VCS_ERR_STRUCT &vcsE, char *string1, int ival) + + /************************************************************************** + * + * vcs_err_check: + * + * This routine checks the error flag, prints a message, and then + * resets the error counters. + * + * Input + * severity: Affects what happens in the routine. + * 0 = Ignore warning, don't print anything + * 1 = Note + * 2 = Warning + * 3 = Error -> Probably fatal + * 4 = Immediate Fatal Error + * ival = Another integer input that means different things + * depending on the error condition. Frequently, it will + * point to a species or reaction which is the culprit. + * + * Return + * Returns the error flag value + ***************************************************************************/ +{ + int flag = vcsE.Flag, doPrint, severity; + /* + * Do a fast exit if there is no error encountered + */ + if (flag == VCS_SUCCESS) return flag; + switch (flag) { + case VCS_FAILED_CONVERGENCE: + severity = 1; + break; + case VCS_THERMO_OUTOFRANGE: + severity = 2; + break; + case VCS_NOMEMORY: + case VCS_PUB_BAD: + severity = 3; + break; + case VCS_SHOULDNT_BE_HERE: + severity = 4; + break; + } + doPrint = (vcsE.PrintLevel <= severity); + if (doPrint) { + printSev(severity); + if (string1 != NULL) plogf("%s ", string1); + } + switch (flag) { + case VCS_NOMEMORY: + if (doPrint) { + plogf("Out of Memory"); + } + break; + case VCS_FAILED_CONVERGENCE: + if (doPrint) { + plogf("Failed Convergence"); + } + break; + case VCS_SHOULDNT_BE_HERE: + if (doPrint) { + plogf("Shouldn't be here, internal vcsc error"); + } + break; + case VCS_PUB_BAD: + if (doPrint) { + plogf("Public data structure is corrupt"); + } + break; + case VCS_THERMO_OUTOFRANGE: + if (doPrint) { + plogf("Thermo Data out of Range"); + if (vcsE.Species1 >= 0) plogf(", Species = %d", vcsE.Species1); + if (ival >= 0) plogf(", Species = %d", ival); + if (vcsE.Value1 != VCS_ERR_NOVALUE) + plogf(", TKelvin = %g", vcsE.Value1); + if (vcsE.Value1 != VCS_ERR_NOVALUE) + plogf(", Tlimit = %g", vcsE.Value2); + } + break; + default: + if (doPrint) { + plogf("Unknown Error Condition = %d ??", flag); + } + break; + } + + if (doPrint) { + plogf("\n"); + if (vcsE.Mess[0] != '\0') { + printSev(severity); + plogf(" %s\n", vcsE.Mess); + } + fflush(stdout); + } + if (severity > 3) exit(-1); + vcsUtil_err_reset(vcsE); + return flag; +} +/*****************************************************************************/ +/*****************************************************************************/ +/*****************************************************************************/ + +void vcsUtil_err_reset (VCS_ERR_STRUCT &vcsE) + + /************************************************************************** + * + * vcs_err_reset: + * + * Sets the error handler back to default conditions. + ***************************************************************************/ +{ + vcsE.Flag = VCS_SUCCESS; + vcsE.Species1 = -1; + vcsE.Species2 = -1; + vcsE.Value1 = VCS_ERR_NOVALUE; + vcsE.Value2 = VCS_ERR_NOVALUE; + vcsE.Mess[0] = '\0'; + vcsE.Mess[119] = '\0'; +} +/*****************************************************************************/ +/*****************************************************************************/ +/*****************************************************************************/ + +void vcs_print_line(const char *string, int num) + + /************************************************************************** + * + * vcs_print_char: + * + * Print a line consisting of a multiple of the same string + * + ***************************************************************************/ +{ + if (string) { + for (int j = 0; j < num; j++) plogf("%s", string); + } + plogf("\n"); +} + +/***************************************************************************/ +/************************************************************************ **/ +/************************************************************************ **/ + +void vcs_print_stringTrunc(const char *str, int space, int alignment) + + /*********************************************************************** + * vcs_print_stringTrunc(): + * + * Print a string within a given space limit. This routine + * limits the amount of the string that will be printed to a + * maximum of "space" characters. + * + * str = String -> must be null terminated. + * space = space limit for the printing. + * alignment = 0 centered + * 1 right aligned + * 2 left aligned + ***********************************************************************/ +{ + int i, ls=0, rs=0; + int len = strlen(str); + if ((len) >= space) { + for (i = 0; i < space; i++) { + plogf("%c", str[i]); + } + } else { + if (alignment == 1) { + ls = space - len; + } else if (alignment == 2) { + rs = space - len; + } else { + ls = (space - len) / 2; + rs = space - len - ls; + } + if (ls != 0) { + for (i = 0; i < ls; i++) plogf(" "); + } + plogf("%s", str); + if (rs != 0) { + for (i = 0; i < rs; i++) plogf(" "); + } + } +} + +/*****************************************************************************/ +/*****************************************************************************/ +/*****************************************************************************/ + +bool vcs_doubleEqual(double d1, double d2) + + /************************************************************************* + * vcs_doubleEqual() + * + * Simple routine to check whether two doubles are equal up to + * roundoff error. Currently it's set to check for 10 digits of + * accuracy. + *************************************************************************/ +{ + double denom = fabs(d1) + fabs(d2) + 1.0; + double fac = fabs(d1 - d2) / denom; + if (fac > 1.0E-10) { + return false; + } + return true; +} + +/*****************************************************************************/ +/*****************************************************************************/ +/*****************************************************************************/ + +}