Cantera  2.2.1
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
DebyeHuckel.cpp
Go to the documentation of this file.
1 /**
2  * @file DebyeHuckel.cpp
3  * Declarations for the DebyeHuckel ThermoPhase object, which models dilute
4  * electrolyte solutions
5  * (see \ref thermoprops and \link Cantera::DebyeHuckel DebyeHuckel \endlink).
6  *
7  * Class DebyeHuckel represents a dilute liquid electrolyte phase which
8  * obeys the Debye Huckel formulation for nonideality.
9  */
10 /*
11  * Copyright (2006) Sandia Corporation. Under the terms of
12  * Contract DE-AC04-94AL85000 with Sandia Corporation, the
13  * U.S. Government retains certain rights in this software.
14  */
15 
21 #include "cantera/base/ctml.h"
22 
23 #include <cstdio>
24 
25 using namespace std;
26 
27 namespace Cantera
28 {
29 
30 DebyeHuckel::DebyeHuckel() :
31  m_formDH(DHFORM_DILUTE_LIMIT),
32  m_formGC(2),
33  m_IionicMolality(0.0),
34  m_maxIionicStrength(30.0),
35  m_useHelgesonFixedForm(false),
36  m_IionicMolalityStoich(0.0),
37  m_form_A_Debye(A_DEBYE_CONST),
38  m_A_Debye(1.172576), // units = sqrt(kg/gmol)
39  m_B_Debye(3.28640E9), // units = sqrt(kg/gmol) / m
40  m_waterSS(0),
41  m_densWaterSS(1000.),
42  m_waterProps(0)
43 {
44 
45  m_npActCoeff.resize(3);
46  m_npActCoeff[0] = 0.1127;
47  m_npActCoeff[1] = -0.01049;
48  m_npActCoeff[2] = 1.545E-3;
49 }
50 
51 DebyeHuckel::DebyeHuckel(const std::string& inputFile,
52  const std::string& id_) :
53  m_formDH(DHFORM_DILUTE_LIMIT),
54  m_formGC(2),
55  m_IionicMolality(0.0),
56  m_maxIionicStrength(30.0),
57  m_useHelgesonFixedForm(false),
58  m_IionicMolalityStoich(0.0),
59  m_form_A_Debye(A_DEBYE_CONST),
60  m_A_Debye(1.172576), // units = sqrt(kg/gmol)
61  m_B_Debye(3.28640E9), // units = sqrt(kg/gmol) / m
62  m_waterSS(0),
63  m_densWaterSS(1000.),
64  m_waterProps(0)
65 {
66  m_npActCoeff.resize(3);
67  m_npActCoeff[0] = 0.1127;
68  m_npActCoeff[1] = -0.01049;
69  m_npActCoeff[2] = 1.545E-3;
70  initThermoFile(inputFile, id_);
71 }
72 
73 DebyeHuckel::DebyeHuckel(XML_Node& phaseRoot, const std::string& id_) :
74  m_formDH(DHFORM_DILUTE_LIMIT),
75  m_formGC(2),
76  m_IionicMolality(0.0),
77  m_maxIionicStrength(3.0),
78  m_useHelgesonFixedForm(false),
79  m_IionicMolalityStoich(0.0),
80  m_form_A_Debye(A_DEBYE_CONST),
81  m_A_Debye(1.172576), // units = sqrt(kg/gmol)
82  m_B_Debye(3.28640E9), // units = sqrt(kg/gmol) / m
83  m_waterSS(0),
84  m_densWaterSS(1000.),
85  m_waterProps(0)
86 {
87  m_npActCoeff.resize(3);
88  m_npActCoeff[0] = 0.1127;
89  m_npActCoeff[1] = -0.01049;
90  m_npActCoeff[2] = 1.545E-3;
91  importPhase(*findXMLPhase(&phaseRoot, id_), this);
92 }
93 
95  m_formDH(DHFORM_DILUTE_LIMIT),
96  m_formGC(2),
97  m_IionicMolality(0.0),
98  m_maxIionicStrength(30.0),
99  m_useHelgesonFixedForm(false),
100  m_IionicMolalityStoich(0.0),
101  m_form_A_Debye(A_DEBYE_CONST),
102  m_A_Debye(1.172576), // units = sqrt(kg/gmol)
103  m_B_Debye(3.28640E9), // units = sqrt(kg/gmol) / m
104  m_waterSS(0),
105  m_densWaterSS(1000.),
106  m_waterProps(0)
107 {
108  /*
109  * Use the assignment operator to do the brunt
110  * of the work for the copy constructor.
111  */
112  *this = b;
113 }
114 
116 {
117  if (&b != this) {
119  m_formDH = b.m_formDH;
120  m_formGC = b.m_formGC;
121  m_Aionic = b.m_Aionic;
128  m_A_Debye = b.m_A_Debye;
129  m_B_Debye = b.m_B_Debye;
130  m_B_Dot = b.m_B_Dot;
132 
133  // This is an internal shallow copy of the PDSS_Water pointer
134  m_waterSS = dynamic_cast<PDSS_Water*>(providePDSS(0)) ;
135  if (!m_waterSS) {
136  throw CanteraError("DebyHuckel::operator=()", "Dynamic cast to waterPDSS failed");
137  }
138 
140 
141  delete m_waterProps;
142  m_waterProps = 0;
143  if (b.m_waterProps) {
145  }
146 
147  m_pp = b.m_pp;
148  m_tmpV = b.m_tmpV;
150  m_Beta_ij = b.m_Beta_ij;
153  }
154  return *this;
155 }
156 
158 {
159  delete m_waterProps;
160  m_waterProps = 0;
161 }
162 
164 {
165  return new DebyeHuckel(*this);
166 }
167 
169 {
170  int res;
171  switch (m_formGC) {
172  case 0:
173  res = cDebyeHuckel0;
174  break;
175  case 1:
176  res = cDebyeHuckel1;
177  break;
178  case 2:
179  res = cDebyeHuckel2;
180  break;
181  default:
182  throw CanteraError("eosType", "Unknown type");
183  break;
184  }
185  return res;
186 }
187 
188 //
189 // -------- Molar Thermodynamic Properties of the Solution ---------------
190 //
191 doublereal DebyeHuckel::enthalpy_mole() const
192 {
194  return mean_X(m_tmpV);
195 }
196 
197 doublereal DebyeHuckel::entropy_mole() const
198 {
200  return mean_X(m_tmpV);
201 }
202 
203 doublereal DebyeHuckel::gibbs_mole() const
204 {
206  return mean_X(m_tmpV);
207 }
208 
209 doublereal DebyeHuckel::cp_mole() const
210 {
212  return mean_X(m_tmpV);
213 }
214 
215 doublereal DebyeHuckel::cv_mole() const
216 {
217  throw NotImplementedError("DebyeHuckel::cv_mole");
218  //getPartialMolarCv(m_tmpV.begin());
219  //return mean_X(m_tmpV.begin());
220 }
221 
222 //
223 // ------- Mechanical Equation of State Properties ------------------------
224 //
225 
226 doublereal DebyeHuckel::pressure() const
227 {
228  return m_Pcurrent;
229 }
230 
231 void DebyeHuckel::setPressure(doublereal p)
232 {
233  setState_TP(temperature(), p);
234 }
235 
236 void DebyeHuckel::setState_TP(doublereal t, doublereal p)
237 {
238 
240  /*
241  * Store the current pressure
242  */
243  m_Pcurrent = p;
244 
245  /*
246  * update the standard state thermo
247  * -> This involves calling the water function and setting the pressure
248  */
250 
251  /*
252  * Calculate all of the other standard volumes
253  * -> note these are constant for now
254  */
255  calcDensity();
256 }
257 
259 {
260  if (m_waterSS) {
261 
262  /*
263  * Store the internal density of the water SS.
264  * Note, we would have to do this for all other
265  * species if they had pressure dependent properties.
266  */
268  }
269  double* vbar = &m_pp[0];
271  double* x = &m_tmpV[0];
272  getMoleFractions(x);
273  doublereal vtotal = 0.0;
274  for (size_t i = 0; i < m_kk; i++) {
275  vtotal += vbar[i] * x[i];
276  }
277  doublereal dd = meanMolecularWeight() / vtotal;
278  Phase::setDensity(dd);
279 }
280 
281 void DebyeHuckel::setDensity(doublereal rho)
282 {
283  double dens = density();
284  if (rho != dens) {
285  throw CanteraError("Idea;MolalSoln::setDensity",
286  "Density is not an independent variable");
287  }
288 }
289 
290 void DebyeHuckel::setMolarDensity(const doublereal conc)
291 {
292  double concI = molarDensity();
293  if (conc != concI) {
294  throw CanteraError("Idea;MolalSoln::setMolarDensity",
295  "molarDensity/density is not an independent variable");
296  }
297 }
298 
299 void DebyeHuckel::setTemperature(const doublereal temp)
300 {
301  setState_TP(temp, m_Pcurrent);
302 }
303 
304 //
305 // ------- Activities and Activity Concentrations
306 //
307 
308 void DebyeHuckel::getActivityConcentrations(doublereal* c) const
309 {
310  double c_solvent = standardConcentration();
311  getActivities(c);
312  for (size_t k = 0; k < m_kk; k++) {
313  c[k] *= c_solvent;
314  }
315 }
316 
317 doublereal DebyeHuckel::standardConcentration(size_t k) const
318 {
319  double mvSolvent = m_speciesSize[m_indexSolvent];
320  return 1.0 / mvSolvent;
321 }
322 
323 void DebyeHuckel::getUnitsStandardConc(double* uA, int k, int sizeUA) const
324 {
325  warn_deprecated("DebyeHuckel::getUnitsStandardConc",
326  "To be removed after Cantera 2.2.");
327  for (int i = 0; i < sizeUA; i++) {
328  if (i == 0) {
329  uA[0] = 1.0;
330  }
331  if (i == 1) {
332  uA[1] = -int(nDim());
333  }
334  if (i == 2) {
335  uA[2] = 0.0;
336  }
337  if (i == 3) {
338  uA[3] = 0.0;
339  }
340  if (i == 4) {
341  uA[4] = 0.0;
342  }
343  if (i == 5) {
344  uA[5] = 0.0;
345  }
346  }
347 }
348 
349 void DebyeHuckel::getActivities(doublereal* ac) const
350 {
352  /*
353  * Update the molality array, m_molalities()
354  * This requires an update due to mole fractions
355  */
357  for (size_t k = 0; k < m_kk; k++) {
358  if (k != m_indexSolvent) {
359  ac[k] = m_molalities[k] * exp(m_lnActCoeffMolal[k]);
360  }
361  }
362  double xmolSolvent = moleFraction(m_indexSolvent);
363  ac[m_indexSolvent] =
364  exp(m_lnActCoeffMolal[m_indexSolvent]) * xmolSolvent;
365 }
366 
367 void DebyeHuckel::getMolalityActivityCoefficients(doublereal* acMolality) const
368 {
370  A_Debye_TP(-1.0, -1.0);
372  copy(m_lnActCoeffMolal.begin(), m_lnActCoeffMolal.end(), acMolality);
373  for (size_t k = 0; k < m_kk; k++) {
374  acMolality[k] = exp(acMolality[k]);
375  }
376 }
377 
378 //
379 // ------ Partial Molar Properties of the Solution -----------------
380 //
381 void DebyeHuckel::getChemPotentials(doublereal* mu) const
382 {
383  double xx;
384  /*
385  * First get the standard chemical potentials in
386  * molar form.
387  * -> this requires updates of standard state as a function
388  * of T and P
389  */
391  /*
392  * Update the activity coefficients
393  * This also updates the internal molality array.
394  */
396  doublereal RT = GasConstant * temperature();
397  double xmolSolvent = moleFraction(m_indexSolvent);
398  for (size_t k = 0; k < m_kk; k++) {
399  if (m_indexSolvent != k) {
400  xx = std::max(m_molalities[k], SmallNumber);
401  mu[k] += RT * (log(xx) + m_lnActCoeffMolal[k]);
402  }
403  }
404  xx = std::max(xmolSolvent, SmallNumber);
405  mu[m_indexSolvent] +=
406  RT * (log(xx) + m_lnActCoeffMolal[m_indexSolvent]);
407 }
408 
409 void DebyeHuckel::getPartialMolarEnthalpies(doublereal* hbar) const
410 {
411  /*
412  * Get the nondimensional standard state enthalpies
413  */
414  getEnthalpy_RT(hbar);
415  /*
416  * Dimensionalize it.
417  */
418  double T = temperature();
419  double RT = GasConstant * T;
420  for (size_t k = 0; k < m_kk; k++) {
421  hbar[k] *= RT;
422  }
423  /*
424  * Check to see whether activity coefficients are temperature
425  * dependent. If they are, then calculate the their temperature
426  * derivatives and add them into the result.
427  */
428  double dAdT = dA_DebyedT_TP();
429  if (dAdT != 0.0) {
430  /*
431  * Update the activity coefficients, This also update the
432  * internally stored molalities.
433  */
436  double RTT = GasConstant * T * T;
437  for (size_t k = 0; k < m_kk; k++) {
438  hbar[k] -= RTT * m_dlnActCoeffMolaldT[k];
439  }
440  }
441 }
442 
443 void DebyeHuckel::getPartialMolarEntropies(doublereal* sbar) const
444 {
445  /*
446  * Get the standard state entropies at the temperature
447  * and pressure of the solution.
448  */
449  getEntropy_R(sbar);
450  /*
451  * Dimensionalize the entropies
452  */
453  doublereal R = GasConstant;
454  for (size_t k = 0; k < m_kk; k++) {
455  sbar[k] *= R;
456  }
457  /*
458  * Update the activity coefficients, This also update the
459  * internally stored molalities.
460  */
462  /*
463  * First we will add in the obvious dependence on the T
464  * term out front of the log activity term
465  */
466  doublereal mm;
467  for (size_t k = 0; k < m_kk; k++) {
468  if (k != m_indexSolvent) {
469  mm = std::max(SmallNumber, m_molalities[k]);
470  sbar[k] -= R * (log(mm) + m_lnActCoeffMolal[k]);
471  }
472  }
473  double xmolSolvent = moleFraction(m_indexSolvent);
474  mm = std::max(SmallNumber, xmolSolvent);
475  sbar[m_indexSolvent] -= R *(log(mm) + m_lnActCoeffMolal[m_indexSolvent]);
476  /*
477  * Check to see whether activity coefficients are temperature
478  * dependent. If they are, then calculate the their temperature
479  * derivatives and add them into the result.
480  */
481  double dAdT = dA_DebyedT_TP();
482  if (dAdT != 0.0) {
484  double RT = R * temperature();
485  for (size_t k = 0; k < m_kk; k++) {
486  sbar[k] -= RT * m_dlnActCoeffMolaldT[k];
487  }
488  }
489 }
490 
491 void DebyeHuckel::getPartialMolarVolumes(doublereal* vbar) const
492 {
493  getStandardVolumes(vbar);
494  /*
495  * Update the derivatives wrt the activity coefficients.
496  */
499  double T = temperature();
500  double RT = GasConstant * T;
501  for (size_t k = 0; k < m_kk; k++) {
502  vbar[k] += RT * m_dlnActCoeffMolaldP[k];
503  }
504 }
505 
506 void DebyeHuckel::getPartialMolarCp(doublereal* cpbar) const
507 {
508  /*
509  * Get the nondimensional Gibbs standard state of the
510  * species at the T and P of the solution.
511  */
512  getCp_R(cpbar);
513 
514  for (size_t k = 0; k < m_kk; k++) {
515  cpbar[k] *= GasConstant;
516  }
517 
518  /*
519  * Check to see whether activity coefficients are temperature
520  * dependent. If they are, then calculate the their temperature
521  * derivatives and add them into the result.
522  */
523  double dAdT = dA_DebyedT_TP();
524  if (dAdT != 0.0) {
525  /*
526  * Update the activity coefficients, This also update the
527  * internally stored molalities.
528  */
532  double T = temperature();
533  double RT = GasConstant * T;
534  double RTT = RT * T;
535  for (size_t k = 0; k < m_kk; k++) {
536  cpbar[k] -= (2.0 * RT * m_dlnActCoeffMolaldT[k] +
537  RTT * m_d2lnActCoeffMolaldT2[k]);
538  }
539  }
540 }
541 
542 /*
543  * -------------- Utilities -------------------------------
544  */
545 
547 {
549  initLengths();
550 }
551 
552 //! Utility function to assign an integer value from a string for the ElectrolyteSpeciesType field.
553 /*!
554  * @param estString input string that will be interpreted
555  */
556 static int interp_est(const std::string& estString)
557 {
558  const char* cc = estString.c_str();
559  string lc = lowercase(estString);
560  const char* ccl = lc.c_str();
561  if (!strcmp(ccl, "solvent")) {
562  return cEST_solvent;
563  } else if (!strcmp(ccl, "chargedspecies")) {
564  return cEST_chargedSpecies;
565  } else if (!strcmp(ccl, "weakacidassociated")) {
566  return cEST_weakAcidAssociated;
567  } else if (!strcmp(ccl, "strongacidassociated")) {
568  return cEST_strongAcidAssociated;
569  } else if (!strcmp(ccl, "polarneutral")) {
570  return cEST_polarNeutral;
571  } else if (!strcmp(ccl, "nonpolarneutral")) {
572  return cEST_nonpolarNeutral;
573  }
574  int retn, rval;
575  if ((retn = sscanf(cc, "%d", &rval)) != 1) {
576  return -1;
577  }
578  return rval;
579 }
580 
581 void DebyeHuckel::initThermoXML(XML_Node& phaseNode, const std::string& id_)
582 {
583  if (id_.size() > 0) {
584  std::string idp = phaseNode.id();
585  if (idp != id_) {
586  throw CanteraError("DebyeHuckel::initThermoXML",
587  "phasenode and Id are incompatible");
588  }
589  }
590 
591  /*
592  * Find the Thermo XML node
593  */
594  if (!phaseNode.hasChild("thermo")) {
595  throw CanteraError("DebyeHuckel::initThermoXML",
596  "no thermo XML node");
597  }
598  XML_Node& thermoNode = phaseNode.child("thermo");
599 
600  /*
601  * Determine the form of the Debye-Huckel model,
602  * m_formDH. We will use this information to size arrays below.
603  */
604  if (thermoNode.hasChild("activityCoefficients")) {
605  XML_Node& scNode = thermoNode.child("activityCoefficients");
606  m_formDH = DHFORM_DILUTE_LIMIT;
607  std::string formString = scNode.attrib("model");
608  if (formString != "") {
609  if (formString == "Dilute_limit") {
610  m_formDH = DHFORM_DILUTE_LIMIT;
611  } else if (formString == "Bdot_with_variable_a") {
612  m_formDH = DHFORM_BDOT_AK ;
613  } else if (formString == "Bdot_with_common_a") {
614  m_formDH = DHFORM_BDOT_ACOMMON;
615  } else if (formString == "Beta_ij") {
616  m_formDH = DHFORM_BETAIJ;
617  } else if (formString == "Pitzer_with_Beta_ij") {
618  m_formDH = DHFORM_PITZER_BETAIJ;
619  } else {
620  throw CanteraError("DebyeHuckel::initThermoXML",
621  "Unknown standardConc model: " + formString);
622  }
623  }
624  } else {
625  /*
626  * If there is no XML node named "activityCoefficients", assume
627  * that we are doing the extreme dilute limit assumption
628  */
629  m_formDH = DHFORM_DILUTE_LIMIT;
630  }
631 
632  /*
633  * Possibly change the form of the standard concentrations
634  */
635  if (thermoNode.hasChild("standardConc")) {
636  XML_Node& scNode = thermoNode.child("standardConc");
637  m_formGC = 2;
638  std::string formString = scNode.attrib("model");
639  if (formString != "") {
640  if (formString == "unity") {
641  m_formGC = 0;
642  throw CanteraError("DebyeHuckel::initThermoXML",
643  "standardConc = unity not done");
644  } else if (formString == "molar_volume") {
645  m_formGC = 1;
646  throw CanteraError("DebyeHuckel::initThermoXML",
647  "standardConc = molar_volume not done");
648  } else if (formString == "solvent_volume") {
649  m_formGC = 2;
650  } else {
651  throw CanteraError("DebyeHuckel::initThermoXML",
652  "Unknown standardConc model: " + formString);
653  }
654  }
655  }
656 
657  /*
658  * Reconcile the solvent name and index.
659  */
660  /*
661  * Get the Name of the Solvent:
662  * <solvent> solventName </solvent>
663  */
664  std::string solventName = "";
665  if (thermoNode.hasChild("solvent")) {
666  XML_Node& scNode = thermoNode.child("solvent");
667  vector<std::string> nameSolventa;
668  getStringArray(scNode, nameSolventa);
669  if (nameSolventa.size() != 1) {
670  throw CanteraError("DebyeHuckel::initThermoXML",
671  "badly formed solvent XML node");
672  }
673  solventName = nameSolventa[0];
674  }
675  for (size_t k = 0; k < m_kk; k++) {
676  std::string sname = speciesName(k);
677  if (solventName == sname) {
678  m_indexSolvent = k;
679  break;
680  }
681  }
682  if (m_indexSolvent == npos) {
683  cout << "DebyeHuckel::initThermoXML: Solvent Name not found"
684  << endl;
685  throw CanteraError("DebyeHuckel::initThermoXML",
686  "Solvent name not found");
687  }
688  if (m_indexSolvent != 0) {
689  throw CanteraError("DebyeHuckel::initThermoXML",
690  "Solvent " + solventName +
691  " should be first species");
692  }
693 
694  /*
695  * Initialize all of the lengths of arrays in the object
696  * now that we know what species are in the phase.
697  */
698  initThermo();
699 
700  /*
701  * Now go get the specification of the standard states for
702  * species in the solution. This includes the molar volumes
703  * data blocks for incompressible species.
704  */
705  XML_Node& speciesList = phaseNode.child("speciesArray");
706  XML_Node* speciesDB =
707  get_XML_NameID("speciesData", speciesList["datasrc"],
708  &phaseNode.root());
709  const vector<string>&sss = speciesNames();
710 
711  for (size_t k = 0; k < m_kk; k++) {
712  XML_Node* s = speciesDB->findByAttr("name", sss[k]);
713  if (!s) {
714  throw CanteraError("DebyeHuckel::initThermoXML",
715  "Species Data Base " + sss[k] + " not found");
716  }
717  XML_Node* ss = s->findByName("standardState");
718  if (!ss) {
719  throw CanteraError("DebyeHuckel::initThermoXML",
720  "Species " + sss[k] +
721  " standardState XML block not found");
722  }
723  std::string modelStringa = ss->attrib("model");
724  if (modelStringa == "") {
725  throw CanteraError("DebyeHuckel::initThermoXML",
726  "Species " + sss[k] +
727  " standardState XML block model attribute not found");
728  }
729  std::string modelString = lowercase(modelStringa);
730 
731  if (k == 0) {
732  if (modelString == "wateriapws" || modelString == "real_water" ||
733  modelString == "waterpdss") {
734  /*
735  * Initialize the water standard state model
736  */
737  m_waterSS = dynamic_cast<PDSS_Water*>(providePDSS(0)) ;
738  if (!m_waterSS) {
739  throw CanteraError("HMWSoln::installThermoXML",
740  "Dynamic cast to PDSS_Water failed");
741  }
742  /*
743  * Fill in the molar volume of water (m3/kmol)
744  * at standard conditions to fill in the m_speciesSize entry
745  * with something reasonable.
746  */
747  m_waterSS->setState_TP(300., OneAtm);
748  double dens = m_waterSS->density();
749  double mw = m_waterSS->molecularWeight();
750  m_speciesSize[0] = mw / dens;
751 #ifdef DEBUG_MODE_NOT
752  cout << "Solvent species " << sss[k] << " has volume " <<
753  m_speciesSize[k] << endl;
754 #endif
755  } else if (modelString == "constant_incompressible") {
756  m_speciesSize[k] = getFloat(*ss, "molarVolume", "toSi");
757 #ifdef DEBUG_MODE_NOT
758  cout << "species " << sss[k] << " has volume " <<
759  m_speciesSize[k] << endl;
760 #endif
761  } else {
762  throw CanteraError("DebyeHuckel::initThermoXML",
763  "Solvent SS Model \"" + modelStringa +
764  "\" is not known");
765  }
766  } else {
767  if (modelString != "constant_incompressible") {
768  throw CanteraError("DebyeHuckel::initThermoXML",
769  "Solute SS Model \"" + modelStringa +
770  "\" is not known");
771  }
772  m_speciesSize[k] = getFloat(*ss, "molarVolume", "toSI");
773 #ifdef DEBUG_MODE_NOT
774  cout << "species " << sss[k] << " has volume " <<
775  m_speciesSize[k] << endl;
776 #endif
777  }
778 
779  }
780 
781  /*
782  * Go get all of the coefficients and factors in the
783  * activityCoefficients XML block
784  */
785  XML_Node* acNodePtr = 0;
786  if (thermoNode.hasChild("activityCoefficients")) {
787  XML_Node& acNode = thermoNode.child("activityCoefficients");
788  acNodePtr = &acNode;
789  /*
790  * Look for parameters for A_Debye
791  */
792  if (acNode.hasChild("A_Debye")) {
793  XML_Node* ss = acNode.findByName("A_Debye");
794  string modelStringa = ss->attrib("model");
795  string modelString = lowercase(modelStringa);
796  if (modelString != "") {
797  if (modelString == "water") {
798  m_form_A_Debye = A_DEBYE_WATER;
799  } else {
800  throw CanteraError("DebyeHuckel::initThermoXML",
801  "A_Debye Model \"" + modelStringa +
802  "\" is not known");
803  }
804  } else {
805  m_A_Debye = getFloat(acNode, "A_Debye");
806 #ifdef DEBUG_HKM_NOT
807  cout << "A_Debye = " << m_A_Debye << endl;
808 #endif
809  }
810  }
811 
812  /*
813  * Initialize the water property calculator. It will share
814  * the internal eos water calculator.
815  */
816  if (m_form_A_Debye == A_DEBYE_WATER) {
817  delete m_waterProps;
819  }
820 
821  /*
822  * Look for parameters for B_Debye
823  */
824  if (acNode.hasChild("B_Debye")) {
825  m_B_Debye = getFloat(acNode, "B_Debye");
826 #ifdef DEBUG_HKM_NOT
827  cout << "B_Debye = " << m_B_Debye << endl;
828 #endif
829  }
830 
831  /*
832  * Look for parameters for B_dot
833  */
834  if (acNode.hasChild("B_dot")) {
835  if (m_formDH == DHFORM_BETAIJ ||
836  m_formDH == DHFORM_DILUTE_LIMIT ||
837  m_formDH == DHFORM_PITZER_BETAIJ) {
838  throw CanteraError("DebyeHuckel:init",
839  "B_dot entry in the wrong DH form");
840  }
841  double bdot_common = getFloat(acNode, "B_dot");
842 #ifdef DEBUG_HKM_NOT
843  cout << "B_dot = " << bdot_common << endl;
844 #endif
845  /*
846  * Set B_dot parameters for charged species
847  */
848  for (size_t k = 0; k < m_kk; k++) {
849  double z_k = charge(k);
850  if (fabs(z_k) > 0.0001) {
851  m_B_Dot[k] = bdot_common;
852  } else {
853  m_B_Dot[k] = 0.0;
854  }
855  }
856  }
857 
858  /*
859  * Look for Parameters for the Maximum Ionic Strength
860  */
861  if (acNode.hasChild("maxIonicStrength")) {
862  m_maxIionicStrength = getFloat(acNode, "maxIonicStrength");
863 #ifdef DEBUG_HKM_NOT
864  cout << "m_maxIionicStrength = "
865  <<m_maxIionicStrength << endl;
866 #endif
867  }
868 
869  /*
870  * Look for Helgeson Parameters
871  */
872  if (acNode.hasChild("UseHelgesonFixedForm")) {
873  m_useHelgesonFixedForm = true;
874  } else {
875  m_useHelgesonFixedForm = false;
876  }
877 
878  /*
879  * Look for parameters for the Ionic radius
880  */
881  if (acNode.hasChild("ionicRadius")) {
882  XML_Node& irNode = acNode.child("ionicRadius");
883 
884  double Afactor = 1.0;
885  if (irNode.hasAttrib("units")) {
886  std::string Aunits = irNode.attrib("units");
887  Afactor = toSI(Aunits);
888  }
889 
890  if (irNode.hasAttrib("default")) {
891  std::string ads = irNode.attrib("default");
892  double ad = fpValue(ads);
893  for (size_t k = 0; k < m_kk; k++) {
894  m_Aionic[k] = ad * Afactor;
895  }
896  }
897 
898  /*
899  * If the Debye-Huckel form is BDOT_AK, we can
900  * have separate values for the denominator's ionic
901  * size. -> That's how the activity coefficient is
902  * parameterized. In this case only do we allow the
903  * code to read in these parameters.
904  */
905  if (m_formDH == DHFORM_BDOT_AK) {
906  /*
907  * Define a string-string map, and interpret the
908  * value of the XML element as binary pairs separated
909  * by colons, e.g.:
910  * Na+:3.0
911  * Cl-:4.0
912  * H+:9.0
913  * OH-:3.5
914  * Read them into the map.
915  */
916  map<string, string> m;
917  getMap(irNode, m);
918  /*
919  * Iterate over the map pairs, interpreting the
920  * first string as a species in the current phase.
921  * If no match is made, silently ignore the
922  * lack of agreement (HKM -> may be changed in the
923  * future).
924  */
925  for (map<std::string,std::string>::const_iterator _b = m.begin();
926  _b != m.end();
927  ++_b) {
928  size_t kk = speciesIndex(_b->first);
929  m_Aionic[kk] = fpValue(_b->second) * Afactor;
930 
931  }
932  }
933  }
934  /*
935  * Get the matrix of coefficients for the Beta
936  * binary interaction parameters. We assume here that
937  * this matrix is symmetric, so that we only have to
938  * input 1/2 of the values.
939  */
940  if (acNode.hasChild("DHBetaMatrix")) {
941  if (m_formDH == DHFORM_BETAIJ ||
942  m_formDH == DHFORM_PITZER_BETAIJ) {
943  XML_Node& irNode = acNode.child("DHBetaMatrix");
944  const vector<string>& sn = speciesNames();
945  getMatrixValues(irNode, sn, sn, m_Beta_ij, true, true);
946  } else {
947  throw CanteraError("DebyeHuckel::initThermoXML:",
948  "DHBetaMatrix found for wrong type");
949  }
950  }
951 
952  /*
953  * Fill in parameters for the calculation of the
954  * stoichiometric Ionic Strength
955  *
956  * The default is that stoich charge is the same as the
957  * regular charge.
958  */
959  m_speciesCharge_Stoich.resize(m_kk, 0.0);
960  for (size_t k = 0; k < m_kk; k++) {
962  }
963  /*
964  * First look at the species database.
965  * -> Look for the subelement "stoichIsMods"
966  * in each of the species SS databases.
967  */
968  std::vector<const XML_Node*> xspecies= speciesData();
969  size_t jj = xspecies.size();
970  for (size_t k = 0; k < m_kk; k++) {
971  size_t jmap = npos;
972  std::string kname = speciesName(k);
973  for (size_t j = 0; j < jj; j++) {
974  const XML_Node& sp = *xspecies[j];
975  std::string jname = sp["name"];
976  if (jname == kname) {
977  jmap = j;
978  break;
979  }
980  }
981  if (jmap != npos) {
982  const XML_Node& sp = *xspecies[jmap];
983  if (sp.hasChild("stoichIsMods")) {
984  double val = getFloat(sp, "stoichIsMods");
985  m_speciesCharge_Stoich[k] = val;
986  }
987  }
988  }
989 
990  /*
991  * Now look at the activity coefficient database
992  */
993  if (acNodePtr) {
994  if (acNodePtr->hasChild("stoichIsMods")) {
995  XML_Node& sIsNode = acNodePtr->child("stoichIsMods");
996 
997  map<std::string, std::string> msIs;
998  getMap(sIsNode, msIs);
999  for (map<std::string,std::string>::const_iterator _b = msIs.begin();
1000  _b != msIs.end();
1001  ++_b) {
1002  size_t kk = speciesIndex(_b->first);
1003  double val = fpValue(_b->second);
1004  m_speciesCharge_Stoich[kk] = val;
1005  }
1006  }
1007  }
1008  }
1009 
1010  /*
1011  * Fill in the vector specifying the electrolyte species
1012  * type
1013  *
1014  * First fill in default values. Everything is either
1015  * a charge species, a nonpolar neutral, or the solvent.
1016  */
1017  for (size_t k = 0; k < m_kk; k++) {
1018  if (fabs(m_speciesCharge[k]) > 0.0001) {
1019  m_electrolyteSpeciesType[k] = cEST_chargedSpecies;
1020  if (fabs(m_speciesCharge_Stoich[k] - m_speciesCharge[k])
1021  > 0.0001) {
1022  m_electrolyteSpeciesType[k] = cEST_weakAcidAssociated;
1023  }
1024  } else if (fabs(m_speciesCharge_Stoich[k]) > 0.0001) {
1025  m_electrolyteSpeciesType[k] = cEST_weakAcidAssociated;
1026  } else {
1027  m_electrolyteSpeciesType[k] = cEST_nonpolarNeutral;
1028  }
1029  }
1031  /*
1032  * First look at the species database.
1033  * -> Look for the subelement "stoichIsMods"
1034  * in each of the species SS databases.
1035  */
1036  std::vector<const XML_Node*> xspecies= speciesData();
1037  for (size_t k = 0; k < m_kk; k++) {
1038  std::string kname = speciesName(k);
1039  const XML_Node* spPtr = xspecies[k];
1040  if (spPtr) {
1041  if (spPtr->hasChild("electrolyteSpeciesType")) {
1042  std::string est = getChildValue(*spPtr, "electrolyteSpeciesType");
1043  if ((m_electrolyteSpeciesType[k] = interp_est(est)) == -1) {
1044  throw CanteraError("DebyeHuckel:initThermoXML",
1045  "Bad electrolyte type: " + est);
1046  }
1047  }
1048  }
1049  }
1050  /*
1051  * Then look at the phase thermo specification
1052  */
1053  if (acNodePtr) {
1054  if (acNodePtr->hasChild("electrolyteSpeciesType")) {
1055  XML_Node& ESTNode = acNodePtr->child("electrolyteSpeciesType");
1056  map<std::string, std::string> msEST;
1057  getMap(ESTNode, msEST);
1058  for (map<std::string,std::string>::const_iterator _b = msEST.begin();
1059  _b != msEST.end();
1060  ++_b) {
1061  size_t kk = speciesIndex(_b->first);
1062  std::string est = _b->second;
1063  if ((m_electrolyteSpeciesType[kk] = interp_est(est)) == -1) {
1064  throw CanteraError("DebyeHuckel:initThermoXML",
1065  "Bad electrolyte type: " + est);
1066  }
1067  }
1068  }
1069  }
1070 
1071  /*
1072  * Lastly set the state
1073  */
1074  if (phaseNode.hasChild("state")) {
1075  XML_Node& stateNode = phaseNode.child("state");
1076  setStateFromXML(stateNode);
1077  }
1078 
1079 }
1080 
1081 double DebyeHuckel::A_Debye_TP(double tempArg, double presArg) const
1082 {
1083  double T = temperature();
1084  double A;
1085  if (tempArg != -1.0) {
1086  T = tempArg;
1087  }
1088  double P = pressure();
1089  if (presArg != -1.0) {
1090  P = presArg;
1091  }
1092 
1093  switch (m_form_A_Debye) {
1094  case A_DEBYE_CONST:
1095  A = m_A_Debye;
1096  break;
1097  case A_DEBYE_WATER:
1098  A = m_waterProps->ADebye(T, P, 0);
1099  m_A_Debye = A;
1100  break;
1101  default:
1102  throw CanteraError("DebyeHuckel::A_Debye_TP", "shouldn't be here");
1103  }
1104  return A;
1105 }
1106 
1107 double DebyeHuckel::dA_DebyedT_TP(double tempArg, double presArg) const
1108 {
1109  double T = temperature();
1110  if (tempArg != -1.0) {
1111  T = tempArg;
1112  }
1113  double P = pressure();
1114  if (presArg != -1.0) {
1115  P = presArg;
1116  }
1117  double dAdT;
1118  switch (m_form_A_Debye) {
1119  case A_DEBYE_CONST:
1120  dAdT = 0.0;
1121  break;
1122  case A_DEBYE_WATER:
1123  dAdT = m_waterProps->ADebye(T, P, 1);
1124  break;
1125  default:
1126  throw CanteraError("DebyeHuckel::dA_DebyedT_TP", "shouldn't be here");
1127  }
1128  return dAdT;
1129 }
1130 
1131 double DebyeHuckel::d2A_DebyedT2_TP(double tempArg, double presArg) const
1132 {
1133  double T = temperature();
1134  if (tempArg != -1.0) {
1135  T = tempArg;
1136  }
1137  double P = pressure();
1138  if (presArg != -1.0) {
1139  P = presArg;
1140  }
1141  double d2AdT2;
1142  switch (m_form_A_Debye) {
1143  case A_DEBYE_CONST:
1144  d2AdT2 = 0.0;
1145  break;
1146  case A_DEBYE_WATER:
1147  d2AdT2 = m_waterProps->ADebye(T, P, 2);
1148  break;
1149  default:
1150  throw CanteraError("DebyeHuckel::d2A_DebyedT2_TP", "shouldn't be here");
1151  }
1152  return d2AdT2;
1153 }
1154 
1155 double DebyeHuckel::dA_DebyedP_TP(double tempArg, double presArg) const
1156 {
1157  double T = temperature();
1158  if (tempArg != -1.0) {
1159  T = tempArg;
1160  }
1161  double P = pressure();
1162  if (presArg != -1.0) {
1163  P = presArg;
1164  }
1165  double dAdP;
1166  switch (m_form_A_Debye) {
1167  case A_DEBYE_CONST:
1168  dAdP = 0.0;
1169  break;
1170  case A_DEBYE_WATER:
1171  dAdP = m_waterProps->ADebye(T, P, 3);
1172  break;
1173  default:
1174  throw CanteraError("DebyeHuckel::dA_DebyedP_TP", "shouldn't be here");
1175  }
1176  return dAdP;
1177 }
1178 
1179 /*
1180  * ---------- Other Property Functions
1181  */
1182 
1183 double DebyeHuckel::AionicRadius(int k) const
1184 {
1185  return m_Aionic[k];
1186 }
1187 
1188 /*
1189  * ------------ Private and Restricted Functions ------------------
1190  */
1191 
1193 {
1194  /*
1195  * Obtain the limits of the temperature from the species
1196  * thermo handler's limits.
1197  */
1198  m_electrolyteSpeciesType.resize(m_kk, cEST_polarNeutral);
1199  m_speciesSize.resize(m_kk);
1200  m_Aionic.resize(m_kk, 0.0);
1201  m_lnActCoeffMolal.resize(m_kk, 0.0);
1202  m_dlnActCoeffMolaldT.resize(m_kk, 0.0);
1203  m_d2lnActCoeffMolaldT2.resize(m_kk, 0.0);
1204  m_dlnActCoeffMolaldP.resize(m_kk, 0.0);
1205  m_B_Dot.resize(m_kk, 0.0);
1206  m_pp.resize(m_kk, 0.0);
1207  m_tmpV.resize(m_kk, 0.0);
1208  if (m_formDH == DHFORM_BETAIJ ||
1209  m_formDH == DHFORM_PITZER_BETAIJ) {
1210  m_Beta_ij.resize(m_kk, m_kk, 0.0);
1211  }
1212 }
1213 
1214 double DebyeHuckel::_nonpolarActCoeff(double IionicMolality) const
1215 {
1216  double I2 = IionicMolality * IionicMolality;
1217  double l10actCoeff =
1218  m_npActCoeff[0] * IionicMolality +
1219  m_npActCoeff[1] * I2 +
1220  m_npActCoeff[2] * I2 * IionicMolality;
1221  return pow(10.0 , l10actCoeff);
1222 }
1223 
1225 {
1226  const double a0 = 1.454;
1227  const double b0 = 0.02236;
1228  const double c0 = 9.380E-3;
1229  const double d0 = -5.362E-4;
1230  double Is = m_IionicMolalityStoich;
1231  if (Is <= 0.0) {
1232  return 0.0;
1233  }
1234  double Is2 = Is * Is;
1235  double bhat = 1.0 + a0 * sqrt(Is);
1236  double func = bhat - 2.0 * log(bhat) - 1.0/bhat;
1237  double v1 = m_A_Debye / (a0 * a0 * a0 * Is) * func;
1238  double oc = 1.0 - v1 + b0 * Is / 2.0 + 2.0 * c0 * Is2 / 3.0
1239  + 3.0 * d0 * Is2 * Is / 4.0;
1240  return oc;
1241 }
1242 
1244 {
1245  /*
1246  * Update the internally stored vector of molalities
1247  */
1248  calcMolalities();
1249  double oc = _osmoticCoeffHelgesonFixedForm();
1250  double sum = 0.0;
1251  for (size_t k = 0; k < m_kk; k++) {
1252  if (k != m_indexSolvent) {
1253  sum += std::max(m_molalities[k], 0.0);
1254  }
1255  }
1256  if (sum > 2.0 * m_maxIionicStrength) {
1257  sum = 2.0 * m_maxIionicStrength;
1258  };
1259  return - m_Mnaught * sum * oc;
1260 }
1261 
1263 {
1264  double z_k, zs_k1, zs_k2;
1265  /*
1266  * Update the internally stored vector of molalities
1267  */
1268  calcMolalities();
1269  /*
1270  * Calculate the apparent (real) ionic strength.
1271  *
1272  * Note this is not the stoichiometric ionic strengh,
1273  * where reactions of ions forming neutral salts
1274  * are ignorred in calculating the ionic strength.
1275  */
1276  m_IionicMolality = 0.0;
1277  for (size_t k = 0; k < m_kk; k++) {
1278  z_k = m_speciesCharge[k];
1279  m_IionicMolality += m_molalities[k] * z_k * z_k;
1280  }
1281  m_IionicMolality /= 2.0;
1283 
1284  /*
1285  * Calculate the stoichiometric ionic charge
1286  */
1287  m_IionicMolalityStoich = 0.0;
1288  for (size_t k = 0; k < m_kk; k++) {
1289  z_k = m_speciesCharge[k];
1290  zs_k1 = m_speciesCharge_Stoich[k];
1291  if (z_k == zs_k1) {
1292  m_IionicMolalityStoich += m_molalities[k] * z_k * z_k;
1293  } else {
1294  zs_k2 = z_k - zs_k1;
1296  += m_molalities[k] * (zs_k1 * zs_k1 + zs_k2 * zs_k2);
1297  }
1298  }
1299  m_IionicMolalityStoich /= 2.0;
1301 
1302  /*
1303  * Possibly update the stored value of the
1304  * Debye-Huckel parameter A_Debye
1305  * This parameter appears on the top of the activity
1306  * coefficient expression.
1307  * It depends on T (and P), as it depends explicitly
1308  * on the temperature. Also, the dielectric constant
1309  * is usually a fairly strong function of T, also.
1310  */
1311  m_A_Debye = A_Debye_TP();
1312 
1313  /*
1314  * Calculate a safe value for the mole fraction
1315  * of the solvent
1316  */
1317  double xmolSolvent = moleFraction(m_indexSolvent);
1318  xmolSolvent = std::max(8.689E-3, xmolSolvent);
1319 
1320  int est;
1321  double ac_nonPolar = 1.0;
1322  double numTmp = m_A_Debye * sqrt(m_IionicMolality);
1323  double denomTmp = m_B_Debye * sqrt(m_IionicMolality);
1324  double coeff;
1325  double lnActivitySolvent = 0.0;
1326  double tmp;
1327  double tmpLn;
1328  double y, yp1, sigma;
1329  switch (m_formDH) {
1330  case DHFORM_DILUTE_LIMIT:
1331  for (size_t k = 0; k < m_kk; k++) {
1332  z_k = m_speciesCharge[k];
1333  m_lnActCoeffMolal[k] = - z_k * z_k * numTmp;
1334  }
1335  lnActivitySolvent =
1336  (xmolSolvent - 1.0)/xmolSolvent +
1337  2.0 / 3.0 * m_A_Debye * m_Mnaught *
1339  break;
1340 
1341  case DHFORM_BDOT_AK:
1342  ac_nonPolar = _nonpolarActCoeff(m_IionicMolality);
1343  for (size_t k = 0; k < m_kk; k++) {
1344  est = m_electrolyteSpeciesType[k];
1345  if (est == cEST_nonpolarNeutral) {
1346  m_lnActCoeffMolal[k] = log(ac_nonPolar);
1347  } else {
1348  z_k = m_speciesCharge[k];
1349  m_lnActCoeffMolal[k] =
1350  - z_k * z_k * numTmp / (1.0 + denomTmp * m_Aionic[k])
1351  + log(10.0) * m_B_Dot[k] * m_IionicMolality;
1352  }
1353  }
1354 
1355  lnActivitySolvent = (xmolSolvent - 1.0)/xmolSolvent;
1356  coeff = 2.0 / 3.0 * m_A_Debye * m_Mnaught
1357  * sqrt(m_IionicMolality);
1358  tmp = 0.0;
1359  if (denomTmp > 0.0) {
1360  for (size_t k = 0; k < m_kk; k++) {
1361  if (k != m_indexSolvent || m_Aionic[k] != 0.0) {
1362  y = denomTmp * m_Aionic[k];
1363  yp1 = y + 1.0;
1364  sigma = 3.0 / (y * y * y) * (yp1 - 1.0/yp1 - 2.0*log(yp1));
1365  z_k = m_speciesCharge[k];
1366  tmp += m_molalities[k] * z_k * z_k * sigma / 2.0;
1367  }
1368  }
1369  }
1370  lnActivitySolvent += coeff * tmp;
1371  tmp = 0.0;
1372  for (size_t k = 0; k < m_kk; k++) {
1373  z_k = m_speciesCharge[k];
1374  if ((k != m_indexSolvent) && (z_k != 0.0)) {
1375  tmp += m_B_Dot[k] * m_molalities[k];
1376  }
1377  }
1378  lnActivitySolvent -=
1379  m_Mnaught * log(10.0) * m_IionicMolality * tmp / 2.0;
1380 
1381  /*
1382  * Special section to implement the Helgeson fixed form
1383  * for the water brine activity coefficient.
1384  */
1385  if (m_useHelgesonFixedForm) {
1386  lnActivitySolvent = _lnactivityWaterHelgesonFixedForm();
1387  }
1388  break;
1389 
1390  case DHFORM_BDOT_ACOMMON:
1391  denomTmp *= m_Aionic[0];
1392  for (size_t k = 0; k < m_kk; k++) {
1393  z_k = m_speciesCharge[k];
1394  m_lnActCoeffMolal[k] =
1395  - z_k * z_k * numTmp / (1.0 + denomTmp)
1396  + log(10.0) * m_B_Dot[k] * m_IionicMolality;
1397  }
1398  if (denomTmp > 0.0) {
1399  y = denomTmp;
1400  yp1 = y + 1.0;
1401  sigma = 3.0 / (y * y * y) * (yp1 - 1.0/yp1 - 2.0*log(yp1));
1402  } else {
1403  sigma = 0.0;
1404  }
1405  lnActivitySolvent =
1406  (xmolSolvent - 1.0)/xmolSolvent +
1407  2.0 /3.0 * m_A_Debye * m_Mnaught *
1408  m_IionicMolality * sqrt(m_IionicMolality) * sigma;
1409  tmp = 0.0;
1410  for (size_t k = 0; k < m_kk; k++) {
1411  z_k = m_speciesCharge[k];
1412  if ((k != m_indexSolvent) && (z_k != 0.0)) {
1413  tmp += m_B_Dot[k] * m_molalities[k];
1414  }
1415  }
1416  lnActivitySolvent -=
1417  m_Mnaught * log(10.0) * m_IionicMolality * tmp / 2.0;
1418 
1419  break;
1420 
1421  case DHFORM_BETAIJ:
1422  denomTmp = m_B_Debye * m_Aionic[0];
1423  denomTmp *= sqrt(m_IionicMolality);
1424  lnActivitySolvent =
1425  (xmolSolvent - 1.0)/xmolSolvent;
1426 
1427  for (size_t k = 0; k < m_kk; k++) {
1428  if (k != m_indexSolvent) {
1429  z_k = m_speciesCharge[k];
1430  m_lnActCoeffMolal[k] =
1431  - z_k * z_k * numTmp / (1.0 + denomTmp);
1432  for (size_t j = 0; j < m_kk; j++) {
1433  double beta = m_Beta_ij.value(k, j);
1434 #ifdef DEBUG_HKM_NOT
1435  if (beta != 0.0) {
1436  printf("b: k = %d, j = %d, betakj = %g\n",
1437  k, j, beta);
1438  }
1439 #endif
1440  m_lnActCoeffMolal[k] += 2.0 * m_molalities[j] * beta;
1441  }
1442  }
1443  }
1444  if (denomTmp > 0.0) {
1445  y = denomTmp;
1446  yp1 = y + 1.0;
1447  sigma = 3.0 / (y * y * y) * (yp1 - 1.0/yp1 -2.0*log(yp1));
1448  } else {
1449  sigma = 0.0;
1450  }
1451  lnActivitySolvent =
1452  (xmolSolvent - 1.0)/xmolSolvent +
1453  2.0 /3.0 * m_A_Debye * m_Mnaught *
1454  m_IionicMolality * sqrt(m_IionicMolality) * sigma;
1455  tmp = 0.0;
1456  for (size_t k = 0; k < m_kk; k++) {
1457  for (size_t j = 0; j < m_kk; j++) {
1458  tmp +=
1459  m_Beta_ij.value(k, j) * m_molalities[k] * m_molalities[j];
1460  }
1461  }
1462  lnActivitySolvent -= m_Mnaught * tmp;
1463  break;
1464 
1465  case DHFORM_PITZER_BETAIJ:
1466  denomTmp = m_B_Debye * sqrt(m_IionicMolality);
1467  denomTmp *= m_Aionic[0];
1468  numTmp = m_A_Debye * sqrt(m_IionicMolality);
1469  tmpLn = log(1.0 + denomTmp);
1470  for (size_t k = 0; k < m_kk; k++) {
1471  if (k != m_indexSolvent) {
1472  z_k = m_speciesCharge[k];
1473  m_lnActCoeffMolal[k] =
1474  - z_k * z_k * numTmp / 3.0 / (1.0 + denomTmp);
1475  m_lnActCoeffMolal[k] +=
1476  - 2.0 * z_k * z_k * m_A_Debye * tmpLn /
1477  (3.0 * m_B_Debye * m_Aionic[0]);
1478  for (size_t j = 0; j < m_kk; j++) {
1479  m_lnActCoeffMolal[k] += 2.0 * m_molalities[j] *
1480  m_Beta_ij.value(k, j);
1481  }
1482  }
1483  }
1484  sigma = 1.0 / (1.0 + denomTmp);
1485  lnActivitySolvent =
1486  (xmolSolvent - 1.0)/xmolSolvent +
1487  2.0 /3.0 * m_A_Debye * m_Mnaught *
1488  m_IionicMolality * sqrt(m_IionicMolality) * sigma;
1489  tmp = 0.0;
1490  for (size_t k = 0; k < m_kk; k++) {
1491  for (size_t j = 0; j < m_kk; j++) {
1492  tmp +=
1493  m_Beta_ij.value(k, j) * m_molalities[k] * m_molalities[j];
1494  }
1495  }
1496  lnActivitySolvent -= m_Mnaught * tmp;
1497  break;
1498 
1499  default:
1500  throw CanteraError("DebyeHuckel::s_update_lnMolalityActCoeff", "ERROR");
1501  }
1502  /*
1503  * Above, we calculated the ln(activitySolvent). Translate that
1504  * into the molar-based activity coefficient by dividing by
1505  * the solvent mole fraction. Solvents are not on the molality
1506  * scale.
1507  */
1508  xmolSolvent = moleFraction(m_indexSolvent);
1510  lnActivitySolvent - log(xmolSolvent);
1511 }
1512 
1514 {
1515  double z_k, coeff, tmp, y, yp1, sigma, tmpLn;
1516  // First we store dAdT explicitly here
1517  double dAdT = dA_DebyedT_TP();
1518  if (dAdT == 0.0) {
1519  for (size_t k = 0; k < m_kk; k++) {
1520  m_dlnActCoeffMolaldT[k] = 0.0;
1521  }
1522  return;
1523  }
1524  /*
1525  * Calculate a safe value for the mole fraction
1526  * of the solvent
1527  */
1528  double xmolSolvent = moleFraction(m_indexSolvent);
1529  xmolSolvent = std::max(8.689E-3, xmolSolvent);
1530 
1531 
1532  double sqrtI = sqrt(m_IionicMolality);
1533  double numdAdTTmp = dAdT * sqrtI;
1534  double denomTmp = m_B_Debye * sqrtI;
1535  double d_lnActivitySolvent_dT = 0;
1536 
1537  switch (m_formDH) {
1538  case DHFORM_DILUTE_LIMIT:
1539  for (size_t k = 1; k < m_kk; k++) {
1541  m_lnActCoeffMolal[k] * dAdT / m_A_Debye;
1542  }
1543  d_lnActivitySolvent_dT = 2.0 / 3.0 * dAdT * m_Mnaught *
1545  m_dlnActCoeffMolaldT[m_indexSolvent] = d_lnActivitySolvent_dT;
1546  break;
1547 
1548  case DHFORM_BDOT_AK:
1549  for (size_t k = 0; k < m_kk; k++) {
1550  z_k = m_speciesCharge[k];
1552  - z_k * z_k * numdAdTTmp / (1.0 + denomTmp * m_Aionic[k]);
1553  }
1554 
1556 
1557  coeff = 2.0 / 3.0 * dAdT * m_Mnaught * sqrtI;
1558  tmp = 0.0;
1559  if (denomTmp > 0.0) {
1560  for (size_t k = 0; k < m_kk; k++) {
1561  y = denomTmp * m_Aionic[k];
1562  yp1 = y + 1.0;
1563  sigma = 3.0 / (y * y * y) * (yp1 - 1.0/yp1 - 2.0*log(yp1));
1564  z_k = m_speciesCharge[k];
1565  tmp += m_molalities[k] * z_k * z_k * sigma / 2.0;
1566  }
1567  }
1568  m_dlnActCoeffMolaldT[m_indexSolvent] += coeff * tmp;
1569  break;
1570 
1571  case DHFORM_BDOT_ACOMMON:
1572  denomTmp *= m_Aionic[0];
1573  for (size_t k = 0; k < m_kk; k++) {
1574  z_k = m_speciesCharge[k];
1576  - z_k * z_k * numdAdTTmp / (1.0 + denomTmp);
1577  }
1578  if (denomTmp > 0.0) {
1579  y = denomTmp;
1580  yp1 = y + 1.0;
1581  sigma = 3.0 / (y * y * y) * (yp1 - 1.0/yp1 - 2.0*log(yp1));
1582  } else {
1583  sigma = 0.0;
1584  }
1586  2.0 /3.0 * dAdT * m_Mnaught *
1587  m_IionicMolality * sqrtI * sigma;
1588  break;
1589 
1590  case DHFORM_BETAIJ:
1591  denomTmp *= m_Aionic[0];
1592  for (size_t k = 0; k < m_kk; k++) {
1593  if (k != m_indexSolvent) {
1594  z_k = m_speciesCharge[k];
1596  - z_k * z_k * numdAdTTmp / (1.0 + denomTmp);
1597  }
1598  }
1599  if (denomTmp > 0.0) {
1600  y = denomTmp;
1601  yp1 = y + 1.0;
1602  sigma = 3.0 / (y * y * y) * (yp1 - 1.0/yp1 - 2.0*log(yp1));
1603  } else {
1604  sigma = 0.0;
1605  }
1607  2.0 /3.0 * dAdT * m_Mnaught *
1608  m_IionicMolality * sqrtI * sigma;
1609  break;
1610 
1611  case DHFORM_PITZER_BETAIJ:
1612  denomTmp *= m_Aionic[0];
1613  tmpLn = log(1.0 + denomTmp);
1614  for (size_t k = 0; k < m_kk; k++) {
1615  if (k != m_indexSolvent) {
1616  z_k = m_speciesCharge[k];
1618  - z_k * z_k * numdAdTTmp / (1.0 + denomTmp)
1619  - 2.0 * z_k * z_k * dAdT * tmpLn
1620  / (m_B_Debye * m_Aionic[0]);
1621  m_dlnActCoeffMolaldT[k] /= 3.0;
1622  }
1623  }
1624 
1625  sigma = 1.0 / (1.0 + denomTmp);
1627  2.0 /3.0 * dAdT * m_Mnaught *
1628  m_IionicMolality * sqrtI * sigma;
1629  break;
1630 
1631  default:
1632  throw CanteraError("DebyeHuckel::s_update_dlnMolalityActCoeff_dT",
1633  "ERROR");
1634  }
1635 
1636 
1637 }
1638 
1640 {
1641  double z_k, coeff, tmp, y, yp1, sigma, tmpLn;
1642  double dAdT = dA_DebyedT_TP();
1643  double d2AdT2 = d2A_DebyedT2_TP();
1644  if (d2AdT2 == 0.0 && dAdT == 0.0) {
1645  for (size_t k = 0; k < m_kk; k++) {
1646  m_d2lnActCoeffMolaldT2[k] = 0.0;
1647  }
1648  return;
1649  }
1650 
1651  /*
1652  * Calculate a safe value for the mole fraction
1653  * of the solvent
1654  */
1655  double xmolSolvent = moleFraction(m_indexSolvent);
1656  xmolSolvent = std::max(8.689E-3, xmolSolvent);
1657 
1658 
1659  double sqrtI = sqrt(m_IionicMolality);
1660  double numd2AdT2Tmp = d2AdT2 * sqrtI;
1661  double denomTmp = m_B_Debye * sqrtI;
1662 
1663  switch (m_formDH) {
1664  case DHFORM_DILUTE_LIMIT:
1665  for (size_t k = 0; k < m_kk; k++) {
1667  m_lnActCoeffMolal[k] * d2AdT2 / m_A_Debye;
1668  }
1669  break;
1670 
1671  case DHFORM_BDOT_AK:
1672  for (size_t k = 0; k < m_kk; k++) {
1673  z_k = m_speciesCharge[k];
1675  - z_k * z_k * numd2AdT2Tmp / (1.0 + denomTmp * m_Aionic[k]);
1676  }
1677 
1679 
1680  coeff = 2.0 / 3.0 * d2AdT2 * m_Mnaught * sqrtI;
1681  tmp = 0.0;
1682  if (denomTmp > 0.0) {
1683  for (size_t k = 0; k < m_kk; k++) {
1684  y = denomTmp * m_Aionic[k];
1685  yp1 = y + 1.0;
1686  sigma = 3.0 / (y * y * y) * (yp1 - 1.0/yp1 - 2.0*log(yp1));
1687  z_k = m_speciesCharge[k];
1688  tmp += m_molalities[k] * z_k * z_k * sigma / 2.0;
1689  }
1690  }
1691  m_d2lnActCoeffMolaldT2[m_indexSolvent] += coeff * tmp;
1692  break;
1693 
1694  case DHFORM_BDOT_ACOMMON:
1695  denomTmp *= m_Aionic[0];
1696  for (size_t k = 0; k < m_kk; k++) {
1697  z_k = m_speciesCharge[k];
1699  - z_k * z_k * numd2AdT2Tmp / (1.0 + denomTmp);
1700  }
1701  if (denomTmp > 0.0) {
1702  y = denomTmp;
1703  yp1 = y + 1.0;
1704  sigma = 3.0 / (y * y * y) * (yp1 - 1.0/yp1 - 2.0*log(yp1));
1705  } else {
1706  sigma = 0.0;
1707  }
1709  2.0 /3.0 * d2AdT2 * m_Mnaught *
1710  m_IionicMolality * sqrtI * sigma;
1711  break;
1712 
1713  case DHFORM_BETAIJ:
1714  denomTmp *= m_Aionic[0];
1715  for (size_t k = 0; k < m_kk; k++) {
1716  if (k != m_indexSolvent) {
1717  z_k = m_speciesCharge[k];
1719  - z_k * z_k * numd2AdT2Tmp / (1.0 + denomTmp);
1720  }
1721  }
1722  if (denomTmp > 0.0) {
1723  y = denomTmp;
1724  yp1 = y + 1.0;
1725  sigma = 3.0 / (y * y * y) * (yp1 - 1.0/yp1 -2.0*log(yp1));
1726  } else {
1727  sigma = 0.0;
1728  }
1730  2.0 /3.0 * d2AdT2 * m_Mnaught *
1731  m_IionicMolality * sqrtI * sigma;
1732  break;
1733 
1734  case DHFORM_PITZER_BETAIJ:
1735  denomTmp *= m_Aionic[0];
1736  tmpLn = log(1.0 + denomTmp);
1737  for (size_t k = 0; k < m_kk; k++) {
1738  if (k != m_indexSolvent) {
1739  z_k = m_speciesCharge[k];
1741  - z_k * z_k * numd2AdT2Tmp / (1.0 + denomTmp)
1742  - 2.0 * z_k * z_k * d2AdT2 * tmpLn
1743  / (m_B_Debye * m_Aionic[0]);
1744  m_d2lnActCoeffMolaldT2[k] /= 3.0;
1745  }
1746  }
1747 
1748  sigma = 1.0 / (1.0 + denomTmp);
1750  2.0 /3.0 * d2AdT2 * m_Mnaught *
1751  m_IionicMolality * sqrtI * sigma;
1752  break;
1753 
1754  default:
1755  throw CanteraError("DebyeHuckel::s_update_d2lnMolalityActCoeff_dT2",
1756  "ERROR");
1757  }
1758 }
1759 
1761 {
1762  double z_k, coeff, tmp, y, yp1, sigma, tmpLn;
1763  int est;
1764  double dAdP = dA_DebyedP_TP();
1765  if (dAdP == 0.0) {
1766  for (size_t k = 0; k < m_kk; k++) {
1767  m_dlnActCoeffMolaldP[k] = 0.0;
1768  }
1769  return;
1770  }
1771  /*
1772  * Calculate a safe value for the mole fraction
1773  * of the solvent
1774  */
1775  double xmolSolvent = moleFraction(m_indexSolvent);
1776  xmolSolvent = std::max(8.689E-3, xmolSolvent);
1777 
1778 
1779  double sqrtI = sqrt(m_IionicMolality);
1780  double numdAdPTmp = dAdP * sqrtI;
1781  double denomTmp = m_B_Debye * sqrtI;
1782 
1783  switch (m_formDH) {
1784  case DHFORM_DILUTE_LIMIT:
1785  for (size_t k = 0; k < m_kk; k++) {
1787  m_lnActCoeffMolal[k] * dAdP / m_A_Debye;
1788  }
1789  break;
1790 
1791  case DHFORM_BDOT_AK:
1792  for (size_t k = 0; k < m_kk; k++) {
1793  est = m_electrolyteSpeciesType[k];
1794  if (est == cEST_nonpolarNeutral) {
1795  m_lnActCoeffMolal[k] = 0.0;
1796  } else {
1797  z_k = m_speciesCharge[k];
1799  - z_k * z_k * numdAdPTmp / (1.0 + denomTmp * m_Aionic[k]);
1800  }
1801  }
1802 
1804 
1805  coeff = 2.0 / 3.0 * dAdP * m_Mnaught * sqrtI;
1806  tmp = 0.0;
1807  if (denomTmp > 0.0) {
1808  for (size_t k = 0; k < m_kk; k++) {
1809  y = denomTmp * m_Aionic[k];
1810  yp1 = y + 1.0;
1811  sigma = 3.0 / (y * y * y) * (yp1 - 1.0/yp1 - 2.0*log(yp1));
1812  z_k = m_speciesCharge[k];
1813  tmp += m_molalities[k] * z_k * z_k * sigma / 2.0;
1814  }
1815  }
1816  m_dlnActCoeffMolaldP[m_indexSolvent] += coeff * tmp;
1817  break;
1818 
1819  case DHFORM_BDOT_ACOMMON:
1820  denomTmp *= m_Aionic[0];
1821  for (size_t k = 0; k < m_kk; k++) {
1822  z_k = m_speciesCharge[k];
1824  - z_k * z_k * numdAdPTmp / (1.0 + denomTmp);
1825  }
1826  if (denomTmp > 0.0) {
1827  y = denomTmp;
1828  yp1 = y + 1.0;
1829  sigma = 3.0 / (y * y * y) * (yp1 - 1.0/yp1 - 2.0*log(yp1));
1830  } else {
1831  sigma = 0.0;
1832  }
1834  2.0 /3.0 * dAdP * m_Mnaught *
1835  m_IionicMolality * sqrtI * sigma;
1836  break;
1837 
1838  case DHFORM_BETAIJ:
1839  denomTmp *= m_Aionic[0];
1840  for (size_t k = 0; k < m_kk; k++) {
1841  if (k != m_indexSolvent) {
1842  z_k = m_speciesCharge[k];
1844  - z_k * z_k * numdAdPTmp / (1.0 + denomTmp);
1845  }
1846  }
1847  if (denomTmp > 0.0) {
1848  y = denomTmp;
1849  yp1 = y + 1.0;
1850  sigma = 3.0 / (y * y * y) * (yp1 - 1.0/yp1 - 2.0*log(yp1));
1851  } else {
1852  sigma = 0.0;
1853  }
1855  2.0 /3.0 * dAdP * m_Mnaught *
1856  m_IionicMolality * sqrtI * sigma;
1857  break;
1858 
1859  case DHFORM_PITZER_BETAIJ:
1860  denomTmp *= m_Aionic[0];
1861  tmpLn = log(1.0 + denomTmp);
1862  for (size_t k = 0; k < m_kk; k++) {
1863  if (k != m_indexSolvent) {
1864  z_k = m_speciesCharge[k];
1866  - z_k * z_k * numdAdPTmp / (1.0 + denomTmp)
1867  - 2.0 * z_k * z_k * dAdP * tmpLn
1868  / (m_B_Debye * m_Aionic[0]);
1869  m_dlnActCoeffMolaldP[k] /= 3.0;
1870  }
1871  }
1872 
1873  sigma = 1.0 / (1.0 + denomTmp);
1875  2.0 /3.0 * dAdP * m_Mnaught *
1876  m_IionicMolality * sqrtI * sigma;
1877  break;
1878 
1879  default:
1880  throw CanteraError("DebyeHuckel::s_update_dlnMolalityActCoeff_dP",
1881  "ERROR");
1882  }
1883 }
1884 
1885 }
void getMap(const XML_Node &node, std::map< std::string, std::string > &m)
This routine is used to interpret the value portions of XML elements that contain colon separated pai...
Definition: ctml.cpp:403
const int cEST_solvent
Electrolyte species type.
Definition: electrolytes.h:19
double m_IionicMolalityStoich
Stoichiometric ionic strength on the molality scale.
Definition: DebyeHuckel.h:1299
doublereal fpValue(const std::string &val)
Translate a string into one doublereal value.
XML_Node * findByAttr(const std::string &attr, const std::string &val, int depth=100000) const
This routine carries out a recursive search for an XML node based on an attribute of each XML node...
Definition: xml.cpp:704
void s_update_d2lnMolalityActCoeff_dT2() const
Calculate the temperature 2nd derivative of the activity coefficient.
double _lnactivityWaterHelgesonFixedForm() const
Formula for the log of the water activity that occurs in the GWB.
int m_form_A_Debye
Form of the constant outside the Debye-Huckel term called A.
Definition: DebyeHuckel.h:1318
virtual doublereal density() const
Density (kg/m^3).
Definition: Phase.h:608
virtual double A_Debye_TP(double temperature=-1.0, double pressure=-1.0) const
Return the Debye Huckel constant as a function of temperature and pressure (Units = sqrt(kg/gmol)) ...
virtual void getPartialMolarCp(doublereal *cpbar) const
Return an array of partial molar heat capacities for the species in the mixture.
CTML ("Cantera Markup Language") is the variant of XML that Cantera uses to store data...
XML_Node * findXMLPhase(XML_Node *root, const std::string &idtarget)
Search an XML_Node tree for a named phase XML_Node.
Definition: xml.cpp:1108
std::string getChildValue(const XML_Node &parent, const std::string &nameString)
This function reads a child node with the name, nameString, and returns its XML value as the return s...
Definition: ctml.cpp:142
static int interp_est(const std::string &estString)
Utility function to assign an integer value from a string for the ElectrolyteSpeciesType field...
const doublereal OneAtm
One atmosphere [Pa].
Definition: ct_defs.h:69
virtual doublereal pressure() const
Return the thermodynamic pressure (Pa).
std::string attrib(const std::string &attr) const
Function returns the value of an attribute.
Definition: xml.cpp:527
vector_fp m_dlnActCoeffMolaldT
Derivative of log act coeff wrt T.
Definition: DebyeHuckel.h:1429
void resize(size_t n, size_t m, doublereal v=0.0)
Resize the array, and fill the new entries with 'v'.
Definition: Array.h:121
An error indicating that an unimplemented function has been called.
Definition: ctexceptions.h:213
doublereal m_Mnaught
This is the multiplication factor that goes inside log expressions involving the molalities of specie...
virtual void getStandardVolumes(doublereal *vol) const
Get the molar volumes of each species in their standard states at the current T and P of the solution...
virtual void getActivityConcentrations(doublereal *c) const
This method returns an array of generalized concentrations.
doublereal toSI(const std::string &unit)
Return the conversion factor to convert unit std::string 'unit' to SI units.
Definition: global.cpp:161
const size_t npos
index returned by functions to indicate "no position"
Definition: ct_defs.h:165
vector_fp m_speciesCharge
Vector of species charges. length m_kk.
Definition: Phase.h:858
virtual void getChemPotentials(doublereal *mu) const
Get the species chemical potentials. Units: J/kmol.
ThermoPhase * duplMyselfAsThermoPhase() const
Duplicator from the ThermoPhase parent class.
Header file for a common definitions used in electrolytes thermodynamics.
virtual void getCp_R(doublereal *cpr) const
Get the nondimensional Heat Capacities at constant pressure for the standard state of the species at ...
Headers for the factory class that can create known ThermoPhase objects (see Thermodynamic Properties...
virtual doublereal cp_mole() const
Molar heat capacity at constant pressure. Units: J/kmol/K.
Class XML_Node is a tree-based representation of the contents of an XML file.
Definition: xml.h:100
Implementation of a pressure dependent standard state virtual function for a Pure Water Phase (see Sp...
virtual void initThermo()
Initialize the object's internal lengths after species are set.
virtual double d2A_DebyedT2_TP(double temperature=-1.0, double pressure=-1.0) const
Value of the 2nd derivative of the Debye Huckel constant with respect to temperature as a function of...
void warn_deprecated(const std::string &method, const std::string &extra)
Print a warning indicating that method is deprecated.
Definition: global.cpp:78
vector_fp m_speciesSize
Vector of species sizes.
Definition: Phase.h:856
bool m_useHelgesonFixedForm
If true, then the fixed for of Helgeson's activity for water is used instead of the rigorous form obt...
Definition: DebyeHuckel.h:1295
virtual double dA_DebyedP_TP(double temperature=-1.0, double pressure=-1.0) const
Value of the derivative of the Debye Huckel constant with respect to pressure, as a function of tempe...
virtual doublereal entropy_mole() const
Molar entropy. Units: J/kmol/K.
doublereal molarDensity() const
Molar density (kmol/m^3).
Definition: Phase.cpp:663
std::string lowercase(const std::string &s)
Cast a copy of a string to lower case.
Definition: stringUtils.cpp:73
virtual doublereal density() const
Return the standard state density at standard state.
Definition: PDSS_Water.cpp:378
void getMoleFractions(doublereal *const x) const
Get the species mole fraction vector.
Definition: Phase.cpp:556
virtual doublereal gibbs_mole() const
Molar Gibbs function. Units: J/kmol.
void setDensity(const doublereal rho)
Set the internally stored density (gm/m^3) of the phase.
virtual void getPartialMolarEnthalpies(doublereal *hbar) const
Returns an array of partial molar enthalpies for the species in the mixture.
virtual doublereal cv_mole() const
Molar heat capacity at constant volume. Units: J/kmol/K.
Headers for the DebyeHuckel ThermoPhase object, which models dilute electrolyte solutions (see Thermo...
virtual void setState_TP(doublereal t, doublereal p)
Set the temperature (K) and pressure (Pa)
XML_Node & child(const size_t n) const
Return a changeable reference to the n'th child of the current node.
Definition: xml.cpp:573
const int cDebyeHuckel0
eosTypes returned for this ThermoPhase Object
Definition: electrolytes.h:48
DebyeHuckel()
Default Constructor.
Definition: DebyeHuckel.cpp:30
virtual int eosType() const
Equation of state type flag.
Class DebyeHuckel represents a dilute liquid electrolyte phase which obeys the Debye Huckel formulati...
Definition: DebyeHuckel.h:599
Base class for a phase with thermodynamic properties.
Definition: ThermoPhase.h:97
virtual void setTemperature(const doublereal temp)
Set the temperature (K)
doublereal mean_X(const doublereal *const Q) const
Evaluate the mole-fraction-weighted mean of an array Q.
Definition: Phase.cpp:687
const XML_Node * findByName(const std::string &nm, int depth=100000) const
This routine carries out a recursive search for an XML node based on the name of the node...
Definition: xml.cpp:742
virtual double dA_DebyedT_TP(double temperature=-1.0, double pressure=-1.0) const
Value of the derivative of the Debye Huckel constant with respect to temperature. ...
size_t speciesIndex(const std::string &name) const
Returns the index of a species named 'name' within the Phase object.
Definition: Phase.cpp:257
bool importPhase(XML_Node &phase, ThermoPhase *th, SpeciesThermoFactory *spfactory)
Import a phase information into an empty ThermoPhase object.
The WaterProps class is used to house several approximation routines for properties of water...
Definition: WaterProps.h:96
Array2D m_Beta_ij
Array of 2D data used in the DHFORM_BETAIJ formulation Beta_ij.value(i,j) is the coefficient of the j...
Definition: DebyeHuckel.h:1419
virtual void setState_TP(doublereal temp, doublereal pres)
Set the internal temperature and pressure.
Definition: PDSS_Water.cpp:389
void s_update_lnMolalityActCoeff() const
Calculate the log activity coefficients.
vector_fp m_Aionic
a_k = Size of the ionic species in the DH formulation units = meters
Definition: DebyeHuckel.h:1275
Class for the liquid water pressure dependent standard state.
Definition: PDSS_Water.h:51
MolalityVPSSTP & operator=(const MolalityVPSSTP &b)
Assignment operator.
double _nonpolarActCoeff(double IionicMolality) const
Static function that implements the non-polar species salt-out modifications.
double m_maxIionicStrength
Maximum value of the ionic strength allowed in the calculation of the activity coefficients.
Definition: DebyeHuckel.h:1284
int m_formDH
form of the Debye-Huckel parameterization used in the model.
Definition: DebyeHuckel.h:1225
double m_A_Debye
Current value of the Debye Constant, A_Debye.
Definition: DebyeHuckel.h:1341
vector_fp m_lnActCoeffMolal
Logarithm of the activity coefficients on the molality scale.
Definition: DebyeHuckel.h:1426
Base class for exceptions thrown by Cantera classes.
Definition: ctexceptions.h:99
PDSS_Water * m_waterSS
Pointer to the Water standard state object.
Definition: DebyeHuckel.h:1382
vector_fp m_B_Dot
Array of B_Dot values.
Definition: DebyeHuckel.h:1368
int m_formGC
Format for the generalized concentration:
Definition: DebyeHuckel.h:1256
bool hasChild(const std::string &ch) const
Tests whether the current node has a child node with a particular name.
Definition: xml.cpp:563
double _osmoticCoeffHelgesonFixedForm() const
Formula for the osmotic coefficient that occurs in the GWB.
virtual void setStateFromXML(const XML_Node &state)
Set equation of state parameter values from XML entries.
doublereal & value(size_t i, size_t j)
Returns a changeable reference to position in the matrix.
Definition: Array.h:295
virtual doublereal enthalpy_mole() const
Molar enthalpy of the solution. Units: J/kmol.
void initLengths()
Initialize the internal lengths.
virtual void getPartialMolarEntropies(doublereal *sbar) const
Returns an array of partial molar entropies of the species in the solution.
virtual void calcDensity()
Calculate the density of the mixture using the partial molar volumes and mole fractions as input...
DebyeHuckel & operator=(const DebyeHuckel &)
Assignment operator.
vector_int m_electrolyteSpeciesType
Vector containing the electrolyte species type.
Definition: DebyeHuckel.h:1269
virtual void getPartialMolarVolumes(doublereal *vbar) const
Return an array of partial molar volumes for the species in the mixture.
void s_update_dlnMolalityActCoeff_dT() const
Calculation of temperature derivative of activity coefficient.
const std::vector< std::string > & speciesNames() const
Return a const reference to the vector of species names.
Definition: Phase.cpp:278
vector_fp m_speciesCharge_Stoich
Stoichiometric species charge -> This is for calculations of the ionic strength which ignore ion-ion ...
Definition: DebyeHuckel.h:1411
doublereal moleFraction(size_t k) const
Return the mole fraction of a single species.
Definition: Phase.cpp:561
void getMatrixValues(const XML_Node &node, const std::vector< std::string > &keyStringRow, const std::vector< std::string > &keyStringCol, Array2D &retnValues, const bool convert, const bool matrixSymmetric)
This function interprets the value portion of an XML element as a series of "Matrix ids and entries" ...
Definition: ctml.cpp:435
double m_IionicMolality
Current value of the ionic strength on the molality scale.
Definition: DebyeHuckel.h:1278
virtual void getEntropy_R(doublereal *sr) const
Get the array of nondimensional Enthalpy functions for the standard state species at the current T an...
doublereal temperature() const
Temperature (K).
Definition: Phase.h:602
size_t nDim() const
Returns the number of spatial dimensions (1, 2, or 3)
Definition: Phase.h:586
double AionicRadius(int k=0) const
Reports the ionic radius of the kth species.
vector_fp m_molalities
Current value of the molalities of the species in the phase.
std::string id() const
Return the id attribute, if present.
Definition: xml.cpp:448
const doublereal SmallNumber
smallest number to compare to zero.
Definition: ct_defs.h:126
vector_fp m_npActCoeff
These are coefficients to describe the increase in activity coeff for non-polar molecules due to the ...
Definition: DebyeHuckel.h:1375
vector_fp m_d2lnActCoeffMolaldT2
2nd Derivative of log act coeff wrt T
Definition: DebyeHuckel.h:1432
vector_fp m_tmpV
vector of size m_kk, used as a temporary holding area.
Definition: DebyeHuckel.h:1397
void getStringArray(const XML_Node &node, std::vector< std::string > &v)
This function interprets the value portion of an XML element as a string.
Definition: ctml.cpp:503
virtual void setTemperature(const doublereal temp)
Set the internally stored temperature of the phase (K).
Definition: Phase.h:638
virtual void getEnthalpy_RT(doublereal *hrt) const
Get the nondimensional Enthalpy functions for the species at their standard states at the current T a...
doublereal molecularWeight() const
Return the molecular weight of the species in units of kg kmol-1.
Definition: PDSS.cpp:353
vector_fp m_pp
Temporary array used in equilibrium calculations.
Definition: DebyeHuckel.h:1394
doublereal meanMolecularWeight() const
The mean molecular weight. Units: (kg/kmol)
Definition: Phase.h:669
const doublereal GasConstant
Universal Gas Constant. [J/kmol/K].
Definition: ct_defs.h:64
double m_B_Debye
Current value of the constant that appears in the denominator.
Definition: DebyeHuckel.h:1360
void s_update_dlnMolalityActCoeff_dP() const
Calculate the pressure derivative of the activity coefficient.
const std::vector< const XML_Node * > & speciesData() const
Return a pointer to the vector of XML nodes containing the species data for this phase.
WaterProps * m_waterProps
Pointer to the water property calculator.
Definition: DebyeHuckel.h:1391
Contains declarations for string manipulation functions within Cantera.
doublereal getFloat(const XML_Node &parent, const std::string &name, const std::string &type)
Get a floating-point value from a child element.
Definition: ctml.cpp:194
#define DATA_PTR(vec)
Creates a pointer to the start of the raw data for a vector.
Definition: ct_defs.h:36
virtual void initThermoXML(XML_Node &phaseNode, const std::string &id)
Process the XML file after species are set up.
doublereal ADebye(doublereal T, doublereal P, int ifunc)
ADebye calculates the value of A_Debye as a function of temperature and pressure according to relatio...
Definition: WaterProps.cpp:199
virtual doublereal standardConcentration(size_t k=0) const
Return the standard concentration for the kth species.
virtual void getActivities(doublereal *ac) const
Get the array of non-dimensional activities at the current solution temperature, pressure, and solution concentration.
size_t m_kk
Number of species in the phase.
Definition: Phase.h:843
vector_fp m_dlnActCoeffMolaldP
Derivative of log act coeff wrt P.
Definition: DebyeHuckel.h:1435
virtual void getMolalityActivityCoefficients(doublereal *acMolality) const
Get the array of non-dimensional molality-based activity coefficients at the current solution tempera...
virtual void initThermoFile(const std::string &inputFile, const std::string &id)
doublereal m_Pcurrent
Current value of the pressure - state variable.
virtual void getStandardChemPotentials(doublereal *mu) const
Get the array of chemical potentials at unit activity.
XML_Node & root() const
Return the root of the current XML_Node tree.
Definition: xml.cpp:1095
virtual void getUnitsStandardConc(double *uA, int k=0, int sizeUA=6) const
Returns the units of the standard and generalized concentrations.
size_t m_indexSolvent
Index of the solvent.
virtual ~DebyeHuckel()
Destructor.
virtual void setPressure(doublereal p)
Set the internally stored pressure (Pa) at constant temperature and composition.
void calcMolalities() const
Calculates the molality of all species and stores the result internally.
std::string speciesName(size_t k) const
Name of the species with index k.
Definition: Phase.cpp:272
XML_Node * get_XML_NameID(const std::string &nameTarget, const std::string &file_ID, XML_Node *root)
This routine will locate an XML node in either the input XML tree or in another input file specified ...
Definition: global.cpp:252
virtual void setDensity(const doublereal density_)
Set the internally stored density (kg/m^3) of the phase Note the density of a phase is an independent...
Definition: Phase.h:623
bool hasAttrib(const std::string &a) const
Tests whether the current node has an attribute with a particular name.
Definition: xml.cpp:568
double m_densWaterSS
Storage for the density of water's standard state.
Definition: DebyeHuckel.h:1388
doublereal charge(size_t k) const
Dimensionless electrical charge of a single molecule of species k The charge is normalized by the the...
Definition: Phase.h:578
virtual void setMolarDensity(const doublereal conc)
Set the internally stored molar density (kmol/m^3) of the phase.
virtual void _updateStandardStateThermo() const
Updates the standard state thermodynamic functions at the current T and P of the solution.