[Input] Parse YAML entries for elementary reactions

This commit is contained in:
Ray Speth 2018-12-05 13:09:25 -05:00
parent 224c46ebbb
commit de80f06887
4 changed files with 147 additions and 0 deletions

View file

@ -35,6 +35,7 @@ namespace Cantera
using std::shared_ptr;
using std::make_shared;
using std::unique_ptr;
using std::isnan; // workaround for bug in libstdc++ 4.8
/*!

View file

@ -16,6 +16,8 @@ namespace Cantera
{
class Kinetics;
class AnyMap;
class UnitSystem;
//! Intermediate class which stores data about a reaction and its rate
//! parameterization so that it can be added to a Kinetics object.
@ -257,6 +259,10 @@ public:
//! Create a new Reaction object for the reaction defined in `rxn_node`
shared_ptr<Reaction> newReaction(const XML_Node& rxn_node);
//! Create a new Reaction object using the specified parameters
unique_ptr<Reaction> newReaction(const AnyMap& rxn_node, const Kinetics& kin,
const UnitSystem& units);
//! Create Reaction objects for all `<reaction>` nodes in an XML document.
//!
//! The `<reaction>` nodes are assumed to be children of the `<reactionData>`

View file

@ -7,8 +7,11 @@
#include "cantera/kinetics/Reaction.h"
#include "cantera/kinetics/FalloffFactory.h"
#include "cantera/kinetics/Kinetics.h"
#include "cantera/base/ctml.h"
#include "cantera/base/Array.h"
#include "cantera/base/AnyMap.h"
#include "cantera/base/Units.h"
#include <sstream>
namespace Cantera
@ -277,6 +280,32 @@ Arrhenius readArrhenius(const XML_Node& arrhenius_node)
getFloat(arrhenius_node, "E", "actEnergy") / GasConstant);
}
Arrhenius readArrhenius(const Reaction& R, const AnyValue& rate_node,
const Kinetics& kin, const UnitSystem& units)
{
// Determine the units of the rate coefficient
double len_dim = - static_cast<double>(kin.thermo(kin.reactionPhaseIndex()).nDim());
double quantity_dim = 1.0;
for (const auto& order : R.orders) {
len_dim += order.second * kin.speciesPhase(order.first).nDim();
quantity_dim -= order.second;
}
for (const auto& stoich : R.reactants) {
// Order for each reactant is the reactant stoichiometric coefficient,
// unless already overridden by user-specified orders
if (R.orders.find(stoich.first) == R.orders.end()) {
len_dim += stoich.second * kin.speciesPhase(stoich.first).nDim();
quantity_dim -= stoich.second;
}
}
const auto& rate = rate_node.asVector<AnyValue>();
double A = units.convert(rate[0], Units(1.0, 0, len_dim, -1, 0, 0, quantity_dim));
double b = rate[1].asDouble();
double Ta = units.convertMolarEnergy(rate[2], "K");
return Arrhenius(A, b, Ta);
}
//! Parse falloff parameters, given a rateCoeff node
/*!
* @verbatim
@ -350,6 +379,64 @@ void setupReaction(Reaction& R, const XML_Node& rxn_node)
R.reversible = (rev == "true" || rev == "yes");
}
void setupReaction(Reaction& R, const AnyMap& node)
{
// Parse the reaction equation to determine participating species and
// stoichiometric coefficients
std::vector<std::string> tokens;
tokenizeString(node.at("equation").asString(), tokens);
tokens.push_back("+"); // makes parsing last species not a special case
size_t last_used = npos; // index of last-used token
bool reactants = true;
for (size_t i = 0; i < tokens.size(); i++) {
if (tokens[i] == "+" || tokens[i] == "<=>" || tokens[i] == "=>") {
std::string species = tokens[i-1];
double stoich;
if (last_used == i-2) { // Species with no stoich. coefficient
stoich = 1.0;
} else if (last_used == i-3) { // Stoich. coefficient and species
stoich = fpValueCheck(tokens[i-2]);
} else {
throw CanteraError("setupReaction", "Error parsing reaction "
"string '{}'", node.at("equation").asString());
}
if (reactants) {
R.reactants[species] += stoich;
} else {
R.products[species] += stoich;
}
last_used = i;
}
// Tokens after this point are part of the products string
if (tokens[i] == "<=>") {
R.reversible = true;
reactants = false;
} else if (tokens[i] == "=>") {
R.reversible = false;
reactants = false;
}
}
// Non-stoichiometric reaction orders
std::map<std::string, double> orders;
if (node.hasKey("orders")) {
for (const auto& order : node.at("orders").asMap<double>()) {
R.orders[order.first] = order.second;
}
}
//Flags
R.id = node.getString("id", "");
R.duplicate = node.getBool("duplicate", false);
R.allow_negative_orders = node.getBool("negative-orders", false);
R.allow_nonreactant_orders = node.getBool("nonreactant-orders", false);
}
void setupElementaryReaction(ElementaryReaction& R, const XML_Node& rxn_node)
{
const XML_Node& rc_node = rxn_node.child("rateCoeff");
@ -372,6 +459,14 @@ void setupElementaryReaction(ElementaryReaction& R, const XML_Node& rxn_node)
setupReaction(R, rxn_node);
}
void setupElementaryReaction(ElementaryReaction& R, const AnyMap& node,
const Kinetics& kin, const UnitSystem& units)
{
setupReaction(R, node);
R.allow_negative_pre_exponential_factor = node.getBool("negative-A", false);
R.rate = readArrhenius(R, node.at("rate-constant"), kin, units);
}
void setupThreeBodyReaction(ThreeBodyReaction& R, const XML_Node& rxn_node)
{
readEfficiencies(R.third_body, rxn_node.child("rateCoeff"));
@ -654,6 +749,23 @@ shared_ptr<Reaction> newReaction(const XML_Node& rxn_node)
}
}
unique_ptr<Reaction> newReaction(const AnyMap& node, const Kinetics& kin,
const UnitSystem& units)
{
std::string type = "elementary";
if (node.hasKey("type")) {
type = node.at("type").asString();
}
if (type == "elementary") {
unique_ptr<ElementaryReaction> R(new ElementaryReaction());
setupElementaryReaction(*R, node, kin, units);
return unique_ptr<Reaction>(move(R));
} else {
throw CanteraError("newReaction", "Unknown reaction type '{}'", type);
}
}
std::vector<shared_ptr<Reaction> > getReactions(const XML_Node& node)
{
std::vector<shared_ptr<Reaction> > all_reactions;

View file

@ -0,0 +1,28 @@
#include "gtest/gtest.h"
#include "cantera/base/Units.h"
#include "cantera/IdealGasMix.h"
using namespace Cantera;
TEST(Reaction, ElementaryFromYaml)
{
// @TODO: Use of XML input files in these tests of the YAML format needs to
// be eliminated before we can deprecate the XML format.
IdealGasMix gas("gri30.xml");
AnyMap rxn = AnyMap::fromYamlString(
"{equation: N + NO <=> N2 + O,"
" rate-constant: [-2.70000E+13 cm^3/mol/s, 0, 355 cal/mol],"
" negative-A: true}");
UnitSystem U;
auto R = newReaction(rxn, gas, U);
EXPECT_EQ(R->reactants.at("NO"), 1);
EXPECT_EQ(R->products.at("N2"), 1);
EXPECT_EQ(R->reaction_type, ELEMENTARY_RXN);
auto ER = dynamic_cast<ElementaryReaction&>(*R);
EXPECT_DOUBLE_EQ(ER.rate.preExponentialFactor(), -2.7e10);
EXPECT_DOUBLE_EQ(ER.rate.activationEnergy_R(), 355 / GasConst_cal_mol_K);
EXPECT_TRUE(ER.allow_negative_pre_exponential_factor);
EXPECT_FALSE(ER.allow_negative_orders);
}