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