//! @file ReactorNet.cpp #include "cantera/zeroD/ReactorNet.h" #include "cantera/zeroD/FlowDevice.h" #include "cantera/zeroD/Wall.h" #include using namespace std; namespace Cantera { ReactorNet::ReactorNet() : m_integ(0), m_time(0.0), m_init(false), m_integrator_init(false), m_nv(0), m_rtol(1.0e-9), m_rtolsens(1.0e-4), m_atols(1.0e-15), m_atolsens(1.0e-6), m_maxstep(0.0), m_maxErrTestFails(0), m_verbose(false) { 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); } ReactorNet::~ReactorNet() { delete m_integ; } void ReactorNet::setInitialTime(double time) { m_time = time; m_integrator_init = false; } void ReactorNet::setMaxTimeStep(double maxstep) { m_maxstep = maxstep; m_init = false; } void ReactorNet::setMaxErrTestFails(int nmax) { m_maxErrTestFails = nmax; m_init = false; } void ReactorNet::setTolerances(double rtol, double atol) { if (rtol >= 0.0) { m_rtol = rtol; } if (atol >= 0.0) { m_atols = atol; } m_init = false; } void ReactorNet::setSensitivityTolerances(double rtol, double atol) { if (rtol >= 0.0) { m_rtolsens = rtol; } if (atol >= 0.0) { m_atolsens = atol; } m_init = false; } void ReactorNet::initialize() { size_t n, nv; m_nv = 0; debuglog("Initializing reactor network.\n", m_verbose); if (m_reactors.empty()) { throw CanteraError("ReactorNet::initialize", "no reactors in network!"); } m_start.assign(1, 0); for (n = 0; n < m_reactors.size(); n++) { Reactor& r = *m_reactors[n]; r.initialize(m_time); nv = r.neq(); m_nv += nv; m_start.push_back(m_nv); if (m_verbose) { writelog("Reactor {:d}: {:d} variables.\n", n, nv); writelog(" {:d} sensitivity params.\n", r.nSensParams()); } if (r.type() == FlowReactorType && m_reactors.size() > 1) { throw CanteraError("ReactorNet::initialize", "FlowReactors must be used alone."); } } m_ydot.resize(m_nv,0.0); m_atol.resize(neq()); fill(m_atol.begin(), m_atol.end(), m_atols); m_integ->setTolerances(m_rtol, neq(), m_atol.data()); m_integ->setSensitivityTolerances(m_rtolsens, m_atolsens); m_integ->setMaxStepSize(m_maxstep); m_integ->setMaxErrTestFails(m_maxErrTestFails); if (m_verbose) { writelog("Number of equations: {:d}\n", neq()); writelog("Maximum time step: {:14.6g}\n", m_maxstep); } m_integ->initialize(m_time, *this); m_integrator_init = true; m_init = true; } void ReactorNet::reinitialize() { if (m_init) { debuglog("Re-initializing reactor network.\n", m_verbose); m_integ->reinitialize(m_time, *this); m_integrator_init = true; } else { initialize(); } } void ReactorNet::advance(doublereal time) { if (!m_init) { initialize(); } else if (!m_integrator_init) { reinitialize(); } m_integ->integrate(time); m_time = time; updateState(m_integ->solution()); } double ReactorNet::step(doublereal time) { if (time != -999) { warn_deprecated("ReactorNet::step(t)", "The argument to this function" " is deprecated and will be removed after Cantera 2.3."); } if (!m_init) { initialize(); } else if (!m_integrator_init) { reinitialize(); } m_time = m_integ->step(m_time + 1.0); updateState(m_integ->solution()); return m_time; } void ReactorNet::addReactor(Reactor& r) { r.setNetwork(this); m_reactors.push_back(&r); } void ReactorNet::eval(doublereal t, doublereal* y, doublereal* ydot, doublereal* p) { size_t n; updateState(y); for (n = 0; n < m_reactors.size(); n++) { m_reactors[n]->evalEqs(t, y + m_start[n], ydot + m_start[n], p); } checkFinite("ydot", ydot, m_nv); } double ReactorNet::sensitivity(size_t k, size_t p) { if (!m_init) { initialize(); } if (p >= m_sens_params.size()) { throw IndexError("ReactorNet::sensitivity", "m_sens_params", p, m_sens_params.size()-1); } double denom = m_integ->solution(k); if (denom == 0.0) { denom = SmallNumber; } return m_integ->sensitivity(k, p) / denom; } void ReactorNet::evalJacobian(doublereal t, doublereal* y, doublereal* ydot, doublereal* p, Array2D* j) { doublereal ysave, dy; Array2D& jac = *j; //evaluate the unperturbed ydot eval(t, y, ydot, p); for (size_t n = 0; n < m_nv; n++) { // perturb x(n) ysave = y[n]; dy = m_atol[n] + fabs(ysave)*m_rtol; y[n] = ysave + dy; dy = y[n] - ysave; // calculate perturbed residual eval(t, y, m_ydot.data(), p); // compute nth column of Jacobian for (size_t m = 0; m < m_nv; m++) { jac(m,n) = (m_ydot[m] - ydot[m])/dy; } y[n] = ysave; } } void ReactorNet::updateState(doublereal* y) { checkFinite("y", y, m_nv); for (size_t n = 0; n < m_reactors.size(); n++) { m_reactors[n]->updateState(y + m_start[n]); } } void ReactorNet::getInitialConditions(double t0, size_t leny, double* y) { warn_deprecated("ReactorNet::getInitialConditions", "Use getState instead. To be removed after Cantera 2.3."); getState(y); } void ReactorNet::getState(double* y) { for (size_t n = 0; n < m_reactors.size(); n++) { m_reactors[n]->getState(y + m_start[n]); } } size_t ReactorNet::globalComponentIndex(const string& component, size_t reactor) { if (!m_init) { initialize(); } return m_start[reactor] + m_reactors[reactor]->componentIndex(component); } std::string ReactorNet::componentName(size_t i) const { for (auto r : m_reactors) { if (i < r->neq()) { return r->name() + ": " + r->componentName(i); } else { i -= r->neq(); } } throw CanteraError("ReactorNet::componentName", "Index out of bounds"); } size_t ReactorNet::registerSensitivityParameter( const std::string& name, double value, double scale) { if (m_integrator_init) { throw CanteraError("ReactorNet::registerSensitivityParameter", "Sensitivity parameters cannot be added after the" "integrator has been initialized."); } m_paramNames.push_back(name); m_sens_params.push_back(value); m_paramScales.push_back(scale); return m_sens_params.size() - 1; } }