orthonym.perception.rings#
Note
Internal API. Names and behaviour may change between releases.
Ring system detection and analysis.
Handles detection of: - Simple rings - Fused ring systems - Spiro systems - Aromatic rings - Heterocyclic rings
- orthonym.perception.rings.get_ring_info(mol)#
Get basic ring information from RDKit.
- Parameters:
mol – RDKit Mol object
- Returns:
Dictionary with ring information –
atom_rings: tuple of tuples of atom indices
bond_rings: tuple of tuples of bond indices
num_rings: number of rings
- Return type:
Dict
- orthonym.perception.rings.get_ring_systems(mol, include_spiro=False)#
Find connected ring systems.
Groups rings that share atoms into ring systems: - Fused rings share >1 atom - Spiro rings share exactly 1 atom
- Parameters:
mol – RDKit Mol object
include_spiro (bool) – If True, spiro-connected rings are in same system
- Returns:
List of sets, each set contains atom indices in one ring system
- Return type:
List[Set[int]]
- orthonym.perception.rings.get_complete_ring_atom_set(mol)#
Return ALL atoms in ALL ring systems (fused, bridged, spiro merged).
IUPAC: Ring system = all atoms in connected ring components. Includes bridgehead atoms, bridge atoms, spiro atoms. Does NOT include exocyclic atoms (=O, -OH, etc.) per. RDKit’s AtomRings correctly reports only ring-member atoms.
- Parameters:
mol – RDKit Mol object
- Returns:
frozenset of all ring atom indices across all ring systems
- Return type:
frozenset
- orthonym.perception.rings.get_containing_ring_system(mol, ring_atoms)#
Return all atoms in the ring system(s) containing the given ring atoms.
For a single SSSR ring that is part of a fused/bridged/spiro system, this returns the complete merged system. Used by Type A callers (ring-parent substituent detection) to prevent BFS from walking into fused partner rings.
- Parameters:
mol – RDKit Mol object
ring_atoms – Iterable of atom indices (e.g., one SSSR ring)
- Returns:
frozenset of all atom indices in the containing ring system(s)
- Return type:
frozenset
- orthonym.perception.rings.is_aromatic_ring(mol, ring_atoms)#
Check if all atoms in a ring are aromatic.
- Parameters:
mol – RDKit Mol object
ring_atoms – Iterable of atom indices
- Returns:
True if all atoms in the ring are aromatic
- Return type:
bool
- orthonym.perception.rings.get_aromatic_rings(mol)#
Get all aromatic rings in the molecule.
- Parameters:
mol – RDKit Mol object
- Returns:
List of tuples, each tuple contains atom indices of an aromatic ring
- Return type:
List[Tuple[int, …]]
- orthonym.perception.rings.get_ring_size(mol, atom_idx)#
Get the smallest ring size containing an atom.
- Parameters:
mol – RDKit Mol object
atom_idx (int) – Index of atom
- Returns:
Smallest ring size, or 0 if atom is not in any ring
- Return type:
int
- orthonym.perception.rings.is_in_ring(mol, atom_idx)#
Check if an atom is in any ring.
- Parameters:
mol – RDKit Mol object
atom_idx (int) – Index of atom
- Returns:
True if atom is in a ring
- Return type:
bool
- orthonym.perception.rings.atoms_in_same_ring(mol, idx1, idx2)#
Check if two atoms are in the same ring.
- Parameters:
mol – RDKit Mol object
idx1 (int) – Atom indices
idx2 (int) – Atom indices
- Returns:
True if atoms share at least one ring
- Return type:
bool
- orthonym.perception.rings.get_ring_heteroatoms(mol, ring_atoms)#
Get heteroatoms (non-carbon) in a ring.
- Parameters:
mol – RDKit Mol object
ring_atoms – Iterable of atom indices
- Returns:
List of (position_in_ring, element_symbol) tuples Position is 0-indexed within the ring
- Return type:
List[Tuple[int, str]]
- orthonym.perception.rings.count_ring_double_bonds(mol, ring_atoms)#
Count double bonds within a ring.
- Parameters:
mol – RDKit Mol object
ring_atoms – Iterable of atom indices
- Returns:
Number of double bonds in the ring
- Return type:
int
- orthonym.perception.rings.get_ring_double_bond_atoms(mol, ring_atoms)#
Get all double bonds within a ring as atom pairs.
- Parameters:
mol – RDKit Mol object
ring_atoms – Iterable of atom indices
- Returns:
List of (atom_idx1, atom_idx2) tuples for each double bond
- Return type:
List[Tuple[int, int]]
- orthonym.perception.rings.is_saturated_ring(mol, ring_atoms)#
Check if a ring is fully saturated (no double bonds).
- Parameters:
mol – RDKit Mol object
ring_atoms – Iterable of atom indices
- Returns:
True if ring has no double bonds
- Return type:
bool
- orthonym.perception.rings.is_heterocyclic(mol, ring_atoms)#
Check if a ring contains heteroatoms.
- Parameters:
mol – RDKit Mol object
ring_atoms – Iterable of atom indices
- Returns:
True if ring contains non-carbon atoms
- Return type:
bool
- orthonym.perception.rings.get_spiro_atoms(mol)#
Memoising front of:func:_get_spiro_atoms_impl (perf lever A7, 2026-09-13).
Ring-classification helpers (
is_spiro_system,classify_ring_system_type,is_bicyclo_system,_spiro_fusion_count,…) call this about 11 times per pipeline pass on the same Mol (26,161 calls per 300 molecules, 2.2 s). Ring membership depends on bonds only, which cannot change on aChem.Molwithout an RWMol (never cached), so the key is the Mol identity. A freshsetis returned on every call because some callers mutate the result.
- orthonym.perception.rings.get_bridgehead_atoms(mol)#
Find bridgehead atoms in bridged ring systems.
- Parameters:
mol – RDKit Mol object
- Returns:
Set of atom indices that are bridgeheads
- Return type:
Set[int]
- orthonym.perception.rings.find_ring_bridgeheads(mol, ring_atoms=None)#
Find von-Baeyer bridgehead atoms: ring-skeletal atoms bonded to >=3 other ring-skeletal atoms.
/: the SINGLE consolidated bridgehead predicate (seeded from VonBaeyerAnalyzer._find_all_bridgeheads, polycyclic.py:425-433). Counts only ring-member neighbours, so an exocyclic substituent (camphor’s gem-dimethyl bridgehead) does NOT disqualify a bridgehead — exactly the
fix. This is distinct from get_bridgehead_atoms (count>=2 ring
membership, too loose) which is left unchanged because it is widely imported.
IUPAC: a bridgehead is a skeletal atom bonded to three or more other skeletal atoms (excluding H); bridgeheads MAY be quaternary/ substituted.
- Parameters:
mol – RDKit Mol object
- Returns:
Set of atom indices that are bridgeheads (>=3 ring-member neighbours).
- Return type:
Set[int]
- orthonym.perception.rings.classify_ring(mol, ring_atoms)#
Classify a ring by its chemical type.
Classification order (check in this order): 1. Heterocyclic aromatic - contains non-carbon atoms AND all atoms aromatic 2. Heterocyclic saturated - contains non-carbon atoms AND not all aromatic 3. Aromatic - all atoms are aromatic (RDKit detection), carbocyclic 4. Cycloalkane - saturated, all carbon, no double bonds 5. Cycloalkene - unsaturated, all carbon, has double bonds but not aromatic
All heterocyclic return values start with ‘heterocyclic’ so callers can use
ring_type.startswith('heterocyclic')for backward-compatible matching.- Parameters:
mol – RDKit Mol object
ring_atoms (Tuple[int, ...]) – Tuple of atom indices defining the ring
- Returns:
Classification string – ‘heterocyclic_aromatic’, ‘heterocyclic_saturated’, ‘aromatic’, ‘cycloalkane’, or ‘cycloalkene’
- Return type:
str
Examples
>>> mol = Chem.MolFromSmiles('C1CCCCC1') # cyclohexane >>> classify_ring(mol, mol.GetRingInfo.AtomRings[0]) 'cycloalkane' >>> mol = Chem.MolFromSmiles('c1ccccc1') # benzene >>> classify_ring(mol, mol.GetRingInfo.AtomRings[0]) 'aromatic' >>> mol = Chem.MolFromSmiles('c1ccncc1') # pyridine >>> classify_ring(mol, mol.GetRingInfo.AtomRings[0]) 'heterocyclic_aromatic' >>> mol = Chem.MolFromSmiles('C1CCNCC1') # piperidine >>> classify_ring(mol, mol.GetRingInfo.AtomRings[0]) 'heterocyclic_saturated'