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 }
Cantera::HMWSoln::m_PhiPhi_IJ_LL
vector_fp m_PhiPhi_IJ_LL
Derivative of m_PhiPhi_IJ wrt TT. Vector index is counterIJ.
Definition: HMWSoln.h:2212
Cantera::HMWSoln::s_updateIMS_lnMolalityActCoeff
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
Cantera::ThermoPhase::initThermoXML
virtual void initThermoXML(XML_Node &phaseNode, const std::string &id)
Import and initialize a ThermoPhase object using an XML tree.
Definition: ThermoPhase.cpp:1089
Cantera::HMWSoln::s_update_dlnMolalityActCoeff_dP
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
Cantera::getOptionalFloat
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
Cantera::HMWSoln::HMWSoln
HMWSoln()
Default Constructor.
Definition: HMWSoln.cpp:30
Cantera::VPStandardStateTP::pressure
virtual doublereal pressure() const
Returns the current pressure of the phase.
Definition: VPStandardStateTP.h:138
Cantera::HMWSoln::m_PhiPhi_IJ_P
vector_fp m_PhiPhi_IJ_P
Derivative of m_PhiPhi_IJ wrt P. Vector index is counterIJ.
Definition: HMWSoln.h:2215
Cantera::Array2D::ptrColumn
doublereal * ptrColumn(size_t j)
Return a pointer to the top of column j, columns are contiguous in memory.
Definition: Array.h:292
Cantera::HMWSoln::m_formPitzerTemp
int m_formPitzerTemp
This is the form of the temperature dependence of Pitzer parameterization used in the model.
Definition: HMWSoln.h:1727
Cantera::HMWSoln::getPartialMolarEntropies
virtual void getPartialMolarEntropies(doublereal *sbar) const
Returns an array of partial molar entropies of the species in the solution.
Definition: HMWSoln.cpp:357
Cantera::HMWSoln::calc_thetas
void calc_thetas(int z1, int z2, double *etheta, double *etheta_prime) const
Calculate etheta and etheta_prime.
Definition: HMWSoln.cpp:4085
Cantera::HMWSoln::m_Mu_nnn_L
vector_fp m_Mu_nnn_L
Mu coefficient temperature derivative for the self-ternary neutral coefficient.
Definition: HMWSoln.h:2042
Cantera::HMWSoln::m_Beta2MX_ij_P
vector_fp m_Beta2MX_ij_P
Derivative of Beta2_ij[i][j] wrt P. Vector index is counterIJ.
Definition: HMWSoln.h:1866
Cantera::HMWSoln::m_CphiMX_ij_LL
vector_fp m_CphiMX_ij_LL
Derivative of Cphi_ij[i][j] wrt TT. Vector index is counterIJ.
Definition: HMWSoln.h:1904
Cantera::HMWSoln::m_CphiMX_ij_L
vector_fp m_CphiMX_ij_L
Derivative of Cphi_ij[i][j] wrt T. Vector index is counterIJ.
Definition: HMWSoln.h:1901
Cantera::HMWSoln::m_CphiMX_ij_P
vector_fp m_CphiMX_ij_P
Derivative of Cphi_ij[i][j] wrt P. Vector index is counterIJ.
Definition: HMWSoln.h:1907
Cantera::HMWSoln::dA_DebyedP_TP
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
Cantera::HMWSoln::m_Beta1MX_ij_P
vector_fp m_Beta1MX_ij_P
Derivative of Beta1_ij[i][j] wrt P. Vector index is counterIJ.
Definition: HMWSoln.h:1842
Cantera::Phase::m_cache
ValueCache m_cache
Cached for saved calculations within each ThermoPhase.
Definition: Phase.h:922
Cantera::HMWSoln::m_d2lnActCoeffMolaldT2_Scaled
vector_fp m_d2lnActCoeffMolaldT2_Scaled
Derivative of the Logarithm of the activity coefficients on the molality scale wrt TT.
Definition: HMWSoln.h:2091
Cantera::HMWSoln::m_BMX_IJ
vector_fp m_BMX_IJ
Intermediate variable called BMX in Pitzer's paper.
Definition: HMWSoln.h:2150
Cantera::HMWSoln::m_molalitiesCropped
vector_fp m_molalitiesCropped
Cropped and modified values of the molalities used in activity coefficient calculations.
Definition: HMWSoln.h:2109
Cantera::HMWSoln::s_updateScaling_pHScaling_dT
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
Cantera::Phase::getMoleFractions
void getMoleFractions(double *const x) const
Get the species mole fraction vector.
Definition: Phase.cpp:572
Cantera::HMWSoln::m_dlnActCoeffMolaldT_Unscaled
vector_fp m_dlnActCoeffMolaldT_Unscaled
Derivative of the Logarithm of the activity coefficients on the molality scale wrt T.
Definition: HMWSoln.h:2087
Cantera::SmallNumber
const double SmallNumber
smallest number to compare to zero.
Definition: ct_defs.h:149
Cantera::HMWSoln::m_dlnActCoeffMolaldT_Scaled
vector_fp m_dlnActCoeffMolaldT_Scaled
Derivative of the Logarithm of the activity coefficients on the molality scale wrt T.
Definition: HMWSoln.h:2083
Cantera::HMWSoln::m_Psi_ijk
vector_fp m_Psi_ijk
Array of 3D data used in the Pitzer/HMW formulation.
Definition: HMWSoln.h:1962
Cantera::HMWSoln::m_hfunc_IJ
vector_fp m_hfunc_IJ
hfunc, was called gprime in Pitzer's paper.
Definition: HMWSoln.h:2142
Cantera::Phase::stateMFNumber
int stateMFNumber() const
Return the State Mole Fraction Number.
Definition: Phase.h:856
Cantera::HMWSoln::m_CMX_IJ_L
vector_fp m_CMX_IJ_L
Derivative of m_CMX_IJ wrt T. Vector index is counterIJ.
Definition: HMWSoln.h:2222
Cantera::CachedValue
Definition: ValueCache.h:32
Cantera::writelog
void writelog(const std::string &fmt, const Args &... args)
Write a formatted message to the screen.
Definition: global.h:158
Cantera::HMWSoln::m_dlnActCoeffMolaldP_Scaled
vector_fp m_dlnActCoeffMolaldP_Scaled
Derivative of the Logarithm of the activity coefficients on the molality scale wrt P.
Definition: HMWSoln.h:2099
Cantera::HMWSoln::m_Lambda_nj
Array2D m_Lambda_nj
Lambda coefficient for the ij interaction.
Definition: HMWSoln.h:2000
Cantera::MolalityVPSSTP::m_xmolSolventMIN
doublereal m_xmolSolventMIN
Definition: MolalityVPSSTP.h:596
Cantera::HMWSoln::s_update_d2lnMolalityActCoeff_dT2
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
Cantera::HMWSoln::elambda1
double elambda1[17]
This is elambda1, MEC.
Definition: HMWSoln.h:2126
AssertThrowMsg
#define AssertThrowMsg(expr, procedure,...)
Assertion must be true or an error is thrown.
Definition: ctexceptions.h:265
Cantera::HMWSoln::enthalpy_mole
virtual doublereal enthalpy_mole() const
Molar enthalpy. Units: J/kmol.
Definition: HMWSoln.cpp:140
Cantera::HMWSoln::readXMLMunnnNeutral
void readXMLMunnnNeutral(XML_Node &BinSalt)
Process an XML node called "MunnnNeutral".
Definition: HMWSoln.cpp:1604
Cantera::warn_deprecated
void warn_deprecated(const std::string &method, const std::string &extra)
Print a warning indicating that method is deprecated.
Definition: global.cpp:54
Cantera::HMWSoln::s_updateScaling_pHScaling
void s_updateScaling_pHScaling() const
Apply the current phScale to a set of activity Coefficients.
Definition: HMWSoln.cpp:4212
Cantera::HMWSoln::s_updatePitzer_dlnMolalityActCoeff_dT
void s_updatePitzer_dlnMolalityActCoeff_dT() const
Calculates the temperature derivative of the natural logarithm of the molality activity coefficients.
Definition: HMWSoln.cpp:2530
Cantera::HMWSoln::m_Beta2MX_ij_L
vector_fp m_Beta2MX_ij_L
Derivative of Beta2_ij[i][j] wrt T. Vector index is counterIJ.
Definition: HMWSoln.h:1860
Cantera::HMWSoln::m_Beta1MX_ij_LL
vector_fp m_Beta1MX_ij_LL
Derivative of Beta1_ij[i][j] wrt TT. Vector index is counterIJ.
Definition: HMWSoln.h:1839
Cantera::XML_Node::id
std::string id() const
Return the id attribute, if present.
Definition: xml.cpp:538
Cantera::Phase::meanMolecularWeight
doublereal meanMolecularWeight() const
The mean molecular weight. Units: (kg/kmol)
Definition: Phase.h:748
Cantera::HMWSoln::m_BMX_IJ_L
vector_fp m_BMX_IJ_L
Derivative of BMX_IJ wrt T. Vector index is counterIJ.
Definition: HMWSoln.h:2153
Cantera::XML_Node::hasChild
bool hasChild(const std::string &ch) const
Tests whether the current node has a child node with a particular name.
Definition: xml.cpp:528
Cantera::HMWSoln::m_gfunc_IJ
vector_fp m_gfunc_IJ
Various temporary arrays used in the calculation of the Pitzer activity coefficients.
Definition: HMWSoln.h:2135
Cantera::MolalityVPSSTP::m_weightSolvent
doublereal m_weightSolvent
Molecular weight of the Solvent.
Definition: MolalityVPSSTP.h:587
Cantera::MolalityVPSSTP::m_pHScalingType
int m_pHScalingType
Scaling to be used for output of single-ion species activity coefficients.
Definition: MolalityVPSSTP.h:577
Cantera::VPStandardStateTP::getEntropy_R
virtual void getEntropy_R(doublereal *sr) const
Get the array of nondimensional Entropy functions for the standard state species at the current T and...
Definition: VPStandardStateTP.cpp:64
Cantera::AnyMap::hasKey
bool hasKey(const std::string &key) const
Returns true if the map contains an item named key.
Definition: AnyMap.cpp:984
Cantera::HMWSoln::readXMLPsi
void readXMLPsi(XML_Node &BinSalt)
Process an XML node called "psiCommonAnion" or "psiCommonCation".
Definition: HMWSoln.cpp:1525
Cantera::MolalityVPSSTP::setMoleFSolventMin
void setMoleFSolventMin(doublereal xmolSolventMIN)
Sets the minimum mole fraction in the molality formulation.
Definition: MolalityVPSSTP.cpp:53
Cantera::HMWSoln::getActivityConcentrations
virtual void getActivityConcentrations(doublereal *c) const
This method returns an array of generalized activity concentrations.
Definition: HMWSoln.cpp:266
Cantera::HMWSoln::m_Mu_nnn_coeff
Array2D m_Mu_nnn_coeff
Array of coefficients form_Mu_nnn term.
Definition: HMWSoln.h:2065
Cantera::HMWSoln::m_maxIionicStrength
double m_maxIionicStrength
Maximum value of the ionic strength allowed in the calculation of the activity coefficients.
Definition: HMWSoln.h:1736
Cantera::HMWSoln::m_Phi_IJ_P
vector_fp m_Phi_IJ_P
Derivative of m_Phi_IJ wrt P. Vector index is counterIJ.
Definition: HMWSoln.h:2198
Cantera::HMWSoln::m_Phi_IJ
vector_fp m_Phi_IJ
Intermediate variable called Phi in Pitzer's paper.
Definition: HMWSoln.h:2189
electrolytes.h
Cantera::MolalityVPSSTP::m_indexCLM
size_t m_indexCLM
Index of the phScale species.
Definition: MolalityVPSSTP.h:584
Cantera::Phase::assignDensity
void assignDensity(const double density_)
Set the internally stored constant density (kg/m^3) of the phase.
Definition: Phase.cpp:727
Cantera::HMWSoln::m_Phiprime_IJ
vector_fp m_Phiprime_IJ
Intermediate variable called Phiprime in Pitzer's paper.
Definition: HMWSoln.h:2202
Cantera::HMWSoln::m_lnActCoeffMolal_Scaled
vector_fp m_lnActCoeffMolal_Scaled
Logarithm of the activity coefficients on the molality scale.
Definition: HMWSoln.h:2072
Cantera::HMWSoln::m_Phi_IJ_LL
vector_fp m_Phi_IJ_LL
Derivative of m_Phi_IJ wrt TT. Vector index is counterIJ.
Definition: HMWSoln.h:2195
Cantera::HMWSoln::m_Mu_nnn
vector_fp m_Mu_nnn
Mu coefficient for the self-ternary neutral coefficient.
Definition: HMWSoln.h:2032
Cantera::XML_Node::attrib
std::string attrib(const std::string &attr) const
Function returns the value of an attribute.
Definition: xml.cpp:492
Cantera::HMWSoln::m_Psi_ijk_L
vector_fp m_Psi_ijk_L
Derivative of Psi_ijk[n] wrt T.
Definition: HMWSoln.h:1966
Cantera::HMWSoln::m_IionicMolality
double m_IionicMolality
Current value of the ionic strength on the molality scale Associated Salts, if present in the mechani...
Definition: HMWSoln.h:1732
Cantera::HMWSoln::s_update_dlnMolalityActCoeff_dT
void s_update_dlnMolalityActCoeff_dT() const
This function calculates the temperature derivative of the natural logarithm of the molality activity...
Definition: HMWSoln.cpp:2502
Cantera::HMWSoln::s_NBS_CLM_dlnMolalityActCoeff_dP
doublereal s_NBS_CLM_dlnMolalityActCoeff_dP() const
Calculate the pressure derivative of the Chlorine activity coefficient.
Definition: HMWSoln.cpp:4294
Cantera::PDSS::setState_TP
virtual void setState_TP(doublereal temp, doublereal pres)
Set the internal temperature and pressure.
Definition: PDSS.cpp:181
Cantera::HMWSoln::m_CphiMX_ij_coeff
Array2D m_CphiMX_ij_coeff
Array of coefficients for CphiMX, a parameter in the activity coefficient formulation.
Definition: HMWSoln.h:1916
Cantera::HMWSoln::m_Beta2MX_ij_coeff
Array2D m_Beta2MX_ij_coeff
Array of coefficients for Beta2, a variable in Pitzer's papers.
Definition: HMWSoln.h:1876
Cantera::HMWSoln::m_Lambda_nj_LL
Array2D m_Lambda_nj_LL
Derivative of Lambda_nj[i][j] wrt TT.
Definition: HMWSoln.h:2006
Cantera::HMWSoln::readXMLTheta
void readXMLTheta(XML_Node &BinSalt)
Process an XML node called "thetaAnion" or "thetaCation".
Definition: HMWSoln.cpp:1485
Cantera::HMWSoln::A_Debye_TP
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
Cantera::HMWSoln::initLengths
void initLengths()
Initialize all of the species-dependent lengths in the object.
Definition: HMWSoln.cpp:1098
ThermoFactory.h
Cantera::HMWSoln::calcIMSCutoffParams_
void calcIMSCutoffParams_()
Precalculate the IMS Cutoff parameters for typeCutoff = 2.
Definition: HMWSoln.cpp:1664
Cantera::vector_fp
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
Cantera::HMWSoln::m_waterSS
PDSS * m_waterSS
Water standard state calculator.
Definition: HMWSoln.h:1794
Cantera::HMWSoln::m_Beta0MX_ij_coeff
Array2D m_Beta0MX_ij_coeff
Array of coefficients for Beta0, a variable in Pitzer's papers.
Definition: HMWSoln.h:1826
Cantera::Phase::moleFraction
double moleFraction(size_t k) const
Return the mole fraction of a single species.
Definition: Phase.cpp:577
Cantera::HMWSoln::s_updatePitzer_dlnMolalityActCoeff_dP
void s_updatePitzer_dlnMolalityActCoeff_dP() const
Calculates the Pressure derivative of the natural logarithm of the molality activity coefficients.
Definition: HMWSoln.cpp:3556
Cantera::HMWSoln::counterIJ_setup
void counterIJ_setup() const
Set up a counter variable for keeping track of symmetric binary interactions amongst the solute speci...
Definition: HMWSoln.cpp:1416
Cantera::HMWSoln::IMS_X_o_cutoff_
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
Cantera::VPStandardStateTP::getCp_R
virtual void getCp_R(doublereal *cpr) const
Get the nondimensional Heat Capacities at constant pressure for the species standard states at the cu...
Definition: VPStandardStateTP.cpp:92
Cantera::HMWSoln::m_Psi_ijk_P
vector_fp m_Psi_ijk_P
Derivative of Psi_ijk[n] wrt P.
Definition: HMWSoln.h:1974
Cantera::HMWSoln::MC_X_o_cutoff_
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
AssertTrace
#define AssertTrace(expr)
Assertion must be true or an error is thrown.
Definition: ctexceptions.h:234
Cantera::MolalityVPSSTP::initThermo
virtual void initThermo()
Definition: MolalityVPSSTP.cpp:291
Cantera::MolalityVPSSTP::calcMolalities
void calcMolalities() const
Calculates the molality of all species and stores the result internally.
Definition: MolalityVPSSTP.cpp:68
Cantera::HMWSoln::getChemPotentials
virtual void getChemPotentials(doublereal *mu) const
Get the species chemical potentials. Units: J/kmol.
Definition: HMWSoln.cpp:318
Cantera::Array2D::resize
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
PDSS_Water.h
Cantera::HMWSoln::m_CMX_IJ
vector_fp m_CMX_IJ
Intermediate variable called CMX in Pitzer's paper.
Definition: HMWSoln.h:2219
Cantera::HMWSoln::s_NBS_CLM_dlnMolalityActCoeff_dT
doublereal s_NBS_CLM_dlnMolalityActCoeff_dT() const
Calculate the temperature derivative of the Chlorine activity coefficient on the NBS scale.
Definition: HMWSoln.cpp:4280
Cantera::HMWSoln::setMolarDensity
virtual void setMolarDensity(const doublereal conc)
Set the internally stored molar density (kmol/m^3) for the phase.
Definition: HMWSoln.cpp:255
Cantera::Phase::m_kk
size_t m_kk
Number of species in the phase.
Definition: Phase.h:942
Cantera::Phase::charge
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
Cantera::ValueCache::getScalar
CachedScalar getScalar(int id)
Get a reference to a CachedValue object representing a scalar (doublereal) with the given id.
Definition: ValueCache.h:165
Cantera::HMWSoln::getPartialMolarEnthalpies
virtual void getPartialMolarEnthalpies(doublereal *hbar) const
Returns an array of partial molar enthalpies for the species in the mixture.
Definition: HMWSoln.cpp:338
Cantera::HMWSoln::s_updatePitzer_lnMolalityActCoeff
void s_updatePitzer_lnMolalityActCoeff() const
Calculate the Pitzer portion of the activity coefficients.
Definition: HMWSoln.cpp:1999
Cantera::ThermoPhase::m_input
AnyMap m_input
Data supplied via setParameters.
Definition: ThermoPhase.h:1874
Cantera::Phase::density
virtual double density() const
Density (kg/m^3).
Definition: Phase.h:685
Cantera::HMWSoln::m_CMX_IJ_LL
vector_fp m_CMX_IJ_LL
Derivative of m_CMX_IJ wrt TT. Vector index is counterIJ.
Definition: HMWSoln.h:2225
Cantera::ValueCache::getId
int getId()
Get a unique id for a cached value.
Definition: ValueCache.cpp:21
Cantera::HMWSoln::elambda
double elambda[17]
This is elambda, MEC.
Definition: HMWSoln.h:2123
Cantera::HMWSoln::m_PhiPhi_IJ
vector_fp m_PhiPhi_IJ
Intermediate variable called PhiPhi in Pitzer's paper.
Definition: HMWSoln.h:2206
Cantera::HMWSoln::calc_lambdas
void calc_lambdas(double is) const
Calculate the lambda interactions.
Definition: HMWSoln.cpp:4041
Cantera::HMWSoln::m_h2func_IJ
vector_fp m_h2func_IJ
hfunc2, was called gprime in Pitzer's paper.
Definition: HMWSoln.h:2146
Cantera::HMWSoln::s_updateScaling_pHScaling_dP
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
Cantera::HMWSoln::m_dlnActCoeffMolaldP_Unscaled
vector_fp m_dlnActCoeffMolaldP_Unscaled
Derivative of the Logarithm of the activity coefficients on the molality scale wrt P.
Definition: HMWSoln.h:2103
Cantera::HMWSoln::calcMolalitiesCropped
void calcMolalitiesCropped() const
Calculate the cropped molalities.
Definition: HMWSoln.cpp:1275
Cantera::HMWSoln::m_BprimeMX_IJ_P
vector_fp m_BprimeMX_IJ_P
Derivative of BprimeMX wrt P. Vector index is counterIJ.
Definition: HMWSoln.h:2172
Cantera::ThermoPhase::thermalExpansionCoeff
virtual doublereal thermalExpansionCoeff() const
Return the volumetric thermal expansion coefficient. Units: 1/K.
Definition: ThermoPhase.h:289
Cantera::Phase::molarVolume
double molarVolume() const
Molar volume (m^3/kmol).
Definition: Phase.cpp:711
Cantera::HMWSoln::ADebye_V
double ADebye_V(double temperature=-1.0, double pressure=-1.0) const
Return Pitzer's definition of A_V.
Definition: HMWSoln.cpp:1047
Cantera::HMWSoln::getActivities
virtual void getActivities(doublereal *ac) const
Get the array of non-dimensional activities at the current solution temperature, pressure,...
Definition: HMWSoln.cpp:289
Cantera::HMWSoln::m_Mu_nnn_P
vector_fp m_Mu_nnn_P
Mu coefficient pressure derivative for the self-ternary neutral coefficient.
Definition: HMWSoln.h:2062
Cantera::XML_Node::name
std::string name() const
Returns the name of the XML node.
Definition: xml.h:372
Cantera::HMWSoln::m_BphiMX_IJ_P
vector_fp m_BphiMX_IJ_P
Derivative of BphiMX_IJ wrt P. Vector index is counterIJ.
Definition: HMWSoln.h:2185
Cantera::HMWSoln::m_Theta_ij
vector_fp m_Theta_ij
Array of 2D data for Theta_ij[i][j] in the Pitzer/HMW formulation.
Definition: HMWSoln.h:1928
Cantera::PHSCALE_NBS
const int PHSCALE_NBS
Scale to be used for evaluation of single-ion activity coefficients is that used by the NBS standard ...
Definition: MolalityVPSSTP.h:652
Cantera::HMWSoln::m_Lambda_nj_L
Array2D m_Lambda_nj_L
Derivative of Lambda_nj[i][j] wrt T. see m_Lambda_ij.
Definition: HMWSoln.h:2003
Cantera::HMWSoln::m_Alpha2MX_ij
vector_fp m_Alpha2MX_ij
Array of 2D data used in the Pitzer/HMW formulation.
Definition: HMWSoln.h:1891
Cantera::HMWSoln::ADebye_J
double ADebye_J(double temperature=-1.0, double pressure=-1.0) const
Return Pitzer's definition of A_J.
Definition: HMWSoln.cpp:1058
Cantera::HMWSoln::m_BprimeMX_IJ_LL
vector_fp m_BprimeMX_IJ_LL
Derivative of BprimeMX wrt TT. Vector index is counterIJ.
Definition: HMWSoln.h:2169
Cantera::HMWSoln::m_BprimeMX_IJ
vector_fp m_BprimeMX_IJ
Intermediate variable called BprimeMX in Pitzer's paper.
Definition: HMWSoln.h:2163
Cantera::HMWSoln::readXMLZetaCation
void readXMLZetaCation(const XML_Node &BinSalt)
Process an XML node called "zetaCation".
Definition: HMWSoln.cpp:1629
Cantera::HMWSoln::m_CMX_IJ_P
vector_fp m_CMX_IJ_P
Derivative of m_CMX_IJ wrt P. Vector index is counterIJ.
Definition: HMWSoln.h:2228
Cantera::HMWSoln::getPartialMolarCp
virtual void getPartialMolarCp(doublereal *cpbar) const
Return an array of partial molar heat capacities for the species in the mixture.
Definition: HMWSoln.cpp:405
Cantera::HMWSoln::cv_mole
virtual doublereal cv_mole() const
Molar heat capacity at constant volume. Units: J/kmol/K.
Definition: HMWSoln.cpp:216
Cantera::HMWSoln::s_updatePitzer_CoeffWRTemp
void s_updatePitzer_CoeffWRTemp(int doDerivs=2) const
Calculates the Pitzer coefficients' dependence on the temperature.
Definition: HMWSoln.cpp:1753
Cantera::HMWSoln::d2A_DebyedT2_TP
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
Cantera::InputFileError
Error thrown for problems processing information contained in an AnyMap or AnyValue.
Definition: AnyMap.h:538
Cantera::HMWSoln::m_Beta0MX_ij_L
vector_fp m_Beta0MX_ij_L
Derivative of Beta0_ij[i][j] wrt T. Vector index is counterIJ.
Definition: HMWSoln.h:1812
Cantera::HMWSoln::m_BMX_IJ_LL
vector_fp m_BMX_IJ_LL
Derivative of BMX_IJ wrt TT. Vector index is counterIJ.
Definition: HMWSoln.h:2156
Cantera::HMWSoln::initThermo
virtual void initThermo()
Definition: HMWSoln.cpp:684
Cantera::Phase::speciesIndex
size_t speciesIndex(const std::string &name) const
Returns the index of a species named 'name' within the Phase object.
Definition: Phase.cpp:201
Cantera::XML_Node
Class XML_Node is a tree-based representation of the contents of an XML file.
Definition: xml.h:103
Cantera::HMWSoln::getUnscaledMolalityActivityCoefficients
void getUnscaledMolalityActivityCoefficients(doublereal *acMolality) const
Get the array of unscaled non-dimensional molality based activity coefficients at the current solutio...
Definition: HMWSoln.cpp:305
Cantera::HMWSoln::printCoeffs
void printCoeffs() const
Print out all of the input Pitzer coefficients.
Definition: HMWSoln.cpp:4154
Cantera::HMWSoln::calcMCCutoffParams_
void calcMCCutoffParams_()
Calculate molality cut-off parameters.
Definition: HMWSoln.cpp:1717
Cantera::caseInsensitiveEquals
bool caseInsensitiveEquals(const std::string &input, const std::string &test)
Case insensitive equality predicate.
Definition: stringUtils.cpp:268
Cantera::HMWSoln::relative_molal_enthalpy
virtual doublereal relative_molal_enthalpy() const
Excess molar enthalpy of the solution from the mixing process on a molality basis.
Definition: HMWSoln.cpp:158
Cantera::HMWSoln::m_Psi_ijk_coeff
Array2D m_Psi_ijk_coeff
Array of coefficients for Psi_ijk[n] in the Pitzer/HMW formulation.
Definition: HMWSoln.h:1990
Cantera::ThermoPhase::RT
doublereal RT() const
Return the Gas Constant multiplied by the current temperature.
Definition: ThermoPhase.h:776
Cantera::HMWSoln::m_Lambda_nj_coeff
Array2D m_Lambda_nj_coeff
Array of coefficients for Lambda_nj[i][j] in the Pitzer/HMW formulation.
Definition: HMWSoln.h:2024
Cantera::VPStandardStateTP::getEnthalpy_RT
virtual void getEnthalpy_RT(doublereal *hrt) const
Get the nondimensional Enthalpy functions for the species at their standard states at the current T a...
Definition: VPStandardStateTP.cpp:58
Cantera::ThermoPhase::initThermoFile
virtual void initThermoFile(const std::string &inputFile, const std::string &id)
Definition: ThermoPhase.cpp:1064
Cantera::Phase::setMoleFractions
virtual void setMoleFractions(const double *const x)
Set the mole fractions to the specified values.
Definition: Phase.cpp:368
Cantera::WaterProps
The WaterProps class is used to house several approximation routines for properties of water.
Definition: WaterProps.h:99
Cantera::HMWSoln::m_BprimeMX_IJ_L
vector_fp m_BprimeMX_IJ_L
Derivative of BprimeMX wrt T. Vector index is counterIJ.
Definition: HMWSoln.h:2166
Cantera::HMWSoln::m_Beta1MX_ij_L
vector_fp m_Beta1MX_ij_L
Derivative of Beta1_ij[i][j] wrt T. Vector index is counterIJ.
Definition: HMWSoln.h:1836
Cantera::writelogf
void writelogf(const char *fmt, const Args &... args)
Write a formatted message to the screen.
Definition: global.h:179
Cantera::HMWSoln::IMS_slopegCut_
doublereal IMS_slopegCut_
Parameter in the polyExp cutoff treatment.
Definition: HMWSoln.h:2250
Cantera::HMWSoln::m_A_Debye
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
Cantera::PDSS::satPressure
virtual doublereal satPressure(doublereal T)
saturation pressure
Definition: PDSS.cpp:191
Cantera::HMWSoln::m_Beta2MX_ij_LL
vector_fp m_Beta2MX_ij_LL
Derivative of Beta2_ij[i][j] wrt TT. Vector index is counterIJ.
Definition: HMWSoln.h:1863
Cantera::HMWSoln::m_Beta0MX_ij_P
vector_fp m_Beta0MX_ij_P
Derivative of Beta0_ij[i][j] wrt P. Vector index is counterIJ.
Definition: HMWSoln.h:1818
ctml.h
Cantera::MolalityVPSSTP::m_Mnaught
doublereal m_Mnaught
This is the multiplication factor that goes inside log expressions involving the molalities of specie...
Definition: MolalityVPSSTP.h:601
Cantera::HMWSoln::m_waterProps
std::unique_ptr< WaterProps > m_waterProps
Pointer to the water property calculator.
Definition: HMWSoln.h:1797
stringUtils.h
Cantera::PHSCALE_PITZER
const int PHSCALE_PITZER
Scale to be used for the output of single-ion activity coefficients is that used by Pitzer.
Definition: MolalityVPSSTP.h:627
Cantera::HMWSoln::setDensity
virtual void setDensity(const doublereal rho)
Set the internally stored density (kg/m^3) of the phase.
Definition: HMWSoln.cpp:243
Cantera::MolalityVPSSTP::m_molalities
vector_fp m_molalities
Current value of the molalities of the species in the phase.
Definition: MolalityVPSSTP.h:605
Cantera::HMWSoln::readXMLBinarySalt
void readXMLBinarySalt(XML_Node &BinSalt)
Process an XML node called "binarySaltParameters".
Definition: HMWSoln.cpp:1439
Cantera::Phase::temperature
doublereal temperature() const
Temperature (K).
Definition: Phase.h:667
Cantera::HMWSoln::IMS_cCut_
doublereal IMS_cCut_
Parameter in the polyExp cutoff treatment having to do with rate of exp decay.
Definition: HMWSoln.h:2243
Cantera::HMWSoln::dA_DebyedT_TP
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
Cantera::HMWSoln::s_updateScaling_pHScaling_dT2
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
Cantera::HMWSoln::m_CphiMX_ij
vector_fp m_CphiMX_ij
Array of 2D data used in the Pitzer/HMW formulation.
Definition: HMWSoln.h:1898
Cantera::HMWSoln::m_BphiMX_IJ_L
vector_fp m_BphiMX_IJ_L
Derivative of BphiMX_IJ wrt T. Vector index is counterIJ.
Definition: HMWSoln.h:2179
Cantera::HMWSoln::m_d2lnActCoeffMolaldT2_Unscaled
vector_fp m_d2lnActCoeffMolaldT2_Unscaled
Derivative of the Logarithm of the activity coefficients on the molality scale wrt TT.
Definition: HMWSoln.h:2095
Cantera::HMWSoln::m_PhiPhi_IJ_L
vector_fp m_PhiPhi_IJ_L
Derivative of m_PhiPhi_IJ wrt T. Vector index is counterIJ.
Definition: HMWSoln.h:2209
Cantera::HMWSoln::cp_mole
virtual doublereal cp_mole() const
Molar heat capacity at constant pressure. Units: J/kmol/K.
Definition: HMWSoln.cpp:210
Cantera::HMWSoln::satPressure
virtual doublereal satPressure(doublereal T)
Get the saturation pressure for a given temperature.
Definition: HMWSoln.cpp:425
Cantera::HMWSoln::m_g2func_IJ
vector_fp m_g2func_IJ
This is the value of g2(x2) in Pitzer's papers. Vector index is counterIJ.
Definition: HMWSoln.h:2138
Cantera::HMWSoln::m_Theta_ij_coeff
Array2D m_Theta_ij_coeff
Array of coefficients for Theta_ij[i][j] in the Pitzer/HMW formulation.
Definition: HMWSoln.h:1949
Cantera::HMWSoln::s_NBS_CLM_d2lnMolalityActCoeff_dT2
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
Cantera::HMWSoln::s_NBS_CLM_lnMolalityActCoeff
doublereal s_NBS_CLM_lnMolalityActCoeff() const
Calculate the Chlorine activity coefficient on the NBS scale.
Definition: HMWSoln.cpp:4272
Cantera::HMWSoln::initThermoXML
virtual void initThermoXML(XML_Node &phaseNode, const std::string &id)
Initialize the phase parameters from an XML file.
Definition: HMWSoln.cpp:849
Cantera::HMWSoln::standardConcentration
virtual doublereal standardConcentration(size_t k=0) const
Return the standard concentration for the kth species.
Definition: HMWSoln.cpp:279
Cantera::HMWSoln::getPartialMolarVolumes
virtual void getPartialMolarVolumes(doublereal *vbar) const
Return an array of partial molar volumes for the species in the mixture.
Definition: HMWSoln.cpp:392
Cantera::getFloat
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
Cantera::HMWSoln::ADebye_L
double ADebye_L(double temperature=-1.0, double pressure=-1.0) const
Return Pitzer's definition of A_L.
Definition: HMWSoln.cpp:1036
Cantera::HMWSoln::m_Beta1MX_ij_coeff
Array2D m_Beta1MX_ij_coeff
Array of coefficients for Beta1, a variable in Pitzer's papers.
Definition: HMWSoln.h:1850
Cantera::HMWSoln::m_TempPitzerRef
double m_TempPitzerRef
Reference Temperature for the Pitzer formulations.
Definition: HMWSoln.h:1739
Cantera::Phase::mean_X
doublereal mean_X(const doublereal *const Q) const
Evaluate the mole-fraction-weighted mean of an array Q.
Definition: Phase.cpp:746
Cantera::HMWSoln::gibbs_mole
virtual doublereal gibbs_mole() const
Molar Gibbs function. Units: J/kmol.
Definition: HMWSoln.cpp:204
Cantera::HMWSoln::m_lnActCoeffMolal_Unscaled
vector_fp m_lnActCoeffMolal_Unscaled
Logarithm of the activity coefficients on the molality scale.
Definition: HMWSoln.h:2079
Cantera::HMWSoln::m_Theta_ij_P
vector_fp m_Theta_ij_P
Derivative of Theta_ij[i][j] wrt P. Vector index is counterIJ.
Definition: HMWSoln.h:1937
Cantera::CachedValue::value
T value
The value of the cached property.
Definition: ValueCache.h:118
Cantera::AnyMap
A map of string keys to values whose type can vary at runtime.
Definition: AnyMap.h:359
Cantera::HMWSoln::m_Psi_ijk_LL
vector_fp m_Psi_ijk_LL
Derivative of Psi_ijk[n] wrt TT.
Definition: HMWSoln.h:1970
Cantera::HMWSoln::m_BphiMX_IJ
vector_fp m_BphiMX_IJ
Intermediate variable called BphiMX in Pitzer's paper.
Definition: HMWSoln.h:2176
Cantera::GasConstant
const double GasConstant
Universal Gas Constant [J/kmol/K].
Definition: ct_defs.h:109
Cantera::VPStandardStateTP::getStandardVolumes
virtual void getStandardVolumes(doublereal *vol) const
Get the molar volumes of the species standard states at the current T and P of the solution.
Definition: VPStandardStateTP.cpp:98
Cantera::HMWSoln::applyphScale
virtual void applyphScale(doublereal *acMolality) const
Apply the current phScale to a set of activity Coefficients or activities.
Definition: HMWSoln.cpp:4198
Cantera::HMWSoln::m_Beta1MX_ij
vector_fp m_Beta1MX_ij
Array of 2D data used in the Pitzer/HMW formulation.
Definition: HMWSoln.h:1833
Cantera::XML_Node::children
const std::vector< XML_Node * > & children() const
Return an unchangeable reference to the vector of children of the current node.
Definition: xml.cpp:551
Cantera::HMWSoln::m_molalitiesAreCropped
bool m_molalitiesAreCropped
Boolean indicating whether the molalities are cropped or are modified.
Definition: HMWSoln.h:2112
Cantera::HMWSoln::m_BphiMX_IJ_LL
vector_fp m_BphiMX_IJ_LL
Derivative of BphiMX_IJ wrt TT. Vector index is counterIJ.
Definition: HMWSoln.h:2182
Cantera::sign
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
Cantera::CanteraError
Base class for exceptions thrown by Cantera classes.
Definition: ctexceptions.h:60
Cantera::HMWSoln::m_Mu_nnn_LL
vector_fp m_Mu_nnn_LL
Mu coefficient 2nd temperature derivative for the self-ternary neutral coefficient.
Definition: HMWSoln.h:2052
Cantera::HMWSoln::readXMLLambdaNeutral
void readXMLLambdaNeutral(XML_Node &BinSalt)
Process an XML node called "lambdaNeutral".
Definition: HMWSoln.cpp:1574
Cantera::importPhase
void importPhase(XML_Node &phase, ThermoPhase *th)
Import a phase information into an empty ThermoPhase object.
Definition: ThermoFactory.cpp:237
Cantera::VPStandardStateTP::updateStandardStateThermo
virtual void updateStandardStateThermo() const
Updates the standard state thermodynamic functions at the current T and P of the solution.
Definition: VPStandardStateTP.cpp:288
Cantera::fpValueCheck
doublereal fpValueCheck(const std::string &val)
Translate a string into one doublereal value, with error checking.
Definition: stringUtils.cpp:138
Cantera::HMWSoln::setA_Debye
void setA_Debye(double A)
Set the A_Debye parameter.
Definition: HMWSoln.cpp:647
Cantera::npos
const size_t npos
index returned by functions to indicate "no position"
Definition: ct_defs.h:188
Cantera::HMWSoln::calcDensity
void calcDensity()
Calculate the density of the mixture using the partial molar volumes and mole fractions as input.
Definition: HMWSoln.cpp:228
Cantera::HMWSoln::IMS_lnActCoeffMolal_
vector_fp IMS_lnActCoeffMolal_
Logarithm of the molal activity coefficients.
Definition: HMWSoln.h:2236
Cantera::HMWSoln::m_Theta_ij_L
vector_fp m_Theta_ij_L
Derivative of Theta_ij[i][j] wrt T. Vector index is counterIJ.
Definition: HMWSoln.h:1931
Cantera::Phase::species
shared_ptr< Species > species(const std::string &name) const
Return the Species object for the named species.
Definition: Phase.cpp:980
Cantera
Namespace for the Cantera kernel.
Definition: AnyMap.cpp:263
Cantera::CachedValue::validate
bool validate(double state1New)
Check whether the currently cached value is valid based on a single state variable.
Definition: ValueCache.h:44
Cantera::HMWSoln::s_updatePitzer_d2lnMolalityActCoeff_dT2
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
Cantera::HMWSoln::m_CounterIJ
vector_int m_CounterIJ
a counter variable for keeping track of symmetric binary interactions amongst the solute species.
Definition: HMWSoln.h:2120
Cantera::HMWSoln::entropy_mole
virtual doublereal entropy_mole() const
Molar entropy. Units: J/kmol/K.
Definition: HMWSoln.cpp:198
Cantera::VPStandardStateTP::getStandardChemPotentials
virtual void getStandardChemPotentials(doublereal *mu) const
Get the array of chemical potentials at unit activity for the species at their standard states at the...
Definition: VPStandardStateTP.cpp:50
HMWSoln.h
Cantera::Phase::speciesName
std::string speciesName(size_t k) const
Name of the species with index k.
Definition: Phase.cpp:229
Cantera::HMWSoln::m_Theta_ij_LL
vector_fp m_Theta_ij_LL
Derivative of Theta_ij[i][j] wrt TT. Vector index is counterIJ.
Definition: HMWSoln.h:1934
Cantera::HMWSoln::m_BMX_IJ_P
vector_fp m_BMX_IJ_P
Derivative of BMX_IJ wrt P. Vector index is counterIJ.
Definition: HMWSoln.h:2159
Cantera::getFloatArray
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
Cantera::HMWSoln::m_Beta0MX_ij_LL
vector_fp m_Beta0MX_ij_LL
Derivative of Beta0_ij[i][j] wrt TT. Vector index is counterIJ.
Definition: HMWSoln.h:1815
Cantera::HMWSoln::CROP_speciesCropped_
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
Cantera::ThermoPhase::isothermalCompressibility
virtual doublereal isothermalCompressibility() const
Returns the isothermal compressibility. Units: 1/Pa.
Definition: ThermoPhase.h:278
Cantera::HMWSoln::m_Beta2MX_ij
vector_fp m_Beta2MX_ij
Array of 2D data used in the Pitzer/HMW formulation.
Definition: HMWSoln.h:1857
Cantera::HMWSoln::m_gamma_tmp
vector_fp m_gamma_tmp
Intermediate storage of the activity coefficient itself.
Definition: HMWSoln.h:2232
Cantera::XML_Node::child
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
Cantera::HMWSoln::m_Phi_IJ_L
vector_fp m_Phi_IJ_L
Derivative of m_Phi_IJ wrt T. Vector index is counterIJ.
Definition: HMWSoln.h:2192
Cantera::HMWSoln::m_form_A_Debye
int m_form_A_Debye
Form of the constant outside the Debye-Huckel term called A.
Definition: HMWSoln.h:1756
Cantera::PDSS_Water
Class for the liquid water pressure dependent standard state.
Definition: PDSS_Water.h:49
Cantera::HMWSoln::relative_enthalpy
virtual doublereal relative_enthalpy() const
Excess molar enthalpy of the solution from the mixing process.
Definition: HMWSoln.cpp:146
Cantera::HMWSoln::m_Beta0MX_ij
vector_fp m_Beta0MX_ij
Array of 2D data used in the Pitzer/HMW formulation.
Definition: HMWSoln.h:1809
Cantera::HMWSoln::m_Lambda_nj_P
Array2D m_Lambda_nj_P
Derivative of Lambda_nj[i][j] wrt P.
Definition: HMWSoln.h:2009
Cantera::HMWSoln::m_tmpV
vector_fp m_tmpV
vector of size m_kk, used as a temporary holding area.
Definition: HMWSoln.h:1800