pyEQL 0.12.2__py2.py3-none-any.whl → 0.14.0__py2.py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- pyEQL/__init__.py +1 -1
- pyEQL/activity_correction.py +318 -451
- pyEQL/engines.py +40 -40
- pyEQL/equilibrium.py +94 -160
- pyEQL/functions.py +15 -13
- pyEQL/logging_system.py +1 -1
- pyEQL/salt_ion_match.py +19 -27
- pyEQL/solute.py +8 -4
- pyEQL/solution.py +378 -439
- pyEQL/utils.py +11 -5
- {pyEQL-0.12.2.dist-info → pyEQL-0.14.0.dist-info}/METADATA +1 -1
- pyEQL-0.14.0.dist-info/RECORD +28 -0
- pyEQL-0.12.2.dist-info/RECORD +0 -28
- {pyEQL-0.12.2.dist-info → pyEQL-0.14.0.dist-info}/AUTHORS.md +0 -0
- {pyEQL-0.12.2.dist-info → pyEQL-0.14.0.dist-info}/COPYING +0 -0
- {pyEQL-0.12.2.dist-info → pyEQL-0.14.0.dist-info}/LICENSE.txt +0 -0
- {pyEQL-0.12.2.dist-info → pyEQL-0.14.0.dist-info}/WHEEL +0 -0
- {pyEQL-0.12.2.dist-info → pyEQL-0.14.0.dist-info}/top_level.txt +0 -0
pyEQL/engines.py
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"""
|
|
2
2
|
pyEQL engines for computing aqueous equilibria (e.g., speciation, redox, etc.).
|
|
3
3
|
|
|
4
|
-
:copyright: 2013-
|
|
4
|
+
:copyright: 2013-2024 by Ryan S. Kingsbury
|
|
5
5
|
:license: LGPL, see LICENSE for more details.
|
|
6
6
|
|
|
7
7
|
"""
|
|
@@ -52,8 +52,7 @@ class EOS(ABC):
|
|
|
52
52
|
Quantity: dimensionless quantity object
|
|
53
53
|
|
|
54
54
|
Raises:
|
|
55
|
-
ValueError if the calculation cannot be completed, e.g. due to insufficient number of
|
|
56
|
-
parameters.
|
|
55
|
+
ValueError if the calculation cannot be completed, e.g. due to insufficient number of parameters.
|
|
57
56
|
"""
|
|
58
57
|
|
|
59
58
|
@abstractmethod
|
|
@@ -68,8 +67,7 @@ class EOS(ABC):
|
|
|
68
67
|
Quantity: dimensionless molal scale osmotic coefficient
|
|
69
68
|
|
|
70
69
|
Raises:
|
|
71
|
-
ValueError if the calculation cannot be completed, e.g. due to insufficient number of
|
|
72
|
-
parameters.
|
|
70
|
+
ValueError if the calculation cannot be completed, e.g. due to insufficient number of parameters.
|
|
73
71
|
"""
|
|
74
72
|
|
|
75
73
|
@abstractmethod
|
|
@@ -84,8 +82,7 @@ class EOS(ABC):
|
|
|
84
82
|
Quantity: solute volume in L
|
|
85
83
|
|
|
86
84
|
Raises:
|
|
87
|
-
ValueError if the calculation cannot be completed, e.g. due to insufficient number of
|
|
88
|
-
parameters.
|
|
85
|
+
ValueError if the calculation cannot be completed, e.g. due to insufficient number of parameters.
|
|
89
86
|
"""
|
|
90
87
|
|
|
91
88
|
@abstractmethod
|
|
@@ -102,8 +99,7 @@ class EOS(ABC):
|
|
|
102
99
|
Nothing. The speciation of the Solution is modified in-place.
|
|
103
100
|
|
|
104
101
|
Raises:
|
|
105
|
-
ValueError if the calculation cannot be completed, e.g. due to insufficient number of
|
|
106
|
-
parameters or lack of convergence.
|
|
102
|
+
ValueError if the calculation cannot be completed, e.g. due to insufficient number of parameters or lack of convergence.
|
|
107
103
|
"""
|
|
108
104
|
|
|
109
105
|
|
|
@@ -163,7 +159,15 @@ class NativeEOS(EOS):
|
|
|
163
159
|
Path(os.path.dirname(__file__)) / "database" if self.phreeqc_db in ["llnl.dat", "geothermal.dat"] else None
|
|
164
160
|
)
|
|
165
161
|
# create the PhreeqcPython instance
|
|
166
|
-
|
|
162
|
+
# try/except added to catch unsupported architectures, such as Apple Silicon
|
|
163
|
+
try:
|
|
164
|
+
self.pp = PhreeqPython(database=self.phreeqc_db, database_directory=self.db_path)
|
|
165
|
+
except OSError:
|
|
166
|
+
logger.error(
|
|
167
|
+
"OSError encountered when trying to instantiate phreeqpython. Most likely this means you"
|
|
168
|
+
" are running on an architecture that is not supported by PHREEQC, such as Apple M1/M2 chips."
|
|
169
|
+
" pyEQL will work, but equilibrate() will have no effect."
|
|
170
|
+
)
|
|
167
171
|
# attributes to hold the PhreeqPython solution.
|
|
168
172
|
self.ppsol = None
|
|
169
173
|
# store the solution composition to see whether we need to re-instantiate the solution
|
|
@@ -238,7 +242,7 @@ class NativeEOS(EOS):
|
|
|
238
242
|
self.ppsol = None
|
|
239
243
|
|
|
240
244
|
def get_activity_coefficient(self, solution, solute):
|
|
241
|
-
"""
|
|
245
|
+
r"""
|
|
242
246
|
Whenever the appropriate parameters are available, the Pitzer model [may]_ is used.
|
|
243
247
|
If no Pitzer parameters are available, then the appropriate equations are selected
|
|
244
248
|
according to the following logic: [stumm]_.
|
|
@@ -250,17 +254,17 @@ class NativeEOS(EOS):
|
|
|
250
254
|
|
|
251
255
|
The ionic strength, activity coefficients, and activities are all
|
|
252
256
|
calculated based on the molal (mol/kg) concentration scale. If a different
|
|
253
|
-
scale is given as input, then the molal-scale activity coefficient :math
|
|
257
|
+
scale is given as input, then the molal-scale activity coefficient :math:`\gamma_\pm` is
|
|
254
258
|
converted according to [rbs]_
|
|
255
259
|
|
|
256
|
-
.. math:: f_
|
|
260
|
+
.. math:: f_\pm = \gamma_\pm * (1 + M_w \sum_i \nu_i m_i)
|
|
257
261
|
|
|
258
|
-
.. math:: y_
|
|
262
|
+
.. math:: y_\pm = \frac{m \rho_w}{C \gamma_\pm}
|
|
259
263
|
|
|
260
|
-
where :math:`f_
|
|
264
|
+
where :math:`f_\pm` is the rational activity coefficient, :math:`M_w` is
|
|
261
265
|
the molecular weight of water, the summation represents the total molality of
|
|
262
|
-
all solute species, :math:`y_
|
|
263
|
-
:math
|
|
266
|
+
all solute species, :math:`y_\pm` is the molar activity coefficient,
|
|
267
|
+
:math:`\rho_w` is the density of pure water, :math:`m` and :math:`C` are
|
|
264
268
|
the molal and molar concentrations of the chosen salt (not individual solute), respectively.
|
|
265
269
|
|
|
266
270
|
Args:
|
|
@@ -280,11 +284,11 @@ class NativeEOS(EOS):
|
|
|
280
284
|
molality," which is the molality that would result in a single-salt
|
|
281
285
|
mixture with the same total ionic strength as the multicomponent solution.
|
|
282
286
|
|
|
283
|
-
.. math::
|
|
287
|
+
.. math:: m_{effective} = \frac{2 I}{(\nu_{+} z_{+}^2 + \nu_{-}- z_{-}^2)}
|
|
284
288
|
|
|
285
289
|
References:
|
|
286
|
-
|
|
287
|
-
|
|
290
|
+
.. [may] May, P. M., Rowland, D., Hefter, G., & Königsberger, E. (2011).
|
|
291
|
+
A Generic and Updatable Pitzer Characterization of Aqueous Binary Electrolyte Solutions at 1 bar and 25 °C.
|
|
288
292
|
*Journal of Chemical & Engineering Data*, 56(12), 5066-5077. doi:10.1021/je2009329
|
|
289
293
|
|
|
290
294
|
.. [stumm] Stumm, Werner and Morgan, James J. *Aquatic Chemistry*, 3rd ed,
|
|
@@ -297,12 +301,11 @@ class NativeEOS(EOS):
|
|
|
297
301
|
desalination calculations for mixed electrolyte solutions with comparison to seawater. Desalination 2013, 318, 34-47.
|
|
298
302
|
|
|
299
303
|
See Also:
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
activity_correction.
|
|
303
|
-
activity_correction.
|
|
304
|
-
activity_correction.
|
|
305
|
-
activity_correction.get_activity_coefficient_pitzer
|
|
304
|
+
:attr:`pyEQL.solution.Solution.ionic_strength`
|
|
305
|
+
:func:`pyEQL.activity_correction.get_activity_coefficient_debyehuckel`
|
|
306
|
+
:func:`pyEQL.activity_correction.get_activity_coefficient_guntelberg`
|
|
307
|
+
:func:`pyEQL.activity_correction.get_activity_coefficient_davies`
|
|
308
|
+
:func:`pyEQL.activity_correction.get_activity_coefficient_pitzer`
|
|
306
309
|
"""
|
|
307
310
|
# identify the predominant salt that this ion is a member of
|
|
308
311
|
salt = None
|
|
@@ -418,7 +421,7 @@ class NativeEOS(EOS):
|
|
|
418
421
|
return molal
|
|
419
422
|
|
|
420
423
|
def get_osmotic_coefficient(self, solution):
|
|
421
|
-
"""
|
|
424
|
+
r"""
|
|
422
425
|
Return the *molal scale* osmotic coefficient of solute, given a Solution
|
|
423
426
|
object.
|
|
424
427
|
|
|
@@ -427,28 +430,27 @@ class NativeEOS(EOS):
|
|
|
427
430
|
coefficient of 1.
|
|
428
431
|
|
|
429
432
|
If the 'rational' scale is given as input, then the molal-scale osmotic
|
|
430
|
-
coefficient :math
|
|
433
|
+
coefficient :math:`\phi` is converted according to [rbs]_
|
|
431
434
|
|
|
432
|
-
.. math:: g = -
|
|
435
|
+
.. math:: g = - \phi M_{w} \frac{\sum_{i} \nu_{i} m_{i}}{\ln x_{w}}
|
|
433
436
|
|
|
434
437
|
where :math:`g` is the rational osmotic coefficient, :math:`M_{w}` is
|
|
435
438
|
the molecular weight of water, the summation represents the total molality of
|
|
436
439
|
all solute species, and :math:`x_{w}` is the mole fraction of water.
|
|
437
440
|
|
|
438
441
|
Args:
|
|
439
|
-
scale:
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
coefficient is returned.
|
|
442
|
+
scale: The concentration scale for the returned osmotic coefficient. Valid options are "molal",
|
|
443
|
+
"rational" (i.e., mole fraction), and "fugacity". By default, the molal scale osmotic
|
|
444
|
+
coefficient is returned.
|
|
443
445
|
|
|
444
|
-
Returns
|
|
446
|
+
Returns:
|
|
445
447
|
Quantity:
|
|
446
448
|
The osmotic coefficient
|
|
447
449
|
|
|
448
450
|
See Also:
|
|
449
|
-
get_water_activity
|
|
450
|
-
|
|
451
|
-
|
|
451
|
+
:meth:`pyEQL.solution.Solution.get_water_activity`
|
|
452
|
+
:meth:`pyEQL.solution.Solution.get_salt`
|
|
453
|
+
:attr:`pyEQL.solution.Solution.ionic_strength`
|
|
452
454
|
|
|
453
455
|
Notes:
|
|
454
456
|
For multicomponent mixtures, pyEQL adopts the "effective Pitzer model"
|
|
@@ -469,8 +471,7 @@ class NativeEOS(EOS):
|
|
|
469
471
|
the author confirmed that the weight factor should be the true molality, and that is what is implemented
|
|
470
472
|
in pyEQL.)
|
|
471
473
|
|
|
472
|
-
References
|
|
473
|
-
|
|
474
|
+
References:
|
|
474
475
|
.. [may] May, P. M., Rowland, D., Hefter, G., & Königsberger, E. (2011).
|
|
475
476
|
A Generic and Updatable Pitzer Characterization of Aqueous Binary Electrolyte Solutions at 1 bar and
|
|
476
477
|
25 °C. Journal of Chemical & Engineering Data, 56(12), 5066-5077. doi:10.1021/je2009329
|
|
@@ -482,7 +483,6 @@ class NativeEOS(EOS):
|
|
|
482
483
|
behavior on desalination calculations for mixed electrolyte solutions with comparison to seawater. Desalination 2013, 318, 34-47.
|
|
483
484
|
|
|
484
485
|
Examples:
|
|
485
|
-
|
|
486
486
|
>>> s1 = pyEQL.Solution([['Na+','0.2 mol/kg'],['Cl-','0.2 mol/kg']])
|
|
487
487
|
>>> s1.get_osmotic_coefficient()
|
|
488
488
|
<Quantity(0.923715281, 'dimensionless')>
|
pyEQL/equilibrium.py
CHANGED
|
@@ -4,7 +4,7 @@ redox, complexation, etc.).
|
|
|
4
4
|
|
|
5
5
|
NOTE: these methods are not currently used but are here for the future.
|
|
6
6
|
|
|
7
|
-
:copyright: 2013-
|
|
7
|
+
:copyright: 2013-2024 by Ryan S. Kingsbury
|
|
8
8
|
:license: LGPL, see LICENSE for more details.
|
|
9
9
|
|
|
10
10
|
"""
|
|
@@ -36,23 +36,15 @@ SPECIES_ALIAISES = {
|
|
|
36
36
|
|
|
37
37
|
def adjust_temp_pitzer(c1, c2, c3, c4, c5, temp, temp_ref=ureg.Quantity("298.15 K")):
|
|
38
38
|
"""
|
|
39
|
-
Calculate a parameter for the Pitzer model based on temperature-dependent
|
|
40
|
-
coefficients c1,c2,c3,c4,and c5.
|
|
41
|
-
|
|
42
|
-
Parameters
|
|
43
|
-
----------
|
|
44
|
-
c1, c2, c3, c4, c5: float
|
|
45
|
-
Temperature-dependent coefficients for the pitzer parameter of
|
|
46
|
-
interest.
|
|
47
|
-
temp: Quantity
|
|
48
|
-
The temperature at which the Pitzer parameter is to be calculated
|
|
49
|
-
temp_ref: Quantity, optional
|
|
50
|
-
The reference temperature on which the parameters are based.
|
|
51
|
-
298.15 K if omitted.
|
|
52
|
-
|
|
53
|
-
As described in the PHREEQC documentation
|
|
39
|
+
Calculate a parameter for the Pitzer model based on temperature-dependent coefficients c1,c2,c3,c4,and c5.
|
|
54
40
|
|
|
41
|
+
Args:
|
|
42
|
+
c1, c2, c3, c4, c5 (float): Temperature-dependent coefficients for the pitzer parameter of interest.
|
|
43
|
+
temp (Quantity): The temperature at which the Pitzer parameter is to be calculated.
|
|
44
|
+
temp_ref (Quantity, optional): The reference temperature on which the parameters are based. Defaults to 298.15 K if omitted.
|
|
55
45
|
|
|
46
|
+
Note:
|
|
47
|
+
As described in the PHREEQC documentation.
|
|
56
48
|
"""
|
|
57
49
|
return (
|
|
58
50
|
c1
|
|
@@ -65,53 +57,37 @@ def adjust_temp_pitzer(c1, c2, c3, c4, c5, temp, temp_ref=ureg.Quantity("298.15
|
|
|
65
57
|
|
|
66
58
|
|
|
67
59
|
def adjust_temp_vanthoff(equilibrium_constant, enthalpy, temperature, reference_temperature=ureg.Quantity(25, "degC")):
|
|
68
|
-
r"""
|
|
69
|
-
|
|
60
|
+
r"""
|
|
70
61
|
Adjust a reaction equilibrium constant from one temperature to another.
|
|
71
62
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
The enthalpy change (delta H) for the reaction in kJ/mol. Assumed
|
|
78
|
-
independent of temperature (see Notes).
|
|
79
|
-
temperature : Quantity
|
|
80
|
-
the desired reaction temperature in degrees Celsius
|
|
81
|
-
reference_temperature : Quantity, optional
|
|
82
|
-
the temperature at which equilibrium_constant is valid. (25 degrees C if omitted).
|
|
83
|
-
|
|
84
|
-
Returns
|
|
85
|
-
-------
|
|
86
|
-
float
|
|
87
|
-
adjusted reaction equilibrium constant
|
|
88
|
-
|
|
89
|
-
Notes
|
|
90
|
-
-----
|
|
91
|
-
This function implements the Van't Hoff equation to adjust measured
|
|
92
|
-
equilibrium constants to other temperatures.
|
|
93
|
-
|
|
94
|
-
.. math::
|
|
95
|
-
ln(K2 / K1) = {\delta H \over R} ( {1 \over T_1} - {1 \over T_2} )
|
|
96
|
-
|
|
97
|
-
This implementation assumes that the enthalpy is independent of temperature
|
|
98
|
-
over the range of interest.
|
|
99
|
-
|
|
100
|
-
References
|
|
101
|
-
----------
|
|
102
|
-
Stumm, Werner and Morgan, James J. Aquatic Chemistry, 3rd ed, pp 53.
|
|
103
|
-
Wiley Interscience, 1996.
|
|
63
|
+
Args:
|
|
64
|
+
equilibrium_constant (float): The reaction equilibrium constant for the reaction.
|
|
65
|
+
enthalpy (Quantity): The enthalpy change (delta H) for the reaction in kJ/mol. Assumed independent of temperature (see Notes).
|
|
66
|
+
temperature (Quantity): The desired reaction temperature in degrees Celsius.
|
|
67
|
+
reference_temperature (Quantity, optional): The temperature at which equilibrium_constant is valid. Defaults to 25 degrees C if omitted.
|
|
104
68
|
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
69
|
+
Returns:
|
|
70
|
+
float: The adjusted reaction equilibrium constant.
|
|
71
|
+
|
|
72
|
+
Note:
|
|
73
|
+
This function implements the Van't Hoff equation to adjust measured equilibrium constants to other temperatures.
|
|
74
|
+
|
|
75
|
+
.. math::
|
|
76
|
+
ln(K2 / K1) = {\delta H \over R} ( {1 \over T_1} - {1 \over T_2} )
|
|
77
|
+
|
|
78
|
+
This implementation assumes that the enthalpy is independent of temperature over the range of interest.
|
|
79
|
+
|
|
80
|
+
References:
|
|
81
|
+
Stumm, Werner and Morgan, James J. Aquatic Chemistry, 3rd ed, pp 53. Wiley Interscience, 1996.
|
|
109
82
|
|
|
110
|
-
|
|
83
|
+
Examples:
|
|
84
|
+
>>> adjust_temp_vanthoff(0.15,ureg.Quantity('-197.6 kJ/mol'),ureg.Quantity('42 degC'),ureg.Quantity(' 25degC')) #doctest: +ELLIPSIS
|
|
85
|
+
0.00203566...
|
|
111
86
|
|
|
112
|
-
|
|
113
|
-
0.00203566...
|
|
87
|
+
If the 'ref_temperature' parameter is omitted, a default of 25 C is used.
|
|
114
88
|
|
|
89
|
+
>>> adjust_temp_vanthoff(0.15,ureg.Quantity('-197.6 kJ/mol'),ureg.Quantity('42 degC')) #doctest: +ELLIPSIS
|
|
90
|
+
0.00203566...
|
|
115
91
|
"""
|
|
116
92
|
output = equilibrium_constant * math.exp(
|
|
117
93
|
enthalpy / ureg.R * (1 / reference_temperature.to("K") - 1 / temperature.to("K"))
|
|
@@ -131,51 +107,31 @@ def adjust_temp_arrhenius(
|
|
|
131
107
|
temperature,
|
|
132
108
|
reference_temperature=ureg.Quantity(25, "degC"),
|
|
133
109
|
):
|
|
134
|
-
r"""
|
|
135
|
-
|
|
110
|
+
r"""
|
|
136
111
|
Adjust a reaction equilibrium constant from one temperature to another.
|
|
137
112
|
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
The activation energy of the process, in kJ/mol
|
|
144
|
-
temperature : Quantity
|
|
145
|
-
the desired reaction temperature.
|
|
146
|
-
reference_temperature : Quantity, optional
|
|
147
|
-
the temperature at which equilibrium_constant is valid
|
|
148
|
-
Defaults to 25 degrees C if omitted.
|
|
149
|
-
|
|
150
|
-
Returns
|
|
151
|
-
-------
|
|
152
|
-
Quantity
|
|
153
|
-
The adjusted reaction equilibrium constant
|
|
113
|
+
Args:
|
|
114
|
+
rate_constant (Quantity): The parameter value (usually a rate constant) being adjusted.
|
|
115
|
+
activation_energy (Quantity): The activation energy of the process, in kJ/mol.
|
|
116
|
+
temperature (Quantity): The desired reaction temperature.
|
|
117
|
+
reference_temperature (Quantity, optional): The temperature at which equilibrium_constant is valid. Defaults to 25 degrees C if omitted.
|
|
154
118
|
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
kelvin
|
|
119
|
+
Returns:
|
|
120
|
+
Quantity: The adjusted reaction equilibrium constant.
|
|
158
121
|
|
|
159
|
-
Notes
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
constants to other temperatures.
|
|
163
|
-
TODO - add better reference
|
|
122
|
+
Notes:
|
|
123
|
+
This function implements the Arrhenius equation to adjust measured rate constants to other temperatures.
|
|
124
|
+
TODO - add better reference
|
|
164
125
|
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
References
|
|
169
|
-
----------
|
|
170
|
-
|
|
171
|
-
http://chemwiki.ucdavis.edu/Physical_Chemistry/Kinetics/Reaction_Rates/Temperature_Dependence_of_Reaction_Rates/Arrhenius_Equation
|
|
126
|
+
.. math::
|
|
127
|
+
ln(\frac{K2}{K1} = \frac{E_a}{R} ( \frac{1}{T_{1}} - {\frac{1}{T_2}} )
|
|
172
128
|
|
|
129
|
+
References:
|
|
130
|
+
http://chemwiki.ucdavis.edu/Physical_Chemistry/Kinetics/Reaction_Rates/Temperature_Dependence_of_Reaction_Rates/Arrhenius_Equation
|
|
173
131
|
|
|
174
132
|
Examples:
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
1.8867225...e-24
|
|
178
|
-
|
|
133
|
+
>>> adjust_temp_arrhenius(7,900*ureg.Quantity('kJ/mol'),37*ureg.Quantity('degC'),97*ureg.Quantity('degC')) #doctest: +ELLIPSIS
|
|
134
|
+
1.8867225...e-24
|
|
179
135
|
"""
|
|
180
136
|
output = rate_constant * math.exp(
|
|
181
137
|
activation_energy / ureg.R * (1 / reference_temperature.to("K") - 1 / temperature.to("K"))
|
|
@@ -189,98 +145,76 @@ def adjust_temp_arrhenius(
|
|
|
189
145
|
|
|
190
146
|
|
|
191
147
|
def alpha(n, pH, pKa_list):
|
|
192
|
-
"""
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
(
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
Returns
|
|
209
|
-
-------
|
|
210
|
-
float
|
|
211
|
-
The fraction of total acid present in the specified form.
|
|
212
|
-
|
|
213
|
-
Notes
|
|
214
|
-
-----
|
|
215
|
-
The acid-base cient is calculated as follows: [stm]_
|
|
148
|
+
r"""
|
|
149
|
+
Returns the acid-base distribution coefficient (alpha) of an acid in the n-deprotonated form at a given pH.
|
|
150
|
+
|
|
151
|
+
Args:
|
|
152
|
+
n (int): The number of protons that have been lost by the desired form of the acid. Also the subscript on the
|
|
153
|
+
alpha value. E.g. for bicarbonate (HCO3-), n=1 because 1 proton has been lost from the fully-protonated
|
|
154
|
+
carbonic acid (H2CO3) form.
|
|
155
|
+
pH (float or int): The pH of the solution.
|
|
156
|
+
pKa_list (list of floats or ints): The pKa values (negative log of equilibrium constants) for the acid of
|
|
157
|
+
interest. There must be a minimum of n pKa values in the list.
|
|
158
|
+
|
|
159
|
+
Returns:
|
|
160
|
+
float: The fraction of total acid present in the specified form.
|
|
161
|
+
|
|
162
|
+
Notes:
|
|
163
|
+
The acid-base coefficient is calculated as follows: [stm]_
|
|
216
164
|
|
|
217
165
|
.. math::
|
|
218
166
|
|
|
219
|
-
|
|
167
|
+
\alpha_n = \frac{term_n}{[H+]^n + k_{a1}[H+]^{n-1} + k_{a1}k_{a2}[H+]^{n-2} ... k_{a1}k_{a2}...k_{an} }
|
|
220
168
|
|
|
221
169
|
Where :math: '\term_n' refers to the nth term in the denominator, starting from 0
|
|
222
170
|
|
|
223
|
-
References
|
|
224
|
-
|
|
225
|
-
.. [stm] Stumm, Werner and Morgan, James J. Aquatic Chemistry, 3rd ed, pp 127-130. Wiley Interscience, 1996.
|
|
171
|
+
References:
|
|
172
|
+
Stumm, Werner and Morgan, James J. Aquatic Chemistry, 3rd ed, pp 127-130. Wiley Interscience, 1996.
|
|
226
173
|
|
|
227
174
|
Examples:
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
0.999...
|
|
231
|
-
|
|
232
|
-
The sum of all alpha values should equal 1
|
|
175
|
+
>>> alpha(1,8,[4.7]) #doctest: +ELLIPSIS
|
|
176
|
+
0.999...
|
|
233
177
|
|
|
234
|
-
|
|
235
|
-
0.021...
|
|
236
|
-
>>> alpha(1,8,[6.35,10.33]) #doctest: +ELLIPSIS
|
|
237
|
-
0.979...
|
|
238
|
-
>>> alpha(2,8,[6.35,10.33]) #doctest: +ELLIPSIS
|
|
239
|
-
2.043...e-09
|
|
178
|
+
The sum of all alpha values should equal 1
|
|
240
179
|
|
|
241
|
-
|
|
180
|
+
>>> alpha(0,8,[6.35,10.33]) #doctest: +ELLIPSIS
|
|
181
|
+
0.021...
|
|
182
|
+
>>> alpha(1,8,[6.35,10.33]) #doctest: +ELLIPSIS
|
|
183
|
+
0.979...
|
|
184
|
+
>>> alpha(2,8,[6.35,10.33]) #doctest: +ELLIPSIS
|
|
185
|
+
2.043...e-09
|
|
242
186
|
|
|
243
|
-
|
|
244
|
-
0.5
|
|
187
|
+
If pH is equal to one of the pKa values the function should return 0.5.
|
|
245
188
|
|
|
189
|
+
>>> alpha(1,6.35,[6.35,10.33])
|
|
190
|
+
0.5
|
|
246
191
|
"""
|
|
247
192
|
# generate an error if no pKa values are specified
|
|
248
193
|
if len(pKa_list) == 0:
|
|
249
|
-
|
|
250
|
-
return None
|
|
194
|
+
raise ValueError("No pKa values given. Cannot calculate distribution coefficient.")
|
|
251
195
|
|
|
252
196
|
# generate an error if n > number of pKa values
|
|
253
197
|
if len(pKa_list) < n:
|
|
254
|
-
|
|
255
|
-
return None
|
|
198
|
+
raise ValueError("Insufficient number of pKa values given. Cannot calculate distribution coefficient.")
|
|
256
199
|
|
|
257
|
-
#
|
|
258
|
-
|
|
200
|
+
# sort the pKa list
|
|
201
|
+
pKa_list = sorted(pKa_list)
|
|
259
202
|
|
|
260
203
|
# determine how many protons the acid has
|
|
261
204
|
num_protons = len(pKa_list)
|
|
262
205
|
|
|
263
206
|
# build a list of terms where the term subscript corresponds to the list index
|
|
264
|
-
terms_list = []
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
# add the term to the list
|
|
274
|
-
terms_list.append(k_term * Hplus ** (num_protons - item))
|
|
275
|
-
|
|
276
|
-
# build the expression
|
|
277
|
-
numerator = terms_list[n]
|
|
278
|
-
denominator = 0
|
|
279
|
-
for item in terms_list:
|
|
280
|
-
denominator += item
|
|
207
|
+
terms_list = [10 ** (-pH * num_protons)]
|
|
208
|
+
for i in range(len(pKa_list)):
|
|
209
|
+
# multiply together all K values with
|
|
210
|
+
k_term = 1
|
|
211
|
+
for pK in pKa_list[0 : i + 1]:
|
|
212
|
+
k_term *= 10**-pK
|
|
213
|
+
terms_list.append(k_term * 10 ** (-pH * (num_protons - (i + 1))))
|
|
214
|
+
print(terms_list)
|
|
281
215
|
|
|
282
216
|
# return the desired distribution factor
|
|
283
|
-
alpha =
|
|
217
|
+
alpha = terms_list[n] / sum(terms_list)
|
|
284
218
|
logger.info(
|
|
285
219
|
"Calculated %s-deprotonated acid distribution coefficient of %s for pKa=%s at pH %s % n,alpha,pKa_list,pH"
|
|
286
220
|
)
|
pyEQL/functions.py
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"""
|
|
2
2
|
pyEQL functions that take Solution objects as inputs or return Solution objects.
|
|
3
3
|
|
|
4
|
-
:copyright: 2013-
|
|
4
|
+
:copyright: 2013-2024 by Ryan S. Kingsbury
|
|
5
5
|
:license: LGPL, see LICENSE for more details.
|
|
6
6
|
|
|
7
7
|
"""
|
|
@@ -19,7 +19,8 @@ def gibbs_mix(solution1: Solution, solution2: Solution):
|
|
|
19
19
|
Return the Gibbs energy change associated with mixing two solutions.
|
|
20
20
|
|
|
21
21
|
Args:
|
|
22
|
-
solution1
|
|
22
|
+
solution1: a solution to be mixed.
|
|
23
|
+
solution2: a solution to be mixed.
|
|
23
24
|
|
|
24
25
|
Returns:
|
|
25
26
|
The change in Gibbs energy associated with complete mixing of the
|
|
@@ -30,7 +31,7 @@ def gibbs_mix(solution1: Solution, solution2: Solution):
|
|
|
30
31
|
|
|
31
32
|
.. math::
|
|
32
33
|
|
|
33
|
-
|
|
34
|
+
\Delta_{mix} G = \sum_i {(n_c + n_d) R T \ln a_b} - \sum_i {n_c R T \ln a_c} - \sum_i {n_d R T \ln a_d}
|
|
34
35
|
|
|
35
36
|
Where :math:`n` is the number of moles of substance, :math:`T` is the temperature in kelvin,
|
|
36
37
|
and subscripts :math:`b`, :math:`c`, and :math:`d` refer to the concentrated, dilute, and blended
|
|
@@ -41,8 +42,8 @@ def gibbs_mix(solution1: Solution, solution2: Solution):
|
|
|
41
42
|
anion, and water).
|
|
42
43
|
|
|
43
44
|
References:
|
|
44
|
-
Koga, Yoshikata, 2007.
|
|
45
|
-
A differential approach
|
|
45
|
+
Koga, Yoshikata, 2007. Solution Thermodynamics and its Application to Aqueous Solutions:
|
|
46
|
+
A differential approach. Elsevier, 2007, pp. 23-37.
|
|
46
47
|
|
|
47
48
|
"""
|
|
48
49
|
concentrate = solution1
|
|
@@ -77,7 +78,7 @@ def entropy_mix(solution1: Solution, solution2: Solution):
|
|
|
77
78
|
|
|
78
79
|
.. math::
|
|
79
80
|
|
|
80
|
-
|
|
81
|
+
\Delta_{mix} S = \sum_i {(n_c + n_d) R T \ln x_b} - \sum_i {n_c R T \ln x_c} - \sum_i {n_d R T \ln x_d}
|
|
81
82
|
|
|
82
83
|
Where :math:`n` is the number of moles of substance, :math:`T` is the temperature in kelvin,
|
|
83
84
|
and subscripts :math:`b`, :math:`c`, and :math:`d` refer to the concentrated, dilute, and blended
|
|
@@ -111,10 +112,10 @@ def entropy_mix(solution1: Solution, solution2: Solution):
|
|
|
111
112
|
|
|
112
113
|
|
|
113
114
|
def donnan_eql(solution: Solution, fixed_charge: str):
|
|
114
|
-
"""
|
|
115
|
+
r"""
|
|
115
116
|
Return a solution object in equilibrium with fixed_charge.
|
|
116
117
|
|
|
117
|
-
|
|
118
|
+
Args:
|
|
118
119
|
solution : Solution object
|
|
119
120
|
The external solution to be brought into equilibrium with the fixed
|
|
120
121
|
charges
|
|
@@ -133,19 +134,20 @@ def donnan_eql(solution: Solution, fixed_charge: str):
|
|
|
133
134
|
|
|
134
135
|
.. math::
|
|
135
136
|
|
|
136
|
-
|
|
137
|
-
|
|
137
|
+
\big(\frac{a_{-}}{\bar a_{-}} \big)^(\frac{1}{z_{-})
|
|
138
|
+
\big(\frac{\bar a_{+}}{a_{+}}\big)^(\frac{1}{z_{+})
|
|
139
|
+
\exp(\frac{\Delta \pi \bar V}{RT z_{+} \nu_{+}})
|
|
138
140
|
|
|
139
141
|
Where subscripts :math:`+` and :math:`-` indicate the cation and anion, respectively,
|
|
140
142
|
the overbar indicates the membrane phase,
|
|
141
|
-
:math:`a` represents activity, :math:`z` represents charge, :math
|
|
143
|
+
:math:`a` represents activity, :math:`z` represents charge, :math:`\nu` represents the stoichiometric
|
|
142
144
|
coefficient, :math:`V` represents the partial molar volume of the salt, and
|
|
143
|
-
:math
|
|
145
|
+
:math:`\Delta \pi` is the difference in osmotic pressure between the membrane and the
|
|
144
146
|
solution phase.
|
|
145
147
|
|
|
146
148
|
In addition, electroneutrality must prevail within the membrane phase:
|
|
147
149
|
|
|
148
|
-
.. math::
|
|
150
|
+
.. math:: \bar C_{+} z_{+} + \bar X + \bar C_{-} z_{-} = 0
|
|
149
151
|
|
|
150
152
|
Where :math:`C` represents concentration and :math:`X` is the fixed charge concentration
|
|
151
153
|
in the membrane or ion exchange phase.
|
pyEQL/logging_system.py
CHANGED
|
@@ -15,7 +15,7 @@ WARNING - assumptions or limitations of module output
|
|
|
15
15
|
ERROR - Module could not complete a task due to invalid input or other problem
|
|
16
16
|
CRITICAL - not used
|
|
17
17
|
|
|
18
|
-
:copyright: 2013-
|
|
18
|
+
:copyright: 2013-2024 by Ryan S. Kingsbury
|
|
19
19
|
:license: LGPL, see LICENSE for more details.
|
|
20
20
|
|
|
21
21
|
"""
|