Cantera  2.0
MolalityVPSSTP.cpp
Go to the documentation of this file.
1 /**
2  * @file MolalityVPSSTP.cpp
3  * Definitions for intermediate ThermoPhase object for phases which
4  * employ molality based activity coefficient formulations
5  * (see \ref thermoprops
6  * and class \link Cantera::MolalityVPSSTP MolalityVPSSTP\endlink).
7  *
8  * Header file for a derived class of ThermoPhase that handles
9  * variable pressure standard state methods for calculating
10  * thermodynamic properties that are further based upon activities
11  * based on the molality scale. These include most of the methods for
12  * calculating liquid electrolyte thermodynamics.
13  */
14 /*
15  * Copyright (2005) Sandia Corporation. Under the terms of
16  * Contract DE-AC04-94AL85000 with Sandia Corporation, the
17  * U.S. Government retains certain rights in this software.
18  */
21 
22 #include <iomanip>
23 #include <cstdio>
24 #include <fstream>
25 
26 using namespace std;
27 
28 namespace Cantera
29 {
30 
31 /*
32  * Default constructor.
33  *
34  * This doesn't do much more than initialize constants with
35  * default values for water at 25C. Water molecular weight
36  * comes from the default elements.xml file. It actually
37  * differs slightly from the IAPWS95 value of 18.015268. However,
38  * density conservation and therefore element conservation
39  * is the more important principle to follow.
40  */
41 MolalityVPSSTP::MolalityVPSSTP() :
43  m_indexSolvent(0),
44  m_pHScalingType(PHSCALE_PITZER),
45  m_indexCLM(npos),
46  m_weightSolvent(18.01528),
47  m_xmolSolventMIN(0.01),
48  m_Mnaught(18.01528E-3)
49 {
50  /*
51  * Change the default to be that charge neutrality in the
52  * phase is necessary condition for the proper specification
53  * of thermodynamic functions within the phase
54  */
56 }
57 
58 /*
59  * Copy Constructor:
60  *
61  * Note this stuff will not work until the underlying phase
62  * has a working copy constructor
63  */
66  m_indexSolvent(b.m_indexSolvent),
67  m_pHScalingType(b.m_pHScalingType),
68  m_indexCLM(b.m_indexCLM),
69  m_xmolSolventMIN(b.m_xmolSolventMIN),
70  m_Mnaught(b.m_Mnaught),
71  m_molalities(b.m_molalities)
72 {
73  *this = operator=(b);
74 }
75 
76 /*
77  * operator=()
78  *
79  * Note this stuff will not work until the underlying phase
80  * has a working assignment operator
81  */
84 {
85  if (&b != this) {
92  m_Mnaught = b.m_Mnaught;
94  }
95  return *this;
96 }
97 
98 /**
99  *
100  * ~MolalityVPSSTP(): (virtual)
101  *
102  * Destructor: does nothing:
103  *
104  */
106 {
107 }
108 
109 /*
110  * This routine duplicates the current object and returns
111  * a pointer to ThermoPhase.
112  */
115 {
116  MolalityVPSSTP* mtp = new MolalityVPSSTP(*this);
117  return (ThermoPhase*) mtp;
118 }
119 
120 /*
121  * -------------- Utilities -------------------------------
122  */
123 
124 // Equation of state type flag.
125 /*
126  * The ThermoPhase base class returns
127  * zero. Subclasses should define this to return a unique
128  * non-zero value. Known constants defined for this purpose are
129  * listed in mix_defs.h. The MolalityVPSSTP class also returns
130  * zero, as it is a non-complete class.
131  */
133 {
134  return 0;
135 }
136 
137 // Set the pH scale, which determines the scale for single-ion activity
138 // coefficients.
139 /*
140  * Single ion activity coefficients are not unique in terms of the
141  * representing actual measurable quantities.
142  */
143 void MolalityVPSSTP::setpHScale(const int pHscaleType)
144 {
145  m_pHScalingType = pHscaleType;
146  if (pHscaleType != PHSCALE_PITZER && pHscaleType != PHSCALE_NBS) {
147  throw CanteraError("MolalityVPSSTP::setpHScale",
148  "Unknown scale type: " + int2str(pHscaleType));
149  }
150 }
151 
152 // Reports the pH scale, which determines the scale for single-ion activity
153 // coefficients.
154 /*
155  * Single ion activity coefficients are not unique in terms of the
156  * representing actual measurable quantities.
157  */
159 {
160  return m_pHScalingType;
161 }
162 
163 /*
164  * setSolvent():
165  * Utilities for Solvent ID and Molality
166  * Here we also calculate and store the molecular weight
167  * of the solvent and the m_Mnaught parameter.
168  * @param k index of the solvent.
169  */
171 {
172  if (k >= m_kk) {
173  throw CanteraError("MolalityVPSSTP::setSolute ",
174  "bad value");
175  }
176  m_indexSolvent = k;
177  AssertThrowMsg(m_indexSolvent==0, "MolalityVPSSTP::setSolvent",
178  "Molality-based methods limit solvent id to being 0");
180  m_Mnaught = m_weightSolvent / 1000.;
181 }
182 
183 /*
184  * return the solvent id index number.
185  */
187 {
188  return m_indexSolvent;
189 }
190 
191 /*
192  * Sets the minimum mole fraction in the molality formulation. The
193  * minimum mole fraction must be in the range 0 to 0.9.
194  */
195 void MolalityVPSSTP::
196 setMoleFSolventMin(doublereal xmolSolventMIN)
197 {
198  if (xmolSolventMIN <= 0.0) {
199  throw CanteraError("MolalityVPSSTP::setSolute ", "trouble");
200  } else if (xmolSolventMIN > 0.9) {
201  throw CanteraError("MolalityVPSSTP::setSolute ", "trouble");
202  }
203  m_xmolSolventMIN = xmolSolventMIN;
204 }
205 
206 /**
207  * Returns the minimum mole fraction in the molality formulation.
208  */
210 {
211  return m_xmolSolventMIN;
212 }
213 
214 /*
215  * calcMolalities():
216  * We calculate the vector of molalities of the species
217  * in the phase and store the result internally:
218  * \f[
219  * m_i = (n_i) / (1000 * M_o * n_{o,p})
220  * \f]
221  * where
222  * - \f$ M_o \f$ is the molecular weight of the solvent
223  * - \f$ n_o \f$ is the mole fraction of the solvent
224  * - \f$ n_i \f$ is the mole fraction of the solute.
225  * - \f$ n_{o,p} = max (n_{o, min}, n_o) \f$
226  * - \f$ n_{o,min} \f$ = minimum mole fraction of solvent allowed
227  * in the denominator.
228  */
230 {
232  double xmolSolvent = m_molalities[m_indexSolvent];
233  if (xmolSolvent < m_xmolSolventMIN) {
234  xmolSolvent = m_xmolSolventMIN;
235  }
236  double denomInv = 1.0/ (m_Mnaught * xmolSolvent);
237  for (size_t k = 0; k < m_kk; k++) {
238  m_molalities[k] *= denomInv;
239  }
240 }
241 
242 /*
243  * getMolalities():
244  * We calculate the vector of molalities of the species
245  * in the phase
246  * \f[
247  * m_i = (n_i) / (1000 * M_o * n_{o,p})
248  * \f]
249  * where
250  * - \f$ M_o \f$ is the molecular weight of the solvent
251  * - \f$ n_o \f$ is the mole fraction of the solvent
252  * - \f$ n_i \f$ is the mole fraction of the solute.
253  * - \f$ n_{o,p} = max (n_{o, min}, n_o) \f$
254  * - \f$ n_{o,min} \f$ = minimum mole fraction of solvent allowed
255  * in the denominator.
256  */
257 void MolalityVPSSTP::getMolalities(doublereal* const molal) const
258 {
259  calcMolalities();
260  for (size_t k = 0; k < m_kk; k++) {
261  molal[k] = m_molalities[k];
262  }
263 }
264 
265 /*
266  * setMolalities():
267  * We are supplied with the molalities of all of the
268  * solute species. We then calculate the mole fractions of all
269  * species and update the ThermoPhase object.
270  *
271  * m_i = (n_i) / (W_o/1000 * n_o_p)
272  *
273  * where M_o is the molecular weight of the solvent
274  * n_o is the mole fraction of the solvent
275  * n_i is the mole fraction of the solute.
276  * n_o_p = max (n_o_min, n_o)
277  * n_o_min = minimum mole fraction of solvent allowed
278  * in the denominator.
279  */
280 void MolalityVPSSTP::setMolalities(const doublereal* const molal)
281 {
282 
283  double Lsum = 1.0 / m_Mnaught;
284  for (size_t k = 1; k < m_kk; k++) {
285  m_molalities[k] = molal[k];
286  Lsum += molal[k];
287  }
288  double tmp = 1.0 / Lsum;
290  double sum = m_molalities[m_indexSolvent];
291  for (size_t k = 1; k < m_kk; k++) {
292  m_molalities[k] = tmp * molal[k];
293  sum += m_molalities[k];
294  }
295  if (sum != 1.0) {
296  tmp = 1.0 / sum;
297  for (size_t k = 0; k < m_kk; k++) {
298  m_molalities[k] *= tmp;
299  }
300  }
302  /*
303  * Essentially we don't trust the input: We calculate
304  * the molalities from the mole fractions that we
305  * just obtained.
306  */
307  calcMolalities();
308 }
309 
310 /*
311  * setMolalitiesByName()
312  *
313  * This routine sets the molalities by name
314  * HKM -> Might need to be more complicated here, setting
315  * neutrals so that the existing mole fractions are
316  * preserved.
317  */
319 {
320  size_t kk = nSpecies();
321  doublereal x;
322  /*
323  * Get a vector of mole fractions
324  */
325  vector_fp mf(kk, 0.0);
327  double xmolS = mf[m_indexSolvent];
328  double xmolSmin = std::max(xmolS, m_xmolSolventMIN);
329  compositionMap::iterator p;
330  for (size_t k = 0; k < kk; k++) {
331  p = mMap.find(speciesName(k));
332  if (p != mMap.end()) {
333  x = mMap[speciesName(k)];
334  if (x > 0.0) {
335  mf[k] = x * m_Mnaught * xmolSmin;
336  }
337  }
338  }
339  /*
340  * check charge neutrality
341  */
342  size_t largePos = npos;
343  double cPos = 0.0;
344  size_t largeNeg = npos;
345  double cNeg = 0.0;
346  double sum = 0.0;
347  for (size_t k = 0; k < kk; k++) {
348  double ch = charge(k);
349  if (mf[k] > 0.0) {
350  if (ch > 0.0) {
351  if (ch * mf[k] > cPos) {
352  largePos = k;
353  cPos = ch * mf[k];
354  }
355  }
356  if (ch < 0.0) {
357  if (fabs(ch) * mf[k] > cNeg) {
358  largeNeg = k;
359  cNeg = fabs(ch) * mf[k];
360  }
361  }
362  }
363  sum += mf[k] * ch;
364  }
365  if (sum != 0.0) {
366  if (sum > 0.0) {
367  if (cPos > sum) {
368  mf[largePos] -= sum / charge(largePos);
369  } else {
370  throw CanteraError("MolalityVPSSTP:setMolalitiesbyName",
371  "unbalanced charges");
372  }
373  } else {
374  if (cNeg > (-sum)) {
375  mf[largeNeg] -= (-sum) / fabs(charge(largeNeg));
376  } else {
377  throw CanteraError("MolalityVPSSTP:setMolalitiesbyName",
378  "unbalanced charges");
379  }
380  }
381 
382  }
383  sum = 0.0;
384  for (size_t k = 0; k < kk; k++) {
385  sum += mf[k];
386  }
387  sum = 1.0/sum;
388  for (size_t k = 0; k < kk; k++) {
389  mf[k] *= sum;
390  }
392  /*
393  * After we formally set the mole fractions, we
394  * calculate the molalities again and store it in
395  * this object.
396  */
397  calcMolalities();
398 }
399 
400 /*
401  * setMolalitiesByNames()
402  *
403  * Set the molalities of the solutes by name
404  */
405 void MolalityVPSSTP::setMolalitiesByName(const std::string& x)
406 {
407  compositionMap xx;
408  for (size_t k = 0; k < nSpecies(); k++) {
409  xx[speciesName(k)] = -1.0;
410  }
411  parseCompString(x, xx);
413 }
414 
415 
416 /*
417  * ------------ Molar Thermodynamic Properties ----------------------
418  */
419 
420 
421 /*
422  * - Activities, Standard States, Activity Concentrations -----------
423  */
424 
425 /*
426  * This method returns the activity convention.
427  * Currently, there are two activity conventions
428  * Molar-based activities
429  * Unit activity of species at either a hypothetical pure
430  * solution of the species or at a hypothetical
431  * pure ideal solution at infinite dilution
432  * cAC_CONVENTION_MOLAR 0
433  * - default
434  *
435  * Molality based activities
436  * (unit activity of solutes at a hypothetical 1 molal
437  * solution referenced to infinite dilution at all
438  * pressures and temperatures).
439  * (solvent is still on molar basis).
440  * cAC_CONVENTION_MOLALITY 1
441  *
442  * We set the convention to molality here.
443  */
445 {
447 }
448 
450 {
451  err("getActivityConcentrations");
452 }
453 
454 doublereal MolalityVPSSTP::standardConcentration(size_t k) const
455 {
456  err("standardConcentration");
457  return -1.0;
458 }
459 
460 doublereal MolalityVPSSTP::logStandardConc(size_t k) const
461 {
462  err("logStandardConc");
463  return -1.0;
464 }
465 
466 void MolalityVPSSTP::getActivities(doublereal* ac) const
467 {
468  err("getActivities");
469 }
470 
471 /*
472  * Get the array of non-dimensional activity coefficients at
473  * the current solution temperature, pressure, and
474  * solution concentration.
475  * These are mole fraction based activity coefficients. In this
476  * object, their calculation is based on translating the values
477  * of Molality based activity coefficients.
478  * See Denbigh p. 278 for a thorough discussion.
479  *
480  * Note, the solvent is treated differently. getMolalityActivityCoeff()
481  * returns the molar based solvent activity coefficient already.
482  * Therefore, we do not have to divide by x_s here.
483  */
484 void MolalityVPSSTP::getActivityCoefficients(doublereal* ac) const
485 {
487  AssertThrow(m_indexSolvent==0, "MolalityVPSSTP::getActivityCoefficients");
488  double xmolSolvent = moleFraction(m_indexSolvent);
489  if (xmolSolvent < m_xmolSolventMIN) {
490  xmolSolvent = m_xmolSolventMIN;
491  }
492  for (size_t k = 1; k < m_kk; k++) {
493  ac[k] /= xmolSolvent;
494  }
495 }
496 
497 // Get the array of non-dimensional molality based
498 // activity coefficients at the current solution temperature,
499 // pressure, and solution concentration.
500 /*
501  * See Denbigh p. 278 for a thorough discussion. This class must be overwritten in
502  * classes which derive from %MolalityVPSSTP. This function takes over from the
503  * molar-based activity coefficient calculation, getActivityCoefficients(), in
504  * derived classes.
505  *
506  * Note these activity coefficients have the current pH scale applied to them.
507  *
508  * @param acMolality Output vector containing the molality based activity coefficients.
509  * length: m_kk.
510  */
511 void MolalityVPSSTP::getMolalityActivityCoefficients(doublereal* acMolality) const
512 {
514  applyphScale(acMolality);
515 }
516 
517 /*
518  * osmotic coefficient:
519  *
520  * Calculate the osmotic coefficient of the solvent. Note there
521  * are lots of definitions of the osmotic coefficient floating
522  * around. We use the one defined in the Pitzer's book:
523  * (Activity Coeff in Electrolyte Solutions, K. S. Pitzer
524  * CRC Press, Boca Raton, 1991, p. 85, Eqn. 28).
525  *
526  * Definition:
527  * - sum(m_i) * Mnaught * oc = ln(activity_solvent)
528  */
530 {
531  /*
532  * First, we calculate the activities all over again
533  */
534  vector_fp act(m_kk);
535  getActivities(DATA_PTR(act));
536  /*
537  * Then, we calculate the sum of the solvent molalities
538  */
539  double sum = 0;
540  for (size_t k = 1; k < m_kk; k++) {
541  sum += std::max(m_molalities[k], 0.0);
542  }
543  double oc = 1.0;
544  double lac = log(act[m_indexSolvent]);
545  if (sum > 1.0E-200) {
546  oc = - lac / (m_Mnaught * sum);
547  }
548  return oc;
549 }
550 
551 
553 {
554  getChemPotentials(mu);
555  double ve = Faraday * electricPotential();
556  for (size_t k = 0; k < m_kk; k++) {
557  mu[k] += ve*charge(k);
558  }
559 }
560 
561 /*
562  * ------------ Partial Molar Properties of the Solution ------------
563  */
564 
565 
566 doublereal MolalityVPSSTP::err(std::string msg) const
567 {
568  throw CanteraError("MolalityVPSSTP","Base class method "
569  +msg+" called. Equation of state type: "+int2str(eosType()));
570  return 0;
571 }
572 
573 /*
574  * Returns the units of the standard and general concentrations
575  * Note they have the same units, as their divisor is
576  * defined to be equal to the activity of the kth species
577  * in the solution, which is unitless.
578  *
579  * This routine is used in print out applications where the
580  * units are needed. Usually, MKS units are assumed throughout
581  * the program and in the XML input files.
582  *
583  * On return uA contains the powers of the units (MKS assumed)
584  * of the standard concentrations and generalized concentrations
585  * for the kth species.
586  *
587  * uA[0] = kmol units - default = 1
588  * uA[1] = m units - default = -nDim(), the number of spatial
589  * dimensions in the Phase class.
590  * uA[2] = kg units - default = 0;
591  * uA[3] = Pa(pressure) units - default = 0;
592  * uA[4] = Temperature units - default = 0;
593  * uA[5] = time units - default = 0
594  */
595 void MolalityVPSSTP::getUnitsStandardConc(double* uA, int k, int sizeUA) const
596 {
597  for (int i = 0; i < sizeUA; i++) {
598  if (i == 0) {
599  uA[0] = 1.0;
600  }
601  if (i == 1) {
602  uA[1] = -int(nDim());
603  }
604  if (i == 2) {
605  uA[2] = 0.0;
606  }
607  if (i == 3) {
608  uA[3] = 0.0;
609  }
610  if (i == 4) {
611  uA[4] = 0.0;
612  }
613  if (i == 5) {
614  uA[5] = 0.0;
615  }
616  }
617 }
618 
619 void MolalityVPSSTP::setToEquilState(const doublereal* lambda_RT)
620 {
622  err("setToEquilState");
623 }
624 
625 /*
626  * Set the thermodynamic state.
627  */
629 {
631  string comp = ctml::getChildValue(state,"soluteMolalities");
632  if (comp != "") {
633  setMolalitiesByName(comp);
634  }
635  if (state.hasChild("pressure")) {
636  double p = ctml::getFloat(state, "pressure", "pressure");
637  setPressure(p);
638  }
639 }
640 
641 /*
642  * Set the temperature (K), pressure (Pa), and molalities
643  * (gmol kg-1) of the solutes
644  */
645 void MolalityVPSSTP::setState_TPM(doublereal t, doublereal p,
646  const doublereal* const molalities)
647 {
648  setMolalities(molalities);
649  setState_TP(t, p);
650 }
651 
652 /*
653  * Set the temperature (K), pressure (Pa), and molalities.
654  */
655 void MolalityVPSSTP::setState_TPM(doublereal t, doublereal p, compositionMap& m)
656 {
658  setState_TP(t, p);
659 }
660 
661 /*
662  * Set the temperature (K), pressure (Pa), and molality.
663  */
664 void MolalityVPSSTP::setState_TPM(doublereal t, doublereal p, const std::string& m)
665 {
667  setState_TP(t, p);
668 }
669 
670 
671 /*
672  * @internal Initialize. This method is provided to allow
673  * subclasses to perform any initialization required after all
674  * species have been added. For example, it might be used to
675  * resize internal work arrays that must have an entry for
676  * each species. The base class implementation does nothing,
677  * and subclasses that do not require initialization do not
678  * need to overload this method. When importing a CTML phase
679  * description, this method is called just prior to returning
680  * from function importPhase.
681  *
682  * @see importCTML.cpp
683  */
685 {
686  initLengths();
688 
689  /*
690  * The solvent defaults to species 0
691  */
692  setSolvent(0);
693  /*
694  * Find the Cl- species
695  */
697 }
698 
699 // Get the array of unscaled non-dimensional molality based
700 // activity coefficients at the current solution temperature,
701 // pressure, and solution concentration.
702 /*
703  * See Denbigh p. 278 for a thorough discussion. This class must be overwritten in
704  * classes which derive from %MolalityVPSSTP. This function takes over from the
705  * molar-based activity coefficient calculation, getActivityCoefficients(), in
706  * derived classes.
707  *
708  * @param acMolality Output vector containing the molality based activity coefficients.
709  * length: m_kk.
710  */
712 {
713  err("getUnscaledMolalityActivityCoefficients");
714 }
715 
716 // Apply the current phScale to a set of activity Coefficients or activities
717 /*
718  * See the Eq3/6 Manual for a thorough discussion.
719  *
720  * @param acMolality input/Output vector containing the molality based
721  * activity coefficients. length: m_kk.
722  */
723 void MolalityVPSSTP::applyphScale(doublereal* acMolality) const
724 {
725  err("applyphScale");
726 }
727 
728 // Returns the index of the Cl- species.
729 /*
730  * The Cl- species is special in the sense that its single ion
731  * molality-based activity coefficient is used in the specification
732  * of the pH scale for single ions. Therefore, we need to know
733  * what species index Cl- is. If the species isn't in the species
734  * list then this routine returns -1, and we can't use the NBS
735  * pH scale.
736  *
737  * Right now we use a restrictive interpretation. The species
738  * must be named "Cl-". It must consist of exactly one Cl and one E
739  * atom.
740  */
742 {
743  size_t indexCLM = npos;
744  size_t eCl = npos;
745  size_t eE = npos;
746  size_t ne = nElements();
747  string sn;
748  for (size_t e = 0; e < ne; e++) {
749  sn = elementName(e);
750  if (sn == "Cl" || sn == "CL") {
751  eCl = e;
752  break;
753  }
754  }
755  // We have failed if we can't find the Cl element index
756  if (eCl == npos) {
757  return npos;
758  }
759  for (size_t e = 0; e < ne; e++) {
760  sn = elementName(e);
761  if (sn == "E" || sn == "e") {
762  eE = e;
763  break;
764  }
765  }
766  // We have failed if we can't find the E element index
767  if (eE == npos) {
768  return npos;
769  }
770  for (size_t k = 1; k < m_kk; k++) {
771  doublereal nCl = nAtoms(k, eCl);
772  if (nCl != 1.0) {
773  continue;
774  }
775  doublereal nE = nAtoms(k, eE);
776  if (nE != 1.0) {
777  continue;
778  }
779  for (size_t e = 0; e < ne; e++) {
780  if (e != eE && e != eCl) {
781  doublereal nA = nAtoms(k, e);
782  if (nA != 0.0) {
783  continue;
784  }
785  }
786  }
787  sn = speciesName(k);
788  if (sn != "Cl-" && sn != "CL-") {
789  continue;
790  }
791 
792  indexCLM = k;
793  break;
794  }
795  return indexCLM;
796 }
797 
798 // Initialize lengths of local variables after all species have
799 // been identified.
801 {
802  m_kk = nSpecies();
803  m_molalities.resize(m_kk);
804 }
805 
806 /*
807  * initThermoXML() (virtual from ThermoPhase)
808  * Import and initialize a ThermoPhase object
809  *
810  * @param phaseNode This object must be the phase node of a
811  * complete XML tree
812  * description of the phase, including all of the
813  * species data. In other words while "phase" must
814  * point to an XML phase object, it must have
815  * sibling nodes "speciesData" that describe
816  * the species in the phase.
817  * @param id ID of the phase. If nonnull, a check is done
818  * to see if phaseNode is pointing to the phase
819  * with the correct id.
820  */
821 void MolalityVPSSTP::initThermoXML(XML_Node& phaseNode, std::string id)
822 {
823 
824  initLengths();
825  /*
826  * The solvent defaults to species 0
827  */
828  setSolvent(0);
829 
830  VPStandardStateTP::initThermoXML(phaseNode, id);
831 }
832 
833 /**
834  * Format a summary of the mixture state for output.
835  */
836 std::string MolalityVPSSTP::report(bool show_thermo) const
837 {
838 
839 
840  char p[800];
841  string s = "";
842  try {
843  if (name() != "") {
844  sprintf(p, " \n %s:\n", name().c_str());
845  s += p;
846  }
847  sprintf(p, " \n temperature %12.6g K\n", temperature());
848  s += p;
849  sprintf(p, " pressure %12.6g Pa\n", pressure());
850  s += p;
851  sprintf(p, " density %12.6g kg/m^3\n", density());
852  s += p;
853  sprintf(p, " mean mol. weight %12.6g amu\n", meanMolecularWeight());
854  s += p;
855 
856  doublereal phi = electricPotential();
857  sprintf(p, " potential %12.6g V\n", phi);
858  s += p;
859 
860  size_t kk = nSpecies();
861  vector_fp x(kk);
862  vector_fp molal(kk);
863  vector_fp mu(kk);
864  vector_fp muss(kk);
865  vector_fp acMolal(kk);
866  vector_fp actMolal(kk);
867  getMoleFractions(&x[0]);
868  getMolalities(&molal[0]);
869  getChemPotentials(&mu[0]);
870  getStandardChemPotentials(&muss[0]);
871  getMolalityActivityCoefficients(&acMolal[0]);
872  getActivities(&actMolal[0]);
873 
874  size_t iHp = speciesIndex("H+");
875  if (iHp != npos) {
876  double pH = -log(actMolal[iHp]) / log(10.0);
877  sprintf(p, " pH %12.4g \n", pH);
878  s += p;
879  }
880 
881  if (show_thermo) {
882  sprintf(p, " \n");
883  s += p;
884  sprintf(p, " 1 kg 1 kmol\n");
885  s += p;
886  sprintf(p, " ----------- ------------\n");
887  s += p;
888  sprintf(p, " enthalpy %12.6g %12.4g J\n",
890  s += p;
891  sprintf(p, " internal energy %12.6g %12.4g J\n",
893  s += p;
894  sprintf(p, " entropy %12.6g %12.4g J/K\n",
896  s += p;
897  sprintf(p, " Gibbs function %12.6g %12.4g J\n",
898  gibbs_mass(), gibbs_mole());
899  s += p;
900  sprintf(p, " heat capacity c_p %12.6g %12.4g J/K\n",
901  cp_mass(), cp_mole());
902  s += p;
903  try {
904  sprintf(p, " heat capacity c_v %12.6g %12.4g J/K\n",
905  cv_mass(), cv_mole());
906  s += p;
907  } catch (CanteraError& err) {
908  err.save();
909  sprintf(p, " heat capacity c_v <not implemented> \n");
910  s += p;
911  }
912  }
913 
914  sprintf(p, " \n");
915  s += p;
916  if (show_thermo) {
917  sprintf(p, " X "
918  " Molalities Chem.Pot. ChemPotSS ActCoeffMolal\n");
919  s += p;
920  sprintf(p, " "
921  " (J/kmol) (J/kmol) \n");
922  s += p;
923  sprintf(p, " ------------- "
924  " ------------ ------------ ------------ ------------\n");
925  s += p;
926  for (size_t k = 0; k < kk; k++) {
927  if (x[k] > SmallNumber) {
928  sprintf(p, "%18s %12.6g %12.6g %12.6g %12.6g %12.6g\n",
929  speciesName(k).c_str(), x[k], molal[k], mu[k], muss[k], acMolal[k]);
930  } else {
931  sprintf(p, "%18s %12.6g %12.6g N/A %12.6g %12.6g \n",
932  speciesName(k).c_str(), x[k], molal[k], muss[k], acMolal[k]);
933  }
934  s += p;
935  }
936  } else {
937  sprintf(p, " X"
938  "Molalities\n");
939  s += p;
940  sprintf(p, " -------------"
941  " ------------\n");
942  s += p;
943  for (size_t k = 0; k < kk; k++) {
944  sprintf(p, "%18s %12.6g %12.6g\n",
945  speciesName(k).c_str(), x[k], molal[k]);
946  s += p;
947  }
948  }
949  } catch (CanteraError& err) {
950  err.save();
951  }
952  return s;
953 }
954 
955 /*
956  * Format a summary of the mixture state for output.
957  */
958 void MolalityVPSSTP::reportCSV(std::ofstream& csvFile) const
959 {
960 
961 
962  csvFile.precision(3);
963  int tabS = 15;
964  int tabM = 30;
965  int tabL = 40;
966  try {
967  if (name() != "") {
968  csvFile << "\n"+name()+"\n\n";
969  }
970  csvFile << setw(tabL) << "temperature (K) =" << setw(tabS) << temperature() << endl;
971  csvFile << setw(tabL) << "pressure (Pa) =" << setw(tabS) << pressure() << endl;
972  csvFile << setw(tabL) << "density (kg/m^3) =" << setw(tabS) << density() << endl;
973  csvFile << setw(tabL) << "mean mol. weight (amu) =" << setw(tabS) << meanMolecularWeight() << endl;
974  csvFile << setw(tabL) << "potential (V) =" << setw(tabS) << electricPotential() << endl;
975  csvFile << endl;
976 
977  csvFile << setw(tabL) << "enthalpy (J/kg) = " << setw(tabS) << enthalpy_mass() << setw(tabL) << "enthalpy (J/kmol) = " << setw(tabS) << enthalpy_mole() << endl;
978  csvFile << setw(tabL) << "internal E (J/kg) = " << setw(tabS) << intEnergy_mass() << setw(tabL) << "internal E (J/kmol) = " << setw(tabS) << intEnergy_mole() << endl;
979  csvFile << setw(tabL) << "entropy (J/kg) = " << setw(tabS) << entropy_mass() << setw(tabL) << "entropy (J/kmol) = " << setw(tabS) << entropy_mole() << endl;
980  csvFile << setw(tabL) << "Gibbs (J/kg) = " << setw(tabS) << gibbs_mass() << setw(tabL) << "Gibbs (J/kmol) = " << setw(tabS) << gibbs_mole() << endl;
981  csvFile << setw(tabL) << "heat capacity c_p (J/K/kg) = " << setw(tabS) << cp_mass() << setw(tabL) << "heat capacity c_p (J/K/kmol) = " << setw(tabS) << cp_mole() << endl;
982  csvFile << setw(tabL) << "heat capacity c_v (J/K/kg) = " << setw(tabS) << cv_mass() << setw(tabL) << "heat capacity c_v (J/K/kmol) = " << setw(tabS) << cv_mole() << endl;
983 
984  csvFile.precision(8);
985 
986  vector<std::string> pNames;
987  vector<vector_fp> data;
988  vector_fp temp(nSpecies());
989 
990  getMoleFractions(&temp[0]);
991  pNames.push_back("X");
992  data.push_back(temp);
993  try {
994  getMolalities(&temp[0]);
995  pNames.push_back("Molal");
996  data.push_back(temp);
997  } catch (CanteraError& err) {
998  err.save();
999  }
1000  try {
1001  getChemPotentials(&temp[0]);
1002  pNames.push_back("Chem. Pot. (J/kmol)");
1003  data.push_back(temp);
1004  } catch (CanteraError& err) {
1005  err.save();
1006  }
1007  try {
1008  getStandardChemPotentials(&temp[0]);
1009  pNames.push_back("Chem. Pot. SS (J/kmol)");
1010  data.push_back(temp);
1011  } catch (CanteraError& err) {
1012  err.save();
1013  }
1014  try {
1016  pNames.push_back("Molal Act. Coeff.");
1017  data.push_back(temp);
1018  } catch (CanteraError& err) {
1019  err.save();
1020  }
1021  try {
1022  getActivities(&temp[0]);
1023  pNames.push_back("Molal Activity");
1024  data.push_back(temp);
1025  size_t iHp = speciesIndex("H+");
1026  if (iHp != npos) {
1027  double pH = -log(temp[iHp]) / log(10.0);
1028  csvFile << setw(tabL) << "pH = " << setw(tabS) << pH << endl;
1029  }
1030  } catch (CanteraError& err) {
1031  err.save();
1032  }
1033  try {
1034  getPartialMolarEnthalpies(&temp[0]);
1035  pNames.push_back("Part. Mol Enthalpy (J/kmol)");
1036  data.push_back(temp);
1037  } catch (CanteraError& err) {
1038  err.save();
1039  }
1040  try {
1041  getPartialMolarEntropies(&temp[0]);
1042  pNames.push_back("Part. Mol. Entropy (J/K/kmol)");
1043  data.push_back(temp);
1044  } catch (CanteraError& err) {
1045  err.save();
1046  }
1047  try {
1048  getPartialMolarIntEnergies(&temp[0]);
1049  pNames.push_back("Part. Mol. Energy (J/kmol)");
1050  data.push_back(temp);
1051  } catch (CanteraError& err) {
1052  err.save();
1053  }
1054  try {
1055  getPartialMolarCp(&temp[0]);
1056  pNames.push_back("Part. Mol. Cp (J/K/kmol");
1057  data.push_back(temp);
1058  } catch (CanteraError& err) {
1059  err.save();
1060  }
1061  try {
1062  getPartialMolarVolumes(&temp[0]);
1063  pNames.push_back("Part. Mol. Cv (J/K/kmol)");
1064  data.push_back(temp);
1065  } catch (CanteraError& err) {
1066  err.save();
1067  }
1068 
1069  csvFile << endl << setw(tabS) << "Species,";
1070  for (size_t i = 0; i < pNames.size(); i++) {
1071  csvFile << setw(tabM) << pNames[i] << ",";
1072  }
1073  csvFile << endl;
1074  /*
1075  csvFile.fill('-');
1076  csvFile << setw(tabS+(tabM+1)*pNames.size()) << "-\n";
1077  csvFile.fill(' ');
1078  */
1079  for (size_t k = 0; k < nSpecies(); k++) {
1080  csvFile << setw(tabS) << speciesName(k) + ",";
1081  if (data[0][k] > SmallNumber) {
1082  for (size_t i = 0; i < pNames.size(); i++) {
1083  csvFile << setw(tabM) << data[i][k] << ",";
1084  }
1085  csvFile << endl;
1086  } else {
1087  for (size_t i = 0; i < pNames.size(); i++) {
1088  csvFile << setw(tabM) << 0 << ",";
1089  }
1090  csvFile << endl;
1091  }
1092  }
1093  } catch (CanteraError& err) {
1094  err.save();
1095  }
1096 }
1097 
1098 }
1099