Skip to content

Write CONECT records for bonds between residues differing in insertion code - #946

Open
haeganm wants to merge 3 commits into
biotite-dev:mainfrom
haeganm:fix-conect-ins-code
Open

haeganm wants to merge 3 commits into
biotite-dev:mainfrom
haeganm:fix-conect-ins-code

Conversation

@haeganm

@haeganm haeganm commented Sep 17, 2026

Copy link
Copy Markdown

Fixes #945

_remove_non_conect_bonds() found inter-residue bonds by comparing res_id and chain_id only. Two residues that differ just in ins_code (e.g. 52 and 52A) looked like one residue, so a bond between them got no CONECT record and was lost when writing a PDB file.

This now compares residue starts via get_residue_starts_for(), the same way _filter_bonds() does for the PDBx writer.

Added one small test with a disulfide between 52 and 52A. It fails on main and passes here. tests/structure/io/test_pdb.py, test_pdbx.py and tests/structure/test_bonds.py pass, ruff 0.9.7 and pyright are clean on the changed file.

…n code

_remove_non_conect_bonds() compared only res_id and chain_id to find
inter-residue bonds, so a bond between e.g. residues 52 and 52A was
treated as intra-residue and not written. Compare residue starts
instead, like the PDBx writer does.

@padix-key padix-key left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Good catch! Thank you for the fix 👍

Comment thread tests/structure/io/test_pdb.py Outdated
np.testing.assert_array_equal(actual_bonds, expected_bonds)


def test_inter_residue_bond_with_ins_code():

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Would it be possible to generalize this test? For example we could check that PDBFile.set_structure() adds the same CONECT records as there are in the PDB file downloaded from RCSB. The test assets do contain structures with insertion codes, so this should catch it. If for some reason complete CONECT equivalence is not possible, we could at least compare the number of CONECT records.

@codspeed

codspeed Bot commented Sep 18, 2026 •

Copy link
Copy Markdown

Merging this PR will degrade performance by 11.38%

❌ 3 (👁 1) regressed benchmarks
✅ 106 untouched benchmarks
⏩ 14 skipped benchmarks1

Warning

Please fix the performance issues or acknowledge them on CodSpeed.

Performance Changes

Benchmark BASE HEAD Efficiency
❌ benchmark_match_kmer_selection[KmerTable-None] 246.2 µs 278.2 µs -11.5%
❌ benchmark_match[KmerTable-None] 294 µs 327 µs -10.11%
👁 benchmark_match_kmer_selection[KmerTable-11*11*1*1***111] 242.1 µs 276.8 µs -12.51%

Tip

Investigate this regression by commenting @codspeedbot fix this regression on this PR, or directly use the CodSpeed MCP with your agent.


Comparing haeganm:fix-conect-ins-code (9777950) with main (1aaf784)

Open in CodSpeed

Footnotes

  1. 14 benchmarks were skipped, so the baseline results were used instead. If they were deleted from the codebase, click here and archive them to remove them from the performance reports. ↩

Compare the written CONECT records with the CONECT records of the
original file and with the inter-residue bonds of the structure,
matched by atom identity since serials are renumbered.
@haeganm

haeganm commented Sep 21, 2026

Copy link
Copy Markdown
Author

Good idea. I replaced the synthetic test with one over every PDB test asset. It checks that the written CONECT records include all pairs from the original file's CONECT records (matched by chain, residue, insertion code and atom name, since serials get renumbered) and all inter-residue bonds of the parsed structure.

One thing I found while doing this: the RCSB comparison alone doesn't catch the bug. RCSB only writes CONECT for hetero groups and disulfides, and hetero bonds are kept by the separate hetero check. What the fix changes in 1igy are 8 peptide bonds between Kabat-numbered residues like 82/82A, which RCSB doesn't record. So the inter-residue check is the half that fails on main (for 1igy only) and passes with the fix. The RCSB check holds on both.

The failing "databases" job is the biotite.database.rcsb doctest, which is also failing on main.

Comment thread tests/structure/io/test_pdb.py Outdated
Keeping all altlocs and the original atom IDs makes the identity
matching unnecessary.
test_file.set_structure(atoms)
test_pairs = _conect_pairs(test_file)

assert _conect_pairs(ref_file) <= test_pairs

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Shouldn't these two sets be equal?

Suggested change
assert _conect_pairs(ref_file) <= test_pairs
assert _conect_pairs(ref_file) = test_pairs

This branch has not been deployed

No deployments
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.

PDB writer drops bonds between residues that differ only in insertion code

2 participants