/* $Revision$ $Author$ $Date$ * * Copyright (C) 2006-2007 Miguel Rojas <miguel.rojas@uni-koeln.de> * * 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. * * 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.tools; import java.util.ArrayList; import java.util.List; 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.exception.CDKException; import org.openscience.cdk.interfaces.IAtom; import org.openscience.cdk.interfaces.IAtomContainer; import org.openscience.cdk.interfaces.IAtomContainerSet; import org.openscience.cdk.interfaces.IBond; import org.openscience.cdk.interfaces.IMolecule; import org.openscience.cdk.interfaces.IMoleculeSet; import org.openscience.cdk.interfaces.IReactionSet; import org.openscience.cdk.isomorphism.UniversalIsomorphismTester; import org.openscience.cdk.isomorphism.matchers.QueryAtomContainer; import org.openscience.cdk.isomorphism.matchers.QueryAtomContainerCreator; import org.openscience.cdk.reaction.IReactionProcess; import org.openscience.cdk.reaction.type.PiBondingMovementReaction; import org.openscience.cdk.reaction.type.RearrangementAnionReaction; import org.openscience.cdk.reaction.type.RearrangementCationReaction; import org.openscience.cdk.reaction.type.RearrangementLonePairReaction; import org.openscience.cdk.reaction.type.RearrangementRadicalReaction; import org.openscience.cdk.reaction.type.SharingLonePairReaction; import org.openscience.cdk.reaction.type.parameters.IParameterReact; import org.openscience.cdk.reaction.type.parameters.SetReactionCenter; /** * <p>This class try to generate resonance structure for a determinate molecule.</p> * <p>Make sure that the molecule has the corresponding lone pair electrons * for each atom. You can use the method: <pre> LonePairElectronChecker </pre> * <p>It is needed to call the addExplicitHydrogensToSatisfyValency * from the class tools.HydrogenAdder.</p> * <p>It is based on rearrangements of electrons and charge</p> * <p>The method is based on call by reactions which occur in a resonance.</p> * * <pre> * StructureResonanceGenerator srG = new StructureReseonanceGenerator(true,true,true,true,false); * MoleculeSet setOf = srG.getResonances(new Molecule()); * </pre> * * <p>We have the possibility to localize the reactive center. Good method if you * want to localize the reaction in a fixed point</p> * <pre>atoms[0].setFlag(CDKConstants.REACTIVE_CENTER,true);</pre> * <p>Moreover you must put the parameter as true</p> * <p>If the reactive center is not localized then the reaction process will * try to find automatically the possible reactive center.</p> * * @author Miguel Rojas * @cdk.created 2006-5-05 * @cdk.module reaction * @cdk.githash * * @see org.openscience.cdk.reaction.IReactionProcess */ @TestClass("org.openscience.cdk.tools.StructureResonanceGeneratorTest") public class StructureResonanceGenerator { private ILoggingTool logger = LoggingToolFactory.createLoggingTool(StructureResonanceGenerator.class); private List<IReactionProcess> reactionsList = new ArrayList<IReactionProcess>(); /**Generate resonance structure without looking at the symmetry*/ private boolean lookingSymmetry; /** TODO: REACT: some time takes too much time. At the moment fixed to 50 structures*/ private int maxStructures = 50; /** * Construct an instance of StructureResonanceGenerator. Default restrictions * are initiated. * * @see #setDefaultReactions() */ public StructureResonanceGenerator(){ this(false); } /** * Construct an instance of StructureResonanceGenerator. Default restrictions * are initiated. * * @param lookingSymmetry Specify if the resonance generation is based looking at the symmetry * @see #setDefaultReactions() */ public StructureResonanceGenerator(boolean lookingSymmetry){ logger.info("Initiate StructureResonanceGenerator"); this.lookingSymmetry = lookingSymmetry; setDefaultReactions(); } /** * Set the reactions that must be used in the generation of the resonance. * * @param newReactionsList The IReactionsProcess's to use * * @see #getReactions() * @see #setReactions(java.util.List) * @see IReactionProcess */ @TestMethod("testSetReactions_List") public void setReactions(List<IReactionProcess> newReactionsList) { reactionsList = newReactionsList; } /** * Get the reactions that must be presents in the generation of the resonance. * * @return The reactions to be imposed * * * @see #setDefaultReactions() */ @TestMethod("testGetReactions") public List<IReactionProcess> getReactions(){ return this.reactionsList; } /** * Set the number maximal of resonance structures to be found. The * algorithm breaks the process when is came to this number. * * @param maxStruct The maximal number */ @TestMethod("testSetMaximalStructures_int") public void setMaximalStructures(int maxStruct){ maxStructures = maxStruct; } /** * Get the number maximal of resonance structures to be found. * * @return The maximal number */ @TestMethod("testGetMaximalStructures") public int getMaximalStructures(){ return maxStructures; } /** * Set the default reactions that must be presents to generate the resonance. * * @see #getReactions() */ @TestMethod("testSetDefaultReactions") public void setDefaultReactions(){ callDefaultReactions(); } private void callDefaultReactions() { List<IParameterReact> paramList = new ArrayList<IParameterReact>(); IParameterReact param = new SetReactionCenter(); param.setParameter(Boolean.FALSE); paramList.add(param); IReactionProcess type = new SharingLonePairReaction(); try { type.setParameterList(paramList); } catch (CDKException e) { e.printStackTrace(); } reactionsList.add(type); type = new PiBondingMovementReaction(); List<IParameterReact> paramList2 = new ArrayList<IParameterReact>(); IParameterReact param2 = new SetReactionCenter(); param2.setParameter(Boolean.FALSE); paramList2.add(param2); try { type.setParameterList(paramList2); } catch (CDKException e) { e.printStackTrace(); } reactionsList.add(type); type = new RearrangementAnionReaction(); try { type.setParameterList(paramList); } catch (CDKException e) { e.printStackTrace(); } reactionsList.add(type); type = new RearrangementCationReaction(); try { type.setParameterList(paramList); } catch (CDKException e) { e.printStackTrace(); } reactionsList.add(type); type = new RearrangementLonePairReaction(); try { type.setParameterList(paramList); } catch (CDKException e) { e.printStackTrace(); } reactionsList.add(type); type = new RearrangementRadicalReaction(); try { type.setParameterList(paramList); } catch (CDKException e) { e.printStackTrace(); } reactionsList.add(type); } /** * <p>Get the resonance structures from an IMolecule. </p> * * @param molecule The IMolecule to analyze * @return The different resonance structures */ @TestMethod("testGetStructures_IMolecule") public IMoleculeSet getStructures(IMolecule molecule) { int countStructure = 0; IMoleculeSet setOfMol = molecule.getBuilder().newMoleculeSet(); setOfMol.addMolecule(molecule); for(int i = 0 ; i < setOfMol.getMoleculeCount() ; i++){ IMolecule mol = setOfMol.getMolecule(i); for (IReactionProcess aReactionsList : reactionsList) { IReactionProcess reaction = aReactionsList; IMoleculeSet setOfReactants = molecule.getBuilder().newMoleculeSet(); setOfReactants.addMolecule(mol); try { IReactionSet setOfReactions = reaction.initiate(setOfReactants, null); if (setOfReactions.getReactionCount() != 0) for (int k = 0; k < setOfReactions.getReactionCount(); k++) for (int j = 0; j < setOfReactions.getReaction(k).getProducts().getAtomContainerCount(); j++) { IMolecule product = setOfReactions.getReaction(k).getProducts().getMolecule(j); if (!existAC(setOfMol, product)) { setOfMol.addMolecule(product); countStructure++; if (countStructure > maxStructures) return setOfMol; } } } catch (CDKException e) { e.printStackTrace(); } } } return setOfMol; } /** * <p>Get the container which is found resonance from a IMolecule. * It is based on looking if the order of the bond changes.</p> * * @param molecule The IMolecule to analyze * @return The different containers */ @TestMethod("testGetContainers_IMolecule") public IAtomContainerSet getContainers(IMolecule molecule) { IAtomContainerSet setOfCont = molecule.getBuilder().newAtomContainerSet(); IMoleculeSet setOfMol = getStructures(molecule); if(setOfMol.getMoleculeCount() == 0) return setOfCont; /*extraction of all bonds which has been produced a changes of order*/ List<IBond> bondList = new ArrayList<IBond>(); for(int i = 1 ; i < setOfMol.getMoleculeCount() ; i++){ IMolecule mol = setOfMol.getMolecule(i); for(int j = 0; j < mol.getBondCount(); j++){ IBond bond = molecule.getBond(j); if(!mol.getBond(j).getOrder().equals(bond.getOrder())){ if(!bondList.contains(bond)) bondList.add(bond); } } } if(bondList.size() == 0) return null; int[] flagBelonging = new int[bondList.size()]; for(int i = 0; i < flagBelonging.length; i++) flagBelonging[i] = 0; int[] position = new int[bondList.size()]; int maxGroup = 1; /*Analysis if the bond are linked together*/ List<IBond> newBondList = new ArrayList<IBond>(); newBondList.add(bondList.get(0)); int pos = 0; for(int i = 0 ; i < newBondList.size(); i ++){ if(i==0) flagBelonging[i] = maxGroup; else{ if(flagBelonging[position[i]] == 0){ maxGroup++; flagBelonging[position[i]] = maxGroup; } } IBond bondA = newBondList.get(i); for(int ato = 0; ato < 2; ato++){ IAtom atomA1 = bondA.getAtom(ato); List<IBond> bondA1s = molecule.getConnectedBondsList(atomA1); for(int j = 0 ; j < bondA1s.size(); j ++){ IBond bondB = bondA1s.get(j); if(!newBondList.contains(bondB)) for(int k = 0 ; k < bondList.size(); k ++) if(bondList.get(k).equals(bondB)) if(flagBelonging[k] == 0){ flagBelonging[k] = maxGroup; pos++; newBondList.add(bondB); position[pos] = k; } } } //if it is final size and not all are added if(newBondList.size()-1 == i) for(int k = 0 ; k < bondList.size(); k ++) if(!newBondList.contains(bondList.get(k))){ newBondList.add(bondList.get(k)); position[i+1] = k; break; } } /*creating containers according groups*/ for(int i = 0 ; i < maxGroup; i ++){ IAtomContainer container = molecule.getBuilder().newAtomContainer(); for(int j = 0 ; j < bondList.size(); j++){ if(flagBelonging[j] != i+1) continue; IBond bond = bondList.get(j); IAtom atomA1 = bond.getAtom(0); IAtom atomA2 = bond.getAtom(1); if(!container.contains(atomA1)) container.addAtom(atomA1); if(!container.contains(atomA2)) container.addAtom(atomA2); container.addBond(bond); } setOfCont.addAtomContainer(container); } return setOfCont; } /** * <p>Get the container which the atom is found on resonance from a IMolecule. * It is based on looking if the order of the bond changes. Return null * is any is found.</p> * * @param molecule The IMolecule to analyze * @param atom The IAtom * @return The container with the atom */ @TestMethod("testGetContainer_IMolecule_IAtom") public IAtomContainer getContainer(IMolecule molecule, IAtom atom) { IAtomContainerSet setOfCont = getContainers(molecule); if(setOfCont == null) return null; for (IAtomContainer container : setOfCont.atomContainers()) { if (container.contains(atom)) return container; } return null; } /** * <p>Get the container which the bond is found on resonance from a IMolecule. * It is based on looking if the order of the bond changes. Return null * is any is found.</p> * * @param molecule The IMolecule to analyze * @param bond The IBond * @return The container with the bond */ @TestMethod("testGetContainer_IMolecule_IBond") public IAtomContainer getContainer(IMolecule molecule, IBond bond) { IAtomContainerSet setOfCont = getContainers(molecule); if(setOfCont == null) return null; for (IAtomContainer container : setOfCont.atomContainers()) { if (container.contains(bond)) return container; } return null; } /** * Search if the setOfAtomContainer contains the atomContainer * * * @param set ISetOfAtomContainer object where to search * @param atomContainer IAtomContainer to search * @return True, if the atomContainer is contained */ private boolean existAC(IAtomContainerSet set, IAtomContainer atomContainer) { IAtomContainer acClone = null; try { acClone = (IMolecule) atomContainer.clone(); if(!lookingSymmetry){ /*remove all aromatic flags*/ for (IAtom atom : acClone.atoms()) atom.setFlag(CDKConstants.ISAROMATIC, false); for (IBond bond : acClone.bonds()) bond.setFlag(CDKConstants.ISAROMATIC, false); } } catch (CloneNotSupportedException e1) { e1.printStackTrace(); } for(int i = 0 ; i < acClone.getAtomCount(); i++) // if(acClone.getAtom(i).getID() == null) acClone.getAtom(i).setID(""+acClone.getAtomNumber(acClone.getAtom(i))); if(lookingSymmetry){ try { CDKHueckelAromaticityDetector.detectAromaticity(acClone); } catch (CDKException e) { e.printStackTrace(); } }else{ if(!lookingSymmetry){ /*remove all aromatic flags*/ for (IAtom atom : acClone.atoms()) atom.setFlag(CDKConstants.ISAROMATIC, false); for (IBond bond : acClone.bonds()) bond.setFlag(CDKConstants.ISAROMATIC, false); } } for(int i = 0 ; i < set.getAtomContainerCount(); i++){ IAtomContainer ss = set.getAtomContainer(i); for(int j = 0 ; j < ss.getAtomCount(); j++) // if(ss.getAtom(j).getID() == null) ss.getAtom(j).setID(""+ss.getAtomNumber(ss.getAtom(j))); try { if(!lookingSymmetry ){ QueryAtomContainer qAC = QueryAtomContainerCreator.createSymbolChargeIDQueryContainer(acClone); if(UniversalIsomorphismTester.isIsomorph(ss,qAC)){ QueryAtomContainer qAC2 = QueryAtomContainerCreator.createSymbolAndBondOrderQueryContainer(acClone); if(UniversalIsomorphismTester.isIsomorph(ss,qAC2)) return true; } }else{ QueryAtomContainer qAC = QueryAtomContainerCreator.createSymbolAndChargeQueryContainer(acClone); CDKHueckelAromaticityDetector.detectAromaticity(ss); if(UniversalIsomorphismTester.isIsomorph(ss,qAC)) return true; } } catch (CDKException e1) { System.err.println(e1); logger.error(e1.getMessage()); logger.debug(e1); } } return false; } }