Cantera  2.0
IdealSolidSolnPhase.cpp
Go to the documentation of this file.
1 /**
2  * @file IdealSolidSolnPhase.cpp
3  * Implementation file for an ideal solid solution model
4  * with incompressible thermodynamics (see \ref thermoprops and
5  * \link Cantera::IdealSolidSolnPhase IdealSolidSolnPhase\endlink).
6  */
7 /*
8  * Copyright 2006 Sandia Corporation. Under the terms of Contract
9  * DE-AC04-94AL85000, with Sandia Corporation, the U.S. Government
10  * retains certain rights in this software.
11  */
12 
15 
16 #include <iostream>
17 #include <fstream>
18 
19 using namespace std;
20 
21 namespace Cantera
22 {
23 
24 /*
25  * Constructor for IdealSolidSolnPhase class:
26  * The default form for the generalized concentrations is 0
27  * i.e., unity.
28  */
29 IdealSolidSolnPhase::IdealSolidSolnPhase(int formGC) :
30  ThermoPhase(),
31  m_formGC(formGC),
32  m_mm(0),
33  m_tmin(0.0),
34  m_tmax(1000000.),
35  m_Pref(OneAtm),
36  m_Pcurrent(OneAtm),
37  m_tlast(0.0)
38 {
39  if (formGC < 0 || formGC > 2) {
40  throw CanteraError(" IdealSolidSolnPhase Constructor",
41  " Illegal value of formGC");
42  }
43 }
44 
45 IdealSolidSolnPhase::IdealSolidSolnPhase(std::string inputFile, std::string id,
46  int formGC) :
47  ThermoPhase(),
48  m_formGC(formGC),
49  m_mm(0),
50  m_tmin(0.0),
51  m_tmax(1000000.),
52  m_Pref(OneAtm),
53  m_Pcurrent(OneAtm),
54  m_tlast(0.0)
55 {
56  if (formGC < 0 || formGC > 2) {
57  throw CanteraError(" IdealSolidSolnPhase Constructor",
58  " Illegal value of formGC");
59  }
60  constructPhaseFile(inputFile, id);
61 }
62 //====================================================================================================================
64  int formGC) :
65  ThermoPhase(),
66  m_formGC(formGC),
67  m_mm(0),
68  m_tmin(0.0),
69  m_tmax(1000000.),
70  m_Pref(OneAtm),
71  m_Pcurrent(OneAtm),
72  m_tlast(0.0)
73 {
74  if (formGC < 0 || formGC > 2) {
75  throw CanteraError(" IdealSolidSolnPhase Constructor",
76  " Illegal value of formGC");
77  }
78  constructPhaseXML(root, id);
79 }
80 //====================================================================================================================
82 {
83  *this = b;
84 }
85 //====================================================================================================================
86 
89 {
90  if (this != &b) {
91  //ThermoPhase::operator=(b);
92  // m_spthermo = dupMyselfAsSpeciesThermo(b.m_spthermo);
93  m_formGC = b.m_formGC;
94  m_mm = b.m_mm;
95  m_tmin = b.m_tmin;
96  m_tmax = b.m_tmax;
97  m_Pref = b.m_Pref;
100  m_tlast = b.m_tlast;
101  m_h0_RT = b.m_h0_RT;
102  m_cp0_R = b.m_cp0_R;
103  m_g0_RT = b.m_g0_RT;
104  m_s0_R = b.m_s0_R;
106  m_pe = b.m_pe;
107  m_pp = b.m_pp;
108  }
109  return *this;
110 }
111 
112 /*
113  * Base Class Duplication Function
114  * -> given a pointer to ThermoPhase, this function can
115  * duplicate the object. (note has to be a separate function
116  * not the copy constructor, because it has to be
117  * a virtual function)
118  */
120 {
121  IdealSolidSolnPhase* ii = new IdealSolidSolnPhase(*this);
122  return (ThermoPhase*) ii;
123 }
124 //====================================================================================================================
125 /**
126  * Equation of state flag. Returns the value cIdealGas, defined
127  * in mix_defs.h.
128  */
130 {
131  integer res;
132  switch (m_formGC) {
133  case 0:
134  res = cIdealSolidSolnPhase0;
135  break;
136  case 1:
137  res = cIdealSolidSolnPhase1;
138  break;
139  case 2:
140  res = cIdealSolidSolnPhase2;
141  break;
142  default:
143  throw CanteraError("eosType", "Unknown type");
144  break;
145  }
146  return res;
147 }
148 
149 /********************************************************************
150  * Molar Thermodynamic Properties of the Solution
151  ********************************************************************/
152 /**
153  * Molar enthalpy of the solution. Units: J/kmol.
154  * For an ideal, constant partial molar volume solution mixture with
155  * pure species phases which exhibit zero volume expansivity and
156  * zero isothermal compressibility:
157  * \f[
158  * \hat h(T,P) = \sum_k X_k \hat h^0_k(T) + (P - P_{ref}) (\sum_k X_k \hat V^0_k)
159  * \f]
160  * The reference-state pure-species enthalpies at the reference pressure Pref
161  * \f$ \hat h^0_k(T) \f$, are computed by the species thermodynamic
162  * property manager. They are polynomial functions of temperature.
163  * @see SpeciesThermo
164  */
165 doublereal IdealSolidSolnPhase::
167 {
168  const double* eptr = &(enthalpy_RT_ref()[0]);
169  doublereal htp = (GasConstant * temperature() * mean_X(eptr));
170  return (htp + (pressure() - m_Pref)/molarDensity());
171 }
172 
173 /**
174  * Molar internal energy of the solution. J/kmol.
175  * For an ideal, constant partial molar volume solution mixture with
176  * pure species phases which exhibit zero volume expansivity and
177  * zero isothermal compressibility:
178  * \f[
179  * \hat u(T) = \hat h(T,P) - p \hat V = \sum_k X_k \hat h^0_k(T)
180  * - P_{ref} (\sum_k X_k \hat V^0_k)
181  * \f]
182  * and is a function only of temperature.
183  * The reference-state pure-species enthalpies
184  * \f$ \hat h^0_k(T) \f$ are computed by the species thermodynamic
185  * property manager.
186  * @see SpeciesThermo
187  */
189 {
190  const double* eptr = DATA_PTR(enthalpy_RT_ref().begin());
191  doublereal htp = (GasConstant * temperature() *
192  mean_X(eptr));
193  return (htp - m_Pref / molarDensity());
194 }
195 
196 /**
197  * Molar entropy of the solution. Units: J/kmol/K.
198  * For an ideal, constant partial molar volume solution mixture with
199  * pure species phases which exhibit zero volume expansivity:
200  * \f[
201  * \hat s(T, P, X_k) = \sum_k X_k \hat s^0_k(T)
202  * - \hat R \sum_k X_k log(X_k)
203  * \f]
204  * The reference-state pure-species entropies
205  * \f$ \hat s^0_k(T,p_{ref}) \f$ are computed by the species thermodynamic
206  * property manager. The pure species entropies are independent of
207  * temperature since the volume expansivities are equal to zero.
208  * @see SpeciesThermo
209  */
211 {
212  const double* dptr = DATA_PTR(entropy_R_ref());
213  return GasConstant * (mean_X(dptr) - sum_xlogx());
214 }
215 
216 /**
217  * Molar gibbs free energy of the solution. Units: J/kmol.
218  * For an ideal, constant partial molar volume solution mixture with
219  * pure species phases which exhibit zero volume expansivity:
220  * \f[
221  * \hat g(T, P) = \sum_k X_k \hat g^0_k(T,P) + \hat R T \sum_k X_k log(X_k)
222  * \f]
223  * The reference-state pure-species gibbs free energies
224  * \f$ \hat g^0_k(T) \f$ are computed by the species thermodynamic
225  * property manager, while the standard state gibbs free energies
226  * \f$ \hat g^0_k(T,P) \f$ are computed by the member function, gibbs_RT().
227  * @see SpeciesThermo
228  */
230 {
231  const double* dptr = DATA_PTR(gibbs_RT_ref());
232  doublereal g = mean_X(dptr);
233  return (GasConstant * temperature() * (g + sum_xlogx()));
234 }
235 
236 /**
237  * Molar heat capacity at constant pressure of the solution.
238  * Units: J/kmol/K.
239  * For an ideal, constant partial molar volume solution mixture with
240  * pure species phases which exhibit zero volume expansivity:
241  * \f[
242  * \hat c_p(T,P) = \sum_k X_k \hat c^0_{p,k}(T) .
243  * \f]
244  * The heat capacity is independent of pressure.
245  * The reference-state pure-species heat capacities
246  * \f$ \hat c^0_{p,k}(T) \f$ are computed by the species thermodynamic
247  * property manager.
248  * @see SpeciesThermo
249  */
251 {
252  const double* dptr = DATA_PTR(cp_R_ref());
253  return GasConstant * mean_X(dptr);
254 }
255 
256 /********************************************************************
257  * Mechanical Equation of State
258  ********************************************************************/
259 /**
260  *
261  * Calculate the density of the mixture using the partial
262  * molar volumes and mole fractions as input
263  *
264  * The formula for this is
265  *
266  * \f[
267  * \rho = \frac{\sum_k{X_k W_k}}{\sum_k{X_k V_k}}
268  * \f]
269  *
270  * where \f$ X_k \f$ are the mole fractions, \f$W_k\f$ are
271  * the molecular weights, and \f$V_k\f$ are the pure species
272  * molar volumes.
273  *
274  * Note, the basis behind this formula is that in an ideal
275  * solution the partial molar volumes are equal to the pure
276  * species molar volumes. We have additionally specified that
277  * in this class that the pure species molar volumes are
278  * independent of temperature and pressure.
279  */
281 {
282  /*
283  * Calculate the molarVolume of the solution (m**3 kmol-1)
284  */
285  const doublereal* const dtmp = moleFractdivMMW();
286  double invDens = dot(m_speciesMolarVolume.begin(),
287  m_speciesMolarVolume.end(), dtmp);
288  /*
289  * Set the density in the parent State object directly,
290  * by calling the Phase::setDensity() function.
291  */
292  double dens = 1.0/invDens;
293  Phase::setDensity(dens);
294 }
295 
296 /**
297  * Overwritten setDensity() function is necessary because the
298  * density is not an independent variable.
299  *
300  * This function will now throw an error condition
301  *
302  * @internal May have to adjust the strategy here to make
303  * the eos for these materials slightly compressible, in order
304  * to create a condition where the density is a function of
305  * the pressure.
306  *
307  * This function will now throw an error condition.
308  *
309  * NOTE: This is a virtual function that overwrites the State.h
310  * class
311  */
313 setDensity(const doublereal rho)
314 {
315  /*
316  * Unless the input density is exactly equal to the density
317  * calculated and stored in the State object, we throw an
318  * exception. This is because the density is NOT an
319  * independent variable.
320  */
321  double dens = density();
322  if (rho != dens) {
323  throw CanteraError("IdealSolidSolnPhase::setDensity",
324  "Density is not an independent variable");
325  }
326 }
327 
328 /*
329  * setPressure(double) (virtual from ThermoPhase)
330  *
331  * Set the pressure at constant temperature. Units: Pa.
332  * This method sets a constant within the object.
333  * The mass density is not a function of pressure.
334  * Note: This function overrides the setPressure() function
335  * in the ThermoPhase object.
336  * We calculate the density and store it in the
337  * State object, because this density is supposed to
338  * be current after setting the pressure, and is now
339  * a dependent variable.
340  */
342 {
343  m_Pcurrent = p;
344  calcDensity();
345 }
346 
347 /*
348  * setMolarDensity() (virtual from State)
349  * Overwritten setMolarDensity() function is necessary because the
350  * density is not an independent variable.
351  *
352  * This function will now throw an error condition.
353  *
354  * NOTE: This is a virtual function that overrides the State.h
355  * class
356  */
357 void IdealSolidSolnPhase::setMolarDensity(const doublereal n)
358 {
359  throw CanteraError("IdealSolidSolnPhase::setMolarDensity",
360  "Density is not an independent variable");
361 }
362 
363 /*
364  * setMoleFractions() (virtual from State)
365  *
366  * Sets the mole fractions and adjusts the internal density.
367  */
368 void IdealSolidSolnPhase::setMoleFractions(const doublereal* const x)
369 {
371  calcDensity();
372 }
373 
374 /**
375  * setMoleFractions_NoNorm() (virtual from State)
376  *
377  * Sets the mole fractions and adjusts the internal density.
378  */
379 void IdealSolidSolnPhase::setMoleFractions_NoNorm(const doublereal* const x)
380 {
382  calcDensity();
383 }
384 
385 /*
386  * setMassFractions() (virtual from State)
387  *
388  * Sets the mass fractions and adjusts the internal density.
389  */
390 void IdealSolidSolnPhase::setMassFractions(const doublereal* const y)
391 {
393  calcDensity();
394 }
395 
396 /*
397  * setMassFractions_NoNorm() (virtual from State)
398  *
399  * Sets the mass fractions and adjusts the internal density.
400  */
401 void IdealSolidSolnPhase::setMassFractions_NoNorm(const doublereal* const y)
402 {
404  calcDensity();
405 }
406 
407 /*
408  * setConcentrations (virtual from State)
409  *
410  * Sets the concentrations and adjusts the internal density
411  */
412 void IdealSolidSolnPhase::setConcentrations(const doublereal* const c)
413 {
415  calcDensity();
416 }
417 
418 /********************************************************************
419  * Chemical Potentials and Activities
420  ********************************************************************/
421 
422 /********************************************************************
423  *
424  * getActivitConcentrations():
425  *
426  * This method returns the array of generalized
427  * concentrations. The generalized concentrations are used
428  * in the evaluation of the rates of progress for reactions
429  * involving species in this phase. The generalized
430  * concentration dividied by the standard concentration is also
431  * equal to the activity of species.
432  *
433  * For this implentation the activity is defined to be the
434  * mole fraction of the species. The generalized concentration
435  * is defined to be equal to the mole fraction divided by
436  * the partial molar volume. The generalized concentrations
437  * for species in this phase therefore have units of
438  * kmol m<SUP>-3</SUP>. Rate constants must reflect this fact.
439  *
440  * On a general note, the following must be true.
441  * For an ideal solution, the generalized concentration must consist
442  * of the mole fraction multiplied by a constant. The constant may be
443  * fairly arbitrarily chosen, with differences adsorbed into the
444  * reaction rate expression. 1/V_N, 1/V_k, or 1 are equally good,
445  * as long as the standard concentration is adjusted accordingly.
446  * However, it must be a constant (and not the concentration, btw,
447  * which is a function of the mole fractions) in order for the
448  * ideal solution properties to hold at the same time having the
449  * standard concentration to be independent of the mole fractions.
450  *
451  * In this implementation the form of the generalized concentrations
452  * depend upon the member attribute, m_formGC:
453  *
454  * <TABLE>
455  * <TR><TD> m_formGC </TD><TD> GeneralizedConc </TD><TD> StandardConc </TD></TR>
456  * <TR><TD> 0 </TD><TD> X_k </TD><TD> 1.0 </TD></TR>
457  * <TR><TD> 1 </TD><TD> X_k / V_k </TD><TD> 1.0 / V_k </TD></TR>
458  * <TR><TD> 2 </TD><TD> X_k / V_N </TD><TD> 1.0 / V_N </TD></TR>
459  * </TABLE>
460  *
461  * HKM Note: We have absorbed the pressure dependence of the pure species
462  * state into the thermodynamics functions. Therefore the
463  * standard state on which the activities are based depend
464  * on both temperature and pressure. If we hadn't, it would have
465  * appeared in this function in a very awkwards exp[] format.
466  *
467  * @param c[] Pointer to array of doubles of length m_kk, which on exit
468  * will contain the generalized concentrations.
469  */
471 getActivityConcentrations(doublereal* c) const
472 {
473  const doublereal* const dtmp = moleFractdivMMW();
474  const double mmw = meanMolecularWeight();
475  switch (m_formGC) {
476  case 0:
477  for (size_t k = 0; k < m_kk; k++) {
478  c[k] = dtmp[k] * mmw;
479  }
480  break;
481  case 1:
482  for (size_t k = 0; k < m_kk; k++) {
483  c[k] = dtmp[k] * mmw / m_speciesMolarVolume[k];
484  }
485  break;
486  case 2:
487  double atmp = mmw / m_speciesMolarVolume[m_kk-1];
488  for (size_t k = 0; k < m_kk; k++) {
489  c[k] = dtmp[k] * atmp;
490  }
491  break;
492  }
493 }
494 
495 /*********************************************************************
496  *
497  * standardConcentration()
498  *
499  * The standard concentration \f$ C^0_k \f$ used to normalize
500  * the generalized concentration.
501  * In many cases, this quantity
502  * will be the same for all species in a phase.
503  * However, for this case, we will return a distinct concentration
504  * for each species. This is the inverse of the species molar
505  * volume. Units are m<SUP>3</SUP> kmol<SUP>-1</SUP>.
506  *
507  *
508  * @param k Species number: this is a require parameter,
509  * a change from the ThermoPhase base class, where it was
510  * an optional parameter.
511  */
512 doublereal IdealSolidSolnPhase::
513 standardConcentration(size_t k) const
514 {
515  switch (m_formGC) {
516  case 0:
517  return 1.0;
518  case 1:
519  return 1.0 / m_speciesMolarVolume[k];
520  case 2:
521  return 1.0/m_speciesMolarVolume[m_kk-1];
522  }
523  return 0.0;
524 }
525 doublereal IdealSolidSolnPhase::
527 {
528  switch (m_formGC) {
529  case 0:
530  return 1.0;
531  case 1:
532  return 1.0 / m_speciesMolarVolume[k];
533  case 2:
534  return 1.0 / m_speciesMolarVolume[m_kk-1];
535  }
536  return 0.0;
537 }
538 
539 /*********************************************************************
540  *
541  * logStandardConc()
542  *
543  * Returns the log of the standard concentration
544  *
545  * @param k Species number: this is a require parameter,
546  * a change from the ThermoPhase base class, where it was
547  * an optional parameter.
548  */
549 doublereal IdealSolidSolnPhase::
550 logStandardConc(size_t k) const
551 {
552  _updateThermo();
553  double res;
554  switch (m_formGC) {
555  case 0:
556  res = 0.0;
557  break;
558  case 1:
559  res = log(1.0/m_speciesMolarVolume[k]);
560  break;
561  case 2:
562  res = log(1.0/m_speciesMolarVolume[m_kk-1]);
563  break;
564  default:
565  throw CanteraError("eosType", "Unknown type");
566  break;
567  }
568  return res;
569 }
570 
571 /***********************************************************************
572  *
573  * getUnitsStandardConcentration()
574  *
575  * Returns the units of the standard and general concentrations
576  * Note they have the same units, as their divisor is
577  * defined to be equal to the activity of the kth species
578  * in the solution, which is unitless.
579  *
580  * This routine is used in print out applications where the
581  * units are needed. Usually, MKS units are assumed throughout
582  * the program and in the XML input files.
583  *
584  * uA[0] = kmol units - default = 1
585  * uA[1] = m units - default = -nDim(), the number of spatial
586  * dimensions in the Phase class.
587  * uA[2] = kg units - default = 0;
588  * uA[3] = Pa(pressure) units - default = 0;
589  * uA[4] = Temperature units - default = 0;
590  * uA[5] = time units - default = 0
591  *
592  * For EOS types other than cIdealSolidSolnPhase1, the default
593  * kmol/m3 holds for standard concentration units. For
594  * cIdealSolidSolnPhase0 type, the standard concentration is
595  * unitless.
596  */
598 getUnitsStandardConc(double* uA, int, int sizeUA) const
599 {
600  int eos = eosType();
601  if (eos == cIdealSolidSolnPhase0) {
602  for (int i = 0; i < sizeUA; i++) {
603  uA[i] = 0.0;
604  }
605  } else {
606  for (int i = 0; i < sizeUA; i++) {
607  if (i == 0) {
608  uA[0] = 1.0;
609  }
610  if (i == 1) {
611  uA[1] = -int(nDim());
612  }
613  if (i == 2) {
614  uA[2] = 0.0;
615  }
616  if (i == 3) {
617  uA[3] = 0.0;
618  }
619  if (i == 4) {
620  uA[4] = 0.0;
621  }
622  if (i == 5) {
623  uA[5] = 0.0;
624  }
625  }
626  }
627 }
628 
629 /*
630  * getActivityCoefficients():
631  *
632  */
634 getActivityCoefficients(doublereal* ac) const
635 {
636  for (size_t k = 0; k < m_kk; k++) {
637  ac[k] = 1.0;
638  }
639 }
640 //================================================================================================
641 /*
642  *
643  * getChemPotentials():
644  *
645  * This function returns a vector of chemical potentials of the
646  * species.
647  * \f[
648  * \mu_k = \mu^o_k(T) + V_k * (p - p_o) + R T ln(X_k)
649  * \f]
650  * or another way to phrase this is
651  * \f[
652  * \mu_k = \mu^o_k(T,p) + R T ln(X_k)
653  * \f]
654  * where \f$ \mu^o_k(T,p) = \mu^o_k(T) + V_k * (p - p_o)\f$
655  *
656  */
658 getChemPotentials(doublereal* mu) const
659 {
660  doublereal delta_p = m_Pcurrent - m_Pref;
661  doublereal xx;
662  doublereal RT = temperature() * GasConstant;
663  const vector_fp& g_RT = gibbs_RT_ref();
664  for (size_t k = 0; k < m_kk; k++) {
666  mu[k] = RT * (g_RT[k] + log(xx))
667  + delta_p * m_speciesMolarVolume[k];
668  }
669 }
670 //================================================================================================
671 /*
672  *
673  * getChemPotentials_RT()
674  *
675  * Get the array of non-dimensional chemical potentials \f$
676  * \mu_k / \hat R T \f$, where
677  *
678  * \f[
679  * \mu_k = \mu^o_k(T) + V_k * (p - p_o) + R T ln(X_k)
680  * \f]
681  * or another way to phrase this is
682  * \f[
683  * \mu_k = \mu^o_k(T,p) + R T ln(X_k)
684  * \f]
685  * where \f$ \mu^o_k(T,p) = \mu^o_k(T) + V_k * (p - p_o)\f$
686  *
687  */
689 getChemPotentials_RT(doublereal* mu) const
690 {
691  doublereal RT = temperature() * GasConstant;
692  doublereal delta_pdRT = (m_Pcurrent - m_Pref) / RT;
693  doublereal xx;
694  const vector_fp& g_RT = gibbs_RT_ref();
695  for (size_t k = 0; k < m_kk; k++) {
697  mu[k] = (g_RT[k] + log(xx))
698  + delta_pdRT * m_speciesMolarVolume[k];
699  }
700 }
701 
702 /********************************************************************
703  * Partial Molar Properties
704  ********************************************************************/
705 
706 /********************************************************************
707  *
708  * getPartialMolarEnthalpies()
709  *
710  * For this phase, the partial molar enthalpies are equal to the
711  * pure species enthalpies.
712  * \f[
713  * \hat h_k(T,P) = \sum_k X_k \hat h^0_k(T) + (p - p_{ref}) (\sum_k X_k \hat V^0_k)
714  * \f]
715  * The reference-state pure-species enthalpies at the reference
716  * pressure p_ref
717  * \f$ \hat h^0_k(T) \f$, are computed by the species thermodynamic
718  * property manager. They are polynomial functions of temperature.
719  * @see SpeciesThermo
720  */
722 {
723  const vector_fp& _h = enthalpy_RT_ref();
724  doublereal rt = GasConstant * temperature();
725  scale(_h.begin(), _h.end(), hbar, rt);
726 }
727 
728 /********************************************************************
729  *
730  * getPartialMolarEntropies()
731  *
732  * Returns an array of partial molar entropies of the species in the
733  * solution. Units: J/kmol.
734  * For this phase, the partial molar entropies are equal to the
735  * pure species entropies plus the ideal solution contribution.
736  * \f[
737  * \bar s_k(T,P) = \hat s^0_k(T) - R log(X_k)
738  * \f]
739  * The reference-state pure-species entropies,\f$ \hat s^0_k(T) \f$,
740  * at the reference pressure, \f$ P_{ref} \f$, are computed by the
741  * species thermodynamic
742  * property manager. They are polynomial functions of temperature.
743  * @see SpeciesThermo
744  */
746 getPartialMolarEntropies(doublereal* sbar) const
747 {
748  const vector_fp& _s = entropy_R_ref();
749  doublereal r = GasConstant;
750  doublereal xx;
751  for (size_t k = 0; k < m_kk; k++) {
753  sbar[k] = r * (_s[k] - log(xx));
754  }
755 }
756 
757 /********************************************************************
758  *
759  * getPartialMolarCp()
760  *
761  * For this phase, the partial molar heat capacities are equal
762  * to the standard state heat capacities.
763 
764  */
766 getPartialMolarCp(doublereal* cpbar) const
767 {
768  getCp_R(cpbar);
769  for (size_t k = 0; k < m_kk; k++) {
770  cpbar[k] *= GasConstant;
771  }
772 }
773 
774 /******************************************************************
775  *
776  * getPartialMolarVolumes()
777  *
778  * returns an array of partial molar volumes of the species
779  * in the solution. Units: m^3 kmol-1.
780  *
781  * For this solution, thepartial molar volumes are equal to the
782  * constant species molar volumes.
783  */
785 getPartialMolarVolumes(doublereal* vbar) const
786 {
787  getStandardVolumes(vbar);
788 }
789 
790 /*****************************************************************
791  * Properties of the Standard State of the Species
792  * in the Solution
793  *****************************************************************/
794 
795 /******************************************************************
796  *
797  * getPureGibbs()
798  *
799  * Get the Gibbs functions for the pure species
800  * at the current <I>T</I> and <I>P</I> of the solution.
801  * We assume an incompressible constant partial molar
802  * volume here:
803  * \f[
804  * \mu^0_k(T,p) = \mu^{ref}_k(T) + (P - P_{ref}) * V_k
805  * \f]
806  * where \f$V_k\f$ is the molar volume of pure species <I>k<\I>.
807  * \f$ u^{ref}_k(T)\f$ is the chemical potential of pure
808  * species <I>k<\I> at the reference pressure, \f$P_{ref}\f$.
809  */
811 getPureGibbs(doublereal* gpure) const
812 {
813  const vector_fp& gibbsrt = gibbs_RT_ref();
814  doublereal RT = _RT();
815  const doublereal* const gk = DATA_PTR(gibbsrt);
816  doublereal delta_p = (m_Pcurrent - m_Pref);
817  for (size_t k = 0; k < m_kk; k++) {
818  gpure[k] = RT * gk[k] + delta_p * m_speciesMolarVolume[k];
819  }
820 }
821 
822 /**
823  * Get the nondimensional gibbs function for the species
824  * standard states at the current T and P of the solution.
825  *
826  * \f[
827  * \mu^0_k(T,P) = \mu^{ref}_k(T) + (P - P_{ref}) * V_k
828  * \f]
829  * where \f$V_k\f$ is the molar volume of pure species <I>k</I>.
830  * \f$ \mu^{ref}_k(T)\f$ is the chemical potential of pure
831  * species <I>k</I> at the reference pressure, \f$P_{ref}\f$.
832  *
833  * @param grt Vector of length m_kk, which on return sr[k]
834  * will contain the nondimensional
835  * standard state gibbs function for species k.
836  */
838 getGibbs_RT(doublereal* grt) const
839 {
840  const vector_fp& gibbsrt = gibbs_RT_ref();
841  doublereal RT = _RT();
842  const doublereal* const gk = DATA_PTR(gibbsrt);
843  doublereal delta_prt = (m_Pcurrent - m_Pref)/ RT;
844  for (size_t k = 0; k < m_kk; k++) {
845  grt[k] = gk[k] + delta_prt * m_speciesMolarVolume[k];
846  }
847 }
848 
849 /********************************************************************
850  *
851  * getEnthalpy_RT()
852  *
853  * Get the array of nondimensional Enthalpy functions for the ss
854  * species at the current <I>T</I> and <I>P</I> of the solution.
855  * We assume an incompressible constant partial molar
856  * volume here:
857  * \f[
858  * h^0_k(T,P) = h^{ref}_k(T) + (P - P_{ref}) * V_k
859  * \f]
860  * where \f$V_k\f$ is the molar volume of pure species <I>k<\I>.
861  * \f$ h^{ref}_k(T)\f$ is the enthalpy of the pure
862  * species <I>k<\I> at the reference pressure, \f$P_{ref}\f$.
863  */
865 getEnthalpy_RT(doublereal* hrt) const
866 {
867  const vector_fp& _h = enthalpy_RT_ref();
868  doublereal delta_prt = ((m_Pcurrent - m_Pref) /
869  (GasConstant * temperature()));
870  for (size_t k = 0; k < m_kk; k++) {
871  hrt[k] = _h[k] + delta_prt * m_speciesMolarVolume[k];
872  }
873 }
874 
875 /**
876  * Get the nondimensional Entropies for the species
877  * standard states at the current T and P of the solution.
878  *
879  * Note, this is equal to the reference state entropies
880  * due to the zero volume expansivity:
881  * i.e., (dS/dp)_T = (dV/dT)_P = 0.0
882  *
883  * @param sr Vector of length m_kk, which on return sr[k]
884  * will contain the nondimensional
885  * standard state entropy of species k.
886  */
887 void IdealSolidSolnPhase::getEntropy_R(doublereal* sr) const
888 {
889  const vector_fp& _s = entropy_R_ref();
890  copy(_s.begin(), _s.end(), sr);
891 }
892 
893 /*
894  * Returns the vector of nondimensional
895  * internal Energies of the standard state at the current temperature
896  * of the solution and current pressure for each species.
897  * \f[
898  * u^0_k(T,P) = h^{ref}_k(T) - P_{ref} * V_k
899  * \f]
900  *
901  * The standard state internal energy is independent of
902  * pressure in this equation of state.
903  * (inherited from ThermoPhase.h)
904  */
905 void IdealSolidSolnPhase::getIntEnergy_RT(doublereal* urt) const
906 {
907  const vector_fp& _h = enthalpy_RT_ref();
908  doublereal prefrt = m_Pref / (GasConstant * temperature());
909  for (size_t k = 0; k < m_kk; k++) {
910  urt[k] = _h[k] - prefrt * m_speciesMolarVolume[k];
911  }
912 }
913 
914 /*
915  * Get the nondimensional heat capacity at constant pressure
916  * function for the species
917  * standard states at the current T and P of the solution.
918  *
919  * \f[
920  * Cp^0_k(T,P) = Cp^{ref}_k(T)
921  * \f]
922  * where \f$V_k\f$ is the molar volume of pure species <I>k<\I>.
923  * \f$ Cp^{ref}_k(T)\f$ is the constant pressure heat capacity
924  * of species <I>k<\I> at the reference pressure, \f$P_{ref}\f$.
925  *
926  * @param cpr Vector of length m_kk, which on return cpr[k]
927  * will contain the nondimensional
928  * constant pressure heat capacity for species k.
929  */
930 void IdealSolidSolnPhase::getCp_R(doublereal* cpr) const
931 {
932  const vector_fp& _cpr = cp_R_ref();
933  copy(_cpr.begin(), _cpr.end(), cpr);
934 }
935 
936 /*
937  * Get the molar volumes of each species in their standard
938  * states at the current
939  * <I>T</I> and <I>P</I> of the solution.
940  * units = m^3 / kmol
941  */
942 void IdealSolidSolnPhase::getStandardVolumes(doublereal* vol) const
943 {
944  copy(m_speciesMolarVolume.begin(), m_speciesMolarVolume.end(), vol);
945 }
946 
947 
948 /*********************************************************************
949  * Thermodynamic Values for the Species Reference States
950  *********************************************************************/
951 
952 /*
953  * Returns the vector of non-dimensional Enthalpy function
954  * of the reference state at the current temperature
955  * of the solution and the reference pressure for the species.
956  * Units = unitless
957  */
958 void IdealSolidSolnPhase::getEnthalpy_RT_ref(doublereal* hrt) const
959 {
960  _updateThermo();
961  for (size_t k = 0; k != m_kk; k++) {
962  hrt[k] = m_h0_RT[k];
963  }
964 }
965 
966 /*
967  * Returns the vector of non-dimensional Gibbs function
968  * of the reference state at the current temperature
969  * of the solution and the reference pressure for the species.
970  * Units = unitless
971  */
972 void IdealSolidSolnPhase::getGibbs_RT_ref(doublereal* grt) const
973 {
974  _updateThermo();
975  for (size_t k = 0; k != m_kk; k++) {
976  grt[k] = m_g0_RT[k];
977  }
978 }
979 
980 /*
981  * Returns the vector of Gibbs function
982  * of the reference state at the current temperature
983  * of the solution and the reference pressure for the species.
984  * Units = J / kmol
985  */
986 void IdealSolidSolnPhase::getGibbs_ref(doublereal* g) const
987 {
988  _updateThermo();
989  double tmp = GasConstant * temperature();
990  for (size_t k = 0; k != m_kk; k++) {
991  g[k] = tmp * m_g0_RT[k];
992  }
993 }
994 
995 /*
996  * Returns the vector of nondimensional
997  * internal Energies of the standard state at the current temperature
998  * of the solution and current pressure for each species.
999  * (inherited from ThermoPhase.h)
1000  */
1001 void IdealSolidSolnPhase::getIntEnergy_RT_ref(doublereal* urt) const
1002 {
1003  const vector_fp& _h = enthalpy_RT_ref();
1004  doublereal prefrt = m_Pref / (GasConstant * temperature());
1005  for (size_t k = 0; k < m_kk; k++) {
1006  urt[k] = _h[k] - prefrt * m_speciesMolarVolume[k];
1007  }
1008 }
1009 
1010 /*
1011  * Returns the vector of non-dimensional Entropy function
1012  * of the reference state at the current temperature
1013  * of the solution and the reference pressure for the species.
1014  * Units = unitless
1015  */
1016 void IdealSolidSolnPhase::getEntropy_R_ref(doublereal* er) const
1017 {
1018  _updateThermo();
1019  for (size_t k = 0; k != m_kk; k++) {
1020  er[k] = m_s0_R[k];
1021  }
1022 }
1023 
1024 /*
1025  * Returns the vector of non-dimensional Entropy function
1026  * of the reference state at the current temperature
1027  * of the solution and the reference pressure for the species.
1028  * Units = unitless
1029  */
1030 void IdealSolidSolnPhase::getCp_R_ref(doublereal* cpr) const
1031 {
1032  _updateThermo();
1033  for (size_t k = 0; k != m_kk; k++) {
1034  cpr[k] = m_cp0_R[k];
1035  }
1036 }
1037 
1038 /*
1039  * Returns a reference to the vector of nondimensional
1040  * enthalpies of the reference state at the current temperature.
1041  * Real reason for its existence is that it also checks
1042  * to see if a recalculation of the reference thermodynamics
1043  * functions needs to be done.
1044  */
1046 {
1047  _updateThermo();
1048  return m_h0_RT;
1049 }
1050 
1051 /*
1052  * Returns a reference to the vector of nondimensional
1053  * enthalpies of the reference state at the current temperature.
1054  * Real reason for its existence is that it also checks
1055  * to see if a recalculation of the reference thermodynamics
1056  * functions needs to be done.
1057  */
1059 {
1060  _updateThermo();
1061  for (size_t k = 0; k != m_kk; k++) {
1062  m_expg0_RT[k] = exp(m_g0_RT[k]);
1063  }
1064  return m_expg0_RT;
1065 }
1066 
1067 /*
1068  * Returns a reference to the vector of nondimensional
1069  * enthalpies of the reference state at the current temperature.
1070  * Real reason for its existence is that it also checks
1071  * to see if a recalculation of the reference thermodynamics
1072  * functions needs to be done.
1073  */
1075 {
1076  _updateThermo();
1077  return m_s0_R;
1078 }
1079 
1080 /*********************************************************************
1081  * Utility Functions
1082  *********************************************************************/
1083 /*
1084  * initThermo() function initializes the object for use.
1085  *
1086  * Before its invokation, the class isn't ready for calculation.
1087  */
1089 {
1090 }
1091 
1092 /*
1093  * Import and initialize an IdealSolidSolnPhase phase
1094  * specification in an XML tree into the current object.
1095  * Here we read an XML description of the phase.
1096  * We import descriptions of the elements that make up the
1097  * species in a phase.
1098  * We import information about the species, including their
1099  * reference state thermodynamic polynomials. We then freeze
1100  * the state of the species.
1101  *
1102  * This routine calls importPhase() to do most of its work.
1103  * Then, importPhase() calls initThermoXML() to finish
1104  * off the work.
1105  *
1106  * @param phaseNode This object must be the phase node of a
1107  * complete XML tree
1108  * description of the phase, including all of the
1109  * species data. In other words while "phase" must
1110  * point to an XML phase object, it must have
1111  * sibling nodes "speciesData" that describe
1112  * the species in the phase.
1113  * @param id ID of the phase. If nonnull, a check is done
1114  * to see if phaseNode is pointing to the phase
1115  * with the correct id.
1116  */
1118 constructPhaseXML(XML_Node& phaseNode, std::string id)
1119 {
1120  string subname = "IdealSolidSolnPhase::constructPhaseXML";
1121  if (id.size() > 0) {
1122  string idp = phaseNode.id();
1123  if (idp != id) {
1124  throw CanteraError(subname.c_str(),
1125  "phasenode and Id are incompatible");
1126  }
1127  }
1128 
1129  /*
1130  * Check on the thermo field. Must have:
1131  * <thermo model="IdealSolidSolution" />
1132  */
1133  if (phaseNode.hasChild("thermo")) {
1134  XML_Node& thNode = phaseNode.child("thermo");
1135  string mStringa = thNode.attrib("model");
1136  string mString = lowercase(mStringa);
1137  if (mString != "idealsolidsolution") {
1138  throw CanteraError(subname.c_str(),
1139  "Unknown thermo model: " + mStringa);
1140  }
1141  } else {
1142  throw CanteraError(subname.c_str(),
1143  "Unspecified thermo model");
1144  }
1145 
1146  /*
1147  * Form of the standard concentrations. Must have one of:
1148  *
1149  * <standardConc model="unity" />
1150  * <standardConc model="molar_volume" />
1151  * <standardConc model="solvent_volume" />
1152  */
1153  if (phaseNode.hasChild("standardConc")) {
1154  XML_Node& scNode = phaseNode.child("standardConc");
1155  string formStringa = scNode.attrib("model");
1156  string formString = lowercase(formStringa);
1157  if (formString == "unity") {
1158  m_formGC = 0;
1159  } else if (formString == "molar_volume") {
1160  m_formGC = 1;
1161  } else if (formString == "solvent_volume") {
1162  m_formGC = 2;
1163  } else {
1164  throw CanteraError(subname.c_str(),
1165  "Unknown standardConc model: " + formStringa);
1166  }
1167  } else {
1168  throw CanteraError(subname.c_str(),
1169  "Unspecified standardConc model");
1170  }
1171 
1172  bool m_ok = importPhase(phaseNode, this);
1173  if (!m_ok) {
1174  throw CanteraError(subname.c_str(),"importPhase failed ");
1175  }
1176 }
1177 
1178 /*
1179  * Initialization of an IdealSolidSolnPhase phase using an
1180  * xml file
1181  *
1182  * This routine is a precursor to constructPhaseFile(XML_Node*)
1183  * routine, which does most of the work.
1184  *
1185  * @param infile XML file containing the description of the
1186  * phase
1187  *
1188  * @param id Optional parameter identifying the name of the
1189  * phase. If none is given, the first XML
1190  * phase element will be used.
1191  */
1193 constructPhaseFile(std::string inputFile, std::string id)
1194 {
1195  if (inputFile.size() == 0) {
1196  throw CanteraError("IdealSolidSolnPhase::constructPhaseFile",
1197  "input file is null");
1198  }
1199  string path = findInputFile(inputFile);
1200  ifstream fin(path.c_str());
1201  if (!fin) {
1202  throw CanteraError("IdealSolidSolnPhase::constructPhaseFile","could not open "
1203  +path+" for reading.");
1204  }
1205  /*
1206  * The phase object automatically constructs an XML object.
1207  * Use this object to store information.
1208  */
1209  XML_Node& phaseNode_XML = xml();
1210  XML_Node* fxml = new XML_Node();
1211  fxml->build(fin);
1212  XML_Node* fxml_phase = findXMLPhase(fxml, id);
1213  if (!fxml_phase) {
1214  throw CanteraError("IdealSolidSolnPhase::constructPhaseFile",
1215  "ERROR: Can not find phase named " +
1216  id + " in file named " + inputFile);
1217  }
1218  fxml_phase->copy(&phaseNode_XML);
1219 
1220  constructPhaseXML(*fxml_phase, id);
1221  delete fxml;
1222 }
1223 
1224 /*
1225  * @internal
1226  * Import and initialize a ThermoPhase object
1227  * using an XML tree.
1228  * Here we read extra information about the XML description
1229  * of a phase. Regular information about elements and species
1230  * and their reference state thermodynamic information
1231  * have already been read at this point.
1232  * For example, we do not need to call this function for
1233  * ideal gas equations of state.
1234  * This function is called from importPhase()
1235  * after the elements and the
1236  * species are initialized with default ideal solution
1237  * level data.
1238  *
1239  * @param phaseNode This object must be the phase node of a
1240  * complete XML tree
1241  * description of the phase, including all of the
1242  * species data. In other words while "phase" must
1243  * point to an XML phase object, it must have
1244  * sibling nodes "speciesData" that describe
1245  * the species in the phase.
1246  * @param id ID of the phase. If nonnull, a check is done
1247  * to see if phaseNode is pointing to the phase
1248  * with the correct id.
1249  */
1250 void IdealSolidSolnPhase::initThermoXML(XML_Node& phaseNode, std::string id)
1251 {
1252  string subname = "IdealSolidSolnPhase::initThermoXML";
1253  /*
1254  * Check on the thermo field. Must have:
1255  * <thermo model="IdealSolidSolution" />
1256  */
1257  if (phaseNode.hasChild("thermo")) {
1258  XML_Node& thNode = phaseNode.child("thermo");
1259  string mStringa = thNode.attrib("model");
1260  string mString = lowercase(mStringa);
1261  if (mString != "idealsolidsolution") {
1262  throw CanteraError(subname.c_str(),
1263  "Unknown thermo model: " + mStringa);
1264  }
1265  } else {
1266  throw CanteraError(subname.c_str(),
1267  "Unspecified thermo model");
1268  }
1269 
1270  /*
1271  * Form of the standard concentrations. Must have one of:
1272  *
1273  * <standardConc model="unity" />
1274  * <standardConc model="molar_volume" />
1275  * <standardConc model="solvent_volume" />
1276  */
1277  if (phaseNode.hasChild("standardConc")) {
1278  XML_Node& scNode = phaseNode.child("standardConc");
1279  string formStringa = scNode.attrib("model");
1280  string formString = lowercase(formStringa);
1281  if (formString == "unity") {
1282  m_formGC = 0;
1283  } else if (formString == "molar_volume") {
1284  m_formGC = 1;
1285  } else if (formString == "solvent_volume") {
1286  m_formGC = 2;
1287  } else {
1288  throw CanteraError(subname.c_str(),
1289  "Unknown standardConc model: " + formStringa);
1290  }
1291  } else {
1292  throw CanteraError(subname.c_str(), "Unspecified standardConc model");
1293  }
1294 
1295  /*
1296  * Initialize all of the lengths now that we know how many species
1297  * there are in the phase.
1298  */
1299  initLengths();
1300  /*
1301  * Now go get the molar volumes
1302  */
1303  XML_Node& speciesList = phaseNode.child("speciesArray");
1304  XML_Node* speciesDB = get_XML_NameID("speciesData", speciesList["datasrc"],
1305  &phaseNode.root());
1306  const vector<string>&sss = speciesNames();
1307 
1308  for (size_t k = 0; k < m_kk; k++) {
1309  XML_Node* s = speciesDB->findByAttr("name", sss[k]);
1310  XML_Node* ss = s->findByName("standardState");
1311  m_speciesMolarVolume[k] = ctml::getFloat(*ss, "molarVolume", "toSI");
1312  }
1313 
1314  /*
1315  * Call the base initThermo, which handles setting the initial
1316  * state.
1317  */
1318  ThermoPhase::initThermoXML(phaseNode, id);
1319 }
1320 
1321 /*
1322  * This internal function adjusts the lengths of arrays
1323  */
1326 {
1327  m_kk = nSpecies();
1328  m_mm = nElements();
1329 
1330  /*
1331  * Obtain the limits of the temperature from the species
1332  * thermo handler's limits.
1333  */
1334  doublereal tmin = m_spthermo->minTemp();
1335  doublereal tmax = m_spthermo->maxTemp();
1336  if (tmin > 0.0) {
1337  m_tmin = tmin;
1338  }
1339  if (tmax > 0.0) {
1340  m_tmax = tmax;
1341  }
1342 
1343  /*
1344  * Obtain the reference pressure by calling the ThermoPhase
1345  * function refPressure, which in turm calls the
1346  * species thermo reference pressure function of the
1347  * same name.
1348  */
1349  m_Pref = refPressure();
1350 
1351  m_h0_RT.resize(m_kk);
1352  m_g0_RT.resize(m_kk);
1353  m_expg0_RT.resize(m_kk);
1354  m_cp0_R.resize(m_kk);
1355  m_s0_R.resize(m_kk);
1356  m_pe.resize(m_kk, 0.0);
1357  m_pp.resize(m_kk);
1358  m_speciesMolarVolume.resize(m_kk);
1359 }
1360 
1361 /*
1362  * Set mixture to an equilibrium state consistent with specified
1363  * element potentials and temperature.
1364  *
1365  * @param lambda_RT vector of non-dimensional element potentials
1366  * \f$ \lambda_m/RT \f$.
1367  *
1368  */
1370 setToEquilState(const doublereal* lambda_RT)
1371 {
1372  const vector_fp& grt = gibbs_RT_ref();
1373 
1374  // set the pressure and composition to be consistent with
1375  // the temperature,
1376  doublereal pres = 0.0;
1377  for (size_t k = 0; k < m_kk; k++) {
1378  m_pp[k] = -grt[k];
1379  for (size_t m = 0; m < m_mm; m++) {
1380  m_pp[k] += nAtoms(k,m)*lambda_RT[m];
1381  }
1382  m_pp[k] = m_Pref * exp(m_pp[k]);
1383  pres += m_pp[k];
1384  }
1385  doublereal* dptr = DATA_PTR(m_pp);
1386  setState_PX(pres, dptr);
1387 }
1388 //================================================================================================
1389 /*
1390  *
1391  * speciesMolarVolume()
1392  *
1393  * Report the molar volume of species k
1394  *
1395  * units - \f$ m^3 kmol^-1 \f$
1396  */
1397 double IdealSolidSolnPhase::
1399 {
1400  return m_speciesMolarVolume[k];
1401 }
1402 
1403 /*
1404  *
1405  * getSpeciesMolarVolumes():
1406  *
1407  * Fill in a return vector containing the species molar volumes
1408  * units - \f$ m^3 kmol^-1 \f$
1409  */
1411 getSpeciesMolarVolumes(doublereal* smv) const
1412 {
1413  copy(m_speciesMolarVolume.begin(), m_speciesMolarVolume.end(), smv);
1414 }
1415 //================================================================================================
1416 /*
1417  *
1418  * _updateThermo()
1419  *
1420  * This function gets called for every call to functions in this
1421  * class. It checks to see whether the temperature has changed and
1422  * thus the reference thermodynamics functions for all of the species
1423  * must be recalculated.
1424  * If the temperature has changed, the species thermo manager is called
1425  * to recalculate G, Cp, H, and S at the current temperature.
1426  */
1429 {
1430  doublereal tnow = temperature();
1431  if (m_tlast != tnow) {
1432  /*
1433  * Update the thermodynamic functions of the reference state.
1434  */
1436  DATA_PTR(m_s0_R));
1437  m_tlast = tnow;
1438  doublereal rrt = 1.0 / (GasConstant * tnow);
1439  doublereal deltaE;
1440  for (size_t k = 0; k < m_kk; k++) {
1441  deltaE = rrt * m_pe[k];
1442  m_h0_RT[k] += deltaE;
1443  m_g0_RT[k] = m_h0_RT[k] - m_s0_R[k];
1444  }
1445  m_tlast = tnow;
1446  }
1447 }
1448 //================================================================================================
1449 } // end namespace Cantera
1450 //==================================================================================================