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