class exANM(ANMBase): """Class for explicit ANM (exANM) method ([FT00]_). Optional arguments build a membrane lattice permit analysis of membrane effect on elastic network models in *exANM* method described in [TL12]_. .. [TL12] Lezon TR, Bahar I, Constraints Imposed by the Membrane Selectively Guide the Alternating Access Dynamics of the Glutamate Transporter GltPh """ def __init__(self, name='Unknown'): super(exANM, self).__init__(name) self._membrane = None self._combined = None def buildMembrane(self, coords, **kwargs): """Build Hessian matrix for given coordinate set. :arg coords: a coordinate set or an object with ``getCoords`` method :type coords: :class:`numpy.ndarray` :arg membrane_hi: the maximum z coordinate of the pdb default is 13.0 :type membrane_hi: float :arg membrane_lo: the minimum z coordinate of the pdb default is -13.0 :type membrane_lo: float :arg R: radius of all membrane in x-y direction default is 80. :type R: float :arg r: radius of individual barrel-type membrane protein default is 2.5. :type :arg lat: lattice type which could be FCC(face-centered-cubic)(default), SC(simple cubic), SH(simple hexagonal) :type lat: str """ if type(coords) is AtomGroup: buildAg = True else: buildAg = False try: coords = (coords._getCoords() if hasattr(coords, '_getCoords') else coords.getCoords()) except AttributeError: try: checkCoords(coords) except TypeError: raise TypeError('coords must be a Numpy array or an object ' 'with `getCoords` method') self._n_atoms = natoms = int(coords.shape[0]) pxlo = min(np.append(coords[:, 0], 10000)) pxhi = max(np.append(coords[:, 0], -10000)) pylo = min(np.append(coords[:, 1], 10000)) pyhi = max(np.append(coords[:, 1], -10000)) pzlo = min(np.append(coords[:, 2], 10000)) pzhi = max(np.append(coords[:, 2], -10000)) membrane_hi = float(kwargs.get('membrane_hi', 13.0)) membrane_lo = float(kwargs.get('membrane_lo', -13.0)) R = float(kwargs.get('R', 80)) r = float(kwargs.get('r', 5)) lat = str(kwargs.get('lat', 'FCC')) lpv = assign_lpvs(lat) imax = (R + lpv[0, 2] * (membrane_hi - membrane_lo) / 2.) / r jmax = (R + lpv[1, 2] * (membrane_hi - membrane_lo) / 2.) / r kmax = (R + lpv[2, 2] * (membrane_hi - membrane_lo) / 2.) / r #print pxlo, pxhi, pylo, pyhi, pzlo, pzhi #print lpv[0,2],lpv[1,2],lpv[2,2] #print R,r,imax,jmax,kmax membrane = zeros((1, 3)) LOGGER.timeit('_membrane') membrane = zeros((1, 3)) atm = 0 for i in range(-int(imax), int(imax + 1)): for j in range(-int(jmax), int(jmax + 1)): for k in range(-int(kmax), int(kmax + 1)): X = zeros((1, 3)) for p in range(3): X[0, p] = 2. * r * (i * lpv[0, p] + j * lpv[1, p] + k * lpv[2, p]) dd = 0 for p in range(3): dd += X[0, p]**2 if dd < R**2 and X[0, 2] > membrane_lo and X[0, 2] < membrane_hi: if X[0, 0] > pxlo - R / 2 and X[ 0, 0] < pxhi + R / 2 and X[ 0, 1] > pylo - R / 2 and X[ 0, 1] < pyhi + R / 2 and X[ 0, 2] > pzlo and X[0, 2] < pzhi: if checkClash(X, coords[:natoms, :], radius=5): if atm == 0: membrane = X else: membrane = np.append(membrane, X, axis=0) atm = atm + 1 #print atm self._membrane = AtomGroup(title="Membrane") self._membrane.setCoords(membrane) self._membrane.setResnums(list(range(atm))) self._membrane.setResnames(["NE1" for i in range(atm)]) self._membrane.setChids(["Q" for i in range(atm)]) self._membrane.setElements(["Q1" for i in range(atm)]) self._membrane.setNames(["Q1" for i in range(atm)]) LOGGER.report('Membrane was built in %2.fs.', label='_membrane') def buildHessian(self, coords, cutoff=15., gamma=1., **kwargs): """Build Hessian matrix for given coordinate set. :arg coords: a coordinate set or an object with ``getCoords`` method :type coords: :class:`numpy.ndarray` :arg cutoff: cutoff distance (Å) for pairwise interactions, default is 15.0 Å :type cutoff: float :arg gamma: spring constant, default is 1.0 :type gamma: float :arg membrane_hi: the maximum z coordinate of the pdb default is 13.0 :type membrane_hi: float :arg membrane_lo: the minimum z coordinate of the pdb default is -13.0 :type membrane_lo: float :arg R: radius of all membrane in x-y direction default is 80. :type R: float :arg r: radius of individual barrel-type membrane protein default is 2.5. :type :arg lat: lattice type which could be FCC(face-centered-cubic)(default), SC(simple cubic), SH(simple hexagonal) :type lat: str """ try: coords = (coords._getCoords() if hasattr(coords, '_getCoords') else coords.getCoords()) except AttributeError: try: checkCoords(coords) except TypeError: raise TypeError('coords must be a Numpy array or an object ' 'with `getCoords` method') self._n_atoms = natoms = int(coords.shape[0]) if self._membrane is None: membrane_hi = float(kwargs.get('membrane_hi', 13.0)) membrane_lo = float(kwargs.get('membrane_lo', -13.0)) R = float(kwargs.get('R', 80)) r = float(kwargs.get('r', 5)) lat = str(kwargs.get('lat', 'FCC')) self.buildMembrane(coords, membrane_hi=membrane_hi, membrane_lo=membrane_lo, R=R, r=r, lat=lat) LOGGER.timeit('_exanm') coords = np.concatenate((coords, self._membrane.getCoords()), axis=0) self._combined_coords = coords total_natoms = int(coords.shape[0]) self._hessian = np.zeros((natoms * 3, natoms * 3), float) total_hessian = np.zeros((total_natoms * 3, total_natoms * 3), float) cutoff, g, gamma = checkENMParameters(cutoff, gamma) cutoff2 = cutoff * cutoff for i in range(total_natoms): res_i3 = i * 3 res_i33 = res_i3 + 3 i_p1 = i + 1 i2j_all = coords[i_p1:, :] - coords[i] for j, dist2 in enumerate((i2j_all**2).sum(1)): if dist2 > cutoff2: continue i2j = i2j_all[j] j += i_p1 g = gamma(dist2, i, j) res_j3 = j * 3 res_j33 = res_j3 + 3 super_element = np.outer(i2j, i2j) * (-g / dist2) total_hessian[res_i3:res_i33, res_j3:res_j33] = super_element total_hessian[res_j3:res_j33, res_i3:res_i33] = super_element total_hessian[res_i3:res_i33, res_i3:res_i33] = total_hessian[ res_i3:res_i33, res_i3:res_i33] - super_element total_hessian[res_j3:res_j33, res_j3:res_j33] = total_hessian[ res_j3:res_j33, res_j3:res_j33] - super_element ss = total_hessian[:natoms * 3, :natoms * 3] so = total_hessian[:natoms * 3, natoms * 3 + 1:] os = total_hessian[natoms * 3 + 1:, :natoms * 3] oo = total_hessian[natoms * 3 + 1:, natoms * 3 + 1:] self._hessian = ss - np.dot(so, np.dot(linalg.inv(oo), os)) LOGGER.report('Hessian was built in %.2fs.', label='_exanm') self._dof = self._hessian.shape[0] def calcModes(self, n_modes=20, zeros=False, turbo=True): """Calculate normal modes. This method uses :func:`scipy.linalg.eigh` function to diagonalize the Hessian matrix. When Scipy is not found, :func:`numpy.linalg.eigh` is used. :arg n_modes: number of non-zero eigenvalues/vectors to calculate. If ``None`` is given, all modes will be calculated. :type n_modes: int or None, default is 20 :arg zeros: If ``True``, modes with zero eigenvalues will be kept. :type zeros: bool, default is ``False`` :arg turbo: Use a memory intensive, but faster way to calculate modes. :type turbo: bool, default is ``True`` """ super(exANM, self).calcModes(n_modes, zeros, turbo) def getMembrane(self): """Returns a copy of the membrane coordinates.""" if self._membrane is not None: return self._membrane.copy() def _getMembrane(self): return self._membrane def combineMembraneProtein(self, coords): try: if type(coords) is AtomGroup: self._combined = coords + self._membrane except TypeError: raise TypeError('coords must be an AtomGroup object ' 'with `getCoords` method') def writeCombinedPDB(self, filename): """ Given membrane coordinates it will write a pdb file with membrane coordinates. :arg filename: filename for the pdb file. :type filename: str :arg membrane: membrane coordinates or the membrane structure. :type membrane: nd.array """ if self._combined is None: combineMembraneProtein(self, coords) try: writePDB(filename, self._combined) except TypeError: raise "Membrane not found. Use buildMembrane() function."
class exANM(ANMBase): """Class for explicit ANM (exANM) method ([FT00]_). Optional arguments build a membrane lattice permit analysis of membrane effect on elastic network models in *exANM* method described in [TL12]_. .. [TL12] Lezon TR, Bahar I, Constraints Imposed by the Membrane Selectively Guide the Alternating Access Dynamics of the Glutamate Transporter GltPh """ def __init__(self, name='Unknown'): super(exANM, self).__init__(name) self._membrane = None self._combined = None def buildMembrane(self, coords, **kwargs): """Build Hessian matrix for given coordinate set. :arg coords: a coordinate set or an object with ``getCoords`` method :type coords: :class:`numpy.ndarray` :arg membrane_hi: the maximum z coordinate of the pdb default is 13.0 :type membrane_hi: float :arg membrane_lo: the minimum z coordinate of the pdb default is -13.0 :type membrane_lo: float :arg R: radius of all membrane in x-y direction default is 80. :type R: float :arg r: radius of individual barrel-type membrane protein default is 2.5. :type :arg lat: lattice type which could be FCC(face-centered-cubic)(default), SC(simple cubic), SH(simple hexagonal) :type lat: str """ if type(coords) is AtomGroup: buildAg = True else: buildAg = False try: coords = (coords._getCoords() if hasattr(coords, '_getCoords') else coords.getCoords()) except AttributeError: try: checkCoords(coords) except TypeError: raise TypeError('coords must be a Numpy array or an object ' 'with `getCoords` method') self._n_atoms = natoms = int(coords.shape[0]) pxlo = min(np.append(coords[:,0],10000)) pxhi = max(np.append(coords[:,0],-10000)) pylo = min(np.append(coords[:,1],10000)) pyhi = max(np.append(coords[:,1],-10000)) pzlo = min(np.append(coords[:,2],10000)) pzhi = max(np.append(coords[:,2],-10000)) membrane_hi = float(kwargs.get('membrane_hi', 13.0)) membrane_lo = float(kwargs.get('membrane_lo', -13.0)) R = float(kwargs.get('R', 80)) r = float(kwargs.get('r', 5)) lat = str(kwargs.get('lat', 'FCC')) lpv = assign_lpvs(lat) imax = (R + lpv[0,2] * (membrane_hi - membrane_lo)/2.)/r jmax = (R + lpv[1,2] * (membrane_hi - membrane_lo)/2.)/r kmax = (R + lpv[2,2] * (membrane_hi - membrane_lo)/2.)/r #print pxlo, pxhi, pylo, pyhi, pzlo, pzhi #print lpv[0,2],lpv[1,2],lpv[2,2] #print R,r,imax,jmax,kmax membrane = zeros((1,3)) LOGGER.timeit('_membrane') membrane = zeros((1,3)) atm = 0 for i in range(-int(imax),int(imax+1)): for j in range(-int(jmax),int(jmax+1)): for k in range(-int(kmax),int(kmax+1)): X = zeros((1,3)) for p in range(3): X[0,p]=2.*r*(i*lpv[0,p]+j*lpv[1,p]+k*lpv[2,p]) dd=0 for p in range(3): dd += X[0,p] ** 2 if dd<R**2 and X[0,2]>membrane_lo and X[0,2]<membrane_hi: if X[0,0]>pxlo-R/2 and X[0,0]<pxhi+R/2 and X[0,1]>pylo-R/2 and X[0,1]<pyhi+R/2 and X[0,2]>pzlo and X[0,2]<pzhi: if checkClash(X, coords[:natoms,:], radius=5): if atm == 0: membrane = X else: membrane = np.append(membrane, X, axis=0) atm = atm + 1 #print atm self._membrane = AtomGroup(title="Membrane") self._membrane.setCoords(membrane) self._membrane.setResnums(range(atm)) self._membrane.setResnames(["NE1" for i in range(atm)]) self._membrane.setChids(["Q" for i in range(atm)]) self._membrane.setElements(["Q1" for i in range(atm)]) self._membrane.setNames(["Q1" for i in range(atm)]) LOGGER.report('Membrane was built in %2.fs.', label='_membrane') def buildHessian(self, coords, cutoff=15., gamma=1., **kwargs): """Build Hessian matrix for given coordinate set. :arg coords: a coordinate set or an object with ``getCoords`` method :type coords: :class:`numpy.ndarray` :arg cutoff: cutoff distance (Å) for pairwise interactions, default is 15.0 Å :type cutoff: float :arg gamma: spring constant, default is 1.0 :type gamma: float :arg membrane_hi: the maximum z coordinate of the pdb default is 13.0 :type membrane_hi: float :arg membrane_lo: the minimum z coordinate of the pdb default is -13.0 :type membrane_lo: float :arg R: radius of all membrane in x-y direction default is 80. :type R: float :arg r: radius of individual barrel-type membrane protein default is 2.5. :type :arg lat: lattice type which could be FCC(face-centered-cubic)(default), SC(simple cubic), SH(simple hexagonal) :type lat: str """ try: coords = (coords._getCoords() if hasattr(coords, '_getCoords') else coords.getCoords()) except AttributeError: try: checkCoords(coords) except TypeError: raise TypeError('coords must be a Numpy array or an object ' 'with `getCoords` method') self._n_atoms = natoms = int(coords.shape[0]) if self._membrane is None: membrane_hi = float(kwargs.get('membrane_hi', 13.0)) membrane_lo = float(kwargs.get('membrane_lo', -13.0)) R = float(kwargs.get('R', 80)) r = float(kwargs.get('r', 5)) lat = str(kwargs.get('lat', 'FCC')) buildMembrane(self,coords,membrane_hi=membrane_hi, membrane_lo=membrane_lo,R=R,r=r,lat=lat) LOGGER.timeit('_exanm') coords = np.concatenate((coords,self._membrane.getCoords()),axis=0) self._combined_coords = coords total_natoms = int(coords.shape[0]) self._hessian = np.zeros((natoms*3, natoms*3), float) total_hessian = np.zeros((total_natoms*3, total_natoms*3), float) cutoff, g, gamma = checkENMParameters(cutoff, gamma) cutoff2 = cutoff * cutoff for i in range(total_natoms): res_i3 = i*3 res_i33 = res_i3+3 i_p1 = i+1 i2j_all = coords[i_p1:, :] - coords[i] for j, dist2 in enumerate((i2j_all ** 2).sum(1)): if dist2 > cutoff2: continue i2j = i2j_all[j] j += i_p1 g = gamma(dist2, i, j) res_j3 = j*3 res_j33 = res_j3+3 super_element = np.outer(i2j, i2j) * (- g / dist2) total_hessian[res_i3:res_i33, res_j3:res_j33] = super_element total_hessian[res_j3:res_j33, res_i3:res_i33] = super_element total_hessian[res_i3:res_i33, res_i3:res_i33] = total_hessian[res_i3:res_i33, res_i3:res_i33] - super_element total_hessian[res_j3:res_j33, res_j3:res_j33] = total_hessian[res_j3:res_j33, res_j3:res_j33] - super_element ss = total_hessian[:natoms*3, :natoms*3] so = total_hessian[:natoms*3, natoms*3+1:] os = total_hessian[natoms*3+1:,:natoms*3] oo = total_hessian[natoms*3+1:, natoms*3+1:] self._hessian = ss - np.dot(so, np.dot(linalg.inv(oo), os)) LOGGER.report('Hessian was built in %.2fs.', label='_exanm') self._dof = self._hessian.shape[0] def calcModes(self, n_modes=20, zeros=False, turbo=True): """Calculate normal modes. This method uses :func:`scipy.linalg.eigh` function to diagonalize the Hessian matrix. When Scipy is not found, :func:`numpy.linalg.eigh` is used. :arg n_modes: number of non-zero eigenvalues/vectors to calculate. If ``None`` is given, all modes will be calculated. :type n_modes: int or None, default is 20 :arg zeros: If ``True``, modes with zero eigenvalues will be kept. :type zeros: bool, default is ``False`` :arg turbo: Use a memory intensive, but faster way to calculate modes. :type turbo: bool, default is ``True`` """ super(exANM, self).calcModes(n_modes, zeros, turbo) def getMembrane(self): """Returns a copy of the membrane coordinates.""" if self._membrane is not None: return self._membrane.copy() def _getMembrane(self): return self._membrane def combineMembraneProtein(self, coords): try: if type(coords) is AtomGroup: self._combined = coords + self._membrane except TypeError: raise TypeError('coords must be an AtomGroup object ' 'with `getCoords` method') def writeCombinedPDB(self,filename): """ Given membrane coordinates it will write a pdb file with membrane coordinates. :arg filename: filename for the pdb file. :type filename: str :arg membrane: membrane coordinates or the membrane structure. :type membrane: nd.array """ if self._combined is None: combineMembraneProtein(self,coords) try: writePDB(filename,self._combined) except TypeError: raise "Membrane not found. Use buildMembrane() function."
class exANM(ANM): """Class for explicit ANM (exANM) method ([FT00]_). Optional arguments build a membrane lattice permit analysis of membrane effect on elastic network models in *exANM* method described in [TL12]_. .. [TL12] Lezon TR, Bahar I, Constraints Imposed by the Membrane Selectively Guide the Alternating Access Dynamics of the Glutamate Transporter GltPh """ def __init__(self, name='Unknown'): super(exANM, self).__init__(name) self._membrane = None self._combined = None def buildMembrane(self, coords, **kwargs): """Build membrane lattice around **coords**. :arg coords: a coordinate set or an object with ``getCoords`` method :type coords: :class:`numpy.ndarray` :arg membrane_high: the maximum z coordinate of the membrane. Default is **13.0** :type membrane_high: float :arg membrane_low: the minimum z coordinate of the membrane. Default is **-13.0** :type membrane_low: float :arg R: radius of all membrane in x-y direction. Default is **80** :type R: float :arg Ri: inner radius of the membrane in x-y direction if it needs to be hollow. Default is **0**, which is not hollow :type Ri: float :arg r: radius of each membrane node. Default is **3.1** :type r: float :arg lat: lattice type which could be **FCC** (face-centered-cubic, default), **SC** (simple cubic), **SH** (simple hexagonal) :type lat: str :arg exr: exclusive radius of each protein node. Default is **5.0** :type exr: float :arg hull: whether use convex hull to determine the protein's interior. Turn it off if protein is multimer. Default is **True** :type hull: bool :arg center: whether transform the structure to the origin (only x- and y-axis). Default is **True** :type center: bool """ atoms = coords try: coords = (coords._getCoords() if hasattr(coords, '_getCoords') else coords.getCoords()) except AttributeError: try: checkCoords(coords) except TypeError: raise TypeError('coords must be a Numpy array or an object ' 'with `getCoords` method') self._n_atoms = int(coords.shape[0]) LOGGER.timeit('_membrane') depth = kwargs.pop('depth', None) h = depth / 2 if depth is not None else None h = kwargs.pop('h', h) if h is not None: h = float(h) hu = h hl = -h else: hu = kwargs.pop('membrane_high', 13.0) hu = kwargs.pop('high', hu) hu = float(hu) hl = kwargs.pop('membrane_low', -13.0) hl = kwargs.pop('low', hl) hl = float(hl) R = float(kwargs.pop('R', 80.)) Ri = float(kwargs.pop('Ri', 0.)) r = float(kwargs.pop('r', 3.1)) lat = str(kwargs.pop('lat', 'FCC')) exr = float(kwargs.pop('exr', 5.)) use_hull = kwargs.pop('hull', True) centering = kwargs.pop('center', True) V = assign_lpvs(lat) if centering: c0 = coords.mean(axis=0) c0[-1] = 0. coords -= c0 # determine transmembrane part torf = np.logical_and(coords[:, -1] < hu, coords[:, -1] > hl) transmembrane = coords[torf, :] if not np.any(torf): raise ValueError( 'No region was identified as membrane. Please use a structure from opm/ppm.' ) if use_hull: from scipy.spatial import ConvexHull hull = ConvexHull(transmembrane) else: hull = transmembrane ## determine the bound for ijk imax = (R + V[0, 2] * (hu - hl) / 2.) / r jmax = (R + V[1, 2] * (hu - hl) / 2.) / r kmax = (R + V[2, 2] * (hu - hl) / 2.) / r imax = int(ceil(imax)) jmax = int(ceil(jmax)) kmax = int(ceil(kmax)) membrane = [] atm = 0 for i in range(-imax, imax): for j in range(-jmax, jmax): for k in range(-kmax, kmax): c = array([i, j, k]) xyz = 2. * r * dot(c, V) if xyz[2]>hl and xyz[2]<hu and \ xyz[0]>-R and xyz[0]<R and \ xyz[1]>-R and xyz[1]<R: dd = norm(xyz[:2]) if dd < R and dd > Ri: if checkClash(xyz, hull, radius=exr): membrane.append(xyz) atm = atm + 1 membrane = array(membrane) if len(membrane) == 0: self._membrane = None LOGGER.warn( 'no membrane is built. The protein should be transformed to the correct origin as in OPM' ) return coords else: self._membrane = AtomGroup(title="Membrane") self._membrane.setCoords(membrane) self._membrane.setResnums(range(atm)) self._membrane.setResnames(["NE1" for i in range(atm)]) self._membrane.setChids(["Q" for i in range(atm)]) self._membrane.setElements(["Q1" for i in range(atm)]) self._membrane.setNames(["Q1" for i in range(atm)]) LOGGER.report('Membrane was built in %2.fs.', label='_membrane') coords = self._combineMembraneProtein(atoms) return coords def buildHessian(self, coords, cutoff=15., gamma=1., **kwargs): """Build Hessian matrix for given coordinate set. **kwargs** are passed to :method:`.buildMembrane`. :arg coords: a coordinate set or an object with ``getCoords`` method :type coords: :class:`numpy.ndarray` :arg cutoff: cutoff distance (Å) for pairwise interactions, default is 15.0 Å :type cutoff: float :arg gamma: spring constant, default is 1.0 :type gamma: float """ atoms = coords turbo = kwargs.pop('turbo', True) try: coords = (coords._getCoords() if hasattr(coords, '_getCoords') else coords.getCoords()) except AttributeError: try: checkCoords(coords) except TypeError: raise TypeError('coords must be a Numpy array or an object ' 'with `getCoords` method') n_atoms = int(coords.shape[0]) if self._membrane is None: coords = self.buildMembrane(atoms, **kwargs) else: coords = self._combined.getCoords() system = zeros(coords.shape[0], dtype=bool) system[:n_atoms] = True LOGGER.timeit('_exanm') if turbo: self._hessian = buildReducedHessian(coords, system, cutoff, gamma, **kwargs) else: super(exANM, self).buildHessian(coords, cutoff, gamma, **kwargs) system = np.repeat(system, 3) self._hessian = _reduceModel(self._hessian, system) LOGGER.report('Hessian was built in %.2fs.', label='_exanm') self._dof = self._hessian.shape[0] self._n_atoms = n_atoms def calcModes(self, n_modes=20, zeros=False, turbo=True): """Calculate normal modes. This method uses :func:`scipy.linalg.eigh` function to diagonalize the Hessian matrix. When Scipy is not found, :func:`numpy.linalg.eigh` is used. :arg n_modes: number of non-zero eigenvalues/vectors to calculate. If **None** is given, all modes will be calculated. :type n_modes: int or None, default is 20 :arg zeros: If **True**, modes with zero eigenvalues will be kept. :type zeros: bool, default is **True** :arg turbo: Use a memory intensive, but faster way to calculate modes. :type turbo: bool, default is **True** """ super(exANM, self).calcModes(n_modes, zeros, turbo) def getMembrane(self): """Returns a copy of the membrane coordinates.""" return self._membrane def getCombined(self): """Returns a copy of the combined atoms or coordinates.""" return self._combined def _combineMembraneProtein(self, coords): if self._membrane is not None: if isinstance(coords, Atomic): self._combined = coords.copy() + self._membrane coords = self._combined.getCoords() else: self._combined = coords = np.concatenate( (coords, self._membrane.getCoords()), axis=0) else: self._combined = coords = copy(coords) return coords