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