pychnosz 1.1.11__cp312-cp312-win_amd64.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.
- pychnosz/__init__.py +129 -0
- pychnosz/biomolecules/__init__.py +29 -0
- pychnosz/biomolecules/ionize_aa.py +197 -0
- pychnosz/biomolecules/proteins.py +595 -0
- pychnosz/core/__init__.py +46 -0
- pychnosz/core/affinity.py +1256 -0
- pychnosz/core/animation.py +593 -0
- pychnosz/core/balance.py +334 -0
- pychnosz/core/basis.py +716 -0
- pychnosz/core/diagram.py +3336 -0
- pychnosz/core/equilibrate.py +813 -0
- pychnosz/core/equilibrium.py +554 -0
- pychnosz/core/info.py +821 -0
- pychnosz/core/retrieve.py +364 -0
- pychnosz/core/speciation.py +580 -0
- pychnosz/core/species.py +599 -0
- pychnosz/core/subcrt.py +1696 -0
- pychnosz/core/thermo.py +593 -0
- pychnosz/core/unicurve.py +1226 -0
- pychnosz/data/__init__.py +11 -0
- pychnosz/data/add_obigt.py +327 -0
- pychnosz/data/extdata/Berman/BDat17_2017.csv +2 -0
- pychnosz/data/extdata/Berman/Ber88_1988.csv +68 -0
- pychnosz/data/extdata/Berman/Ber90_1990.csv +5 -0
- pychnosz/data/extdata/Berman/DS10_2010.csv +6 -0
- pychnosz/data/extdata/Berman/FDM+14_2014.csv +2 -0
- pychnosz/data/extdata/Berman/Got04_2004.csv +5 -0
- pychnosz/data/extdata/Berman/JUN92_1992.csv +3 -0
- pychnosz/data/extdata/Berman/SHD91_1991.csv +12 -0
- pychnosz/data/extdata/Berman/VGT92_1992.csv +2 -0
- pychnosz/data/extdata/Berman/VPT01_2001.csv +3 -0
- pychnosz/data/extdata/Berman/VPV05_2005.csv +2 -0
- pychnosz/data/extdata/Berman/ZS92_1992.csv +11 -0
- pychnosz/data/extdata/Berman/sympy.R +99 -0
- pychnosz/data/extdata/Berman/testing/BA96.bib +12 -0
- pychnosz/data/extdata/Berman/testing/BA96_Berman.csv +21 -0
- pychnosz/data/extdata/Berman/testing/BA96_OBIGT.csv +21 -0
- pychnosz/data/extdata/Berman/testing/BA96_refs.csv +6 -0
- pychnosz/data/extdata/OBIGT/AD.csv +25 -0
- pychnosz/data/extdata/OBIGT/Berman_cr.csv +93 -0
- pychnosz/data/extdata/OBIGT/DEW.csv +211 -0
- pychnosz/data/extdata/OBIGT/H2O_aq.csv +4 -0
- pychnosz/data/extdata/OBIGT/SLOP98.csv +411 -0
- pychnosz/data/extdata/OBIGT/SUPCRT92.csv +178 -0
- pychnosz/data/extdata/OBIGT/inorganic_aq.csv +729 -0
- pychnosz/data/extdata/OBIGT/inorganic_cr.csv +273 -0
- pychnosz/data/extdata/OBIGT/inorganic_gas.csv +20 -0
- pychnosz/data/extdata/OBIGT/organic_aq.csv +1104 -0
- pychnosz/data/extdata/OBIGT/organic_cr.csv +481 -0
- pychnosz/data/extdata/OBIGT/organic_gas.csv +268 -0
- pychnosz/data/extdata/OBIGT/organic_liq.csv +533 -0
- pychnosz/data/extdata/OBIGT/testing/GEMSFIT.csv +43 -0
- pychnosz/data/extdata/OBIGT/testing/IGEM.csv +17 -0
- pychnosz/data/extdata/OBIGT/testing/Sandia.csv +8 -0
- pychnosz/data/extdata/OBIGT/testing/SiO2.csv +4 -0
- pychnosz/data/extdata/misc/AD03_Fig1a.csv +69 -0
- pychnosz/data/extdata/misc/AD03_Fig1b.csv +43 -0
- pychnosz/data/extdata/misc/AD03_Fig1c.csv +89 -0
- pychnosz/data/extdata/misc/AD03_Fig1d.csv +30 -0
- pychnosz/data/extdata/misc/BZA10.csv +5 -0
- pychnosz/data/extdata/misc/HW97_Cp.csv +90 -0
- pychnosz/data/extdata/misc/HWM96_V.csv +229 -0
- pychnosz/data/extdata/misc/LA19_test.csv +7 -0
- pychnosz/data/extdata/misc/Mer75_Table4.csv +42 -0
- pychnosz/data/extdata/misc/OBIGT_check.csv +423 -0
- pychnosz/data/extdata/misc/PM90.csv +7 -0
- pychnosz/data/extdata/misc/RH95.csv +23 -0
- pychnosz/data/extdata/misc/RH98_Table15.csv +17 -0
- pychnosz/data/extdata/misc/SC10_Rainbow.csv +19 -0
- pychnosz/data/extdata/misc/SK95.csv +55 -0
- pychnosz/data/extdata/misc/SOJSH.csv +61 -0
- pychnosz/data/extdata/misc/SS98_Fig5a.csv +81 -0
- pychnosz/data/extdata/misc/SS98_Fig5b.csv +84 -0
- pychnosz/data/extdata/misc/TKSS14_Fig2.csv +25 -0
- pychnosz/data/extdata/misc/bluered.txt +1000 -0
- pychnosz/data/extdata/protein/Cas/Cas_aa.csv +177 -0
- pychnosz/data/extdata/protein/Cas/Cas_uniprot.csv +186 -0
- pychnosz/data/extdata/protein/Cas/download.R +34 -0
- pychnosz/data/extdata/protein/Cas/mkaa.R +34 -0
- pychnosz/data/extdata/protein/POLG.csv +12 -0
- pychnosz/data/extdata/protein/TBD+05.csv +393 -0
- pychnosz/data/extdata/protein/TBD+05_aa.csv +393 -0
- pychnosz/data/extdata/protein/rubisco.csv +28 -0
- pychnosz/data/extdata/protein/rubisco.fasta +239 -0
- pychnosz/data/extdata/protein/rubisco_aa.csv +28 -0
- pychnosz/data/extdata/src/H2O92D.f.orig +3457 -0
- pychnosz/data/extdata/src/README.txt +5 -0
- pychnosz/data/extdata/taxonomy/names.dmp +215 -0
- pychnosz/data/extdata/taxonomy/nodes.dmp +63 -0
- pychnosz/data/extdata/thermo/Bdot_acirc.csv +60 -0
- pychnosz/data/extdata/thermo/buffer.csv +40 -0
- pychnosz/data/extdata/thermo/element.csv +135 -0
- pychnosz/data/extdata/thermo/groups.csv +6 -0
- pychnosz/data/extdata/thermo/opt.csv +2 -0
- pychnosz/data/extdata/thermo/protein.csv +506 -0
- pychnosz/data/extdata/thermo/refs.csv +343 -0
- pychnosz/data/extdata/thermo/stoich.csv.xz +0 -0
- pychnosz/data/loader.py +431 -0
- pychnosz/data/mod_obigt.py +322 -0
- pychnosz/data/obigt.py +471 -0
- pychnosz/data/worm.py +228 -0
- pychnosz/fortran/__init__.py +16 -0
- pychnosz/fortran/h2o92.dll +0 -0
- pychnosz/fortran/h2o92_interface.py +527 -0
- pychnosz/geochemistry/__init__.py +21 -0
- pychnosz/geochemistry/minerals.py +514 -0
- pychnosz/geochemistry/redox.py +500 -0
- pychnosz/models/__init__.py +47 -0
- pychnosz/models/archer_wang.py +165 -0
- pychnosz/models/berman.py +309 -0
- pychnosz/models/cgl.py +381 -0
- pychnosz/models/dew.py +997 -0
- pychnosz/models/hkf.py +523 -0
- pychnosz/models/hkf_helpers.py +231 -0
- pychnosz/models/iapws95.py +1113 -0
- pychnosz/models/supcrt92_fortran.py +238 -0
- pychnosz/models/water.py +480 -0
- pychnosz/utils/__init__.py +27 -0
- pychnosz/utils/expression.py +1074 -0
- pychnosz/utils/formula.py +830 -0
- pychnosz/utils/formula_ox.py +227 -0
- pychnosz/utils/reset.py +33 -0
- pychnosz/utils/units.py +259 -0
- pychnosz-1.1.11.dist-info/METADATA +197 -0
- pychnosz-1.1.11.dist-info/RECORD +128 -0
- pychnosz-1.1.11.dist-info/WHEEL +5 -0
- pychnosz-1.1.11.dist-info/licenses/LICENSE.txt +19 -0
- pychnosz-1.1.11.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Formula oxidation state utilities for CHNOSZ.
|
|
3
|
+
|
|
4
|
+
This module provides functions for working with chemical formulas that include
|
|
5
|
+
element oxidation states, specifically for use with the WORM thermodynamic database.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from typing import Union, Dict, List
|
|
9
|
+
import pandas as pd
|
|
10
|
+
|
|
11
|
+
from ..core.thermo import thermo
|
|
12
|
+
from ..core.info import info
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def get_formula_ox(name: Union[str, int]) -> Dict[str, float]:
|
|
16
|
+
"""
|
|
17
|
+
Get quantities of elements and their oxidation states in a chemical compound.
|
|
18
|
+
|
|
19
|
+
This function only works when a thermodynamic database with the 'formula_ox'
|
|
20
|
+
column is loaded (e.g., the WORM database). For example, an input of "magnetite"
|
|
21
|
+
would return the following: {'Fe+3': 2.0, 'Fe+2': 1.0, 'O-2': 4.0}.
|
|
22
|
+
|
|
23
|
+
Parameters
|
|
24
|
+
----------
|
|
25
|
+
name : str or int
|
|
26
|
+
The name or database index of the chemical species of interest. Example:
|
|
27
|
+
"magnetite" or 738.
|
|
28
|
+
|
|
29
|
+
Returns
|
|
30
|
+
-------
|
|
31
|
+
dict
|
|
32
|
+
A dictionary where each key represents an element in a specific
|
|
33
|
+
oxidation state, and its value is the number of that element in the
|
|
34
|
+
chemical species' formula.
|
|
35
|
+
|
|
36
|
+
Raises
|
|
37
|
+
------
|
|
38
|
+
TypeError
|
|
39
|
+
If input is not a string or integer.
|
|
40
|
+
AttributeError
|
|
41
|
+
If the WORM thermodynamic database is not loaded (no formula_ox attribute).
|
|
42
|
+
ValueError
|
|
43
|
+
If the species is not found in the database or does not have oxidation
|
|
44
|
+
state information.
|
|
45
|
+
|
|
46
|
+
Examples
|
|
47
|
+
--------
|
|
48
|
+
>>> import pychnosz
|
|
49
|
+
>>> # Load the WORM database
|
|
50
|
+
>>> pychnosz.thermo("WORM")
|
|
51
|
+
>>> # Get formula with oxidation states for magnetite
|
|
52
|
+
>>> pychnosz.get_formula_ox("magnetite")
|
|
53
|
+
{'Fe+3': 2.0, 'Fe+2': 1.0, 'O-2': 4.0}
|
|
54
|
+
>>> # Can also use species index
|
|
55
|
+
>>> pychnosz.get_formula_ox(738)
|
|
56
|
+
{'Fe+3': 2.0, 'Fe+2': 1.0, 'O-2': 4.0}
|
|
57
|
+
|
|
58
|
+
Notes
|
|
59
|
+
-----
|
|
60
|
+
This function requires the wormutils package to be installed for parsing
|
|
61
|
+
the formula_ox strings. Install it with: pip install wormutils
|
|
62
|
+
"""
|
|
63
|
+
|
|
64
|
+
# Import parse_formula_ox from wormutils
|
|
65
|
+
try:
|
|
66
|
+
from wormutils import parse_formula_ox
|
|
67
|
+
except ImportError:
|
|
68
|
+
raise ImportError(
|
|
69
|
+
"The wormutils package is required to use get_formula_ox(). "
|
|
70
|
+
"Install it with: pip install wormutils"
|
|
71
|
+
)
|
|
72
|
+
|
|
73
|
+
# Validate input type
|
|
74
|
+
if not isinstance(name, str) and not isinstance(name, int):
|
|
75
|
+
raise TypeError(
|
|
76
|
+
"Must provide input as a string (chemical species name) or "
|
|
77
|
+
"an integer (chemical species index)."
|
|
78
|
+
)
|
|
79
|
+
|
|
80
|
+
# Get the thermo system
|
|
81
|
+
thermo_sys = thermo()
|
|
82
|
+
|
|
83
|
+
# Convert index to name if necessary
|
|
84
|
+
if isinstance(name, int):
|
|
85
|
+
species_info = info(name, messages=False)
|
|
86
|
+
if species_info is None or len(species_info) == 0:
|
|
87
|
+
raise ValueError(f"Species index {name} not found in the database.")
|
|
88
|
+
name = species_info.name.iloc[0]
|
|
89
|
+
|
|
90
|
+
# Check if formula_ox exists in thermo()
|
|
91
|
+
if not hasattr(thermo_sys, 'formula_ox') or thermo_sys.formula_ox is None:
|
|
92
|
+
raise AttributeError(
|
|
93
|
+
"The 'formula_ox' attribute is not available. "
|
|
94
|
+
"This function only works when the WORM thermodynamic database "
|
|
95
|
+
"is loaded. Load it with: pychnosz.thermo('WORM')"
|
|
96
|
+
)
|
|
97
|
+
|
|
98
|
+
df = thermo_sys.formula_ox
|
|
99
|
+
|
|
100
|
+
# Check if the species name exists in the database
|
|
101
|
+
if name not in list(df["name"]):
|
|
102
|
+
raise ValueError(
|
|
103
|
+
f"The species '{name}' was not found in the loaded thermodynamic database."
|
|
104
|
+
)
|
|
105
|
+
|
|
106
|
+
# Get the formula_ox string for this species
|
|
107
|
+
try:
|
|
108
|
+
formula_ox_str = df[df["name"] == name]["formula_ox"].iloc[0]
|
|
109
|
+
except (KeyError, IndexError):
|
|
110
|
+
raise ValueError(
|
|
111
|
+
f"The species '{name}' does not have elemental oxidation states "
|
|
112
|
+
"given in the 'formula_ox' column of the loaded thermodynamic database."
|
|
113
|
+
)
|
|
114
|
+
|
|
115
|
+
# Check if formula_ox is valid (not NaN or empty)
|
|
116
|
+
if formula_ox_str is None or (isinstance(formula_ox_str, float) and pd.isna(formula_ox_str)) or formula_ox_str == "":
|
|
117
|
+
raise ValueError(
|
|
118
|
+
f"The species '{name}' does not have elemental oxidation states "
|
|
119
|
+
"given in the 'formula_ox' column of the loaded thermodynamic database."
|
|
120
|
+
)
|
|
121
|
+
|
|
122
|
+
# Parse the formula_ox string and return
|
|
123
|
+
return parse_formula_ox(formula_ox_str)
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
def get_n_element_ox(names: Union[str, int, List[Union[str, int]], pd.Series],
|
|
127
|
+
element_ox: str,
|
|
128
|
+
binary: bool = False) -> List[Union[float, bool]]:
|
|
129
|
+
"""
|
|
130
|
+
Get the number of an element of a chosen oxidation state in chemical species formulas.
|
|
131
|
+
|
|
132
|
+
This function only works when a thermodynamic database with the 'formula_ox'
|
|
133
|
+
column is loaded (e.g., the WORM database).
|
|
134
|
+
|
|
135
|
+
If binary is False, returns a list containing the number of the chosen
|
|
136
|
+
element and oxidation state in the chemical species. For example, how many
|
|
137
|
+
ferrous irons are in the formulae of hematite, fayalite, and magnetite,
|
|
138
|
+
respectively?
|
|
139
|
+
|
|
140
|
+
>>> get_n_element_ox(names=["hematite", "fayalite", "magnetite"],
|
|
141
|
+
... element_ox="Fe+2",
|
|
142
|
+
... binary=False)
|
|
143
|
+
[0, 2.0, 1.0]
|
|
144
|
+
|
|
145
|
+
If binary is True, returns a list of whether or not ferrous iron is in their
|
|
146
|
+
formulas:
|
|
147
|
+
|
|
148
|
+
>>> get_n_element_ox(names=["hematite", "fayalite", "magnetite"],
|
|
149
|
+
... element_ox="Fe+2",
|
|
150
|
+
... binary=True)
|
|
151
|
+
[False, True, True]
|
|
152
|
+
|
|
153
|
+
Parameters
|
|
154
|
+
----------
|
|
155
|
+
names : str, int, list of str/int, or pd.Series
|
|
156
|
+
The name or database index of a chemical species, or a list of
|
|
157
|
+
names or indices. Can also be a pandas Series (e.g., from retrieve()).
|
|
158
|
+
Example: ["hematite", "fayalite", "magnetite"] or [788, 782, 798].
|
|
159
|
+
element_ox : str
|
|
160
|
+
An element with a specific oxidation state. For example: "Fe+2" for
|
|
161
|
+
ferrous iron.
|
|
162
|
+
binary : bool, default False
|
|
163
|
+
Should the output list show True/False for presence or absence of the
|
|
164
|
+
element defined by `element_ox`? By default, this parameter is set to
|
|
165
|
+
False so the output list shows quantities of the element instead.
|
|
166
|
+
|
|
167
|
+
Returns
|
|
168
|
+
-------
|
|
169
|
+
list of float or list of bool
|
|
170
|
+
A list containing quantities of the chosen element oxidation state in
|
|
171
|
+
the formulas of the chemical species (if `binary=False`) or whether the
|
|
172
|
+
chosen element oxidation state is present in the formulae (if `binary=True`).
|
|
173
|
+
|
|
174
|
+
Raises
|
|
175
|
+
------
|
|
176
|
+
AttributeError
|
|
177
|
+
If the WORM thermodynamic database is not loaded (no formula_ox attribute).
|
|
178
|
+
ValueError
|
|
179
|
+
If a species is not found in the database or does not have oxidation
|
|
180
|
+
state information.
|
|
181
|
+
|
|
182
|
+
Examples
|
|
183
|
+
--------
|
|
184
|
+
>>> import pychnosz
|
|
185
|
+
>>> # Load the WORM database
|
|
186
|
+
>>> pychnosz.thermo("WORM")
|
|
187
|
+
>>> # Get counts of Fe+2 in several minerals
|
|
188
|
+
>>> pychnosz.get_n_element_ox(["hematite", "fayalite", "magnetite"], "Fe+2")
|
|
189
|
+
[0, 2.0, 1.0]
|
|
190
|
+
>>> # Get binary presence/absence
|
|
191
|
+
>>> pychnosz.get_n_element_ox(["hematite", "fayalite", "magnetite"], "Fe+2", binary=True)
|
|
192
|
+
[False, True, True]
|
|
193
|
+
>>> # Can also use with retrieve()
|
|
194
|
+
>>> r = pychnosz.retrieve("Fe", ["Si", "O", "H"], state=["cr"])
|
|
195
|
+
>>> pychnosz.get_n_element_ox(r, "Fe+2")
|
|
196
|
+
[1, 0, 0, 2.0, 1, 0, 1, 3.0, 1, 3.0, 0, 7.0]
|
|
197
|
+
|
|
198
|
+
Notes
|
|
199
|
+
-----
|
|
200
|
+
This function requires the wormutils package to be installed for parsing
|
|
201
|
+
the formula_ox strings. Install it with: pip install wormutils
|
|
202
|
+
"""
|
|
203
|
+
|
|
204
|
+
# Handle pandas Series (e.g., from retrieve())
|
|
205
|
+
if isinstance(names, pd.Series):
|
|
206
|
+
# Convert Series to list of indices
|
|
207
|
+
names = names.values.tolist()
|
|
208
|
+
# Handle single name/index
|
|
209
|
+
elif not isinstance(names, list):
|
|
210
|
+
names = [names]
|
|
211
|
+
|
|
212
|
+
# Get the count of element_ox for each species
|
|
213
|
+
n_list = []
|
|
214
|
+
for name in names:
|
|
215
|
+
# Get the formula_ox dictionary for this species
|
|
216
|
+
formula_ox_dict = get_formula_ox(name)
|
|
217
|
+
# Get the count of element_ox (default to 0 if not present)
|
|
218
|
+
count = formula_ox_dict.get(element_ox, 0)
|
|
219
|
+
n_list.append(count)
|
|
220
|
+
|
|
221
|
+
# Convert to binary if requested
|
|
222
|
+
if binary:
|
|
223
|
+
out_list = [True if n != 0 else False for n in n_list]
|
|
224
|
+
else:
|
|
225
|
+
out_list = n_list
|
|
226
|
+
|
|
227
|
+
return out_list
|
pychnosz/utils/reset.py
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Reset function for initializing the CHNOSZ thermodynamic system.
|
|
3
|
+
|
|
4
|
+
This provides the reset() function that initializes/resets the global
|
|
5
|
+
thermodynamic system, equivalent to reset() in the R version.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from ..core.thermo import get_thermo_system
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def reset(messages: bool = True):
|
|
12
|
+
"""
|
|
13
|
+
Initialize or reset the CHNOSZ thermodynamic system.
|
|
14
|
+
|
|
15
|
+
This function initializes the global thermodynamic system by loading
|
|
16
|
+
all thermodynamic data files, setting up the OBIGT database, and
|
|
17
|
+
preparing the system for calculations.
|
|
18
|
+
|
|
19
|
+
This is equivalent to the reset() function in the R version of CHNOSZ.
|
|
20
|
+
|
|
21
|
+
Parameters
|
|
22
|
+
----------
|
|
23
|
+
messages : bool, default True
|
|
24
|
+
Whether to print informational messages
|
|
25
|
+
|
|
26
|
+
Examples
|
|
27
|
+
--------
|
|
28
|
+
>>> import pychnosz
|
|
29
|
+
>>> pychnosz.reset() # Initialize the system
|
|
30
|
+
reset: thermodynamic system initialized
|
|
31
|
+
"""
|
|
32
|
+
thermo_system = get_thermo_system()
|
|
33
|
+
thermo_system.reset(messages=messages)
|
pychnosz/utils/units.py
ADDED
|
@@ -0,0 +1,259 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Unit conversion utilities for CHNOSZ.
|
|
3
|
+
|
|
4
|
+
This module provides functions for converting between different units commonly
|
|
5
|
+
used in geochemical thermodynamics, such as temperature (C/K), pressure (bar/MPa),
|
|
6
|
+
energy (cal/J), and electrochemical potentials (Eh/pe).
|
|
7
|
+
|
|
8
|
+
Based on R CHNOSZ util.units.R
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
import numpy as np
|
|
12
|
+
from typing import Union, List, Optional
|
|
13
|
+
import warnings
|
|
14
|
+
|
|
15
|
+
from ..core.thermo import thermo
|
|
16
|
+
from ..core.subcrt import subcrt
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def convert(value: Union[float, np.ndarray, List[float]],
|
|
20
|
+
units: str,
|
|
21
|
+
T: Union[float, np.ndarray] = 298.15,
|
|
22
|
+
P: Union[float, np.ndarray] = 1,
|
|
23
|
+
pH: Union[float, np.ndarray] = 7,
|
|
24
|
+
logaH2O: Union[float, np.ndarray] = 0,
|
|
25
|
+
messages: bool = True) -> Union[float, np.ndarray]:
|
|
26
|
+
"""
|
|
27
|
+
Convert values to the specified units.
|
|
28
|
+
|
|
29
|
+
This function converts thermodynamic values between different units commonly
|
|
30
|
+
used in geochemistry.
|
|
31
|
+
|
|
32
|
+
Parameters
|
|
33
|
+
----------
|
|
34
|
+
value : float, ndarray, or list
|
|
35
|
+
Value(s) to convert
|
|
36
|
+
units : str
|
|
37
|
+
Target units. Options include:
|
|
38
|
+
- Temperature: 'C', 'K'
|
|
39
|
+
- Energy: 'J', 'cal'
|
|
40
|
+
- Pressure: 'bar', 'MPa'
|
|
41
|
+
- Thermodynamic: 'G', 'logK'
|
|
42
|
+
- Electrochemical: 'Eh', 'pe', 'E0', 'logfO2'
|
|
43
|
+
- Volume: 'cm3bar', 'joules'
|
|
44
|
+
T : float or ndarray, default 298.15
|
|
45
|
+
Temperature in K (for Eh/pe/logK conversions)
|
|
46
|
+
P : float or ndarray, default 1
|
|
47
|
+
Pressure in bar (for E0/logfO2 conversions)
|
|
48
|
+
pH : float or ndarray, default 7
|
|
49
|
+
pH value (for E0/logfO2 conversions)
|
|
50
|
+
logaH2O : float or ndarray, default 0
|
|
51
|
+
Log activity of water (for E0/logfO2 conversions)
|
|
52
|
+
messages : bool, default True
|
|
53
|
+
Whether to print informational messages
|
|
54
|
+
|
|
55
|
+
Returns
|
|
56
|
+
-------
|
|
57
|
+
float or ndarray
|
|
58
|
+
Converted value(s)
|
|
59
|
+
|
|
60
|
+
Examples
|
|
61
|
+
--------
|
|
62
|
+
>>> convert(25, 'K') # Convert 25°C to K
|
|
63
|
+
298.15
|
|
64
|
+
>>> convert(1.0, 'pe', T=298.15) # Convert 1V Eh to pe
|
|
65
|
+
16.9
|
|
66
|
+
"""
|
|
67
|
+
|
|
68
|
+
if value is None:
|
|
69
|
+
return None
|
|
70
|
+
|
|
71
|
+
# Convert to numpy array for uniform handling
|
|
72
|
+
value = np.asarray(value)
|
|
73
|
+
T = np.asarray(T)
|
|
74
|
+
P = np.asarray(P)
|
|
75
|
+
pH = np.asarray(pH)
|
|
76
|
+
logaH2O = np.asarray(logaH2O)
|
|
77
|
+
|
|
78
|
+
units = units.lower()
|
|
79
|
+
|
|
80
|
+
# Temperature conversions (C <-> K)
|
|
81
|
+
if units in ['c', 'k']:
|
|
82
|
+
CK = 273.15
|
|
83
|
+
if units == 'k':
|
|
84
|
+
return value + CK
|
|
85
|
+
if units == 'c':
|
|
86
|
+
return value - CK
|
|
87
|
+
|
|
88
|
+
# Energy conversions (J <-> cal)
|
|
89
|
+
elif units in ['j', 'cal']:
|
|
90
|
+
Jcal = 4.184
|
|
91
|
+
if units == 'j':
|
|
92
|
+
return value * Jcal
|
|
93
|
+
if units == 'cal':
|
|
94
|
+
return value / Jcal
|
|
95
|
+
|
|
96
|
+
# Gibbs energy <-> logK conversions
|
|
97
|
+
elif units in ['g', 'logk']:
|
|
98
|
+
# Gas constant (J K^-1 mol^-1)
|
|
99
|
+
R = 8.314463 # NIST value
|
|
100
|
+
if units == 'logk':
|
|
101
|
+
return value / (-np.log(10) * R * T)
|
|
102
|
+
if units == 'g':
|
|
103
|
+
return value * (-np.log(10) * R * T)
|
|
104
|
+
|
|
105
|
+
# Volume conversions (cm3bar <-> joules)
|
|
106
|
+
elif units in ['cm3bar', 'joules']:
|
|
107
|
+
if units == 'cm3bar':
|
|
108
|
+
return value * 10
|
|
109
|
+
if units == 'joules':
|
|
110
|
+
return value / 10
|
|
111
|
+
|
|
112
|
+
# Electrochemical potential conversions (Eh <-> pe)
|
|
113
|
+
elif units in ['eh', 'pe']:
|
|
114
|
+
R = 0.00831470 # Gas constant in kJ K^-1 mol^-1
|
|
115
|
+
F = 96.4935 # Faraday constant in kJ V^-1 mol^-1
|
|
116
|
+
if units == 'pe':
|
|
117
|
+
return value * F / (np.log(10) * R * T)
|
|
118
|
+
if units == 'eh':
|
|
119
|
+
return value * (np.log(10) * R * T) / F
|
|
120
|
+
|
|
121
|
+
# Pressure conversions (bar <-> MPa)
|
|
122
|
+
elif units in ['bar', 'mpa']:
|
|
123
|
+
barmpa = 10
|
|
124
|
+
if units == 'mpa':
|
|
125
|
+
return value / barmpa
|
|
126
|
+
if units == 'bar':
|
|
127
|
+
return value * barmpa
|
|
128
|
+
|
|
129
|
+
# Eh <-> logfO2 conversions
|
|
130
|
+
elif units in ['e0', 'logfo2']:
|
|
131
|
+
# Calculate equilibrium constant for: H2O = 1/2 O2 + 2 H+ + 2 e-
|
|
132
|
+
# Handle P="Psat" case (pass it directly to subcrt)
|
|
133
|
+
# Check if P is a string (including numpy string types)
|
|
134
|
+
P_is_psat = False
|
|
135
|
+
if isinstance(P, (str, np.str_)):
|
|
136
|
+
P_is_psat = str(P).lower() == 'psat'
|
|
137
|
+
elif isinstance(P, (list, tuple)):
|
|
138
|
+
# P is a list/tuple - check if it's a single-element string
|
|
139
|
+
if len(P) == 1 and isinstance(P[0], (str, np.str_)):
|
|
140
|
+
P_is_psat = str(P[0]).lower() == 'psat'
|
|
141
|
+
elif isinstance(P, np.ndarray):
|
|
142
|
+
# P is a numpy array
|
|
143
|
+
if P.ndim == 0:
|
|
144
|
+
# Scalar array - check if it's a string
|
|
145
|
+
try:
|
|
146
|
+
if isinstance(P.item(), (str, np.str_)):
|
|
147
|
+
P_is_psat = str(P.item()).lower() == 'psat'
|
|
148
|
+
except (ValueError, AttributeError):
|
|
149
|
+
pass
|
|
150
|
+
elif P.size == 1:
|
|
151
|
+
# Single-element array - check if it's a string
|
|
152
|
+
try:
|
|
153
|
+
if isinstance(P.flat[0], (str, np.str_)):
|
|
154
|
+
P_is_psat = str(P.flat[0]).lower() == 'psat'
|
|
155
|
+
except (ValueError, AttributeError, IndexError):
|
|
156
|
+
pass
|
|
157
|
+
|
|
158
|
+
if P_is_psat:
|
|
159
|
+
P_arg = 'Psat'
|
|
160
|
+
T_arg = np.atleast_1d(T)
|
|
161
|
+
if len(T_arg) == 1:
|
|
162
|
+
T_arg = float(T_arg[0])
|
|
163
|
+
else:
|
|
164
|
+
T_arg = T_arg.tolist()
|
|
165
|
+
else:
|
|
166
|
+
# Convert T and P to proper format for subcrt
|
|
167
|
+
T_vals = np.atleast_1d(T)
|
|
168
|
+
P_vals = np.atleast_1d(P)
|
|
169
|
+
|
|
170
|
+
# subcrt needs lists for multiple T/P values
|
|
171
|
+
if len(T_vals) > 1 or len(P_vals) > 1:
|
|
172
|
+
T_arg = T_vals.tolist() if len(T_vals) > 1 else float(T_vals[0])
|
|
173
|
+
P_arg = P_vals.tolist() if len(P_vals) > 1 else float(P_vals[0])
|
|
174
|
+
else:
|
|
175
|
+
T_arg = float(T_vals[0])
|
|
176
|
+
P_arg = float(P_vals[0])
|
|
177
|
+
|
|
178
|
+
supcrt_out = subcrt(['H2O', 'oxygen', 'H+', 'e-'],
|
|
179
|
+
[-1, 0.5, 2, 2],
|
|
180
|
+
T=T_arg, P=P_arg, convert=False, messages=messages, show=False)
|
|
181
|
+
|
|
182
|
+
# Extract logK values
|
|
183
|
+
if hasattr(supcrt_out.out, 'logK'):
|
|
184
|
+
logK = supcrt_out.out.logK
|
|
185
|
+
else:
|
|
186
|
+
logK = supcrt_out.out['logK']
|
|
187
|
+
|
|
188
|
+
# Convert to numpy array
|
|
189
|
+
logK = np.asarray(logK)
|
|
190
|
+
|
|
191
|
+
if units == 'logfo2':
|
|
192
|
+
# Convert Eh to logfO2
|
|
193
|
+
pe_value = convert(value, 'pe', T=T, messages=messages)
|
|
194
|
+
return 2 * (logK + logaH2O + 2*pH + 2*pe_value)
|
|
195
|
+
if units == 'e0':
|
|
196
|
+
# Convert logfO2 to Eh
|
|
197
|
+
pe_value = (-logK - 2*pH + value/2 - logaH2O) / 2
|
|
198
|
+
return convert(pe_value, 'Eh', T=T, messages=messages)
|
|
199
|
+
|
|
200
|
+
else:
|
|
201
|
+
warnings.warn(f"convert: no conversion to {units} found")
|
|
202
|
+
return value
|
|
203
|
+
|
|
204
|
+
|
|
205
|
+
def envert(value: Union[float, np.ndarray, List[float]],
|
|
206
|
+
units: str) -> Union[float, np.ndarray]:
|
|
207
|
+
"""
|
|
208
|
+
Convert values to the specified units from those given in thermo()$opt.
|
|
209
|
+
|
|
210
|
+
This function is used internally to convert from the user's preferred units
|
|
211
|
+
(stored in thermo().opt) to standard internal units.
|
|
212
|
+
|
|
213
|
+
Parameters
|
|
214
|
+
----------
|
|
215
|
+
value : float, ndarray, or list
|
|
216
|
+
Value(s) to convert
|
|
217
|
+
units : str
|
|
218
|
+
Target units ('C', 'K', 'bar', 'MPa', 'J', 'cal')
|
|
219
|
+
|
|
220
|
+
Returns
|
|
221
|
+
-------
|
|
222
|
+
float or ndarray
|
|
223
|
+
Converted value(s)
|
|
224
|
+
"""
|
|
225
|
+
|
|
226
|
+
if not isinstance(value, (int, float, np.ndarray, list)):
|
|
227
|
+
return value
|
|
228
|
+
|
|
229
|
+
value = np.asarray(value)
|
|
230
|
+
|
|
231
|
+
# Check if first element is numeric
|
|
232
|
+
if value.size > 0 and not np.issubdtype(value.dtype, np.number):
|
|
233
|
+
return value
|
|
234
|
+
|
|
235
|
+
units = units.lower()
|
|
236
|
+
opt = thermo().opt
|
|
237
|
+
|
|
238
|
+
# Temperature conversions
|
|
239
|
+
if units in ['c', 'k', 't.units']:
|
|
240
|
+
if units == 'c' and opt['T.units'] == 'K':
|
|
241
|
+
return convert(value, 'c')
|
|
242
|
+
if units == 'k' and opt['T.units'] == 'C':
|
|
243
|
+
return convert(value, 'k')
|
|
244
|
+
|
|
245
|
+
# Energy conversions
|
|
246
|
+
if units in ['j', 'cal', 'e.units']:
|
|
247
|
+
if units == 'j' and opt['E.units'] == 'cal':
|
|
248
|
+
return convert(value, 'j')
|
|
249
|
+
if units == 'cal' and opt['E.units'] == 'J':
|
|
250
|
+
return convert(value, 'cal')
|
|
251
|
+
|
|
252
|
+
# Pressure conversions
|
|
253
|
+
if units in ['bar', 'mpa', 'p.units']:
|
|
254
|
+
if units == 'mpa' and opt['P.units'] == 'bar':
|
|
255
|
+
return convert(value, 'mpa')
|
|
256
|
+
if units == 'bar' and opt['P.units'] == 'MPa':
|
|
257
|
+
return convert(value, 'bar')
|
|
258
|
+
|
|
259
|
+
return value
|