moleculekit.tools.sybyl module#
SYBYL atom typing from perceived chemistry and stored bond orders.
PROPKA decides which groups of a non-canonical residue can titrate entirely
from SYBYL atom types, and it has no bond-order field to consult: an
propka.atom.Atom carries bonded_atoms, num_pi_elec_2_3_bonds
and steric_number, and nothing else about how its bonds are ordered. So
propka.ligand.assign_sybyl_type reconstructs the chemistry from coordinates
- ring membership, planarity and aromaticity by geometry, double bonds by a
distance threshold - and most of its helpers exist only for that.
Reconstruction gets wrong what a stored bond order states outright. PROPKA’s
double-bond test compares against MAX_C_DOUBLE_BOND of 1.3 A, calibrated
for carbon, so a P=O at ~1.5 A never registers and every terminal phosphate
oxygen is typed O.3. Each then becomes an independent titratable site, and
since a phosphate has one ionizable proton fewer than it has terminal oxygens,
the group is over-charged by one.
This module reads the bond orders the molecule already carries and lets RDKit perceive the rest. Ring membership, aromaticity, conjugation and hybridisation are RDKit’s answers, not re-derived here: aromaticity in particular cannot be read off bond types, since mmCIF commonly stores aromatic rings in Kekule form with no aromatic bond recorded at all.
Reproducing PROPKA’s conventions#
PROPKA reads certain types as markers rather than as neutral chemistry, so this module matches its spellings rather than any external SYBYL implementation:
a carboxylate is emitted as an
O.co2-/O.co2pair, one per oxygen;N.pl3doubles as the guanidinium and amidinium detector - PROPKA’sC.2branch looks for a carbon bearing twoN.pl3nitrogens that each have a single heavy neighbour;P.3is emitted for phosphorus so that, when these types are handed to PROPKA, its phosphorus branch - which ends by resetting every bonded oxygen toO.3- returns early instead of overwriting them.
What this does and does not settle#
Atom types settle group identity - which groups PROPKA will find - and
nothing else. They cannot settle protonation, because SYBYL has no vocabulary
for it: a neutral, mono-anionic and di-anionic phosphate all type identically,
as O.2 plus terminal O.3. So bond orders are respected here, and the
hydrogens and formal charges an input states are not, with one exception -
N.4 needs the charge, and O.co2- is given to the oxygen the input
charged, since both types carry charge information themselves.
A templated residue’s protonation is therefore respected one layer up rather
than here: preparation_propka._apply_templated_formal_charges pins each group’s
charge from mol.formalcharge and marks it non-titratable, so PROPKA never
re-decides what the template already settled. Anything calling this module and
handing the result to PROPKA without that step will get protonation re-invented.
Only the types PROPKA consumes are produced, in group detection (C.2,
Cl, F, N.1, N.3, N.4, N.am, N.ar, N.pl3,
O.2, O.3, O.co2, S.3) and in the pi-electron tables (C.1,
C.ar, O.co2). Anything else falls back to the capitalised element
symbol, as PROPKA itself does.
- moleculekit.tools.sybyl.sybylTypes(mol, sel=None)#
Assign SYBYL atom types from perceived chemistry and stored bond orders.
- Parameters:
mol (
Molecule) – A molecule carryingbonds,bondtypeandformalcharge.sel (
strornumpy.ndarrayorNone) – Atom selection, boolean mask or index array restricting which atoms are typed. Perception always runs on the whole molecule, so a residue is typed in the context of its neighbours.
- Returns:
types –
{atom_index: sybyl_type}for the selected heavy atoms. Hydrogens are omitted: PROPKA types themHand never branches on it.- Return type:
Notes
For a residue that records no bond orders only the phosphorus types are returned - the one judgement here that does not need them - and everything else is left to whatever the caller had. See
_ordered_residues().Examples
>>> from moleculekit.molecule import Molecule >>> from moleculekit.tools.sybyl import sybylTypes >>> mol = Molecule("3ptb") >>> mol.templateResidueFromSmiles( ... "resname BEN", "NC(=[NH2+])c1ccccc1", addHs=True ... ) >>> types = sybylTypes(mol, "resname BEN") >>> sorted(set(types.values())) ['C.2', 'C.ar', 'N.pl3']