Cantera  3.1.0a1
YamlWriter.cpp
1 // This file is part of Cantera. See License.txt in the top-level directory or
2 // at https://cantera.org/license.txt for license and copyright information.
3 
5 #include "cantera/base/AnyMap.h"
13 
14 #include <fstream>
15 #include <chrono>
16 
17 namespace Cantera {
18 
19 void YamlWriter::setHeader(const AnyMap& header) {
20  m_header = header;
21 }
22 
23 void YamlWriter::addPhase(shared_ptr<Solution> soln, bool includeAdjacent) {
24  for (auto& phase : m_phases) {
25  if (phase->name() == soln->name()) {
26  if (phase.get() == soln.get()) {
27  // This phase has already been added, so nothing needs to be done. This
28  // is expected in cases such as bulk phases adjacent to multiple
29  // surface phases.
30  return;
31  } else {
32  throw CanteraError("YamlWriter::addPhase",
33  "Duplicate phase name '{}'", soln->name());
34  }
35  }
36  }
37  m_phases.push_back(soln);
38  if (includeAdjacent) {
39  for (size_t i = 0; i < soln->nAdjacent(); i++) {
40  addPhase(soln->adjacent(i));
41  }
42  }
43 }
44 
45 void YamlWriter::addPhase(shared_ptr<ThermoPhase> thermo,
46  shared_ptr<Kinetics> kin,
47  shared_ptr<Transport> tran) {
48  auto soln = Solution::create();
49  soln->setThermo(thermo);
50  soln->setKinetics(kin);
51  soln->setTransport(tran);
52  addPhase(soln);
53 }
54 
56 {
57  AnyMap output;
58  bool hasDescription = m_header.hasKey("description");
59  if (hasDescription) {
60  output["description"] = m_header["description"];
61  }
62  output["generator"] = "YamlWriter";
63  output["cantera-version"] = CANTERA_VERSION;
64  output["git-commit"] = gitCommit();
65  time_t now = std::chrono::system_clock::to_time_t(std::chrono::system_clock::now());
66  output["date"] = trimCopy(std::ctime(&now));
67  if (hasDescription) {
68  output["description"].setLoc(-6, 0);
69  }
70  output["generator"].setLoc(-5, 0);
71  output["cantera-version"].setLoc(-4, 0);
72  output["git-commit"].setLoc(-3, 0);
73  output["date"].setLoc(-2, 0);
74 
75  // Add remaining header information, ignoring obsolete information
76  set<string> exclude = {
77  "description", "generator", "cantera-version", "git-commit", "date"};
78  for (const auto& [key, value] : m_header) {
79  if (!exclude.count(key)) {
80  output[key] = value;
81  }
82  }
83 
84  // Build phase definitions
85  vector<AnyMap> phaseDefs(m_phases.size());
86  size_t nspecies_total = 0;
87  for (size_t i = 0; i < m_phases.size(); i++) {
88  phaseDefs[i] = m_phases[i]->parameters(!m_skip_user_defined);
89  if (m_phases[i]->nAdjacent()) {
90  vector<string> adj_names;
91  for (size_t j = 0; j < m_phases[i]->nAdjacent(); j++) {
92  adj_names.push_back(m_phases[i]->adjacent(j)->name());
93  }
94  phaseDefs[i]["adjacent-phases"] = adj_names;
95  }
96  nspecies_total += m_phases[i]->thermo()->nSpecies();
97  }
98  output["phases"] = phaseDefs;
99 
100  // Build species definitions for all phases
101  vector<AnyMap> speciesDefs;
102  speciesDefs.reserve(nspecies_total);
103  std::unordered_map<string, size_t> speciesDefIndex;
104  for (const auto& phase : m_phases) {
105  const auto thermo = phase->thermo();
106  for (const auto& name : thermo->speciesNames()) {
107  const auto& species = thermo->species(name);
108  AnyMap speciesDef = species->parameters(thermo.get(), !m_skip_user_defined);
109 
110  if (speciesDefIndex.count(name) == 0) {
111  speciesDefs.emplace_back(speciesDef);
112  speciesDefIndex[name] = speciesDefs.size() - 1;
113  } else if (speciesDefs[speciesDefIndex[name]] != speciesDef) {
114  throw CanteraError("YamlWriter::toYamlString",
115  "Multiple species with different definitions are not "
116  "supported:\n>>>>>>\n{}\n======\n{}\n<<<<<<\n",
117  speciesDef.toYamlString(),
118  speciesDefs[speciesDefIndex[name]].toYamlString());
119  }
120  }
121  }
122  output["species"] = speciesDefs;
123 
124  // build reaction definitions for all phases
125  map<string, vector<AnyMap>> allReactions;
126  for (const auto& phase : m_phases) {
127  const auto kin = phase->kinetics();
128  if (!kin || !kin->nReactions()) {
129  continue;
130  }
131  vector<AnyMap> reactions;
132  for (size_t i = 0; i < kin->nReactions(); i++) {
133  reactions.push_back(kin->reaction(i)->parameters(!m_skip_user_defined));
134  }
135  allReactions[phase->name()] = std::move(reactions);
136  }
137 
138  // Figure out which phase definitions have identical sets of reactions,
139  // and can share a reaction definition section
140 
141  // key: canonical phase in allReactions
142  // value: phases using this reaction set
143  map<string, vector<string>> phaseGroups;
144 
145  for (const auto& phase : m_phases) {
146  const auto kin = phase->kinetics();
147  string name = phase->name();
148  if (!kin || !kin->nReactions()) {
149  continue;
150  }
151  bool match = false;
152  for (auto& [canonicalPhase, dependentPhases] : phaseGroups) {
153  if (allReactions[canonicalPhase] == allReactions[name]) {
154  dependentPhases.push_back(name);
155  allReactions.erase(name);
156  match = true;
157  break;
158  }
159  }
160  if (!match) {
161  phaseGroups[name].push_back(name);
162  }
163  }
164 
165  // Generate the reactions section(s) in the output file
166  if (phaseGroups.size() == 1) {
167  output["reactions"] = std::move(allReactions[phaseGroups.begin()->first]);
168  } else {
169  for (const auto& [canonicalPhase, dependentPhases] : phaseGroups) {
170  string groupName;
171  for (auto& name : dependentPhases) {
172  groupName += name + "-";
173  }
174  groupName += "reactions";
175  output[groupName] = std::move(allReactions[canonicalPhase]);
176 
177  for (auto& name : dependentPhases) {
178  AnyMap& phaseDef = output["phases"].getMapWhere("name", name);
179  phaseDef["reactions"] = vector<string>{groupName};
180  }
181  }
182  }
183 
184  output.setMetadata("precision", AnyValue(m_float_precision));
185  output.setUnits(m_output_units);
186  return output.toYamlString();
187 }
188 
189 void YamlWriter::toYamlFile(const string& filename) const
190 {
191  std::ofstream out(filename);
192  out << toYamlString();
193 }
194 
195 void YamlWriter::setUnits(const map<string, string>& units)
196 {
199 }
200 
202 {
203  m_output_units = units;
204 }
205 
206 }
Base class for kinetics managers and also contains the kineticsmgr module documentation (see Kinetics...
Declaration for class Cantera::Species.
Header file for class ThermoPhase, the base class for phases with thermodynamic properties,...
Headers for the Transport object, which is the virtual base class for all transport property evaluato...
Declaration for class Cantera::YamlWriter.
void setLoc(int line, int column)
For values which are derived from an input file, set the line and column of this value in that file.
Definition: AnyMap.cpp:574
A map of string keys to values whose type can vary at runtime.
Definition: AnyMap.h:427
size_t size() const
Returns the number of elements in this map.
Definition: AnyMap.h:622
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
void setUnits(const UnitSystem &units)
Set the unit system for this AnyMap.
Definition: AnyMap.cpp:1714
A wrapper for a variable whose type is determined at runtime.
Definition: AnyMap.h:86
Base class for exceptions thrown by Cantera classes.
Definition: ctexceptions.h:66
static shared_ptr< Solution > create()
Create an empty Solution object.
Definition: Solution.h:54
Unit conversion utility.
Definition: Units.h:169
void setDefaults(std::initializer_list< string > units)
Set the default units to convert from when explicit units are not provided.
Definition: Units.cpp:428
bool m_skip_user_defined
See skipUserDefined()
Definition: YamlWriter.h:87
string toYamlString() const
Return a YAML string that contains the definitions for the added phases, species, and reactions.
Definition: YamlWriter.cpp:55
void setUnitSystem(const UnitSystem &units=UnitSystem())
Set the units to be used in the output file.
Definition: YamlWriter.cpp:201
long int m_float_precision
See setPrecision()
Definition: YamlWriter.h:84
AnyMap m_header
Top-level information used in YAML header block.
Definition: YamlWriter.h:79
void setHeader(const AnyMap &header)
Include top-level information used in YAML header block.
Definition: YamlWriter.cpp:19
void addPhase(shared_ptr< Solution > soln, bool includeAdjacent=true)
Include a phase definition for the specified Solution object.
Definition: YamlWriter.cpp:23
UnitSystem m_output_units
Top-level units directive for the output file.
Definition: YamlWriter.h:91
void toYamlFile(const string &filename) const
Write the definitions for the added phases, species and reactions to the specified file.
Definition: YamlWriter.cpp:189
void setUnits(const map< string, string > &units={})
Set the units to be used in the output file.
Definition: YamlWriter.cpp:195
string trimCopy(const string &input)
Trim.
string gitCommit()
Returns the hash of the git commit from which Cantera was compiled, if known.
Definition: global.cpp:150
Namespace for the Cantera kernel.
Definition: AnyMap.cpp:564
Contains declarations for string manipulation functions within Cantera.