コード例 #1
0
ファイル: _yaehmop.py プロジェクト: hainm/kugupu
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])
コード例 #2
0
ファイル: test_topology.py プロジェクト: Saxenauts/mdanalysis
 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))
コード例 #3
0
 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))
コード例 #4
0
 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))
コード例 #5
0
ファイル: test_topology.py プロジェクト: Saxenauts/mdanalysis
 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))
コード例 #6
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])
コード例 #7
0
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]
コード例 #8
0
ファイル: distances.py プロジェクト: MDAnalysis/mdanalysis
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])
コード例 #9
0
    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__)
コード例 #10
0
    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
コード例 #11
0
    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
コード例 #12
0
        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],
コード例 #13
0
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")