"""
pyEQL methods for chemical equilibrium calculations (e.g. acid/base, reactions,
redox, complexation, etc.).
NOTE: these methods are not currently used but are here for the future.
:copyright: 2013-2023 by Ryan S. Kingsbury
:license: LGPL, see LICENSE for more details.
"""
# import libraries for scientific functions
import math
# the pint unit registry
from pyEQL import unit
from pyEQL.logging_system import logger
[docs]def adjust_temp_pitzer(c1, c2, c3, c4, c5, temp, temp_ref=unit.Quantity("298.15 K")):
"""
Calculate a parameter for the Pitzer model based on temperature-dependent
coefficients c1,c2,c3,c4,and c5.
Parameters
----------
c1, c2, c3, c4, c5: float
Temperature-dependent coefficients for the pitzer parameter of
interest.
temp: Quantity
The temperature at which the Pitzer parameter is to be calculated
temp_ref: Quantity, optional
The reference temperature on which the parameters are based.
298.15 K if omitted.
As described in the PHREEQC documentation
"""
return (
c1
+ c2 * (1 / temp + 1 / temp_ref)
+ c2 * math.log(temp / temp_ref)
+ c3 * (temp - temp_ref)
+ c4 * (temp**2 - temp_ref**2)
+ c5 * (temp**-2 - temp_ref**-2)
)
[docs]def adjust_temp_vanthoff(equilibrium_constant, enthalpy, temperature, reference_temperature=25 * unit.Quantity("degC")):
r"""(float,float,number, optional number) -> float.
Adjust a reaction equilibrium constant from one temperature to another.
Parameters
----------
equilibrium_constant : float
The reaction equilibrium constant for the reaction
enthalpy : Quantity
The enthalpy change (delta H) for the reaction in kJ/mol. Assumed
independent of temperature (see Notes).
temperature : Quantity
the desired reaction temperature in degrees Celsius
reference_temperature : Quantity, optional
the temperature at which equilibrium_constant is valid. (25 degrees C if omitted).
Returns
-------
float
adjusted reaction equilibrium constant
Notes
-----
This function implements the Van't Hoff equation to adjust measured
equilibrium constants to other temperatures.
.. math::
ln(K2 / K1) = {\delta H \over R} ( {1 \over T_1} - {1 \over T_2} )
This implementation assumes that the enthalpy is independent of temperature
over the range of interest.
References
----------
Stumm, Werner and Morgan, James J. Aquatic Chemistry, 3rd ed, pp 53.
Wiley Interscience, 1996.
Examples:
--------
>>> adjust_temp_vanthoff(0.15,-197.6*unit.Quantity('kJ/mol'),unit.Quantity('42 degC'),unit.Quantity(' 25degC')) #doctest: +ELLIPSIS
0.00203566...
If the 'ref_temperature' parameter is omitted, a default of 25 C is used.
>>> adjust_temp_vanthoff(0.15,-197.6*unit.Quantity('kJ/mol'),unit.Quantity('42 degC')) #doctest: +ELLIPSIS
0.00203566...
"""
output = equilibrium_constant * math.exp(
enthalpy / unit.R * (1 / reference_temperature.to("K") - 1 / temperature.to("K"))
)
logger.info(
"Adjusted equilibrium constant K=%s from %s to %s degrees Celsius with Delta H = %s. Adjusted K = %s % equilibrium_constant,reference_temperature,temperature,enthalpy,output"
)
logger.warning("Van't Hoff equation assumes enthalpy is independent of temperature over the range of interest")
return output
[docs]def adjust_temp_arrhenius(
rate_constant,
activation_energy,
temperature,
reference_temperature=25 * unit.Quantity("degC"),
):
r"""(float,float,number, optional number) -> float.
Adjust a reaction equilibrium constant from one temperature to another.
Parameters
----------
rate_constant : Quantity
The parameter value (usually a rate constant) being adjusted
activation_energy : Quantity
The activation energy of the process, in kJ/mol
temperature : Quantity
the desired reaction temperature.
reference_temperature : Quantity, optional
the temperature at which equilibrium_constant is valid
Defaults to 25 degrees C if omitted.
Returns
-------
Quantity
The adjusted reaction equilibrium constant
See Also:
--------
kelvin
Notes
-----
This function implements the Arrhenius equation to adjust measured rate
constants to other temperatures.
TODO - add better reference
.. math::
ln(\\frac{K2}{K1} = \\frac{E_a}{R} ( \\frac{1}{T_{1}} - {\\frac{1}{T_2}} )
References
----------
http://chemwiki.ucdavis.edu/Physical_Chemistry/Kinetics/Reaction_Rates/Temperature_Dependence_of_Reaction_Rates/Arrhenius_Equation
Examples:
--------
>>> adjust_temp_arrhenius(7,900*unit.Quantity('kJ/mol'),37*unit.Quantity('degC'),97*unit.Quantity('degC')) #doctest: +ELLIPSIS
1.8867225...e-24
"""
output = rate_constant * math.exp(
activation_energy / unit.R * (1 / reference_temperature.to("K") - 1 / temperature.to("K"))
)
logger.info(
"Adjusted parameter %s from %s to %s degrees Celsius with Activation Energy = %s kJ/mol. Adjusted value = %s % rate_constant,reference_temperature,temperature,activation_energy,output"
)
return output
[docs]def alpha(n, pH, pKa_list):
"""Returns the acid-base distribution coefficient (alpha) of an acid in the
n-deprotonated form at a given pH.
Parameters
----------
n : int
The number of protons that have been lost by the desired form of the
acid. Also the subscript on the alpha value. E.g. for bicarbonate
(HCO3-), n=1 because 1 proton has been lost from the fully-protonated
carbonic acid (H2CO3) form.
pH : float or int
The pH of the solution.
pKa_list : list of floats or ints
The pKa values (negative log of equilibrium constants) for the acid
of interest. There must be a minimum of n pKa values in the list.
Returns
-------
float
The fraction of total acid present in the specified form.
Notes
-----
The acid-base cient is calculated as follows: [stm]_
.. math::
\\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} }
Where :math: '\term_n' refers to the nth term in the denominator, starting from 0
References
----------
.. [stm] Stumm, Werner and Morgan, James J. Aquatic Chemistry, 3rd ed, pp 127-130. Wiley Interscience, 1996.
Examples:
--------
>>> alpha(1,8,[4.7]) #doctest: +ELLIPSIS
0.999...
The sum of all alpha values should equal 1
>>> alpha(0,8,[6.35,10.33]) #doctest: +ELLIPSIS
0.021...
>>> alpha(1,8,[6.35,10.33]) #doctest: +ELLIPSIS
0.979...
>>> alpha(2,8,[6.35,10.33]) #doctest: +ELLIPSIS
2.043...e-09
If pH is equal to one of the pKa values the function should return 0.5.
>>> alpha(1,6.35,[6.35,10.33])
0.5
"""
# generate an error if no pKa values are specified
if len(pKa_list) == 0:
logger.error("No pKa values given. Cannot calculate distribution coeffiicent.")
return None
# generate an error if n > number of pKa values
if len(pKa_list) < n:
logger.error("Insufficient number of pKa values given. Cannot calculate distribution coeffiicent.")
return None
# convert pH to hydrogen ion concentration
Hplus = 10**-pH
# determine how many protons the acid has
num_protons = len(pKa_list)
# build a list of terms where the term subscript corresponds to the list index
terms_list = []
k_term = 1
# the 'item' index counts from 0 to the number of protons, inclusive
for item in range(0, num_protons + 1):
# multiply the preceding k values together
for i in range(len(pKa_list[:item])):
k_term *= 10 ** -pKa_list[i]
# add the term to the list
terms_list.append(k_term * Hplus ** (num_protons - item))
# build the expression
numerator = terms_list[n]
denominator = 0
for item in terms_list:
denominator += item
# return the desired distribution factor
alpha = numerator / denominator
logger.info(
"Calculated %s-deprotonated acid distribution coefficient of %s for pKa=%s at pH %s % n,alpha,pKa_list,pH"
)
return alpha