1 /**
2 * @file KineticsFactory.cpp
3 */
4
5 // This file is part of Cantera. See License.txt in the top-level directory or
6 // at https://cantera.org/license.txt for license and copyright information.
7
8 #include "cantera/kinetics/KineticsFactory.h"
9 #include "cantera/kinetics/GasKinetics.h"
10 #include "cantera/kinetics/InterfaceKinetics.h"
11 #include "cantera/kinetics/EdgeKinetics.h"
12 #include "cantera/kinetics/importKinetics.h"
13 #include "cantera/base/xml.h"
14 #include "cantera/base/stringUtils.h"
15
16 using namespace std;
17
18 namespace Cantera
19 {
20
21 KineticsFactory* KineticsFactory::s_factory = 0;
22 std::mutex KineticsFactory::kinetics_mutex;
23
newKinetics(XML_Node & phaseData,vector<ThermoPhase * > th)24 Kinetics* KineticsFactory::newKinetics(XML_Node& phaseData,
25 vector<ThermoPhase*> th)
26 {
27 // Look for a child of the XML element phase called "kinetics". It has an
28 // attribute name "model". Store the value of that attribute in the variable
29 // kintype
30 string kintype = phaseData.child("kinetics")["model"];
31
32 // Create a kinetics object of the desired type
33 Kinetics* k = newKinetics(kintype);
34 // Now that we have the kinetics manager, we can import the reaction
35 // mechanism into it.
36 importKinetics(phaseData, th, k);
37
38 // Return the pointer to the kinetics manager
39 return k;
40 }
41
KineticsFactory()42 KineticsFactory::KineticsFactory() {
43 reg("none", []() { return new Kinetics(); });
44 addAlias("none", "Kinetics");
45 reg("gas", []() { return new GasKinetics(); });
46 addAlias("gas", "gaskinetics");
47 addAlias("gas", "Gas");
48 reg("surface", []() { return new InterfaceKinetics(); });
49 addAlias("surface", "interface");
50 addAlias("surface", "Surf");
51 reg("edge", []() { return new EdgeKinetics(); });
52 addAlias("edge", "Edge");
53 }
54
newKinetics(const string & model)55 Kinetics* KineticsFactory::newKinetics(const string& model)
56 {
57 return create(toLowerCopy(model));
58 }
59
newKinetics(const vector<ThermoPhase * > & phases,const AnyMap & phaseNode,const AnyMap & rootNode)60 unique_ptr<Kinetics> newKinetics(const vector<ThermoPhase*>& phases,
61 const AnyMap& phaseNode,
62 const AnyMap& rootNode)
63 {
64 unique_ptr<Kinetics> kin(KineticsFactory::factory()->newKinetics(
65 phaseNode.getString("kinetics", "none")));
66 for (auto& phase : phases) {
67 kin->addPhase(*phase);
68 }
69 kin->init();
70 addReactions(*kin, phaseNode, rootNode);
71 return kin;
72 }
73
newKinetics(const std::vector<ThermoPhase * > & phases,const std::string & filename,const std::string & phase_name)74 unique_ptr<Kinetics> newKinetics(const std::vector<ThermoPhase*>& phases,
75 const std::string& filename,
76 const std::string& phase_name)
77 {
78 size_t dot = filename.find_last_of(".");
79 string extension;
80 if (dot != npos) {
81 extension = toLowerCopy(filename.substr(dot+1));
82 }
83
84 if (extension == "yml" || extension == "yaml") {
85 AnyMap root = AnyMap::fromYamlFile(filename);
86 AnyMap& phaseNode = root["phases"].getMapWhere("name", phase_name);
87 return newKinetics(phases, phaseNode, root);
88 } else {
89 XML_Node* root = get_XML_File(filename);
90 XML_Node* xphase = get_XML_NameID("phase", "#"+phase_name, root);
91 if (!xphase) {
92 throw CanteraError("newKinetics",
93 "Couldn't find phase named '{}' in file '{}'.",
94 phase_name, filename);
95 }
96 return unique_ptr<Kinetics>(newKineticsMgr(*xphase, phases));
97 }
98 }
99
addReactions(Kinetics & kin,const AnyMap & phaseNode,const AnyMap & rootNode)100 void addReactions(Kinetics& kin, const AnyMap& phaseNode, const AnyMap& rootNode)
101 {
102 kin.skipUndeclaredThirdBodies(
103 phaseNode.getBool("skip-undeclared-third-bodies", false));
104
105 // Find sections containing reactions to add
106 vector<string> sections, rules;
107
108 if (phaseNode.hasKey("reactions")) {
109 if (kin.kineticsType() == "Kinetics") {
110 throw InputFileError("addReactions", phaseNode["reactions"],
111 "Phase entry includes a 'reactions' field but does not "
112 "specify a kinetics model.");
113 }
114 const auto& reactionsNode = phaseNode.at("reactions");
115 if (reactionsNode.is<string>()) {
116 if (rootNode.hasKey("reactions")) {
117 // Specification of the rule for adding species from the default
118 // 'reactions' section, if it exists
119 sections.push_back("reactions");
120 rules.push_back(reactionsNode.asString());
121 } else if (reactionsNode.asString() != "none") {
122 throw InputFileError("addReactions", reactionsNode,
123 "Phase entry implies existence of 'reactions' section "
124 "which does not exist in the current input file.");
125 }
126 } else if (reactionsNode.is<vector<string>>()) {
127 // List of sections from which all species should be added
128 for (const auto& item : reactionsNode.as<vector<string>>()) {
129 sections.push_back(item);
130 rules.push_back("all");
131 }
132 } else if (reactionsNode.is<vector<AnyMap>>()) {
133 // Mapping of rules to apply for each specified section containing
134 // reactions
135 for (const auto& item : reactionsNode.as<vector<AnyMap>>()) {
136 sections.push_back(item.begin()->first);
137 rules.push_back(item.begin()->second.asString());
138 }
139 }
140 } else if (kin.kineticsType() != "Kinetics") {
141 if (rootNode.hasKey("reactions")) {
142 // Default behavior is to add all reactions from the 'reactions'
143 // section, if a 'kinetics' model has been specified
144 sections.push_back("reactions");
145 rules.push_back("all");
146 } else {
147 throw InputFileError("addReactions", phaseNode,
148 "Phase entry implies existence of 'reactions' section which "
149 "does not exist in the current input file. Add the field "
150 "'reactions: none' to the phase entry to specify a kinetics "
151 "model with no reactions.");
152 }
153 }
154
155 // Add reactions from each section
156 fmt::memory_buffer add_rxn_err;
157 for (size_t i = 0; i < sections.size(); i++) {
158 if (rules[i] == "all") {
159 kin.skipUndeclaredSpecies(false);
160 } else if (rules[i] == "declared-species") {
161 kin.skipUndeclaredSpecies(true);
162 } else if (rules[i] == "none") {
163 continue;
164 } else {
165 throw InputFileError("addReactions", phaseNode.at("reactions"),
166 "Unknown rule '{}' for adding species from the '{}' section.",
167 rules[i], sections[i]);
168 }
169 const auto& slash = boost::ifind_last(sections[i], "/");
170 if (slash) {
171 // specified section is in a different file
172 string fileName (sections[i].begin(), slash.begin());
173 string node(slash.end(), sections[i].end());
174 AnyMap reactions = AnyMap::fromYamlFile(fileName,
175 rootNode.getString("__file__", ""));
176 for (const auto& R : reactions[node].asVector<AnyMap>()) {
177 try {
178 kin.addReaction(newReaction(R, kin), false);
179 } catch (CanteraError& err) {
180 format_to(add_rxn_err, "{}", err.what());
181 }
182 }
183 } else {
184 // specified section is in the current file
185 for (const auto& R : rootNode.at(sections[i]).asVector<AnyMap>()) {
186 try {
187 kin.addReaction(newReaction(R, kin), false);
188 } catch (CanteraError& err) {
189 format_to(add_rxn_err, "{}", err.what());
190 }
191 }
192 }
193 }
194
195 kin.checkDuplicates();
196 if (add_rxn_err.size()) {
197 throw CanteraError("addReactions", to_string(add_rxn_err));
198 }
199
200 kin.resizeReactions();
201 }
202
203 }
204