Cantera 2.6.0

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