Cantera  2.5.1
HMWSoln.cpp
Go to the documentation of this file.
1 /**
2  * @file HMWSoln.cpp
3  * Definitions for the HMWSoln ThermoPhase object, which
4  * models concentrated electrolyte solutions
5  * (see \ref thermoprops and \link Cantera::HMWSoln HMWSoln \endlink) .
6  *
7  * Class HMWSoln represents a concentrated liquid electrolyte phase which obeys
8  * the Pitzer formulation for nonideality using molality-based standard states.
9  *
10  * This version of the code was modified to have the binary Beta2 Pitzer
11  * parameter consistent with the temperature expansions used for Beta0,
12  * Beta1, and Cphi.(CFJC, SNL)
13  */
14 
15 // This file is part of Cantera. See License.txt in the top-level directory or
16 // at https://cantera.org/license.txt for license and copyright information.
17 
18 #include "cantera/thermo/HMWSoln.h"
23 #include "cantera/base/ctml.h"
24 
25 using namespace std;
26 
27 namespace Cantera
28 {
29 
30 HMWSoln::HMWSoln() :
31  m_formPitzerTemp(PITZER_TEMP_CONSTANT),
32  m_IionicMolality(0.0),
33  m_maxIionicStrength(100.0),
34  m_TempPitzerRef(298.15),
35  m_form_A_Debye(A_DEBYE_CONST),
36  m_A_Debye(1.172576), // units = sqrt(kg/gmol)
37  m_waterSS(0),
38  m_molalitiesAreCropped(false),
39  IMS_X_o_cutoff_(0.2),
40  IMS_cCut_(0.05),
41  IMS_slopegCut_(0.0),
42  IMS_dfCut_(0.0),
43  IMS_efCut_(0.0),
44  IMS_afCut_(0.0),
45  IMS_bfCut_(0.0),
46  IMS_dgCut_(0.0),
47  IMS_egCut_(0.0),
48  IMS_agCut_(0.0),
49  IMS_bgCut_(0.0),
50  MC_X_o_cutoff_(0.0),
51  MC_dpCut_(0.0),
52  MC_epCut_(0.0),
53  MC_apCut_(0.0),
54  MC_bpCut_(0.0),
55  MC_cpCut_(0.0),
56  CROP_ln_gamma_o_min(-6.0),
57  CROP_ln_gamma_o_max(3.0),
58  CROP_ln_gamma_k_min(-5.0),
59  CROP_ln_gamma_k_max(15.0),
60  m_last_is(-1.0)
61 {
62 }
63 
64 HMWSoln::~HMWSoln()
65 {
66 }
67 
68 HMWSoln::HMWSoln(const std::string& inputFile, const std::string& id_) :
69  m_formPitzerTemp(PITZER_TEMP_CONSTANT),
70  m_IionicMolality(0.0),
71  m_maxIionicStrength(100.0),
72  m_TempPitzerRef(298.15),
73  m_form_A_Debye(A_DEBYE_CONST),
74  m_A_Debye(1.172576), // units = sqrt(kg/gmol)
75  m_waterSS(0),
76  m_molalitiesAreCropped(false),
77  IMS_X_o_cutoff_(0.2),
78  IMS_cCut_(0.05),
79  IMS_slopegCut_(0.0),
80  IMS_dfCut_(0.0),
81  IMS_efCut_(0.0),
82  IMS_afCut_(0.0),
83  IMS_bfCut_(0.0),
84  IMS_dgCut_(0.0),
85  IMS_egCut_(0.0),
86  IMS_agCut_(0.0),
87  IMS_bgCut_(0.0),
88  MC_X_o_cutoff_(0.0),
89  MC_dpCut_(0.0),
90  MC_epCut_(0.0),
91  MC_apCut_(0.0),
92  MC_bpCut_(0.0),
93  MC_cpCut_(0.0),
94  CROP_ln_gamma_o_min(-6.0),
95  CROP_ln_gamma_o_max(3.0),
96  CROP_ln_gamma_k_min(-5.0),
97  CROP_ln_gamma_k_max(15.0),
98  m_last_is(-1.0)
99 {
100  initThermoFile(inputFile, id_);
101 }
102 
103 HMWSoln::HMWSoln(XML_Node& phaseRoot, const std::string& id_) :
104  m_formPitzerTemp(PITZER_TEMP_CONSTANT),
105  m_IionicMolality(0.0),
106  m_maxIionicStrength(100.0),
107  m_TempPitzerRef(298.15),
108  m_form_A_Debye(A_DEBYE_CONST),
109  m_A_Debye(1.172576), // units = sqrt(kg/gmol)
110  m_waterSS(0),
111  m_molalitiesAreCropped(false),
112  IMS_X_o_cutoff_(0.2),
113  IMS_cCut_(0.05),
114  IMS_slopegCut_(0.0),
115  IMS_dfCut_(0.0),
116  IMS_efCut_(0.0),
117  IMS_afCut_(0.0),
118  IMS_bfCut_(0.0),
119  IMS_dgCut_(0.0),
120  IMS_egCut_(0.0),
121  IMS_agCut_(0.0),
122  IMS_bgCut_(0.0),
123  MC_X_o_cutoff_(0.0),
124  MC_dpCut_(0.0),
125  MC_epCut_(0.0),
126  MC_apCut_(0.0),
127  MC_bpCut_(0.0),
128  MC_cpCut_(0.0),
129  CROP_ln_gamma_o_min(-6.0),
130  CROP_ln_gamma_o_max(3.0),
131  CROP_ln_gamma_k_min(-5.0),
132  CROP_ln_gamma_k_max(15.0),
133  m_last_is(-1.0)
134 {
135  importPhase(phaseRoot, this);
136 }
137 
138 // -------- Molar Thermodynamic Properties of the Solution ---------------
139 
140 doublereal HMWSoln::enthalpy_mole() const
141 {
143  return mean_X(m_tmpV);
144 }
145 
146 doublereal HMWSoln::relative_enthalpy() const
147 {
149  double hbar = mean_X(m_tmpV);
150  getEnthalpy_RT(m_gamma_tmp.data());
151  for (size_t k = 0; k < m_kk; k++) {
152  m_gamma_tmp[k] *= RT();
153  }
154  double h0bar = mean_X(m_gamma_tmp);
155  return hbar - h0bar;
156 }
157 
159 {
160  double L = relative_enthalpy();
161  getMoleFractions(m_tmpV.data());
162  double xanion = 0.0;
163  size_t kcation = npos;
164  double xcation = 0.0;
165  size_t kanion = npos;
166  for (size_t k = 0; k < m_kk; k++) {
167  if (charge(k) > 0.0) {
168  if (m_tmpV[k] > xanion) {
169  xanion = m_tmpV[k];
170  kanion = k;
171  }
172  } else if (charge(k) < 0.0) {
173  if (m_tmpV[k] > xcation) {
174  xcation = m_tmpV[k];
175  kcation = k;
176  }
177  }
178  }
179  if (kcation == npos || kanion == npos) {
180  return L;
181  }
182  double xuse = xcation;
183  double factor = 1;
184  if (xanion < xcation) {
185  xuse = xanion;
186  if (charge(kcation) != 1.0) {
187  factor = charge(kcation);
188  }
189  } else {
190  if (charge(kanion) != 1.0) {
191  factor = charge(kanion);
192  }
193  }
194  xuse = xuse / factor;
195  return L / xuse;
196 }
197 
198 doublereal HMWSoln::entropy_mole() const
199 {
201  return mean_X(m_tmpV);
202 }
203 
204 doublereal HMWSoln::gibbs_mole() const
205 {
206  getChemPotentials(m_tmpV.data());
207  return mean_X(m_tmpV);
208 }
209 
210 doublereal HMWSoln::cp_mole() const
211 {
212  getPartialMolarCp(m_tmpV.data());
213  return mean_X(m_tmpV);
214 }
215 
216 doublereal HMWSoln::cv_mole() const
217 {
218  double kappa_t = isothermalCompressibility();
219  double beta = thermalExpansionCoeff();
220  double cp = cp_mole();
221  double tt = temperature();
222  double molarV = molarVolume();
223  return cp - beta * beta * tt * molarV / kappa_t;
224 }
225 
226 // ------- Mechanical Equation of State Properties ------------------------
227 
229 {
230  static const int cacheId = m_cache.getId();
231  CachedScalar cached = m_cache.getScalar(cacheId);
232  if(cached.validate(temperature(), pressure(), stateMFNumber())) {
233  return;
234  }
235 
236  // Calculate all of the other standard volumes. Note these are constant for
237  // now
239  double dd = meanMolecularWeight() / mean_X(m_tmpV);
241 }
242 
243 void HMWSoln::setDensity(const doublereal rho)
244 {
245  warn_deprecated("HMWSoln::setDensity",
246  "Overloaded function to be removed after Cantera 2.5. "
247  "Error will be thrown by Phase::setDensity instead");
248  double dens_old = density();
249  if (rho != dens_old) {
250  throw CanteraError("HMWSoln::setDensity",
251  "Density is not an independent variable");
252  }
253 }
254 
255 void HMWSoln::setMolarDensity(const doublereal rho)
256 {
257  warn_deprecated("HMWSoln::setMolarDensity",
258  "Overloaded function to be removed after Cantera 2.5. "
259  "Error will be thrown by Phase::setMolarDensity instead");
260  throw CanteraError("HMWSoln::setMolarDensity",
261  "Density is not an independent variable");
262 }
263 
264 // ------- Activities and Activity Concentrations
265 
266 void HMWSoln::getActivityConcentrations(doublereal* c) const
267 {
268  double cs_solvent = standardConcentration();
269  getActivities(c);
270  c[0] *= cs_solvent;
271  if (m_kk > 1) {
272  double cs_solute = standardConcentration(1);
273  for (size_t k = 1; k < m_kk; k++) {
274  c[k] *= cs_solute;
275  }
276  }
277 }
278 
279 doublereal HMWSoln::standardConcentration(size_t k) const
280 {
281  getStandardVolumes(m_tmpV.data());
282  double mvSolvent = m_tmpV[0];
283  if (k > 0) {
284  return m_Mnaught / mvSolvent;
285  }
286  return 1.0 / mvSolvent;
287 }
288 
289 void HMWSoln::getActivities(doublereal* ac) const
290 {
292 
293  // Update the molality array, m_molalities(). This requires an update due to
294  // mole fractions
295  s_update_lnMolalityActCoeff();
296 
297  // Now calculate the array of activities.
298  for (size_t k = 1; k < m_kk; k++) {
299  ac[k] = m_molalities[k] * exp(m_lnActCoeffMolal_Scaled[k]);
300  }
301  double xmolSolvent = moleFraction(0);
302  ac[0] = exp(m_lnActCoeffMolal_Scaled[0]) * xmolSolvent;
303 }
304 
305 void HMWSoln::getUnscaledMolalityActivityCoefficients(doublereal* acMolality) const
306 {
308  A_Debye_TP(-1.0, -1.0);
309  s_update_lnMolalityActCoeff();
310  std::copy(m_lnActCoeffMolal_Unscaled.begin(), m_lnActCoeffMolal_Unscaled.end(), acMolality);
311  for (size_t k = 0; k < m_kk; k++) {
312  acMolality[k] = exp(acMolality[k]);
313  }
314 }
315 
316 // ------ Partial Molar Properties of the Solution -----------------
317 
318 void HMWSoln::getChemPotentials(doublereal* mu) const
319 {
320  double xx;
321 
322  // First get the standard chemical potentials in molar form. This requires
323  // updates of standard state as a function of T and P
325 
326  // Update the activity coefficients. This also updates the internal molality
327  // array.
328  s_update_lnMolalityActCoeff();
329  double xmolSolvent = moleFraction(0);
330  for (size_t k = 1; k < m_kk; k++) {
331  xx = std::max(m_molalities[k], SmallNumber);
332  mu[k] += RT() * (log(xx) + m_lnActCoeffMolal_Scaled[k]);
333  }
334  xx = std::max(xmolSolvent, SmallNumber);
335  mu[0] += RT() * (log(xx) + m_lnActCoeffMolal_Scaled[0]);
336 }
337 
338 void HMWSoln::getPartialMolarEnthalpies(doublereal* hbar) const
339 {
340  // Get the nondimensional standard state enthalpies
341  getEnthalpy_RT(hbar);
342 
343  // dimensionalize it.
344  for (size_t k = 0; k < m_kk; k++) {
345  hbar[k] *= RT();
346  }
347 
348  // Update the activity coefficients, This also update the internally stored
349  // molalities.
350  s_update_lnMolalityActCoeff();
352  for (size_t k = 0; k < m_kk; k++) {
353  hbar[k] -= RT() * temperature() * m_dlnActCoeffMolaldT_Scaled[k];
354  }
355 }
356 
357 void HMWSoln::getPartialMolarEntropies(doublereal* sbar) const
358 {
359  // Get the standard state entropies at the temperature and pressure of the
360  // solution.
361  getEntropy_R(sbar);
362 
363  // Dimensionalize the entropies
364  for (size_t k = 0; k < m_kk; k++) {
365  sbar[k] *= GasConstant;
366  }
367 
368  // Update the activity coefficients, This also update the internally stored
369  // molalities.
370  s_update_lnMolalityActCoeff();
371 
372  // First we will add in the obvious dependence on the T term out front of
373  // the log activity term
374  doublereal mm;
375  for (size_t k = 1; k < m_kk; k++) {
376  mm = std::max(SmallNumber, m_molalities[k]);
377  sbar[k] -= GasConstant * (log(mm) + m_lnActCoeffMolal_Scaled[k]);
378  }
379  double xmolSolvent = moleFraction(0);
380  mm = std::max(SmallNumber, xmolSolvent);
381  sbar[0] -= GasConstant *(log(mm) + m_lnActCoeffMolal_Scaled[0]);
382 
383  // Check to see whether activity coefficients are temperature dependent. If
384  // they are, then calculate the their temperature derivatives and add them
385  // into the result.
387  for (size_t k = 0; k < m_kk; k++) {
388  sbar[k] -= RT() * m_dlnActCoeffMolaldT_Scaled[k];
389  }
390 }
391 
392 void HMWSoln::getPartialMolarVolumes(doublereal* vbar) const
393 {
394  // Get the standard state values in m^3 kmol-1
395  getStandardVolumes(vbar);
396 
397  // Update the derivatives wrt the activity coefficients.
398  s_update_lnMolalityActCoeff();
400  for (size_t k = 0; k < m_kk; k++) {
401  vbar[k] += RT() * m_dlnActCoeffMolaldP_Scaled[k];
402  }
403 }
404 
405 void HMWSoln::getPartialMolarCp(doublereal* cpbar) const
406 {
407  getCp_R(cpbar);
408  for (size_t k = 0; k < m_kk; k++) {
409  cpbar[k] *= GasConstant;
410  }
411 
412  // Update the activity coefficients, This also update the internally stored
413  // molalities.
414  s_update_lnMolalityActCoeff();
417  for (size_t k = 0; k < m_kk; k++) {
418  cpbar[k] -= (2.0 * RT() * m_dlnActCoeffMolaldT_Scaled[k] +
420  }
421 }
422 
423 // -------------- Utilities -------------------------------
424 
425 doublereal HMWSoln::satPressure(doublereal t) {
426  double p_old = pressure();
427  double t_old = temperature();
428  double pres = m_waterSS->satPressure(t);
429 
430  // Set the underlying object back to its original state.
431  m_waterSS->setState_TP(t_old, p_old);
432  return pres;
433 }
434 
435 static void check_nParams(const std::string& method, size_t nParams,
436  size_t m_formPitzerTemp)
437 {
438  if (m_formPitzerTemp == PITZER_TEMP_CONSTANT && nParams != 1) {
439  throw CanteraError(method, "'constant' temperature model requires one"
440  " coefficient for each of parameter, but {} were given", nParams);
441  } else if (m_formPitzerTemp == PITZER_TEMP_LINEAR && nParams != 2) {
442  throw CanteraError(method, "'linear' temperature model requires two"
443  " coefficients for each parameter, but {} were given", nParams);
444  }
445  if (m_formPitzerTemp == PITZER_TEMP_COMPLEX1 && nParams != 5) {
446  throw CanteraError(method, "'complex' temperature model requires five"
447  " coefficients for each parameter, but {} were given", nParams);
448  }
449 }
450 
451 void HMWSoln::setBinarySalt(const std::string& sp1, const std::string& sp2,
452  size_t nParams, double* beta0, double* beta1, double* beta2,
453  double* Cphi, double alpha1, double alpha2)
454 {
455  size_t k1 = speciesIndex(sp1);
456  size_t k2 = speciesIndex(sp2);
457  if (k1 == npos) {
458  throw CanteraError("HMWSoln::setBinarySalt", "Species '{}' not found", sp1);
459  } else if (k2 == npos) {
460  throw CanteraError("HMWSoln::setBinarySalt", "Species '{}' not found", sp2);
461  }
462  if (charge(k1) < 0 && charge(k2) > 0) {
463  std::swap(k1, k2);
464  } else if (charge(k1) * charge(k2) >= 0) {
465  throw CanteraError("HMWSoln::setBinarySalt", "Species '{}' and '{}' "
466  "do not have opposite charges ({}, {})", sp1, sp2,
467  charge(k1), charge(k2));
468  }
469  check_nParams("HMWSoln::setBinarySalt", nParams, m_formPitzerTemp);
470 
471  size_t c = m_CounterIJ[k1 * m_kk + k2];
472  m_Beta0MX_ij[c] = beta0[0];
473  m_Beta1MX_ij[c] = beta1[0];
474  m_Beta2MX_ij[c] = beta2[0];
475  m_CphiMX_ij[c] = Cphi[0];
476  for (size_t n = 0; n < nParams; n++) {
477  m_Beta0MX_ij_coeff(n, c) = beta0[n];
478  m_Beta1MX_ij_coeff(n, c) = beta1[n];
479  m_Beta2MX_ij_coeff(n, c) = beta2[n];
480  m_CphiMX_ij_coeff(n, c) = Cphi[n];
481  }
482  m_Alpha1MX_ij[c] = alpha1;
483  m_Alpha2MX_ij[c] = alpha2;
484 }
485 
486 void HMWSoln::setTheta(const std::string& sp1, const std::string& sp2,
487  size_t nParams, double* theta)
488 {
489  size_t k1 = speciesIndex(sp1);
490  size_t k2 = speciesIndex(sp2);
491  if (k1 == npos) {
492  throw CanteraError("HMWSoln::setTheta", "Species '{}' not found", sp1);
493  } else if (k2 == npos) {
494  throw CanteraError("HMWSoln::setTheta", "Species '{}' not found", sp2);
495  }
496  if (charge(k1) * charge(k2) <= 0) {
497  throw CanteraError("HMWSoln::setTheta", "Species '{}' and '{}' "
498  "should both have the same (non-zero) charge ({}, {})", sp1, sp2,
499  charge(k1), charge(k2));
500  }
501  check_nParams("HMWSoln::setTheta", nParams, m_formPitzerTemp);
502  size_t c = m_CounterIJ[k1 * m_kk + k2];
503  m_Theta_ij[c] = theta[0];
504  for (size_t n = 0; n < nParams; n++) {
505  m_Theta_ij_coeff(n, c) = theta[n];
506  }
507 }
508 
509 void HMWSoln::setPsi(const std::string& sp1, const std::string& sp2,
510  const std::string& sp3, size_t nParams, double* psi)
511 {
512  size_t k1 = speciesIndex(sp1);
513  size_t k2 = speciesIndex(sp2);
514  size_t k3 = speciesIndex(sp3);
515  if (k1 == npos) {
516  throw CanteraError("HMWSoln::setPsi", "Species '{}' not found", sp1);
517  } else if (k2 == npos) {
518  throw CanteraError("HMWSoln::setPsi", "Species '{}' not found", sp2);
519  } else if (k3 == npos) {
520  throw CanteraError("HMWSoln::setPsi", "Species '{}' not found", sp3);
521  }
522 
523  if (!charge(k1) || !charge(k2) || !charge(k3) ||
524  std::abs(sign(charge(k1) + sign(charge(k2)) + sign(charge(k3)))) != 1) {
525  throw CanteraError("HMWSoln::setPsi", "All species must be ions and"
526  " must include at least one cation and one anion, but given species"
527  " (charges) were: {} ({}), {} ({}), and {} ({}).",
528  sp1, charge(k1), sp2, charge(k2), sp3, charge(k3));
529  }
530  check_nParams("HMWSoln::setPsi", nParams, m_formPitzerTemp);
531  auto cc = {k1*m_kk*m_kk + k2*m_kk + k3,
532  k1*m_kk*m_kk + k3*m_kk + k2,
533  k2*m_kk*m_kk + k1*m_kk + k3,
534  k2*m_kk*m_kk + k3*m_kk + k1,
535  k3*m_kk*m_kk + k2*m_kk + k1,
536  k3*m_kk*m_kk + k1*m_kk + k2};
537  for (auto c : cc) {
538  for (size_t n = 0; n < nParams; n++) {
539  m_Psi_ijk_coeff(n, c) = psi[n];
540  }
541  m_Psi_ijk[c] = psi[0];
542  }
543 }
544 
545 void HMWSoln::setLambda(const std::string& sp1, const std::string& sp2,
546  size_t nParams, double* lambda)
547 {
548  size_t k1 = speciesIndex(sp1);
549  size_t k2 = speciesIndex(sp2);
550  if (k1 == npos) {
551  throw CanteraError("HMWSoln::setLambda", "Species '{}' not found", sp1);
552  } else if (k2 == npos) {
553  throw CanteraError("HMWSoln::setLambda", "Species '{}' not found", sp2);
554  }
555 
556  if (charge(k1) != 0 && charge(k2) != 0) {
557  throw CanteraError("HMWSoln::setLambda", "Expected at least one neutral"
558  " species, but given species (charges) were: {} ({}) and {} ({}).",
559  sp1, charge(k1), sp2, charge(k2));
560  }
561  if (charge(k1) != 0) {
562  std::swap(k1, k2);
563  }
564  check_nParams("HMWSoln::setLambda", nParams, m_formPitzerTemp);
565  size_t c = k1*m_kk + k2;
566  for (size_t n = 0; n < nParams; n++) {
567  m_Lambda_nj_coeff(n, c) = lambda[n];
568  }
569  m_Lambda_nj(k1, k2) = lambda[0];
570 }
571 
572 void HMWSoln::setMunnn(const std::string& sp, size_t nParams, double* munnn)
573 {
574  size_t k = speciesIndex(sp);
575  if (k == npos) {
576  throw CanteraError("HMWSoln::setMunnn", "Species '{}' not found", sp);
577  }
578 
579  if (charge(k) != 0) {
580  throw CanteraError("HMWSoln::setMunnn", "Expected a neutral species,"
581  " got {} ({}).", sp, charge(k));
582  }
583  check_nParams("HMWSoln::setMunnn", nParams, m_formPitzerTemp);
584  for (size_t n = 0; n < nParams; n++) {
585  m_Mu_nnn_coeff(n, k) = munnn[n];
586  }
587  m_Mu_nnn[k] = munnn[0];
588 }
589 
590 void HMWSoln::setZeta(const std::string& sp1, const std::string& sp2,
591  const std::string& sp3, size_t nParams, double* psi)
592 {
593  size_t k1 = speciesIndex(sp1);
594  size_t k2 = speciesIndex(sp2);
595  size_t k3 = speciesIndex(sp3);
596  if (k1 == npos) {
597  throw CanteraError("HMWSoln::setZeta", "Species '{}' not found", sp1);
598  } else if (k2 == npos) {
599  throw CanteraError("HMWSoln::setZeta", "Species '{}' not found", sp2);
600  } else if (k3 == npos) {
601  throw CanteraError("HMWSoln::setZeta", "Species '{}' not found", sp3);
602  }
603 
604  if (charge(k1)*charge(k2)*charge(k3) != 0 ||
605  sign(charge(k1)) + sign(charge(k2)) + sign(charge(k3)) != 0) {
606  throw CanteraError("HMWSoln::setZeta", "Requires one neutral species, "
607  "one cation, and one anion, but given species (charges) were: "
608  "{} ({}), {} ({}), and {} ({}).",
609  sp1, charge(k1), sp2, charge(k2), sp3, charge(k3));
610  }
611 
612  //! Make k1 the neutral species
613  if (charge(k2) == 0) {
614  std::swap(k1, k2);
615  } else if (charge(k3) == 0) {
616  std::swap(k1, k3);
617  }
618 
619  // Make k2 the cation
620  if (charge(k3) > 0) {
621  std::swap(k2, k3);
622  }
623 
624  check_nParams("HMWSoln::setZeta", nParams, m_formPitzerTemp);
625  // In contrast to setPsi, there are no duplicate entries
626  size_t c = k1 * m_kk *m_kk + k2 * m_kk + k3;
627  for (size_t n = 0; n < nParams; n++) {
628  m_Psi_ijk_coeff(n, c) = psi[n];
629  }
630  m_Psi_ijk[c] = psi[0];
631 }
632 
633 void HMWSoln::setPitzerTempModel(const std::string& model)
634 {
635  if (caseInsensitiveEquals(model, "constant") || caseInsensitiveEquals(model, "default")) {
636  m_formPitzerTemp = PITZER_TEMP_CONSTANT;
637  } else if (caseInsensitiveEquals(model, "linear")) {
638  m_formPitzerTemp = PITZER_TEMP_LINEAR;
639  } else if (caseInsensitiveEquals(model, "complex") || caseInsensitiveEquals(model, "complex1")) {
640  m_formPitzerTemp = PITZER_TEMP_COMPLEX1;
641  } else {
642  throw CanteraError("HMWSoln::setPitzerTempModel",
643  "Unknown Pitzer ActivityCoeff Temp model: {}", model);
644  }
645 }
646 
647 void HMWSoln::setA_Debye(double A)
648 {
649  if (A < 0) {
650  m_form_A_Debye = A_DEBYE_WATER;
651  } else {
652  m_form_A_Debye = A_DEBYE_CONST;
653  m_A_Debye = A;
654  }
655 }
656 
657 void HMWSoln::setCroppingCoefficients(double ln_gamma_k_min,
658  double ln_gamma_k_max, double ln_gamma_o_min, double ln_gamma_o_max)
659 {
660  CROP_ln_gamma_k_min = ln_gamma_k_min;
661  CROP_ln_gamma_k_max = ln_gamma_k_max;
662  CROP_ln_gamma_o_min = ln_gamma_o_min;
663  CROP_ln_gamma_o_max = ln_gamma_o_max;
664 }
665 
666 vector_fp getSizedVector(const AnyMap& item, const std::string& key, size_t nCoeffs)
667 {
668  vector_fp v;
669  if (item[key].is<double>()) {
670  // Allow a single value to be given directly, rather than as a list of
671  // one item
672  v.push_back(item[key].asDouble());
673  } else {
674  v = item[key].asVector<double>(1, nCoeffs);
675  }
676  if (v.size() == 1 && nCoeffs == 5) {
677  // Adapt constant-temperature data to be compatible with the "complex"
678  // temperature model
679  v.resize(5, 0.0);
680  }
681  return v;
682 }
683 
685 {
687  if (m_input.hasKey("activity-data")) {
688  auto& actData = m_input["activity-data"].as<AnyMap>();
689  setPitzerTempModel(actData["temperature-model"].asString());
690  initLengths();
691  size_t nCoeffs = 1;
692  if (m_formPitzerTemp == PITZER_TEMP_LINEAR) {
693  nCoeffs = 2;
694  } else if (m_formPitzerTemp == PITZER_TEMP_COMPLEX1) {
695  nCoeffs = 5;
696  }
697  if (actData.hasKey("A_Debye")) {
698  if (actData["A_Debye"] == "variable") {
699  setA_Debye(-1);
700  } else {
701  setA_Debye(actData.convert("A_Debye", "kg^0.5/gmol^0.5"));
702  }
703  }
704  if (actData.hasKey("max-ionic-strength")) {
705  setMaxIonicStrength(actData["max-ionic-strength"].asDouble());
706  }
707  if (actData.hasKey("interactions")) {
708  for (auto& item : actData["interactions"].asVector<AnyMap>()) {
709  auto& species = item["species"].asVector<string>(1, 3);
710  size_t nsp = species.size();
711  double q0 = charge(speciesIndex(species[0]));
712  double q1 = (nsp > 1) ? charge(speciesIndex(species[1])) : 0;
713  double q2 = (nsp == 3) ? charge(speciesIndex(species[2])) : 0;
714  if (nsp == 2 && q0 * q1 < 0) {
715  // Two species with opposite charges - binary salt
716  vector_fp beta0 = getSizedVector(item, "beta0", nCoeffs);
717  vector_fp beta1 = getSizedVector(item, "beta1", nCoeffs);
718  vector_fp beta2 = getSizedVector(item, "beta2", nCoeffs);
719  vector_fp Cphi = getSizedVector(item, "Cphi", nCoeffs);
720  if (beta0.size() != beta1.size() || beta0.size() != beta2.size()
721  || beta0.size() != Cphi.size()) {
722  throw InputFileError("HMWSoln::initThermo", item,
723  "Inconsistent binary salt array sizes ({}, {}, {}, {})",
724  beta0.size(), beta1.size(), beta2.size(), Cphi.size());
725  }
726  double alpha1 = item["alpha1"].asDouble();
727  double alpha2 = item.getDouble("alpha2", 0.0);
728  setBinarySalt(species[0], species[1], beta0.size(),
729  beta0.data(), beta1.data(), beta2.data(), Cphi.data(),
730  alpha1, alpha2);
731  } else if (nsp == 2 && q0 * q1 > 0) {
732  // Two species with like charges - "theta" interaction
733  vector_fp theta = getSizedVector(item, "theta", nCoeffs);
734  setTheta(species[0], species[1], theta.size(), theta.data());
735  } else if (nsp == 2 && q0 * q1 == 0) {
736  // Two species, including at least one neutral
737  vector_fp lambda = getSizedVector(item, "lambda", nCoeffs);
738  setLambda(species[0], species[1], lambda.size(), lambda.data());
739  } else if (nsp == 3 && q0 * q1 * q2 != 0) {
740  // Three charged species - "psi" interaction
741  vector_fp psi = getSizedVector(item, "psi", nCoeffs);
742  setPsi(species[0], species[1], species[2],
743  psi.size(), psi.data());
744  } else if (nsp == 3 && q0 * q1 * q2 == 0) {
745  // Three species, including one neutral
746  vector_fp zeta = getSizedVector(item, "zeta", nCoeffs);
747  setZeta(species[0], species[1], species[2],
748  zeta.size(), zeta.data());
749  } else if (nsp == 1) {
750  // single species (should be neutral)
751  vector_fp mu = getSizedVector(item, "mu", nCoeffs);
752  setMunnn(species[0], mu.size(), mu.data());
753  }
754  }
755  }
756  if (actData.hasKey("cropping-coefficients")) {
757  auto& crop = actData["cropping-coefficients"].as<AnyMap>();
758  setCroppingCoefficients(
759  crop.getDouble("ln_gamma_k_min", -5.0),
760  crop.getDouble("ln_gamma_k_max", 15.0),
761  crop.getDouble("ln_gamma_o_min", -6.0),
762  crop.getDouble("ln_gamma_o_max", 3.0));
763  }
764  } else {
765  initLengths();
766  }
767 
768  for (int i = 0; i < 17; i++) {
769  elambda[i] = 0.0;
770  elambda1[i] = 0.0;
771  }
772 
773  // Store a local pointer to the water standard state model.
774  m_waterSS = providePDSS(0);
775 
776  // Initialize the water property calculator. It will share the internal eos
777  // water calculator.
778  m_waterProps.reset(new WaterProps(dynamic_cast<PDSS_Water*>(m_waterSS)));
779 
780  // Lastly calculate the charge balance and then add stuff until the charges
781  // compensate
782  vector_fp mf(m_kk, 0.0);
783  getMoleFractions(mf.data());
784  bool notDone = true;
785 
786  while (notDone) {
787  double sum = 0.0;
788  size_t kMaxC = npos;
789  double MaxC = 0.0;
790  for (size_t k = 0; k < m_kk; k++) {
791  sum += mf[k] * charge(k);
792  if (fabs(mf[k] * charge(k)) > MaxC) {
793  kMaxC = k;
794  }
795  }
796  size_t kHp = speciesIndex("H+");
797  size_t kOHm = speciesIndex("OH-");
798 
799  if (fabs(sum) > 1.0E-30) {
800  if (kHp != npos) {
801  if (mf[kHp] > sum * 1.1) {
802  mf[kHp] -= sum;
803  mf[0] += sum;
804  notDone = false;
805  } else {
806  if (sum > 0.0) {
807  mf[kHp] *= 0.5;
808  mf[0] += mf[kHp];
809  sum -= mf[kHp];
810  }
811  }
812  }
813  if (notDone) {
814  if (kOHm != npos) {
815  if (mf[kOHm] > -sum * 1.1) {
816  mf[kOHm] += sum;
817  mf[0] -= sum;
818  notDone = false;
819  } else {
820  if (sum < 0.0) {
821  mf[kOHm] *= 0.5;
822  mf[0] += mf[kOHm];
823  sum += mf[kOHm];
824  }
825  }
826  }
827  if (notDone && kMaxC != npos) {
828  if (mf[kMaxC] > (1.1 * sum / charge(kMaxC))) {
829  mf[kMaxC] -= sum / charge(kMaxC);
830  mf[0] += sum / charge(kMaxC);
831  } else {
832  mf[kMaxC] *= 0.5;
833  mf[0] += mf[kMaxC];
834  notDone = true;
835  }
836  }
837  }
838  setMoleFractions(mf.data());
839  } else {
840  notDone = false;
841  }
842  }
843 
846  setMoleFSolventMin(1.0E-5);
847 }
848 
849 void HMWSoln::initThermoXML(XML_Node& phaseNode, const std::string& id_)
850 {
851  if (id_.size() > 0) {
852  string idp = phaseNode.id();
853  if (idp != id_) {
854  throw CanteraError("HMWSoln::initThermoXML",
855  "phasenode and Id are incompatible");
856  }
857  }
858 
859  // Find the Thermo XML node
860  if (!phaseNode.hasChild("thermo")) {
861  throw CanteraError("HMWSoln::initThermoXML",
862  "no thermo XML node");
863  }
864  XML_Node& thermoNode = phaseNode.child("thermo");
865 
866  // Determine the form of the Pitzer model, We will use this information to
867  // size arrays below.
868  if (thermoNode.hasChild("activityCoefficients")) {
869  XML_Node& scNode = thermoNode.child("activityCoefficients");
870 
871  // Determine the form of the temperature dependence of the Pitzer
872  // activity coefficient model.
873  string formString = scNode.attrib("TempModel");
874  if (formString != "") {
875  setPitzerTempModel(formString);
876  }
877 
878  // Determine the reference temperature of the Pitzer activity
879  // coefficient model's temperature dependence formulation: defaults to
880  // 25C
881  formString = scNode.attrib("TempReference");
882  if (formString != "") {
883  setPitzerRefTemperature(fpValueCheck(formString));
884  }
885  }
886 
887  // Initialize all of the lengths of arrays in the object
888  // now that we know what species are in the phase.
889  initLengths();
890 
891  // Go get all of the coefficients and factors in the activityCoefficients
892  // XML block
893  if (thermoNode.hasChild("activityCoefficients")) {
894  XML_Node& acNode = thermoNode.child("activityCoefficients");
895 
896  // Look for parameters for A_Debye
897  if (acNode.hasChild("A_Debye")) {
898  XML_Node& ADebye = acNode.child("A_Debye");
899  if (caseInsensitiveEquals(ADebye["model"], "water")) {
900  setA_Debye(-1);
901  } else {
902  setA_Debye(getFloat(acNode, "A_Debye"));
903  }
904  }
905 
906  // Look for Parameters for the Maximum Ionic Strength
907  if (acNode.hasChild("maxIonicStrength")) {
908  setMaxIonicStrength(getFloat(acNode, "maxIonicStrength"));
909  }
910 
911  for (const auto& xmlACChild : acNode.children()) {
912  string nodeName = xmlACChild->name();
913 
914  // Process any of the XML fields that make up the Pitzer Database.
915  // Entries will be ignored if any of the species in the entry aren't
916  // in the solution.
917  if (caseInsensitiveEquals(nodeName, "binarysaltparameters")) {
918  readXMLBinarySalt(*xmlACChild);
919  } else if (caseInsensitiveEquals(nodeName, "thetaanion")) {
920  readXMLTheta(*xmlACChild);
921  } else if (caseInsensitiveEquals(nodeName, "thetacation")) {
922  readXMLTheta(*xmlACChild);
923  } else if (caseInsensitiveEquals(nodeName, "psicommonanion")) {
924  readXMLPsi(*xmlACChild);
925  } else if (caseInsensitiveEquals(nodeName, "psicommoncation")) {
926  readXMLPsi(*xmlACChild);
927  } else if (caseInsensitiveEquals(nodeName, "lambdaneutral")) {
928  readXMLLambdaNeutral(*xmlACChild);
929  } else if (caseInsensitiveEquals(nodeName, "zetacation")) {
930  readXMLZetaCation(*xmlACChild);
931  }
932  }
933 
934  // Go look up the optional Cropping parameters
935  if (acNode.hasChild("croppingCoefficients")) {
936  XML_Node& cropNode = acNode.child("croppingCoefficients");
937  setCroppingCoefficients(
938  getFloat(cropNode.child("ln_gamma_k_min"), "pureSolventValue"),
939  getFloat(cropNode.child("ln_gamma_k_max"), "pureSolventValue"),
940  getFloat(cropNode.child("ln_gamma_o_min"), "pureSolventValue"),
941  getFloat(cropNode.child("ln_gamma_o_max"), "pureSolventValue"));
942  }
943  }
944 
945  MolalityVPSSTP::initThermoXML(phaseNode, id_);
946 }
947 
948 double HMWSoln::A_Debye_TP(double tempArg, double presArg) const
949 {
950  double T = temperature();
951  double A;
952  if (tempArg != -1.0) {
953  T = tempArg;
954  }
955  double P = pressure();
956  if (presArg != -1.0) {
957  P = presArg;
958  }
959 
960  static const int cacheId = m_cache.getId();
961  CachedScalar cached = m_cache.getScalar(cacheId);
962  if(cached.validate(T, P)) {
963  return m_A_Debye;
964  }
965 
966  switch (m_form_A_Debye) {
967  case A_DEBYE_CONST:
968  A = m_A_Debye;
969  break;
970  case A_DEBYE_WATER:
971  A = m_waterProps->ADebye(T, P, 0);
972  m_A_Debye = A;
973  break;
974  default:
975  throw CanteraError("HMWSoln::A_Debye_TP", "shouldn't be here");
976  }
977  return A;
978 }
979 
980 double HMWSoln::dA_DebyedT_TP(double tempArg, double presArg) const
981 {
982  doublereal T = temperature();
983  if (tempArg != -1.0) {
984  T = tempArg;
985  }
986  doublereal P = pressure();
987  if (presArg != -1.0) {
988  P = presArg;
989  }
990  doublereal dAdT;
991  switch (m_form_A_Debye) {
992  case A_DEBYE_CONST:
993  dAdT = 0.0;
994  break;
995  case A_DEBYE_WATER:
996  dAdT = m_waterProps->ADebye(T, P, 1);
997  break;
998  default:
999  throw CanteraError("HMWSoln::dA_DebyedT_TP", "shouldn't be here");
1000  }
1001  return dAdT;
1002 }
1003 
1004 double HMWSoln::dA_DebyedP_TP(double tempArg, double presArg) const
1005 {
1006  double T = temperature();
1007  if (tempArg != -1.0) {
1008  T = tempArg;
1009  }
1010  double P = pressure();
1011  if (presArg != -1.0) {
1012  P = presArg;
1013  }
1014 
1015  double dAdP;
1016  static const int cacheId = m_cache.getId();
1017  CachedScalar cached = m_cache.getScalar(cacheId);
1018  switch (m_form_A_Debye) {
1019  case A_DEBYE_CONST:
1020  dAdP = 0.0;
1021  break;
1022  case A_DEBYE_WATER:
1023  if(cached.validate(T, P)) {
1024  dAdP = cached.value;
1025  } else {
1026  dAdP = m_waterProps->ADebye(T, P, 3);
1027  cached.value = dAdP;
1028  }
1029  break;
1030  default:
1031  throw CanteraError("HMWSoln::dA_DebyedP_TP", "shouldn't be here");
1032  }
1033  return dAdP;
1034 }
1035 
1036 double HMWSoln::ADebye_L(double tempArg, double presArg) const
1037 {
1038  double dAdT = dA_DebyedT_TP();
1039  double dAphidT = dAdT /3.0;
1040  double T = temperature();
1041  if (tempArg != -1.0) {
1042  T = tempArg;
1043  }
1044  return dAphidT * (4.0 * GasConstant * T * T);
1045 }
1046 
1047 double HMWSoln::ADebye_V(double tempArg, double presArg) const
1048 {
1049  double dAdP = dA_DebyedP_TP();
1050  double dAphidP = dAdP /3.0;
1051  double T = temperature();
1052  if (tempArg != -1.0) {
1053  T = tempArg;
1054  }
1055  return - dAphidP * (4.0 * GasConstant * T);
1056 }
1057 
1058 double HMWSoln::ADebye_J(double tempArg, double presArg) const
1059 {
1060  double T = temperature();
1061  if (tempArg != -1.0) {
1062  T = tempArg;
1063  }
1064  double A_L = ADebye_L(T, presArg);
1065  double d2 = d2A_DebyedT2_TP(T, presArg);
1066  double d2Aphi = d2 / 3.0;
1067  return 2.0 * A_L / T + 4.0 * GasConstant * T * T *d2Aphi;
1068 }
1069 
1070 double HMWSoln::d2A_DebyedT2_TP(double tempArg, double presArg) const
1071 {
1072  double T = temperature();
1073  if (tempArg != -1.0) {
1074  T = tempArg;
1075  }
1076  double P = pressure();
1077  if (presArg != -1.0) {
1078  P = presArg;
1079  }
1080  double d2AdT2;
1081  switch (m_form_A_Debye) {
1082  case A_DEBYE_CONST:
1083  d2AdT2 = 0.0;
1084  break;
1085  case A_DEBYE_WATER:
1086  d2AdT2 = m_waterProps->ADebye(T, P, 2);
1087  break;
1088  default:
1089  throw CanteraError("HMWSoln::d2A_DebyedT2_TP", "shouldn't be here");
1090  }
1091  return d2AdT2;
1092 }
1093 
1094 // ---------- Other Property Functions
1095 
1096 // ------------ Private and Restricted Functions ------------------
1097 
1099 {
1100  m_tmpV.resize(m_kk, 0.0);
1101  m_molalitiesCropped.resize(m_kk, 0.0);
1102 
1103  size_t maxCounterIJlen = 1 + (m_kk-1) * (m_kk-2) / 2;
1104 
1105  // Figure out the size of the temperature coefficient arrays
1106  int TCoeffLength = 1;
1107  if (m_formPitzerTemp == PITZER_TEMP_LINEAR) {
1108  TCoeffLength = 2;
1109  } else if (m_formPitzerTemp == PITZER_TEMP_COMPLEX1) {
1110  TCoeffLength = 5;
1111  }
1112 
1113  m_Beta0MX_ij.resize(maxCounterIJlen, 0.0);
1114  m_Beta0MX_ij_L.resize(maxCounterIJlen, 0.0);
1115  m_Beta0MX_ij_LL.resize(maxCounterIJlen, 0.0);
1116  m_Beta0MX_ij_P.resize(maxCounterIJlen, 0.0);
1117  m_Beta0MX_ij_coeff.resize(TCoeffLength, maxCounterIJlen, 0.0);
1118 
1119  m_Beta1MX_ij.resize(maxCounterIJlen, 0.0);
1120  m_Beta1MX_ij_L.resize(maxCounterIJlen, 0.0);
1121  m_Beta1MX_ij_LL.resize(maxCounterIJlen, 0.0);
1122  m_Beta1MX_ij_P.resize(maxCounterIJlen, 0.0);
1123  m_Beta1MX_ij_coeff.resize(TCoeffLength, maxCounterIJlen, 0.0);
1124 
1125  m_Beta2MX_ij.resize(maxCounterIJlen, 0.0);
1126  m_Beta2MX_ij_L.resize(maxCounterIJlen, 0.0);
1127  m_Beta2MX_ij_LL.resize(maxCounterIJlen, 0.0);
1128  m_Beta2MX_ij_P.resize(maxCounterIJlen, 0.0);
1129  m_Beta2MX_ij_coeff.resize(TCoeffLength, maxCounterIJlen, 0.0);
1130 
1131  m_CphiMX_ij.resize(maxCounterIJlen, 0.0);
1132  m_CphiMX_ij_L.resize(maxCounterIJlen, 0.0);
1133  m_CphiMX_ij_LL.resize(maxCounterIJlen, 0.0);
1134  m_CphiMX_ij_P.resize(maxCounterIJlen, 0.0);
1135  m_CphiMX_ij_coeff.resize(TCoeffLength, maxCounterIJlen, 0.0);
1136 
1137  m_Alpha1MX_ij.resize(maxCounterIJlen, 2.0);
1138  m_Alpha2MX_ij.resize(maxCounterIJlen, 12.0);
1139  m_Theta_ij.resize(maxCounterIJlen, 0.0);
1140  m_Theta_ij_L.resize(maxCounterIJlen, 0.0);
1141  m_Theta_ij_LL.resize(maxCounterIJlen, 0.0);
1142  m_Theta_ij_P.resize(maxCounterIJlen, 0.0);
1143  m_Theta_ij_coeff.resize(TCoeffLength, maxCounterIJlen, 0.0);
1144 
1145  size_t n = m_kk*m_kk*m_kk;
1146  m_Psi_ijk.resize(n, 0.0);
1147  m_Psi_ijk_L.resize(n, 0.0);
1148  m_Psi_ijk_LL.resize(n, 0.0);
1149  m_Psi_ijk_P.resize(n, 0.0);
1150  m_Psi_ijk_coeff.resize(TCoeffLength, n, 0.0);
1151 
1152  m_Lambda_nj.resize(m_kk, m_kk, 0.0);
1153  m_Lambda_nj_L.resize(m_kk, m_kk, 0.0);
1154  m_Lambda_nj_LL.resize(m_kk, m_kk, 0.0);
1155  m_Lambda_nj_P.resize(m_kk, m_kk, 0.0);
1156  m_Lambda_nj_coeff.resize(TCoeffLength, m_kk * m_kk, 0.0);
1157 
1158  m_Mu_nnn.resize(m_kk, 0.0);
1159  m_Mu_nnn_L.resize(m_kk, 0.0);
1160  m_Mu_nnn_LL.resize(m_kk, 0.0);
1161  m_Mu_nnn_P.resize(m_kk, 0.0);
1162  m_Mu_nnn_coeff.resize(TCoeffLength, m_kk, 0.0);
1163 
1164  m_lnActCoeffMolal_Scaled.resize(m_kk, 0.0);
1165  m_dlnActCoeffMolaldT_Scaled.resize(m_kk, 0.0);
1166  m_d2lnActCoeffMolaldT2_Scaled.resize(m_kk, 0.0);
1167  m_dlnActCoeffMolaldP_Scaled.resize(m_kk, 0.0);
1168  m_lnActCoeffMolal_Unscaled.resize(m_kk, 0.0);
1169  m_dlnActCoeffMolaldT_Unscaled.resize(m_kk, 0.0);
1171  m_dlnActCoeffMolaldP_Unscaled.resize(m_kk, 0.0);
1172 
1173  m_CounterIJ.resize(m_kk*m_kk, 0);
1174  m_gfunc_IJ.resize(maxCounterIJlen, 0.0);
1175  m_g2func_IJ.resize(maxCounterIJlen, 0.0);
1176  m_hfunc_IJ.resize(maxCounterIJlen, 0.0);
1177  m_h2func_IJ.resize(maxCounterIJlen, 0.0);
1178  m_BMX_IJ.resize(maxCounterIJlen, 0.0);
1179  m_BMX_IJ_L.resize(maxCounterIJlen, 0.0);
1180  m_BMX_IJ_LL.resize(maxCounterIJlen, 0.0);
1181  m_BMX_IJ_P.resize(maxCounterIJlen, 0.0);
1182  m_BprimeMX_IJ.resize(maxCounterIJlen, 0.0);
1183  m_BprimeMX_IJ_L.resize(maxCounterIJlen, 0.0);
1184  m_BprimeMX_IJ_LL.resize(maxCounterIJlen, 0.0);
1185  m_BprimeMX_IJ_P.resize(maxCounterIJlen, 0.0);
1186  m_BphiMX_IJ.resize(maxCounterIJlen, 0.0);
1187  m_BphiMX_IJ_L.resize(maxCounterIJlen, 0.0);
1188  m_BphiMX_IJ_LL.resize(maxCounterIJlen, 0.0);
1189  m_BphiMX_IJ_P.resize(maxCounterIJlen, 0.0);
1190  m_Phi_IJ.resize(maxCounterIJlen, 0.0);
1191  m_Phi_IJ_L.resize(maxCounterIJlen, 0.0);
1192  m_Phi_IJ_LL.resize(maxCounterIJlen, 0.0);
1193  m_Phi_IJ_P.resize(maxCounterIJlen, 0.0);
1194  m_Phiprime_IJ.resize(maxCounterIJlen, 0.0);
1195  m_PhiPhi_IJ.resize(maxCounterIJlen, 0.0);
1196  m_PhiPhi_IJ_L.resize(maxCounterIJlen, 0.0);
1197  m_PhiPhi_IJ_LL.resize(maxCounterIJlen, 0.0);
1198  m_PhiPhi_IJ_P.resize(maxCounterIJlen, 0.0);
1199  m_CMX_IJ.resize(maxCounterIJlen, 0.0);
1200  m_CMX_IJ_L.resize(maxCounterIJlen, 0.0);
1201  m_CMX_IJ_LL.resize(maxCounterIJlen, 0.0);
1202  m_CMX_IJ_P.resize(maxCounterIJlen, 0.0);
1203 
1204  m_gamma_tmp.resize(m_kk, 0.0);
1205  IMS_lnActCoeffMolal_.resize(m_kk, 0.0);
1206  CROP_speciesCropped_.resize(m_kk, 0);
1207 
1208  counterIJ_setup();
1209 }
1210 
1211 void HMWSoln::s_update_lnMolalityActCoeff() const
1212 {
1213  static const int cacheId = m_cache.getId();
1214  CachedScalar cached = m_cache.getScalar(cacheId);
1215  if( cached.validate(temperature(), pressure(), stateMFNumber()) ) {
1216  return;
1217  }
1218 
1219  // Calculate the molalities. Currently, the molalities may not be current
1220  // with respect to the contents of the State objects' data.
1221  calcMolalities();
1222 
1223  // Calculate a cropped set of molalities that will be used in all activity
1224  // coefficient calculations.
1226 
1227  // Update the temperature dependence of the Pitzer coefficients and their
1228  // derivatives
1230 
1231  // Calculate the IMS cutoff factors
1233 
1234  // Now do the main calculation.
1236 
1237  double xmolSolvent = moleFraction(0);
1238  double xx = std::max(m_xmolSolventMIN, xmolSolvent);
1239  double lnActCoeffMolal0 = - log(xx) + (xx - 1.0)/xx;
1240  double lnxs = log(xx);
1241 
1242  for (size_t k = 1; k < m_kk; k++) {
1243  CROP_speciesCropped_[k] = 0;
1245  if (m_lnActCoeffMolal_Unscaled[k] > (CROP_ln_gamma_k_max- 2.5 *lnxs)) {
1246  CROP_speciesCropped_[k] = 2;
1247  m_lnActCoeffMolal_Unscaled[k] = CROP_ln_gamma_k_max - 2.5 * lnxs;
1248  }
1249  if (m_lnActCoeffMolal_Unscaled[k] < (CROP_ln_gamma_k_min - 2.5 *lnxs)) {
1250  // -1.0 and -1.5 caused multiple solutions
1251  CROP_speciesCropped_[k] = 2;
1252  m_lnActCoeffMolal_Unscaled[k] = CROP_ln_gamma_k_min - 2.5 * lnxs;
1253  }
1254  }
1255  CROP_speciesCropped_[0] = 0;
1256  m_lnActCoeffMolal_Unscaled[0] += (IMS_lnActCoeffMolal_[0] - lnActCoeffMolal0);
1257  if (m_lnActCoeffMolal_Unscaled[0] < CROP_ln_gamma_o_min) {
1258  CROP_speciesCropped_[0] = 2;
1259  m_lnActCoeffMolal_Unscaled[0] = CROP_ln_gamma_o_min;
1260  }
1261  if (m_lnActCoeffMolal_Unscaled[0] > CROP_ln_gamma_o_max) {
1262  CROP_speciesCropped_[0] = 2;
1263  // -0.5 caused multiple solutions
1264  m_lnActCoeffMolal_Unscaled[0] = CROP_ln_gamma_o_max;
1265  }
1266  if (m_lnActCoeffMolal_Unscaled[0] > CROP_ln_gamma_o_max - 0.5 * lnxs) {
1267  CROP_speciesCropped_[0] = 2;
1268  m_lnActCoeffMolal_Unscaled[0] = CROP_ln_gamma_o_max - 0.5 * lnxs;
1269  }
1270 
1271  // Now do the pH Scaling
1273 }
1274 
1276 {
1277  doublereal Imax = 0.0;
1278  m_molalitiesAreCropped = false;
1279 
1280  for (size_t k = 0; k < m_kk; k++) {
1282  Imax = std::max(m_molalities[k] * charge(k) * charge(k), Imax);
1283  }
1284 
1285  int cropMethod = 1;
1286  if (cropMethod == 0) {
1287  // Quick return
1288  if (Imax < m_maxIionicStrength) {
1289  return;
1290  }
1291 
1292  m_molalitiesAreCropped = true;
1293  for (size_t i = 1; i < (m_kk - 1); i++) {
1294  double charge_i = charge(i);
1295  double abs_charge_i = fabs(charge_i);
1296  if (charge_i == 0.0) {
1297  continue;
1298  }
1299  for (size_t j = (i+1); j < m_kk; j++) {
1300  double charge_j = charge(j);
1301  double abs_charge_j = fabs(charge_j);
1302 
1303  // Only loop over oppositely charge species
1304  if (charge_i * charge_j < 0) {
1305  double Iac_max = m_maxIionicStrength;
1306 
1308  Imax = m_molalitiesCropped[i] * abs_charge_i * abs_charge_i;
1309  if (Imax > Iac_max) {
1310  m_molalitiesCropped[i] = Iac_max / (abs_charge_i * abs_charge_i);
1311  }
1312  Imax = m_molalitiesCropped[j] * fabs(abs_charge_j * abs_charge_i);
1313  if (Imax > Iac_max) {
1314  m_molalitiesCropped[j] = Iac_max / (abs_charge_j * abs_charge_i);
1315  }
1316  } else {
1317  Imax = m_molalitiesCropped[j] * abs_charge_j * abs_charge_j;
1318  if (Imax > Iac_max) {
1319  m_molalitiesCropped[j] = Iac_max / (abs_charge_j * abs_charge_j);
1320  }
1321  Imax = m_molalitiesCropped[i] * abs_charge_j * abs_charge_i;
1322  if (Imax > Iac_max) {
1323  m_molalitiesCropped[i] = Iac_max / (abs_charge_j * abs_charge_i);
1324  }
1325  }
1326  }
1327  }
1328  }
1329 
1330  // Do this loop 10 times until we have achieved charge neutrality in the
1331  // cropped molalities
1332  for (int times = 0; times< 10; times++) {
1333  double anion_charge = 0.0;
1334  double cation_charge = 0.0;
1335  size_t anion_contrib_max_i = npos;
1336  double anion_contrib_max = -1.0;
1337  size_t cation_contrib_max_i = npos;
1338  double cation_contrib_max = -1.0;
1339  for (size_t i = 0; i < m_kk; i++) {
1340  double charge_i = charge(i);
1341  if (charge_i < 0.0) {
1342  double anion_contrib = - m_molalitiesCropped[i] * charge_i;
1343  anion_charge += anion_contrib;
1344  if (anion_contrib > anion_contrib_max) {
1345  anion_contrib_max = anion_contrib;
1346  anion_contrib_max_i = i;
1347  }
1348  } else if (charge_i > 0.0) {
1349  double cation_contrib = m_molalitiesCropped[i] * charge_i;
1350  cation_charge += cation_contrib;
1351  if (cation_contrib > cation_contrib_max) {
1352  cation_contrib_max = cation_contrib;
1353  cation_contrib_max_i = i;
1354  }
1355  }
1356  }
1357  double total_charge = cation_charge - anion_charge;
1358  if (total_charge > 1.0E-8) {
1359  double desiredCrop = total_charge/charge(cation_contrib_max_i);
1360  double maxCrop = 0.66 * m_molalitiesCropped[cation_contrib_max_i];
1361  if (desiredCrop < maxCrop) {
1362  m_molalitiesCropped[cation_contrib_max_i] -= desiredCrop;
1363  break;
1364  } else {
1365  m_molalitiesCropped[cation_contrib_max_i] -= maxCrop;
1366  }
1367  } else if (total_charge < -1.0E-8) {
1368  double desiredCrop = total_charge/charge(anion_contrib_max_i);
1369  double maxCrop = 0.66 * m_molalitiesCropped[anion_contrib_max_i];
1370  if (desiredCrop < maxCrop) {
1371  m_molalitiesCropped[anion_contrib_max_i] -= desiredCrop;
1372  break;
1373  } else {
1374  m_molalitiesCropped[anion_contrib_max_i] -= maxCrop;
1375  }
1376  } else {
1377  break;
1378  }
1379  }
1380  }
1381 
1382  if (cropMethod == 1) {
1383  double* molF = m_gamma_tmp.data();
1384  getMoleFractions(molF);
1385  double xmolSolvent = molF[0];
1386  if (xmolSolvent >= MC_X_o_cutoff_) {
1387  return;
1388  }
1389 
1390  m_molalitiesAreCropped = true;
1391  double poly = MC_apCut_ + MC_bpCut_ * xmolSolvent + MC_dpCut_* xmolSolvent * xmolSolvent;
1392  double p = xmolSolvent + MC_epCut_ + exp(- xmolSolvent/ MC_cpCut_) * poly;
1393  double denomInv = 1.0/ (m_Mnaught * p);
1394  for (size_t k = 0; k < m_kk; k++) {
1395  m_molalitiesCropped[k] = molF[k] * denomInv;
1396  }
1397 
1398  // Do a further check to see if the Ionic strength is below a max value
1399  // Reduce the molalities to enforce this. Note, this algorithm preserves
1400  // the charge neutrality of the solution after cropping.
1401  double Itmp = 0.0;
1402  for (size_t k = 0; k < m_kk; k++) {
1403  Itmp += m_molalitiesCropped[k] * charge(k) * charge(k);
1404  }
1405  if (Itmp > m_maxIionicStrength) {
1406  double ratio = Itmp / m_maxIionicStrength;
1407  for (size_t k = 0; k < m_kk; k++) {
1408  if (charge(k) != 0.0) {
1409  m_molalitiesCropped[k] *= ratio;
1410  }
1411  }
1412  }
1413  }
1414 }
1415 
1417 {
1418  m_CounterIJ.resize(m_kk * m_kk);
1419  int counter = 0;
1420  for (size_t i = 0; i < m_kk; i++) {
1421  size_t n = i;
1422  size_t nc = m_kk * i;
1423  m_CounterIJ[n] = 0;
1424  m_CounterIJ[nc] = 0;
1425  }
1426  for (size_t i = 1; i < (m_kk - 1); i++) {
1427  size_t n = m_kk * i + i;
1428  m_CounterIJ[n] = 0;
1429  for (size_t j = (i+1); j < m_kk; j++) {
1430  n = m_kk * j + i;
1431  size_t nc = m_kk * i + j;
1432  counter++;
1433  m_CounterIJ[n] = counter;
1434  m_CounterIJ[nc] = counter;
1435  }
1436  }
1437 }
1438 
1440 {
1441  if (BinSalt.name() != "binarySaltParameters") {
1442  throw CanteraError("HMWSoln::readXMLBinarySalt",
1443  "Incorrect name for processing this routine: " + BinSalt.name());
1444  }
1445 
1446  string iName = BinSalt.attrib("cation");
1447  if (iName == "") {
1448  throw CanteraError("HMWSoln::readXMLBinarySalt", "no cation attrib");
1449  }
1450  string jName = BinSalt.attrib("anion");
1451  if (jName == "") {
1452  throw CanteraError("HMWSoln::readXMLBinarySalt", "no anion attrib");
1453  }
1454 
1455  // Find the index of the species in the current phase. It's not an error to
1456  // not find the species
1457  if (speciesIndex(iName) == npos || speciesIndex(jName) == npos) {
1458  return;
1459  }
1460 
1461  vector_fp beta0, beta1, beta2, Cphi;
1462  getFloatArray(BinSalt, beta0, false, "", "beta0");
1463  getFloatArray(BinSalt, beta1, false, "", "beta1");
1464  getFloatArray(BinSalt, beta2, false, "", "beta2");
1465  getFloatArray(BinSalt, Cphi, false, "", "Cphi");
1466  if (beta0.size() != beta1.size() || beta0.size() != beta2.size() ||
1467  beta0.size() != Cphi.size()) {
1468  throw CanteraError("HMWSoln::readXMLBinarySalt", "Inconsistent"
1469  " array sizes ({}, {}, {}, {})", beta0.size(), beta1.size(),
1470  beta2.size(), Cphi.size());
1471  }
1472  if (beta0.size() == 1 && m_formPitzerTemp == PITZER_TEMP_COMPLEX1) {
1473  beta0.resize(5, 0.0);
1474  beta1.resize(5, 0.0);
1475  beta2.resize(5, 0.0);
1476  Cphi.resize(5, 0.0);
1477  }
1478  double alpha1 = getFloat(BinSalt, "Alpha1");
1479  double alpha2 = 0.0;
1480  getOptionalFloat(BinSalt, "Alpha2", alpha2);
1481  setBinarySalt(iName, jName, beta0.size(), beta0.data(), beta1.data(),
1482  beta2.data(), Cphi.data(), alpha1, alpha2);
1483 }
1484 
1486 {
1487  string ispName, jspName;
1488  if (node.name() == "thetaAnion") {
1489  ispName = node.attrib("anion1");
1490  if (ispName == "") {
1491  throw CanteraError("HMWSoln::readXMLTheta", "no anion1 attrib");
1492  }
1493  jspName = node.attrib("anion2");
1494  if (jspName == "") {
1495  throw CanteraError("HMWSoln::readXMLTheta", "no anion2 attrib");
1496  }
1497  } else if (node.name() == "thetaCation") {
1498  ispName = node.attrib("cation1");
1499  if (ispName == "") {
1500  throw CanteraError("HMWSoln::readXMLTheta", "no cation1 attrib");
1501  }
1502  jspName = node.attrib("cation2");
1503  if (jspName == "") {
1504  throw CanteraError("HMWSoln::readXMLTheta", "no cation2 attrib");
1505  }
1506  } else {
1507  throw CanteraError("HMWSoln::readXMLTheta",
1508  "Incorrect name for processing this routine: " + node.name());
1509  }
1510 
1511  // Find the index of the species in the current phase. It's not an error to
1512  // not find the species
1513  if (speciesIndex(ispName) == npos || speciesIndex(jspName) == npos) {
1514  return;
1515  }
1516 
1517  vector_fp theta;
1518  getFloatArray(node, theta, false, "", "theta");
1519  if (theta.size() == 1 && m_formPitzerTemp == PITZER_TEMP_COMPLEX1) {
1520  theta.resize(5, 0.0);
1521  }
1522  setTheta(ispName, jspName, theta.size(), theta.data());
1523 }
1524 
1526 {
1527  string iName, jName, kName;
1528  if (node.name() == "psiCommonCation") {
1529  kName = node.attrib("cation");
1530  if (kName == "") {
1531  throw CanteraError("HMWSoln::readXMLPsi", "no cation attrib");
1532  }
1533  iName = node.attrib("anion1");
1534  if (iName == "") {
1535  throw CanteraError("HMWSoln::readXMLPsi", "no anion1 attrib");
1536  }
1537  jName = node.attrib("anion2");
1538  if (jName == "") {
1539  throw CanteraError("HMWSoln::readXMLPsi", "no anion2 attrib");
1540  }
1541  } else if (node.name() == "psiCommonAnion") {
1542  kName = node.attrib("anion");
1543  if (kName == "") {
1544  throw CanteraError("HMWSoln::readXMLPsi", "no anion attrib");
1545  }
1546  iName = node.attrib("cation1");
1547  if (iName == "") {
1548  throw CanteraError("HMWSoln::readXMLPsi", "no cation1 attrib");
1549  }
1550  jName = node.attrib("cation2");
1551  if (jName == "") {
1552  throw CanteraError("HMWSoln::readXMLPsi", "no cation2 attrib");
1553  }
1554  } else {
1555  throw CanteraError("HMWSoln::readXMLPsi",
1556  "Incorrect name for processing this routine: " + node.name());
1557  }
1558 
1559  // Find the index of the species in the current phase. It's not an error to
1560  // not find the species
1561  if (speciesIndex(iName) == npos || speciesIndex(jName) == npos ||
1562  speciesIndex(kName) == npos) {
1563  return;
1564  }
1565 
1566  vector_fp psi;
1567  getFloatArray(node, psi, false, "", "psi");
1568  if (psi.size() == 1 && m_formPitzerTemp == PITZER_TEMP_COMPLEX1) {
1569  psi.resize(5, 0.0);
1570  }
1571  setPsi(iName, jName, kName, psi.size(), psi.data());
1572 }
1573 
1575 {
1576  vector_fp vParams;
1577  if (node.name() != "lambdaNeutral") {
1578  throw CanteraError("HMWSoln::readXMLLambdaNeutral",
1579  "Incorrect name for processing this routine: " + node.name());
1580  }
1581  string iName = node.attrib("species1");
1582  if (iName == "") {
1583  throw CanteraError("HMWSoln::readXMLLambdaNeutral", "no species1 attrib");
1584  }
1585  string jName = node.attrib("species2");
1586  if (jName == "") {
1587  throw CanteraError("HMWSoln::readXMLLambdaNeutral", "no species2 attrib");
1588  }
1589 
1590  // Find the index of the species in the current phase. It's not an error to
1591  // not find the species
1592  if (speciesIndex(iName) == npos || speciesIndex(jName) == npos) {
1593  return;
1594  }
1595 
1596  vector_fp lambda;
1597  getFloatArray(node, lambda, false, "", "lambda");
1598  if (lambda.size() == 1 && m_formPitzerTemp == PITZER_TEMP_COMPLEX1) {
1599  lambda.resize(5, 0.0);
1600  }
1601  setLambda(iName, jName, lambda.size(), lambda.data());
1602 }
1603 
1605 {
1606  if (node.name() != "MunnnNeutral") {
1607  throw CanteraError("HMWSoln::readXMLMunnnNeutral",
1608  "Incorrect name for processing this routine: " + node.name());
1609  }
1610  string iName = node.attrib("species1");
1611  if (iName == "") {
1612  throw CanteraError("HMWSoln::readXMLMunnnNeutral", "no species1 attrib");
1613  }
1614 
1615  // Find the index of the species in the current phase. It's not an error to
1616  // not find the species
1617  if (speciesIndex(iName) == npos) {
1618  return;
1619  }
1620 
1621  vector_fp munnn;
1622  getFloatArray(node, munnn, false, "", "munnn");
1623  if (munnn.size() == 1 && m_formPitzerTemp == PITZER_TEMP_COMPLEX1) {
1624  munnn.resize(5, 0.0);
1625  }
1626  setMunnn(iName, munnn.size(), munnn.data());
1627 }
1628 
1630 {
1631  if (node.name() != "zetaCation") {
1632  throw CanteraError("HMWSoln::readXMLZetaCation",
1633  "Incorrect name for processing this routine: " + node.name());
1634  }
1635 
1636  string iName = node.attrib("neutral");
1637  if (iName == "") {
1638  throw CanteraError("HMWSoln::readXMLZetaCation", "no neutral attrib");
1639  }
1640  string jName = node.attrib("cation1");
1641  if (jName == "") {
1642  throw CanteraError("HMWSoln::readXMLZetaCation", "no cation1 attrib");
1643  }
1644  string kName = node.attrib("anion1");
1645  if (kName == "") {
1646  throw CanteraError("HMWSoln::readXMLZetaCation", "no anion1 attrib");
1647  }
1648 
1649  // Find the index of the species in the current phase. It's not an error to
1650  // not find the species
1651  if (speciesIndex(iName) == npos || speciesIndex(jName) == npos ||
1652  speciesIndex(kName) == npos) {
1653  return;
1654  }
1655 
1656  vector_fp zeta;
1657  getFloatArray(node, zeta, false, "", "zeta");
1658  if (zeta.size() == 1 && m_formPitzerTemp == PITZER_TEMP_COMPLEX1) {
1659  zeta.resize(5, 0.0);
1660  }
1661  setZeta(iName, jName, kName, zeta.size(), zeta.data());
1662 }
1663 
1665 {
1666  double IMS_gamma_o_min_ = 1.0E-5; // value at the zero solvent point
1667  double IMS_gamma_k_min_ = 10.0; // minimum at the zero solvent point
1668  double IMS_slopefCut_ = 0.6; // slope of the f function at the zero solvent point
1669 
1670  IMS_afCut_ = 1.0 / (std::exp(1.0) * IMS_gamma_k_min_);
1671  IMS_efCut_ = 0.0;
1672  bool converged = false;
1673  double oldV = 0.0;
1674  for (int its = 0; its < 100 && !converged; its++) {
1675  oldV = IMS_efCut_;
1676  IMS_afCut_ = 1.0 / (std::exp(1.0) * IMS_gamma_k_min_) -IMS_efCut_;
1677  IMS_bfCut_ = IMS_afCut_ / IMS_cCut_ + IMS_slopefCut_ - 1.0;
1678  IMS_dfCut_ = ((- IMS_afCut_/IMS_cCut_ + IMS_bfCut_ - IMS_bfCut_*IMS_X_o_cutoff_/IMS_cCut_)
1679  /
1681  double tmp = IMS_afCut_ + IMS_X_o_cutoff_*(IMS_bfCut_ + IMS_dfCut_ *IMS_X_o_cutoff_);
1682  double eterm = std::exp(-IMS_X_o_cutoff_/IMS_cCut_);
1683  IMS_efCut_ = - eterm * tmp;
1684  if (fabs(IMS_efCut_ - oldV) < 1.0E-14) {
1685  converged = true;
1686  }
1687  }
1688  if (!converged) {
1689  throw CanteraError("HMWSoln::calcIMSCutoffParams_()",
1690  " failed to converge on the f polynomial");
1691  }
1692  converged = false;
1693  double f_0 = IMS_afCut_ + IMS_efCut_;
1694  double f_prime_0 = 1.0 - IMS_afCut_ / IMS_cCut_ + IMS_bfCut_;
1695  IMS_egCut_ = 0.0;
1696  for (int its = 0; its < 100 && !converged; its++) {
1697  oldV = IMS_egCut_;
1698  double lng_0 = -log(IMS_gamma_o_min_) - f_prime_0 / f_0;
1699  IMS_agCut_ = exp(lng_0) - IMS_egCut_;
1700  IMS_bgCut_ = IMS_agCut_ / IMS_cCut_ + IMS_slopegCut_ - 1.0;
1701  IMS_dgCut_ = ((- IMS_agCut_/IMS_cCut_ + IMS_bgCut_ - IMS_bgCut_*IMS_X_o_cutoff_/IMS_cCut_)
1702  /
1704  double tmp = IMS_agCut_ + IMS_X_o_cutoff_*(IMS_bgCut_ + IMS_dgCut_ *IMS_X_o_cutoff_);
1705  double eterm = std::exp(-IMS_X_o_cutoff_/IMS_cCut_);
1706  IMS_egCut_ = - eterm * tmp;
1707  if (fabs(IMS_egCut_ - oldV) < 1.0E-14) {
1708  converged = true;
1709  }
1710  }
1711  if (!converged) {
1712  throw CanteraError("HMWSoln::calcIMSCutoffParams_()",
1713  " failed to converge on the g polynomial");
1714  }
1715 }
1716 
1718 {
1719  double MC_X_o_min_ = 0.35; // value at the zero solvent point
1720  MC_X_o_cutoff_ = 0.6;
1721  double MC_slopepCut_ = 0.02; // slope of the p function at the zero solvent point
1722  MC_cpCut_ = 0.25;
1723 
1724  // Initial starting values
1725  MC_apCut_ = MC_X_o_min_;
1726  MC_epCut_ = 0.0;
1727  bool converged = false;
1728  double oldV = 0.0;
1729  double damp = 0.5;
1730  for (int its = 0; its < 500 && !converged; its++) {
1731  oldV = MC_epCut_;
1732  MC_apCut_ = damp *(MC_X_o_min_ - MC_epCut_) + (1-damp) * MC_apCut_;
1733  double MC_bpCutNew = MC_apCut_ / MC_cpCut_ + MC_slopepCut_ - 1.0;
1734  MC_bpCut_ = damp * MC_bpCutNew + (1-damp) * MC_bpCut_;
1735  double MC_dpCutNew = ((- MC_apCut_/MC_cpCut_ + MC_bpCut_ - MC_bpCut_ * MC_X_o_cutoff_/MC_cpCut_)
1736  /
1737  (MC_X_o_cutoff_ * MC_X_o_cutoff_/MC_cpCut_ - 2.0 * MC_X_o_cutoff_));
1738  MC_dpCut_ = damp * MC_dpCutNew + (1-damp) * MC_dpCut_;
1739  double tmp = MC_apCut_ + MC_X_o_cutoff_*(MC_bpCut_ + MC_dpCut_ * MC_X_o_cutoff_);
1740  double eterm = std::exp(- MC_X_o_cutoff_ / MC_cpCut_);
1741  MC_epCut_ = - eterm * tmp;
1742  double diff = MC_epCut_ - oldV;
1743  if (fabs(diff) < 1.0E-14) {
1744  converged = true;
1745  }
1746  }
1747  if (!converged) {
1748  throw CanteraError("HMWSoln::calcMCCutoffParams_()",
1749  " failed to converge on the p polynomial");
1750  }
1751 }
1752 
1753 void HMWSoln::s_updatePitzer_CoeffWRTemp(int doDerivs) const
1754 {
1755  double T = temperature();
1756  const double twoT = 2.0 * T;
1757  const double invT = 1.0 / T;
1758  const double invT2 = invT * invT;
1759  const double twoinvT3 = 2.0 * invT * invT2;
1760  double tinv = 0.0, tln = 0.0, tlin = 0.0, tquad = 0.0;
1761  if (m_formPitzerTemp == PITZER_TEMP_LINEAR) {
1762  tlin = T - m_TempPitzerRef;
1763  } else if (m_formPitzerTemp == PITZER_TEMP_COMPLEX1) {
1764  tlin = T - m_TempPitzerRef;
1765  tquad = T * T - m_TempPitzerRef * m_TempPitzerRef;
1766  tln = log(T/ m_TempPitzerRef);
1767  tinv = 1.0/T - 1.0/m_TempPitzerRef;
1768  }
1769 
1770  for (size_t i = 1; i < (m_kk - 1); i++) {
1771  for (size_t j = (i+1); j < m_kk; j++) {
1772  // Find the counterIJ for the symmetric binary interaction
1773  size_t n = m_kk*i + j;
1774  size_t counterIJ = m_CounterIJ[n];
1775 
1776  const double* beta0MX_coeff = m_Beta0MX_ij_coeff.ptrColumn(counterIJ);
1777  const double* beta1MX_coeff = m_Beta1MX_ij_coeff.ptrColumn(counterIJ);
1778  const double* beta2MX_coeff = m_Beta2MX_ij_coeff.ptrColumn(counterIJ);
1779  const double* CphiMX_coeff = m_CphiMX_ij_coeff.ptrColumn(counterIJ);
1780  const double* Theta_coeff = m_Theta_ij_coeff.ptrColumn(counterIJ);
1781 
1782  switch (m_formPitzerTemp) {
1783  case PITZER_TEMP_CONSTANT:
1784  break;
1785  case PITZER_TEMP_LINEAR:
1786 
1787  m_Beta0MX_ij[counterIJ] = beta0MX_coeff[0]
1788  + beta0MX_coeff[1]*tlin;
1789  m_Beta0MX_ij_L[counterIJ] = beta0MX_coeff[1];
1790  m_Beta0MX_ij_LL[counterIJ] = 0.0;
1791  m_Beta1MX_ij[counterIJ] = beta1MX_coeff[0]
1792  + beta1MX_coeff[1]*tlin;
1793  m_Beta1MX_ij_L[counterIJ] = beta1MX_coeff[1];
1794  m_Beta1MX_ij_LL[counterIJ] = 0.0;
1795  m_Beta2MX_ij[counterIJ] = beta2MX_coeff[0]
1796  + beta2MX_coeff[1]*tlin;
1797  m_Beta2MX_ij_L[counterIJ] = beta2MX_coeff[1];
1798  m_Beta2MX_ij_LL[counterIJ] = 0.0;
1799  m_CphiMX_ij[counterIJ] = CphiMX_coeff[0]
1800  + CphiMX_coeff[1]*tlin;
1801  m_CphiMX_ij_L[counterIJ] = CphiMX_coeff[1];
1802  m_CphiMX_ij_LL[counterIJ] = 0.0;
1803  m_Theta_ij[counterIJ] = Theta_coeff[0] + Theta_coeff[1]*tlin;
1804  m_Theta_ij_L[counterIJ] = Theta_coeff[1];
1805  m_Theta_ij_LL[counterIJ] = 0.0;
1806  break;
1807 
1808  case PITZER_TEMP_COMPLEX1:
1809  m_Beta0MX_ij[counterIJ] = beta0MX_coeff[0]
1810  + beta0MX_coeff[1]*tlin
1811  + beta0MX_coeff[2]*tquad
1812  + beta0MX_coeff[3]*tinv
1813  + beta0MX_coeff[4]*tln;
1814  m_Beta1MX_ij[counterIJ] = beta1MX_coeff[0]
1815  + beta1MX_coeff[1]*tlin
1816  + beta1MX_coeff[2]*tquad
1817  + beta1MX_coeff[3]*tinv
1818  + beta1MX_coeff[4]*tln;
1819  m_Beta2MX_ij[counterIJ] = beta2MX_coeff[0]
1820  + beta2MX_coeff[1]*tlin
1821  + beta2MX_coeff[2]*tquad
1822  + beta2MX_coeff[3]*tinv
1823  + beta2MX_coeff[4]*tln;
1824  m_CphiMX_ij[counterIJ] = CphiMX_coeff[0]
1825  + CphiMX_coeff[1]*tlin
1826  + CphiMX_coeff[2]*tquad
1827  + CphiMX_coeff[3]*tinv
1828  + CphiMX_coeff[4]*tln;
1829  m_Theta_ij[counterIJ] = Theta_coeff[0]
1830  + Theta_coeff[1]*tlin
1831  + Theta_coeff[2]*tquad
1832  + Theta_coeff[3]*tinv
1833  + Theta_coeff[4]*tln;
1834  m_Beta0MX_ij_L[counterIJ] = beta0MX_coeff[1]
1835  + beta0MX_coeff[2]*twoT
1836  - beta0MX_coeff[3]*invT2
1837  + beta0MX_coeff[4]*invT;
1838  m_Beta1MX_ij_L[counterIJ] = beta1MX_coeff[1]
1839  + beta1MX_coeff[2]*twoT
1840  - beta1MX_coeff[3]*invT2
1841  + beta1MX_coeff[4]*invT;
1842  m_Beta2MX_ij_L[counterIJ] = beta2MX_coeff[1]
1843  + beta2MX_coeff[2]*twoT
1844  - beta2MX_coeff[3]*invT2
1845  + beta2MX_coeff[4]*invT;
1846  m_CphiMX_ij_L[counterIJ] = CphiMX_coeff[1]
1847  + CphiMX_coeff[2]*twoT
1848  - CphiMX_coeff[3]*invT2
1849  + CphiMX_coeff[4]*invT;
1850  m_Theta_ij_L[counterIJ] = Theta_coeff[1]
1851  + Theta_coeff[2]*twoT
1852  - Theta_coeff[3]*invT2
1853  + Theta_coeff[4]*invT;
1854  doDerivs = 2;
1855  if (doDerivs > 1) {
1856  m_Beta0MX_ij_LL[counterIJ] =
1857  + beta0MX_coeff[2]*2.0
1858  + beta0MX_coeff[3]*twoinvT3
1859  - beta0MX_coeff[4]*invT2;
1860  m_Beta1MX_ij_LL[counterIJ] =
1861  + beta1MX_coeff[2]*2.0
1862  + beta1MX_coeff[3]*twoinvT3
1863  - beta1MX_coeff[4]*invT2;
1864  m_Beta2MX_ij_LL[counterIJ] =
1865  + beta2MX_coeff[2]*2.0
1866  + beta2MX_coeff[3]*twoinvT3
1867  - beta2MX_coeff[4]*invT2;
1868  m_CphiMX_ij_LL[counterIJ] =
1869  + CphiMX_coeff[2]*2.0
1870  + CphiMX_coeff[3]*twoinvT3
1871  - CphiMX_coeff[4]*invT2;
1872  m_Theta_ij_LL[counterIJ] =
1873  + Theta_coeff[2]*2.0
1874  + Theta_coeff[3]*twoinvT3
1875  - Theta_coeff[4]*invT2;
1876  }
1877  break;
1878  }
1879  }
1880  }
1881 
1882  // Lambda interactions and Mu_nnn
1883  // i must be neutral for this term to be nonzero. We take advantage of this
1884  // here to lower the operation count.
1885  for (size_t i = 1; i < m_kk; i++) {
1886  if (charge(i) == 0.0) {
1887  for (size_t j = 1; j < m_kk; j++) {
1888  size_t n = i * m_kk + j;
1889  const double* Lambda_coeff = m_Lambda_nj_coeff.ptrColumn(n);
1890  switch (m_formPitzerTemp) {
1891  case PITZER_TEMP_CONSTANT:
1892  m_Lambda_nj(i,j) = Lambda_coeff[0];
1893  break;
1894  case PITZER_TEMP_LINEAR:
1895  m_Lambda_nj(i,j) = Lambda_coeff[0] + Lambda_coeff[1]*tlin;
1896  m_Lambda_nj_L(i,j) = Lambda_coeff[1];
1897  m_Lambda_nj_LL(i,j) = 0.0;
1898  break;
1899  case PITZER_TEMP_COMPLEX1:
1900  m_Lambda_nj(i,j) = Lambda_coeff[0]
1901  + Lambda_coeff[1]*tlin
1902  + Lambda_coeff[2]*tquad
1903  + Lambda_coeff[3]*tinv
1904  + Lambda_coeff[4]*tln;
1905 
1906  m_Lambda_nj_L(i,j) = Lambda_coeff[1]
1907  + Lambda_coeff[2]*twoT
1908  - Lambda_coeff[3]*invT2
1909  + Lambda_coeff[4]*invT;
1910 
1911  m_Lambda_nj_LL(i,j) =
1912  Lambda_coeff[2]*2.0
1913  + Lambda_coeff[3]*twoinvT3
1914  - Lambda_coeff[4]*invT2;
1915  }
1916 
1917  if (j == i) {
1918  const double* Mu_coeff = m_Mu_nnn_coeff.ptrColumn(i);
1919  switch (m_formPitzerTemp) {
1920  case PITZER_TEMP_CONSTANT:
1921  m_Mu_nnn[i] = Mu_coeff[0];
1922  break;
1923  case PITZER_TEMP_LINEAR:
1924  m_Mu_nnn[i] = Mu_coeff[0] + Mu_coeff[1]*tlin;
1925  m_Mu_nnn_L[i] = Mu_coeff[1];
1926  m_Mu_nnn_LL[i] = 0.0;
1927  break;
1928  case PITZER_TEMP_COMPLEX1:
1929  m_Mu_nnn[i] = Mu_coeff[0]
1930  + Mu_coeff[1]*tlin
1931  + Mu_coeff[2]*tquad
1932  + Mu_coeff[3]*tinv
1933  + Mu_coeff[4]*tln;
1934  m_Mu_nnn_L[i] = Mu_coeff[1]
1935  + Mu_coeff[2]*twoT
1936  - Mu_coeff[3]*invT2
1937  + Mu_coeff[4]*invT;
1938  m_Mu_nnn_LL[i] =
1939  Mu_coeff[2]*2.0
1940  + Mu_coeff[3]*twoinvT3
1941  - Mu_coeff[4]*invT2;
1942  }
1943  }
1944  }
1945  }
1946  }
1947 
1948  switch(m_formPitzerTemp) {
1949  case PITZER_TEMP_CONSTANT:
1950  for (size_t i = 1; i < m_kk; i++) {
1951  for (size_t j = 1; j < m_kk; j++) {
1952  for (size_t k = 1; k < m_kk; k++) {
1953  size_t n = i * m_kk *m_kk + j * m_kk + k;
1954  const double* Psi_coeff = m_Psi_ijk_coeff.ptrColumn(n);
1955  m_Psi_ijk[n] = Psi_coeff[0];
1956  }
1957  }
1958  }
1959  break;
1960  case PITZER_TEMP_LINEAR:
1961  for (size_t i = 1; i < m_kk; i++) {
1962  for (size_t j = 1; j < m_kk; j++) {
1963  for (size_t k = 1; k < m_kk; k++) {
1964  size_t n = i * m_kk *m_kk + j * m_kk + k;
1965  const double* Psi_coeff = m_Psi_ijk_coeff.ptrColumn(n);
1966  m_Psi_ijk[n] = Psi_coeff[0] + Psi_coeff[1]*tlin;
1967  m_Psi_ijk_L[n] = Psi_coeff[1];
1968  m_Psi_ijk_LL[n] = 0.0;
1969  }
1970  }
1971  }
1972  break;
1973  case PITZER_TEMP_COMPLEX1:
1974  for (size_t i = 1; i < m_kk; i++) {
1975  for (size_t j = 1; j < m_kk; j++) {
1976  for (size_t k = 1; k < m_kk; k++) {
1977  size_t n = i * m_kk *m_kk + j * m_kk + k;
1978  const double* Psi_coeff = m_Psi_ijk_coeff.ptrColumn(n);
1979  m_Psi_ijk[n] = Psi_coeff[0]
1980  + Psi_coeff[1]*tlin
1981  + Psi_coeff[2]*tquad
1982  + Psi_coeff[3]*tinv
1983  + Psi_coeff[4]*tln;
1984  m_Psi_ijk_L[n] = Psi_coeff[1]
1985  + Psi_coeff[2]*twoT
1986  - Psi_coeff[3]*invT2
1987  + Psi_coeff[4]*invT;
1988  m_Psi_ijk_LL[n] =
1989  Psi_coeff[2]*2.0
1990  + Psi_coeff[3]*twoinvT3
1991  - Psi_coeff[4]*invT2;
1992  }
1993  }
1994  }
1995  break;
1996  }
1997 }
1998 
2000 {
2001  // Use the CROPPED molality of the species in solution.
2002  const vector_fp& molality = m_molalitiesCropped;
2003 
2004  // These are data inputs about the Pitzer correlation. They come from the
2005  // input file for the Pitzer model.
2006  vector_fp& gamma_Unscaled = m_gamma_tmp;
2007 
2008  // Local variables defined by Coltrin
2009  double etheta[5][5], etheta_prime[5][5], sqrtIs;
2010 
2011  // Molality based ionic strength of the solution
2012  double Is = 0.0;
2013 
2014  // Molar charge of the solution: In Pitzer's notation, this is his variable
2015  // called "Z".
2016  double molarcharge = 0.0;
2017 
2018  // molalitysum is the sum of the molalities over all solutes, even those
2019  // with zero charge.
2020  double molalitysumUncropped = 0.0;
2021 
2022  // Make sure the counter variables are setup
2023  counterIJ_setup();
2024 
2025  // ---------- Calculate common sums over solutes ---------------------
2026  for (size_t n = 1; n < m_kk; n++) {
2027  // ionic strength
2028  Is += charge(n) * charge(n) * molality[n];
2029  // total molar charge
2030  molarcharge += fabs(charge(n)) * molality[n];
2031  molalitysumUncropped += m_molalities[n];
2032  }
2033  Is *= 0.5;
2034 
2035  // Store the ionic molality in the object for reference.
2036  m_IionicMolality = Is;
2037  sqrtIs = sqrt(Is);
2038 
2039  // The following call to calc_lambdas() calculates all 16 elements of the
2040  // elambda and elambda1 arrays, given the value of the ionic strength (Is)
2041  calc_lambdas(Is);
2042 
2043  // Step 2: Find the coefficients E-theta and E-thetaprime for all
2044  // combinations of positive unlike charges up to 4
2045  for (int z1 = 1; z1 <=4; z1++) {
2046  for (int z2 =1; z2 <=4; z2++) {
2047  calc_thetas(z1, z2, &etheta[z1][z2], &etheta_prime[z1][z2]);
2048  }
2049  }
2050 
2051  // calculate g(x) and hfunc(x) for each cation-anion pair MX. In the
2052  // original literature, hfunc, was called gprime. However, it's not the
2053  // derivative of g(x), so I renamed it.
2054  for (size_t i = 1; i < (m_kk - 1); i++) {
2055  for (size_t j = (i+1); j < m_kk; j++) {
2056  // Find the counterIJ for the symmetric binary interaction
2057  size_t n = m_kk*i + j;
2058  size_t counterIJ = m_CounterIJ[n];
2059 
2060  // Only loop over oppositely charge species
2061  if (charge(i)*charge(j) < 0) {
2062  // x is a reduced function variable
2063  double x1 = sqrtIs * m_Alpha1MX_ij[counterIJ];
2064  if (x1 > 1.0E-100) {
2065  m_gfunc_IJ[counterIJ] = 2.0*(1.0-(1.0 + x1) * exp(-x1)) / (x1 * x1);
2066  m_hfunc_IJ[counterIJ] = -2.0 *
2067  (1.0-(1.0 + x1 + 0.5 * x1 * x1) * exp(-x1)) / (x1 * x1);
2068  } else {
2069  m_gfunc_IJ[counterIJ] = 0.0;
2070  m_hfunc_IJ[counterIJ] = 0.0;
2071  }
2072 
2073  if (m_Beta2MX_ij[counterIJ] != 0.0) {
2074  double x2 = sqrtIs * m_Alpha2MX_ij[counterIJ];
2075  if (x2 > 1.0E-100) {
2076  m_g2func_IJ[counterIJ] = 2.0*(1.0-(1.0 + x2) * exp(-x2)) / (x2 * x2);
2077  m_h2func_IJ[counterIJ] = -2.0 *
2078  (1.0-(1.0 + x2 + 0.5 * x2 * x2) * exp(-x2)) / (x2 * x2);
2079  } else {
2080  m_g2func_IJ[counterIJ] = 0.0;
2081  m_h2func_IJ[counterIJ] = 0.0;
2082  }
2083  }
2084  } else {
2085  m_gfunc_IJ[counterIJ] = 0.0;
2086  m_hfunc_IJ[counterIJ] = 0.0;
2087  }
2088  }
2089  }
2090 
2091  // SUBSECTION TO CALCULATE BMX, BprimeMX, BphiMX
2092  // Agrees with Pitzer, Eq. (49), (51), (55)
2093  for (size_t i = 1; i < m_kk - 1; i++) {
2094  for (size_t j = i+1; j < m_kk; j++) {
2095  // Find the counterIJ for the symmetric binary interaction
2096  size_t n = m_kk*i + j;
2097  size_t counterIJ = m_CounterIJ[n];
2098 
2099  // both species have a non-zero charge, and one is positive and the
2100  // other is negative
2101  if (charge(i)*charge(j) < 0.0) {
2102  m_BMX_IJ[counterIJ] = m_Beta0MX_ij[counterIJ]
2103  + m_Beta1MX_ij[counterIJ] * m_gfunc_IJ[counterIJ]
2104  + m_Beta2MX_ij[counterIJ] * m_g2func_IJ[counterIJ];
2105 
2106  if (Is > 1.0E-150) {
2107  m_BprimeMX_IJ[counterIJ] = (m_Beta1MX_ij[counterIJ] * m_hfunc_IJ[counterIJ]/Is +
2108  m_Beta2MX_ij[counterIJ] * m_h2func_IJ[counterIJ]/Is);
2109  } else {
2110  m_BprimeMX_IJ[counterIJ] = 0.0;
2111  }
2112  m_BphiMX_IJ[counterIJ] = m_BMX_IJ[counterIJ] + Is*m_BprimeMX_IJ[counterIJ];
2113  } else {
2114  m_BMX_IJ[counterIJ] = 0.0;
2115  m_BprimeMX_IJ[counterIJ] = 0.0;
2116  m_BphiMX_IJ[counterIJ] = 0.0;
2117  }
2118  }
2119  }
2120 
2121  // SUBSECTION TO CALCULATE CMX
2122  // Agrees with Pitzer, Eq. (53).
2123  for (size_t i = 1; i < m_kk-1; i++) {
2124  for (size_t j = i+1; j < m_kk; j++) {
2125  // Find the counterIJ for the symmetric binary interaction
2126  size_t n = m_kk*i + j;
2127  size_t counterIJ = m_CounterIJ[n];
2128 
2129  // both species have a non-zero charge, and one is positive
2130  // and the other is negative
2131  if (charge(i)*charge(j) < 0.0) {
2132  m_CMX_IJ[counterIJ] = m_CphiMX_ij[counterIJ]/
2133  (2.0* sqrt(fabs(charge(i)*charge(j))));
2134  } else {
2135  m_CMX_IJ[counterIJ] = 0.0;
2136  }
2137  }
2138  }
2139 
2140  // SUBSECTION TO CALCULATE Phi, PhiPrime, and PhiPhi
2141  // Agrees with Pitzer, Eq. 72, 73, 74
2142  for (size_t i = 1; i < m_kk-1; i++) {
2143  for (size_t j = i+1; j < m_kk; j++) {
2144  // Find the counterIJ for the symmetric binary interaction
2145  size_t n = m_kk*i + j;
2146  size_t counterIJ = m_CounterIJ[n];
2147 
2148  // both species have a non-zero charge, and one is positive and the
2149  // other is negative
2150  if (charge(i)*charge(j) > 0) {
2151  int z1 = (int) fabs(charge(i));
2152  int z2 = (int) fabs(charge(j));
2153  m_Phi_IJ[counterIJ] = m_Theta_ij[counterIJ] + etheta[z1][z2];
2154  m_Phiprime_IJ[counterIJ] = etheta_prime[z1][z2];
2155  m_PhiPhi_IJ[counterIJ] = m_Phi_IJ[counterIJ] + Is * m_Phiprime_IJ[counterIJ];
2156  } else {
2157  m_Phi_IJ[counterIJ] = 0.0;
2158  m_Phiprime_IJ[counterIJ] = 0.0;
2159  m_PhiPhi_IJ[counterIJ] = 0.0;
2160  }
2161  }
2162  }
2163 
2164  // SUBSECTION FOR CALCULATION OF F
2165  // Agrees with Pitzer Eqn. (65)
2166  double Aphi = A_Debye_TP() / 3.0;
2167  double F = -Aphi * (sqrt(Is) / (1.0 + 1.2*sqrt(Is))
2168  + (2.0/1.2) * log(1.0+1.2*(sqrtIs)));
2169  for (size_t i = 1; i < m_kk-1; i++) {
2170  for (size_t j = i+1; j < m_kk; j++) {
2171  // Find the counterIJ for the symmetric binary interaction
2172  size_t n = m_kk*i + j;
2173  size_t counterIJ = m_CounterIJ[n];
2174 
2175  // both species have a non-zero charge, and one is positive and the
2176  // other is negative
2177  if (charge(i)*charge(j) < 0) {
2178  F += molality[i]*molality[j] * m_BprimeMX_IJ[counterIJ];
2179  }
2180 
2181  // Both species have a non-zero charge, and they
2182  // have the same sign
2183  if (charge(i)*charge(j) > 0) {
2184  F += molality[i]*molality[j] * m_Phiprime_IJ[counterIJ];
2185  }
2186  }
2187  }
2188 
2189  for (size_t i = 1; i < m_kk; i++) {
2190 
2191  // SUBSECTION FOR CALCULATING THE ACTCOEFF FOR CATIONS
2192  // equations agree with my notes, Eqn. (118).
2193  // Equations agree with Pitzer, eqn.(63)
2194  if (charge(i) > 0.0) {
2195  // species i is the cation (positive) to calc the actcoeff
2196  double zsqF = charge(i)*charge(i)*F;
2197  double sum1 = 0.0;
2198  double sum2 = 0.0;
2199  double sum3 = 0.0;
2200  double sum4 = 0.0;
2201  double sum5 = 0.0;
2202  for (size_t j = 1; j < m_kk; j++) {
2203  // Find the counterIJ for the symmetric binary interaction
2204  size_t n = m_kk*i + j;
2205  size_t counterIJ = m_CounterIJ[n];
2206 
2207  if (charge(j) < 0.0) {
2208  // sum over all anions
2209  sum1 += molality[j] *
2210  (2.0*m_BMX_IJ[counterIJ] + molarcharge*m_CMX_IJ[counterIJ]);
2211  if (j < m_kk-1) {
2212  // This term is the ternary interaction involving the
2213  // non-duplicate sum over double anions, j, k, with
2214  // respect to the cation, i.
2215  for (size_t k = j+1; k < m_kk; k++) {
2216  // an inner sum over all anions
2217  if (charge(k) < 0.0) {
2218  n = k + j * m_kk + i * m_kk * m_kk;
2219  sum3 += molality[j]*molality[k]*m_Psi_ijk[n];
2220  }
2221  }
2222  }
2223  }
2224 
2225  if (charge(j) > 0.0) {
2226  // sum over all cations
2227  if (j != i) {
2228  sum2 += molality[j]*(2.0*m_Phi_IJ[counterIJ]);
2229  }
2230  for (size_t k = 1; k < m_kk; k++) {
2231  if (charge(k) < 0.0) {
2232  // two inner sums over anions
2233  n = k + j * m_kk + i * m_kk * m_kk;
2234  sum2 += molality[j]*molality[k]*m_Psi_ijk[n];
2235 
2236  // Find the counterIJ for the j,k interaction
2237  n = m_kk*j + k;
2238  size_t counterIJ2 = m_CounterIJ[n];
2239  sum4 += (fabs(charge(i))*
2240  molality[j]*molality[k]*m_CMX_IJ[counterIJ2]);
2241  }
2242  }
2243  }
2244 
2245  // Handle neutral j species
2246  if (charge(j) == 0) {
2247  sum5 += molality[j]*2.0*m_Lambda_nj(j,i);
2248 
2249  // Zeta interaction term
2250  for (size_t k = 1; k < m_kk; k++) {
2251  if (charge(k) < 0.0) {
2252  size_t izeta = j;
2253  size_t jzeta = i;
2254  n = izeta * m_kk * m_kk + jzeta * m_kk + k;
2255  double zeta = m_Psi_ijk[n];
2256  if (zeta != 0.0) {
2257  sum5 += molality[j]*molality[k]*zeta;
2258  }
2259  }
2260  }
2261  }
2262  }
2263 
2264  // Add all of the contributions up to yield the log of the solute
2265  // activity coefficients (molality scale)
2266  m_lnActCoeffMolal_Unscaled[i] = zsqF + sum1 + sum2 + sum3 + sum4 + sum5;
2267  gamma_Unscaled[i] = exp(m_lnActCoeffMolal_Unscaled[i]);
2268  }
2269 
2270  // SUBSECTION FOR CALCULATING THE ACTCOEFF FOR ANIONS
2271  // equations agree with my notes, Eqn. (119).
2272  // Equations agree with Pitzer, eqn.(64)
2273  if (charge(i) < 0) {
2274  // species i is an anion (negative)
2275  double zsqF = charge(i)*charge(i)*F;
2276  double sum1 = 0.0;
2277  double sum2 = 0.0;
2278  double sum3 = 0.0;
2279  double sum4 = 0.0;
2280  double sum5 = 0.0;
2281  for (size_t j = 1; j < m_kk; j++) {
2282  // Find the counterIJ for the symmetric binary interaction
2283  size_t n = m_kk*i + j;
2284  size_t counterIJ = m_CounterIJ[n];
2285 
2286  // For Anions, do the cation interactions.
2287  if (charge(j) > 0) {
2288  sum1 += molality[j]*
2289  (2.0*m_BMX_IJ[counterIJ]+molarcharge*m_CMX_IJ[counterIJ]);
2290  if (j < m_kk-1) {
2291  for (size_t k = j+1; k < m_kk; k++) {
2292  // an inner sum over all cations
2293  if (charge(k) > 0) {
2294  n = k + j * m_kk + i * m_kk * m_kk;
2295  sum3 += molality[j]*molality[k]*m_Psi_ijk[n];
2296  }
2297  }
2298  }
2299  }
2300 
2301  // For Anions, do the other anion interactions.
2302  if (charge(j) < 0.0) {
2303  // sum over all anions
2304  if (j != i) {
2305  sum2 += molality[j]*(2.0*m_Phi_IJ[counterIJ]);
2306  }
2307  for (size_t k = 1; k < m_kk; k++) {
2308  if (charge(k) > 0.0) {
2309  // two inner sums over cations
2310  n = k + j * m_kk + i * m_kk * m_kk;
2311  sum2 += molality[j]*molality[k]*m_Psi_ijk[n];
2312  // Find the counterIJ for the symmetric binary interaction
2313  n = m_kk*j + k;
2314  size_t counterIJ2 = m_CounterIJ[n];
2315  sum4 += fabs(charge(i))*
2316  molality[j]*molality[k]*m_CMX_IJ[counterIJ2];
2317  }
2318  }
2319  }
2320 
2321  // for Anions, do the neutral species interaction
2322  if (charge(j) == 0.0) {
2323  sum5 += molality[j]*2.0*m_Lambda_nj(j,i);
2324  // Zeta interaction term
2325  for (size_t k = 1; k < m_kk; k++) {
2326  if (charge(k) > 0.0) {
2327  size_t izeta = j;
2328  size_t jzeta = k;
2329  size_t kzeta = i;
2330  n = izeta * m_kk * m_kk + jzeta * m_kk + kzeta;
2331  double zeta = m_Psi_ijk[n];
2332  if (zeta != 0.0) {
2333  sum5 += molality[j]*molality[k]*zeta;
2334  }
2335  }
2336  }
2337  }
2338  }
2339  m_lnActCoeffMolal_Unscaled[i] = zsqF + sum1 + sum2 + sum3 + sum4 + sum5;
2340  gamma_Unscaled[i] = exp(m_lnActCoeffMolal_Unscaled[i]);
2341  }
2342 
2343  // SUBSECTION FOR CALCULATING NEUTRAL SOLUTE ACT COEFF
2344  // equations agree with my notes,
2345  // Equations agree with Pitzer,
2346  if (charge(i) == 0.0) {
2347  double sum1 = 0.0;
2348  double sum3 = 0.0;
2349  for (size_t j = 1; j < m_kk; j++) {
2350  sum1 += molality[j]*2.0*m_Lambda_nj(i,j);
2351  // Zeta term -> we piggyback on the psi term
2352  if (charge(j) > 0.0) {
2353  for (size_t k = 1; k < m_kk; k++) {
2354  if (charge(k) < 0.0) {
2355  size_t n = k + j * m_kk + i * m_kk * m_kk;
2356  sum3 += molality[j]*molality[k]*m_Psi_ijk[n];
2357  }
2358  }
2359  }
2360  }
2361  double sum2 = 3.0 * molality[i]* molality[i] * m_Mu_nnn[i];
2362  m_lnActCoeffMolal_Unscaled[i] = sum1 + sum2 + sum3;
2363  gamma_Unscaled[i] = exp(m_lnActCoeffMolal_Unscaled[i]);
2364  }
2365  }
2366 
2367  // SUBSECTION FOR CALCULATING THE OSMOTIC COEFF
2368  // equations agree with my notes, Eqn. (117).
2369  // Equations agree with Pitzer, eqn.(62)
2370  double sum1 = 0.0;
2371  double sum2 = 0.0;
2372  double sum3 = 0.0;
2373  double sum4 = 0.0;
2374  double sum5 = 0.0;
2375  double sum6 = 0.0;
2376  double sum7 = 0.0;
2377 
2378  // term1 is the DH term in the osmotic coefficient expression
2379  // b = 1.2 sqrt(kg/gmol) <- arbitrarily set in all Pitzer
2380  // implementations.
2381  // Is = Ionic strength on the molality scale (units of (gmol/kg))
2382  // Aphi = A_Debye / 3 (units of sqrt(kg/gmol))
2383  double term1 = -Aphi * pow(Is,1.5) / (1.0 + 1.2 * sqrt(Is));
2384 
2385  for (size_t j = 1; j < m_kk; j++) {
2386  // Loop Over Cations
2387  if (charge(j) > 0.0) {
2388  for (size_t k = 1; k < m_kk; k++) {
2389  if (charge(k) < 0.0) {
2390  // Find the counterIJ for the symmetric j,k binary interaction
2391  size_t n = m_kk*j + k;
2392  size_t counterIJ = m_CounterIJ[n];
2393 
2394  sum1 += molality[j]*molality[k]*
2395  (m_BphiMX_IJ[counterIJ] + molarcharge*m_CMX_IJ[counterIJ]);
2396  }
2397  }
2398 
2399  for (size_t k = j+1; k < m_kk; k++) {
2400  if (j == (m_kk-1)) {
2401  // we should never reach this step
2402  throw CanteraError("HMWSoln::s_updatePitzer_lnMolalityActCoeff",
2403  "logic error 1 in Step 9 of hmw_act");
2404  }
2405  if (charge(k) > 0.0) {
2406  // Find the counterIJ for the symmetric j,k binary interaction
2407  // between 2 cations.
2408  size_t n = m_kk*j + k;
2409  size_t counterIJ = m_CounterIJ[n];
2410  sum2 += molality[j]*molality[k]*m_PhiPhi_IJ[counterIJ];
2411  for (size_t m = 1; m < m_kk; m++) {
2412  if (charge(m) < 0.0) {
2413  // species m is an anion
2414  n = m + k * m_kk + j * m_kk * m_kk;
2415  sum2 += molality[j]*molality[k]*molality[m]*m_Psi_ijk[n];
2416  }
2417  }
2418  }
2419  }
2420  }
2421 
2422  // Loop Over Anions
2423  if (charge(j) < 0) {
2424  for (size_t k = j+1; k < m_kk; k++) {
2425  if (j == m_kk-1) {
2426  // we should never reach this step
2427  throw CanteraError("HMWSoln::s_updatePitzer_lnMolalityActCoeff",
2428  "logic error 2 in Step 9 of hmw_act");
2429  }
2430  if (charge(k) < 0) {
2431  // Find the counterIJ for the symmetric j,k binary interaction
2432  // between two anions
2433  size_t n = m_kk*j + k;
2434  size_t counterIJ = m_CounterIJ[n];
2435  sum3 += molality[j]*molality[k]*m_PhiPhi_IJ[counterIJ];
2436  for (size_t m = 1; m < m_kk; m++) {
2437  if (charge(m) > 0.0) {
2438  n = m + k * m_kk + j * m_kk * m_kk;
2439  sum3 += molality[j]*molality[k]*molality[m]*m_Psi_ijk[n];
2440  }
2441  }
2442  }
2443  }
2444  }
2445 
2446  // Loop Over Neutral Species
2447  if (charge(j) == 0) {
2448  for (size_t k = 1; k < m_kk; k++) {
2449  if (charge(k) < 0.0) {
2450  sum4 += molality[j]*molality[k]*m_Lambda_nj(j,k);
2451  }
2452  if (charge(k) > 0.0) {
2453  sum5 += molality[j]*molality[k]*m_Lambda_nj(j,k);
2454  }
2455  if (charge(k) == 0.0) {
2456  if (k > j) {
2457  sum6 += molality[j]*molality[k]*m_Lambda_nj(j,k);
2458  } else if (k == j) {
2459  sum6 += 0.5 * molality[j]*molality[k]*m_Lambda_nj(j,k);
2460  }
2461  }
2462  if (charge(k) < 0.0) {
2463  size_t izeta = j;
2464  for (size_t m = 1; m < m_kk; m++) {
2465  if (charge(m) > 0.0) {
2466  size_t jzeta = m;
2467  size_t n = k + jzeta * m_kk + izeta * m_kk * m_kk;
2468  double zeta = m_Psi_ijk[n];
2469  if (zeta != 0.0) {
2470  sum7 += molality[izeta]*molality[jzeta]*molality[k]*zeta;
2471  }
2472  }
2473  }
2474  }
2475  }
2476  sum7 += molality[j]*molality[j]*molality[j]*m_Mu_nnn[j];
2477  }
2478  }
2479  double sum_m_phi_minus_1 = 2.0 *
2480  (term1 + sum1 + sum2 + sum3 + sum4 + sum5 + sum6 + sum7);
2481  // Calculate the osmotic coefficient from
2482  // osmotic_coeff = 1 + dGex/d(M0noRT) / sum(molality_i)
2483  double osmotic_coef;
2484  if (molalitysumUncropped > 1.0E-150) {
2485  osmotic_coef = 1.0 + (sum_m_phi_minus_1 / molalitysumUncropped);
2486  } else {
2487  osmotic_coef = 1.0;
2488  }
2489  double lnwateract = -(m_weightSolvent/1000.0) * molalitysumUncropped * osmotic_coef;
2490 
2491  // In Cantera, we define the activity coefficient of the solvent as
2492  //
2493  // act_0 = actcoeff_0 * Xmol_0
2494  //
2495  // We have just computed act_0. However, this routine returns
2496  // ln(actcoeff[]). Therefore, we must calculate ln(actcoeff_0).
2497  double xmolSolvent = moleFraction(0);
2498  double xx = std::max(m_xmolSolventMIN, xmolSolvent);
2499  m_lnActCoeffMolal_Unscaled[0] = lnwateract - log(xx);
2500 }
2501 
2503 {
2504  static const int cacheId = m_cache.getId();
2505  CachedScalar cached = m_cache.getScalar(cacheId);
2506  if( cached.validate(temperature(), pressure(), stateMFNumber()) ) {
2507  return;
2508  }
2509 
2510  // Zero the unscaled 2nd derivatives
2511  m_dlnActCoeffMolaldT_Unscaled.assign(m_kk, 0.0);
2512 
2513  // Do the actual calculation of the unscaled temperature derivatives
2515 
2516  for (size_t k = 1; k < m_kk; k++) {
2517  if (CROP_speciesCropped_[k] == 2) {
2519  }
2520  }
2521 
2522  if (CROP_speciesCropped_[0]) {
2524  }
2525 
2526  // Do the pH scaling to the derivatives
2528 }
2529 
2531 {
2532  // It may be assumed that the Pitzer activity coefficient routine is called
2533  // immediately preceding the calling of this routine. Therefore, some
2534  // quantities do not need to be recalculated in this routine.
2535 
2536  const vector_fp& molality = m_molalitiesCropped;
2537  double* d_gamma_dT_Unscaled = m_gamma_tmp.data();
2538 
2539  // Local variables defined by Coltrin
2540  double etheta[5][5], etheta_prime[5][5], sqrtIs;
2541 
2542  // Molality based ionic strength of the solution
2543  double Is = 0.0;
2544 
2545  // Molar charge of the solution: In Pitzer's notation, this is his variable
2546  // called "Z".
2547  double molarcharge = 0.0;
2548 
2549  // molalitysum is the sum of the molalities over all solutes, even those
2550  // with zero charge.
2551  double molalitysum = 0.0;
2552 
2553  // Make sure the counter variables are setup
2554  counterIJ_setup();
2555 
2556  // ---------- Calculate common sums over solutes ---------------------
2557  for (size_t n = 1; n < m_kk; n++) {
2558  // ionic strength
2559  Is += charge(n) * charge(n) * molality[n];
2560  // total molar charge
2561  molarcharge += fabs(charge(n)) * molality[n];
2562  molalitysum += molality[n];
2563  }
2564  Is *= 0.5;
2565 
2566  // Store the ionic molality in the object for reference.
2567  m_IionicMolality = Is;
2568  sqrtIs = sqrt(Is);
2569 
2570  // The following call to calc_lambdas() calculates all 16 elements of the
2571  // elambda and elambda1 arrays, given the value of the ionic strength (Is)
2572  calc_lambdas(Is);
2573 
2574  // Step 2: Find the coefficients E-theta and E-thetaprime for all
2575  // combinations of positive unlike charges up to 4
2576  for (int z1 = 1; z1 <=4; z1++) {
2577  for (int z2 =1; z2 <=4; z2++) {
2578  calc_thetas(z1, z2, &etheta[z1][z2], &etheta_prime[z1][z2]);
2579  }
2580  }
2581 
2582  // calculate g(x) and hfunc(x) for each cation-anion pair MX
2583  // In the original literature, hfunc, was called gprime. However,
2584  // it's not the derivative of g(x), so I renamed it.
2585  for (size_t i = 1; i < (m_kk - 1); i++) {
2586  for (size_t j = (i+1); j < m_kk; j++) {
2587  // Find the counterIJ for the symmetric binary interaction
2588  size_t n = m_kk*i + j;
2589  size_t counterIJ = m_CounterIJ[n];
2590 
2591  // Only loop over oppositely charge species
2592  if (charge(i)*charge(j) < 0) {
2593  // x is a reduced function variable
2594  double x1 = sqrtIs * m_Alpha1MX_ij[counterIJ];
2595  if (x1 > 1.0E-100) {
2596  m_gfunc_IJ[counterIJ] = 2.0*(1.0-(1.0 + x1) * exp(-x1)) / (x1 * x1);
2597  m_hfunc_IJ[counterIJ] = -2.0 *
2598  (1.0-(1.0 + x1 + 0.5 * x1 *x1) * exp(-x1)) / (x1 * x1);
2599  } else {
2600  m_gfunc_IJ[counterIJ] = 0.0;
2601  m_hfunc_IJ[counterIJ] = 0.0;
2602  }
2603 
2604  if (m_Beta2MX_ij_L[counterIJ] != 0.0) {
2605  double x2 = sqrtIs * m_Alpha2MX_ij[counterIJ];
2606  if (x2 > 1.0E-100) {
2607  m_g2func_IJ[counterIJ] = 2.0*(1.0-(1.0 + x2) * exp(-x2)) / (x2 * x2);
2608  m_h2func_IJ[counterIJ] = -2.0 *
2609  (1.0-(1.0 + x2 + 0.5 * x2 * x2) * exp(-x2)) / (x2 * x2);
2610  } else {
2611  m_g2func_IJ[counterIJ] = 0.0;
2612  m_h2func_IJ[counterIJ] = 0.0;
2613  }
2614  }
2615  } else {
2616  m_gfunc_IJ[counterIJ] = 0.0;
2617  m_hfunc_IJ[counterIJ] = 0.0;
2618  }
2619  }
2620  }
2621 
2622  // SUBSECTION TO CALCULATE BMX_L, BprimeMX_L, BphiMX_L
2623  // These are now temperature derivatives of the previously calculated
2624  // quantities.
2625  for (size_t i = 1; i < m_kk - 1; i++) {
2626  for (size_t j = i+1; j < m_kk; j++) {
2627  // Find the counterIJ for the symmetric binary interaction
2628  size_t n = m_kk*i + j;
2629  size_t counterIJ = m_CounterIJ[n];
2630 
2631  // both species have a non-zero charge, and one is positive
2632  // and the other is negative
2633  if (charge(i)*charge(j) < 0.0) {
2634  m_BMX_IJ_L[counterIJ] = m_Beta0MX_ij_L[counterIJ]
2635  + m_Beta1MX_ij_L[counterIJ] * m_gfunc_IJ[counterIJ]
2636  + m_Beta2MX_ij_L[counterIJ] * m_gfunc_IJ[counterIJ];
2637  if (Is > 1.0E-150) {
2638  m_BprimeMX_IJ_L[counterIJ] = (m_Beta1MX_ij_L[counterIJ] * m_hfunc_IJ[counterIJ]/Is +
2639  m_Beta2MX_ij_L[counterIJ] * m_h2func_IJ[counterIJ]/Is);
2640  } else {
2641  m_BprimeMX_IJ_L[counterIJ] = 0.0;
2642  }
2643  m_BphiMX_IJ_L[counterIJ] = m_BMX_IJ_L[counterIJ] + Is*m_BprimeMX_IJ_L[counterIJ];
2644  } else {
2645  m_BMX_IJ_L[counterIJ] = 0.0;
2646  m_BprimeMX_IJ_L[counterIJ] = 0.0;
2647  m_BphiMX_IJ_L[counterIJ] = 0.0;
2648  }
2649  }
2650  }
2651 
2652  // --------- SUBSECTION TO CALCULATE CMX_L ----------
2653  for (size_t i = 1; i < m_kk-1; i++) {
2654  for (size_t j = i+1; j < m_kk; j++) {
2655  // Find the counterIJ for the symmetric binary interaction
2656  size_t n = m_kk*i + j;
2657  size_t counterIJ = m_CounterIJ[n];
2658 
2659  // both species have a non-zero charge, and one is positive
2660  // and the other is negative
2661  if (charge(i)*charge(j) < 0.0) {
2662  m_CMX_IJ_L[counterIJ] = m_CphiMX_ij_L[counterIJ]/
2663  (2.0* sqrt(fabs(charge(i)*charge(j))));
2664  } else {
2665  m_CMX_IJ_L[counterIJ] = 0.0;
2666  }
2667  }
2668  }
2669 
2670  // ------- SUBSECTION TO CALCULATE Phi, PhiPrime, and PhiPhi ----------
2671  for (size_t i = 1; i < m_kk-1; i++) {
2672  for (size_t j = i+1; j < m_kk; j++) {
2673  // Find the counterIJ for the symmetric binary interaction
2674  size_t n = m_kk*i + j;
2675  size_t counterIJ = m_CounterIJ[n];
2676 
2677  // both species have a non-zero charge, and one is positive
2678  // and the other is negative
2679  if (charge(i)*charge(j) > 0) {
2680  m_Phi_IJ_L[counterIJ] = m_Theta_ij_L[counterIJ];
2681  m_Phiprime_IJ[counterIJ] = 0.0;
2682  m_PhiPhi_IJ_L[counterIJ] = m_Phi_IJ_L[counterIJ] + Is * m_Phiprime_IJ[counterIJ];
2683  } else {
2684  m_Phi_IJ_L[counterIJ] = 0.0;
2685  m_Phiprime_IJ[counterIJ] = 0.0;
2686  m_PhiPhi_IJ_L[counterIJ] = 0.0;
2687  }
2688  }
2689  }
2690 
2691  // ----------- SUBSECTION FOR CALCULATION OF dFdT ---------------------
2692  double dA_DebyedT = dA_DebyedT_TP();
2693  double dAphidT = dA_DebyedT /3.0;
2694  double dFdT = -dAphidT * (sqrt(Is) / (1.0 + 1.2*sqrt(Is))
2695  + (2.0/1.2) * log(1.0+1.2*(sqrtIs)));
2696  for (size_t i = 1; i < m_kk-1; i++) {
2697  for (size_t j = i+1; j < m_kk; j++) {
2698  // Find the counterIJ for the symmetric binary interaction
2699  size_t n = m_kk*i + j;
2700  size_t counterIJ = m_CounterIJ[n];
2701 
2702  // both species have a non-zero charge, and one is positive
2703  // and the other is negative
2704  if (charge(i)*charge(j) < 0) {
2705  dFdT += molality[i]*molality[j] * m_BprimeMX_IJ_L[counterIJ];
2706  }
2707 
2708  // Both species have a non-zero charge, and they
2709  // have the same sign, e.g., both positive or both negative.
2710  if (charge(i)*charge(j) > 0) {
2711  dFdT += molality[i]*molality[j] * m_Phiprime_IJ[counterIJ];
2712  }
2713  }
2714  }
2715 
2716  for (size_t i = 1; i < m_kk; i++) {
2717  // -------- SUBSECTION FOR CALCULATING THE dACTCOEFFdT FOR CATIONS -----
2718  if (charge(i) > 0) {
2719  // species i is the cation (positive) to calc the actcoeff
2720  double zsqdFdT = charge(i)*charge(i)*dFdT;
2721  double sum1 = 0.0;
2722  double sum2 = 0.0;
2723  double sum3 = 0.0;
2724  double sum4 = 0.0;
2725  double sum5 = 0.0;
2726  for (size_t j = 1; j < m_kk; j++) {
2727  // Find the counterIJ for the symmetric binary interaction
2728  size_t n = m_kk*i + j;
2729  size_t counterIJ = m_CounterIJ[n];
2730 
2731  if (charge(j) < 0.0) {
2732  // sum over all anions
2733  sum1 += molality[j]*
2734  (2.0*m_BMX_IJ_L[counterIJ] + molarcharge*m_CMX_IJ_L[counterIJ]);
2735  if (j < m_kk-1) {
2736  // This term is the ternary interaction involving the
2737  // non-duplicate sum over double anions, j, k, with
2738  // respect to the cation, i.
2739  for (size_t k = j+1; k < m_kk; k++) {
2740  // an inner sum over all anions
2741  if (charge(k) < 0.0) {
2742  n = k + j * m_kk + i * m_kk * m_kk;
2743  sum3 += molality[j]*molality[k]*m_Psi_ijk_L[n];
2744  }
2745  }
2746  }
2747  }
2748 
2749  if (charge(j) > 0.0) {
2750  // sum over all cations
2751  if (j != i) {
2752  sum2 += molality[j]*(2.0*m_Phi_IJ_L[counterIJ]);
2753  }
2754  for (size_t k = 1; k < m_kk; k++) {
2755  if (charge(k) < 0.0) {
2756  // two inner sums over anions
2757  n = k + j * m_kk + i * m_kk * m_kk;
2758  sum2 += molality[j]*molality[k]*m_Psi_ijk_L[n];
2759 
2760  // Find the counterIJ for the j,k interaction
2761  n = m_kk*j + k;
2762  size_t counterIJ2 = m_CounterIJ[n];
2763  sum4 += fabs(charge(i))*
2764  molality[j]*molality[k]*m_CMX_IJ_L[counterIJ2];
2765  }
2766  }
2767  }
2768 
2769  // Handle neutral j species
2770  if (charge(j) == 0) {
2771  sum5 += molality[j]*2.0*m_Lambda_nj_L(j,i);
2772  }
2773 
2774  // Zeta interaction term
2775  for (size_t k = 1; k < m_kk; k++) {
2776  if (charge(k) < 0.0) {
2777  size_t izeta = j;
2778  size_t jzeta = i;
2779  n = izeta * m_kk * m_kk + jzeta * m_kk + k;
2780  double zeta_L = m_Psi_ijk_L[n];
2781  if (zeta_L != 0.0) {
2782  sum5 += molality[j]*molality[k]*zeta_L;
2783  }
2784  }
2785  }
2786  }
2787 
2788  // Add all of the contributions up to yield the log of the
2789  // solute activity coefficients (molality scale)
2791  zsqdFdT + sum1 + sum2 + sum3 + sum4 + sum5;
2792  d_gamma_dT_Unscaled[i] = exp(m_dlnActCoeffMolaldT_Unscaled[i]);
2793  }
2794 
2795  // ------ SUBSECTION FOR CALCULATING THE dACTCOEFFdT FOR ANIONS ------
2796  if (charge(i) < 0) {
2797  // species i is an anion (negative)
2798  double zsqdFdT = charge(i)*charge(i)*dFdT;
2799  double sum1 = 0.0;
2800  double sum2 = 0.0;
2801  double sum3 = 0.0;
2802  double sum4 = 0.0;
2803  double sum5 = 0.0;
2804  for (size_t j = 1; j < m_kk; j++) {
2805  // Find the counterIJ for the symmetric binary interaction
2806  size_t n = m_kk*i + j;
2807  size_t counterIJ = m_CounterIJ[n];
2808 
2809  // For Anions, do the cation interactions.
2810  if (charge(j) > 0) {
2811  sum1 += molality[j]*
2812  (2.0*m_BMX_IJ_L[counterIJ] + molarcharge*m_CMX_IJ_L[counterIJ]);
2813  if (j < m_kk-1) {
2814  for (size_t k = j+1; k < m_kk; k++) {
2815  // an inner sum over all cations
2816  if (charge(k) > 0) {
2817  n = k + j * m_kk + i * m_kk * m_kk;
2818  sum3 += molality[j]*molality[k]*m_Psi_ijk_L[n];
2819  }
2820  }
2821  }
2822  }
2823 
2824  // For Anions, do the other anion interactions.
2825  if (charge(j) < 0.0) {
2826  // sum over all anions
2827  if (j != i) {
2828  sum2 += molality[j]*(2.0*m_Phi_IJ_L[counterIJ]);
2829  }
2830  for (size_t k = 1; k < m_kk; k++) {
2831  if (charge(k) > 0.0) {
2832  // two inner sums over cations
2833  n = k + j * m_kk + i * m_kk * m_kk;
2834  sum2 += molality[j]*molality[k]*m_Psi_ijk_L[n];
2835  // Find the counterIJ for the symmetric binary interaction
2836  n = m_kk*j + k;
2837  size_t counterIJ2 = m_CounterIJ[n];
2838  sum4 += fabs(charge(i)) *
2839  molality[j]*molality[k]*m_CMX_IJ_L[counterIJ2];
2840  }
2841  }
2842  }
2843 
2844  // for Anions, do the neutral species interaction
2845  if (charge(j) == 0.0) {
2846  sum5 += molality[j]*2.0*m_Lambda_nj_L(j,i);
2847  for (size_t k = 1; k < m_kk; k++) {
2848  if (charge(k) > 0.0) {
2849  size_t izeta = j;
2850  size_t jzeta = k;
2851  size_t kzeta = i;
2852  n = izeta * m_kk * m_kk + jzeta * m_kk + kzeta;
2853  double zeta_L = m_Psi_ijk_L[n];
2854  if (zeta_L != 0.0) {
2855  sum5 += molality[j]*molality[k]*zeta_L;
2856  }
2857  }
2858  }
2859  }
2860  }
2862  zsqdFdT + sum1 + sum2 + sum3 + sum4 + sum5;
2863  d_gamma_dT_Unscaled[i] = exp(m_dlnActCoeffMolaldT_Unscaled[i]);
2864  }
2865 
2866  // SUBSECTION FOR CALCULATING NEUTRAL SOLUTE ACT COEFF
2867  // equations agree with my notes,
2868  // Equations agree with Pitzer,
2869  if (charge(i) == 0.0) {
2870  double sum1 = 0.0;
2871  double sum3 = 0.0;
2872  for (size_t j = 1; j < m_kk; j++) {
2873  sum1 += molality[j]*2.0*m_Lambda_nj_L(i,j);
2874  // Zeta term -> we piggyback on the psi term
2875  if (charge(j) > 0.0) {
2876  for (size_t k = 1; k < m_kk; k++) {
2877  if (charge(k) < 0.0) {
2878  size_t n = k + j * m_kk + i * m_kk * m_kk;
2879  sum3 += molality[j]*molality[k]*m_Psi_ijk_L[n];
2880  }
2881  }
2882  }
2883  }
2884  double sum2 = 3.0 * molality[i] * molality[i] * m_Mu_nnn_L[i];
2885  m_dlnActCoeffMolaldT_Unscaled[i] = sum1 + sum2 + sum3;
2886  d_gamma_dT_Unscaled[i] = exp(m_dlnActCoeffMolaldT_Unscaled[i]);
2887  }
2888  }
2889 
2890  // ------ SUBSECTION FOR CALCULATING THE d OSMOTIC COEFF dT ---------
2891  double sum1 = 0.0;
2892  double sum2 = 0.0;
2893  double sum3 = 0.0;
2894  double sum4 = 0.0;
2895  double sum5 = 0.0;
2896  double sum6 = 0.0;
2897  double sum7 = 0.0;
2898 
2899  // term1 is the temperature derivative of the DH term in the osmotic
2900  // coefficient expression
2901  // b = 1.2 sqrt(kg/gmol) <- arbitrarily set in all Pitzer implementations.
2902  // Is = Ionic strength on the molality scale (units of (gmol/kg))
2903  // Aphi = A_Debye / 3 (units of sqrt(kg/gmol))
2904  double term1 = -dAphidT * Is * sqrt(Is) / (1.0 + 1.2 * sqrt(Is));
2905 
2906  for (size_t j = 1; j < m_kk; j++) {
2907  // Loop Over Cations
2908  if (charge(j) > 0.0) {
2909  for (size_t k = 1; k < m_kk; k++) {
2910  if (charge(k) < 0.0) {
2911  // Find the counterIJ for the symmetric j,k binary interaction
2912  size_t n = m_kk*j + k;
2913  size_t counterIJ = m_CounterIJ[n];
2914  sum1 += molality[j]*molality[k]*
2915  (m_BphiMX_IJ_L[counterIJ] + molarcharge*m_CMX_IJ_L[counterIJ]);
2916  }
2917  }
2918 
2919  for (size_t k = j+1; k < m_kk; k++) {
2920  if (j == (m_kk-1)) {
2921  // we should never reach this step
2922  throw CanteraError("HMWSoln::s_updatePitzer_dlnMolalityActCoeff_dT",
2923  "logic error 1 in Step 9 of hmw_act");
2924  }
2925  if (charge(k) > 0.0) {
2926  // Find the counterIJ for the symmetric j,k binary interaction
2927  // between 2 cations.
2928  size_t n = m_kk*j + k;
2929  size_t counterIJ = m_CounterIJ[n];
2930  sum2 += molality[j]*molality[k]*m_PhiPhi_IJ_L[counterIJ];
2931  for (size_t m = 1; m < m_kk; m++) {
2932  if (charge(m) < 0.0) {
2933  // species m is an anion
2934  n = m + k * m_kk + j * m_kk * m_kk;
2935  sum2 += molality[j]*molality[k]*molality[m]*m_Psi_ijk_L[n];
2936  }
2937  }
2938  }
2939  }
2940  }
2941 
2942  // Loop Over Anions
2943  if (charge(j) < 0) {
2944  for (size_t k = j+1; k < m_kk; k++) {
2945  if (j == m_kk-1) {
2946  // we should never reach this step
2947  throw CanteraError("HMWSoln::s_updatePitzer_dlnMolalityActCoeff_dT",
2948  "logic error 2 in Step 9 of hmw_act");
2949  }
2950  if (charge(k) < 0) {
2951  // Find the counterIJ for the symmetric j,k binary interaction
2952  // between two anions
2953  size_t n = m_kk*j + k;
2954  size_t counterIJ = m_CounterIJ[n];
2955  sum3 += molality[j]*molality[k]*m_PhiPhi_IJ_L[counterIJ];
2956  for (size_t m = 1; m < m_kk; m++) {
2957  if (charge(m) > 0.0) {
2958  n = m + k * m_kk + j * m_kk * m_kk;
2959  sum3 += molality[j]*molality[k]*molality[m]*m_Psi_ijk_L[n];
2960  }
2961  }
2962  }
2963  }
2964  }
2965 
2966  // Loop Over Neutral Species
2967  if (charge(j) == 0) {
2968  for (size_t k = 1; k < m_kk; k++) {
2969  if (charge(k) < 0.0) {
2970  sum4 += molality[j]*molality[k]*m_Lambda_nj_L(j,k);
2971  }
2972  if (charge(k) > 0.0) {
2973  sum5 += molality[j]*molality[k]*m_Lambda_nj_L(j,k);
2974  }
2975  if (charge(k) == 0.0) {
2976  if (k > j) {
2977  sum6 += molality[j]*molality[k]*m_Lambda_nj_L(j,k);
2978  } else if (k == j) {
2979  sum6 += 0.5 * molality[j]*molality[k]*m_Lambda_nj_L(j,k);
2980  }
2981  }
2982  if (charge(k) < 0.0) {
2983  size_t izeta = j;
2984  for (size_t m = 1; m < m_kk; m++) {
2985  if (charge(m) > 0.0) {
2986  size_t jzeta = m;
2987  size_t n = k + jzeta * m_kk + izeta * m_kk * m_kk;
2988  double zeta_L = m_Psi_ijk_L[n];
2989  if (zeta_L != 0.0) {
2990  sum7 += molality[izeta]*molality[jzeta]*molality[k]*zeta_L;
2991  }
2992  }
2993  }
2994  }
2995  }
2996  sum7 += molality[j]*molality[j]*molality[j]*m_Mu_nnn_L[j];
2997  }
2998  }
2999  double sum_m_phi_minus_1 = 2.0 *
3000  (term1 + sum1 + sum2 + sum3 + sum4 + sum5 + sum6 + sum7);
3001  // Calculate the osmotic coefficient from
3002  // osmotic_coeff = 1 + dGex/d(M0noRT) / sum(molality_i)
3003  double d_osmotic_coef_dT;
3004  if (molalitysum > 1.0E-150) {
3005  d_osmotic_coef_dT = 0.0 + (sum_m_phi_minus_1 / molalitysum);
3006  } else {
3007  d_osmotic_coef_dT = 0.0;
3008  }
3009 
3010  double d_lnwateract_dT = -(m_weightSolvent/1000.0) * molalitysum * d_osmotic_coef_dT;
3011 
3012  // In Cantera, we define the activity coefficient of the solvent as
3013  //
3014  // act_0 = actcoeff_0 * Xmol_0
3015  //
3016  // We have just computed act_0. However, this routine returns
3017  // ln(actcoeff[]). Therefore, we must calculate ln(actcoeff_0).
3018  m_dlnActCoeffMolaldT_Unscaled[0] = d_lnwateract_dT;
3019 }
3020 
3022 {
3023  static const int cacheId = m_cache.getId();
3024  CachedScalar cached = m_cache.getScalar(cacheId);
3025  if( cached.validate(temperature(), pressure(), stateMFNumber()) ) {
3026  return;
3027  }
3028 
3029  // Zero the unscaled 2nd derivatives
3031 
3032  //! Calculate the unscaled 2nd derivatives
3034 
3035  for (size_t k = 1; k < m_kk; k++) {
3036  if (CROP_speciesCropped_[k] == 2) {
3038  }
3039  }
3040 
3041  if (CROP_speciesCropped_[0]) {
3043  }
3044 
3045  // Scale the 2nd derivatives
3047 }
3048 
3050 {
3051  const double* molality = m_molalitiesCropped.data();
3052 
3053  // Local variables defined by Coltrin
3054  double etheta[5][5], etheta_prime[5][5], sqrtIs;
3055 
3056  // Molality based ionic strength of the solution
3057  double Is = 0.0;
3058 
3059  // Molar charge of the solution: In Pitzer's notation, this is his variable
3060  // called "Z".
3061  double molarcharge = 0.0;
3062 
3063  // molalitysum is the sum of the molalities over all solutes, even those
3064  // with zero charge.
3065  double molalitysum = 0.0;
3066 
3067  // Make sure the counter variables are setup
3068  counterIJ_setup();
3069 
3070  // ---------- Calculate common sums over solutes ---------------------
3071  for (size_t n = 1; n < m_kk; n++) {
3072  // ionic strength
3073  Is += charge(n) * charge(n) * molality[n];
3074  // total molar charge
3075  molarcharge += fabs(charge(n)) * molality[n];
3076  molalitysum += molality[n];
3077  }
3078  Is *= 0.5;
3079 
3080  // Store the ionic molality in the object for reference.
3081  m_IionicMolality = Is;
3082  sqrtIs = sqrt(Is);
3083 
3084  // The following call to calc_lambdas() calculates all 16 elements of the
3085  // elambda and elambda1 arrays, given the value of the ionic strength (Is)
3086  calc_lambdas(Is);
3087 
3088  // Step 2: Find the coefficients E-theta and E-thetaprime for all
3089  // combinations of positive unlike charges up to 4
3090  for (int z1 = 1; z1 <=4; z1++) {
3091  for (int z2 =1; z2 <=4; z2++) {
3092  calc_thetas(z1, z2, &etheta[z1][z2], &etheta_prime[z1][z2]);
3093  }
3094  }
3095 
3096  // calculate gfunc(x) and hfunc(x) for each cation-anion pair MX. In the
3097  // original literature, hfunc, was called gprime. However, it's not the
3098  // derivative of gfunc(x), so I renamed it.
3099  for (size_t i = 1; i < (m_kk - 1); i++) {
3100  for (size_t j = (i+1); j < m_kk; j++) {
3101  // Find the counterIJ for the symmetric binary interaction
3102  size_t n = m_kk*i + j;
3103  size_t counterIJ = m_CounterIJ[n];
3104 
3105  // Only loop over oppositely charge species
3106  if (charge(i)*charge(j) < 0) {
3107  // x is a reduced function variable
3108  double x1 = sqrtIs * m_Alpha1MX_ij[counterIJ];
3109  if (x1 > 1.0E-100) {
3110  m_gfunc_IJ[counterIJ] = 2.0*(1.0-(1.0 + x1) * exp(-x1)) / (x1 *x1);
3111  m_hfunc_IJ[counterIJ] = -2.0*
3112  (1.0-(1.0 + x1 + 0.5*x1 * x1) * exp(-x1)) / (x1 * x1);
3113  } else {
3114  m_gfunc_IJ[counterIJ] = 0.0;
3115  m_hfunc_IJ[counterIJ] = 0.0;
3116  }
3117 
3118  if (m_Beta2MX_ij_LL[counterIJ] != 0.0) {
3119  double x2 = sqrtIs * m_Alpha2MX_ij[counterIJ];
3120  if (x2 > 1.0E-100) {
3121  m_g2func_IJ[counterIJ] = 2.0*(1.0-(1.0 + x2) * exp(-x2)) / (x2 * x2);
3122  m_h2func_IJ[counterIJ] = -2.0 *
3123  (1.0-(1.0 + x2 + 0.5 * x2 * x2) * exp(-x2)) / (x2 * x2);
3124  } else {
3125  m_g2func_IJ[counterIJ] = 0.0;
3126  m_h2func_IJ[counterIJ] = 0.0;
3127  }
3128  }
3129  } else {
3130  m_gfunc_IJ[counterIJ] = 0.0;
3131  m_hfunc_IJ[counterIJ] = 0.0;
3132  }
3133  }
3134  }
3135 
3136  // SUBSECTION TO CALCULATE BMX_L, BprimeMX_LL, BphiMX_L
3137  // These are now temperature derivatives of the previously calculated
3138  // quantities.
3139  for (size_t i = 1; i < m_kk - 1; i++) {
3140  for (size_t j = i+1; j < m_kk; j++) {
3141  // Find the counterIJ for the symmetric binary interaction
3142  size_t n = m_kk*i + j;
3143  size_t counterIJ = m_CounterIJ[n];
3144 
3145  // both species have a non-zero charge, and one is positive
3146  // and the other is negative
3147  if (charge(i)*charge(j) < 0.0) {
3148  m_BMX_IJ_LL[counterIJ] = m_Beta0MX_ij_LL[counterIJ]
3149  + m_Beta1MX_ij_LL[counterIJ] * m_gfunc_IJ[counterIJ]
3150  + m_Beta2MX_ij_LL[counterIJ] * m_g2func_IJ[counterIJ];
3151  if (Is > 1.0E-150) {
3152  m_BprimeMX_IJ_LL[counterIJ] = (m_Beta1MX_ij_LL[counterIJ] * m_hfunc_IJ[counterIJ]/Is +
3153  m_Beta2MX_ij_LL[counterIJ] * m_h2func_IJ[counterIJ]/Is);
3154  } else {
3155  m_BprimeMX_IJ_LL[counterIJ] = 0.0;
3156  }
3157  m_BphiMX_IJ_LL[counterIJ] = m_BMX_IJ_LL[counterIJ] + Is*m_BprimeMX_IJ_LL[counterIJ];
3158  } else {
3159  m_BMX_IJ_LL[counterIJ] = 0.0;
3160  m_BprimeMX_IJ_LL[counterIJ] = 0.0;
3161  m_BphiMX_IJ_LL[counterIJ] = 0.0;
3162  }
3163  }
3164  }
3165 
3166  // --------- SUBSECTION TO CALCULATE CMX_LL ----------
3167  for (size_t i = 1; i < m_kk-1; i++) {
3168  for (size_t j = i+1; j < m_kk; j++) {
3169  // Find the counterIJ for the symmetric binary interaction
3170  size_t n = m_kk*i + j;
3171  size_t counterIJ = m_CounterIJ[n];
3172 
3173  // both species have a non-zero charge, and one is positive
3174  // and the other is negative
3175  if (charge(i)*charge(j) < 0.0) {
3176  m_CMX_IJ_LL[counterIJ] = m_CphiMX_ij_LL[counterIJ]/
3177  (2.0* sqrt(fabs(charge(i)*charge(j))));
3178  } else {
3179  m_CMX_IJ_LL[counterIJ] = 0.0;
3180  }
3181  }
3182  }
3183 
3184  // ------- SUBSECTION TO CALCULATE Phi, PhiPrime, and PhiPhi ----------
3185  for (size_t i = 1; i < m_kk-1; i++) {
3186  for (size_t j = i+1; j < m_kk; j++) {
3187  // Find the counterIJ for the symmetric binary interaction
3188  size_t n = m_kk*i + j;
3189  size_t counterIJ = m_CounterIJ[n];
3190 
3191  // both species have a non-zero charge, and one is positive
3192  // and the other is negative
3193  if (charge(i)*charge(j) > 0) {
3194  m_Phi_IJ_LL[counterIJ] = m_Theta_ij_LL[counterIJ];
3195  m_Phiprime_IJ[counterIJ] = 0.0;
3196  m_PhiPhi_IJ_LL[counterIJ] = m_Phi_IJ_LL[counterIJ];
3197  } else {
3198  m_Phi_IJ_LL[counterIJ] = 0.0;
3199  m_Phiprime_IJ[counterIJ] = 0.0;
3200  m_PhiPhi_IJ_LL[counterIJ] = 0.0;
3201  }
3202  }
3203  }
3204 
3205  // ----------- SUBSECTION FOR CALCULATION OF d2FdT2 ---------------------
3206  double d2AphidT2 = d2A_DebyedT2_TP() / 3.0;
3207  double d2FdT2 = -d2AphidT2 * (sqrt(Is) / (1.0 + 1.2*sqrt(Is))
3208  + (2.0/1.2) * log(1.0+1.2*(sqrtIs)));
3209  for (size_t i = 1; i < m_kk-1; i++) {
3210  for (size_t j = i+1; j < m_kk; j++) {
3211  // Find the counterIJ for the symmetric binary interaction
3212  size_t n = m_kk*i + j;
3213  size_t counterIJ = m_CounterIJ[n];
3214 
3215  // both species have a non-zero charge, and one is positive
3216  // and the other is negative
3217  if (charge(i)*charge(j) < 0) {
3218  d2FdT2 += molality[i]*molality[j] * m_BprimeMX_IJ_LL[counterIJ];
3219  }
3220 
3221  // Both species have a non-zero charge, and they
3222  // have the same sign, e.g., both positive or both negative.
3223  if (charge(i)*charge(j) > 0) {
3224  d2FdT2 += molality[i]*molality[j] * m_Phiprime_IJ[counterIJ];
3225  }
3226  }
3227  }
3228 
3229  for (size_t i = 1; i < m_kk; i++) {
3230  // -------- SUBSECTION FOR CALCULATING THE dACTCOEFFdT FOR CATIONS -----
3231  if (charge(i) > 0) {
3232  // species i is the cation (positive) to calc the actcoeff
3233  double zsqd2FdT2 = charge(i)*charge(i)*d2FdT2;
3234  double sum1 = 0.0;
3235  double sum2 = 0.0;
3236  double sum3 = 0.0;
3237  double sum4 = 0.0;
3238  double sum5 = 0.0;
3239  for (size_t j = 1; j < m_kk; j++) {
3240  // Find the counterIJ for the symmetric binary interaction
3241  size_t n = m_kk*i + j;
3242  size_t counterIJ = m_CounterIJ[n];
3243 
3244  if (charge(j) < 0.0) {
3245  // sum over all anions
3246  sum1 += molality[j]*
3247  (2.0*m_BMX_IJ_LL[counterIJ] + molarcharge*m_CMX_IJ_LL[counterIJ]);
3248  if (j < m_kk-1) {
3249  // This term is the ternary interaction involving the
3250  // non-duplicate sum over double anions, j, k, with
3251  // respect to the cation, i.
3252  for (size_t k = j+1; k < m_kk; k++) {
3253  // an inner sum over all anions
3254  if (charge(k) < 0.0) {
3255  n = k + j * m_kk + i * m_kk * m_kk;
3256  sum3 += molality[j]*molality[k]*m_Psi_ijk_LL[n];
3257  }
3258  }
3259  }
3260  }
3261 
3262  if (charge(j) > 0.0) {
3263  // sum over all cations
3264  if (j != i) {
3265  sum2 += molality[j]*(2.0*m_Phi_IJ_LL[counterIJ]);
3266  }
3267  for (size_t k = 1; k < m_kk; k++) {
3268  if (charge(k) < 0.0) {
3269  // two inner sums over anions
3270  n = k + j * m_kk + i * m_kk * m_kk;
3271  sum2 += molality[j]*molality[k]*m_Psi_ijk_LL[n];
3272 
3273  // Find the counterIJ for the j,k interaction
3274  n = m_kk*j + k;
3275  size_t counterIJ2 = m_CounterIJ[n];
3276  sum4 += fabs(charge(i)) *
3277  molality[j]*molality[k]*m_CMX_IJ_LL[counterIJ2];
3278  }
3279  }
3280  }
3281 
3282  // Handle neutral j species
3283  if (charge(j) == 0) {
3284  sum5 += molality[j]*2.0*m_Lambda_nj_LL(j,i);
3285  // Zeta interaction term
3286  for (size_t k = 1; k < m_kk; k++) {
3287  if (charge(k) < 0.0) {
3288  size_t izeta = j;
3289  size_t jzeta = i;
3290  n = izeta * m_kk * m_kk + jzeta * m_kk + k;
3291  double zeta_LL = m_Psi_ijk_LL[n];
3292  if (zeta_LL != 0.0) {
3293  sum5 += molality[j]*molality[k]*zeta_LL;
3294  }
3295  }
3296  }
3297  }
3298  }
3299  // Add all of the contributions up to yield the log of the
3300  // solute activity coefficients (molality scale)
3302  zsqd2FdT2 + sum1 + sum2 + sum3 + sum4 + sum5;
3303  }
3304 
3305  // ------ SUBSECTION FOR CALCULATING THE d2ACTCOEFFdT2 FOR ANIONS ------
3306  if (charge(i) < 0) {
3307  // species i is an anion (negative)
3308  double zsqd2FdT2 = charge(i)*charge(i)*d2FdT2;
3309  double sum1 = 0.0;
3310  double sum2 = 0.0;
3311  double sum3 = 0.0;
3312  double sum4 = 0.0;
3313  double sum5 = 0.0;
3314  for (size_t j = 1; j < m_kk; j++) {
3315  // Find the counterIJ for the symmetric binary interaction
3316  size_t n = m_kk*i + j;
3317  size_t counterIJ = m_CounterIJ[n];
3318 
3319  // For Anions, do the cation interactions.
3320  if (charge(j) > 0) {
3321  sum1 += molality[j]*
3322  (2.0*m_BMX_IJ_LL[counterIJ] + molarcharge*m_CMX_IJ_LL[counterIJ]);
3323  if (j < m_kk-1) {
3324  for (size_t k = j+1; k < m_kk; k++) {
3325  // an inner sum over all cations
3326  if (charge(k) > 0) {
3327  n = k + j * m_kk + i * m_kk * m_kk;
3328  sum3 += molality[j]*molality[k]*m_Psi_ijk_LL[n];
3329  }
3330  }
3331  }
3332  }
3333 
3334  // For Anions, do the other anion interactions.
3335  if (charge(j) < 0.0) {
3336  // sum over all anions
3337  if (j != i) {
3338  sum2 += molality[j]*(2.0*m_Phi_IJ_LL[counterIJ]);
3339  }
3340  for (size_t k = 1; k < m_kk; k++) {
3341  if (charge(k) > 0.0) {
3342  // two inner sums over cations
3343  n = k + j * m_kk + i * m_kk * m_kk;
3344  sum2 += molality[j]*molality[k]*m_Psi_ijk_LL[n];
3345  // Find the counterIJ for the symmetric binary interaction
3346  n = m_kk*j + k;
3347  size_t counterIJ2 = m_CounterIJ[n];
3348  sum4 += fabs(charge(i)) *
3349  molality[j]*molality[k]*m_CMX_IJ_LL[counterIJ2];
3350  }
3351  }
3352  }
3353 
3354  // for Anions, do the neutral species interaction
3355  if (charge(j) == 0.0) {
3356  sum5 += molality[j]*2.0*m_Lambda_nj_LL(j,i);
3357  // Zeta interaction term
3358  for (size_t k = 1; k < m_kk; k++) {
3359  if (charge(k) > 0.0) {
3360  size_t izeta = j;
3361  size_t jzeta = k;
3362  size_t kzeta = i;
3363  n = izeta * m_kk * m_kk + jzeta * m_kk + kzeta;
3364  double zeta_LL = m_Psi_ijk_LL[n];
3365  if (zeta_LL != 0.0) {
3366  sum5 += molality[j]*molality[k]*zeta_LL;
3367  }
3368  }
3369  }
3370  }
3371  }
3373  zsqd2FdT2 + sum1 + sum2 + sum3 + sum4 + sum5;
3374  }
3375 
3376  // SUBSECTION FOR CALCULATING NEUTRAL SOLUTE ACT COEFF
3377  // equations agree with my notes,
3378  // Equations agree with Pitzer,
3379  if (charge(i) == 0.0) {
3380  double sum1 = 0.0;
3381  double sum3 = 0.0;
3382  for (size_t j = 1; j < m_kk; j++) {
3383  sum1 += molality[j]*2.0*m_Lambda_nj_LL(i,j);
3384  // Zeta term -> we piggyback on the psi term
3385  if (charge(j) > 0.0) {
3386  for (size_t k = 1; k < m_kk; k++) {
3387  if (charge(k) < 0.0) {
3388  size_t n = k + j * m_kk + i * m_kk * m_kk;
3389  sum3 += molality[j]*molality[k]*m_Psi_ijk_LL[n];
3390  }
3391  }
3392  }
3393  }
3394  double sum2 = 3.0 * molality[i] * molality[i] * m_Mu_nnn_LL[i];
3395  m_d2lnActCoeffMolaldT2_Unscaled[i] = sum1 + sum2 + sum3;
3396  }
3397  }
3398 
3399  // ------ SUBSECTION FOR CALCULATING THE d2 OSMOTIC COEFF dT2 ---------
3400  double sum1 = 0.0;
3401  double sum2 = 0.0;
3402  double sum3 = 0.0;
3403  double sum4 = 0.0;
3404  double sum5 = 0.0;
3405  double sum6 = 0.0;
3406  double sum7 = 0.0;
3407 
3408  // term1 is the temperature derivative of the DH term in the osmotic
3409  // coefficient expression
3410  // b = 1.2 sqrt(kg/gmol) <- arbitrarily set in all Pitzer implementations.
3411  // Is = Ionic strength on the molality scale (units of (gmol/kg))
3412  // Aphi = A_Debye / 3 (units of sqrt(kg/gmol))
3413  double term1 = -d2AphidT2 * Is * sqrt(Is) / (1.0 + 1.2 * sqrt(Is));
3414 
3415  for (size_t j = 1; j < m_kk; j++) {
3416  // Loop Over Cations
3417  if (charge(j) > 0.0) {
3418  for (size_t k = 1; k < m_kk; k++) {
3419  if (charge(k) < 0.0) {
3420  // Find the counterIJ for the symmetric j,k binary interaction
3421  size_t n = m_kk*j + k;
3422  size_t counterIJ = m_CounterIJ[n];
3423 
3424  sum1 += molality[j]*molality[k] *
3425  (m_BphiMX_IJ_LL[counterIJ] + molarcharge*m_CMX_IJ_LL[counterIJ]);
3426  }
3427  }
3428 
3429  for (size_t k = j+1; k < m_kk; k++) {
3430  if (j == (m_kk-1)) {
3431  // we should never reach this step
3432  throw CanteraError("HMWSoln::s_updatePitzer_d2lnMolalityActCoeff_dT2",
3433  "logic error 1 in Step 9 of hmw_act");
3434  }
3435  if (charge(k) > 0.0) {
3436  // Find the counterIJ for the symmetric j,k binary interaction
3437  // between 2 cations.
3438  size_t n = m_kk*j + k;
3439  size_t counterIJ = m_CounterIJ[n];
3440  sum2 += molality[j]*molality[k]*m_PhiPhi_IJ_LL[counterIJ];
3441  for (size_t m = 1; m < m_kk; m++) {
3442  if (charge(m) < 0.0) {
3443  // species m is an anion
3444  n = m + k * m_kk + j * m_kk * m_kk;
3445  sum2 += molality[j]*molality[k]*molality[m]*m_Psi_ijk_LL[n];
3446  }
3447  }
3448  }
3449  }
3450  }
3451 
3452  // Loop Over Anions
3453  if (charge(j) < 0) {
3454  for (size_t k = j+1; k < m_kk; k++) {
3455  if (j == m_kk-1) {
3456  // we should never reach this step
3457  throw CanteraError("HMWSoln::s_updatePitzer_d2lnMolalityActCoeff_dT2",
3458  "logic error 2 in Step 9 of hmw_act");
3459  }
3460  if (charge(k) < 0) {
3461  // Find the counterIJ for the symmetric j,k binary interaction
3462  // between two anions
3463  size_t n = m_kk*j + k;
3464  size_t counterIJ = m_CounterIJ[n];
3465 
3466  sum3 += molality[j]*molality[k]*m_PhiPhi_IJ_LL[counterIJ];
3467  for (size_t m = 1; m < m_kk; m++) {
3468  if (charge(m) > 0.0) {
3469  n = m + k * m_kk + j * m_kk * m_kk;
3470  sum3 += molality[j]*molality[k]*molality[m]*m_Psi_ijk_LL[n];
3471  }
3472  }
3473  }
3474  }
3475  }
3476 
3477  // Loop Over Neutral Species
3478  if (charge(j) == 0) {
3479  for (size_t k = 1; k < m_kk; k++) {
3480  if (charge(k) < 0.0) {
3481  sum4 += molality[j]*molality[k]*m_Lambda_nj_LL(j,k);
3482  }
3483  if (charge(k) > 0.0) {
3484  sum5 += molality[j]*molality[k]*m_Lambda_nj_LL(j,k);
3485  }
3486  if (charge(k) == 0.0) {
3487  if (k > j) {
3488  sum6 += molality[j]*molality[k]*m_Lambda_nj_LL(j,k);
3489  } else if (k == j) {
3490  sum6 += 0.5 * molality[j]*molality[k]*m_Lambda_nj_LL(j,k);
3491  }
3492  }
3493  if (charge(k) < 0.0) {
3494  size_t izeta = j;
3495  for (size_t m = 1; m < m_kk; m++) {
3496  if (charge(m) > 0.0) {
3497  size_t jzeta = m;
3498  size_t n = k + jzeta * m_kk + izeta * m_kk * m_kk;
3499  double zeta_LL = m_Psi_ijk_LL[n];
3500  if (zeta_LL != 0.0) {
3501  sum7 += molality[izeta]*molality[jzeta]*molality[k]*zeta_LL;
3502  }
3503  }
3504  }
3505  }
3506  }
3507 
3508  sum7 += molality[j] * molality[j] * molality[j] * m_Mu_nnn_LL[j];
3509  }
3510  }
3511  double sum_m_phi_minus_1 = 2.0 *
3512  (term1 + sum1 + sum2 + sum3 + sum4 + sum5 + sum6 + sum7);
3513  // Calculate the osmotic coefficient from
3514  // osmotic_coeff = 1 + dGex/d(M0noRT) / sum(molality_i)
3515  double d2_osmotic_coef_dT2;
3516  if (molalitysum > 1.0E-150) {
3517  d2_osmotic_coef_dT2 = 0.0 + (sum_m_phi_minus_1 / molalitysum);
3518  } else {
3519  d2_osmotic_coef_dT2 = 0.0;
3520  }
3521  double d2_lnwateract_dT2 = -(m_weightSolvent/1000.0) * molalitysum * d2_osmotic_coef_dT2;
3522 
3523  // In Cantera, we define the activity coefficient of the solvent as
3524  //
3525  // act_0 = actcoeff_0 * Xmol_0
3526  //
3527  // We have just computed act_0. However, this routine returns
3528  // ln(actcoeff[]). Therefore, we must calculate ln(actcoeff_0).
3529  m_d2lnActCoeffMolaldT2_Unscaled[0] = d2_lnwateract_dT2;
3530 }
3531 
3533 {
3534  static const int cacheId = m_cache.getId();
3535  CachedScalar cached = m_cache.getScalar(cacheId);
3536  if( cached.validate(temperature(), pressure(), stateMFNumber()) ) {
3537  return;
3538  }
3539 
3540  m_dlnActCoeffMolaldP_Unscaled.assign(m_kk, 0.0);
3542 
3543  for (size_t k = 1; k < m_kk; k++) {
3544  if (CROP_speciesCropped_[k] == 2) {
3546  }
3547  }
3548 
3549  if (CROP_speciesCropped_[0]) {
3551  }
3552 
3554 }
3555 
3557 {
3558  const double* molality = m_molalitiesCropped.data();
3559 
3560  // Local variables defined by Coltrin
3561  double etheta[5][5], etheta_prime[5][5], sqrtIs;
3562 
3563  // Molality based ionic strength of the solution
3564  double Is = 0.0;
3565 
3566  // Molar charge of the solution: In Pitzer's notation, this is his variable
3567  // called "Z".
3568  double molarcharge = 0.0;
3569 
3570  // molalitysum is the sum of the molalities over all solutes, even those
3571  // with zero charge.
3572  double molalitysum = 0.0;
3573  double currTemp = temperature();
3574  double currPres = pressure();
3575 
3576  // Make sure the counter variables are setup
3577  counterIJ_setup();
3578 
3579  // ---------- Calculate common sums over solutes ---------------------
3580  for (size_t n = 1; n < m_kk; n++) {
3581  // ionic strength
3582  Is += charge(n) * charge(n) * molality[n];
3583  // total molar charge
3584  molarcharge += fabs(charge(n)) * molality[n];
3585  molalitysum += molality[n];
3586  }
3587  Is *= 0.5;
3588 
3589  // Store the ionic molality in the object for reference.
3590  m_IionicMolality = Is;
3591  sqrtIs = sqrt(Is);
3592 
3593  // The following call to calc_lambdas() calculates all 16 elements of the
3594  // elambda and elambda1 arrays, given the value of the ionic strength (Is)
3595  calc_lambdas(Is);
3596 
3597  // Step 2: Find the coefficients E-theta and E-thetaprime for all
3598  // combinations of positive unlike charges up to 4
3599  for (int z1 = 1; z1 <=4; z1++) {
3600  for (int z2 =1; z2 <=4; z2++) {
3601  calc_thetas(z1, z2, &etheta[z1][z2], &etheta_prime[z1][z2]);
3602  }
3603  }
3604 
3605  // calculate g(x) and hfunc(x) for each cation-anion pair MX
3606  // In the original literature, hfunc, was called gprime. However,
3607  // it's not the derivative of g(x), so I renamed it.
3608  for (size_t i = 1; i < (m_kk - 1); i++) {
3609  for (size_t j = (i+1); j < m_kk; j++) {
3610  // Find the counterIJ for the symmetric binary interaction
3611  size_t n = m_kk*i + j;
3612  size_t counterIJ = m_CounterIJ[n];
3613 
3614  // Only loop over oppositely charge species
3615  if (charge(i)*charge(j) < 0) {
3616  // x is a reduced function variable
3617  double x1 = sqrtIs * m_Alpha1MX_ij[counterIJ];
3618  if (x1 > 1.0E-100) {
3619  m_gfunc_IJ[counterIJ] = 2.0*(1.0-(1.0 + x1) * exp(-x1)) / (x1 * x1);
3620  m_hfunc_IJ[counterIJ] = -2.0*
3621  (1.0-(1.0 + x1 + 0.5 * x1 * x1) * exp(-x1)) / (x1 * x1);
3622  } else {
3623  m_gfunc_IJ[counterIJ] = 0.0;
3624  m_hfunc_IJ[counterIJ] = 0.0;
3625  }
3626 
3627  if (m_Beta2MX_ij_P[counterIJ] != 0.0) {
3628  double x2 = sqrtIs * m_Alpha2MX_ij[counterIJ];
3629  if (x2 > 1.0E-100) {
3630  m_g2func_IJ[counterIJ] = 2.0*(1.0-(1.0 + x2) * exp(-x2)) / (x2 * x2);
3631  m_h2func_IJ[counterIJ] = -2.0 *
3632  (1.0-(1.0 + x2 + 0.5 * x2 * x2) * exp(-x2)) / (x2 * x2);
3633  } else {
3634  m_g2func_IJ[counterIJ] = 0.0;
3635  m_h2func_IJ[counterIJ] = 0.0;
3636  }
3637  }
3638  } else {
3639  m_gfunc_IJ[counterIJ] = 0.0;
3640  m_hfunc_IJ[counterIJ] = 0.0;
3641  }
3642  }
3643  }
3644 
3645  // SUBSECTION TO CALCULATE BMX_P, BprimeMX_P, BphiMX_P
3646  // These are now temperature derivatives of the previously calculated
3647  // quantities.
3648  for (size_t i = 1; i < m_kk - 1; i++) {
3649  for (size_t j = i+1; j < m_kk; j++) {
3650  // Find the counterIJ for the symmetric binary interaction
3651  size_t n = m_kk*i + j;
3652  size_t counterIJ = m_CounterIJ[n];
3653 
3654  // both species have a non-zero charge, and one is positive
3655  // and the other is negative
3656  if (charge(i)*charge(j) < 0.0) {
3657  m_BMX_IJ_P[counterIJ] = m_Beta0MX_ij_P[counterIJ]
3658  + m_Beta1MX_ij_P[counterIJ] * m_gfunc_IJ[counterIJ]
3659  + m_Beta2MX_ij_P[counterIJ] * m_g2func_IJ[counterIJ];
3660  if (Is > 1.0E-150) {
3661  m_BprimeMX_IJ_P[counterIJ] = (m_Beta1MX_ij_P[counterIJ] * m_hfunc_IJ[counterIJ]/Is +
3662  m_Beta2MX_ij_P[counterIJ] * m_h2func_IJ[counterIJ]/Is);
3663  } else {
3664  m_BprimeMX_IJ_P[counterIJ] = 0.0;
3665  }
3666  m_BphiMX_IJ_P[counterIJ] = m_BMX_IJ_P[counterIJ] + Is*m_BprimeMX_IJ_P[counterIJ];
3667  } else {
3668  m_BMX_IJ_P[counterIJ] = 0.0;
3669  m_BprimeMX_IJ_P[counterIJ] = 0.0;
3670  m_BphiMX_IJ_P[counterIJ] = 0.0;
3671  }
3672  }
3673  }
3674 
3675  // --------- SUBSECTION TO CALCULATE CMX_P ----------
3676  for (size_t i = 1; i < m_kk-1; i++) {
3677  for (size_t j = i+1; j < m_kk; j++) {
3678  // Find the counterIJ for the symmetric binary interaction
3679  size_t n = m_kk*i + j;
3680  size_t counterIJ = m_CounterIJ[n];
3681 
3682  // both species have a non-zero charge, and one is positive
3683  // and the other is negative
3684  if (charge(i)*charge(j) < 0.0) {
3685  m_CMX_IJ_P[counterIJ] = m_CphiMX_ij_P[counterIJ]/
3686  (2.0* sqrt(fabs(charge(i)*charge(j))));
3687  } else {
3688  m_CMX_IJ_P[counterIJ] = 0.0;
3689  }
3690  }
3691  }
3692 
3693  // ------- SUBSECTION TO CALCULATE Phi, PhiPrime, and PhiPhi ----------
3694  for (size_t i = 1; i < m_kk-1; i++) {
3695  for (size_t j = i+1; j < m_kk; j++) {
3696  // Find the counterIJ for the symmetric binary interaction
3697  size_t n = m_kk*i + j;
3698  size_t counterIJ = m_CounterIJ[n];
3699 
3700  // both species have a non-zero charge, and one is positive
3701  // and the other is negative
3702  if (charge(i)*charge(j) > 0) {
3703  m_Phi_IJ_P[counterIJ] = m_Theta_ij_P[counterIJ];
3704  m_Phiprime_IJ[counterIJ] = 0.0;
3705  m_PhiPhi_IJ_P[counterIJ] = m_Phi_IJ_P[counterIJ] + Is * m_Phiprime_IJ[counterIJ];
3706  } else {
3707  m_Phi_IJ_P[counterIJ] = 0.0;
3708  m_Phiprime_IJ[counterIJ] = 0.0;
3709  m_PhiPhi_IJ_P[counterIJ] = 0.0;
3710  }
3711  }
3712  }
3713 
3714  // ----------- SUBSECTION FOR CALCULATION OF dFdT ---------------------
3715  double dA_DebyedP = dA_DebyedP_TP(currTemp, currPres);
3716  double dAphidP = dA_DebyedP /3.0;
3717  double dFdP = -dAphidP * (sqrt(Is) / (1.0 + 1.2*sqrt(Is))
3718  + (2.0/1.2) * log(1.0+1.2*(sqrtIs)));
3719  for (size_t i = 1; i < m_kk-1; i++) {
3720  for (size_t j = i+1; j < m_kk; j++) {
3721  // Find the counterIJ for the symmetric binary interaction
3722  size_t n = m_kk*i + j;
3723  size_t counterIJ = m_CounterIJ[n];
3724 
3725  // both species have a non-zero charge, and one is positive
3726  // and the other is negative
3727  if (charge(i)*charge(j) < 0) {
3728  dFdP += molality[i]*molality[j] * m_BprimeMX_IJ_P[counterIJ];
3729  }
3730 
3731  // Both species have a non-zero charge, and they
3732  // have the same sign, e.g., both positive or both negative.
3733  if (charge(i)*charge(j) > 0) {
3734  dFdP += molality[i]*molality[j] * m_Phiprime_IJ[counterIJ];
3735  }
3736  }
3737  }
3738 
3739  for (size_t i = 1; i < m_kk; i++) {
3740  // -------- SUBSECTION FOR CALCULATING THE dACTCOEFFdP FOR CATIONS -----
3741  if (charge(i) > 0) {
3742  // species i is the cation (positive) to calc the actcoeff
3743  double zsqdFdP = charge(i)*charge(i)*dFdP;
3744  double sum1 = 0.0;
3745  double sum2 = 0.0;
3746  double sum3 = 0.0;
3747  double sum4 = 0.0;
3748  double sum5 = 0.0;
3749  for (size_t j = 1; j < m_kk; j++) {
3750  // Find the counterIJ for the symmetric binary interaction
3751  size_t n = m_kk*i + j;
3752  size_t counterIJ = m_CounterIJ[n];
3753 
3754  if (charge(j) < 0.0) {
3755  // sum over all anions
3756  sum1 += molality[j]*
3757  (2.0*m_BMX_IJ_P[counterIJ] + molarcharge*m_CMX_IJ_P[counterIJ]);
3758  if (j < m_kk-1) {
3759  // This term is the ternary interaction involving the
3760  // non-duplicate sum over double anions, j, k, with
3761  // respect to the cation, i.
3762  for (size_t k = j+1; k < m_kk; k++) {
3763  // an inner sum over all anions
3764  if (charge(k) < 0.0) {
3765  n = k + j * m_kk + i * m_kk * m_kk;
3766  sum3 += molality[j]*molality[k]*m_Psi_ijk_P[n];
3767  }
3768  }
3769  }
3770  }
3771 
3772  if (charge(j) > 0.0) {
3773  // sum over all cations
3774  if (j != i) {
3775  sum2 += molality[j]*(2.0*m_Phi_IJ_P[counterIJ]);
3776  }
3777  for (size_t k = 1; k < m_kk; k++) {
3778  if (charge(k) < 0.0) {
3779  // two inner sums over anions
3780  n = k + j * m_kk + i * m_kk * m_kk;
3781  sum2 += molality[j]*molality[k]*m_Psi_ijk_P[n];
3782 
3783  // Find the counterIJ for the j,k interaction
3784  n = m_kk*j + k;
3785  size_t counterIJ2 = m_CounterIJ[n];
3786  sum4 += fabs(charge(i)) *
3787  molality[j]*molality[k]*m_CMX_IJ_P[counterIJ2];
3788  }
3789  }
3790  }
3791 
3792  // for Anions, do the neutral species interaction
3793  if (charge(j) == 0) {
3794  sum5 += molality[j]*2.0*m_Lambda_nj_P(j,i);
3795  // Zeta interaction term
3796  for (size_t k = 1; k < m_kk; k++) {
3797  if (charge(k) < 0.0) {
3798  size_t izeta = j;
3799  size_t jzeta = i;
3800  n = izeta * m_kk * m_kk + jzeta * m_kk + k;
3801  double zeta_P = m_Psi_ijk_P[n];
3802  if (zeta_P != 0.0) {
3803  sum5 += molality[j]*molality[k]*zeta_P;
3804  }
3805  }
3806  }
3807  }
3808  }
3809 
3810  // Add all of the contributions up to yield the log of the
3811  // solute activity coefficients (molality scale)
3813  zsqdFdP + sum1 + sum2 + sum3 + sum4 + sum5;
3814  }
3815 
3816  // ------ SUBSECTION FOR CALCULATING THE dACTCOEFFdP FOR ANIONS ------
3817  if (charge(i) < 0) {
3818  // species i is an anion (negative)
3819  double zsqdFdP = charge(i)*charge(i)*dFdP;
3820  double sum1 = 0.0;
3821  double sum2 = 0.0;
3822  double sum3 = 0.0;
3823  double sum4 = 0.0;
3824  double sum5 = 0.0;
3825  for (size_t j = 1; j < m_kk; j++) {
3826  // Find the counterIJ for the symmetric binary interaction
3827  size_t n = m_kk*i + j;
3828  size_t counterIJ = m_CounterIJ[n];
3829 
3830  // For Anions, do the cation interactions.
3831  if (charge(j) > 0) {
3832  sum1 += molality[j] *
3833  (2.0*m_BMX_IJ_P[counterIJ] + molarcharge*m_CMX_IJ_P[counterIJ]);
3834  if (j < m_kk-1) {
3835  for (size_t k = j+1; k < m_kk; k++) {
3836  // an inner sum over all cations
3837  if (charge(k) > 0) {
3838  n = k + j * m_kk + i * m_kk * m_kk;
3839  sum3 += molality[j]*molality[k]*m_Psi_ijk_P[n];
3840  }
3841  }
3842  }
3843  }
3844 
3845  // For Anions, do the other anion interactions.
3846  if (charge(j) < 0.0) {
3847  // sum over all anions
3848  if (j != i) {
3849  sum2 += molality[j]*(2.0*m_Phi_IJ_P[counterIJ]);
3850  }
3851  for (size_t k = 1; k < m_kk; k++) {
3852  if (charge(k) > 0.0) {
3853  // two inner sums over cations
3854  n = k + j * m_kk + i * m_kk * m_kk;
3855  sum2 += molality[j]*molality[k]*m_Psi_ijk_P[n];
3856  // Find the counterIJ for the symmetric binary interaction
3857  n = m_kk*j + k;
3858  size_t counterIJ2 = m_CounterIJ[n];
3859  sum4 += fabs(charge(i))*
3860  molality[j]*molality[k]*m_CMX_IJ_P[counterIJ2];
3861  }
3862  }
3863  }
3864 
3865  // for Anions, do the neutral species interaction
3866  if (charge(j) == 0.0) {
3867  sum5 += molality[j]*2.0*m_Lambda_nj_P(j,i);
3868  // Zeta interaction term
3869  for (size_t k = 1; k < m_kk; k++) {
3870  if (charge(k) > 0.0) {
3871  size_t izeta = j;
3872  size_t jzeta = k;
3873  size_t kzeta = i;
3874  n = izeta * m_kk * m_kk + jzeta * m_kk + kzeta;
3875  double zeta_P = m_Psi_ijk_P[n];
3876  if (zeta_P != 0.0) {
3877  sum5 += molality[j]*molality[k]*zeta_P;
3878  }
3879  }
3880  }
3881  }
3882  }
3884  zsqdFdP + sum1 + sum2 + sum3 + sum4 + sum5;
3885  }
3886 
3887  // ------ SUBSECTION FOR CALCULATING d NEUTRAL SOLUTE ACT COEFF dP -----
3888  if (charge(i) == 0.0) {
3889  double sum1 = 0.0;
3890  double sum3 = 0.0;
3891  for (size_t j = 1; j < m_kk; j++) {
3892  sum1 += molality[j]*2.0*m_Lambda_nj_P(i,j);
3893  // Zeta term -> we piggyback on the psi term
3894  if (charge(j) > 0.0) {
3895  for (size_t k = 1; k < m_kk; k++) {
3896  if (charge(k) < 0.0) {
3897  size_t n = k + j * m_kk + i * m_kk * m_kk;
3898  sum3 += molality[j]*molality[k]*m_Psi_ijk_P[n];
3899  }
3900  }
3901  }
3902  }
3903  double sum2 = 3.0 * molality[i] * molality[i] * m_Mu_nnn_P[i];
3904  m_dlnActCoeffMolaldP_Unscaled[i] = sum1 + sum2 + sum3;
3905  }
3906  }
3907 
3908  // ------ SUBSECTION FOR CALCULATING THE d OSMOTIC COEFF dP ---------
3909  double sum1 = 0.0;
3910  double sum2 = 0.0;
3911  double sum3 = 0.0;
3912  double sum4 = 0.0;
3913  double sum5 = 0.0;
3914  double sum6 = 0.0;
3915  double sum7 = 0.0;
3916 
3917  // term1 is the temperature derivative of the DH term in the osmotic
3918  // coefficient expression
3919  // b = 1.2 sqrt(kg/gmol) <- arbitrarily set in all Pitzer implementations.
3920  // Is = Ionic strength on the molality scale (units of (gmol/kg))
3921  // Aphi = A_Debye / 3 (units of sqrt(kg/gmol))
3922  double term1 = -dAphidP * Is * sqrt(Is) / (1.0 + 1.2 * sqrt(Is));
3923 
3924  for (size_t j = 1; j < m_kk; j++) {
3925  // Loop Over Cations
3926  if (charge(j) > 0.0) {
3927  for (size_t k = 1; k < m_kk; k++) {
3928  if (charge(k) < 0.0) {
3929  // Find the counterIJ for the symmetric j,k binary interaction
3930  size_t n = m_kk*j + k;
3931  size_t counterIJ = m_CounterIJ[n];
3932  sum1 += molality[j]*molality[k]*
3933  (m_BphiMX_IJ_P[counterIJ] + molarcharge*m_CMX_IJ_P[counterIJ]);
3934  }
3935  }
3936 
3937  for (size_t k = j+1; k < m_kk; k++) {
3938  if (j == (m_kk-1)) {
3939  // we should never reach this step
3940  throw CanteraError("HMWSoln::s_updatePitzer_dlnMolalityActCoeff_dP",
3941  "logic error 1 in Step 9 of hmw_act");
3942  }
3943  if (charge(k) > 0.0) {
3944  // Find the counterIJ for the symmetric j,k binary interaction
3945  // between 2 cations.
3946  size_t n = m_kk*j + k;
3947  size_t counterIJ = m_CounterIJ[n];
3948  sum2 += molality[j]*molality[k]*m_PhiPhi_IJ_P[counterIJ];
3949  for (size_t m = 1; m < m_kk; m++) {
3950  if (charge(m) < 0.0) {
3951  // species m is an anion
3952  n = m + k * m_kk + j * m_kk * m_kk;
3953  sum2 += molality[j]*molality[k]*molality[m]*m_Psi_ijk_P[n];
3954  }
3955  }
3956  }
3957  }
3958  }
3959 
3960  // Loop Over Anions
3961  if (charge(j) < 0) {
3962  for (size_t k = j+1; k < m_kk; k++) {
3963  if (j == m_kk-1) {
3964  // we should never reach this step
3965  throw CanteraError("HMWSoln::s_updatePitzer_dlnMolalityActCoeff_dP",
3966  "logic error 2 in Step 9 of hmw_act");
3967  }
3968  if (charge(k) < 0) {
3969  // Find the counterIJ for the symmetric j,k binary interaction
3970  // between two anions
3971  size_t n = m_kk*j + k;
3972  size_t counterIJ = m_CounterIJ[n];
3973 
3974  sum3 += molality[j]*molality[k]*m_PhiPhi_IJ_P[counterIJ];
3975  for (size_t m = 1; m < m_kk; m++) {
3976  if (charge(m) > 0.0) {
3977  n = m + k * m_kk + j * m_kk * m_kk;
3978  sum3 += molality[j]*molality[k]*molality[m]*m_Psi_ijk_P[n];
3979  }
3980  }
3981  }
3982  }
3983  }
3984 
3985  // Loop Over Neutral Species
3986  if (charge(j) == 0) {
3987  for (size_t k = 1; k < m_kk; k++) {
3988  if (charge(k) < 0.0) {
3989  sum4 += molality[j]*molality[k]*m_Lambda_nj_P(j,k);
3990  }
3991  if (charge(k) > 0.0) {
3992  sum5 += molality[j]*molality[k]*m_Lambda_nj_P(j,k);
3993  }
3994  if (charge(k) == 0.0) {
3995  if (k > j) {
3996  sum6 += molality[j]*molality[k]*m_Lambda_nj_P(j,k);
3997  } else if (k == j) {
3998  sum6 += 0.5 * molality[j]*molality[k]*m_Lambda_nj_P(j,k);
3999  }
4000  }
4001  if (charge(k) < 0.0) {
4002  size_t izeta = j;
4003  for (size_t m = 1; m < m_kk; m++) {
4004  if (charge(m) > 0.0) {
4005  size_t jzeta = m;
4006  size_t n = k + jzeta * m_kk + izeta * m_kk * m_kk;
4007  double zeta_P = m_Psi_ijk_P[n];
4008  if (zeta_P != 0.0) {
4009  sum7 += molality[izeta]*molality[jzeta]*molality[k]*zeta_P;
4010  }
4011  }
4012  }
4013  }
4014  }
4015 
4016  sum7 += molality[j] * molality[j] * molality[j] * m_Mu_nnn_P[j];
4017  }
4018  }
4019  double sum_m_phi_minus_1 = 2.0 *
4020  (term1 + sum1 + sum2 + sum3 + sum4 + sum5 + sum6 + sum7);
4021 
4022  // Calculate the osmotic coefficient from
4023  // osmotic_coeff = 1 + dGex/d(M0noRT) / sum(molality_i)
4024  double d_osmotic_coef_dP;
4025  if (molalitysum > 1.0E-150) {
4026  d_osmotic_coef_dP = 0.0 + (sum_m_phi_minus_1 / molalitysum);
4027  } else {
4028  d_osmotic_coef_dP = 0.0;
4029  }
4030  double d_lnwateract_dP = -(m_weightSolvent/1000.0) * molalitysum * d_osmotic_coef_dP;
4031 
4032  // In Cantera, we define the activity coefficient of the solvent as
4033  //
4034  // act_0 = actcoeff_0 * Xmol_0
4035  //
4036  // We have just computed act_0. However, this routine returns
4037  // ln(actcoeff[]). Therefore, we must calculate ln(actcoeff_0).
4038  m_dlnActCoeffMolaldP_Unscaled[0] = d_lnwateract_dP;
4039 }
4040 
4041 void HMWSoln::calc_lambdas(double is) const
4042 {
4043  if( m_last_is == is ) {
4044  return;
4045  }
4046  m_last_is = is;
4047 
4048  // Coefficients c1-c4 are used to approximate the integral function "J";
4049  // aphi is the Debye-Huckel constant at 25 C
4050  double c1 = 4.581, c2 = 0.7237, c3 = 0.0120, c4 = 0.528;
4051  double aphi = 0.392; /* Value at 25 C */
4052  if (is < 1.0E-150) {
4053  for (int i = 0; i < 17; i++) {
4054  elambda[i] = 0.0;
4055  elambda1[i] = 0.0;
4056  }
4057  return;
4058  }
4059 
4060  // Calculate E-lambda terms for charge combinations of like sign,
4061  // using method of Pitzer (1975). Charges up to 4 are calculated.
4062  for (int i=1; i<=4; i++) {
4063  for (int j=i; j<=4; j++) {
4064  int ij = i*j;
4065 
4066  // calculate the product of the charges
4067  double zprod = (double)ij;
4068 
4069  // calculate Xmn (A1) from Harvie, Weare (1980).
4070  double x = 6.0* zprod * aphi * sqrt(is); // eqn 23
4071 
4072  double jfunc = x / (4.0 + c1*pow(x,-c2)*exp(-c3*pow(x,c4))); // eqn 47
4073 
4074  double t = c3 * c4 * pow(x,c4);
4075  double dj = c1* pow(x,(-c2-1.0)) * (c2+t) * exp(-c3*pow(x,c4));
4076  double jprime = (jfunc/x)*(1.0 + jfunc*dj);
4077 
4078  elambda[ij] = zprod*jfunc / (4.0*is); // eqn 14
4079  elambda1[ij] = (3.0*zprod*zprod*aphi*jprime/(4.0*sqrt(is))
4080  - elambda[ij])/is;
4081  }
4082  }
4083 }
4084 
4085 void HMWSoln::calc_thetas(int z1, int z2,
4086  double* etheta, double* etheta_prime) const
4087 {
4088  // Calculate E-theta(i) and E-theta'(I) using method of Pitzer (1987)
4089  int i = abs(z1);
4090  int j = abs(z2);
4091 
4092  AssertThrowMsg(i <= 4 && j <= 4, "HMWSoln::calc_thetas",
4093  "we shouldn't be here");
4094  AssertThrowMsg(i != 0 && j != 0, "HMWSoln::calc_thetas",
4095  "called with one species being neutral");
4096 
4097  // Check to see if the charges are of opposite sign. If they are of opposite
4098  // sign then their etheta interaction is zero.
4099  if (z1*z2 < 0) {
4100  *etheta = 0.0;
4101  *etheta_prime = 0.0;
4102  } else {
4103  // Actually calculate the interaction.
4104  double f1 = (double)i / (2.0 * j);
4105  double f2 = (double)j / (2.0 * i);
4106  *etheta = elambda[i*j] - f1*elambda[j*j] - f2*elambda[i*i];
4107  *etheta_prime = elambda1[i*j] - f1*elambda1[j*j] - f2*elambda1[i*i];
4108  }
4109 }
4110 
4112 {
4113  // Calculate the molalities. Currently, the molalities may not be current
4114  // with respect to the contents of the State objects' data.
4115  calcMolalities();
4116  double xmolSolvent = moleFraction(0);
4117  double xx = std::max(m_xmolSolventMIN, xmolSolvent);
4118  // Exponentials - trial 2
4119  if (xmolSolvent > IMS_X_o_cutoff_) {
4120  for (size_t k = 1; k < m_kk; k++) {
4121  IMS_lnActCoeffMolal_[k]= 0.0;
4122  }
4123  IMS_lnActCoeffMolal_[0] = - log(xx) + (xx - 1.0)/xx;
4124  return;
4125  } else {
4126  double xoverc = xmolSolvent/IMS_cCut_;
4127  double eterm = std::exp(-xoverc);
4128 
4129  double fptmp = IMS_bfCut_ - IMS_afCut_ / IMS_cCut_ - IMS_bfCut_*xoverc
4130  + 2.0*IMS_dfCut_*xmolSolvent - IMS_dfCut_*xmolSolvent*xoverc;
4131  double f_prime = 1.0 + eterm*fptmp;
4132  double f = xmolSolvent + IMS_efCut_
4133  + eterm * (IMS_afCut_ + xmolSolvent * (IMS_bfCut_ + IMS_dfCut_*xmolSolvent));
4134 
4135  double gptmp = IMS_bgCut_ - IMS_agCut_ / IMS_cCut_ - IMS_bgCut_*xoverc
4136  + 2.0*IMS_dgCut_*xmolSolvent - IMS_dgCut_*xmolSolvent*xoverc;
4137  double g_prime = 1.0 + eterm*gptmp;
4138  double g = xmolSolvent + IMS_egCut_
4139  + eterm * (IMS_agCut_ + xmolSolvent * (IMS_bgCut_ + IMS_dgCut_*xmolSolvent));
4140 
4141  double tmp = (xmolSolvent / g * g_prime + (1.0 - xmolSolvent) / f * f_prime);
4142  double lngammak = -1.0 - log(f) + tmp * xmolSolvent;
4143  double lngammao =-log(g) - tmp * (1.0-xmolSolvent);
4144 
4145  tmp = log(xx) + lngammak;
4146  for (size_t k = 1; k < m_kk; k++) {
4147  IMS_lnActCoeffMolal_[k]= tmp;
4148  }
4149  IMS_lnActCoeffMolal_[0] = lngammao;
4150  }
4151  return;
4152 }
4153 
4155 {
4156  calcMolalities();
4157  vector_fp& moleF = m_tmpV;
4158 
4159  // Update the coefficients wrt Temperature. Calculate the derivatives as well
4161  getMoleFractions(moleF.data());
4162 
4163  writelog("Index Name MoleF MolalityCropped Charge\n");
4164  for (size_t k = 0; k < m_kk; k++) {
4165  writelogf("%2d %-16s %14.7le %14.7le %5.1f \n",
4166  k, speciesName(k), moleF[k], m_molalitiesCropped[k], charge(k));
4167  }
4168 
4169  writelog("\n Species Species beta0MX "
4170  "beta1MX beta2MX CphiMX alphaMX thetaij\n");
4171  for (size_t i = 1; i < m_kk - 1; i++) {
4172  for (size_t j = i+1; j < m_kk; j++) {
4173  size_t n = i * m_kk + j;
4174  size_t ct = m_CounterIJ[n];
4175  writelogf(" %-16s %-16s %9.5f %9.5f %9.5f %9.5f %9.5f %9.5f \n",
4176  speciesName(i), speciesName(j),
4177  m_Beta0MX_ij[ct], m_Beta1MX_ij[ct],
4178  m_Beta2MX_ij[ct], m_CphiMX_ij[ct],
4179  m_Alpha1MX_ij[ct], m_Theta_ij[ct]);
4180  }
4181  }
4182 
4183  writelog("\n Species Species Species psi \n");
4184  for (size_t i = 1; i < m_kk; i++) {
4185  for (size_t j = 1; j < m_kk; j++) {
4186  for (size_t k = 1; k < m_kk; k++) {
4187  size_t n = k + j * m_kk + i * m_kk * m_kk;
4188  if (m_Psi_ijk[n] != 0.0) {
4189  writelogf(" %-16s %-16s %-16s %9.5f \n",
4190  speciesName(i), speciesName(j),
4191  speciesName(k), m_Psi_ijk[n]);
4192  }
4193  }
4194  }
4195  }
4196 }
4197 
4198 void HMWSoln::applyphScale(doublereal* acMolality) const
4199 {
4201  return;
4202  }
4204  doublereal lnGammaClMs2 = s_NBS_CLM_lnMolalityActCoeff();
4205  doublereal lnGammaCLMs1 = m_lnActCoeffMolal_Unscaled[m_indexCLM];
4206  doublereal afac = -1.0 *(lnGammaClMs2 - lnGammaCLMs1);
4207  for (size_t k = 0; k < m_kk; k++) {
4208  acMolality[k] *= exp(charge(k) * afac);
4209  }
4210 }
4211 
4213 {
4216  return;
4217  }
4219  doublereal lnGammaClMs2 = s_NBS_CLM_lnMolalityActCoeff();
4220  doublereal lnGammaCLMs1 = m_lnActCoeffMolal_Unscaled[m_indexCLM];
4221  doublereal afac = -1.0 *(lnGammaClMs2 - lnGammaCLMs1);
4222  for (size_t k = 0; k < m_kk; k++) {
4224  }
4225 }
4226 
4228 {
4231  return;
4232  }
4234  doublereal dlnGammaClM_dT_s2 = s_NBS_CLM_dlnMolalityActCoeff_dT();
4235  doublereal dlnGammaCLM_dT_s1 = m_dlnActCoeffMolaldT_Unscaled[m_indexCLM];
4236  doublereal afac = -1.0 *(dlnGammaClM_dT_s2 - dlnGammaCLM_dT_s1);
4237  for (size_t k = 0; k < m_kk; k++) {
4239  }
4240 }
4241 
4243 {
4246  return;
4247  }
4249  doublereal d2lnGammaClM_dT2_s2 = s_NBS_CLM_d2lnMolalityActCoeff_dT2();
4250  doublereal d2lnGammaCLM_dT2_s1 = m_d2lnActCoeffMolaldT2_Unscaled[m_indexCLM];
4251  doublereal afac = -1.0 *(d2lnGammaClM_dT2_s2 - d2lnGammaCLM_dT2_s1);
4252  for (size_t k = 0; k < m_kk; k++) {
4254  }
4255 }
4256 
4258 {
4261  return;
4262  }
4264  doublereal dlnGammaClM_dP_s2 = s_NBS_CLM_dlnMolalityActCoeff_dP();
4265  doublereal dlnGammaCLM_dP_s1 = m_dlnActCoeffMolaldP_Unscaled[m_indexCLM];
4266  doublereal afac = -1.0 *(dlnGammaClM_dP_s2 - dlnGammaCLM_dP_s1);
4267  for (size_t k = 0; k < m_kk; k++) {
4269  }
4270 }
4271 
4273 {
4274  doublereal sqrtIs = sqrt(m_IionicMolality);
4275  doublereal A = A_Debye_TP();
4276  doublereal lnGammaClMs2 = - A * sqrtIs /(1.0 + 1.5 * sqrtIs);
4277  return lnGammaClMs2;
4278 }
4279 
4281 {
4282  doublereal sqrtIs = sqrt(m_IionicMolality);
4283  doublereal dAdT = dA_DebyedT_TP();
4284  return - dAdT * sqrtIs /(1.0 + 1.5 * sqrtIs);
4285 }
4286 
4288 {
4289  doublereal sqrtIs = sqrt(m_IionicMolality);
4290  doublereal d2AdT2 = d2A_DebyedT2_TP();
4291  return - d2AdT2 * sqrtIs /(1.0 + 1.5 * sqrtIs);
4292 }
4293 
4295 {
4296  doublereal sqrtIs = sqrt(m_IionicMolality);
4297  doublereal dAdP = dA_DebyedP_TP();
4298  return - dAdP * sqrtIs /(1.0 + 1.5 * sqrtIs);
4299 }
4300 
4301 }
Headers for the HMWSoln ThermoPhase object, which models concentrated electrolyte solutions (see Ther...
Implementation of a pressure dependent standard state virtual function for a Pure Water Phase (see Sp...
Headers for the factory class that can create known ThermoPhase objects (see Thermodynamic Properties...
A map of string keys to values whose type can vary at runtime.
Definition: AnyMap.h:360
bool hasKey(const std::string &key) const
Returns true if the map contains an item named key.
Definition: AnyMap.cpp:984
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:112
doublereal * ptrColumn(size_t j)
Return a pointer to the top of column j, columns are contiguous in memory.
Definition: Array.h:292
Base class for exceptions thrown by Cantera classes.
Definition: ctexceptions.h:61
vector_fp m_CMX_IJ_P
Derivative of m_CMX_IJ wrt P. Vector index is counterIJ.
Definition: HMWSoln.h:2228
void calcIMSCutoffParams_()
Precalculate the IMS Cutoff parameters for typeCutoff = 2.
Definition: HMWSoln.cpp:1664
Array2D m_Theta_ij_coeff
Array of coefficients for Theta_ij[i][j] in the Pitzer/HMW formulation.
Definition: HMWSoln.h:1949
vector_fp m_BphiMX_IJ_P
Derivative of BphiMX_IJ wrt P. Vector index is counterIJ.
Definition: HMWSoln.h:2185
doublereal MC_X_o_cutoff_
value of the solvent mole fraction that centers the cutoff polynomials for the cutoff =1 process;
Definition: HMWSoln.h:2266
virtual void applyphScale(doublereal *acMolality) const
Apply the current phScale to a set of activity Coefficients or activities.
Definition: HMWSoln.cpp:4198
Array2D m_Beta0MX_ij_coeff
Array of coefficients for Beta0, a variable in Pitzer's papers.
Definition: HMWSoln.h:1826
vector_fp m_PhiPhi_IJ
Intermediate variable called PhiPhi in Pitzer's paper.
Definition: HMWSoln.h:2206
vector_fp m_Theta_ij_L
Derivative of Theta_ij[i][j] wrt T. Vector index is counterIJ.
Definition: HMWSoln.h:1931
vector_fp m_BMX_IJ_L
Derivative of BMX_IJ wrt T. Vector index is counterIJ.
Definition: HMWSoln.h:2153
vector_fp m_BMX_IJ_P
Derivative of BMX_IJ wrt P. Vector index is counterIJ.
Definition: HMWSoln.h:2159
double m_A_Debye
A_Debye: this expression appears on the top of the ln actCoeff term in the general Debye-Huckel expre...
Definition: HMWSoln.h:1788
void readXMLLambdaNeutral(XML_Node &BinSalt)
Process an XML node called "lambdaNeutral".
Definition: HMWSoln.cpp:1574
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...
Definition: HMWSoln.cpp:1070
vector_fp m_Phi_IJ_LL
Derivative of m_Phi_IJ wrt TT. Vector index is counterIJ.
Definition: HMWSoln.h:2195
vector_fp m_Beta0MX_ij_P
Derivative of Beta0_ij[i][j] wrt P. Vector index is counterIJ.
Definition: HMWSoln.h:1818
Array2D m_Lambda_nj_P
Derivative of Lambda_nj[i][j] wrt P.
Definition: HMWSoln.h:2009
void s_updateScaling_pHScaling_dT() const
Apply the current phScale to a set of derivatives of the activity Coefficients wrt temperature.
Definition: HMWSoln.cpp:4227
double m_IionicMolality
Current value of the ionic strength on the molality scale Associated Salts, if present in the mechani...
Definition: HMWSoln.h:1732
vector_fp m_PhiPhi_IJ_L
Derivative of m_PhiPhi_IJ wrt T. Vector index is counterIJ.
Definition: HMWSoln.h:2209
void calcMCCutoffParams_()
Calculate molality cut-off parameters.
Definition: HMWSoln.cpp:1717
vector_fp m_dlnActCoeffMolaldT_Scaled
Derivative of the Logarithm of the activity coefficients on the molality scale wrt T.
Definition: HMWSoln.h:2083
Array2D m_Beta2MX_ij_coeff
Array of coefficients for Beta2, a variable in Pitzer's papers.
Definition: HMWSoln.h:1876
vector_fp m_BphiMX_IJ_LL
Derivative of BphiMX_IJ wrt TT. Vector index is counterIJ.
Definition: HMWSoln.h:2182
void s_update_d2lnMolalityActCoeff_dT2() const
This function calculates the temperature second derivative of the natural logarithm of the molality a...
Definition: HMWSoln.cpp:3021
doublereal IMS_cCut_
Parameter in the polyExp cutoff treatment having to do with rate of exp decay.
Definition: HMWSoln.h:2243
doublereal IMS_slopegCut_
Parameter in the polyExp cutoff treatment.
Definition: HMWSoln.h:2250
int m_form_A_Debye
Form of the constant outside the Debye-Huckel term called A.
Definition: HMWSoln.h:1756
void getUnscaledMolalityActivityCoefficients(doublereal *acMolality) const
Get the array of unscaled non-dimensional molality based activity coefficients at the current solutio...
Definition: HMWSoln.cpp:305
vector_int CROP_speciesCropped_
This is a boolean-type vector indicating whether a species's activity coefficient is in the cropped r...
Definition: HMWSoln.h:2289
vector_fp m_Psi_ijk_L
Derivative of Psi_ijk[n] wrt T.
Definition: HMWSoln.h:1966
virtual doublereal cp_mole() const
Molar heat capacity at constant pressure. Units: J/kmol/K.
Definition: HMWSoln.cpp:210
vector_fp m_Phiprime_IJ
Intermediate variable called Phiprime in Pitzer's paper.
Definition: HMWSoln.h:2202
doublereal s_NBS_CLM_dlnMolalityActCoeff_dP() const
Calculate the pressure derivative of the Chlorine activity coefficient.
Definition: HMWSoln.cpp:4294
virtual void getPartialMolarEnthalpies(doublereal *hbar) const
Returns an array of partial molar enthalpies for the species in the mixture.
Definition: HMWSoln.cpp:338
virtual void getPartialMolarEntropies(doublereal *sbar) const
Returns an array of partial molar entropies of the species in the solution.
Definition: HMWSoln.cpp:357
vector_fp m_Beta1MX_ij
Array of 2D data used in the Pitzer/HMW formulation.
Definition: HMWSoln.h:1833
vector_fp m_lnActCoeffMolal_Unscaled
Logarithm of the activity coefficients on the molality scale.
Definition: HMWSoln.h:2079
Array2D m_Lambda_nj_L
Derivative of Lambda_nj[i][j] wrt T. see m_Lambda_ij.
Definition: HMWSoln.h:2003
virtual void initThermoXML(XML_Node &phaseNode, const std::string &id)
Initialize the phase parameters from an XML file.
Definition: HMWSoln.cpp:849
vector_fp m_BprimeMX_IJ_P
Derivative of BprimeMX wrt P. Vector index is counterIJ.
Definition: HMWSoln.h:2172
virtual doublereal enthalpy_mole() const
Molar enthalpy. Units: J/kmol.
Definition: HMWSoln.cpp:140
vector_fp m_h2func_IJ
hfunc2, was called gprime in Pitzer's paper.
Definition: HMWSoln.h:2146
vector_fp m_Alpha2MX_ij
Array of 2D data used in the Pitzer/HMW formulation.
Definition: HMWSoln.h:1891
doublereal IMS_X_o_cutoff_
value of the solute mole fraction that centers the cutoff polynomials for the cutoff =1 process;
Definition: HMWSoln.h:2240
vector_fp m_Psi_ijk_P
Derivative of Psi_ijk[n] wrt P.
Definition: HMWSoln.h:1974
virtual void getPartialMolarVolumes(doublereal *vbar) const
Return an array of partial molar volumes for the species in the mixture.
Definition: HMWSoln.cpp:392
vector_fp m_dlnActCoeffMolaldT_Unscaled
Derivative of the Logarithm of the activity coefficients on the molality scale wrt T.
Definition: HMWSoln.h:2087
doublereal s_NBS_CLM_dlnMolalityActCoeff_dT() const
Calculate the temperature derivative of the Chlorine activity coefficient on the NBS scale.
Definition: HMWSoln.cpp:4280
vector_fp m_PhiPhi_IJ_LL
Derivative of m_PhiPhi_IJ wrt TT. Vector index is counterIJ.
Definition: HMWSoln.h:2212
vector_fp m_CMX_IJ_L
Derivative of m_CMX_IJ wrt T. Vector index is counterIJ.
Definition: HMWSoln.h:2222
void s_updateScaling_pHScaling_dP() const
Apply the current phScale to a set of derivatives of the activity Coefficients wrt pressure.
Definition: HMWSoln.cpp:4257
void printCoeffs() const
Print out all of the input Pitzer coefficients.
Definition: HMWSoln.cpp:4154
virtual doublereal cv_mole() const
Molar heat capacity at constant volume. Units: J/kmol/K.
Definition: HMWSoln.cpp:216
virtual void getActivityConcentrations(doublereal *c) const
This method returns an array of generalized activity concentrations.
Definition: HMWSoln.cpp:266
vector_fp m_BphiMX_IJ
Intermediate variable called BphiMX in Pitzer's paper.
Definition: HMWSoln.h:2176
void s_update_dlnMolalityActCoeff_dT() const
This function calculates the temperature derivative of the natural logarithm of the molality activity...
Definition: HMWSoln.cpp:2502
vector_fp m_CphiMX_ij
Array of 2D data used in the Pitzer/HMW formulation.
Definition: HMWSoln.h:1898
vector_fp m_CphiMX_ij_L
Derivative of Cphi_ij[i][j] wrt T. Vector index is counterIJ.
Definition: HMWSoln.h:1901
double ADebye_V(double temperature=-1.0, double pressure=-1.0) const
Return Pitzer's definition of A_V.
Definition: HMWSoln.cpp:1047
vector_fp m_Beta0MX_ij_L
Derivative of Beta0_ij[i][j] wrt T. Vector index is counterIJ.
Definition: HMWSoln.h:1812
virtual void getPartialMolarCp(doublereal *cpbar) const
Return an array of partial molar heat capacities for the species in the mixture.
Definition: HMWSoln.cpp:405
vector_fp m_Phi_IJ_P
Derivative of m_Phi_IJ wrt P. Vector index is counterIJ.
Definition: HMWSoln.h:2198
void counterIJ_setup() const
Set up a counter variable for keeping track of symmetric binary interactions amongst the solute speci...
Definition: HMWSoln.cpp:1416
vector_fp m_hfunc_IJ
hfunc, was called gprime in Pitzer's paper.
Definition: HMWSoln.h:2142
vector_fp m_Theta_ij_P
Derivative of Theta_ij[i][j] wrt P. Vector index is counterIJ.
Definition: HMWSoln.h:1937
vector_fp m_Beta1MX_ij_LL
Derivative of Beta1_ij[i][j] wrt TT. Vector index is counterIJ.
Definition: HMWSoln.h:1839
vector_fp m_Mu_nnn
Mu coefficient for the self-ternary neutral coefficient.
Definition: HMWSoln.h:2032
vector_fp m_d2lnActCoeffMolaldT2_Scaled
Derivative of the Logarithm of the activity coefficients on the molality scale wrt TT.
Definition: HMWSoln.h:2091
Array2D m_Psi_ijk_coeff
Array of coefficients for Psi_ijk[n] in the Pitzer/HMW formulation.
Definition: HMWSoln.h:1990
virtual void setMolarDensity(const doublereal conc)
Set the internally stored molar density (kmol/m^3) for the phase.
Definition: HMWSoln.cpp:255
HMWSoln()
Default Constructor.
Definition: HMWSoln.cpp:30
void readXMLZetaCation(const XML_Node &BinSalt)
Process an XML node called "zetaCation".
Definition: HMWSoln.cpp:1629
vector_fp m_CphiMX_ij_LL
Derivative of Cphi_ij[i][j] wrt TT. Vector index is counterIJ.
Definition: HMWSoln.h:1904
vector_fp m_Beta2MX_ij_L
Derivative of Beta2_ij[i][j] wrt T. Vector index is counterIJ.
Definition: HMWSoln.h:1860
void readXMLPsi(XML_Node &BinSalt)
Process an XML node called "psiCommonAnion" or "psiCommonCation".
Definition: HMWSoln.cpp:1525
void setA_Debye(double A)
Set the A_Debye parameter.
Definition: HMWSoln.cpp:647
int m_formPitzerTemp
This is the form of the temperature dependence of Pitzer parameterization used in the model.
Definition: HMWSoln.h:1727
virtual void setDensity(const doublereal rho)
Set the internally stored density (kg/m^3) of the phase.
Definition: HMWSoln.cpp:243
virtual doublereal entropy_mole() const
Molar entropy. Units: J/kmol/K.
Definition: HMWSoln.cpp:198
vector_fp m_Mu_nnn_LL
Mu coefficient 2nd temperature derivative for the self-ternary neutral coefficient.
Definition: HMWSoln.h:2052
vector_fp m_BMX_IJ_LL
Derivative of BMX_IJ wrt TT. Vector index is counterIJ.
Definition: HMWSoln.h:2156
void s_updatePitzer_CoeffWRTemp(int doDerivs=2) const
Calculates the Pitzer coefficients' dependence on the temperature.
Definition: HMWSoln.cpp:1753
vector_fp m_molalitiesCropped
Cropped and modified values of the molalities used in activity coefficient calculations.
Definition: HMWSoln.h:2109
vector_fp m_Psi_ijk_LL
Derivative of Psi_ijk[n] wrt TT.
Definition: HMWSoln.h:1970
doublereal s_NBS_CLM_d2lnMolalityActCoeff_dT2() const
Calculate the second temperature derivative of the Chlorine activity coefficient on the NBS scale.
Definition: HMWSoln.cpp:4287
virtual void initThermo()
Definition: HMWSoln.cpp:684
void s_updatePitzer_dlnMolalityActCoeff_dP() const
Calculates the Pressure derivative of the natural logarithm of the molality activity coefficients.
Definition: HMWSoln.cpp:3556
vector_fp m_Beta2MX_ij_P
Derivative of Beta2_ij[i][j] wrt P. Vector index is counterIJ.
Definition: HMWSoln.h:1866
Array2D m_Lambda_nj
Lambda coefficient for the ij interaction.
Definition: HMWSoln.h:2000
vector_fp m_Phi_IJ
Intermediate variable called Phi in Pitzer's paper.
Definition: HMWSoln.h:2189
vector_fp m_Beta2MX_ij
Array of 2D data used in the Pitzer/HMW formulation.
Definition: HMWSoln.h:1857
vector_fp m_BprimeMX_IJ_L
Derivative of BprimeMX wrt T. Vector index is counterIJ.
Definition: HMWSoln.h:2166
vector_fp m_PhiPhi_IJ_P
Derivative of m_PhiPhi_IJ wrt P. Vector index is counterIJ.
Definition: HMWSoln.h:2215
void calc_thetas(int z1, int z2, double *etheta, double *etheta_prime) const
Calculate etheta and etheta_prime.
Definition: HMWSoln.cpp:4085
vector_fp m_Beta2MX_ij_LL
Derivative of Beta2_ij[i][j] wrt TT. Vector index is counterIJ.
Definition: HMWSoln.h:1863
vector_fp m_d2lnActCoeffMolaldT2_Unscaled
Derivative of the Logarithm of the activity coefficients on the molality scale wrt TT.
Definition: HMWSoln.h:2095
Array2D m_Mu_nnn_coeff
Array of coefficients form_Mu_nnn term.
Definition: HMWSoln.h:2065
double ADebye_L(double temperature=-1.0, double pressure=-1.0) const
Return Pitzer's definition of A_L.
Definition: HMWSoln.cpp:1036
vector_fp m_Mu_nnn_P
Mu coefficient pressure derivative for the self-ternary neutral coefficient.
Definition: HMWSoln.h:2062
vector_fp m_CMX_IJ
Intermediate variable called CMX in Pitzer's paper.
Definition: HMWSoln.h:2219
PDSS * m_waterSS
Water standard state calculator.
Definition: HMWSoln.h:1794
void calc_lambdas(double is) const
Calculate the lambda interactions.
Definition: HMWSoln.cpp:4041
double elambda1[17]
This is elambda1, MEC.
Definition: HMWSoln.h:2126
virtual doublereal relative_molal_enthalpy() const
Excess molar enthalpy of the solution from the mixing process on a molality basis.
Definition: HMWSoln.cpp:158
vector_fp m_Phi_IJ_L
Derivative of m_Phi_IJ wrt T. Vector index is counterIJ.
Definition: HMWSoln.h:2192
vector_fp m_Theta_ij_LL
Derivative of Theta_ij[i][j] wrt TT. Vector index is counterIJ.
Definition: HMWSoln.h:1934
double m_maxIionicStrength
Maximum value of the ionic strength allowed in the calculation of the activity coefficients.
Definition: HMWSoln.h:1736
vector_fp m_BphiMX_IJ_L
Derivative of BphiMX_IJ wrt T. Vector index is counterIJ.
Definition: HMWSoln.h:2179
virtual doublereal gibbs_mole() const
Molar Gibbs function. Units: J/kmol.
Definition: HMWSoln.cpp:204
vector_fp m_dlnActCoeffMolaldP_Unscaled
Derivative of the Logarithm of the activity coefficients on the molality scale wrt P.
Definition: HMWSoln.h:2103
vector_fp m_BprimeMX_IJ_LL
Derivative of BprimeMX wrt TT. Vector index is counterIJ.
Definition: HMWSoln.h:2169
vector_fp IMS_lnActCoeffMolal_
Logarithm of the molal activity coefficients.
Definition: HMWSoln.h:2236
void s_updatePitzer_dlnMolalityActCoeff_dT() const
Calculates the temperature derivative of the natural logarithm of the molality activity coefficients.
Definition: HMWSoln.cpp:2530
void s_updatePitzer_lnMolalityActCoeff() const
Calculate the Pitzer portion of the activity coefficients.
Definition: HMWSoln.cpp:1999
vector_fp m_CMX_IJ_LL
Derivative of m_CMX_IJ wrt TT. Vector index is counterIJ.
Definition: HMWSoln.h:2225
vector_fp m_gamma_tmp
Intermediate storage of the activity coefficient itself.
Definition: HMWSoln.h:2232
vector_fp m_Psi_ijk
Array of 3D data used in the Pitzer/HMW formulation.
Definition: HMWSoln.h:1962
void readXMLMunnnNeutral(XML_Node &BinSalt)
Process an XML node called "MunnnNeutral".
Definition: HMWSoln.cpp:1604
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 as a function of tem...
Definition: HMWSoln.cpp:980
virtual doublereal relative_enthalpy() const
Excess molar enthalpy of the solution from the mixing process.
Definition: HMWSoln.cpp:146
vector_fp m_Beta1MX_ij_P
Derivative of Beta1_ij[i][j] wrt P. Vector index is counterIJ.
Definition: HMWSoln.h:1842
vector_fp m_Mu_nnn_L
Mu coefficient temperature derivative for the self-ternary neutral coefficient.
Definition: HMWSoln.h:2042
Array2D m_Lambda_nj_LL
Derivative of Lambda_nj[i][j] wrt TT.
Definition: HMWSoln.h:2006
void s_updatePitzer_d2lnMolalityActCoeff_dT2() const
This function calculates the temperature second derivative of the natural logarithm of the molality a...
Definition: HMWSoln.cpp:3049
void readXMLTheta(XML_Node &BinSalt)
Process an XML node called "thetaAnion" or "thetaCation".
Definition: HMWSoln.cpp:1485
Array2D m_Lambda_nj_coeff
Array of coefficients for Lambda_nj[i][j] in the Pitzer/HMW formulation.
Definition: HMWSoln.h:2024
vector_fp m_Theta_ij
Array of 2D data for Theta_ij[i][j] in the Pitzer/HMW formulation.
Definition: HMWSoln.h:1928
void initLengths()
Initialize all of the species-dependent lengths in the object.
Definition: HMWSoln.cpp:1098
vector_fp m_Beta0MX_ij
Array of 2D data used in the Pitzer/HMW formulation.
Definition: HMWSoln.h:1809
vector_fp m_BMX_IJ
Intermediate variable called BMX in Pitzer's paper.
Definition: HMWSoln.h:2150
vector_fp m_Beta0MX_ij_LL
Derivative of Beta0_ij[i][j] wrt TT. Vector index is counterIJ.
Definition: HMWSoln.h:1815
vector_fp m_gfunc_IJ
Various temporary arrays used in the calculation of the Pitzer activity coefficients.
Definition: HMWSoln.h:2135
void s_update_dlnMolalityActCoeff_dP() const
This function calculates the pressure derivative of the natural logarithm of the molality activity co...
Definition: HMWSoln.cpp:3532
double m_TempPitzerRef
Reference Temperature for the Pitzer formulations.
Definition: HMWSoln.h:1739
virtual void getChemPotentials(doublereal *mu) const
Get the species chemical potentials. Units: J/kmol.
Definition: HMWSoln.cpp:318
vector_fp m_Beta1MX_ij_L
Derivative of Beta1_ij[i][j] wrt T. Vector index is counterIJ.
Definition: HMWSoln.h:1836
void readXMLBinarySalt(XML_Node &BinSalt)
Process an XML node called "binarySaltParameters".
Definition: HMWSoln.cpp:1439
virtual doublereal satPressure(doublereal T)
Get the saturation pressure for a given temperature.
Definition: HMWSoln.cpp:425
double elambda[17]
This is elambda, MEC.
Definition: HMWSoln.h:2123
std::unique_ptr< WaterProps > m_waterProps
Pointer to the water property calculator.
Definition: HMWSoln.h:1797
vector_fp m_BprimeMX_IJ
Intermediate variable called BprimeMX in Pitzer's paper.
Definition: HMWSoln.h:2163
void s_updateIMS_lnMolalityActCoeff() const
This function will be called to update the internally stored natural logarithm of the molality activi...
Definition: HMWSoln.cpp:4111
void calcMolalitiesCropped() const
Calculate the cropped molalities.
Definition: HMWSoln.cpp:1275
virtual void getActivities(doublereal *ac) const
Get the array of non-dimensional activities at the current solution temperature, pressure,...
Definition: HMWSoln.cpp:289
vector_fp m_CphiMX_ij_P
Derivative of Cphi_ij[i][j] wrt P. Vector index is counterIJ.
Definition: HMWSoln.h:1907
vector_fp m_tmpV
vector of size m_kk, used as a temporary holding area.
Definition: HMWSoln.h:1800
double ADebye_J(double temperature=-1.0, double pressure=-1.0) const
Return Pitzer's definition of A_J.
Definition: HMWSoln.cpp:1058
void s_updateScaling_pHScaling_dT2() const
Apply the current phScale to a set of 2nd derivatives of the activity Coefficients wrt temperature.
Definition: HMWSoln.cpp:4242
Array2D m_Beta1MX_ij_coeff
Array of coefficients for Beta1, a variable in Pitzer's papers.
Definition: HMWSoln.h:1850
doublereal s_NBS_CLM_lnMolalityActCoeff() const
Calculate the Chlorine activity coefficient on the NBS scale.
Definition: HMWSoln.cpp:4272
void s_updateScaling_pHScaling() const
Apply the current phScale to a set of activity Coefficients.
Definition: HMWSoln.cpp:4212
Array2D m_CphiMX_ij_coeff
Array of coefficients for CphiMX, a parameter in the activity coefficient formulation.
Definition: HMWSoln.h:1916
vector_fp m_g2func_IJ
This is the value of g2(x2) in Pitzer's papers. Vector index is counterIJ.
Definition: HMWSoln.h:2138
virtual double A_Debye_TP(double temperature=-1.0, double pressure=-1.0) const
Value of the Debye Huckel constant as a function of temperature and pressure.
Definition: HMWSoln.cpp:948
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...
Definition: HMWSoln.cpp:1004
void calcDensity()
Calculate the density of the mixture using the partial molar volumes and mole fractions as input.
Definition: HMWSoln.cpp:228
vector_fp m_dlnActCoeffMolaldP_Scaled
Derivative of the Logarithm of the activity coefficients on the molality scale wrt P.
Definition: HMWSoln.h:2099
vector_int m_CounterIJ
a counter variable for keeping track of symmetric binary interactions amongst the solute species.
Definition: HMWSoln.h:2120
vector_fp m_lnActCoeffMolal_Scaled
Logarithm of the activity coefficients on the molality scale.
Definition: HMWSoln.h:2072
bool m_molalitiesAreCropped
Boolean indicating whether the molalities are cropped or are modified.
Definition: HMWSoln.h:2112
virtual doublereal standardConcentration(size_t k=0) const
Return the standard concentration for the kth species.
Definition: HMWSoln.cpp:279
Error thrown for problems processing information contained in an AnyMap or AnyValue.
Definition: AnyMap.h:539
size_t m_indexCLM
Index of the phScale species.
void setMoleFSolventMin(doublereal xmolSolventMIN)
Sets the minimum mole fraction in the molality formulation.
int m_pHScalingType
Scaling to be used for output of single-ion species activity coefficients.
doublereal m_Mnaught
This is the multiplication factor that goes inside log expressions involving the molalities of specie...
void calcMolalities() const
Calculates the molality of all species and stores the result internally.
vector_fp m_molalities
Current value of the molalities of the species in the phase.
doublereal m_weightSolvent
Molecular weight of the Solvent.
Class for the liquid water pressure dependent standard state.
Definition: PDSS_Water.h:50
virtual void setState_TP(doublereal temp, doublereal pres)
Set the internal temperature and pressure.
Definition: PDSS.cpp:181
virtual doublereal satPressure(doublereal T)
saturation pressure
Definition: PDSS.cpp:191
void assignDensity(const double density_)
Set the internally stored constant density (kg/m^3) of the phase.
Definition: Phase.cpp:727
virtual void setMoleFractions(const double *const x)
Set the mole fractions to the specified values.
Definition: Phase.cpp:368
doublereal mean_X(const doublereal *const Q) const
Evaluate the mole-fraction-weighted mean of an array Q.
Definition: Phase.cpp:746
ValueCache m_cache
Cached for saved calculations within each ThermoPhase.
Definition: Phase.h:922
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:643
size_t m_kk
Number of species in the phase.
Definition: Phase.h:942
std::string speciesName(size_t k) const
Name of the species with index k.
Definition: Phase.cpp:229
doublereal meanMolecularWeight() const
The mean molecular weight. Units: (kg/kmol)
Definition: Phase.h:748
void getMoleFractions(double *const x) const
Get the species mole fraction vector.
Definition: Phase.cpp:572
double moleFraction(size_t k) const
Return the mole fraction of a single species.
Definition: Phase.cpp:577
virtual double density() const
Density (kg/m^3).
Definition: Phase.h:685
int stateMFNumber() const
Return the State Mole Fraction Number.
Definition: Phase.h:856
doublereal temperature() const
Temperature (K).
Definition: Phase.h:667
size_t speciesIndex(const std::string &name) const
Returns the index of a species named 'name' within the Phase object.
Definition: Phase.cpp:201
double molarVolume() const
Molar volume (m^3/kmol).
Definition: Phase.cpp:711
shared_ptr< Species > species(const std::string &name) const
Return the Species object for the named species.
Definition: Phase.cpp:980
doublereal RT() const
Return the Gas Constant multiplied by the current temperature.
Definition: ThermoPhase.h:776
virtual doublereal isothermalCompressibility() const
Returns the isothermal compressibility. Units: 1/Pa.
Definition: ThermoPhase.h:278
virtual void initThermoFile(const std::string &inputFile, const std::string &id)
virtual void initThermoXML(XML_Node &phaseNode, const std::string &id)
Import and initialize a ThermoPhase object using an XML tree.
virtual doublereal thermalExpansionCoeff() const
Return the volumetric thermal expansion coefficient. Units: 1/K.
Definition: ThermoPhase.h:289
AnyMap m_input
Data supplied via setParameters.
Definition: ThermoPhase.h:1874
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 getCp_R(doublereal *cpr) const
Get the nondimensional Heat Capacities at constant pressure for the species standard states at the cu...
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 pressure() const
Returns the current pressure of the phase.
virtual void getStandardChemPotentials(doublereal *mu) const
Get the array of chemical potentials at unit activity for the species at their standard states at the...
virtual void updateStandardStateThermo() const
Updates the standard state thermodynamic functions at the current T and P of the solution.
virtual void getEnthalpy_RT(doublereal *hrt) const
Get the nondimensional Enthalpy functions for the species at their standard states at the current T a...
CachedScalar getScalar(int id)
Get a reference to a CachedValue object representing a scalar (doublereal) with the given id.
Definition: ValueCache.h:165
int getId()
Get a unique id for a cached value.
Definition: ValueCache.cpp:21
The WaterProps class is used to house several approximation routines for properties of water.
Definition: WaterProps.h:100
Class XML_Node is a tree-based representation of the contents of an XML file.
Definition: xml.h:104
std::string attrib(const std::string &attr) const
Function returns the value of an attribute.
Definition: xml.cpp:492
std::string name() const
Returns the name of the XML node.
Definition: xml.h:372
bool hasChild(const std::string &ch) const
Tests whether the current node has a child node with a particular name.
Definition: xml.cpp:528
std::string id() const
Return the id attribute, if present.
Definition: xml.cpp:538
const std::vector< XML_Node * > & children() const
Return an unchangeable reference to the vector of children of the current node.
Definition: xml.cpp:551
XML_Node & child(const size_t n) const
Return a changeable reference to the n'th child of the current node.
Definition: xml.cpp:546
CTML ("Cantera Markup Language") is the variant of XML that Cantera uses to store data.
Header file for a common definitions used in electrolytes thermodynamics.
#define AssertTrace(expr)
Assertion must be true or an error is thrown.
Definition: ctexceptions.h:234
#define AssertThrowMsg(expr, procedure,...)
Assertion must be true or an error is thrown.
Definition: ctexceptions.h:265
int sign(T x)
Sign of a number. Returns -1 if x < 0, 1 if x > 0 and 0 if x == 0.
Definition: global.h:306
void warn_deprecated(const std::string &method, const std::string &extra)
Print a warning indicating that method is deprecated.
Definition: global.cpp:54
const size_t npos
index returned by functions to indicate "no position"
Definition: ct_defs.h:188
const double SmallNumber
smallest number to compare to zero.
Definition: ct_defs.h:149
std::vector< double > vector_fp
Turn on the use of stl vectors for the basic array type within cantera Vector of doubles.
Definition: ct_defs.h:180
const double GasConstant
Universal Gas Constant [J/kmol/K].
Definition: ct_defs.h:109
void writelog(const std::string &fmt, const Args &... args)
Write a formatted message to the screen.
Definition: global.h:158
void writelogf(const char *fmt, const Args &... args)
Write a formatted message to the screen.
Definition: global.h:179
void importPhase(XML_Node &phase, ThermoPhase *th)
Import a phase information into an empty ThermoPhase object.
Namespace for the Cantera kernel.
Definition: AnyMap.cpp:264
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:164
bool getOptionalFloat(const XML_Node &parent, const std::string &name, doublereal &fltRtn, const std::string &type)
Get an optional floating-point value from a child element.
Definition: ctml.cpp:212
bool caseInsensitiveEquals(const std::string &input, const std::string &test)
Case insensitive equality predicate.
const int PHSCALE_PITZER
Scale to be used for the output of single-ion activity coefficients is that used by Pitzer.
size_t getFloatArray(const XML_Node &node, vector_fp &v, const bool convert, const std::string &unitsString, const std::string &nodeName)
This function reads the current node or a child node of the current node with the default name,...
Definition: ctml.cpp:256
doublereal fpValueCheck(const std::string &val)
Translate a string into one doublereal value, with error checking.
const int PHSCALE_NBS
Scale to be used for evaluation of single-ion activity coefficients is that used by the NBS standard ...
Contains declarations for string manipulation functions within Cantera.
T value
The value of the cached property.
Definition: ValueCache.h:118
bool validate(double state1New)
Check whether the currently cached value is valid based on a single state variable.
Definition: ValueCache.h:44