Reduction and Oxidation Potentials¶
Calculate one-electron oxidation and reduction potentials with three PLAMS redox workflows: a fast screening method, a direct implicit-solvent method, and a thermodynamic-cycle method.
Definitions and introduction¶
Reduction potential: A + e- -> A- ; ΔG0 = -nFE0
Oxidation potential: A -> A+ + e- ; ΔG0 = nFE0
There are three PLAMS recipes for calculating one-electron reduction or oxidation potentials in implicit solvent:
AMSRedoxDirectJob: The best method. Geometry optimizations (and optionally frequencies) are calculated for both neutral and reduced/oxidized species in implicit solvent. The solvent must be supported by ADF with the COSMO solvation method. Requires an ADF license.AMSRedoxThermodynamicCycleJob: Only useful if you include the vibrations (frequencies) and the molecule is large (in which case it is faster but less accurate than AMSRedoxDirectJob). The frequencies are only calculated for the gasphase molecule. A thermodynamic cycle gives the reduction or oxidation potential. The solvent must be supported by ADF with the COSMO solvation method. Requires an ADF license.AMSRedoxScreeningJob: The fastest (and least accurate) method. Geometry optimizations are performed at the GFN1-xTB level of theory. The solvation free energy is evaluated by COSMO-RS. Vibrational effects are always implicitly accounted for. A.coskffile for the solvent is required - this can either be obtained from the ADFCRS-2018 database or generated with anADFCOSMORSCompoundJob. Requires DFTB, ADF, and COSMO-RS licenses.
The AMSRedoxScreeningJob workflow was developed by Belic et al., Phys. Chem. Chem. Phys. 24, 197–210 (2022) for calculating oxidation potentials. The paper also describes the other workflows in detail.
Note
The AMSRedoxScreeningJob uses the recommended ADF settings for generating .coskf files. Belic et al. used a different density functional. Using the class will give slightly different results compared to Belic et al.
The free energy of the electron is set to be –0.0375 eV (see Belic et al.).
In this example the above three workflows are used to evaluate the reduction potential of benzoquinone in water. The experimental value is E⁰ = +0.10 V relative to SHE (standard hydrogen electrode).
Ho et al. (preprint pdf) evaluated E⁰ = -0.40 V or -0.28 V for benzoquinone relative to SHE using different computational methods.
The reduction and oxidation potentials calculated by the PLAMS classes are given on an absolute scale. On this scale, the SHE is at +4.42 or +4.28 V (see Ho et al.). To get potentials relative to SHE, we therefore subtract 4.42 V from the calculated values.
Note
Energy differences in eV correspond directly to potentials in V.
Tip
If you compare many calculated reduction potentials, use one of them as the reference state. See for example Huyhn et al., J. Am. Chem. Soc. 2016, 138, 49, 15903–15910 in the case of substituted quinones.
Important
These classes can only be used for
molecules that do not undergo big conformational changes (or dissociation) upon oxidation/reduction.
one-electron reduction/oxidation. For multiple electrons, run the same script with different initial charges for the molecule
molecules with 0 or 1 unpaired electrons
Downloads: Notebook | Script ?
Requires: AMS2026 or later
Related examples
Related documentation
Reduction and oxidation potentials¶
This example runs the three PLAMS redox recipes for benzoquinone in water and compares their computed oxidation/reduction potentials.
Import PLAMS recipe classes and utilities.¶
from typing import Iterable
from scm.plams import CRSJob, Settings, from_smiles, view
from scm.plams.recipes.redox import (
AMSRedoxDirectJob,
AMSRedoxScreeningJob,
AMSRedoxThermodynamicCycleJob,
)
Build benzoquinone and define solvent settings.¶
molecule = from_smiles("C1=CC(=O)C=CC1=O", forcefield="uff")
view(molecule, width=260, height=220)
solvent_name = "Water"
solvent_coskf = CRSJob.database() + "/Water.coskf"
reduction = True
oxidation = True
vibrations = True
Define ADF settings used by the direct and thermodynamic-cycle workflows.¶
adf_settings = Settings()
adf_settings.input.adf.basis.type = "DZP"
adf_settings.input.adf.xc.gga = "PBE"
adf_settings.input.ams.GeometryOptimization.Convergence.Quality = "Basic"
Create helper printing function and instantiate jobs.¶
def print_results(jobs: Iterable) -> None:
she = 4.42 # eV, absolute SHE scale
print("The experimental reduction potential of benzoquinone is +0.10 V vs. SHE")
print(
f"{'Jobname':24s} {'Eox(vib,rel-to-SHE)[V]':24s} {'Ered(vib,rel-to-SHE)[V]':24s} "
f"{'Eox(rel-to-SHE)[V]':24s} {'Ered(rel-to-SHE)[V]':24s}"
)
for job in jobs:
row = f"{job.name:24s}"
for vib in [True, False]:
try:
eox = f"{job.results.get_oxidation_potential(vibrations=vib) - she:.2f}"
except Exception:
eox = "N/A"
row += f" {eox:24s}"
try:
ered = f"{job.results.get_reduction_potential(vibrations=vib) - she:.2f}"
except Exception:
ered = "N/A"
row += f" {ered:24s}"
print(row)
jobs = [
AMSRedoxScreeningJob(
name="quick_screening",
molecule=molecule,
reduction=reduction,
oxidation=oxidation,
solvent_coskf=solvent_coskf,
),
AMSRedoxDirectJob(
name="direct_best_method",
molecule=molecule,
settings=adf_settings,
reduction=reduction,
oxidation=oxidation,
vibrations=vibrations,
solvent=solvent_name,
),
AMSRedoxThermodynamicCycleJob(
name="thermodynamic_cycle",
molecule=molecule,
settings=adf_settings,
reduction=reduction,
oxidation=oxidation,
vibrations=vibrations,
solvent=solvent_name,
),
]
Run all workflows and print per-workflow plus summary tables¶
for job in jobs:
job.run()
for job in jobs:
job.ok()
[02.04|12:29:07] JOB quick_screening STARTED
[02.04|12:29:07] JOB quick_screening RUNNING
[02.04|12:29:07] JOB quick_screening/dftb_go_0 STARTED
[02.04|12:29:07] JOB quick_screening/dftb_go_0 RUNNING
[02.04|12:29:08] JOB quick_screening/dftb_go_0 FINISHED
[02.04|12:29:08] JOB quick_screening/dftb_go_0 SUCCESSFUL
[02.04|12:29:08] JOB quick_screening/dftb_go_ox STARTED
[02.04|12:29:08] JOB quick_screening/dftb_go_ox RUNNING
[02.04|12:29:09] JOB quick_screening/dftb_go_ox FINISHED
[02.04|12:29:09] JOB quick_screening/dftb_go_ox SUCCESSFUL
... output trimmed ....
[02.04|12:51:11] JOB thermodynamic_cycle/go_red_solvated STARTED
[02.04|12:51:11] JOB thermodynamic_cycle/go_red_solvated RUNNING
[02.04|12:54:17] JOB thermodynamic_cycle/go_red_solvated FINISHED
[02.04|12:54:17] JOB thermodynamic_cycle/go_red_solvated SUCCESSFUL
[02.04|12:54:17] JOB thermodynamic_cycle/go_red_solvated_sp_vacuum STARTED
[02.04|12:54:17] JOB thermodynamic_cycle/go_red_solvated_sp_vacuum RUNNING
[02.04|12:54:24] JOB thermodynamic_cycle/go_red_solvated_sp_vacuum FINISHED
[02.04|12:54:24] JOB thermodynamic_cycle/go_red_solvated_sp_vacuum SUCCESSFUL
[02.04|12:54:24] JOB thermodynamic_cycle FINISHED
[02.04|12:54:24] JOB thermodynamic_cycle SUCCESSFUL
print("Final summary:")
print_results(jobs)
Final summary:
The experimental reduction potential of benzoquinone is +0.10 V vs. SHE
Jobname Eox(vib,rel-to-SHE)[V] Ered(vib,rel-to-SHE)[V] Eox(rel-to-SHE)[V] Ered(rel-to-SHE)[V]
quick_screening 2.95 0.44 2.95 0.44
direct_best_method 2.61 -0.08 2.72 -0.08
thermodynamic_cycle 2.63 -0.07 2.71 -0.11
See also¶
Python Script¶
#!/usr/bin/env python
# coding: utf-8
# ## Reduction and oxidation potentials
#
# This example runs the three PLAMS redox recipes for benzoquinone in water and compares their computed oxidation/reduction potentials.
#
# ### Import PLAMS recipe classes and utilities.
#
from typing import Iterable
from scm.plams import CRSJob, Settings, from_smiles, view
from scm.plams.recipes.redox import (
AMSRedoxDirectJob,
AMSRedoxScreeningJob,
AMSRedoxThermodynamicCycleJob,
)
# ### Build benzoquinone and define solvent settings.
#
molecule = from_smiles("C1=CC(=O)C=CC1=O", forcefield="uff")
view(molecule, width=260, height=220)
solvent_name = "Water"
solvent_coskf = CRSJob.database() + "/Water.coskf"
reduction = True
oxidation = True
vibrations = True
# ### Define ADF settings used by the direct and thermodynamic-cycle workflows.
#
adf_settings = Settings()
adf_settings.input.adf.basis.type = "DZP"
adf_settings.input.adf.xc.gga = "PBE"
adf_settings.input.ams.GeometryOptimization.Convergence.Quality = "Basic"
# ### Create helper printing function and instantiate jobs.
def print_results(jobs: Iterable) -> None:
she = 4.42 # eV, absolute SHE scale
print("The experimental reduction potential of benzoquinone is +0.10 V vs. SHE")
print(
f"{'Jobname':24s} {'Eox(vib,rel-to-SHE)[V]':24s} {'Ered(vib,rel-to-SHE)[V]':24s} "
f"{'Eox(rel-to-SHE)[V]':24s} {'Ered(rel-to-SHE)[V]':24s}"
)
for job in jobs:
row = f"{job.name:24s}"
for vib in [True, False]:
try:
eox = f"{job.results.get_oxidation_potential(vibrations=vib) - she:.2f}"
except Exception:
eox = "N/A"
row += f" {eox:24s}"
try:
ered = f"{job.results.get_reduction_potential(vibrations=vib) - she:.2f}"
except Exception:
ered = "N/A"
row += f" {ered:24s}"
print(row)
jobs = [
AMSRedoxScreeningJob(
name="quick_screening",
molecule=molecule,
reduction=reduction,
oxidation=oxidation,
solvent_coskf=solvent_coskf,
),
AMSRedoxDirectJob(
name="direct_best_method",
molecule=molecule,
settings=adf_settings,
reduction=reduction,
oxidation=oxidation,
vibrations=vibrations,
solvent=solvent_name,
),
AMSRedoxThermodynamicCycleJob(
name="thermodynamic_cycle",
molecule=molecule,
settings=adf_settings,
reduction=reduction,
oxidation=oxidation,
vibrations=vibrations,
solvent=solvent_name,
),
]
# ### Run all workflows and print per-workflow plus summary tables
for job in jobs:
job.run()
for job in jobs:
job.ok()
print("Final summary:")
print_results(jobs)