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