Cantera  3.2.0a4
Loading...
Searching...
No Matches
Solution.cpp
Go to the documentation of this file.
1/**
2 * @file Solution.cpp
3 * Definition file for class Solution.
4 */
5
6// This file is part of Cantera. See License.txt in the top-level directory or
7// at https://cantera.org/license.txt for license and copyright information.
8
19
20#include <boost/algorithm/string.hpp>
21
22namespace Cantera
23{
24
25shared_ptr<Solution> Solution::clone(const vector<shared_ptr<Solution>>& adjacent,
26 bool withKinetics, bool withTransport) const
27{
28 shared_ptr<Solution> out = create();
29 out->setThermo(m_thermo->clone());
30 if (withKinetics) {
31 vector<shared_ptr<ThermoPhase>> kinPhases;
32 map<string, shared_ptr<Solution>> adjacentByName;
33 for (const auto& soln : adjacent) {
34 adjacentByName[soln->name()] = soln;
35 if (m_kinetics->phaseIndex(soln->name()) == -1) {
36 throw CanteraError("Solution::clone", "Provided adjacent phase '{}'"
37 " not found in Kinetics object.", soln->name());
38 }
39 }
40
41 kinPhases.push_back(out->thermo());
42 for (size_t i = 1; i < m_kinetics->nPhases(); i++) {
43 string name = m_kinetics->phase(i)->name();
44 if (adjacentByName.count(name) != 0) {
45 kinPhases.push_back(adjacentByName[name]->thermo());
46 out->addAdjacent(adjacentByName[name]);
47 adjacentByName.erase(name);
48 } else {
49 auto soln = m_kinetics->phase(i)->root()->clone({}, false, false);
50 kinPhases.push_back(soln->thermo());
51 out->addAdjacent(soln);
52 }
53 }
54 out->setKinetics(m_kinetics->clone(kinPhases));
55 } else {
56 out->setKinetics(newKinetics("none"));
57 }
58 if (withTransport) {
59 out->setTransport(m_transport->clone(out->thermo()));
60 } else {
61 out->setTransport(newTransport(m_thermo, "none"));
62 }
63 return out;
64}
65
66string Solution::name() const {
67 if (m_thermo) {
68 return m_thermo->name();
69 } else {
70 throw CanteraError("Solution::name",
71 "Requires associated 'ThermoPhase'");
72 }
73}
74
75void Solution::setName(const string& name) {
76 if (m_thermo) {
77 m_thermo->setName(name);
78 } else {
79 throw CanteraError("Solution::setName",
80 "Requires associated 'ThermoPhase'");
81 }
82}
83
84void Solution::setThermo(shared_ptr<ThermoPhase> thermo) {
86 m_thermo->setSolution(weak_from_this());
87 for (const auto& [id, callback] : m_changeCallbacks) {
88 callback();
89 }
90}
91
92void Solution::setKinetics(shared_ptr<Kinetics> kinetics) {
93 if (kinetics == m_kinetics) {
94 return;
95 }
97 if (m_kinetics) {
98 m_kinetics->setRoot(shared_from_this());
99 }
100 for (const auto& [id, callback] : m_changeCallbacks) {
101 callback();
102 }
103}
104
106{
107 if (!m_transport) {
108 throw CanteraError("Solution::transportModel",
109 "The Transport object is not initialized.");
110 }
111 return m_transport->transportModel();
112}
113
114void Solution::setTransport(shared_ptr<Transport> transport) {
115 if (transport == m_transport) {
116 return;
117 }
119 for (const auto& [id, callback] : m_changeCallbacks) {
120 callback();
121 }
122}
123
124void Solution::setTransportModel(const string& model) {
125 if (!m_thermo) {
126 throw CanteraError("Solution::setTransportModel",
127 "Unable to set Transport model without valid ThermoPhase object.");
128 }
129 if (m_transport && transportModel() == model) {
130 return;
131 }
133}
134
135void Solution::addAdjacent(shared_ptr<Solution> adjacent) {
136 if (m_adjacentByName.count(adjacent->name())) {
137 throw CanteraError("Solution::addAdjacent",
138 "Solution '{}' already contains an adjacent phase named '{}'.",
139 name(), adjacent->name());
140 }
141 if (m_thermo && adjacent->thermo()
142 && adjacent->thermo()->nDim() <= m_thermo->nDim())
143 {
144 throw CanteraError("Solution::addAdjacent",
145 "Adjacent phases should have higher dimensionality than the reacting ",
146 "phase.\n'{}' is {}-dimensional while '{}' is {}-dimensional",
147 adjacent->thermo()->name(), adjacent->thermo()->nDim(),
148 m_thermo->name(), m_thermo->nDim());
149 }
150 m_adjacent.push_back(adjacent);
152}
153
154shared_ptr<Solution> Solution::adjacent(const string& name)
155{
156 try {
157 return m_adjacentByName.at(name);
158 } catch (std::exception&) {
159 throw CanteraError("Solution::adjacent", "Solution '{}' does not have an "
160 "adjacent phase named '{}'", this->name(), name);
161 }
162}
163
164AnyMap Solution::parameters(bool withInput) const
165{
166 AnyMap out = m_thermo->parameters(false);
167 AnyValue empty("<NULL>");
168 if (m_kinetics) {
169 out.update(m_kinetics->parameters());
170 }
171 if (!m_transport) {
172 out["transport"] = empty;
173 } else if (m_transport->transportModel() == "none") {
174 out["transport"] = empty;
175 } else {
176 out.update(m_transport->parameters());
177 }
178 if (withInput) {
179 auto transport = out["transport"];
180 AnyMap input = m_thermo->input();
181 if (input.hasKey("reactions")) {
182 // all reactions are listed in the standard 'reactions' section
183 input.erase("reactions");
184 }
185 out.update(input);
186 if (input.hasKey("transport")) {
187 // revert changes / ensure that correct model is referenced
188 out["transport"] = transport;
189 }
190 }
191 if (out["transport"] == empty) {
192 out.erase("transport");
193 }
194 return out;
195}
196
198{
199 return m_header;
200}
201
203{
204 return m_header;
205}
206
207const string Solution::source() const {
208 AnyValue source = m_header.getMetadata("filename");
209 return source.empty() ? "<unknown>" : source.asString();
210}
211
212void Solution::setSource(const string& source) {
213 AnyValue filename(source);
214 m_header.setMetadata("filename", filename);
215}
216
217void Solution::holdExternalHandle(const string& name,
218 shared_ptr<ExternalHandle> handle)
219{
220 m_externalHandles[name] = handle;
221}
222
223shared_ptr<ExternalHandle> Solution::getExternalHandle(const string& name) const
224{
225 if (m_externalHandles.count(name)) {
226 return m_externalHandles.at(name);
227 } else {
228 return shared_ptr<ExternalHandle>();
229 }
230}
231
232void Solution::registerChangedCallback(void *id, const function<void()>& callback)
233{
234 m_changeCallbacks[id] = callback;
235}
236
238{
239 m_changeCallbacks.erase(id);
240}
241
242shared_ptr<Solution> newSolution(const string &infile,
243 const string &name,
244 const string &transport,
245 const vector<shared_ptr<Solution>> &adjacent)
246{
247 // get file extension
248 size_t dot = infile.find_last_of(".");
249 string extension;
250 if (dot != npos) {
251 extension = toLowerCopy(infile.substr(dot+1));
252 }
253
254 if (extension == "cti" || extension == "xml") {
255 throw CanteraError("newSolution",
256 "The CTI and XML formats are no longer supported.");
257 }
258
259 // load YAML file
260 auto rootNode = AnyMap::fromYamlFile(infile);
261 const AnyMap& phaseNode = rootNode.at("phases").getMapWhere("name", name);
262 auto sol = newSolution(phaseNode, rootNode, transport, adjacent);
263 sol->setSource(infile);
264 return sol;
265}
266
267shared_ptr<Solution> newSolution(const string& infile, const string& name,
268 const string& transport, const vector<string>& adjacent)
269{
270 auto rootNode = AnyMap::fromYamlFile(infile);
271 const AnyMap& phaseNode = rootNode.at("phases").getMapWhere("name", name);
272
273 vector<shared_ptr<Solution>> adjPhases;
274 // Create explicitly-specified adjacent bulk phases
275 for (auto& name : adjacent) {
276 const auto& adjNode = rootNode.at("phases").getMapWhere("name", name);
277 adjPhases.push_back(newSolution(adjNode, rootNode));
278 }
279 return newSolution(phaseNode, rootNode, transport, adjPhases);
280}
281
282shared_ptr<Solution> newSolution(const AnyMap& phaseNode,
283 const AnyMap& rootNode,
284 const string& transport,
285 const vector<shared_ptr<Solution>>& adjacent,
286 const map<string, shared_ptr<Solution>>& related)
287{
288 // thermo phase
289 auto thermo = newThermo(phaseNode, rootNode);
290
291 // instantiate Solution object of the correct derived type
292 shared_ptr<Solution> sol;
293 switch (thermo->nDim()) {
294 case 2:
295 sol = Interface::create();
296 break;
297 default:
298 sol = Solution::create();
299 }
300 sol->setSource("custom YAML");
301 sol->setThermo(thermo);
302
303 // Add explicitly-specified adjacent phases
304 for (auto& adj : adjacent) {
305 sol->addAdjacent(adj);
306 }
307
308 // If no adjacent phases were explicitly specified, look for them in the interface
309 // phase definition
310 if (adjacent.empty() && phaseNode.hasKey("adjacent-phases")) {
311 auto all_related = related;
312 for (auto& phase : adjacent) {
313 all_related[phase->name()] = phase;
314 }
315
316 // Helper function for adding individual phases
317 auto addPhase = [&](const AnyValue& phases, const AnyMap& root,
318 const string& name)
319 {
320 if (!all_related.count(name)) {
321 // Create a new phase only if there isn't already one with the same name
322 auto adj = newSolution(phases.getMapWhere("name", name), root,
323 "default", {}, all_related);
324 all_related[name] = adj;
325 for (size_t i = 0; i < adj->nAdjacent(); i++) {
326 all_related[adj->adjacent(i)->name()] = adj->adjacent(i);
327 }
328 }
329 sol->addAdjacent(all_related[name]);
330 };
331
332 auto& adjPhases = phaseNode["adjacent-phases"];
333 if (adjPhases.is<vector<string>>()) {
334 // 'adjacent' is a list of bulk phases from the current input file
335 for (auto& phase : adjPhases.as<vector<string>>()) {
336 addPhase(rootNode["phases"], rootNode, phase);
337 }
338 } else if (adjPhases.is<vector<AnyMap>>()) {
339 // Each element of 'adjacent' is a map with one item, where the key is
340 // a section in this file or another YAML file, and the value is a list of
341 // phase names to read from that section
342 for (auto& item : adjPhases.asVector<AnyMap>()) {
343 const string& source = item.begin()->first;
344 const auto& names = item.begin()->second.asVector<string>();
345 const auto& slash = boost::ifind_last(source, "/");
346 if (slash) {
347 // source is a different input file
348 string fileName(source.begin(), slash.begin());
349 string node(slash.end(), source.end());
350 AnyMap phaseSource = AnyMap::fromYamlFile(fileName,
351 rootNode.getString("__file__", ""));
352 for (auto& phase : names) {
353 addPhase(phaseSource[node], phaseSource, phase);
354 }
355 } else if (rootNode.hasKey(source)) {
356 // source is in the current file
357 for (auto& phase : names) {
358 addPhase(rootNode[source], rootNode, phase);
359 }
360 } else {
361 throw InputFileError("newSolution", adjPhases,
362 "Could not find a phases section named '{}'.", source);
363 }
364 }
365 } else {
366 throw InputFileError("addAdjacentPhases", adjPhases,
367 "Could not parse adjacent phase declaration of type '{}'",
368 adjPhases.type_str());
369 }
370 }
371
372 // kinetics
373 vector<shared_ptr<ThermoPhase>> phases;
374 phases.push_back(sol->thermo());
375 for (size_t i = 0; i < sol->nAdjacent(); i++) {
376 phases.push_back(sol->adjacent(i)->thermo());
377 }
378 sol->setKinetics(newKinetics(phases, phaseNode, rootNode, sol));
379
380 // set transport model by name
381 sol->setTransportModel(transport);
382
383 // save root-level information (YAML header)
384 AnyMap header;
385 for (const auto& [key, value] : rootNode.ordered()) {
386 if (key == "phases") {
387 // header ends with "phases" field
388 break;
389 }
390 header[key] = value;
391 }
392 sol->header() = header;
393
394 return sol;
395}
396
397} // namespace Cantera
Base class for kinetics managers and also contains the kineticsmgr module documentation (see Kinetics...
Headers for the factory class that can create known ThermoPhase objects (see Thermodynamic Properties...
Header file for class ThermoPhase, the base class for phases with thermodynamic properties,...
Header file defining class TransportFactory (see TransportFactory)
Headers for the Transport object, which is the virtual base class for all transport property evaluato...
const AnyValue & getMetadata(const string &key) const
Get a value from the metadata applicable to the AnyMap tree containing this node.
Definition AnyMap.cpp:623
A map of string keys to values whose type can vary at runtime.
Definition AnyMap.h:431
bool hasKey(const string &key) const
Returns true if the map contains an item named key.
Definition AnyMap.cpp:1477
void setMetadata(const string &key, const AnyValue &value)
Set a metadata value that applies to this AnyMap and its children.
Definition AnyMap.cpp:1541
OrderedProxy ordered(bool withUnits=false) const
Return a proxy object that allows iteration in an order determined by the order of insertion,...
Definition AnyMap.h:629
const string & getString(const string &key, const string &default_) const
If key exists, return it as a string, otherwise return default_.
Definition AnyMap.cpp:1590
void erase(const string &key)
Erase the value held by key.
Definition AnyMap.cpp:1483
static AnyMap fromYamlFile(const string &name, const string &parent_name="")
Create an AnyMap from a YAML file.
Definition AnyMap.cpp:1841
const AnyValue & at(const string &key) const
Get the value of the item stored in key.
Definition AnyMap.cpp:1458
void update(const AnyMap &other, bool keepExisting=true)
Add items from other to this AnyMap.
Definition AnyMap.cpp:1493
A wrapper for a variable whose type is determined at runtime.
Definition AnyMap.h:88
AnyMap & getMapWhere(const string &key, const string &value, bool create=false)
Treating the value as vector<AnyMap>, return the item where the given key has the specified value.
Definition AnyMap.cpp:1084
Base class for exceptions thrown by Cantera classes.
Error thrown for problems processing information contained in an AnyMap or AnyValue.
Definition AnyMap.h:749
static shared_ptr< Interface > create()
Create an empty Interface object.
Definition Interface.h:33
static shared_ptr< Solution > create()
Create an empty Solution object.
Definition Solution.h:54
vector< shared_ptr< Solution > > m_adjacent
Adjacent phases, for access by index.
Definition Solution.h:182
string transportModel()
Retrieve transport model name.
Definition Solution.cpp:105
void removeChangedCallback(void *id)
Remove the callback function associated with the specified object.
Definition Solution.cpp:237
map< string, shared_ptr< Solution > > m_adjacentByName
Adjacent phases, for access by name.
Definition Solution.h:185
void setSource(const string &source)
Overwrite source (only required if object is not created using newSolution)
Definition Solution.cpp:212
shared_ptr< Kinetics > kinetics()
Accessor for the Kinetics pointer.
Definition Solution.h:104
void setTransportModel(const string &model="default")
Set the Transport object by name.
Definition Solution.cpp:124
void addAdjacent(shared_ptr< Solution > adjacent)
Add a phase adjacent to this phase.
Definition Solution.cpp:135
void setName(const string &name)
Set the name of this Solution object.
Definition Solution.cpp:75
shared_ptr< Kinetics > m_kinetics
Kinetics manager.
Definition Solution.h:178
virtual void setThermo(shared_ptr< ThermoPhase > thermo)
Set the ThermoPhase object.
Definition Solution.cpp:84
map< string, shared_ptr< ExternalHandle > > m_externalHandles
Wrappers for this Solution object in extension languages, for evaluation of user-defined reaction rat...
Definition Solution.h:191
shared_ptr< ThermoPhase > m_thermo
ThermoPhase manager.
Definition Solution.h:177
void registerChangedCallback(void *id, const function< void()> &callback)
Register a function to be called if any of the Solution's thermo, kinetics, or transport objects is r...
Definition Solution.cpp:232
shared_ptr< Solution > clone(const vector< shared_ptr< Solution > > &adjacent={}, bool withKinetics=true, bool withTransport=true) const
Create a new Solution object with cloned ThermoPhase, Kinetics, and Transport objects that use the sa...
Definition Solution.cpp:25
AnyMap m_header
Additional input fields; usually from a YAML input file.
Definition Solution.h:187
void holdExternalHandle(const string &name, shared_ptr< ExternalHandle > handle)
Store a handle to a wrapper for this Solution object from an external language interface (for example...
Definition Solution.cpp:217
map< void *, function< void()> > m_changeCallbacks
Callback functions that are invoked when the therm, kinetics, or transport members of the Solution ar...
Definition Solution.h:195
const string source() const
Retrieve source used for object creation; usually an input file name.
Definition Solution.cpp:207
virtual void setTransport(shared_ptr< Transport > transport)
Set the Transport object directly.
Definition Solution.cpp:114
shared_ptr< ThermoPhase > thermo()
Accessor for the ThermoPhase pointer.
Definition Solution.h:99
shared_ptr< Transport > transport()
Accessor for the Transport pointer.
Definition Solution.h:109
const AnyMap & header() const
Access input data associated with header definition.
Definition Solution.cpp:197
shared_ptr< Solution > adjacent(size_t i)
Get the Solution object for an adjacent phase by index.
Definition Solution.h:118
shared_ptr< Transport > m_transport
Transport manager.
Definition Solution.h:179
virtual void setKinetics(shared_ptr< Kinetics > kinetics)
Set the Kinetics object.
Definition Solution.cpp:92
shared_ptr< ExternalHandle > getExternalHandle(const string &name) const
Get the handle for a wrapper for this Solution object from an external language interface.
Definition Solution.cpp:223
string name() const
Return the name of this Solution object.
Definition Solution.cpp:66
string toLowerCopy(const string &input)
Convert to lower case.
shared_ptr< Kinetics > newKinetics(const string &model)
Create a new Kinetics instance.
double dot(InputIter x_begin, InputIter x_end, InputIter2 y_begin)
Function that calculates a templated inner product.
Definition utilities.h:82
shared_ptr< Solution > newSolution(const string &infile, const string &name, const string &transport, const vector< shared_ptr< Solution > > &adjacent)
Create and initialize a new Solution manager from an input file.
Definition Solution.cpp:242
shared_ptr< ThermoPhase > newThermo(const AnyMap &phaseNode, const AnyMap &rootNode)
Create a new ThermoPhase object and initialize it.
shared_ptr< Transport > newTransport(shared_ptr< ThermoPhase > thermo, const string &model)
Create a new Transport instance.
Namespace for the Cantera kernel.
Definition AnyMap.cpp:595
const size_t npos
index returned by functions to indicate "no position"
Definition ct_defs.h:180
Contains declarations for string manipulation functions within Cantera.