1 /* Copyright (C) 2001-2007 The Chemistry Development Kit (CDK) project 2 * 3 * Contact: cdk-devel@lists.sourceforge.net 4 * 5 * This program is free software; you can redistribute it and/or 6 * modify it under the terms of the GNU Lesser General Public License 7 * as published by the Free Software Foundation; either version 2.1 8 * of the License, or (at your option) any later version. 9 * All we ask is that proper credit is given for our work, which includes 10 * - but is not limited to - adding the above copyright notice to the beginning 11 * of your source code files, and to any copyright notice that you may distribute 12 * with programs based on this work. 13 * 14 * This program is distributed in the hope that it will be useful, 15 * but WITHOUT ANY WARRANTY; without even the implied warranty of 16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 * GNU Lesser General Public License for more details. 18 * 19 * You should have received a copy of the GNU Lesser General Public License 20 * along with this program; if not, write to the Free Software 21 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 22 * 23 */ 24 package org.openscience.cdk.tools; 25 26 import java.util.ArrayList; 27 import java.util.List; 28 29 import org.openscience.cdk.CDKConstants; 30 import org.openscience.cdk.config.AtomTypeFactory; 31 import org.openscience.cdk.exception.CDKException; 32 import org.openscience.cdk.graph.Cycles; 33 import org.openscience.cdk.interfaces.IAtom; 34 import org.openscience.cdk.interfaces.IAtomContainer; 35 import org.openscience.cdk.interfaces.IAtomType; 36 import org.openscience.cdk.interfaces.IBond; 37 import org.openscience.cdk.interfaces.IBond.Order; 38 import org.openscience.cdk.interfaces.IChemObjectBuilder; 39 import org.openscience.cdk.interfaces.IPseudoAtom; 40 import org.openscience.cdk.interfaces.IRingSet; 41 import org.openscience.cdk.ringsearch.RingPartitioner; 42 import org.openscience.cdk.tools.manipulator.BondManipulator; 43 import org.openscience.cdk.tools.manipulator.RingSetManipulator; 44 45 /** 46 * Provides methods for checking whether an atoms valences are saturated with 47 * respect to a particular atom type. 48 * 49 * <p>Important: this class does not deal with hybridization states, which makes 50 * it fail, for example, for situations where bonds are marked as aromatic (either 51 * 1.5 or single an AROMATIC). 52 * 53 * @author steinbeck 54 * @author Egon Willighagen 55 * @cdk.created 2001-09-04 56 * 57 * @cdk.keyword saturation 58 * @cdk.keyword atom, valency 59 * 60 * @cdk.module valencycheck 61 * @cdk.githash 62 */ 63 public class SaturationChecker implements IValencyChecker, IDeduceBondOrderTool { 64 65 AtomTypeFactory structgenATF; 66 67 private static ILoggingTool logger = LoggingToolFactory.createLoggingTool(SaturationChecker.class); 68 69 /** 70 * @param builder the ChemObjectBuilder implementation used to construct the AtomType's. 71 */ getAtomTypeFactory(IChemObjectBuilder builder)72 protected AtomTypeFactory getAtomTypeFactory(IChemObjectBuilder builder) throws CDKException { 73 if (structgenATF == null) { 74 try { 75 structgenATF = AtomTypeFactory.getInstance("org/openscience/cdk/config/data/structgen_atomtypes.xml", 76 builder); 77 } catch (Exception exception) { 78 logger.debug(exception); 79 throw new CDKException("Could not instantiate AtomTypeFactory!", exception); 80 } 81 } 82 return structgenATF; 83 } 84 hasPerfectConfiguration(IAtom atom, IAtomContainer ac)85 public boolean hasPerfectConfiguration(IAtom atom, IAtomContainer ac) throws CDKException { 86 double bondOrderSum = ac.getBondOrderSum(atom); 87 IBond.Order maxBondOrder = ac.getMaximumBondOrder(atom); 88 IAtomType[] atomTypes = getAtomTypeFactory(atom.getBuilder()).getAtomTypes(atom.getSymbol()); 89 if (atomTypes.length == 0) return true; 90 logger.debug("*** Checking for perfect configuration ***"); 91 try { 92 logger.debug("Checking configuration of atom " + ac.indexOf(atom)); 93 logger.debug("Atom has bondOrderSum = " + bondOrderSum); 94 logger.debug("Atom has max = " + bondOrderSum); 95 } catch (Exception exc) { 96 } 97 for (int f = 0; f < atomTypes.length; f++) { 98 if (bondOrderSum == atomTypes[f].getBondOrderSum() && maxBondOrder == atomTypes[f].getMaxBondOrder()) { 99 try { 100 logger.debug("Atom " + ac.indexOf(atom) + " has perfect configuration"); 101 } catch (Exception exc) { 102 } 103 return true; 104 } 105 } 106 try { 107 logger.debug("*** Atom " + ac.indexOf(atom) + " has imperfect configuration ***"); 108 } catch (Exception exc) { 109 } 110 return false; 111 } 112 113 /** 114 * Determines of all atoms on the AtomContainer are saturated. 115 */ 116 @Override isSaturated(IAtomContainer container)117 public boolean isSaturated(IAtomContainer container) throws CDKException { 118 return allSaturated(container); 119 } 120 allSaturated(IAtomContainer ac)121 public boolean allSaturated(IAtomContainer ac) throws CDKException { 122 logger.debug("Are all atoms saturated?"); 123 for (int f = 0; f < ac.getAtomCount(); f++) { 124 if (!isSaturated(ac.getAtom(f), ac)) { 125 return false; 126 } 127 } 128 return true; 129 } 130 131 /** 132 * Returns whether a bond is unsaturated. A bond is unsaturated if 133 * <b>both</b> Atoms in the bond are unsaturated. 134 */ isUnsaturated(IBond bond, IAtomContainer atomContainer)135 public boolean isUnsaturated(IBond bond, IAtomContainer atomContainer) throws CDKException { 136 137 IAtom[] atoms = BondManipulator.getAtomArray(bond); 138 boolean isUnsaturated = true; 139 for (int i = 0; i < atoms.length; i++) { 140 isUnsaturated = isUnsaturated && !isSaturated(atoms[i], atomContainer); 141 } 142 return isUnsaturated; 143 } 144 145 /** 146 * Returns whether a bond is saturated. A bond is saturated if 147 * <b>both</b> Atoms in the bond are saturated. 148 */ isSaturated(IBond bond, IAtomContainer atomContainer)149 public boolean isSaturated(IBond bond, IAtomContainer atomContainer) throws CDKException { 150 IAtom[] atoms = BondManipulator.getAtomArray(bond); 151 boolean isSaturated = true; 152 for (int i = 0; i < atoms.length; i++) { 153 isSaturated = isSaturated && isSaturated(atoms[i], atomContainer); 154 } 155 return isSaturated; 156 } 157 158 /** 159 * Checks whether an Atom is saturated by comparing it with known AtomTypes. 160 */ 161 @Override isSaturated(IAtom atom, IAtomContainer ac)162 public boolean isSaturated(IAtom atom, IAtomContainer ac) throws CDKException { 163 IAtomType[] atomTypes = getAtomTypeFactory(atom.getBuilder()).getAtomTypes(atom.getSymbol()); 164 if (atomTypes.length == 0) return true; 165 double bondOrderSum = ac.getBondOrderSum(atom); 166 IBond.Order maxBondOrder = ac.getMaximumBondOrder(atom); 167 Integer hcount = atom.getImplicitHydrogenCount() == CDKConstants.UNSET ? 0 : atom.getImplicitHydrogenCount(); 168 Integer charge = atom.getFormalCharge() == CDKConstants.UNSET ? 0 : atom.getFormalCharge(); 169 try { 170 logger.debug("*** Checking saturation of atom ", atom.getSymbol(), "" + ac.indexOf(atom) + " ***"); 171 logger.debug("bondOrderSum: " + bondOrderSum); 172 logger.debug("maxBondOrder: " + maxBondOrder); 173 logger.debug("hcount: " + hcount); 174 } catch (Exception exc) { 175 logger.debug(exc); 176 } 177 for (int f = 0; f < atomTypes.length; f++) { 178 if (bondOrderSum - charge + hcount == atomTypes[f].getBondOrderSum() 179 && !BondManipulator.isHigherOrder(maxBondOrder, atomTypes[f].getMaxBondOrder())) { 180 logger.debug("*** Good ! ***"); 181 return true; 182 } 183 } 184 logger.debug("*** Bad ! ***"); 185 return false; 186 } 187 188 /** 189 * Checks if the current atom has exceeded its bond order sum value. 190 * 191 * @param atom The Atom to check 192 * @param ac The atomcontainer context 193 * @return oversaturated or not 194 */ isOverSaturated(IAtom atom, IAtomContainer ac)195 public boolean isOverSaturated(IAtom atom, IAtomContainer ac) throws CDKException { 196 IAtomType[] atomTypes = getAtomTypeFactory(atom.getBuilder()).getAtomTypes(atom.getSymbol()); 197 if (atomTypes.length == 0) return false; 198 double bondOrderSum = ac.getBondOrderSum(atom); 199 IBond.Order maxBondOrder = ac.getMaximumBondOrder(atom); 200 Integer hcount = atom.getImplicitHydrogenCount() == CDKConstants.UNSET ? 0 : atom.getImplicitHydrogenCount(); 201 Integer charge = atom.getFormalCharge() == CDKConstants.UNSET ? 0 : atom.getFormalCharge(); 202 try { 203 logger.debug("*** Checking saturation of atom " + ac.indexOf(atom) + " ***"); 204 logger.debug("bondOrderSum: " + bondOrderSum); 205 logger.debug("maxBondOrder: " + maxBondOrder); 206 logger.debug("hcount: " + hcount); 207 } catch (Exception exc) { 208 } 209 for (int f = 0; f < atomTypes.length; f++) { 210 if (bondOrderSum - charge + hcount > atomTypes[f].getBondOrderSum()) { 211 logger.debug("*** Good ! ***"); 212 return true; 213 } 214 } 215 logger.debug("*** Bad ! ***"); 216 return false; 217 } 218 219 /** 220 * Returns the currently maximum formable bond order for this atom. 221 * 222 * @param atom The atom to be checked 223 * @param ac The AtomContainer that provides the context 224 * @return the currently maximum formable bond order for this atom 225 */ getCurrentMaxBondOrder(IAtom atom, IAtomContainer ac)226 public double getCurrentMaxBondOrder(IAtom atom, IAtomContainer ac) throws CDKException { 227 IAtomType[] atomTypes = getAtomTypeFactory(atom.getBuilder()).getAtomTypes(atom.getSymbol()); 228 if (atomTypes.length == 0) return 0; 229 double bondOrderSum = ac.getBondOrderSum(atom); 230 Integer hcount = atom.getImplicitHydrogenCount() == CDKConstants.UNSET ? 0 : atom.getImplicitHydrogenCount(); 231 double max = 0; 232 double current = 0; 233 for (int f = 0; f < atomTypes.length; f++) { 234 current = hcount + bondOrderSum; 235 if (atomTypes[f].getBondOrderSum() - current > max) { 236 max = atomTypes[f].getBondOrderSum() - current; 237 } 238 } 239 return max; 240 } 241 242 /** 243 * Resets the bond orders of all atoms to 1.0. 244 */ unsaturate(IAtomContainer atomContainer)245 public void unsaturate(IAtomContainer atomContainer) { 246 for (IBond bond : atomContainer.bonds()) 247 bond.setOrder(Order.SINGLE); 248 } 249 250 /** 251 * Resets the bond order of the Bond to 1.0. 252 */ unsaturateBonds(IAtomContainer container)253 public void unsaturateBonds(IAtomContainer container) { 254 for (IBond bond : container.bonds()) 255 bond.setOrder(Order.SINGLE); 256 } 257 258 /** 259 * Saturates a molecule by setting appropriate bond orders. 260 * This method is known to fail, especially on pyrolle-like compounds. 261 * Consider using import org.openscience.cdk.smiles.DeduceBondSystemTool, which should work better 262 * 263 * @cdk.keyword bond order, calculation 264 * @cdk.created 2003-10-03 265 */ newSaturate(IAtomContainer atomContainer)266 public void newSaturate(IAtomContainer atomContainer) throws CDKException { 267 logger.info("Saturating atomContainer by adjusting bond orders..."); 268 boolean allSaturated = allSaturated(atomContainer); 269 if (!allSaturated) { 270 IBond[] bonds = new IBond[atomContainer.getBondCount()]; 271 for (int i = 0; i < bonds.length; i++) 272 bonds[i] = atomContainer.getBond(i); 273 boolean succeeded = newSaturate(bonds, atomContainer); 274 for (int i = 0; i < bonds.length; i++) { 275 if (bonds[i].getOrder() == IBond.Order.DOUBLE && bonds[i].getFlag(CDKConstants.ISAROMATIC) 276 && (bonds[i].getBegin().getSymbol().equals("N") && bonds[i].getEnd().getSymbol().equals("N"))) { 277 int atomtohandle = 0; 278 if (bonds[i].getBegin().getSymbol().equals("N")) atomtohandle = 1; 279 List<IBond> bondstohandle = atomContainer.getConnectedBondsList(bonds[i].getAtom(atomtohandle)); 280 for (int k = 0; k < bondstohandle.size(); k++) { 281 IBond bond = bondstohandle.get(k); 282 if (bond.getOrder() == IBond.Order.SINGLE && bond.getFlag(CDKConstants.ISAROMATIC)) { 283 bond.setOrder(IBond.Order.DOUBLE); 284 bonds[i].setOrder(IBond.Order.SINGLE); 285 break; 286 } 287 } 288 } 289 } 290 if (!succeeded) { 291 throw new CDKException("Could not saturate this atomContainer!"); 292 } 293 } 294 } 295 296 /** 297 * Saturates a set of Bonds in an AtomContainer. 298 * This method is known to fail, especially on pyrolle-like compounds. 299 * Consider using import org.openscience.cdk.smiles.DeduceBondSystemTool, which should work better 300 */ newSaturate(IBond[] bonds, IAtomContainer atomContainer)301 public boolean newSaturate(IBond[] bonds, IAtomContainer atomContainer) throws CDKException { 302 logger.debug("Saturating bond set of size: " + bonds.length); 303 boolean bondsAreFullySaturated = true; 304 if (bonds.length > 0) { 305 IBond bond = bonds[0]; 306 307 // determine bonds left 308 int leftBondCount = bonds.length - 1; 309 IBond[] leftBonds = new IBond[leftBondCount]; 310 System.arraycopy(bonds, 1, leftBonds, 0, leftBondCount); 311 312 // examine this bond 313 if (isUnsaturated(bond, atomContainer)) { 314 // either this bonds should be saturated or not 315 316 // try to leave this bond unsaturated and saturate the left bondssaturate this bond 317 if (leftBondCount > 0) { 318 logger.debug("Recursing with unsaturated bond with #bonds: " + leftBondCount); 319 bondsAreFullySaturated = newSaturate(leftBonds, atomContainer) 320 && !isUnsaturated(bond, atomContainer); 321 } else { 322 bondsAreFullySaturated = false; 323 } 324 325 // ok, did it work? if not, saturate this bond, and recurse 326 if (!bondsAreFullySaturated) { 327 logger.debug("First try did not work..."); 328 // ok, revert saturating this bond, and recurse again 329 boolean couldSaturate = newSaturate(bond, atomContainer); 330 if (couldSaturate) { 331 if (leftBondCount > 0) { 332 logger.debug("Recursing with saturated bond with #bonds: " + leftBondCount); 333 bondsAreFullySaturated = newSaturate(leftBonds, atomContainer); 334 } else { 335 bondsAreFullySaturated = true; 336 } 337 } else { 338 bondsAreFullySaturated = false; 339 // no need to recurse, because we already know that this bond 340 // unsaturated does not work 341 } 342 } 343 } else if (isSaturated(bond, atomContainer)) { 344 logger.debug("This bond is already saturated."); 345 if (leftBondCount > 0) { 346 logger.debug("Recursing with #bonds: " + leftBondCount); 347 bondsAreFullySaturated = newSaturate(leftBonds, atomContainer); 348 } else { 349 bondsAreFullySaturated = true; 350 } 351 } else { 352 logger.debug("Cannot saturate this bond"); 353 // but, still recurse (if possible) 354 if (leftBondCount > 0) { 355 logger.debug("Recursing with saturated bond with #bonds: " + leftBondCount); 356 bondsAreFullySaturated = newSaturate(leftBonds, atomContainer) 357 && !isUnsaturated(bond, atomContainer); 358 } else { 359 bondsAreFullySaturated = !isUnsaturated(bond, atomContainer); 360 } 361 } 362 } 363 logger.debug("Is bond set fully saturated?: " + bondsAreFullySaturated); 364 logger.debug("Returning to level: " + (bonds.length + 1)); 365 return bondsAreFullySaturated; 366 } 367 368 /** 369 * Saturate atom by adjusting its bond orders. 370 * This method is known to fail, especially on pyrolle-like compounds. 371 * Consider using import org.openscience.cdk.smiles.DeduceBondSystemTool, which should work better 372 */ newSaturate(IBond bond, IAtomContainer atomContainer)373 public boolean newSaturate(IBond bond, IAtomContainer atomContainer) throws CDKException { 374 IAtom[] atoms = BondManipulator.getAtomArray(bond); 375 IAtom atom = atoms[0]; 376 IAtom partner = atoms[1]; 377 logger.debug(" saturating bond: ", atom.getSymbol(), "-", partner.getSymbol()); 378 IAtomType[] atomTypes1 = getAtomTypeFactory(bond.getBuilder()).getAtomTypes(atom.getSymbol()); 379 IAtomType[] atomTypes2 = getAtomTypeFactory(bond.getBuilder()).getAtomTypes(partner.getSymbol()); 380 boolean bondOrderIncreased = true; 381 while (bondOrderIncreased && !isSaturated(bond, atomContainer)) { 382 logger.debug("Can increase bond order"); 383 bondOrderIncreased = false; 384 for (int atCounter1 = 0; atCounter1 < atomTypes1.length && !bondOrderIncreased; atCounter1++) { 385 IAtomType aType1 = atomTypes1[atCounter1]; 386 logger.debug(" condidering atom type: ", aType1); 387 if (couldMatchAtomType(atomContainer, atom, aType1)) { 388 logger.debug(" trying atom type: ", aType1); 389 for (int atCounter2 = 0; atCounter2 < atomTypes2.length && !bondOrderIncreased; atCounter2++) { 390 IAtomType aType2 = atomTypes2[atCounter2]; 391 logger.debug(" condidering partner type: ", aType1); 392 if (couldMatchAtomType(atomContainer, partner, atomTypes2[atCounter2])) { 393 logger.debug(" with atom type: ", aType2); 394 if (!BondManipulator.isLowerOrder(bond.getOrder(), aType2.getMaxBondOrder()) 395 || !BondManipulator.isLowerOrder(bond.getOrder(), aType1.getMaxBondOrder())) { 396 logger.debug("Bond order not increased: atoms has reached (or exceeded) maximum bond order for this atom type"); 397 } else if (BondManipulator.isLowerOrder(bond.getOrder(), aType2.getMaxBondOrder()) 398 && BondManipulator.isLowerOrder(bond.getOrder(), aType1.getMaxBondOrder())) { 399 BondManipulator.increaseBondOrder(bond); 400 logger.debug("Bond order now " + bond.getOrder()); 401 bondOrderIncreased = true; 402 } 403 } 404 } 405 } 406 } 407 } 408 return isSaturated(bond, atomContainer); 409 } 410 411 /** 412 * Determines if the atom can be of type AtomType. 413 */ couldMatchAtomType(IAtomContainer atomContainer, IAtom atom, IAtomType atomType)414 public boolean couldMatchAtomType(IAtomContainer atomContainer, IAtom atom, IAtomType atomType) { 415 logger.debug(" ... matching atom ", atom.getSymbol(), " vs ", atomType); 416 if (atomContainer.getBondOrderSum(atom) + atom.getImplicitHydrogenCount() < atomType.getBondOrderSum()) { 417 logger.debug(" Match!"); 418 return true; 419 } 420 logger.debug(" No Match"); 421 return false; 422 } 423 424 /** 425 * The method is known to fail for certain compounds. For more information, see 426 * cdk.test.limitations package. 427 * This method is known to fail, especially on pyrolle-like compounds. 428 * Consider using import org.openscience.cdk.smiles.DeduceBondSystemTool, which should work better 429 * 430 */ 431 @Override saturate(IAtomContainer atomContainer)432 public void saturate(IAtomContainer atomContainer) throws CDKException { 433 /* 434 * newSaturate(atomContainer); } public void oldSaturate(AtomContainer 435 * atomContainer) throws CDKException { 436 */ 437 IAtom partner = null; 438 IAtom atom = null; 439 List<IAtom> partners = null; 440 IAtomType[] atomTypes1 = null; 441 IAtomType[] atomTypes2 = null; 442 IBond bond = null; 443 for (int i = 1; i < 4; i++) { 444 // handle atoms with degree 1 first and then proceed to higher order 445 for (int f = 0; f < atomContainer.getAtomCount(); f++) { 446 atom = atomContainer.getAtom(f); 447 logger.debug("symbol: ", atom.getSymbol()); 448 atomTypes1 = getAtomTypeFactory(atom.getBuilder()).getAtomTypes(atom.getSymbol()); 449 if (atomTypes1.length > 0) { 450 logger.debug("first atom type: ", atomTypes1[0]); 451 if (atomContainer.getConnectedBondsCount(atom) == i) { 452 Integer hcount = atom.getImplicitHydrogenCount() == CDKConstants.UNSET ? 0 : atom 453 .getImplicitHydrogenCount(); 454 if (atom.getFlag(CDKConstants.ISAROMATIC) 455 && atomContainer.getBondOrderSum(atom) < atomTypes1[0].getBondOrderSum() - hcount) { 456 partners = atomContainer.getConnectedAtomsList(atom); 457 for (int g = 0; g < partners.size(); g++) { 458 partner = partners.get(g); 459 logger.debug("Atom has " + partners.size() + " partners"); 460 atomTypes2 = getAtomTypeFactory(atom.getBuilder()).getAtomTypes(partner.getSymbol()); 461 if (atomTypes2.length == 0) return; 462 463 hcount = partner.getImplicitHydrogenCount() == CDKConstants.UNSET ? 0 : partner 464 .getImplicitHydrogenCount(); 465 if (atomContainer.getBond(partner, atom).getFlag(CDKConstants.ISAROMATIC) 466 && atomContainer.getBondOrderSum(partner) < atomTypes2[0].getBondOrderSum() 467 - hcount) { 468 logger.debug("Partner has " + atomContainer.getBondOrderSum(partner) 469 + ", may have: " + atomTypes2[0].getBondOrderSum()); 470 bond = atomContainer.getBond(atom, partner); 471 logger.debug("Bond order was " + bond.getOrder()); 472 BondManipulator.increaseBondOrder(bond); 473 logger.debug("Bond order now " + bond.getOrder()); 474 break; 475 } 476 } 477 } 478 479 Double bondOrderSum = atomTypes1[0].getBondOrderSum() == CDKConstants.UNSET ? 0.0 480 : atomTypes1[0].getBondOrderSum(); 481 Integer hydrogenCount = atom.getImplicitHydrogenCount() == CDKConstants.UNSET ? 0 : atom 482 .getImplicitHydrogenCount(); 483 Double atomContainerBondOrderSum = atomContainer.getBondOrderSum(atom); 484 if (atomContainerBondOrderSum == CDKConstants.UNSET) atomContainerBondOrderSum = 0.0; 485 486 if (atomContainerBondOrderSum < bondOrderSum - hydrogenCount) { 487 logger.debug("Atom has " + atomContainerBondOrderSum + ", may have: " + bondOrderSum); 488 partners = atomContainer.getConnectedAtomsList(atom); 489 for (int g = 0; g < partners.size(); g++) { 490 partner = (IAtom) partners.get(g); 491 logger.debug("Atom has " + partners.size() + " partners"); 492 atomTypes2 = getAtomTypeFactory(atom.getBuilder()).getAtomTypes(partner.getSymbol()); 493 if (atomTypes2.length == 0) return; 494 495 Double bos2 = atomTypes2[0].getBondOrderSum(); 496 Integer hc2 = partner.getImplicitHydrogenCount(); 497 Double acbos2 = atomContainer.getBondOrderSum(partner); 498 if (bos2 == CDKConstants.UNSET) bos2 = 0.0; 499 if (hc2 == CDKConstants.UNSET) hc2 = 0; 500 if (acbos2 == CDKConstants.UNSET) acbos2 = 0.0; 501 502 if (acbos2 < bos2 - hc2) { 503 logger.debug("Partner has " + acbos2 + ", may have: " + bos2); 504 bond = atomContainer.getBond(atom, partner); 505 logger.debug("Bond order was " + bond.getOrder()); 506 BondManipulator.increaseBondOrder(bond); 507 logger.debug("Bond order now " + bond.getOrder()); 508 break; 509 } 510 } 511 } 512 } 513 } 514 } 515 } 516 } 517 saturateRingSystems(IAtomContainer atomContainer)518 public void saturateRingSystems(IAtomContainer atomContainer) throws CDKException { 519 IRingSet rs = Cycles.sssr(atomContainer.getBuilder().newInstance(IAtomContainer.class, atomContainer)) 520 .toRingSet(); 521 List<IRingSet> ringSets = RingPartitioner.partitionRings(rs); 522 IAtomContainer ac = null; 523 IAtom atom = null; 524 int temp[]; 525 for (int f = 0; f < ringSets.size(); f++) { 526 rs = ringSets.get(f); 527 List<IAtomContainer> containers = RingSetManipulator.getAllAtomContainers(rs); 528 for (int counter = 0; counter < containers.size(); counter++) { 529 ac = containers.get(counter); 530 temp = new int[ac.getAtomCount()]; 531 for (int g = 0; g < ac.getAtomCount(); g++) { 532 atom = ac.getAtom(g); 533 temp[g] = atom.getImplicitHydrogenCount(); 534 atom.setImplicitHydrogenCount(atomContainer.getConnectedBondsCount(atom) 535 - ac.getConnectedBondsCount(atom) - temp[g]); 536 } 537 saturate(ac); 538 for (int g = 0; g < ac.getAtomCount(); g++) { 539 atom = ac.getAtom(g); 540 atom.setImplicitHydrogenCount(temp[g]); 541 } 542 } 543 } 544 } 545 546 /* 547 * Recursively fixes bond orders in a molecule for which only connectivities 548 * but no bond orders are know. 549 * @ param molecule The molecule to fix the bond orders for 550 * @ param bond The number of the bond to treat in this recursion step 551 * @ return true if the bond order which was implemented was ok. 552 */ 553 /* 554 * private boolean recursiveBondOrderFix(Molecule molecule, int bondNumber) 555 * { Atom partner = null; Atom atom = null; Atom[] partners = null; 556 * AtomType[] atomTypes1 = null; AtomType[] atomTypes2 = null; int 557 * maxBondOrder = 0; int oldBondOrder = 0; if (bondNumber < 558 * molecule.getBondCount()) { Bond bond = molecule.getBondAt(f); } else { 559 * return true; } atom = bond.getAtomAt(0); partner = bond.getAtomAt(1); 560 * atomTypes1 = atf.getAtomTypes(atom.getSymbol(), 561 * atf.ATOMTYPE_ID_STRUCTGEN); atomTypes2 = 562 * atf.getAtomTypes(partner.getSymbol(), atf.ATOMTYPE_ID_STRUCTGEN); 563 * maxBondOrder = Math.min(atomTypes1[0].getMaxBondOrder(), 564 * atomTypes2[0].getMaxBondOrder()); for (int f = 1; f <= maxBondOrder; f++) 565 * { oldBondOrder = bond.getOrder() bond.setOrder(f); if 566 * (!isOverSaturated(atom, molecule) && !isOverSaturated(partner, molecule)) 567 * { if (!recursiveBondOrderFix(molecule, bondNumber + 1)) break; } else { 568 * bond.setOrder(oldBondOrder); return false; } } return true; } 569 */ 570 571 /** 572 * Calculate the number of missing hydrogens by subtracting the number of 573 * bonds for the atom from the expected number of bonds. Charges are included 574 * in the calculation. The number of expected bonds is defined by the AtomType 575 * generated with the AtomTypeFactory. 576 * 577 * @param atom Description of the Parameter 578 * @param container Description of the Parameter 579 * @return Description of the Return Value 580 * @see AtomTypeFactory 581 */ calculateNumberOfImplicitHydrogens(IAtom atom, IAtomContainer container)582 public int calculateNumberOfImplicitHydrogens(IAtom atom, IAtomContainer container) throws CDKException { 583 return this.calculateNumberOfImplicitHydrogens(atom, container, false); 584 } 585 calculateNumberOfImplicitHydrogens(IAtom atom)586 public int calculateNumberOfImplicitHydrogens(IAtom atom) throws CDKException { 587 List<IBond> bonds = new ArrayList<IBond>(); 588 return this.calculateNumberOfImplicitHydrogens(atom, 0, 0, bonds, false); 589 } 590 calculateNumberOfImplicitHydrogens(IAtom atom, IAtomContainer container, boolean throwExceptionForUnknowAtom)591 public int calculateNumberOfImplicitHydrogens(IAtom atom, IAtomContainer container, 592 boolean throwExceptionForUnknowAtom) throws CDKException { 593 return this.calculateNumberOfImplicitHydrogens(atom, container.getBondOrderSum(atom), 594 container.getConnectedSingleElectronsCount(atom), container.getConnectedBondsList(atom), 595 throwExceptionForUnknowAtom); 596 } 597 598 /** 599 * Calculate the number of missing hydrogens by subtracting the number of 600 * bonds for the atom from the expected number of bonds. Charges are included 601 * in the calculation. The number of expected bonds is defined by the AtomType 602 * generated with the AtomTypeFactory. 603 * 604 * @param atom Description of the Parameter 605 * @param throwExceptionForUnknowAtom Should an exception be thrown if an unknown atomtype is found or 0 returned ? 606 * @return Description of the Return Value 607 * @see AtomTypeFactory 608 */ calculateNumberOfImplicitHydrogens(IAtom atom, double bondOrderSum, double singleElectronSum, List<IBond> connectedBonds, boolean throwExceptionForUnknowAtom)609 public int calculateNumberOfImplicitHydrogens(IAtom atom, double bondOrderSum, double singleElectronSum, 610 List<IBond> connectedBonds, boolean throwExceptionForUnknowAtom) throws CDKException { 611 int missingHydrogen = 0; 612 if (atom instanceof IPseudoAtom) { 613 // don't figure it out... it simply does not lack H's 614 } else if (atom.getAtomicNumber() != null && atom.getAtomicNumber() == 1 || atom.getSymbol().equals("H")) { 615 missingHydrogen = (int) (1 - bondOrderSum - singleElectronSum - atom.getFormalCharge()); 616 } else { 617 logger.info("Calculating number of missing hydrogen atoms"); 618 // get default atom 619 IAtomType[] atomTypes = getAtomTypeFactory(atom.getBuilder()).getAtomTypes(atom.getSymbol()); 620 if (atomTypes.length == 0 && throwExceptionForUnknowAtom) return 0; 621 logger.debug("Found atomtypes: " + atomTypes.length); 622 if (atomTypes.length > 0) { 623 IAtomType defaultAtom = atomTypes[0]; 624 logger.debug("DefAtom: ", defaultAtom); 625 626 Integer formalCharge = atom.getFormalCharge(); 627 if (formalCharge == null) formalCharge = 0; 628 629 Double tmpBondOrderSum = defaultAtom.getBondOrderSum(); 630 if (tmpBondOrderSum == null) tmpBondOrderSum = 0.0; 631 632 missingHydrogen = (int) (tmpBondOrderSum - bondOrderSum - singleElectronSum + formalCharge); 633 634 if (atom.getFlag(CDKConstants.ISAROMATIC)) { 635 boolean subtractOne = true; 636 for (int i = 0; i < connectedBonds.size(); i++) { 637 IBond conBond = (IBond) connectedBonds.get(i); 638 if (conBond.getOrder() == IBond.Order.DOUBLE || conBond.getFlag(CDKConstants.ISAROMATIC)) 639 subtractOne = false; 640 } 641 if (subtractOne) missingHydrogen--; 642 } 643 logger.debug("Atom: ", atom.getSymbol()); 644 logger.debug(" max bond order: " + tmpBondOrderSum); 645 logger.debug(" bond order sum: " + bondOrderSum); 646 logger.debug(" charge : " + formalCharge); 647 } else { 648 logger.warn("Could not find atom type for ", atom.getSymbol()); 649 } 650 } 651 return missingHydrogen; 652 } 653 654 } 655