pyEQL 1.1.5__py3-none-any.whl → 1.1.6__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/functions.py +27 -13
- pyEQL/solution.py +4 -5
- pyEQL/utils.py +6 -0
- {pyEQL-1.1.5.dist-info → pyEQL-1.1.6.dist-info}/AUTHORS.md +7 -4
- {pyEQL-1.1.5.dist-info → pyEQL-1.1.6.dist-info}/METADATA +12 -12
- {pyEQL-1.1.5.dist-info → pyEQL-1.1.6.dist-info}/RECORD +10 -10
- {pyEQL-1.1.5.dist-info → pyEQL-1.1.6.dist-info}/WHEEL +1 -1
- {pyEQL-1.1.5.dist-info → pyEQL-1.1.6.dist-info}/COPYING +0 -0
- {pyEQL-1.1.5.dist-info → pyEQL-1.1.6.dist-info}/LICENSE.txt +0 -0
- {pyEQL-1.1.5.dist-info → pyEQL-1.1.6.dist-info}/top_level.txt +0 -0
pyEQL/functions.py
CHANGED
|
@@ -15,13 +15,17 @@ from pyEQL import Solution, ureg
|
|
|
15
15
|
logger = logging.getLogger(__name__)
|
|
16
16
|
|
|
17
17
|
|
|
18
|
-
def gibbs_mix(solution1: Solution, solution2: Solution):
|
|
18
|
+
def gibbs_mix(solution1: Solution, solution2: Solution, activity_correction: bool = True):
|
|
19
19
|
r"""
|
|
20
20
|
Return the Gibbs energy change associated with mixing two solutions.
|
|
21
21
|
|
|
22
22
|
Args:
|
|
23
23
|
solution1: a solution to be mixed.
|
|
24
24
|
solution2: a solution to be mixed.
|
|
25
|
+
activity_correction : bool, optional
|
|
26
|
+
If True, activities will be used to calculate the true energy of
|
|
27
|
+
mixing. If False, mole fraction will be used, resulting in a
|
|
28
|
+
calculation of the ideal energy of mixing (i.e., :math:`\Delta_{mix} S \times T` )
|
|
25
29
|
|
|
26
30
|
Returns:
|
|
27
31
|
The change in Gibbs energy associated with complete mixing of the
|
|
@@ -32,10 +36,17 @@ def gibbs_mix(solution1: Solution, solution2: Solution):
|
|
|
32
36
|
|
|
33
37
|
.. math::
|
|
34
38
|
|
|
35
|
-
\Delta_{mix}
|
|
39
|
+
\Delta_{mix} G_{true} = \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}
|
|
36
40
|
|
|
37
|
-
|
|
38
|
-
|
|
41
|
+
or
|
|
42
|
+
|
|
43
|
+
.. math::
|
|
44
|
+
|
|
45
|
+
\Delta_{mix} G_{ideal} = \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}
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
Where :math:`n` is the number of moles of substance, :math:`T` is the temperature in kelvin, :math:`a` is the activity of solute :math:`i`,
|
|
49
|
+
:math:`x` is the mole fraction of solute :math:`i`, and subscripts :math:`b`, :math:`c`, and :math:`d` refer to the concentrated, dilute, and blended
|
|
39
50
|
Solutions, respectively.
|
|
40
51
|
|
|
41
52
|
Note that dissociated ions must be counted as separate components,
|
|
@@ -56,7 +67,10 @@ def gibbs_mix(solution1: Solution, solution2: Solution):
|
|
|
56
67
|
for solution in term_list:
|
|
57
68
|
for solute in solution.components:
|
|
58
69
|
if solution.get_amount(solute, "fraction") != 0:
|
|
59
|
-
|
|
70
|
+
if activity_correction is True:
|
|
71
|
+
term_list[solution] += solution.get_amount(solute, "mol") * np.log(solution.get_activity(solute))
|
|
72
|
+
else:
|
|
73
|
+
term_list[solution] += solution.get_amount(solute, "mol") * np.log(solution.get_amount(solute, "fraction"))
|
|
60
74
|
|
|
61
75
|
return (ureg.R * blend.temperature.to("K") * (term_list[blend] - term_list[concentrate] - term_list[dilute])).to(
|
|
62
76
|
"J"
|
|
@@ -72,16 +86,16 @@ def entropy_mix(solution1: Solution, solution2: Solution):
|
|
|
72
86
|
|
|
73
87
|
Returns:
|
|
74
88
|
The ideal mixing entropy associated with complete mixing of the
|
|
75
|
-
Solutions, in Joules.
|
|
89
|
+
Solutions, in Joules per Kelvin.
|
|
76
90
|
|
|
77
91
|
Notes:
|
|
78
92
|
The ideal entropy of mixing is calculated as follows
|
|
79
93
|
|
|
80
94
|
.. math::
|
|
81
95
|
|
|
82
|
-
\Delta_{mix} S = \sum_i {(n_c + n_d) R
|
|
96
|
+
\Delta_{mix} S = \sum_i {(n_c + n_d) R \ln x_b} - \sum_i {n_c R \ln x_c} - \sum_i {n_d R \ln x_d}
|
|
83
97
|
|
|
84
|
-
Where :math:`n` is the number of moles of substance, :math:`T` is the temperature in kelvin,
|
|
98
|
+
Where :math:`n` is the number of moles of substance, :math:`T` is the temperature in kelvin, :math:`x` is the mole fraction of solute :math:`i`,
|
|
85
99
|
and subscripts :math:`b`, :math:`c`, and :math:`d` refer to the concentrated, dilute, and blended
|
|
86
100
|
Solutions, respectively.
|
|
87
101
|
|
|
@@ -107,8 +121,8 @@ def entropy_mix(solution1: Solution, solution2: Solution):
|
|
|
107
121
|
solution.get_amount(solute, "fraction")
|
|
108
122
|
)
|
|
109
123
|
|
|
110
|
-
return (ureg.R *
|
|
111
|
-
"J"
|
|
124
|
+
return (ureg.R * (term_list[blend] - term_list[concentrate] - term_list[dilute])).to(
|
|
125
|
+
"J/K"
|
|
112
126
|
)
|
|
113
127
|
|
|
114
128
|
|
|
@@ -135,9 +149,9 @@ def donnan_eql(solution: Solution, fixed_charge: str):
|
|
|
135
149
|
|
|
136
150
|
.. math::
|
|
137
151
|
|
|
138
|
-
\big(\frac{a_{-}}{\bar a_{-}} \big)^(\frac{1}{z_{-})
|
|
139
|
-
\big(\frac{\bar a_{+}}{a_{+}}\big)^(\frac{1}{z_{+})
|
|
140
|
-
\
|
|
152
|
+
\big(\frac{a_{-}}{\bar a_{-}} \big)^{(\frac{1}{z_{-}})}
|
|
153
|
+
\big(\frac{\bar a_{+}}{a_{+}}\big)^{(\frac{1}{z_{+}})}
|
|
154
|
+
=\exp \big(\frac{\Delta \pi \bar V}{RT z_{+} \nu_{+}}\big)
|
|
141
155
|
|
|
142
156
|
Where subscripts :math:`+` and :math:`-` indicate the cation and anion, respectively,
|
|
143
157
|
the overbar indicates the membrane phase,
|
pyEQL/solution.py
CHANGED
|
@@ -1161,7 +1161,8 @@ class Solution(MSONable):
|
|
|
1161
1161
|
:meth:`get_amount`
|
|
1162
1162
|
:func:`pyEQL.utils.interpret_units`
|
|
1163
1163
|
"""
|
|
1164
|
-
|
|
1164
|
+
_units = interpret_units(units)
|
|
1165
|
+
TOT: Quantity = ureg.Quantity(0, _units)
|
|
1165
1166
|
|
|
1166
1167
|
# standardize the element formula and units
|
|
1167
1168
|
el = str(Element(element.split("(")[0]))
|
|
@@ -1178,7 +1179,7 @@ class Solution(MSONable):
|
|
|
1178
1179
|
else:
|
|
1179
1180
|
species = []
|
|
1180
1181
|
for k, v in comp_by_element.items():
|
|
1181
|
-
if
|
|
1182
|
+
if k.split("(")[0] == el:
|
|
1182
1183
|
species.extend(v)
|
|
1183
1184
|
|
|
1184
1185
|
# loop through the species of interest, adding moles of element
|
|
@@ -2294,9 +2295,7 @@ class Solution(MSONable):
|
|
|
2294
2295
|
self.logger.info("balance_charge is None, so no charge balancing will be performed.")
|
|
2295
2296
|
return
|
|
2296
2297
|
|
|
2297
|
-
self.logger.info(
|
|
2298
|
-
f"Solution is not electroneutral (C.B. = {cb} eq/L). Adjusting {self._cb_species} to compensate."
|
|
2299
|
-
)
|
|
2298
|
+
self.logger.info(f"Adjusting {self._cb_species} to compensate.")
|
|
2300
2299
|
|
|
2301
2300
|
if self.balance_charge == "pH":
|
|
2302
2301
|
# the charge imbalance associated with the H+ / OH- system can be expressed
|
pyEQL/utils.py
CHANGED
|
@@ -137,6 +137,12 @@ def standardize_formula(formula: str):
|
|
|
137
137
|
elif sform == "C2I2ClO2[-1]":
|
|
138
138
|
sform = "CI2ClCOO[-1]"
|
|
139
139
|
|
|
140
|
+
# ammonium sulfate salts
|
|
141
|
+
elif sform == "H8S(NO2)2(aq)":
|
|
142
|
+
sform = "(NH4)2SO4(aq)"
|
|
143
|
+
elif sform == "H4SNO4[-1]":
|
|
144
|
+
sform = "NH4SO4[-1]"
|
|
145
|
+
|
|
140
146
|
# TODO - consider adding recognition of special formulas like MeOH for methanol or Cit for citrate
|
|
141
147
|
return sform
|
|
142
148
|
|
|
@@ -5,9 +5,12 @@ developed and maintained by the Kingsbury Lab at Princeton University.
|
|
|
5
5
|
|
|
6
6
|
Other contributors, listed alphabetically, are:
|
|
7
7
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
8
|
+
- Arpit Bhardwaj (@abhardwaj73)
|
|
9
|
+
- Dhruv Duseja (@DhruvDuseja)
|
|
10
|
+
- Hernan Grecco (@hgrecco)
|
|
11
|
+
- Jaebeom Park (@Jaebeom-P)
|
|
12
|
+
- Kirill Pushkarev (@kirill-push)
|
|
13
|
+
- Andrew Rosen (@arosen93)
|
|
14
|
+
- Sui Xiong Tay (@SuixiongTay)
|
|
12
15
|
|
|
13
16
|
(If you think that your name belongs here, please let the maintainer know)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: pyEQL
|
|
3
|
-
Version: 1.1.
|
|
3
|
+
Version: 1.1.6
|
|
4
4
|
Summary: A python interface for solution chemistry
|
|
5
5
|
Author-email: Ryan Kingsbury <kingsbury@princeton.edu>
|
|
6
6
|
Project-URL: Docs, https://pyeql.readthedocs.io/
|
|
@@ -21,13 +21,13 @@ License-File: LICENSE.txt
|
|
|
21
21
|
License-File: COPYING
|
|
22
22
|
License-File: AUTHORS.md
|
|
23
23
|
Requires-Dist: pint >=0.19
|
|
24
|
-
Requires-Dist: numpy <2
|
|
25
|
-
Requires-Dist: scipy
|
|
24
|
+
Requires-Dist: numpy <2,>1.26
|
|
25
|
+
Requires-Dist: scipy >=1.12
|
|
26
26
|
Requires-Dist: pymatgen ==2024.5.1
|
|
27
|
-
Requires-Dist: iapws
|
|
27
|
+
Requires-Dist: iapws >=1.5.3
|
|
28
28
|
Requires-Dist: monty >=2024.7.12
|
|
29
29
|
Requires-Dist: maggma >=0.67.0
|
|
30
|
-
Requires-Dist: phreeqpython
|
|
30
|
+
Requires-Dist: phreeqpython >=1.5.2
|
|
31
31
|
Provides-Extra: docs
|
|
32
32
|
Requires-Dist: sphinx >=3.2.1 ; extra == 'docs'
|
|
33
33
|
Requires-Dist: sphinx-rtd-theme ; extra == 'docs'
|
|
@@ -35,14 +35,14 @@ Requires-Dist: myst-parser[linkify] ; extra == 'docs'
|
|
|
35
35
|
Provides-Extra: full
|
|
36
36
|
Requires-Dist: rich ; extra == 'full'
|
|
37
37
|
Provides-Extra: testing
|
|
38
|
-
Requires-Dist: setuptools ; extra == 'testing'
|
|
39
|
-
Requires-Dist: pre-commit ; extra == 'testing'
|
|
40
|
-
Requires-Dist: pytest ; extra == 'testing'
|
|
41
|
-
Requires-Dist: pytest-cov ; extra == 'testing'
|
|
42
|
-
Requires-Dist: pytest-xdist ; extra == 'testing'
|
|
38
|
+
Requires-Dist: setuptools >=68 ; extra == 'testing'
|
|
39
|
+
Requires-Dist: pre-commit >=2 ; extra == 'testing'
|
|
40
|
+
Requires-Dist: pytest >=7 ; extra == 'testing'
|
|
41
|
+
Requires-Dist: pytest-cov >=2.11 ; extra == 'testing'
|
|
42
|
+
Requires-Dist: pytest-xdist >2 ; extra == 'testing'
|
|
43
43
|
Requires-Dist: black ; extra == 'testing'
|
|
44
|
-
Requires-Dist: mypy ; extra == 'testing'
|
|
45
|
-
Requires-Dist: ruff ; extra == 'testing'
|
|
44
|
+
Requires-Dist: mypy >1 ; extra == 'testing'
|
|
45
|
+
Requires-Dist: ruff >0.0.100 ; extra == 'testing'
|
|
46
46
|
Requires-Dist: tox <4 ; extra == 'testing'
|
|
47
47
|
|
|
48
48
|
[](https://pyeql.readthedocs.io/en/latest/)
|
|
@@ -2,11 +2,11 @@ pyEQL/__init__.py,sha256=OCp_PiQEPyVoi1VX0ursBzHJWN6nDS1Id6bTBOgqCYs,1999
|
|
|
2
2
|
pyEQL/activity_correction.py,sha256=eOixjgTd5hTrTRD5s6aPCCG12lAIH7-lRN0Z1qHu678,37151
|
|
3
3
|
pyEQL/engines.py,sha256=VbdQSPKlNehW96U1XxWYwjTy6k6WDpZZEx_Y4l3qZv4,34686
|
|
4
4
|
pyEQL/equilibrium.py,sha256=YCtoAJSgn1WC9NJnc3H4FTJdKQvogsvCuj7HqlKMtww,8307
|
|
5
|
-
pyEQL/functions.py,sha256=
|
|
5
|
+
pyEQL/functions.py,sha256=BZXkiNJWuo4ZMQSVdFQkwqQmzbSkwi-vg1DFFQuFsos,11435
|
|
6
6
|
pyEQL/salt_ion_match.py,sha256=0nCZXmeo67VqcyYWQpPx-81hjSvnsg8HFB3fIyfjW_k,4070
|
|
7
7
|
pyEQL/solute.py,sha256=no00Rc3tRfHmyht4wm2UXA1KZhKC45tWMO5QEkZY6yg,5140
|
|
8
|
-
pyEQL/solution.py,sha256=
|
|
9
|
-
pyEQL/utils.py,sha256=
|
|
8
|
+
pyEQL/solution.py,sha256=3hvtqeu5KzfFKzeOdKlzS_aIFPTNgUfhjIwBurfuGJw,115754
|
|
9
|
+
pyEQL/utils.py,sha256=zOXqg89jTqD1mPVAtlBbRxkpP9-IdHfJpK_1VDOywIQ,6828
|
|
10
10
|
pyEQL/database/geothermal.dat,sha256=kksnfcBtWdOTpNn4CLXU1Mz16cwas2WuVKpuMU8CaVI,234230
|
|
11
11
|
pyEQL/database/llnl.dat,sha256=jN-a0kfUFbQlYMn2shTVRg1JX_ZhLa-tJ0lLw2YSpLU,751462
|
|
12
12
|
pyEQL/database/phreeqc_license.txt,sha256=8W1r8VxC2kVptIMSU9sDFNASYqN7MdwKEtIWWfjTQuM,2906
|
|
@@ -17,10 +17,10 @@ pyEQL/presets/rainwater.yaml,sha256=S0WHZNDfCJyjSSFxNFdkypjn2s3P0jJGCiYIxvi1ibA,
|
|
|
17
17
|
pyEQL/presets/seawater.yaml,sha256=oryc1CkhRz20RpWE6uiGiT93HoZnqlB0s-0PmBWr3-U,843
|
|
18
18
|
pyEQL/presets/urine.yaml,sha256=0Njtc-H1fFRo7UhquHdiSTT4z-8VZJ1utDCk02qk28M,679
|
|
19
19
|
pyEQL/presets/wastewater.yaml,sha256=jTTFBpmKxczaEtkCZb0xUULIPZt7wfC8eAJ6rthGnmw,502
|
|
20
|
-
pyEQL-1.1.
|
|
21
|
-
pyEQL-1.1.
|
|
22
|
-
pyEQL-1.1.
|
|
23
|
-
pyEQL-1.1.
|
|
24
|
-
pyEQL-1.1.
|
|
25
|
-
pyEQL-1.1.
|
|
26
|
-
pyEQL-1.1.
|
|
20
|
+
pyEQL-1.1.6.dist-info/AUTHORS.md,sha256=Ukdh6f3jO6jdgl2k_r_xdNOuzmjXcISMQqUTSH39QVw,506
|
|
21
|
+
pyEQL-1.1.6.dist-info/COPYING,sha256=Ww2oUywfFTn242v9ksCgQdIVSpcMXJiKKePn0GFm25E,7649
|
|
22
|
+
pyEQL-1.1.6.dist-info/LICENSE.txt,sha256=2Zf1F7RzbpeposgIxUydpurqNCMoMgDi2gAB65_GjwQ,969
|
|
23
|
+
pyEQL-1.1.6.dist-info/METADATA,sha256=zV5ynllRkS-BEc8prG7c-e_7O7tR4MOvAgX2altqT08,6160
|
|
24
|
+
pyEQL-1.1.6.dist-info/WHEEL,sha256=UvcQYKBHoFqaQd6LKyqHw9fxEolWLQnlzP0h_LgJAfI,91
|
|
25
|
+
pyEQL-1.1.6.dist-info/top_level.txt,sha256=QMOaZjCAm_lS4Njsjh4L0B5aWnJFGQMYKhuH88CG1co,6
|
|
26
|
+
pyEQL-1.1.6.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|