def shift_dimer_images(frag_i, frag_j): """Determine positions that place frag_j next to frag_i Important as yaehmop doesn't do periodic boundaries, but our distance search does! Returns ------- pos : numpy ndarray concatenated positions of frag_i and frag_j """ logger.debug("Checking if fragments are in correct image") c_i = frag_i.center_of_mass() c_j = frag_j.center_of_mass() tol = 0.001 d1 = distances.calc_bonds(c_i, c_j) d2 = distances.calc_bonds(c_i, c_j, frag_i.dimensions) # does the distance between the two molecules changes if we use # periodic boundaries? if not abs(d1 - d2) < tol: logger.debug("Shifting fragment") # calculate number of box images to shift shift = (c_i - c_j) / frag_i.dimensions[:3] pos_j = frag_j.positions + (np.rint(shift) * frag_i.dimensions[:3]) else: # else just take positions as-is pos_j = frag_j.positions return np.concatenate([frag_i.positions, pos_j])
def test_right_type_bonds(self): assert_equal(self.bgroup.bonds(), calc_bonds(self.bgroup.atom1.positions, self.bgroup.atom2.positions)) assert_equal(self.bgroup.bonds(pbc=True), calc_bonds(self.bgroup.atom1.positions, self.bgroup.atom2.positions, box=self.u.dimensions)) assert_equal(self.bgroup.values(), calc_bonds(self.bgroup.atom1.positions, self.bgroup.atom2.positions)) assert_equal(self.bgroup.values(pbc=True), calc_bonds(self.bgroup.atom1.positions, self.bgroup.atom2.positions, box=self.u.dimensions))
def test_right_type_bonds(self, bgroup, PSFDCD): assert_equal(bgroup.bonds(), calc_bonds(bgroup.atom1.positions, bgroup.atom2.positions)) assert_equal(bgroup.bonds(pbc=True), calc_bonds(bgroup.atom1.positions, bgroup.atom2.positions, box=PSFDCD.dimensions)) assert_equal(bgroup.values(), calc_bonds(bgroup.atom1.positions, bgroup.atom2.positions)) assert_equal(bgroup.values(pbc=True), calc_bonds(bgroup.atom1.positions, bgroup.atom2.positions, box=PSFDCD.dimensions))
def test_right_type_bonds(self, bgroup, PSFDCD): assert_equal( bgroup.bonds(), calc_bonds(bgroup.atom1.positions, bgroup.atom2.positions)) assert_equal( bgroup.bonds(pbc=True), calc_bonds(bgroup.atom1.positions, bgroup.atom2.positions, box=PSFDCD.dimensions)) assert_equal( bgroup.values(), calc_bonds(bgroup.atom1.positions, bgroup.atom2.positions)) assert_equal( bgroup.values(pbc=True), calc_bonds(bgroup.atom1.positions, bgroup.atom2.positions, box=PSFDCD.dimensions))
def test_right_type_bonds(self): assert_equal( self.bgroup.bonds(), calc_bonds(self.bgroup.atom1.positions, self.bgroup.atom2.positions)) assert_equal( self.bgroup.bonds(pbc=True), calc_bonds(self.bgroup.atom1.positions, self.bgroup.atom2.positions, box=self.u.dimensions)) assert_equal( self.bgroup.values(), calc_bonds(self.bgroup.atom1.positions, self.bgroup.atom2.positions)) assert_equal( self.bgroup.values(pbc=True), calc_bonds(self.bgroup.atom1.positions, self.bgroup.atom2.positions, box=self.u.dimensions))
def dist(A, B, offset=0, box=None): """Return distance between atoms in two atom groups. The distance is calculated atom-wise. The residue ids are also returned because a typical use case is to look at CA distances before and after an alignment. Using the `offset` keyword one can also add a constant offset to the resids which facilitates comparison with PDB numbering. Arguments --------- A, B : AtomGroup :class:`~MDAnalysis.core.groups.AtomGroup` with the same number of atoms offset : integer or tuple, optional, default 0 An integer `offset` is added to *resids_A* and *resids_B* (see below) in order to produce PDB numbers. If `offset` is :class:`tuple` then ``offset[0]`` is added to *resids_A* and ``offset[1]`` to *resids_B*. Note that one can actually supply numpy arrays of the same length as the atom group so that an individual offset is added to each resid. Returns ------- resids_A : array residue ids of the `A` group (possibly changed with `offset`) resids_B : array residue ids of the `B` group (possibly changed with `offset`) distances : array distances between the atoms """ if A.atoms.n_atoms != B.atoms.n_atoms: raise ValueError( "AtomGroups A and B do not have the same number of atoms") try: off_A, off_B = offset except (TypeError, ValueError): off_A = off_B = int(offset) residues_A = np.array(A.resids) + off_A residues_B = np.array(B.resids) + off_B d = calc_bonds(A.positions, B.positions, box) return np.array([residues_A, residues_B, d])
def calcSingleDistBetweenCoords_minImageConv(inpCell, coordA, coordB): """ Gets the distance between two co-ordinates in inpCell using the minimum image convention; thus, this will return the smallest distance possible with the PBCs Args: inpCell: (plato_pylib UnitCell object) We use this to get the lattice vectors needed to account for PBCs coordA: (len 3 iter) Co-ordinates of first atom coordB: (len 3 iter) Co-ordinates of second atom Returns dist: (float) The distance between the co-ordinates after taking PBCs into account """ dims = mdAnalysisInter.getMDAnalysisDimsFromUCellObj(inpCell) dists = distLib.calc_bonds(np.array([coordA[:3]]), np.array([coordB[:3]]), box=dims) assert len(dists) == 1 return dists[0]
def dist(A, B, offset=0, box=None): """Return distance between atoms in two atom groups. The distance is calculated atom-wise. The residue ids are also returned because a typical use case is to look at CA distances before and after an alignment. Using the `offset` keyword one can also add a constant offset to the resids which facilitates comparison with PDB numbering. Arguments --------- A, B : AtomGroup :class:`~MDAnalysis.core.groups.AtomGroup` with the same number of atoms offset : integer or tuple, optional, default 0 An integer `offset` is added to *resids_A* and *resids_B* (see below) in order to produce PDB numbers. If `offset` is :class:`tuple` then ``offset[0]`` is added to *resids_A* and ``offset[1]`` to *resids_B*. Note that one can actually supply numpy arrays of the same length as the atom group so that an individual offset is added to each resid. Returns ------- resids_A : array residue ids of the `A` group (possibly changed with `offset`) resids_B : array residue ids of the `B` group (possibly changed with `offset`) distances : array distances between the atoms """ if A.atoms.n_atoms != B.atoms.n_atoms: raise ValueError("AtomGroups A and B do not have the same number of atoms") try: off_A, off_B = offset except (TypeError, ValueError): off_A = off_B = int(offset) residues_A = np.array(A.resids) + off_A residues_B = np.array(B.resids) + off_B d = calc_bonds(A.positions, B.positions, box) return np.array([residues_A, residues_B, d])
def run(self, start=None, stop=None, step=None, verbose=None, debug=None): """Analyze trajectory and produce timeseries. Stores the water bridge data per frame as :attr:`WaterBridgeAnalysis.timeseries` (see there for output format). Parameters ---------- start : int (optional) starting frame-index for analysis, ``None`` is the first one, 0. `start` and `stop` are 0-based frame indices and are used to slice the trajectory (if supported) [``None``] stop : int (optional) last trajectory frame for analysis, ``None`` is the last one [``None``] step : int (optional) read every `step` between `start` (included) and `stop` (excluded), ``None`` selects 1. [``None``] verbose : bool (optional) toggle progress meter output :class:`~MDAnalysis.lib.log.ProgressMeter` [``True``] debug : bool (optional) enable detailed logging of debugging information; this can create *very big* log files so it is disabled (``False``) by default; setting `debug` toggles the debug status for :class:`WaterBridgeAnalysis`, namely the value of :attr:`WaterBridgeAnalysis.debug`. See Also -------- :meth:`WaterBridgeAnalysis.generate_table` : processing the data into a different format. """ self._setup_frames(self.u.trajectory, start, stop, step) logger.info("WBridge analysis: starting") logger.debug("WBridge analysis: donors %r", self.donors) logger.debug("WBridge analysis: acceptors %r", self.acceptors) logger.debug("WBridge analysis: water bridge %r", self.water_selection) if debug is not None and debug != self.debug: self.debug = debug logger.debug("Toggling debug to %r", self.debug) if not self.debug: logger.debug("WBridge analysis: For full step-by-step debugging output use debug=True") self._timeseries = [] self.timesteps = [] self._water_network = [] if verbose is None: verbose = self._verbose pm = ProgressMeter(self.n_frames, format="WBridge frame {current_step:5d}: {step:5d}/{numsteps} [{percentage:5.1f}%]\r", verbose=verbose) logger.info("Starting analysis (frame index start=%d stop=%d, step=%d)", self.start, self.stop, self.step) for progress, ts in enumerate(self.u.trajectory[self.start:self.stop:self.step]): # all bonds for this timestep # dict of tuples (atom.index, atom.index) for quick check if # we already have the bond (to avoid duplicates) frame = ts.frame timestep = ts.time self.timesteps.append(timestep) pm.echo(progress, current_step=frame) self.logger_debug("Analyzing frame %(frame)d, timestep %(timestep)f ps", vars()) if self.update_selection1: self._update_selection_1() if self.update_selection2: self._update_selection_2() if self.update_water_selection: self._update_water_selection() s1_frame_results_dict = defaultdict(list) if (self.selection1_type in ('donor', 'both') and self._water_acceptors): self.logger_debug("Selection 1 Donors <-> Water Acceptors") ns_acceptors = AtomNeighborSearch(self._water_acceptors) for i, donor_h_set in self._s1_donors_h.items(): d = self._s1_donors[i] for h in donor_h_set: res = ns_acceptors.search(h, self.distance) for a in res: donor_atom = h if self.distance_type != 'heavy' else d dist = distances.calc_bonds(donor_atom.position, a.position) if dist <= self.distance: angle = distances.calc_angles(d.position, h.position, a.position) angle = np.rad2deg(angle) if angle >= self.angle: self.logger_debug( "S1-D: {0!s} <-> W-A: {1!s} {2:f} A, {3:f} DEG"\ .format(h.index, a.index, dist, angle)) s1_frame_results_dict[(a.resname, a.resid)].append( (h.index, a.index, (h.resname, h.resid, h.name), (a.resname, a.resid, a.name), dist, angle)) if (self.selection1_type in ('acceptor', 'both') and self._s1_acceptors): self.logger_debug("Selection 1 Acceptors <-> Water Donors") ns_acceptors = AtomNeighborSearch(self._s1_acceptors) for i, donor_h_set in self._water_donors_h.items(): d = self._water_donors[i] for h in donor_h_set: res = ns_acceptors.search(h, self.distance) for a in res: donor_atom = h if self.distance_type != 'heavy' else d dist = distances.calc_bonds(donor_atom.position, a.position) if dist <= self.distance: angle = distances.calc_angles(d.position, h.position, a.position) angle = np.rad2deg(angle) if angle >= self.angle: self.logger_debug( "S1-A: {0!s} <-> W-D: {1!s} {2:f} A, {3:f} DEG"\ .format(a.index, h.index, dist, angle)) s1_frame_results_dict[(h.resname, h.resid)].append( (h.index, a.index, (h.resname, h.resid, h.name), (a.resname, a.resid, a.name), dist, angle)) # Narrow down the water selection selection_resn_id = list(s1_frame_results_dict.keys()) if not selection_resn_id: self._timeseries.append([]) continue selection_resn_id = ['(resname {} and resid {})'.format( resname, resid) for resname, resid in selection_resn_id] water_bridges = self._water.select_atoms(' or '.join(selection_resn_id)) self.logger_debug("Size of water bridge selection: {0} atoms".format(len(water_bridges))) if not water_bridges: logger.warning("No water forming hydrogen bonding with selection 1.") water_bridges_donors = water_bridges.select_atoms( 'name {0}'.format(' '.join(self.donors))) water_bridges_donors_h = {} for i, d in enumerate(water_bridges_donors): tmp = self._get_bonded_hydrogens(d) if tmp: water_bridges_donors_h[i] = tmp self.logger_debug("water bridge donors: {0}".format(len(water_bridges_donors))) self.logger_debug("water bridge donor hydrogens: {0}".format(len(water_bridges_donors_h))) water_bridges_acceptors = water_bridges.select_atoms( 'name {0}'.format(' '.join(self.acceptors))) self.logger_debug("water bridge: {0}".format(len(water_bridges_acceptors))) # Finding the hydrogen bonds between water bridge and selection 2 s2_frame_results_dict = defaultdict(list) if self._s2_acceptors: self.logger_debug("Water bridge Donors <-> Selection 2 Acceptors") ns_acceptors = AtomNeighborSearch(self._s2_acceptors) for i, donor_h_set in water_bridges_donors_h.items(): d = water_bridges_donors[i] for h in donor_h_set: res = ns_acceptors.search(h, self.distance) for a in res: donor_atom = h if self.distance_type != 'heavy' else d dist = distances.calc_bonds(donor_atom.position, a.position) if dist <= self.distance: angle = distances.calc_angles(d.position, h.position, a.position) angle = np.rad2deg(angle) if angle >= self.angle: self.logger_debug( "WB-D: {0!s} <-> S2-A: {1!s} {2:f} A, {3:f} DEG"\ .format(h.index, a.index, dist, angle)) s2_frame_results_dict[(h.resname, h.resid)].append( (h.index, a.index, (h.resname, h.resid, h.name), (a.resname, a.resid, a.name), dist, angle)) if water_bridges_acceptors: self.logger_debug("Selection 2 Donors <-> Selection 2 Acceptors") ns_acceptors = AtomNeighborSearch(water_bridges_acceptors) for i, donor_h_set in self._s2_donors_h.items(): d = self._s2_donors[i] for h in donor_h_set: res = ns_acceptors.search(h, self.distance) for a in res: donor_atom = h if self.distance_type != 'heavy' else d dist = distances.calc_bonds(donor_atom.position, a.position) if dist <= self.distance: angle = distances.calc_angles(d.position, h.position, a.position) angle = np.rad2deg(angle) if angle >= self.angle: self.logger_debug( "WB-A: {0!s} <-> S2-D: {1!s} {2:f} A, {3:f} DEG"\ .format(a.index, h.index, dist, angle)) s2_frame_results_dict[(a.resname, a.resid)].append( (h.index, a.index, (h.resname, h.resid, h.name), (a.resname, a.resid, a.name), dist, angle)) # Generate the water network water_network = {} for key in s2_frame_results_dict: s1_frame_results = set(s1_frame_results_dict[key]) s2_frame_results = set(s2_frame_results_dict[key]) if len(s1_frame_results.union(s2_frame_results)) > 1: # Thus if selection 1 and selection 2 are the same and both # only form a single hydrogen bond with a water, this entry # won't be included. water_network[key] = [s1_frame_results, s2_frame_results.difference(s1_frame_results)] # Generate frame_results frame_results = [] for s1_frame_results, s2_frame_results in water_network.values(): frame_results.extend(list(s1_frame_results)) frame_results.extend(list(s2_frame_results)) self._timeseries.append(frame_results) self._water_network.append(water_network) logger.info("WBridge analysis: complete; timeseries %s.timeseries", self.__class__.__name__)
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 d = distance_array(self.h.positions, self.a.positions, box=box) if self.exclusions: # set to above dist crit to exclude d[self.exclusions] = self.d_crit + 1.0 # find which partners satisfy distance criteria hidx, aidx = np.where(d < self.d_crit) 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 is 'continuous': # Remove losers for continuous definition hidx = hidx[np.where(winners)] aidx = aidx[np.where(winners)] elif self.bond_type is '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 _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 d = distance_array(self.h.positions, self.a.positions, box=box) if self.exclusions: # set to above dist crit to exclude d[self.exclusions] = self.d_crit + 1.0 # find which partners satisfy distance criteria hidx, aidx = numpy.where(d < self.d_crit) 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 = numpy.where(a > self.a_crit) hidx = hidx[idx2] aidx = aidx[idx2] nbonds = len(hidx) # number of hbonds at t=0 results = numpy.zeros_like(numpy.arange(start, stop, self._skip), dtype=numpy.float32) if self.time_cut: # counter for time criteria count = numpy.zeros(nbonds, dtype=numpy.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 is 'continuous': # Remove losers for continuous definition hidx = hidx[numpy.where(winners)] aidx = aidx[numpy.where(winners)] elif self.bond_type is '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
elif dot < -.5: vec += boxvec[i] return vec fout = open(folder + 'cationpi.dat', 'w') for ts in u.trajectory: sys.stdout.write('\rTime = %d' % u.trajectory.time) boxvec = mdamath.triclinic_vectors(u.dimensions) for ipi in range(npigroup): pigroup = pigroups[ipi] picenter = pigroup.centroid(pbc=True) for icat in range(ncat): cation = cations[icat] dist = distances.calc_bonds(coords1=cation.position, coords2=picenter, box=u.dimensions) if dist < 7.0: vec = vector(cation.position, picenter, boxvec) v1 = vector(pigroup.positions[0], pigroup.positions[5], boxvec) v2 = vector(pigroup.positions[1], pigroup.positions[4], boxvec) v3 = vector(pigroup.positions[2], pigroup.positions[3], boxvec) n1 = mdamath.normal(v1, v2) n2 = mdamath.normal(v3, v1) n3 = mdamath.normal(v3, v2) n = (n1 + n2 + n3) angle = np.degrees(mdamath.angle(n, vec)) if angle > 120 or angle < 60: fout.write( '%.1f %d %s %d %s %.6f %.6f\n' % (u.trajectory.time, catresid[icat], catresname[icat],
def main(): """Run main procedure.""" # TODO(schneiderfelipe): accept multiple files parser = argparse.ArgumentParser(description=__doc__) parser.add_argument("traj_files", nargs="+") args = parser.parse_args() gnorms = [] energies = [] all_atomnos = [] all_atomcoords = [] for traj_file in args.traj_files: atomnos, comments, atomcoords = read_xyz(traj_file) all_atomnos.extend(atomnos) all_atomcoords.extend(atomcoords) for comment in comments: fields = comment.split() gnorms.append(float(fields[3])) energies.append(float(fields[1])) energies = np.array(energies) energies -= energies.min() energies *= hartree * N_A / (kilo * calorie) u = mda.Universe.empty(n_atoms=len(all_atomnos[0]), trajectory=True) u.add_TopologyAttr("type", [element[i] for i in all_atomnos[0]]) u.load_new(all_atomcoords, order="fac") print(u) selection = None print("(enter 'q' for exit, 'h' for help)") while True: code = input("select> ").strip().split() if code[0] == "q": break elif code[0] == "h": for key in commands: print(f"{key:15s}: {commands[key]}") elif code[0] == "e": fig, ax = plt.subplots(2) ax[0].plot(energies) ax[0].set_xlabel("frame") ax[0].set_ylabel("energy (kcal/mol)") ax[1].plot(gnorms) ax[1].set_xlabel("frame") ax[1].set_ylabel("grad. norm (Eh/a0)") plt.show() elif code[0] == "s": print(selection) if selection is not None: print(selection_text) elif code[0] == "pca": if selection is None: print("empty selection, doing nothing") continue p = pca.PCA(u, select=selection_text) p.run() n_pcs = np.where(p.cumulated_variance > 0.95)[0][0] print(n_pcs) print(p.cumulated_variance[0:n_pcs]) pca_space = p.transform(selection, n_components=n_pcs) print(pca_space) print(pca.cosine_content(pca_space, 0)) elif code[0] == "p": if selection is None: print("empty selection, doing nothing") continue n = len(selection) if n == 2: data_label = "bond length (Å)" elif n == 3: data_label = "bond angle (°)" elif n == 4: data_label = "dihedral angle (°)" else: print("too few or too many indices") continue data = [] for i, (e, ts) in enumerate(zip(energies, u.trajectory)): if n == 2: d = distances.calc_bonds( selection[0].position, selection[1].position ) elif n == 3: d = np.degrees( distances.calc_angles( selection[0].position, selection[1].position, selection[2].position, ) ) elif n == 4: d = np.degrees( distances.calc_dihedrals( selection[0].position, selection[1].position, selection[2].position, selection[3].position, ) ) data.append(d) if i % 100 == 0 or i == len(u.trajectory) - 1: print( f"frame = {ts.frame:4d}: e = {e:5.1f} kcal/mol, {data_label.split('(')[0][:-1]} = {d:7.3f} {data_label[-2]}" ) data = np.array(data) fig, ax = plt.subplots(1, 2) ax[0].plot(data) ax[0].set_xlabel("frame") ax[0].set_ylabel(data_label) ax[1].plot(energies, data, "o", label="data points") ax[1].set_xlabel("energy (kcal/mol)") ax[1].set_ylabel(data_label) if n == 2: dx = 0.1 elif n == 3: dx = 10.0 elif n == 4: dx = 10.0 res = stats.binned_statistic( data, energies, "min", min(25, (data.max() - data.min()) / dx) ) # print(res.statistic) mask = np.isnan(res.statistic) res.statistic[mask] = np.interp( np.flatnonzero(mask), np.flatnonzero(~mask), res.statistic[~mask] ) # print(res.statistic) # ax[1].hlines(res.statistic, res.bin_edges[:-1], res.bin_edges[1:], colors='g', lw=2, label='binned min. energies') ax[1].barh( (res.bin_edges[:-1] + res.bin_edges[1:]) / 2, res.statistic, align="center", height=res.bin_edges[1:] - res.bin_edges[:-1], alpha=0.25, label="binned min. energies", ) ax[1].legend() plt.show() else: try: selection_text = " ".join(code) selection = u.select_atoms(selection_text) except mda.exceptions.SelectionError as e: print(e) print("bye")