Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
66 changes: 66 additions & 0 deletions ec_tools/potential_reference.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
r"""
This module contains the :class: `PotentialReference` which contains every to with reference
potentials.
"""
# ********************************************************************
# This file is part of ec_tools.
#
# Copyright (C) 2022 Johannes Hermann
#
# ec_tools is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# ec_tools is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with ec_tools. If not, see <https://www.gnu.org/licenses/>.
# ********************************************************************
from astropy import units as u


class PotentialReference:
r"""
:class: PotentialReference models the electrolyte i.e. the solution with all it's components.
EXAMPLES:
>>> from ec_tools.potential_reference import PotentialReference
>>> potential_reference = PotentialReference("MSE-sat")

"""
# Data take from:
# Inzelt, G., Lewenstam, A., Scholz, F. (Eds.), 2013. Handbook of Reference Electrodes.
# Springer Berlin Heidelberg, Berlin, Heidelberg. https://doi.org/10.1007/978-3-642-36188-3

# TODO: allow to account for a temperature influence
# references = { "Ag/AgCl-sat": {"E(T)": lambda T: 0.23659 - 4.8564e-4 * T - 3.4205e-6 * T**2 + 5.869e-9 * T**3},
# "SCE-sat": {"E(T)": lambda T: 0.2412 - 6.61e-4*(T-25) - 1.75e-6*(T-25)**2 - 9e-10*(T-25)**3},
# "MSE-1M": {"E(T)": lambda T: 0.63495 - 781.44e-6*T - 426.89e-9*T**2},
# "SHE": {"E(T)": lambda T: 0}
# }

references = { "Ag/AgCl-sat": 0.197,
"Ag/AgCl-3M": 0.22249,
"SCE-sat": 0.241,
"MSE-1M": 0.,
"MSE-sat": 0.64,
"SHE": 0,
}
def __init__(self, reference, T = 25, pH = None):
self.reference = reference
self.T = T
self.pH = pH

def to_reference(self, new_reference):
""" TESTS:
>>> from ec_tools.potential_reference import PotentialReference
>>> potential_reference = PotentialReference("MSE-1M")
>>> potential_reference.to_reference("SCE-sat")
-0.3739471937500001
>>> potential_reference.to_reference("SHE")
-0.61514719375
"""
return self.references[new_reference]["E(T)"](self.T) - self.references[self.reference]["E(T)"](self.T)
190 changes: 190 additions & 0 deletions ec_tools/reference_potential.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,190 @@
r"""
This module contains the :class: `PotentialReference` which contains every to with reference
potentials.
"""
# ********************************************************************
# This file is part of ec-tools.
#
# Copyright (C) 2025 Johannes Hermann
# Copyright (C) 2025 Albert Engstfeld
#
# ec-tools is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# ec-tools is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with ec-tools. If not, see <https://www.gnu.org/licenses/>.
# ********************************************************************

from dataclasses import dataclass
from astropy import units as u


# reference_data.py
REFERENCE_ELECTRODE_DATA = {
"SHE": {
"value_vs_she": 0.000,
"unit": "V",
"vs": "SHE",
"source": "Definition (zero point).",
},
"Ag/AgCl-sat": {
"value_vs_she": 0.197,
"unit": "V",
"vs": "SHE",
"source": "Inzelt et al., Handbook of Reference Electrodes, Springer, 2013.",
},
"Ag/AgCl-3M": {
"value_vs_she": 0.210,
"unit": "V",
"vs": "SHE",
"source": "Inzelt et al., Handbook of Reference Electrodes, Springer, 2013.",
},
"SCE-sat": {
"value_vs_she": 0.241,
"unit": "V",
"vs": "SHE",
"source": "Inzelt et al., Handbook of Reference Electrodes, Springer, 2013.",
},
"MSE-sat": {
"value_vs_she": 0.640,
"unit": "V",
"vs": "SHE",
"source": "Inzelt et al., Handbook of Reference Electrodes, Springer, 2013.",
},
"MSE-1M": {
"value_vs_she": 0.000,
"unit": "V",
"vs": "MSE-1M",
"source": "Chosen as internal zero for MSE family.",
},
"RHE": {
"value_vs_she": 0.000,
"unit": "V",
"vs": "SHE",
"formula": "E(RHE) = E(SHE) - 0.0591 × pH",
"source": "Nernst equation, 25 °C.",
},
}





@dataclass(frozen=True)
class ReferenceElectrode:
"""
Represents a single electrochemical reference electrode.

Attributes
----------
name : str
Common name of the reference electrode (e.g., 'Ag/AgCl-3M', 'SHE').
value_vs_she : float
Potential of the electrode relative to the standard hydrogen electrode (SHE), in volts.
unit : str
Unit of the potential, typically 'V'.
vs : str
The reference scale against which the value is reported.
source : str
Bibliographic or textual source for the potential value.
formula : str, optional
Formula for cases where the potential depends on pH or temperature (e.g., RHE).

Examples
--------
Accessing reference electrode data:

>>> from ec_tools.reference_potential import ReferenceElectrodes
>>> ReferenceElectrodes["Ag/AgCl-3M"]
ReferenceElectrode(name='Ag/AgCl-3M', value_vs_she=0.21, unit='V', vs='SHE', source='Inzelt et al., Handbook of Reference Electrodes, Springer, 2013.', formula=None)

Converting between reference scales:

>>> ReferenceElectrodes.convert(0.55, "Ag/AgCl-3M", "SHE")
0.3400000000000001

>>> ReferenceElectrodes.convert(ref_from="SHE", ref_to="RHE", pH=5)
-0.2955
"""

name: str
value_vs_she: float
unit: str = "V"
vs: str = "SHE"
source: str = ""
formula: str | None = None


class ReferenceElectrodes:
"""Registry and converter for electrochemical reference electrodes."""

_registry: dict[str, ReferenceElectrode] = {
name: ReferenceElectrode(name=name, **params)
for name, params in REFERENCE_ELECTRODE_DATA.items()
}

def __class_getitem__(cls, key: str) -> ReferenceElectrode:
"""Allow dictionary-style access, e.g. ReferenceElectrodes['SHE']"""
if key not in cls._registry:
raise KeyError(f"Unknown reference electrode: '{key}'")
return cls._registry[key]

@classmethod
def convert(
cls,
potential: float | u.Quantity | None = None,
ref_from: str = "SHE",
ref_to: str = "SHE",
pH: float | None = None
):
"""
Convert a potential between reference electrode scales or compute their shift.

Parameters
----------
potential : float, Quantity, or None
Potential vs. `ref_from`. If None, returns only the shift.
ref_from : str
The reference scale of the input potential.
ref_to : str
The target reference scale.
pH : float, optional
Required if RHE is involved.

Returns
-------
float or Quantity
Converted potential (if `potential` provided) or shift (if not).

Examples
--------
>>> ReferenceElectrodes.convert(ref_from="Ag/AgCl-3M", ref_to="SHE")
-0.21

>>> ReferenceElectrodes.convert(0.55, "Ag/AgCl-3M", "SHE")
0.3400000000000001

>>> ReferenceElectrodes.convert(ref_from="SHE", ref_to="RHE", pH=7)
-0.4137
"""
def get_value_vs_she(ref: str) -> float:
if ref == "RHE":
if pH is None:
raise ValueError("pH must be provided for RHE conversion.")
return -0.0591 * pH
return cls[ref].value_vs_she

shift = get_value_vs_she(ref_to) - get_value_vs_she(ref_from)

if potential is None:
return shift
if isinstance(potential, u.Quantity):
return potential + shift * u.volt
return potential + shift
Loading
Loading