1 /**
2 * @file LatticePhase.cpp
3 * Definitions for a simple thermodynamics model of a bulk phase
4 * derived from ThermoPhase,
5 * assuming a lattice of solid atoms
6 * (see \ref thermoprops and class \link Cantera::LatticePhase LatticePhase\endlink).
7 */
8
9 // This file is part of Cantera. See License.txt in the top-level directory or
10 // at https://cantera.org/license.txt for license and copyright information.
11
12 #include "cantera/thermo/LatticePhase.h"
13 #include "cantera/thermo/ThermoFactory.h"
14 #include "cantera/thermo/Species.h"
15 #include "cantera/base/stringUtils.h"
16 #include "cantera/base/ctml.h"
17 #include "cantera/base/utilities.h"
18
19 namespace Cantera
20 {
21
LatticePhase(const std::string & inputFile,const std::string & id_)22 LatticePhase::LatticePhase(const std::string& inputFile, const std::string& id_)
23 : m_Pref(OneAtm)
24 , m_Pcurrent(OneAtm)
25 , m_site_density(0.0)
26 {
27 initThermoFile(inputFile, id_);
28 }
29
LatticePhase(XML_Node & phaseRef,const std::string & id_)30 LatticePhase::LatticePhase(XML_Node& phaseRef, const std::string& id_)
31 {
32 importPhase(phaseRef, this);
33 }
34
enthalpy_mole() const35 doublereal LatticePhase::enthalpy_mole() const
36 {
37 return RT() * mean_X(enthalpy_RT_ref()) +
38 (pressure() - m_Pref)/molarDensity();
39 }
40
entropy_mole() const41 doublereal LatticePhase::entropy_mole() const
42 {
43 return GasConstant * (mean_X(entropy_R_ref()) - sum_xlogx());
44 }
45
cp_mole() const46 doublereal LatticePhase::cp_mole() const
47 {
48 return GasConstant * mean_X(cp_R_ref());
49 }
50
cv_mole() const51 doublereal LatticePhase::cv_mole() const
52 {
53 return cp_mole();
54 }
55
calcDensity()56 doublereal LatticePhase::calcDensity()
57 {
58 assignDensity(std::max(meanMolecularWeight() * m_site_density, SmallNumber));
59 return meanMolecularWeight() * m_site_density;
60 }
61
setPressure(doublereal p)62 void LatticePhase::setPressure(doublereal p)
63 {
64 m_Pcurrent = p;
65 calcDensity();
66 }
67
compositionChanged()68 void LatticePhase::compositionChanged()
69 {
70 Phase::compositionChanged();
71 calcDensity();
72 }
73
standardConcentrationUnits() const74 Units LatticePhase::standardConcentrationUnits() const
75 {
76 return Units(1.0);
77 }
78
getActivityConcentrations(doublereal * c) const79 void LatticePhase::getActivityConcentrations(doublereal* c) const
80 {
81 getMoleFractions(c);
82 }
83
getActivityCoefficients(doublereal * ac) const84 void LatticePhase::getActivityCoefficients(doublereal* ac) const
85 {
86 for (size_t k = 0; k < m_kk; k++) {
87 ac[k] = 1.0;
88 }
89 }
90
standardConcentration(size_t k) const91 doublereal LatticePhase::standardConcentration(size_t k) const
92 {
93 return 1.0;
94 }
95
logStandardConc(size_t k) const96 doublereal LatticePhase::logStandardConc(size_t k) const
97 {
98 return 0.0;
99 }
100
getChemPotentials(doublereal * mu) const101 void LatticePhase::getChemPotentials(doublereal* mu) const
102 {
103 doublereal delta_p = m_Pcurrent - m_Pref;
104 const vector_fp& g_RT = gibbs_RT_ref();
105 for (size_t k = 0; k < m_kk; k++) {
106 double xx = std::max(SmallNumber, moleFraction(k));
107 mu[k] = RT() * (g_RT[k] + log(xx))
108 + delta_p * m_speciesMolarVolume[k];
109 }
110 }
111
getPartialMolarEnthalpies(doublereal * hbar) const112 void LatticePhase::getPartialMolarEnthalpies(doublereal* hbar) const
113 {
114 const vector_fp& _h = enthalpy_RT_ref();
115 scale(_h.begin(), _h.end(), hbar, RT());
116 }
117
getPartialMolarEntropies(doublereal * sbar) const118 void LatticePhase::getPartialMolarEntropies(doublereal* sbar) const
119 {
120 const vector_fp& _s = entropy_R_ref();
121 for (size_t k = 0; k < m_kk; k++) {
122 double xx = std::max(SmallNumber, moleFraction(k));
123 sbar[k] = GasConstant * (_s[k] - log(xx));
124 }
125 }
126
getPartialMolarCp(doublereal * cpbar) const127 void LatticePhase::getPartialMolarCp(doublereal* cpbar) const
128 {
129 getCp_R(cpbar);
130 for (size_t k = 0; k < m_kk; k++) {
131 cpbar[k] *= GasConstant;
132 }
133 }
134
getPartialMolarVolumes(doublereal * vbar) const135 void LatticePhase::getPartialMolarVolumes(doublereal* vbar) const
136 {
137 getStandardVolumes(vbar);
138 }
139
getStandardChemPotentials(doublereal * mu0) const140 void LatticePhase::getStandardChemPotentials(doublereal* mu0) const
141 {
142 const vector_fp& gibbsrt = gibbs_RT_ref();
143 scale(gibbsrt.begin(), gibbsrt.end(), mu0, RT());
144 }
145
getPureGibbs(doublereal * gpure) const146 void LatticePhase::getPureGibbs(doublereal* gpure) const
147 {
148 const vector_fp& gibbsrt = gibbs_RT_ref();
149 doublereal delta_p = (m_Pcurrent - m_Pref);
150 for (size_t k = 0; k < m_kk; k++) {
151 gpure[k] = RT() * gibbsrt[k] + delta_p * m_speciesMolarVolume[k];
152 }
153 }
154
getEnthalpy_RT(doublereal * hrt) const155 void LatticePhase::getEnthalpy_RT(doublereal* hrt) const
156 {
157 const vector_fp& _h = enthalpy_RT_ref();
158 doublereal delta_prt = (m_Pcurrent - m_Pref) / RT();
159 for (size_t k = 0; k < m_kk; k++) {
160 hrt[k] = _h[k] + delta_prt * m_speciesMolarVolume[k];
161 }
162 }
163
getEntropy_R(doublereal * sr) const164 void LatticePhase::getEntropy_R(doublereal* sr) const
165 {
166 const vector_fp& _s = entropy_R_ref();
167 std::copy(_s.begin(), _s.end(), sr);
168 }
169
getGibbs_RT(doublereal * grt) const170 void LatticePhase::getGibbs_RT(doublereal* grt) const
171 {
172 const vector_fp& gibbsrt = gibbs_RT_ref();
173 doublereal delta_prt = (m_Pcurrent - m_Pref) / RT();
174 for (size_t k = 0; k < m_kk; k++) {
175 grt[k] = gibbsrt[k] + delta_prt * m_speciesMolarVolume[k];
176 }
177 }
178
getGibbs_ref(doublereal * g) const179 void LatticePhase::getGibbs_ref(doublereal* g) const
180 {
181 getGibbs_RT_ref(g);
182 for (size_t k = 0; k < m_kk; k++) {
183 g[k] *= RT();
184 }
185 }
186
getCp_R(doublereal * cpr) const187 void LatticePhase::getCp_R(doublereal* cpr) const
188 {
189 const vector_fp& _cpr = cp_R_ref();
190 std::copy(_cpr.begin(), _cpr.end(), cpr);
191 }
192
getStandardVolumes(doublereal * vbar) const193 void LatticePhase::getStandardVolumes(doublereal* vbar) const
194 {
195 copy(m_speciesMolarVolume.begin(), m_speciesMolarVolume.end(), vbar);
196 }
197
enthalpy_RT_ref() const198 const vector_fp& LatticePhase::enthalpy_RT_ref() const
199 {
200 _updateThermo();
201 return m_h0_RT;
202 }
203
gibbs_RT_ref() const204 const vector_fp& LatticePhase::gibbs_RT_ref() const
205 {
206 _updateThermo();
207 return m_g0_RT;
208 }
209
getGibbs_RT_ref(doublereal * grt) const210 void LatticePhase::getGibbs_RT_ref(doublereal* grt) const
211 {
212 _updateThermo();
213 for (size_t k = 0; k < m_kk; k++) {
214 grt[k] = m_g0_RT[k];
215 }
216 }
217
entropy_R_ref() const218 const vector_fp& LatticePhase::entropy_R_ref() const
219 {
220 _updateThermo();
221 return m_s0_R;
222 }
223
cp_R_ref() const224 const vector_fp& LatticePhase::cp_R_ref() const
225 {
226 _updateThermo();
227 return m_cp0_R;
228 }
229
addSpecies(shared_ptr<Species> spec)230 bool LatticePhase::addSpecies(shared_ptr<Species> spec)
231 {
232 bool added = ThermoPhase::addSpecies(spec);
233 if (added) {
234 if (m_kk == 1) {
235 m_Pref = refPressure();
236 }
237 m_h0_RT.push_back(0.0);
238 m_g0_RT.push_back(0.0);
239 m_cp0_R.push_back(0.0);
240 m_s0_R.push_back(0.0);
241 double mv = 1.0 / m_site_density;
242 if (spec->input.hasKey("equation-of-state")) {
243 auto& eos = spec->input["equation-of-state"].getMapWhere(
244 "model", "constant-volume");
245 if (eos.hasKey("density")) {
246 mv = molecularWeight(m_kk-1) / eos.convert("density", "kg/m^3");
247 } else if (eos.hasKey("molar-density")) {
248 mv = 1.0 / eos.convert("molar-density", "kmol/m^3");
249 } else if (eos.hasKey("molar-volume")) {
250 mv = eos.convert("molar-volume", "m^3/kmol");
251 }
252 } else if (spec->input.hasKey("molar_volume")) {
253 // @Deprecated - remove this case for Cantera 3.0 with removal of the XML format
254 mv = spec->input["molar_volume"].asDouble();
255 }
256 m_speciesMolarVolume.push_back(mv);
257 }
258 return added;
259 }
260
setSiteDensity(double sitedens)261 void LatticePhase::setSiteDensity(double sitedens)
262 {
263 m_site_density = sitedens;
264 for (size_t k = 0; k < m_kk; k++) {
265 if (species(k)->input.hasKey("molar_volume")) {
266 // @Deprecated - remove this case for Cantera 3.0 with removal of the XML format
267 continue;
268 } else if (species(k)->input.hasKey("equation-of-state")) {
269 auto& eos = species(k)->input["equation-of-state"].getMapWhere(
270 "model", "constant-volume");
271 if (eos.hasKey("molar-volume") || eos.hasKey("density")
272 || eos.hasKey("molar-density")) {
273 continue;
274 }
275 }
276 m_speciesMolarVolume[k] = 1.0 / m_site_density;
277 }
278 }
279
_updateThermo() const280 void LatticePhase::_updateThermo() const
281 {
282 doublereal tnow = temperature();
283 if (m_tlast != tnow) {
284 m_spthermo.update(tnow, &m_cp0_R[0], &m_h0_RT[0], &m_s0_R[0]);
285 m_tlast = tnow;
286 for (size_t k = 0; k < m_kk; k++) {
287 m_g0_RT[k] = m_h0_RT[k] - m_s0_R[k];
288 }
289 m_tlast = tnow;
290 }
291 }
292
initThermo()293 void LatticePhase::initThermo()
294 {
295 if (m_input.hasKey("site-density")) {
296 setSiteDensity(m_input.convert("site-density", "kmol/m^3"));
297 }
298 }
299
getParameters(AnyMap & phaseNode) const300 void LatticePhase::getParameters(AnyMap& phaseNode) const
301 {
302 ThermoPhase::getParameters(phaseNode);
303 phaseNode["site-density"].setQuantity(m_site_density, "kmol/m^3");
304 }
305
getSpeciesParameters(const std::string & name,AnyMap & speciesNode) const306 void LatticePhase::getSpeciesParameters(const std::string& name,
307 AnyMap& speciesNode) const
308 {
309 ThermoPhase::getSpeciesParameters(name, speciesNode);
310 size_t k = speciesIndex(name);
311 // Output volume information in a form consistent with the input
312 const auto S = species(k);
313 if (S->input.hasKey("equation-of-state")) {
314 auto& eosIn = S->input["equation-of-state"].getMapWhere(
315 "model", "constant-volume");
316 auto& eosOut = speciesNode["equation-of-state"].getMapWhere(
317 "model", "constant-volume", true);
318
319 if (eosIn.hasKey("density")) {
320 eosOut["model"] = "constant-volume";
321 eosOut["density"].setQuantity(
322 molecularWeight(k) / m_speciesMolarVolume[k], "kg/m^3");
323 } else if (eosIn.hasKey("molar-density")) {
324 eosOut["model"] = "constant-volume";
325 eosOut["molar-density"].setQuantity(1.0 / m_speciesMolarVolume[k],
326 "kmol/m^3");
327 } else if (eosIn.hasKey("molar-volume")) {
328 eosOut["model"] = "constant-volume";
329 eosOut["molar-volume"].setQuantity(m_speciesMolarVolume[k],
330 "m^3/kmol");
331 }
332 } else if (S->input.hasKey("molar_volume")) {
333 // Species came from XML
334 auto& eosOut = speciesNode["equation-of-state"].getMapWhere(
335 "model", "constant-volume", true);
336 eosOut["model"] = "constant-volume";
337 eosOut["molar-volume"].setQuantity(m_speciesMolarVolume[k], "m^3/kmol");
338 }
339 // Otherwise, species volume is determined by the phase-level site density
340 }
341
342
setParametersFromXML(const XML_Node & eosdata)343 void LatticePhase::setParametersFromXML(const XML_Node& eosdata)
344 {
345 eosdata._require("model", "Lattice");
346 setSiteDensity(getFloat(eosdata, "site_density", "toSI"));
347 }
348
349 }
350