com.actelion.research.chem.conf.TorsionDetail Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of openchemlib Show documentation
Show all versions of openchemlib Show documentation
Open Source Chemistry Library
/*
* 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