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
4 #include "cantera/base/YamlWriter.h"
5 #include "cantera/base/AnyMap.h"
6 #include "cantera/base/Solution.h"
7 #include "cantera/base/stringUtils.h"
8 #include "cantera/thermo/ThermoPhase.h"
9 #include "cantera/thermo/Species.h"
10 #include "cantera/kinetics/Kinetics.h"
11 #include "cantera/kinetics/Reaction.h"
12 #include "cantera/transport/TransportBase.h"
13
14 #include <fstream>
15 #include <chrono>
16
17 namespace Cantera {
18
YamlWriter()19 YamlWriter::YamlWriter()
20 : m_float_precision(15)
21 , m_skip_user_defined(false)
22 {
23 }
24
addPhase(shared_ptr<Solution> soln)25 void YamlWriter::addPhase(shared_ptr<Solution> soln) {
26 for (auto& phase : m_phases) {
27 if (phase->name() == soln->name()) {
28 throw CanteraError("YamlWriter::addPhase",
29 "Duplicate phase name '{}'", soln->name());
30 }
31 }
32 m_phases.push_back(soln);
33 }
34
addPhase(shared_ptr<ThermoPhase> thermo,shared_ptr<Kinetics> kin,shared_ptr<Transport> tran)35 void YamlWriter::addPhase(shared_ptr<ThermoPhase> thermo,
36 shared_ptr<Kinetics> kin,
37 shared_ptr<Transport> tran) {
38 auto soln = Solution::create();
39 soln->setThermo(thermo);
40 soln->setKinetics(kin);
41 soln->setTransport(tran);
42 addPhase(soln);
43 }
44
toYamlString() const45 std::string YamlWriter::toYamlString() const
46 {
47 AnyMap output;
48 output["generator"] = "YamlWriter";
49 output["cantera-version"] = CANTERA_VERSION;
50 time_t now = std::chrono::system_clock::to_time_t(std::chrono::system_clock::now());
51 output["date"] = trimCopy(std::ctime(&now));
52
53 // Build phase definitions
54 std::vector<AnyMap> phaseDefs(m_phases.size());
55 size_t nspecies_total = 0;
56 for (size_t i = 0; i < m_phases.size(); i++) {
57 phaseDefs[i] = m_phases[i]->parameters(!m_skip_user_defined);
58 nspecies_total += m_phases[i]->thermo()->nSpecies();
59 }
60 output["phases"] = phaseDefs;
61
62 // Build species definitions for all phases
63 std::vector<AnyMap> speciesDefs;
64 speciesDefs.reserve(nspecies_total);
65 std::unordered_map<std::string, size_t> speciesDefIndex;
66 for (const auto& phase : m_phases) {
67 const auto thermo = phase->thermo();
68 for (const auto& name : thermo->speciesNames()) {
69 const auto& species = thermo->species(name);
70 AnyMap speciesDef = species->parameters(thermo.get(), !m_skip_user_defined);
71
72 if (speciesDefIndex.count(name) == 0) {
73 speciesDefs.emplace_back(speciesDef);
74 speciesDefIndex[name] = speciesDefs.size() - 1;
75 } else if (speciesDefs[speciesDefIndex[name]] != speciesDef) {
76 throw CanteraError("YamlWriter::toYamlString",
77 "Multiple species with different definitions are not "
78 "supported:\n>>>>>>\n{}\n======\n{}\n<<<<<<\n",
79 speciesDef.toYamlString(),
80 speciesDefs[speciesDefIndex[name]].toYamlString());
81 }
82 }
83 }
84 output["species"] = speciesDefs;
85
86 // build reaction definitions for all phases
87 std::map<std::string, std::vector<AnyMap>> allReactions;
88 for (const auto& phase : m_phases) {
89 const auto kin = phase->kinetics();
90 if (!kin || !kin->nReactions()) {
91 continue;
92 }
93 std::vector<AnyMap> reactions;
94 for (size_t i = 0; i < kin->nReactions(); i++) {
95 reactions.push_back(kin->reaction(i)->parameters(!m_skip_user_defined));
96 }
97 allReactions[phase->name()] = std::move(reactions);
98 }
99
100 // Figure out which phase definitions have identical sets of reactions,
101 // and can share a reaction definition section
102
103 // key: canonical phase in allReactions
104 // value: phases using this reaction set
105 std::map<std::string, std::vector<std::string>> phaseGroups;
106
107 for (const auto& phase : m_phases) {
108 const auto kin = phase->kinetics();
109 std::string name = phase->name();
110 if (!kin || !kin->nReactions()) {
111 continue;
112 }
113 bool match = false;
114 for (auto& group : phaseGroups) {
115 if (allReactions[group.first] == allReactions[name]) {
116 group.second.push_back(name);
117 allReactions.erase(name);
118 match = true;
119 break;
120 }
121 }
122 if (!match) {
123 phaseGroups[name].push_back(name);
124 }
125 }
126
127 // Generate the reactions section(s) in the output file
128 if (phaseGroups.size() == 1) {
129 output["reactions"] = std::move(allReactions[phaseGroups.begin()->first]);
130 } else {
131 for (const auto& group : phaseGroups) {
132 std::string groupName;
133 for (auto& name : group.second) {
134 groupName += name + "-";
135 }
136 groupName += "reactions";
137 output[groupName] = std::move(allReactions[group.first]);
138
139 for (auto& name : group.second) {
140 AnyMap& phaseDef = output["phases"].getMapWhere("name", name);
141 phaseDef["reactions"] = std::vector<std::string>{groupName};
142 }
143 }
144 }
145
146 output.setMetadata("precision", AnyValue(m_float_precision));
147 output.setUnits(m_output_units);
148 return output.toYamlString();
149 }
150
toYamlFile(const std::string & filename) const151 void YamlWriter::toYamlFile(const std::string& filename) const
152 {
153 std::ofstream out(filename);
154 out << toYamlString();
155 }
156
setUnits(const std::map<std::string,std::string> & units)157 void YamlWriter::setUnits(const std::map<std::string, std::string>& units)
158 {
159 m_output_units = UnitSystem();
160 m_output_units.setDefaults(units);
161 }
162
setUnitSystem(const UnitSystem & units)163 void YamlWriter::setUnitSystem(const UnitSystem& units)
164 {
165 m_output_units = units;
166 }
167
168 }
169