Skip to content

Seed H-abstraction TS reactive-core bonds per element - #1034

Open
calvinp0 wants to merge 1 commit into
mainfrom
fix_h_abs_per_element_bond_lengths
Open

Seed H-abstraction TS reactive-core bonds per element#1034
calvinp0 wants to merge 1 commit into
mainfrom
fix_h_abs_per_element_bond_lengths

Conversation

@calvinp0

Copy link
Copy Markdown
Member

Seeds the two reactive-core bonds of an H_Abstraction TS guess from the element that carries each
bond, instead of scaling both by a single factor.

The problem

combine_coordinates_with_redundant_atoms set both the A–H(*2) breaking bond and the H(*2)–B
forming bond to 1.2 × the corresponding equilibrium single-bond length. One factor cannot suit
every element: applied to a heavy X–H it under-stretches the core, and applied to the other end it
is calibrated on nothing in particular.

What replaces it

get_h_abs_reactive_bond_length(symbol, bond) returns get_single_bond_length(symbol, 'H') plus a
per-element elongation — the breaking bond keyed on the donor A, the forming bond on the acceptor B.

The elongations are medians of d − r_e(X–H) over 1670 DFT-optimised H-abstraction transition
states, with r_e taken from the same tabulated source the code adds it back to, so the rule
reproduces each measured median to within 0.003 Å.

element n (donor) breaking Δ, 95% CI n (acceptor) forming Δ, 95% CI
C 1273 0.223 [0.219, 0.227] 1340 0.282 [0.280, 0.287]
N 207 0.190 [0.181, 0.199] 150 0.259 [0.245, 0.273]
O 138 0.232 [0.221, 0.249] 117 0.301 [0.273, 0.330]
S 30 0.138 [0.124, 0.172] 22 0.233 [0.200, 0.257]
pooled 0.218 0.281

C, N, O and S carry entries; every other element takes the pooled defaults. Chlorine is not given an
entry — it appears once as an acceptor and never as a donor, which is an anecdote rather than a
median. Hydrogen needs no entry either: the elongation is added to its own r_e(H–H) = 0.75, so the
defaults already yield 0.97 / 1.03 Å.

Subtracting r_e leaves an elongation that is nearly element-free, which is Pauling's bond-order
relation with a fitted order of n ≈ 0.43 (breaking) and n ≈ 0.34 (forming). The textbook n = 0.5
would give +0.18 Å; the data says +0.22 / +0.28. The CH₃ + CH₄ D₃d saddle at 1.34 Å is an
independent check — its elongation of 0.25 Å is the symmetric average of the two.

Conditioning on the donor–acceptor pair rather than on single elements was tested and rejected as
over-fitting: element-only residuals are ≤ 0.05 Å for every pair with n ≥ 80, and the larger
residuals occur only in cells of n ≤ 21.

Accuracy

Mean absolute error against all 1670 reference distances, both bonds:

rule all records the 62 H-partner records
1.2 × multiplicative 0.0669 0.1103
this branch 0.0518 0.0908

This is a net improvement, not a uniform one. For the smallest H-transfer systems the multiplicative
factor happened to sit closer to their own saddles — CH₄ + H has a true saddle at 1.383 / 0.904 Å
(verified in the reference set against a CCSD(T) 1.397 / 0.892), where 1.2 × gives 1.311 / 0.897
and this rule gives 1.310 / 1.030. The gain is in the heavy-atom bulk.

The forming elongation exceeds the breaking one because 70.3% of the reference set is written in the
exothermic direction. The asymmetry therefore describes a reaction written in the RMG template
direction and is not a property of the saddle, which is symmetric for a thermoneutral abstraction.

Passing r1_stretch or r2_stretch explicitly still selects the multiplicative path.

Reuse

Searched before writing: get_code_context for existing bond-length and reactive-core seeding,
git grep -n "def get_single_bond_length", and the element data in arc/common.py. No new element
table was added — get_single_bond_length already tabulates r_e(X–H) for every element needed,
and the elongation dictionaries hold only the four measured offsets. No geometry maths was
hand-rolled; the change is confined to the distances handed to set_zmat_bond().

Tests

Four new tests in arc/job/adapters/ts/heuristics_test.py covering a carbon acceptor, an oxygen
acceptor, a hydrogen acceptor and a hydrogen donor, asserting the resulting seed distances.
48 pass in that file, serially and under -n 4 --dist worksteal.

Validated by mutation rather than coverage — five mutants, each killed by a test: flipping an
element branch, reverting to a flat 1.29 / 1.36, reverting to 1.2 ×, dropping the
r1_stretch / r2_stretch escape hatch, and using the donor element for both bonds.

🤖 Generated with Claude Code

https://claude.ai/code/session_01MbGeU8wLpafo3YFzTky2ho

Copilot AI lite review requested due to automatic review settings August 31, 2026 06:14

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@codecov

codecov Bot commented Aug 31, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 65.67%. Comparing base (b7aa476) to head (b8a4107).
⚠️ Report is 3 commits behind head on main.

Additional details and impacted files
@@           Coverage Diff           @@
##             main    #1034   +/-   ##
=======================================
  Coverage   65.67%   65.67%           
=======================================
  Files         121      121           
  Lines       41018    41036   +18     
  Branches    10549    10553    +4     
=======================================
+ Hits        26937    26950   +13     
- Misses      11066    11068    +2     
- Partials     3015     3018    +3     
Flag Coverage Δ
functionaltests 65.67% <ø> (+<0.01%) ⬆️
unittests 65.67% <ø> (+<0.01%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

… flat factor

The A-H(*2) and H(*2)-B distances of an H_Abstraction TS guess were both set by
multiplying the corresponding well bond length by 1.2. A single factor cannot be
right for every element: it under-stretches a heavy X-H and, applied to the other
end, is calibrated on nothing in particular.

Seed each bond instead from the element that carries it, as the equilibrium X-H
single bond length already available from get_single_bond_length() plus a
per-element elongation. The elongations are medians of d - r_e(X-H) over 1670
DFT-optimized H_Abstraction transition states, conditioned on the donor element
for the breaking bond and on the acceptor element for the forming bond, with r_e
taken from the same tabulated source the code adds it back to. C, N, O and S carry
entries; every other element takes the pooled defaults.

Anchoring the elongation to the element's own X-H bond is what keeps the rule
usable when the donor or the acceptor is itself a hydrogen: a forming H-H bond is
seeded at 1.03 A rather than at 1.36 A, which a Morse curve for H2 prices at 53
kcal/mol of strain against a barrier of roughly 14. Mean absolute error against the
1670 reference distances falls from 0.067 A to 0.052 A overall, and from 0.110 A to
0.091 A on the 62 records in which the donor or the acceptor is a hydrogen. The
gain is in the heavy-atom bulk; for the few diatomic-partner reactions the older
multiplicative factor happened to sit closer to their own saddles.

Passing r1_stretch or r2_stretch explicitly still selects the multiplicative path.
@calvinp0
calvinp0 force-pushed the fix_h_abs_per_element_bond_lengths branch from 02f4cd7 to b8a4107 Compare September 5, 2026 18:58
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants