orthonym.assembly.substituent_naming#

Note

Internal API. Names and behaviour may change between releases.

Centralized substituent fragment naming module.

Provides name_substituent_fragment – the single entry point for naming any substituent (linear, branched, functionalized, or ring-containing) from its atom indices within a parent molecule.

Architecture:
  1. Fast path: linear terminal alkyl substituents use get_alkyl_name directly.

  2. Retained PREFERRED names: phenyl, benzyl, retained cycloalkyls, tert-butyl (F-T9/DD6: isopropyl/sec-butyl/isobutyl/neopentyl are NOT retained — their located PINs come from _located_acyclic_alkyl_name in step 3/2d).

  3. Located / recursive path: a branched or internally-attached acyclic alkyl is named by its own principal chain numbered from the free valence (_located_acyclic_alkyl_name: propan-2-yl, butan-2-yl, 2-methylpropyl); other fragments extract SMILES and convert via parent_to_prefix.

The function returns RAW prefix names WITHOUT enclosing marks (parentheses/brackets). The caller (format_substituent_prefix in naming_utils.py) handles wrapping based on is_complex_substituent and multiplier logic.

References

IUPAC 2013 (substituent prefix naming) IUPAC 2013 (compound substituent enclosing marks)

orthonym.assembly.substituent_naming.polyfunctional_substituent_located(mol, sub_atoms, name, pos)#

The located triple for _add_substituent_stereo from a _name_polyfunctional_acyclic_substituent numbering, or None.

(the Blue Book): “In preferred IUPAC names,

stereodescriptors, preceded by a locant, must be cited”; ‘[(1R)-1-chloropropyl]benzene (PIN)’ (:44668). The producer numbers its chain from the free valence and pos is that numbering, so the centre is cited at its own locant (‘(1R)-1-carboxyethyl’), not bare (‘(R)-1-carboxyethyl’). Admitted only when citing the pos centres expresses EVERY defined stereo element of the fragment (located_map_completes_substituent_stereo); the precondition holds by construction – pos holds backbone carbons only, while any centre expressed inside name sits in a nested composed branch.

orthonym.assembly.substituent_naming.acyl_carbons_to_amido_prefix(acyl_carbons)#

method (1) prefix for a linear saturated acyl R-CO-.

acyl_carbons counts the acyl carbons INCLUDING the carbonyl carbon: 1 -> ‘formamido’, 2 -> ‘acetamido’, n>=3 -> ‘{stem}anamido’ (propanamido, butanamido,…). Returns None when no chain stem exists.

orthonym.assembly.substituent_naming.acid_name_to_amido_prefix(acid_name)#

method (1): acid name -> amido prefix via the amide name.

‘benzoic acid’ -> ‘benzamido’, ‘pentanoic acid’ -> ‘pentanamido’, ‘naphthalene-1-carboxylic acid’ -> ‘naphthalene-1-carboxamido’ (the amide names benzamide/pentanamide/naphthalene-1-carboxamide have their final ‘e’ changed to ‘o’). Returns None when no safe transform exists — poly-acids (’…dioic acid’, ‘…dicarboxylic acid’) and functional-replacement acids fail closed because a single amido prefix cannot describe them.

orthonym.assembly.substituent_naming.record_prefix_from_acid_status(acid_name, prefix)#

Carry the PIN status of acid_name to the prefix built from it by a string conversion (’…oic acid’ -> ‘…amido’ / ‘…oyl’): a recorded non-PIN fragment in the acid name (record_derived_non_pin_fragment), or an acid parent spelled with the systematic alternative of a retained PIN acid (’…-2-phenylethanoic acid’ for a substituted acetic acid,:29725), labels the prefix non-PIN, so the name that cites it cannot ship as pin_verified. Label only.

orthonym.assembly.substituent_naming.fragment_acid_name_verified(acid_name, acid_smiles)#

True iff OPSIN re-perceives acid_name as acid_smiles on the FULL standard InChIKey (constitution, stereo, charge). A prefix built from an acid name by a string conversion (’…oic acid’ -> ‘…amido’ / ‘…oyl’) is only as right as that acid name, and name_fragment_recursively can return an unverified one at the best-effort tier (measured: ‘((4-chlorophenoxy)acetylamino) acetic acid’ for OC(=O)CN(CCC(C)C)C(=O)COc1ccc(Cl)cc1 – the 3-methylbutyl dropped). Fails closed: False when OPSIN rejects the name or cannot be consulted.

orthonym.assembly.substituent_naming.acid_name_to_acyl_prefix(acid_name)#

Monovalent acyl prefix R-CO- from the name of the acid R-COOH.

(the Blue Book): ‘acetyl’, ‘formyl’, ‘benzoyl’ are the

preferred prefixes (:30442-:30446); (:30608) “changing the ending ‘oic acid’ to ‘oyl’”; (:30624) “changing the ‘carboxylic acid’ suffix to the suffix ‘carbonyl’”. A substituted acetic acid keeps its stem (‘(chloroacetyl)oxyl (PIN)’,:40694). The ‘1-oxopropyl’ form is general nomenclature only,:30430).

Fails closed (None) with the same guards as:func:acid_name_to_amido_prefix: a poly-acid (’…dioic acid’, ‘…dicarboxylic acid’, ‘diacetic acid’) would need a divalent or multiplied prefix, and functional-replacement / peroxy acids have their own acyl endings.

orthonym.assembly.substituent_naming.acyl_prefix_from_branch(mol, carbonyl_c, attach_idx, acyl_atoms)#

acyl prefix for the acyl group R-CO- whose carbonyl carbon carbonyl_c is bonded to attach_idx (an atom of the parent).

acyl_atoms is the WHOLE acyl group (carbonyl C, its =O, and R), so nothing can be silently dropped: the group must be closed (no acyl atom bonded outside it except the carbonyl C to attach_idx) and carry exactly one C=O on the carbonyl carbon. The acyl is named as its acid (-OH added at the carbonyl carbon; stereo kept) – with a systematic retry for a retained amino-acid name – verified by OPSIN (fragment_acid_name_verified()), and converted by acid_name_to_acyl_prefix(). Returns the BARE prefix (‘acetyl’, ‘(2S)-2-methyl-3-sulfanylpropanoyl’); callers add enclosing marks. None = fail closed.

orthonym.assembly.substituent_naming.oxamoyl_branch_name(mol, n_idx, acyl_c_idx)#

(BB 33071/55479): recognize the EXACT H2N-CO-CO- branch hanging from an imine N -> ‘oxamoyl’ (the preferred prefix for the H2N-CO-CO-N= group’s acyl part). Returns None for anything else (fail-closed): the first carbon must be a carbonyl (=O, no other substituents besides the =N-bearing N and the second carbonyl C); the second carbon must be a carbamoyl (=O + terminal NH2).

orthonym.assembly.substituent_naming.linear_acyl_amido_prefix(mol, carbonyl_c, n_idx, sub_atoms)#

Amido prefix method (1)) for an N-acyl substituent.

Emits ‘formamido’ (HCO-NH-), ‘acetamido’ (CH3-CO-NH-) or ‘{stem}anamido’ (>=3 C) ONLY when the substituent is exactly -NH-CO-R with R an unbranched, saturated, acyclic, all-carbon chain and every substituent atom accounted for (the N, the carbonyl O and the chain carbons — nothing dropped). Returns None otherwise, so the strict builder can never mint a name for a branch it cannot fully describe; callers keep their legacy fallback for those.

Parameters:
  • mol – RDKit Mol.

  • carbonyl_c (int) – atom index of the acyl C=O carbon.

  • n_idx (int) – atom index of the amide nitrogen (the attachment atom).

  • sub_atoms – all atom indices of the substituent (including n_idx).

orthonym.assembly.substituent_naming.n_substituted_acyl_amido_prefix(mol, n_idx, sub_atoms, parent_atoms)#

method (1): an N-SUBSTITUTED acylamino branch -N(R')-C(=O)-R is the {N-R'}{acyl}amido PREFIX – N-methylacetamido, N-methylformamido, N-ethylpropanamido (the Blue Book; verbatim 2-(N-methylpropanamido)benzene-1-sulfonic acid (PIN):33040).

The unsubstituted sibling linear_acyl_amido_prefix fails closed on a substituted N (its sub_set == branch | {n_idx} check), and the legacy count fallback in _name_amino_branch refuses a non-mono-substituted N (37cd122d F1), so before this builder the whole -N(R')C(=O)R fragment dropped to the ugly general replacement name (1,2-dimethyl-3-oxa-1- azaprop-2-en-1-yl) or abstained.

PIN ONLY as a PREFIX: this fires in _name_amino_branch (a substituent namer), reached only when parent selection already made the amide a prefix (a senior characteristic group is elsewhere). When the amide is the PRINCIPAL group it is the SUFFIX – the Blue Book marks 4-(N-methylacetamido) quinoline explicitly NOT PIN, and Orthonym already names that molecule N-methyl-N-(quinolin-2-yl)acetamide via the suffix path, which this builder never sees.

Strict / fail-closed. Fires ONLY when, with FULL atom coverage:
  • N is uncharged/unradical/unlabelled, NOT a ring member, all single bonds, and has EXACTLY one bond into the parent (a bare tertiary amide N);

  • N carries EXACTLY two non-parent heavy branches – the acyl C (a clean C=O) and one other substituent R’;

  • the acyl side names via linear_acyl_amido_prefix (an unbranched saturated acyclic all-carbon acyl -> formamido/acetamido/{stem}anamido);

  • R’ names via the recursive substituent namer;

  • the fragment is EXACTLY {N} + acyl-subtree + R’-subtree (nothing dropped).

Returns the BARE prefix core 'N-{R'}{amido}' (the caller applies the

enclosing marks – needs_brackets('N-methylacetamido') is

True, so it renders (N-methylacetamido)); None otherwise.

orthonym.assembly.substituent_naming.sulfonamido_prefix_from_n_branch(mol, n_idx, sub_atoms, parent_atoms)#

(the Blue Book): an N-attached -NH-SO2-R branch is the {R}sulfonamido PREFIX (methanesulfonamido / ethanesulfonamido / benzenesulfonamido / cyclohexanesulfonamido) — NOT an amino split of the N, and NOT the cascade’s carbamoyl misroot (which swaps S->C and DROPS S, the two =O and R: a different molecule).

The stem is built by the SAME acid-stem sulfonyl primitive the sulfone-prefix path uses (_acid_stem_oxide_prefix(mol, r_carbon, s_idx, 'sulfonyl') -> ‘methanesulfonyl’ / ‘benzenesulfonyl’ / ‘cyclohexanesulfonyl’), with the suffix rewrite sulfonyl -> sulfonamido. That primitive returns None (so this fails closed) for a substituted-arene / CF3 / branched-hetero R -> those keep abstaining rather than shipping a wrong or atom-dropped name.

Strict / fail-closed. Fires ONLY when, with FULL atom coverage:
  • N carries exactly ONE non-parent heavy neighbour, the sulfonyl S (a bare -NH-, never N,N-disubstituted); N-S is a single bond,

  • S is a clean sulfonyl: exactly two terminal =O (both in the fragment) and exactly one further heavy neighbour R, a carbon,

  • the fragment is EXACTLY {N, S, the two =O} plus the R subtree — nothing dropped.

Returns the BARE prefix core (e.g. ‘methanesulfonamido’), matching every sibling return in _name_amino_branch; None otherwise.

orthonym.assembly.substituent_naming.acyl_amido_prefix_from_branch(mol, n_idx, carbonyl_c, sub_atoms, verify_acid=False)#

method (1) amido prefix for a full N-attached acyl branch.

sub_atoms is the ENTIRE substituent (the amide N plus the whole acyl fragment). Fast path:linear_acyl_amido_prefix(). General path (ring / substituted acyls): take ALL branch atoms except the N as the acyl fragment — nothing can be silently dropped — convert it to the corresponding acid by adding an -OH at the carbonyl carbon, name that acid recursively, then apply:func:acid_name_to_amido_prefix (‘benzoic acid’ -> ‘benzamido’, ‘4-methylbenzoic acid’ -> ‘4-methylbenzamido’). Returns the BARE prefix (callers add enclosing marks for locant-bearing forms) or None (fail closed).

verify_acid: accept the acid name only when OPSIN re-perceives it as the acid fragment on the full InChIKey (fragment_acid_name_verified()), and carry its PIN status to the prefix (record_prefix_from_acid_status()), so a name citing an amido prefix built from a non-PIN acid spelling is not labelled pin_verified. Off by default so existing callers keep their behaviour.

orthonym.assembly.substituent_naming.imidamide_name_to_imidamido_prefix(name)#

Wave2 method 1): turn an amidine parent name (imidamide / carboximidamide) into the non-principal prefix by changing the final ‘e’ -> ‘o’: ‘ethanimidamide’ -> ‘ethanimidamido’, ‘benzenecarboximidamide’ -> ‘benzenecarboximidamido’. Fail-closed (None) for di/poly-imidamide names (one imidamido cannot describe a poly-amidine) or names carrying N-locants the branch cannot describe.

orthonym.assembly.substituent_naming.imidoyl_amido_prefix_from_branch(mol, n_idx, imino_c, sub_atoms)#

Wave2: imidamido prefix for a full N-attached amidine branch -N(H)-C(=NH)-R (the amidine’s AMINO nitrogen is the ring/chain attachment). Mirrors:func:acyl_amido_prefix_from_branch but keyed on the imino C=N instead of a carbonyl C=O. Reconstructs the imidamide parent R-C(=NH)-NH2, names it recursively, then applies imidamide_name_to_imidamido_prefix(). Returns the BARE prefix or None.

Guards (fail-closed): the imino carbon must have exactly one =N (double) whose N bears only H/C (reject amidrazone -C(=N-NH2)-); exactly one single-bonded N == n_idx (reject guanidine’s second amino N); the attachment N connects to the branch ONLY through this carbon; full-branch coverage (no dropped atoms).

orthonym.assembly.substituent_naming.hydrazonamide_name_to_hydrazonamido_prefix(name)#

: turn an amidrazone parent name (hydrazonamide) into the non-principal prefix by changing the final ‘e’ -> ‘o’: ‘ethanehydrazonamide’ -> ‘ethanehydrazonamido’. Fail-closed (None) for di/poly names or names carrying N-locants the branch cannot describe.

orthonym.assembly.substituent_naming.hydrazonoyl_amido_prefix_from_branch(mol, n_idx, imino_c, sub_atoms)#

: hydrazonamido prefix for a full N-attached amidrazone branch -N(H)-C(=N-NH2)-R (the amidrazone AMINO nitrogen is the ring/chain attachment). Sibling of:func:imidoyl_amido_prefix_from_branch, but keyed on the hydrazono C=N-NH2 — the terminal NH2 on the imino N is the discriminant vs a plain amidine (which that sibling rejects by design). Reconstructs the amidrazone parent R-C(=N-NH2)-NH2, names it recursively (-> ‘ethanehydrazonamide’), then applies the e->o transform. Returns the BARE prefix or None.

Guards (fail-closed): the imino carbon has exactly one =N (double) whose N bears exactly one terminal degree-1 NH2 (neutral); exactly one single-bonded N == n_idx (rejects hydrazidine/guanidine); the attachment N connects to the branch ONLY through this carbon; zero formal charges; full-branch coverage.

orthonym.assembly.substituent_naming.sulfino_hydrazonoyl_amido_prefix_from_branch(mol, n_idx, s_idx, sub_atoms)#

/ (plan P1AM Task 8): N-attached R-S(=N-NH2)(-NH-)[=O]? branch -> ‘{R-stem}sulfinohydrazonamido’ (no =O) or ‘{R-stem}sulfonohydrazonamido’ (one =O). Fail-closed None on any deviation (charges, extra substitution, unnameable R).

orthonym.assembly.substituent_naming.ATTACH_LOCANT_UNKNOWN = ATTACH_LOCANT_UNKNOWN#

Explicit “this caller cannot prove where the free valence sits” value for parent_to_prefix. It is a distinct object rather than None so that a caller which simply has no locant is never confused with one that computed 0/None by accident.

orthonym.assembly.substituent_naming.parent_to_prefix(parent_name, chain_length, *, attach_locant)#

Convert a parent compound name to substituent prefix form.

Per IUPAC (the Blue Book), the parent compound name is transformed into a substituent prefix by: 1. Removing the suffix (e.g., -oic acid, -ol, -one) 2. Converting the suffix to its prefix form (e.g., -ol -> hydroxy) 3. Adding the prefix at the correct locant 4. Appending -yl at the free-valence position

⚠ This converter may only emit locants it can justify. (residue Task A.)

It is handed a name string and a carbon count, and nothing else. The string was produced by naming the fragment as a free molecule after capping its free valence with H, so every locant inside it belongs to the CAPPED molecule’s numbering – which was chosen to favour that molecule’s own principal characteristic group. criterion (h) / **** require the opposite for a substituent group: “The principal substituent chain has the lowest locants for free valences of any kind.”

The two numberings genuinely disagree. Measured witness (R8.2), fragment -C(CH3)(C2H5)-(CH2)8-CH(NH2)-CH(CH3)2:

capped + named as a molecule: 2,12-dimethyltetradecan-3-amine
string-surgered to a prefix: 3-amino-2,12-dimethyltetradecyl
numbered from the free valence: 12-amino-3,13-dimethyltetradecan-3-yl

The chain is numbered from opposite ends, so the amino locant and both methyl locants differ. Splicing a free-valence locant onto the borrowed stem therefore cannot repair these branches – it would produce 3-amino-2,12-dimethyltetradecan-3-yl, one name in two numberings. The numbering has to be recomputed from the structure, which only a caller holding the molecule can do (see _located_acyclic_alkyl_name).

The count-derived branches fail the same way for a second reason. A whole fragment carbon COUNT is not a proof of the fragment’s shape: for the branched acyl -C(=O)CH(CH3)2 the count is 4 while the principal chain is 3, so f"{chain_length}-oxo" spliced locant 4 onto a three-carbon propyl stem (4-oxo-2-methylpropyl).

So both families now DECLINE (return None) rather than fabricate. A one-position stem is the exception that needs no proof: **** (the Blue Book) “All locants are omitted for parent compounds when all substitutable hydrogen atoms have the same locant” – carbamoylmethyl, never 1-carbamoylmethyl.

Parameters:
  • parent_name (str) – Parent compound IUPAC name (e.g., “propan-2-ol”).

  • chain_length (int) – Number of carbons in the substituent chain.

  • attach_locant – Locant of the free-valence atom, or ATTACH_LOCANT_UNKNOWN when the caller cannot prove one. Required – “make a prefix unrenderable without its locant” (eval/LOG.md:345). It used to default to 1 and was read by nothing, so all six call sites silently omitted it.

Returns:

Prefix-form name (e.g., “hydroxymethyl”), ""/None when this converter cannot express the fragment. Returns the raw name WITHOUT enclosing marks.

Return type:

str

orthonym.assembly.substituent_naming.located_map_completes_substituent_stereo(mol, sub_atoms, name, pos)#

Would citing descriptors for exactly the centres pos covers make name express EVERY defined stereo element of sub_atoms?

The admission test for a producer-supplied located map (see _add_substituent_stereo’s located argument). A producer that owns a CHAIN numbering can only cite its own chain’s centres; a ring-yl or heteroatom-branch centre it cannot reach must ALREADY be spelled inside name. When that is not the case, citing the reachable subset would ship a PARTIALLY stereo-specified prefix – a name that claims one configuration and leaves the rest silent. Under (“missing beats wrong”) and the same all-or-nothing principle as general_engine_stereo_complete, that must fail CLOSED: the pre-existing descriptor-less name is emitted instead, and no partial configuration is ever asserted.

PRECONDITION (caller’s responsibility – the count identity cannot see a violation): the centres reachable through pos must be DISJOINT from the centres already expressed inside name. Every current caller (_compound_ring_on_chain_substituent via Tier 1.95) satisfies this by construction – pos holds only carrier-CHAIN carbons while name expresses only the nested RING-yl centres, two disjoint sets. A future caller passing an OVERLAPPING map would get a false ADMIT (one centre double-cited, another left silent), so it must re-establish the disjointness first.

Counts DEFINED elements only: _CIPCode on an atom, or on a bond with both ends inside the fragment. NOTE this atoms-AND-bonds population is WIDER than the atoms-only stereo_atoms that _add_substituent_stereo cites over; a defined stereo BOND therefore pushes the sum away from equality and DECLINES (safe direction – carriers are all-single by the producer’s own guard, so no such bond reaches here today). Carbohydrate alpha-D- notation is NOT counted as expressed by count_expressed_stereo_descriptors, so a glycosyl-bearing fragment under-counts and therefore declines – safe again.

orthonym.assembly.substituent_naming.carbocyclic_ring_yl_attachment_descriptor(mol, frag_atoms, attach_idx)#

'(1R)' (or '(1S)', '(1r)',…) for a substituent group whose free-valence atom attach_idx lies in a carbocyclic monocycle and is the group’s ONLY stereogenic unit; else None.

Derived from structure, not from the group’s name: in a carbocyclic monocycle no heteroatom or indicated hydrogen precedes the free valence in the order, so criterion (c) gives the free valence locant 1 (the Blue Book “(c) principal characteristic groups and free valences (suffixes)”,:3268 ‘cyclohex-3-en-1-yl (preferred prefix)’). (:44643): stereodescriptors relating to a substituent group “are cited at the front of the corresponding prefix. They are preceded by a numerical or letter locant to describe the position of the stereogenic unit when such locants are present” – ‘[(1R)-1-chloropropyl]benzene (PIN)’. So ‘(1R)-cyclohex-3-en-1-yl’, never the locant-free ‘(R)-cyclohex-3-en-1-yl’.

Guarded to the case where the numbering is certain: the ring of attach_idx is a single, unfused, all-carbon, non-aromatic ring wholly inside the group; the group holds no other ring atom (a second ring could make the prefix a ring assembly with its own numbering); the only CIP-labelled atom of the group is attach_idx and no bond of the group carries a stereodescriptor.

orthonym.assembly.substituent_naming.fragment_is_linear_terminal_alkyl(mol, sub_atoms, attach_idx)#

Is get_alkyl_name(carbon_count) an HONEST name for this fragment?

get_alkyl_name(n) can only ever spell an unbranched saturated acyclic chain attached at a terminus (‘propyl’). The two carbon-count fallbacks below used to call it for ANY fragment whose carbon count was non-zero, so a fragment they could not name recursively was renamed by counting its carbons and DISCARDING everything else: CCS[Zn]SCC came out 'ethylethane' and CCO[Zn]OCC came out 'oxylethane'. A name must never claim atoms it dropped, so the fallback is now gated on the fragment actually being the one shape the function can spell.

Requires: every atom carbon, none in a ring, every internal bond single, the induced subgraph a simple path, and attach_idx one of its two ends.

orthonym.assembly.substituent_naming.name_substituent_fragment(mol, sub_atoms, attach_idx, parent_chain)#

_name_substituent_fragment_uncached(), memoised for the top-level naming call (assembly.memo scope; ORTHONYM_MEMO=verify recomputes and compares, off disables it), None included.

The recursive substituent naming asks for the same fragment of the same molecule object again and again: the chain tie-break names every substituent of each tied chain, and the located-FG namers, the compound-substituent namer and the alkoxy prefixes then name the same branches again for each enclosing candidate. Measured on the ChEBI lysine-rich peptide (136 heavy atoms, fresh process): 26,366 calls, 16,898 of them repeats of an earlier call with the same molecule, fragment, attachment and parent, every one returning the earlier value; the outermost repeats took about 26 of 84 s.

Only a call whose side effects a hit can repeat is stored: it charged no unit of the perf, analysis or work budget, the only provenance variable it wrote is the name-scoped non-PIN record, and it read no provenance (metrics.provenance.PROVENANCE_READ). A hit makes the same record_non_pin_fragment calls the stored call made (logged, in order, also those whose fragment was already recorded) and returns the stored value. The repeat of such a call returns the same value: its nested namings are hits in caches whose entries never change once written (the fragment cache is consulted before the cycle and depth guards, and a miss there charges a work unit), so it takes the same path. It makes the same record calls or a subset (a nested memo hit skips the records its computation made); either way they only re-add fragments the stored call already added, because within one naming the record only grows (the one exception, the NP-parent probe’s restore_provenance, can at most leave a hit’s name labelled non-PIN where the repeat would not). Any other call is recomputed on every ask, as before. The key is _nsf_memo_key(); an RWMol (mutable) is never memoised.

orthonym.assembly.substituent_naming.needs_recursive_naming(mol, sub_atoms)#

Check whether a substituent requires recursive naming.

Returns True if the substituent is branched or contains heteroatoms (i.e., not a simple linear alkyl chain).

Parameters:
  • mol – RDKit Mol object.

  • sub_atoms (List[int]) – Atom indices of the substituent.

Returns:

True if recursive naming may be needed, False for simple linear alkyls.

Return type:

bool

orthonym.assembly.substituent_naming.cation_to_prefix(mol, cation_idx, parent_attach_idx, as_free_ion=False)#

Build the cation-as-substituent prefix for a zwitterion.

The cationic atom (cation_idx, e.g. a quaternary ammonium N) is the attachment atom of the substituent prefix; parent_attach_idx is its neighbour that lies on the path into the anionic parent (the bond that is “consumed” by the attachment). Every OTHER neighbour branch of the cation atom becomes an N-substituent prefix, named by the existing structured substituent machinery (name_substituent_fragment), then composed as:

{alphabetized, multiplied N-substituent prefixes}azaniumyl

azane (NH3) is the parent hydride of a nitrogen cation; azanium = NH4+; azaniumyl = the N-attached cationic substituent , OPSIN-parseable equivalent of the -aminiumyl PIN — see module note above). Structured, NOT a hardcoded f-string.

Parameters:
  • mol – RDKit Mol of the whole zwitterion.

  • cation_idx (int) – Atom index of the (non-internal) cationic centre.

  • parent_attach_idx (int) – Neighbour atom index on the path to the anion.

  • as_free_ion (bool) – when True, build the standalone onium-cation UNIT name (stem + ium, e.g. trimethylazanium) instead of the -iumyl substituent-PREFIX form. Used by the /.2 multiplicative bis(…)/tris(…) polycation assembly (a phase, e.g. hexane-1,6-diylbis(trimethylazanium)), where the repeated cationic UNIT is cited as a complete parent-cation name, not a -yl substituent (cf. BB PIN example (1,4-phenylene)bis(phosphanium)). The N-substituent composition (alphabetized/multiplied) is identical either way; only the trailing suffix differs.

Returns:

The cation prefix WITHOUT enclosing marks (e.g. trimethylazaniumyl, or trimethylazanium when as_free_ion), or ‘’ for an out-of-scope cation (ylide / non-N onium / amine-oxide / 1,n-dipolar —

deferred, honest-fail).

Return type:

str

orthonym.assembly.substituent_naming.record_amine_cation_prefix(fragment)#

Label a name that cites a CARBON-substituted N+ as ‘…azaniumyl’ (or a ‘…azanium’ unit) – fragment – as a general-tier name, never a PIN. Label only: the name is unchanged, and only a shipped name that CONTAINS the fragment is demoted (record_non_pin_fragment), so a speculative or discarded call cannot demote a name built another way.

A carbon group on the N+ makes the cation an AMINE cation, whose PIN prefix is built on the ‘-aminium’ suffix: method (1) “all prefix names are formed by adding the suffixes ‘yl’, ‘ylidene’, etc. to the cation name” (the Blue Book) and “Method (1) leads to preferred IUPAC names” (:42299), with the cation name from (:41431, ‘N,*N*,*N*-trimethylmethanaminium (PIN)’:41438). Hence ‘(N,*N*-dimethylmethanaminiumyl)acetate (PIN)’:42473), ‘2-(N,*N*-dimethylmethanaminiumyl)propan-2-ide (PIN)’ :42517), and for a polycation the substitutive ‘-bis(aminium)’ :42154,:42366). The round-trip parser reads none of the ‘-aminiumyl’ forms, so the verified azaniumyl/azanium form ships at a general tier. An N+ with no carbon group keeps ‘azaniumyl (preselected prefix)’ (:42303), and the P/As/O/S onium stems have no amine-type suffix (‘methyldi(phenyl)phosphaniumyl’ is part of a PIN at:42468).