def run(self): # creating the atom group _acidic = self.u.select_atoms(self.sel_acidic) _basic = self.u.select_atoms(self.sel_basic) #defining the matrix _salt_bridge = np.zeros((self.nres, self.nres), int) for ts in self.u.trajectory[self.start:self.stop:self.stride]: _dists = capped_distance(_acidic.positions, _basic.positions, max_cutoff=self.sb_cutoff, return_distances=False, box=_acidic.dimensions) # At least for salt bridges, we need only that one bridge is formed. # Therefore we will map the pairs of atoms indices into the pairs of # residues and take the unique pair to compose the matrix. # It is symmetric, but we can use the upper diagonal part. _residxs = [] for resAcid, resBasic in zip(_acidic[_dists[:, 0]].resindices, _basic[_dists[:, 1]].resindices): _residxs.append([resAcid, resBasic]) _residxs = np.unique(np.array(_residxs), axis=0) # shifting back for r in _residxs: _salt_bridge[r[0], r[1]] += 1 SB_matrix = (_salt_bridge + _salt_bridge.T) * 100 / self.nframes np.savetxt(self.sb_file, SB_matrix) return SB_matrix
def search(self, atoms, radius, level='A'): """ Return all atoms/residues/segments that are within *radius* of the atoms in *atoms*. Parameters ---------- atoms : AtomGroup, MDAnalysis.core.groups.Atom list of atoms radius : float Radius for search in Angstrom. level : str char (A, R, S). Return atoms(A), residues(R) or segments(S) within *radius* of *atoms*. """ unique_idx = [] if isinstance(atoms, Atom): positions = atoms.position.reshape(1, 3) else: positions = atoms.positions pairs = capped_distance(positions, self.atom_group.positions, radius, box=self._box, return_distances=False) if pairs.size > 0: unique_idx = unique_int_1d(np.asarray(pairs[:, 1], dtype=np.int64)) return self._index2level(unique_idx, level)
def _unwrap_ns(self, refset_cog, configset_cog, added, to_be_added, box, method="pkdtree"): """ Find NN in refset_cog and configset_cog and pass back the indices stored in added and to_be added. """ distances = [] dist = 8.0 while len(distances) < 1: pairs, distances = capped_distance( refset_cog, configset_cog, dist, box=box, method=method, return_distances=True, ) dist += 0.5 minpair = np.where(distances == np.amin(distances))[0][0] imin = added[pairs[minpair][0]] jmin = to_be_added[pairs[minpair][1]] return imin, jmin
def get_cluster_distance( self, run_start: int, run_end: int, neighbor_cutoff: float, cluster_center: str = "center" ) -> np.floating: """Calculates the average distance of the center of clusters/molecules Args: run_start: Start frame of analysis. run_end: End frame of analysis. neighbor_cutoff: Upper limit of first nearest neighbor. cluster_center: species name of cluster center. Default to "center". Return: The averaged distance. """ center_atoms = self.wrapped_run.select_atoms(self.select_dict.get(cluster_center)) trj = self.wrapped_run.trajectory[run_start:run_end:] means = [] for ts in trj: distance_matrix = capped_distance( center_atoms.positions, center_atoms.positions, max_cutoff=neighbor_cutoff, box=ts.dimensions, return_distances=True, )[1] distance_matrix[distance_matrix == 0] = np.nan means.append(np.nanmean(distance_matrix)) return np.mean(means)
def _single_frame(self): pairs = capped_distance(self.membrane.positions, self.membrane.positions, max_cutoff=self.cutoff, box=self._ts.dimensions, return_distances=False) self.pairs = pairs # Find unique pairs of residues interacting # Currently we have pairs of atoms ref, neigh = np.unique(self._sorted_membrane_resindices[pairs], axis=0).T # Dont keep self-interactions between lipids different = ref != neigh ref = ref[different] neigh = neigh[different] # store neighbours for this frame frame_start = self._frame_index * self.membrane.n_residues self.neighbours[ref, neigh + frame_start] = 1 self.neighbours[ neigh, ref + frame_start] = 1 # the neighbour matrix must be symmetric
def _single_frame(self): pairs = capped_distance( self.membrane.positions, self.membrane.positions, max_cutoff=self.cutoff, box=self._ts.dimensions, return_distances=False ) # Find unique pairs of residues interacting # Currently we have pairs of atoms ref, neigh = np.unique(self._sorted_membrane_resindices[pairs], axis=0).T # Dont keep self-interactions between lipids different = ref != neigh ref = ref[different] neigh = neigh[different] # store neighbours for this frame data = np.ones_like(ref) self.neighbours[self._frame_index] = scipy.sparse.csr_matrix( (data, (ref, neigh)), dtype=np.int8, shape=(self.membrane.n_residues, self.membrane.n_residues) )
def get_cluster_distance(self, run_start, run_end, neighbor_cutoff, cluster_center="center"): """ Calculates the average distance of the center of clusters/molecules Args: run_start (int): Start time step. run_end (int): End time step. neighbor_cutoff (int of float): Upper limit of first nearest neighbor. cluster_center (str): species name of cluster center. Default to "center". """ center_atoms = ( self.wrapped_run.select_atoms(self.select_dict.get(cluster_center)) ) trj = self.wrapped_run.trajectory[run_start:run_end:] means = [] for ts in trj: distance_matrix = capped_distance(center_atoms.positions, center_atoms.positions, max_cutoff=neighbor_cutoff, box=ts.dimensions, return_distances=True)[1] distance_matrix[distance_matrix == 0] = np.nan means.append(np.nanmean(distance_matrix)) return np.mean(means)
def _single_frame(self, ts, atomgroups): u = atomgroups[0].universe box = ts.dimensions # Update donor-hydrogen pairs if necessary if self.update_selections: acceptors = u.select_atoms(self.acceptors_sel) donors, hydrogens = self._get_dh_pairs(u) else: acceptors = u.atoms[self._acceptors_ids] donors = u.atoms[self._donors_ids] hydrogens = u.atoms[self._hydrogens_ids] # find D and A within cutoff distance of one another # min_cutoff = 1.0 as an atom cannot form a hydrogen bond with itself d_a_indices, d_a_distances = capped_distance( donors.positions, acceptors.positions, max_cutoff=self.d_a_cutoff, min_cutoff=1.0, box=box, return_distances=True, ) # Remove D-A pairs more than d_a_cutoff away from one another tmp_donors = donors[d_a_indices.T[0]] tmp_hydrogens = hydrogens[d_a_indices.T[0]] tmp_acceptors = acceptors[d_a_indices.T[1]] # Find D-H-A angles greater than d_h_a_angle_cutoff d_h_a_angles = np.rad2deg( calc_angles( tmp_donors.positions, tmp_hydrogens.positions, tmp_acceptors.positions, box=box ) ) hbond_indices = np.where(d_h_a_angles > self.d_h_a_angle)[0] # Retrieve atoms, distances and angles of hydrogen bonds hbond_donors = tmp_donors[hbond_indices] hbond_hydrogens = tmp_hydrogens[hbond_indices] hbond_acceptors = tmp_acceptors[hbond_indices] hbond_distances = d_a_distances[hbond_indices] hbond_angles = d_h_a_angles[hbond_indices] # Store data on hydrogen bonds found at this frame hbonds = [[], [], [], [], [], []] hbonds[0].extend(np.full_like(hbond_donors, ts.frame)) hbonds[1].extend(hbond_donors.ids) hbonds[2].extend(hbond_hydrogens.ids) hbonds[3].extend(hbond_acceptors.ids) hbonds[4].extend(hbond_distances) hbonds[5].extend(hbond_angles) return np.asarray(hbonds).T
def _single_frame(self): box = self._ts.dimensions # Update donor-hydrogen pairs if necessary if self.update_selections: self._donors, self._hydrogens = self._get_dh_pairs() # find D and A within cutoff distance of one another # min_cutoff = 1.0 as an atom cannot form a hydrogen bond with itself d_a_indices, d_a_distances = capped_distance( self._donors.positions, self._acceptors.positions, max_cutoff=self.d_a_cutoff, min_cutoff=1.0, box=box, return_distances=True, ) # Remove D-A pairs more than d_a_cutoff away from one another tmp_donors = self._donors[d_a_indices.T[0]] tmp_hydrogens = self._hydrogens[d_a_indices.T[0]] tmp_acceptors = self._acceptors[d_a_indices.T[1]] # Find D-H-A angles greater than d_h_a_angle_cutoff d_h_a_angles = np.rad2deg( calc_angles( tmp_donors.positions, tmp_hydrogens.positions, tmp_acceptors.positions, box=box ) ) hbond_indices = np.where(d_h_a_angles > self.d_h_a_angle)[0] # Retrieve atoms, distances and angles of hydrogen bonds hbond_donors = tmp_donors[hbond_indices] hbond_hydrogens = tmp_hydrogens[hbond_indices] hbond_acceptors = tmp_acceptors[hbond_indices] hbond_distances = d_a_distances[hbond_indices] hbond_angles = d_h_a_angles[hbond_indices] # Store data on hydrogen bonds found at this frame self.hbonds[0].extend(np.full_like(hbond_donors, self._ts.frame)) self.hbonds[1].extend(hbond_donors.indices) self.hbonds[2].extend(hbond_hydrogens.indices) self.hbonds[3].extend(hbond_acceptors.indices) self.hbonds[4].extend(hbond_distances) self.hbonds[5].extend(hbond_angles)
def _single_frame(self): solute = self._solute[self._key] solvent = self._solvent[self._key] pairs, distances = capped_distance(solute.positions, solvent.positions, max(self._dists), box=self._ts.dimensions) solute_i, solvent_j = np.transpose(pairs) for d in self._dists: close_solv_atoms = solvent[solvent_j[distances < d]] result = [ d, self._key[0], self._key[1], self._key[2], self._ts.time, close_solv_atoms.n_atoms ] for i in range(len(self._col)): self._res_dict[self._col[i]].append(result[i])
def _single_frame(self, ts, atomgroups): g1, g2 = atomgroups u = g1.universe pairs, dist = distances.capped_distance(g1.positions, g2.positions, self._maxrange, box=u.dimensions) # If provided exclusions, create a mask of _result which # lets us take these out. if self._exclusion_block is not None: idxA = pairs[:, 0] // self._exclusion_block[0] idxB = pairs[:, 1] // self._exclusion_block[1] mask = np.where(idxA != idxB)[0] dist = dist[mask] count = np.histogram(dist, **self.rdf_settings)[0] volume = u.trajectory.ts.volume return np.array([count, np.array(volume, dtype=np.float64)])
def _get_dh_pairs(self, u): """Finds donor-hydrogen pairs. Returns ------- donors, hydrogens: AtomGroup, AtomGroup AtomGroups corresponding to all donors and all hydrogens. AtomGroups are ordered such that, if zipped, will produce a list of donor-hydrogen pairs. """ # If donors_sel is not provided, use topology to find d-h pairs if not self.donors_sel: if not (hasattr(u, 'bonds') and len(u.bonds) != 0): raise ValueError( 'Cannot assign donor-hydrogen pairs via topology as no' 'bonded information is present. ', 'Please either: ', 'load a topology file with bonded information; ', 'use the guess_bonds() topology guesser; ', 'or set HydrogenBondAnalysis.donors_sel so that a ' 'distance cutoff can be used.') hydrogens = u.select_atoms(self.hydrogens_sel) donors = sum(h.bonded_atoms[0] for h in hydrogens) # Otherwise, use d_h_cutoff as a cutoff distance else: hydrogens = u.select_atoms(self.hydrogens_sel) donors = u.select_atoms(self.donors_sel) donors_indices, hydrogen_indices = capped_distance( donors.positions, hydrogens.positions, max_cutoff=self.d_h_cutoff, box=u.dimensions, return_distances=False ).T donors = donors[donors_indices] hydrogens = hydrogens[hydrogen_indices] return donors, hydrogens
def _get_dh_pairs(self): """Finds donor-hydrogen pairs. Returns ------- donors, hydrogens: AtomGroup, AtomGroup AtomGroups corresponding to all donors and all hydrogens. AtomGroups are ordered such that, if zipped, will produce a list of donor-hydrogen pairs. """ # If donors_sel is not provided, use topology to find d-h pairs if not self.donors_sel: # We're using u._topology.bonds rather than u.bonds as it is a million times faster to access. # This is because u.bonds also calculates properties of each bond (e.g bond length). # See https://github.com/MDAnalysis/mdanalysis/issues/2396#issuecomment-596251787 if not (hasattr(self.u._topology, 'bonds') and len(self.u._topology.bonds.values) != 0): raise NoDataError( 'Cannot assign donor-hydrogen pairs via topology as no bond information is present. ' 'Please either: load a topology file with bond information; use the guess_bonds() ' 'topology guesser; or set HydrogenBondAnalysis.donors_sel so that a distance cutoff ' 'can be used.') hydrogens = self.u.select_atoms(self.hydrogens_sel) donors = sum(h.bonded_atoms[0] for h in hydrogens) if hydrogens \ else AtomGroup([], self.u) # Otherwise, use d_h_cutoff as a cutoff distance else: hydrogens = self.u.select_atoms(self.hydrogens_sel) donors = self.u.select_atoms(self.donors_sel) donors_indices, hydrogen_indices = capped_distance( donors.positions, hydrogens.positions, max_cutoff=self.d_h_cutoff, box=self.u.dimensions, return_distances=False).T donors = donors[donors_indices] hydrogens = hydrogens[hydrogen_indices] return donors, hydrogens
def _single_frame(self, ts, atomgroups): ags = [[atomgroups[2 * i], atomgroups[2 * i + 1]] for i in range(self.n)] count = [ np.zeros((ag1.n_atoms, ag2.n_atoms, self.len), dtype=np.float64) for ag1, ag2 in ags ] for i, (ag1, ag2) in enumerate(ags): u = ag1.universe pairs, dist = distances.capped_distance(ag1.positions, ag2.positions, self._maxrange, box=u.dimensions) for j, (idx1, idx2) in enumerate(pairs): count[i][idx1, idx2, :] = np.histogram(dist[j], **self.rdf_settings)[0] volume = u.trajectory.ts.volume return np.array([np.array(count), np.array(volume, dtype=np.float64)])
def _single_run(self, start, stop): """Perform a single pass of the trajectory""" self.u.trajectory[start] # Calculate partners at t=0 box = self.u.dimensions if self.pbc else None # 2d array of all distances pair, d = capped_distance(self.h.positions, self.a.positions, max_cutoff=self.d_crit, box=box) if self.exclusions: # set to above dist crit to exclude exclude = np.column_stack((self.exclusions[0], self.exclusions[1])) pair = np.delete(pair, np.where(pair == exclude), 0) hidx, aidx = np.transpose(pair) a = calc_angles(self.d.positions[hidx], self.h.positions[hidx], self.a.positions[aidx], box=box) # from amongst those, who also satisfiess angle crit idx2 = np.where(a > self.a_crit) hidx = hidx[idx2] aidx = aidx[idx2] nbonds = len(hidx) # number of hbonds at t=0 results = np.zeros_like(np.arange(start, stop, self._skip), dtype=np.float32) if self.time_cut: # counter for time criteria count = np.zeros(nbonds, dtype=np.float64) for i, ts in enumerate(self.u.trajectory[start:stop:self._skip]): box = self.u.dimensions if self.pbc else None d = calc_bonds(self.h.positions[hidx], self.a.positions[aidx], box=box) a = calc_angles(self.d.positions[hidx], self.h.positions[hidx], self.a.positions[aidx], box=box) winners = (d < self.d_crit) & (a > self.a_crit) results[i] = winners.sum() if self.bond_type == 'continuous': # Remove losers for continuous definition hidx = hidx[np.where(winners)] aidx = aidx[np.where(winners)] elif self.bond_type == 'intermittent': if self.time_cut: # Add to counter of where losers are count[~winners] += self._skip * self.u.trajectory.dt count[winners] = 0 # Reset timer for winners # Remove if you've lost too many times # New arrays contain everything but removals hidx = hidx[count < self.time_cut] aidx = aidx[count < self.time_cut] count = count[count < self.time_cut] else: pass if len(hidx) == 0: # Once everyone has lost, the fun stops break results /= nbonds return results
def contact_matrix(coord, cutoff=15.0, returntype="numpy", box=None): '''Calculates a matrix of contacts. There is a fast, high-memory-usage version for small systems (*returntype* = 'numpy'), and a slower, low-memory-usage version for larger systems (*returntype* = 'sparse'). If *box* dimensions are passed then periodic boundary conditions are applied. Parameters --------- coord : array Array of coordinates of shape ``(N, 3)`` and dtype float32. cutoff : float, optional, default 15 Particles within `cutoff` are considered to form a contact. returntype : string, optional, default "numpy" Select how the contact matrix is returned. * ``"numpy"``: return as an ``(N. N)`` :class:`numpy.ndarray` * ``"sparse"``: return as a :class:`scipy.sparse.lil_matrix` box : array-like or ``None``, optional, default ``None`` Simulation cell dimensions in the form of :attr:`MDAnalysis.trajectory.base.Timestep.dimensions` when periodic boundary conditions should be taken into account for the calculation of contacts. Returns ------- array or sparse matrix The contact matrix is returned in a format determined by the `returntype` keyword. See Also -------- :mod:`MDAnalysis.analysis.contacts` for native contact analysis .. versionchanged:: 0.11.0 Keyword *suppress_progmet* and *progress_meter_freq* were removed. ''' if returntype == "numpy": adj = np.full((len(coord), len(coord)), False, dtype=bool) pairs = capped_distance(coord, coord, max_cutoff=cutoff, box=box, return_distances=False) idx, idy = np.transpose(pairs) adj[idx, idy] = True return adj elif returntype == "sparse": # Initialize square List of Lists matrix of dimensions equal to number # of coordinates passed sparse_contacts = scipy.sparse.lil_matrix((len(coord), len(coord)), dtype='bool') if box is not None: # with PBC contact_matrix_pbc(coord, sparse_contacts, box, cutoff) else: # without PBC contact_matrix_no_pbc(coord, sparse_contacts, cutoff) return sparse_contacts
def condensed_ions( self, cluster, headgroup, ion, distances, method="pkdtree", pbc=True, wrap=False, ): """ Calculate number of species ion around each distance specified in distances around each cluster a cluster. MDAnalsys.lib.distances.capped_distances() is used for this, there is an issue with this code see this PR: https://github.com/MDAnalysis/mdanalysis/pull/2937 as long as this is not fixed, I put pkdtree as standard method. Parameters ---------- cluster: MDAnalysis.ResidueGroup cluster on which to perform analysis on. headgroup : str atom identifier of the headgroup, can also be a specific part of the headgroup or even a tailgroup. ion : str atom identifier of the species whose degree of condensation around the headgroups is to be determined. distances : float, list of floats Distance(s) up to which to determine the degree of condenstation. Can be multiple. method : {'bruteforce', 'nsgrid', 'pkdtree'}, optional Method to be passed to mda.lib.distances.capped_distance(). pbc : bool, optional Wether or not to take pbc into account, by default True Returns: -------- condensed_ions: list of ints the number of ions around headgroup for each distance. """ condensed_ions = [] self.universe = cluster.universe # Handle pbc # self._set_pbc_style(traj_pbc_style) if pbc: box = self.universe.dimensions else: box = None if wrap: UnwrapCluster().unwrap_cluster(cluster) # Define configuration set # This could be done with _select_species if refactored correctly. # Improvement: When looping over multiple distances do it first # for the largest distance, then adapt selection for shorter one. if isinstance(ion, str): ion = [ion] configset = self.universe.select_atoms("name {:s}".format(" ".join(ion))) configset = configset.atoms.positions # Define reference set if isinstance(headgroup, str): headgroup = [headgroup] refset = cluster.atoms.select_atoms("name {:s}".format(" ".join(headgroup))) refset = refset.atoms.positions condensed_ions = [] for distance in distances: unique_idx = [] # Call capped_distance for pairs pairs = capped_distance( refset, configset, distance, box=box, method=method, return_distances=False, ) # Make unique if pairs.size > 0: unique_idx = unique_int_1d(np.asarray(pairs[:, 1], dtype=np.int64)) condensed_ions.append(len(unique_idx)) return condensed_ions