def generate(self, n_confs=100, rrmsd=None, k1=9, k2=3, th_lrmsd=6): ''' This program is for generating new conformations. ''' self.n_confs = n_confs self.rrmsd = rrmsd self.k1 = k1 self.k2 = k2 self.th_lrmsd = th_lrmsd self.eigval = self.allresidue.getEigvals()[6:] self.eigvec = self.allresidue.getArray().T[6:] self.nresil = len(self.eigvec[0]) / 3 - self.nresir n_confs = int(n_confs) n_atoms = self.allatom.numAtoms() initial = self.atoms.getCoords() print "number of total atoms for complex: ", n_atoms ind = self.Bset() array = self.allatom._getArray().T[6:] confs = [] ''' #check x, Rrmsd, Lrmsd = self.rejectsampling(ind) coords = np.zeros(3*(self.nresil+self.nresir)) for j in range(2*k): coords += x[j]/np.sqrt(self.eigval[ind[j]]) * self.eigvec[ind[j]] print ("#%d Rrmsd=%.3f Lrmsd=%.10f" %(1+1, Rrmsd, Lrmsd)) print np.sqrt( np.sum(coords[0:3*self.nresir]**2)/self.nresir ), np.sqrt( np.sum(coords[3*self.nresir:]**2)/self.nresil ) print array[0][0:50], np.linalg.norm(array[0]), self.eigvec[0][0:10] #checkend ''' for i in range(n_confs): x, Rrmsd, Lrmsd = self.rejectsampling(ind) coords = np.zeros(3 * n_atoms) for j in range(k1 + k2): coords += x[j] / np.sqrt(self.eigval[ind[j]]) * array[ind[j]] confs.append(coords.reshape((n_atoms, 3))) print("#%d Rrmsd=%.3f Lrmsd=%.3f" % (i + 1, Rrmsd, Lrmsd)) ensemble = Ensemble('Conformations along {0}'.format(self.allatom)) ensemble.setCoords(initial) ensemble.addCoordset(np.array(confs) + initial) return ensemble
def __getitem__(self, index): if self._closed: raise ValueError('I/O operation on closed file') if isinstance(index, int): return self.getFrame(index) elif isinstance(index, (slice, list, np.ndarray)): if isinstance(index, slice): ens = Ensemble('{0:s} ({1[0]:d}:{1[1]:d}:{1[2]:d})'.format( self._title, index.indices(len(self)))) else: ens = Ensemble('{0:s} slice'.format(self._title)) ens.setCoords(self.getCoords()) if self._weights is not None: ens.setWeights(self._weights.copy()) ens.addCoordset(self.getCoordsets(index)) return ens else: raise IndexError('invalid index')
def calcOneWayAdaptiveANM(a, b, n_steps, **kwargs): """Runs one-way adaptivate ANM. """ n_modes = kwargs.pop('n_modes', 20) coordsA, coordsB, title, atoms, weights, maskA, maskB, rmsd = checkInput( a, b, **kwargs) coordsA = coordsA.copy() LOGGER.timeit('_prody_calcAdaptiveANM') n = 0 resetFmin = True defvecs = [] rmsds = [rmsd] ensemble = Ensemble(title + '_aANM') ensemble.setAtoms(atoms) ensemble.setCoords(coordsB) ensemble.setWeights(weights) ensemble.addCoordset(coordsA.copy()) while n < n_steps: LOGGER.info('\nStarting cycle {0} with initial structure {1}'.format( n + 1, title)) n_modes = calcStep(coordsA, coordsB, n_modes, ensemble, defvecs, rmsds, mask=maskA, resetFmin=resetFmin, **kwargs) n += 1 resetFmin = False if n_modes == 0: LOGGER.report('One-way Adaptive ANM converged in %.2fs.', '_prody_calcAdaptiveANM') break return ensemble
def __getitem__(self, index): if self._closed: raise ValueError('I/O operation on closed file') if isinstance(index, int): return self.getFrame(index) elif isinstance(index, (slice, list, ndarray)): if isinstance(index, slice): ens = Ensemble('{0} ({1[0]}:{1[1]}:{1[2]})'.format( self._title, index.indices(len(self)))) else: ens = Ensemble('{0} slice'.format(self._title)) ens.setCoords(self.getCoords()) if self._weights is not None: ens.setWeights(self._weights.copy()) ens.addCoordset(self.getCoordsets(index)) ens.setAtoms(self._atoms) return ens else: raise IndexError('invalid index')
def sampleModes(modes, atoms=None, n_confs=1000, rmsd=1.0): """Return an ensemble of randomly sampled conformations along given *modes*. If *atoms* are provided, sampling will be around its active coordinate set. Otherwise, sampling is around the 0 coordinate set. :arg modes: Modes along which sampling will be performed. :type modes: :class:`~.Mode`, :class:`~.ModeSet`, :class:`~.PCA`, :class:`~.ANM` or :class:`~.NMA` :arg atoms: Atoms whose active coordinate set will be used as the initial conformation. :type atoms: :class:`~.Atomic` :arg n_confs: Number of conformations to generate. Default is 1000. :type n_steps: int :arg rmsd: The average RMSD that the conformations will have with respect to the initial conformation. Default is 1.0 A. :type rmsd: float For given normal modes :math:`[u_1 u_2 ... u_m]` and their eigenvalues :math:`[\lambda_1 \lambda_2 ... \lambda_m]`, a new conformation is sampled using the relation: .. math:: R_k = R_0 + s \sum_{i=1}^{m} r_i^k \lambda^{-0.5}_i u_i :math:`R_0` is the active coordinate set of *atoms*. :math:`[r_1^k r_2^k ... r_m^k]` are normally distributed random numbers generated for conformation :math:`k` using :func:`numpy.random.randn`. RMSD of the new conformation from :math:`R_0` can be calculated as .. math:: RMSD^k = \sqrt{ {\\left( s \sum_{i=1}^{m} r_i^k \lambda^{-0.5}_i u_i \\right)}^{2} / N } = \\frac{s}{ \sqrt{N}} \sqrt{ \sum_{i=1}^{m} (r_i^k)^2 \lambda^{-1}_i } Average :math:`RMSD` of the generated conformations from the initial conformation is: .. math:: \\left< RMSD^k \\right> = \\frac{s}{ \sqrt{N}} \\left< \sqrt{ \sum_{i=1}^{m} (r_i^k)^2 \lambda^{-1}_i } \\right> From this relation :math:`s` scaling factor obtained using the relation .. math:: s = \\left< RMSD^k \\right> \sqrt{N} {\\left< \sqrt{ \sum_{i=1}^{m} (r_i)^2 \lambda^{-1}_i} \\right>}^{-1} Note that random numbers are generated before conformations are sampled, hence exact value of :math:`s` is known from this relation to ensure that the generated ensemble will have user given average *rmsd* value. Note that if modes are from a :class:`~.PCA`, variances are used instead of inverse eigenvalues, i.e. :math:`\sigma_i \sim \lambda^{-1}_i`. |more| See also :func:`~.showEllipsoid`. .. plot:: :context: :include-source: # Generate 300 conformations using ANM modes 1-3 ensemble = sampleModes( p38_anm[:3], n_confs=500 ) # Project these conformations onto the space spanned by these modes plt.figure(figsize=(5,4)) showProjection(ensemble, p38_anm[:3], rmsd=True) .. plot:: :context: :nofigs: plt.close('all')""" if not isinstance(modes, (Mode, NMA, ModeSet)): raise TypeError('modes must be a NMA or ModeSet instance, ' 'not {0:s}'.format(type(modes))) if not modes.is3d(): raise ValueError('modes must be from a 3-dimensional model') n_confs = int(n_confs) n_atoms = modes.numAtoms() initial = None if atoms is not None: if not isinstance(atoms, (Atomic)): raise TypeError('{0:s} is not correct type for atoms' .format(type(atoms))) if atoms.numAtoms() != n_atoms: raise ValueError('number of atoms do not match') initial = atoms.getCoords() rmsd = float(rmsd) LOGGER.info('Parameter: rmsd = {0:.2f} A'.format(rmsd)) n_confs = int(n_confs) LOGGER.info('Parameter: n_confs = {0:d}'.format(n_confs)) if isinstance(modes, Mode): n_modes = 1 variances = np.array([modes.getVariance()]) else: n_modes = len(modes) variances = modes.getVariances() if np.any(variances == 0): raise ValueError('one or more modes has zero variance') randn = np.random.standard_normal((n_confs, n_modes)) coef = ((randn ** 2 * variances).sum(1) ** 0.5).mean() scale = n_atoms**0.5 * rmsd / coef LOGGER.info('Modes are scaled by {0:g}.'.format(scale)) confs = [] append = confs.append scale = scale * variances ** 0.5 array = modes._getArray() if array.ndim > 1: for i in range(n_confs): append( (array * scale * randn[i]).sum(1).reshape((n_atoms, 3)) ) else: for i in range(n_confs): append( (array * scale * randn[i]).reshape((n_atoms, 3)) ) ensemble = Ensemble('Conformations along {0:s}'.format(modes)) if initial is None: ensemble.setCoords(np.zeros((n_atoms, 3))) ensemble.addCoordset(np.array(confs)) else: ensemble.setCoords(initial) ensemble.addCoordset(np.array(confs) + initial) return ensemble
def traverseMode(mode, atoms, n_steps=10, rmsd=1.5): """Generates a trajectory along a given *mode*, which can be used to animate fluctuations in an external program. :arg mode: Mode along which a trajectory will be generated. :type mode: :class:`~.Mode` :arg atoms: Atoms whose active coordinate set will be used as the initial conformation. :type atoms: :class:`~.Atomic` :arg n_steps: Number of steps to take along each direction. For example, for ``n_steps=10``, 20 conformations will be generated along the first mode. Default is 10. :type n_steps: int :arg rmsd: The maximum RMSD that the conformations will have with respect to the initial conformation. Default is 1.5 A. :type rmsd: float :returns: :class:`~.Ensemble` For given normal mode :math:`u_i`, its eigenvalue :math:`\lambda_i`, number of steps :math:`n`, and maximum :math:`RMSD` conformations :math:`[R_{-n} R_{-n+1} ... R_{-1} R_0 R_1 ... R_n]` are generated. :math:`R_0` is the active coordinate set of *atoms*. :math:`R_k = R_0 + sk\lambda_iu_i`, where :math:`s` is found using :math:`s = ((N (\\frac{RMSD}{n})^2) / \lambda_i^{-1}) ^{0.5}`, where :math:`N` is the number of atoms. .. plot:: :context: :include-source: trajectory = traverseMode( p38_anm[0], p38_structure.select('calpha'), n_steps=8, rmsd=1.4 ) rmsd = calcRMSD(trajectory) plt.figure(figsize=(5,4)) plt.plot(rmsd, '-o') plt.xlabel('Frame index') plt.ylabel('RMSD (A)') .. plot:: :context: :nofigs: plt.close('all')""" if not isinstance(mode, VectorBase): raise TypeError('mode must be a Mode or Vector instance, ' 'not {0:s}'.format(type(mode))) if not mode.is3d(): raise ValueError('mode must be from a 3-dimensional model.') n_atoms = mode.numAtoms() initial = None if atoms is not None: if not isinstance(atoms, Atomic): raise TypeError('{0:s} is not correct type for atoms' .format(type(atoms))) if atoms.numAtoms() != n_atoms: raise ValueError('number of atoms do not match') initial = atoms.getCoords() name = str(mode) rmsd = float(rmsd) + 0.000004 LOGGER.info('Parameter: rmsd = {0:.2f} A'.format(rmsd)) n_steps = int(n_steps) LOGGER.info('Parameter: n_steps = {0:d}'.format(n_steps)) step = rmsd / n_steps LOGGER.info('Step size is {0:.2f} A RMSD'.format(step)) arr = mode.getArrayNx3() var = mode.getVariance() scale = ((n_atoms * step**2) / var) **0.5 LOGGER.info('Mode is scaled by {0:g}.'.format(scale)) array = arr * var**0.5 * scale confs_add = [initial + array] for s in range(1, n_steps): confs_add.append( confs_add[-1] + array) confs_sub = [initial - array] for s in range(1, n_steps): confs_sub.append( confs_sub[-1] - array) confs_sub.reverse() ensemble = Ensemble('Conformations along {0:s}'.format(name)) ensemble.setCoords(initial) ensemble.addCoordset(np.array(confs_sub + [initial] + confs_add)) return ensemble
def sampleModes(modes, atoms=None, n_confs=1000, rmsd=1.0): """Returns an ensemble of randomly sampled conformations along given *modes*. If *atoms* are provided, sampling will be around its active coordinate set. Otherwise, sampling is around the 0 coordinate set. :arg modes: modes along which sampling will be performed :type modes: :class:`.Mode`, :class:`.ModeSet`, :class:`.PCA`, :class:`.ANM` or :class:`.NMA` :arg atoms: atoms whose active coordinate set will be used as the initial conformation :type atoms: :class:`.Atomic` :arg n_confs: number of conformations to generate, default is 1000 :type n_steps: int :arg rmsd: average RMSD that the conformations will have with respect to the initial conformation, default is 1.0 Å :type rmsd: float :returns: :class:`.Ensemble` For given normal modes :math:`[u_1 u_2 ... u_m]` and their eigenvalues :math:`[\\lambda_1 \\lambda_2 ... \\lambda_m]`, a new conformation is sampled using the relation: .. math:: R_k = R_0 + s \\sum_{i=1}^{m} r_i^k \\lambda^{-0.5}_i u_i :math:`R_0` is the active coordinate set of *atoms*. :math:`[r_1^k r_2^k ... r_m^k]` are normally distributed random numbers generated for conformation :math:`k` using :func:`numpy.random.randn`. RMSD of the new conformation from :math:`R_0` can be calculated as .. math:: RMSD^k = \\sqrt{ {\\left( s \\sum_{i=1}^{m} r_i^k \\lambda^{-0.5}_i u_i \\right)}^{2} / N } = \\frac{s}{ \\sqrt{N}} \\sqrt{ \\sum_{i=1}^{m} (r_i^k)^2 \\lambda^{-1}_i } Average :math:`RMSD` of the generated conformations from the initial conformation is: .. math:: \\left< RMSD^k \\right> = \\frac{s}{ \\sqrt{N}} \\left< \\sqrt{ \\sum_{i=1}^{m} (r_i^k)^2 \\lambda^{-1}_i } \\right> From this relation :math:`s` scaling factor obtained using the relation .. math:: s = \\left< RMSD^k \\right> \\sqrt{N} {\\left< \\sqrt{ \\sum_{i=1}^{m} (r_i)^2 \\lambda^{-1}_i} \\right>}^{-1} Note that random numbers are generated before conformations are sampled, hence exact value of :math:`s` is known from this relation to ensure that the generated ensemble will have user given average *rmsd* value. Note that if modes are from a :class:`.PCA`, variances are used instead of inverse eigenvalues, i.e. :math:`\\sigma_i \\sim \\lambda^{-1}_i`. See also :func:`.showEllipsoid`.""" if not isinstance(modes, (Mode, NMA, ModeSet)): raise TypeError('modes must be a NMA or ModeSet instance, ' 'not {0}'.format(type(modes))) if not modes.is3d(): raise ValueError('modes must be from a 3-dimensional model') n_confs = int(n_confs) n_atoms = modes.numAtoms() initial = None if atoms is not None: if isinstance(atoms, Atomic): if atoms.numAtoms() != n_atoms: raise ValueError('number of atoms do not match') initial = atoms.getCoords() elif isinstance(atoms, np.ndarray): initial = atoms else: raise TypeError('{0} is not correct type for atoms' .format(type(atoms))) rmsd = float(rmsd) LOGGER.info('Parameter: rmsd = {0:.2f} A'.format(rmsd)) n_confs = int(n_confs) LOGGER.info('Parameter: n_confs = {0}'.format(n_confs)) if isinstance(modes, Mode): n_modes = 1 variances = np.array([modes.getVariance()]) magnitudes = np.array([abs(modes)]) else: n_modes = len(modes) variances = modes.getVariances() magnitudes = np.array([abs(mode) for mode in modes]) if np.any(variances == 0): raise ValueError('one or more modes has zero variance') randn = np.random.standard_normal((n_confs, n_modes)) coef = ((randn ** 2 * variances).sum(1) ** 0.5).mean() scale = n_atoms**0.5 * rmsd / coef LOGGER.info('Modes are scaled by {0}.'.format(scale)) confs = [] append = confs.append scale = scale / magnitudes * variances ** 0.5 array = modes._getArray() if array.ndim > 1: for i in range(n_confs): append((array * scale * randn[i]).sum(1).reshape((n_atoms, 3))) else: for i in range(n_confs): append((array * scale * randn[i]).reshape((n_atoms, 3))) ensemble = Ensemble('Conformations along {0}'.format(modes)) if initial is None: ensemble.setCoords(np.zeros((n_atoms, 3))) ensemble.addCoordset(np.array(confs)) else: ensemble.setCoords(initial) ensemble.addCoordset(np.array(confs) + initial) return ensemble
def traverseMode(mode, atoms, n_steps=10, rmsd=1.5, **kwargs): """Generates a trajectory along a given *mode*, which can be used to animate fluctuations in an external program. :arg mode: mode along which a trajectory will be generated :type mode: :class:`.Mode` :arg atoms: atoms whose active coordinate set will be used as the initial conformation :type atoms: :class:`.Atomic` :arg n_steps: number of steps to take along each direction, for example, for ``n_steps=10``, 20 conformations will be generated along *mode* with structure *atoms* in between, default is 10. :type n_steps: int :arg rmsd: maximum RMSD that the conformations will have with respect to the initial conformation, default is 1.5 Å :type rmsd: float :arg pos: whether to include steps in the positive mode direction, default is **True** :type pos: bool :arg neg: whether to include steps in the negative mode direction, default is **True** :type neg: bool :arg reverse: whether to reverse the direction default is **False** :type reverse: bool :returns: :class:`.Ensemble` For given normal mode :math:`u_i`, its eigenvalue :math:`\\lambda_i`, number of steps :math:`n`, and maximum :math:`RMSD` conformations :math:`[R_{-n} R_{-n+1} ... R_{-1} R_0 R_1 ... R_n]` are generated. :math:`R_0` is the active coordinate set of *atoms*. :math:`R_k = R_0 + sk\\lambda_iu_i`, where :math:`s` is found using :math:`s = ((N (\\frac{RMSD}{n})^2) / \\lambda_i^{-1}) ^{0.5}`, where :math:`N` is the number of atoms.""" pos = kwargs.get('pos', True) neg = kwargs.get('neg', True) reverse = kwargs.get('reverse', False) if pos is False and neg is False: raise ValueError('pos and neg cannot both be False') if not isinstance(mode, VectorBase): raise TypeError('mode must be a Mode or Vector instance, ' 'not {0}'.format(type(mode))) if not mode.is3d(): raise ValueError('mode must be from a 3-dimensional model.') n_atoms = mode.numAtoms() initial = None if atoms is not None: if not isinstance(atoms, Atomic): raise TypeError('{0} is not correct type for atoms' .format(type(atoms))) if atoms.numAtoms() != n_atoms: raise ValueError('number of atoms do not match') initial = atoms.getCoords() name = str(mode) rmsd = float(rmsd) + 0.000004 LOGGER.info('Parameter: rmsd = {0:.2f} A'.format(rmsd)) n_steps = int(n_steps) LOGGER.info('Parameter: n_steps = {0}'.format(n_steps)) step = rmsd / n_steps LOGGER.info('Step size is {0:.2f} A RMSD'.format(step)) arr = mode.getArrayNx3() try: var = mode.getVariance() except AttributeError: var = 1. scale = ((n_atoms * step**2) / var) ** 0.5 LOGGER.info('Mode is scaled by {0}.'.format(scale)) array = arr * var**0.5 * scale / abs(mode) confs_add = [initial + array] for s in range(1, n_steps): confs_add.append(confs_add[-1] + array) confs_sub = [initial - array] for s in range(1, n_steps): confs_sub.append(confs_sub[-1] - array) confs_sub.reverse() ensemble = Ensemble('Conformations along {0}'.format(name)) ensemble.setAtoms(atoms) ensemble.setCoords(initial) conf_list = [initial] if pos: conf_list = conf_list + confs_add if neg: conf_list = confs_sub + conf_list conf_array = np.array(conf_list) if reverse: conf_array = conf_array[::-1] ensemble.addCoordset(conf_array) return ensemble
def calcBothWaysAdaptiveANM(a, b, n_steps, **kwargs): """Runs both-way adaptivate ANM. """ n_modes0 = n_modes = kwargs.pop('n_modes', 20) coordsA, coordsB, title, atoms, weights, maskA, maskB, rmsd = checkInput( a, b, **kwargs) coordsA = coordsA.copy() coordsB = coordsB.copy() LOGGER.timeit('_prody_calcAdaptiveANM') n = 0 resetFmin = True defvecs = [] rmsds = [rmsd] ensA = Ensemble('A') ensA.setCoords(coordsA) ensA.setWeights(weights) ensA.addCoordset(coordsA.copy()) ensB = Ensemble('B') ensB.setCoords(coordsB.copy()) ensB.setWeights(weights) ensB.addCoordset(coordsB.copy()) while n < n_steps: LOGGER.info('\nStarting cycle {0} with {1}'.format( n + 1, getTitle(a, 'structure A'))) n_modes = calcStep(coordsA, coordsB, n_modes, ensA, defvecs, rmsds, mask=maskA, resetFmin=resetFmin, **kwargs) n += 1 resetFmin = False if n_modes == 0: break n = 0 n_modes = n_modes0 resetFmin = True while n < n_steps: LOGGER.info('\nStarting cycle {0} with structure {1}'.format( n + 1, getTitle(b, 'structure B'))) n_modes = calcStep(coordsB, coordsA, n_modes, ensB, defvecs, rmsds, mask=maskB, resetFmin=resetFmin, **kwargs) n += 1 resetFmin = False if n_modes == 0: LOGGER.report('Alternating Adaptive ANM converged in %.2fs.', '_prody_calcAdaptiveANM') break ensemble = ensA + ensB[::-1] ensemble.setTitle(title + '_aANM') ensemble.setAtoms(atoms) ensemble.setCoords(ensB.getCoords()) LOGGER.report('Both-way Adaptive ANM converged in %.2fs.', '_prody_calcAdaptiveANM') return ensemble