-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathcli.py
More file actions
177 lines (151 loc) · 6.59 KB
/
Copy pathcli.py
File metadata and controls
177 lines (151 loc) · 6.59 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
"""
Command-line interface for PCET rate predictions.
Usage:
pcet rate --V_el 0.5 --delta_G -5.0 --lambda 20.0 --omega 3000 --d_DA 2.7
pcet hessian reactant.fchk product.fchk --proton 5 --donor 3 --acceptor 8
pcet benchmark
"""
import argparse
import sys
import json
def cmd_rate(args):
"""Compute PCET rate from explicit parameters."""
from pcet_engine.core.rate_engine import PCETRateEngine
engine = PCETRateEngine(temperature=args.temperature)
result = engine.compute_rate(
V_el=args.V_el,
delta_G=args.delta_G,
lambda_reorg=args.lambda_reorg,
omega_H=args.omega,
d_DA=args.d_DA,
method=args.method,
delta_0=args.delta_0,
)
if args.json:
print(json.dumps({
"k_H": result.k_H,
"k_D": result.k_D,
"KIE": result.KIE,
"E_a_kcal": result.E_a,
"method": result.method,
"omega_H_cm": result.omega_H,
"omega_D_cm": result.omega_D,
}, indent=2))
else:
print(f"PCET Rate Prediction ({result.method})")
print(f"{'='*40}")
print(f"k_H = {result.k_H:.3e} s⁻¹")
print(f"k_D = {result.k_D:.3e} s⁻¹")
print(f"KIE = {result.KIE:.1f}")
print(f"E_a = {result.E_a:.2f} kcal/mol")
print(f"ω_H = {result.omega_H:.0f} cm⁻¹")
print(f"ω_D = {result.omega_D:.0f} cm⁻¹")
def cmd_hessian(args):
"""Compute PCET rate from Hessian files."""
from pcet_engine.parsers import parse_gaussian_fchk, parse_orca_hess
from pcet_engine.core.rate_engine import PCETRateEngine
def load(path):
if path.endswith(".fchk"):
return parse_gaussian_fchk(path)
elif path.endswith(".hess"):
return parse_orca_hess(path)
else:
sys.exit(f"Unknown file format: {path} (expected .fchk or .hess)")
qc_R = load(args.reactant)
qc_P = load(args.product)
engine = PCETRateEngine(temperature=args.temperature)
result = engine.compute_rate_from_hessian(
hessian_R=qc_R.hessian,
hessian_P=qc_P.hessian,
geom_R=qc_R.geometry,
geom_P=qc_P.geometry,
masses=qc_R.masses,
proton_idx=args.proton,
donor_idx=args.donor,
acceptor_idx=args.acceptor,
V_el=args.V_el,
delta_G=args.delta_G,
lambda_outer=args.lambda_outer,
)
if args.json:
print(json.dumps({
"k_H": result.k_H,
"k_D": result.k_D,
"KIE": result.KIE,
"E_a_kcal": result.E_a,
"omega_H_cm": result.omega_H,
"omega_D_cm": result.omega_D,
"d_DA_A": result.d_DA,
"method": result.method,
}, indent=2))
else:
print(f"PCET Rate from Hessian ({result.method})")
print(f"{'='*40}")
print(f"Reactant: {args.reactant}")
print(f"Product: {args.product}")
print(f"k_H = {result.k_H:.3e} s⁻¹")
print(f"k_D = {result.k_D:.3e} s⁻¹")
print(f"KIE = {result.KIE:.1f}")
print(f"E_a = {result.E_a:.2f} kcal/mol")
print(f"ω_H = {result.omega_H:.0f} cm⁻¹")
print(f"d_DA = {result.d_DA:.3f} Å")
def cmd_benchmark(args):
"""Run benchmark against published enzyme PCET data."""
from pcet_engine.benchmarks.systems import BENCHMARK_SYSTEMS
from pcet_engine.core.rate_engine import PCETRateEngine
engine = PCETRateEngine()
print(f"{'System':<8} {'k_H (s⁻¹)':>12} {'KIE calc':>10} {'KIE exp':>10} {'Δ%':>8}")
print("-" * 52)
for name, sys in BENCHMARK_SYSTEMS.items():
result = engine.compute_rate(
V_el=sys.V_el,
delta_G=sys.delta_G,
lambda_reorg=sys.lambda_reorg,
omega_H=sys.omega_H,
d_DA=sys.d_DA,
delta_0=sys.delta_0,
)
kie_exp = sys.KIE_exp
err = (result.KIE - kie_exp) / kie_exp * 100
print(f"{name:<8} {result.k_H:>12.3e} {result.KIE:>10.1f} {kie_exp:>10.1f} {err:>+7.1f}%")
def main():
parser = argparse.ArgumentParser(
prog="pcet",
description="PCET Rate Theory Engine — predict proton-coupled electron transfer rates",
)
sub = parser.add_subparsers(dest="command")
# rate subcommand
p_rate = sub.add_parser("rate", help="Compute rate from explicit parameters")
p_rate.add_argument("--V_el", type=float, required=True, help="Electronic coupling (kcal/mol)")
p_rate.add_argument("--delta_G", type=float, required=True, help="Driving force (kcal/mol)")
p_rate.add_argument("--lambda_reorg", type=float, required=True, help="Reorganization energy (kcal/mol)")
p_rate.add_argument("--omega", type=float, required=True, help="Proton frequency (cm⁻¹)")
p_rate.add_argument("--d_DA", type=float, required=True, help="Donor-acceptor distance (Å)")
p_rate.add_argument("--method", default="vibronic_multi", choices=["marcus", "vibronic_single", "vibronic_multi"])
p_rate.add_argument("--delta_0", type=float, default=None, help="Tunneling distance (Å)")
p_rate.add_argument("--temperature", type=float, default=298.15, help="Temperature (K)")
p_rate.add_argument("--json", action="store_true", help="Output as JSON")
p_rate.set_defaults(func=cmd_rate)
# hessian subcommand
p_hess = sub.add_parser("hessian", help="Compute rate from Hessian files (.fchk or .hess)")
p_hess.add_argument("reactant", help="Reactant Hessian file")
p_hess.add_argument("product", help="Product Hessian file")
p_hess.add_argument("--proton", type=int, required=True, help="Proton atom index (0-based)")
p_hess.add_argument("--donor", type=int, required=True, help="Donor atom index (0-based)")
p_hess.add_argument("--acceptor", type=int, required=True, help="Acceptor atom index (0-based)")
p_hess.add_argument("--V_el", type=float, required=True, help="Electronic coupling (kcal/mol)")
p_hess.add_argument("--delta_G", type=float, required=True, help="Driving force (kcal/mol)")
p_hess.add_argument("--lambda_outer", type=float, default=0.0, help="Outer-sphere reorganization (kcal/mol)")
p_hess.add_argument("--temperature", type=float, default=298.15, help="Temperature (K)")
p_hess.add_argument("--json", action="store_true", help="Output as JSON")
p_hess.set_defaults(func=cmd_hessian)
# benchmark subcommand
p_bench = sub.add_parser("benchmark", help="Run benchmarks against published enzyme data")
p_bench.set_defaults(func=cmd_benchmark)
args = parser.parse_args()
if not args.command:
parser.print_help()
sys.exit(1)
args.func(args)
if __name__ == "__main__":
main()