Cantera  3.1.0a1
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 
10 #include "cantera/base/Interface.h"
19 
20 #include <boost/algorithm/string.hpp>
21 
22 namespace Cantera
23 {
24 
25 string Solution::name() const {
26  if (m_thermo) {
27  return m_thermo->name();
28  } else {
29  throw CanteraError("Solution::name",
30  "Requires associated 'ThermoPhase'");
31  }
32 }
33 
34 void Solution::setName(const string& name) {
35  if (m_thermo) {
36  m_thermo->setName(name);
37  } else {
38  throw CanteraError("Solution::setName",
39  "Requires associated 'ThermoPhase'");
40  }
41 }
42 
43 void Solution::setThermo(shared_ptr<ThermoPhase> thermo) {
44  m_thermo = thermo;
45  for (const auto& [id, callback] : m_changeCallbacks) {
46  callback();
47  }
48 }
49 
50 void Solution::setKinetics(shared_ptr<Kinetics> kinetics) {
51  if (kinetics == m_kinetics) {
52  return;
53  }
55  if (m_kinetics) {
56  m_kinetics->setRoot(shared_from_this());
57  }
58  for (const auto& [id, callback] : m_changeCallbacks) {
59  callback();
60  }
61 }
62 
63 void Solution::setTransport(shared_ptr<Transport> transport) {
64  if (transport == m_transport) {
65  return;
66  }
68  for (const auto& [id, callback] : m_changeCallbacks) {
69  callback();
70  }
71 }
72 
73 void Solution::setTransportModel(const string& model) {
74  if (!m_thermo) {
75  throw CanteraError("Solution::setTransportModel",
76  "Unable to set Transport model without valid ThermoPhase object.");
77  }
78  if (model == "") {
79  setTransport(newTransport(m_thermo, "default"));
80  } else {
82  }
83 }
84 
85 void Solution::addAdjacent(shared_ptr<Solution> adjacent) {
86  if (m_adjacentByName.count(adjacent->name())) {
87  throw CanteraError("Solution::addAdjacent",
88  "Solution '{}' already contains an adjacent phase named '{}'.",
89  name(), adjacent->name());
90  }
91  if (m_thermo && adjacent->thermo()
92  && adjacent->thermo()->nDim() <= m_thermo->nDim())
93  {
94  throw CanteraError("Solution::addAdjacent",
95  "Adjacent phases should have higher dimensionality than the reacting ",
96  "phase.\n'{}' is {}-dimensional while '{}' is {}-dimensional",
97  adjacent->thermo()->name(), adjacent->thermo()->nDim(),
98  m_thermo->name(), m_thermo->nDim());
99  }
100  m_adjacent.push_back(adjacent);
102 }
103 
104 AnyMap Solution::parameters(bool withInput) const
105 {
106  AnyMap out = m_thermo->parameters(false);
107  AnyValue empty("<NULL>");
108  if (m_kinetics) {
109  out.update(m_kinetics->parameters());
110  }
111  if (!m_transport) {
112  out["transport"] = empty;
113  } else if (m_transport->transportModel() == "none") {
114  out["transport"] = empty;
115  } else {
116  out.update(m_transport->parameters());
117  }
118  if (withInput) {
119  auto transport = out["transport"];
120  AnyMap input = m_thermo->input();
121  if (input.hasKey("reactions")) {
122  // all reactions are listed in the standard 'reactions' section
123  input.erase("reactions");
124  }
125  out.update(input);
126  if (input.hasKey("transport")) {
127  // revert changes / ensure that correct model is referenced
128  out["transport"] = transport;
129  }
130  }
131  if (out["transport"] == empty) {
132  out.erase("transport");
133  }
134  return out;
135 }
136 
137 const AnyMap& Solution::header() const
138 {
139  return m_header;
140 }
141 
143 {
144  return m_header;
145 }
146 
147 const string Solution::source() const {
148  AnyValue source = m_header.getMetadata("filename");
149  return source.empty() ? "<unknown>" : source.asString();
150 }
151 
152 void Solution::setSource(const string& source) {
153  AnyValue filename(source);
154  m_header.setMetadata("filename", filename);
155 }
156 
157 void Solution::holdExternalHandle(const string& name,
158  shared_ptr<ExternalHandle> handle)
159 {
160  m_externalHandles[name] = handle;
161 }
162 
163 shared_ptr<ExternalHandle> Solution::getExternalHandle(const string& name) const
164 {
165  if (m_externalHandles.count(name)) {
166  return m_externalHandles.at(name);
167  } else {
168  return shared_ptr<ExternalHandle>();
169  }
170 }
171 
172 void Solution::registerChangedCallback(void *id, const function<void()>& callback)
173 {
174  m_changeCallbacks[id] = callback;
175 }
176 
178 {
179  m_changeCallbacks.erase(id);
180 }
181 
182 shared_ptr<Solution> newSolution(const string &infile,
183  const string &name,
184  const string &transport,
185  const vector<shared_ptr<Solution>> &adjacent)
186 {
187  // get file extension
188  size_t dot = infile.find_last_of(".");
189  string extension;
190  if (dot != npos) {
191  extension = toLowerCopy(infile.substr(dot+1));
192  }
193 
194  if (extension == "cti" || extension == "xml") {
195  throw CanteraError("newSolution",
196  "The CTI and XML formats are no longer supported.");
197  }
198 
199  // load YAML file
200  auto rootNode = AnyMap::fromYamlFile(infile);
201  AnyMap& phaseNode = rootNode["phases"].getMapWhere("name", name);
202  auto sol = newSolution(phaseNode, rootNode, transport, adjacent);
203  sol->setSource(infile);
204  return sol;
205 }
206 
207 shared_ptr<Solution> newSolution(const string& infile, const string& name,
208  const string& transport, const vector<string>& adjacent)
209 {
210  auto rootNode = AnyMap::fromYamlFile(infile);
211  AnyMap& phaseNode = rootNode["phases"].getMapWhere("name", name);
212 
213  vector<shared_ptr<Solution>> adjPhases;
214  // Create explicitly-specified adjacent bulk phases
215  for (auto& name : adjacent) {
216  auto& adjNode = rootNode["phases"].getMapWhere("name", name);
217  adjPhases.push_back(newSolution(adjNode, rootNode));
218  }
219  return newSolution(phaseNode, rootNode, transport, adjPhases);
220 }
221 
222 shared_ptr<Solution> newSolution(const AnyMap& phaseNode,
223  const AnyMap& rootNode,
224  const string& transport,
225  const vector<shared_ptr<Solution>>& adjacent,
226  const map<string, shared_ptr<Solution>>& related)
227 {
228  // thermo phase
229  auto thermo = newThermo(phaseNode, rootNode);
230 
231  // instantiate Solution object of the correct derived type
232  shared_ptr<Solution> sol;
233  switch (thermo->nDim()) {
234  case 2:
235  sol = Interface::create();
236  break;
237  default:
238  sol = Solution::create();
239  }
240  sol->setSource("custom YAML");
241  sol->setThermo(thermo);
242 
243  // Add explicitly-specified adjacent phases
244  for (auto& adj : adjacent) {
245  sol->addAdjacent(adj);
246  }
247 
248  // If no adjacent phases were explicitly specified, look for them in the interface
249  // phase definition
250  if (adjacent.empty() && phaseNode.hasKey("adjacent-phases")) {
251  auto all_related = related;
252  for (auto& phase : adjacent) {
253  all_related[phase->name()] = phase;
254  }
255 
256  // Helper function for adding individual phases
257  auto addPhase = [&](const AnyValue& phases, const AnyMap& root,
258  const string& name)
259  {
260  if (!all_related.count(name)) {
261  // Create a new phase only if there isn't already one with the same name
262  auto adj = newSolution(phases.getMapWhere("name", name), root,
263  "", {}, all_related);
264  all_related[name] = adj;
265  for (size_t i = 0; i < adj->nAdjacent(); i++) {
266  all_related[adj->adjacent(i)->name()] = adj->adjacent(i);
267  }
268  }
269  sol->addAdjacent(all_related[name]);
270  };
271 
272  auto& adjPhases = phaseNode["adjacent-phases"];
273  if (adjPhases.is<vector<string>>()) {
274  // 'adjacent' is a list of bulk phases from the current input file
275  for (auto& phase : adjPhases.as<vector<string>>()) {
276  addPhase(rootNode["phases"], rootNode, phase);
277  }
278  } else if (adjPhases.is<vector<AnyMap>>()) {
279  // Each element of 'adjacent' is a map with one item, where the key is
280  // a section in this file or another YAML file, and the value is a list of
281  // phase names to read from that section
282  for (auto& item : adjPhases.asVector<AnyMap>()) {
283  const string& source = item.begin()->first;
284  const auto& names = item.begin()->second.asVector<string>();
285  const auto& slash = boost::ifind_last(source, "/");
286  if (slash) {
287  // source is a different input file
288  string fileName(source.begin(), slash.begin());
289  string node(slash.end(), source.end());
290  AnyMap phaseSource = AnyMap::fromYamlFile(fileName,
291  rootNode.getString("__file__", ""));
292  for (auto& phase : names) {
293  addPhase(phaseSource[node], phaseSource, phase);
294  }
295  } else if (rootNode.hasKey(source)) {
296  // source is in the current file
297  for (auto& phase : names) {
298  addPhase(rootNode[source], rootNode, phase);
299  }
300  } else {
301  throw InputFileError("newSolution", adjPhases,
302  "Could not find a phases section named '{}'.", source);
303  }
304  }
305  } else {
306  throw InputFileError("addAdjacentPhases", adjPhases,
307  "Could not parse adjacent phase declaration of type '{}'",
308  adjPhases.type_str());
309  }
310  }
311 
312  // kinetics
313  vector<shared_ptr<ThermoPhase>> phases;
314  phases.push_back(sol->thermo());
315  for (size_t i = 0; i < sol->nAdjacent(); i++) {
316  phases.push_back(sol->adjacent(i)->thermo());
317  }
318  sol->setKinetics(newKinetics(phases, phaseNode, rootNode, sol));
319 
320  // set transport model by name
321  sol->setTransportModel(transport);
322 
323  // save root-level information (YAML header)
324  AnyMap header;
325  for (const auto& [key, value] : rootNode.ordered()) {
326  if (key == "phases") {
327  // header ends with "phases" field
328  break;
329  } else if (key != "units") {
330  header[key] = value;
331  }
332  }
333  sol->header() = header;
334 
335  return sol;
336 }
337 
338 } // 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:580
A map of string keys to values whose type can vary at runtime.
Definition: AnyMap.h:427
bool hasKey(const string &key) const
Returns true if the map contains an item named key.
Definition: AnyMap.cpp:1423
void setMetadata(const string &key, const AnyValue &value)
Set a metadata value that applies to this AnyMap and its children.
Definition: AnyMap.cpp:1481
const string & getString(const string &key, const string &default_) const
If key exists, return it as a string, otherwise return default_.
Definition: AnyMap.cpp:1530
void erase(const string &key)
Erase the value held by key.
Definition: AnyMap.cpp:1428
static AnyMap fromYamlFile(const string &name, const string &parent_name="")
Create an AnyMap from a YAML file.
Definition: AnyMap.cpp:1771
void update(const AnyMap &other, bool keepExisting=true)
Add items from other to this AnyMap.
Definition: AnyMap.cpp:1438
A wrapper for a variable whose type is determined at runtime.
Definition: AnyMap.h:86
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:1034
Base class for exceptions thrown by Cantera classes.
Definition: ctexceptions.h:66
Error thrown for problems processing information contained in an AnyMap or AnyValue.
Definition: AnyMap.h:738
static shared_ptr< Interface > create()
Create an empty Interface object.
Definition: Interface.h:33
shared_ptr< ThermoPhase > thermo()
Accessor for the ThermoPhase pointer.
Definition: Solution.h:79
vector< shared_ptr< Solution > > m_adjacent
Adjacent phases, for access by index.
Definition: Solution.h:155
void removeChangedCallback(void *id)
Remove the callback function associated with the specified object.
Definition: Solution.cpp:177
shared_ptr< Kinetics > kinetics()
Accessor for the Kinetics pointer.
Definition: Solution.h:84
map< string, shared_ptr< Solution > > m_adjacentByName
Adjacent phases, for access by name.
Definition: Solution.h:158
void setSource(const string &source)
Overwrite source (only required if object is not created using newSolution)
Definition: Solution.cpp:152
void addAdjacent(shared_ptr< Solution > adjacent)
Add a phase adjacent to this phase.
Definition: Solution.cpp:85
shared_ptr< Transport > transport()
Accessor for the Transport pointer.
Definition: Solution.h:89
void setName(const string &name)
Set the name of this Solution object.
Definition: Solution.cpp:34
shared_ptr< Kinetics > m_kinetics
Kinetics manager.
Definition: Solution.h:151
void setTransportModel(const string &model="")
Set the Transport object by name.
Definition: Solution.cpp:73
shared_ptr< Solution > adjacent(size_t i)
Get the Solution object for an adjacent phase by index.
Definition: Solution.h:98
virtual void setThermo(shared_ptr< ThermoPhase > thermo)
Set the ThermoPhase object.
Definition: Solution.cpp:43
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:164
shared_ptr< ThermoPhase > m_thermo
ThermoPhase manager.
Definition: Solution.h:150
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:172
AnyMap m_header
Additional input fields; usually from a YAML input file.
Definition: Solution.h:160
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:157
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:168
const string source() const
Retrieve source used for object creation; usually an input file name.
Definition: Solution.cpp:147
virtual void setTransport(shared_ptr< Transport > transport)
Set the Transport object directly.
Definition: Solution.cpp:63
const AnyMap & header() const
Access input data associated with header definition.
Definition: Solution.cpp:137
static shared_ptr< Solution > create()
Create an empty Solution object.
Definition: Solution.h:54
shared_ptr< Transport > m_transport
Transport manager.
Definition: Solution.h:152
virtual void setKinetics(shared_ptr< Kinetics > kinetics)
Set the Kinetics object.
Definition: Solution.cpp:50
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:163
string name() const
Return the name of this Solution object.
Definition: Solution.cpp:25
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:182
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:564
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.