orthonym.rules.ions#

Note

Internal API. Names and behaviour may change between releases.

Ion naming rules per IUPAC 2013.

Handles naming of: - Anions: carboxylates (-ate), alkoxides (-olate), phenolates, aminides, carbanions (-ide) - Cations: aminium (-aminium), carbenium/ylium (-ylium), onium, diazonium

IUPAC 2013 References: -: Anion nomenclature -: Cation nomenclature

Key naming patterns: - Carboxylate anions: acetic acid -> acetate - Alkoxide anions: methanol -> methanolate (PIN) or methoxide (acceptable) - Aminium cations: amine -> aminium (protonated amine) - Carbenium cations: alkane -> ylium (loss of H-)

orthonym.rules.ions.classify_anion(mol, anion_site)#

Classify anion type based on the anionic atom environment.

Examines the local chemical environment of the anionic atom to determine the appropriate naming suffix.

Parameters:
  • mol – RDKit Mol object

  • anion_site (Dict[str, Any]) – Dictionary from get_ion_sites containing: - atom_idx: int - charge: int - element: str - hybridization: str - n_hydrogens: int

Returns:

One of –

‘carboxylate’, ‘alkoxide’, ‘phenolate’, ‘aminide’,

’carbanion’, ‘thiolate’, or ‘unknown’

Return type:

str

Example

>>> mol = Chem.MolFromSmiles('CC(=O)[O-]')
>>> sites = get_ion_sites(mol)
>>> classify_anion(mol, sites['anions'][0])
'carboxylate'
orthonym.rules.ions.classify_cation(mol, cation_site)#

Classify cation type based on the cationic atom environment.

Examines the local chemical environment of the cationic atom to determine the appropriate naming suffix.

Parameters:
  • mol – RDKit Mol object

  • cation_site (Dict[str, Any]) – Dictionary from get_ion_sites containing: - atom_idx: int - charge: int - element: str - hybridization: str - n_hydrogens: int

Returns:

One of – ‘aminium’, ‘ylium’, ‘onium’, ‘diazonium’, or ‘unknown’

Return type:

str

Example

>>> mol = Chem.MolFromSmiles('[NH4+]')
>>> sites = get_ion_sites(mol)
>>> classify_cation(mol, sites['cations'][0])
'aminium'
orthonym.rules.ions.name_carboxylate_anion(parent_name)#

Convert carboxylic acid name to carboxylate anion name.

Transformation rules: - ‘ic acid’ -> ‘ate’ (e.g., acetic acid -> acetate) - ‘oic acid’ -> ‘oate’ (e.g., propanoic acid -> propanoate)

Parameters:

parent_name (str) – Parent acid name (e.g., ‘acetic acid’, ‘propanoic acid’)

Returns:

Anion name (e.g., ‘acetate’, ‘propanoate’)

Return type:

str

Example

>>> name_carboxylate_anion('acetic acid')
'acetate'
>>> name_carboxylate_anion('propanoic acid')
'propanoate'
orthonym.rules.ions.name_aminium_cation(parent_name)#

Convert amine name to aminium cation name.

Parameters:

parent_name (str) – Parent amine name (e.g., ‘methanamine’, ‘ethylamine’)

Returns:

Cation name (e.g., ‘methanaminium’, ‘ethylaminium’)

Return type:

str

Example

>>> name_aminium_cation('methanamine')
'methanaminium'
>>> name_aminium_cation('ammonia')
'ammonium'
orthonym.rules.ions.emit_bis_quaternary_aminium(mol, cation_sites)#

Substitutive ‘-bis(aminium)’ PIN for two quaternary ammonium centres on the ends of a straight carbon chain:

C[N+](C)(C)CCCCCC[N+](C)(C)C
    -> N1,N1,N1,N6,N6,N6-hexamethylhexane-1,6-bis(aminium)

The rule: an N+ carrying carbon groups is a cationic AMINE, named by the cationic suffix ‘aminium’, the Blue Book; ‘N,*N*,*N*- trimethylmethanaminium (PIN)’:41438, not ‘tetramethylazanium’:41354). Two of them on one chain take the suffix twice: “Polycations with cationic centers on characteristic groups are named by substitutive nomenclature or multiplicative nomenclature” (:42138), with the quaternary ‘*N*^1,*N*^1,*N*^3,*N*^3,*N*^3-hexamethylpropanebis(amidium) (PIN)’ (:42154) and ‘butanebis(nitrilium) (PIN)’ beside the non-PIN multiplicative ‘butanediylidynebis(azanium)’ (:42160,:42162); ‘3-(azaniumylmethyl) pentane-1,5-bis(aminium) (PIN)’ (:42366). The chain between the two N is the parent: it is the only chain that carries both suffixes. The N-substituents take N-locants with the chain locant as superscript (written ‘N1’, ‘N6’;:26348), in alphanumerical order.

SCOPE (fail closed, ‘’ on anything else): exactly two acyclic N+ with no H and four carbon neighbours; one unbranched saturated all-carbon bridge of >= 2 atoms between them (_walk_linear_carbon_bridge); the same multiset of N-substituent names on both ends, so the bridge’s two numbering directions are equivalent. The name ships only on a full-InChIKey round trip.

orthonym.rules.ions.emit_bis_quaternary_ammonium(mol, cation_sites)#

/ (a phase): a SYMMETRIC dication with exactly two IDENTICAL quaternary-ammonium centres joined by a straight, saturated, unbranched, unsubstituted all-carbon bridge is named as a multiplicative assembly of parent cations:

{bridge parent-diyl}bis({onium unit})

C[N+](C)(C)CCCCCC[N+](C)(C)C
    -> hexane-1,6-diylbis(trimethylazanium)

(BB, cf. the PIN example ‘(1,4-phenylene)bis(phosphanium)’ and ‘4,4′-(ethane-1,2-diyl)bis(1-methylpyridin-1-ium)’.)

SCOPE (fail closed, i.e. return ‘’, on anything else — asymmetric onium substituents, >2 cations, a ring-borne cationic centre, or a branched/heteroatom-bearing/unsaturated bridge; the caller’s existing fallback then owns the molecule unchanged):

  • exactly 2 cation sites, each N, formal charge +1, 0 attached H, degree 4 (quaternary,, NOT in a ring.

  • the two nitrogens are joined by a SINGLE simple, unbranched, saturated, acyclic, all-carbon bridge (_walk_linear_carbon_bridge) of length >= 2 – this both bounds scope and guarantees the bridge is trivially symmetric (its two numbering directions are equivalent by construction, so locants 1 and n are forced with no orientation choice to make).

  • the two onium substituent sets (cation_to_prefix(..., as_free_ion= True) on each N, reusing the existing zwitterion-onium-prefix machinery) must be BYTE-IDENTICAL strings – the “two units identical” requirement.

orthonym.rules.ions.name_anion(mol, style='pin', _depth=0, retained_only=False)#

Generate IUPAC name for an anionic molecule.

Workflow: 1. Get canonical SMILES 2. Check retained names (unless systematic style) 3. Detect anion sites 4. Classify anion type 5. Generate systematic name

Parameters:
  • mol – RDKit Mol object (must have negative charge)

  • style (str) – Naming style (‘pin’, ‘systematic’, ‘common’)

  • _depth (int) – Internal recursion depth guard (do not set manually)

  • retained_only (bool) – If True, only check retained names and return None if no retained name found (used for namer.py fall-through).

Returns:

Anion name (e.g., ‘acetate’, ‘methoxide’, ‘phenolate’), or empty string if naming fails. When retained_only=True, returns None if no retained name found.

Return type:

str

Example

>>> mol = Chem.MolFromSmiles('CC(=O)[O-]')
>>> name_anion(mol)
'acetate'
orthonym.rules.ions.name_cation(mol, style='pin', _depth=0, retained_only=False)#

Generate IUPAC name for a cationic molecule.

Workflow: 1. Get canonical SMILES 2. Check retained names (unless systematic style) 3. Detect cation sites 4. Classify cation type 5. Generate systematic name

Parameters:
  • mol – RDKit Mol object (must have positive charge)

  • style (str) – Naming style (‘pin’, ‘systematic’, ‘common’)

  • _depth (int) – Internal recursion depth guard (do not set manually)

  • retained_only (bool) – If True, only check retained names and return None if no retained name found (used for namer.py fall-through).

Returns:

Cation name (e.g., ‘ammonium’, ‘methylammonium’, ‘methylium’), or empty string if naming fails. When retained_only=True, returns None if no retained name found.

Return type:

str

Example

>>> mol = Chem.MolFromSmiles('[NH4+]')
>>> name_cation(mol)
'ammonium'
orthonym.rules.ions.specific_method_cation_name(mol)#

The name Specific method gives to mol, or None when the method does not apply to it.

(the Blue Book): “Cations formed formally by the removal of a hydride

ion, H-, from a terminal atom of a saturated unbranched acyclic hydrocarbon… are named by replacing the ‘ane’ ending in the name of the parent hydride by the suffix ‘ylium’”; ‘propylium (PIN)’, ‘methylium (PIN)’ (:41557). The class is read from the structure: one carbon chain of single bonds, no branch, no ring, one carbon with the charge +1 and two hydrogens at the end of the chain (three on a lone carbon). The method names the terminal atom by definition, so the name cites no locant and has one reading; OPSIN reads ‘propylium’ and ‘butylium’ as ambiguous (‘Addition of charge suffix to: prop’) and gives the right structure.

orthonym.rules.ions.apply_ion_suffix_to_name(name, total_charge, allowed_suffixes=None, cation_class=None)#

The SINGLE ionic-suffix re-application primitive for every charged class.

Generalized from _ionize_acid_name (169.6-02 / CHOKE-02). Re-applies the class-correct ionic suffix to a re-entered NEUTRAL name (the output of Orthonym(style).name(neutral_smiles)). Drives the structured resolvers seam (_apply_anion_modification / _apply_cation_modification off the canonical _ANION_SUFFIX_MAP / _CATION_SUFFIX_MAP) — single source of truth, NEVER a per-molecule .replace band-aid (fix-methodology.md).

Two paths:

  1. cation_class in {ylium, acylium, diazonium} -> the class-keyed transform table _CLASS_KEYED_CATION_TRANSFORMS (these are NOT plain suffix swaps and depend on the neutralized form, internal notes):

    • ylium (carbenium): parent-hydride ane->``ylium`` (methane->``methylium``;; methylium is the PIN).

    • acylium: on the ACID name, oic acid->``oylium`` / carboxylic acid->``carbonylium``.

    • diazonium: append diazonium to the hydride name.

    ylium and diazonium are cited at the position of their centre, which the numbering of a neutral name that cites locants does not give: such a name returns ‘’.

  2. Otherwise the generic map-driven path (anion/protonated-amine etc.): longest trailing neutral suffix in the relevant SUFFIX_MAP wins.

allowed_suffixes (GUARD 1 — the heptanolate fix): when given, ONLY those neutral suffixes are eligible for the trailing match, so a name ending “…sulfonic acid” with allowed_suffixes={"ol"} returns ‘’ (it cannot mis-fire to -olate). classify_anion passes the per-class subset so a sulfonate stem can never be mistaken for an alkoxide. Applies to the generic (map) path only; the class-keyed cation path is already class-gated.

Returns the ionized name, or ‘’ if no canonical transform applies (the caller then falls through to the existing cascade — byte-identical contract).

The ion keeps the neutral name’s PIN status: a recorded non-PIN fragment in name (decision A’s demoted ‘N-<acyl>’ float,…) does not survive the suffix swap, so the ionized name is recorded too (metrics.provenance.record_derived_non_pin_fragment).

orthonym.rules.ions.emit_parent_hydride_cumulative_suffix(mol, center_idx, suffix, oxo_prefixes=False)#

Name a parent hydride with a cumulative anionic / radical / cationic suffix on a single carbon centre, with a FIRST-CLASS locant.

suffix in {‘ide’, ‘ium’, ‘yl’, ‘ylidene’, ‘ylidyne’, ‘uide’}.

‘ide’ -> / Table 3.4 (anion, loss of H+) ‘yl’/’ylidene’/’ylidyne’ -> / Table 3.4 (radical, loss of H.) ‘ium’ -> (cation; reserved for later reuse) ‘uide’ -> (anion, addition of H-); a RING centre only

oxo_prefixes (an acyclic carbon ‘-ide’ centre only): a chain carbon’s =O is cited as an ‘oxo’ prefix of the ‘-ide’ parent hydride, (heading the Blue Book) “Systematic names (see are preferred IUPAC names.” (:40872), ‘acetyl anion 1-oxoethan-1-ide (PIN)’ (:40878). Off by default: the ylide composer (_sever_and_name_carbanion) splices its own prefix in front of the returned name without re-sorting the prefixes.

center_idx is the atom index OF THE CHARGED/RADICAL CARBON ON mol (the index-preserving mol passed in — NOT a freshly canonicalized copy; canonical SMILES reorders atoms, RESEARCH Pitfall 1). The centre gets the LOWEST locant over the re-found chain, competing with unsaturation and substituents per

/ (orient_chain criterion (a), principal_group_atoms =

{center_idx}). This is a NEW numbering call (“locants identify positions of the negative charges”, the Blue Book lines 40876-40902), NOT a reused -ol / FG anchor and NOT a hand-rolled carbon-counting scan.

Returns ‘’ on any failure (caller falls through to legacy).

orthonym.rules.ions.emit_mono_ionized_polyfunctional(mol, ion_idx, kind)#

Name a mono-ionized ACYCLIC polyfunctional chain, demoting the identical NEUTRAL sibling groups to prefixes (see _MONO_IONIZED_SPEC / /.

ion_idx is the charged heteroatom index on the index-preserving mol. Fail-closed (’’ -> caller keeps the legacy single-group path) for any shape outside the tight scope: a ring, a substituted / secondary sibling, no sibling (the ordinary single-group case), an extra substituent on the chain, or a chain the acyclic machinery cannot number. Accuracy first: never emit a wrong name.

orthonym.rules.ions.emit_parent_hydride_polyvalent_suffixes(mol, centers)#

/ multi-site free-valence (polyradical) PIN on ONE acyclic all-carbon parent hydride.

centers = [(atom_idx, n_electrons)] with n_electrons in {1, 2, 3} and >= 2 DISTINCT centers (the single-center case is emit_parent_hydride_cumulative_suffix’s job). Returns the polyradical PIN (ethane-1,2-diyl, propane-1,2,3-triyl, pentane-2,4-diylidene, the mixed ethan-1-yl-2-ylidene, …) or '' (fail closed) on anything out of scope.

Generalizes the single-center primitive above to N carbon free-valence centers: saturate every centre on one index-preserving RWMol, choose the longest carbon chain carrying ALL centers (a): the parent must retain the maximum radical centers — demoting a centre to an off-parent prefix is out of v1 scope, so refuse rather than drop), then number by

— “low locants to the free valences as a SET, then in the order

‘yl’, ‘ylidene’, ‘ylidyne’”. Substituents/unsaturation reuse the same machinery as the single-center primitive. ‘’ on any unnameable piece.

orthonym.rules.ions.emit_poly_carbanion_ide(mol, centers)#

(BB 40902): a MULTI-carbanion on one acyclic all-carbon parent hydride — >= 2 carbon -ide centres — named with the ‘-ide’ suffix and the numerical multiplier ‘di’/’tri’: ‘ide’ takes basic multipliers). centers = the carbanion atom indices. Returns e.g. ethynediide ([C-]#[C-], BB 40918), methanediide ([CH2-2]), butane-1,4-diide, or ‘’ on any out-of-scope shape (fail closed).

Mirrors emit_parent_hydride_polyvalent_suffixes (saturate every centre on one index-preserving RWMol -> longest chain carrying ALL centres -> orient for lowest locants to the -ide set), but the ending is ‘-<locs>-<mult>ide’. Locant elision, ‘no ambiguity’): the anionic locants are OMITTED when they occupy EVERY carbon of the chain (a single unambiguous placement: ethyne->ethynediide, methane->methanediide); the unsaturation locant is likewise omitted for a 2-carbon parent (ethyne/ethene carry none).

orthonym.rules.ions.emit_poly_cation_ylium(mol, centers)#

(the Blue Book): a MULTI-carbenium on one acyclic all-carbon parent hydride — carbon -ylium free valences totalling >= 2 across ring-free carbons that each lost a hydride H- — named with the ‘-ylium’ compound suffix multiplied by ‘bis’/’tris’ (the Blue Book note: ‘bis(ylium)’ NOT ‘diylium’, so the COMPLEX multiplier series, mirroring the poly-aminium ‘bis(aminium)’ rule, not the basic ‘di’ the -ide anion uses). centers = the carbenium atom indices; a centre bearing charge +2 counts as TWO -ylium valences on one atom (the geminal degenerate, C[C+2]C). Returns e.g. ethane-1,2-bis(ylium) ([CH2+][CH2+]), propane-1,3-bis(ylium) ([CH2+]C[CH2+]), propane-2,2-bis(ylium) (C[C+2]C), or ‘’ on any out-of-scope shape (fail closed -> caller abstains, never a wrong bis-name).

Mirrors emit_poly_carbanion_ide (saturate every centre — +1 H per unit of positive charge — on one index-preserving RWMol -> longest chain carrying ALL centres -> orient for lowest locants to the -ylium set), but the ending is ‘-<locs>-<bis/tris>(ylium)’. Unlike the -ide anion, the ylium locants are ALWAYS cited (never elided): the examples keep them even when the set covers every carbon (ethane-1,2-bis(ylium), not ethanebis(ylium)).

orthonym.rules.ions.emit_halogen_onium(mol, center_idx)#

(the Blue Book “diphenyliodanium (PIN)”, the Blue Book “…are the preferred IUPAC names and not those given in Table 7.3”): name a disubstituted halogen(III) cation R2X+ on the halogen parent hydride with the ‘-ium’ suffix. The exact CATION mirror of _emit_group13_uide (which builds diphenyliodanuide on the anion side): ligands become prefixes on the ‘-ium’ parent cation.

(C6H5)2I+ -> diphenyliodanium (PIN) [the Blue Book]

Scope (fail closed, so a mono-coordinate halonium / a genuine radical cation / any non-halogen never matches): the centre is a halogen in _HALOGEN_ONIUM_STEMS (I/Br/Cl/F), formal charge +1, and degree >= 2 (a hypervalent halonium — a 1-coordinate R-X+ is a distinct sulfanylium-style species and is excluded). Every heavy atom must be covered by the emitted ligand set (atom-drop veto, identical to the uide emitter); the ligands are named by rules.centre_ligands (the substitutive path’s substituent-prefix namer — classify_substituent named a carbon group by its carbon count: isopropyl ‘propyl’, vinyl ‘ethyl’), and a ligand without a name declines.

Named DIRECTLY: a drop-charge neutralize yields an invalid neutral halogen radical, and an add-H neutralize yields the λ-parent whose ‘-ium’ form (…-λ3-iodanium, a 4-coordinate [IH2+]) is NOT the PIN. No λ-convention is used — diphenyliodanium (the Blue Book) carries none, exactly as diphenyliodanuide (the Blue Book) carries none. Returns ‘’ on any decline (the caller falls through).

orthonym.rules.ions.emit_chalcogen_ylium(mol, center_idx)#

(BB 41565 phenylsulfanylium PIN): name a 1-coordinate chalcogen cation R-S+ / R-Se+ as <R-prefix>sulfanylium / <R-prefix>selanylium.

Scope (tight, so oxonium/sulfonium/phosphonium are never disturbed — they return ‘’ here): the centre is S or Se, formal charge +1, ZERO hydrogens, and exactly ONE heavy neighbour which is a CARBON substituent R (alkyl/aryl). R is named via classify_substituent; a complex R gets enclosing marks: (2,2-dichloroethyl)sulfanylium). Acyl R (R-CO-S+) and R-S-S+ (disulfanylium) are out of scope -> ‘’. Returns ‘’ on any decline (caller falls through).

orthonym.rules.ions.emit_onium_hydride_parent(mol, center_idx)#

(the Blue Book “Cationic compounds derived from… suffixes”; “ethylideneoxidanium (PIN)”:41463, “acetyloxidanium (PIN)”:41474): a single mononuclear chalcogen cation centre (O/S/Se/Te, +1, NOT in a ring, NOT bonded to another chalcogen) carrying substituents (alkyl / acyl / alkylidene) + optional remaining H is named on the mononuclear parent hydride oxidane/sulfane/selane/tellane + ‘-ium’, its substituents cited as detachable prefixes – the systematic PIN, NOT the Table-7.3 ‘-onium’ spelling , general only) nor the ‘-a’/’-onia’-replacement form: CC=[OH+] -> ethylideneoxidanium, not 1-oxaprop-1-en-1-ium.

Mirrors emit_halogen_onium / _emit_group13_uide in SHAPE: substituents named by TYPE from the bond order to the centre (classify_substituent mis-names the ylidene =CHCH3 and the acyl CH3CO- both ‘ethyl’), an atom-drop veto over the whole heavy-atom count, and ‘’ on ANY decline so the caller’s RT gate can never be handed a mis-built name. The substituent-prefix assembly (citation order, multipliers, and the enclosing marks that make ‘benzoyldi(methyl)sulfanium’, BB 16286 ‘tert-butyldi(methyl)phosphane’) follows the same rules as catenated_hydrides._try_aba_parent (called from name_heterochalcogen_aba) – the sibling substituent-prefix assembler – via the shared naming_utils primitives, so no ordering or marking rule is re-spelled here; the ~18-line assembly loop below is a PARALLEL implementation of that sequence (generalized from its local count table to get_multiplier_prefix), not a call into shared code – part of the deferred locant/prefix-render duplication (internal notes). A catenated chalcogen-chalcogen cation (dioxidane, disulfane; the ‘-C(=O)-O-[OH2+]’ -> dioxidan-1-ium family) is out of scope and declines. Returns ‘’ on any decline.

orthonym.rules.ions.emit_catenated_hydride_cation(mol, center_idx)#

(the Blue Book “General rule for systematically naming cationic centers in parent hydrides”; examples:41386-41390): a CATENATED homonuclear parent-hydride cation, or a SUBSTITUTED mononuclear pnictogen cation, named on the parent hydride + ‘-ium’ at the cationic-centre locant.

CN(C)[N+](C)(C)C -> pentamethylhydrazinium (PIN) CS[S+](C)SC -> 1,2,3-trimethyltrisulfan-2-ium (PIN) C[P+](C)(C)Cl -> chlorotri(methyl)phosphanium (PIN) C[P+](C)(C)P(Cl)Cl -> 2,2-dichloro-1,1,1-trimethyldiphosphan-1-ium (PIN) O=C(O[OH2+])c1ccccc1 -> 2-benzoyldioxidan-1-ium (PIN)

Approach (a trace-confirmed: neutralize-re-enter is structurally impossible for a 0-H fully-substituted onium/quaternary centre, and even where a neutral is formable the neutral namer names a DIFFERENT parent – benzenecarboperoxoic acid, not benzoyldioxidane): name the BARE homonuclear parent chain by re-entering the neutral namer (_bare_parent_hydride_name -> trisulfane / hydrazine / diphosphane / dioxidane / phosphane – so the multiplier / retained tables are NOT re-spelled here), number the chain giving the cationic centre the lowest locant (c) /, cite the substituents as detachable prefixes, and append ‘-{loc}-ium’ with ‘e’-elision. Substituents named by TYPE (halogen / acyl-on-chalcogen / organyl) with a whole-heavy-atom drop veto; ‘’ on ANY decline so the caller’s RT gate never sees a mis-built name. A chalcogen MONONUCLEAR onium is out of scope (emit_onium_hydride_parent owns it) as is a mononuclear N (the aminium path).

The hydrazine (retained N-N parent) fully-substituted cation omits all locants per the example ‘pentamethylhydrazinium (PIN)’ (and: every substitutable position of hydrazinium bears the one identical group).

orthonym.rules.ions.emit_ring_poly_cation_ium(mol, centers)#

Several cationic centres of one ring parent hydride, each formed by adding a hydron: (heading the Blue Book) “A cation derived formally by adding one or more hydrons to any position of a neutral parent hydride… is named by replacing the final letter ‘e’ of the parent hydride name, if any, by the suffix ‘ium’, preceded by multiplying prefixes ‘di’, ‘tri’, etc. to denote the multiplicity of identical cationic centers.” (:41368); ‘1,4-dioxane-1,4- diium (PIN)’ (:41409). The final ‘e’ stays before ‘di’/’tri’: it is elided only before a vowel (a),:7595), as in ‘tetramethyldiazene-1,2-diium (PIN)’ (:41407).

Scope: two or more centres in ONE ring system, each a ring heteroatom with charge +1; no other charged atom; every exocyclic group joined by a single bond. The neutral parent hydride is the bare ring: every exocyclic group replaced by hydrogen and one hydrogen (the added hydron) taken off each centre, so a centre keeps its bonding number (a quaternary ring N+ is the parent’s NH plus a hydron, then substituted, as in ‘1,1-dimethylpiperidin-1-ium’). Numbering, heading:3219): the ring’s own heteroatom numbering, then the centres as a set ((c) suffixes,:3256), then the substituted positions ((f),:3301); the exocyclic groups are cited as prefixes in that one numbering. A parent whose name carries indicated hydrogen or hydro prefixes is out of scope (criteria (b),:3246, and (e),:3288, are not modelled here). Returns ‘’ on any decline; the caller verifies the name by a strict round trip.

orthonym.rules.ions.emit_zwitterion_ring_carboxylate(mol, cation_idx, anion_idx)#

F- (DD3,: a zwitterion whose cationic centre is a RING atom of the same ring that bears a carboxylate anion -> the cumulative <prefixes><ring>-<cation-locant>-ium-<carboxyl-locant>-carboxylate name, the cationic suffix cited before the anionic one.

[O-]C(=O)c1ccc[nH+]c1 -> pyridin-1-ium-3-carboxylate C[n+]1ccccc1C(=O)[O-] -> 1-methylpyridin-1-ium-2-carboxylate C[n+]1c(C(=O)[O-])cc(-c2ccccc2)cc1-c1ccccc1 -> 1-methyl-4,6-diphenylpyridin-1-ium-2-carboxylate

the last being the Blue Book’s own worked (PIN) example, :42456, under ** “Zwitterionic compounds with at least one ionic center on a characteristic group”** (:42445).

ONE numbering for the whole name. The old branches took the ring numbering from the namer’s neutral-ring supplier and glued a separately-built cation-substituent prefix onto the resulting substituted ring name: two numberings in one string. Measured on the DEFAULT path (all round-trip through OPSIN, so neither the round-trip metric nor saw them):

  • 1-methyl4-methylpyridin-1-ium-2-carboxylate (no hyphen, unmerged), 1-methyl4-bromopyridin-... (‘bromo’ precedes ‘methyl’,, 1-methyl2,4-dimethylpyridin-...-6-carboxylate (:42447: 2);

and, caught by, 1-propylpyridin-1-ium-2-carboxylate for an isopropyl group. So the numbering is derived ONCE over the ORIGINAL charged mol, with the anion-attachment criterion (_charged_ring_locants(..., anion_attach_idx=...)) and, where that ties, (g) then (j) (ring_numbering_tie_key: ‘(3R,5S)-3,5-dimethylpiperidin-1-ium-4-carboxylate’); every ring substituent is located in THAT numbering by _p74_ring_substituent_prefix and so are the ring’s stereodescriptors , :44643: ‘(2S)-pyrrolidin-1-ium-2-carboxylate’); the stem comes from a ring stripped of all substituents. A hydro mancude monocycle is named by ring_cation_hydro.hydro_ring_zwitterion_name (the bare ring’s own hydro locants are a second numbering), an azolium with a substituted indicated-hydrogen nitrogen by azolium_cations.azolium_zwitterion_name.

Fails closed (‘’) whenever a part is uncorroborated — a fused/bridged ring (no simple cycle, so no

numbering), a substituent the namer cannot spell, a charge outside the cation/anion pair, or

a stereogenic ring N-H (ring_stereo_prefix). The caller then falls through to the legacy path.

orthonym.rules.ions.express_junior_anionic_chalcogens(mol, anions, ionized)#

The name of a multi-anion whose parent is an S/P-oxoacid anion and whose other anionic centres are chalcogen anions (-O-/-S-): the junior centres are cited by their anionic prefixes, ‘oxido’ / ‘sulfido’,:41223 “–O– oxido (preselected prefix)”,:41225 “–S– sulfido (preselected prefix)”), never by the neutral ‘hydroxy’ / ‘sulfanyl’, which drops a charge and denotes a different molecule (‘5-hydroxybenzene-1,3-disulfonate’ is the dianion, not the trianion).

ionized is the neutral acid name with its parent suffix already made anionic (‘5-hydroxybenzene-1,3-disulfonate’). Returns ‘’ unless:

  • every anionic centre is either an S/P-oxoacid centre (sulfonate / sulfinate / phosphonate: the parent) or a chalcogen anion of ONE kind (phenolate/alkoxide -> oxido, or thiolate -> sulfido); a carboxylate belongs to the carboxylate path;

  • (:41261, “CHOICE OF AN ANIONIC PARENT STRUCTURE”) picks the oxoacid parent: (a):41265 “parent with the maximum number of anionic centers, including anionic suffixes” – so the junior centres may not outnumber the oxoacid centres (for [O-]CC([O-])CS(=O)(=O)[O-] the bis(olate) parent has more centres; that shape is left to a producer that builds it); a tie goes on to (d):41281 (O > S: a sulfonate beats a thiolate) and (e):41289, the suffix seniority of (sulfonic and phosphonic acids are senior to alcohols), as in the BB’s “3-oxidonaphthalene-2-carboxylate (PIN) (carboxylate senior to olate)” (:41295);

  • the neutral prefix occurs exactly as often as there are junior centres (a genuine neutral -OH / -SH is never converted);

  • the result round-trips to the input at the full InChIKey.

A swap that may change the prefix citation order is shipped demoted, never as pin_verified (_demote_if_swap_changes_citation_rank).

orthonym.rules.ions.emit_uronium(mol, sites)#

(the Blue Book) substituted URONIUM / THIOURONIUM cation.

Task 8B RC3. A cation formed by adding a hydron to (iso)urea — a carbon bonded to exactly two N and one O (uronium) or one S (thiouronium), one C=heteroatom double bond, net +1 on the skeleton — is named on the retained parent cation uronium / thiouronium: numerical locants are dropped in the PIN; the N/N’/O/S locants follow urea/isourea). ->

CNC(=[NH+]C)Oc1ccccc1 -> N,N’-dimethyl-O-phenyluronium (the Blue Book) CNC(=[NH+]c1ccccc1)SC -> N,S-dimethyl-N’-phenylthiouronium (the Blue Book)

The neutralize -> re-name -> re-suffix path CANNOT reach this class: the neutral isourea names as a carbamimidate ESTER (phenyl N,N'-dimethylcarbamimidate), not a uronium-convertible amine. So this is a bespoke skeleton producer.

Locant priming: the two nitrogens are N / N’; the nitrogen bearing the alphabetically-earliest substituent takes the unprimed N (lowest locant to the prefix cited first). The chalcogen is O or S. Identical substituents group under one multiplier (N,N'-dimethyl); the C-substituent prefixes alphabetise.

Scope (build-the-class-or-degrade): requires >= 1 substituent (the UNSUBSTITUTED parent keeps its retained-name routing) and a single skeleton cation, else returns ‘’ (fail closed). Guanidinium (three N) never matches. The caller RT-gates the result, so a mis-built name fails closed rather than shipping a wrong structure.

orthonym.rules.ions.name_quaternary_aminium(mol, cation_site)#

Systematic -aminium PIN for a STANDALONE quaternary ammonium cation.

a phase.1 method (1) + Table 7.4; the Blue Book:41354, 41429-41438): a quaternary N (formal charge +1, 0 H, degree >= 4) is named by treating the SENIOR carbon chain through N as the amine parent (-amine suffix), the other three N-branches as N,N,N-/N,N-/N- substituent prefixes, then appending -ium (methanamine -> methanaminium):

C[N+](C)(C)C -> ‘N,N,N-trimethylmethanaminium’ OCC[N+](C)(C)C -> ‘2-hydroxy-N,N,N-trimethylethan-1-aminium’ CC[N+](C)(C)CC -> ‘N-ethyl-N,N-dimethylethanaminium’

Mechanism : the quaternary N CANNOT be neutralized — removing a (non-existent) proton leaves an over-valent neutral N and SanitizeMol raises (RESEARCH Pitfall 2). Instead the N is DEMOTED to a neutral carbon-context amine parent: a tertiary_amine functional-group match is injected on the ORIGINAL (still-charged) mol and forced as the principal group, so find_principal_chain selects the senior chain THROUGH N as the parent and the remaining N-branches become N-locant prefixes (NOT select_parent, NOT the azaniumyl prefix). The well-formed neutral amine name is then converted to -aminium via name_aminium_cation (the existing amine->aminium text transform). NO postprocessor, NO regex band-aid, NO per-molecule.replace.

Returns ‘’ (fail-closed) when the cation is NOT a quaternary N or any step fails, so callers fall through to the proven aminium / legacy cascade (the no-crash byte-identical contract).

orthonym.rules.ions.emit_cumulative_ium_ide(mol)#

cumulative same-parent zwitterion: one cationic (‘-ium’) and one anionic (‘-ide’) skeletal centre on the SAME homogeneous heteroatom chain parent hydride. Covers amine imides, azo/azomethine imides /.2) and carbonyl oxides:

C[N-][N+](C)(C)C -> 1,2,2,2-tetramethylhydrazin-2-ium-1-ide (BB 42423)
C[N+]([N-]C)=NC -> 1,2,3-trimethyltriaz-2-en-2-ium-1-ide (BB 43121)
C[N-][N+](=C)C -> 1,2-dimethyl-2-methylidenehydrazin-2-ium-1-ide (BB 43143)
CC(C)=[O+][O-] -> 2-(propan-2-ylidene)dioxidan-2-ium-1-ide (BB 43187)

Numbering: anionic suffixes get seniority for low locants (so the ‘-ide’ locant is minimised before the ‘-ium’ locant); the cationic suffix is cited FIRST in the name (‘<stem>-<ium>-ium-<ide>-ide’). Fail-closed (None).

The charges of an azido group are the bonding of the prefix ‘azido’, the Blue Book), which gives the preferred name,:43298): the azide drawing is declined above, and the zwitterion name that is built for an input DRAWN with its +1 on the amine nitrogen (‘C[N+](C)=NN=[N-]’) names that drawing and is recorded as no PIN.

orthonym.rules.ions.emit_ylide(mol)#

‘ylide’: a closed-shell onium cation X+ (X = N/P/O/S/…, no free H) bonded directly to a carbanion Y-. Named (method 1 = PIN) as the carbanion ‘-ide’ parent hydride with the onium cation as an ‘<…>-aniumyl’ prefix:

C[N+](C)(C)[C-](C)C -> 2-(trimethylazaniumyl)propan-2-ide
C[P+](C)(C)[C-](C)C -> 2-(trimethylphosphaniumyl)propan-2-ide (BB 42526)
CC[C-](CC)[O+](C)C -> 3-(dimethyloxidaniumyl)pentan-3-ide
CC[C-](CC)[S+](C)C -> 3-(dimethylsulfaniumyl)pentan-3-ide
C[N-][O+]=C(C)C -> N-[(propan-2-ylidene)oxidaniumyl]methanaminide

The anion parent is a carbanion (‘-ide’) OR a nitrogen aminide (‘-aminide’, ; the latter carries the cation prefix at the ‘N’ locant.

The nitrogen PIN is the amine-based ‘2-(N,N-dimethylmethanaminiumyl)propan-2- ide’, but that form is OPSIN-unparseable; the azane-based ‘trimethylazaniumyl’ equivalent (which OPSIN accepts) is emitted instead (W4-I4). A ring N+ with an exocyclic N- (an N-imide) is the azanide of its group with the ring cation as a prefix : charged_router.emit_ring_cation_azanide. Fail-closed.