cantera/src/zeroD/Reactor.cpp
Ray Speth a59309e81e [Reactor] Independent variables for Reactor are now T,V, and Yk
This formulation for the reactor's governing equations significantly improves
the performance of integrator, mostly by improving the quality of the
Jacobian. The effect is small for smaller mechanisms (GRI 3.0) but can lead to
order-of-magnitude improvements for mechanisms with hundreds or thousands of
species.

Since this set of variables corresponds to the intrinsic state variables used
for IdealGasPhase objects, we also eliminate the need to iterate when setting
the state of the thermo object.

Additionally, by using temperature as an independent variable, the
temperature-dependent parts of the kinetic rate expressions do not need to be
recomputed while updating the Jacobian (this optimization is not currently
implemented).
2013-05-23 19:33:16 +00:00

367 lines
11 KiB
C++

/**
* @file Reactor.cpp
*
* A zero-dimensional reactor
*/
// Copyright 2001 California Institute of Technology
#include "cantera/zeroD/Reactor.h"
#include "cantera/zeroD/FlowDevice.h"
#include "cantera/zeroD/Wall.h"
#include "cantera/kinetics/InterfaceKinetics.h"
#include "cantera/thermo/SurfPhase.h"
#include "cantera/zeroD/ReactorNet.h"
#include <cfloat>
using namespace std;
namespace Cantera
{
Reactor::Reactor() : ReactorBase(),
m_kin(0),
m_vdot(0.0),
m_Q(0.0),
m_chem(false),
m_energy(true),
m_nsens(npos)
{}
// overloaded method of FuncEval. Called by the integrator to
// get the initial conditions.
void Reactor::getInitialConditions(double t0, size_t leny, double* y)
{
m_init = true;
if (m_thermo == 0) {
cout << "Error: reactor is empty." << endl;
return;
}
m_thermo->restoreState(m_state);
// set the first component to the total mass
m_mass = m_thermo->density() * m_vol;
y[0] = m_mass;
// set the second component to the total volume
y[1] = m_vol;
// Set the third component to the temperature
y[2] = m_thermo->temperature();
// set components y+3 ... y+K+2 to the mass fractions of each species
m_thermo->getMassFractions(y+3);
// set the remaining components to the surface species
// coverages on the walls
size_t loc = m_nsp + 3;
SurfPhase* surf;
for (size_t m = 0; m < m_nwalls; m++) {
surf = m_wall[m]->surface(m_lr[m]);
if (surf) {
m_wall[m]->getCoverages(m_lr[m], y + loc);
loc += surf->nSpecies();
}
}
}
/*
* Must be called before calling method 'advance'
*/
void Reactor::initialize(doublereal t0)
{
m_thermo->restoreState(m_state);
m_sdot.resize(m_nsp, 0.0);
m_wdot.resize(m_nsp, 0.0);
m_uk.resize(m_nsp, 0.0);
m_nv = m_nsp + 3;
for (size_t w = 0; w < m_nwalls; w++)
if (m_wall[w]->surface(m_lr[w])) {
m_nv += m_wall[w]->surface(m_lr[w])->nSpecies();
}
m_enthalpy = m_thermo->enthalpy_mass();
m_pressure = m_thermo->pressure();
m_intEnergy = m_thermo->intEnergy_mass();
size_t nt = 0, maxnt = 0;
for (size_t m = 0; m < m_nwalls; m++) {
m_wall[m]->initialize();
if (m_wall[m]->kinetics(m_lr[m])) {
nt = m_wall[m]->kinetics(m_lr[m])->nTotalSpecies();
if (nt > maxnt) {
maxnt = nt;
}
if (m_wall[m]->kinetics(m_lr[m])) {
if (&m_kin->thermo(0) !=
&m_wall[m]->kinetics(m_lr[m])->thermo(0)) {
throw CanteraError("Reactor::initialize",
"First phase of all kinetics managers must be"
" the gas.");
}
}
}
}
m_work.resize(maxnt);
std::sort(m_pnum.begin(), m_pnum.end());
m_init = true;
}
size_t Reactor::nSensParams()
{
if (m_nsens == npos) {
// determine the number of sensitivity parameters
size_t m, ns;
m_nsens = m_pnum.size();
for (m = 0; m < m_nwalls; m++) {
ns = m_wall[m]->nSensParams(m_lr[m]);
m_nsens_wall.push_back(ns);
m_nsens += ns;
}
}
return m_nsens;
}
void Reactor::updateState(doublereal* y)
{
for (size_t i = 0; i < m_nv; i++) {
AssertFinite(y[i], "Reactor::updateState",
"y[" + int2str(i) + "] is not finite");
}
// The components of y are [0] the total mass, [1] the total volume,
// [2] the temperature, [3...K+3] are the mass fractions of each species,
// and [K+3...] are the coverages of surface species on each wall.
m_mass = y[0];
m_vol = y[1];
m_thermo->setMassFractions_NoNorm(y+3);
m_thermo->setState_TR(y[2], m_mass / m_vol);
size_t loc = m_nsp + 3;
SurfPhase* surf;
for (size_t m = 0; m < m_nwalls; m++) {
surf = m_wall[m]->surface(m_lr[m]);
if (surf) {
m_wall[m]->setCoverages(m_lr[m], y+loc);
loc += surf->nSpecies();
}
}
// save parameters needed by other connected reactors
m_enthalpy = m_thermo->enthalpy_mass();
m_pressure = m_thermo->pressure();
m_intEnergy = m_thermo->intEnergy_mass();
m_thermo->saveState(m_state);
}
/*
* Called by the integrator to evaluate ydot given y at time 'time'.
*/
void Reactor::evalEqs(doublereal time, doublereal* y,
doublereal* ydot, doublereal* params)
{
m_thermo->restoreState(m_state);
// process sensitivity parameters
if (params) {
size_t npar = m_pnum.size();
for (size_t n = 0; n < npar; n++) {
double mult = m_kin->multiplier(m_pnum[n]);
m_kin->setMultiplier(m_pnum[n], mult*params[n]);
}
size_t ploc = npar;
for (size_t m = 0; m < m_nwalls; m++) {
if (m_nsens_wall[m] > 0) {
m_wall[m]->setSensitivityParameters(m_lr[m], params + ploc);
ploc += m_nsens_wall[m];
}
}
}
m_vdot = 0.0;
m_Q = 0.0;
double mcvdTdt = 0.0; // m * c_v * dT/dt
double dmdt = 0.0; // dm/dt (gas phase)
double* dYdt = ydot + 3;
m_thermo->getPartialMolarIntEnergies(&m_uk[0]);
// compute wall terms
size_t loc = m_nsp+3;
fill(m_sdot.begin(), m_sdot.end(), 0.0);
for (size_t i = 0; i < m_nwalls; i++) {
int lr = 1 - 2*m_lr[i];
double vdot = lr*m_wall[i]->vdot(time);
m_vdot += vdot;
m_Q += lr*m_wall[i]->Q(time);
Kinetics* kin = m_wall[i]->kinetics(m_lr[i]);
SurfPhase* surf = m_wall[i]->surface(m_lr[i]);
if (surf && kin) {
double rs0 = 1.0/surf->siteDensity();
size_t nk = surf->nSpecies();
double sum = 0.0;
surf->setTemperature(m_state[0]);
m_wall[i]->syncCoverages(m_lr[i]);
kin->getNetProductionRates(DATA_PTR(m_work));
size_t ns = kin->surfacePhaseIndex();
size_t surfloc = kin->kineticsSpeciesIndex(0,ns);
for (size_t k = 1; k < nk; k++) {
ydot[loc + k] = m_work[surfloc+k]*rs0*surf->size(k);
sum -= ydot[loc + k];
}
ydot[loc] = sum;
loc += nk;
double wallarea = m_wall[i]->area();
for (size_t k = 0; k < m_nsp; k++) {
m_sdot[k] += m_work[k]*wallarea;
}
}
}
const vector_fp& mw = m_thermo->molecularWeights();
const doublereal* Y = m_thermo->massFractions();
if (m_chem) {
m_kin->getNetProductionRates(&m_wdot[0]); // "omega dot"
}
double mdot_surf = 0.0; // net mass flux from surfaces
for (size_t k = 0; k < m_nsp; k++) {
// production in gas phase and from surfaces
dYdt[k] = (m_wdot[k] * m_vol + m_sdot[k]) * mw[k] / m_mass;
mdot_surf += m_sdot[k] * mw[k];
}
dmdt += mdot_surf;
// compression work and external heat transfer
mcvdTdt += - m_pressure * m_vdot - m_Q;
for (size_t n = 0; n < m_nsp; n++) {
// heat release from gas phase and surface reations
mcvdTdt -= m_wdot[n] * m_uk[n] * m_vol;
mcvdTdt -= m_sdot[n] * m_uk[n];
// dilution by net surface mass flux
dYdt[n] -= Y[n] * mdot_surf / m_mass;
}
// add terms for open system
if (m_open) {
// outlets
for (size_t i = 0; i < m_nOutlets; i++) {
double mdot_out = m_outlet[i]->massFlowRate(time);
dmdt -= mdot_out; // mass flow out of system
mcvdTdt -= mdot_out * m_pressure * m_vol / m_mass; // flow work
}
// inlets
for (size_t i = 0; i < m_nInlets; i++) {
double mdot_in = m_inlet[i]->massFlowRate(time);
dmdt += mdot_in; // mass flow into system
mcvdTdt += m_inlet[i]->enthalpy_mass() * mdot_in;
for (size_t n = 0; n < m_nsp; n++) {
double mdot_spec = m_inlet[i]->outletSpeciesMassFlowRate(n);
// flow of species into system and dilution by other species
dYdt[n] += (mdot_spec - mdot_in * Y[n]) / m_mass;
// In combintion with h_in*mdot_in, flow work plus thermal
// energy carried with the species
mcvdTdt -= m_uk[n] / mw[n] * mdot_spec;
}
}
}
ydot[0] = dmdt;
ydot[1] = m_vdot;
if (m_energy) {
ydot[2] = mcvdTdt / (m_mass * m_thermo->cv_mass());
} else {
ydot[2] = 0;
}
for (size_t i = 0; i < m_nv; i++) {
AssertFinite(ydot[i], "Reactor::evalEqs",
"ydot[" + int2str(i) + "] is not finite");
}
// reset sensitivity parameters
if (params) {
size_t npar = m_pnum.size();
for (size_t n = 0; n < npar; n++) {
double mult = m_kin->multiplier(m_pnum[n]);
m_kin->setMultiplier(m_pnum[n], mult/params[n]);
}
size_t ploc = npar;
for (size_t m = 0; m < m_nwalls; m++) {
if (m_nsens_wall[m] > 0) {
m_wall[m]->resetSensitivityParameters(m_lr[m]);
ploc += m_nsens_wall[m];
}
}
}
}
void Reactor::addSensitivityReaction(size_t rxn)
{
if (rxn >= m_kin->nReactions())
throw CanteraError("Reactor::addSensitivityReaction",
"Reaction number out of range ("+int2str(rxn)+")");
network().registerSensitivityReaction(this, rxn,
name()+": "+m_kin->reactionString(rxn));
m_pnum.push_back(rxn);
m_mult_save.push_back(1.0);
}
std::vector<std::pair<void*, int> > Reactor::getSensitivityOrder() const
{
std::vector<std::pair<void*, int> > order;
order.push_back(std::make_pair(const_cast<Reactor*>(this), 0));
for (size_t n = 0; n < m_nwalls; n++) {
if (m_nsens_wall[n]) {
order.push_back(std::make_pair(m_wall[n], m_lr[n]));
}
}
return order;
}
size_t Reactor::componentIndex(const string& nm) const
{
if (nm == "m") {
return 0;
}
if (nm == "V") {
return 1;
}
if (nm == "T") {
return 2;
}
// check for a gas species name
size_t k = m_thermo->speciesIndex(nm);
if (k != npos) {
return k + 3;
}
// check for a wall species
size_t walloffset = 0, kp = 0;
thermo_t* th;
for (size_t m = 0; m < m_nwalls; m++) {
if (m_wall[m]->kinetics(m_lr[m])) {
kp = m_wall[m]->kinetics(m_lr[m])->reactionPhaseIndex();
th = &m_wall[m]->kinetics(m_lr[m])->thermo(kp);
k = th->speciesIndex(nm);
if (k != npos) {
return k + 3 + m_nsp + walloffset;
} else {
walloffset += th->nSpecies();
}
}
}
return npos;
}
}