Skip to content

fix dihedral_backbone() returning garbage angles across chain breaks - #933

Open
steps-re wants to merge 1 commit into
biotite-dev:mainfrom
steps-re:fix/dihedral-backbone-chain-breaks
Open

fix dihedral_backbone() returning garbage angles across chain breaks#933
steps-re wants to merge 1 commit into
biotite-dev:mainfrom
steps-re:fix/dihedral-backbone-chain-breaks

Conversation

@steps-re

Copy link
Copy Markdown

fixes #744

dihedral_backbone() builds its phi/psi/omega coordinate arrays purely by positional adjacency between residues in the AtomArray. it never checks whether two consecutive residues are actually bonded. so if a structure has a missing loop, or you concatenate two unrelated fragments into one AtomArray, the dihedrals spanning that gap come out as finite garbage instead of NaN, exactly like padix-key called out in the issue thread.

went with the approach discussed in the thread: check the C-N distance between consecutive residues directly on the coordinates dihedral_backbone() already extracted, rather than calling check_backbone_continuity() separately (which would mean re-filtering the whole array again). used the same 1.2-1.8 A tolerance that check_backbone_continuity() / filter_linear_bond_continuity() already use elsewhere in the codebase, so the threshold is consistent across the library. where the C(i)-N(i+1) distance falls outside that range, psi[i], omega[i], and phi[i+1] get set to NaN since those are the three angles that straddle the junction between residue i and i+1.

while testing this i found nucleotide_dihedral_backbone() has the exact same bug for the O3'-P bond between residues, so I fixed that too with the same approach (epsilon[i], zeta[i], alpha[i+1] get NaN'd on a broken O3'(i)-P(i+1) junction). that one isn't filed anywhere, just the same root cause turning up twice.

verification:

  • added test_dihedral_backbone_chain_break and test_nucleotide_dihedral_backbone_chain_break to tests/structure/test_geometry.py. both build a synthetic structure from two real fragments of 1l2y / 4p5j, renumber the second fragment's res_id to be perfectly continuous with the first (so the test can't accidentally pass just because of a res_id jump), then translate it 1000 A away so the two fragments are not bonded. confirmed both tests fail on current main (angles come back as finite numbers) and pass with this fix.
  • ran the full tests/structure/ suite (4176 passed, 13 skipped, no failures) to check for regressions, plus the existing multi-model, MDTraj-consistency, and barnaba-consistency dihedral tests specifically.
  • ruff check + ruff format clean on the touched files.

disclosure: this fix (code + tests) was put together with AI assistance (Claude), then reviewed and verified by me before opening the PR.

dihedral_backbone() and nucleotide_dihedral_backbone() built their phi/psi/omega
(and alpha/beta/gamma/delta/epsilon/zeta) coordinate arrays purely from positional
adjacency between residues, with no check that consecutive residues were actually
bonded. When two residues were adjacent in the AtomArray but not chemically
connected (missing loop, concatenated chains), the dihedrals spanning that gap
came out as finite garbage instead of NaN.

Add a bond-distance check (C-N for the peptide backbone, O3'-P for the
phosphate backbone) using the same 1.2-1.8 A tolerance already used by
check_backbone_continuity()/filter_linear_bond_continuity(), and NaN out the
angles that span a junction where the distance falls outside that range.
Applied to both dihedral_backbone() (fixes biotite-dev#744) and the identical defect in
nucleotide_dihedral_backbone(), which was not filed but has the same root cause.

Fixes biotite-dev#744.

Co-Authored-By: Claude <noreply@anthropic.com>
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.

Dihedral Backbone Does Not Account for Gaps, Returns Incorrect Values

1 participant