Cantera  2.0
RedlichKisterVPSSTP.cpp
Go to the documentation of this file.
1 /**
2  * @file RedlichKisterVPSSTP.cpp
3  * Definitions for ThermoPhase object for phases which
4  * employ excess gibbs free energy formulations related to RedlichKister
5  * expansions (see \ref thermoprops
6  * and class \link Cantera::RedlichKisterVPSSTP RedlichKisterVPSSTP\endlink).
7  *
8  */
9 /*
10  * Copyright (2009) Sandia Corporation. Under the terms of
11  * Contract DE-AC04-94AL85000 with Sandia Corporation, the
12  * U.S. Government retains certain rights in this software.
13  */
14 
18 
19 #include <iomanip>
20 #include <fstream>
21 
22 using namespace std;
23 
24 namespace Cantera
25 {
26 
27 static const double xxSmall = 1.0E-150;
28 //====================================================================================================================
29 /*
30  * Default constructor.
31  *
32  */
33 RedlichKisterVPSSTP::RedlichKisterVPSSTP() :
35  numBinaryInteractions_(0),
36  m_pSpecies_A_ij(0),
37  m_pSpecies_B_ij(0),
38  m_N_ij(0),
39  m_HE_m_ij(0),
40  m_SE_m_ij(0),
41  formRedlichKister_(0),
42  formTempModel_(0),
43  dlnActCoeff_dX_()
44 {
45 }
46 //====================================================================================================================
47 /*
48  * Working constructors
49  *
50  * The two constructors below are the normal way
51  * the phase initializes itself. They are shells that call
52  * the routine initThermo(), with a reference to the
53  * XML database to get the info for the phase.
54 
55  */
56 RedlichKisterVPSSTP::RedlichKisterVPSSTP(std::string inputFile, std::string id) :
58  numBinaryInteractions_(0),
59  m_pSpecies_A_ij(0),
60  m_pSpecies_B_ij(0),
61  m_N_ij(0),
62  m_HE_m_ij(0),
63  m_SE_m_ij(0),
64  formRedlichKister_(0),
65  formTempModel_(0),
66  dlnActCoeff_dX_()
67 {
68  constructPhaseFile(inputFile, id);
69 }
70 //====================================================================================================================
73  numBinaryInteractions_(0),
74  m_pSpecies_A_ij(0),
75  m_pSpecies_B_ij(0),
76  m_N_ij(0),
77  m_HE_m_ij(0),
78  m_SE_m_ij(0),
79  formRedlichKister_(0),
80  formTempModel_(0),
81  dlnActCoeff_dX_()
82 {
83  constructPhaseXML(phaseRoot, id);
84 }
85 //====================================================================================================================
86 // Special constructor for a hard-coded problem
87 /*
88  *
89  * LiKCl treating the PseudoBinary layer as passthrough.
90  * -> test to predict the eutectic and liquidus correctly.
91  *
92  */
95  numBinaryInteractions_(0),
96  m_pSpecies_A_ij(0),
97  m_pSpecies_B_ij(0),
98  m_N_ij(0),
99  m_HE_m_ij(0),
100  m_SE_m_ij(0),
101  formRedlichKister_(0),
102  formTempModel_(0),
103  dlnActCoeff_dX_()
104 {
105  constructPhaseFile("LiKCl_liquid.xml", "");
107 
108  m_HE_m_ij.resize(0);
109  m_SE_m_ij.resize(0);
110 
111  vector_fp he(2);
112  he[0] = 0.0;
113  he[1] = 0.0;
114  vector_fp se(2);
115  se[0] = 0.0;
116  se[1] = 0.0;
117 
118  m_HE_m_ij.push_back(he);
119  m_SE_m_ij.push_back(se);
120  m_N_ij.push_back(1);
121  m_pSpecies_A_ij.resize(1);
122  m_pSpecies_B_ij.resize(1);
123 
124  size_t iLiLi = speciesIndex("LiLi");
125  if (iLiLi == npos) {
126  throw CanteraError("RedlichKisterVPSSTP test1 constructor",
127  "Unable to find LiLi");
128  }
129  m_pSpecies_A_ij[0] = iLiLi;
130 
131 
132  size_t iVLi = speciesIndex("VLi");
133  if (iVLi == npos) {
134  throw CanteraError("RedlichKisterVPSSTP test1 constructor",
135  "Unable to find VLi");
136  }
137  m_pSpecies_B_ij[0] = iVLi;
138 
139 
140 }
141 //====================================================================================================================
142 /*
143  * Copy Constructor:
144  *
145  * Note this stuff will not work until the underlying phase
146  * has a working copy constructor
147  */
150  numBinaryInteractions_(0),
151  m_pSpecies_A_ij(0),
152  m_pSpecies_B_ij(0),
153  m_N_ij(0),
154  m_HE_m_ij(0),
155  m_SE_m_ij(0),
156  formRedlichKister_(0),
157  formTempModel_(0),
158  dlnActCoeff_dX_()
159 {
161 }
162 //====================================================================================================================
163 /*
164  * operator=()
165  *
166  * Note this stuff will not work until the underlying phase
167  * has a working assignment operator
168  */
171 {
172  if (&b == this) {
173  return *this;
174  }
175 
177 
181  m_N_ij = b.m_N_ij;
182  m_HE_m_ij = b.m_HE_m_ij;
183  m_SE_m_ij = b.m_SE_m_ij;
187 
188  return *this;
189 }
190 //====================================================================================================================
191 /*
192  *
193  * ~RedlichKisterVPSSTP(): (virtual)
194  *
195  * Destructor: does nothing:
196  *
197  */
199 {
200 }
201 //====================================================================================================================
202 /*
203  * This routine duplicates the current object and returns
204  * a pointer to ThermoPhase.
205  */
208 {
209  RedlichKisterVPSSTP* mtp = new RedlichKisterVPSSTP(*this);
210  return (ThermoPhase*) mtp;
211 }
212 
213 //====================================================================================================================
214 // Equation of state type flag.
215 /*
216  * The ThermoPhase base class returns
217  * zero. Subclasses should define this to return a unique
218  * non-zero value. Known constants defined for this purpose are
219  * listed in mix_defs.h. The RedlichKisterVPSSTP class also returns
220  * zero, as it is a non-complete class.
221  */
223 {
224  return 0;
225 }
226 //====================================================================================================================
227 /*
228  * Import, construct, and initialize a phase
229  * specification from an XML tree into the current object.
230  *
231  * This routine is a precursor to constructPhaseXML(XML_Node*)
232  * routine, which does most of the work.
233  *
234  * @param infile XML file containing the description of the
235  * phase
236  *
237  * @param id Optional parameter identifying the name of the
238  * phase. If none is given, the first XML
239  * phase element will be used.
240  */
241 void RedlichKisterVPSSTP::constructPhaseFile(std::string inputFile, std::string id)
242 {
243 
244  if ((int) inputFile.size() == 0) {
245  throw CanteraError("RedlichKisterVPSSTP:constructPhaseFile",
246  "input file is null");
247  }
248  string path = findInputFile(inputFile);
249  std::ifstream fin(path.c_str());
250  if (!fin) {
251  throw CanteraError("RedlichKisterVPSSTP:constructPhaseFile","could not open "
252  +path+" for reading.");
253  }
254  /*
255  * The phase object automatically constructs an XML object.
256  * Use this object to store information.
257  */
258  XML_Node& phaseNode_XML = xml();
259  XML_Node* fxml = new XML_Node();
260  fxml->build(fin);
261  XML_Node* fxml_phase = findXMLPhase(fxml, id);
262  if (!fxml_phase) {
263  throw CanteraError("RedlichKisterVPSSTP:constructPhaseFile",
264  "ERROR: Can not find phase named " +
265  id + " in file named " + inputFile);
266  }
267  fxml_phase->copy(&phaseNode_XML);
268  constructPhaseXML(*fxml_phase, id);
269  delete fxml;
270 }
271 //====================================================================================================================
272 /*
273  * Import, construct, and initialize a HMWSoln phase
274  * specification from an XML tree into the current object.
275  *
276  * Most of the work is carried out by the cantera base
277  * routine, importPhase(). That routine imports all of the
278  * species and element data, including the standard states
279  * of the species.
280  *
281  * Then, In this routine, we read the information
282  * particular to the specification of the activity
283  * coefficient model for the Pitzer parameterization.
284  *
285  * We also read information about the molar volumes of the
286  * standard states if present in the XML file.
287  *
288  * @param phaseNode This object must be the phase node of a
289  * complete XML tree
290  * description of the phase, including all of the
291  * species data. In other words while "phase" must
292  * point to an XML phase object, it must have
293  * sibling nodes "speciesData" that describe
294  * the species in the phase.
295  * @param id ID of the phase. If nonnull, a check is done
296  * to see if phaseNode is pointing to the phase
297  * with the correct id.
298  */
299 void RedlichKisterVPSSTP::constructPhaseXML(XML_Node& phaseNode, std::string id)
300 {
301  string stemp;
302  if ((int) id.size() > 0) {
303  string idp = phaseNode.id();
304  if (idp != id) {
305  throw CanteraError("RedlichKisterVPSSTP::constructPhaseXML",
306  "phasenode and Id are incompatible");
307  }
308  }
309 
310  /*
311  * Find the Thermo XML node
312  */
313  if (!phaseNode.hasChild("thermo")) {
314  throw CanteraError("RedlichKisterVPSSTP::constructPhaseXML",
315  "no thermo XML node");
316  }
317  XML_Node& thermoNode = phaseNode.child("thermo");
318 
319  /*
320  * Make sure that the thermo model is RedlichKister
321  */
322  stemp = thermoNode.attrib("model");
323  string formString = lowercase(stemp);
324  if (formString != "redlich-kister") {
325  throw CanteraError("RedlichKisterVPSSTP::constructPhaseXML",
326  "model name isn't Redlich-Kister: " + formString);
327 
328  }
329 
330  /*
331  * Call the Cantera importPhase() function. This will import
332  * all of the species into the phase. This will also handle
333  * all of the solvent and solute standard states
334  */
335  bool m_ok = importPhase(phaseNode, this);
336  if (!m_ok) {
337  throw CanteraError("RedlichKisterVPSSTP::constructPhaseXML","importPhase failed ");
338  }
339 
340 }
341 //====================================================================================================================
342 /*
343  * ------------ Molar Thermodynamic Properties ----------------------
344  */
345 //====================================================================================================================
346 /*
347  * - Activities, Standard States, Activity Concentrations -----------
348  */
349 //====================================================================================================================
350 
352 {
353  /*
354  * Update the activity coefficients
355  */
357 
358  /*
359  * take the exp of the internally stored coefficients.
360  */
361  for (size_t k = 0; k < m_kk; k++) {
362  lnac[k] = lnActCoeff_Scaled_[k];
363  }
364 }
365 //====================================================================================================================
366 /*
367  * ------------ Partial Molar Properties of the Solution ------------
368  */
369 //====================================================================================================================
371 {
372  getChemPotentials(mu);
373  double ve = Faraday * electricPotential();
374  for (size_t k = 0; k < m_kk; k++) {
375  mu[k] += ve*charge(k);
376  }
377 }
378 //====================================================================================================================
379 void RedlichKisterVPSSTP::getChemPotentials(doublereal* mu) const
380 {
381  doublereal xx;
382  /*
383  * First get the standard chemical potentials in
384  * molar form.
385  * -> this requires updates of standard state as a function
386  * of T and P
387  */
389  /*
390  * Update the activity coefficients
391  */
393  /*
394  *
395  */
396  doublereal RT = GasConstant * temperature();
397  for (size_t k = 0; k < m_kk; k++) {
398  xx = std::max(moleFractions_[k], xxSmall);
399  mu[k] += RT * (log(xx) + lnActCoeff_Scaled_[k]);
400  }
401 }
402 //====================================================================================================================
403 //Molar enthalpy. Units: J/kmol.
405 {
406  size_t kk = nSpecies();
407  double h = 0;
408  vector_fp hbar(kk);
409  getPartialMolarEnthalpies(&hbar[0]);
410  for (size_t i = 0; i < kk; i++) {
411  h += moleFractions_[i]*hbar[i];
412  }
413  return h;
414 }
415 //====================================================================================================================
416 /// Molar entropy. Units: J/kmol.
418 {
419  size_t kk = nSpecies();
420  double s = 0;
421  vector_fp sbar(kk);
422  getPartialMolarEntropies(&sbar[0]);
423  for (size_t i = 0; i < kk; i++) {
424  s += moleFractions_[i]*sbar[i];
425  }
426  return s;
427 }
428 //====================================================================================================================
429 /// Molar heat capacity at constant pressure. Units: J/kmol/K.
431 {
432  size_t kk = nSpecies();
433  double cp = 0;
434  vector_fp cpbar(kk);
435  getPartialMolarCp(&cpbar[0]);
436  for (size_t i = 0; i < kk; i++) {
437  cp += moleFractions_[i]*cpbar[i];
438  }
439  return cp;
440 }
441 //====================================================================================================================
442 /// Molar heat capacity at constant volume. Units: J/kmol/K.
444 {
445  return cp_mole() - GasConstant;
446 }
447 //====================================================================================================================
448 // Returns an array of partial molar enthalpies for the species
449 // in the mixture.
450 /*
451  * Units (J/kmol)
452  *
453  * For this phase, the partial molar enthalpies are equal to the
454  * standard state enthalpies modified by the derivative of the
455  * molality-based activity coefficient wrt temperature
456  *
457  * \f[
458  * \bar h_k(T,P) = h^o_k(T,P) - R T^2 \frac{d \ln(\gamma_k)}{dT}
459  * \f]
460  *
461  */
463 {
464  /*
465  * Get the nondimensional standard state enthalpies
466  */
467  getEnthalpy_RT(hbar);
468  /*
469  * dimensionalize it.
470  */
471  double T = temperature();
472  double RT = GasConstant * T;
473  for (size_t k = 0; k < m_kk; k++) {
474  hbar[k] *= RT;
475  }
476  /*
477  * Update the activity coefficients, This also update the
478  * internally stored molalities.
479  */
482  double RTT = RT * T;
483  for (size_t k = 0; k < m_kk; k++) {
484  hbar[k] -= RTT * dlnActCoeffdT_Scaled_[k];
485  }
486 }
487 //====================================================================================================================
488 // Returns an array of partial molar heat capacities for the species
489 // in the mixture.
490 /*
491  * Units (J/kmol)
492  *
493  * For this phase, the partial molar enthalpies are equal to the
494  * standard state enthalpies modified by the derivative of the
495  * activity coefficient wrt temperature
496  *
497  * \f[
498  * ??????????? \bar s_k(T,P) = s^o_k(T,P) - R T^2 \frac{d \ln(\gamma_k)}{dT}
499  * \f]
500  *
501  */
502 void RedlichKisterVPSSTP::getPartialMolarCp(doublereal* cpbar) const
503 {
504  /*
505  * Get the nondimensional standard state entropies
506  */
507  getCp_R(cpbar);
508  double T = temperature();
509  /*
510  * Update the activity coefficients, This also update the
511  * internally stored molalities.
512  */
515 
516  for (size_t k = 0; k < m_kk; k++) {
517  cpbar[k] -= 2 * T * dlnActCoeffdT_Scaled_[k] + T * T * d2lnActCoeffdT2_Scaled_[k];
518  }
519  /*
520  * dimensionalize it.
521  */
522  for (size_t k = 0; k < m_kk; k++) {
523  cpbar[k] *= GasConstant;
524  }
525 }
526 //====================================================================================================================
527 // Returns an array of partial molar entropies for the species
528 // in the mixture.
529 /*
530  * Units (J/kmol)
531  *
532  * For this phase, the partial molar enthalpies are equal to the
533  * standard state enthalpies modified by the derivative of the
534  * activity coefficient wrt temperature
535  *
536  * \f[
537  * \bar s_k(T,P) = s^o_k(T,P) - R T^2 \frac{d \ln(\gamma_k)}{dT}
538  * \f]
539  *
540  */
542 {
543  double xx;
544  /*
545  * Get the nondimensional standard state entropies
546  */
547  getEntropy_R(sbar);
548  double T = temperature();
549  /*
550  * Update the activity coefficients, This also update the
551  * internally stored molalities.
552  */
555 
556  for (size_t k = 0; k < m_kk; k++) {
557  xx = std::max(moleFractions_[k], xxSmall);
558  sbar[k] += - lnActCoeff_Scaled_[k] -log(xx) - T * dlnActCoeffdT_Scaled_[k];
559  }
560  /*
561  * dimensionalize it.
562  */
563  for (size_t k = 0; k < m_kk; k++) {
564  sbar[k] *= GasConstant;
565  }
566 }
567 
568 /*
569  * ------------ Partial Molar Properties of the Solution ------------
570  */
571 //====================================================================================================================
572 // Return an array of partial molar volumes for the
573 // species in the mixture. Units: m^3/kmol.
574 /*
575  * Frequently, for this class of thermodynamics representations,
576  * the excess Volume due to mixing is zero. Here, we set it as
577  * a default. It may be overridden in derived classes.
578  *
579  * @param vbar Output vector of species partial molar volumes.
580  * Length = m_kk. units are m^3/kmol.
581  */
583 {
584  /*
585  * Get the standard state values in m^3 kmol-1
586  */
587  getStandardVolumes(vbar);
588  for (size_t iK = 0; iK < m_kk; iK++) {
589 
590  vbar[iK] += 0.0;
591  }
592 }
593 //====================================================================================================================
594 doublereal RedlichKisterVPSSTP::err(std::string msg) const
595 {
596  throw CanteraError("RedlichKisterVPSSTP","Base class method "
597  +msg+" called. Equation of state type: "+int2str(eosType()));
598  return 0;
599 }
600 //====================================================================================================================
601 /*
602  * @internal Initialize. This method is provided to allow
603  * subclasses to perform any initialization required after all
604  * species have been added. For example, it might be used to
605  * resize internal work arrays that must have an entry for
606  * each species. The base class implementation does nothing,
607  * and subclasses that do not require initialization do not
608  * need to overload this method. When importing a CTML phase
609  * description, this method is called just prior to returning
610  * from function importPhase.
611  *
612  * @see importCTML.cpp
613  */
615 {
616  initLengths();
618 }
619 //====================================================================================================================
620 // Initialize lengths of local variables after all species have
621 // been identified.
623 {
624  m_kk = nSpecies();
626 }
627 //====================================================================================================================
628 /*
629  * initThermoXML() (virtual from ThermoPhase)
630  * Import and initialize a ThermoPhase object
631  *
632  * @param phaseNode This object must be the phase node of a complete XML tree
633  * description of the phase, including all of the species data. In other words while "phase" must
634  * point to an XML phase object, it must have sibling nodes "speciesData" that describe
635  * the species in the phase.
636  * @param id ID of the phase. If nonnull, a check is done to see if phaseNode is pointing to the phase
637  * with the correct id.
638  */
639 void RedlichKisterVPSSTP::initThermoXML(XML_Node& phaseNode, std::string id)
640 {
641  std::string subname = "RedlichKisterVPSSTP::initThermoXML";
642  std::string stemp;
643 
644  /*
645  * Check on the thermo field. Must have:
646  * <thermo model="IdealSolidSolution" />
647  */
648 
649  XML_Node& thermoNode = phaseNode.child("thermo");
650  std::string mStringa = thermoNode.attrib("model");
651  std::string mString = lowercase(mStringa);
652  if (mString != "redlich-kister") {
653  throw CanteraError(subname.c_str(),
654  "Unknown thermo model: " + mStringa + " - This object only knows \"Redlich-Kister\" ");
655  }
656 
657  /*
658  * Go get all of the coefficients and factors in the
659  * activityCoefficients XML block
660  */
661  /*
662  * Go get all of the coefficients and factors in the
663  * activityCoefficients XML block
664  */
665  XML_Node* acNodePtr = 0;
666  if (thermoNode.hasChild("activityCoefficients")) {
667  XML_Node& acNode = thermoNode.child("activityCoefficients");
668  acNodePtr = &acNode;
669  std::string mStringa = acNode.attrib("model");
670  std::string mString = lowercase(mStringa);
671  if (mString != "redlich-kister") {
672  throw CanteraError(subname.c_str(),
673  "Unknown activity coefficient model: " + mStringa);
674  }
675  size_t n = acNodePtr->nChildren();
676  for (size_t i = 0; i < n; i++) {
677  XML_Node& xmlACChild = acNodePtr->child(i);
678  stemp = xmlACChild.name();
679  std::string nodeName = lowercase(stemp);
680  /*
681  * Process a binary salt field, or any of the other XML fields
682  * that make up the Pitzer Database. Entries will be ignored
683  * if any of the species in the entry isn't in the solution.
684  */
685  if (nodeName == "binaryneutralspeciesparameters") {
686  readXMLBinarySpecies(xmlACChild);
687  }
688  }
689  }
690  /*
691  * Go down the chain
692  */
693  GibbsExcessVPSSTP::initThermoXML(phaseNode, id);
694 }
695 //===================================================================================================================
696 // Update the activity coefficients
697 /*
698  * This function will be called to update the internally stored
699  * natural logarithm of the activity coefficients
700  *
701  */
703 {
704  doublereal XA, XB;
705  doublereal T = temperature();
706  doublereal RT = GasConstant * T;
707 
708  lnActCoeff_Scaled_.assign(m_kk, 0.0);
709 
710  /*
711  * Scaling: I moved the division of RT higher so that we are always dealing with G/RT dimensionless terms
712  * within the routine. There is a severe problem with roundoff error in these calculations. The
713  * dimensionless terms help.
714  */
715 
716  for (size_t i = 0; i < numBinaryInteractions_; i++) {
717  size_t iA = m_pSpecies_A_ij[i];
718  size_t iB = m_pSpecies_B_ij[i];
719  XA = moleFractions_[iA];
720  XB = moleFractions_[iB];
721  doublereal deltaX = XA - XB;
722  size_t N = m_N_ij[i];
723  vector_fp& he_vec = m_HE_m_ij[i];
724  vector_fp& se_vec = m_SE_m_ij[i];
725  doublereal poly = 1.0;
726  doublereal polyMm1 = 1.0;
727  doublereal sum = 0.0;
728  doublereal sumMm1 = 0.0;
729  doublereal sum2 = 0.0;
730  for (size_t m = 0; m < N; m++) {
731  doublereal A_ge = (he_vec[m] - T * se_vec[m]) / RT;
732  sum += A_ge * poly;
733  sum2 += A_ge * (m + 1) * poly;
734  poly *= deltaX;
735  if (m >= 1) {
736  sumMm1 += (A_ge * polyMm1 * m);
737  polyMm1 *= deltaX;
738  }
739  }
740  doublereal oneMXA = 1.0 - XA;
741  doublereal oneMXB = 1.0 - XB;
742  for (size_t k = 0; k < m_kk; k++) {
743  if (iA == k) {
744  lnActCoeff_Scaled_[k] += (oneMXA * XB * sum) + (XA * XB * sumMm1 * (oneMXA + XB));
745  } else if (iB == k) {
746  lnActCoeff_Scaled_[k] += (oneMXB * XA * sum) + (XA * XB * sumMm1 * (-oneMXB - XA));
747  } else {
748  lnActCoeff_Scaled_[k] += -(XA * XB * sum2);
749  }
750  }
751  // Debug against formula in literature
752 #ifdef DEBUG_MODE_NOT
753  double lnA = 0.0;
754  double lnB = 0.0;
755  double polyk = 1.0;
756  double fac = 2.0 * XA - 1.0;
757  for (int m = 0; m < N; m++) {
758  doublereal A_ge = (he_vec[m] - T * se_vec[m]) / RT;
759  lnA += A_ge * oneMXA * oneMXA * polyk * (1.0 + 2.0 * XA * m / fac);
760  lnB += A_ge * XA * XA * polyk * (1.0 - 2.0 * oneMXA * m / fac);
761  polyk *= fac;
762  }
763  // This gives the same result as above
764  // printf("RT lnActCoeff_Scaled_[iA] = %15.8E , lnA = %15.8E\n", lnActCoeff_Scaled_[iA], lnA);
765  // printf("RT lnActCoeff_Scaled_[iB] = %15.8E , lnB = %15.8E\n", lnActCoeff_Scaled_[iB], lnB);
766 
767 #endif
768 
769  }
770 
771 }
772 //===================================================================================================================
773 // Update the derivative of the log of the activity coefficients wrt T
774 /*
775  * This function will be called to update the internally stored
776  * natural logarithm of the activity coefficients
777  *
778 
779  */
781 {
782  doublereal XA, XB;
783  // doublereal T = temperature();
784 
785  dlnActCoeffdT_Scaled_.assign(m_kk, 0.0);
786  d2lnActCoeffdT2_Scaled_.assign(m_kk, 0.0);
787 
788  for (size_t i = 0; i < numBinaryInteractions_; i++) {
789  size_t iA = m_pSpecies_A_ij[i];
790  size_t iB = m_pSpecies_B_ij[i];
791  XA = moleFractions_[iA];
792  XB = moleFractions_[iB];
793  doublereal deltaX = XA - XB;
794  size_t N = m_N_ij[i];
795  doublereal poly = 1.0;
796  doublereal sum = 0.0;
797 
798  vector_fp& se_vec = m_SE_m_ij[i];
799  doublereal sumMm1 = 0.0;
800  doublereal polyMm1 = 1.0;
801  doublereal sum2 = 0.0;
802  for (size_t m = 0; m < N; m++) {
803  doublereal A_ge = - se_vec[m];
804  sum += A_ge * poly;
805  sum2 += A_ge * (m + 1) * poly;
806  poly *= deltaX;
807  if (m >= 1) {
808  sumMm1 += (A_ge * polyMm1 * m);
809  polyMm1 *= deltaX;
810  }
811  }
812  doublereal oneMXA = 1.0 - XA;
813  doublereal oneMXB = 1.0 - XB;
814  for (size_t k = 0; k < m_kk; k++) {
815  if (iA == k) {
816  dlnActCoeffdT_Scaled_[k] += (oneMXA * XB * sum) + (XA * XB * sumMm1 * (oneMXA + XB));
817  } else if (iB == k) {
818  dlnActCoeffdT_Scaled_[k] += (oneMXB * XA * sum) + (XA * XB * sumMm1 * (-oneMXB - XA));
819  } else {
820  dlnActCoeffdT_Scaled_[k] += -(XA * XB * sum2);
821  }
822  }
823  }
824 }
825 //====================================================================================================================
826 void RedlichKisterVPSSTP::getdlnActCoeffdT(doublereal* dlnActCoeffdT) const
827 {
829  for (size_t k = 0; k < m_kk; k++) {
830  dlnActCoeffdT[k] = dlnActCoeffdT_Scaled_[k];
831  }
832 }
833 //====================================================================================================================
834 void RedlichKisterVPSSTP::getd2lnActCoeffdT2(doublereal* d2lnActCoeffdT2) const
835 {
837  for (size_t k = 0; k < m_kk; k++) {
838  d2lnActCoeffdT2[k] = d2lnActCoeffdT2_Scaled_[k];
839  }
840 }
841 //====================================================================================================================
843 {
844  doublereal XA, XB;
845  doublereal T = temperature();
846 
848 
849  for (size_t i = 0; i < numBinaryInteractions_; i++) {
850  size_t iA = m_pSpecies_A_ij[i];
851  size_t iB = m_pSpecies_B_ij[i];
852  XA = moleFractions_[iA];
853  XB = moleFractions_[iB];
854  doublereal deltaX = XA - XB;
855  size_t N = m_N_ij[i];
856  doublereal poly = 1.0;
857  doublereal sum = 0.0;
858  vector_fp& he_vec = m_HE_m_ij[i];
859  vector_fp& se_vec = m_SE_m_ij[i];
860  doublereal sumMm1 = 0.0;
861  doublereal polyMm1 = 1.0;
862  doublereal polyMm2 = 1.0;
863  doublereal sum2 = 0.0;
864  doublereal sum2Mm1 = 0.0;
865  doublereal sumMm2 = 0.0;
866  for (size_t m = 0; m < N; m++) {
867  doublereal A_ge = he_vec[m] - T * se_vec[m];
868  sum += A_ge * poly;
869  sum2 += A_ge * (m + 1) * poly;
870  poly *= deltaX;
871  if (m >= 1) {
872  sumMm1 += (A_ge * polyMm1 * m);
873  sum2Mm1 += (A_ge * polyMm1 * m * (1.0 + m));
874  polyMm1 *= deltaX;
875  }
876  if (m >= 2) {
877  sumMm2 += (A_ge * polyMm2 * m * (m - 1.0));
878  polyMm2 *= deltaX;
879  }
880  }
881 
882  for (size_t k = 0; k < m_kk; k++) {
883  if (iA == k) {
884 
885  dlnActCoeff_dX_(k, iA) += (- XB * sum + (1.0 - XA) * XB * sumMm1
886  + XB * sumMm1 * (1.0 - 2.0 * XA + XB)
887  + XA * XB * sumMm2 * (1.0 - XA + XB));
888 
889  dlnActCoeff_dX_(k, iB) += ((1.0 - XA) * sum - (1.0 - XA) * XB * sumMm1
890  + XA * sumMm1 * (1.0 + 2.0 * XB - XA)
891  - XA * XB * sumMm2 * (1.0 - XA + XB));
892 
893  } else if (iB == k) {
894 
895  dlnActCoeff_dX_(k, iA) += ((1.0 - XB) * sum + (1.0 - XA) * XB * sumMm1
896  + XB * sumMm1 * (1.0 - 2.0 * XA + XB)
897  + XA * XB * sumMm2 * (1.0 - XA + XB));
898 
899  dlnActCoeff_dX_(k, iB) += (- XA * sum - (1.0 - XB) * XA * sumMm1
900  + XA * sumMm1 * (XB - XA - (1.0 - XB))
901  - XA * XB * sumMm2 * (-XA - (1.0 - XB)));
902  } else {
903 
904  dlnActCoeff_dX_(k, iA) += (- XB * sum2 - XA * XB * sum2Mm1);
905 
906  dlnActCoeff_dX_(k, iB) += (- XA * sum2 + XA * XB * sum2Mm1);
907 
908  }
909  }
910  }
911 }
912 //====================================================================================================================
913 // Get the change in activity coefficients w.r.t. change in state (temp, mole fraction, etc.) along
914 // a line in parameter space or along a line in physical space
915 /*
916  *
917  * @param dTds Input of temperature change along the path
918  * @param dXds Input vector of changes in mole fraction along the path. length = m_kk
919  * Along the path length it must be the case that the mole fractions sum to one.
920  * @param dlnActCoeffds Output vector of the directional derivatives of the
921  * log Activity Coefficients along the path. length = m_kk
922  * units are 1/units(s). if s is a physical coordinate then the units are 1/m.
923  */
924 void RedlichKisterVPSSTP::getdlnActCoeffds(const doublereal dTds, const doublereal* const dXds,
925  doublereal* dlnActCoeffds) const
926 {
929  for (size_t k = 0; k < m_kk; k++) {
930  dlnActCoeffds[k] = dlnActCoeffdT_Scaled_[k] * dTds;
931  for (size_t l = 0; l < m_kk; l++) {
932  dlnActCoeffds[k] += dlnActCoeff_dX_(k, l) * dXds[l];
933  }
934  }
935 }
936 
937 //====================================================================================================================
938 void RedlichKisterVPSSTP::getdlnActCoeffdlnN_diag(doublereal* dlnActCoeffdlnN_diag) const
939 {
941  for (size_t l = 0; l < m_kk; l++) {
942  dlnActCoeffdlnN_diag[l] = dlnActCoeff_dX_(l, l);
943  for (size_t k = 0; k < m_kk; k++) {
944  dlnActCoeffdlnN_diag[k] -= dlnActCoeff_dX_(l, k) * moleFractions_[k];
945  }
946  }
947 }
948 //====================================================================================================================
949 void RedlichKisterVPSSTP::getdlnActCoeffdlnX_diag(doublereal* dlnActCoeffdlnX_diag) const
950 {
952  for (size_t k = 0; k < m_kk; k++) {
953  dlnActCoeffdlnX_diag[k] = dlnActCoeffdlnX_diag_[k];
954  }
955 }
956 //====================================================================================================================
957 void RedlichKisterVPSSTP::getdlnActCoeffdlnN(const size_t ld, doublereal* dlnActCoeffdlnN)
958 {
960  double* data = & dlnActCoeffdlnN_(0,0);
961  for (size_t k = 0; k < m_kk; k++) {
962  for (size_t m = 0; m < m_kk; m++) {
963  dlnActCoeffdlnN[ld * k + m] = data[m_kk * k + m];
964  }
965  }
966 }
967 //====================================================================================================================
969 {
971  m_pSpecies_A_ij.resize(num, npos);
972  m_pSpecies_B_ij.resize(num, npos);
973  m_N_ij.resize(num, npos);
974  m_HE_m_ij.resize(num);
975  m_SE_m_ij.resize(num);
976  dlnActCoeff_dX_.resize(num, num, 0.0);
977 }
978 //====================================================================================================================
979 // Process an XML node called "binaryNeutralSpeciesParameters"
980 /*
981  * This node contains all of the parameters necessary to describe the RedlichKister Interaction for
982  * a single binary interaction. This function reads the XML file and writes the coefficients
983  * it finds to an internal data structures.
984  */
986 {
987  std::string xname = xmLBinarySpecies.name();
988  if (xname != "binaryNeutralSpeciesParameters") {
989  throw CanteraError("RedlichKisterVPSSTP::readXMLBinarySpecies",
990  "Incorrect name for processing this routine: " + xname);
991  }
992  double* charge = DATA_PTR(m_speciesCharge);
993  std::string stemp;
994  size_t Npoly = 0;
995  vector_fp hParams, sParams, vParams;
996  std::string iName = xmLBinarySpecies.attrib("speciesA");
997  if (iName == "") {
998  throw CanteraError("RedlichKisterVPSSTP::readXMLBinarySpecies", "no speciesA attrib");
999  }
1000  std::string jName = xmLBinarySpecies.attrib("speciesB");
1001  if (jName == "") {
1002  throw CanteraError("RedlichKisterVPSSTP::readXMLBinarySpecies", "no speciesB attrib");
1003  }
1004  /*
1005  * Find the index of the species in the current phase. It's not
1006  * an error to not find the species. This means that the interaction doesn't occur for the current
1007  * implementation of the phase.
1008  */
1009  size_t iSpecies = speciesIndex(iName);
1010  if (iSpecies == npos) {
1011  return;
1012  }
1013  string ispName = speciesName(iSpecies);
1014  if (charge[iSpecies] != 0) {
1015  throw CanteraError("RedlichKisterVPSSTP::readXMLBinarySpecies", "speciesA charge problem");
1016  }
1017  size_t jSpecies = speciesIndex(jName);
1018  if (jSpecies == npos) {
1019  return;
1020  }
1021  std::string jspName = speciesName(jSpecies);
1022  if (charge[jSpecies] != 0) {
1023  throw CanteraError("RedlichKisterVPSSTP::readXMLBinarySpecies", "speciesB charge problem");
1024  }
1025  /*
1026  * Ok we have found a valid interaction
1027  */
1029  size_t iSpot = numBinaryInteractions_ - 1;
1032  m_pSpecies_A_ij[iSpot] = iSpecies;
1033  m_pSpecies_B_ij[iSpot] = jSpecies;
1034 
1035  size_t num = xmLBinarySpecies.nChildren();
1036  for (size_t iChild = 0; iChild < num; iChild++) {
1037  XML_Node& xmlChild = xmLBinarySpecies.child(iChild);
1038  stemp = xmlChild.name();
1039  string nodeName = lowercase(stemp);
1040  /*
1041  * Process the binary species interaction child elements
1042  */
1043  if (nodeName == "excessenthalpy") {
1044  /*
1045  * Get the string containing all of the values
1046  */
1047  ctml::getFloatArray(xmlChild, hParams, true, "toSI", "excessEnthalpy");
1048  size_t nParamsFound = hParams.size();
1049  if (nParamsFound > Npoly) {
1050  Npoly = nParamsFound;
1051  }
1052 
1053  }
1054 
1055  if (nodeName == "excessentropy") {
1056  /*
1057  * Get the string containing all of the values
1058  */
1059  ctml::getFloatArray(xmlChild, sParams, true, "toSI", "excessEntropy");
1060  size_t nParamsFound = sParams.size();
1061  if (nParamsFound > Npoly) {
1062  Npoly = nParamsFound;
1063  }
1064  }
1065  }
1066  hParams.resize(Npoly, 0.0);
1067  sParams.resize(Npoly, 0.0);
1068  m_HE_m_ij.push_back(hParams);
1069  m_SE_m_ij.push_back(sParams);
1070  m_N_ij.push_back(Npoly);
1072 }
1073 //====================================================================================================================
1074 #ifdef DEBUG_MODE
1075 void RedlichKisterVPSSTP::Vint(double& VintOut, double& voltsOut)
1076 {
1077  int iA, iB, m;
1078  doublereal XA, XB;
1079  doublereal T = temperature();
1080  doublereal RT = GasConstant * T;
1081  double Volts = 0.0;
1082 
1083  lnActCoeff_Scaled_.assign(m_kk, 0.0);
1084 
1085  for (int i = 0; i < numBinaryInteractions_; i++) {
1086  iA = m_pSpecies_A_ij[i];
1087  iB = m_pSpecies_B_ij[i];
1088  XA = moleFractions_[iA];
1089  XB = moleFractions_[iB];
1090  if (XA <= 1.0E-14) {
1091  XA = 1.0E-14;
1092  }
1093  if (XA >= (1.0 - 1.0E-14)) {
1094  XA = 1.0 - 1.0E-14;
1095  }
1096 
1097  int N = m_N_ij[i];
1098  vector_fp& he_vec = m_HE_m_ij[i];
1099  vector_fp& se_vec = m_SE_m_ij[i];
1100  double fac = 2.0 * XA - 1.0;
1101  if (fabs(fac) < 1.0E-13) {
1102  fac = 1.0E-13;
1103  }
1104  double polykp1 = fac;
1105  double poly1mk = fac;
1106 
1107  for (m = 0; m < N; m++) {
1108  doublereal A_ge = he_vec[m] - T * se_vec[m];
1109  Volts += A_ge * (polykp1 - (2.0 * XA * m * (1.0-XA)) / poly1mk);
1110  polykp1 *= fac;
1111  poly1mk /= fac;
1112  }
1113  }
1114  Volts /= Faraday;
1115 
1116  double termp = RT * log((1.0 - XA)/XA) / Faraday;
1117 
1118  VintOut = Volts;
1119  voltsOut = Volts + termp;
1120 }
1121 #endif
1122 //====================================================================================================================
1123 }
1124