orthonym.assembly.substituent_prefix_forms#

Note

Internal API. Names and behaviour may change between releases.

a phase substituent prefix-form table for non-principal FG-bearing substituents.

Per IUPAC 2013 § + §, a NON-PRINCIPAL functional group MUST be expressed via its IUPAC-defined prefix form when it appears as a substituent:

-C(=O)OCH3 -> methoxycarbonyl -C(=O)NH2 -> carbamoyl -C(=O)NHCH3 -> methylcarbamoyl -NHC(=O)OCH3 -> (methoxycarbonyl)amino -OCH3 -> methoxy -S(=O)CH3 -> methylsulfinyl … (14 rows total — see 160.1-AUDIT-SUBENUM.md)

This module is the SHARED chemistry-rule table consulted by:

  1. rules/polyfunctional.py (principal-chain context; existing caller; backwards-compat preserved via re-export shim per a phase internal notes).

  2. assembly/substituent_enumerator.py:name_substituent Tier-0.5 hook (sub-fragment context; NEW caller per a phase internal notes).

5 generators lifted verbatim from rules/polyfunctional.py per internal notes; 3 NEW generators will be added in Plan-02-02 per RESEARCH (secondary/tertiary amide carbamoyl forms + carbamate orientation-checking form); 1 dispatcher get_substituent_prefix_form(fg_name, mol, atoms, principal_chain) mirrors rules/polyfunctional.get_fg_prefix_form per internal notes + RESEARCH

All functions are PURE: read-only on (mol, atoms, principal_chain); no side effects; no pool.add; no MolecularFeatures mutation. Per internal notes

inheritance from a phase + a phase.

None-guard for principal_chain: per RESEARCH, every lifted function adds chain_set = set(principal_chain) if principal_chain else set at entry, allowing the Tier-0.5 caller to pass principal_chain=None for sub-fragment context (the substituent has no principal-chain).

orthonym.assembly.substituent_prefix_forms.get_alkoxy_prefix(mol, ether_atoms, principal_chain=None)#

Determine the alkoxy prefix for an ether (IUPAC /.

The ether SMARTS [OX2]([CX4])[CX4] matches both carbons. We need to determine which side is the substituent (smaller/not in chain) and name it as alkoxy.

Lifted from rules/polyfunctional._get_alkoxy_prefix verbatim with a NEW None-guard at function entry per a phase RESEARCH lift-blocking analysis: when principal_chain is None (the Tier-0.5 sub-fragment caller passes None), chain_set becomes empty and the orientation guard falls back cleanly to the smaller-fragment selection.

Parameters:
  • mol – RDKit Mol object.

  • ether_atoms (tuple) – Atom indices from ether SMARTS match (O, C, C).

  • principal_chain (List[int] | None) – Atom indices of the principal chain; may be None for the Tier-0.5 sub-fragment caller per a phase internal notes.

Returns:

Alkoxy prefix (e.g., "methoxy", "ethoxy"), or None if naming fails. Never returns the literal string "alkoxy" (not valid IUPAC).

Return type:

str | None

orthonym.assembly.substituent_prefix_forms.get_alkoxycarbonyl_prefix(mol, ester_atoms, principal_chain=None)#

Generate alkoxycarbonyl prefix for ester-as-non-principal-group (IUPAC.

Per IUPAC, when an ester group -C(=O)-O-R is not the principal characteristic group, it is expressed as an alkoxycarbonyl prefix:

-COOCH3 -> methoxycarbonyl
-COOC2H5 -> ethoxycarbonyl
-COOPh -> phenoxycarbonyl

Lifted from rules/polyfunctional._get_alkoxycarbonyl_prefix verbatim with a NEW None-guard at function entry per a phase RESEARCH: when principal_chain is None (Tier-0.5 sub-fragment caller), chain_set becomes empty and the orientation guard auto-fails to the SMARTS-based alkyl-side discrimination.

Parameters:
  • mol – RDKit Mol object.

  • ester_atoms (tuple) – Tuple from ester SMARTS [CX3](=O)[OX2][#6]: (carbonyl_C, carbonyl_O, ester_O, alkyl_C).

  • principal_chain (List[int] | None) – Atom indices of the principal chain; may be None for the Tier-0.5 sub-fragment caller per a phase internal notes.

Returns:

Alkoxycarbonyl prefix string, or None if this ester should not be named as alkoxycarbonyl (e.g., lactones, backbone esters, heteroatom-bearing alkyl, oversized alkyl fragments).

Return type:

str | None

orthonym.assembly.substituent_prefix_forms.get_alkoxycarbonimidoyl_prefix(mol, iminoester_atoms, principal_chain=None)#

Generate R-oxycarbonimidoyl prefix for an imidate (iminoester) as a non-principal group (IUPAC /.

Per the Blue Book, carbonimidoyl is the divalent acyl prefix -C(=NH)- (cf. C-hydroxycarbonimidoyl for -C(=NH)-OH, the Blue Book). An imidate substituent -C(=NH)-O-R, when not the principal characteristic group (e.g. a senior carboxylic acid outranks it), is therefore expressed as an R-oxycarbonimidoyl prefix:

-C(=NH)OCH3 -> methoxycarbonimidoyl
-C(=NH)OC2H5 -> ethoxycarbonimidoyl
-C(=NH)OPh -> phenoxycarbonimidoyl
-C(=NH)OCH2Ph -> (benzyloxy)carbonimidoyl

This is the exact analogue of:func:get_alkoxycarbonyl_prefix (the ester row): the OR fragment is named identically (that side is C=O vs C=NH agnostic); only the double-bonded heteroatom differs, so the suffix is carbonimidoyl instead of carbonyl. All of the ester row’s guards (lactone, orientation, heteroatom OR, size, non-aromatic ring) are mirrored verbatim.

Parameters:
  • mol – RDKit Mol object.

  • iminoester_atoms (tuple) – Tuple from the iminoester SMARTS [CX3](=[NX2H1])[OX2][#6]: (carbonyl_C, imino_N, ester_O, alkyl_C). Note position [1] is the imino nitrogen (not a carbonyl oxygen); the shared ester helpers parse_ester_fragments() /is_lactone() use only tuple positions [0] and [2], so the tuple is directly reusable.

  • principal_chain (List[int] | None) – Atom indices of the principal chain; may be None for the Tier-0.5 sub-fragment caller (mirrors the ester generator).

Returns:

The R-oxycarbonimidoyl prefix string, or None if this imidate should not be named as such (cyclic imidate / imino-lactone, backbone, heteroatom-bearing OR, oversized/ring OR, or an N-substituted imino nitrogen – fail closed).

Return type:

str | None

orthonym.assembly.substituent_prefix_forms.get_sulfinyl_prefix(mol, sulfoxide_atoms, principal_chain=None, suffix='sulfinyl')#

Generate (alkyl)sulfinyl prefix for sulfoxide as non-principal group (IUPAC.

-6I added the suffix param (default “sulfinyl” -> backward- compatible); pass “seleninyl”/”tellurinyl” for the Se/Te oxide analogues.

IUPAC: R-S(=O)-R' when not the principal group is expressed as an (alkyl)sulfinyl prefix on the parent chain.

SMARTS [SX3](=[OX1])([#6])[#6] matches (S, O, C1, C2).

Lifted from rules/polyfunctional._get_sulfinyl_prefix verbatim with a NEW None-guard at function entry per a phase RESEARCH

Parameters:
  • mol – RDKit Mol object.

  • sulfoxide_atoms (tuple) – Atom indices from sulfoxide SMARTS match.

  • principal_chain (List[int] | None) – Atom indices of the principal chain; may be None.

Returns:

Compound prefix string (e.g., "methylsulfinyl"), or None on failure.

Return type:

str | None

orthonym.assembly.substituent_prefix_forms.get_sulfonyl_prefix(mol, sulfone_atoms, principal_chain=None, suffix='sulfonyl')#

Generate (alkyl)sulfonyl prefix for sulfone as non-principal group (IUPAC.

IUPAC: R-S(=O)(=O)-R' when not the principal group is expressed as an (alkyl)sulfonyl prefix on the parent chain. -6I added the suffix param (default “sulfonyl”); pass “selenonyl”/”telluronyl” for the Se/Te oxide analogues.

SMARTS [SX4](=[OX1])(=[OX1])([#6])[#6] matches (S, O1, O2, C1, C2).

Lifted from rules/polyfunctional._get_sulfonyl_prefix verbatim with a NEW None-guard at function entry per a phase RESEARCH

Parameters:
  • mol – RDKit Mol object.

  • sulfone_atoms (tuple) – Atom indices from sulfone SMARTS match.

  • principal_chain (List[int] | None) – Atom indices of the principal chain; may be None.

Returns:

Compound prefix string (e.g., "methylsulfonyl"), or None on failure.

Return type:

str | None

orthonym.assembly.substituent_prefix_forms.get_sulfanyl_prefix(mol, thioether_atoms, principal_chain=None, suffix='sulfanyl')#

Generate (alkyl)sulfanyl/selanyl/tellanyl prefix for a chalcogen ether as non-principal group (IUPAC /.

IUPAC: R-S-R' (or R-Se-R' / R-Te-R') when not the principal group is expressed as an (alkyl)chalcogenyl prefix on the parent chain. The algorithm is chalcogen-agnostic (it walks the C neighbours of the chalcogen atom at thioether_atoms[0]); only the suffix differs: sulfanyl (S), selanyl (Se), tellanyl (Te).

SMARTS [SX2]([#6])[#6] / [SeX2]([#6])[#6] / [TeX2]([#6])[#6] each match (chalcogen, C1, C2).

Lifted from rules/polyfunctional._get_sulfanyl_prefix verbatim with a NEW None-guard at function entry per a phase RESEARCH functional-group perception fix (169.7) added the suffix param (default “sulfanyl” → backward-compatible).

Parameters:
  • mol – RDKit Mol object.

  • thioether_atoms (tuple) – Atom indices from the chalcogen-ether SMARTS match.

  • principal_chain (List[int] | None) – Atom indices of the principal chain; may be None.

  • suffix (str) – chalcogen prefix stem (“sulfanyl” | “selanyl” | “tellanyl”).

Returns:

Compound prefix string (e.g., "methylsulfanyl" / "methylselanyl"), or None on failure.

Return type:

str | None

orthonym.assembly.substituent_prefix_forms.get_alkoxysulfinyl_prefix(mol, s_idx, attach_idx)#

Build the O-alkyl (alkoxy)sulfinyl compound prefix /.

For an S(=O) centre attached to the parent through attach_idx and carrying exactly ONE -O-alkyl arm and the single =O oxo (no C neighbours), name the O-alkyl arm as <alkyl>oxy and concatenate the additive sulfinyl stem -> ethoxysulfinyl. The compound prefix is returned already enclosed in parentheses per ((ethoxysulfinyl)); the caller’s N-substituent wrapper leaves a balanced single-paren name unchanged.

The C-linked R-S(=O)-R' case is handled by get_sulfinyl_prefix (which requires two C-neighbours on S and declines here — S has an O and the attachment N, zero C). This assembler is the O-linked complement.

Parameters:
  • mol – RDKit Mol object.

  • s_idx (int) – Atom index of the sulfinyl sulfur.

  • attach_idx (int) – Atom index of the parent atom the sulfur is bonded to (excluded from the arm walk — e.g. the aniline nitrogen).

Returns:

"(ethoxysulfinyl)" etc., or None (fail-closed) if the shape is not exactly one O-alkyl arm + one =O oxo + the single attachment.

Return type:

str | None

orthonym.assembly.substituent_prefix_forms.get_phosphoryl_prefix(mol, p_idx, attach_idx)#

Build the [(…)phosphoryl] additive compound prefix /.

For a P(=O) centre attached to the parent through attach_idx, name every remaining (non-oxo, non-attachment) P substituent as a substitutive prefix, apply the multiplicative disambiguation (bis(sulfanyl) for two -SH arms), and concatenate the additive phosphoryl stem -> bis(sulfanyl)phosphoryl. Returned WITHOUT an outer enclosure; the caller’s N-substituent wrapper escalates the parens-bearing name to square brackets per -> [bis(sulfanyl)phosphoryl].

Only -SH arms are recognised today (the sole verified class); any other arm shape fails closed so a partial/ambiguous name never leaks.

Parameters:
  • mol – RDKit Mol object.

  • p_idx (int) – Atom index of the phosphoryl phosphorus.

  • attach_idx (int) – Atom index of the parent atom the phosphorus is bonded to.

Returns:

"bis(sulfanyl)phosphoryl" etc., or None (fail-closed).

Return type:

str | None

orthonym.assembly.substituent_prefix_forms.get_n_alkyl_carbamoyl_prefix(mol, amide_atoms, principal_chain=None)#

Generate (alkyl)carbamoyl prefix for secondary-amide-as-substituent.

Per IUPAC, when a secondary amide -C(=O)NHR is not the principal group AND attached to the parent through the carbonyl-C, the prefix form is (alkyl)carbamoyl – the carbamoyl N is the sole substitutable locus, so its italic N-locant is omitted:

-C(=O)NHCH3 -> methylcarbamoyl
-C(=O)NHC2H5 -> ethylcarbamoyl

Returns None for:

  • Lactam (cyclic amide; handled by ring handler)

  • Amide attached through N (acetylamino path; handled by _name_amino_branch)

  • Backbone amide (both ends on principal chain)

Parameters:
  • mol – RDKit Mol (the full molecule, not the substituent fragment).

  • amide_atoms (tuple) – 4-tuple (carbonyl_C, carbonyl_O, amide_N, alkyl_C) from SMARTS [CX3](=O)[NX3H1][#6] match.

  • principal_chain (List[int] | None) – principal-chain atom indices, or None for sub-fragment context (the Tier-0.5 caller; in that case the orientation gate is the caller’s responsibility — this function assumes carbonyl-C-attached orientation and returns the carbamoyl form).

Returns:

IUPAC-canonical "(alkyl)carbamoyl" string (no italic N-locant), or None.

Return type:

str | None

orthonym.assembly.substituent_prefix_forms.get_n_n_dialkyl_carbamoyl_prefix(mol, amide_atoms, principal_chain=None)#

Generate (dialkyl)carbamoyl prefix for tertiary-amide-as-substituent.

Per IUPAC (italic N-locants omitted,; the second substituent of a mixed pair is enclosed per:

-C(=O)N(CH3)2 -> dimethylcarbamoyl
-C(=O)N(CH3)(C2H5) -> ethyl(methyl)carbamoyl (alphabetized)
-C(=O)N(C2H5)2 -> diethylcarbamoyl

BB PIN witness: 5-methyl-2-[methyl(phenyl)carbamoyl]benzoic acid (:32957).

Returns None for cyclic tertiary amide, backbone amide, or N-attached orientation (same gates as get_n_alkyl_carbamoyl_prefix).

orthonym.assembly.substituent_prefix_forms.get_n_substituted_carbamoylamino_prefix(mol, atoms, principal_chain=None)#

: -NH-CO-NR2 substituent -> (carbamoyl-decorated)amino.

Replaces the fixed PREFIX_FORMS['urea'] = 'carbamoylamino' with a dynamic builder that enumerates the distal N’s substituents:

  • unsubstituted distal N -> "carbamoylamino" (BB preselected prefix, NOT ‘ureido’/’3-methylureido’);

  • substituted distal N -> "(methylcarbamoyl)amino" / "(dimethylcarbamoyl)amino" (compound-prefix enclosure,;

  • un-nameable distal substituent or ambiguous orientation -> None (fail closed).

orthonym.assembly.substituent_prefix_forms.get_n_substituted_carbamothioylamino_prefix(mol, atoms, principal_chain=None)#

: -NH-CS-NR2 substituent -> (carbamothioyl…)amino.

Residue R3 root cause. This row used to be the STATIC lookup PREFIX_FORMS['thiourea'] -> "carbamothioylamino", returned unconditionally and ignoring atoms entirely. carbamothioylamino is the MONOVALENT group H2N-CS-NH-, the Blue Book:33489). When the 4-atom core bridges two parts of the molecule — both nitrogens substituted — the static string named a two-attachment bridge with a one-attachment prefix: everything past the distal nitrogen was orphaned and re-attached elsewhere, and the core was consumed a second time from the other direction. Measured on CC(C)(C)NC(=S)NC1CCCCC1: 31 calls, 31 carbamothioylamino returns, output 1-(2-(carbamothioylamino)-2-carbamothioylamino-2-methylpropyl)cyclohexane — one thiourea unit spelled twice, and a ring->N bond rendered ring->C.

This is now the exact mirror of the oxo sibling get_n_substituted_carbamoylamino_prefix, which never had the defect because it resolves the distal nitrogen structurally and returns None when the orientation is ambiguous — measured 12/12 None on the urea analogue of the same molecule, which then correctly re-parents onto urea.

  • unsubstituted distal N -> "carbamothioylamino" (the enumerated preferred prefix,:33489; BB example:33501 3-(carbamothioylamino)propanoic acid (PIN));

  • substituted distal N -> "(methylcarbamothioyl)amino" / "(dimethylcarbamothioyl)amino". enumerates NO substituted-distal-N row — the form is derived across the

    chalcogen-replacement relationship (:33439) from the oxo

    (PIN) example 2-[(methylcarbamoyl)amino]naphthalene-1-carboxylic acid (:33354);

  • both nitrogens substituted / ambiguous orientation -> None. The caller falls through and the molecule re-parents onto the retained thiourea

    18875 over:

    amides class 11:18184 outrank

    carbon rings class 40:18216).

Se/Te analogues return None. enumerates only the sulfur prefix, and no carbamoselenoyl/carbamotelluroyl spelling appears anywhere in the Blue Book, so inventing one would risk a wrong name; the Se/Te RETAINED PARENT is built instead:33451 N-(butan-2-yl)selenourea (PIN)).

orthonym.assembly.substituent_prefix_forms.get_carbamoyloxy_prefix(mol, carbamate_atoms, principal_chain=None)#

Generate carbamate prefix per IUPAC.

The carbamate -NHC(=O)O- has TWO orientation forms:

  • Branch A: connects through N → -NHC(=O)OCH3 → "(methoxycarbonyl)amino"

  • Branch B: connects through O → -OC(=O)NH2 → "carbamoyloxy" (N-substituted variants per _compute_branch_b_carbamoyloxy_name: (N-methylcarbamoyl)oxy etc.)

Returns None for cyclic carbamate (oxazolidinone; handled by ring handler) or backbone carbamate (all atoms on principal chain).

In sub-fragment context (principal_chain=None) defaults to Branch A naming for -NHC(=O)OR fragments (the more-common substituent shape per internal notes row 11); Branch B is invoked by the Tier-0.5 caller when attach_idx == ester_O.

Parameters:
  • mol – RDKit Mol object.

  • carbamate_atoms (tuple) – 5-tuple (amide_N, carbonyl_C, carbonyl_O, ester_O, alkyl_C) from SMARTS [NX3][CX3](=O)[OX2][#6] match.

  • principal_chain (List[int] | None) – principal-chain atom indices, or None.

orthonym.assembly.substituent_prefix_forms.get_guanidine_prefix(mol, atoms, principal_chain=None)#

Prefix for an unsubstituted guanidine group cited as a substituent.

(the Blue Book): “In the presence of a characteristic group

having seniority over guanidine […], the following prefixes are used. The prefix guanidino may be used in general nomenclature.” H2N-C(=NH)-NH- is ‘carbamimidoylamino (preferred prefix)’ (:34268); (H2N)2C=N- is ‘(diaminomethylidene)amino (preferred prefix)’ (:34270-34272; ‘4-[(diaminomethylidene)amino]butanoic acid (PIN)’:34282). 7. Prefixes (g) (:1700): “The prefix ‘guanidino’ is no longer acceptable in preferred IUPAC names”.

atoms is the guanidine match [NX3][CX3](=[NX2])[NX3]. The attachment N is the one N with a heavy neighbour outside the group; the other two N must carry no further heavy atom. Anything else (a substituted guanidine) falls back to the static table entry, which is the same ‘carbamimidoylamino’ string as before this row existed for the NH-attached form.

orthonym.assembly.substituent_prefix_forms.get_substituent_prefix_form(fg_name, mol, atoms, principal_chain=None)#

Dispatcher across the 14-row IUPAC / prefix-form table.

Returns the IUPAC-canonical prefix-form string for fg_name applied to atoms, or None if no prefix-form rule applies (caller falls back to its default behavior).

Per a phase internal notes closed-set: extending beyond these 14 rows is +1 scope. Sibling phases (a phase FRN functional replacement, +1 hydroxamic / hydrazide / phosphorus) attach by adding ADDITIONAL rows below — never deleting or modifying existing rows.

Parameters:
  • fg_name (str) – Functional-group name from FUNCTIONAL_GROUP_SMARTS (e.g., "ester", "primary_amide", "thioether").

  • mol – RDKit Mol object (the full molecule, not the substituent fragment).

  • atoms (tuple) – Atom indices from the SMARTS match for fg_name.

  • principal_chain (List[int] | None) – Atom indices of the principal chain; may be None for the Tier-0.5 sub-fragment caller (substituent_enumerator) per a phase internal notes. Lifted generators internally None-guard.

Returns:

IUPAC-canonical prefix-form string, or None when no rule applies.

Return type:

str | None