| Cantera 3.0.0

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