diff --git a/src/main/org/openscience/cdk/smsd/tools/ExtAtomContainerManipulator.java b/src/main/org/openscience/cdk/smsd/tools/ExtAtomContainerManipulator.java new file mode 100644 index 00000000000..251ca56ea42 --- /dev/null +++ b/src/main/org/openscience/cdk/smsd/tools/ExtAtomContainerManipulator.java @@ -0,0 +1,550 @@ +/** + * + * Copyright (C) 2006-2010 Syed Asad Rahman {asad@ebi.atomContainer.uk} + * + * Contact: cdk-devel@lists.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. + * All we ask is that proper credit is given for our work, which includes + * - but is not limited to - adding the above copyright notice to the beginning + * of your source code files, and to any copyright notice that you may distribute + * with programs based on this work. + * + * 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 atom 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.smsd.tools; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; + +import java.util.Map; +import javax.vecmath.Point2d; +import javax.vecmath.Point3d; + +import org.openscience.cdk.Bond; +import org.openscience.cdk.interfaces.IAtomParity; +import org.openscience.cdk.interfaces.IBond.Order; +import org.openscience.cdk.interfaces.ILonePair; +import org.openscience.cdk.CDKConstants; +import org.openscience.cdk.annotations.TestClass; +import org.openscience.cdk.annotations.TestMethod; +import org.openscience.cdk.aromaticity.CDKHueckelAromaticityDetector; +import org.openscience.cdk.atomtype.CDKAtomTypeMatcher; +import org.openscience.cdk.exception.CDKException; +import org.openscience.cdk.interfaces.IAtom; +import org.openscience.cdk.interfaces.IAtomContainer; +import org.openscience.cdk.interfaces.IAtomType; +import org.openscience.cdk.interfaces.IBond; +import org.openscience.cdk.interfaces.IMolecule; +import org.openscience.cdk.interfaces.IPseudoAtom; +import org.openscience.cdk.interfaces.IRing; +import org.openscience.cdk.interfaces.IRingSet; +import org.openscience.cdk.ringsearch.AllRingsFinder; +import org.openscience.cdk.tools.manipulator.AtomContainerManipulator; +import org.openscience.cdk.tools.manipulator.AtomTypeManipulator; +import org.openscience.cdk.tools.manipulator.RingSetManipulator; + +/** + * Class that handles some cutomised features for SMSD atomcontainers. + *

This is an extension of CDK AtomContainer. + * Some part of this code was taken from CDK source code and modified.

+ * + * @cdk.module smsd + * @cdk.githash + * @author Syed Asad Rahman + */ +@TestClass("org.openscience.cdk.smsd.tools.ExtAtomContainerManipulatorTest") +public class ExtAtomContainerManipulator extends AtomContainerManipulator { + + private static void printAtoms(IAtomContainer mol) { + System.out.print("Atom: "); + for (IAtom a : mol.atoms()) { + + System.out.print(a.getSymbol()); + System.out.print("[" + a.getFormalCharge() + "]"); + if (a.getID() != null) { + System.out.print("[" + a.getID() + "]"); + } + + } + System.out.println(); + System.out.println(); + } + + /** + * Retrurns deep copy of the molecule + * @param container + * @return deep copy of the mol + */ + @TestMethod("testMakeDeepCopy") + public static IAtomContainer makeDeepCopy(IAtomContainer container) { + + + IAtomContainer newAtomContainer = container.getBuilder().newInstance(IAtomContainer.class); +// Deep copy of the Atoms + IAtom[] atoms = copyAtoms(container, newAtomContainer); + +// Deep copy of the bonds + copyBonds(atoms, container, newAtomContainer); + +// Deep copy of the LonePairs + for (int index = 0; index < container.getLonePairCount(); index++) { + if (container.getAtom(index).getSymbol().equalsIgnoreCase("R") || container.getAtom(index).getSymbol().equalsIgnoreCase("A")) { + newAtomContainer.addLonePair(container.getBuilder().newInstance(ILonePair.class, container.getAtom(index))); + } else { + newAtomContainer.addLonePair(index); + } + } + + for (int index = 0; index < container.getSingleElectronCount(); index++) { + newAtomContainer.addSingleElectron(index); + } + newAtomContainer.setProperties(container.getProperties()); + newAtomContainer.setFlags(container.getFlags()); + newAtomContainer.setID(container.getID()); + newAtomContainer.notifyChanged(); + + return newAtomContainer; + } + + /** + * Retrurns deep copy of the molecule + * @param container + * @return deep copy of the mol + */ + @TestMethod("testMakeDeepCopy") + public static IMolecule makeDeepCopy(IMolecule container) { + + IMolecule newMolecule = container.getBuilder().newInstance(IMolecule.class); +// Deep copy of the Atoms + IAtom[] atoms = copyAtoms(container, newMolecule); + +// Deep copy of the bonds + copyBonds(atoms, container, newMolecule); + +// Deep copy of the LonePairs + for (int index = 0; index < container.getLonePairCount(); index++) { + if (container.getAtom(index).getSymbol().equalsIgnoreCase("R") || container.getAtom(index).getSymbol().equalsIgnoreCase("A")) { + newMolecule.addLonePair(container.getBuilder().newInstance(ILonePair.class, container.getAtom(index))); + } else { + newMolecule.addLonePair(index); + } + } + + for (int index = 0; index < container.getSingleElectronCount(); index++) { + newMolecule.addSingleElectron(index); + } + + newMolecule.setProperties(container.getProperties()); + newMolecule.setFlags(container.getFlags()); + newMolecule.setID(container.getID()); + newMolecule.notifyChanged(); + + return newMolecule; + } + + /** + * This function finds rings and uses aromaticity detection code to + * aromatize the molecule. + * @param mol input molecule + */ + @TestMethod("testAromatizeMolecule") + public static void aromatizeMolecule(IAtomContainer mol) { + + // need to find rings and aromaticity again since added H's + + IRingSet ringSet = null; + try { + AllRingsFinder arf = new AllRingsFinder(); + ringSet = arf.findAllRings(mol); + + // SSSRFinder s = new SSSRFinder(atomContainer); + // srs = s.findEssentialRings(); + + } catch (Exception e) { + e.printStackTrace(); + } + + try { + // figure out which atoms are in aromatic rings: +// printAtoms(atomContainer); + ExtAtomContainerManipulator.percieveAtomTypesAndConfigureAtoms(mol); +// printAtoms(atomContainer); + CDKHueckelAromaticityDetector.detectAromaticity(mol); +// printAtoms(atomContainer); + // figure out which rings are aromatic: + RingSetManipulator.markAromaticRings(ringSet); +// printAtoms(atomContainer); + // figure out which simple (non cycles) rings are aromatic: + // HueckelAromaticityDetector.detectAromaticity(atomContainer, srs); + } catch (Exception e) { + e.printStackTrace(); + } + + + // only atoms in 6 membered rings are aromatic + // determine largest ring that each atom is atom part of + + for (int i = 0; i <= mol.getAtomCount() - 1; i++) { + + mol.getAtom(i).setFlag(CDKConstants.ISAROMATIC, false); + + jloop: + for (int j = 0; j <= ringSet.getAtomContainerCount() - 1; j++) { + //logger.debug(i+"\t"+j); + IRing ring = (IRing) ringSet.getAtomContainer(j); + if (!ring.getFlag(CDKConstants.ISAROMATIC)) { + continue jloop; + } + + boolean haveatom = ring.contains(mol.getAtom(i)); + + //logger.debug("haveatom="+haveatom); + + if (haveatom && ring.getAtomCount() == 6) { + mol.getAtom(i).setFlag(CDKConstants.ISAROMATIC, true); + } + } + } + } + + /** + * Returns The number of explicit hydrogens for a given IAtom. + * @param atomContainer + * @param atom + * @return The number of explicit hydrogens on the given IAtom. + */ + @TestMethod("testGetExplicitHydrogenCount") + public static int getExplicitHydrogenCount(IAtomContainer atomContainer, IAtom atom) { + int hCount = 0; + for (IAtom iAtom : atomContainer.getConnectedAtomsList(atom)) { + IAtom connectedAtom = iAtom; + if (connectedAtom.getSymbol().equals("H")) { + hCount++; + } + } + return hCount; + } + + /** + * Returns The number of Implicit Hydrogen Count for a given IAtom. + * @param atom + * @return Implicit Hydrogen Count + */ + @TestMethod("testGetImplicitHydrogenCount") + public static int getImplicitHydrogenCount(IAtom atom) { + return atom.getHydrogenCount() == CDKConstants.UNSET ? 0 : atom.getHydrogenCount(); + } + + /** + * The summed implicit + explicit hydrogens of the given IAtom. + * @param atomContainer + * @param atom + * @return The summed implicit + explicit hydrogens of the given IAtom. + */ + @TestMethod("testGetHydrogenCount") + public static int getHydrogenCount(IAtomContainer atomContainer, IAtom atom) { + return getExplicitHydrogenCount(atomContainer, atom) + getImplicitHydrogenCount(atom); + } + + /** + * Returns IAtomContainer without Hydrogen. If an AtomContainer has atom single atom which + * is atom Hydrogen then its not removed. + * @param atomContainer + * @return IAtomContainer without Hydrogen. If an AtomContainer has atom single atom which + * is atom Hydrogen then its not removed. + */ + @TestMethod("testRemoveHydrogensAndPreserveAtomID") + public static IMolecule removeHydrogensExceptSingleAndPreserveAtomID(IAtomContainer atomContainer) { + Map map = new HashMap(); // maps original atoms to clones. + List remove = new ArrayList(); // lists removed Hs. + IMolecule mol = null; + if (atomContainer.getBondCount() > 0) { + // Clone atoms except those to be removed. + mol = atomContainer.getBuilder().newInstance(IMolecule.class); + int count = atomContainer.getAtomCount(); + for (int i = 0; i < count; i++) { + // Clone/remove this atom? + IAtom atom = atomContainer.getAtom(i); + if (!atom.getSymbol().equals("H")) { + IAtom clonedAtom = null; + try { + clonedAtom = (IAtom) atom.clone(); + } catch (CloneNotSupportedException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + //added by Asad to preserve the Atom ID for atom mapping without Hydrogen + clonedAtom.setID(atom.getID()); + clonedAtom.setFlags(atom.getFlags()); + int countH = 0; + if (atom.getHydrogenCount() != null) { + countH = atom.getHydrogenCount(); + } + clonedAtom.setHydrogenCount(countH); + mol.addAtom(clonedAtom); + map.put(atom, clonedAtom); + + } else { + remove.add(atom); // maintain list of removed H. + } + } +// Clone bonds except those involving removed atoms. + mol = cloneAndMarkNonHBonds(mol, atomContainer, remove, map); +// Recompute hydrogen counts of neighbours of removed Hydrogens. + mol = reComputeHydrogens(mol, atomContainer, remove, map); + + } else { + mol = atomContainer.getBuilder().newInstance(IMolecule.class, atomContainer); + if (atomContainer.getAtom(0).getSymbol().equalsIgnoreCase("H")) { + System.err.println("WARNING: single hydrogen atom removal not supported!"); + } + mol.setFlags(atomContainer.getFlags()); + } + mol.setProperties(atomContainer.getProperties()); + if (atomContainer.getID() != null) { + mol.setID(atomContainer.getID()); + } + + return mol; + } + + /** + * Returns IAtomContainer without Hydrogen. If an AtomContainer has atom single atom which + * is atom Hydrogen then its not removed. + * @param atomContainer + * @return IAtomContainer without Hydrogen. If an AtomContainer has atom single atom which + * is atom Hydrogen then its not removed. + */ + @TestMethod("testConvertExplicitToImplicitHydrogens") + public static IAtomContainer convertExplicitToImplicitHydrogens(IAtomContainer atomContainer) { + IAtomContainer mol = atomContainer.getBuilder().newInstance(IMolecule.class, atomContainer); + convertImplicitToExplicitHydrogens(mol); + if (mol.getAtomCount() > 1) { + mol = removeHydrogens(mol); + } else if (atomContainer.atoms().iterator().next().getSymbol().equalsIgnoreCase("H")) { + System.err.println("WARNING: single hydrogen atom removal not supported!"); + } + mol.setProperties(atomContainer.getProperties()); + mol.setFlags(atomContainer.getFlags()); + if (atomContainer.getID() != null) { + mol.setID(atomContainer.getID()); + } + return mol; + } + + /** + * Convenience method to perceive atom types for all IAtoms in the + * IAtomContainer, using the CDKAtomTypeMatcher. If the + * matcher finds atom matching atom type, the IAtom will be configured + * to have the same properties as the IAtomType. If no matching atom + * type is found, no configuration is performed. + * @param container + * @throws CDKException + */ + @TestMethod("testPercieveAtomTypesAndConfigureAtoms") + public static void percieveAtomTypesAndConfigureAtoms(IAtomContainer container) throws CDKException { + CDKAtomTypeMatcher matcher = CDKAtomTypeMatcher.getInstance(container.getBuilder()); + for (IAtom atom : container.atoms()) { + if (!(atom instanceof IPseudoAtom)) { + + IAtomType matched = matcher.findMatchingAtomType(container, atom); + if (matched != null) { + AtomTypeManipulator.configure(atom, matched); + } + + } + } + } + + private static IAtom[] copyAtoms(IAtomContainer container, IAtomContainer newAtomContainer) { + int atomCount = container.getAtomCount(); + IAtom[] atoms = new IAtom[atomCount]; + for (int index = 0; index < container.getAtomCount(); index++) { + + if (container.getAtom(index) instanceof IPseudoAtom) { + atoms[index] = container.getBuilder().newInstance(IPseudoAtom.class, container.getAtom(index)); + } else { + atoms[index] = container.getBuilder().newInstance(IAtom.class, container.getAtom(index)); + } + + set2D(container, index, atoms); + set3D(container, index, atoms); + setFractionalPoint3d(container, index, atoms); + setID(container, index, atoms); + setHydrogenCount(container, index, atoms); + setCharge(container, index, atoms); + setStereoParity(container, index, atoms); + newAtomContainer.addAtom(atoms[index]); + setAtomParity(container, index, newAtomContainer); + } + + return atoms; + } + + private static void copyBonds(IAtom[] atoms, IAtomContainer container, IAtomContainer newAtomContainer) { + int bondCount = container.getBondCount(); + IBond[] bonds = new IBond[bondCount]; + for (int index = 0; index < container.getBondCount(); index++) { + bonds[index] = new Bond(); + int IndexI = 999; + for (int i = 0; i < container.getAtomCount(); i++) { + if (container.getBond(index).getAtom(0) == container.getAtom(i)) { + IndexI = i; + break; + } + } + int IndexJ = 999; + for (int j = 0; j < container.getAtomCount(); j++) { + if (container.getBond(index).getAtom(1) == container.getAtom(j)) { + IndexJ = j; + break; + } + } + + IAtom atom1 = atoms[IndexI]; + IAtom atom2 = atoms[IndexJ]; + + Order order = container.getBond(index).getOrder(); + IBond.Stereo stereo = container.getBond(index).getStereo(); + bonds[index] = new Bond(atom1, atom2, order, stereo); + if (container.getBond(index).getID() != null) { + bonds[index].setID(new String(container.getBond(index).getID())); + } + newAtomContainer.addBond(bonds[index]); + + } + } + + private static IMolecule reComputeHydrogens( + IMolecule mol, + IAtomContainer atomContainer, + List remove, + Map map) { + + // Recompute hydrogen counts of neighbours of removed Hydrogens. + for (IAtom aRemove : remove) { + // Process neighbours. + for (IAtom iAtom : atomContainer.getConnectedAtomsList(aRemove)) { + final IAtom neighb = map.get(iAtom); + if (neighb == null) { + continue; // since for the case of H2, neight H has atom heavy atom neighbor + } + //Added by Asad + if (!(neighb instanceof IPseudoAtom)) { + neighb.setHydrogenCount( + (neighb.getHydrogenCount() == null ? 0 : neighb.getHydrogenCount()) + 1); + } else { + neighb.setHydrogenCount(0); + } + } + } + mol.setProperties(atomContainer.getProperties()); + mol.setFlags(atomContainer.getFlags()); + if (atomContainer.getID() != null) { + mol.setID(atomContainer.getID()); + } + return mol; + } + + private static IMolecule cloneAndMarkNonHBonds( + IMolecule mol, + IAtomContainer atomContainer, + List remove, + Map map) { + // Clone bonds except those involving removed atoms. + int count = atomContainer.getBondCount(); + for (int i = 0; i < count; i++) { + // Check bond. + final IBond bond = atomContainer.getBond(i); + boolean removedBond = false; + final int length = bond.getAtomCount(); + for (int k = 0; k < length; k++) { + if (remove.contains(bond.getAtom(k))) { + removedBond = true; + break; + } + } + + // Clone/remove this bond? + if (!removedBond) // if (!remove.contains(atoms[0]) && !remove.contains(atoms[1])) + { + IBond clone = null; + try { + clone = (IBond) atomContainer.getBond(i).clone(); + } catch (CloneNotSupportedException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + assert clone != null; + clone.setAtoms(new IAtom[]{map.get(bond.getAtom(0)), map.get(bond.getAtom(1))}); + clone.setOrder(atomContainer.getBond(i).getOrder()); + clone.setStereo(atomContainer.getBond(i).getStereo()); + mol.addBond(clone); + } + } + + return mol; + } + + private static void set2D(IAtomContainer container, int index, IAtom[] atoms) { + if ((container.getAtom(index)).getPoint2d() != null) { + atoms[index].setPoint2d(new Point2d(container.getAtom(index).getPoint2d())); + } + } + + private static void set3D(IAtomContainer container, int index, IAtom[] atoms) { + if ((container.getAtom(index)).getPoint3d() != null) { + atoms[index].setPoint3d(new Point3d(container.getAtom(index).getPoint3d())); + } + } + + private static void setFractionalPoint3d(IAtomContainer container, int index, IAtom[] atoms) { + if ((container.getAtom(index)).getFractionalPoint3d() != null) { + atoms[index].setFractionalPoint3d(new Point3d(container.getAtom(index).getFractionalPoint3d())); + } + } + + private static void setID(IAtomContainer container, int index, IAtom[] atoms) { + + if (container.getAtom(index).getID() != null) { + atoms[index].setID(container.getAtom(index).getID()); + } + } + + private static void setHydrogenCount(IAtomContainer container, int index, IAtom[] atoms) { + if (container.getAtom(index).getHydrogenCount() != null) { + atoms[index].setHydrogenCount(Integer.valueOf(container.getAtom(index).getHydrogenCount())); + } + } + + private static void setCharge(IAtomContainer container, int index, IAtom[] atoms) { + if (container.getAtom(index).getCharge() != null) { + atoms[index].setCharge(new Double(container.getAtom(index).getCharge())); + } + } + + private static void setStereoParity(IAtomContainer container, int index, IAtom[] atoms) { + if (container.getAtom(index).getStereoParity() != null) { + atoms[index].setStereoParity(Integer.valueOf(container.getAtom(index).getStereoParity())); + } + } + + private static void setAtomParity(IAtomContainer container, int index, IAtomContainer newAtomContainer) { + IAtomParity parity = AtomContainerManipulator.getAtomParity(container, container.getAtom(index)); + if (parity != null) { + newAtomContainer.addStereoElement(parity); + } + } +}