orthonym.rules.ring_assemblies#
Note
Internal API. Names and behaviour may change between releases.
Ring assembly detection and naming per IUPAC.
Ring assemblies are molecules composed of two or more identical ring systems joined by single bonds. Examples: biphenyl, bipyridine, bithiophene.
IUPAC: Ring assemblies use multiplicative prefixes (bi-, ter-, quater-) before the parent ring name. Benzene assemblies use “phenyl” as base name.
Locant convention: First ring unprimed, second primed (‘), third double-primed (‘’). Connection locants are separated by commas, multi-connection pairs by colons.
- Example outputs:
biphenyl SMILES -> “1,1’-biphenyl”
2,2’-bipyridine SMILES -> “2,2’-bipyridine”
4-chlorobiphenyl SMILES -> “4-chloro-1,1’-biphenyl”
- orthonym.rules.ring_assemblies.detect_ring_assembly(mol, ring_systems)#
Detect if a molecule is a ring assembly (identical ring systems joined by single bonds).
- Parameters:
mol – RDKit Mol object
ring_systems (List[Set[int]]) – List of sets of atom indices, one per ring system (from get_ring_systems)
- Returns:
Dict with assembly info if detected, None otherwise. Dict keys:
ring_systems: list of sets of atom indices
connections: list of (atom_A, atom_B, system_A, system_B) tuples
count: number of identical ring systems
ring_type: ‘carbocyclic’ or ‘heterocyclic’
- Return type:
Dict | None
- a phase.B cross-handler contract:
Ring-assembly detection (this function) and multiplicative naming (rules.multiplicative.name_multiplicative) are MUTUALLY EXCLUSIVE by topology. Ring assemblies = identical rings joined directly by a single bond (no bridge atom). Multiplicative = identical parent units joined by 1+ bridge atoms (oxy / methylene / nitrilo /…). The split is enforced symmetrically:
this function rejects atom-bridged cases via _find_inter_system_bonds (which only matches ring-to-ring single bonds; bridge atoms are NOT in rings, so atom-bridged cases never produce inter-system bonds)
name_multiplicative rejects single-bond-only cases via _is_pure_single_bond_assembly at the entry point (guard)
Cross-handler regression test: tests/integration/test_assembly_vs_multiplicative_dispatch.py.
Source: 154-internal notes; 151-internal notes (path-topology contract).
- orthonym.rules.ring_assemblies.name_ring_assembly(mol, assembly_info, features)#
Generate IUPAC name for a ring assembly.
IUPAC: Ring assemblies are named with multiplicative prefixes (bi-, ter-, quater-) before the parent ring name, with connection locants using primed notation.
- Parameters:
mol – RDKit Mol object
assembly_info (Dict) – Dict from detect_ring_assembly with keys: ring_systems, connections, count, ring_type
features (Any) – MolecularFeatures object (for substituent context)
- Returns:
Complete IUPAC name string, or None if naming fails.
- Return type:
str | None
Examples
biphenyl -> “1,1’-biphenyl” 2,2’-bipyridine -> “2,2’-bipyridine” 4-chlorobiphenyl -> “4-chloro-1,1’-biphenyl”
- orthonym.rules.ring_assemblies.name_ring_assembly_prefix(mol, assembly_info, attachment_atom_idx)#
The ring-assembly substituent prefix, numbered per whatever the input atom order.
Branch review fixes: the unprimed ring was the first ring system in INPUT order, so the same molecule came out ‘[1,1’-biphenyl]-4-yl’ or ‘[1,1’-biphenyl]-4’-yl’ depending on its SMILES spelling (dev2000, both certified pin_verified by the PIN tier’s re-run). “Substituent prefixes derived from ring assemblies” (the Blue Book): “Low locants are assigned to ring junctions, then to free valences”; ‘[1,1’-biphenyl]-4-yl (preferred prefix)’ (:16118). Both directions of the assembly’s ring chain are numbered and the one with the lower junction locants, then the lower free-valence locant (unprimed before primed), is kept.
- orthonym.rules.ring_assemblies.name_mixed_ring_prefix(mol, ring_systems_list, inter_system_bonds, attachment_atom_idx)#
Generate compound substituent prefix for non-identical connected rings.
When two or more non-identical ring systems are connected by single bonds and appear as a substituent on a parent chain, the ring carrying the free valence (chain attachment) is the parent of the compound substituent prefix. The other ring(s) become simple substituents on it.
For the parent ring of the compound prefix: - Carbocyclic: numbering gives chain attachment locant 1 (lowest locant rule) - Heterocyclic: standard IUPAC numbering (heteroatom at position 1)
- Parameters:
mol – RDKit Mol object
ring_systems_list (List[Set[int]]) – List of sets of atom indices, one per ring system
inter_system_bonds (List[Tuple[int, int, int, int]]) – List of (atom_A, atom_B, system_A, system_B) tuples from _find_inter_system_bonds
attachment_atom_idx (int) – Atom index where the multi-ring fragment connects to the parent chain
- Returns:
Compound prefix string like
(4-(pyridin-2-yl)phenyl)or None.- Return type:
str | None
- orthonym.rules.ring_assemblies.get_ring_assembly_iupac_locants(mol)#
a phase-03 cascade-step-6 supplier for ring assemblies size >= 2.
Returns the per-system IUPAC numbering of every ring atom merged into a single
Dict[int, int]covering ALL ring atoms in the assembly. Primes are NAME-format-layer concerns only (in_format_prime), so the supplier emits plain integer locants — the comparator incompare_locant_setsand_build_ring_possees the integer base.Coverage invariant per Pitfall 7: returns
Nonewhen partial coverage would otherwise leak into the cascade-step-6 gate. The gate incandidate_pool.py:634::_has_iupac_locantschecks dict truthiness only; a partial map would silently mis-rank candidates.- Parameters:
mol – RDKit Mol object.
- Returns:
Dict mapping atom_idx -> int locant covering all ring atoms in every system of the assembly. Returns
Nonewhen:the molecule is not a ring assembly per
detect_ring_assembly(size < 2, mixed signatures, branched topology, oversize, etc.),any system’s per-ring numbering produces partial coverage.
- Return type:
Dict[int, int | Tuple[int, str]] | None
Source: 151-internal notes,,. Source: internal notes Pattern S-3 (cascade-step-6 supplier contract). Source: internal notes §”OPSIN Compatibility Evidence” (12 named cases).