271 lines
6.8 KiB
C++
271 lines
6.8 KiB
C++
//! @file ReactorNet.cpp
|
|
#include "cantera/zeroD/ReactorNet.h"
|
|
#include "cantera/zeroD/FlowDevice.h"
|
|
#include "cantera/zeroD/Wall.h"
|
|
|
|
#include <cstdio>
|
|
|
|
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;
|
|
}
|
|
|
|
}
|