/** * @file ImplicitSurfChem.cpp * Definitions for the implicit integration of surface site density equations * (see \ref kineticsmgr and class * \link Cantera::ImplicitSurfChem ImplicitSurfChem\endlink). */ // Copyright 2001 California Institute of Technology #include "ImplicitSurfChem.h" #include "cantera/numerics/Integrator.h" #include "solveSP.h" using namespace std; namespace Cantera { // Constructor ImplicitSurfChem::ImplicitSurfChem(vector k) : FuncEval(), m_nsurf(0), m_nv(0), m_numBulkPhases(0), m_numTotalBulkSpecies(0), m_numTotalSpecies(0), m_integ(0), m_atol(1.e-14), m_rtol(1.e-7), m_maxstep(0.0), m_mediumSpeciesStart(-1), m_bulkSpeciesStart(-1), m_surfSpeciesStart(-1), m_surfSolver(0), m_commonTempPressForPhases(true), m_ioFlag(0) { m_nsurf = k.size(); size_t ns, nsp; size_t nt, ntmax = 0; size_t kinSpIndex = 0; // Loop over the number of surface kinetics objects for (size_t n = 0; n < m_nsurf; n++) { InterfaceKinetics* kinPtr = k[n]; m_vecKinPtrs.push_back(kinPtr); ns = k[n]->surfacePhaseIndex(); if (ns == npos) throw CanteraError("ImplicitSurfChem", "kinetics manager contains no surface phase"); m_surfindex.push_back(ns); m_surf.push_back((SurfPhase*)&k[n]->thermo(ns)); nsp = m_surf.back()->nSpecies(); m_nsp.push_back(nsp); m_nv += m_nsp.back(); nt = k[n]->nTotalSpecies(); if (nt > ntmax) { ntmax = nt; } m_specStartIndex.push_back(kinSpIndex); kinSpIndex += nsp; size_t nPhases = kinPtr->nPhases(); vector_int pLocTmp(nPhases); size_t imatch = npos; for (size_t ip = 0; ip < nPhases; ip++) { if (ip != ns) { ThermoPhase* thPtr = & kinPtr->thermo(ip); if ((imatch = checkMatch(m_bulkPhases, thPtr)) == npos) { m_bulkPhases.push_back(thPtr); m_numBulkPhases++; nsp = thPtr->nSpecies(); m_nspBulkPhases.push_back(nsp); m_numTotalBulkSpecies += nsp; imatch = m_bulkPhases.size() - 1; } pLocTmp[ip] = int(imatch); } else { pLocTmp[ip] = -int(n); } } pLocVec.push_back(pLocTmp); } m_numTotalSpecies = m_nv + m_numTotalBulkSpecies; m_concSpecies.resize(m_numTotalSpecies, 0.0); m_concSpeciesSave.resize(m_numTotalSpecies, 0.0); m_integ = newIntegrator("CVODE"); // use backward differencing, with a full Jacobian computed // numerically, and use a Newton linear iterator m_integ->setMethod(BDF_Method); m_integ->setProblemType(DENSE + NOJAC); m_integ->setIterator(Newton_Iter); m_work.resize(ntmax); } int ImplicitSurfChem::checkMatch(std::vector m_vec, ThermoPhase* thPtr) { int retn = -1; for (int i = 0; i < (int) m_vec.size(); i++) { ThermoPhase* th = m_vec[i]; if (th == thPtr) { return i; } } return retn; } /* * Destructor. Deletes the integrator. */ ImplicitSurfChem::~ImplicitSurfChem() { if (m_integ) { delete m_integ; } if (m_surfSolver) { delete m_surfSolver; } } // overloaded method of FuncEval. Called by the integrator to // get the initial conditions. void ImplicitSurfChem::getInitialConditions(doublereal t0, size_t lenc, doublereal* c) { size_t loc = 0; for (size_t n = 0; n < m_nsurf; n++) { m_surf[n]->getCoverages(c + loc); loc += m_nsp[n]; } } /* * Must be called before calling method 'advance' */ void ImplicitSurfChem::initialize(doublereal t0) { m_integ->setTolerances(m_rtol, m_atol); m_integ->initialize(t0, *this); } // Integrate from t0 to t1. The integrator is reinitialized first. /* * This routine does a time accurate solve from t = t0 to t = t1. * of the surface problem. * * @param t0 Initial Time -> this is an input * @param t1 Final Time -> This is an input */ void ImplicitSurfChem::integrate(doublereal t0, doublereal t1) { m_integ->initialize(t0, *this); m_integ->setMaxStepSize(t1 - t0); m_integ->integrate(t1); updateState(m_integ->solution()); } // Integrate from t0 to t1 without reinitializing the integrator. /* * Use when the coverages have not changed from * their values on return from the last call to integrate or * integrate0. * * @param t0 Initial Time -> this is an input * @param t1 Final Time -> This is an input */ void ImplicitSurfChem::integrate0(doublereal t0, doublereal t1) { m_integ->integrate(t1); updateState(m_integ->solution()); } void ImplicitSurfChem::updateState(doublereal* c) { size_t loc = 0; for (size_t n = 0; n < m_nsurf; n++) { m_surf[n]->setCoverages(c + loc); loc += m_nsp[n]; } } /* * Called by the integrator to evaluate ydot given y at time 'time'. */ void ImplicitSurfChem::eval(doublereal time, doublereal* y, doublereal* ydot, doublereal* p) { updateState(y); // synchronize the surface state(s) with y doublereal rs0, sum; size_t loc, kstart; for (size_t n = 0; n < m_nsurf; n++) { rs0 = 1.0/m_surf[n]->siteDensity(); m_vecKinPtrs[n]->getNetProductionRates(DATA_PTR(m_work)); kstart = m_vecKinPtrs[n]->kineticsSpeciesIndex(0,m_surfindex[n]); sum = 0.0; loc = 0; for (size_t k = 1; k < m_nsp[n]; k++) { ydot[k + loc] = m_work[kstart + k] * rs0 * m_surf[n]->size(k); sum -= ydot[k]; } ydot[loc] = sum; loc += m_nsp[n]; } } // Solve for the pseudo steady-state of the surface problem /* * Solve for the steady state of the surface problem. * This is the same thing as the advanceCoverages() function, * but at infinite times. * * Note, a direct solve is carried out under the hood here, * to reduce the computational time. */ void ImplicitSurfChem::solvePseudoSteadyStateProblem(int ifuncOverride, doublereal timeScaleOverride) { int ifunc; /* * set bulkFunc * -> We assume that the bulk concentrations are constant. */ int bulkFunc = BULK_ETCH; /* * time scale - time over which to integrate equations */ doublereal time_scale = timeScaleOverride; if (!m_surfSolver) { m_surfSolver = new solveSP(this, bulkFunc); /* * set ifunc, which sets the algorithm. */ ifunc = SFLUX_INITIALIZE; } else { ifunc = SFLUX_RESIDUAL; } // Possibly override the ifunc value if (ifuncOverride >= 0) { ifunc = ifuncOverride; } /* * Get the specifications for the problem from the values * in the ThermoPhase objects for all phases. * * 1) concentrations of all species in all phases, m_concSpecies[] * 2) Temperature and pressure */ getConcSpecies(DATA_PTR(m_concSpecies)); InterfaceKinetics* ik = m_vecKinPtrs[0]; ThermoPhase& tp = ik->thermo(0); doublereal TKelvin = tp.temperature(); doublereal PGas = tp.pressure(); /* * Make sure that there is a common temperature and * pressure for all ThermoPhase objects belonging to the * interfacial kinetics object, if it is required by * the problem statement. */ if (m_commonTempPressForPhases) { setCommonState_TP(TKelvin, PGas); } doublereal reltol = 1.0E-6; doublereal atol = 1.0E-20; /* * Install a filter for negative concentrations. One of the * few ways solvess can fail is if concentrations on input * are below zero. */ bool rset = false; for (size_t k = 0; k < m_nv; k++) { if (m_concSpecies[k] < 0.0) { rset = true; m_concSpecies[k] = 0.0; } } if (rset) { setConcSpecies(DATA_PTR(m_concSpecies)); } m_surfSolver->m_ioflag = m_ioFlag; // Save the current solution copy(m_concSpecies.begin(), m_concSpecies.end(), m_concSpeciesSave.begin()); int retn = m_surfSolver->solveSurfProb(ifunc, time_scale, TKelvin, PGas, reltol, atol); if (retn != 1) { // reset the concentrations copy(m_concSpeciesSave.begin(), m_concSpeciesSave.end(), m_concSpecies.begin()); setConcSpecies(DATA_PTR(m_concSpeciesSave)); ifunc = SFLUX_INITIALIZE; retn = m_surfSolver->solveSurfProb(ifunc, time_scale, TKelvin, PGas, reltol, atol); if (retn != 1) { throw CanteraError("ImplicitSurfChem::solvePseudoSteadyStateProblem", "solveSP return an error condition!"); } } } /* * getConcSpecies(): * * Fills the local concentration vector, m_concSpecies for all of the * species in all of the phases that are unknowns in the surface * problem. * * m_concSpecies[] */ void ImplicitSurfChem::getConcSpecies(doublereal* const vecConcSpecies) const { size_t kstart; for (size_t ip = 0; ip < m_nsurf; ip++) { ThermoPhase* TP_ptr = m_surf[ip]; kstart = m_specStartIndex[ip]; TP_ptr->getConcentrations(vecConcSpecies + kstart); } kstart = m_nv; for (size_t ip = 0; ip < m_numBulkPhases; ip++) { ThermoPhase* TP_ptr = m_bulkPhases[ip]; TP_ptr->getConcentrations(vecConcSpecies + kstart); kstart += TP_ptr->nSpecies(); } } /* * setConcSpecies(): * * Fills the local concentration vector, m_concSpecies for all of the * species in all of the phases that are unknowns in the surface * problem. * * m_concSpecies[] */ void ImplicitSurfChem::setConcSpecies(const doublereal* const vecConcSpecies) { size_t kstart; for (size_t ip = 0; ip < m_nsurf; ip++) { ThermoPhase* TP_ptr = m_surf[ip]; kstart = m_specStartIndex[ip]; TP_ptr->setConcentrations(vecConcSpecies + kstart); } kstart = m_nv; for (size_t ip = 0; ip < m_numBulkPhases; ip++) { ThermoPhase* TP_ptr = m_bulkPhases[ip]; TP_ptr->setConcentrations(vecConcSpecies + kstart); kstart += TP_ptr->nSpecies(); } } /* * setCommonState_TP(): * * Sets a common temperature and pressure amongst the * thermodynamic objects in the interfacial kinetics object. * * Units Temperature = Kelvin * Pressure = Pascal */ void ImplicitSurfChem:: setCommonState_TP(doublereal TKelvin, doublereal PresPa) { for (size_t ip = 0; ip < m_nsurf; ip++) { ThermoPhase* TP_ptr = m_surf[ip]; TP_ptr->setState_TP(TKelvin, PresPa); } for (size_t ip = 0; ip < m_numBulkPhases; ip++) { ThermoPhase* TP_ptr = m_bulkPhases[ip]; TP_ptr->setState_TP(TKelvin, PresPa); } } }