Skip to content

Take the ProtOr element from the CCD, not from the atom name - #938

Merged
padix-key merged 3 commits into
biotite-dev:mainfrom
dylanpulver:fix-protor-element-from-ccd
Sep 16, 2026
Merged

padix-key merged 3 commits into
biotite-dev:mainfrom
dylanpulver:fix-protor-element-from-ccd

Conversation

@dylanpulver

@dylanpulver dylanpulver commented Sep 3, 2026 •

Copy link
Copy Markdown
Contributor

_calculate_protor_radii() takes an atom's element from the first character of its atom name. Wrong when the element has two letters, or a hydrogen's name does not start with H.

On main @ af1c4bb:

vdw_radius_protor("MSE", "SE") -> 1.77  # sulfur's radius, for selenium (same for SEC)
vdw_radius_protor("PHG", "C4") -> 1.76  # mercury named 'HG' read as a bonded H
vdw_radius_protor("GLY", "CA") -> 1.88  # control, unaffected

Across the CCD, 262 atoms in 175 components change: 187 had their own element misread (139 Se, 23 H, rest Cu/Ni/Ca/Cd/Co/Cr/Na/Cl read as C or N); 75 are heavy atoms with a wrong hydrogen count, because a neighbour named HG/HF counted as hydrogen or one named 1HZ/D1 did not. sasa(vdw_radii="ProtOr"), the default, calls this, using its 1.8 fallback where None is now returned.

The element now comes from chem_comp_atom.type_symbol, the same CCD entry the bonds come from. vdw_radius_single() already uses the annotated element; only this path re-derived it.

test_protor_radii cannot see it: it runs 1GYA filtered to standard amino acids — all one-letter elements — and only asserts the radius is not None.

tests/structure collects 4197: 4184 passed / 13 skipped; reverting only radii.py fails exactly the 4 new cases. A naive "two-letter prefix if it is an element" fix instead breaks the existing test_protor_radii, as CA reads as calcium.

Drafted with Claude Opus 5 (claude-opus-5); every measurement was run.

`_calculate_protor_radii()` derived an atom's element from the first
character of its atom name. That is wrong for two-letter elements and
for hydrogen atoms whose name does not start with `H`:

- `SE` in `MSE`/`SEC` was read as sulfur, so `vdw_radius_protor()`
  returned the sulfur ProtOr radius (1.77) for selenium.
- Metals named `CU1`, `NI`, `CA1`, ... were read as C/N and received a
  carbon or nitrogen radius.
- A mercury atom named `HG` was counted as a bonded hydrogen of its
  neighbour, inflating that atom's hydrogen number.
- Hydrogen atoms named `1HZ`, `2HE2`, ... were treated as heavy atoms
  and were not counted as bonded hydrogens.

The element is now taken from the `chem_comp_atom` category of the
Chemical Component Dictionary, i.e. the same source the bonds already
come from.

@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.

Thanks for fixing this oversight 👍. I left a few comments below, otherwise this PR looks good to me!

Comment thread src/biotite/structure/info/radii.py
Comment thread src/biotite/structure/info/radii.py Outdated
Comment thread src/biotite/structure/info/radii.py Outdated
Comment thread src/biotite/structure/info/radii.py Outdated
if atom_names is None or elements is None:
return {}
return {
str(atom_name): str(element).upper()

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.

I think this is the more idiomatic (and likely faster) way to convert a numpy scalar into a native Python value.

Suggested change
str(atom_name): str(element).upper()
atom_name.item(): element.item().upper()

Comment thread tests/structure/test_info.py Outdated
@codspeed

codspeed Bot commented Sep 10, 2026 •

Copy link
Copy Markdown

Merging this PR will not alter performance

✅ 109 untouched benchmarks
⏩ 14 skipped benchmarks1


Comparing dylanpulver:fix-protor-element-from-ccd (fddf7b5) with main (bcaf9e9)

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. ↩

dylanpulver and others added 2 commits September 15, 2026 00:09
Cache the per-residue calculation, index the element maps directly so a
missing atom raises, and convert the numpy scalars with item().

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011M5uTyCU4WcNTsPvGrErDo
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011M5uTyCU4WcNTsPvGrErDo
@padix-key
padix-key merged commit 1aaf784 into biotite-dev:main Sep 16, 2026
24 of 25 checks passed
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