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