/* $Revision$ $Author$ $Date$ * * Copyright (C) 2005-2007 Christian Hoppe <chhoppe@users.sf.net> * * Contact: cdk-devel@list.sourceforge.net * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; either version 2.1 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. */ package org.openscience.cdk.modeling.builder3d; import java.util.Iterator; import java.util.List; import java.util.Map; import javax.vecmath.Point2d; import javax.vecmath.Point3d; import javax.vecmath.Vector3d; import org.openscience.cdk.AtomContainer; import org.openscience.cdk.CDKConstants; import org.openscience.cdk.exception.CDKException; import org.openscience.cdk.geometry.GeometryTools; import org.openscience.cdk.interfaces.IAtom; import org.openscience.cdk.interfaces.IAtomContainer; import org.openscience.cdk.interfaces.IBond; /** * Place aliphatic chains with Z matrix method. * * @author chhoppe * @cdk.keyword AtomPlacer3D * @cdk.created 2004-10-8 * @cdk.module builder3d * @cdk.githash */ public class AtomPlacer3D { private Map<Object,List> pSet = null; private double[] distances; private int[] first_atoms = null; private double[] angles = null; private int[] second_atoms = null; private double[] dihedrals = null; private int[] third_atoms = null; private final static double DIHEDRAL_EXTENDED_CHAIN = (180.0 / 180) * Math.PI; private final static double DIHEDRAL_BRANCHED_CHAIN = 0.0; private final static double DEFAULT_BOND_LENGTH = 1.5; private final static double DEFAULT_SP3_ANGLE = 109.471; private final static double DEFAULT_SP2_ANGLE = 120.000; private final static double DEFAULT_SP_ANGLE = 180.000; AtomPlacer3D(){} /** * Initialize the atomPlacer class * * @param parameterSet Force Field parameter as Hashtable */ public void initilize(Map parameterSet) { pSet = parameterSet; } /** * Count and find first heavy atom(s) (non Hydrogens) in a chain * * @param chain chain to be searched * @return the atom number of the first heavy atom the number of heavy atoms in the chain */ public int[] findHeavyAtomsInChain(IAtomContainer molecule, IAtomContainer chain) { int[] heavy = {-1, -1}; int hc = 0; for (int i = 0; i < chain.getAtomCount(); i++) { if (!(chain.getAtom(i).getSymbol()).equals("H")) { if (heavy[0] < 0) { heavy[0] = molecule.getAtomNumber(chain.getAtom(i)); } hc++; } } heavy[1] = hc; return heavy; } /** * Mark all atoms in chain as placed (CDKConstant ISPLACED) * * @param ac chain * @return chain all atoms marked as placed */ public IAtomContainer markPlaced(IAtomContainer ac) { for (int i = 0; i < ac.getAtomCount(); i++) { ac.getAtom(i).setFlag(CDKConstants.ISPLACED, true); } return ac; } /** * Method assigns 3Dcoordinates to the heavy atoms in an aliphatic chain * * @param chain the atoms to be assigned, must be connected */ public void placeAliphaticHeavyChain(IAtomContainer molecule, IAtomContainer chain) throws CDKException{ //logger.debug("******** Place aliphatic Chain *********"); int[] first = new int[2]; int counter = 1; int nextAtomNr = 0; String ID1 = ""; String ID2 = ""; String ID3 = ""; first = findHeavyAtomsInChain(molecule,chain); distances = new double[first[1]]; first_atoms = new int[first[1]]; angles = new double[first[1]]; second_atoms = new int[first[1]]; dihedrals = new double[first[1]]; third_atoms = new int[first[1]]; first_atoms[0] = first[0]; molecule.getAtom(first_atoms[0]).setFlag(CDKConstants.VISITED, true); int hybridisation = 0; for (int i = 0; i < chain.getAtomCount(); i++) { if (!(chain.getAtom(i).getSymbol()).equals("H") & !chain.getAtom(i).getFlag(CDKConstants.VISITED)) { //logger.debug("Counter:" + counter); nextAtomNr = molecule.getAtomNumber(chain.getAtom(i)); ID2 = molecule.getAtom(first_atoms[counter - 1]).getAtomTypeName(); ID1 = molecule.getAtom(nextAtomNr).getAtomTypeName(); distances[counter] = getBondLengthValue(ID1, ID2); //logger.debug(" Distance:" + distances[counter]); first_atoms[counter] = nextAtomNr; second_atoms[counter] = first_atoms[counter - 1]; if (counter > 1) { ID3 = molecule.getAtom(first_atoms[counter - 2]).getAtomTypeName(); hybridisation = getHybridisationState(molecule.getAtom(first_atoms[counter - 1])); angles[counter] = getAngleValue(ID1, ID2, ID3); //Check if sp,sp2 if (angles[counter] == -1) { if (hybridisation == 3) { angles[counter] = DEFAULT_SP3_ANGLE; } else if (hybridisation == 2) { angles[counter] = DEFAULT_SP2_ANGLE; } else if (hybridisation == 1) { angles[counter] = DEFAULT_SP_ANGLE; } } third_atoms[counter] = first_atoms[counter - 2]; //logger.debug(" Angle:" + angles[counter]); } else { angles[counter] = -1; third_atoms[counter] = -1; } if (counter > 2) { //double bond try{ if (getDoubleBondConfiguration2D( molecule.getBond(molecule.getAtom(first_atoms[counter-1]),molecule.getAtom(first_atoms[counter-2])), (molecule.getAtom(first_atoms[counter])).getPoint2d(),(molecule.getAtom(first_atoms[counter-1])).getPoint2d(), (molecule.getAtom(first_atoms[counter-2])).getPoint2d(),(molecule.getAtom(first_atoms[counter-3])).getPoint2d()) ==5){ dihedrals[counter] = DIHEDRAL_BRANCHED_CHAIN; }else{ dihedrals[counter] = DIHEDRAL_EXTENDED_CHAIN;} }catch(CDKException ex1){ dihedrals[counter] = DIHEDRAL_EXTENDED_CHAIN; } } else { dihedrals[counter] = -1; } counter++; } } } /** * 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. * * @param flag_branched marks branched chain * author: egonw,cho */ public void zmatrixChainToCartesian(IAtomContainer molecule, boolean flag_branched) { Point3d result = null; for (int index = 0; index < distances.length; index++) { if (index == 0) { result = new Point3d(0d, 0d, 0d); } else if (index == 1) { result = new Point3d(distances[1], 0d, 0d); } else if (index == 2) { result = new Point3d(-Math.cos((angles[2] / 180) * Math.PI) * distances[2] + distances[1], Math.sin((angles[2] / 180) * Math.PI) * distances[2], 0d); } else { Vector3d cd = new Vector3d(); cd.sub((molecule.getAtom(third_atoms[index])).getPoint3d(), (molecule.getAtom(second_atoms[index])).getPoint3d()); Vector3d bc = new Vector3d(); bc.sub(molecule.getAtom(second_atoms[index]).getPoint3d(), molecule.getAtom(first_atoms[index - 3]).getPoint3d()); Vector3d n1 = new Vector3d(); n1.cross(cd, bc); n1.normalize(); Vector3d n2 = null; if (index == 3 & flag_branched) { n2 = AtomTetrahedralLigandPlacer3D.rotate(n1, bc, DIHEDRAL_BRANCHED_CHAIN); } else { n2 = AtomTetrahedralLigandPlacer3D.rotate(n1, bc, dihedrals[index]); } n2.normalize(); Vector3d ba = new Vector3d(); if (index == 3 & flag_branched) { 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.normalize(); Vector3d ban = new Vector3d(ba); ban.scale(distances[index]); result = new Point3d(); result.add(molecule.getAtom(first_atoms[index - 1]).getPoint3d(), ban); } if ((molecule.getAtom(first_atoms[index]).getPoint3d() == null || !(molecule.getAtom(first_atoms[index])).getFlag(CDKConstants.ISPLACED)) && !(molecule.getAtom(first_atoms[index])).getFlag(CDKConstants.ISINRING) && !(molecule.getAtom(first_atoms[index])).getSymbol().equals("H")) { molecule.getAtom(first_atoms[index]).setPoint3d(result); molecule.getAtom(first_atoms[index]).setFlag(CDKConstants.ISPLACED, true); } } } /** * Gets the hybridisationState of an atom * *@param atom1 atom *@return The hybridisationState value (sp=1;sp2=2;sp3=3) */ private int getHybridisationState(IAtom atom1) { IBond.Order maxBondOrder = atom1.getMaxBondOrder(); // if (atom1.getFormalNeighbourCount() == 1 || maxBondOrder > 4) { if (atom1.getFormalNeighbourCount() == 1) { // WTF?? } else if (atom1.getFormalNeighbourCount() == 2 || maxBondOrder == IBond.Order.TRIPLE) { //sp return 1; } else if (atom1.getFormalNeighbourCount() == 3 || (maxBondOrder == IBond.Order.DOUBLE)) { //sp2 return 2; } else { //sp3 return 3; } return -1; } /** * Gets the doubleBondConfiguration2D attribute of the AtomPlacer3D object * *@param bond the double bond *@param a coordinates (Point2d) of atom1 connected to bond *@param b coordinates (Point2d) of atom2 connected to bond *@param c coordinates (Point2d) of atom3 connected to bond *@param d coordinates (Point2d) of atom4 connected to bond *@return The doubleBondConfiguration2D value */ private int getDoubleBondConfiguration2D(IBond bond,Point2d a, Point2d b,Point2d c,Point2d d) throws CDKException{ if (bond.getOrder() != IBond.Order.DOUBLE){ return 0; } Point2d cb=new Point2d(c.x-b.x,c.y-b.y); Point2d xT=new Point2d(cb.x-1,cb.y); a.y=a.y-b.y-xT.y; d.y=d.y-b.y-xT.y; if ((a.y>0 && d.y>0)||(a.y<0 && d.y<0)){ return 5; }else {return 6;} } /** * Gets the distanceValue attribute of the parameter set * * @param id1 atom1 id * @param id2 atom2 id * @return The distanceValue value from the force field parameter set */ public double getBondLengthValue(String id1, String id2){ String dkey = ""; if (pSet.containsKey(("bond" + id1 + ";" + id2))) { dkey="bond" + id1 + ";" + id2; }else if (pSet.containsKey(("bond" + id2 + ";" + id1))) { dkey = "bond" + id2 + ";" + id1; } else { System.out.println("KEYError:Unknown distance key in pSet: " + id2 + " ;" + id1+" take default bon length:"+DEFAULT_BOND_LENGTH); return DEFAULT_BOND_LENGTH; } return ((Double) (((List) pSet.get(dkey)).get(0))).doubleValue(); } /** * Gets the angleKey attribute of the AtomPlacer3D object * * @param id1 Description of the Parameter * @param id2 Description of the Parameter * @param id3 Description of the Parameter * @return The angleKey value */ public double getAngleValue(String id1, String id2, String id3) { String akey = ""; if (pSet.containsKey(("angle" + id1 + ";" + id2 + ";" + id3))) { akey = "angle" + id1 + ";" + id2 + ";" + id3; } else if (pSet.containsKey(("angle" + id3 + ";" + id2 + ";" + id1))) { akey = "angle" + id3 + ";" + id2 + ";" + id1; } else if (pSet.containsKey(("angle" + id2 + ";" + id1 + ";" + id3))) { akey = "angle" + id2 + ";" + id1 + ";" + id3; } else if (pSet.containsKey(("angle" + id1 + ";" + id3 + ";" + id2))) { akey = "angle" + id1 + ";" + id3 + ";" + id2; } else if (pSet.containsKey(("angle" + id3 + ";" + id1 + ";" + id2))) { akey = "angle" + id3 + ";" + id1 + ";" + id2; } else if (pSet.containsKey(("angle" + id2 + ";" + id3 + ";" + id1))) { akey = "angle" + id2 + ";" + id3 + ";" + id1; } else { //logger.debug("KEYErrorAngle:Unknown angle key in pSet: " +id2 + " ; " + id3 + " ; " + id1+" take default angle:"+DEFAULT_ANGLE); return -1; } return ((Double) (((List) pSet.get(akey)).get(0))).doubleValue(); } /** * Gets the nextUnplacedHeavyAtomWithAliphaticPlacedNeighbour from an atom container or molecule * * @return The nextUnplacedHeavyAtomWithAliphaticPlacedNeighbour value * author: steinbeck,cho */ public IAtom getNextUnplacedHeavyAtomWithAliphaticPlacedNeighbour(IAtomContainer molecule) { Iterator bonds = molecule.bonds().iterator(); while (bonds.hasNext()) { IBond bond = (IBond) bonds.next(); if (bond.getAtom(0).getFlag(CDKConstants.ISPLACED) & !(bond.getAtom(1).getFlag(CDKConstants.ISPLACED))) { if (bond.getAtom(1).getFlag(CDKConstants.ISALIPHATIC) & !bond.getAtom(1).getSymbol().equals("H")) { return bond.getAtom(1); } } if (bond.getAtom(1).getFlag(CDKConstants.ISPLACED) & !(bond.getAtom(0).getFlag(CDKConstants.ISPLACED))) { if (bond.getAtom(0).getFlag(CDKConstants.ISALIPHATIC) & !bond.getAtom(0).getSymbol().equals("H")) { return bond.getAtom(0); } } } return null; } /** * Gets the nextPlacedHeavyAtomWithAliphaticPlacedNeigbor from an atom container or molecule * * @return The nextUnplacedHeavyAtomWithUnplacedAliphaticNeigbor * author: steinbeck,cho */ public IAtom getNextPlacedHeavyAtomWithUnplacedAliphaticNeighbour(IAtomContainer molecule) { Iterator bonds = molecule.bonds().iterator(); while (bonds.hasNext()) { IBond bond = (IBond) bonds.next(); IAtom atom0 = bond.getAtom(0); IAtom atom1 = bond.getAtom(1); if (atom0.getFlag(CDKConstants.ISPLACED) & !(atom1.getFlag(CDKConstants.ISPLACED))) { if (atom1.getFlag(CDKConstants.ISALIPHATIC) & !atom0.getSymbol().equals("H") & !atom1.getSymbol().equals("H")) { return atom0; } } if (atom1.getFlag(CDKConstants.ISPLACED) & !(atom0.getFlag(CDKConstants.ISPLACED))) { if (atom0.getFlag(CDKConstants.ISALIPHATIC) & !atom1.getSymbol().equals("H") & !atom0.getSymbol().equals("H")) { return atom1; } } } return null; } /** * Gets the nextPlacedHeavyAtomWithUnplacedRingNeighbour attribute of the AtomPlacer3D object * * @return The nextPlacedHeavyAtomWithUnplacedRingNeighbour value */ public IAtom getNextPlacedHeavyAtomWithUnplacedRingNeighbour(IAtomContainer molecule) { // IBond[] bonds = molecule.getBonds(); Iterator bonds = molecule.bonds().iterator(); while (bonds.hasNext()) { IBond bond = (IBond) bonds.next(); IAtom atom0 = bond.getAtom(0); IAtom atom1 = bond.getAtom(1); if (atom0.getFlag(CDKConstants.ISPLACED) & !(atom1.getFlag(CDKConstants.ISPLACED))) { if (atom1.getFlag(CDKConstants.ISINRING) & !atom0.getSymbol().equals("H") & !atom1.getSymbol().equals("H")) { return atom0; } } if (atom1.getFlag(CDKConstants.ISPLACED) & !(atom0.getFlag(CDKConstants.ISPLACED))) { if (atom0.getFlag(CDKConstants.ISINRING) & !atom1.getSymbol().equals("H") & !atom0.getSymbol().equals("H")) { return atom1; } } } return null; } /** * Gets the farthestAtom attribute of the AtomPlacer3D object * * @param refAtomPoint Description of the Parameter * @param ac Description of the Parameter * @return The farthestAtom value */ public IAtom getFarthestAtom(Point3d refAtomPoint, IAtomContainer ac) { double distance = 0; IAtom atom = null; for (int i = 0; i < ac.getAtomCount(); i++) { if (ac.getAtom(i).getPoint3d() != null) { if (Math.abs(refAtomPoint.distance(ac.getAtom(i).getPoint3d())) > distance) { atom = ac.getAtom(i); distance = Math.abs(refAtomPoint.distance(ac.getAtom(i).getPoint3d())); } } } return atom; } /** * Gets the unplacedRingHeavyAtom attribute of the AtomPlacer3D object * * @param atom Description of the Parameter * @return The unplacedRingHeavyAtom value */ public IAtom getUnplacedRingHeavyAtom(IAtomContainer molecule, IAtom atom) { java.util.List<IBond> bonds = molecule.getConnectedBondsList(atom); IAtom connectedAtom = null; for (int i = 0; i < bonds.size(); i++) { connectedAtom = ((IBond)bonds.get(i)).getConnectedAtom(atom); if (!connectedAtom.getFlag(CDKConstants.ISPLACED) && !connectedAtom.getSymbol().equals("H") && connectedAtom.getFlag(CDKConstants.ISINRING)) { return connectedAtom; } } return connectedAtom; } /** * Calculates the geometric center of all placed atoms in the atomcontainer * * @return Point3d the geometric center */ public Point3d geometricCenterAllPlacedAtoms(IAtomContainer molecule) { IAtomContainer allPlacedAtoms = getAllPlacedAtoms(molecule); return GeometryTools.get3DCenter(allPlacedAtoms); } /** * Returns a placed atom connected to a given atom * * @param atom The Atom whose placed bonding partners are to be returned * @return a placed heavy atom connected to a given atom * author: steinbeck */ public IAtom getPlacedHeavyAtom(IAtomContainer molecule, IAtom atom) { java.util.List<IBond> bonds = molecule.getConnectedBondsList(atom); for (int i = 0; i < bonds.size(); i++) { IAtom connectedAtom = ((IBond)bonds.get(i)).getConnectedAtom(atom); if (connectedAtom.getFlag(CDKConstants.ISPLACED) & !connectedAtom.getSymbol().equals("H")) { return connectedAtom; } } return null; } /** * Gets the first placed Heavy Atom around atomA which is not atomB * * @param atomA Description of the Parameter * @param atomB Description of the Parameter * @return The placedHeavyAtom value */ public IAtom getPlacedHeavyAtom(IAtomContainer molecule, IAtom atomA, IAtom atomB) { java.util.List<IBond> bonds = molecule.getConnectedBondsList(atomA); for (int i = 0; i < bonds.size(); i++) { IAtom connectedAtom = ((IBond)bonds.get(i)).getConnectedAtom(atomA); if (connectedAtom.getFlag(CDKConstants.ISPLACED) && !connectedAtom.getSymbol().equals("H") && connectedAtom != atomB) { return connectedAtom; } } return null; } /** * Gets the placed Heavy Atoms connected to an atom. * * @param atom The atom the atoms must be connected to. * @return The placed heavy atoms. */ public IAtomContainer getPlacedHeavyAtoms(IAtomContainer molecule, IAtom atom) { java.util.List<IBond> bonds = molecule.getConnectedBondsList(atom); IAtomContainer connectedAtoms = molecule.getBuilder().newAtomContainer(); IAtom connectedAtom = null; for (int i = 0; i < bonds.size(); i++) { connectedAtom = ((IBond)bonds.get(i)).getConnectedAtom(atom); if (connectedAtom.getFlag(CDKConstants.ISPLACED) & !(connectedAtom.getSymbol().equals("H"))) { connectedAtoms.addAtom(connectedAtom); } } return connectedAtoms; } /** * Gets numberOfUnplacedHeavyAtoms (no Flag ISPLACED, no Hydrogens) * * @param ac AtomContainer * @return int #UnplacedAtoms */ public int numberOfUnplacedHeavyAtoms(IAtomContainer ac) { int nUnplacedHeavyAtoms=0; for (int i = 0; i < ac.getAtomCount(); i++) { if (!ac.getAtom(i).getFlag(CDKConstants.ISPLACED) && !ac.getAtom(i).equals("H")) { nUnplacedHeavyAtoms+=1; } } return nUnplacedHeavyAtoms; } /** * Gets the allPlacedAtoms attribute of the AtomPlacer3D object * * @return The allPlacedAtoms value */ private IAtomContainer getAllPlacedAtoms(IAtomContainer molecule) { IAtomContainer placedAtoms = new AtomContainer(); for (int i = 0; i < molecule.getAtomCount(); i++) { if (molecule.getAtom(i).getFlag(CDKConstants.ISPLACED)) { placedAtoms.addAtom(molecule.getAtom(i)); } } return placedAtoms; } /** * True is all the atoms in the given AtomContainer have been placed * * @param ac The AtomContainer to be searched * @return True is all the atoms in the given AtomContainer have been placed */ public boolean allHeavyAtomsPlaced(IAtomContainer ac) { for (int i = 0; i < ac.getAtomCount(); i++) { if (!ac.getAtom(i).getFlag(CDKConstants.ISPLACED) & !(ac.getAtom(i).getSymbol().equals("H"))) { return false; } } return true; } }