orthonym.perception.charge_shift#

Note

Internal API. Names and behaviour may change between releases.

Charge-only resonance forms of a conjugated cation, found by matching its pi bonds.

A cation whose +1 can sit on several nitrogen atoms is one species whatever SMILES draws the charge (the contributor guide core principle 4 “Determinism”), and the hydron that names it sits on one atom of the delocalised system: “General rule for systematically naming cationic centers in parent hydrides” (the Blue Book),:41368: “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’”.

Give each atom of a Kekule structure p, its number of pi bonds (a double bond counts one, a triple bond two). A shift of the +1 lowers p by one on the nitrogen that loses it and raises p by one on the neutral three-connected nitrogen that takes it. The shifted structure is a perfect matching of the pi bonds in which atom i has p’(i) pi bonds: replace atom i by p’(i) copies, join the copies of bonded atoms, and ask Edmonds’ blossom algorithm for a perfect matching. The search is polynomial, it counts its own work (configurations and matching nodes) and reads no clock, so one molecule gives one answer on every machine.

The +1 does not move in a pi system that holds a carbanion centre, a carbon with a negative charge: an N-heterocyclic carbene drawn as the zwitterion [N+]=[C-] (the +1 would swap between the two nitrogen atoms of N-C(-)=N+), an N-ylide, an isocyanide or a dicyanomethanide conjugated with the cation. RDKit’s enumeration writes a carbene, an N-ylide and an isocyanide as one structure. For a dicyanomethanide it writes more, among them the +1 on the other nitrogen, but only with the charge of the carbanion moved onto a nitrile nitrogen, and:func:accepted_charge_forms rejects that drawing because only nitrogen atoms may change their charge. So the forms RDKit gives such a group are the drawn one alone, and a form that RDKit’s filtered enumeration does not give is not offered here either (_Drawing.in_carbanion_system()). Measured on the corpus rows that RDKit finished and that hold a carbanion in a pi system with a mobile nitrogen cation: 867 rows, RDKit gives a second form from that system for none of them (two rows get theirs from another pi system), and the helper’s forms equal RDKit’s on 844. A carbanion in another pi system, or cut off from the cation by sp3 atoms, does not stop a shift.

The contract of every function that answers a question about forms is: an empty answer is a proof (no form, no site), and None is “undecided” with one reason code in why. A caller treats None as unknown, never as empty. Reason codes:

metal a metal bonded in a fragment that holds a mobile nitrogen cation radical an unpaired electron in such a fragment, for the questions that read structures, not forms kekulize no Kekule structure configurations more lost and donor choices or Kekule alternatives than the count allows nodes more matching nodes than the count allows bond_order a matching that no bond order draws inchi no standard InChIKey for the drawing, so no form can be checked against it anion an anionic pi atom beside a +1 that cannot reach a site without moving the anion too

Two counts of the graph of pi-capable atoms (pi_capable()) choose between RDKit’s resonance enumeration and this module:pi_atoms() for the enumeration with UNCONSTRAINED_CATIONS (charge_drawings()) and pi_work() for the one without a flag. The graph is the same for every drawing of the charge, so one species takes one route.

orthonym.perception.charge_shift.RDKIT_RESONANCE_MAX_CONJUGATED = 32#

pi atoms of the largest pi system up to which RDKit’s enumeration with UNCONSTRAINED_CATIONS is asked. RDKit builds every bond-order arrangement of a conjugated group, about x3 to x8 per linked ring.

orthonym.perception.charge_shift.RDKIT_RESONANCE0_MAX_WORK = 28#

pi_work() up to which RDKit’s enumeration without a flag is asked. Its cost follows the acyclic groups that carry charge (azide, diazonium, azo, nitroso: about x7 to x13 each), not the rings.

orthonym.perception.charge_shift.MAX_CONFIGURATIONS = 400#

lost and donor choices, and Kekule alternatives, tried in one call before the answer is undecided

orthonym.perception.charge_shift.MAX_PI_NODES = 600#

copies of atoms in one matching before the answer is undecided

orthonym.perception.charge_shift.METALS = frozenset({3, 4, 11, 12, 13, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112})#

the alkali and alkaline-earth metals, the d and f blocks, and Al, Ga, In, Tl, Sn, Pb, Sb and Bi. B, Si, Ge, As, Se and Te are ordinary pi atoms here.

Type:

atomic numbers that make a bonded fragment undecided

orthonym.perception.charge_shift.MOBILE_N_CATION = <rdkit.Chem.rdchem.Mol object>#

an N+ with a double or aromatic bond that is no nitro group or N-oxide (the charge of those sits on the oxygen)

Type:

a cationic nitrogen that can hand its charge on

orthonym.perception.charge_shift.pi_capable(atom)#

True when the atom can sit in a pi system in every drawing of its species: it is aromatic, carries a multiple bond, is an N, O, P, S, As, Se or Te with at most three neighbours (hydrogens counted), or is a charged atom with at most three neighbours (the carbocation or carbanion centre of a triarylmethyl dye). The nitrogen that holds the +1 in one drawing and the nitrogen that holds a lone pair in another are both pi-capable, so the set does not depend on where the charge is drawn.

orthonym.perception.charge_shift.pi_system(mol)#

(pi atoms, charged pi atoms) of the largest pi component of two or more atoms; (0, 0) when there is none. A lone atom is no system. Of two components of one size the one with more charged atoms is meant.

orthonym.perception.charge_shift.pi_atoms(mol)#

The pi atoms of the largest pi system: the count that decides whether RDKit’s enumeration with UNCONSTRAINED_CATIONS is asked.

orthonym.perception.charge_shift.pi_work(mol)#

W0: the largest, over the pi components, of pi atoms + 2 x formally charged pi atoms. RDKit’s enumeration without a flag costs what its worst group costs, and a small charge-dense group can sit beside a larger neutral one.

orthonym.perception.charge_shift.rdkit_flags8_affordable(mol)#

True when RDKit’s enumeration with UNCONSTRAINED_CATIONS is asked for mol.

orthonym.perception.charge_shift.rdkit_flags0_affordable(mol)#

True when RDKit’s enumeration without a flag is asked for mol.

orthonym.perception.charge_shift.charge_drawings(mol, rdkit_source, *, why=None)#

The drawings of mol that the charge-only forms of its species are read from: rdkit_source(mol) (RDKit’s resonance structures, with UNCONSTRAINED_CATIONS) when:func:rdkit_flags8_affordable, else charge_shift_forms(). RDKit builds every bond-order arrangement of a conjugated group and its C++ call cannot be interrupted, so a molecule above the bound never reaches rdkit_source. None is undecided (why receives the reason code): the caller names the input as drawn, it does not ask RDKit.

orthonym.perception.charge_shift.accepted_charge_forms(mol, smiles, candidates)#

The charge-only forms of mol among candidates (drawings of the same species, as Mol or None), as {canonical SMILES: (string, atom order or None)}: the drawing smiles itself first (order None), then each candidate that is a charge-only form, in the order the candidates come. The candidates are RDKit’s resonance structures or the output of:func:charge_shift_forms; one filter decides for both. A candidate is a form when every atom keeps its hydrogen count, no atom has an unpaired electron, only nitrogen atoms change their charge, the +1 only changes places and goes onto a neutral nitrogen with three connections (a lone pair to give: amidinium, guanidinium, azolium), and the number of double bonds between an aromatic and a non-aromatic atom is the drawing’s. It is read back from its written string and kept only when the standard InChIKey is the drawing’s, so a form cannot describe another molecule. The bounds on the number of forms are those of namer._charge_forms, not this function’s.

The +1 of an azido group does not move (azide_atoms()): a form in which an atom of the group in either drawing loses or gains the +1 is no form. namer._charge_forms says it: “A cumulated or dipolar nitrogen cation (an azide, a diazo compound, a nitrilium) moves a -1, not a +1, between its resonance structures… such an input is named as it is drawn”. Without the rule RDKit’s enumeration of CN(C)N=[N+]=[N-] offered C[N+](C)=NN=[N-] (the +1 on the amine nitrogen, a neutral nitrogen with three connections) and the form choice shipped the name of that drawing, read by OPSIN to other atoms than the azide the input draws.

orthonym.perception.charge_shift.azide_atoms(mol)#

The atoms of the azido groups of mol, in whichever of the three drawings of:data:_AZIDE_DRAWINGS it has them. “AZIDES” (the Blue Book): the group -N3 (-N=N+=N-) is named by the prefix ‘azido’; its charges are the bonding of that prefix, not ionic centres that move.

orthonym.perception.charge_shift.charge_places(forms)#

How many different places the +1 takes among forms (the values of:func:accepted_charge_forms): the set of atoms of the input that carry a +1 in the form, read from its string through its atom order. Two forms with the +1 on the same atoms are Kekule structures of one placement, so a count of forms exceeds the count of placements wherever a conjugated system that RDKit does not aromatise has more than one.

orthonym.perception.charge_shift.stereo_elements(mol, order=None)#

The configured stereo elements of mol: each double bond that carries an E/Z (cis/trans) configuration and each atom that carries a tetrahedral chiral tag, keyed by its atoms in the numbering of the INPUT (order[k] is the input’s atom of atom k of mol, as in:func:accepted_charge_forms; None is the identity), and valued by the configuration itself in a way that does not depend on how mol numbers or writes its atoms. A double bond: ('=', {begin, end}) is True when the lowest-numbered neighbours at the two ends lie on the same side (RDKit’s reference atoms of the bond, re-expressed on those neighbours). A chiral atom: ('@', atom) is the sense of its tag taken over its neighbours in the order of their numbers in the input. Two molecules of one graph (the forms of one species share their atoms and their connections and differ in bond orders and charges) can then be compared element by element.

orthonym.perception.charge_shift.kept_stereo(mol, forms)#

For each form of forms (the values of:func:accepted_charge_forms for mol, in that order), how many of the configured stereo elements of mol (stereo_elements()) the form’s own string carries with the same configuration. A form is written from a Kekule structure whose double bonds are not the drawing’s, and a bond that is single in it carries no E/Z: the standard InChIKey that admits a form (accepted_charge_forms()) does not see the bonds of a delocalised polymethine, so the key cannot say that a form has lost them. None where a form’s string cannot be read (the caller then does not compare the forms by this count).

orthonym.perception.charge_shift.perfect_matching(nodes, adj)#

A perfect matching of the graph on nodes with the neighbour lists adj (a neighbour outside nodes is ignored), as {node: its mate}, or None when there is none. Edmonds’ blossom algorithm: odd cycles (pyrrole) need it. Nodes and neighbours are taken in sorted order, so the matching does not depend on the order of adj.

orthonym.perception.charge_shift.exocyclic_double_bonds_to_aromatic(mol)#

The number of double bonds between an aromatic atom and an atom that is not.

orthonym.perception.charge_shift.charge_shift_forms(mol, *, lost=None, gained=None, max_moves=3, max_configurations=400, why=None)#

The charge-only resonance forms of mol as molecules (the drawn form excluded), under the filter chain of namer._charge_forms: the +1 moves between nitrogen atoms along pi bonds (not in a pi system that holds a carbanion centre, see the module docstring), every hydrogen count is the drawing’s, the standard InChIKey of the written form is the drawing’s, and the canonical SMILES differs from the drawing’s. Up to max_moves shifts happen together. Without lost and gained the other Kekule structures of a conjugated system (another arrangement of the double bonds of a ring that is not aromatic), of the drawing and of every shifted structure whether or not that one is a form, are forms too when their canonical SMILES differs; so a placement of the charge gives every one of its Kekule structures.

lost restricts the nitrogen cations that give the charge away and gained the atoms that take it (collections of atom indices). means proven: no form. A molecule with no mobile nitrogen cation has none, and neither has one with an unpaired electron (every structure of it keeps the electron, and a structure with one is no form). None means undecided; why (a list) receives one reason code. The forms come in a fixed order and each has its own canonical SMILES; they carry the atom indices of mol.

orthonym.perception.charge_shift.hydron_sites(mol, centre, ring_system, *, why=None, max_configurations=400)#

Site-2 acceptance: {ring atom X: a structure in which the centre is neutral and X, an atom that is not H or C and carries a hydrogen, bears +1}, for the ring atoms X of ring_system (atom indices) other than centre, the +1 of the centre moved onto one neutral three-connected nitrogen. It has no exocyclic-double-bond filter and no InChIKey filter: the acceptance is RDKit’s own for the site. The structures are sanitized and carry the atom indices of mol.

{} means proven: no site by moving the +1 of the centre onto a nitrogen, and none for a centre in a pi system that holds a carbanion centre (the module docstring: RDKit finds no site there either). None means undecided; why receives one reason code. anion is reported when no site is found and an anionic atom sits in the pi system of the centre: moving the +1 may need the -1 to move too (a zwitterion), which this matching does not draw. Sites found are returned whether or not an anion is present.

orthonym.perception.charge_shift.charge_placements(mol, *, why=None, max_moves=3, max_configurations=400)#

The structures of mol with the +1 of each mobile nitrogen cation moved onto a neutral three-connected nitrogen (up to max_moves at once), the drawn structure excluded: the other places the charge of one species can be drawn, for the neutral analogues of validation.protonation_identity. The structures are sanitized and carry the atom indices of mol; no filter beyond a valid structure is applied, as the analogue of a structure is read, not named.

means proven: no placement of a nitrogen +1. None means undecided; why receives one reason code. Only the +1

of a nitrogen moves: an anion, an oxonium or a sulfonium that RDKit’s enumeration moves too stays where it is drawn, so the set of placements can be narrower than RDKit’s. The +1 of a pi system that holds a carbanion centre stays where it is drawn (the module docstring), as it does in RDKit’s enumeration.