/// <summary> /// Search and place branches of a chain or ring. /// </summary> /// <param name="chain">AtomContainer if atoms in an aliphatic chain or ring system</param> private void SearchAndPlaceBranches(IAtomContainer molecule, IAtomContainer chain, AtomPlacer3D ap3d, AtomTetrahedralLigandPlacer3D atlp3d, AtomPlacer atomPlacer) { //Debug.WriteLine("****** SEARCH AND PLACE ****** Chain length: "+chain.Atoms.Count); IAtomContainer branchAtoms = molecule.Builder.NewAtomContainer(); IAtomContainer connectedAtoms = molecule.Builder.NewAtomContainer(); for (int i = 0; i < chain.Atoms.Count; i++) { var atoms = molecule.GetConnectedAtoms(chain.Atoms[i]); foreach (var atom in atoms) { if (!atom.AtomicNumber.Equals(AtomicNumbers.H) & !atom.IsPlaced & !atom.IsInRing) { connectedAtoms.Add(ap3d.GetPlacedHeavyAtoms(molecule, chain.Atoms[i])); try { SetBranchAtom(molecule, atom, chain.Atoms[i], connectedAtoms, ap3d, atlp3d); } catch (CDKException ex2) { Trace.TraceError($"SearchAndPlaceBranchERROR: Cannot find enough neighbour atoms due to {ex2.ToString()}"); throw new CDKException($"SearchAndPlaceBranchERROR: Cannot find enough neighbour atoms: {ex2.Message}", ex2); } branchAtoms.Atoms.Add(atom); connectedAtoms.RemoveAllElements(); } } }//for ac.getAtomCount PlaceLinearChains3D(molecule, branchAtoms, ap3d, atlp3d, atomPlacer); }
/// <summary> /// Layout the molecule, starts with ring systems and than aliphatic chains. /// </summary> /// <param name="ringSetMolecule">ringSystems of the molecule</param> private void LayoutMolecule(IReadOnlyList <IRingSet> ringSetMolecule, IAtomContainer molecule, AtomPlacer3D ap3d, AtomTetrahedralLigandPlacer3D atlp3d, AtomPlacer atomPlacer) { //Debug.WriteLine("****** LAYOUT MOLECULE MAIN *******"); IAtomContainer ac = null; int safetyCounter = 0; IAtom atom = null; //Place rest Chains/Atoms do { safetyCounter++; atom = ap3d.GetNextPlacedHeavyAtomWithUnplacedRingNeighbour(molecule); if (atom != null) { //Debug.WriteLine("layout RingSystem..."); var unplacedAtom = ap3d.GetUnplacedRingHeavyAtom(molecule, atom); var ringSetA = GetRingSetOfAtom(ringSetMolecule, unplacedAtom); var ringSetAContainer = RingSetManipulator.GetAllInOneContainer(ringSetA); templateHandler.MapTemplates(ringSetAContainer, ringSetAContainer.Atoms.Count); if (CheckAllRingAtomsHasCoordinates(ringSetAContainer)) { } else { throw new IOException("RingAtomLayoutError: Not every ring atom is placed! Molecule cannot be layout. Sorry"); } Vector3 firstAtomOriginalCoord = unplacedAtom.Point3D.Value; Vector3 centerPlacedMolecule = ap3d.GeometricCenterAllPlacedAtoms(molecule); SetBranchAtom(molecule, unplacedAtom, atom, ap3d.GetPlacedHeavyAtoms(molecule, atom), ap3d, atlp3d); LayoutRingSystem(firstAtomOriginalCoord, unplacedAtom, ringSetA, centerPlacedMolecule, atom, ap3d); SearchAndPlaceBranches(molecule, ringSetAContainer, ap3d, atlp3d, atomPlacer); //Debug.WriteLine("Ready layout Ring System"); ringSetA = null; unplacedAtom = null; } else { //Debug.WriteLine("layout chains..."); SetAtomsToUnVisited(molecule); atom = ap3d.GetNextPlacedHeavyAtomWithUnplacedAliphaticNeighbour(molecule); if (atom != null) { ac = atom.Builder.NewAtomContainer(); ac.Atoms.Add(atom); SearchAndPlaceBranches(molecule, ac, ap3d, atlp3d, atomPlacer); ac = null; } } } while (!ap3d.AllHeavyAtomsPlaced(molecule) || safetyCounter > molecule.Atoms.Count); }
public void RescaleBondLength_IAtom_IAtom_Point3d() { IAtom atom1 = new Atom("C") { Point3D = new Vector3(1, 1, 1), CovalentRadius = 0.2 }; IAtom atom2 = new Atom("C") { Point3D = new Vector3(2, 2, 2), CovalentRadius = 0.2 }; Vector3 newpoint = new AtomTetrahedralLigandPlacer3D().RescaleBondLength(atom1, atom2, atom2.Point3D.Value); Assert.AreEqual(0.4, Vector3.Distance(newpoint, atom1.Point3D.Value), 0.001); }
public void TestGet3DCoordinatesForLigands_IAtom_IAtomContainer_IAtomContainer_IAtom_int_Double_double() { IAtom atom1 = new Atom("C") { Point3D = new Vector3(1, 1, 1) }; IAtom atom2 = new Atom("H"); IAtom atom3 = new Atom("H"); IAtom atom4 = new Atom("H"); IAtom atom5 = new Atom("H"); IBond bond1 = new Bond(atom1, atom2); IBond bond2 = new Bond(atom1, atom3); IBond bond3 = new Bond(atom1, atom4); IBond bond4 = new Bond(atom1, atom5); IAtomContainer ac = atom1.Builder.NewAtomContainer(); ac.Atoms.Add(atom1); ac.Atoms.Add(atom2); ac.Atoms.Add(atom3); ac.Atoms.Add(atom4); ac.Atoms.Add(atom5); atom1.FormalNeighbourCount = 4; atom2.FormalNeighbourCount = 1; atom3.FormalNeighbourCount = 1; atom4.FormalNeighbourCount = 1; atom5.FormalNeighbourCount = 1; ac.Bonds.Add(bond1); ac.Bonds.Add(bond2); ac.Bonds.Add(bond3); ac.Bonds.Add(bond4); IAtomContainer noCoords = AtomTetrahedralLigandPlacer3D.GetUnsetAtomsInAtomContainer(atom1, ac); IAtomContainer withCoords = AtomTetrahedralLigandPlacer3D.GetPlacedAtomsInAtomContainer(atom1, ac); var placer = new AtomTetrahedralLigandPlacer3D(); Vector3[] newPoints = placer.Get3DCoordinatesForLigands(atom1, noCoords, withCoords, null, 4, placer.DefaultBondLengthH, -1); for (int j = 0; j < noCoords.Atoms.Count; j++) { if (newPoints[j] == null) { Assert.Fail("No coordinates generated for atom " + j); } IAtom ligand = noCoords.Atoms[j]; ligand.Point3D = newPoints[j]; } ModelBuilder3DTest.CheckAverageBondLength(ac); }
/// <summary> /// Layout all aliphatic chains with ZMatrix. /// </summary> /// <param name="startAtoms">AtomContainer of possible start atoms for a chain</param> private void PlaceLinearChains3D(IAtomContainer molecule, IAtomContainer startAtoms, AtomPlacer3D ap3d, AtomTetrahedralLigandPlacer3D atlp3d, AtomPlacer atomPlacer) { //Debug.WriteLine("****** PLACE LINEAR CHAINS ******"); IAtom dihPlacedAtom = null; IAtom thirdPlacedAtom = null; IAtomContainer longestUnplacedChain = molecule.Builder.NewAtomContainer(); if (startAtoms.Atoms.Count == 0) { //no branch points ->linear chain //Debug.WriteLine("------ LINEAR CHAIN - FINISH ------"); } else { for (int i = 0; i < startAtoms.Atoms.Count; i++) { //Debug.WriteLine("FOUND BRANCHED ALKAN"); //Debug.WriteLine("Atom NOT NULL:" + molecule.Atoms.IndexOf(startAtoms.GetAtomAt(i))); thirdPlacedAtom = ap3d.GetPlacedHeavyAtom(molecule, startAtoms.Atoms[i]); dihPlacedAtom = ap3d.GetPlacedHeavyAtom(molecule, thirdPlacedAtom, startAtoms.Atoms[i]); longestUnplacedChain.Atoms.Add(dihPlacedAtom); longestUnplacedChain.Atoms.Add(thirdPlacedAtom); longestUnplacedChain.Atoms.Add(startAtoms.Atoms[i]); longestUnplacedChain.Add(AtomPlacer.GetLongestUnplacedChain(molecule, startAtoms.Atoms[i])); SetAtomsToUnVisited(molecule); if (longestUnplacedChain.Atoms.Count < 4) { //di,third,sec //Debug.WriteLine("------ Single BRANCH METHYLTYP ------"); //break; } else { //Debug.WriteLine("LongestUnchainLength:"+longestUnplacedChain.Atoms.Count); ap3d.PlaceAliphaticHeavyChain(molecule, longestUnplacedChain); ap3d.ZMatrixChainToCartesian(molecule, true); SearchAndPlaceBranches(molecule, longestUnplacedChain, ap3d, atlp3d, atomPlacer); } longestUnplacedChain.RemoveAllElements(); }//for } //Debug.WriteLine("****** HANDLE ALIPHATICS END ******"); }
/// <summary> /// Takes the given Z Matrix coordinates and converts them to cartesian coordinates. /// The first Atom end up in the origin, the second on on the x axis, and the third /// one in the XY plane. The rest is added by applying the Zmatrix distances, angles /// and dihedrals. Assign coordinates directly to the atoms. /// </summary> /// <param name="molecule">the molecule to be placed in 3D</param> /// <param name="flagBranched">marks branched chain</param> // @author: egonw,cho public virtual void ZMatrixChainToCartesian(IAtomContainer molecule, bool flagBranched) { Vector3?result = null; for (int index = 0; index < distances.Length; index++) { if (index == 0) { result = new Vector3(0d, 0d, 0d); } else if (index == 1) { result = new Vector3(distances[1], 0d, 0d); } else if (index == 2) { result = new Vector3(-Math.Cos((angles[2] / 180) * Math.PI) * distances[2] + distances[1], Math.Sin((angles[2] / 180) * Math.PI) * distances[2], 0); } else { var cd = molecule.Atoms[thirdAtoms[index]].Point3D.Value - molecule.Atoms[secondAtoms[index]].Point3D.Value; var bc = molecule.Atoms[secondAtoms[index]].Point3D.Value - molecule.Atoms[firstAtoms[index - 3]].Point3D.Value; var n1 = Vector3.Cross(cd, bc); n1 = Vector3.Normalize(n1); Vector3 n2; if (index == 3 && flagBranched) { n2 = AtomTetrahedralLigandPlacer3D.Rotate(n1, bc, DIHEDRAL_BRANCHED_CHAIN); } else { n2 = AtomTetrahedralLigandPlacer3D.Rotate(n1, bc, dihedrals[index]); } n2 = Vector3.Normalize(n2); Vector3 ba = new Vector3(); if (index == 3 && flagBranched) { ba = AtomTetrahedralLigandPlacer3D.Rotate(cd, n2, (-angles[index] / 180) * Math.PI); ba = AtomTetrahedralLigandPlacer3D.Rotate(ba, cd, (-angles[index] / 180) * Math.PI); } else { ba = AtomTetrahedralLigandPlacer3D.Rotate(cd, n2, (-angles[index] / 180) * Math.PI); } ba = Vector3.Normalize(ba); Vector3 ban = ba; ban *= distances[index]; result = molecule.Atoms[firstAtoms[index - 1]].Point3D.Value + ban; } IAtom atom = molecule.Atoms[firstAtoms[index]]; if ((atom.Point3D == null || !atom.IsPlaced) && !atom.IsInRing && IsHeavyAtom(atom)) { atom.Point3D = result; atom.IsPlaced = true; } } }
/// <summary> /// Sets a branch atom to a ring or aliphatic chain. /// </summary> /// <param name="unplacedAtom">The new branchAtom</param> /// <param name="atomA">placed atom to which the unplaced atom is connected</param> /// <param name="atomNeighbours">placed atomNeighbours of atomA</param> private static void SetBranchAtom(IAtomContainer molecule, IAtom unplacedAtom, IAtom atomA, IAtomContainer atomNeighbours, AtomPlacer3D ap3d, AtomTetrahedralLigandPlacer3D atlp3d) { //Debug.WriteLine("****** SET Branch Atom ****** >"+molecule.Atoms.IndexOf(unplacedAtom)); IAtomContainer noCoords = molecule.Builder.NewAtomContainer(); noCoords.Atoms.Add(unplacedAtom); Vector3 centerPlacedMolecule = ap3d.GeometricCenterAllPlacedAtoms(molecule); IAtom atomB = atomNeighbours.Atoms[0]; string atypeNameA = atomA.AtomTypeName; string atypeNameB = atomB.AtomTypeName; string atypeNameUnplaced = unplacedAtom.AtomTypeName; double length = ap3d.GetBondLengthValue(atypeNameA, atypeNameUnplaced); double angle = (ap3d.GetAngleValue(atypeNameB, atypeNameA, atypeNameUnplaced)) * Math.PI / 180; // Console.Out.WriteLine("A:"+atomA.Symbol+" "+atomA.AtomTypeName+ // " B:"+atomB.Symbol+" "+atomB.AtomTypeName // +" unplaced Atom:" // +unplacedAtom.AtomTypeName+" BL:"+length+" Angle:"+angle // +" FormalNeighbour:" // +atomA.FormalNeighbourCount+" HYB:"+atomA.getFlag // (CDKConstants.HYBRIDIZATION_SP2) // +" #Neigbhours:"+atomNeighbours.Atoms.Count); IAtom atomC = ap3d.GetPlacedHeavyAtom(molecule, atomB, atomA); Vector3[] branchPoints = atlp3d.Get3DCoordinatesForLigands(atomA, noCoords, atomNeighbours, atomC, (atomA.FormalNeighbourCount.Value - atomNeighbours.Atoms.Count), length, angle); double distance = 0; int farthestPoint = 0; for (int i = 0; i < branchPoints.Length; i++) { if (Math.Abs(Vector3.Distance(branchPoints[i], centerPlacedMolecule)) > Math.Abs(distance)) { distance = Vector3.Distance(branchPoints[i], centerPlacedMolecule); farthestPoint = i; } } int stereo = -1; IBond unplacedBond = molecule.GetBond(atomA, unplacedAtom); if (atomA.StereoParity != 0 || (unplacedBond.Stereo == BondStereo.Up || unplacedBond.Stereo == BondStereo.Down) && molecule.GetMaximumBondOrder(atomA) == BondOrder.Single) { if (atomNeighbours.Atoms.Count > 1) { stereo = AtomTetrahedralLigandPlacer3D.MakeStereocenter(atomA.Point3D.Value, molecule.GetBond(atomA, unplacedAtom), (atomNeighbours.Atoms[0]).Point3D.Value, (atomNeighbours.Atoms[1]).Point3D.Value, branchPoints); } } if (stereo != -1) { farthestPoint = stereo; } unplacedAtom.Point3D = branchPoints[farthestPoint]; unplacedAtom.IsPlaced = true; }
/// <summary> /// Layout the ring system, rotate and translate the template. /// </summary> /// <param name="originalCoord">coordinates of the placedRingAtom from the template</param> /// <param name="placedRingAtom">placedRingAtom</param> /// <param name="ringSet">ring system which placedRingAtom is part of</param> /// <param name="centerPlacedMolecule">the geometric center of the already placed molecule</param> /// <param name="atomB">placed neighbour atom of placedRingAtom</param> private static void LayoutRingSystem(Vector3 originalCoord, IAtom placedRingAtom, IRingSet ringSet, Vector3 centerPlacedMolecule, IAtom atomB, AtomPlacer3D ap3d) { //Debug.WriteLine("****** Layout ring System ******");Console.Out.WriteLine(">around atom:"+molecule.Atoms.IndexOf(placedRingAtom)); IAtomContainer ac = RingSetManipulator.GetAllInOneContainer(ringSet); Vector3 newCoord = placedRingAtom.Point3D.Value; Vector3 axis = new Vector3(atomB.Point3D.Value.X - newCoord.X, atomB.Point3D.Value.Y - newCoord.Y, atomB.Point3D.Value.Z - newCoord.Z); TranslateStructure(originalCoord, newCoord, ac); //Rotate Ringsystem to farthest possible point Vector3 startAtomVector = new Vector3(newCoord.X - atomB.Point3D.Value.X, newCoord.Y - atomB.Point3D.Value.Y, newCoord.Z - atomB.Point3D.Value.Z); IAtom farthestAtom = ap3d.GetFarthestAtom(placedRingAtom.Point3D.Value, ac); Vector3 farthestAtomVector = new Vector3(farthestAtom.Point3D.Value.X - newCoord.X, farthestAtom.Point3D.Value.Y - newCoord.Y, farthestAtom.Point3D.Value.Z - newCoord.Z); Vector3 n1 = Vector3.Cross(axis, farthestAtomVector); n1 = Vector3.Normalize(n1); double lengthFarthestAtomVector = farthestAtomVector.Length(); Vector3 farthestVector = startAtomVector; farthestVector = Vector3.Normalize(farthestVector); farthestVector *= startAtomVector.Length() + lengthFarthestAtomVector; double dotProduct = Vector3.Dot(farthestAtomVector, farthestVector); double angle = Math.Acos(dotProduct / (farthestAtomVector.Length() * farthestVector.Length())); Vector3 ringCenter = new Vector3(); for (int i = 0; i < ac.Atoms.Count; i++) { if (!(ac.Atoms[i].IsPlaced)) { ringCenter.X = (ac.Atoms[i].Point3D).Value.X - newCoord.X; ringCenter.Y = (ac.Atoms[i].Point3D).Value.Y - newCoord.Y; ringCenter.Z = (ac.Atoms[i].Point3D).Value.Z - newCoord.Z; ringCenter = AtomTetrahedralLigandPlacer3D.Rotate(ringCenter, n1, angle); ac.Atoms[i].Point3D = new Vector3(ringCenter.X + newCoord.X, ringCenter.Y + newCoord.Y, ringCenter.Z + newCoord.Z); //ac.GetAtomAt(i).IsPlaced = true; } } //Rotate Ring so that geometric center is max from placed center //Debug.WriteLine("Rotate RINGSYSTEM"); Vector3 pointRingCenter = GeometryUtil.Get3DCenter(ac); double distance = 0; double rotAngleMax = 0; angle = 1 / 180 * Math.PI; ringCenter = new Vector3(pointRingCenter.X, pointRingCenter.Y, pointRingCenter.Z); ringCenter.X = ringCenter.X - newCoord.X; ringCenter.Y = ringCenter.Y - newCoord.Y; ringCenter.Z = ringCenter.Z - newCoord.Z; for (int i = 1; i < 360; i++) { ringCenter = AtomTetrahedralLigandPlacer3D.Rotate(ringCenter, axis, angle); if (Vector3.Distance(centerPlacedMolecule, new Vector3(ringCenter.X, ringCenter.Y, ringCenter.Z)) > distance) { rotAngleMax = i; distance = Vector3.Distance(centerPlacedMolecule, new Vector3(ringCenter.X, ringCenter.Y, ringCenter.Z)); } } //rotate ring around axis with best angle rotAngleMax = (rotAngleMax / 180) * Math.PI; for (int i = 0; i < ac.Atoms.Count; i++) { if (!(ac.Atoms[i].IsPlaced)) { ringCenter.X = (ac.Atoms[i].Point3D).Value.X; ringCenter.Y = (ac.Atoms[i].Point3D).Value.Y; ringCenter.Z = (ac.Atoms[i].Point3D).Value.Z; ringCenter = AtomTetrahedralLigandPlacer3D.Rotate(ringCenter, axis, rotAngleMax); ac.Atoms[i].Point3D = new Vector3(ringCenter.X, ringCenter.Y, ringCenter.Z); ac.Atoms[i].IsPlaced = true; } } }
/// <summary> /// Generate 3D coordinates with force field information. /// </summary> public IAtomContainer Generate3DCoordinates(IAtomContainer molecule, bool clone) { var originalAtomTypeNames = molecule.Atoms.Select(n => n.AtomTypeName).ToArray(); Debug.WriteLine("******** GENERATE COORDINATES ********"); foreach (var atom in molecule.Atoms) { atom.IsPlaced = false; atom.IsVisited = false; } //CHECK FOR CONNECTIVITY! Debug.WriteLine($"#atoms>{molecule.Atoms.Count}"); if (!ConnectivityChecker.IsConnected(molecule)) { throw new CDKException("Molecule is NOT connected, could not layout."); } // setup helper classes AtomPlacer atomPlacer = new AtomPlacer(); AtomPlacer3D ap3d = new AtomPlacer3D(parameterSet); AtomTetrahedralLigandPlacer3D atlp3d = new AtomTetrahedralLigandPlacer3D(parameterSet); if (clone) { molecule = (IAtomContainer)molecule.Clone(); } atomPlacer.Molecule = molecule; if (ap3d.NumberOfUnplacedHeavyAtoms(molecule) == 1) { Debug.WriteLine("Only one Heavy Atom"); ap3d.GetUnplacedHeavyAtom(molecule).Point3D = new Vector3(0.0, 0.0, 0.0); try { atlp3d.Add3DCoordinatesForSinglyBondedLigands(molecule); } catch (CDKException ex3) { Trace.TraceError($"PlaceSubstitutensERROR: Cannot place substitutents due to:{ex3.Message}"); Debug.WriteLine(ex3); throw new CDKException("PlaceSubstitutensERROR: Cannot place substitutents due to:" + ex3.Message, ex3); } return(molecule); } //Assing Atoms to Rings,Aliphatic and Atomtype IRingSet ringSetMolecule = ffc.AssignAtomTyps(molecule); IRingSet largestRingSet = null; int numberOfRingAtoms = 0; IReadOnlyList <IRingSet> ringSystems = null; if (ringSetMolecule.Count > 0) { if (templateHandler == null) { throw new CDKException("You are trying to generate coordinates for a molecule with rings, but you have no template handler set. Please do SetTemplateHandler() before generation!"); } ringSystems = RingPartitioner.PartitionRings(ringSetMolecule); largestRingSet = RingSetManipulator.GetLargestRingSet(ringSystems); IAtomContainer largestRingSetContainer = RingSetManipulator.GetAllInOneContainer(largestRingSet); numberOfRingAtoms = largestRingSetContainer.Atoms.Count; templateHandler.MapTemplates(largestRingSetContainer, numberOfRingAtoms); if (!CheckAllRingAtomsHasCoordinates(largestRingSetContainer)) { throw new CDKException("RingAtomLayoutError: Not every ring atom is placed! Molecule cannot be layout."); } SetAtomsToPlace(largestRingSetContainer); SearchAndPlaceBranches(molecule, largestRingSetContainer, ap3d, atlp3d, atomPlacer); largestRingSet = null; } else { //Debug.WriteLine("****** Start of handling aliphatic molecule ******"); IAtomContainer ac = AtomPlacer.GetInitialLongestChain(molecule); SetAtomsToUnVisited(molecule); SetAtomsToUnplaced(molecule); ap3d.PlaceAliphaticHeavyChain(molecule, ac); //ZMatrixApproach ap3d.ZMatrixChainToCartesian(molecule, false); SearchAndPlaceBranches(molecule, ac, ap3d, atlp3d, atomPlacer); } LayoutMolecule(ringSystems, molecule, ap3d, atlp3d, atomPlacer); //Debug.WriteLine("******* PLACE SUBSTITUENTS ******"); try { atlp3d.Add3DCoordinatesForSinglyBondedLigands(molecule); } catch (CDKException ex3) { Trace.TraceError($"PlaceSubstitutensERROR: Cannot place substitutents due to:{ex3.Message}"); Debug.WriteLine(ex3); throw new CDKException("PlaceSubstitutensERROR: Cannot place substitutents due to:" + ex3.Message, ex3); } // restore the original atom type names for (int i = 0; i < originalAtomTypeNames.Length; i++) { molecule.Atoms[i].AtomTypeName = originalAtomTypeNames[i]; } return(molecule); }