public void TestNewTetrahedralChirality() { IChemObjectBuilder builder = RootObject.Builder; IAtomContainer molecule = builder.NewAtomContainer(); molecule.Atoms.Add(builder.NewAtom("Cl")); molecule.Atoms.Add(builder.NewAtom("C")); molecule.Atoms.Add(builder.NewAtom("Br")); molecule.Atoms.Add(builder.NewAtom("I")); molecule.Atoms.Add(builder.NewAtom("H")); molecule.AddBond(molecule.Atoms[0], molecule.Atoms[1], BondOrder.Single); molecule.AddBond(molecule.Atoms[1], molecule.Atoms[2], BondOrder.Single); molecule.AddBond(molecule.Atoms[1], molecule.Atoms[3], BondOrder.Single); molecule.AddBond(molecule.Atoms[1], molecule.Atoms[4], BondOrder.Single); IAtom[] ligands = new IAtom[] { molecule.Atoms[4], molecule.Atoms[3], molecule.Atoms[2], molecule.Atoms[0] }; ITetrahedralChirality chirality = builder.NewTetrahedralChirality(molecule.Atoms[1], ligands, TetrahedralStereo.Clockwise); Assert.IsNotNull(chirality); Assert.AreEqual(builder, chirality.Builder); }
/// <summary> /// Gets structure from InChI, and converts InChI library data structure into an IAtomContainer. /// </summary> /// <param name="builder"></param> private void GenerateAtomContainerFromInChI(IChemObjectBuilder builder) { try { output = NInchiWrapper.GetStructureFromInchi(input); } catch (NInchiException jie) { throw new CDKException("Failed to convert InChI to molecule: " + jie.Message, jie); } //molecule = new AtomContainer(); AtomContainer = builder.NewAtomContainer(); var inchiCdkAtomMap = new Dictionary <NInchiAtom, IAtom>(); for (int i = 0; i < output.Atoms.Count; i++) { var iAt = output.Atoms[i]; var cAt = builder.NewAtom(); inchiCdkAtomMap[iAt] = cAt; cAt.Id = "a" + i; cAt.Symbol = iAt.ElementType; cAt.AtomicNumber = PeriodicTable.GetAtomicNumber(cAt.Symbol); // Ignore coordinates - all zero - unless aux info was given... but // the CDK doesn't have an API to provide that // InChI does not have unset properties so we set charge, // hydrogen count (implicit) and isotopic mass cAt.FormalCharge = iAt.Charge; cAt.ImplicitHydrogenCount = iAt.ImplicitH; var isotopicMass = iAt.IsotopicMass; if (isotopicMass != 0) { if (ISOTOPIC_SHIFT_FLAG == (isotopicMass & ISOTOPIC_SHIFT_FLAG)) { try { var massNumber = CDK.IsotopeFactory.GetMajorIsotope(cAt.AtomicNumber).MassNumber.Value; cAt.MassNumber = massNumber + (isotopicMass - ISOTOPIC_SHIFT_FLAG); } catch (IOException e) { throw new CDKException("Could not load Isotopes data", e); } } else { cAt.MassNumber = isotopicMass; } } AtomContainer.Atoms.Add(cAt); cAt = AtomContainer.Atoms[AtomContainer.Atoms.Count - 1]; for (int j = 0; j < iAt.ImplicitDeuterium; j++) { var deut = builder.NewAtom(); deut.AtomicNumber = 1; deut.Symbol = "H"; deut.MassNumber = 2; deut.ImplicitHydrogenCount = 0; AtomContainer.Atoms.Add(deut); deut = AtomContainer.Atoms[AtomContainer.Atoms.Count - 1]; var bond = builder.NewBond(cAt, deut, BondOrder.Single); AtomContainer.Bonds.Add(bond); } for (int j = 0; j < iAt.ImplicitTritium; j++) { var trit = builder.NewAtom(); trit.AtomicNumber = 1; trit.Symbol = "H"; trit.MassNumber = 3; trit.ImplicitHydrogenCount = 0; AtomContainer.Atoms.Add(trit); trit = AtomContainer.Atoms[AtomContainer.Atoms.Count - 1]; var bond = builder.NewBond(cAt, trit, BondOrder.Single); AtomContainer.Bonds.Add(bond); } } for (int i = 0; i < output.Bonds.Count; i++) { var iBo = output.Bonds[i]; var atO = inchiCdkAtomMap[iBo.OriginAtom]; var atT = inchiCdkAtomMap[iBo.TargetAtom]; var cBo = builder.NewBond(atO, atT); var type = iBo.BondType; switch (type) { case INCHI_BOND_TYPE.Single: cBo.Order = BondOrder.Single; break; case INCHI_BOND_TYPE.Double: cBo.Order = BondOrder.Double; break; case INCHI_BOND_TYPE.Triple: cBo.Order = BondOrder.Triple; break; case INCHI_BOND_TYPE.Altern: cBo.IsAromatic = true; break; default: throw new CDKException("Unknown bond type: " + type); } var stereo = iBo.BondStereo; switch (stereo) { // No stereo definition case INCHI_BOND_STEREO.None: cBo.Stereo = BondStereo.None; break; // Bond ending (fat end of wedge) below the plane case INCHI_BOND_STEREO.Single1Down: cBo.Stereo = BondStereo.Down; break; // Bond ending (fat end of wedge) above the plane case INCHI_BOND_STEREO.Single1Up: cBo.Stereo = BondStereo.Up; break; // Bond starting (pointy end of wedge) below the plane case INCHI_BOND_STEREO.Single2Down: cBo.Stereo = BondStereo.DownInverted; break; // Bond starting (pointy end of wedge) above the plane case INCHI_BOND_STEREO.Single2Up: cBo.Stereo = BondStereo.UpInverted; break; // Bond with undefined stereochemistry case INCHI_BOND_STEREO.Single1Either: case INCHI_BOND_STEREO.DoubleEither: cBo.Stereo = BondStereo.None; break; } AtomContainer.Bonds.Add(cBo); } for (int i = 0; i < output.Stereos.Count; i++) { var stereo0d = output.Stereos[i]; if (stereo0d.StereoType == INCHI_STEREOTYPE.Tetrahedral || stereo0d.StereoType == INCHI_STEREOTYPE.Allene) { var central = stereo0d.CentralAtom; var neighbours = stereo0d.Neighbors; var focus = inchiCdkAtomMap[central]; var neighbors = new IAtom[] { inchiCdkAtomMap[neighbours[0]], inchiCdkAtomMap[neighbours[1]], inchiCdkAtomMap[neighbours[2]], inchiCdkAtomMap[neighbours[3]] }; TetrahedralStereo stereo; // as per JNI InChI doc even is clockwise and odd is // anti-clockwise switch (stereo0d.Parity) { case INCHI_PARITY.Odd: stereo = TetrahedralStereo.AntiClockwise; break; case INCHI_PARITY.Even: stereo = TetrahedralStereo.Clockwise; break; default: // CDK Only supports parities of + or - continue; } IStereoElement <IChemObject, IChemObject> stereoElement = null; switch (stereo0d.StereoType) { case INCHI_STEREOTYPE.Tetrahedral: stereoElement = builder.NewTetrahedralChirality(focus, neighbors, stereo); break; case INCHI_STEREOTYPE.Allene: { // The periphals (p<i>) and terminals (t<i>) are refering to // the following atoms. The focus (f) is also shown. // // p0 p2 // \ / // t0 = f = t1 // / \ // p1 p3 var peripherals = neighbors; var terminals = ExtendedTetrahedral.FindTerminalAtoms(AtomContainer, focus); // InChI always provides the terminal atoms t0 and t1 as // periphals, here we find where they are and then add in // the other explicit atom. As the InChI create hydrogens // for stereo elements, there will always we an explicit // atom that can be found - it may be optionally suppressed // later. // not much documentation on this (at all) but they appear // to always be the middle two atoms (index 1, 2) we therefore // test these first - but handle the other indices just in // case foreach (var terminal in terminals) { if (peripherals[1].Equals(terminal)) { peripherals[1] = FindOtherSinglyBonded(AtomContainer, terminal, peripherals[0]); } else if (peripherals[2].Equals(terminal)) { peripherals[2] = FindOtherSinglyBonded(AtomContainer, terminal, peripherals[3]); } else if (peripherals[0].Equals(terminal)) { peripherals[0] = FindOtherSinglyBonded(AtomContainer, terminal, peripherals[1]); } else if (peripherals[3].Equals(terminal)) { peripherals[3] = FindOtherSinglyBonded(AtomContainer, terminal, peripherals[2]); } } stereoElement = new ExtendedTetrahedral(focus, peripherals, stereo); } break; } Trace.Assert(stereoElement != null); AtomContainer.StereoElements.Add(stereoElement); } else if (stereo0d.StereoType == INCHI_STEREOTYPE.DoubleBond) { NInchiAtom[] neighbors = stereo0d.Neighbors; // from JNI InChI doc // neighbor[4] : {#X,#A,#B,#Y} in this order // X central_atom : NO_ATOM // \ X Y type : INCHI_StereoType_DoubleBond // A == B \ / // \ A == B // Y var x = inchiCdkAtomMap[neighbors[0]]; var a = inchiCdkAtomMap[neighbors[1]]; var b = inchiCdkAtomMap[neighbors[2]]; var y = inchiCdkAtomMap[neighbors[3]]; // XXX: AtomContainer is doing slow lookup var stereoBond = AtomContainer.GetBond(a, b); stereoBond.SetAtoms(new[] { a, b }); // ensure a is first atom var conformation = DoubleBondConformation.Unset; switch (stereo0d.Parity) { case INCHI_PARITY.Odd: conformation = DoubleBondConformation.Together; break; case INCHI_PARITY.Even: conformation = DoubleBondConformation.Opposite; break; } // unspecified not stored if (conformation.IsUnset()) { continue; } AtomContainer.StereoElements.Add(new DoubleBondStereochemistry(stereoBond, new IBond[] { AtomContainer.GetBond(x, a), AtomContainer.GetBond(b, y) }, conformation)); } else { // TODO - other types of atom parity - double bond, etc } } }