Create a complete Kinetics object from a YAML input file
This commit is contained in:
parent
f4d45f8a85
commit
f0dc990764
6 changed files with 193 additions and 1 deletions
|
|
@ -91,6 +91,35 @@ inline Kinetics* newKineticsMgr(const std::string& model)
|
|||
return KineticsFactory::factory()->newKinetics(model);
|
||||
}
|
||||
|
||||
/*!
|
||||
* Create a new kinetics manager, initialize it, and add reactions
|
||||
*
|
||||
* @param phases Vector of phases containing species which participate in
|
||||
* reactions, with the phase where the reactions occur (lowest-dimensional
|
||||
* phase) listed first.
|
||||
* @param phaseNode Phase entry for the phase where the reactions occur. This
|
||||
* phase definition is used to determine the source of the reactions added
|
||||
* to the Kinetics object.
|
||||
* @param rootNode The root node of the file containing the phase definition,
|
||||
* which will be treated as the default source for reactions
|
||||
*/
|
||||
unique_ptr<Kinetics> newKinetics(std::vector<ThermoPhase*>& phases,
|
||||
const AnyMap& phaseNode,
|
||||
const AnyMap& rootNode=AnyMap());
|
||||
|
||||
/*!
|
||||
* Add reactions to a Kinetics object
|
||||
*
|
||||
* @param kin The Kinetics object to be initialized
|
||||
* @param phaseNode Phase entry for the phase where the reactions occur. This
|
||||
* phase definition is used to determine the source of the reactions added
|
||||
* to the Kinetics object.
|
||||
* @param rootNode The root node of the file containing the phase definition,
|
||||
* which will be treated as the default source for reactions
|
||||
*/
|
||||
void addReactions(Kinetics& kin, const AnyMap& phaseNode,
|
||||
const AnyMap& rootNode=AnyMap());
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -41,6 +41,7 @@ Kinetics* KineticsFactory::newKinetics(XML_Node& phaseData,
|
|||
KineticsFactory::KineticsFactory() {
|
||||
reg("none", []() { return new Kinetics(); });
|
||||
reg("gaskinetics", []() { return new GasKinetics(); });
|
||||
m_synonyms["gas"] = "gaskinetics";
|
||||
reg("interface", []() { return new InterfaceKinetics(); });
|
||||
reg("edge", []() { return new EdgeKinetics(); });
|
||||
}
|
||||
|
|
@ -50,4 +51,82 @@ Kinetics* KineticsFactory::newKinetics(const string& model)
|
|||
return create(toLowerCopy(model));
|
||||
}
|
||||
|
||||
unique_ptr<Kinetics> newKinetics(vector<ThermoPhase*>& phases,
|
||||
const AnyMap& phaseNode,
|
||||
const AnyMap& rootNode)
|
||||
{
|
||||
unique_ptr<Kinetics> kin(KineticsFactory::factory()->newKinetics(
|
||||
phaseNode.getString("kinetics", "none")));
|
||||
for (auto& phase : phases) {
|
||||
kin->addPhase(*phase);
|
||||
}
|
||||
kin->init();
|
||||
if (kin->kineticsType() != "Kinetics") {
|
||||
addReactions(*kin, phaseNode, rootNode);
|
||||
}
|
||||
return kin;
|
||||
}
|
||||
|
||||
void addReactions(Kinetics& kin, const AnyMap& phaseNode, const AnyMap& rootNode)
|
||||
{
|
||||
// Find sections containing reactions to add
|
||||
vector<string> sections, rules;
|
||||
|
||||
if (phaseNode.hasKey("reactions")) {
|
||||
const auto& reactionsNode = phaseNode.at("reactions");
|
||||
if (reactionsNode.is<string>()) {
|
||||
// Specification of the rule for adding species from the default
|
||||
// 'reactions' section
|
||||
sections.push_back("reactions");
|
||||
rules.push_back(reactionsNode.asString());
|
||||
} else if (reactionsNode.is<vector<string>>()) {
|
||||
// List of sections from which all species should be added
|
||||
for (const auto& item : reactionsNode.as<vector<string>>()) {
|
||||
sections.push_back(item);
|
||||
rules.push_back("all");
|
||||
}
|
||||
} else if (reactionsNode.is<vector<AnyMap>>()) {
|
||||
// Mapping of rules to apply for each specified section containing
|
||||
// reactions
|
||||
for (const auto& item : reactionsNode.as<vector<AnyMap>>()) {
|
||||
sections.push_back(item.begin()->first);
|
||||
rules.push_back(item.begin()->second.asString());
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Default behavior is to add all reactions from the 'reactions' section
|
||||
sections.push_back("reactions");
|
||||
rules.push_back("all");
|
||||
}
|
||||
|
||||
// Add reactions from each section
|
||||
for (size_t i = 0; i < sections.size(); i++) {
|
||||
if (rules[i] == "all") {
|
||||
kin.skipUndeclaredSpecies(false);
|
||||
kin.skipUndeclaredThirdBodies(false);
|
||||
} else if (rules[i] == "declared-species") {
|
||||
kin.skipUndeclaredSpecies(true);
|
||||
kin.skipUndeclaredThirdBodies(true);
|
||||
} else if (rules[i] != "none") {
|
||||
throw CanteraError("setupKinetics", "Unknown rule '{}' for adding "
|
||||
"species from the '{}' section.", rules[i], sections[i]);
|
||||
}
|
||||
const auto& slash = boost::ifind_first(sections[i], "/");
|
||||
if (slash) {
|
||||
// specified section is in a different file
|
||||
string fileName (sections[i].begin(), slash.begin());
|
||||
string node(slash.end(), sections[i].end());
|
||||
AnyMap reactions = AnyMap::fromYamlFile(fileName);
|
||||
for (const auto& R : reactions[node].asVector<AnyMap>()) {
|
||||
kin.addReaction(newReaction(R, kin));
|
||||
}
|
||||
} else {
|
||||
// specified section is in the current file
|
||||
for (const auto& R : rootNode.at(sections[i]).asVector<AnyMap>()) {
|
||||
kin.addReaction(newReaction(R, kin));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -40,6 +40,20 @@ phases:
|
|||
thermo: ideal-gas
|
||||
state: {T: 300.0, P: 1 atm, X: {AR: 0.2, O2: 0.7, N2: 0.1}}
|
||||
|
||||
- name: simple-kinetics
|
||||
thermo: ideal-gas
|
||||
kinetics: gas
|
||||
state: {T: 300.0, P: 1 atm, X: {AR: 0.2, O2: 0.7, N2: 0.1}}
|
||||
|
||||
- name: remote-kinetics
|
||||
thermo: ideal-gas
|
||||
kinetics: gas
|
||||
species:
|
||||
- species: all
|
||||
- species-elements.yaml/species: all
|
||||
reactions: [reactions, species-elements.yaml/nox-reactions]
|
||||
state: {T: 300.0, P: 1 atm, X: {AR: 0.2, O2: 0.7, N2: 0.1}}
|
||||
|
||||
|
||||
elements:
|
||||
- symbol: Ar
|
||||
|
|
@ -94,3 +108,36 @@ species:
|
|||
- [2.500000000E+00, 0.000000000E+00, 0.000000000E+00, 0.000000000E+00,
|
||||
0.000000000E+00, -7.453750000E+02, 4.366000000E+00]
|
||||
note: "120186"
|
||||
|
||||
- name: O
|
||||
composition: {O: 1}
|
||||
thermo:
|
||||
model: NASA7
|
||||
temperature-ranges: [200, 1000, 3500]
|
||||
data:
|
||||
- [3.168267100E+00, -3.279318840E-03, 6.643063960E-06, -6.128066240E-09,
|
||||
2.112659710E-12, 2.912225920E+04, 2.051933460E+00]
|
||||
- [2.569420780E+00, -8.597411370E-05, 4.194845890E-08, -1.001777990E-11,
|
||||
1.228336910E-15, 2.921757910E+04, 4.784338640E+00]
|
||||
note: "L 1/90"
|
||||
|
||||
- name: N
|
||||
composition: {N: 1}
|
||||
thermo:
|
||||
model: NASA7
|
||||
temperature-ranges: [200.00, 1000.00, 6000.00]
|
||||
data:
|
||||
- [2.500000000E+00, 0.000000000E+00, 0.000000000E+00, 0.000000000E+00,
|
||||
0.000000000E+00, 5.610463700E+04, 4.193908700E+00]
|
||||
- [2.415942900E+00, 1.748906500E-04, -1.190236900E-07, 3.022624500E-11,
|
||||
-2.036098200E-15, 5.613377300E+04, 4.649609600E+00]
|
||||
note: "L 6/88"
|
||||
|
||||
|
||||
reactions:
|
||||
- equation: N + NO <=> N2 + O
|
||||
rate-constant: [2.70000E+13, 0, 355]
|
||||
id: NOx-R1
|
||||
|
||||
- equation: N + O2 <=> NO + O
|
||||
rate-constant: [9.00000E+09, 1, 6500]
|
||||
|
|
|
|||
|
|
@ -30,3 +30,10 @@ species:
|
|||
- [4.823072900E+00, 2.627025100E-03, -9.585087400E-07, 1.600071200E-10,
|
||||
-9.775230300E-15, 8.073404800E+03, -2.201720700E+00]
|
||||
note: "L 7/88"
|
||||
|
||||
nox-reactions:
|
||||
- equation: N2O (+ M) <=> N2 + O (+ M)
|
||||
type: falloff
|
||||
high-P-rate-constant: [7.91000E+10, 0, 56020]
|
||||
low-P-rate-constant: [6.37000E+14, 0, 56640]
|
||||
efficiencies: {AR: 0.625}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,8 @@
|
|||
#include "gtest/gtest.h"
|
||||
#include "cantera/base/Units.h"
|
||||
#include "cantera/IdealGasMix.h"
|
||||
#include "cantera/kinetics/KineticsFactory.h"
|
||||
#include "cantera/thermo/ThermoFactory.h"
|
||||
|
||||
using namespace Cantera;
|
||||
|
||||
|
|
@ -163,3 +165,31 @@ TEST(Reaction, ChebyshevFromYaml)
|
|||
EXPECT_DOUBLE_EQ(CR.rate.Pmin(), 1000);
|
||||
EXPECT_NEAR(CR.rate.updateRC(std::log(T), 1.0/T), 130512.2773948636, 1e-9);
|
||||
}
|
||||
|
||||
TEST(Kinetics, GasKineticsFromYaml1)
|
||||
{
|
||||
AnyMap infile = AnyMap::fromYamlFile("ideal-gas.yaml");
|
||||
auto phaseNodes = infile["phases"].asMap("name");
|
||||
auto phaseNode = phaseNodes.at("simple-kinetics");
|
||||
shared_ptr<ThermoPhase> thermo = newPhase(*phaseNode, infile);
|
||||
std::vector<ThermoPhase*> phases{thermo.get()};
|
||||
auto kin = newKinetics(phases, *phaseNode, infile);
|
||||
EXPECT_EQ(kin->nReactions(), (size_t) 2);
|
||||
const auto& R = kin->reaction(0);
|
||||
EXPECT_EQ(R->reactants.at("NO"), 1);
|
||||
EXPECT_EQ(R->products.at("N2"), 1);
|
||||
EXPECT_EQ(R->id, "NOx-R1");
|
||||
const auto& ER = std::dynamic_pointer_cast<ElementaryReaction>(R);
|
||||
EXPECT_DOUBLE_EQ(ER->rate.preExponentialFactor(), 2.7e10);
|
||||
}
|
||||
|
||||
TEST(Kinetics, GasKineticsFromYaml2)
|
||||
{
|
||||
AnyMap infile = AnyMap::fromYamlFile("ideal-gas.yaml");
|
||||
auto phaseNodes = infile["phases"].asMap("name");
|
||||
auto phaseNode = phaseNodes.at("remote-kinetics");
|
||||
shared_ptr<ThermoPhase> thermo = newPhase(*phaseNode, infile);
|
||||
std::vector<ThermoPhase*> phases{thermo.get()};
|
||||
auto kin = newKinetics(phases, *phaseNode, infile);
|
||||
EXPECT_EQ(kin->nReactions(), (size_t) 3);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -63,7 +63,7 @@ TEST(ThermoFromYaml, speciesAll)
|
|||
auto phaseNodes = infile["phases"].asMap("name");
|
||||
auto thermo = newPhase(*phaseNodes.at("species-all"), infile);
|
||||
EXPECT_EQ(thermo->nElements(), (size_t) 3);
|
||||
EXPECT_EQ(thermo->nSpecies(), (size_t) 4);
|
||||
EXPECT_EQ(thermo->nSpecies(), (size_t) 6);
|
||||
EXPECT_EQ(thermo->species(1)->name, "NO");
|
||||
EXPECT_EQ(thermo->species(2)->name, "N2");
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue