private static IStereoElement <IChemObject, IChemObject> NewExtendedTetrahedral(int u, Graph g, IAtom[] atoms) { var terminals = FindExtendedTetrahedralEnds(g, u); var xs = new int[] { -1, terminals[0], -1, terminals[1] }; int n = 0; foreach (var e in g.GetEdges(terminals[0])) { if (e.Bond.Order == 1) { xs[n++] = e.Other(terminals[0]); } } n = 2; foreach (var e in g.GetEdges(terminals[1])) { if (e.Bond.Order == 1) { xs[n++] = e.Other(terminals[1]); } } Array.Sort(xs); TetrahedralStereo stereo = g.ConfigurationOf(u).Shorthand == Beam.Configuration.Clockwise ? TetrahedralStereo.Clockwise : TetrahedralStereo.AntiClockwise; return(new ExtendedTetrahedral(atoms[u], new IAtom[] { atoms[xs[0]], atoms[xs[1]], atoms[xs[2]], atoms[xs[3]] }, stereo)); }
public void S_penta_2_3_diene_expl_h() { var m = new AtomContainer(); m.Atoms.Add(new Atom("C")); m.Atoms.Add(new Atom("C")); m.Atoms.Add(new Atom("C")); m.Atoms.Add(new Atom("C")); m.Atoms.Add(new Atom("C")); m.Atoms.Add(new Atom("H")); m.Atoms.Add(new Atom("H")); m.AddBond(m.Atoms[0], m.Atoms[1], BondOrder.Single); m.AddBond(m.Atoms[1], m.Atoms[2], BondOrder.Double); m.AddBond(m.Atoms[2], m.Atoms[3], BondOrder.Double); m.AddBond(m.Atoms[3], m.Atoms[4], BondOrder.Single); m.AddBond(m.Atoms[1], m.Atoms[5], BondOrder.Single); m.AddBond(m.Atoms[3], m.Atoms[6], BondOrder.Single); var atoms = new int[][] { new[] { 0, 5, 6, 4 }, new[] { 5, 0, 6, 4 }, new[] { 5, 0, 4, 6 }, new[] { 0, 5, 4, 6 }, new[] { 4, 6, 5, 0 }, new[] { 4, 6, 0, 5 }, new[] { 6, 4, 0, 5 }, new[] { 6, 4, 5, 0 }, }; var stereos = new TetrahedralStereo[] { TetrahedralStereo.Clockwise, TetrahedralStereo.AntiClockwise, TetrahedralStereo.Clockwise, TetrahedralStereo.AntiClockwise, TetrahedralStereo.Clockwise, TetrahedralStereo.AntiClockwise, TetrahedralStereo.Clockwise, TetrahedralStereo.AntiClockwise }; for (int i = 0; i < atoms.Length; i++) { var element = new ExtendedTetrahedral(m.Atoms[2], new IAtom[] { m.Atoms[atoms[i][0]], m.Atoms[atoms[i][1]], m.Atoms[atoms[i][2]], m.Atoms[atoms[i][3]] }, stereos[i]); m.SetStereoElements(new[] { element }); Assert.AreEqual("CC(=[C@]=C(C)[H])[H]", Convert(m, SmiFlavors.Stereo).ToSmiles()); } }
/// <summary> /// Creates a new data model for chirality for the CIP rules. /// </summary> /// <param name="chiralAtom">The <see cref="IAtom"/> that is actually chiral.</param> /// <param name="ligands">An array with exactly four <see cref="ILigand"/>s.</param> /// <param name="stereo">A indication of clockwise or anticlockwise orientation of the atoms.</param> /// <seealso cref="TetrahedralStereo"/> public LigancyFourChirality(IAtom chiralAtom, IEnumerable <ILigand> ligands, TetrahedralStereo stereo) { var _ligands = ligands.ToList(); if (_ligands.Count != 4) { throw new ArgumentException($"Count of {nameof(ligands)} should be 4.", nameof(ligands)); } this.ChiralAtom = chiralAtom; this.ligands = _ligands; this.Stereo = stereo; }
static void AssertTetrahedralCenter(IStereoElement <IChemObject, IChemObject> element, IAtom focus, TetrahedralStereo winding, params IAtom[] neighbors) { Assert.IsInstanceOfType(element, typeof(ITetrahedralChirality)); ITetrahedralChirality actual = (ITetrahedralChirality)element; Assert.AreSame(focus, actual.ChiralAtom); Assert.AreEqual(winding, actual.Stereo); Assert.IsTrue(Compares.AreDeepEqual(neighbors, actual.Ligands)); }
/// <summary> /// Get inverted conformation, /// </summary> /// <remarks> /// <see cref="Invert(TetrahedralStereo)"/> of <see cref="Clockwise"/> == <see cref="AntiClockwise"/> /// <see cref="Invert(TetrahedralStereo)"/> of <see cref="AntiClockwise"/> == <see cref="Clockwise"/> /// </remarks> /// <returns>The inverse conformation</returns> public static TetrahedralStereo Invert(this TetrahedralStereo value) { if (value == Clockwise) { return(AntiClockwise); } if (value == AntiClockwise) { return(Clockwise); } return(value); }
public static StereoConfigurations ToConfiguration(this TetrahedralStereo value) { switch (value) { case AntiClockwise: return(StereoConfigurations.Left); case Clockwise: return(StereoConfigurations.Right); default: throw new System.ArgumentException("Unknown enum value: " + value); } }
// why it does not work? [TestMethod()] public void S_penta_2_3_diene_expl_h() { var m = builder.NewAtomContainer(); m.Atoms.Add(builder.NewAtom("CH3")); m.Atoms.Add(builder.NewAtom("C")); m.Atoms.Add(builder.NewAtom("C")); m.Atoms.Add(builder.NewAtom("C")); m.Atoms.Add(builder.NewAtom("CH3")); m.Atoms.Add(builder.NewAtom("H")); m.Atoms.Add(builder.NewAtom("H")); m.AddBond(m.Atoms[0], m.Atoms[1], BondOrder.Single); m.AddBond(m.Atoms[1], m.Atoms[2], BondOrder.Double); m.AddBond(m.Atoms[2], m.Atoms[3], BondOrder.Double); m.AddBond(m.Atoms[3], m.Atoms[4], BondOrder.Single); m.AddBond(m.Atoms[1], m.Atoms[5], BondOrder.Single); m.AddBond(m.Atoms[3], m.Atoms[6], BondOrder.Single); int[][] atoms = new int[][] { new[] { 0, 5, 6, 4 }, new[] { 5, 0, 6, 4 }, new[] { 5, 0, 4, 6 }, new[] { 0, 5, 4, 6 }, new[] { 4, 6, 5, 0 }, new[] { 4, 6, 0, 5 }, new[] { 6, 4, 0, 5 }, new[] { 6, 4, 5, 0 }, }; var stereos = new TetrahedralStereo[] { TetrahedralStereo.Clockwise, TetrahedralStereo.Clockwise, TetrahedralStereo.Clockwise, TetrahedralStereo.AntiClockwise, TetrahedralStereo.Clockwise, TetrahedralStereo.AntiClockwise, TetrahedralStereo.Clockwise, TetrahedralStereo.Clockwise, }; for (int i = 0; i < atoms.Length; i++) { var element = new ExtendedTetrahedral( m.Atoms[2], new IAtom[] { m.Atoms[atoms[i][0]], m.Atoms[atoms[i][1]], m.Atoms[atoms[i][2]], m.Atoms[atoms[i][3]] }, stereos[i]); m.SetStereoElements(new[] { element }); var generator = factory.GetInChIGenerator(m); Assert.AreEqual("InChI=1S/C5H8/c1-3-5-4-2/h3-4H,1-2H3/t5-/m1/s1", generator.InChI); } }
/// <summary> /// Get the parity (-1,+1) of the tetrahedral configuration. /// </summary> /// <param name="stereo">configuration</param> /// <returns>the parity</returns> private static int Parity(TetrahedralStereo stereo) { return(stereo == TetrahedralStereo.Clockwise ? 1 : -1); }
public ITetrahedralChirality NewTetrahedralChirality(IAtom chiralAtom, IReadOnlyList <IAtom> ligandAtoms, TetrahedralStereo chirality) => (ITetrahedralChirality) new TetrahedralChirality(chiralAtom, ligandAtoms, chirality) { Builder = this };
/// <summary> /// Reads atoms, bonds etc from atom container and converts to format /// InChI library requires, then places call for the library to generate /// the InChI. /// </summary> /// <param name="atomContainer">AtomContainer to generate InChI for.</param> /// <param name="ignore"></param> private void GenerateInChIFromCDKAtomContainer(IAtomContainer atomContainer, bool ignore) { this.ReferringAtomContainer = atomContainer; // Check for 3d coordinates bool all3d = true; bool all2d = true; foreach (var atom in atomContainer.Atoms) { if (all3d && atom.Point3D == null) { all3d = false; } if (all2d && atom.Point2D == null) { all2d = false; } } var atomMap = new Dictionary <IAtom, NInchiAtom>(); foreach (var atom in atomContainer.Atoms) { // Get coordinates // Use 3d if possible, otherwise 2d or none double x, y, z; if (all3d) { var p = atom.Point3D.Value; x = p.X; y = p.Y; z = p.Z; } else if (all2d) { var p = atom.Point2D.Value; x = p.X; y = p.Y; z = 0.0; } else { x = 0.0; y = 0.0; z = 0.0; } // Chemical element symbol var el = atom.Symbol; // Generate InChI atom var iatom = Input.Add(new NInchiAtom(x, y, z, el)); atomMap[atom] = iatom; // Check if charged var charge = atom.FormalCharge.Value; if (charge != 0) { iatom.Charge = charge; } // Check whether isotopic var isotopeNumber = atom.MassNumber; if (isotopeNumber != null) { iatom.IsotopicMass = isotopeNumber.Value; } // Check for implicit hydrogens // atom.HydrogenCount returns number of implicit hydrogens, not // total number // Ref: Posting to cdk-devel list by Egon Willighagen 2005-09-17 int?implicitH = atom.ImplicitHydrogenCount; // set implicit hydrogen count, -1 tells the inchi to determine it iatom.ImplicitH = implicitH ?? -1; // Check if radical int count = atomContainer.GetConnectedSingleElectrons(atom).Count(); if (count == 0) { // TODO - how to check whether singlet or undefined multiplicity } else if (count == 1) { iatom.Radical = INCHI_RADICAL.Doublet; } else if (count == 2) { iatom.Radical = INCHI_RADICAL.Triplet; } else { throw new CDKException("Unrecognised radical type"); } } // Process bonds var bondMap = new Dictionary <IBond, NInchiBond>(); foreach (var bond in atomContainer.Bonds) { // Assumes 2 centre bond var at0 = atomMap[bond.Begin]; var at1 = atomMap[bond.End]; // Get bond order INCHI_BOND_TYPE order; var bo = bond.Order; if (!ignore && bond.IsAromatic) { order = INCHI_BOND_TYPE.Altern; } else if (bo == BondOrder.Single) { order = INCHI_BOND_TYPE.Single; } else if (bo == BondOrder.Double) { order = INCHI_BOND_TYPE.Double; } else if (bo == BondOrder.Triple) { order = INCHI_BOND_TYPE.Triple; } else { throw new CDKException("Failed to generate InChI: Unsupported bond type"); } // Create InChI bond var ibond = new NInchiBond(at0, at1, order); bondMap[bond] = ibond; Input.Add(ibond); // Check for bond stereo definitions var stereo = bond.Stereo; // No stereo definition if (stereo == BondStereo.None) { ibond.BondStereo = INCHI_BOND_STEREO.None; } // Bond ending (fat end of wedge) below the plane else if (stereo == BondStereo.Down) { ibond.BondStereo = INCHI_BOND_STEREO.Single1Down; } // Bond ending (fat end of wedge) above the plane else if (stereo == BondStereo.Up) { ibond.BondStereo = INCHI_BOND_STEREO.Single1Up; } // Bond starting (pointy end of wedge) below the plane else if (stereo == BondStereo.DownInverted) { ibond.BondStereo = INCHI_BOND_STEREO.Single2Down; } // Bond starting (pointy end of wedge) above the plane else if (stereo == BondStereo.UpInverted) { ibond.BondStereo = INCHI_BOND_STEREO.Single2Up; } else if (stereo == BondStereo.EOrZ) { ibond.BondStereo = INCHI_BOND_STEREO.DoubleEither; } else if (stereo == BondStereo.UpOrDown) { ibond.BondStereo = INCHI_BOND_STEREO.Single1Either; } else if (stereo == BondStereo.UpOrDownInverted) { ibond.BondStereo = INCHI_BOND_STEREO.Single2Either; } // Bond with undefined stereochemistry else if (stereo == BondStereo.None) { if (order == INCHI_BOND_TYPE.Single) { ibond.BondStereo = INCHI_BOND_STEREO.Single1Either; } else if (order == INCHI_BOND_TYPE.Double) { ibond.BondStereo = INCHI_BOND_STEREO.DoubleEither; } } } // Process tetrahedral stereo elements foreach (var stereoElem in atomContainer.StereoElements) { if (stereoElem is ITetrahedralChirality chirality) { var stereoType = chirality.Stereo; var atC = atomMap[chirality.ChiralAtom]; var at0 = atomMap[chirality.Ligands[0]]; var at1 = atomMap[chirality.Ligands[1]]; var at2 = atomMap[chirality.Ligands[2]]; var at3 = atomMap[chirality.Ligands[3]]; var p = INCHI_PARITY.Unknown; if (stereoType == TetrahedralStereo.AntiClockwise) { p = INCHI_PARITY.Odd; } else if (stereoType == TetrahedralStereo.Clockwise) { p = INCHI_PARITY.Even; } else { throw new CDKException("Unknown tetrahedral chirality"); } var jniStereo = new NInchiStereo0D(atC, at0, at1, at2, at3, INCHI_STEREOTYPE.Tetrahedral, p); Input.Stereos.Add(jniStereo); } else if (stereoElem is IDoubleBondStereochemistry dbStereo) { var surroundingBonds = dbStereo.Bonds; if (surroundingBonds[0] == null || surroundingBonds[1] == null) { throw new CDKException("Cannot generate an InChI with incomplete double bond info"); } var stereoType = dbStereo.Stereo; IBond stereoBond = dbStereo.StereoBond; NInchiAtom at0 = null; NInchiAtom at1 = null; NInchiAtom at2 = null; NInchiAtom at3 = null; // TODO: I should check for two atom bonds... or maybe that should happen when you // create a double bond stereochemistry if (stereoBond.Contains(surroundingBonds[0].Begin)) { // first atom is A at1 = atomMap[surroundingBonds[0].Begin]; at0 = atomMap[surroundingBonds[0].End]; } else { // first atom is X at0 = atomMap[surroundingBonds[0].Begin]; at1 = atomMap[surroundingBonds[0].End]; } if (stereoBond.Contains(surroundingBonds[1].Begin)) { // first atom is B at2 = atomMap[surroundingBonds[1].Begin]; at3 = atomMap[surroundingBonds[1].End]; } else { // first atom is Y at2 = atomMap[surroundingBonds[1].End]; at3 = atomMap[surroundingBonds[1].Begin]; } var p = INCHI_PARITY.Unknown; if (stereoType == DoubleBondConformation.Together) { p = INCHI_PARITY.Odd; } else if (stereoType == DoubleBondConformation.Opposite) { p = INCHI_PARITY.Even; } else { throw new CDKException("Unknown double bond stereochemistry"); } var jniStereo = new NInchiStereo0D(null, at0, at1, at2, at3, INCHI_STEREOTYPE.DoubleBond, p); Input.Stereos.Add(jniStereo); } else if (stereoElem is ExtendedTetrahedral extendedTetrahedral) { TetrahedralStereo winding = extendedTetrahedral.Winding; // The peripherals (p<i>) and terminals (t<i>) are referring to // the following atoms. The focus (f) is also shown. // // p0 p2 // \ / // t0 = f = t1 // / \ // p1 p3 var terminals = extendedTetrahedral.FindTerminalAtoms(atomContainer); var peripherals = extendedTetrahedral.Peripherals.ToArray(); // InChI API is particular about the input, each terminal atom // needs to be present in the list of neighbors and they must // be at index 1 and 2 (i.e. in the middle). This is true even // of explicit atoms. For the implicit atoms, the terminals may // be in the peripherals already and so we correct the winding // and reposition as needed. var t0Bonds = OnlySingleBonded(atomContainer.GetConnectedBonds(terminals[0])); var t1Bonds = OnlySingleBonded(atomContainer.GetConnectedBonds(terminals[1])); // first if there are two explicit atoms we need to replace one // with the terminal atom - the configuration does not change if (t0Bonds.Count == 2) { var orgBond = t0Bonds[0]; t0Bonds.RemoveAt(0); var replace = orgBond.GetOther(terminals[0]); for (int i = 0; i < peripherals.Length; i++) { if (replace == peripherals[i]) { peripherals[i] = terminals[0]; } } } if (t1Bonds.Count == 2) { var orgBond = t0Bonds[0]; t1Bonds.RemoveAt(0); var replace = orgBond.GetOther(terminals[1]); for (int i = 0; i < peripherals.Length; i++) { if (replace == peripherals[i]) { peripherals[i] = terminals[1]; } } } // the neighbor attached to each terminal atom that we will // define the configuration of var t0Neighbor = t0Bonds[0].GetOther(terminals[0]); var t1Neighbor = t1Bonds[0].GetOther(terminals[1]); // we now need to move all the atoms into the correct positions // everytime we exchange atoms the configuration inverts for (int i = 0; i < peripherals.Length; i++) { if (i != 0 && t0Neighbor == peripherals[i]) { Swap(peripherals, i, 0); winding = winding.Invert(); } else if (i != 1 && terminals[0] == peripherals[i]) { Swap(peripherals, i, 1); winding = winding.Invert(); } else if (i != 2 && terminals[1] == peripherals[i]) { Swap(peripherals, i, 2); winding = winding.Invert(); } else if (i != 3 && t1Neighbor == peripherals[i]) { Swap(peripherals, i, 3); winding = winding.Invert(); } } var parity = INCHI_PARITY.Unknown; if (winding == TetrahedralStereo.AntiClockwise) { parity = INCHI_PARITY.Odd; } else if (winding == TetrahedralStereo.Clockwise) { parity = INCHI_PARITY.Even; } else { throw new CDKException("Unknown extended tetrahedral chirality"); } NInchiStereo0D jniStereo = new NInchiStereo0D(atomMap[extendedTetrahedral.Focus], atomMap[peripherals[0]], atomMap[peripherals[1]], atomMap[peripherals[2]], atomMap[peripherals[3]], INCHI_STEREOTYPE.Allene, parity); Input.Stereos.Add(jniStereo); } } try { Output = NInchiWrapper.GetInchi(Input); } catch (NInchiException jie) { throw new CDKException("Failed to generate InChI: " + jie.Message, jie); } }
/// <summary> /// Creates a ligancy for chirality around a single chiral atom, where the involved /// atoms are identified by there index in the <see cref="IAtomContainer"/>. For the four ligand /// atoms, <see cref="Hydrogen"/> can be passed as index, which will indicate the presence of /// an implicit hydrogen, not explicitly present in the chemical graph of the /// given <paramref name="container"/>. /// </summary> /// <param name="container"><see cref="IAtomContainer"/> for which the returned <see cref="ILigand"/>s are defined</param> /// <param name="chiralAtom">int pointing to the <see cref="IAtom"/> index of the chiral atom</param> /// <param name="ligand1">int pointing to the <see cref="IAtom"/> index of the first <see cref="ILigand"/></param> /// <param name="ligand2">int pointing to the <see cref="IAtom"/> index of the second <see cref="ILigand"/></param> /// <param name="ligand3">int pointing to the <see cref="IAtom"/> index of the third <see cref="ILigand"/></param> /// <param name="ligand4">int pointing to the <see cref="IAtom"/> index of the fourth <see cref="ILigand"/></param> /// <param name="stereo"><see cref="TetrahedralStereo"/> for the chirality</param> /// <returns>the created <see cref="LigancyFourChirality"/></returns> public static LigancyFourChirality DefineLigancyFourChirality(IAtomContainer container, int chiralAtom, int ligand1, int ligand2, int ligand3, int ligand4, TetrahedralStereo stereo) { var atomIndices = new int[] { ligand1, ligand2, ligand3, ligand4 }; var visitedAtoms = new VisitedAtoms(); var ligands = new ILigand[4]; for (int i = 0; i < 4; i++) { ligands[i] = DefineLigand(container, visitedAtoms, chiralAtom, atomIndices[i]); } return(new LigancyFourChirality(container.Atoms[chiralAtom], ligands, stereo)); }
/// <summary> /// Create an extended tetrahedral stereo element for the provided 'focus' /// and 'peripherals' in the given 'winding'. See class documentation an /// annotated storage description. /// </summary> /// <param name="focus">the central cumulated atom</param> /// <param name="peripherals">atoms attached to the terminal atoms</param> /// <param name="winding">the configuration</param> public ExtendedTetrahedral(IAtom focus, IEnumerable <IAtom> peripherals, TetrahedralStereo winding) : this(focus, peripherals, winding.ToConfiguration()) { }
public WoundProjection(Projection projection, TetrahedralStereo winding) { this.Projection = projection; this.Winding = winding; }
public TetrahedralChirality(IAtom chiralAtom, IReadOnlyList <IAtom> ligands, TetrahedralStereo stereo) : this(chiralAtom, ligands, stereo.ToConfiguration()) { }
public static bool IsUnset(this TetrahedralStereo value) => value == Unset;