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

com.actelion.research.chem.TautomerHelper 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;

import com.actelion.research.util.IntArrayComparator;

import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.TreeSet;

public class TautomerHelper {
	private static final int MAX_TAUTOMERS = 100000;
	private static int sMaxTautomers = MAX_TAUTOMERS;
	private static boolean sSuppressWarning = false;

	private StereoMolecule mOriginalMol;
	private boolean[] mIsTautomerBond;
	private boolean[] mHasFreeValence;
	private int[] mRegionPiCount;
	private int[] mRegionDCount;
	private int[] mRegionTCount;
	private int[] mAtomRegionNo;
	private int[] mAtomDCount;
	private int[] mAtomTCount;
	private int mRegionCount;

	private Iterator mBondOrderIterator;
	private TreeSet mBondOrderSet;
	private ArrayDeque mBondOrderDeque;

	public static void setMaxTautomers(int maxTautomers) {
		sMaxTautomers = maxTautomers;
		}

	public static void setSuppressWarning(boolean suppressWarning) {
		sSuppressWarning = suppressWarning;
		}

	/**
	 * @param mol
	 */
	public TautomerHelper(StereoMolecule mol) {
		mOriginalMol = mol.getCompactCopy();
		moveDeuteriumAndTritiumToTableEnd();
		mOriginalMol.ensureHelperArrays(Molecule.cHelperRings);

		mIsTautomerBond = new boolean[mOriginalMol.getBonds()];
		mHasFreeValence = new boolean[mOriginalMol.getAtoms()];
		for (int i=0; i 0
					 && tautomer.getAtomPi(atom) < mOriginalMol.getAtomPi(atom)
					 && freeValence[atom] != 0) {
						addHydrogen(tautomer, atom, 2);
						freeValence[atom]--;
						regionDCount[mAtomRegionNo[atom]-1]--;
						}
					}
				}

			if (mAtomTCount != null) {
				int[] regionTCount = mRegionTCount.clone();
				for (int atom=0; atom 0
					 && tautomer.getAtomPi(atom) < mOriginalMol.getAtomPi(atom)
					 && freeValence[atom] != 0) {
						addHydrogen(tautomer, atom, 3);
						freeValence[atom]--;
						regionTCount[mAtomRegionNo[atom]-1]--;
						}
					}
				}
			}

		// Double bonds, which are single bonds in other tautomers, and which carry two different
		// substituents on both double bond atoms, may have two configurations (E and Z),
		// unless the double bond is in a small ring.
		// In these cases we use a cross-bond instead of creating both configurations,
		// to avoid another dimension of tautomer count explosion.
		// Here we need to convert erroneous cross-bond assignments back to double, if the bond isn't a stereo bond.
		tautomer.ensureHelperArrays(Molecule.cHelperParities);
		for (int bond=0; bond= 0) && mOriginalMol.getAtomicNo(lastNonHAtom) == 1);

		for (int atom=0; atom= 0) && isHydrogenBond[lastNonHBond]);

		for (int bond=0; bond 1
				 && mOriginalMol.getAtomicNo(atom2) > 1
				 && mAtomRegionNo[atom2] != 0) {
					if (mOriginalMol.getAtomMass(atom1) == 2) {
						if (mAtomDCount == null)
							mAtomDCount = new int[mOriginalMol.getAllAtoms()];
						mAtomDCount[atom2]++;
						}
					else {
						if (mAtomTCount == null)
							mAtomTCount = new int[mOriginalMol.getAllAtoms()];
						mAtomTCount[atom2]++;
						}
					mOriginalMol.markAtomForDeletion(atom1);
					}
				}
			}
		if (mAtomDCount != null || mAtomTCount != null)
			mOriginalMol.deleteMarkedAtomsAndBonds();
		}

	/**
	 * @return number of tautomeric regions, i.e. atoms being connected by tautomeric bonds
	 */
	public int getAtomRegionCount() {
		return mRegionCount;
		}

	/**
	 * Returns region numbers for all atoms, i.e. numbers identifying the respective connected
	 * tautomeric regions the atom belongs to. Atoms sharing the same region share the same number.
* 0: not member of a tautomer region; 1 and above: region number * @return region count */ public int[] getAtomRegionNumbers() { return mAtomRegionNo; } /** * Considers connected tautomer bonds to belong to a tautomer region. * All independent tautomer regions are located and member atoms assigned to them. * mAtomRegionNo[] is set accordingly. * 0: not member of a tautomer region; 1 and above: region number */ private void assignRegionNumbers() { mAtomRegionNo = new int[mOriginalMol.getAtoms()]; int[] graphAtom = new int[mOriginalMol.getAtoms()]; boolean[] bondWasSeen = new boolean[mOriginalMol.getBonds()]; int region = 0; for (int bond=0; bond(); mBondOrderDeque = new ArrayDeque<>(); addTautomerIfNew(new BondOrders(mOriginalMol)); // This serves as recycled molecule container StereoMolecule tautomer = mOriginalMol.getCompactCopy(); while (!mBondOrderDeque.isEmpty()) { mBondOrderDeque.poll().copyToTautomer(tautomer); addAllTautomers(tautomer); if (mBondOrderSet.size() >= sMaxTautomers) { if (!sSuppressWarning) System.out.println("Tautomer count exceeds maximum: "+new Canonizer(mOriginalMol).getIDCode()); break; } } // TODO racemize stereo centers } /** * Find all HX-Y=Z / X=Y-ZH type 3-atom sequences and recursively vinylogous sequences. */ private void addAllTautomers(StereoMolecule mol) { ArrayList bondList = new ArrayList<>(); mol.ensureHelperArrays(Molecule.cHelperNeighbours); boolean [] isUsedAtom = new boolean[mol.getAtoms()]; // atom use buffer for recursive methods for (int atom1=0; atom1= order23) { if (mol.getAtomPi(atom3) < order23) { // if atom3 has no other double bond if (hasAcidicHydrogen(mol, atom3)) addVinylogousTautomers(mol, atom3, true, false, isUsedAtom, bondList); } else { addVinylogousTautomers(mol, atom3, true, true, isUsedAtom, bondList); } } if (order23 >= order12 && hasAcidicHydrogen(mol, atom1)) { addVinylogousTautomers(mol, atom3, false, false, isUsedAtom, bondList); } if (isValidDonorAtom(atom3) && mol.getAtomPi(atom3) < order23) { // make sure atom3 has no other double bond if (order12<=2 && order23>=2 && hasAcidicHydrogen(mol, atom1)) { addDirectTautomer(mol, bond12, bond23); } if (order12>=2 && order23<=2 && hasAcidicHydrogen(mol, atom3)) { addDirectTautomer(mol, bond23, bond12); } } bondList.remove(bondList.size()-1); } isUsedAtom[atom3] = false; } } bondList.remove(bondList.size()-1); isUsedAtom[atom2] = false; } } isUsedAtom[atom1] = false; } } } private boolean isValidDonorAtom(int atom) { return mHasFreeValence[atom] && (mOriginalMol.getAtomicNo(atom) == 5 || mOriginalMol.getAtomicNo(atom) == 6 || mOriginalMol.getAtomicNo(atom) == 7 || mOriginalMol.getAtomicNo(atom) == 8 || mOriginalMol.getAtomicNo(atom) == 16 || mOriginalMol.getAtomicNo(atom) == 34 || mOriginalMol.getAtomicNo(atom) == 52); } private boolean isValidHeteroAtom(int atom) { return mHasFreeValence[atom] && (mOriginalMol.getAtomicNo(atom) == 7 || mOriginalMol.getAtomicNo(atom) == 8 || mOriginalMol.getAtomicNo(atom) == 16 || mOriginalMol.getAtomicNo(atom) == 34 || mOriginalMol.getAtomicNo(atom) == 52); } private void addDirectTautomer(StereoMolecule mol, int bondSToD, int bondDToS) { BondOrders bondOrders = new BondOrders(mol); bondOrders.setBond(bondSToD, mol.getBondOrder(bondSToD) == 1 ? 2 : 3); bondOrders.setBond(bondDToS, mol.getBondOrder(bondDToS) == 2 ? 1 : 2); mIsTautomerBond[bondSToD] = true; mIsTautomerBond[bondDToS] = true; addTautomerIfNew(bondOrders); } private boolean addVinylogousTautomers(StereoMolecule mol, int atom1, boolean firstBondIsDouble, boolean thirdBondIsDouble, boolean[] isUsedAtom, ArrayList bondList) { for (int i=0; i= 2) || (!firstBondIsDouble && order12 <= 2)) { isUsedAtom[atom2] = true; bondList.add(bond12); for (int j=0; j= 2))) { isUsedAtom[atom3] = true; bondList.add(bond23); if (isValidDonorAtom(atom3) && (!firstBondIsDouble || hasAcidicHydrogen(mol, atom3))) { BondOrders bondOrders = new BondOrders(mol); for (int k=0; k { private int[] encoding; public BondOrders(StereoMolecule mol) { encoding = new int[(mOriginalMol.getBonds()+15) / 16]; for (int i=0; i> 4] |= (Math.min(3, mol.getBondOrder(i)) << (2*(i & 15))); } @Override public int compareTo(BondOrders o) { return new IntArrayComparator().compare(encoding, o.encoding); } public void setBond(int bond, int order) { int high = bond >> 4; int shift = 2 * (bond & 15); encoding[high] &= ~(3 << shift); encoding[high] |= (order << shift); } public void copyToTautomer(StereoMolecule tautomer) { for (int i=0; i> 4] >> (2*(i & 15))); tautomer.setBondType(i, bo == 1 ? Molecule.cBondTypeSingle : bo == 2 ? (mIsTautomerBond[i] && !mOriginalMol.isSmallRingBond(i) ? Molecule.cBondTypeCross : Molecule.cBondTypeDouble) : bo == 3 ? Molecule.cBondTypeTriple : Molecule.cBondTypeMetalLigand); } } } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy