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.12.1
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.
*
*/

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) {	// P,S,As,Se
			int freeValence = mMol.getFreeValence(atom);
			if (freeValence == 1 || freeValence == 2)	// we allow one more free valence, because the atom may have a missing charge
				return true;

			if (mMol.getAtomCharge(atom) == 0) {
				if ((atomicNo == 15 || atomicNo == 33) && freeValence == 3) {
					if (correctCharge)
						mMol.setAtomCharge(atom, 1);
					return true;
					}
				if ((atomicNo == 16 || atomicNo == 34) && freeValence == 4) {
					if (correctCharge)
						mMol.setAtomCharge(atom, 1);
					return true;
					}
				if (atomicNo == 5 && freeValence == 0) {
					if (correctCharge)
						mMol.setAtomCharge(atom, -1);
					return true;
					}
				if ((atomicNo == 7 || atomicNo == 8) && freeValence == 0) {
					if (correctCharge)
						mMol.setAtomCharge(atom, 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) {
			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 - 2025 Weber Informatics LLC | Privacy Policy