autopcet 1.1.0__tar.gz

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.
Files changed (151) hide show
  1. autopcet-1.1.0/CHANGELOG.md +56 -0
  2. autopcet-1.1.0/LICENSE +21 -0
  3. autopcet-1.1.0/MANIFEST.in +10 -0
  4. autopcet-1.1.0/PKG-INFO +531 -0
  5. autopcet-1.1.0/README.md +499 -0
  6. autopcet-1.1.0/autopcet/__init__.py +179 -0
  7. autopcet-1.1.0/autopcet/_types.py +23 -0
  8. autopcet-1.1.0/autopcet/ase_io.py +485 -0
  9. autopcet-1.1.0/autopcet/cli/__init__.py +7 -0
  10. autopcet-1.1.0/autopcet/cli/_io.py +56 -0
  11. autopcet-1.1.0/autopcet/cli/align_average.py +196 -0
  12. autopcet-1.1.0/autopcet/cli/calc_keff.py +82 -0
  13. autopcet-1.1.0/autopcet/cli/scan_da_distance.py +178 -0
  14. autopcet-1.1.0/autopcet/cli/scan_proton_coord.py +116 -0
  15. autopcet-1.1.0/autopcet/constants.py +64 -0
  16. autopcet-1.1.0/autopcet/edl.py +198 -0
  17. autopcet-1.1.0/autopcet/fgh.py +63 -0
  18. autopcet-1.1.0/autopcet/gaussian_io.py +238 -0
  19. autopcet-1.1.0/autopcet/kappa.py +335 -0
  20. autopcet-1.1.0/autopcet/plotting/__init__.py +109 -0
  21. autopcet-1.1.0/autopcet/plotting/_mpl.py +99 -0
  22. autopcet-1.1.0/autopcet/plotting/analysis.py +251 -0
  23. autopcet-1.1.0/autopcet/plotting/averaging.py +82 -0
  24. autopcet-1.1.0/autopcet/plotting/potentials.py +379 -0
  25. autopcet-1.1.0/autopcet/plotting/states.py +207 -0
  26. autopcet-1.1.0/autopcet/plotting/style.py +103 -0
  27. autopcet-1.1.0/autopcet/plotting/sweeps.py +151 -0
  28. autopcet-1.1.0/autopcet/potentials.py +221 -0
  29. autopcet-1.1.0/autopcet/py.typed +0 -0
  30. autopcet-1.1.0/autopcet/rates.py +327 -0
  31. autopcet-1.1.0/autopcet/reporting.py +94 -0
  32. autopcet-1.1.0/autopcet/structure.py +211 -0
  33. autopcet-1.1.0/autopcet/utils.py +39 -0
  34. autopcet-1.1.0/autopcet.egg-info/PKG-INFO +531 -0
  35. autopcet-1.1.0/autopcet.egg-info/SOURCES.txt +149 -0
  36. autopcet-1.1.0/autopcet.egg-info/dependency_links.txt +1 -0
  37. autopcet-1.1.0/autopcet.egg-info/entry_points.txt +5 -0
  38. autopcet-1.1.0/autopcet.egg-info/requires.txt +13 -0
  39. autopcet-1.1.0/autopcet.egg-info/top_level.txt +1 -0
  40. autopcet-1.1.0/examples/example1_basic_usage/double_well_potentials.dat +22 -0
  41. autopcet-1.1.0/examples/example1_basic_usage/example1_basic_usage.py +88 -0
  42. autopcet-1.1.0/examples/example1_basic_usage/reference_output.txt +25 -0
  43. autopcet-1.1.0/examples/example2_Y356-Y731/example2_Y356-Y731.py +206 -0
  44. autopcet-1.1.0/examples/example2_Y356-Y731/p_R_umbrella_A.dat +82 -0
  45. autopcet-1.1.0/examples/example2_Y356-Y731/proton_potentials/R2.42_potential.dat +49 -0
  46. autopcet-1.1.0/examples/example2_Y356-Y731/proton_potentials/R2.52_potential.dat +49 -0
  47. autopcet-1.1.0/examples/example2_Y356-Y731/proton_potentials/R2.62_potential.dat +49 -0
  48. autopcet-1.1.0/examples/example2_Y356-Y731/proton_potentials/R2.72_potential.dat +49 -0
  49. autopcet-1.1.0/examples/example2_Y356-Y731/proton_potentials/R2.82_potential.dat +49 -0
  50. autopcet-1.1.0/examples/example2_Y356-Y731/proton_potentials/R2.92_potential.dat +49 -0
  51. autopcet-1.1.0/examples/example2_Y356-Y731/proton_potentials/R3.02_potential.dat +73 -0
  52. autopcet-1.1.0/examples/example2_Y356-Y731/proton_potentials/R3.12_potential.dat +73 -0
  53. autopcet-1.1.0/examples/example2_Y356-Y731/reference_output.txt +11 -0
  54. autopcet-1.1.0/examples/example3_BIP_KIE/electrochem_reference_output.txt +17 -0
  55. autopcet-1.1.0/examples/example3_BIP_KIE/example3_BIP_KIE_electrochemical.py +254 -0
  56. autopcet-1.1.0/examples/example3_BIP_KIE/example3_BIP_KIE_photochemical.py +201 -0
  57. autopcet-1.1.0/examples/example3_BIP_KIE/photochem_reference_output.txt +17 -0
  58. autopcet-1.1.0/examples/example3_BIP_KIE/proton_potentials/Oxidized_BIP_2.37A.csv +26 -0
  59. autopcet-1.1.0/examples/example3_BIP_KIE/proton_potentials/Oxidized_BIP_2.42A.csv +26 -0
  60. autopcet-1.1.0/examples/example3_BIP_KIE/proton_potentials/Oxidized_BIP_2.47A.csv +26 -0
  61. autopcet-1.1.0/examples/example3_BIP_KIE/proton_potentials/Oxidized_BIP_2.52A.csv +26 -0
  62. autopcet-1.1.0/examples/example3_BIP_KIE/proton_potentials/Oxidized_BIP_2.57A.csv +26 -0
  63. autopcet-1.1.0/examples/example3_BIP_KIE/proton_potentials/Oxidized_BIP_2.62A.csv +26 -0
  64. autopcet-1.1.0/examples/example3_BIP_KIE/proton_potentials/Oxidized_BIP_2.67A.csv +26 -0
  65. autopcet-1.1.0/examples/example3_BIP_KIE/proton_potentials/Oxidized_BIP_2.72A.csv +26 -0
  66. autopcet-1.1.0/examples/example3_BIP_KIE/proton_potentials/Oxidized_BIP_2.77A.csv +26 -0
  67. autopcet-1.1.0/examples/example3_BIP_KIE/proton_potentials/Oxidized_BIP_2.82A.csv +26 -0
  68. autopcet-1.1.0/examples/example3_BIP_KIE/proton_potentials/Oxidized_BIP_2.87A.csv +26 -0
  69. autopcet-1.1.0/examples/example3_BIP_KIE/proton_potentials/Reduced_BIP_2.37A.csv +25 -0
  70. autopcet-1.1.0/examples/example3_BIP_KIE/proton_potentials/Reduced_BIP_2.42A.csv +25 -0
  71. autopcet-1.1.0/examples/example3_BIP_KIE/proton_potentials/Reduced_BIP_2.47A.csv +25 -0
  72. autopcet-1.1.0/examples/example3_BIP_KIE/proton_potentials/Reduced_BIP_2.52A.csv +25 -0
  73. autopcet-1.1.0/examples/example3_BIP_KIE/proton_potentials/Reduced_BIP_2.57A.csv +25 -0
  74. autopcet-1.1.0/examples/example3_BIP_KIE/proton_potentials/Reduced_BIP_2.62A.csv +25 -0
  75. autopcet-1.1.0/examples/example3_BIP_KIE/proton_potentials/Reduced_BIP_2.67A.csv +25 -0
  76. autopcet-1.1.0/examples/example3_BIP_KIE/proton_potentials/Reduced_BIP_2.72A.csv +25 -0
  77. autopcet-1.1.0/examples/example3_BIP_KIE/proton_potentials/Reduced_BIP_2.77A.csv +25 -0
  78. autopcet-1.1.0/examples/example3_BIP_KIE/proton_potentials/Reduced_BIP_2.82A.csv +27 -0
  79. autopcet-1.1.0/examples/example3_BIP_KIE/proton_potentials/Reduced_BIP_2.87A.csv +27 -0
  80. autopcet-1.1.0/examples/example4_CoTPP/CoTPP_reference_output.txt +299 -0
  81. autopcet-1.1.0/examples/example4_CoTPP/example4_CoTPP.py +371 -0
  82. autopcet-1.1.0/examples/example4_CoTPP/graphene_DOS_norm_gauss.csv +4001 -0
  83. autopcet-1.1.0/examples/example4_CoTPP/proton_potentials/rDA_3.057_P.csv +257 -0
  84. autopcet-1.1.0/examples/example4_CoTPP/proton_potentials/rDA_3.057_R.csv +257 -0
  85. autopcet-1.1.0/examples/example4_CoTPP/proton_potentials/rDA_3.157_P.csv +257 -0
  86. autopcet-1.1.0/examples/example4_CoTPP/proton_potentials/rDA_3.157_R.csv +257 -0
  87. autopcet-1.1.0/examples/example4_CoTPP/proton_potentials/rDA_3.207_P.csv +257 -0
  88. autopcet-1.1.0/examples/example4_CoTPP/proton_potentials/rDA_3.207_R.csv +257 -0
  89. autopcet-1.1.0/examples/example4_CoTPP/proton_potentials/rDA_3.257_P.csv +257 -0
  90. autopcet-1.1.0/examples/example4_CoTPP/proton_potentials/rDA_3.257_R.csv +257 -0
  91. autopcet-1.1.0/examples/example4_CoTPP/proton_potentials/rDA_3.307_P.csv +257 -0
  92. autopcet-1.1.0/examples/example4_CoTPP/proton_potentials/rDA_3.307_R.csv +257 -0
  93. autopcet-1.1.0/examples/example4_CoTPP/proton_potentials/rDA_3.357_P.csv +257 -0
  94. autopcet-1.1.0/examples/example4_CoTPP/proton_potentials/rDA_3.357_R.csv +257 -0
  95. autopcet-1.1.0/examples/example4_CoTPP/proton_potentials/rDA_3.379_P.csv +257 -0
  96. autopcet-1.1.0/examples/example4_CoTPP/proton_potentials/rDA_3.379_R.csv +257 -0
  97. autopcet-1.1.0/examples/example4_CoTPP/proton_potentials/rDA_3.407_P.csv +257 -0
  98. autopcet-1.1.0/examples/example4_CoTPP/proton_potentials/rDA_3.407_R.csv +257 -0
  99. autopcet-1.1.0/examples/example4_CoTPP/proton_potentials/rDA_3.457_P.csv +257 -0
  100. autopcet-1.1.0/examples/example4_CoTPP/proton_potentials/rDA_3.457_R.csv +257 -0
  101. autopcet-1.1.0/examples/example4_CoTPP/proton_potentials/rDA_3.507_P.csv +257 -0
  102. autopcet-1.1.0/examples/example4_CoTPP/proton_potentials/rDA_3.507_R.csv +257 -0
  103. autopcet-1.1.0/examples/example4_CoTPP/proton_potentials/rDA_3.557_P.csv +257 -0
  104. autopcet-1.1.0/examples/example4_CoTPP/proton_potentials/rDA_3.557_R.csv +257 -0
  105. autopcet-1.1.0/examples/example4_CoTPP/proton_potentials/rDA_3.607_P.csv +257 -0
  106. autopcet-1.1.0/examples/example4_CoTPP/proton_potentials/rDA_3.607_R.csv +257 -0
  107. autopcet-1.1.0/examples/example4_CoTPP/proton_potentials/rDA_3.657_P.csv +257 -0
  108. autopcet-1.1.0/examples/example4_CoTPP/proton_potentials/rDA_3.657_R.csv +257 -0
  109. autopcet-1.1.0/examples/example4_CoTPP/proton_potentials/rDA_3.757_P.csv +257 -0
  110. autopcet-1.1.0/examples/example4_CoTPP/proton_potentials/rDA_3.757_R.csv +257 -0
  111. autopcet-1.1.0/examples/example4_CoTPP/proton_potentials/rDA_3.857_P.csv +257 -0
  112. autopcet-1.1.0/examples/example4_CoTPP/proton_potentials/rDA_3.857_R.csv +257 -0
  113. autopcet-1.1.0/examples/example4_CoTPP/proton_potentials/rDA_3.957_P.csv +257 -0
  114. autopcet-1.1.0/examples/example4_CoTPP/proton_potentials/rDA_3.957_R.csv +257 -0
  115. autopcet-1.1.0/examples/example4_CoTPP/proton_potentials/rDA_4.057_P.csv +257 -0
  116. autopcet-1.1.0/examples/example4_CoTPP/proton_potentials/rDA_4.057_R.csv +257 -0
  117. autopcet-1.1.0/examples/example4_CoTPP/proton_potentials/rDA_4.157_P.csv +257 -0
  118. autopcet-1.1.0/examples/example4_CoTPP/proton_potentials/rDA_4.157_R.csv +257 -0
  119. autopcet-1.1.0/examples/example4_CoTPP/proton_potentials/rDA_4.257_P.csv +257 -0
  120. autopcet-1.1.0/examples/example4_CoTPP/proton_potentials/rDA_4.257_R.csv +257 -0
  121. autopcet-1.1.0/examples/example5_RNR_nonadiabaticity/Y356_Y731_config2_env.dat +28 -0
  122. autopcet-1.1.0/examples/example5_RNR_nonadiabaticity/Y356_Y731_config2_gas.dat +28 -0
  123. autopcet-1.1.0/examples/example5_RNR_nonadiabaticity/example5_nonadiabaticity_Y356-Y731.py +104 -0
  124. autopcet-1.1.0/examples/example5_RNR_nonadiabaticity/reference_output_env.txt +7 -0
  125. autopcet-1.1.0/examples/example5_RNR_nonadiabaticity/reference_output_gas.txt +7 -0
  126. autopcet-1.1.0/examples/example6_ORCA_ASE/example6_orca_ase.py +237 -0
  127. autopcet-1.1.0/pyproject.toml +129 -0
  128. autopcet-1.1.0/setup.cfg +4 -0
  129. autopcet-1.1.0/tests/conftest.py +94 -0
  130. autopcet-1.1.0/tests/data/donor_acceptor.xyz +5 -0
  131. autopcet-1.1.0/tests/data/freq_hpmodes.log +34 -0
  132. autopcet-1.1.0/tests/data/product.xyz +8 -0
  133. autopcet-1.1.0/tests/data/reactant.xyz +8 -0
  134. autopcet-1.1.0/tests/data/reference_average.xyz +8 -0
  135. autopcet-1.1.0/tests/data/reference_average_mismatched_acceptor.xyz +8 -0
  136. autopcet-1.1.0/tests/data/reference_product_aligned.xyz +8 -0
  137. autopcet-1.1.0/tests/data/reference_reactant_aligned.xyz +8 -0
  138. autopcet-1.1.0/tests/example1_data.py +29 -0
  139. autopcet-1.1.0/tests/test_ase_io.py +421 -0
  140. autopcet-1.1.0/tests/test_cli.py +390 -0
  141. autopcet-1.1.0/tests/test_double_well.py +64 -0
  142. autopcet-1.1.0/tests/test_edl_model.py +130 -0
  143. autopcet-1.1.0/tests/test_examples.py +167 -0
  144. autopcet-1.1.0/tests/test_fgh.py +44 -0
  145. autopcet-1.1.0/tests/test_gaussian_io.py +160 -0
  146. autopcet-1.1.0/tests/test_kappa_coupling.py +189 -0
  147. autopcet-1.1.0/tests/test_pcet.py +370 -0
  148. autopcet-1.1.0/tests/test_plotting.py +718 -0
  149. autopcet-1.1.0/tests/test_potentials_and_utils.py +221 -0
  150. autopcet-1.1.0/tests/test_reporting.py +129 -0
  151. autopcet-1.1.0/tests/test_structure.py +197 -0
@@ -0,0 +1,56 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project are documented here. The format follows
4
+ [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and the project
5
+ adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
6
+
7
+ ## [1.1.0] - 2026-09-18
8
+
9
+ ### Added
10
+
11
+ - Support for Python 3.12 and 3.13 alongside 3.14. The declared floor moves
12
+ from `>=3.14` to `>=3.12`, and CI tests all three.
13
+
14
+ ### Fixed
15
+
16
+ - `import autopcet` raised `NameError` on Python 3.12 and 3.13. Annotations
17
+ naming an import made only under `if TYPE_CHECKING:` are evaluated eagerly
18
+ before 3.14, which defers them under PEP 649; every module doing so now
19
+ carries `from __future__ import annotations`. Moving those imports out of the
20
+ type-checking block would have made `ase` and `matplotlib` mandatory, so the
21
+ `TC004` lint rule guards the pattern instead.
22
+
23
+ ## [1.0.0] - 2026-09-18
24
+
25
+ First public release.
26
+
27
+ ### Added
28
+
29
+ - Vibronically nonadiabatic PCET rate constants from diabatic proton
30
+ potentials, with proton vibrational states solved on a Fourier grid
31
+ Hamiltonian (`autopcet.fgh`) and golden-rule rate matrices summed over pairs
32
+ of reactant and product vibronic states (`PCET`).
33
+ - Kinetic isotope effects from the same rate machinery
34
+ (`kinetic_isotope_effect`).
35
+ - Nonadiabaticity analysis after Georgievskii and Stuchebrukhov
36
+ (`KappaCoupling`).
37
+ - An electrical double layer model of the interfacial potential drop for
38
+ electrochemical PCET (`make_edl_model`).
39
+ - Analytic and tabulated proton potentials, including Morse, inverted Morse and
40
+ Gaussian forms, with polynomial and B-spline fitting of scan data
41
+ (`autopcet.potentials`).
42
+ - Readers for Gaussian output and, through the optional `ase` extra, scan and
43
+ vibrational drivers for any ASE calculator (`autopcet.gaussian_io`,
44
+ `autopcet.ase_io`).
45
+ - Structure alignment and averaging helpers for donor-acceptor geometries
46
+ (`autopcet.structure`).
47
+ - Four console scripts: `autopcet-align-average`, `autopcet-keff`,
48
+ `autopcet-scan-da` and `autopcet-scan-proton`.
49
+ - An optional matplotlib layer under the `plotting` extra
50
+ (`autopcet.plotting`). Importing the package, or the subpackage, never
51
+ requires matplotlib; only calling a plotting function does.
52
+ - Inline type annotations, exported through `py.typed`.
53
+ - Six worked examples with committed reference output, under `examples/`.
54
+
55
+ [1.1.0]: https://github.com/LouieSlocombe/autopcet/compare/v1.0.0...v1.1.0
56
+ [1.0.0]: https://github.com/LouieSlocombe/autopcet/releases/tag/v1.0.0
autopcet-1.1.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Louie Slocombe
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,10 @@
1
+ # The tests read fixtures from tests/data, and test_examples.py runs the
2
+ # scripts under examples/ against their committed reference output. Both have
3
+ # to travel with the sdist for its test suite to be runnable.
4
+ include CHANGELOG.md
5
+
6
+ graft tests
7
+ graft examples
8
+
9
+ global-exclude *.py[cod]
10
+ prune **/__pycache__
@@ -0,0 +1,531 @@
1
+ Metadata-Version: 2.4
2
+ Name: autopcet
3
+ Version: 1.1.0
4
+ Summary: Automated proton-coupled electron transfer (PCET) tooling.
5
+ Author-email: Louie Slocombe <louies@hotmail.co.uk>
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://github.com/LouieSlocombe/autopcet
8
+ Project-URL: Issues, https://github.com/LouieSlocombe/autopcet/issues
9
+ Project-URL: Repository, https://github.com/LouieSlocombe/autopcet
10
+ Keywords: chemistry,electron-transfer,pcet,proton-transfer
11
+ Classifier: Development Status :: 5 - Production/Stable
12
+ Classifier: Operating System :: OS Independent
13
+ Classifier: Programming Language :: Python :: 3
14
+ Classifier: Programming Language :: Python :: 3.12
15
+ Classifier: Programming Language :: Python :: 3.13
16
+ Classifier: Programming Language :: Python :: 3.14
17
+ Classifier: Typing :: Typed
18
+ Requires-Python: >=3.12
19
+ Description-Content-Type: text/markdown
20
+ License-File: LICENSE
21
+ Requires-Dist: numba>=0.67
22
+ Requires-Dist: numpy>=2.5.2
23
+ Requires-Dist: scipy>=1.14
24
+ Provides-Extra: ase
25
+ Requires-Dist: ase>=3.23; extra == "ase"
26
+ Provides-Extra: examples
27
+ Requires-Dist: autopcet[plotting]; extra == "examples"
28
+ Requires-Dist: pandas>=2.2; extra == "examples"
29
+ Provides-Extra: plotting
30
+ Requires-Dist: matplotlib>=3.10; extra == "plotting"
31
+ Dynamic: license-file
32
+
33
+ # autopcet
34
+
35
+ Automated proton-coupled electron transfer (PCET) rate calculations in Python.
36
+
37
+ The library computes vibronically nonadiabatic PCET rate constants from
38
+ diabatic proton potentials: it solves for the proton vibrational states with a
39
+ Fourier grid Hamiltonian (FGH) method, builds the golden-rule rate matrices
40
+ over pairs of reactant/product vibronic states (`PCET`), and evaluates
41
+ kinetic isotope effects (KIEs). It also provides a nonadiabaticity analysis
42
+ following Georgievskii and Stuchebrukhov (`KappaCoupling`) and an electrical
43
+ double layer (EDL) model of the interfacial potential drop for
44
+ electrochemical PCET (`make_edl_model`). Everything it computes can be drawn
45
+ with `autopcet.plotting`, an optional matplotlib layer described under
46
+ [Plotting](#plotting).
47
+
48
+ ## Requirements
49
+
50
+ - Python 3.12 or newer
51
+ - pip 25.1 or newer (for dependency groups)
52
+
53
+ NumPy, SciPy, and Numba are installed automatically as dependencies.
54
+
55
+ ## Installation
56
+
57
+ Create an isolated environment and install the package:
58
+
59
+ ```bash
60
+ python -m venv .venv
61
+ source .venv/bin/activate
62
+ python -m pip install --upgrade pip
63
+ python -m pip install -e .
64
+ ```
65
+
66
+ On Windows PowerShell, activate the environment with
67
+ `.venv\Scripts\Activate.ps1` instead.
68
+
69
+ `autopcet.plotting` needs matplotlib, the examples additionally use pandas,
70
+ and ASE powers both the in-process scan runners in `autopcet.ase_io` and the
71
+ structure-file reading of the `autopcet-*` command-line helpers; install them
72
+ through the optional extras:
73
+
74
+ ```bash
75
+ python -m pip install -e ".[plotting]"
76
+ python -m pip install -e ".[examples]"
77
+ python -m pip install -e ".[ase]"
78
+ ```
79
+
80
+ (`examples` includes `plotting`.) Importing `autopcet` itself never needs any
81
+ of them.
82
+
83
+ For development, install the dev dependency group as well:
84
+
85
+ ```bash
86
+ python -m pip install --group dev -e .
87
+ ```
88
+
89
+ If you prefer Conda, `build_tools/environment.yml` creates the base environment:
90
+
91
+ ```bash
92
+ conda env create -f build_tools/environment.yml
93
+ conda activate autopcet
94
+ python -m pip install --group dev -e .
95
+ ```
96
+
97
+ ## Quick start
98
+
99
+ Fit tabulated diabatic proton potentials, set up the golden-rule rate model,
100
+ and compute rate constants and the KIE:
101
+
102
+ ```python
103
+ from autopcet import PCET, MASS_DEUTERON, MASS_PROTON, fit_poly8
104
+
105
+ # rp, reactant_energies, product_energies: 1D arrays holding the tabulated
106
+ # diabatic proton potentials
107
+ reactant_potential = fit_poly8(rp, reactant_energies)
108
+ product_potential = fit_poly8(rp, product_energies)
109
+
110
+ system = PCET(
111
+ reactant_potential,
112
+ product_potential,
113
+ reaction_free_energy=-0.50,
114
+ reorganization_energy=1.00,
115
+ electronic_coupling=0.0434,
116
+ )
117
+
118
+ rate_h = system.calculate(MASS_PROTON, temperature=298)
119
+ rate_d = system.calculate(MASS_DEUTERON, temperature=298)
120
+ print(f"k(H) = {rate_h:.2e} s^-1, KIE = {rate_h / rate_d:.2f}")
121
+ ```
122
+
123
+ Once `calculate` has run, the per-state results are plain attributes:
124
+ `populations`, `overlaps`, `pair_free_energies`, `pair_activation_energies`,
125
+ `rate_contributions`, and `total_rate_constant`. The thermodynamic parameters
126
+ are attributes too, so a sweep just reassigns one and calls `calculate` again
127
+ with `reuse_states=True` to keep the proton states it already solved for.
128
+ `temperature_sweep` and `driving_force_sweep` do exactly that over a grid --
129
+ one FGH solve for the whole sweep -- and `kinetic_isotope_effect` returns
130
+ `k(H) / k(D)` in one call.
131
+
132
+ A rate constant computed at one proton donor-acceptor distance is usually
133
+ averaged over the distribution of distances the mode samples.
134
+ `donor_acceptor_distribution` builds the harmonic `P(R)` from the effective
135
+ force constant that `autopcet-keff` reports:
136
+
137
+ ```python
138
+ from scipy.integrate import simpson
139
+
140
+ from autopcet import donor_acceptor_distribution
141
+
142
+ # distances: the grid k(R) was evaluated on; force_constant in atomic units
143
+ distribution = donor_acceptor_distribution(distances, 2.58, 0.0443, temperature=298)
144
+ distribution /= simpson(distribution, x=distances)
145
+ average_rate = simpson(distribution * rates, x=distances)
146
+ ```
147
+
148
+ To quantify the (non)adiabaticity of a reaction from the same inputs:
149
+
150
+ ```python
151
+ from autopcet import KappaCoupling, MASS_PROTON
152
+
153
+ # potentials tabulated on a 2^n grid, electronic coupling in eV
154
+ system = KappaCoupling(rp, reactant_energies, product_energies, electronic_coupling)
155
+ system.calculate(MASS_PROTON)
156
+
157
+ print(system.tau_electron, system.tau_proton, system.adiabaticity, system.kappa)
158
+ print(system.v_semiclassical, system.v_nonadiabatic, system.v_adiabatic)
159
+ ```
160
+
161
+ ### Units and naming
162
+
163
+ Energies are in electronvolts, lengths in ångström, and particle masses in
164
+ electron masses (atomic units), unless a name says otherwise. Unit conversions
165
+ are exported as named constants (`KCAL_TO_EV`, `HARTREE_TO_EV`,
166
+ `ANGSTROM_TO_BOHR`, ...), as are the physical constants (`BOLTZMANN`, `HBAR`,
167
+ `MASS_PROTON`, `MASS_DEUTERON`, ...).
168
+
169
+ ## Plotting
170
+
171
+ `autopcet.plotting` draws the quantities above in a consistent house style. It
172
+ needs matplotlib, from the `plotting` extra, and is imported explicitly --
173
+ `import autopcet` itself never pulls matplotlib in:
174
+
175
+ ```python
176
+ import matplotlib.pyplot as plt
177
+
178
+ from autopcet.plotting import (
179
+ figure_of,
180
+ plot_proton_states,
181
+ plot_state_pair_map,
182
+ use_style,
183
+ )
184
+
185
+ use_style()
186
+
187
+ # the two diabats with their vibrational states, one panel each
188
+ reactant_axes, _ = plot_proton_states(system, n_states=6)
189
+ figure_of(reactant_axes).savefig("proton_states.png")
190
+
191
+ # which pairs of vibronic states actually carry the rate constant
192
+ ax = plot_state_pair_map(system, "contribution")
193
+ figure_of(ax).savefig("state_pairs.png")
194
+ plt.close("all")
195
+ ```
196
+
197
+ Every function takes an optional axes to draw on and returns the axes it drew,
198
+ so figures compose. None of them saves, shows, or closes a figure, and none
199
+ sets an axis limit that was not read off the data -- placing and saving the
200
+ figure stays yours.
201
+
202
+ What it can draw:
203
+
204
+ | Function | Figure |
205
+ | --- | --- |
206
+ | `plot_proton_states`, `plot_state_ladder` | diabatic potentials with their vibrational states |
207
+ | `plot_isotope_states` | proton and deuteron states on shared axes |
208
+ | `plot_potential_family` | one proton potential per donor-acceptor distance |
209
+ | `plot_potential_fit` | a fitted potential against the data, with residuals |
210
+ | `plot_state_pair_map`, `plot_state_pair_grid` | the (u, v) matrices as annotated heat maps |
211
+ | `plot_populations` | Boltzmann populations of the reactant states |
212
+ | `plot_arrhenius`, `plot_kie_vs_temperature` | rate constant and KIE against temperature |
213
+ | `plot_rate_vs_driving_force` | the Marcus curve, marking $`-\lambda`$ |
214
+ | `plot_thermal_average` | $`k(R)`$, $`P(R)`$, and the product that decides the average |
215
+ | `plot_crossing`, `plot_diabats_and_adiabats` | the `KappaCoupling` nonadiabaticity analysis |
216
+ | `plot_edl_profile` | the interfacial potential drop of an EDL model |
217
+ | `plot_distance_scan`, `plot_proton_scan` | the raw energies off an ASE scan |
218
+
219
+ The sweeps behind the temperature and driving-force figures are ordinary
220
+ library functions, so they work without matplotlib:
221
+
222
+ ```python
223
+ import numpy as np
224
+
225
+ from autopcet import temperature_sweep
226
+ from autopcet.plotting import plot_arrhenius
227
+
228
+ temperatures = np.linspace(250, 400, 16)
229
+ plot_arrhenius(temperatures, temperature_sweep(system, temperatures))
230
+ ```
231
+
232
+ A sweep leaves the system holding the results of its last point, as any
233
+ `calculate` call does; the input it assigns is put back before it returns.
234
+
235
+ Colours, sizes, and the axis labels come from `autopcet.plotting.style`.
236
+ `use_style()` sets them globally, `style_context()` only for a block, and both
237
+ take overrides:
238
+
239
+ ```python
240
+ from autopcet.plotting import style_context
241
+
242
+ with style_context(**{"axes.labelsize": 18}):
243
+ ...
244
+ ```
245
+
246
+ ## Reporting
247
+
248
+ The same state-pair matrices go out as text through `autopcet.reporting`, which
249
+ needs no matplotlib:
250
+
251
+ ```python
252
+ import sys
253
+
254
+ from autopcet import write_contribution_table
255
+
256
+ # every pair's population, |S_uv|^2, free energy, activation energy, and share
257
+ # of the total rate constant
258
+ write_contribution_table(sys.stdout, system, n_states=4)
259
+
260
+ with open("contributions.log", "w") as log:
261
+ write_contribution_table(log, system, label="H", n_states=4)
262
+ write_contribution_table(log, system_d, label="D", n_states=4)
263
+ ```
264
+
265
+ `format_contribution_table` returns the same block as a string, and
266
+ `contribution_percentages` returns just the percentage matrix.
267
+ `plot_state_pair_map` draws these numbers instead; the one difference is that
268
+ the map shows `|S_uv|`, which reads better on a linear colour scale, where the
269
+ table prints the `|S_uv|^2` that actually enters the rate.
270
+
271
+ ## Examples
272
+
273
+ The `examples/` directory contains worked calculations, each with its input
274
+ data and a reference output to compare against:
275
+
276
+ 1. `example1_basic_usage` — rate constant, vibronic-state analysis, and H/D
277
+ KIE for a first-principles double-well potential.
278
+ 2. `example2_Y356-Y731` — rate constants as a function of the proton
279
+ donor-acceptor distance for the RNR Y356-Y731 interface, thermally averaged
280
+ over an umbrella-sampled P(R) distribution.
281
+ 3. `example3_BIP_KIE` — electrochemical and photochemical KIEs for a
282
+ benzimidazole-phenol (BIP) system, one script each
283
+ (`example3_BIP_KIE_electrochemical.py` and `..._photochemical.py`). They
284
+ share a directory, so every file each writes is tagged with its own name.
285
+ 4. `example4_CoTPP` — heterogeneous electrochemical PCET for CoTPP on
286
+ graphene, combining the EDL model with a density-of-states average.
287
+ 5. `example5_RNR_nonadiabaticity` — vibronic couplings and nonadiabaticity
288
+ analysis for the RNR Y356-Y731 interface, in the gas phase
289
+ (`--config gas`) or in the protein environment (`--config env`).
290
+ 6. `example6_ORCA_ASE` — the whole workflow run in-process through ASE with
291
+ ORCA: donor-acceptor scan, proton endpoint optimizations, proton
292
+ potentials, effective mode, rate constant, and KIE. Unlike the others it
293
+ needs a working ORCA installation (pass its path with `--orca`) and your
294
+ own reactant/product structures.
295
+
296
+ Run an example from inside its own directory, e.g.:
297
+
298
+ ```bash
299
+ cd examples/example1_basic_usage
300
+ python example1_basic_usage.py
301
+ ```
302
+
303
+ Example 5 takes the configuration to run as an argument, and writes a separate
304
+ set of figures for each:
305
+
306
+ ```bash
307
+ cd examples/example5_RNR_nonadiabaticity
308
+ python example5_nonadiabaticity_Y356-Y731.py --config gas
309
+ python example5_nonadiabaticity_Y356-Y731.py --config env
310
+ ```
311
+
312
+ The reference outputs were generated with the NumPy and SciPy versions current
313
+ at the time; results can drift in the third significant digit across releases.
314
+ `tests/test_examples.py` re-runs examples 1 and 5 and compares them at a
315
+ relative tolerance.
316
+
317
+ ## Command-line tools
318
+
319
+ Installing the package also installs four helpers that prepare the Gaussian
320
+ inputs a calculation like the ones above starts from. They wrap
321
+ `autopcet.structure` and `autopcet.gaussian_io`, so anything they do is also
322
+ available from Python. Reading structure files in formats other than xyz needs
323
+ ASE, from the `ase` extra. Every one of them takes `--help`.
324
+
325
+ The four run in the order below, which is the order a proton potential is built
326
+ in.
327
+
328
+ ### 1. Scan the proton donor-acceptor distance
329
+
330
+ `autopcet-scan-da` takes the fully optimized reactant and product structures --
331
+ the atoms must appear in the same order in both -- and writes, for each
332
+ donor-acceptor distance $`R`$, a pair of Gaussian inputs that reoptimize the two
333
+ states with $`R`$ frozen:
334
+
335
+ ```bash
336
+ autopcet-scan-da -r reac.xyz -p prod.xyz -D 0 -A 2 --start 2.4 --stop 2.8 --points 9
337
+ ```
338
+
339
+ `-D` and `-A` are the 0-based indices of the proton donor and acceptor. Charges
340
+ and multiplicities default to a neutral singlet reactant and a +1 doublet
341
+ product; `--reactant-charge` and friends override them. This assumes the two
342
+ diabatic electronic states differ only in the charge of the whole system, as
343
+ they do in homogeneous electrochemical PCET or in photoexcited PCET with an
344
+ external photoreceptor.
345
+
346
+ `--template` points at a file holding your own Gaussian header, in place of the
347
+ built-in B3LYP/6-31+G(d,p) one. It is formatted with `{state}`, `{charge}`, and
348
+ `{multiplicity}`.
349
+
350
+ > [!NOTE]
351
+ > Set `Nosymm` in these constrained optimizations. Without it Gaussian rotates
352
+ > the molecule and the later steps will not work.
353
+
354
+ ### 2. Align and average the structures
355
+
356
+ `autopcet-align-average` overlays the reactant and product so the proton donor
357
+ and acceptor are superimposed along Z with their midpoint at the origin, rotates
358
+ the product about that axis to minimize the RMSD to the reactant, and averages
359
+ the two geometries:
360
+
361
+ ```bash
362
+ autopcet-align-average -r reac.xyz -p prod.xyz --r-donor 1 --r-acceptor 2 --p-donor 1 --p-acceptor 2
363
+ ```
364
+
365
+ These indices are 1-based, as printed by most quantum chemistry programs. The
366
+ aligned intermediates are written alongside their inputs, and the average goes
367
+ to `AVERAGE_STRUCTURE.xyz` unless `-o` says otherwise.
368
+
369
+ From the averaged structure, optimize the proton on the donor for the reactant
370
+ and on the acceptor for the product, with every other nucleus frozen. With
371
+ Gaussian, set that frozen optimization up by hand; with an ASE calculator,
372
+ `optimize_proton` does it in-process (see
373
+ [Running the scans with an ASE calculator](#running-the-scans-with-an-ase-calculator)).
374
+
375
+ ### 3. Scan the proton along its transfer axis
376
+
377
+ `autopcet-scan-proton` builds the axis through the two optimized proton
378
+ positions and writes a Gaussian single point for each point along it. Run it
379
+ once per state:
380
+
381
+ ```bash
382
+ autopcet-scan-proton -r averaged_optH_reac.xyz -p averaged_optH_prod.xyz -H 1 --state reactant --charge 0 --multiplicity 1
383
+ ```
384
+
385
+ `-H` is the 0-based index of the transferring proton. The scan spans 1.7 times
386
+ the distance between the two equilibrium proton positions, and never less than
387
+ 1 Å, so that the proton comes close to both the donor and the acceptor.
388
+
389
+ ### 4. Effective donor-acceptor force constant
390
+
391
+ `autopcet-keff` projects the normal modes of a Gaussian frequency job onto the
392
+ donor-acceptor axis and prints the effective force constant (a.u.), reduced mass
393
+ (amu), and frequency (cm<sup>-1</sup>) of the donor-acceptor mode:
394
+
395
+ ```bash
396
+ autopcet-keff --log freq.log -D 0 -A 1
397
+ ```
398
+
399
+ The force constant is what `donor_acceptor_distribution` needs to build the
400
+ $`P(R)`$ the rate constant is thermally averaged over.
401
+
402
+ > [!NOTE]
403
+ > Run the frequency job with `#P` and `freq=HPmodes`. The parser reads the
404
+ > high-precision normal modes those settings print and will not work without
405
+ > them.
406
+
407
+ ## Running the scans with an ASE calculator
408
+
409
+ Everything the helpers above prepare Gaussian inputs for can also run
410
+ in-process against any [ASE](https://ase-lib.org) calculator — ORCA, xTB, or
411
+ anything else implementing ASE's `Calculator` interface — through
412
+ `autopcet.ase_io`, installed with the `ase` extra. The diabatic electronic
413
+ states are selected through the calculator: configure one per state, carrying
414
+ that state's charge and multiplicity, and run each scan once per state.
415
+
416
+ Everything that moves atoms — `run_da_scan`, `optimize_proton`,
417
+ `run_vibrations` — needs forces, so a calculator that runs an external program
418
+ has to be told to compute the gradient. For ORCA that means `EnGrad` in
419
+ `orcasimpleinput`: without it ORCA writes no `.engrad` file, ASE reports no
420
+ forces, and the scan stops on its first step. `run_proton_scan` and
421
+ `read_scan_energies` need energies only.
422
+
423
+ ```python
424
+ from ase.calculators.orca import ORCA, OrcaProfile
425
+ from ase.io import read
426
+
427
+ from autopcet import (
428
+ effective_mode_from_vibrations,
429
+ optimize_proton,
430
+ run_da_scan,
431
+ run_proton_scan,
432
+ run_vibrations,
433
+ )
434
+
435
+ profile = OrcaProfile(command="/opt/orca/orca")
436
+ reactant_calc = ORCA(
437
+ profile=profile,
438
+ directory="reac",
439
+ charge=0,
440
+ mult=1,
441
+ orcasimpleinput="B3LYP def2-SVP TightSCF EnGrad",
442
+ # ASE replaces its whole default block, so set the memory here too
443
+ orcablocks="%pal nprocs 8 end\n%maxcore 3000",
444
+ )
445
+ # ... and a product_calc with that state's charge and multiplicity
446
+
447
+ # 1. reoptimize the state on a grid of frozen donor-acceptor distances
448
+ scan = run_da_scan(read("reac.xyz"), 0, 2, reactant_calc, [2.4, 2.5, 2.6])
449
+
450
+ # 2. align and average with autopcet.structure as before, then optimize the
451
+ # proton endpoints with the rest of the frame frozen
452
+ endpoint = optimize_proton(averaged, 1, reactant_calc)
453
+
454
+ # 3. the proton potential along the transfer axis, on the same grid the
455
+ # command-line helper writes; feeds PCET directly
456
+ potential = run_proton_scan(reactant_endpoint, product_endpoint, 1, reactant_calc)
457
+ pcet = PCET((potential.offsets, potential.relative_energies), ...)
458
+
459
+ # 4. the effective donor-acceptor mode, in place of autopcet-keff
460
+ mode = effective_mode_from_vibrations(
461
+ run_vibrations(equilibrium, reactant_calc), donor=0, acceptor=2
462
+ )
463
+ ```
464
+
465
+ Every grid point reuses the calculator's `directory`, so each job overwrites
466
+ the last one's files. Give a scan its own per-point directory if you want to
467
+ keep them, or to reuse a converged wavefunction as the next point's guess.
468
+
469
+ `examples/example6_ORCA_ASE` walks the full pipeline. For jobs run elsewhere —
470
+ on a cluster, say — keep writing inputs with `autopcet-scan-proton` and skip
471
+ the manual energy table afterwards: `read_scan_energies(directory, state)`
472
+ walks the numbered directories the helper created and reads the finished
473
+ outputs in any format ASE recognizes, Gaussian logs and ORCA outputs included.
474
+ Name each job's output after its input, so grid point `03` of a reactant scan
475
+ holds `reactant_sp.out` (or `.log`, `.xyz`, `.traj`) beside its `reactant_sp.gjf`.
476
+
477
+ > [!NOTE]
478
+ > Point `OrcaProfile` at the full path of your ORCA binary. On desktop Linux a
479
+ > bare `orca` on the PATH is usually the GNOME screen reader, not ORCA.
480
+
481
+ > [!NOTE]
482
+ > ASE warns `Geometry optimization did not converge!` for every ORCA gradient
483
+ > single point, reading the gradient header as the start of a relaxation, and
484
+ > prints a caution about reading `.engrad` files from optimizations. Both are
485
+ > artefacts of ASE's ORCA reader and say nothing about your calculation.
486
+
487
+ ## Development
488
+
489
+ Run the complete local checks:
490
+
491
+ ```bash
492
+ ruff check .
493
+ ruff format --check .
494
+ mypy
495
+ pytest
496
+ python -m build
497
+ python -m twine check dist/*
498
+ ```
499
+
500
+ Ruff can apply safe lint and formatting changes with:
501
+
502
+ ```bash
503
+ ruff check --fix .
504
+ ruff format .
505
+ ```
506
+
507
+ Install the Git hooks once, then pre-commit will run the fast checks before
508
+ each commit:
509
+
510
+ ```bash
511
+ pre-commit install
512
+ pre-commit run --all-files
513
+ ```
514
+
515
+ ## Project layout
516
+
517
+ ```text
518
+ .
519
+ ├── .github/workflows/ci.yml # automated quality and packaging checks
520
+ ├── autopcet/ # the installable, typed package
521
+ │ ├── cli/ # the autopcet-* command-line helpers
522
+ │ └── plotting/ # optional matplotlib figures (plotting extra)
523
+ ├── build_tools/ # optional Conda setup
524
+ ├── examples/ # worked examples with reference outputs
525
+ ├── tests/ # behavior-focused tests
526
+ └── pyproject.toml # project metadata and tool configuration
527
+ ```
528
+
529
+ ## License
530
+
531
+ Released under the [MIT License](https://github.com/LouieSlocombe/autopcet/blob/main/LICENSE).