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 in compare_locant_sets and _build_ring_pos sees the integer base.

Coverage invariant per Pitfall 7: returns None when partial coverage would otherwise leak into the cascade-step-6 gate. The gate in candidate_pool.py:634::_has_iupac_locants checks 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 None when:

  • 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).