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.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;

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 StereoMolecule mOriginalMol;
	private boolean[] mIsTautomerBond;
	private boolean[] mHasFreeValence;
	private int[] mRegionPiCount;
	private int[] mRegionDCount;
	private int[] mRegionTCount;

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

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

		mIsTautomerBond = new boolean[mOriginalMol.getBonds()];
		mHasFreeValence = new boolean[mOriginalMol.getAtoms()];
		for (int i=0; i
	 * 0: not member of a tautomer region; 1 and above: region number
	 * @param atomRegionNo int[mol.getAtoms()] filled with 0
	 * @return region count
	 */
	public int getAtomRegionNumbers(int[] atomRegionNo) {
		if (mBondOrderSet.size() == 1)
			return 0;

		int regionCount = assignRegionNumbers(atomRegionNo);
		return regionCount;
		}

	/**
	 * 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
	 * @param atomRegionNo int[mol.getAtoms()] filled with 0
	 * @return number of found tautomer regions
	 */
	private int assignRegionNumbers(int[] atomRegionNo) {
		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() >= MAX_TAUTOMERS) {
				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);
		}

	private boolean isValidHeteroAtom(int atom) {
		return mHasFreeValence[atom]
			&& (mOriginalMol.getAtomicNo(atom) == 7
			 || mOriginalMol.getAtomicNo(atom) == 8
			 || mOriginalMol.getAtomicNo(atom) == 16);
		}

	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] |= (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 ? Molecule.cBondTypeDouble
						: bo == 3 ? Molecule.cBondTypeTriple
						: Molecule.cBondTypeMetalLigand);
				}
			}
		}
	}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy