package org.openscience.cdk.layout; import com.google.common.collect.HashMultimap; import com.google.common.collect.Multimap; import org.openscience.cdk.CDKConstants; import org.openscience.cdk.exception.CDKException; import org.openscience.cdk.interfaces.IAtom; import org.openscience.cdk.interfaces.IAtomContainer; import org.openscience.cdk.interfaces.IBond; import org.openscience.cdk.interfaces.IStereoElement; import org.openscience.cdk.io.SDFWriter; import org.openscience.cdk.ringsearch.RingSearch; import org.openscience.cdk.silent.AtomContainer; import org.openscience.cdk.smiles.SmilesGenerator; import org.openscience.cdk.tools.manipulator.AtomContainerManipulator; import javax.vecmath.Point2d; import java.io.File; import java.io.FileWriter; import java.io.IOException; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.Comparator; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; /** * @author John May */ public class RingTemplateExtractor { private Multimap<String, IAtomContainer> anonymous = HashMultimap.create(); private Multimap<String, IAtomContainer> skeltons = HashMultimap.create(); private Multimap<String, IAtomContainer> library = HashMultimap.create(); SmilesGenerator smigen = SmilesGenerator.unique(); public RingTemplateExtractor() { } void add(IAtomContainer container) { // remove explicit hydrogens and all stereochemistry AtomContainerManipulator.suppressHydrogens(container); container.setStereoElements(new ArrayList<IStereoElement>()); RingSearch ringSearch = new RingSearch(container); List<IAtomContainer> fused = ringSearch.fusedRingFragments(); List<IAtomContainer> isolated = ringSearch.isolatedRingFragments(); // only use compounds with a single ring system if (fused.size() == 1 && isolated.isEmpty()) { add(fused.get(0), container); } else if (isolated.size() == 1 && fused.isEmpty()) { add(isolated.get(0), container); } } void writeSDfile(File file) throws CDKException, IOException { final SDFWriter sdfw = new SDFWriter(new FileWriter(file)); List<Map.Entry<String, Collection<IAtomContainer>>> list = new ArrayList<Map.Entry<String, Collection<IAtomContainer>>>(library.asMap().entrySet()); // sort by frequency Collections.sort(list, new Comparator<Map.Entry<String, Collection<IAtomContainer>>>() { @Override public int compare(Map.Entry<String, Collection<IAtomContainer>> o1, Map.Entry<String, Collection<IAtomContainer>> o2) { return o2.getValue().size() - o1.getValue().size(); } }); for (Map.Entry<String, Collection<IAtomContainer>> entry : list) { // only one occurance of this system don't write to the output if (entry.getValue().size() < 2) continue; String key = entry.getKey(); if (anonymous.containsKey(key)) { sdfw.write(anonymous.get(key).iterator().next()); } else if (skeltons.containsKey(key)) { sdfw.write(skeltons.get(key).iterator().next()); } else { sdfw.write(library.get(key).iterator().next()); } } sdfw.close(); } void add(IAtomContainer ringSystem, IAtomContainer container) { ringSystem.setProperty(CDKConstants.TITLE, container.getProperty(CDKConstants.TITLE)); for (IBond bond : ringSystem.bonds()) if (bond.getOrder().numeric() > 2 || bond.getOrder().numeric() < 1) return; // if whole compound is cyclic add these to the skeleton and/or anonymous maps // already. these will then not be pushed out once we reduce system later if (ringSystem.getAtomCount() == container.getAtomCount()) { if (allCarbon(ringSystem)) addAnonymous(ringSystem); else addSkeleton(ringSystem); } Set<IAtom> ringAtoms = new HashSet<IAtom>(); for (IAtom atom : ringSystem.atoms()) ringAtoms.add(atom); IAtomContainer ringWithStubs = new AtomContainer(); ringWithStubs.setProperty(CDKConstants.TITLE, container.getProperty(CDKConstants.TITLE)); ringWithStubs.add(ringSystem); for (IBond bond : container.bonds()) { IAtom atom1 = bond.getAtom(0); IAtom atom2 = bond.getAtom(1); // only add when one atom is in the ring if (ringAtoms.contains(atom1) ^ ringAtoms.contains(atom2)) { ringWithStubs.addBond(bond); ringWithStubs.addAtom(atom1); ringWithStubs.addAtom(atom2); } } addSkeletonToMainLib(ringWithStubs); addSkeletonToMainLib(ringSystem); addAnonymousToMainLib(ringSystem); } void addSkeletonToMainLib(IAtomContainer container) { IAtomContainer skeleton = clearHydrogens(AtomContainerManipulator.skeleton(container)); for (int i = 0; i < container.getAtomCount(); i++) skeleton.getAtom(i).setPoint2d(new Point2d(container.getAtom(i).getPoint2d())); skeleton.setProperty(CDKConstants.TITLE, container.getProperty(CDKConstants.TITLE)); String skeletonKey = toCanSmi(skeleton); library.put(skeletonKey, skeleton); } void addAnonymousToMainLib(IAtomContainer container) { IAtomContainer skeleton = clearHydrogens(AtomContainerManipulator.skeleton(container)); for (int i = 0; i < container.getAtomCount(); i++) skeleton.getAtom(i).setPoint2d(new Point2d(container.getAtom(i).getPoint2d())); skeleton.setProperty(CDKConstants.TITLE, container.getProperty(CDKConstants.TITLE)); String skeletonKey = toCanSmi(skeleton); library.put(skeletonKey, skeleton); } void addSkeleton(IAtomContainer container) { IAtomContainer skeleton = clearHydrogens(AtomContainerManipulator.skeleton(container)); for (int i = 0; i < container.getAtomCount(); i++) skeleton.getAtom(i).setPoint2d(new Point2d(container.getAtom(i).getPoint2d())); skeleton.setProperty(CDKConstants.TITLE, container.getProperty(CDKConstants.TITLE)); String skeletonKey = toCanSmi(skeleton); if (!skeltons.containsKey(skeletonKey)) skeltons.put(skeletonKey, skeleton); } void addAnonymous(IAtomContainer container) { IAtomContainer anonymous = clearHydrogens(AtomContainerManipulator.anonymise(container)); for (int i = 0; i < container.getAtomCount(); i++) anonymous.getAtom(i).setPoint2d(new Point2d(container.getAtom(i).getPoint2d())); anonymous.setProperty(CDKConstants.TITLE, container.getProperty(CDKConstants.TITLE)); String anonymousKey = toCanSmi(anonymous); if (!this.anonymous.containsKey(anonymousKey)) this.anonymous.put(anonymousKey, anonymous); } String toCanSmi(IAtomContainer container) { try { return smigen.create(container); } catch (CDKException e) { return null; } } static IAtomContainer clearHydrogens(IAtomContainer container) { for (IAtom atom : container.atoms()) atom.setImplicitHydrogenCount(0); return container; } static boolean allCarbon(IAtomContainer container) { for (IAtom atom : container.atoms()) if (!"C".equals(atom.getSymbol())) return false; return true; } }