Cantera  3.1.0a1
ThermoFactory.cpp
Go to the documentation of this file.
1 /**
2  * @file ThermoFactory.cpp
3  * Definitions for the factory class that can create known ThermoPhase objects
4  * (see @ref thermoprops and class @link Cantera::ThermoFactory ThermoFactory@endlink).
5  */
6 
7 // This file is part of Cantera. See License.txt in the top-level directory or
8 // at https://cantera.org/license.txt for license and copyright information.
9 
11 
12 #include "cantera/thermo/Species.h"
19 
33 #include "cantera/thermo/HMWSoln.h"
40 
41 #include <boost/algorithm/string.hpp>
42 
43 namespace Cantera
44 {
45 
46 ThermoFactory* ThermoFactory::s_factory = 0;
48 
50 {
51  reg("none", []() { return new ThermoPhase(); });
52  addDeprecatedAlias("none", "ThermoPhase");
53  addDeprecatedAlias("none", "None");
54  reg("ideal-gas", []() { return new IdealGasPhase(); });
55  addDeprecatedAlias("ideal-gas", "IdealGas");
56  reg("plasma", []() { return new PlasmaPhase(); });
57  reg("ideal-surface", []() { return new SurfPhase(); });
58  addDeprecatedAlias("ideal-surface", "Surface");
59  addDeprecatedAlias("ideal-surface", "Surf");
60  reg("coverage-dependent-surface", []() { return new CoverageDependentSurfPhase(); });
61  reg("edge", []() { return new EdgePhase(); });
62  addDeprecatedAlias("edge", "Edge");
63  reg("electron-cloud", []() { return new MetalPhase(); });
64  addDeprecatedAlias("electron-cloud", "Metal");
65  reg("fixed-stoichiometry", []() { return new StoichSubstance(); });
66  addDeprecatedAlias("fixed-stoichiometry", "StoichSubstance");
67  reg("pure-fluid", []() { return new PureFluidPhase(); });
68  addDeprecatedAlias("pure-fluid", "PureFluid");
69  reg("compound-lattice", []() { return new LatticeSolidPhase(); });
70  addDeprecatedAlias("compound-lattice", "LatticeSolid");
71  reg("lattice", []() { return new LatticePhase(); });
72  addDeprecatedAlias("lattice", "Lattice");
73  reg("HMW-electrolyte", []() { return new HMWSoln(); });
74  addDeprecatedAlias("HMW-electrolyte", "HMW");
75  addDeprecatedAlias("HMW-electrolyte", "HMWSoln");
76  reg("ideal-condensed", []() { return new IdealSolidSolnPhase(); });
77  addDeprecatedAlias("ideal-condensed", "IdealSolidSolution");
78  addDeprecatedAlias("ideal-condensed", "IdealSolidSoln");
79  reg("Debye-Huckel", []() { return new DebyeHuckel(); });
80  addDeprecatedAlias("Debye-Huckel", "DebyeHuckel");
81  reg("ideal-molal-solution", []() { return new IdealMolalSoln(); });
82  addDeprecatedAlias("ideal-molal-solution", "IdealMolalSolution");
83  addDeprecatedAlias("ideal-molal-solution", "IdealMolalSoln");
84  reg("ideal-solution-VPSS", []() { return new IdealSolnGasVPSS(); });
85  reg("ideal-gas-VPSS", []() { return new IdealSolnGasVPSS(); });
86  addDeprecatedAlias("ideal-solution-VPSS", "IdealSolnVPSS");
87  addDeprecatedAlias("ideal-solution-VPSS", "IdealSolnGas");
88  addDeprecatedAlias("ideal-gas-VPSS", "IdealGasVPSS");
89  reg("Margules", []() { return new MargulesVPSSTP(); });
90  reg("Redlich-Kister", []() { return new RedlichKisterVPSSTP(); });
91  addDeprecatedAlias("Redlich-Kister", "RedlichKister");
92  reg("Redlich-Kwong", []() { return new RedlichKwongMFTP(); });
93  addDeprecatedAlias("Redlich-Kwong", "RedlichKwongMFTP");
94  addDeprecatedAlias("Redlich-Kwong", "RedlichKwong");
95  reg("liquid-water-IAPWS95", []() { return new WaterSSTP(); });
96  addDeprecatedAlias("liquid-water-IAPWS95", "PureLiquidWater");
97  addDeprecatedAlias("liquid-water-IAPWS95", "Water");
98  reg("binary-solution-tabulated", []() { return new BinarySolutionTabulatedThermo(); });
99  addDeprecatedAlias("binary-solution-tabulated", "BinarySolutionTabulatedThermo");
100  reg("Peng-Robinson", []() { return new PengRobinson(); });
101 }
102 
104 {
105  std::unique_lock<std::mutex> lock(thermo_mutex);
106  if (!s_factory) {
107  s_factory = new ThermoFactory;
108  }
109  return s_factory;
110 }
111 
113 {
114  std::unique_lock<std::mutex> lock(thermo_mutex);
115  delete s_factory;
116  s_factory = 0;
117 }
118 
119 shared_ptr<ThermoPhase> newThermoModel(const string& model)
120 {
121  shared_ptr<ThermoPhase> tptr(ThermoFactory::factory()->create(model));
122  return tptr;
123 }
124 
125 shared_ptr<ThermoPhase> newThermo(const AnyMap& phaseNode, const AnyMap& rootNode)
126 {
127  if (!phaseNode.hasKey("kinetics") && phaseNode.hasKey("reactions")) {
128  throw InputFileError("newThermo", phaseNode["reactions"],
129  "Phase entry includes a 'reactions' field but does not "
130  "specify a kinetics model.");
131  }
132  string model = phaseNode["thermo"].asString();
133  shared_ptr<ThermoPhase> t = newThermoModel(model);
134  setupPhase(*t, phaseNode, rootNode);
135  return t;
136 }
137 
138 shared_ptr<ThermoPhase> newThermo(const string& infile, const string& id)
139 {
140  size_t dot = infile.find_last_of(".");
141  string extension;
142  extension = toLowerCopy(infile.substr(dot+1));
143  string id_ = id;
144  if (id == "-") {
145  id_ = "";
146  }
147  if (extension == "cti" || extension == "xml") {
148  throw CanteraError("newThermo",
149  "The CTI and XML formats are no longer supported.");
150  }
151 
152  AnyMap root = AnyMap::fromYamlFile(infile);
153  AnyMap& phase = root["phases"].getMapWhere("name", id_);
154  return newThermo(phase, root);
155 }
156 
157 void addDefaultElements(ThermoPhase& thermo, const vector<string>& element_names) {
158  for (const auto& symbol : element_names) {
159  thermo.addElement(symbol);
160  }
161 }
162 
163 void addElements(ThermoPhase& thermo, const vector<string>& element_names,
164  const AnyValue& elements, bool allow_default)
165 {
166  const auto& local_elements = elements.asMap("symbol");
167  for (const auto& symbol : element_names) {
168  if (local_elements.count(symbol)) {
169  auto& element = *local_elements.at(symbol);
170  double weight = element["atomic-weight"].asDouble();
171  long int number = element.getInt("atomic-number", 0);
172  double e298 = element.getDouble("entropy298", ENTROPY298_UNKNOWN);
173  thermo.addElement(symbol, weight, number, e298);
174  } else if (allow_default) {
175  thermo.addElement(symbol);
176  } else {
177  throw InputFileError("addElements", elements,
178  "Element '{}' not found", symbol);
179  }
180  }
181 }
182 
183 void addSpecies(ThermoPhase& thermo, const AnyValue& names, const AnyValue& species)
184 {
185  if (names.is<vector<string>>()) {
186  // 'names' is a list of species names which should be found in 'species'
187  const auto& species_nodes = species.asMap("name");
188  for (const auto& name : names.asVector<string>()) {
189  if (species_nodes.count(name)) {
190  thermo.addSpecies(newSpecies(*species_nodes.at(name)));
191  } else {
192  throw InputFileError("addSpecies", names, species,
193  "Could not find a species named '{}'.", name);
194  }
195  }
196  } else if (names == "all") {
197  // The keyword 'all' means to add all species from this source
198  for (const auto& item : species.asVector<AnyMap>()) {
199  thermo.addSpecies(newSpecies(item));
200  }
201  } else {
202  throw InputFileError("addSpecies", names,
203  "Could not parse species declaration of type '{}'", names.type_str());
204  }
205 }
206 
207 void setupPhase(ThermoPhase& thermo, const AnyMap& phaseNode, const AnyMap& rootNode)
208 {
209  thermo.setName(phaseNode["name"].asString());
210 
211  if (phaseNode.hasKey("deprecated")) {
212  string msg = phaseNode["deprecated"].asString();
213  string filename = phaseNode.getString("__file__",
214  rootNode.getString("__file__", "unknown file"));
215  string method = fmt::format("{}/{}", filename, phaseNode["name"].asString());
216  warn_deprecated(method, phaseNode, msg);
217  }
218 
219  // Add elements
220  if (phaseNode.hasKey("elements")) {
221  if (phaseNode.getBool("skip-undeclared-elements", false)) {
222  thermo.ignoreUndefinedElements();
223  } else {
224  thermo.throwUndefinedElements();
225  }
226 
227  if (phaseNode["elements"].is<vector<string>>()) {
228  // 'elements' is a list of element symbols
229  if (rootNode.hasKey("elements")) {
230  addElements(thermo, phaseNode["elements"].asVector<string>(),
231  rootNode["elements"], true);
232  } else {
233  addDefaultElements(thermo, phaseNode["elements"].asVector<string>());
234  }
235  } else if (phaseNode["elements"].is<vector<AnyMap>>()) {
236  // Each item in 'elements' is a map with one item, where the key is
237  // a section in this file or another YAML file, and the value is a
238  // list of element symbols to read from that section
239  for (const auto& elemNode : phaseNode["elements"].asVector<AnyMap>()) {
240  const string& source = elemNode.begin()->first;
241  const auto& names = elemNode.begin()->second.asVector<string>();
242  const auto& slash = boost::ifind_last(source, "/");
243  if (slash) {
244  string fileName(source.begin(), slash.begin());
245  string node(slash.end(), source.end());
246  const AnyMap elements = AnyMap::fromYamlFile(fileName,
247  rootNode.getString("__file__", ""));
248  addElements(thermo, names, elements.at(node), false);
249  } else if (rootNode.hasKey(source)) {
250  addElements(thermo, names, rootNode.at(source), false);
251  } else if (source == "default") {
252  addDefaultElements(thermo, names);
253  } else {
254  throw InputFileError("setupPhase", elemNode,
255  "Could not find elements section named '{}'", source);
256  }
257  }
258  } else {
259  throw InputFileError("setupPhase", phaseNode["elements"],
260  "Could not parse elements declaration of type '{}'",
261  phaseNode["elements"].type_str());
262  }
263  } else {
264  // If no elements list is provided, just add elements as-needed from the
265  // default list.
266  thermo.addUndefinedElements();
267  }
268 
269  // Add species
270  if (phaseNode.hasKey("species")) {
271  if (phaseNode["species"].is<vector<string>>()) {
272  // 'species' is a list of species names to be added from the current
273  // file's 'species' section
274  addSpecies(thermo, phaseNode["species"], rootNode["species"]);
275  } else if (phaseNode["species"].is<string>()) {
276  // 'species' is a keyword applicable to the current file's 'species'
277  // section
278  addSpecies(thermo, phaseNode["species"], rootNode["species"]);
279  } else if (phaseNode["species"].is<vector<AnyMap>>()) {
280  // Each item in 'species' is a map with one item, where the key is
281  // a section in this file or another YAML file, and the value is a
282  // list of species names to read from that section
283  for (const auto& speciesNode : phaseNode["species"].asVector<AnyMap>()) {
284  const string& source = speciesNode.begin()->first;
285  const auto& names = speciesNode.begin()->second;
286  const auto& slash = boost::ifind_last(source, "/");
287  if (slash) {
288  // source is a different input file
289  string fileName(source.begin(), slash.begin());
290  string node(slash.end(), source.end());
291  AnyMap species = AnyMap::fromYamlFile(fileName,
292  rootNode.getString("__file__", ""));
293  addSpecies(thermo, names, species[node]);
294  } else if (rootNode.hasKey(source)) {
295  // source is in the current file
296  addSpecies(thermo, names, rootNode[source]);
297  } else {
298  throw InputFileError("setupPhase", speciesNode,
299  "Could not find species section named '{}'", source);
300  }
301  }
302  } else {
303  throw InputFileError("setupPhase", phaseNode["species"],
304  "Could not parse species declaration of type '{}'",
305  phaseNode["species"].type_str());
306  }
307  } else if (rootNode.hasKey("species")) {
308  // By default, add all species from the 'species' section
309  addSpecies(thermo, AnyValue("all"), rootNode["species"]);
310  }
311 
312  auto* vpssThermo = dynamic_cast<VPStandardStateTP*>(&thermo);
313  if (vpssThermo) {
314  for (size_t k = 0; k < thermo.nSpecies(); k++) {
315  unique_ptr<PDSS> pdss;
316  if (!thermo.species(k)->input.hasKey("equation-of-state")) {
317  throw InputFileError("setupPhase", thermo.species(k)->input,
318  "Species '{}' in use by a ThermoPhase model of type '{}'\n"
319  "must define an 'equation-of-state' field.",
320  thermo.speciesName(k), thermo.type());
321  }
322  // Use the first node which specifies a valid PDSS model
323  auto& eos = thermo.species(k)->input["equation-of-state"];
324  bool found = false;
325  for (auto& node : eos.asVector<AnyMap>()) {
326  string model = node["model"].asString();
327  if (PDSSFactory::factory()->exists(model)) {
328  pdss.reset(newPDSS(model));
329  pdss->setParameters(node);
330  found = true;
331  break;
332  }
333  }
334  if (!found) {
335  throw InputFileError("setupPhase", eos,
336  "Could not find an equation-of-state specification "
337  "which defines a known PDSS model.");
338  }
339  vpssThermo->installPDSS(k, std::move(pdss));
340  }
341  }
342 
343  thermo.setParameters(phaseNode, rootNode);
344  thermo.initThermo();
345 
346  if (phaseNode.hasKey("state")) {
347  auto node = phaseNode["state"].as<AnyMap>();
348  thermo.setState(node);
349  } else {
350  thermo.setState_TP(298.15, OneAtm);
351  }
352 }
353 
354 }
Header file for an binary solution model with tabulated standard state thermodynamic data (see Thermo...
Header for a thermodynamics model of a coverage-dependent surface phase derived from SurfPhase,...
Headers for the DebyeHuckel ThermoPhase object, which models dilute electrolyte solutions (see Thermo...
Declarations for the EdgePhase ThermoPhase object, which models the interface between two surfaces (s...
#define ENTROPY298_UNKNOWN
Number indicating we don't know the entropy of the element in its most stable state at 298....
Definition: Elements.h:85
Headers for the HMWSoln ThermoPhase object, which models concentrated electrolyte solutions (see Ther...
ThermoPhase object for the ideal gas equation of state - workhorse for Cantera (see Thermodynamic Pro...
ThermoPhase object for the ideal molal equation of state (see Thermodynamic Properties and class Idea...
Header file for an ideal solid solution model with incompressible thermodynamics (see Thermodynamic P...
Definition file for a derived class of ThermoPhase that assumes an ideal solution approximation and h...
Header for a simple thermodynamics model of a bulk phase derived from ThermoPhase,...
Header for a simple thermodynamics model of a bulk solid phase derived from ThermoPhase,...
(see Thermodynamic Properties and class MargulesVPSSTP).
Header for a general species thermodynamic property manager for a phase (see MultiSpeciesThermo).
Header file for class PlasmaPhase.
Header for a ThermoPhase class for a pure fluid phase consisting of gas, liquid, mixed-gas-liquid and...
(see Thermodynamic Properties and class RedlichKisterVPSSTP).
Header for factory functions to build instances of classes that manage the standard-state thermodynam...
Declaration for class Cantera::Species.
Header file for the StoichSubstance class, which represents a fixed-composition incompressible substa...
Header for a simple thermodynamics model of a surface phase derived from ThermoPhase,...
Headers for the factory class that can create known ThermoPhase objects (see Thermodynamic Properties...
Declares a ThermoPhase class consisting of pure water (see Thermodynamic Properties and class WaterSS...
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
bool getBool(const string &key, bool default_) const
If key exists, return it as a bool, otherwise return default_.
Definition: AnyMap.cpp:1515
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
static AnyMap fromYamlFile(const string &name, const string &parent_name="")
Create an AnyMap from a YAML file.
Definition: AnyMap.cpp:1771
const AnyValue & at(const string &key) const
Get the value of the item stored in key.
Definition: AnyMap.cpp:1408
A wrapper for a variable whose type is determined at runtime.
Definition: AnyMap.h:86
Overloads the virtual methods of class IdealSolidSolnPhase to implement tabulated standard state ther...
Base class for exceptions thrown by Cantera classes.
Definition: ctexceptions.h:66
A thermodynamic model for a coverage-dependent surface phase, applying surface species lateral intera...
Class DebyeHuckel represents a dilute liquid electrolyte phase which obeys the Debye Huckel formulati...
Definition: DebyeHuckel.h:415
A thermodynamic phase representing a one dimensional edge between two surfaces.
Definition: EdgePhase.h:31
void reg(const string &name, function< ThermoPhase *(Args...)> f)
Register a new object construction function.
Definition: FactoryBase.h:80
void addDeprecatedAlias(const string &original, const string &alias)
Add a deprecated alias for an existing registered type.
Definition: FactoryBase.h:116
Class HMWSoln represents a dilute or concentrated liquid electrolyte phase which obeys the Pitzer for...
Definition: HMWSoln.h:778
Class IdealGasPhase represents low-density gases that obey the ideal gas equation of state.
This phase is based upon the mixing-rule assumption that all molality-based activity coefficients are...
Class IdealSolidSolnPhase represents a condensed phase ideal solution compound.
An ideal solution approximation of a phase.
Error thrown for problems processing information contained in an AnyMap or AnyValue.
Definition: AnyMap.h:738
A simple thermodynamic model for a bulk phase, assuming a lattice of solid atoms.
Definition: LatticePhase.h:184
A phase that is comprised of a fixed additive combination of other lattice phases.
MargulesVPSSTP is a derived class of GibbsExcessVPSSTP that employs the Margules approximation for th...
Class MetalPhase represents electrons in a metal.
Definition: MetalPhase.h:22
Implementation of a multi-species Peng-Robinson equation of state.
Definition: PengRobinson.h:20
size_t nSpecies() const
Returns the number of species in the phase.
Definition: Phase.h:231
void ignoreUndefinedElements()
Set behavior when adding a species containing undefined elements to just skip the species.
Definition: Phase.cpp:873
void addUndefinedElements()
Set behavior when adding a species containing undefined elements to add those elements to the phase.
Definition: Phase.cpp:877
string speciesName(size_t k) const
Name of the species with index k.
Definition: Phase.cpp:142
shared_ptr< Species > species(const string &name) const
Return the Species object for the named species.
Definition: Phase.cpp:856
void throwUndefinedElements()
Set the behavior when adding a species containing undefined elements to throw an exception.
Definition: Phase.cpp:881
void setName(const string &nm)
Sets the string name for the phase.
Definition: Phase.cpp:25
Base class for a phase with plasma properties.
Definition: PlasmaPhase.h:58
This phase object consists of a single component that can be a gas, a liquid, a mixed gas-liquid flui...
RedlichKisterVPSSTP is a derived class of GibbsExcessVPSSTP that employs the Redlich-Kister approxima...
Implementation of a multi-species Redlich-Kwong equation of state.
Class StoichSubstance represents a stoichiometric (fixed composition) incompressible substance.
A simple thermodynamic model for a surface phase, assuming an ideal solution model.
Definition: SurfPhase.h:98
Factory class for thermodynamic property managers.
Definition: ThermoFactory.h:31
void deleteFactory() override
delete the static instance of this factory
ThermoFactory()
Private constructors prevents usage.
static ThermoFactory * factory()
Static function that creates a static instance of the factory.
static std::mutex thermo_mutex
Decl for locking mutex for thermo factory singleton.
Definition: ThermoFactory.h:47
static ThermoFactory * s_factory
static member of a single instance
Definition: ThermoFactory.h:41
Base class for a phase with thermodynamic properties.
Definition: ThermoPhase.h:390
virtual void setParameters(const AnyMap &phaseNode, const AnyMap &rootNode=AnyMap())
Set equation of state parameters from an AnyMap phase description.
virtual void setState_TP(double t, double p)
Set the temperature (K) and pressure (Pa)
virtual void setState(const AnyMap &state)
Set the state using an AnyMap containing any combination of properties supported by the thermodynamic...
string type() const override
String indicating the thermodynamic model implemented.
Definition: ThermoPhase.h:399
virtual void initThermo()
Initialize the ThermoPhase object after all species have been set up.
This is a filter class for ThermoPhase that implements some preparatory steps for efficiently handlin...
Class for single-component water.
Definition: WaterSSTP.h:69
string toLowerCopy(const string &input)
Convert to lower case.
double dot(InputIter x_begin, InputIter x_end, InputIter2 y_begin)
Function that calculates a templated inner product.
Definition: utilities.h:82
const double OneAtm
One atmosphere [Pa].
Definition: ct_defs.h:96
shared_ptr< ThermoPhase > newThermo(const AnyMap &phaseNode, const AnyMap &rootNode)
Create a new ThermoPhase object and initialize it.
void setupPhase(ThermoPhase &thermo, const AnyMap &phaseNode, const AnyMap &rootNode)
Initialize a ThermoPhase object.
shared_ptr< ThermoPhase > newThermoModel(const string &model)
Create a new ThermoPhase instance.
Namespace for the Cantera kernel.
Definition: AnyMap.cpp:564
unique_ptr< Species > newSpecies(const AnyMap &node)
Create a new Species object from an AnyMap specification.
Definition: Species.cpp:105
void warn_deprecated(const string &source, const AnyBase &node, const string &message)
A deprecation warning for syntax in an input file.
Definition: AnyMap.cpp:1926
Contains const definitions for types of species reference-state thermodynamics managers (see Species ...
Contains declarations for string manipulation functions within Cantera.