Logo Search packages:      
Sourcecode: cdk version File versions  Download package

CDKHueckelAromaticityDetector.java
/* $Revision$ $Author$ $Date$
 *
 * Copyright (C) 2007  Egon Willighagen <egonw@users.sf.net>
 *
 * 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 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.aromaticity;

import java.util.Iterator;

import org.openscience.cdk.CDKConstants;
import org.openscience.cdk.annotations.TestClass;
import org.openscience.cdk.annotations.TestMethod;
import org.openscience.cdk.atomtype.CDKAtomTypeMatcher;
import org.openscience.cdk.config.AtomTypeFactory;
import org.openscience.cdk.exception.CDKException;
import org.openscience.cdk.graph.ConnectivityChecker;
import org.openscience.cdk.graph.SpanningTree;
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.IRingSet;
import org.openscience.cdk.interfaces.IAtomType.Hybridization;
import org.openscience.cdk.ringsearch.AllRingsFinder;
import org.openscience.cdk.ringsearch.SSSRFinder;

/**
 * This aromaticity detector detects the aromaticity based on the H&uuml;ckel
 * 4n+2 pi-electrons rule applied to isolated ring systems. It assumes
 * CDK atom types to be perceived with the {@link CDKAtomTypeMatcher} or with
 * any compatible class. For example:
 * <pre>
 * Molecule molecule = MoleculeFactory.makePyridineOxide();
 * AtomContainerManipulator.percieveAtomTypesAndConfigureAtoms(molecule);
 * CDKHueckelAromaticityDetector.detectAromaticity(molecule);
 * </pre>
 *
 * @author         egonw
 * @cdk.module     standard
 * @cdk.githash
 * @cdk.created    2007-10-05
 * 
 * @see org.openscience.cdk.CDKConstants
 */
@TestClass("org.openscience.cdk.aromaticity.CDKHueckelAromaticityDetectorTest")
00065 public class CDKHueckelAromaticityDetector {

      private static AtomTypeFactory factory = null;

    @TestMethod("testDetectAromaticity_IAtomContainer")
    public static boolean detectAromaticity(IAtomContainer atomContainer) throws CDKException {
            SpanningTree spanningTree = new SpanningTree(atomContainer);
            IAtomContainer ringSystems = spanningTree.getCyclicFragmentsContainer();
            if (ringSystems.getAtomCount() == 0) {
                  // If there are no rings, then there cannot be any aromaticity
                  return false;
            }
            // disregard all atoms we know that cannot be aromatic anyway
        for (IAtom atom : ringSystems.atoms())
            if (!atomIsPotentiallyAromatic(atom))
                ringSystems.removeAtomAndConnectedElectronContainers(atom);

        // FIXME: should not really mark them here
            Iterator<IAtom> atoms = ringSystems.atoms().iterator();
            while (atoms.hasNext()) atoms.next().setFlag(CDKConstants.ISINRING, true);
            Iterator<IBond> bonds = ringSystems.bonds().iterator();
            while (bonds.hasNext()) bonds.next().setFlag(CDKConstants.ISINRING, true);          
            
            boolean foundSomeAromaticity = false;
            Iterator<IAtomContainer> isolatedRingSystems =
                ConnectivityChecker.partitionIntoMolecules(ringSystems).atomContainers().iterator();
            while (isolatedRingSystems.hasNext()) {
                  IAtomContainer isolatedSystem = isolatedRingSystems.next();
                  IRingSet singleRings = new SSSRFinder(isolatedSystem).findSSSR();
                  Iterator<IAtomContainer> singleRingsIterator = singleRings.atomContainers().iterator();
                  int maxRingSize = 20;
                  boolean atLeastOneRingIsSprouted = false;
                  boolean allRingsAreAromatic = true;
                  // test single rings in SSSR
                  while (singleRingsIterator.hasNext()) {
                        IAtomContainer singleRing = singleRingsIterator.next();
                        if (singleRing.getAtomCount() > maxRingSize) maxRingSize = singleRing.getAtomCount();
                        if (isRingSystemSproutedWithNonRingDoubleBonds(atomContainer, singleRing)) {
//                            OK, this ring is not aromatic
                              atLeastOneRingIsSprouted = true;
                              allRingsAreAromatic = false;
                        } else {
                              // possibly aromatic
                            boolean ringIsAromatic = isHueckelValid(singleRing);
                              foundSomeAromaticity |= ringIsAromatic;
                              allRingsAreAromatic &= ringIsAromatic;
                              if (ringIsAromatic) markRingAtomsAndBondsAromatic(singleRing);
                        }
                  }
                  // OK, what about the one larger ring (if no aromaticity found in SSSR)?
                  if (!allRingsAreAromatic && !atLeastOneRingIsSprouted &&
                        singleRings.getAtomContainerCount() <= 3) {
                        // every ring system consisting of more than two rings is too difficult
                        Iterator<IAtomContainer> allRingsIterator = new AllRingsFinder().findAllRingsInIsolatedRingSystem(isolatedSystem).atomContainers().iterator();
                        while (allRingsIterator.hasNext()) {
                              // there should be exactly three rings, of which only one has a size larger
                              // than the two previous ones
                              IAtomContainer ring = allRingsIterator.next();
                              if (ring.getAtomCount() <= maxRingSize) {
                                    // possibly aromatic
                                  boolean ringIsAromatic = isHueckelValid(ring);
                                    foundSomeAromaticity |= ringIsAromatic;
                                    if (ringIsAromatic) markRingAtomsAndBondsAromatic(ring);
                              }
                        }
                  }
            }
            
            return foundSomeAromaticity;
      }

      /**
       * Tests if the electron count matches the H&uuml;ckel 4n+2 rule.
       */
00139       private static boolean isHueckelValid(IAtomContainer singleRing) throws CDKException {
            int electronCount = 0;
            for (IAtom ringAtom : singleRing.atoms()) {
                  if (ringAtom.getHybridization() != CDKConstants.UNSET &&
                        (ringAtom.getHybridization() == Hybridization.SP2) ||
                    ringAtom.getHybridization() == Hybridization.PLANAR3) {
                        // for example, a carbon
                        // note: the double bond is in the ring, that has been tested earlier
                        // FIXME: this does assume bond orders to be resolved too, when detecting
                        // sprouting double bonds
                        if ("N.planar3".equals(ringAtom.getAtomTypeName())) {
                              electronCount += 2;
                        } else if ("N.minus.planar3".equals(ringAtom.getAtomTypeName())) {
                              electronCount += 2;
                } else if ("N.amide".equals(ringAtom.getAtomTypeName())) {
                    electronCount += 2;
                        } else if ("S.2".equals(ringAtom.getAtomTypeName())) {
                    electronCount += 2;
                        } else if ("S.planar3".equals(ringAtom.getAtomTypeName())) {
                    electronCount += 2;
                } else if ("C.minus.planar".equals(ringAtom.getAtomTypeName())) {
                    electronCount += 2;
                } else if ("O.planar3".equals(ringAtom.getAtomTypeName())) {
                    electronCount += 2;
                } else {
                    if (factory == null) {
                                    factory = AtomTypeFactory.getInstance(
                                          "org/openscience/cdk/dict/data/cdk-atom-types.owl",
                                          ringAtom.getBuilder()
                                    );
                              }
                              IAtomType type = factory.getAtomType(ringAtom.getAtomTypeName());
                              Object property = type.getProperty(CDKConstants.PI_BOND_COUNT);
                              if (property != null && property instanceof Integer) {
                                    electronCount += ((Integer)property).intValue();
                              }
                        }
                  } else if (ringAtom.getHybridization() != null &&
                                 ringAtom.getHybridization() == Hybridization.SP3 &&
                                 getLonePairCount(ringAtom) > 0) {
                        // for example, a nitrogen or oxygen
                        electronCount += 2;
                  }
            }
            return (electronCount % 4 == 2) && (electronCount > 2);
      }

      private static boolean atomIsPotentiallyAromatic(IAtom atom) {
          if (atom.getHybridization() == Hybridization.SP2) return true;
        if (atom.getHybridization() == Hybridization.PLANAR3) return true;
        if (atom.getHybridization() == Hybridization.SP3 && getLonePairCount(atom) > 0) return true;
        return false;
      }

    /**
       * Determines if the isolatedRingSystem has attached double bonds, which are not part of the ring system itself,
       * and not part of any other ring system.
       */
00197       private static boolean isRingSystemSproutedWithNonRingDoubleBonds(IAtomContainer fullContainer, IAtomContainer isolatedRingSystem) {
            Iterator<IAtom> atoms = isolatedRingSystem.atoms().iterator();
            while (atoms.hasNext()) {
                  Iterator<IBond> neighborBonds = fullContainer.getConnectedBondsList(atoms.next()).iterator();
                  while (neighborBonds.hasNext()) {
                        IBond neighborBond = neighborBonds.next();
                        if (!neighborBond.getFlag(CDKConstants.ISINRING) &&
                              neighborBond.getOrder() == CDKConstants.BONDORDER_DOUBLE ||
                              neighborBond.getOrder() == CDKConstants.BONDORDER_TRIPLE) {
                              return true;
                        }
                  }
            }
            return false;
      }
      
      private static int getLonePairCount(IAtom atom) {
            Integer count = (Integer)atom.getProperty(CDKConstants.LONE_PAIR_COUNT);
            if (count == null) {
                  return 0;
            } else {
                  return count;
            }
      }
      
      private static void markRingAtomsAndBondsAromatic(IAtomContainer container) {
          for (IAtom atom : container.atoms()) atom.setFlag(CDKConstants.ISAROMATIC, true);
              for (IBond bond : container.bonds()) bond.setFlag(CDKConstants.ISAROMATIC, true);
      }
}


Generated by  Doxygen 1.6.0   Back to index