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

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

import com.actelion.research.chem.Molecule;
import com.actelion.research.chem.RingCollection;
import com.actelion.research.chem.StereoMolecule;

public class TorsionDetail {
    public static final int SYMMETRY_C1C1_OR_C1D1 = 0;  // 0 -> 359 degrees
    public static final int SYMMETRY_C1D2 = 1;  // 0 -> 179 (equal to -180 -> -1)
    public static final int SYMMETRY_D1D1 = 2;  // 0, 1 -> 179 (equals -1 -> -179), 180
    public static final int SYMMETRY_D1D2_OR_D2D2 = 3;  // 0 -> 90 match 180 -> 90, 0 -> -90, -180 -> -90

    private static final int HALF_SYMMETRY_C1 = 0;  // three distinct terminal neighbors
    private static final int HALF_SYMMETRY_D1 = 1;  // e.g. single terminal neighbor or two equal sp3 neighbors
    private static final int HALF_SYMMETRY_D2 = 2;  // two equal sp2 neighbors
//  private static final int HALF_SYMMETRY_D3 = 3;  // for simplicity reasons this is covered by D1

    private static final int[][] SYMMETRY =
        { { SYMMETRY_C1C1_OR_C1D1, SYMMETRY_C1C1_OR_C1D1, SYMMETRY_C1D2 },
          { SYMMETRY_C1C1_OR_C1D1, SYMMETRY_D1D1, SYMMETRY_D1D2_OR_D2D2 },
          { SYMMETRY_C1D2, SYMMETRY_D1D2_OR_D2D2, SYMMETRY_D1D2_OR_D2D2 } };
    private static final int MAX_TRIPLE_BONDS = 8;
    private static final int FRAGMENT_ATOMS = 8+2*MAX_TRIPLE_BONDS;
	private static final int FRAGMENT_BONDS = 13+2*MAX_TRIPLE_BONDS;

	private StereoMolecule mFragment;
	private int[] mCentralAtom,mRearAtom,mRefAtom,mToFragmentAtom,mToMoleculeAtom;
	private int mAlkyneAtomCount;
	private String mID;

	/**
	 * This creates an empty torsion classification detail, which multiply can be used
	 * to classify the environment of a rotatable bond. The result is an identifier
	 * for this torsion situation that may serve to lookup probable torsion angles
	 * for this rotatable bond form a torsion library. Along with the identifier a
	 * 4-atom-chain is uniquely determined, to which torsion angles should refer to.
	 */
	public TorsionDetail() {
		mFragment = new StereoMolecule(FRAGMENT_ATOMS, FRAGMENT_BONDS);
		mToMoleculeAtom = new int[FRAGMENT_ATOMS];
		mCentralAtom = new int[2];
        mRearAtom = new int[2];
		mRefAtom = new int[2];
		}

	/**
	 * @return whether the previous classification created a valid result and ID
	 */
	public boolean isValid() {
		return mID != null;
		}

	/**
	 * Returns the torsion identifier that resulted from the most previous classification.
	 * @return a valid ID or null, if the classification failed
	 */
	public String getID() {
		return mID;
		}

	/**
	 * Returns one of the atoms of the torsion fragment's central rotatable bond.
	 * Two central and two terminal atoms define the strand that torsion angles refer to.
	 * If the rotatable bond is extended by (a) triple bond(s) then the central atom
	 * is one of the first non-sp atoms at the end of the linear atom strand.
	 * @param no 0 or 1; 0 refers to the higher ranking central atom
	 * @return
	 */
	public int getCentralAtom(int no) {
		return mCentralAtom[no];
		}

	/**
	 * Returns that neighbor atom of a central atom that lies in the rotatable bond axis.
	 * Usually this is the other atom of the rotatable bond. However, if the rotatable
	 * bond is extended by (a) triple bond(s) then getRearAtom(no) returns that sp-atom,
	 * which is attached to the atom returned by getCentralAtom(no).
	 * @param no 0 or 1; 0 refers to that atom being connected to central atom 0
	 * @return
	 */
	public int getRearAtom(int no) {
		return mRearAtom[no];
		}

	/**
	 * Returns the reference atom of one part of the torsion fragment. Two central
	 * and two terminal reference atoms define the strand that torsion angles refer to.
	 * @param no 0 or 1; 0 refers to the side with higher ranking central bond atom
	 * @return
	 */
	public int getReferenceAtom(int no) {
		return mRefAtom[no];
		}

	/**
	 * @return count of linear strand of sp-hybridized atoms between torsion atoms (usually 0)
	 */
	public int getAlkyneAtomCount() {
		return mAlkyneAtomCount;
		}

	/**
	 * Returns the direct surrounding of the previously classified rotatable bond.
	 * This fragment may contain query features to describe characteristics, which
	 * have an influence on torsion angles, but are beyond its atom and bond types
	 * (e.g. aromaticity and ring membership). Stereo centers and ESR attributes
	 * are normalized. Thus, atom parities may be inverted to the source molecule.
	 * @return
	 */
	public StereoMolecule getFragment() {
		return mFragment;
		}

	/**
     * Determines uniquely an identifying name for the rotatable bond and its vicinity.
     * If the bond is not a single bond, is aromatic, is a member of a <=5-membered ring,
     * if one of its atoms has 0,3 or more neighbours other than the other bond atom,
     * or if one of its atoms is a stereo center with unknown parity, then the classification fails.
     * The four atoms, that define the torsion angle, are determined and are available through
     * getReferenceAtom(0 or 1) and getCentralAtom(0 or 1). A fragment being unique for the
     * this particular torsion situation is determined.
     * If one of the bond's atoms is a stereo center, this fragment may be inverted for
     * normalization, which would be represented with a trailing '<'. A trailing '>' indicates
     * a non-inverted stereo center. If bond is part of a consecutive sp-sp atom chain, then
     * the classifying fragment covers all linear atoms plus two end atoms not being member
     * of the linear sp-atom strand.
     * If a TorsionDetail is passed, then this will be filled.
	 * @param mol
	 * @param bond the rotatable bond 
	 * @return true if a valid torsion identifier could be determined
	 */
	public boolean classify(StereoMolecule mol, int bond) {
		mFragment.clear();
		mID = null;

        mol.ensureHelperArrays(Molecule.cHelperSymmetrySimple);

        if (mol.getBondOrder(bond) != 1
         || mol.isAromaticBond(bond))
        	return false;

        if (mol.getAtomicNo(mol.getBondAtom(0, bond)) == 1
         || mol.getAtomicNo(mol.getBondAtom(1, bond)) == 1)
        	return false;

        boolean isSmallRingBond = mol.isSmallRingBond(bond);
        if (isSmallRingBond && mol.getBondRingSize(bond) < 6)  // 3- to 5-membered rings
            return false;

        // create fragment with all properties encoded as query features
        boolean[] atomMask = new boolean[mol.getAtoms()];

    	mAlkyneAtomCount = 0;
        for (int i=0; i<2; i++) {
        	mCentralAtom[i] = mol.getBondAtom(i, bond);
        	mRearAtom[i] = mol.getBondAtom(1-i, bond);

        	// walk along sp-chains to first sp2 or sp3 atom
        	while (mol.getAtomPi(mCentralAtom[i]) == 2
        		&& mol.getNonHydrogenNeighbourCount(mCentralAtom[i]) == 2
        		&& mol.getAtomicNo(mCentralAtom[i]) < 10) {
        		for (int j=0; j 4
             || nonHNeighbours == 1)
        		return false;

        	atomMask[mCentralAtom[i]] = true;
        	}

        for (int i=0; i<2; i++) {
	        for (int j=0; j")
                          : (symmetryType == SYMMETRY_C1D2) ?          (isInverted ? "-" : "+")
                          : (symmetryType == SYMMETRY_D1D2_OR_D2D2) ?                 "=" : "";

        mID = idcode + symmetryID;
        return true;
		}

	/**
     * Tries to uniquely determine one of the terminal neighbor's of atom in mFragment
     * to serve as reference atom that any torsion angles are assigned to.
     * The logic is as follows: If we have one terminal neighbor, this is selected.
     * If there is a neighbor with a unique symmetry rank, then the one with the highest
     * rank is selected. If three neighbors share the same rank, then one of them is
     * selected. If we have two neighbors that share the same rank, then if atom is sp2
     * then one of them is selected. If atom is sp3 then we have to refer to a virtual
     * neighbor (-1) that is assumed to be at the third sp3 position.
     * @param atom one of the bond atoms of the rotatable bond
     * @param remoteBondAtom the remote atom of the rotatable bond
     * @return a unique neighbour atom or -1 if we have a virtual neighbor
     */
    private int getReferenceNeighbor(int atom, int remoteBondAtom) {
        int maxConn = -1;
        int maxRank = -1;
        int symConn = -1;
        boolean[] connHandled = new boolean[mFragment.getConnAtoms(atom)];
        for (int i=0; i just take one of them
                                equalRankFound = true;
                                }
                            }
                        if (!equalRankFound) {
                            maxRank = connRank;
                            maxConn = conn;
                            }
                        else {
                            symConn = conn;
                        	}
                        }
                    }
                }
            }

        if (maxConn == -1) // no outer neighbor with unique rank found
        	if (isFlatAtom(atom))
        		return symConn;

        return maxConn;	// may be -1 if we have two outer neighbors with same rank and atom is SP3
        }

    /**
     * Checks whether atom is sp2 hybridized.
     * Amide nitrogens are also considered to be sp2.
     * @param atom
     * @return
     */
    private boolean isFlatAtom(int atom) {
	    return (mFragment.getAtomPi(atom) == 1 && mFragment.getAtomicNo(atom)<10)
			    || mFragment.isAromaticAtom(atom)
			    || mFragment.isFlatNitrogen(atom);
        }

    /**
     * Determines the symmetry of one end of the 4-atom sequence,
     * which may be one of:
     * HALF_SYMMETRY_C1: not symmetric due to stereo center or tetrahedral nitrogen.
     * HALF_SYMMETRY_D1: mirror plane due to one terminal atom only
     * or at least 2 symmetrical atoms at sp3 center.
     * HALF_SYMMETRY_D2: two symmetrical atoms at sp2 center.
     * The fragment's helper array level should be cHelperSymmetrySimple.
     * @param atom one of the bond atoms of the rotatable bond
     * @param rearAtom the remote atom of the rotatable bond
     * @return
     */
    private int getHalfSymmetry(int atom, int rearAtom) {
        if (mFragment.getConnAtoms(atom) == 2)
            return HALF_SYMMETRY_D1;

        int[] connAtom = getTerminalAtoms(atom, rearAtom);

        if (mFragment.getConnAtoms(atom) == 3) {
            if (mFragment.getSymmetryRank(connAtom[0]) == mFragment.getSymmetryRank(connAtom[1]))
                return isFlatAtom(atom) ? HALF_SYMMETRY_D2 : HALF_SYMMETRY_D1;
            else
                return isFlatAtom(atom) ? HALF_SYMMETRY_D1 : HALF_SYMMETRY_C1;
            }

        if (mFragment.getConnAtoms(atom) == 4) {
            // two equal ranks with additional neighbor that will serve as reference atom
            for (int i=0; i




© 2015 - 2024 Weber Informatics LLC | Privacy Policy