cantera/src/zeroD/IdealGasReactor.cpp
Ray Speth f1b28158bf [Reactor] Fix behavior of componentIndex with ambigous names
The name 'H' can mean either the species by that name or the entahlpy
of the reactor, in the case of ConstPressureReactor, and the previous
behavior always returned the index of the enthalpy.

This changes the behavior to preferentially return the species, and
adds alternative names for reactor state variables that are less
likely to generate namespace collisions: 'mass', 'volume',
'int_energy', 'enthalpy', 'temperature', 'distance', 'velocity'. The
single character names are still supported.

Resolves Issue 193.
2013-12-14 00:18:12 +00:00

268 lines
8.2 KiB
C++

/**
* @file IdealGasReactor.cpp A zero-dimensional reactor
*/
#include "cantera/zeroD/IdealGasReactor.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
{
void IdealGasReactor::setThermoMgr(ThermoPhase& thermo)
{
//! @TODO: Add a method to ThermoPhase that indicates whether a given
//! subclass is compatible with this reactor model
if (thermo.eosType() != cIdealGas) {
throw CanteraError("IdealGasReactor::setThermoMgr",
"Incompatible phase type provided");
}
Reactor::setThermoMgr(thermo);
}
void IdealGasReactor::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();
}
}
}
void IdealGasReactor::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("IdealGasReactor::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;
}
void IdealGasReactor::updateState(doublereal* y)
{
for (size_t i = 0; i < m_nv; i++) {
AssertFinite(y[i], "IdealGasReactor::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);
}
void IdealGasReactor::evalEqs(doublereal time, doublereal* y,
doublereal* ydot, doublereal* params)
{
m_thermo->restoreState(m_state);
applySensitivity(params);
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], "IdealGasReactor::evalEqs",
"ydot[" + int2str(i) + "] is not finite");
}
resetSensitivity(params);
}
size_t IdealGasReactor::componentIndex(const string& nm) const
{
size_t k = speciesIndex(nm);
if (k != npos) {
return k + 3;
} else if (nm == "m" || nm == "mass") {
return 0;
} else if (nm == "V" || nm == "volume") {
return 1;
} else if (nm == "T" || nm == "temperature") {
return 2;
} else {
return npos;
}
}
}