def takeFrames( self, indices ): """ Return a copy of the trajectory containing only the specified frames. :param indices: positions to take :type indices: [int] :return: copy of this Trajectory (fewer frames, semi-deep copy of ref) :rtype: Trajectory """ ## remove out-of-bound indices indices = N0.compress( N0.less( indices, len( self.frames) ), indices ) r = self.__class__() ## this step takes some time for large frames ! r.frames = N0.take( self.frames, indices, 0 ) ## semi-deep copy of reference model r.setRef( self.ref.take( list(range( self.ref.lenAtoms()))) ) if self.frameNames is not None: r.frameNames = N0.take( self.frameNames, indices, 0 ) r.frameNames = list(map( ''.join, r.frameNames.tolist() )) r.pc = self.__takePca( indices ) r.profiles = self.profiles.take( indices ) r.resIndex = self.resIndex return r
def take(self, rec_pos, lig_pos): """ Get copy of this complex with given atoms of rec and lig. @param rec_pos: receptor indices to take @type rec_pos: [int] @param lig_pos: ligand indices to take @type lig_pos: [int] @return: new complex @rtype: Complex """ r = self.__class__() r.lig_model = self.lig_model.take(lig_pos) r.rec_model = self.rec_model.take(rec_pos) r.info = deepcopy(self.info) if self.pw_dist: r.pw_dist = N0.take(self.pw_dist, rec_pos, 1) r.pw_dist = N0.take(r.pw_dist, lig_pos) r.ligandMatrix = copy(self.ligandMatrix) ## todo: take cached contacts as well return r
def take( self, rec_pos, lig_pos ): """ Get copy of this complex with given atoms of rec and lig. @param rec_pos: receptor indices to take @type rec_pos: [int] @param lig_pos: ligand indices to take @type lig_pos: [int] @return: new complex @rtype: Complex """ r = self.__class__() r.lig_model = self.lig_model.take( lig_pos ) r.rec_model = self.rec_model.take( rec_pos ) r.info = deepcopy( self.info ) if self.pw_dist: r.pw_dist = N0.take( self.pw_dist, rec_pos, 1 ) r.pw_dist = N0.take( r.pw_dist, lig_pos ) r.ligandMatrix = copy( self.ligandMatrix ) ## todo: take cached contacts as well return r
def __thinarray( self, a, step ): """ @param a: input array @type a: N0.array @param step: stepping in both dimensions @type step: int @return: smaller array @rtype: N0.array """ r = N0.take( a, list(range( 0, len(a), step)), axis=0 ) r = N0.take( r, list(range( 0, len(r[0]), step)), axis=1 ) return r
def reduceToModel( self, xyz=None, reduce_profiles=1 ): """ Create a reduced PDBModel from coordinates. Atom profiles the source PDBModel are reduced by averaging over the grouped atoms. @param xyz: coordinte array (N_atoms x 3) or None (->use reference coordinates) @type xyz: array OR None @return: PDBModel with reduced atom set and profile 'mass' @rtype: PDBModel """ mass = self.m.atoms.get('mass') if xyz is None: xyz = self.m.getXyz() mProf = [ N0.sum( N0.take( mass, group ) ) for group in self.groups ] xyz = self.reduceXyz( xyz ) result = PDBModel() for k in self.atoms.keys(): result.atoms.set( k, self.atoms.valuesOf(k) ) ## result.setAtoms( self.atoms ) result.setXyz( xyz ) result.atoms.set( 'mass', mProf ) if reduce_profiles: self.reduceAtomProfiles( self.m, result ) result.residues = self.m.residues return result
def group( self, a_indices, maxPerCenter ): """ Group a bunch of integers (atom indices in PDBModel) so that each group has at most maxPerCenter items. @param a_indices: atom indices @type a_indices: [int] @param maxPerCenter: max entries per group @type maxPerCenter: int @return: list of lists of int @rtype: [[int],[int]..] """ ## how many groups are necessary? n_centers = len( a_indices ) // maxPerCenter ## floor division if len( a_indices ) % maxPerCenter: n_centers += 1 ## how many items/atoms go into each group? nAtoms = N0.ones(n_centers, N0.Int) * int(len( a_indices ) / n_centers) i=0 while N0.sum(nAtoms) != len( a_indices ): nAtoms[i] += 1 i += 1 ## distribute atom indices into groups result = [] pos = 0 for n in nAtoms: result += [ N0.take( a_indices, N0.arange(n) + pos) ] pos += n return result
def tripples(self, lst, n): """ Group items of lst into n tripples with minimal overlap. """ all = [] l = len(lst) ## get all possible tripples for i in range(l): for j in range(i + 1, l): for k in range(j + 1, l): all += [(lst[i], lst[j], lst[k])] ## calculate pairwise "distance" between tripples pw = N0.zeros((len(all), len(all)), N0.Float32) for i in range(len(all)): for j in range(i, len(all)): pw[i, j] = pw[j, i] = len(MU.intersection(all[i], all[j]))**2 pos = 0 r = [] while len(r) < n: r += [pos] ## overlap of selected tripples with all others overlap = N0.sum(N0.array([pw[i] for i in r])) ## select one with lowest overlap to all tripples selected before pos = N0.argmin(overlap) return N0.take(all, r)
def valuesOf(self, infoKey, default=None, indices=None, unique=0): """ Get all values of a certain info record of all or some Complexes. @param infoKey: key for info dict @type infoKey: str @param default: default value if infoKey is not found (None) @type default: any @param indices: list of int OR None(=all), indices of Complexes (None) @type indices: [int] OR None @param unique: report each value only once (set union), (default 0) @type unique: 1|0 @return: list of values @rtype: [any] """ l = self if indices is not None: l = N0.take(N0.array(l, 'O'), indices) if not unique: return [c.info.get(infoKey, default) for c in l] r = [] for c in l: if c.info.get(infoKey, default) not in r: r += [c.info.get(infoKey)] return r
def __translateChainIndices( self, atomIndices, newChainMap ): """ Translate current chain indices into what they would look like in a PDBModel containing only the given atoms in the given order. @param atomIndices: indices of atoms @type atomIndices: [int] @param newChainMap: chain map [0000011133333..] @type newChainMap: [int] @return: { int:int, .. } map current chain indices to new ones @rtype: {int:int} @raise ComplexTrajError: if (parts of) chains are inserted into each other """ ## todo: looks not very elegant oldChainMap = N0.take( self.ref.chainMap(), atomIndices ) r = {} for i in range( len( oldChainMap ) ): old, new = oldChainMap[i], newChainMap[i] if old in r: if r[old] != new: raise ComplexTrajError( "Can't insert different chains into each other.") else: r[old] = new return r
def takeAtoms( self, indices, returnClass=None ): """ Take atoms from frames:: takeAtoms( indices, type=None ) -> copy of Trajectory :param indices: list of atom indices :type indices: [int] :param returnClass: default: None, same class as this object :type returnClass: class OR None :return: copy of this Trajectory (with fewer atoms) :rtype: Trajectory """ returnClass = returnClass or self.__class__ r = returnClass() ## copy over everything, so that child classes can preserve own fields r.__dict__.update( self.__dict__ ) r.frames = r.ref = r.frameNames = r.profiles = None r.frames = N0.take( self.frames, indices, 1 ) r.setRef( self.ref.take( indices ) ) r.frameNames = copy.copy( self.frameNames ) r.resIndex = None r.profiles = self.profiles.clone() r.pc = self.pc ## this is not really clean return r
def tripples( self, lst, n ): """ Group items of lst into n tripples with minimal overlap. """ all = [] l = len( lst ) ## get all possible tripples for i in range( l ): for j in range( i+1, l ): for k in range( j+1, l ): all += [ ( lst[i], lst[j], lst[k] ) ] ## calculate pairwise "distance" between tripples pw = N0.zeros( (len(all), len(all)), N0.Float32 ) for i in range( len( all ) ): for j in range( i, len(all) ): pw[i,j] = pw[j,i] = len( MU.intersection(all[i],all[j]) )**2 pos = 0 r = [] while len( r ) < n: r += [ pos ] ## overlap of selected tripples with all others overlap = N0.sum( N0.array( [ pw[ i ] for i in r ] ) ) ## select one with lowest overlap to all tripples selected before pos = N0.argmin( overlap ) return N0.take( all, r )
def valuesOf(self, infoKey, default=None, indices=None, unique=0 ): """ Get all values of a certain info record of all or some Complexes. @param infoKey: key for info dict @type infoKey: str @param default: default value if infoKey is not found (None) @type default: any @param indices: list of int OR None(=all), indices of Complexes (None) @type indices: [int] OR None @param unique: report each value only once (set union), (default 0) @type unique: 1|0 @return: list of values @rtype: [any] """ l = self if indices is not None: l = N0.take( N0.array(l,'O'), indices ) if not unique: return [ c.info.get(infoKey, default) for c in l ] r = [] for c in l: if c.info.get(infoKey, default) not in r: r += [ c.info.get( infoKey ) ] return r
def __translateChainIndices(self, atomIndices, newChainMap): """ Translate current chain indices into what they would look like in a PDBModel containing only the given atoms in the given order. @param atomIndices: indices of atoms @type atomIndices: [int] @param newChainMap: chain map [0000011133333..] @type newChainMap: [int] @return: { int:int, .. } map current chain indices to new ones @rtype: {int:int} @raise ComplexTrajError: if (parts of) chains are inserted into each other """ ## todo: looks not very elegant oldChainMap = N0.take(self.ref.chainMap(), atomIndices) r = {} for i in range(len(oldChainMap)): old, new = oldChainMap[i], newChainMap[i] if old in r: if r[old] != new: raise ComplexTrajError( "Can't insert different chains into each other.") else: r[old] = new return r
def getResFluct( self, atomFluctList=None ): """ Convert list of atomic fluctuations to list of residue fluctuation. :param atomFluctList: array 1 x N_atoms of float :type atomFluctList: [float] :return: array 1 x N_residues of float :rtype: [float] :raise TrajError: if result length <> N_residues: """ if atomFluctList is None: atomFluctList = self.getFluct_global() ## Give all atoms of each res. the same fluct. value ## (the highest fluctuation of any backbone atom) result = self.residusMaximus( atomFluctList, self.ref.maskBB() ) ## take first atoms only result = N0.take( result, self.ref.resIndex() ) ## result = N0.compress( self.ref.maskCA(), atomFluctList) ## check dimension if len( result ) != self.ref.lenResidues(): raise TrajError( "getResFluct(): Length of result list (%i) <>" % len(result)+ " number of residues (%i)." % self.ref.lenResidues() ) return result
def convertChainIdsNter(self, model, chains): """ Convert normal chain ids to chain ids considering chain breaks. """ if len(chains) == 0: return chains i = N0.take(model.chainIndex(), chains) ## convert back to chain indices but this time including chain breaks return model.atom2chainIndices(i, breaks=1)
def convertChainIdsNter( self, model, chains ): """ Convert normal chain ids to chain ids considering chain breaks. """ if len(chains) == 0: return chains i = N0.take( model.chainIndex(), chains ) ## convert back to chain indices but this time including chain breaks return model.atom2chainIndices( i, breaks=1 )
def convertChainIdsCter( self, model, chains ): """ Convert normal chain ids to chain ids considering chain breaks. """ if len(chains) == 0: return chains ## fetch last atom of given chains index = N0.concatenate( (model.chainIndex(), [len(model)]) ) i = N0.take( index, N0.array( chains ) + 1 ) - 1 ## convert back to chain indices but this time including chain breaks return model.atom2chainIndices( i, breaks=1 )
def convertChainIdsCter(self, model, chains): """ Convert normal chain ids to chain ids considering chain breaks. """ if len(chains) == 0: return chains ## fetch last atom of given chains index = N0.concatenate((model.chainIndex(), [len(model)])) i = N0.take(index, N0.array(chains) + 1) - 1 ## convert back to chain indices but this time including chain breaks return model.atom2chainIndices(i, breaks=1)
def fit(self, traj, refModel=None, mask=None, conv=1e-6): """ Fit trajectory until convergence onto it's own average and then transform the average of all frames onto the reference. :param traj: trajectory in which to fit frames :type traj: Trajectory :param refModel: reference PDBModel :type refModel: PDBModel :param mask: atom mask for superposition (default: all) :type mask: [1|0] :param conv: convergence criteria (default: 1e-6) :type conv: float """ self.fit_e = self.fit_e or len(traj) self.fit_s = self.fit_s or 0 traj.fit(ref=traj.ref, mask=mask, verbose=self.verbose) m_avg = traj[self.fit_s:self.fit_e].avgModel() ## fit on average until it's not getting better d = 1. dd = 1. while dd >= conv: traj.fit(ref=m_avg, mask=mask, verbose=self.verbose) m_new_avg = traj[self.fit_s:self.fit_e].avgModel() oldD, d = d, m_avg.rms(m_new_avg, mask=mask) if self.verbose: self.log.add("rms difference: %f" % d) dd = oldD - d m_avg = m_new_avg ## transform trajectory en block onto reference if refModel: if self.verbose: self.log.add('fitting trajectory en-block onto reference...') if refModel.atomNames() != traj.ref.atomNames(): if self.verbose: self.log.add('casting ref for fitting...') ref_i, i = refModel.compareAtoms(m_avg) refModel = refModel.take(ref_i) m_avg = m_avg.take(i) if not mask is None: mask = N0.take(mask, i) r, t = m_avg.transformation(refModel, mask) traj.transform(r, t)
def fit( self, traj, refModel=None, mask=None, conv=1e-6 ): """ Fit trajectory until convergence onto it's own average and then transform the average of all frames onto the reference. :param traj: trajectory in which to fit frames :type traj: Trajectory :param refModel: reference PDBModel :type refModel: PDBModel :param mask: atom mask for superposition (default: all) :type mask: [1|0] :param conv: convergence criteria (default: 1e-6) :type conv: float """ self.fit_e = self.fit_e or len( traj ) self.fit_s = self.fit_s or 0 traj.fit( ref=traj.ref, mask=mask, verbose=self.verbose ) m_avg = traj[self.fit_s : self.fit_e ].avgModel() ## fit on average until it's not getting better d = 1. dd= 1. while dd >= conv: traj.fit( ref=m_avg, mask=mask, verbose=self.verbose ) m_new_avg = traj[self.fit_s : self.fit_e].avgModel() oldD, d = d, m_avg.rms( m_new_avg, mask=mask ) if self.verbose: self.log.add( "rms difference: %f" % d ) dd = oldD - d m_avg = m_new_avg ## transform trajectory en block onto reference if refModel: if self.verbose: self.log.add('fitting trajectory en-block onto reference...') if refModel.atomNames() != traj.ref.atomNames(): if self.verbose: self.log.add('casting ref for fitting...') ref_i, i = refModel.compareAtoms( m_avg ) refModel = refModel.take( ref_i ) m_avg = m_avg.take( i ) if not mask is None: mask = N0.take( mask, i ) r, t = m_avg.transformation( refModel, mask ) traj.transform( r, t )
def __takePca( self, indices ): """ extract PCA results for certain frames. :param indices: frame indecies :type indices: [int] :return: list of pca values :rtype: [float] """ result = copy.deepcopy( getattr(self, 'pc', None )) if result is not None: result['p'] = N0.take( result['p'], indices, 0 ) result['u'] = N0.take( result['u'], indices, 0 ) if result['fMask'] is not None: result['fMask'] = N0.take( result['fMask'], indices, 0 ) return result
def __resWindow( self, res, n_neighbores, rchainMap=None, left_allowed=None, right_allowed=None ): """ Get indices of all atoms of a residue and some atoms of its neighboring residues (if they belong to the same chain). :param res: residue index :type res: int :param n_neighbores: number of residues to include right and left :type n_neighbores: int :param right_allowed: array 1 x N_atoms of 1|0, possible neighbore atoms :type right_allowed: array :param left_allowed: array 1 x N_atoms of 1|0, possible neighbore atoms :type left_allowed: array :param rchainMap: array 1 x N_residues of int, chain id of each res :type rchainMap: array :return: atoms of res, atoms of neighbores :rtype: [ int ], [ int ] """ ## some defaults.. time-consuming.. if rchainMap is None: rchainMap = N0.take( self.chainMap(), self.resIndex() ) if left_allowed is None: left_allowed = N0.nonzero( self.ref.maskBB() ) if right_allowed is None: right_allowed= N0.nonzero( self.ref.maskBB() ) ## atom indices of center residue result = self.ref.res2atomIndices( [ res ] ).tolist() ## get indices of neighbore residues that still belong to same chain l = self.ref.lenResidues() chain = rchainMap[res] outer_left = range( res-n_neighbores, res ) outer_right= range( res+1, res+n_neighbores+1 ) outer_left = [ i for i in outer_left if i > 0 and rchainMap[i]==chain] outer_right= [ i for i in outer_right if i < l and rchainMap[i]==chain] ## convert to atom indices, filter them against allowed neighbore atoms if outer_left: outer_left = self.ref.res2atomIndices( outer_left ) outer_left = MU.intersection( left_allowed, outer_left ) if outer_right: outer_right= self.ref.res2atomIndices( outer_right) outer_right= MU.intersection( right_allowed, outer_right) return result, outer_left + outer_right
def reduceXyz( self, xyz, axis=0 ): """ Reduce the number of atoms in the given coordinate set. The set must have the same length and order as the reference model. It may have an additional (time) dimension as first axis. @param xyz: coordinates (N_atoms x 3) or (N_frames x N_atoms x 3) @type xyz: array @param axis: axis with atoms (default: 0) @type axis: int @return: coordinate array (N_less_atoms x 3) or (N_frames x N_less_atoms x 3) @rtype: array """ masses = self.m.atoms.get('mass') r_xyz = None for atom_indices in self.groups: x = N0.take( xyz, atom_indices, axis ) m = N0.take( masses, atom_indices ) center = N0.sum( x * N0.transpose([m,]), axis=axis) / N0.sum( m ) if axis == 0: center = center[N0.NewAxis, :] if axis == 1: center = center[:, N0.NewAxis, :] if r_xyz is None: r_xyz = center else: r_xyz = N0.concatenate( (r_xyz, center), axis ) return r_xyz
def memberIndices( self, member, step=1 ): """ List of frame indices for this member:: memberIndices( int_member, [int_step] ) :param member: member trajectory :type member: int :param step: return only every i'th frame (default: 1) :type step: int :return: indices for members :rtype: [int] """ r = list(range( member, self.lenFrames(), self.n_members)) if step != 1: r = N0.take( r, range( 0, len( r ), step ) ).tolist() return r
def memberIndices( self, member, step=1 ): """ List of frame indices for this member:: memberIndices( int_member, [int_step] ) :param member: member trajectory :type member: int :param step: return only every i'th frame (default: 1) :type step: int :return: indices for members :rtype: [int] """ r = list(range( member, self.lenFrames(), self.n_members)) if step != 1: r = N0.take( r, range( 0, len( r ), step ) ).tolist() return r
def calcProfiles(self, m): """ Calculate needed profiles. @param m: PDBModel to calculate data for @type m: PDBModel """ if self.verbose: print("Initiating PDBDope...") d = PDBDope(m) if not self.profileName in m.atoms.keys(): if self.profileName in ['MS', 'AS', 'curvature', 'relAS', 'relMS']: if self.verbose: print("Adding SurfaceRacer profile...", end=' ') d.addSurfaceRacer() if self.profileName in ['density']: if self.verbose: print("Adding surface density...", end=' ') d.addDensity() if not self.profileName in m.residues.keys(): if self.profileName in ['cons_abs', 'cons_max', 'cons_ent']: if self.verbose: print("Adding conservation data...", end=' ') d.addConservation() if self.verbose: print('Done.') ## convert atom profiles to average residue profile if self.profileName in m.atoms.keys(): prof = [] aProfile = m.profile(self.profileName) resIdx = m.resIndex().tolist() resIdx += [m.lenAtoms()] for i in range(len(resIdx) - 1): prof += [ N0.average( N0.take(aProfile, list(range(resIdx[i], resIdx[i + 1])))) ] else: prof = m.profile(self.profileName) return prof
def valuesOf(self, infoKey, version=None, default=None, indices=None, unique=0): """ Get all values of a certain info record of all or some Complexes. @param infoKey: key for info dict @type infoKey: str @param version: index in history or None (=current) (default: None) @type version: int @param default: default value if infoKey is not found (default: None) @type default: any @param indices: list of int OR None(=all), indices of Complexes (default: None) @type indices: [int] OR None @param unique: report each value only once (set union), (default: 0) @type unique: 1|0 @return: list of values @rtype: [any] """ l = self if indices is not None: l = N0.take(l, indices) if not unique: if version is None: return [c.get(infoKey, default) for c in l] return [c[version].get(infoKey, default) for c in l] r = [] for c in l: if version is not None: c = c[version] if c.info.get(infoKey, default) not in r: r += [c.info.get(infoKey)] return r
def calcProfiles( self, m ): """ Calculate needed profiles. @param m: PDBModel to calculate data for @type m: PDBModel """ if self.verbose: print("Initiating PDBDope...") d = PDBDope( m ) if not self.profileName in m.atoms.keys(): if self.profileName in ['MS', 'AS', 'curvature', 'relAS', 'relMS']: if self.verbose: print("Adding SurfaceRacer profile...", end=' ') d.addSurfaceRacer() if self.profileName in ['density']: if self.verbose: print("Adding surface density...", end=' ') d.addDensity() if not self.profileName in m.residues.keys(): if self.profileName in ['cons_abs', 'cons_max', 'cons_ent']: if self.verbose: print("Adding conservation data...", end=' ') d.addConservation() if self.verbose: print('Done.') ## convert atom profiles to average residue profile if self.profileName in m.atoms.keys(): prof = [] aProfile = m.profile( self.profileName ) resIdx = m.resIndex().tolist() resIdx += [ m.lenAtoms()] for i in range(len(resIdx)-1): prof += [ N0.average( N0.take(aProfile, list(range(resIdx[i], resIdx[i+1])) ) )] else: prof = m.profile( self.profileName ) return prof
def valuesOf( self, infoKey, version=None, default=None, indices=None, unique=0 ): """ Get all values of a certain info record of all or some Complexes. @param infoKey: key for info dict @type infoKey: str @param version: index in history or None (=current) (default: None) @type version: int @param default: default value if infoKey is not found (default: None) @type default: any @param indices: list of int OR None(=all), indices of Complexes (default: None) @type indices: [int] OR None @param unique: report each value only once (set union), (default: 0) @type unique: 1|0 @return: list of values @rtype: [any] """ l = self if indices is not None: l = N0.take( l, indices ) if not unique: if version is None: return [ c.get(infoKey, default) for c in l ] return [ c[version].get( infoKey, default) for c in l ] r = [] for c in l: if version is not None: c = c[ version ] if c.info.get(infoKey, default) not in r: r += [ c.info.get( infoKey ) ] return r
def thin( self, step=1 ): """ Keep only each step'th frame from trajectory with 10 ensemble members. :param step: 1..keep all frames, 2..skip first and every second, .. (default: 1) :type step: int :return: reduced EnsembleTraj :rtype: EnsembleTraj """ T.ensure( step, int, forbidden=[0] ) ## 10 x lenFrames/10, frame indices of each member mI = [ self.memberIndices( i ) for i in range(self.n_members) ] mI = N0.array( mI ) mI = N0.take( mI, range( -1, N0.shape( mI )[1], step )[1:], 1 ) mI = N0.transpose( mI ) return self.takeFrames( N0.ravel( mI ))
def thin( self, step=1 ): """ Keep only each step'th frame from trajectory with 10 ensemble members. :param step: 1..keep all frames, 2..skip first and every second, .. (default: 1) :type step: int :return: reduced EnsembleTraj :rtype: EnsembleTraj """ T.ensure( step, int, forbidden=[0] ) ## 10 x lenFrames/10, frame indices of each member mI = [ self.memberIndices( i ) for i in range(self.n_members) ] mI = N0.array( mI ) mI = N0.take( mI, range( -1, N0.shape( mI )[1], step )[1:], 1 ) mI = N0.transpose( mI ) return self.takeFrames( N0.ravel( mI ))
def reduceAtomProfiles( self, from_model, to_model ): """ reduce all atom profiles according to the calculated map by calculating the average over the grouped atoms. @param from_model: model @type from_model: PDBModel @param to_model: model @type to_model: PDBModel """ for profname in from_model.atoms: p0 = from_model.atoms.get(profname) info = from_model.profileInfo( profname ) try: pr = [ N0.average( N0.take( p0, group ) ) for group in self.groups ] to_model.atoms.set( profname, pr ) except: pass to_model.atoms.setInfo( profname, **info )
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