orthonym.decomposition.weave#

Note

Internal API. Names and behaviour may change between releases.

a phase Step 2 – the assembly WEAVER (internal notes).

Step 1 made every fragment of a multi-linkage molecule name successfully, but the whole molecule still abstained because the flat weaver (fragment_assembly.py’s _assemble_by_bond_type / the _assemble_multi_* star assemblers) can only combine fragments whose STANDALONE names happen to carry a matching suffix (an acid, an alcohol, an amine,…). A T4-rescued or seniority-demoted fragment often carries none (a ring parent with every group demoted to a prefix), so the converter returns None and the fragment is either silently dropped or space-joined – OPSIN then sees disconnected components and rejects the name.

This module is the fix the trace recommends: a core-and-arms composer that never converts a standalone fragment NAME – it builds every arm’s prefix directly from the ORIGINAL (uncapped) molecule, anchored at the real attachment atom, via assembly.substituent_enumerator.name_substituent (the same structure-based primitive composer.py already uses for every prefix in a normal, non-decomposed name) or the proven acid-fragment-reuse acyloxy builder rules.lipids._acyloxy_for_site already uses for glycerides.

Scope (v1, FAILS CLOSED outside it – never guesses, never ships a partial):

  • A single connected component, wholly ACYCLIC (no ring anywhere in the molecule). A ring hub (e.g. a GPI-anchor’s mannose core) is out of scope for this version and declines honestly – see the trace’s (ii) bucket.

  • A carbon-only “core” chain, selected as the carbon-only connected component (after excluding ester-carbonyl carbons, so an acyl group never fuses onto the backbone graph) with the most external heavy-atom attachment points (>= 2) – the structural HUB of the star. This is chosen by attachment COUNT, not atom count: a 3-carbon glycerol backbone is the hub even though every one of its fatty-acid arms is far larger.

  • Every attachment directly on a core atom must be exactly one of: a free hydroxyl (the -ol suffix), an ether/alkoxy arm, an acyloxy (ester) arm, or a phosphoryloxy (neutral, mono-protonated phosphodiester) arm whose own far (“head”) side is nameable either directly via name_substituent (an ordinary alkoxy substituent) or via the narrow charged-onium-arm builder below (Part B – e.g. choline). Any other shape on a core atom (a non-oxygen substituent, a charged/anionic phosphate, more than one free valence per position,…) declines the WHOLE molecule – never a partial name.

Part B (narrow, PC-family): a cut quaternary-ammonium/onium arm reached via a SIMPLE, unbranched, all-single-bond carbon chain (e.g. choline’s -CH2CH2-N+(CH3)3) is named structurally – cation_to_prefix for the onium’s own substituents, wrapped in a chain-length + locant string (e.g. 2-(trimethylazaniumyl)ethyl) – never a hardcoded per-head-group string.

Part C (atom-coverage / 0-wrong guard): this module returns a CANDIDATE only. The caller (decomposition/engine.py) MUST verify the candidate covers every heavy atom of the input before shipping it (weave_is_verified below performs the check via a full OPSIN round-trip + heavy-atom-count comparison); a candidate that fails is discarded, never shipped.

orthonym.decomposition.weave.try_weave(mol, style='pin')#

Return a core-and-arms substitutive name for mol, or None.

Never raises – any internal failure is a decline (returns None) so the caller falls through to the existing flat weaver / an honest abstention. This function does NOT verify the result; the caller must run it through weave_is_verified before shipping it (Part C).

orthonym.decomposition.weave.weave_is_verified(mol, name)#

True iff name denotes EXACTLY mol (full OPSIN round-trip, same heavy-atom count AND matching InChI) – the atom-complete-or-abstain guard. Fails CLOSED (False) on any error/unavailable-OPSIN, so an unverifiable weave candidate is never shipped.