pyEQL 1.0.0__py2.py3-none-any.whl → 1.0.1__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 +15 -11
- pyEQL/engines.py +27 -11
- pyEQL/equilibrium.py +6 -5
- pyEQL/functions.py +5 -4
- pyEQL/solution.py +13 -12
- {pyEQL-1.0.0.dist-info → pyEQL-1.0.1.dist-info}/METADATA +5 -4
- {pyEQL-1.0.0.dist-info → pyEQL-1.0.1.dist-info}/RECORD +13 -13
- {pyEQL-1.0.0.dist-info → pyEQL-1.0.1.dist-info}/AUTHORS.md +0 -0
- {pyEQL-1.0.0.dist-info → pyEQL-1.0.1.dist-info}/COPYING +0 -0
- {pyEQL-1.0.0.dist-info → pyEQL-1.0.1.dist-info}/LICENSE.txt +0 -0
- {pyEQL-1.0.0.dist-info → pyEQL-1.0.1.dist-info}/WHEEL +0 -0
- {pyEQL-1.0.0.dist-info → pyEQL-1.0.1.dist-info}/top_level.txt +0 -0
pyEQL/__init__.py
CHANGED
|
@@ -47,7 +47,7 @@ ureg.default_format = "P~"
|
|
|
47
47
|
|
|
48
48
|
# create a Store for the default database
|
|
49
49
|
json_db_file = files("pyEQL") / "database" / "pyeql_db.json"
|
|
50
|
-
IonDB = JSONStore(str(json_db_file), key="formula")
|
|
50
|
+
IonDB = JSONStore(str(json_db_file), key="formula", encoding="utf8")
|
|
51
51
|
# By calling connect on init, we get the expensive JSON reading operation out
|
|
52
52
|
# of the way. Subsequent calls to connect will bypass this and access the already-
|
|
53
53
|
# instantiated Store in memory, which should speed up instantiation of Solution objects.
|
pyEQL/activity_correction.py
CHANGED
|
@@ -14,8 +14,8 @@ are called from within the get_activity_coefficient method of the Solution class
|
|
|
14
14
|
"""
|
|
15
15
|
|
|
16
16
|
import logging
|
|
17
|
-
import math
|
|
18
17
|
|
|
18
|
+
import numpy as np
|
|
19
19
|
from pint import Quantity
|
|
20
20
|
|
|
21
21
|
from pyEQL import ureg
|
|
@@ -114,8 +114,8 @@ def _debye_parameter_activity(temperature: str = "25 degC") -> "Quantity":
|
|
|
114
114
|
|
|
115
115
|
debyeparam = (
|
|
116
116
|
ureg.elementary_charge**3
|
|
117
|
-
* (2 *
|
|
118
|
-
/ (4 *
|
|
117
|
+
* (2 * np.pi * ureg.N_A * ureg.Quantity(water_substance.rho, "g/L")) ** 0.5
|
|
118
|
+
/ (4 * np.pi * ureg.epsilon_0 * water_substance.epsilon * ureg.boltzmann_constant * T) ** 1.5
|
|
119
119
|
)
|
|
120
120
|
|
|
121
121
|
logger.debug(rf"Computed Debye-Huckel Limiting Law Constant A^{{\gamma}} = {debyeparam} at {temperature}")
|
|
@@ -248,7 +248,7 @@ def get_activity_coefficient_debyehuckel(ionic_strength, z=1, temperature="25 de
|
|
|
248
248
|
|
|
249
249
|
log_f = -_debye_parameter_activity(temperature) * z**2 * ionic_strength**0.5
|
|
250
250
|
|
|
251
|
-
return
|
|
251
|
+
return np.exp(log_f) * ureg.Quantity(1, "dimensionless")
|
|
252
252
|
|
|
253
253
|
|
|
254
254
|
def get_activity_coefficient_guntelberg(ionic_strength, z=1, temperature="25 degC"):
|
|
@@ -285,7 +285,7 @@ def get_activity_coefficient_guntelberg(ionic_strength, z=1, temperature="25 deg
|
|
|
285
285
|
|
|
286
286
|
log_f = -_debye_parameter_activity(temperature) * z**2 * ionic_strength**0.5 / (1 + ionic_strength.magnitude**0.5)
|
|
287
287
|
|
|
288
|
-
return
|
|
288
|
+
return np.exp(log_f) * ureg.Quantity(1, "dimensionless")
|
|
289
289
|
|
|
290
290
|
|
|
291
291
|
def get_activity_coefficient_davies(ionic_strength, z=1, temperature="25 degC"):
|
|
@@ -327,7 +327,7 @@ def get_activity_coefficient_davies(ionic_strength, z=1, temperature="25 degC"):
|
|
|
327
327
|
* (ionic_strength.magnitude**0.5 / (1 + ionic_strength.magnitude**0.5) - 0.2 * ionic_strength.magnitude)
|
|
328
328
|
)
|
|
329
329
|
|
|
330
|
-
return
|
|
330
|
+
return np.exp(log_f) * ureg.Quantity(1, "dimensionless")
|
|
331
331
|
|
|
332
332
|
|
|
333
333
|
def get_activity_coefficient_pitzer(
|
|
@@ -437,7 +437,7 @@ def get_activity_coefficient_pitzer(
|
|
|
437
437
|
b,
|
|
438
438
|
)
|
|
439
439
|
|
|
440
|
-
return
|
|
440
|
+
return np.exp(loggamma) * ureg.Quantity(1, "dimensionless")
|
|
441
441
|
|
|
442
442
|
|
|
443
443
|
def get_apparent_volume_pitzer(
|
|
@@ -533,7 +533,7 @@ def get_apparent_volume_pitzer(
|
|
|
533
533
|
(nu_cation + nu_anion)
|
|
534
534
|
* abs(z_cation * z_anion)
|
|
535
535
|
* (_debye_parameter_volume(temperature) / 2 / b)
|
|
536
|
-
*
|
|
536
|
+
* np.log(1 + b * ionic_strength**0.5)
|
|
537
537
|
)
|
|
538
538
|
|
|
539
539
|
third_term = (
|
|
@@ -566,7 +566,7 @@ def _pitzer_f1(x):
|
|
|
566
566
|
# return 0 if the input is 0
|
|
567
567
|
if x == 0:
|
|
568
568
|
return 0
|
|
569
|
-
return 2 * (1 - (1 + x) *
|
|
569
|
+
return 2 * (1 - (1 + x) * np.exp(-x)) / x**2
|
|
570
570
|
|
|
571
571
|
|
|
572
572
|
def _pitzer_f2(x):
|
|
@@ -586,7 +586,7 @@ def _pitzer_f2(x):
|
|
|
586
586
|
# return 0 if the input is 0
|
|
587
587
|
if x == 0:
|
|
588
588
|
return 0
|
|
589
|
-
return -2 * (1 - (1 + x + x**2 / 2) *
|
|
589
|
+
return -2 * (1 - (1 + x + x**2 / 2) * np.exp(-x)) / x**2
|
|
590
590
|
|
|
591
591
|
|
|
592
592
|
def _pitzer_B_MX(ionic_strength, alpha1, alpha2, beta0, beta1, beta2):
|
|
@@ -692,6 +692,10 @@ def _pitzer_B_phi(ionic_strength, alpha1, alpha2, beta0, beta1, beta2):
|
|
|
692
692
|
and Representation with an Ion Interaction (Pitzer) Model.
|
|
693
693
|
Journal of Chemical & Engineering Data, 55(2), 830-838. doi:10.1021/je900487a
|
|
694
694
|
"""
|
|
695
|
+
import math
|
|
696
|
+
|
|
697
|
+
# TODO - for some reason this specific method requires the use of math.exp rather than np.exp. Using np.exp raises
|
|
698
|
+
# a dimensionalityerror.
|
|
695
699
|
return beta0 + beta1 * math.exp(-alpha1 * ionic_strength**0.5) + beta2 * math.exp(-alpha2 * ionic_strength**0.5)
|
|
696
700
|
|
|
697
701
|
|
|
@@ -772,7 +776,7 @@ def _pitzer_log_gamma(
|
|
|
772
776
|
-1
|
|
773
777
|
* abs(z_cation * z_anion)
|
|
774
778
|
* _debye_parameter_osmotic(temperature)
|
|
775
|
-
* (ionic_strength**0.5 / (1 + b * ionic_strength**0.5) + 2 / b *
|
|
779
|
+
* (ionic_strength**0.5 / (1 + b * ionic_strength**0.5) + 2 / b * np.log(1 + b * ionic_strength**0.5))
|
|
776
780
|
)
|
|
777
781
|
second_term = 2 * molality * nu_cation * nu_anion / (nu_cation + nu_anion) * (B_MX + B_phi)
|
|
778
782
|
third_term = 3 * molality**2 * (nu_cation * nu_anion) ** 1.5 / (nu_cation + nu_anion) * C_phi
|
pyEQL/engines.py
CHANGED
|
@@ -47,7 +47,7 @@ class EOS(ABC):
|
|
|
47
47
|
solution: pyEQL Solution object
|
|
48
48
|
solute: str identifying the solute of interest
|
|
49
49
|
|
|
50
|
-
Returns
|
|
50
|
+
Returns:
|
|
51
51
|
Quantity: dimensionless quantity object
|
|
52
52
|
|
|
53
53
|
Raises:
|
|
@@ -62,7 +62,7 @@ class EOS(ABC):
|
|
|
62
62
|
Args:
|
|
63
63
|
solution: pyEQL Solution object
|
|
64
64
|
|
|
65
|
-
Returns
|
|
65
|
+
Returns:
|
|
66
66
|
Quantity: dimensionless molal scale osmotic coefficient
|
|
67
67
|
|
|
68
68
|
Raises:
|
|
@@ -77,7 +77,7 @@ class EOS(ABC):
|
|
|
77
77
|
Args:
|
|
78
78
|
solution: pyEQL Solution object
|
|
79
79
|
|
|
80
|
-
Returns
|
|
80
|
+
Returns:
|
|
81
81
|
Quantity: solute volume in L
|
|
82
82
|
|
|
83
83
|
Raises:
|
|
@@ -94,7 +94,7 @@ class EOS(ABC):
|
|
|
94
94
|
Args:
|
|
95
95
|
solution: pyEQL Solution object
|
|
96
96
|
|
|
97
|
-
Returns
|
|
97
|
+
Returns:
|
|
98
98
|
Nothing. The speciation of the Solution is modified in-place.
|
|
99
99
|
|
|
100
100
|
Raises:
|
|
@@ -173,9 +173,7 @@ class NativeEOS(EOS):
|
|
|
173
173
|
self._stored_comp = None
|
|
174
174
|
|
|
175
175
|
def _setup_ppsol(self, solution):
|
|
176
|
-
"""
|
|
177
|
-
Helper method to set up a PhreeqPython solution for subsequent analysis.
|
|
178
|
-
"""
|
|
176
|
+
"""Helper method to set up a PhreeqPython solution for subsequent analysis."""
|
|
179
177
|
self._stored_comp = solution.components.copy()
|
|
180
178
|
solv_mass = solution.solvent_mass.to("kg").magnitude
|
|
181
179
|
# inherit bulk solution properties
|
|
@@ -197,6 +195,16 @@ class NativeEOS(EOS):
|
|
|
197
195
|
# add the composition to the dict
|
|
198
196
|
# also, skip H and O
|
|
199
197
|
for el, mol in solution.get_el_amt_dict().items():
|
|
198
|
+
# CAUTION - care must be taken to avoid unintended behavior here. get_el_amt_dict() will return
|
|
199
|
+
# all distinct oxi states of each element present. If there are elements present whose oxi states
|
|
200
|
+
# are NOT recognized by PHREEQC (via SPECIAL_ELEMENTS) then the amount of only 1 oxi state will be
|
|
201
|
+
# entered into the composition dict. This can especially cause problems after equilibrate() has already
|
|
202
|
+
# been called once. For example, equilibrating a simple NaCl solution generates Cl species that are assigned
|
|
203
|
+
# various oxidations states, -1 mostly, but also 1, 2, and 3. Since the concentrations of everything
|
|
204
|
+
# except the -1 oxi state are tiny, this can result in Cl "disappearing" from the solution if
|
|
205
|
+
# equlibrate is called again. It also causes non-determinism, because the amount is taken from whatever
|
|
206
|
+
# oxi state happens to be iterated through last.
|
|
207
|
+
|
|
200
208
|
# strip off the oxi state
|
|
201
209
|
bare_el = el.split("(")[0]
|
|
202
210
|
if bare_el in SPECIAL_ELEMENTS:
|
|
@@ -208,7 +216,12 @@ class NativeEOS(EOS):
|
|
|
208
216
|
else:
|
|
209
217
|
key = bare_el
|
|
210
218
|
|
|
211
|
-
|
|
219
|
+
if key in d:
|
|
220
|
+
# when multiple oxi states for the same (non-SPECIAL) element are present, make sure to
|
|
221
|
+
# add all their amounts together
|
|
222
|
+
d[key] += str(mol / solv_mass)
|
|
223
|
+
else:
|
|
224
|
+
d[key] = str(mol / solv_mass)
|
|
212
225
|
|
|
213
226
|
# tell PHREEQC which species to use for charge balance
|
|
214
227
|
if (
|
|
@@ -660,7 +673,6 @@ class NativeEOS(EOS):
|
|
|
660
673
|
solution.components[s] = mol
|
|
661
674
|
|
|
662
675
|
# make sure all species are accounted for
|
|
663
|
-
charge_adjust = 0
|
|
664
676
|
assert set(self._stored_comp.keys()) - set(solution.components.keys()) == set()
|
|
665
677
|
|
|
666
678
|
# log a message if any components were not touched by PHREEQC
|
|
@@ -671,6 +683,11 @@ class NativeEOS(EOS):
|
|
|
671
683
|
f"After equilibration, the amounts of species {missing_species} were not modified "
|
|
672
684
|
"by PHREEQC. These species are likely absent from its database."
|
|
673
685
|
)
|
|
686
|
+
|
|
687
|
+
# re-adjust charge balance for any missing species
|
|
688
|
+
# note that if balance_charge is set, it will have been passed to PHREEQC, so we only need to adjust
|
|
689
|
+
# for any missing species here.
|
|
690
|
+
charge_adjust = 0
|
|
674
691
|
for s in missing_species:
|
|
675
692
|
charge_adjust += -1 * solution.get_amount(s, "eq").magnitude
|
|
676
693
|
if charge_adjust != 0:
|
|
@@ -679,11 +696,10 @@ class NativeEOS(EOS):
|
|
|
679
696
|
f" {charge_adjust} eq of charge were added via {solution.balance_charge}"
|
|
680
697
|
)
|
|
681
698
|
|
|
682
|
-
# re-adjust charge balance
|
|
683
699
|
if solution.balance_charge is None:
|
|
684
700
|
pass
|
|
685
701
|
elif solution.balance_charge == "pH":
|
|
686
|
-
solution.components["H+"] += charge_adjust
|
|
702
|
+
solution.components["H+"] += charge_adjust.magnitude
|
|
687
703
|
elif solution.balance_charge == "pE":
|
|
688
704
|
raise NotImplementedError
|
|
689
705
|
else:
|
pyEQL/equilibrium.py
CHANGED
|
@@ -11,7 +11,8 @@ NOTE: these methods are not currently used but are here for the future.
|
|
|
11
11
|
|
|
12
12
|
# import libraries for scientific functions
|
|
13
13
|
import logging
|
|
14
|
-
|
|
14
|
+
|
|
15
|
+
import numpy as np
|
|
15
16
|
|
|
16
17
|
from pyEQL import ureg
|
|
17
18
|
|
|
@@ -51,7 +52,7 @@ def adjust_temp_pitzer(c1, c2, c3, c4, c5, temp, temp_ref=ureg.Quantity("298.15
|
|
|
51
52
|
return (
|
|
52
53
|
c1
|
|
53
54
|
+ c2 * (1 / temp + 1 / temp_ref)
|
|
54
|
-
+ c2 *
|
|
55
|
+
+ c2 * np.log(temp / temp_ref)
|
|
55
56
|
+ c3 * (temp - temp_ref)
|
|
56
57
|
+ c4 * (temp**2 - temp_ref**2)
|
|
57
58
|
+ c5 * (temp**-2 - temp_ref**-2)
|
|
@@ -91,7 +92,7 @@ def adjust_temp_vanthoff(equilibrium_constant, enthalpy, temperature, reference_
|
|
|
91
92
|
>>> adjust_temp_vanthoff(0.15,ureg.Quantity('-197.6 kJ/mol'),ureg.Quantity('42 degC')) #doctest: +ELLIPSIS
|
|
92
93
|
0.00203566...
|
|
93
94
|
"""
|
|
94
|
-
output = equilibrium_constant *
|
|
95
|
+
output = equilibrium_constant * np.exp(
|
|
95
96
|
enthalpy / ureg.R * (1 / reference_temperature.to("K") - 1 / temperature.to("K"))
|
|
96
97
|
)
|
|
97
98
|
|
|
@@ -128,7 +129,7 @@ def adjust_temp_arrhenius(
|
|
|
128
129
|
TODO - add better reference
|
|
129
130
|
|
|
130
131
|
.. math::
|
|
131
|
-
ln(\frac{K2}{K1} = \frac{E_a}{R} ( \frac{1}{T_{1}} - {\frac{1}{T_2}} )
|
|
132
|
+
ln(\frac{K2}{K1}) = \frac{E_a}{R} ( \frac{1}{T_{1}} - {\frac{1}{T_2}} )
|
|
132
133
|
|
|
133
134
|
References:
|
|
134
135
|
http://chemwiki.ucdavis.edu/Physical_Chemistry/Kinetics/Reaction_Rates/Temperature_Dependence_of_Reaction_Rates/Arrhenius_Equation
|
|
@@ -137,7 +138,7 @@ def adjust_temp_arrhenius(
|
|
|
137
138
|
>>> adjust_temp_arrhenius(7,900*ureg.Quantity('kJ/mol'),37*ureg.Quantity('degC'),97*ureg.Quantity('degC')) #doctest: +ELLIPSIS
|
|
138
139
|
1.8867225...e-24
|
|
139
140
|
"""
|
|
140
|
-
output = rate_constant *
|
|
141
|
+
output = rate_constant * np.exp(
|
|
141
142
|
activation_energy / ureg.R * (1 / reference_temperature.to("K") - 1 / temperature.to("K"))
|
|
142
143
|
)
|
|
143
144
|
|
pyEQL/functions.py
CHANGED
|
@@ -7,7 +7,8 @@ pyEQL functions that take Solution objects as inputs or return Solution objects.
|
|
|
7
7
|
"""
|
|
8
8
|
|
|
9
9
|
import logging
|
|
10
|
-
|
|
10
|
+
|
|
11
|
+
import numpy as np
|
|
11
12
|
|
|
12
13
|
from pyEQL import Solution, ureg
|
|
13
14
|
|
|
@@ -55,7 +56,7 @@ def gibbs_mix(solution1: Solution, solution2: Solution):
|
|
|
55
56
|
for solution in term_list:
|
|
56
57
|
for solute in solution.components:
|
|
57
58
|
if solution.get_amount(solute, "fraction") != 0:
|
|
58
|
-
term_list[solution] += solution.get_amount(solute, "mol") *
|
|
59
|
+
term_list[solution] += solution.get_amount(solute, "mol") * np.log(solution.get_activity(solute))
|
|
59
60
|
|
|
60
61
|
return (ureg.R * blend.temperature.to("K") * (term_list[blend] - term_list[concentrate] - term_list[dilute])).to(
|
|
61
62
|
"J"
|
|
@@ -102,7 +103,7 @@ def entropy_mix(solution1: Solution, solution2: Solution):
|
|
|
102
103
|
for solution in term_list:
|
|
103
104
|
for solute in solution.components:
|
|
104
105
|
if solution.get_amount(solute, "fraction") != 0:
|
|
105
|
-
term_list[solution] += solution.get_amount(solute, "mol") *
|
|
106
|
+
term_list[solution] += solution.get_amount(solute, "mol") * np.log(
|
|
106
107
|
solution.get_amount(solute, "fraction")
|
|
107
108
|
)
|
|
108
109
|
|
|
@@ -242,7 +243,7 @@ def donnan_eql(solution: Solution, fixed_charge: str):
|
|
|
242
243
|
|
|
243
244
|
return (act_cation_mem / act_cation_soln) ** (1 / z_cation) * (act_anion_soln / act_anion_mem) ** (
|
|
244
245
|
1 / z_anion
|
|
245
|
-
) -
|
|
246
|
+
) - np.exp(delta_pi * exp_term)
|
|
246
247
|
|
|
247
248
|
# solve the function above using one of scipy's nonlinear solvers
|
|
248
249
|
|
pyEQL/solution.py
CHANGED
|
@@ -9,7 +9,6 @@ pyEQL Solution Class.
|
|
|
9
9
|
from __future__ import annotations
|
|
10
10
|
|
|
11
11
|
import logging
|
|
12
|
-
import math
|
|
13
12
|
import os
|
|
14
13
|
import warnings
|
|
15
14
|
from functools import lru_cache
|
|
@@ -433,6 +432,10 @@ class Solution(MSONable):
|
|
|
433
432
|
activity = False) of the solute.
|
|
434
433
|
"""
|
|
435
434
|
try:
|
|
435
|
+
# TODO - for some reason this specific method requires the use of math.log10 rather than np.log10.
|
|
436
|
+
# Using np.exp raises ZeroDivisionError
|
|
437
|
+
import math
|
|
438
|
+
|
|
436
439
|
if activity is True:
|
|
437
440
|
return -1 * math.log10(self.get_activity(solute))
|
|
438
441
|
return -1 * math.log10(self.get_amount(solute, "mol/L").magnitude)
|
|
@@ -647,7 +650,7 @@ class Solution(MSONable):
|
|
|
647
650
|
x_cat = self.get_amount(salt.cation, "fraction").magnitude
|
|
648
651
|
|
|
649
652
|
# calculate the kinematic viscosity
|
|
650
|
-
nu =
|
|
653
|
+
nu = np.log(nu_w * MW_w / MW) + 15 * x_cat**2 + x_cat**3 * G_123 + 3 * x_cat * G_23 * (1 - 0.05 * x_cat)
|
|
651
654
|
|
|
652
655
|
return ureg.Quantity(np.exp(nu), "m**2 / s")
|
|
653
656
|
|
|
@@ -930,9 +933,7 @@ class Solution(MSONable):
|
|
|
930
933
|
:attr:`dielectric_constant`
|
|
931
934
|
|
|
932
935
|
"""
|
|
933
|
-
bjerrum_length = ureg.e**2 / (
|
|
934
|
-
4 * math.pi * self.dielectric_constant * ureg.epsilon_0 * ureg.k * self.temperature
|
|
935
|
-
)
|
|
936
|
+
bjerrum_length = ureg.e**2 / (4 * np.pi * self.dielectric_constant * ureg.epsilon_0 * ureg.k * self.temperature)
|
|
936
937
|
return bjerrum_length.to("nm")
|
|
937
938
|
|
|
938
939
|
@property
|
|
@@ -975,7 +976,7 @@ class Solution(MSONable):
|
|
|
975
976
|
partial_molar_volume_water = self.get_property(self.solvent, "size.molar_volume")
|
|
976
977
|
|
|
977
978
|
osmotic_pressure = (
|
|
978
|
-
-1 * ureg.R * self.temperature / partial_molar_volume_water *
|
|
979
|
+
-1 * ureg.R * self.temperature / partial_molar_volume_water * np.log(self.get_water_activity())
|
|
979
980
|
)
|
|
980
981
|
self.logger.debug(
|
|
981
982
|
f"Calculated osmotic pressure of solution as {osmotic_pressure} Pa at T= {self.temperature} degrees C"
|
|
@@ -1835,12 +1836,12 @@ class Solution(MSONable):
|
|
|
1835
1836
|
* ureg.Quantity(0.018015, "kg/mol")
|
|
1836
1837
|
* self.get_total_moles_solute()
|
|
1837
1838
|
/ self.solvent_mass
|
|
1838
|
-
/
|
|
1839
|
+
/ np.log(self.get_amount(self.solvent, "fraction"))
|
|
1839
1840
|
)
|
|
1840
1841
|
if scale == "fugacity":
|
|
1841
1842
|
return np.exp(
|
|
1842
1843
|
-molal_phi * ureg.Quantity(0.018015, "kg/mol") * self.get_total_moles_solute() / self.solvent_mass
|
|
1843
|
-
-
|
|
1844
|
+
- np.log(self.get_amount(self.solvent, "fraction"))
|
|
1844
1845
|
) * ureg.Quantity(1, "dimensionless")
|
|
1845
1846
|
|
|
1846
1847
|
raise ValueError("Invalid scale argument. Pass 'molal', 'rational', or 'fugacity'.")
|
|
@@ -1939,14 +1940,14 @@ class Solution(MSONable):
|
|
|
1939
1940
|
ureg.R
|
|
1940
1941
|
* self.temperature.to("K")
|
|
1941
1942
|
* self.get_amount(item, "mol")
|
|
1942
|
-
*
|
|
1943
|
+
* np.log(self.get_activity(item))
|
|
1943
1944
|
)
|
|
1944
1945
|
else:
|
|
1945
1946
|
E += (
|
|
1946
1947
|
ureg.R
|
|
1947
1948
|
* self.temperature.to("K")
|
|
1948
1949
|
* self.get_amount(item, "mol")
|
|
1949
|
-
*
|
|
1950
|
+
* np.log(self.get_amount(item, "fraction"))
|
|
1950
1951
|
)
|
|
1951
1952
|
# If we have a solute with zero concentration, we will get a ValueError
|
|
1952
1953
|
except ValueError:
|
|
@@ -2582,12 +2583,12 @@ class Solution(MSONable):
|
|
|
2582
2583
|
"this property is planned for a future release."
|
|
2583
2584
|
)
|
|
2584
2585
|
# calculate the new pH and pE (before reactions) by mixing
|
|
2585
|
-
mix_pH = -
|
|
2586
|
+
mix_pH = -np.log10(float(mix_species["H+"].split(" ")[0]) / mix_vol.to("L").magnitude)
|
|
2586
2587
|
|
|
2587
2588
|
# pE = -log[e-], so calculate the moles of e- in each solution and mix them
|
|
2588
2589
|
mol_e_self = 10 ** (-1 * self.pE) * self.volume.to("L").magnitude
|
|
2589
2590
|
mol_e_other = 10 ** (-1 * other.pE) * other.volume.to("L").magnitude
|
|
2590
|
-
mix_pE = -
|
|
2591
|
+
mix_pE = -np.log10((mol_e_self + mol_e_other) / mix_vol.to("L").magnitude)
|
|
2591
2592
|
|
|
2592
2593
|
# create a new solution
|
|
2593
2594
|
return Solution(
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: pyEQL
|
|
3
|
-
Version: 1.0.
|
|
3
|
+
Version: 1.0.1
|
|
4
4
|
Summary: Python tools for solution chemistry
|
|
5
5
|
Home-page: https://github.com/KingsburyLab/pyEQL
|
|
6
6
|
Author: Ryan Kingsbury
|
|
@@ -19,12 +19,12 @@ License-File: LICENSE.txt
|
|
|
19
19
|
License-File: COPYING
|
|
20
20
|
License-File: AUTHORS.md
|
|
21
21
|
Requires-Dist: pint >=0.19
|
|
22
|
-
Requires-Dist: numpy
|
|
22
|
+
Requires-Dist: numpy <2
|
|
23
23
|
Requires-Dist: scipy
|
|
24
|
-
Requires-Dist: pymatgen
|
|
24
|
+
Requires-Dist: pymatgen ==2024.5.1
|
|
25
25
|
Requires-Dist: iapws
|
|
26
26
|
Requires-Dist: monty
|
|
27
|
-
Requires-Dist: maggma >=0.
|
|
27
|
+
Requires-Dist: maggma >=0.67.0
|
|
28
28
|
Requires-Dist: phreeqpython
|
|
29
29
|
Provides-Extra: docs
|
|
30
30
|
Requires-Dist: sphinx >=3.2.1 ; extra == 'docs'
|
|
@@ -37,6 +37,7 @@ Requires-Dist: setuptools ; extra == 'testing'
|
|
|
37
37
|
Requires-Dist: pre-commit ; extra == 'testing'
|
|
38
38
|
Requires-Dist: pytest ; extra == 'testing'
|
|
39
39
|
Requires-Dist: pytest-cov ; extra == 'testing'
|
|
40
|
+
Requires-Dist: pytest-xdist ; extra == 'testing'
|
|
40
41
|
Requires-Dist: black ; extra == 'testing'
|
|
41
42
|
Requires-Dist: mypy ; extra == 'testing'
|
|
42
43
|
Requires-Dist: ruff ; extra == 'testing'
|
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
pyEQL/__init__.py,sha256=
|
|
2
|
-
pyEQL/activity_correction.py,sha256=
|
|
3
|
-
pyEQL/engines.py,sha256=
|
|
4
|
-
pyEQL/equilibrium.py,sha256=
|
|
5
|
-
pyEQL/functions.py,sha256=
|
|
1
|
+
pyEQL/__init__.py,sha256=JErflmaJVP373dm3-YGHUomFu05dgX0iXWS-Z5AgSTU,2118
|
|
2
|
+
pyEQL/activity_correction.py,sha256=eOixjgTd5hTrTRD5s6aPCCG12lAIH7-lRN0Z1qHu678,37151
|
|
3
|
+
pyEQL/engines.py,sha256=ma4KhCmW2XR4FNebQBa2vTkKygRH8l40rnsSTmqTECQ,34915
|
|
4
|
+
pyEQL/equilibrium.py,sha256=YCtoAJSgn1WC9NJnc3H4FTJdKQvogsvCuj7HqlKMtww,8307
|
|
5
|
+
pyEQL/functions.py,sha256=nc-Hc61MmW-ELBR1PByJvQnELxM7PZexMHbU_O5-Bnw,10584
|
|
6
6
|
pyEQL/pint_custom_units.txt,sha256=XHmcMlwVvqF9nEW7_e9Xgyq-xWEr-cDYqieas11T3eY,2882
|
|
7
7
|
pyEQL/salt_ion_match.py,sha256=D4HJdV7iVsBEW5bW9tWpvc6pK-jrexTlvJV80YabHo4,4062
|
|
8
8
|
pyEQL/solute.py,sha256=R4XjPiAZP2787ImIo072CWD1vjIYjGPt6zvxCrVO1pA,5133
|
|
9
|
-
pyEQL/solution.py,sha256=
|
|
9
|
+
pyEQL/solution.py,sha256=PogXcBn3vPaK5EZy742-tW6om_kmrkitF7U7NeMj37g,115889
|
|
10
10
|
pyEQL/utils.py,sha256=2VLl_sqcKcqBrVdP0ogtIYsOlc-wl4repi43Jqr-8yg,4156
|
|
11
11
|
pyEQL/database/geothermal.dat,sha256=kksnfcBtWdOTpNn4CLXU1Mz16cwas2WuVKpuMU8CaVI,234230
|
|
12
12
|
pyEQL/database/llnl.dat,sha256=jN-a0kfUFbQlYMn2shTVRg1JX_ZhLa-tJ0lLw2YSpLU,751462
|
|
@@ -18,10 +18,10 @@ pyEQL/presets/rainwater.yaml,sha256=S0WHZNDfCJyjSSFxNFdkypjn2s3P0jJGCiYIxvi1ibA,
|
|
|
18
18
|
pyEQL/presets/seawater.yaml,sha256=oryc1CkhRz20RpWE6uiGiT93HoZnqlB0s-0PmBWr3-U,843
|
|
19
19
|
pyEQL/presets/urine.yaml,sha256=0Njtc-H1fFRo7UhquHdiSTT4z-8VZJ1utDCk02qk28M,679
|
|
20
20
|
pyEQL/presets/wastewater.yaml,sha256=jTTFBpmKxczaEtkCZb0xUULIPZt7wfC8eAJ6rthGnmw,502
|
|
21
|
-
pyEQL-1.0.
|
|
22
|
-
pyEQL-1.0.
|
|
23
|
-
pyEQL-1.0.
|
|
24
|
-
pyEQL-1.0.
|
|
25
|
-
pyEQL-1.0.
|
|
26
|
-
pyEQL-1.0.
|
|
27
|
-
pyEQL-1.0.
|
|
21
|
+
pyEQL-1.0.1.dist-info/AUTHORS.md,sha256=K9ZLhKFwZ2zLlFXwN62VuUYCpr5T6n4mOUCUHlytTUs,415
|
|
22
|
+
pyEQL-1.0.1.dist-info/COPYING,sha256=Ww2oUywfFTn242v9ksCgQdIVSpcMXJiKKePn0GFm25E,7649
|
|
23
|
+
pyEQL-1.0.1.dist-info/LICENSE.txt,sha256=2Zf1F7RzbpeposgIxUydpurqNCMoMgDi2gAB65_GjwQ,969
|
|
24
|
+
pyEQL-1.0.1.dist-info/METADATA,sha256=Nt-13evnZEXe5D0L8a-QrtIjwvYKo8cb2ew3UG2JwlQ,5889
|
|
25
|
+
pyEQL-1.0.1.dist-info/WHEEL,sha256=DZajD4pwLWue70CAfc7YaxT1wLUciNBvN_TTcvXpltE,110
|
|
26
|
+
pyEQL-1.0.1.dist-info/top_level.txt,sha256=QMOaZjCAm_lS4Njsjh4L0B5aWnJFGQMYKhuH88CG1co,6
|
|
27
|
+
pyEQL-1.0.1.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|