/* * @file: solveSP.cpp Implicit surface site concentration solver */ /* * Copyright 2004 Sandia Corporation. Under the terms of Contract * DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government * retains certain rights in this software. * See file License.txt for licensing information. */ #include "solveSP.h" #include "cantera/base/clockWC.h" #include "cantera/numerics/ctlapack.h" /* Standard include files */ #include #include #include #include using namespace std; namespace Cantera { /*************************************************************************** * STATIC ROUTINES DEFINED IN THIS FILE ***************************************************************************/ static doublereal calc_damping(doublereal* x, doublereal* dx, size_t dim, int*); static doublereal calcWeightedNorm(const doublereal [], const doublereal dx[], size_t); /*************************************************************************** * LAPACK PROTOTYPES ***************************************************************************/ //#define FSUB_TYPE void // extern "C" { // extern FSUB_TYPE dgetrf_(int *, int *, doublereal *, int *, int [], int *); // extern FSUB_TYPE dgetrs_(char *, int *, int *, doublereal *, int *, int [], // doublereal [], int *, int *, unsigned int); // } /***************************************************************************** * PROTOTYPES and PREPROC DIRECTIVES FOR MISC. ROUTINES *****************************************************************************/ #ifndef DAMPING # define DAMPING true #endif /*************************************************************************** * solveSP Class Definitinos ***************************************************************************/ // Main constructor solveSP::solveSP(ImplicitSurfChem* surfChemPtr, int bulkFunc) : m_SurfChemPtr(surfChemPtr), m_objects(surfChemPtr->getObjects()), m_neq(0), m_bulkFunc(bulkFunc), m_numSurfPhases(0), m_numTotSurfSpecies(0), m_numBulkPhasesSS(0), m_numTotBulkSpeciesSS(0), m_atol(1.0E-15), m_rtol(1.0E-4), m_maxstep(1000), m_maxTotSpecies(0), m_ioflag(0) { m_numSurfPhases = 0; size_t numPossibleSurfPhases = m_objects.size(); for (size_t n = 0; n < numPossibleSurfPhases; n++) { InterfaceKinetics* m_kin = m_objects[n]; size_t surfPhaseIndex = m_kin->surfacePhaseIndex(); if (surfPhaseIndex != npos) { m_numSurfPhases++; m_indexKinObjSurfPhase.push_back(n); m_kinObjPhaseIDSurfPhase.push_back(surfPhaseIndex); } else { throw CanteraError("solveSP", "InterfaceKinetics object has no surface phase"); } ThermoPhase* tp = &(m_kin->thermo(surfPhaseIndex)); SurfPhase* sp = dynamic_cast(tp); if (!sp) { throw CanteraError("solveSP", "Inconsistent ThermoPhase object within " "InterfaceKinetics object"); } m_ptrsSurfPhase.push_back(sp); size_t nsp = sp->nSpecies(); m_nSpeciesSurfPhase.push_back(nsp); m_numTotSurfSpecies += nsp; } /* * We rely on ordering to figure things out */ if (1) { //m_numBulkPhases = m_kin0->nPhases() - 1 - m_numSurfPhases; // Disable the capability until we figure out what is going on m_numBulkPhasesSS = 0; //if (m_numBulkPhasesSS > 0) { //m_numBulkSpecies.resize(m_numBulkPhasesSS, 0); //m_bulkPhasePtrs.resize(m_numBulkPhasesSS, 0); //m_bulkIndex = 1; //if (m_bulkIndex == surfPhaseIndex) { // m_bulkIndex += m_numSurfPhases; //} //for (i = 0; i < m_numBulkPhasesSS; i++) { // m_bulkPhasePtrs[i] = &(m_kin0->thermo(m_bulkIndex + i)); // m_numBulkSpecies[i] = m_bulkPhasePtrs[i]->nSpecies(); // m_numTotBulkSpeciesSS += m_numBulkSpecies[i]; //} //} } if (bulkFunc == BULK_DEPOSITION) { m_neq = m_numTotSurfSpecies + m_numTotBulkSpeciesSS; } else { m_neq = m_numTotSurfSpecies; } m_maxTotSpecies = 0; for (size_t n = 0; n < m_numSurfPhases; n++) { size_t tsp = m_objects[n]->nTotalSpecies(); m_maxTotSpecies = std::max(m_maxTotSpecies, tsp); } m_maxTotSpecies = std::max(m_maxTotSpecies, m_neq); m_netProductionRatesSave.resize(m_maxTotSpecies, 0.0); m_numEqn1.resize(m_maxTotSpecies, 0.0); m_numEqn2.resize(m_maxTotSpecies, 0.0); m_XMolKinSpecies.resize(m_maxTotSpecies, 0.0); m_CSolnSave.resize(m_neq, 0.0); m_spSurfLarge.resize(m_numSurfPhases, 0); m_kinSpecIndex.resize(m_numTotSurfSpecies + m_numTotBulkSpeciesSS, 0); m_kinObjIndex.resize(m_numTotSurfSpecies + m_numTotBulkSpeciesSS, 0); m_eqnIndexStartSolnPhase.resize(m_numSurfPhases + m_numBulkPhasesSS, 0); size_t kindexSP = 0; size_t isp, k, nsp, kstart; for (isp = 0; isp < m_numSurfPhases; isp++) { size_t iKinObject = m_indexKinObjSurfPhase[isp]; InterfaceKinetics* m_kin = m_objects[iKinObject]; size_t surfPhaseIndex = m_kinObjPhaseIDSurfPhase[isp]; kstart = m_kin->kineticsSpeciesIndex(0, surfPhaseIndex); nsp = m_nSpeciesSurfPhase[isp]; m_eqnIndexStartSolnPhase[isp] = kindexSP; for (k = 0; k < nsp; k++, kindexSP++) { m_kinSpecIndex[kindexSP] = kstart + k; m_kinObjIndex[kindexSP] = isp; } } if (0) { //for (isp = 0; isp < m_numBulkPhasesSS; isp++) { //nt iKinObject = m_bulkKinObjID[isp]; //InterfaceKinetics *m_kin = m_objects[iKinObject]; //int bulkIndex = m_bulkKinObjPhaseID[isp]; //kstart = m_kin->kineticsSpeciesIndex(0, bulkIndex); //nsp = m_numBulkSpecies[isp]; //m_eqnIndexStartSolnPhase[isp] = kindexSP; //for (k = 0; k < nsp; k++, kindexSP++) { // m_kinSpecIndex[kindexSP] = kstart + k; // m_kinObjIndex[kindexSP] = m_numSurfPhases + isp; //} //} } // Dimension solution vector size_t dim1 = std::max(1, m_neq); m_CSolnSP.resize(dim1, 0.0); m_CSolnSPInit.resize(dim1, 0.0); m_CSolnSPOld.resize(dim1, 0.0); m_wtResid.resize(dim1, 0.0); m_wtSpecies.resize(dim1, 0.0); m_resid.resize(dim1, 0.0); m_ipiv.resize(dim1, 0); m_Jac.resize(dim1, dim1, 0.0); m_JacCol.resize(dim1, 0); for (size_t k = 0; k < dim1; k++) { m_JacCol[k] = m_Jac.ptrColumn(k); } } // Empty destructor solveSP::~solveSP() { } /* * The following calculation is a Newton's method to * get the surface fractions of the surface and bulk species by * requiring that the * surface species production rate = 0 and that the bulk fractions are * proportional to their production rates. */ int solveSP::solveSurfProb(int ifunc, doublereal time_scale, doublereal TKelvin, doublereal PGas, doublereal reltol, doublereal abstol) { doublereal EXTRA_ACCURACY = 0.001; if (ifunc == SFLUX_JACOBIAN) { EXTRA_ACCURACY *= 0.001; } int info = 0; int label_t=-1; /* Species IDs for time control */ int label_d = -1; /* Species IDs for damping control */ int label_t_old=-1; doublereal label_factor = 1.0; int iter=0; // iteration number on numlinear solver int iter_max=1000; // maximum number of nonlinear iterations int nrhs=1; doublereal deltaT = 1.0E-10; // Delta time step doublereal damp=1.0, tmp; // Weighted L2 norm of the residual. Currently, this is only // used for IO purposes. It doesn't control convergence. // Therefore, it is turned off when DEBUG_SOLVESP isn't defined. doublereal resid_norm; doublereal inv_t = 0.0; doublereal t_real = 0.0, update_norm = 1.0E6; bool do_time = false, not_converged = true; #ifdef DEBUG_SOLVESP #ifdef DEBUG_SOLVESP_TIME doublereal t1; #endif #else if (m_ioflag > 1) { m_ioflag = 1; } #endif #ifdef DEBUG_SOLVESP #ifdef DEBUG_SOLVESP_TIME Cantera::clockWC wc; if (m_ioflag) { t1 = wc.secondsWC(); } #endif #endif /* * Set the initial value of the do_time parameter */ if (ifunc == SFLUX_INITIALIZE || ifunc == SFLUX_TRANSIENT) { do_time = true; } /* * Store the initial guess for the surface problem in the soln vector, * CSoln, and in an separate vector CSolnInit. */ size_t loc = 0; for (size_t n = 0; n < m_numSurfPhases; n++) { SurfPhase* sf_ptr = m_ptrsSurfPhase[n]; sf_ptr->getConcentrations(DATA_PTR(m_numEqn1)); size_t nsp = m_nSpeciesSurfPhase[n]; for (size_t k = 0; k getConcentrations(DATA_PTR(m_numEqn1)); //int nsp = m_numBulkSpecies[isp]; //for (k = 0; k < nsp; k++, kindex++) { // m_CSolnSP[loc] = m_numEqn1[k]; // loc++; //} //} } std::copy(m_CSolnSP.begin(), m_CSolnSP.end(), m_CSolnSPInit.begin()); // Calculate the largest species in each phase evalSurfLarge(DATA_PTR(m_CSolnSP)); /* * Get the net production rate of all species in the kinetics manager. */ // m_kin->getNetProductionRates(DATA_PTR(m_netProductionRatesSave)); if (m_ioflag) { print_header(m_ioflag, ifunc, time_scale, DAMPING, reltol, abstol, TKelvin, PGas, DATA_PTR(m_netProductionRatesSave), DATA_PTR(m_XMolKinSpecies)); } /* * Quick return when there isn't a surface problem to solve */ if (m_neq == 0) { not_converged = false; update_norm = 0.0; } /* ------------------------------------------------------------------ * Start of Newton's method * ------------------------------------------------------------------ */ while (not_converged && iter < iter_max) { iter++; /* * Store previous iteration's solution in the old solution vector */ std::copy(m_CSolnSP.begin(), m_CSolnSP.end(), m_CSolnSPOld.begin()); /* * Evaluate the largest surface species for each surface phase every * 5 iterations. */ if (iter%5 == 4) { evalSurfLarge(DATA_PTR(m_CSolnSP)); } /* * Calculate the value of the time step * - heuristics to stop large oscillations in deltaT */ if (do_time) { /* don't hurry increase in time step at the same time as damping */ if (damp < 1.0) { label_factor = 1.0; } tmp = calc_t(DATA_PTR(m_netProductionRatesSave), DATA_PTR(m_XMolKinSpecies), &label_t, &label_t_old, &label_factor, m_ioflag); if (iter < 10) { inv_t = tmp; } else if (tmp > 2.0*inv_t) { inv_t = 2.0*inv_t; } else { inv_t = tmp; } /* * Check end condition */ if (ifunc == SFLUX_TRANSIENT) { tmp = t_real + 1.0/inv_t; if (tmp > time_scale) { inv_t = 1.0/(time_scale - t_real); } } } else { /* make steady state calc a step of 1 million seconds to prevent singular jacobians for some pathological cases */ inv_t = 1.0e-6; } deltaT = 1.0/inv_t; /* * Call the routine to numerically evaluation the jacobian * and residual for the current iteration. */ resjac_eval(m_JacCol, DATA_PTR(m_resid), DATA_PTR(m_CSolnSP), DATA_PTR(m_CSolnSPOld), do_time, deltaT); /* * Calculate the weights. Make sure the calculation is carried * out on the first iteration. */ if (iter%4 == 1) { calcWeights(DATA_PTR(m_wtSpecies), DATA_PTR(m_wtResid), m_Jac, DATA_PTR(m_CSolnSP), abstol, reltol); } /* * Find the weighted norm of the residual */ resid_norm = calcWeightedNorm(DATA_PTR(m_wtResid), DATA_PTR(m_resid), m_neq); #ifdef DEBUG_SOLVESP if (m_ioflag > 1) { printIterationHeader(m_ioflag, damp, inv_t, t_real, iter, do_time); /* * Print out the residual and jacobian */ printResJac(m_ioflag, m_neq, m_Jac, DATA_PTR(m_resid), DATA_PTR(m_wtResid), resid_norm); } #endif /* * Solve Linear system (with LAPACK). The solution is in resid[] */ // (void) dgetrf_(&m_neq, &m_neq, m_JacCol[0], &m_neq, // DATA_PTR(m_ipiv), &info); ct_dgetrf(m_neq, m_neq, m_JacCol[0], m_neq, DATA_PTR(m_ipiv), info); if (info==0) { ct_dgetrs(ctlapack::NoTranspose, m_neq, nrhs, m_JacCol[0], m_neq, DATA_PTR(m_ipiv), DATA_PTR(m_resid), m_neq, info); } /* * Force convergence if residual is small to avoid * "nan" results from the linear solve. */ else { if (m_ioflag) { printf("solveSurfSS: Zero pivot, assuming converged: %g (%d)\n", resid_norm, info); } for (size_t jcol = 0; jcol < m_neq; jcol++) { m_resid[jcol] = 0.0; } /* print out some helpful info */ if (m_ioflag > 1) { printf("-----\n"); printf("solveSurfProb: iter %d t_real %g delta_t %g\n\n", iter,t_real, 1.0/inv_t); printf("solveSurfProb: init guess, current concentration," "and prod rate:\n"); for (size_t jcol = 0; jcol < m_neq; jcol++) { printf("\t%s %g %g %g\n", int2str(jcol).c_str(), m_CSolnSPInit[jcol], m_CSolnSP[jcol], m_netProductionRatesSave[m_kinSpecIndex[jcol]]); } printf("-----\n"); } if (do_time) { t_real += time_scale; } #ifdef DEBUG_SOLVESP if (m_ioflag) { printf("\nResidual is small, forcing convergence!\n"); } #endif } /* * Calculate the Damping factor needed to keep all unknowns * between 0 and 1, and not allow too large a change (factor of 2) * in any unknown. */ #ifdef DAMPING damp = calc_damping(DATA_PTR(m_CSolnSP), DATA_PTR(m_resid), m_neq, &label_d); #endif /* * Calculate the weighted norm of the update vector * Here, resid is the delta of the solution, in concentration * units. */ update_norm = calcWeightedNorm(DATA_PTR(m_wtSpecies), DATA_PTR(m_resid), m_neq); /* * Update the solution vector and real time * Crop the concentrations to zero. */ for (size_t irow = 0; irow < m_neq; irow++) { m_CSolnSP[irow] -= damp * m_resid[irow]; } for (size_t irow = 0; irow < m_neq; irow++) { m_CSolnSP[irow] = std::max(0.0, m_CSolnSP[irow]); } updateState(DATA_PTR(m_CSolnSP)); if (do_time) { t_real += damp/inv_t; } if (m_ioflag) { printIteration(m_ioflag, damp, label_d, label_t, inv_t, t_real, iter, update_norm, resid_norm, DATA_PTR(m_netProductionRatesSave), DATA_PTR(m_CSolnSP), DATA_PTR(m_resid), DATA_PTR(m_XMolKinSpecies), DATA_PTR(m_wtSpecies), m_neq, do_time); } if (ifunc == SFLUX_TRANSIENT) { not_converged = (t_real < time_scale); } else { if (do_time) { if (t_real > time_scale || (resid_norm < 1.0e-7 && update_norm*time_scale/t_real < EXTRA_ACCURACY)) { do_time = false; #ifdef DEBUG_SOLVESP if (m_ioflag > 1) { printf("\t\tSwitching to steady solve.\n"); } #endif } } else { not_converged = ((update_norm > EXTRA_ACCURACY) || (resid_norm > EXTRA_ACCURACY)); } } } /* End of Newton's Method while statement */ /* * End Newton's method. If not converged, print error message and * recalculate sdot's at equal site fractions. */ if (not_converged) { if (m_ioflag) { printf("#$#$#$# Error in solveSP $#$#$#$ \n"); printf("Newton iter on surface species did not converge, " "update_norm = %e \n", update_norm); printf("Continuing anyway\n"); } } #ifdef DEBUG_SOLVESP #ifdef DEBUG_SOLVESP_TIME if (m_ioflag) { printf("\nEnd of solve, time used: %e\n", wc.secondsWC()-t1); } #endif #endif /* * Decide on what to return in the solution vector * - right now, will always return the last solution * no matter how bad */ if (m_ioflag) { fun_eval(DATA_PTR(m_resid), DATA_PTR(m_CSolnSP), DATA_PTR(m_CSolnSPOld), false, deltaT); resid_norm = calcWeightedNorm(DATA_PTR(m_wtResid), DATA_PTR(m_resid), m_neq); printFinal(m_ioflag, damp, label_d, label_t, inv_t, t_real, iter, update_norm, resid_norm, DATA_PTR(m_netProductionRatesSave), DATA_PTR(m_CSolnSP), DATA_PTR(m_resid), DATA_PTR(m_XMolKinSpecies), DATA_PTR(m_wtSpecies), DATA_PTR(m_wtResid), m_neq, do_time, TKelvin, PGas); } /* * Return with the appropriate flag */ if (update_norm > 1.0) { return -1; } return 1; } #undef DAMPING /* * Update the surface states of the surface phases. */ void solveSP::updateState(const doublereal* CSolnSP) { size_t loc = 0; for (size_t n = 0; n < m_numSurfPhases; n++) { m_ptrsSurfPhase[n]->setConcentrations(CSolnSP + loc); loc += m_nSpeciesSurfPhase[n]; } //if (m_bulkFunc == BULK_DEPOSITION) { // for (int n = 0; n < m_numBulkPhasesSS; n++) { // m_bulkPhasePtrs[n]->setConcentrations(CSolnSP + loc); // loc += m_numBulkSpecies[n]; // } //} } /* * Update the mole fractions for phases which are part of the equation set */ void solveSP::updateMFSolnSP(doublereal* XMolSolnSP) { for (size_t isp = 0; isp < m_numSurfPhases; isp++) { size_t keqnStart = m_eqnIndexStartSolnPhase[isp]; m_ptrsSurfPhase[isp]->getMoleFractions(XMolSolnSP + keqnStart); } //if (m_bulkFunc == BULK_DEPOSITION) { // for (int isp = 0; isp < m_numBulkPhasesSS; isp++) { // int keqnStart = m_eqnIndexStartSolnPhase[isp + m_numSurfPhases]; // m_bulkPhasePtrs[isp]->getMoleFractions(XMolSolnSP + keqnStart); // } //} } /* * Update the mole fractions for phases which are part of a single * interfacial kinetics object */ void solveSP::updateMFKinSpecies(doublereal* XMolKinSpecies, int isp) { InterfaceKinetics* m_kin = m_objects[isp]; size_t nph = m_kin->nPhases(); for (size_t iph = 0; iph < nph; iph++) { size_t ksi = m_kin->kineticsSpeciesIndex(0, iph); ThermoPhase& thref = m_kin->thermo(iph); thref.getMoleFractions(XMolKinSpecies + ksi); } } /* * Update the vector that keeps track of the largest species in each * surface phase. */ void solveSP::evalSurfLarge(const doublereal* CSolnSP) { size_t kindexSP = 0; for (size_t isp = 0; isp < m_numSurfPhases; isp++) { size_t nsp = m_nSpeciesSurfPhase[isp]; doublereal Clarge = CSolnSP[kindexSP]; m_spSurfLarge[isp] = 0; kindexSP++; for (size_t k = 1; k < nsp; k++, kindexSP++) { if (CSolnSP[kindexSP] > Clarge) { Clarge = CSolnSP[kindexSP]; m_spSurfLarge[isp] = k; } } } } /* * This calculates the net production rates of all species * * This calculates the function eval. * (should switch to special_species formulation for sum condition) * * @internal * This routine uses the m_numEqn1 and m_netProductionRatesSave vectors * as temporary internal storage. */ void solveSP::fun_eval(doublereal* resid, const doublereal* CSoln, const doublereal* CSolnOld, const bool do_time, const doublereal deltaT) { size_t isp, nsp, kstart, k, kindexSP, kins, kspecial; doublereal lenScale = 1.0E-9; doublereal sd = 0.0; doublereal grRate; if (m_numSurfPhases > 0) { /* * update the surface concentrations with the input surface * concentration vector */ updateState(CSoln); /* * Get the net production rates of all of the species in the * surface kinetics mechanism * * HKM Should do it here for all kinetics objects so that * bulk will eventually work. */ if (do_time) { kindexSP = 0; for (isp = 0; isp < m_numSurfPhases; isp++) { nsp = m_nSpeciesSurfPhase[isp]; InterfaceKinetics* kinPtr = m_objects[isp]; size_t surfIndex = kinPtr->surfacePhaseIndex(); kstart = kinPtr->kineticsSpeciesIndex(0, surfIndex); kins = kindexSP; kinPtr->getNetProductionRates(DATA_PTR(m_netProductionRatesSave)); for (k = 0; k < nsp; k++, kindexSP++) { resid[kindexSP] = (CSoln[kindexSP] - CSolnOld[kindexSP]) / deltaT - m_netProductionRatesSave[kstart + k]; } kspecial = kins + m_spSurfLarge[isp]; sd = m_ptrsSurfPhase[isp]->siteDensity(); resid[kspecial] = sd; for (k = 0; k < nsp; k++) { resid[kspecial] -= CSoln[kins + k]; } } } else { kindexSP = 0; for (isp = 0; isp < m_numSurfPhases; isp++) { nsp = m_nSpeciesSurfPhase[isp]; InterfaceKinetics* kinPtr = m_objects[isp]; size_t surfIndex = kinPtr->surfacePhaseIndex(); kstart = kinPtr->kineticsSpeciesIndex(0, surfIndex); kins = kindexSP; kinPtr->getNetProductionRates(DATA_PTR(m_netProductionRatesSave)); for (k = 0; k < nsp; k++, kindexSP++) { resid[kindexSP] = - m_netProductionRatesSave[kstart + k]; } kspecial = kins + m_spSurfLarge[isp]; sd = m_ptrsSurfPhase[isp]->siteDensity(); resid[kspecial] = sd; for (k = 0; k < nsp; k++) { resid[kspecial] -= CSoln[kins + k]; } } } if (m_bulkFunc == BULK_DEPOSITION) { kindexSP = m_numTotSurfSpecies; for (isp = 0; isp < m_numBulkPhasesSS; isp++) { doublereal* XBlk = DATA_PTR(m_numEqn1); //ThermoPhase *THptr = m_bulkPhasePtrs[isp]; //THptr->getMoleFractions(XBlk); nsp = m_nSpeciesSurfPhase[isp]; size_t surfPhaseIndex = m_indexKinObjSurfPhase[isp]; InterfaceKinetics* m_kin = m_objects[isp]; grRate = 0.0; kstart = m_kin->kineticsSpeciesIndex(0, surfPhaseIndex); for (k = 0; k < nsp; k++) { if (m_netProductionRatesSave[kstart + k] > 0.0) { grRate += m_netProductionRatesSave[kstart + k]; } } resid[kindexSP] = m_bulkPhasePtrs[isp]->molarDensity(); for (k = 0; k < nsp; k++) { resid[kindexSP] -= CSoln[kindexSP + k]; } if (grRate > 0.0) { for (k = 1; k < nsp; k++) { if (m_netProductionRatesSave[kstart + k] > 0.0) { resid[kindexSP + k] = XBlk[k] * grRate - m_netProductionRatesSave[kstart + k]; } else { resid[kindexSP + k] = XBlk[k] * grRate; } } } else { grRate = 1.0E-6; grRate += fabs(m_netProductionRatesSave[kstart + k]); for (k = 1; k < nsp; k++) { resid[kindexSP + k] = grRate * (XBlk[k] - 1.0/nsp); } } if (do_time) { for (k = 1; k < nsp; k++) { resid[kindexSP + k] += lenScale / deltaT * (CSoln[kindexSP + k]- CSolnOld[kindexSP + k]); } } kindexSP += nsp; } } } } /* * Calculate the Jacobian and residual * * @internal * This routine uses the m_numEqn2 vector * as temporary internal storage. */ void solveSP::resjac_eval(std::vector &JacCol, doublereal resid[], doublereal CSoln[], const doublereal CSolnOld[], const bool do_time, const doublereal deltaT) { size_t kColIndex = 0, nsp, jsp, i, kCol; doublereal dc, cSave, sd; doublereal* col_j; /* * Calculate the residual */ fun_eval(resid, CSoln, CSolnOld, do_time, deltaT); /* * Now we will look over the columns perturbing each unknown. */ for (jsp = 0; jsp < m_numSurfPhases; jsp++) { nsp = m_nSpeciesSurfPhase[jsp]; sd = m_ptrsSurfPhase[jsp]->siteDensity(); for (kCol = 0; kCol < nsp; kCol++) { cSave = CSoln[kColIndex]; dc = std::max(1.0E-10 * sd, fabs(cSave) * 1.0E-7); CSoln[kColIndex] += dc; fun_eval(DATA_PTR(m_numEqn2), CSoln, CSolnOld, do_time, deltaT); col_j = JacCol[kColIndex]; for (i = 0; i < m_neq; i++) { col_j[i] = (m_numEqn2[i] - resid[i])/dc; } CSoln[kColIndex] = cSave; kColIndex++; } } if (m_bulkFunc == BULK_DEPOSITION) { for (jsp = 0; jsp < m_numBulkPhasesSS; jsp++) { nsp = m_numBulkSpecies[jsp]; sd = m_bulkPhasePtrs[jsp]->molarDensity(); for (kCol = 0; kCol < nsp; kCol++) { cSave = CSoln[kColIndex]; dc = std::max(1.0E-10 * sd, fabs(cSave) * 1.0E-7); CSoln[kColIndex] += dc; fun_eval(DATA_PTR(m_numEqn2), CSoln, CSolnOld, do_time, deltaT); col_j = JacCol[kColIndex]; for (i = 0; i < m_neq; i++) { col_j[i] = (m_numEqn2[i] - resid[i])/dc; } CSoln[kColIndex] = cSave; kColIndex++; } } } } #define APPROACH 0.80 static doublereal calc_damping(doublereal x[], doublereal dxneg[], size_t dim, int* label) /* This function calculates a damping factor for the Newton iteration update * vector, dxneg, to insure that all site and bulk fractions, x, remain * bounded between zero and one. * * dxneg[] = negative of the update vector. * * The constant "APPROACH" sets the fraction of the distance to the boundary * that the step can take. If the full step would not force any fraction * outside of 0-1, then Newton's method is allowed to operate normally. */ { doublereal damp = 1.0, xnew, xtop, xbot; static doublereal damp_old = 1.0; *label = -1; for (size_t i = 0; i < dim; i++) { /* * Calculate the new suggested new value of x[i] */ xnew = x[i] - damp * dxneg[i]; /* * Calculate the allowed maximum and minimum values of x[i] * - Only going to allow x[i] to converge to zero by a * single order of magnitude at a time */ xtop = 1.0 - 0.1*fabs(1.0-x[i]); xbot = fabs(x[i]*0.1) - 1.0e-16; if (xnew > xtop) { damp = - APPROACH * (1.0 - x[i]) / dxneg[i]; *label = int(i); } else if (xnew < xbot) { damp = APPROACH * x[i] / dxneg[i]; *label = int(i); } else if (xnew > 3.0*std::max(x[i], 1.0E-10)) { damp = - 2.0 * std::max(x[i], 1.0E-10) / dxneg[i]; *label = int(i); } } if (damp < 1.0e-2) { damp = 1.0e-2; } /* * Only allow the damping parameter to increase by a factor of three each * iteration. Heuristic to avoid oscillations in the value of damp */ if (damp > damp_old*3) { damp = damp_old*3; *label = -1; } /* * Save old value of the damping parameter for use * in subsequent calls. */ damp_old = damp; return damp; } /* calc_damping */ #undef APPROACH /* * This function calculates the norm of an update, dx[], * based on the weighted values of x. */ static doublereal calcWeightedNorm(const doublereal wtX[], const doublereal dx[], size_t dim) { doublereal norm = 0.0; doublereal tmp; if (dim == 0) { return 0.0; } for (size_t i = 0; i < dim; i++) { tmp = dx[i] / wtX[i]; norm += tmp * tmp; } return (sqrt(norm/dim)); } /* * Calculate the weighting factors for norms wrt both the species * concentration unknowns and the residual unknowns. * */ void solveSP::calcWeights(doublereal wtSpecies[], doublereal wtResid[], const Array2D& Jac, const doublereal CSoln[], const doublereal abstol, const doublereal reltol) { size_t k, jcol, kindex, isp, nsp; doublereal sd; /* * First calculate the weighting factor for the concentrations of * the surface species and bulk species. */ kindex = 0; for (isp = 0; isp < m_numSurfPhases; isp++) { nsp = m_nSpeciesSurfPhase[isp]; sd = m_ptrsSurfPhase[isp]->siteDensity(); for (k = 0; k < nsp; k++, kindex++) { wtSpecies[kindex] = abstol * sd + reltol * fabs(CSoln[kindex]); } } if (m_bulkFunc == BULK_DEPOSITION) { for (isp = 0; isp < m_numBulkPhasesSS; isp++) { nsp = m_numBulkSpecies[isp]; sd = m_bulkPhasePtrs[isp]->molarDensity(); for (k = 0; k < nsp; k++, kindex++) { wtSpecies[kindex] = abstol * sd + reltol * fabs(CSoln[kindex]); } } } /* * Now do the residual Weights. Since we have the Jacobian, we * will use it to generate a number based on the what a significant * change in a solution variable does to each residual. * This is a row sum scale operation. */ for (k = 0; k < m_neq; k++) { wtResid[k] = 0.0; for (jcol = 0; jcol < m_neq; jcol++) { wtResid[k] += fabs(Jac(k,jcol) * wtSpecies[jcol]); } } } /* * This routine calculates a pretty conservative 1/del_t based * on MAX_i(sdot_i/(X_i*SDen0)). This probably guarantees * diagonal dominance. * * Small surface fractions are allowed to intervene in the del_t * determination, no matter how small. This may be changed. * Now minimum changed to 1.0e-12, * * Maximum time step set to time_scale. */ doublereal solveSP:: calc_t(doublereal netProdRateSolnSP[], doublereal XMolSolnSP[], int* label, int* label_old, doublereal* label_factor, int ioflag) { size_t k, isp, nsp, kstart; doublereal inv_timeScale = 1.0E-10; doublereal sden, tmp; size_t kindexSP = 0; *label = 0; updateMFSolnSP(XMolSolnSP); for (isp = 0; isp < m_numSurfPhases; isp++) { nsp = m_nSpeciesSurfPhase[isp]; // Get the interface kinetics associated with this surface InterfaceKinetics* m_kin = m_objects[isp]; // Calcuate the start of the species index for surfaces within // the InterfaceKinetics object size_t surfIndex = m_kin->surfacePhaseIndex(); kstart = m_kin->kineticsSpeciesIndex(0, surfIndex); ThermoPhase& THref = m_kin->thermo(surfIndex); m_kin->getNetProductionRates(DATA_PTR(m_numEqn1)); sden = THref.molarDensity(); for (k = 0; k < nsp; k++, kindexSP++) { size_t kspindex = kstart + k; netProdRateSolnSP[kindexSP] = m_numEqn1[kspindex]; if (XMolSolnSP[kindexSP] <= 1.0E-10) { tmp = 1.0E-10; } else { tmp = XMolSolnSP[kindexSP]; } tmp *= sden; tmp = fabs(netProdRateSolnSP[kindexSP]/ tmp); if (netProdRateSolnSP[kindexSP]> 0.0) { tmp /= 100.; } if (tmp > inv_timeScale) { inv_timeScale = tmp; *label = int(kindexSP); } } } /* * Increase time step exponentially as same species repeatedly * controls time step */ if (*label == *label_old) { *label_factor *= 1.5; } else { *label_old = *label; *label_factor = 1.0; } inv_timeScale = inv_timeScale / *label_factor; #ifdef DEBUG_SOLVESP if (ioflag > 1) { if (*label_factor > 1.0) { printf("Delta_t increase due to repeated controlling species = %e\n", *label_factor); } size_t kkin = m_kinSpecIndex[*label]; InterfaceKinetics* m_kin = m_objects[ispSpecial]; string sn = m_kin->kineticsSpeciesName(kkin); printf("calc_t: spec=%d(%s) sf=%e pr=%e dt=%e\n", *label, sn.c_str(), XMolSolnSP[*label], netProdRateSolnSP[*label], 1.0/inv_timeScale); } #endif return (inv_timeScale); } /* calc_t */ /** * printResJac(): prints out the residual and Jacobian. * */ #ifdef DEBUG_SOLVESP void solveSP::printResJac(int ioflag, int neq, const Array2D& Jac, doublereal resid[], doublereal wtRes[], doublereal norm) { int i, j, isp, nsp, irowKSI, irowISP; int kstartKSI; int kindexSP = 0; string sname, pname, cname; if (ioflag > 1) { printf(" Printout of residual and jacobian\n"); printf("\t Residual: weighted norm = %10.4e\n", norm); printf("\t Index Species_Name Residual " "Resid/wtRes wtRes\n"); for (isp = 0; isp < m_numSurfPhases; isp++) { nsp = m_nSpeciesSurfPhase[isp]; InterfaceKinetics* m_kin = m_objects[isp]; int surfPhaseIndex = m_kinObjPhaseIDSurfPhase[isp]; m_kin->getNetProductionRates(DATA_PTR(m_numEqn1)); kstartKSI = m_kin->kineticsSpeciesIndex(0, surfPhaseIndex); SurfPhase* sp_ptr = m_ptrsSurfPhase[isp]; pname = sp_ptr->id(); for (int k = 0; k < nsp; k++, kindexSP++) { sname = sp_ptr->speciesName(k); cname = pname + ":" + sname; printf("\t %d: %-24s: %11.3e %11.3e %11.3e\n", kindexSP, cname.c_str(), resid[kindexSP], resid[kindexSP]/wtRes[kindexSP], wtRes[kindexSP]); } } if (m_bulkFunc == BULK_DEPOSITION) { for (isp = 0; isp < m_numBulkPhasesSS; isp++) { // fill in } } if (ioflag > 2) { printf("\t Jacobian:\n"); for (i = 0; i < m_neq; i++) { irowISP = m_kinObjIndex[i]; InterfaceKinetics* m_kin = m_objects[irowISP]; irowKSI = m_kinSpecIndex[i]; ThermoPhase& THref = m_kin->speciesPhase(irowKSI); int phaseIndex = m_kin->speciesPhaseIndex(irowKSI); kstartKSI = m_kin->kineticsSpeciesIndex(0, phaseIndex); int klocal = i - m_eqnIndexStartSolnPhase[irowISP]; sname = THref.speciesName(klocal); printf("\t Row %d:%-16s:\n", i, sname.c_str()); printf("\t "); for (j = 0; j < m_neq; j++) { printf("%10.4e ", Jac(i,j)); } printf("\n"); } } } } /* printResJac */ #endif /* * Optional printing at the start of the solveSP problem */ void solveSP::print_header(int ioflag, int ifunc, doublereal time_scale, int damping, doublereal reltol, doublereal abstol, doublereal TKelvin, doublereal PGas, doublereal netProdRate[], doublereal XMolKinSpecies[]) { if (ioflag) { printf("\n================================ SOLVESP CALL SETUP " "========================================\n"); if (ifunc == SFLUX_INITIALIZE) { printf("\n SOLVESP Called with Initialization turned on\n"); printf(" Time scale input = %9.3e\n", time_scale); } else if (ifunc == SFLUX_RESIDUAL) { printf("\n SOLVESP Called to calculate steady state residual\n"); printf(" from a good initial guess\n"); } else if (ifunc == SFLUX_JACOBIAN) { printf("\n SOLVESP Called to calculate steady state jacobian\n"); printf(" from a good initial guess\n"); } else if (ifunc == SFLUX_TRANSIENT) { printf("\n SOLVESP Called to integrate surface in time\n"); printf(" for a total of %9.3e sec\n", time_scale); } else { fprintf(stderr,"Unknown ifunc flag = %d\n", ifunc); exit(EXIT_FAILURE); } if (m_bulkFunc == BULK_DEPOSITION) { printf(" The composition of the Bulk Phases will be calculated\n"); } else if (m_bulkFunc == BULK_ETCH) { printf(" Bulk Phases have fixed compositions\n"); } else { fprintf(stderr,"Unknown bulkFunc flag = %d\n", m_bulkFunc); exit(EXIT_FAILURE); } if (damping) { printf(" Damping is ON \n"); } else { printf(" Damping is OFF \n"); } printf(" Reltol = %9.3e, Abstol = %9.3e\n", reltol, abstol); } /* * Print out the initial guess */ #ifdef DEBUG_SOLVESP if (ioflag > 1) { printf("\n================================ INITIAL GUESS " "========================================\n"); int kindexSP = 0; for (size_t isp = 0; isp < m_numSurfPhases; isp++) { InterfaceKinetics* m_kin = m_objects[isp]; int surfIndex = m_kin->surfacePhaseIndex(); int nPhases = m_kin->nPhases(); m_kin->getNetProductionRates(netProdRate); updateMFKinSpecies(XMolKinSpecies, isp); printf("\n IntefaceKinetics Object # %d\n\n", isp); printf("\t Number of Phases = %d\n", nPhases); printf("\t Temperature = %10.3e Kelvin\n", TKelvin); printf("\t Pressure = %10.3g Pa\n\n", PGas); printf("\t Phase:SpecName Prod_Rate MoleFraction kindexSP\n"); printf("\t -------------------------------------------------------" "----------\n"); int kspindex = 0; bool inSurfacePhase = false; for (int ip = 0; ip < nPhases; ip++) { if (ip == surfIndex) { inSurfacePhase = true; } else { inSurfacePhase = false; } ThermoPhase& THref = m_kin->thermo(ip); int nsp = THref.nSpecies(); string pname = THref.id(); for (int k = 0; k < nsp; k++) { string sname = THref.speciesName(k); string cname = pname + ":" + sname; if (inSurfacePhase) { printf("\t %-24s %10.3e %10.3e %d\n", cname.c_str(), netProdRate[kspindex], XMolKinSpecies[kspindex], kindexSP); kindexSP++; } else { printf("\t %-24s %10.3e %10.3e\n", cname.c_str(), netProdRate[kspindex], XMolKinSpecies[kspindex]); } kspindex++; } } printf("==========================================================" "=================================\n"); } } #endif if (ioflag == 1) { printf("\n\n\t Iter Time Del_t Damp DelX " " Resid Name-Time Name-Damp\n"); printf("\t -----------------------------------------------" "------------------------------------\n"); } } void solveSP::printIteration(int ioflag, doublereal damp, int label_d, int label_t, doublereal inv_t, doublereal t_real, size_t iter, doublereal update_norm, doublereal resid_norm, doublereal netProdRate[], doublereal CSolnSP[], doublereal resid[], doublereal XMolSolnSP[], doublereal wtSpecies[], size_t dim, bool do_time) { size_t i, k; string nm; if (ioflag == 1) { printf("\t%6s ", int2str(iter).c_str()); if (do_time) { printf("%9.4e %9.4e ", t_real, 1.0/inv_t); } else for (i = 0; i < 22; i++) { printf(" "); } if (damp < 1.0) { printf("%9.4e ", damp); } else for (i = 0; i < 11; i++) { printf(" "); } printf("%9.4e %9.4e", update_norm, resid_norm); if (do_time) { k = m_kinSpecIndex[label_t]; size_t isp = m_kinObjIndex[label_t]; InterfaceKinetics* m_kin = m_objects[isp]; nm = m_kin->kineticsSpeciesName(k); printf(" %-16s", nm.c_str()); } else { for (i = 0; i < 16; i++) { printf(" "); } } if (label_d >= 0) { k = m_kinSpecIndex[label_d]; size_t isp = m_kinObjIndex[label_d]; InterfaceKinetics* m_kin = m_objects[isp]; nm = m_kin->kineticsSpeciesName(k); printf(" %-16s", nm.c_str()); } printf("\n"); } #ifdef DEBUG_SOLVESP else if (ioflag > 1) { updateMFSolnSP(XMolSolnSP); printf("\n\t Weighted norm of update = %10.4e\n", update_norm); printf("\t Name Prod_Rate XMol Conc " " Conc_Old wtConc"); if (damp < 1.0) { printf(" UnDamped_Conc"); } printf("\n"); printf("\t---------------------------------------------------------" "-----------------------------\n"); int kindexSP = 0; for (size_t isp = 0; isp < m_numSurfPhases; isp++) { int nsp = m_nSpeciesSurfPhase[isp]; InterfaceKinetics* m_kin = m_objects[isp]; //int surfPhaseIndex = m_kinObjPhaseIDSurfPhase[isp]; m_kin->getNetProductionRates(DATA_PTR(m_numEqn1)); for (int k = 0; k < nsp; k++, kindexSP++) { int kspIndex = m_kinSpecIndex[kindexSP]; nm = m_kin->kineticsSpeciesName(kspIndex); printf("\t%-16s %10.3e %10.3e %10.3e %10.3e %10.3e ", nm.c_str(), m_numEqn1[kspIndex], XMolSolnSP[kindexSP], CSolnSP[kindexSP], CSolnSP[kindexSP]+damp*resid[kindexSP], wtSpecies[kindexSP]); if (damp < 1.0) { printf("%10.4e ", CSolnSP[kindexSP]+(damp-1.0)*resid[kindexSP]); if (label_d == kindexSP) { printf(" Damp "); } } if (label_t == kindexSP) { printf(" Tctrl"); } printf("\n"); } } printf("\t--------------------------------------------------------" "------------------------------\n"); } #endif } /* printIteration */ void solveSP::printFinal(int ioflag, doublereal damp, int label_d, int label_t, doublereal inv_t, doublereal t_real, size_t iter, doublereal update_norm, doublereal resid_norm, doublereal netProdRateKinSpecies[], const doublereal CSolnSP[], const doublereal resid[], doublereal XMolSolnSP[], const doublereal wtSpecies[], const doublereal wtRes[], size_t dim, bool do_time, doublereal TKelvin, doublereal PGas) { size_t i, k; string nm; if (ioflag == 1) { printf("\tFIN%3s ", int2str(iter).c_str()); if (do_time) { printf("%9.4e %9.4e ", t_real, 1.0/inv_t); } else for (i = 0; i < 22; i++) { printf(" "); } if (damp < 1.0) { printf("%9.4e ", damp); } else for (i = 0; i < 11; i++) { printf(" "); } printf("%9.4e %9.4e", update_norm, resid_norm); if (do_time) { k = m_kinSpecIndex[label_t]; size_t isp = m_kinObjIndex[label_t]; InterfaceKinetics* m_kin = m_objects[isp]; nm = m_kin->kineticsSpeciesName(k); printf(" %-16s", nm.c_str()); } else { for (i = 0; i < 16; i++) { printf(" "); } } if (label_d >= 0) { k = m_kinSpecIndex[label_d]; size_t isp = m_kinObjIndex[label_d]; InterfaceKinetics* m_kin = m_objects[isp]; nm = m_kin->kineticsSpeciesName(k); printf(" %-16s", nm.c_str()); } printf(" -- success\n"); } #ifdef DEBUG_SOLVESP else if (ioflag > 1) { printf("\n================================== FINAL RESULT =========" "==================================================\n"); updateMFSolnSP(XMolSolnSP); printf("\n Weighted norm of solution update = %10.4e\n", update_norm); printf(" Weighted norm of residual update = %10.4e\n\n", resid_norm); printf(" Name Prod_Rate XMol Conc " " wtConc Resid Resid/wtResid wtResid"); if (damp < 1.0) { printf(" UnDamped_Conc"); } printf("\n"); printf("---------------------------------------------------------------" "---------------------------------------------\n"); int kindexSP = 0; for (size_t isp = 0; isp < m_numSurfPhases; isp++) { int nsp = m_nSpeciesSurfPhase[isp]; InterfaceKinetics* m_kin = m_objects[isp]; //int surfPhaseIndex = m_kinObjPhaseIDSurfPhase[isp]; m_kin->getNetProductionRates(DATA_PTR(m_numEqn1)); for (int k = 0; k < nsp; k++, kindexSP++) { int kspIndex = m_kinSpecIndex[kindexSP]; nm = m_kin->kineticsSpeciesName(kspIndex); printf("%-16s %10.3e %10.3e %10.3e %10.3e %10.3e %10.3e %10.3e", nm.c_str(), m_numEqn1[kspIndex], XMolSolnSP[kindexSP], CSolnSP[kindexSP], wtSpecies[kindexSP], resid[kindexSP], resid[kindexSP]/wtRes[kindexSP], wtRes[kindexSP]); if (damp < 1.0) { printf("%10.4e ", CSolnSP[kindexSP]+(damp-1.0)*resid[kindexSP]); if (label_d == kindexSP) { printf(" Damp "); } } if (label_t == kindexSP) { printf(" Tctrl"); } printf("\n"); } } printf("---------------------------------------------------------------" "---------------------------------------------\n"); doublereal* XMolKinSpecies = DATA_PTR(m_numEqn2); kindexSP = 0; for (size_t isp = 0; isp < m_numSurfPhases; isp++) { InterfaceKinetics* m_kin = m_objects[isp]; int surfIndex = m_kin->surfacePhaseIndex(); int nPhases = m_kin->nPhases(); m_kin->getNetProductionRates(netProdRateKinSpecies); updateMFKinSpecies(XMolKinSpecies, isp); printf("\n IntefaceKinetics Object # %d\n\n", isp); printf("\t Number of Phases = %d\n", nPhases); printf("\t Temperature = %10.3e Kelvin\n", TKelvin); printf("\t Pressure = %10.3g Pa\n\n", PGas); printf("\t Phase:SpecName Prod_Rate MoleFraction kindexSP\n"); printf("\t--------------------------------------------------------------" "---\n"); int kspindex = 0; bool inSurfacePhase = false; for (int ip = 0; ip < nPhases; ip++) { if (ip == surfIndex) { inSurfacePhase = true; } else { inSurfacePhase = false; } ThermoPhase& THref = m_kin->thermo(ip); int nsp = THref.nSpecies(); string pname = THref.id(); for (k = 0; k < nsp; k++) { string sname = THref.speciesName(k); string cname = pname + ":" + sname; if (inSurfacePhase) { printf("\t%-24s %10.3e %10.3e %d\n", cname.c_str(), netProdRateKinSpecies[kspindex], XMolKinSpecies[kspindex], kindexSP); kindexSP++; } else { printf("\t%-24s %10.3e %10.3e\n", cname.c_str(), netProdRateKinSpecies[kspindex], XMolKinSpecies[kspindex]); } kspindex++; } } } printf("\n"); printf("===============================================================" "============================================\n\n"); } #endif } #ifdef DEBUG_SOLVESP void solveSP:: printIterationHeader(int ioflag, doublereal damp,doublereal inv_t, doublereal t_real, int iter, bool do_time) { if (ioflag > 1) { printf("\n===============================Iteration %5d " "=================================\n", iter); if (do_time) { printf(" Transient step with: Real Time_n-1 = %10.4e sec,", t_real); printf(" Time_n = %10.4e sec\n", t_real + 1.0/inv_t); printf(" Delta t = %10.4e sec", 1.0/inv_t); } else { printf(" Steady Solve "); } if (damp < 1.0) { printf(", Damping value = %10.4e\n", damp); } else { printf("\n"); } } } #endif }