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

org.biojava.nbio.structure.Calc Maven / Gradle / Ivy

The newest version!
/*
 *                    BioJava development code
 *
 * This code may be freely distributed and modified under the
 * terms of the GNU Lesser General Public Licence.  This should
 * be distributed with the code.  If you do not have a copy,
 * see:
 *
 *      http://www.gnu.org/copyleft/lesser.html
 *
 * Copyright for this code is held jointly by the individual
 * authors.  These should be listed in @author doc comments.
 *
 * For more information on the BioJava project and its aims,
 * or to join the biojava-l mailing list, visit the home page
 * at:
 *
 *      http://www.biojava.org/
 *
 * Created on 08.05.2004
 *
 */
package org.biojava.nbio.structure ;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

import javax.vecmath.Matrix3d;
import javax.vecmath.Matrix4d;
import javax.vecmath.Point3d;
import javax.vecmath.Vector3d;

import org.biojava.nbio.structure.geometry.CalcPoint;
import org.biojava.nbio.structure.geometry.Matrices;
import org.biojava.nbio.structure.geometry.SuperPositionSVD;
import org.biojava.nbio.structure.jama.Matrix;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * Utility operations on Atoms, AminoAcids, Matrices, Point3d, etc.
 * 

* Currently the coordinates of an Atom are stored as an array of size 3 * (double[3]). It would be more powerful to use Point3d from javax.vecmath. * Overloaded methods for Point3d operations exist in the {@link CalcPoint} * Class. * * @author Andreas Prlic * @author Aleix Lafita * @since 1.4 * @version %I% %G% */ public class Calc { private final static Logger logger = LoggerFactory.getLogger(Calc.class); /** * calculate distance between two atoms. * * @param a * an Atom object * @param b * an Atom object * @return a double */ public static final double getDistance(Atom a, Atom b) { double x = a.getX() - b.getX(); double y = a.getY() - b.getY(); double z = a.getZ() - b.getZ(); double s = x * x + y * y + z * z; return Math.sqrt(s); } /** * Will calculate the square of distances between two atoms. This will be * faster as it will not perform the final square root to get the actual * distance. Use this if doing large numbers of distance comparisons - it is * marginally faster than getDistance(). * * @param a * an Atom object * @param b * an Atom object * @return a double */ public static double getDistanceFast(Atom a, Atom b) { double x = a.getX() - b.getX(); double y = a.getY() - b.getY(); double z = a.getZ() - b.getZ(); return x * x + y * y + z * z; } public static final Atom invert(Atom a) { double[] coords = new double[]{0.0,0.0,0.0} ; Atom zero = new AtomImpl(); zero.setCoords(coords); return subtract(zero, a); } /** * add two atoms ( a + b). * * @param a * an Atom object * @param b * an Atom object * @return an Atom object */ public static final Atom add(Atom a, Atom b){ Atom c = new AtomImpl(); c.setX( a.getX() + b.getX() ); c.setY( a.getY() + b.getY() ); c.setZ( a.getZ() + b.getZ() ); return c ; } /** * subtract two atoms ( a - b). * * @param a * an Atom object * @param b * an Atom object * @return n new Atom object representing the difference */ public static final Atom subtract(Atom a, Atom b) { Atom c = new AtomImpl(); c.setX( a.getX() - b.getX() ); c.setY( a.getY() - b.getY() ); c.setZ( a.getZ() - b.getZ() ); return c ; } /** * Vector product (cross product). * * @param a * an Atom object * @param b * an Atom object * @return an Atom object */ public static final Atom vectorProduct(Atom a , Atom b){ Atom c = new AtomImpl(); c.setX( a.getY() * b.getZ() - a.getZ() * b.getY() ) ; c.setY( a.getZ() * b.getX() - a.getX() * b.getZ() ) ; c.setZ( a.getX() * b.getY() - a.getY() * b.getX() ) ; return c ; } /** * Scalar product (dot product). * * @param a * an Atom object * @param b * an Atom object * @return a double */ public static final double scalarProduct(Atom a, Atom b) { return a.getX() * b.getX() + a.getY() * b.getY() + a.getZ() * b.getZ(); } /** * Gets the length of the vector (2-norm) * * @param a * an Atom object * @return Square root of the sum of the squared elements */ public static final double amount(Atom a){ return Math.sqrt(scalarProduct(a,a)); } /** * Gets the angle between two vectors * * @param a * an Atom object * @param b * an Atom object * @return Angle between a and b in degrees, in range [0,180]. If either * vector has length 0 then angle is not defined and NaN is returned */ public static final double angle(Atom a, Atom b){ Vector3d va = new Vector3d(a.getCoordsAsPoint3d()); Vector3d vb = new Vector3d(b.getCoordsAsPoint3d()); return Math.toDegrees(va.angle(vb)); } /** * Returns the unit vector of vector a . * * @param a * an Atom object * @return an Atom object */ public static final Atom unitVector(Atom a) { double amount = amount(a) ; double[] coords = new double[3]; coords[0] = a.getX() / amount ; coords[1] = a.getY() / amount ; coords[2] = a.getZ() / amount ; a.setCoords(coords); return a; } /** * Calculate the torsion angle, i.e. the angle between the normal vectors of * the two plains a-b-c and b-c-d. See * http://en.wikipedia.org/wiki/Dihedral_angle * * @param a * an Atom object * @param b * an Atom object * @param c * an Atom object * @param d * an Atom object * @return the torsion angle in degrees, in range +-[0,180]. If either first * 3 or last 3 atoms are colinear then torsion angle is not defined * and NaN is returned */ public static final double torsionAngle(Atom a, Atom b, Atom c, Atom d) { Atom ab = subtract(a,b); Atom cb = subtract(c,b); Atom bc = subtract(b,c); Atom dc = subtract(d,c); Atom abc = vectorProduct(ab,cb); Atom bcd = vectorProduct(bc,dc); double angl = angle(abc,bcd) ; /* calc the sign: */ Atom vecprod = vectorProduct(abc,bcd); double val = scalarProduct(cb,vecprod); if (val < 0.0) angl = -angl; return angl; } /** * Calculate the phi angle. * * @param a * an AminoAcid object * @param b * an AminoAcid object * @return a double * @throws StructureException * if aminoacids not connected or if any of the 4 needed atoms * missing */ public static final double getPhi(AminoAcid a, AminoAcid b) throws StructureException { if ( ! isConnected(a,b)){ throw new StructureException( "can not calc Phi - AminoAcids are not connected!"); } Atom a_C = a.getC(); Atom b_N = b.getN(); Atom b_CA = b.getCA(); Atom b_C = b.getC(); // C and N were checked in isConnected already if (b_CA == null) throw new StructureException( "Can not calculate Phi, CA atom is missing"); return torsionAngle(a_C,b_N,b_CA,b_C); } /** * Calculate the psi angle. * * @param a * an AminoAcid object * @param b * an AminoAcid object * @return a double * @throws StructureException * if aminoacids not connected or if any of the 4 needed atoms * missing */ public static final double getPsi(AminoAcid a, AminoAcid b) throws StructureException { if ( ! isConnected(a,b)) { throw new StructureException( "can not calc Psi - AminoAcids are not connected!"); } Atom a_N = a.getN(); Atom a_CA = a.getCA(); Atom a_C = a.getC(); Atom b_N = b.getN(); // C and N were checked in isConnected already if (a_CA == null) throw new StructureException( "Can not calculate Psi, CA atom is missing"); return torsionAngle(a_N,a_CA,a_C,b_N); } /** * Test if two amino acids are connected, i.e. if the distance from C to N < * 2.5 Angstrom. *

* If one of the AminoAcids has an atom missing, returns false. * * @param a * an AminoAcid object * @param b * an AminoAcid object * @return true if ... */ public static final boolean isConnected(AminoAcid a, AminoAcid b) { Atom C = null ; Atom N = null; C = a.getC(); N = b.getN(); if ( C == null || N == null) return false; // one could also check if the CA atoms are < 4 A... double distance = getDistance(C,N); return distance < 2.5; } /** * Rotate a single Atom aroud a rotation matrix. The rotation Matrix must be * a pre-multiplication 3x3 Matrix. * * If the matrix is indexed m[row][col], then the matrix will be * pre-multiplied (y=atom*M) * * @param atom * atom to be rotated * @param m * a rotation matrix represented as a double[3][3] array */ public static final void rotate(Atom atom, double[][] m){ double x = atom.getX(); double y = atom.getY() ; double z = atom.getZ(); double nx = m[0][0] * x + m[0][1] * y + m[0][2] * z ; double ny = m[1][0] * x + m[1][1] * y + m[1][2] * z ; double nz = m[2][0] * x + m[2][1] * y + m[2][2] * z ; atom.setX(nx); atom.setY(ny); atom.setZ(nz); } /** * Rotate a structure. The rotation Matrix must be a pre-multiplication * Matrix. * * @param structure * a Structure object * @param rotationmatrix * an array (3x3) of double representing the rotation matrix. * @throws StructureException * ... */ public static final void rotate(Structure structure, double[][] rotationmatrix) throws StructureException { if ( rotationmatrix.length != 3 ) { throw new StructureException ("matrix does not have size 3x3 !"); } AtomIterator iter = new AtomIterator(structure) ; while (iter.hasNext()) { Atom atom = iter.next() ; Calc.rotate(atom,rotationmatrix); } } /** * Rotate a Group. The rotation Matrix must be a pre-multiplication Matrix. * * @param group * a group object * @param rotationmatrix * an array (3x3) of double representing the rotation matrix. * @throws StructureException * ... */ public static final void rotate(Group group, double[][] rotationmatrix) throws StructureException { if ( rotationmatrix.length != 3 ) { throw new StructureException ("matrix does not have size 3x3 !"); } AtomIterator iter = new AtomIterator(group) ; while (iter.hasNext()) { Atom atom = null ; atom = iter.next() ; rotate(atom,rotationmatrix); } } /** * Rotate an Atom around a Matrix object. The rotation Matrix must be a * pre-multiplication Matrix. * * @param atom * atom to be rotated * @param m * rotation matrix to be applied to the atom */ public static final void rotate(Atom atom, Matrix m){ double x = atom.getX(); double y = atom.getY(); double z = atom.getZ(); double[][] ad = new double[][]{{x,y,z}}; Matrix am = new Matrix(ad); Matrix na = am.times(m); atom.setX(na.get(0,0)); atom.setY(na.get(0,1)); atom.setZ(na.get(0,2)); } /** * Rotate a group object. The rotation Matrix must be a pre-multiplication * Matrix. * * @param group * a group to be rotated * @param m * a Matrix object representing the rotation matrix */ public static final void rotate(Group group, Matrix m){ AtomIterator iter = new AtomIterator(group) ; while (iter.hasNext()) { Atom atom = iter.next() ; rotate(atom,m); } } /** * Rotate a structure object. The rotation Matrix must be a * pre-multiplication Matrix. * * @param structure * the structure to be rotated * @param m * rotation matrix to be applied */ public static final void rotate(Structure structure, Matrix m){ AtomIterator iter = new AtomIterator(structure) ; while (iter.hasNext()) { Atom atom = iter.next() ; rotate(atom,m); } } /** * Transform an array of atoms at once. The transformation Matrix must be a * post-multiplication Matrix. * * @param ca * array of Atoms to shift * @param t * transformation Matrix4d */ public static void transform(Atom[] ca, Matrix4d t) { for (Atom atom : ca) Calc.transform(atom, t); } /** * Transforms an atom object, given a Matrix4d (i.e. the vecmath library * double-precision 4x4 rotation+translation matrix). The transformation * Matrix must be a post-multiplication Matrix. * * @param atom * @param m */ public static final void transform (Atom atom, Matrix4d m) { Point3d p = new Point3d(atom.getX(),atom.getY(),atom.getZ()); m.transform(p); atom.setX(p.x); atom.setY(p.y); atom.setZ(p.z); } /** * Transforms a group object, given a Matrix4d (i.e. the vecmath library * double-precision 4x4 rotation+translation matrix). The transformation * Matrix must be a post-multiplication Matrix. * * @param group * @param m */ public static final void transform(Group group, Matrix4d m) { for (Atom atom : group.getAtoms()) { transform(atom, m); } for (Group altG : group.getAltLocs()) { transform(altG, m); } } /** * Transforms a structure object, given a Matrix4d (i.e. the vecmath library * double-precision 4x4 rotation+translation matrix). The transformation * Matrix must be a post-multiplication Matrix. * * @param structure * @param m */ public static final void transform(Structure structure, Matrix4d m) { for (int n=0; n .999d || m22 < -.999d) { rZ1 = Math.toDegrees(Math.atan2(m.get(1,0), m.get(1,1))); rZ2 = 0; } else { rZ1 = Math.toDegrees(Math.atan2(m.get(2,1), -m.get(2,0))); rZ2 = Math.toDegrees(Math.atan2(m.get(1,2), m.get(0,2))); } return new double[] {rZ1,rY,rZ2}; } /** * Convert a rotation Matrix to Euler angles. This conversion uses * conventions as described on page: * http://www.euclideanspace.com/maths/geometry/rotations/euler/index.htm * Coordinate System: right hand Positive angle: right hand Order of euler * angles: heading first, then attitude, then bank * * @param m * the rotation matrix * @return a array of three doubles containing the three euler angles in * radians */ public static final double[] getXYZEuler(Matrix m){ double heading, attitude, bank; // Assuming the angles are in radians. if (m.get(1,0) > 0.998) { // singularity at north pole heading = Math.atan2(m.get(0,2),m.get(2,2)); attitude = Math.PI/2; bank = 0; } else if (m.get(1,0) < -0.998) { // singularity at south pole heading = Math.atan2(m.get(0,2),m.get(2,2)); attitude = -Math.PI/2; bank = 0; } else { heading = Math.atan2(-m.get(2,0),m.get(0,0)); bank = Math.atan2(-m.get(1,2),m.get(1,1)); attitude = Math.asin(m.get(1,0)); } return new double[] { heading, attitude, bank }; } /** * This conversion uses NASA standard aeroplane conventions as described on * page: * http://www.euclideanspace.com/maths/geometry/rotations/euler/index.htm * Coordinate System: right hand Positive angle: right hand Order of euler * angles: heading first, then attitude, then bank. matrix row column * ordering: [m00 m01 m02] [m10 m11 m12] [m20 m21 m22] * * @param heading * in radians * @param attitude * in radians * @param bank * in radians * @return the rotation matrix */ public static final Matrix matrixFromEuler(double heading, double attitude, double bank) { // Assuming the angles are in radians. double ch = Math.cos(heading); double sh = Math.sin(heading); double ca = Math.cos(attitude); double sa = Math.sin(attitude); double cb = Math.cos(bank); double sb = Math.sin(bank); Matrix m = new Matrix(3,3); m.set(0,0, ch * ca); m.set(0,1, sh*sb - ch*sa*cb); m.set(0,2, ch*sa*sb + sh*cb); m.set(1,0, sa); m.set(1,1, ca*cb); m.set(1,2, -ca*sb); m.set(2,0, -sh*ca); m.set(2,1, sh*sa*cb + ch*sb); m.set(2,2, -sh*sa*sb + ch*cb); return m; } /** * Calculates the angle from centerPt to targetPt in degrees. The return * should range from [0,360), rotating CLOCKWISE, 0 and 360 degrees * represents NORTH, 90 degrees represents EAST, etc... * * Assumes all points are in the same coordinate space. If they are not, you * will need to call SwingUtilities.convertPointToScreen or equivalent on * all arguments before passing them to this function. * * @param centerPt * Point we are rotating around. * @param targetPt * Point we want to calculate the angle to. * @return angle in degrees. This is the angle from centerPt to targetPt. */ public static double calcRotationAngleInDegrees(Atom centerPt, Atom targetPt) { // calculate the angle theta from the deltaY and deltaX values // (atan2 returns radians values from [-PI,PI]) // 0 currently points EAST. // NOTE: By preserving Y and X param order to atan2, we are expecting // a CLOCKWISE angle direction. double theta = Math.atan2(targetPt.getY() - centerPt.getY(), targetPt.getX() - centerPt.getX()); // rotate the theta angle clockwise by 90 degrees // (this makes 0 point NORTH) // NOTE: adding to an angle rotates it clockwise. // subtracting would rotate it counter-clockwise theta += Math.PI/2.0; // convert from radians to degrees // this will give you an angle from [0->270],[-180,0] double angle = Math.toDegrees(theta); // convert to positive range [0-360) // since we want to prevent negative angles, adjust them now. // we can assume that atan2 will not return a negative value // greater than one partial rotation if (angle < 0) { angle += 360; } return angle; } public static void main(String[] args){ Atom a =new AtomImpl(); a.setX(0); a.setY(0); a.setZ(0); Atom b = new AtomImpl(); b.setX(1); b.setY(1); b.setZ(0); logger.info("Angle between atoms: ", calcRotationAngleInDegrees(a, b)); } public static void rotate(Atom[] ca, Matrix matrix) { for (Atom atom : ca) Calc.rotate(atom, matrix); } /** * Shift an array of atoms at once. * * @param ca * array of Atoms to shift * @param b * reference Atom vector */ public static void shift(Atom[] ca, Atom b) { for (Atom atom : ca) Calc.shift(atom, b); } /** * Convert JAMA rotation and translation to a Vecmath transformation matrix. * Because the JAMA matrix is a pre-multiplication matrix and the Vecmath * matrix is a post-multiplication one, the rotation matrix is transposed to * ensure that the transformation they produce is the same. * * @param rot * 3x3 Rotation matrix * @param trans * 3x1 translation vector in Atom coordinates * @return 4x4 transformation matrix */ public static Matrix4d getTransformation(Matrix rot, Atom trans) { return new Matrix4d(new Matrix3d(rot.getColumnPackedCopy()), new Vector3d(trans.getCoordsAsPoint3d()), 1.0); } /** * Extract the translational vector as an Atom of a transformation matrix. * * @param transform * Matrix4d * @return Atom shift vector */ public static Atom getTranslationVector(Matrix4d transform){ Atom transl = new AtomImpl(); double[] coords = {transform.m03, transform.m13, transform.m23}; transl.setCoords(coords); return transl; } /** * Convert an array of atoms into an array of vecmath points * * @param atoms * list of atoms * @return list of Point3ds storing the x,y,z coordinates of each atom */ public static Point3d[] atomsToPoints(Atom[] atoms) { Point3d[] points = new Point3d[atoms.length]; for(int i = 0; i< atoms.length;i++) { points[i] = atoms[i].getCoordsAsPoint3d(); } return points; } /** * Convert an array of atoms into an array of vecmath points * * @param atoms * list of atoms * @return list of Point3ds storing the x,y,z coordinates of each atom */ public static List atomsToPoints(Collection atoms) { ArrayList points = new ArrayList<>(atoms.size()); for (Atom atom : atoms) { points.add(atom.getCoordsAsPoint3d()); } return points; } /** * Calculate the RMSD of two Atom arrays, already superposed. * * @param x * array of Atoms superposed to y * @param y * array of Atoms superposed to x * @return RMSD */ public static double rmsd(Atom[] x, Atom[] y) { return CalcPoint.rmsd(atomsToPoints(x), atomsToPoints(y)); } /** * Calculate the TM-Score for the superposition. * * Normalizes by the minimum-length structure (that is, {@code min\{len1,len2\}}). * * Atom sets must be pre-rotated. * *

* Citation:
* Zhang Y and Skolnick J (2004). "Scoring function for automated * assessment of protein structure template quality". Proteins 57: 702 - * 710. * * @param atomSet1 * atom array 1 * @param atomSet2 * atom array 2 * @param len1 * The full length of the protein supplying atomSet1 * @param len2 * The full length of the protein supplying atomSet2 * @return The TM-Score * @throws StructureException */ public static double getTMScore(Atom[] atomSet1, Atom[] atomSet2, int len1, int len2) throws StructureException { return getTMScore(atomSet1, atomSet2, len1, len2,true); } /** * Calculate the TM-Score for the superposition. * * Atom sets must be pre-rotated. * *

* Citation:
* Zhang Y and Skolnick J (2004). "Scoring function for automated * assessment of protein structure template quality". Proteins 57: 702 - * 710. * * @param atomSet1 * atom array 1 * @param atomSet2 * atom array 2 * @param len1 * The full length of the protein supplying atomSet1 * @param len2 * The full length of the protein supplying atomSet2 * @param normalizeMin * Whether to normalize by the minimum-length structure, * that is, {@code min\{len1,len2\}}. If false, normalized by the {@code max\{len1,len2\}}). * * @return The TM-Score * @throws StructureException */ public static double getTMScore(Atom[] atomSet1, Atom[] atomSet2, int len1, int len2, boolean normalizeMin) throws StructureException { if (atomSet1.length != atomSet2.length) { throw new StructureException( "The two atom sets are not of same length!"); } if (atomSet1.length > len1) { throw new StructureException( "len1 must be greater or equal to the alignment length!"); } if (atomSet2.length > len2) { throw new StructureException( "len2 must be greater or equal to the alignment length!"); } int Lnorm; if (normalizeMin) { Lnorm = Math.min(len1, len2); } else { Lnorm = Math.max(len1, len2); } int Laln = atomSet1.length; double d0 = 1.24 * Math.cbrt(Lnorm - 15.) - 1.8; double d0sq = d0 * d0; double sum = 0; for (int i = 0; i < Laln; i++) { double d = Calc.getDistance(atomSet1[i], atomSet2[i]); sum += 1. / (1 + d * d / d0sq); } return sum / Lnorm; } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy