Cantera  2.0
MineralEQ3.cpp
Go to the documentation of this file.
1 /**
2  * @file MineralEQ3.cpp
3  * Definition file for the MineralEQ3 class, which represents a fixed-composition
4  * incompressible substance (see \ref thermoprops and
5  * class \link Cantera::MineralEQ3 MineralEQ3\endlink)
6  */
7 
8 /*
9  * Copyright (2005) Sandia Corporation. Under the terms of
10  * Contract DE-AC04-94AL85000 with Sandia Corporation, the
11  * U.S. Government retains certain rights in this software.
12  *
13  * Copyright 2001 California Institute of Technology
14  */
15 #include "cantera/base/ct_defs.h"
16 #include "cantera/thermo/mix_defs.h"
19 
22 
23 #include <string>
24 
25 using namespace std;
26 
27 namespace Cantera
28 {
29 
30 /*
31  * ---- Constructors -------
32  */
33 
34 /*
35  * Default Constructor for the MineralEQ3 class
36  */
37 MineralEQ3::MineralEQ3():
39 {
40 }
41 
42 // Create and initialize a MineralEQ3 ThermoPhase object
43 // from an ASCII input file
44 /*
45  * @param infile name of the input file
46  * @param id name of the phase id in the file.
47  * If this is blank, the first phase in the file is used.
48  */
49 MineralEQ3::MineralEQ3(std::string infile, std::string id) :
51 {
52  XML_Node* root = get_XML_File(infile);
53  if (id == "-") {
54  id = "";
55  }
56  XML_Node* xphase = get_XML_NameID("phase", std::string("#")+id, root);
57  if (!xphase) {
58  throw CanteraError("MineralEQ3::MineralEQ3",
59  "Couldn't find phase name in file:" + id);
60  }
61  // Check the model name to ensure we have compatibility
62  const XML_Node& th = xphase->child("thermo");
63  std::string model = th["model"];
64  if (model != "StoichSubstance" && model != "MineralEQ3") {
65  throw CanteraError("MineralEQ3::MineralEQ3",
66  "thermo model attribute must be StoichSubstance");
67  }
68  importPhase(*xphase, this);
69 }
70 
71 // Full Constructor.
72 /*
73  * @param phaseRef XML node pointing to a MineralEQ3 description
74  * @param id Id of the phase.
75  */
76 MineralEQ3::MineralEQ3(XML_Node& xmlphase, std::string id) :
78 {
79  if (id != "") {
80  std::string idxml = xmlphase["id"];
81  if (id != idxml) {
82  throw CanteraError("MineralEQ3::MineralEQ3",
83  "id's don't match");
84  }
85  }
86  const XML_Node& th = xmlphase.child("thermo");
87  std::string model = th["model"];
88  if (model != "StoichSubstance" && model != "MineralEQ3") {
89  throw CanteraError("MineralEQ3::MineralEQ3",
90  "thermo model attribute must be StoichSubstance");
91  }
92  importPhase(xmlphase, this);
93 }
94 
95 //! Copy constructor
96 /*!
97  * @param right Object to be copied
98  */
101 {
102  *this = operator=(right);
103 }
104 
105 //! Assignment operator
106 /*!
107  * @param right Object to be copied
108  */
109 MineralEQ3&
111 {
112  if (&right == this) {
113  return *this;
114  }
116  m_Mu0_pr_tr = right.m_Mu0_pr_tr;
120  m_V0_pr_tr = right.m_V0_pr_tr;
121  m_a = right.m_a;
122  m_b = right.m_b;
123  m_c = right.m_c;
124 
125  return *this;
126 }
127 
128 /*
129  * Destructor for the routine (virtual)
130  *
131  */
133 {
134 }
135 
136 // Duplication function
137 /*
138  * This virtual function is used to create a duplicate of the
139  * current phase. It's used to duplicate the phase when given
140  * a ThermoPhase pointer to the phase.
141  *
142  * @return It returns a ThermoPhase pointer.
143  */
145 {
146  MineralEQ3* stp = new MineralEQ3(*this);
147  return (ThermoPhase*) stp;
148 }
149 
150 
151 /*
152  * ---- Utilities -----
153  */
154 
155 /*
156  * Equation of state flag. Returns the value cStoichSubstance,
157  * defined in mix_defs.h.
158  */
160 {
161  return cStoichSubstance;
162 }
163 
164 /*
165  * ---- Molar Thermodynamic properties of the solution ----
166  */
167 
168 /**
169  * ----- Mechanical Equation of State ------
170  */
171 
172 /*
173  * Pressure. Units: Pa.
174  * For an incompressible substance, the density is independent
175  * of pressure. This method simply returns the stored
176  * pressure value.
177  */
178 doublereal MineralEQ3::pressure() const
179 {
180  return m_press;
181 }
182 
183 /*
184  * Set the pressure at constant temperature. Units: Pa.
185  * For an incompressible substance, the density is
186  * independent of pressure. Therefore, this method only
187  * stores the specified pressure value. It does not
188  * modify the density.
189  */
190 void MineralEQ3::setPressure(doublereal p)
191 {
192  m_press = p;
193 }
194 
195 /*
196  * The isothermal compressibility. Units: 1/Pa.
197  * The isothermal compressibility is defined as
198  * \f[
199  * \kappa_T = -\frac{1}{v}\left(\frac{\partial v}{\partial P}\right)_T
200  * \f]
201  *
202  * It's equal to zero for this model, since the molar volume
203  * doesn't change with pressure or temperature.
204  */
206 {
207  return 0.0;
208 }
209 
210 /*
211  * The thermal expansion coefficient. Units: 1/K.
212  * The thermal expansion coefficient is defined as
213  *
214  * \f[
215  * \beta = \frac{1}{v}\left(\frac{\partial v}{\partial T}\right)_P
216  * \f]
217  *
218  * It's equal to zero for this model, since the molar volume
219  * doesn't change with pressure or temperature.
220  */
222 {
223  return 0.0;
224 }
225 
226 /*
227  * ---- Chemical Potentials and Activities ----
228  */
229 
230 /*
231  * This method returns the array of generalized
232  * concentrations. For a stoichiometric substance, there is
233  * only one species, and the generalized concentration is 1.0.
234  */
235 void MineralEQ3::
236 getActivityConcentrations(doublereal* c) const
237 {
238  c[0] = 1.0;
239 }
240 
241 /*
242  * The standard concentration. This is defined as the concentration
243  * by which the generalized concentration is normalized to produce
244  * the activity.
245  */
246 doublereal MineralEQ3::standardConcentration(size_t k) const
247 {
248  return 1.0;
249 }
250 
251 /*
252  * Returns the natural logarithm of the standard
253  * concentration of the kth species
254  */
255 doublereal MineralEQ3::logStandardConc(size_t k) const
256 {
257  return 0.0;
258 }
259 
260 /*
261  * Returns the units of the standard and generalized
262  * concentrations Note they have the same units, as their
263  * ratio is defined to be equal to the activity of the kth
264  * species in the solution, which is unitless.
265  *
266  * This routine is used in print out applications where the
267  * units are needed. Usually, MKS units are assumed throughout
268  * the program and in the XML input files.
269  *
270  * uA[0] = kmol units - default = 1
271  * uA[1] = m units - default = -nDim(), the number of spatial
272  * dimensions in the Phase class.
273  * uA[2] = kg units - default = 0;
274  * uA[3] = Pa(pressure) units - default = 0;
275  * uA[4] = Temperature units - default = 0;
276  * uA[5] = time units - default = 0
277  */
278 void MineralEQ3::
279 getUnitsStandardConc(doublereal* uA, int k, int sizeUA) const
280 {
281  for (int i = 0; i < 6; i++) {
282  uA[i] = 0;
283  }
284 }
285 
286 /*
287  * ---- Partial Molar Properties of the Solution ----
288  */
289 
290 
291 
292 /*
293  * ---- Properties of the Standard State of the Species in the Solution
294  * ----
295  */
296 
297 /*
298  * Get the array of chemical potentials at unit activity
299  * \f$ \mu^0_k \f$.
300  *
301  * For a stoichiometric substance, there is no activity term in
302  * the chemical potential expression, and therefore the
303  * standard chemical potential and the chemical potential
304  * are both equal to the molar Gibbs function.
305  */
306 void MineralEQ3::
307 getStandardChemPotentials(doublereal* mu0) const
308 {
309  getGibbs_RT(mu0);
310  mu0[0] *= GasConstant * temperature();
311 }
312 
313 /*
314  * Get the nondimensional Enthalpy functions for the species
315  * at their standard states at the current
316  * <I>T</I> and <I>P</I> of the solution.
317  * Molar enthalpy. Units: J/kmol. For an incompressible,
318  * stoichiometric substance, the internal energy is
319  * independent of pressure, and therefore the molar enthalpy
320  * is \f[ \hat h(T, P) = \hat u(T) + P \hat v \f], where the
321  * molar specific volume is constant.
322  */
323 void MineralEQ3::getEnthalpy_RT(doublereal* hrt) const
324 {
325  getEnthalpy_RT_ref(hrt);
326  doublereal RT = GasConstant * temperature();
327  doublereal presCorrect = (m_press - m_p0) / molarDensity();
328  hrt[0] += presCorrect / RT;
329 }
330 
331 /*
332  * Get the array of nondimensional Entropy functions for the
333  * standard state species
334  * at the current <I>T</I> and <I>P</I> of the solution.
335  */
336 void MineralEQ3::getEntropy_R(doublereal* sr) const
337 {
338  getEntropy_R_ref(sr);
339 }
340 
341 /*
342  * Get the nondimensional Gibbs functions for the species
343  * at their standard states of solution at the current T and P
344  * of the solution
345  */
346 void MineralEQ3::getGibbs_RT(doublereal* grt) const
347 {
348  getEnthalpy_RT(grt);
349  grt[0] -= m_s0_R[0];
350 }
351 
352 /*
353  * Get the nondimensional Gibbs functions for the standard
354  * state of the species at the current T and P.
355  */
356 void MineralEQ3::getCp_R(doublereal* cpr) const
357 {
358  _updateThermo();
359  cpr[0] = m_cp0_R[0];
360 }
361 
362 /*
363  * Molar internal energy (J/kmol).
364  * For an incompressible,
365  * stoichiometric substance, the molar internal energy is
366  * independent of pressure. Since the thermodynamic properties
367  * are specified by giving the standard-state enthalpy, the
368  * term \f$ P_0 \hat v\f$ is subtracted from the specified molar
369  * enthalpy to compute the molar internal energy.
370  */
371 void MineralEQ3::getIntEnergy_RT(doublereal* urt) const
372 {
373  _updateThermo();
374  doublereal RT = GasConstant * temperature();
375  doublereal PV = m_p0 / molarDensity();
376  urt[0] = m_h0_RT[0] - PV / RT;
377 }
378 
379 /*
380  * ---- Thermodynamic Values for the Species Reference States ----
381  */
382 /*
383  * Molar internal energy or the reference state at the current
384  * temperature, T (J/kmol).
385  * For an incompressible,
386  * stoichiometric substance, the molar internal energy is
387  * independent of pressure. Since the thermodynamic properties
388  * are specified by giving the standard-state enthalpy, the
389  * term \f$ P_0 \hat v\f$ is subtracted from the specified molar
390  * enthalpy to compute the molar internal energy.
391  *
392  * Note, this is equal to the standard state internal energy
393  * evaluated at the reference pressure.
394  */
395 void MineralEQ3::getIntEnergy_RT_ref(doublereal* urt) const
396 {
397  _updateThermo();
398  doublereal RT = GasConstant * temperature();
399  doublereal PV = m_p0 / molarDensity();
400  urt[0] = m_h0_RT[0] - PV / RT;
401 }
402 
403 /*
404  * ---- Saturation Properties
405  */
406 
407 
408 
409 /*
410  * ---- Initialization and Internal functions
411  */
412 
413 /**
414  * @internal Initialize. This method is provided to allow
415  * subclasses to perform any initialization required after all
416  * species have been added. For example, it might be used to
417  * resize internal work arrays that must have an entry for
418  * each species. The base class implementation does nothing,
419  * and subclasses that do not require initialization do not
420  * need to overload this method. When importing a CTML phase
421  * description, this method is called just prior to returning
422  * from function importPhase.
423  *
424  * @see importCTML.cpp
425  */
427 {
428 
429  /*
430  * Call the base class thermo initializer
431  */
433 }
434 
435 /**
436  * setParameters:
437  *
438  * Generic routine that is used to set the parameters used
439  * by this model.
440  * C[0] = density of phase [ kg/m3 ]
441  */
442 void MineralEQ3::setParameters(int n, doublereal* const c)
443 {
444  doublereal rho = c[0];
445  setDensity(rho);
446 }
447 
448 /**
449  * getParameters:
450  *
451  * Generic routine that is used to get the parameters used
452  * by this model.
453  * n = 1
454  * C[0] = density of phase [ kg/m3 ]
455  */
456 void MineralEQ3::getParameters(int& n, doublereal* const c) const
457 {
458  doublereal rho = density();
459  n = 1;
460  c[0] = rho;
461 }
462 
463 // Initialize the phase parameters from an XML file.
464 /*
465  * initThermoXML() (virtual from ThermoPhase)
466  *
467  * This gets called from importPhase(). It processes the XML file
468  * after the species are set up. This is the main routine for
469  * reading in activity coefficient parameters.
470  *
471  * @param phaseNode This object must be the phase node of a
472  * complete XML tree
473  * description of the phase, including all of the
474  * species data. In other words while "phase" must
475  * point to an XML phase object, it must have
476  * sibling nodes "speciesData" that describe
477  * the species in the phase.
478  * @param id ID of the phase. If nonnull, a check is done
479  * to see if phaseNode is pointing to the phase
480  * with the correct id.
481  */
482 void MineralEQ3::initThermoXML(XML_Node& phaseNode, std::string id)
483 {
484  /*
485  * Find the Thermo XML node
486  */
487  if (!phaseNode.hasChild("thermo")) {
488  throw CanteraError("HMWSoln::initThermoXML",
489  "no thermo XML node");
490  }
491 
492  std::vector<const XML_Node*> xspecies = speciesData();
493  const XML_Node* xsp = xspecies[0];
494 
495  XML_Node* aStandardState = 0;
496  if (xsp->hasChild("standardState")) {
497  aStandardState = &xsp->child("standardState");
498  } else {
499  throw CanteraError("MineralEQ3::initThermoXML",
500  "no standard state mode");
501  }
502  doublereal volVal = 0.0;
503  string smodel = (*aStandardState)["model"];
504  if (smodel != "constantVolume") {
505  throw CanteraError("MineralEQ3::initThermoXML",
506  "wrong standard state mode");
507  }
508  if (aStandardState->hasChild("V0_Pr_Tr")) {
509  XML_Node& aV = aStandardState->child("V0_Pr_Tr");
510  string Aunits = "";
511  double Afactor = toSI("cm3/gmol");
512  if (aV.hasAttrib("units")) {
513  Aunits = aV.attrib("units");
514  Afactor = toSI(Aunits);
515  }
516  volVal = ctml::getFloat(*aStandardState, "V0_Pr_Tr");
517  m_V0_pr_tr= volVal;
518  volVal *= Afactor;
519  m_speciesSize[0] = volVal;
520  } else {
521  throw CanteraError("MineralEQ3::initThermoXML",
522  "wrong standard state mode");
523  }
524  doublereal rho = molecularWeight(0) / volVal;
525  setDensity(rho);
526 
527  const XML_Node& sThermo = xsp->child("thermo");
528  const XML_Node& MinEQ3node = sThermo.child("MinEQ3");
529 
530 
532  ctml::getFloatDefaultUnits(MinEQ3node, "DG0_f_Pr_Tr", "cal/gmol", "actEnergy");
534  ctml::getFloatDefaultUnits(MinEQ3node, "DH0_f_Pr_Tr", "cal/gmol", "actEnergy");
535  m_Entrop_pr_tr = ctml::getFloatDefaultUnits(MinEQ3node, "S0_Pr_Tr", "cal/gmol/K");
536  m_a = ctml::getFloatDefaultUnits(MinEQ3node, "a", "cal/gmol/K");
537  m_b = ctml::getFloatDefaultUnits(MinEQ3node, "b", "cal/gmol/K2");
538  m_c = ctml::getFloatDefaultUnits(MinEQ3node, "c", "cal-K/gmol");
539 
540 
541  convertDGFormation();
542 
543 }
544 
545 
547 {
548  std::string model = eosdata["model"];
549  if (model != "MineralEQ3") {
550  throw CanteraError("MineralEQ3::MineralEQ3",
551  "thermo model attribute must be MineralEQ3");
552  }
553 }
554 
555 doublereal MineralEQ3::LookupGe(const std::string& elemName)
556 {
557  size_t iE = elementIndex(elemName);
558  if (iE == npos) {
559  throw CanteraError("PDSS_HKFT::LookupGe", "element " + elemName + " not found");
560  }
561  doublereal geValue = entropyElement298(iE);
562  if (geValue == ENTROPY298_UNKNOWN) {
563  throw CanteraError("PDSS_HKFT::LookupGe",
564  "element " + elemName + " doesn not have a supplied entropy298");
565  }
566  geValue *= (-298.15);
567  return geValue;
568 }
569 
570 void MineralEQ3::convertDGFormation()
571 {
572  /*
573  * Ok let's get the element compositions and conversion factors.
574  */
575  doublereal na;
576  doublereal ge;
577  string ename;
578 
579  doublereal totalSum = 0.0;
580  for (size_t m = 0; m < nElements(); m++) {
581  na = nAtoms(0, m);
582  if (na > 0.0) {
583  ename = elementName(m);
584  ge = LookupGe(ename);
585  totalSum += na * ge;
586  }
587  }
588  // Add in the charge
589  // if (m_charge_j != 0.0) {
590  // ename = "H";
591  // ge = LookupGe(ename);
592  // totalSum -= m_charge_j * ge;
593  //}
594  // Ok, now do the calculation. Convert to joules kmol-1
595  doublereal dg = m_deltaG_formation_pr_tr * 4.184 * 1.0E3;
596  //! Store the result into an internal variable.
597  m_Mu0_pr_tr = dg + totalSum;
598 }
599 
600 }
601 
602