https://mooseframework.inl.gov
Loading...
Searching...
No Matches
ReactionNetworkUtils.C
Go to the documentation of this file.
1//* This file is part of the MOOSE framework
2//* https://mooseframework.inl.gov
3//*
4//* All rights reserved, see COPYRIGHT for full restrictions
5//* https://github.com/idaholab/moose/blob/master/COPYRIGHT
6//*
7//* Licensed under LGPL 2.1, please see LICENSE for details
8//* https://www.gnu.org/licenses/lgpl-2.1.html
9
11#include "MooseTypes.h"
12#include "MooseUtils.h"
13#include "libmesh/utility.h"
14#include "peglib.h"
15
16// not needed
17#include "Conversion.h"
18
20{
21
22std::vector<Reaction>
23parseReactionNetwork(const std::string & reaction_network_string, bool output_to_cout)
24{
25 using namespace peg;
26 parser parser(R"(
27 Reactions <- (_ Reaction _ Newline?)* _
28 Reaction <- TermList _ '->' _ TermList _ Metadata?
29 TermList <- Term (_ Term)*
30 Term <- Coefficient? Species
31 Coefficient <- Float
32 Float <- [+-]? (_)? ([0-9]+)? ('.' [0-9]+)?
33 Species <- BaseSpecies State? Charge?
34 BaseSpecies <- [a-zA-Z][a-zA-Z0-9_]*
35 State <- '(' [a-z]{1,3} ')'
36 Charge <- ('^'? [0-9]* [+-])
37 Metadata <- '[' _ Pair (_ ',' _ Pair)* _ ']'
38 Pair <- Key _ '=' _ Value
39 Key <- [A-Za-z_][A-Za-z0-9_]*
40 Value <- [A-Za-z0-9_.-]+
41 Newline <- [\r\n]+
42 ~_ <- [ \t]*
43 )");
44 mooseAssert(parser, "Failed to build parser");
45
46 // Rules for how to parse each type
47 parser["Reactions"] = [](const SemanticValues & vs)
48 {
49 std::vector<Reaction> reactions;
50 for (const auto & v : vs)
51 {
52 if (v.type() == typeid(Reaction))
53 {
54 reactions.push_back(std::any_cast<Reaction>(v));
55 }
56 }
57 return reactions;
58 };
59
60 parser["Float"] = [](const SemanticValues & vs)
61 {
62 // We need to remove whitespace in case the reaction was A - B -> C
63 std::string vs_str = std::string(std::any_cast<std::string_view>(vs.token()));
64 vs_str.erase(std::remove(vs_str.begin(), vs_str.end(), ' '), vs_str.end());
65 if (vs_str == "-")
66 return -1.;
67 else if (vs_str == "+" || vs_str == "")
68 return 1.;
69 else
70 return std::stod(std::string(vs_str));
71 };
72
73 parser["Coefficient"] = [](const SemanticValues & vs) { return std::any_cast<double>(vs[0]); };
74
75 parser["BaseSpecies"] = [](const SemanticValues & vs) { return vs.token(); };
76
77 parser["State"] = [](const SemanticValues & vs) { return vs.token(); };
78
79 parser["Charge"] = [](const SemanticValues & vs) { return vs.token(); };
80
81 parser["Species"] = [](const SemanticValues & vs)
82 {
83 std::string base = std::string(std::any_cast<std::string_view>(vs[0]));
84 std::optional<std::string> state;
85 std::optional<std::string> charge;
86
87 // Parse the state and charge
88 if (vs.size() >= 2)
89 {
90 for (size_t i = 1; i < vs.size(); ++i)
91 if (vs[i].type() == typeid(std::string_view))
92 {
93 std::string val = std::string(std::any_cast<std::string_view>(vs[i]));
94 if (val.front() == '(' && val.back() == ')')
95 state = val;
96 else
97 charge = val;
98 }
99 }
100
101 return Term{1.0, base, state, charge}; // default coefficient = 1.0
102 };
103
104 parser["Term"] = [](const SemanticValues & vs)
105 {
106 mooseAssert(vs.size() <= 2, "Should only have a coefficient and a species");
107 if (vs.size() == 2)
108 {
109 double coeff = std::any_cast<double>(vs[0]);
110 Term term = std::any_cast<Term>(vs[1]);
111 term.coefficient = coeff;
112 return term;
113 }
114 else
115 return std::any_cast<Term>(vs[0]);
116 };
117
118 parser["TermList"] = [](const SemanticValues & vs)
119 {
120 TermList terms;
121 for (const auto & v : vs)
122 {
123 // One term
124 if (v.type() == typeid(Term))
125 terms.push_back(std::any_cast<Term>(v));
126 // Multiple terms
127 else if (v.type() == typeid(std::vector<Term>))
128 {
129 const auto & more = std::any_cast<std::vector<Term>>(v);
130 terms.insert(terms.end(), more.begin(), more.end());
131 }
132 }
133 return terms;
134 };
135
136 // Leaf types can just use 'vs.token()'
137 parser["Key"] = [](const SemanticValues & vs) { return vs.token(); };
138
139 parser["Value"] = [](const SemanticValues & vs) { return vs.token(); };
140
141 parser["Pair"] = [](const SemanticValues & vs)
142 {
143 mooseAssert(vs.size() == 2, "Metadata entry should have exactly one key and one value");
144 return std::make_pair(std::string(std::any_cast<std::string_view>(vs[0])),
145 std::string(std::any_cast<std::string_view>(vs[1])));
146 };
147
148 parser["Metadata"] = [](const SemanticValues & vs)
149 {
150 Metadata meta;
151 for (const auto & v : vs)
152 {
153 if (v.type() == typeid(std::pair<std::string, std::string>))
154 {
155 const auto & [k, val] = std::any_cast<std::pair<std::string, std::string>>(v);
156 meta[k] = val;
157 }
158 else if (v.type() == typeid(std::vector<std::pair<std::string, std::string>>))
159 {
160 const auto & pairs = std::any_cast<std::vector<std::pair<std::string, std::string>>>(v);
161 for (const auto & [k, val] : pairs)
162 meta[k] = val;
163 }
164 }
165 return meta;
166 };
167
168 parser["Reaction"] = [](const SemanticValues & vs)
169 {
170 Reaction r;
171 if (vs.size() != 2 && vs.size() != 3)
172 mooseError("Reaction failed to read as 'reactors -> products [metadata]");
173 r.reactants = std::any_cast<TermList>(vs[0]);
174 r.products = std::any_cast<TermList>(vs[1]);
175 if (vs.size() == 3)
176 r.metadata = std::any_cast<Metadata>(vs[2]);
177 return r;
178 };
179
180 parser.enable_packrat_parsing();
181
182 std::vector<Reaction> reactions;
183 if (!parser.parse(reaction_network_string, reactions))
184 mooseError("Failed to parse reaction.");
185
186 // Output the reaction network
187 if (output_to_cout)
188 {
189 for (size_t i = 0; i < reactions.size(); ++i)
190 {
191 const auto & r = reactions[i];
192 Moose::out << "Reaction " << i + 1 << ":\n";
193 Moose::out << Moose::stringify(r) << std::endl;
194 }
195 }
196
197 return reactions;
198}
199
200std::vector<VariableName>
201Reaction::getSpecies() const
202{
203 std::vector<VariableName> species;
204 for (const auto & term : reactants)
205 species.push_back(term.species + term.state.value_or("") + term.charge.value_or(""));
206 for (const auto & term : products)
207 species.push_back(term.species + term.state.value_or("") + term.charge.value_or(""));
208 return species;
209}
210
211std::vector<VariableName>
212Reaction::getUniqueSpecies() const
213{
214 std::vector<VariableName> species;
215 std::set<Term> terms_gathered;
216 for (const auto & term : reactants)
217 if (terms_gathered.find(term) == terms_gathered.end())
218 {
219 species.push_back(term.species + term.state.value_or("") + term.charge.value_or(""));
220 terms_gathered.insert(term);
221 }
222 for (const auto & term : products)
223 if (terms_gathered.find(term) == terms_gathered.end())
224 {
225 species.push_back(term.species + term.state.value_or("") + term.charge.value_or(""));
226 terms_gathered.insert(term);
227 }
228 return species;
229}
230
231std::vector<Real>
232Reaction::getStoichiometricCoefficients() const
233{
234 std::vector<Real> coeffs;
235 for (const auto & term : reactants)
236 coeffs.push_back(term.coefficient);
237 for (const auto & term : products)
238 coeffs.push_back(term.coefficient);
239 return coeffs;
240}
241
242std::map<VariableName, Real>
243Reaction::getUniqueStoichiometricCoefficients() const
244{
245 std::map<VariableName, Real> terms_gathered;
246 for (const auto & term : reactants)
247 {
248 const auto term_str = term.species + term.state.value_or("") + term.charge.value_or("");
249 terms_gathered[term_str] -= term.coefficient;
250 }
251 for (const auto & term : products)
252 {
253 const auto term_str = term.species + term.state.value_or("") + term.charge.value_or("");
254 terms_gathered[term_str] += term.coefficient;
255 }
256 return terms_gathered;
257}
258
259std::vector<VariableName>
260Reaction::getReactantSpecies() const
261{
262 std::vector<VariableName> species;
263 for (const auto & term : reactants)
264 species.push_back(term.species + term.state.value_or("") + term.charge.value_or(""));
265 return species;
266}
267
268std::vector<VariableName>
269Reaction::getUniqueReactantSpecies() const
270{
271 std::vector<VariableName> species;
272 std::set<Term> terms_gathered;
273 for (const auto & term : reactants)
274 if (terms_gathered.find(term) == terms_gathered.end())
275 {
276 species.push_back(term.species + term.state.value_or("") + term.charge.value_or(""));
277 terms_gathered.insert(term);
278 }
279 return species;
280}
281
282std::vector<std::string>
283Reaction::getProductSpecies() const
284{
285 std::vector<std::string> species;
286 for (const auto & term : products)
287 species.push_back(term.species + term.state.value_or("") + term.charge.value_or(""));
288 return species;
289}
290
291std::vector<std::string>
292Reaction::getUniqueProductSpecies() const
293{
294 std::vector<std::string> species;
295 std::set<Term> terms_gathered;
296 for (const auto & term : products)
297 if (terms_gathered.find(term) == terms_gathered.end())
298 {
299 species.push_back(term.species + term.state.value_or("") + term.charge.value_or(""));
300 terms_gathered.insert(term);
301 }
302 return species;
303}
304
305template <>
306bool
307Reaction::hasMetaData<Real>(const std::string & key) const
308{
309 return metadata.count(key) && MooseUtils::isFloat(libmesh_map_find(metadata, key));
310}
311
312template <typename T>
313bool
314Reaction::hasMetaData(const std::string & /*key*/) const
315{
316 mooseError("Has metadata not implemented for type " + MooseUtils::prettyCppType<T>());
317 return false;
318}
319
320bool
321Reaction::hasMetaData(const std::string & key) const
322{
323 return metadata.count(key);
324}
325
326const std::string &
327Reaction::getMetaData(const std::string & key) const
328{
329 // Get a better error
330 if (hasMetaData(key))
331 return libmesh_map_find(metadata, key);
332 else
333 mooseError("MetaData item '" + key + "' was not found in reaction:\n" +
334 Moose::stringify(*this));
335}
336
337// namespace ReactionNetworkUtils
338}
339
340namespace Moose
341{
342std::string
344{
345 std::string str_out = " Reactants:\n";
346 for (const auto & t : r.reactants)
347 {
348 str_out += " " + std::to_string(t.coefficient) + " " + t.species;
349 if (t.charge)
350 str_out += t.charge.value();
351 if (t.state)
352 str_out += " (" + t.state.value() + ")";
353 str_out += "\n";
354 }
355 str_out += " Products:\n";
356 for (const auto & t : r.products)
357 {
358 str_out += " " + std::to_string(t.coefficient) + " " + t.species;
359 if (t.charge)
360 str_out += t.charge.value();
361 if (t.state)
362 str_out += " (" + t.state.value() + ")";
363 str_out += "\n";
364 }
365 if (!r.metadata.empty())
366 {
367 str_out += " Metadata:\n";
368 for (const auto & [k, v] : r.metadata)
369 str_out += " " + k + " = " + v + "\n";
370 }
371 return str_out;
372}
373}
const double v
void mooseError(Args &&... args)
std::string stringify(const T &t)
std::vector< Term > TermList
std::vector< Reaction > parseReactionNetwork(const std::string &reaction_network_string, bool output_to_cout)
Parses the reaction network from a string form to a vector a Reaction.
std::map< std::string, std::string > Metadata
bool hasMetaData(const std::string &key) const
Whether the reaction has the metadata with the given type.