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