From 8c181f8bfcfb01e5ae06e8d854e2a09df57a05a0 Mon Sep 17 00:00:00 2001 From: linuxrider Date: Tue, 16 Aug 2022 12:31:59 +0200 Subject: [PATCH 1/5] add priliminary potential reference implementation --- ec_tools/potential_reference.py | 66 +++++++++++++++++++++++++++++++++ pyproject.toml | 2 +- 2 files changed, 67 insertions(+), 1 deletion(-) create mode 100644 ec_tools/potential_reference.py diff --git a/ec_tools/potential_reference.py b/ec_tools/potential_reference.py new file mode 100644 index 0000000..abff1a2 --- /dev/null +++ b/ec_tools/potential_reference.py @@ -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 . +# ******************************************************************** +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) diff --git a/pyproject.toml b/pyproject.toml index f05a954..ebb5b4f 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -13,7 +13,7 @@ requires-python = ">=3.9.0" dependencies = [ "numpy", ] -version = "0.0.1" +version = "0.0.2" [tool.setuptools] license-files = ["LICENSE"] # Keep in sync with setup.cfg From 456e5efaa0e83b8d580536987250214bf4979f7a Mon Sep 17 00:00:00 2001 From: Albert Engstfeld Date: Mon, 10 Nov 2025 17:38:25 +0100 Subject: [PATCH 2/5] Create class for Reference Electrode --- ec_tools/potential_reference.py | 20 ++-- ec_tools/reference_potential.py | 190 ++++++++++++++++++++++++++++++++ 2 files changed, 200 insertions(+), 10 deletions(-) create mode 100644 ec_tools/reference_potential.py diff --git a/ec_tools/potential_reference.py b/ec_tools/potential_reference.py index abff1a2..fcb2940 100644 --- a/ec_tools/potential_reference.py +++ b/ec_tools/potential_reference.py @@ -1,24 +1,24 @@ r""" This module contains the :class: `PotentialReference` which contains every to with reference -potentials. +potentials. """ # ******************************************************************** -# This file is part of ec-tools. +# This file is part of ec_tools. # # Copyright (C) 2022 Johannes Hermann # -# ec-tools is free software: you can redistribute it and/or modify +# 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, +# 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 . +# along with ec_tools. If not, see . # ******************************************************************** from astropy import units as u @@ -27,15 +27,15 @@ 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 + >>> 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. + # 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 + # 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}, @@ -53,10 +53,10 @@ 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 + >>> from ec_tools.potential_reference import PotentialReference >>> potential_reference = PotentialReference("MSE-1M") >>> potential_reference.to_reference("SCE-sat") -0.3739471937500001 diff --git a/ec_tools/reference_potential.py b/ec_tools/reference_potential.py new file mode 100644 index 0000000..66a7ad4 --- /dev/null +++ b/ec_tools/reference_potential.py @@ -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 . +# ******************************************************************** + +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 From 05bdd7bacc64d1ab20f783c6de2ab8d16bebe573 Mon Sep 17 00:00:00 2001 From: Albert Engstfeld Date: Mon, 10 Nov 2025 22:29:01 +0100 Subject: [PATCH 3/5] set setuptools, version --- pixi.lock | 2 +- pyproject.toml | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/pixi.lock b/pixi.lock index 48e8e72..b69811f 100644 --- a/pixi.lock +++ b/pixi.lock @@ -5198,7 +5198,7 @@ packages: - pypi: ./ name: ec-tools version: 0.1.0 - sha256: 9bcd57101724ec07741fb66a34775a6564ef81fe1b37b087235b6768082b6a6d + sha256: 2c24953c62fff258a67a3b39e12b25f7d5fa7cbb8556b45a20892d1c93f734d7 requires_dist: - numpy>=2.0.2,<3 - transonic>=0.7.3,<0.8 diff --git a/pyproject.toml b/pyproject.toml index 6aa9cdd..1752ccc 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,5 +1,5 @@ [build-system] -requires = ["setuptools"] +requires = ["setuptools>=61.2"] build-backend = "setuptools.build_meta" [project] @@ -7,7 +7,6 @@ name = "ec-tools" classifiers = ["License :: OSI Approved :: GNU General Public License v3 or later (GPLv3+)"] description = "tools to work with electrochemical data" readme = "README.md" - requires-python = ">=3.10.0" dependencies = [ "numpy>=2.0.2,<3", From 55756a8404830e63a4277e433bc591a42baca783 Mon Sep 17 00:00:00 2001 From: Albert Engstfeld Date: Wed, 12 Nov 2025 10:25:35 +0100 Subject: [PATCH 4/5] Adapt astropy version --- pixi.lock | 4 ++-- pyproject.toml | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/pixi.lock b/pixi.lock index b69811f..42c42e8 100644 --- a/pixi.lock +++ b/pixi.lock @@ -5198,11 +5198,11 @@ packages: - pypi: ./ name: ec-tools version: 0.1.0 - sha256: 2c24953c62fff258a67a3b39e12b25f7d5fa7cbb8556b45a20892d1c93f734d7 + sha256: 6d07043274acc4c1087e9d5c92e399013196cb9c6bd2f2469e361345e297e3dc requires_dist: - numpy>=2.0.2,<3 - transonic>=0.7.3,<0.8 - - astropy>=5,<7 + - astropy>=5,<8 requires_python: '>=3.10.0' editable: true - conda: https://conda.anaconda.org/conda-forge/noarch/exceptiongroup-1.3.0-pyhd8ed1ab_0.conda diff --git a/pyproject.toml b/pyproject.toml index 1752ccc..ebdd31e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -11,7 +11,7 @@ requires-python = ">=3.10.0" dependencies = [ "numpy>=2.0.2,<3", "transonic>=0.7.3,<0.8", - "astropy>=5,<7", + "astropy>=5,<8", ] version = "0.1.0" @@ -19,7 +19,7 @@ version = "0.1.0" [tool.setuptools] packages = ["ec_tools",] -[tool.pixi.project] +[tool.pixi.workspace] channels = ["conda-forge"] platforms = ["linux-64", "linux-aarch64", "osx-64", "osx-arm64", "win-64"] From ef178491003ccc46527dc7cb9e8fe2af5fa99b85 Mon Sep 17 00:00:00 2001 From: Albert Engstfeld Date: Wed, 12 Nov 2025 11:21:31 +0100 Subject: [PATCH 5/5] Lift uper version bound fro transonic from 8 to 9 --- pixi.lock | 4 ++-- pyproject.toml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/pixi.lock b/pixi.lock index 42c42e8..7b4d9a5 100644 --- a/pixi.lock +++ b/pixi.lock @@ -5198,10 +5198,10 @@ packages: - pypi: ./ name: ec-tools version: 0.1.0 - sha256: 6d07043274acc4c1087e9d5c92e399013196cb9c6bd2f2469e361345e297e3dc + sha256: 7c7a67054afd99fb13ee99caac732cc8a479157eeb71e2340ee6fc6f0f169594 requires_dist: - numpy>=2.0.2,<3 - - transonic>=0.7.3,<0.8 + - transonic>=0.7.3,<0.9 - astropy>=5,<8 requires_python: '>=3.10.0' editable: true diff --git a/pyproject.toml b/pyproject.toml index ebdd31e..f44f083 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -10,7 +10,7 @@ readme = "README.md" requires-python = ">=3.10.0" dependencies = [ "numpy>=2.0.2,<3", - "transonic>=0.7.3,<0.8", + "transonic>=0.7.3,<0.9", "astropy>=5,<8", ] version = "0.1.0"