All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.actelion.research.chem.AromaticityResolver Maven / Gradle / Ivy

There is a newer version: 2024.11.2
Show newest version
/*
 * Copyright (c) 1997 - 2016
 * Actelion Pharmaceuticals Ltd.
 * Gewerbestrasse 16
 * CH-4123 Allschwil, Switzerland
 *
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * 1. Redistributions of source code must retain the above copyright notice, this
 *    list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright notice,
 *    this list of conditions and the following disclaimer in the documentation
 *    and/or other materials provided with the distribution.
 * 3. Neither the name of the the copyright holder nor the
 *    names of its contributors may be used to endorse or promote products
 *    derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * @author Thomas Sander
 */

package com.actelion.research.chem;

public class AromaticityResolver {
	ExtendedMolecule	mMol;
	private boolean		mAllHydrogensAreExplicit;
	private boolean[]	mIsDelocalizedAtom,mIsDelocalizedBond;
    private int         mAromaticAtoms,mAromaticBonds,mPiElectronsAdded;

    /**
     * Creates a new AromaticityResolver for molecule mol.
     * @param mol
     */
    public AromaticityResolver(ExtendedMolecule mol) {
        mMol = mol;
        }

    /**
     * This method promotes all necessary bonds of the defined delocalized part of the molecule
     * from single to double bonds in order to create a valid delocalized system
     * of conjugated single and double bonds.
	 * The delocalized part of the molecule may be defined by passing an array
	 * to isAromaticBond that has all bonds flagged, which are part of a delocalized area.
	 * In this case these bonds are assumed to have bond type cBondTypeSingle.
	 * Alternatively, one may pass null and indicate affected bonds with bond type cBondTypeDelocalized.
     * Non-cyclic atom chains defined to be delocalized are treated depending
     * on whether we have a molecule or a query fragment. For fragments the respective bond
     * types will be set to cBondTypeDelocalized; for molecules the chain will
     * have alternating single and double bonds starting with double at a non-ring end.
     * @return true if all bonds of the delocalized area could be consistently converted. 
     */
	public boolean locateDelocalizedDoubleBonds(boolean[] isAromaticBond) {
		return locateDelocalizedDoubleBonds(isAromaticBond, false, false);
		}

	/**
	 * This method promotes all necessary bonds of the defined delocalized part of the molecule
	 * from single to double bonds in order to create a valid delocalized system
	 * of conjugated single and double bonds.
	 * The delocalized part of the molecule may be defined by passing an array
	 * to isAromaticBond that has all bonds flagged, which are part of a delocalized area.
	 * In this case these bonds are assumed to have bond type cBondTypeSingle.
	 * Alternatively, one may pass null and indicate affected bonds with bond type cBondTypeDelocalized.
	 * Non-cyclic atom chains defined to be delocalized are treated depending
	 * on whether we have a molecule or a query fragment. For fragments the respective bond
	 * types will be set to cBondTypeDelocalized; for molecules the chain will
	 * have alternating single and double bonds starting with double at a non-ring end.
	 * @param isAromaticBond if null, then bond type cBondTypeDelocalized is used to indicate delocalized bonds
	 * @param mayChangeAtomCharges true if input molecule doesn't carry atom charges and these may be added to achieve aromaticity
	 * @param allHydrogensAreExplicit true this method can rely on all hydrogens being explicitly present
	 * @return true if all bonds of the delocalized area could be consistently converted.
	 */
	public boolean locateDelocalizedDoubleBonds(boolean[] isAromaticBond, boolean mayChangeAtomCharges, boolean allHydrogensAreExplicit) {
		mMol.ensureHelperArrays(Molecule.cHelperNeighbours);

		if (isAromaticBond != null) {
			mIsDelocalizedBond = isAromaticBond;
			}
		else {
			mIsDelocalizedBond = new boolean[mMol.getBonds()];
			for (int bond=0; bond 1)
				return true;
		return false;
		}


	private int getNextOuterDelocalizedConnIndex(int atom, int previousAtom, int[] sharedDelocalizedRingCount) {
		for (int i=0; i 0)))
				protectAtom(atom);
		}


	private void protectAtom(int atom) {
		if (mIsDelocalizedAtom[atom]) {
			mIsDelocalizedAtom[atom] = false;
			mAromaticAtoms--;
			}
		for (int i=0; i 1) {	// bridgehead atom
							if (!checkAtomTypePi1(atom, false)) {
								possible = false;
								break;
								}
							}
						else {	// non-bridgehead in 5- or 7-membered ring
							int priority = (ringSize == 5) ?
									checkAtomTypeLeak5(atom, false) : checkAtomTypeLeak7(atom, false);
							if (!checkAtomTypePi1(atom, false)) {
								if (leakPriority == 10) {
									possible = false;
									break;
									}
								leakAtom = atom;
								leakPriority = 20;	// MAX
								}
							else if (leakPriority < priority) {
								leakPriority = priority;
								leakAtom = atom;
								}
							}
						}

					if (possible) {
						for (int atom : ringSet.getRingAtoms(ring)) {
							if (atom == leakAtom) {
								if (ringSize == 5)
									checkAtomTypeLeak5(atom, true);	// 5-membered
								else
									checkAtomTypeLeak7(atom, true);	// 3- or 7-membered

								protectAtom(atom);
								}
							else {
								checkAtomTypePi1(atom, true);
								}
							}
						}
					}
				}
			}

		// From here locate delocalized strings of atoms, which are not member
		// of an aromatic ring. Protect preferred atoms and add obvious atom charges.

		// count for every atom the number of delocalized bonds attached
		int[] delocalizedNeighbourCount = new int[mMol.getAtoms()];
		boolean[] hasMetalLigandBond = new boolean[mMol.getAtoms()];
		for (int bond=0; bond 0) {
							checkAtomTypeLeakNonRing(maxAtom, true);
							protectAtom(maxAtom);
							}
						}
					}
				}
			}
		}

	/**
	 * Checks, whether the atom is compatible with an aromatic atom of the type
	 * that carries one half of a delocalized double bond.
	 * @param atom
	 * @param correctCharge if true then may add a charge to make the atom compatible
	 * @return
	 */
	private boolean checkAtomTypePi1(int atom, boolean correctCharge) {
		int atomicNo = mMol.getAtomicNo(atom);
		if ((atomicNo >= 5 && atomicNo <= 8)
		 || atomicNo == 15 || atomicNo == 16 || atomicNo == 33 || atomicNo == 34 || atomicNo == 52) {	// P,S,As,Se,Te

			int freeValence = mMol.getLowestFreeValence(atom);
			if (freeValence != 0)
				return true;

			int charge = mMol.getAtomCharge(atom);
			if (atomicNo == 5 && charge >= 0) {
				if (correctCharge)
					mMol.setAtomCharge(atom, charge-1);
				return true;
				}
			if (atomicNo != 5 && charge <= 0) {
				if (correctCharge)
					mMol.setAtomCharge(atom, charge+1);
				return true;
				}
			}

		return false;
		}

	/**
	 * Checks, whether the atom is compatible with that aromatic atom of
	 * a 5-membered ring that supplies the additional electron pair.
	 * @param atom
	 * @param correctCharge if true then may add a charge to make the atom compatible
	 * @return 0 (not compatible) or priority to be used (higher numbers have higher priority)
	 */
	private int checkAtomTypeLeak5(int atom, boolean correctCharge) {
		if (mMol.getAtomicNo(atom) == 7) {
			if (mMol.getAllConnAtoms(atom) == 3)
				return 6;
			else if (mMol.getConnAtoms(atom) == 2)
				return 4;
			}
		else if (mMol.getAtomicNo(atom) == 8) {
			return 10;
			}
		else if (mMol.getAtomicNo(atom) == 15 || mMol.getAtomicNo(atom) == 33) {
			if (mMol.getConnAtoms(atom) == 3)
				return 8;
			}
		else if (mMol.getAtomicNo(atom) == 16 || mMol.getAtomicNo(atom) == 34 || mMol.getAtomicNo(atom) == 52) {
			if (mMol.getConnAtoms(atom) == 2)
				return 12;
			}
		else if (mMol.getAtomicNo(atom) == 6) {
			if (correctCharge)
				mMol.setAtomCharge(atom, -1);
			return (mMol.getAllConnAtoms(atom) != mMol.getAllConnAtomsPlusMetalBonds(atom)) ? 2 : 3;
			}

		return 0;
		}


	/**
	 * Checks, whether the atom is compatible with that aromatic atom of
	 * a 3- or 7-membered ring that supplies the empty orbital.
	 * @param atom
	 * @param correctCharge if true then may add a charge to make the atom compatible
	 * @return 0 (not compatible) or priority to be used (higher numbers have higher priority)
	 */
	private int checkAtomTypeLeak7(int atom, boolean correctCharge) {
		if (mAllHydrogensAreExplicit) {
			if (mMol.getAllConnAtoms(atom) != 3)
				return 0;
			}
		else {
			if (mMol.getAllConnAtoms(atom) > 3)
				return 0;
			}

		if (mMol.getAtomicNo(atom) == 6) {
			if (correctCharge)
				mMol.setAtomCharge(atom, 1);
			return 2;
			}
		if (mMol.getAtomicNo(atom) == 5) {
			return 4;
			}

		return 0;
		}

	/**
	 * Checks, whether the atom is compatible with the (typically charged) atom
	 * in a delocalized chain of an odd number of atoms that does not carry a pi bond.
	 * @param atom
	 * @param correctCharge if true then may add a charge to make the atom compatible
	 * @return 0 (not compatible) or priority to be used (higher numbers have higher priority)
	 */
	private int checkAtomTypeLeakNonRing(int atom, boolean correctCharge) {
		if (mMol.getAtomCharge(atom) != 0)
			return 0;

		if (mAllHydrogensAreExplicit) {
			if (mMol.getAtomicNo(atom) == 5) {
				if (mMol.getOccupiedValence(atom) != 2)
					return 0;
				if (correctCharge)
					mMol.setAtomCharge(atom, 1);
				return 1;
				}

			if (mMol.getAtomicNo(atom) == 7) {
				if (mMol.getOccupiedValence(atom) != 2)
					return 0;
				if (correctCharge)
					mMol.setAtomCharge(atom, -1);
				return hasMetalNeighbour(atom) ? 6 : 3;
				}

			if (mMol.getAtomicNo(atom) == 8) {
				if (mMol.getOccupiedValence(atom) != 1)
					return 0;
				if (correctCharge)
					mMol.setAtomCharge(atom, -1);
				return hasMetalNeighbour(atom) ? 7 : 4;
				}

			if (mMol.getAtomicNo(atom) == 16) {
				if (mMol.getOccupiedValence(atom) != 1)
					return 0;
				if (correctCharge)
					mMol.setAtomCharge(atom, -1);
				return hasMetalNeighbour(atom) ? 5 : 2;
				}

			if (mMol.getAtomicNo(atom) == 34) {
				if (mMol.getOccupiedValence(atom) != 1)
					return 0;
				if (correctCharge)
					mMol.setAtomCharge(atom, -1);
				return hasMetalNeighbour(atom) ? 4 : 1;
				}
			}
		else {
			if (mMol.getAtomicNo(atom) == 5) {
				if (mMol.getOccupiedValence(atom) > 2)
					return 0;
				if (correctCharge)
					mMol.setAtomCharge(atom, 1);
				return 1;
				}

			if (mMol.getAtomicNo(atom) == 7) {
				if (mMol.getOccupiedValence(atom) > 2)
					return 0;
				if (correctCharge)
					mMol.setAtomCharge(atom, -1);
				return hasMetalNeighbour(atom) ? 5 : 3;
				}

			if (mMol.getAtomicNo(atom) == 8) {
				if (mMol.getOccupiedValence(atom) > 1)
					return 0;
				if (correctCharge)
					mMol.setAtomCharge(atom, -1);
				return hasMetalNeighbour(atom) ? 7 : 4;
				}

			if (mMol.getAtomicNo(atom) == 16) {
				if (mMol.getOccupiedValence(atom) > 1)
					return 0;
				if (correctCharge)
					mMol.setAtomCharge(atom, -1);
				return hasMetalNeighbour(atom) ? 5 : 2;
				}
			}

		return 0;
		}

	private boolean hasMetalNeighbour(int atom) {
		for (int i=0; i




© 2015 - 2024 Weber Informatics LLC | Privacy Policy