Cantera  2.3.0
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 // This file is part of Cantera. See License.txt in the top-level directory or
12 // at http://www.cantera.org/license.txt for license and copyright information.
13 
19 #include "cantera/base/ctml.h"
20 
21 #include <cstdio>
22 
23 using namespace std;
24 
25 namespace Cantera
26 {
27 
28 DebyeHuckel::DebyeHuckel() :
29  m_formDH(DHFORM_DILUTE_LIMIT),
30  m_formGC(2),
31  m_IionicMolality(0.0),
32  m_maxIionicStrength(30.0),
33  m_useHelgesonFixedForm(false),
34  m_IionicMolalityStoich(0.0),
35  m_form_A_Debye(A_DEBYE_CONST),
36  m_A_Debye(1.172576), // units = sqrt(kg/gmol)
37  m_B_Debye(3.28640E9), // units = sqrt(kg/gmol) / m
38  m_waterSS(0),
39  m_densWaterSS(1000.)
40 {
41 }
42 
43 DebyeHuckel::DebyeHuckel(const std::string& inputFile,
44  const std::string& id_) :
45  m_formDH(DHFORM_DILUTE_LIMIT),
46  m_formGC(2),
47  m_IionicMolality(0.0),
48  m_maxIionicStrength(30.0),
49  m_useHelgesonFixedForm(false),
50  m_IionicMolalityStoich(0.0),
51  m_form_A_Debye(A_DEBYE_CONST),
52  m_A_Debye(1.172576), // units = sqrt(kg/gmol)
53  m_B_Debye(3.28640E9), // units = sqrt(kg/gmol) / m
54  m_waterSS(0),
55  m_densWaterSS(1000.)
56 {
57  initThermoFile(inputFile, id_);
58 }
59 
60 DebyeHuckel::DebyeHuckel(XML_Node& phaseRoot, const std::string& id_) :
61  m_formDH(DHFORM_DILUTE_LIMIT),
62  m_formGC(2),
63  m_IionicMolality(0.0),
64  m_maxIionicStrength(3.0),
65  m_useHelgesonFixedForm(false),
66  m_IionicMolalityStoich(0.0),
67  m_form_A_Debye(A_DEBYE_CONST),
68  m_A_Debye(1.172576), // units = sqrt(kg/gmol)
69  m_B_Debye(3.28640E9), // units = sqrt(kg/gmol) / m
70  m_waterSS(0),
71  m_densWaterSS(1000.)
72 {
73  importPhase(phaseRoot, this);
74 }
75 
77  m_formDH(DHFORM_DILUTE_LIMIT),
78  m_formGC(2),
79  m_IionicMolality(0.0),
80  m_maxIionicStrength(30.0),
81  m_useHelgesonFixedForm(false),
82  m_IionicMolalityStoich(0.0),
83  m_form_A_Debye(A_DEBYE_CONST),
84  m_A_Debye(1.172576), // units = sqrt(kg/gmol)
85  m_B_Debye(3.28640E9), // units = sqrt(kg/gmol) / m
86  m_waterSS(0),
87  m_densWaterSS(1000.)
88 {
89  // Use the assignment operator to do the brunt of the work for the copy
90  // constructor.
91  *this = b;
92 }
93 
94 DebyeHuckel& DebyeHuckel::operator=(const DebyeHuckel& b)
95 {
96  if (&b != this) {
97  MolalityVPSSTP::operator=(b);
98  m_formDH = b.m_formDH;
99  m_formGC = b.m_formGC;
100  m_Aionic = b.m_Aionic;
101  m_IionicMolality = b.m_IionicMolality;
102  m_maxIionicStrength = b.m_maxIionicStrength;
103  m_useHelgesonFixedForm= b.m_useHelgesonFixedForm;
104  m_IionicMolalityStoich= b.m_IionicMolalityStoich;
105  m_form_A_Debye = b.m_form_A_Debye;
106  m_A_Debye = b.m_A_Debye;
107  m_B_Debye = b.m_B_Debye;
108  m_B_Dot = b.m_B_Dot;
109 
110  // This is an internal shallow copy of the PDSS_Water pointer
111  m_waterSS = dynamic_cast<PDSS_Water*>(providePDSS(0));
112  if (!m_waterSS) {
113  throw CanteraError("DebyeHuckel::operator=()", "Dynamic cast to waterPDSS failed");
114  }
115 
116  m_densWaterSS = b.m_densWaterSS;
117 
118  if (b.m_waterProps) {
119  m_waterProps.reset(new WaterProps(m_waterSS));
120  }
121 
122  m_tmpV = b.m_tmpV;
123  m_speciesCharge_Stoich= b.m_speciesCharge_Stoich;
124  m_Beta_ij = b.m_Beta_ij;
125  m_lnActCoeffMolal = b.m_lnActCoeffMolal;
126  m_d2lnActCoeffMolaldT2= b.m_d2lnActCoeffMolaldT2;
127  }
128  return *this;
129 }
130 
131 DebyeHuckel::~DebyeHuckel()
132 {
133 }
134 
136 {
137  return new DebyeHuckel(*this);
138 }
139 
141 {
142  warn_deprecated("DebyeHuckel::eosType",
143  "To be removed after Cantera 2.3.");
144  int res;
145  switch (m_formGC) {
146  case 0:
147  res = cDebyeHuckel0;
148  break;
149  case 1:
150  res = cDebyeHuckel1;
151  break;
152  case 2:
153  res = cDebyeHuckel2;
154  break;
155  default:
156  throw CanteraError("eosType", "Unknown type");
157  break;
158  }
159  return res;
160 }
161 
162 // -------- Molar Thermodynamic Properties of the Solution ---------------
163 
164 doublereal DebyeHuckel::enthalpy_mole() const
165 {
167  return mean_X(m_tmpV);
168 }
169 
170 doublereal DebyeHuckel::entropy_mole() const
171 {
173  return mean_X(m_tmpV);
174 }
175 
176 doublereal DebyeHuckel::gibbs_mole() const
177 {
178  getChemPotentials(m_tmpV.data());
179  return mean_X(m_tmpV);
180 }
181 
182 doublereal DebyeHuckel::cp_mole() const
183 {
184  getPartialMolarCp(m_tmpV.data());
185  return mean_X(m_tmpV);
186 }
187 
188 // ------- Mechanical Equation of State Properties ------------------------
189 
191 {
192  if (m_waterSS) {
193  // Store the internal density of the water SS. Note, we would have to do
194  // this for all other species if they had pressure dependent properties.
196  }
198  double dd = meanMolecularWeight() / mean_X(m_tmpV);
199  Phase::setDensity(dd);
200 }
201 
202 void DebyeHuckel::setDensity(doublereal rho)
203 {
204  double dens = density();
205  if (rho != dens) {
206  throw CanteraError("Idea;MolalSoln::setDensity",
207  "Density is not an independent variable");
208  }
209 }
210 
211 void DebyeHuckel::setMolarDensity(const doublereal conc)
212 {
213  double concI = molarDensity();
214  if (conc != concI) {
215  throw CanteraError("Idea;MolalSoln::setMolarDensity",
216  "molarDensity/density is not an independent variable");
217  }
218 }
219 
220 // ------- Activities and Activity Concentrations
221 
222 void DebyeHuckel::getActivityConcentrations(doublereal* c) const
223 {
224  double c_solvent = standardConcentration();
225  getActivities(c);
226  for (size_t k = 0; k < m_kk; k++) {
227  c[k] *= c_solvent;
228  }
229 }
230 
231 doublereal DebyeHuckel::standardConcentration(size_t k) const
232 {
233  double mvSolvent = m_speciesSize[m_indexSolvent];
234  return 1.0 / mvSolvent;
235 }
236 
237 void DebyeHuckel::getActivities(doublereal* ac) const
238 {
240 
241  // Update the molality array, m_molalities(). This requires an update due to
242  // mole fractions
244  for (size_t k = 0; k < m_kk; k++) {
245  if (k != m_indexSolvent) {
246  ac[k] = m_molalities[k] * exp(m_lnActCoeffMolal[k]);
247  }
248  }
249  double xmolSolvent = moleFraction(m_indexSolvent);
250  ac[m_indexSolvent] =
251  exp(m_lnActCoeffMolal[m_indexSolvent]) * xmolSolvent;
252 }
253 
254 void DebyeHuckel::getMolalityActivityCoefficients(doublereal* acMolality) const
255 {
257  A_Debye_TP(-1.0, -1.0);
259  copy(m_lnActCoeffMolal.begin(), m_lnActCoeffMolal.end(), acMolality);
260  for (size_t k = 0; k < m_kk; k++) {
261  acMolality[k] = exp(acMolality[k]);
262  }
263 }
264 
265 // ------ Partial Molar Properties of the Solution -----------------
266 
267 void DebyeHuckel::getChemPotentials(doublereal* mu) const
268 {
269  double xx;
270 
271  // First get the standard chemical potentials in molar form. This requires
272  // updates of standard state as a function of T and P
274 
275  // Update the activity coefficients. This also updates the internal molality
276  // array.
278  double xmolSolvent = moleFraction(m_indexSolvent);
279  for (size_t k = 0; k < m_kk; k++) {
280  if (m_indexSolvent != k) {
281  xx = std::max(m_molalities[k], SmallNumber);
282  mu[k] += RT() * (log(xx) + m_lnActCoeffMolal[k]);
283  }
284  }
285  xx = std::max(xmolSolvent, SmallNumber);
286  mu[m_indexSolvent] +=
287  RT() * (log(xx) + m_lnActCoeffMolal[m_indexSolvent]);
288 }
289 
290 void DebyeHuckel::getPartialMolarEnthalpies(doublereal* hbar) const
291 {
292  // Get the nondimensional standard state enthalpies
293  getEnthalpy_RT(hbar);
294 
295  // Dimensionalize it.
296  for (size_t k = 0; k < m_kk; k++) {
297  hbar[k] *= RT();
298  }
299 
300  // Check to see whether activity coefficients are temperature
301  // dependent. If they are, then calculate the their temperature
302  // derivatives and add them into the result.
303  double dAdT = dA_DebyedT_TP();
304  if (dAdT != 0.0) {
305  // Update the activity coefficients, This also update the
306  // internally stored molalities.
309  for (size_t k = 0; k < m_kk; k++) {
310  hbar[k] -= RT() * temperature() * m_dlnActCoeffMolaldT[k];
311  }
312  }
313 }
314 
315 void DebyeHuckel::getPartialMolarEntropies(doublereal* sbar) const
316 {
317  // Get the standard state entropies at the temperature and pressure of the
318  // solution.
319  getEntropy_R(sbar);
320 
321  // Dimensionalize the entropies
322  for (size_t k = 0; k < m_kk; k++) {
323  sbar[k] *= GasConstant;
324  }
325 
326  // Update the activity coefficients, This also update the internally stored
327  // molalities.
329 
330  // First we will add in the obvious dependence on the T term out front of
331  // the log activity term
332  doublereal mm;
333  for (size_t k = 0; k < m_kk; k++) {
334  if (k != m_indexSolvent) {
335  mm = std::max(SmallNumber, m_molalities[k]);
336  sbar[k] -= GasConstant * (log(mm) + m_lnActCoeffMolal[k]);
337  }
338  }
339  double xmolSolvent = moleFraction(m_indexSolvent);
340  mm = std::max(SmallNumber, xmolSolvent);
342 
343  // Check to see whether activity coefficients are temperature dependent. If
344  // they are, then calculate the their temperature derivatives and add them
345  // into the result.
346  double dAdT = dA_DebyedT_TP();
347  if (dAdT != 0.0) {
349  for (size_t k = 0; k < m_kk; k++) {
350  sbar[k] -= RT() * m_dlnActCoeffMolaldT[k];
351  }
352  }
353 }
354 
355 void DebyeHuckel::getPartialMolarVolumes(doublereal* vbar) const
356 {
357  getStandardVolumes(vbar);
358 
359  // Update the derivatives wrt the activity coefficients.
362  for (size_t k = 0; k < m_kk; k++) {
363  vbar[k] += RT() * m_dlnActCoeffMolaldP[k];
364  }
365 }
366 
367 void DebyeHuckel::getPartialMolarCp(doublereal* cpbar) const
368 {
369  getCp_R(cpbar);
370  for (size_t k = 0; k < m_kk; k++) {
371  cpbar[k] *= GasConstant;
372  }
373 
374  // Check to see whether activity coefficients are temperature dependent. If
375  // they are, then calculate the their temperature derivatives and add them
376  // into the result.
377  double dAdT = dA_DebyedT_TP();
378  if (dAdT != 0.0) {
379  // Update the activity coefficients, This also update the internally
380  // stored molalities.
384  for (size_t k = 0; k < m_kk; k++) {
385  cpbar[k] -= (2.0 * RT() * m_dlnActCoeffMolaldT[k] +
387  }
388  }
389 }
390 
391 // -------------- Utilities -------------------------------
392 
393 //! Utility function to assign an integer value from a string for the
394 //! ElectrolyteSpeciesType field.
395 /*!
396  * @param estString input string that will be interpreted
397  */
398 static int interp_est(const std::string& estString)
399 {
400  if (ba::iequals(estString, "solvent")) {
401  return cEST_solvent;
402  } else if (ba::iequals(estString, "chargedspecies")) {
403  return cEST_chargedSpecies;
404  } else if (ba::iequals(estString, "weakacidassociated")) {
405  return cEST_weakAcidAssociated;
406  } else if (ba::iequals(estString, "strongacidassociated")) {
407  return cEST_strongAcidAssociated;
408  } else if (ba::iequals(estString, "polarneutral")) {
409  return cEST_polarNeutral;
410  } else if (ba::iequals(estString, "nonpolarneutral")) {
411  return cEST_nonpolarNeutral;
412  }
413  int retn, rval;
414  if ((retn = sscanf(estString.c_str(), "%d", &rval)) != 1) {
415  return -1;
416  }
417  return rval;
418 }
419 
420 void DebyeHuckel::initThermoXML(XML_Node& phaseNode, const std::string& id_)
421 {
422  if (id_.size() > 0) {
423  std::string idp = phaseNode.id();
424  if (idp != id_) {
425  throw CanteraError("DebyeHuckel::initThermoXML",
426  "phasenode and Id are incompatible");
427  }
428  }
429 
430  // Find the Thermo XML node
431  if (!phaseNode.hasChild("thermo")) {
432  throw CanteraError("DebyeHuckel::initThermoXML",
433  "no thermo XML node");
434  }
435  XML_Node& thermoNode = phaseNode.child("thermo");
436 
437  // Determine the form of the Debye-Huckel model, m_formDH. We will use this
438  // information to size arrays below.
439  if (thermoNode.hasChild("activityCoefficients")) {
440  XML_Node& scNode = thermoNode.child("activityCoefficients");
441  m_formDH = DHFORM_DILUTE_LIMIT;
442  std::string formString = scNode.attrib("model");
443  if (formString != "") {
444  if (formString == "Dilute_limit") {
445  m_formDH = DHFORM_DILUTE_LIMIT;
446  } else if (formString == "Bdot_with_variable_a") {
447  m_formDH = DHFORM_BDOT_AK;
448  } else if (formString == "Bdot_with_common_a") {
449  m_formDH = DHFORM_BDOT_ACOMMON;
450  } else if (formString == "Beta_ij") {
451  m_formDH = DHFORM_BETAIJ;
452  } else if (formString == "Pitzer_with_Beta_ij") {
453  m_formDH = DHFORM_PITZER_BETAIJ;
454  } else {
455  throw CanteraError("DebyeHuckel::initThermoXML",
456  "Unknown standardConc model: " + formString);
457  }
458  }
459  } else {
460  // If there is no XML node named "activityCoefficients", assume
461  // that we are doing the extreme dilute limit assumption
462  m_formDH = DHFORM_DILUTE_LIMIT;
463  }
464 
465  // Possibly change the form of the standard concentrations
466  if (thermoNode.hasChild("standardConc")) {
467  XML_Node& scNode = thermoNode.child("standardConc");
468  m_formGC = 2;
469  std::string formString = scNode.attrib("model");
470  if (formString != "") {
471  if (formString == "unity") {
472  m_formGC = 0;
473  throw CanteraError("DebyeHuckel::initThermoXML",
474  "standardConc = unity not done");
475  } else if (formString == "molar_volume") {
476  m_formGC = 1;
477  throw CanteraError("DebyeHuckel::initThermoXML",
478  "standardConc = molar_volume not done");
479  } else if (formString == "solvent_volume") {
480  m_formGC = 2;
481  } else {
482  throw CanteraError("DebyeHuckel::initThermoXML",
483  "Unknown standardConc model: " + formString);
484  }
485  }
486  }
487 
488  // Reconcile the solvent name and index.
489 
490  // Get the Name of the Solvent:
491  // <solvent> solventName </solvent>
492  std::string solventName = "";
493  if (thermoNode.hasChild("solvent")) {
494  XML_Node& scNode = thermoNode.child("solvent");
495  vector<std::string> nameSolventa;
496  getStringArray(scNode, nameSolventa);
497  if (nameSolventa.size() != 1) {
498  throw CanteraError("DebyeHuckel::initThermoXML",
499  "badly formed solvent XML node");
500  }
501  solventName = nameSolventa[0];
502  }
503  for (size_t k = 0; k < m_kk; k++) {
504  std::string sname = speciesName(k);
505  if (solventName == sname) {
506  m_indexSolvent = k;
507  break;
508  }
509  }
510  if (m_indexSolvent == npos) {
511  cout << "DebyeHuckel::initThermoXML: Solvent Name not found"
512  << endl;
513  throw CanteraError("DebyeHuckel::initThermoXML",
514  "Solvent name not found");
515  }
516  if (m_indexSolvent != 0) {
517  throw CanteraError("DebyeHuckel::initThermoXML",
518  "Solvent " + solventName +
519  " should be first species");
520  }
521 
522  // Now go get the specification of the standard states for species in the
523  // solution. This includes the molar volumes data blocks for incompressible
524  // species.
525  XML_Node& speciesList = phaseNode.child("speciesArray");
526  XML_Node* speciesDB =
527  get_XML_NameID("speciesData", speciesList["datasrc"],
528  &phaseNode.root());
529  const vector<string>&sss = speciesNames();
530 
531  for (size_t k = 0; k < m_kk; k++) {
532  XML_Node* s = speciesDB->findByAttr("name", sss[k]);
533  if (!s) {
534  throw CanteraError("DebyeHuckel::initThermoXML",
535  "Species Data Base " + sss[k] + " not found");
536  }
537  XML_Node* ss = s->findByName("standardState");
538  if (!ss) {
539  throw CanteraError("DebyeHuckel::initThermoXML",
540  "Species " + sss[k] +
541  " standardState XML block not found");
542  }
543  std::string modelString = ss->attrib("model");
544  if (modelString == "") {
545  throw CanteraError("DebyeHuckel::initThermoXML",
546  "Species " + sss[k] +
547  " standardState XML block model attribute not found");
548  }
549 
550  if (k == 0) {
551  if (ba::iequals(modelString, "wateriapws") || ba::iequals(modelString, "real_water") ||
552  ba::iequals(modelString, "waterpdss")) {
553  // Initialize the water standard state model
554  m_waterSS = dynamic_cast<PDSS_Water*>(providePDSS(0));
555  if (!m_waterSS) {
556  throw CanteraError("HMWSoln::installThermoXML",
557  "Dynamic cast to PDSS_Water failed");
558  }
559 
560  // Fill in the molar volume of water (m3/kmol) at standard
561  // conditions to fill in the m_speciesSize entry with something
562  // reasonable.
563  m_waterSS->setState_TP(300., OneAtm);
564  double dens = m_waterSS->density();
565  double mw = m_waterSS->molecularWeight();
566  m_speciesSize[0] = mw / dens;
567  } else if (ba::iequals(modelString, "constant_incompressible")) {
568  m_speciesSize[k] = getFloat(*ss, "molarVolume", "toSi");
569  } else {
570  throw CanteraError("DebyeHuckel::initThermoXML",
571  "Solvent SS Model \"" + modelString +
572  "\" is not known");
573  }
574  } else {
575  if (!ba::iequals(modelString, "constant_incompressible")) {
576  throw CanteraError("DebyeHuckel::initThermoXML",
577  "Solute SS Model \"" + modelString +
578  "\" is not known");
579  }
580  m_speciesSize[k] = getFloat(*ss, "molarVolume", "toSI");
581  }
582  }
583 
584  // Go get all of the coefficients and factors in the activityCoefficients
585  // XML block
586  XML_Node* acNodePtr = 0;
587  if (thermoNode.hasChild("activityCoefficients")) {
588  XML_Node& acNode = thermoNode.child("activityCoefficients");
589  acNodePtr = &acNode;
590 
591  // Look for parameters for A_Debye
592  if (acNode.hasChild("A_Debye")) {
593  XML_Node* ss = acNode.findByName("A_Debye");
594  string modelString = ss->attrib("model");
595  if (modelString != "") {
596  if (ba::iequals(modelString, "water")) {
597  m_form_A_Debye = A_DEBYE_WATER;
598  } else {
599  throw CanteraError("DebyeHuckel::initThermoXML",
600  "A_Debye Model \"" + modelString +
601  "\" is not known");
602  }
603  } else {
604  m_A_Debye = getFloat(acNode, "A_Debye");
605  }
606  }
607 
608  // Initialize the water property calculator. It will share the internal
609  // eos water calculator.
610  if (m_form_A_Debye == A_DEBYE_WATER) {
611  m_waterProps.reset(new WaterProps(m_waterSS));
612  }
613 
614  // Look for parameters for B_Debye
615  if (acNode.hasChild("B_Debye")) {
616  m_B_Debye = getFloat(acNode, "B_Debye");
617  }
618 
619  // Look for parameters for B_dot
620  if (acNode.hasChild("B_dot")) {
621  if (m_formDH == DHFORM_BETAIJ ||
622  m_formDH == DHFORM_DILUTE_LIMIT ||
623  m_formDH == DHFORM_PITZER_BETAIJ) {
624  throw CanteraError("DebyeHuckel:init",
625  "B_dot entry in the wrong DH form");
626  }
627  double bdot_common = getFloat(acNode, "B_dot");
628  // Set B_dot parameters for charged species
629  for (size_t k = 0; k < m_kk; k++) {
630  double z_k = charge(k);
631  if (fabs(z_k) > 0.0001) {
632  m_B_Dot[k] = bdot_common;
633  } else {
634  m_B_Dot[k] = 0.0;
635  }
636  }
637  }
638 
639  // Look for Parameters for the Maximum Ionic Strength
640  if (acNode.hasChild("maxIonicStrength")) {
641  m_maxIionicStrength = getFloat(acNode, "maxIonicStrength");
642  }
643 
644  // Look for Helgeson Parameters
645  if (acNode.hasChild("UseHelgesonFixedForm")) {
646  m_useHelgesonFixedForm = true;
647  } else {
648  m_useHelgesonFixedForm = false;
649  }
650 
651  // Look for parameters for the Ionic radius
652  if (acNode.hasChild("ionicRadius")) {
653  XML_Node& irNode = acNode.child("ionicRadius");
654 
655  double Afactor = 1.0;
656  if (irNode.hasAttrib("units")) {
657  std::string Aunits = irNode.attrib("units");
658  Afactor = toSI(Aunits);
659  }
660 
661  if (irNode.hasAttrib("default")) {
662  std::string ads = irNode.attrib("default");
663  double ad = fpValue(ads);
664  for (size_t k = 0; k < m_kk; k++) {
665  m_Aionic[k] = ad * Afactor;
666  }
667  }
668 
669  // If the Debye-Huckel form is BDOT_AK, we can have separate values
670  // for the denominator's ionic size. -> That's how the activity
671  // coefficient is parameterized. In this case only do we allow the
672  // code to read in these parameters.
673  if (m_formDH == DHFORM_BDOT_AK) {
674  // Define a string-string map, and interpret the value of the
675  // XML element as binary pairs separated by colons, e.g.:
676  // Na+:3.0
677  // Cl-:4.0
678  // H+:9.0
679  // OH-:3.5
680  // Read them into the map.
681  map<string, string> m;
682  getMap(irNode, m);
683 
684  // Iterate over the map pairs, interpreting the first string as
685  // a species in the current phase. If no match is made, silently
686  // ignore the lack of agreement (HKM -> may be changed in the
687  // future).
688  for (const auto& b : m) {
689  size_t kk = speciesIndex(b.first);
690  m_Aionic[kk] = fpValue(b.second) * Afactor;
691  }
692  }
693  }
694 
695  // Get the matrix of coefficients for the Beta binary interaction
696  // parameters. We assume here that this matrix is symmetric, so that we
697  // only have to input 1/2 of the values.
698  if (acNode.hasChild("DHBetaMatrix")) {
699  if (m_formDH == DHFORM_BETAIJ ||
700  m_formDH == DHFORM_PITZER_BETAIJ) {
701  m_Beta_ij.resize(m_kk, m_kk, 0.0);
702  XML_Node& irNode = acNode.child("DHBetaMatrix");
703  const vector<string>& sn = speciesNames();
704  getMatrixValues(irNode, sn, sn, m_Beta_ij, true, true);
705  } else {
706  throw CanteraError("DebyeHuckel::initThermoXML:",
707  "DHBetaMatrix found for wrong type");
708  }
709  }
710 
711  // Fill in parameters for the calculation of the stoichiometric Ionic
712  // Strength. The default is that stoich charge is the same as the
713  // regular charge.
714  m_speciesCharge_Stoich.resize(m_kk, 0.0);
715  for (size_t k = 0; k < m_kk; k++) {
717  }
718 
719  // First look at the species database. Look for the subelement
720  // "stoichIsMods" in each of the species SS databases.
721  std::vector<const XML_Node*> xspecies= speciesData();
722  size_t jj = xspecies.size();
723  for (size_t k = 0; k < m_kk; k++) {
724  size_t jmap = npos;
725  std::string kname = speciesName(k);
726  for (size_t j = 0; j < jj; j++) {
727  const XML_Node& sp = *xspecies[j];
728  std::string jname = sp["name"];
729  if (jname == kname) {
730  jmap = j;
731  break;
732  }
733  }
734  if (jmap != npos) {
735  const XML_Node& sp = *xspecies[jmap];
736  if (sp.hasChild("stoichIsMods")) {
737  double val = getFloat(sp, "stoichIsMods");
738  m_speciesCharge_Stoich[k] = val;
739  }
740  }
741  }
742 
743  // Now look at the activity coefficient database
744  if (acNodePtr && acNodePtr->hasChild("stoichIsMods")) {
745  XML_Node& sIsNode = acNodePtr->child("stoichIsMods");
746  map<std::string, std::string> msIs;
747  getMap(sIsNode, msIs);
748  for (const auto& b : msIs) {
749  size_t kk = speciesIndex(b.first);
750  double val = fpValue(b.second);
751  m_speciesCharge_Stoich[kk] = val;
752  }
753  }
754  }
755 
756  // Fill in the vector specifying the electrolyte species type. First fill in
757  // default values. Everything is either a charge species, a nonpolar
758  // neutral, or the solvent.
759  for (size_t k = 0; k < m_kk; k++) {
760  if (fabs(m_speciesCharge[k]) > 0.0001) {
761  m_electrolyteSpeciesType[k] = cEST_chargedSpecies;
762  if (fabs(m_speciesCharge_Stoich[k] - m_speciesCharge[k]) > 0.0001) {
763  m_electrolyteSpeciesType[k] = cEST_weakAcidAssociated;
764  }
765  } else if (fabs(m_speciesCharge_Stoich[k]) > 0.0001) {
766  m_electrolyteSpeciesType[k] = cEST_weakAcidAssociated;
767  } else {
768  m_electrolyteSpeciesType[k] = cEST_nonpolarNeutral;
769  }
770  }
772 
773  // First look at the species database. Look for the subelement
774  // "stoichIsMods" in each of the species SS databases.
775  std::vector<const XML_Node*> xspecies= speciesData();
776  for (size_t k = 0; k < m_kk; k++) {
777  std::string kname = speciesName(k);
778  const XML_Node* spPtr = xspecies[k];
779  if (spPtr && spPtr->hasChild("electrolyteSpeciesType")) {
780  std::string est = getChildValue(*spPtr, "electrolyteSpeciesType");
781  if ((m_electrolyteSpeciesType[k] = interp_est(est)) == -1) {
782  throw CanteraError("DebyeHuckel:initThermoXML",
783  "Bad electrolyte type: " + est);
784  }
785  }
786  }
787 
788  // Then look at the phase thermo specification
789  if (acNodePtr && acNodePtr->hasChild("electrolyteSpeciesType")) {
790  XML_Node& ESTNode = acNodePtr->child("electrolyteSpeciesType");
791  map<std::string, std::string> msEST;
792  getMap(ESTNode, msEST);
793  for (const auto& b : msEST) {
794  size_t kk = speciesIndex(b.first);
795  std::string est = b.second;
796  if ((m_electrolyteSpeciesType[kk] = interp_est(est)) == -1) {
797  throw CanteraError("DebyeHuckel:initThermoXML",
798  "Bad electrolyte type: " + est);
799  }
800  }
801  }
802 
803  // Lastly set the state
804  if (phaseNode.hasChild("state")) {
805  XML_Node& stateNode = phaseNode.child("state");
806  setStateFromXML(stateNode);
807  }
808 }
809 
810 double DebyeHuckel::A_Debye_TP(double tempArg, double presArg) const
811 {
812  double T = temperature();
813  double A;
814  if (tempArg != -1.0) {
815  T = tempArg;
816  }
817  double P = pressure();
818  if (presArg != -1.0) {
819  P = presArg;
820  }
821 
822  switch (m_form_A_Debye) {
823  case A_DEBYE_CONST:
824  A = m_A_Debye;
825  break;
826  case A_DEBYE_WATER:
827  A = m_waterProps->ADebye(T, P, 0);
828  m_A_Debye = A;
829  break;
830  default:
831  throw CanteraError("DebyeHuckel::A_Debye_TP", "shouldn't be here");
832  }
833  return A;
834 }
835 
836 double DebyeHuckel::dA_DebyedT_TP(double tempArg, double presArg) const
837 {
838  double T = temperature();
839  if (tempArg != -1.0) {
840  T = tempArg;
841  }
842  double P = pressure();
843  if (presArg != -1.0) {
844  P = presArg;
845  }
846  double dAdT;
847  switch (m_form_A_Debye) {
848  case A_DEBYE_CONST:
849  dAdT = 0.0;
850  break;
851  case A_DEBYE_WATER:
852  dAdT = m_waterProps->ADebye(T, P, 1);
853  break;
854  default:
855  throw CanteraError("DebyeHuckel::dA_DebyedT_TP", "shouldn't be here");
856  }
857  return dAdT;
858 }
859 
860 double DebyeHuckel::d2A_DebyedT2_TP(double tempArg, double presArg) const
861 {
862  double T = temperature();
863  if (tempArg != -1.0) {
864  T = tempArg;
865  }
866  double P = pressure();
867  if (presArg != -1.0) {
868  P = presArg;
869  }
870  double d2AdT2;
871  switch (m_form_A_Debye) {
872  case A_DEBYE_CONST:
873  d2AdT2 = 0.0;
874  break;
875  case A_DEBYE_WATER:
876  d2AdT2 = m_waterProps->ADebye(T, P, 2);
877  break;
878  default:
879  throw CanteraError("DebyeHuckel::d2A_DebyedT2_TP", "shouldn't be here");
880  }
881  return d2AdT2;
882 }
883 
884 double DebyeHuckel::dA_DebyedP_TP(double tempArg, double presArg) const
885 {
886  double T = temperature();
887  if (tempArg != -1.0) {
888  T = tempArg;
889  }
890  double P = pressure();
891  if (presArg != -1.0) {
892  P = presArg;
893  }
894  double dAdP;
895  switch (m_form_A_Debye) {
896  case A_DEBYE_CONST:
897  dAdP = 0.0;
898  break;
899  case A_DEBYE_WATER:
900  dAdP = m_waterProps->ADebye(T, P, 3);
901  break;
902  default:
903  throw CanteraError("DebyeHuckel::dA_DebyedP_TP", "shouldn't be here");
904  }
905  return dAdP;
906 }
907 
908 // ---------- Other Property Functions
909 
910 double DebyeHuckel::AionicRadius(int k) const
911 {
912  return m_Aionic[k];
913 }
914 
915 // ------------ Private and Restricted Functions ------------------
916 
917 bool DebyeHuckel::addSpecies(shared_ptr<Species> spec)
918 {
919  bool added = MolalityVPSSTP::addSpecies(spec);
920  if (added) {
921  m_electrolyteSpeciesType.push_back(cEST_polarNeutral);
922  m_speciesSize.push_back(0.0);
923  m_Aionic.push_back(0.0);
924  m_lnActCoeffMolal.push_back(0.0);
925  m_dlnActCoeffMolaldT.push_back(0.0);
926  m_d2lnActCoeffMolaldT2.push_back(0.0);
927  m_dlnActCoeffMolaldP.push_back(0.0);
928  m_B_Dot.push_back(0.0);
929  m_tmpV.push_back(0.0);
930  }
931  return added;
932 }
933 
934 double DebyeHuckel::_nonpolarActCoeff(double IionicMolality)
935 {
936  // These are coefficients to describe the increase in activity coeff for
937  // non-polar molecules due to the electrolyte becoming stronger (the so-
938  // called salt-out effect)
939  const static double npActCoeff[] = {0.1127, -0.01049, 1.545E-3};
940  double I2 = IionicMolality * IionicMolality;
941  double l10actCoeff =
942  npActCoeff[0] * IionicMolality +
943  npActCoeff[1] * I2 +
944  npActCoeff[2] * I2 * IionicMolality;
945  return pow(10.0 , l10actCoeff);
946 }
947 
949 {
950  const double a0 = 1.454;
951  const double b0 = 0.02236;
952  const double c0 = 9.380E-3;
953  const double d0 = -5.362E-4;
954  double Is = m_IionicMolalityStoich;
955  if (Is <= 0.0) {
956  return 0.0;
957  }
958  double Is2 = Is * Is;
959  double bhat = 1.0 + a0 * sqrt(Is);
960  double func = bhat - 2.0 * log(bhat) - 1.0/bhat;
961  double v1 = m_A_Debye / (a0 * a0 * a0 * Is) * func;
962  double oc = 1.0 - v1 + b0 * Is / 2.0 + 2.0 * c0 * Is2 / 3.0
963  + 3.0 * d0 * Is2 * Is / 4.0;
964  return oc;
965 }
966 
968 {
969  // Update the internally stored vector of molalities
970  calcMolalities();
971  double oc = _osmoticCoeffHelgesonFixedForm();
972  double sum = 0.0;
973  for (size_t k = 0; k < m_kk; k++) {
974  if (k != m_indexSolvent) {
975  sum += std::max(m_molalities[k], 0.0);
976  }
977  }
978  if (sum > 2.0 * m_maxIionicStrength) {
979  sum = 2.0 * m_maxIionicStrength;
980  };
981  return - m_Mnaught * sum * oc;
982 }
983 
985 {
986  double z_k, zs_k1, zs_k2;
987 
988  // Update the internally stored vector of molalities
989  calcMolalities();
990 
991  // Calculate the apparent (real) ionic strength.
992  //
993  // Note this is not the stoichiometric ionic strengh, where reactions of
994  // ions forming neutral salts are ignorred in calculating the ionic
995  // strength.
996  m_IionicMolality = 0.0;
997  for (size_t k = 0; k < m_kk; k++) {
998  z_k = m_speciesCharge[k];
999  m_IionicMolality += m_molalities[k] * z_k * z_k;
1000  }
1001  m_IionicMolality /= 2.0;
1003 
1004  // Calculate the stoichiometric ionic charge
1005  m_IionicMolalityStoich = 0.0;
1006  for (size_t k = 0; k < m_kk; k++) {
1007  z_k = m_speciesCharge[k];
1008  zs_k1 = m_speciesCharge_Stoich[k];
1009  if (z_k == zs_k1) {
1010  m_IionicMolalityStoich += m_molalities[k] * z_k * z_k;
1011  } else {
1012  zs_k2 = z_k - zs_k1;
1013  m_IionicMolalityStoich += m_molalities[k] * (zs_k1 * zs_k1 + zs_k2 * zs_k2);
1014  }
1015  }
1016  m_IionicMolalityStoich /= 2.0;
1018 
1019  // Possibly update the stored value of the Debye-Huckel parameter A_Debye
1020  // This parameter appears on the top of the activity coefficient expression.
1021  // It depends on T (and P), as it depends explicitly on the temperature.
1022  // Also, the dielectric constant is usually a fairly strong function of T,
1023  // also.
1024  m_A_Debye = A_Debye_TP();
1025 
1026  // Calculate a safe value for the mole fraction of the solvent
1027  double xmolSolvent = moleFraction(m_indexSolvent);
1028  xmolSolvent = std::max(8.689E-3, xmolSolvent);
1029 
1030  int est;
1031  double ac_nonPolar = 1.0;
1032  double numTmp = m_A_Debye * sqrt(m_IionicMolality);
1033  double denomTmp = m_B_Debye * sqrt(m_IionicMolality);
1034  double coeff;
1035  double lnActivitySolvent = 0.0;
1036  double tmp;
1037  double tmpLn;
1038  double y, yp1, sigma;
1039  switch (m_formDH) {
1040  case DHFORM_DILUTE_LIMIT:
1041  for (size_t k = 0; k < m_kk; k++) {
1042  z_k = m_speciesCharge[k];
1043  m_lnActCoeffMolal[k] = - z_k * z_k * numTmp;
1044  }
1045  lnActivitySolvent =
1046  (xmolSolvent - 1.0)/xmolSolvent +
1047  2.0 / 3.0 * m_A_Debye * m_Mnaught *
1049  break;
1050 
1051  case DHFORM_BDOT_AK:
1052  ac_nonPolar = _nonpolarActCoeff(m_IionicMolality);
1053  for (size_t k = 0; k < m_kk; k++) {
1054  est = m_electrolyteSpeciesType[k];
1055  if (est == cEST_nonpolarNeutral) {
1056  m_lnActCoeffMolal[k] = log(ac_nonPolar);
1057  } else {
1058  z_k = m_speciesCharge[k];
1059  m_lnActCoeffMolal[k] =
1060  - z_k * z_k * numTmp / (1.0 + denomTmp * m_Aionic[k])
1061  + log(10.0) * m_B_Dot[k] * m_IionicMolality;
1062  }
1063  }
1064 
1065  lnActivitySolvent = (xmolSolvent - 1.0)/xmolSolvent;
1066  coeff = 2.0 / 3.0 * m_A_Debye * m_Mnaught
1067  * sqrt(m_IionicMolality);
1068  tmp = 0.0;
1069  if (denomTmp > 0.0) {
1070  for (size_t k = 0; k < m_kk; k++) {
1071  if (k != m_indexSolvent || m_Aionic[k] != 0.0) {
1072  y = denomTmp * m_Aionic[k];
1073  yp1 = y + 1.0;
1074  sigma = 3.0 / (y * y * y) * (yp1 - 1.0/yp1 - 2.0*log(yp1));
1075  z_k = m_speciesCharge[k];
1076  tmp += m_molalities[k] * z_k * z_k * sigma / 2.0;
1077  }
1078  }
1079  }
1080  lnActivitySolvent += coeff * tmp;
1081  tmp = 0.0;
1082  for (size_t k = 0; k < m_kk; k++) {
1083  z_k = m_speciesCharge[k];
1084  if ((k != m_indexSolvent) && (z_k != 0.0)) {
1085  tmp += m_B_Dot[k] * m_molalities[k];
1086  }
1087  }
1088  lnActivitySolvent -=
1089  m_Mnaught * log(10.0) * m_IionicMolality * tmp / 2.0;
1090 
1091  // Special section to implement the Helgeson fixed form for the water
1092  // brine activity coefficient.
1093  if (m_useHelgesonFixedForm) {
1094  lnActivitySolvent = _lnactivityWaterHelgesonFixedForm();
1095  }
1096  break;
1097 
1098  case DHFORM_BDOT_ACOMMON:
1099  denomTmp *= m_Aionic[0];
1100  for (size_t k = 0; k < m_kk; k++) {
1101  z_k = m_speciesCharge[k];
1102  m_lnActCoeffMolal[k] =
1103  - z_k * z_k * numTmp / (1.0 + denomTmp)
1104  + log(10.0) * m_B_Dot[k] * m_IionicMolality;
1105  }
1106  if (denomTmp > 0.0) {
1107  y = denomTmp;
1108  yp1 = y + 1.0;
1109  sigma = 3.0 / (y * y * y) * (yp1 - 1.0/yp1 - 2.0*log(yp1));
1110  } else {
1111  sigma = 0.0;
1112  }
1113  lnActivitySolvent =
1114  (xmolSolvent - 1.0)/xmolSolvent +
1115  2.0 /3.0 * m_A_Debye * m_Mnaught *
1116  m_IionicMolality * sqrt(m_IionicMolality) * sigma;
1117  tmp = 0.0;
1118  for (size_t k = 0; k < m_kk; k++) {
1119  z_k = m_speciesCharge[k];
1120  if ((k != m_indexSolvent) && (z_k != 0.0)) {
1121  tmp += m_B_Dot[k] * m_molalities[k];
1122  }
1123  }
1124  lnActivitySolvent -=
1125  m_Mnaught * log(10.0) * m_IionicMolality * tmp / 2.0;
1126  break;
1127 
1128  case DHFORM_BETAIJ:
1129  denomTmp = m_B_Debye * m_Aionic[0];
1130  denomTmp *= sqrt(m_IionicMolality);
1131  lnActivitySolvent =
1132  (xmolSolvent - 1.0)/xmolSolvent;
1133 
1134  for (size_t k = 0; k < m_kk; k++) {
1135  if (k != m_indexSolvent) {
1136  z_k = m_speciesCharge[k];
1137  m_lnActCoeffMolal[k] =
1138  - z_k * z_k * numTmp / (1.0 + denomTmp);
1139  for (size_t j = 0; j < m_kk; j++) {
1140  double beta = m_Beta_ij.value(k, j);
1141  m_lnActCoeffMolal[k] += 2.0 * m_molalities[j] * beta;
1142  }
1143  }
1144  }
1145  if (denomTmp > 0.0) {
1146  y = denomTmp;
1147  yp1 = y + 1.0;
1148  sigma = 3.0 / (y * y * y) * (yp1 - 1.0/yp1 -2.0*log(yp1));
1149  } else {
1150  sigma = 0.0;
1151  }
1152  lnActivitySolvent =
1153  (xmolSolvent - 1.0)/xmolSolvent +
1154  2.0 /3.0 * m_A_Debye * m_Mnaught *
1155  m_IionicMolality * sqrt(m_IionicMolality) * sigma;
1156  tmp = 0.0;
1157  for (size_t k = 0; k < m_kk; k++) {
1158  for (size_t j = 0; j < m_kk; j++) {
1159  tmp +=
1160  m_Beta_ij.value(k, j) * m_molalities[k] * m_molalities[j];
1161  }
1162  }
1163  lnActivitySolvent -= m_Mnaught * tmp;
1164  break;
1165 
1166  case DHFORM_PITZER_BETAIJ:
1167  denomTmp = m_B_Debye * sqrt(m_IionicMolality);
1168  denomTmp *= m_Aionic[0];
1169  numTmp = m_A_Debye * sqrt(m_IionicMolality);
1170  tmpLn = log(1.0 + denomTmp);
1171  for (size_t k = 0; k < m_kk; k++) {
1172  if (k != m_indexSolvent) {
1173  z_k = m_speciesCharge[k];
1174  m_lnActCoeffMolal[k] =
1175  - z_k * z_k * numTmp / 3.0 / (1.0 + denomTmp);
1176  m_lnActCoeffMolal[k] +=
1177  - 2.0 * z_k * z_k * m_A_Debye * tmpLn /
1178  (3.0 * m_B_Debye * m_Aionic[0]);
1179  for (size_t j = 0; j < m_kk; j++) {
1180  m_lnActCoeffMolal[k] += 2.0 * m_molalities[j] *
1181  m_Beta_ij.value(k, j);
1182  }
1183  }
1184  }
1185  sigma = 1.0 / (1.0 + denomTmp);
1186  lnActivitySolvent =
1187  (xmolSolvent - 1.0)/xmolSolvent +
1188  2.0 /3.0 * m_A_Debye * m_Mnaught *
1189  m_IionicMolality * sqrt(m_IionicMolality) * sigma;
1190  tmp = 0.0;
1191  for (size_t k = 0; k < m_kk; k++) {
1192  for (size_t j = 0; j < m_kk; j++) {
1193  tmp +=
1194  m_Beta_ij.value(k, j) * m_molalities[k] * m_molalities[j];
1195  }
1196  }
1197  lnActivitySolvent -= m_Mnaught * tmp;
1198  break;
1199 
1200  default:
1201  throw CanteraError("DebyeHuckel::s_update_lnMolalityActCoeff", "ERROR");
1202  }
1203 
1204  // Above, we calculated the ln(activitySolvent). Translate that into the
1205  // molar-based activity coefficient by dividing by the solvent mole
1206  // fraction. Solvents are not on the molality scale.
1207  xmolSolvent = moleFraction(m_indexSolvent);
1209  lnActivitySolvent - log(xmolSolvent);
1210 }
1211 
1213 {
1214  double z_k, coeff, tmp, y, yp1, sigma, tmpLn;
1215  // First we store dAdT explicitly here
1216  double dAdT = dA_DebyedT_TP();
1217  if (dAdT == 0.0) {
1218  for (size_t k = 0; k < m_kk; k++) {
1219  m_dlnActCoeffMolaldT[k] = 0.0;
1220  }
1221  return;
1222  }
1223 
1224  // Calculate a safe value for the mole fraction of the solvent
1225  double xmolSolvent = moleFraction(m_indexSolvent);
1226  xmolSolvent = std::max(8.689E-3, xmolSolvent);
1227  double sqrtI = sqrt(m_IionicMolality);
1228  double numdAdTTmp = dAdT * sqrtI;
1229  double denomTmp = m_B_Debye * sqrtI;
1230  double d_lnActivitySolvent_dT = 0;
1231 
1232  switch (m_formDH) {
1233  case DHFORM_DILUTE_LIMIT:
1234  for (size_t k = 1; k < m_kk; k++) {
1236  m_lnActCoeffMolal[k] * dAdT / m_A_Debye;
1237  }
1238  d_lnActivitySolvent_dT = 2.0 / 3.0 * dAdT * m_Mnaught *
1240  m_dlnActCoeffMolaldT[m_indexSolvent] = d_lnActivitySolvent_dT;
1241  break;
1242 
1243  case DHFORM_BDOT_AK:
1244  for (size_t k = 0; k < m_kk; k++) {
1245  z_k = m_speciesCharge[k];
1247  - z_k * z_k * numdAdTTmp / (1.0 + denomTmp * m_Aionic[k]);
1248  }
1249 
1251  coeff = 2.0 / 3.0 * dAdT * m_Mnaught * sqrtI;
1252  tmp = 0.0;
1253  if (denomTmp > 0.0) {
1254  for (size_t k = 0; k < m_kk; k++) {
1255  y = denomTmp * m_Aionic[k];
1256  yp1 = y + 1.0;
1257  sigma = 3.0 / (y * y * y) * (yp1 - 1.0/yp1 - 2.0*log(yp1));
1258  z_k = m_speciesCharge[k];
1259  tmp += m_molalities[k] * z_k * z_k * sigma / 2.0;
1260  }
1261  }
1262  m_dlnActCoeffMolaldT[m_indexSolvent] += coeff * tmp;
1263  break;
1264 
1265  case DHFORM_BDOT_ACOMMON:
1266  denomTmp *= m_Aionic[0];
1267  for (size_t k = 0; k < m_kk; k++) {
1268  z_k = m_speciesCharge[k];
1270  - z_k * z_k * numdAdTTmp / (1.0 + denomTmp);
1271  }
1272  if (denomTmp > 0.0) {
1273  y = denomTmp;
1274  yp1 = y + 1.0;
1275  sigma = 3.0 / (y * y * y) * (yp1 - 1.0/yp1 - 2.0*log(yp1));
1276  } else {
1277  sigma = 0.0;
1278  }
1280  2.0 /3.0 * dAdT * m_Mnaught *
1281  m_IionicMolality * sqrtI * sigma;
1282  break;
1283 
1284  case DHFORM_BETAIJ:
1285  denomTmp *= m_Aionic[0];
1286  for (size_t k = 0; k < m_kk; k++) {
1287  if (k != m_indexSolvent) {
1288  z_k = m_speciesCharge[k];
1290  - z_k * z_k * numdAdTTmp / (1.0 + denomTmp);
1291  }
1292  }
1293  if (denomTmp > 0.0) {
1294  y = denomTmp;
1295  yp1 = y + 1.0;
1296  sigma = 3.0 / (y * y * y) * (yp1 - 1.0/yp1 - 2.0*log(yp1));
1297  } else {
1298  sigma = 0.0;
1299  }
1301  2.0 /3.0 * dAdT * m_Mnaught *
1302  m_IionicMolality * sqrtI * sigma;
1303  break;
1304 
1305  case DHFORM_PITZER_BETAIJ:
1306  denomTmp *= m_Aionic[0];
1307  tmpLn = log(1.0 + denomTmp);
1308  for (size_t k = 0; k < m_kk; k++) {
1309  if (k != m_indexSolvent) {
1310  z_k = m_speciesCharge[k];
1312  - z_k * z_k * numdAdTTmp / (1.0 + denomTmp)
1313  - 2.0 * z_k * z_k * dAdT * tmpLn
1314  / (m_B_Debye * m_Aionic[0]);
1315  m_dlnActCoeffMolaldT[k] /= 3.0;
1316  }
1317  }
1318 
1319  sigma = 1.0 / (1.0 + denomTmp);
1321  2.0 /3.0 * dAdT * m_Mnaught *
1322  m_IionicMolality * sqrtI * sigma;
1323  break;
1324 
1325  default:
1326  throw CanteraError("DebyeHuckel::s_update_dlnMolalityActCoeff_dT",
1327  "ERROR");
1328  }
1329 }
1330 
1332 {
1333  double z_k, coeff, tmp, y, yp1, sigma, tmpLn;
1334  double dAdT = dA_DebyedT_TP();
1335  double d2AdT2 = d2A_DebyedT2_TP();
1336  if (d2AdT2 == 0.0 && dAdT == 0.0) {
1337  for (size_t k = 0; k < m_kk; k++) {
1338  m_d2lnActCoeffMolaldT2[k] = 0.0;
1339  }
1340  return;
1341  }
1342 
1343  // Calculate a safe value for the mole fraction of the solvent
1344  double xmolSolvent = moleFraction(m_indexSolvent);
1345  xmolSolvent = std::max(8.689E-3, xmolSolvent);
1346  double sqrtI = sqrt(m_IionicMolality);
1347  double numd2AdT2Tmp = d2AdT2 * sqrtI;
1348  double denomTmp = m_B_Debye * sqrtI;
1349 
1350  switch (m_formDH) {
1351  case DHFORM_DILUTE_LIMIT:
1352  for (size_t k = 0; k < m_kk; k++) {
1354  m_lnActCoeffMolal[k] * d2AdT2 / m_A_Debye;
1355  }
1356  break;
1357 
1358  case DHFORM_BDOT_AK:
1359  for (size_t k = 0; k < m_kk; k++) {
1360  z_k = m_speciesCharge[k];
1362  - z_k * z_k * numd2AdT2Tmp / (1.0 + denomTmp * m_Aionic[k]);
1363  }
1364 
1366  coeff = 2.0 / 3.0 * d2AdT2 * m_Mnaught * sqrtI;
1367  tmp = 0.0;
1368  if (denomTmp > 0.0) {
1369  for (size_t k = 0; k < m_kk; k++) {
1370  y = denomTmp * m_Aionic[k];
1371  yp1 = y + 1.0;
1372  sigma = 3.0 / (y * y * y) * (yp1 - 1.0/yp1 - 2.0*log(yp1));
1373  z_k = m_speciesCharge[k];
1374  tmp += m_molalities[k] * z_k * z_k * sigma / 2.0;
1375  }
1376  }
1377  m_d2lnActCoeffMolaldT2[m_indexSolvent] += coeff * tmp;
1378  break;
1379 
1380  case DHFORM_BDOT_ACOMMON:
1381  denomTmp *= m_Aionic[0];
1382  for (size_t k = 0; k < m_kk; k++) {
1383  z_k = m_speciesCharge[k];
1385  - z_k * z_k * numd2AdT2Tmp / (1.0 + denomTmp);
1386  }
1387  if (denomTmp > 0.0) {
1388  y = denomTmp;
1389  yp1 = y + 1.0;
1390  sigma = 3.0 / (y * y * y) * (yp1 - 1.0/yp1 - 2.0*log(yp1));
1391  } else {
1392  sigma = 0.0;
1393  }
1395  2.0 /3.0 * d2AdT2 * m_Mnaught *
1396  m_IionicMolality * sqrtI * sigma;
1397  break;
1398 
1399  case DHFORM_BETAIJ:
1400  denomTmp *= m_Aionic[0];
1401  for (size_t k = 0; k < m_kk; k++) {
1402  if (k != m_indexSolvent) {
1403  z_k = m_speciesCharge[k];
1405  - z_k * z_k * numd2AdT2Tmp / (1.0 + denomTmp);
1406  }
1407  }
1408  if (denomTmp > 0.0) {
1409  y = denomTmp;
1410  yp1 = y + 1.0;
1411  sigma = 3.0 / (y * y * y) * (yp1 - 1.0/yp1 -2.0*log(yp1));
1412  } else {
1413  sigma = 0.0;
1414  }
1416  2.0 /3.0 * d2AdT2 * m_Mnaught *
1417  m_IionicMolality * sqrtI * sigma;
1418  break;
1419 
1420  case DHFORM_PITZER_BETAIJ:
1421  denomTmp *= m_Aionic[0];
1422  tmpLn = log(1.0 + denomTmp);
1423  for (size_t k = 0; k < m_kk; k++) {
1424  if (k != m_indexSolvent) {
1425  z_k = m_speciesCharge[k];
1427  - z_k * z_k * numd2AdT2Tmp / (1.0 + denomTmp)
1428  - 2.0 * z_k * z_k * d2AdT2 * tmpLn
1429  / (m_B_Debye * m_Aionic[0]);
1430  m_d2lnActCoeffMolaldT2[k] /= 3.0;
1431  }
1432  }
1433 
1434  sigma = 1.0 / (1.0 + denomTmp);
1436  2.0 /3.0 * d2AdT2 * m_Mnaught *
1437  m_IionicMolality * sqrtI * sigma;
1438  break;
1439 
1440  default:
1441  throw CanteraError("DebyeHuckel::s_update_d2lnMolalityActCoeff_dT2",
1442  "ERROR");
1443  }
1444 }
1445 
1447 {
1448  double z_k, coeff, tmp, y, yp1, sigma, tmpLn;
1449  int est;
1450  double dAdP = dA_DebyedP_TP();
1451  if (dAdP == 0.0) {
1452  for (size_t k = 0; k < m_kk; k++) {
1453  m_dlnActCoeffMolaldP[k] = 0.0;
1454  }
1455  return;
1456  }
1457 
1458  // Calculate a safe value for the mole fraction of the solvent
1459  double xmolSolvent = moleFraction(m_indexSolvent);
1460  xmolSolvent = std::max(8.689E-3, xmolSolvent);
1461  double sqrtI = sqrt(m_IionicMolality);
1462  double numdAdPTmp = dAdP * sqrtI;
1463  double denomTmp = m_B_Debye * sqrtI;
1464 
1465  switch (m_formDH) {
1466  case DHFORM_DILUTE_LIMIT:
1467  for (size_t k = 0; k < m_kk; k++) {
1469  m_lnActCoeffMolal[k] * dAdP / m_A_Debye;
1470  }
1471  break;
1472 
1473  case DHFORM_BDOT_AK:
1474  for (size_t k = 0; k < m_kk; k++) {
1475  est = m_electrolyteSpeciesType[k];
1476  if (est == cEST_nonpolarNeutral) {
1477  m_lnActCoeffMolal[k] = 0.0;
1478  } else {
1479  z_k = m_speciesCharge[k];
1481  - z_k * z_k * numdAdPTmp / (1.0 + denomTmp * m_Aionic[k]);
1482  }
1483  }
1484 
1486  coeff = 2.0 / 3.0 * dAdP * m_Mnaught * sqrtI;
1487  tmp = 0.0;
1488  if (denomTmp > 0.0) {
1489  for (size_t k = 0; k < m_kk; k++) {
1490  y = denomTmp * m_Aionic[k];
1491  yp1 = y + 1.0;
1492  sigma = 3.0 / (y * y * y) * (yp1 - 1.0/yp1 - 2.0*log(yp1));
1493  z_k = m_speciesCharge[k];
1494  tmp += m_molalities[k] * z_k * z_k * sigma / 2.0;
1495  }
1496  }
1497  m_dlnActCoeffMolaldP[m_indexSolvent] += coeff * tmp;
1498  break;
1499 
1500  case DHFORM_BDOT_ACOMMON:
1501  denomTmp *= m_Aionic[0];
1502  for (size_t k = 0; k < m_kk; k++) {
1503  z_k = m_speciesCharge[k];
1505  - z_k * z_k * numdAdPTmp / (1.0 + denomTmp);
1506  }
1507  if (denomTmp > 0.0) {
1508  y = denomTmp;
1509  yp1 = y + 1.0;
1510  sigma = 3.0 / (y * y * y) * (yp1 - 1.0/yp1 - 2.0*log(yp1));
1511  } else {
1512  sigma = 0.0;
1513  }
1515  2.0 /3.0 * dAdP * m_Mnaught *
1516  m_IionicMolality * sqrtI * sigma;
1517  break;
1518 
1519  case DHFORM_BETAIJ:
1520  denomTmp *= m_Aionic[0];
1521  for (size_t k = 0; k < m_kk; k++) {
1522  if (k != m_indexSolvent) {
1523  z_k = m_speciesCharge[k];
1525  - z_k * z_k * numdAdPTmp / (1.0 + denomTmp);
1526  }
1527  }
1528  if (denomTmp > 0.0) {
1529  y = denomTmp;
1530  yp1 = y + 1.0;
1531  sigma = 3.0 / (y * y * y) * (yp1 - 1.0/yp1 - 2.0*log(yp1));
1532  } else {
1533  sigma = 0.0;
1534  }
1536  2.0 /3.0 * dAdP * m_Mnaught *
1537  m_IionicMolality * sqrtI * sigma;
1538  break;
1539 
1540  case DHFORM_PITZER_BETAIJ:
1541  denomTmp *= m_Aionic[0];
1542  tmpLn = log(1.0 + denomTmp);
1543  for (size_t k = 0; k < m_kk; k++) {
1544  if (k != m_indexSolvent) {
1545  z_k = m_speciesCharge[k];
1547  - z_k * z_k * numdAdPTmp / (1.0 + denomTmp)
1548  - 2.0 * z_k * z_k * dAdP * tmpLn
1549  / (m_B_Debye * m_Aionic[0]);
1550  m_dlnActCoeffMolaldP[k] /= 3.0;
1551  }
1552  }
1553 
1554  sigma = 1.0 / (1.0 + denomTmp);
1556  2.0 /3.0 * dAdP * m_Mnaught *
1557  m_IionicMolality * sqrtI * sigma;
1558  break;
1559 
1560  default:
1561  throw CanteraError("DebyeHuckel::s_update_dlnMolalityActCoeff_dP",
1562  "ERROR");
1563  }
1564 }
1565 
1566 }
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:373
const int cEST_solvent
Electrolyte species type.
Definition: electrolytes.h:18
double m_IionicMolalityStoich
Stoichiometric ionic strength on the molality scale.
Definition: DebyeHuckel.h:1034
doublereal fpValue(const std::string &val)
Translate a string into one doublereal value.
int m_form_A_Debye
Form of the constant outside the Debye-Huckel term called A.
Definition: DebyeHuckel.h:1053
virtual doublereal cp_mole() const
Molar heat capacity at constant pressure. Units: J/kmol/K.
CTML ("Cantera Markup Language") is the variant of XML that Cantera uses to store data...
static double _nonpolarActCoeff(double IionicMolality)
Static function that implements the non-polar species salt-out modifications.
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:145
static int interp_est(const std::string &estString)
Utility function to assign an integer value from a string for the ElectrolyteSpeciesType field...
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:695
const doublereal OneAtm
One atmosphere [Pa].
Definition: ct_defs.h:69
doublereal temperature() const
Temperature (K).
Definition: Phase.h:601
vector_fp m_dlnActCoeffMolaldT
Derivative of log act coeff wrt T.
Definition: DebyeHuckel.h:1151
void resize(size_t n, size_t m, doublereal v=0.0)
Resize the array, and fill the new entries with &#39;v&#39;.
Definition: Array.h:112
const std::vector< const XML_Node * > & speciesData() const
Return a pointer to the vector of XML nodes containing the species data for this phase.
size_t speciesIndex(const std::string &name) const
Returns the index of a species named &#39;name&#39; within the Phase object.
Definition: Phase.cpp:251
doublereal m_Mnaught
This is the multiplication factor that goes inside log expressions involving the molalities of specie...
doublereal toSI(const std::string &unit)
Return the conversion factor to convert unit std::string &#39;unit&#39; to SI units.
Definition: global.cpp:160
doublereal moleFraction(size_t k) const
Return the mole fraction of a single species.
Definition: Phase.cpp:547
virtual void getChemPotentials(doublereal *mu) const
Get the species chemical potentials. Units: J/kmol.
std::unique_ptr< WaterProps > m_waterProps
Pointer to the water property calculator.
Definition: DebyeHuckel.h:1116
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:800
Header file for a common definitions used in electrolytes thermodynamics.
Headers for the factory class that can create known ThermoPhase objects (see Thermodynamic Properties...
virtual doublereal enthalpy_mole() const
Molar enthalpy. Units: J/kmol.
doublereal molecularWeight() const
Return the molecular weight of the species in units of kg kmol-1.
Definition: PDSS.cpp:356
Class XML_Node is a tree-based representation of the contents of an XML file.
Definition: xml.h:97
Implementation of a pressure dependent standard state virtual function for a Pure Water Phase (see Sp...
virtual void getCp_R(doublereal *cpr) const
Get the nondimensional Heat Capacities at constant pressure for the species standard states at the cu...
void warn_deprecated(const std::string &method, const std::string &extra)
Print a warning indicating that method is deprecated.
Definition: global.cpp:54
vector_fp m_speciesSize
Vector of species sizes.
Definition: Phase.h:798
STL namespace.
virtual doublereal density() const
Density (kg/m^3).
Definition: Phase.h:607
bool m_useHelgesonFixedForm
If true, then the fixed for of Helgeson&#39;s activity for water is used instead of the rigorous form obt...
Definition: DebyeHuckel.h:1031
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...
void s_update_lnMolalityActCoeff() const
Calculate the log activity coefficients.
virtual bool addSpecies(shared_ptr< Species > spec)
double _osmoticCoeffHelgesonFixedForm() const
Formula for the osmotic coefficient that occurs in the GWB.
bool hasAttrib(const std::string &a) const
Tests whether the current node has an attribute with a particular name.
Definition: xml.cpp:541
virtual void setDensity(const doublereal rho)
Set the internally stored density (gm/m^3) of the phase.
Headers for the DebyeHuckel ThermoPhase object, which models dilute electrolyte solutions (see Thermo...
const std::vector< std::string > & speciesNames() const
Return a const reference to the vector of species names.
Definition: Phase.cpp:273
doublereal mean_X(const doublereal *const Q) const
Evaluate the mole-fraction-weighted mean of an array Q.
Definition: Phase.cpp:690
doublereal RT() const
Return the Gas Constant multiplied by the current temperature.
Definition: ThermoPhase.h:809
const int cDebyeHuckel0
eosTypes returned for this ThermoPhase Object
Definition: electrolytes.h:49
DebyeHuckel()
Default Constructor.
Definition: DebyeHuckel.cpp:28
Class DebyeHuckel represents a dilute liquid electrolyte phase which obeys the Debye Huckel formulati...
Definition: DebyeHuckel.h:563
Base class for a phase with thermodynamic properties.
Definition: ThermoPhase.h:93
virtual int eosType() const
Equation of state type flag.
virtual doublereal entropy_mole() const
Molar entropy. Units: J/kmol/K.
doublereal molarDensity() const
Molar density (kmol/m^3).
Definition: Phase.cpp:666
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. ...
The WaterProps class is used to house several approximation routines for properties of water...
Definition: WaterProps.h:93
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:1141
virtual void setState_TP(doublereal temp, doublereal pres)
Set the internal temperature and pressure.
Definition: PDSS_Water.cpp:364
vector_fp m_Aionic
a_k = Size of the ionic species in the DH formulation. units = meters
Definition: DebyeHuckel.h:1017
std::string speciesName(size_t k) const
Name of the species with index k.
Definition: Phase.cpp:267
Class for the liquid water pressure dependent standard state.
Definition: PDSS_Water.h:49
double m_maxIionicStrength
Maximum value of the ionic strength allowed in the calculation of the activity coefficients.
Definition: DebyeHuckel.h:1024
int m_formDH
form of the Debye-Huckel parameterization used in the model.
Definition: DebyeHuckel.h:971
virtual void _updateStandardStateThermo() const
Updates the standard state thermodynamic functions at the current T and P of the solution.
double m_A_Debye
Current value of the Debye Constant, A_Debye.
Definition: DebyeHuckel.h:1075
virtual void getPartialMolarEntropies(doublereal *sbar) const
Returns an array of partial molar entropies of the species in the solution.
double AionicRadius(int k=0) const
Reports the ionic radius of the kth species.
vector_fp m_lnActCoeffMolal
Logarithm of the activity coefficients on the molality scale.
Definition: DebyeHuckel.h:1148
void s_update_dlnMolalityActCoeff_dP() const
Calculate the pressure derivative of the activity coefficient.
virtual void getStandardChemPotentials(doublereal *mu) const
Get the array of chemical potentials at unit activity for the species at their standard states at the...
Base class for exceptions thrown by Cantera classes.
Definition: ctexceptions.h:65
virtual void getPartialMolarVolumes(doublereal *vbar) const
Return an array of partial molar volumes for the species in the mixture.
PDSS_Water * m_waterSS
Pointer to the Water standard state object.
Definition: DebyeHuckel.h:1107
vector_fp m_B_Dot
Array of B_Dot values.
Definition: DebyeHuckel.h:1101
virtual void getPartialMolarCp(doublereal *cpbar) const
Return an array of partial molar heat capacities for the species in the mixture.
void s_update_d2lnMolalityActCoeff_dT2() const
Calculate the temperature 2nd derivative of the activity coefficient.
virtual void getPartialMolarEnthalpies(doublereal *hbar) const
Returns an array of partial molar enthalpies for the species in the mixture.
int m_formGC
Format for the generalized concentration:
Definition: DebyeHuckel.h:1001
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:253
void importPhase(XML_Node &phase, ThermoPhase *th)
Import a phase information into an empty ThermoPhase object.
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)) ...
void s_update_dlnMolalityActCoeff_dT() const
Calculation of temperature derivative of activity coefficient.
virtual void getActivities(doublereal *ac) const
Get the array of non-dimensional activities at the current solution temperature, pressure, and solution concentration.
virtual doublereal pressure() const
Returns the current pressure of the phase.
virtual void calcDensity()
Calculate the density of the mixture using the partial molar volumes and mole fractions as input...
bool hasChild(const std::string &ch) const
Tests whether the current node has a child node with a particular name.
Definition: xml.cpp:536
vector_int m_electrolyteSpeciesType
Vector containing the electrolyte species type.
Definition: DebyeHuckel.h:1014
virtual void getActivityConcentrations(doublereal *c) const
This method returns an array of generalized concentrations.
XML_Node & child(const size_t n) const
Return a changeable reference to the n&#39;th child of the current node.
Definition: xml.cpp:546
XML_Node & root() const
Return the root of the current XML_Node tree.
Definition: xml.cpp:1025
vector_fp m_speciesCharge_Stoich
Stoichiometric species charge -> This is for calculations of the ionic strength which ignore ion-ion ...
Definition: DebyeHuckel.h:1133
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:405
double m_IionicMolality
Current value of the ionic strength on the molality scale.
Definition: DebyeHuckel.h:1020
virtual void getMolalityActivityCoefficients(doublereal *acMolality) const
Get the array of non-dimensional molality-based activity coefficients at the current solution tempera...
vector_fp m_molalities
Current value of the molalities of the species in the phase.
const doublereal SmallNumber
smallest number to compare to zero.
Definition: ct_defs.h:126
std::string attrib(const std::string &attr) const
Function returns the value of an attribute.
Definition: xml.cpp:500
doublereal meanMolecularWeight() const
The mean molecular weight. Units: (kg/kmol)
Definition: Phase.h:661
vector_fp m_d2lnActCoeffMolaldT2
2nd Derivative of log act coeff wrt T
Definition: DebyeHuckel.h:1154
vector_fp m_tmpV
vector of size m_kk, used as a temporary holding area.
Definition: DebyeHuckel.h:1119
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:470
std::string id() const
Return the id attribute, if present.
Definition: xml.cpp:428
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:1093
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:178
virtual doublereal gibbs_mole() const
Molar Gibbs function. Units: J/kmol.
virtual void initThermoXML(XML_Node &phaseNode, const std::string &id)
Import and initialize a ThermoPhase object using an XML tree.
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...
virtual ThermoPhase * duplMyselfAsThermoPhase() const
Duplication routine for objects which inherit from ThermoPhase.
virtual doublereal standardConcentration(size_t k=0) const
Return the standard concentration for the kth species.
size_t m_kk
Number of species in the phase.
Definition: Phase.h:784
vector_fp m_dlnActCoeffMolaldP
Derivative of log act coeff wrt P.
Definition: DebyeHuckel.h:1157
virtual void getEntropy_R(doublereal *sr) const
Get the array of nondimensional Entropy functions for the standard state species at the current T and...
virtual doublereal density() const
Return the standard state density at standard state.
Definition: PDSS_Water.cpp:353
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:661
virtual void initThermoFile(const std::string &inputFile, const std::string &id)
void calcMolalities() const
Calculates the molality of all species and stores the result internally.
Namespace for the Cantera kernel.
Definition: application.cpp:29
size_t m_indexSolvent
Index of the solvent.
double _lnactivityWaterHelgesonFixedForm() const
Formula for the log of the water activity that occurs in the GWB.
virtual void getEnthalpy_RT(doublereal *hrt) const
Get the nondimensional Enthalpy functions for the species at their standard states at the current T a...
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
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:577
virtual bool addSpecies(shared_ptr< Species > spec)
virtual void getStandardVolumes(doublereal *vol) const
Get the molar volumes of the species standard states at the current T and P of the solution...
virtual void setDensity(const doublereal density_)
Set the internally stored density (kg/m^3) of the phase.
Definition: Phase.h:622
double m_densWaterSS
Storage for the density of water&#39;s standard state.
Definition: DebyeHuckel.h:1113
virtual void setMolarDensity(const doublereal conc)
Set the internally stored molar density (kmol/m^3) of the phase.