def pairwiseRmsd( self, aMask=None, noFit=0 ): """ Calculate rmsd between each 2 coordinate frames. :param aMask: atom mask :type aMask: [1|0] :return: frames x frames array of float :rtype: array """ frames = self.frames if aMask is not None: frames = N0.compress( aMask, frames, 1 ) result = N0.zeros( (len( frames ), len( frames )), N0.Float32 ) for i in range(0, len( frames ) ): for j in range( i+1, len( frames ) ): if noFit: d = N0.sqrt(N0.sum(N0.power(frames[i]-frames[j], 2), 1)) result[i,j] = result[j,i] = N0.sqrt( N0.average(d**2) ) else: rt, rmsdLst = rmsFit.match( frames[i], frames[j], 1 ) result[i,j] = result[j,i] = rmsdLst[0][1] return result
def centerSurfDist(model, surf_mask, mask=None): """ Calculate the longest and shortest distance from the center of the molecule to the surface. @param mask: atoms not to be considerd (default: None) @type mask: [1|0] @param surf_mask: atom surface mask, needed for minimum surface distance @type surf_mask: [1|0] @return: max distance, min distance @rtype: float, float """ if mask is None: mask = model.maskHeavy() ## calculate center of mass center = model.centerOfMass() ## surface atom coordinates surf_xyz = N0.compress(mask * surf_mask, model.getXyz(), 0) ## find the atom closest and furthest away from center dist = N0.sqrt(N0.sum((surf_xyz - center)**2, 1)) minDist = min(dist) maxDist = max(dist) return maxDist, minDist
def projectOnSphere(xyz, radius=None, center=None): """ Project the coordinates xyz on a sphere with a given radius around a given center. :param xyz: cartesian coordinates :type xyz: array N x 3 of float :param radius: radius of target sphere, if not provided the maximal distance to center will be used (default: None) :type radius: float :param center: center of the sphere, if not given the average of xyz will be assigned to the center (default: None) :type center: array 0 x 3 of float :return: array of cartesian coordinates (x, y, z) :rtype: array """ if center is None: center = N0.average(xyz) if radius is None: radius = max(N0.sqrt(N0.sum(N0.power(xyz - center, 2), 1))) rtp = cartesianToPolar(xyz - center) rtp[:, 0] = radius return polarToCartesian(rtp) + center
def __pairwiseDistances(self, u, v): """ pairwise distance between 2 3-D numpy arrays of atom coordinates. @param u: coordinates @type u: array @param v: coordinates @type v: array @return: Numpy array len(u) x len(v) @rtype:array @author: Wolfgang Rieping. """ ## check input if not type( u ) == arraytype or\ not type( v ) == arraytype: raise ComplexError('unsupported argument type ' + \ str( type(u) ) + ' or ' + str( type(v) ) ) diag1= N0.diagonal(N0.dot(u,N0.transpose(u))) diag2= N0.diagonal(N0.dot(v,N0.transpose(v))) dist= -N0.dot(v,N0.transpose(u))-N0.transpose(N0.dot(u,N0.transpose(v))) dist= N0.transpose(N0.asarray(list(map(lambda column,a:column+a, \ N0.transpose(dist), diag1)))) return N0.transpose(N0.sqrt(N0.asarray( list(map(lambda row,a: row+a, dist, diag2)))))
def projectOnSphere( xyz, radius=None, center=None ): """ Project the coordinates xyz on a sphere with a given radius around a given center. :param xyz: cartesian coordinates :type xyz: array N x 3 of float :param radius: radius of target sphere, if not provided the maximal distance to center will be used (default: None) :type radius: float :param center: center of the sphere, if not given the average of xyz will be assigned to the center (default: None) :type center: array 0 x 3 of float :return: array of cartesian coordinates (x, y, z) :rtype: array """ if center is None: center = N0.average( xyz ) if radius is None: radius = max( N0.sqrt( N0.sum( N0.power( xyz - center, 2 ), 1 ) ) ) rtp = cartesianToPolar( xyz - center ) rtp[ :, 0 ] = radius return polarToCartesian( rtp ) + center
def __pairwiseDistances(self, u, v): """ pairwise distance between 2 3-D numpy arrays of atom coordinates. @param u: coordinates @type u: array @param v: coordinates @type v: array @return: Numpy array len(u) x len(v) @rtype:array @author: Wolfgang Rieping. """ ## check input if not type( u ) == arraytype or\ not type( v ) == arraytype: raise ComplexError('unsupported argument type ' + \ str( type(u) ) + ' or ' + str( type(v) ) ) diag1 = N0.diagonal(N0.dot(u, N0.transpose(u))) diag2 = N0.diagonal(N0.dot(v, N0.transpose(v))) dist = -N0.dot(v, N0.transpose(u)) - N0.transpose( N0.dot(u, N0.transpose(v))) dist= N0.transpose(N0.asarray(list(map(lambda column,a:column+a, \ N0.transpose(dist), diag1)))) return N0.transpose( N0.sqrt(N0.asarray(list(map(lambda row, a: row + a, dist, diag2)))))
def SD(x, avg = None): """ Standard deviation, S{sigma} :param x: data :type x: array('f') or float :param avg: use this average, otherwise calculated from x :type avg: float OR None :return: float :rtype: float """ return N0.sqrt(variance(x, avg))
def dihedral( self, coor1, coor2, coor3, coor4 ): """ Calculates the torsion angle of a set of four atom coordinates. The dihedral angle returned is the angle between the projection of i1-i2 and the projection of i4-i3 onto a plane normal to i2-i3. @param coor1: coordinates @type coor1: [float] @param coor2: coordinates @type coor2: [float] @param coor3: coordinates @type coor3: [float] @param coor4: coordinates @type coor4: [float] """ vec21 = coor2 - coor1 vec32 = coor3 - coor2 L = N0.cross( vec21, vec32 ) L_norm = N0.sqrt(sum(L**2)) vec43 = coor4 - coor3 vec23 = coor2 - coor3 R = N0.cross( vec43, vec23 ) R_norm = N0.sqrt(sum(R**2)) S = N0.cross( L, R ) angle = sum( L*R ) / ( L_norm * R_norm ) ## sometimes the value turns out to be ever so little greater than ## one, to prevent N0.arccos errors for this, set angle = 1.0 if angle > 1.0: angle = 1.0 if angle < -1.0: angle = -1.0 angle = N0.arccos(angle) *180/N0.pi if sum(S*vec32) < 0.0: angle = -angle return angle
def wSD(x, w): """ Standard deviation of weighted data. :param x: X-D array with numbers :type x: array :param w: 1-D array of same length as x with weight factors :type w: array :return: array('f') or float :rtype: array('f') or float """ return N0.sqrt(wVar(x, w))
def wSD(x, w): """ Standard deviation of weighted data. :param x: X-D array with numbers :type x: array :param w: 1-D array of same length as x with weight factors :type w: array :return: array('f') or float :rtype: array('f') or float """ return N0.sqrt( wVar(x, w) )
def dihedral(self, coor1, coor2, coor3, coor4): """ Calculates the torsion angle of a set of four atom coordinates. The dihedral angle returned is the angle between the projection of i1-i2 and the projection of i4-i3 onto a plane normal to i2-i3. @param coor1: coordinates @type coor1: [float] @param coor2: coordinates @type coor2: [float] @param coor3: coordinates @type coor3: [float] @param coor4: coordinates @type coor4: [float] """ vec21 = coor2 - coor1 vec32 = coor3 - coor2 L = N0.cross(vec21, vec32) L_norm = N0.sqrt(sum(L**2)) vec43 = coor4 - coor3 vec23 = coor2 - coor3 R = N0.cross(vec43, vec23) R_norm = N0.sqrt(sum(R**2)) S = N0.cross(L, R) angle = sum(L * R) / (L_norm * R_norm) ## sometimes the value turns out to be ever so little greater than ## one, to prevent N0.arccos errors for this, set angle = 1.0 if angle > 1.0: angle = 1.0 if angle < -1.0: angle = -1.0 angle = N0.arccos(angle) * 180 / N0.pi if sum(S * vec32) < 0.0: angle = -angle return angle
def SD(x, avg=None): """ Standard deviation, S{sigma} :param x: data :type x: array('f') or float :param avg: use this average, otherwise calculated from x :type avg: float OR None :return: float :rtype: float """ return N0.sqrt(variance(x, avg))
def __random_translation( self ): """ Random translation on a sphere around 0,0,0 with fixed radius The radius is the sum of the (max) radius of receptor and ligand @return: translation array 3 x 1 of float @rtype: array """ radius = (self.d_max_rec + self.d_max_lig) / 2.0 xyz = R.random_sample( 3 ) - 0.5 scale = radius*1.0 / N0.sqrt( N0.sum( xyz**2 ) ) return scale * xyz
def __max_distance( self, model ): """ largest center to any other atom distance @param model: model with centered coordinates @type model: PDBModel @return: largest distance @rtype: float """ center = model.centerOfMass() dist = N0.sqrt( N0.sum( ( model.getXyz()-center )**2 , 1 ) ) return max( dist )
def pairwiseDistances(u, v): """ Pairwise distances between two arrays. :param u: first array :type u: array :param v: second array :type v: array :return: array( len(u) x len(v) ) of double :rtype: array """ diag1 = N0.diagonal( N0.dot( u, N0.transpose(u) ) ) diag2 = N0.diagonal( N0.dot( v, N0.transpose(v) ) ) dist = -N0.dot( v,N0.transpose(u) )\ -N0.transpose( N0.dot( u, N0.transpose(v) ) ) dist = N0.transpose( N0.asarray( list(map( lambda column,a:column+a, \ N0.transpose(dist), diag1)) ) ) return N0.transpose( N0.sqrt( N0.asarray( list(map( lambda row,a: row+a, dist, diag2 ) ) )))
def getFluct_global( self, mask=None ): """ Get RMS of each atom from it's average position in trajectory. The frames should be superimposed (fit() ) to a reference. :param mask: N x 1 list/Numpy array of 0|1, (N=atoms), atoms to be considered. :type mask: [1|0] :return: Numpy array ( N_unmasked x 1 ) of float. :rtype: array """ frames = self.frames if mask is not None: frames = N0.compress( mask, frames, 1 ) ## mean position of each atom in all frames avg = N0.average( frames ) return N0.average(N0.sqrt(N0.sum(N0.power(frames - avg, 2), 2) ))
def pairwiseDistances(u, v): """ Pairwise distances between two arrays. :param u: first array :type u: array :param v: second array :type v: array :return: array( len(u) x len(v) ) of double :rtype: array """ diag1 = N0.diagonal(N0.dot(u, N0.transpose(u))) diag2 = N0.diagonal(N0.dot(v, N0.transpose(v))) dist = -N0.dot( v,N0.transpose(u) )\ -N0.transpose( N0.dot( u, N0.transpose(v) ) ) dist = N0.transpose( N0.asarray( list(map( lambda column,a:column+a, \ N0.transpose(dist), diag1)) ) ) return N0.transpose( N0.sqrt(N0.asarray(list(map(lambda row, a: row + a, dist, diag2)))))
def rowDistances( x, y ): """ Calculate the distances between the items of two arrays (of same shape) after least-squares superpositioning. :param x: first set of coordinates :type x: array('f') :param y: second set of coordinates :type y: array('f') :return: array( len(x), 'f' ), distance between x[i] and y[i] for all i :rtype: array """ ## find transformation for best match r, t = findTransformation(x, y) ## transform coordinates z = N0.dot(y, N0.transpose(r)) + t ## calculate row distances return N0.sqrt(N0.sum(N0.power(x - z, 2), 1))
def cartesianToPolar(xyz): """ Convert cartesian coordinate array to polar coordinate array: C{ x,y,z -> r, S{theta}, S{phi} } :param xyz: array of cartesian coordinates (x, y, z) :type xyz: array :return: array of polar coordinates (r, theta, phi) :rtype: array """ r = N0.sqrt(N0.sum(xyz**2, 1)) p = N0.arccos(xyz[:, 2] / r) ## have to take care of that we end up in the correct quadrant t = [] for i in range(len(xyz)): ## for theta (arctan) t += [math.atan2(xyz[i, 1], xyz[i, 0])] return N0.transpose(N0.concatenate(([r], [t], [p])))
def rowDistances(x, y): """ Calculate the distances between the items of two arrays (of same shape) after least-squares superpositioning. :param x: first set of coordinates :type x: array('f') :param y: second set of coordinates :type y: array('f') :return: array( len(x), 'f' ), distance between x[i] and y[i] for all i :rtype: array """ ## find transformation for best match r, t = findTransformation(x, y) ## transform coordinates z = N0.dot(y, N0.transpose(r)) + t ## calculate row distances return N0.sqrt(N0.sum(N0.power(x - z, 2), 1))
def cartesianToPolar( xyz ): """ Convert cartesian coordinate array to polar coordinate array: C{ x,y,z -> r, S{theta}, S{phi} } :param xyz: array of cartesian coordinates (x, y, z) :type xyz: array :return: array of polar coordinates (r, theta, phi) :rtype: array """ r = N0.sqrt( N0.sum( xyz**2, 1 ) ) p = N0.arccos( xyz[:,2] / r ) ## have to take care of that we end up in the correct quadrant t=[] for i in range(len(xyz)): ## for theta (arctan) t += [math.atan2( xyz[i,1], xyz[i,0] )] return N0.transpose( N0.concatenate( ([r],[t],[p]) ) )
def distance_matrix(x, y): return N0.sqrt(squared_distance_matrix(x, y))
def match(x, y, n_iterations=1, z=2, eps_rmsd=0.5, eps_stdv=0.05): """ Matches two arrays onto each other, while iteratively removing outliers. Superimposed array y would be C{ N0.dot(y, N0.transpose(r)) + t }. :param n_iterations: number of calculations:: 1 .. no iteration 0 .. until convergence :type n_iterations: 1|0 :param z: number of standard deviations for outlier definition (default: 2) :type z: float :param eps_rmsd: tolerance in rmsd (default: 0.5) :type eps_rmsd: float :param eps_stdv: tolerance in standard deviations (default: 0.05) :type eps_stdv: float :return: (r,t), [ [percent_considered, rmsd_for_it, outliers] ] :rtype: (array, array), [float, float, int] """ iter_trace = [] rmsd_old = 0 stdv_old = 0 n = 0 converged = 0 mask = N0.ones(len(y), N0.Int32 ) while not converged: ## find transformation for best match r, t = findTransformation(N0.compress(mask, x, 0), N0.compress(mask, y, 0)) ## transform coordinates xt = N0.dot(y, N0.transpose(r)) + t ## calculate row distances d = N0.sqrt(N0.sum(N0.power(x - xt, 2), 1)) * mask ## calculate rmsd and stdv rmsd = N0.sqrt(N0.average(N0.compress(mask, d)**2)) stdv = MU.SD(N0.compress(mask, d)) ## check conditions for convergence d_rmsd = abs(rmsd - rmsd_old) d_stdv = abs(1 - stdv_old / stdv) if d_rmsd < eps_rmsd and d_stdv < eps_stdv: converged = 1 else: rmsd_old = rmsd stdv_old = stdv ## store result perc = round(float(N0.sum(mask)) / float(len(mask)), 2) ## throw out non-matching rows mask = N0.logical_and(mask, N0.less(d, rmsd + z * stdv)) outliers = N0.nonzero( N0.logical_not( mask ) ) iter_trace.append([perc, round(rmsd, 3), outliers]) n += 1 if n_iterations and n >= n_iterations: break return (r, t), iter_trace
def match(x, y, n_iterations=1, z=2, eps_rmsd=0.5, eps_stdv=0.05): """ Matches two arrays onto each other, while iteratively removing outliers. Superimposed array y would be C{ N0.dot(y, N0.transpose(r)) + t }. :param n_iterations: number of calculations:: 1 .. no iteration 0 .. until convergence :type n_iterations: 1|0 :param z: number of standard deviations for outlier definition (default: 2) :type z: float :param eps_rmsd: tolerance in rmsd (default: 0.5) :type eps_rmsd: float :param eps_stdv: tolerance in standard deviations (default: 0.05) :type eps_stdv: float :return: (r,t), [ [percent_considered, rmsd_for_it, outliers] ] :rtype: (array, array), [float, float, int] """ iter_trace = [] rmsd_old = 0 stdv_old = 0 n = 0 converged = 0 mask = N0.ones(len(y), N0.Int32) while not converged: ## find transformation for best match r, t = findTransformation(N0.compress(mask, x, 0), N0.compress(mask, y, 0)) ## transform coordinates xt = N0.dot(y, N0.transpose(r)) + t ## calculate row distances d = N0.sqrt(N0.sum(N0.power(x - xt, 2), 1)) * mask ## calculate rmsd and stdv rmsd = N0.sqrt(N0.average(N0.compress(mask, d)**2)) stdv = MU.SD(N0.compress(mask, d)) ## check conditions for convergence d_rmsd = abs(rmsd - rmsd_old) d_stdv = abs(1 - stdv_old / stdv) if d_rmsd < eps_rmsd and d_stdv < eps_stdv: converged = 1 else: rmsd_old = rmsd stdv_old = stdv ## store result perc = round(float(N0.sum(mask)) / float(len(mask)), 2) ## throw out non-matching rows mask = N0.logical_and(mask, N0.less(d, rmsd + z * stdv)) outliers = N0.nonzero(N0.logical_not(mask)) iter_trace.append([perc, round(rmsd, 3), outliers]) n += 1 if n_iterations and n >= n_iterations: break return (r, t), iter_trace
def getFluct_local( self, mask=None, border_res=1, left_atoms=['C'], right_atoms=['N'], verbose=1 ): """ Get mean displacement of each atom from it's average position after fitting of each residue to the reference backbone coordinates of itself and selected atoms of neighboring residues to the right and left. :param mask: N_atoms x 1 array of 0||1, atoms for which fluctuation should be calculated :type mask: array :param border_res: number of neighboring residues to use for fitting :type border_res: int :param left_atoms: atoms (names) to use from these neighbore residues :type left_atoms: [str] :param right_atoms: atoms (names) to use from these neighbore residues :type right_atoms: [str] :return: Numpy array ( N_unmasked x 1 ) of float :rtype: array """ if mask is None: mask = N0.ones( len( self.frames[0] ), N0.Int32 ) if verbose: T.errWrite( "rmsd fitting per residue..." ) residues = N0.nonzero( self.ref.atom2resMask( mask ) ) ## backbone atoms used for fit fit_atoms_right = N0.nonzero( self.ref.mask( right_atoms ) ) fit_atoms_left = N0.nonzero( self.ref.mask( left_atoms ) ) ## chain index of each residue rchainMap = N0.take( self.ref.chainMap(), self.ref.resIndex() ) result = [] for res in residues: i_res, i_border = self.__resWindow(res, border_res, rchainMap, fit_atoms_left, fit_atoms_right) try: if not len( i_res ): raise PDBError('empty residue') t_res = self.takeAtoms( i_res + i_border ) i_center = range( len( i_res ) ) mask_BB = t_res.ref.maskBB() * t_res.ref.maskHeavy() ## fit with border atoms .. t_res.fit( ref=t_res.ref, mask=mask_BB, verbose=0 ) ## .. but calculate only with center residue atoms frames = N0.take( t_res.frames, i_center, 1 ) avg = N0.average( frames ) rmsd = N0.average(N0.sqrt(N0.sum(N0.power(frames - avg, 2), 2) )) result.extend( rmsd ) if verbose: T.errWrite('#') except ZeroDivisionError: result.extend( N0.zeros( len(i_res), N0.Float32 ) ) T.errWrite('?' + str( res )) if verbose: T.errWriteln( "done" ) return result
def fit( self, mask=None, ref=None, n_it=1, prof='rms', verbose=1, fit=1, **profInfos ): """ Superimpose all coordinate frames on reference coordinates. Put rms values in a profile. If n_it > 1, the fraction of atoms considered for the fit is put into a profile called |prof|_considered (i.e. by default 'rms_considered'). :param mask: atom mask, atoms to consider default: [all] :type mask: [1|0] :param ref: use as reference, default: None, average Structure :type ref: PDBModel :param n_it: number of fit iterations, kicking out outliers on the way 1 -> classic single fit, 0 -> until convergence (default: 1) :type n_it: int :param prof: save rms per frame in profile of this name, ['rms'] :type prof: str :param verbose: print progress info to STDERR (default: 1) :type verbose: 1|0 :param fit: transform frames after match, otherwise just calc rms (default: 1) :type fit: 1|0 :param profInfos: additional key=value pairs for rms profile info [] :type profInfos: key=value """ if ref is None: refxyz = N0.average( self.frames, 0 ) else: refxyz = ref.getXyz() if mask is None: mask = N0.ones( len( refxyz ), N0.Int32 ) refxyz = N0.compress( mask, refxyz, 0 ) if verbose: T.errWrite( "rmsd fitting..." ) rms = [] ## rms value of each frame non_outliers = [] ## fraction of atoms considered for rms and fit iterations = [] ## number of iterations performed on each frame for i in range(0, len( self.frames) ): xyz = self.frames[i] if n_it != 1: (r, t), rmsdList = rmsFit.match( refxyz, N0.compress( mask, xyz, 0), n_it) iterations.append( len( rmsdList ) ) non_outliers.append( rmsdList[-1][0] ) xyz_transformed = N0.dot( xyz, N0.transpose(r)) + t rms += [ rmsdList[-1][1] ] else: r, t = rmsFit.findTransformation( refxyz, N0.compress( mask, xyz, 0)) xyz_transformed = N0.dot( xyz, N0.transpose(r)) + t d = N0.sqrt(N0.sum(N0.power( N0.compress(mask, xyz_transformed,0)\ - refxyz, 2), 1)) rms += [ N0.sqrt( N0.average(d**2) ) ] if fit: self.frames[i] = xyz_transformed.astype(N0.Float32) if verbose and i%100 == 0: T.errWrite( '#' ) self.setProfile( prof, rms, n_iterations=n_it, **profInfos ) if non_outliers: self.setProfile( prof+'_considered', non_outliers, n_iterations=n_it, comment='fraction of atoms considered for iterative fit' ) if verbose: T.errWrite( 'done\n' )