示例#1
0
def LabZ(molecule,
         sel1=None,
         sel2=None,
         Nuc=None,
         resids=None,
         segids=None,
         filter_str=None):
    """Motion projected to the Z-axis of the Lab frame. Use only for systems
    that remain aligned along z
    """
    if Nuc is not None:
        sel1, sel2 = selt.protein_defaults(Nuc, molecule, resids, segids,
                                           filter_str)
    else:
        sel1 = selt.sel_simple(molecule, sel1, resids, segids, filter_str)
        sel2 = selt.sel_simple(molecule, sel2, resids, segids, filter_str)
    uni = molecule.mda_object

    def sub():
        box = uni.dimensions[0:3]
        v = sel1.positions - sel2.positions
        v = vft.pbc_corr(v.T, box)
        v[:2] = 0
        return v

    return sub
示例#2
0
def bond(molecule,
         sel1=None,
         sel2=None,
         sel3=None,
         Nuc=None,
         resids=None,
         segids=None,
         filter_str=None):
    """Bond defines the frame. 
    sel1/sel2   :   Defines the z-axis of the frame (the bond itself). Follows 
                    the argument rules of sel_simple (sel2 should usually be
                    the heteroatom) 
    Nuc         :   Automatically sets sel1 and sel2 for a given nucleus definition
    sel3        :   sel2 and sel3 will define the xz-plane of the bond frame. 
                    This is optional: however, if this frame is the PAS of the
                    bond responsible for relaxation, then frames may not 
                    function correctly if this is not provided. By default, sel3
                    is set to None and is omitted. However, if called from within
                    mol.
    resids, segids, filter_str apply additional filters to sel1, sel2, and sel3
    if defined.
    """
    if Nuc is not None:
        sel1, sel2 = selt.protein_defaults(Nuc, molecule, resids, segids,
                                           filter_str)
    else:
        sel1 = selt.sel_simple(molecule, sel1, resids, segids, filter_str)
        sel2 = selt.sel_simple(molecule, sel2, resids, segids, filter_str)

    if isinstance(sel3, str) and sel3 == 'auto':
        uni = sel1.universe
        resids = np.unique(sel1.resids)
        sel0 = uni.residues[np.isin(uni.residues.resids, resids)].atoms
        sel3 = selt.find_bonded(sel2, sel0, exclude=sel1, n=1,
                                sort='cchain')[0]
    elif sel3 is not None:
        sel3 = selt.sel_simple(molecule, sel3, resids, segids, filter_str)

    uni = molecule.mda_object

    if sel3 is None:

        def sub():
            box = uni.dimensions[0:3]
            v = sel1.positions - sel2.positions
            v = vft.pbc_corr(v.T, box)
            return v
    else:

        def sub():
            box = uni.dimensions[0:3]
            vZ = sel1.positions - sel2.positions
            vXZ = sel3.positions - sel2.positions
            vZ = vft.pbc_corr(vZ.T, box)
            vXZ = vft.pbc_corr(vXZ.T, box)
            return vZ, vXZ

    return sub
示例#3
0
def bond_rotate(molecule,
                sel1=None,
                sel2=None,
                sel3=None,
                Nuc=None,
                resids=None,
                segids=None,
                filter_str=None):
    """
    Rotation around a given bond, defined by sel1 and sel2. Has a very similar
    effect to simply using bond with the same sel1 and sel2. However, an addition
    selection is created to a third atom. Then, the vector between sel1 and
    sel2 defines the rotation axis. However, rotation around this axis caused
    by more distant motions is removed, because a third selection (sel3) is
    used with sel2 to create a second vector, which then remains in the xz plane
    
    (if only sel1 and sel2 are specified for rotation, then some rotation further
    up a carbon chain, for example, may not move the vector between sel1 and sel2,
    but does cause rotation of the inner bonds- in most cases it is not clear if
    this is happening, but becomes particularly apparent when rotation appears
    on double bonds, where rotation should be highly restricted)
    
    sel3 may be defined, but is not required. If it is not provided, a third 
    atom will be found that is bound to sel2 (this frame won't work if sel2 is
    not bound to any other atom). 
    """

    if Nuc is not None:
        sel1, sel2 = selt.protein_defaults(Nuc, molecule, resids, segids,
                                           filter_str)
    else:
        sel1 = selt.sel_simple(molecule, sel1, resids, segids, filter_str)
        sel2 = selt.sel_simple(molecule, sel2, resids, segids, filter_str)

    if sel3 is not None:
        sel3 = selt.sel_simple(molecule, sel3, resids, segids, filter_str)
    else:
        resids = np.unique(sel1.resids)
        i = np.isin(sel1.universe.residues.resids,
                    resids)  #Filter for atoms in the same residues
        sel0 = sel1.universe.residues[i].atoms
        sel3 = selt.find_bonded(sel2, sel0, sel1, n=1, sort='cchain')[0]

    uni = molecule.mda_object

    def sub():
        box = uni.dimensions[0:3]
        v1 = sel1.positions - sel2.positions
        v2 = sel2.positions - sel3.positions
        v1 = vft.pbc_corr(v1.T, box)
        v2 = vft.pbc_corr(v2.T, box)
        return v1, v2

    return sub
示例#4
0
def chain_rotate(molecule,
                 sel=None,
                 Nuc=None,
                 resids=None,
                 segids=None,
                 filter_str=None):
    """
    Creates a frame for which a chain of atoms (usually carbons) is aligned
    such that the vector formed by the previous and next heteroatom (not 1H)
    are aligned along z.
    
    Note that the frame is selected with a single initial selection, and the
    function automatically searches for the surrounding atoms. In case a methyl
    carbon is included, the rotation is defined by the carbon itself and its
    nearest neighbor, instead of the surrounding two atoms (which would then
    have to include a methyl proton)
    """

    uni = molecule.mda_object

    "Get the initial selection"
    if Nuc is not None:
        sel, _ = selt.protein_defaults(Nuc, molecule, resids, segids,
                                       filter_str)
    else:
        sel = selt.sel_simple(molecule, sel, resids, segids, filter_str)

    "Get all atoms in the residues included in the initial selection"
    resids = np.unique(sel.resids)
    sel0 = uni.residues[np.isin(uni.residues.resids, resids)].atoms

    "Get bonded"
    sel1, sel2 = selt.find_bonded(sel, sel0=sel0, n=2, sort='cchain')

    "Replace 1H with the original selection"
    i = sel2.types == 'H'

    sel20 = sel2
    sel2 = uni.atoms[:0]
    for s2, s, i0 in zip(sel20, sel, i):
        if i0:
            sel2 += s
        else:
            sel2 += s2

    def sub():
        box = uni.dimensions[0:3]
        v = sel2.positions - sel1.positions
        v = vft.pbc_corr(v.T, box)
        return v

    return sub
示例#5
0
    def select_atoms(self,
                     sel1=None,
                     sel2=None,
                     sel1in=None,
                     sel2in=None,
                     Nuc=None,
                     resids=None,
                     segids=None,
                     filter_str=None):
        """
        Selects the atoms to be used for bond definitions. 
        sel1/sel2 : A string or an atom group (MDanalysis) defining the 
                    first/second atom in the bond
        sel1in/sel2in : Index to re-assign sel1/sel2 possibly to multiple bonds
                        (example: if using string assignment, maybe to calculate
                        for multiple H's bonded to the same C)
        Nuc : Keyword argument for selecting a particular type of bond 
              (for example, N, C, CA would selection NH, C=O bonds, or CA-HA 
              bonds, respectively)
        resids : Filter the selection defined by the above arguments for only
                 certain residues
        segids : Filter the selection defined by the above arguments for only 
                 certain segments
        filter_str : Filter the selection by a string (MDAnalysis string selection)
        """

        if Nuc is None:
            "Apply sel1 and sel2 selections directly"
            if sel1 is not None:
                self.sel1 = selt.sel_simple(self, sel1, resids, segids,
                                            filter_str)
                if sel1in is not None:
                    self.sel1 = self.sel1[sel1in]
            if sel2 is not None:
                self.sel2 = selt.sel_simple(self, sel2, resids, segids,
                                            filter_str)
                if sel2in is not None:
                    self.sel2 = self.sel2[sel2in]
        else:
            self.sel1, self.sel2 = selt.protein_defaults(
                Nuc, self, resids, segids, filter_str)

        if self.sel1 is not None and self.sel2 is not None and self.sel1.n_atoms == self.sel2.n_atoms:
            self.set_selection()

            "An attempt to generate a unique label under various conditions"
            count, cont = 0, True
            while cont:
                if count == 0:  #One bond per residue- just take the residue number
                    label = self.sel1.resids
                elif count == 1:  #Multiple segments with same residue numbers
                    label = np.array([
                        '{0}_{1}'.format(s.segid, s.resid) for s in self.sel1
                    ])
                elif count == 2:  #Same segment, but multiple bonds on the same residue (include names)
                    label = np.array([
                        '{0}_{1}_{2}'.format(s1.resid, s1.name, s2.name)
                        for s1, s2 in zip(self.sel1, self.sel2)
                    ])
                elif count == 3:  #Multiple bonds per residue, and multiple segments
                    label=np.array(['{0}_{1}_{2}_{3}'.format(s1.segid,s1.resid,s1.name,s2.name) \
                                    for s1,s2 in zip(self.sel1,self.sel2)])
                "We give up after this"
                count = count + 1
                if np.unique(label).size == label.size or count == 4:
                    cont = False

            self.label = label
示例#6
0
def MOIbeta(molecule,
            sel,
            sel1=None,
            sel2=None,
            Nuc=None,
            index=None,
            resids=None,
            segids=None,
            filter_str=None):
    """
    Separates out rotation within the moment of inertia frame (should be used in
    conjunction with MOIz). That is, we identify rotational motion, where the z-axis
    is the direction of the Moment of Inertia vector. 
    
    The user must provide one or more selections to define the moment of inertia 
    (sel). The user must also provide the selections to which the MOI is applied
    (sel1 and sel2, or Nuc). Additional filters will be used as normal, applied 
    to all selections (resids,segids,filter_str). In case multiple MOI selections
    are provided (in a list), the user must provide an index, to specifify which
    bond goes with which MOI selection. This should usually be the same variable
    as provided for the frame_index when using MOIz (and one will usually not
    use a frame_index when setting up this frame)
    
    MOIxy(sel,sel1=None,sel2=None,Nuc=None,index=None,resids=None,segids=None,filter_str=None)
    """

    sel = selt.sel_lists(molecule, sel, resids, segids, filter_str)

    if Nuc is not None:
        sel1, sel2 = selt.protein_defaults(Nuc, molecule, resids, segids,
                                           filter_str)
    else:
        sel1 = selt.sel_simple(molecule, sel1, resids, segids, filter_str)
        sel2 = selt.sel_simple(molecule, sel2, resids, segids, filter_str)

    uni = molecule.mda_object
    uni.trajectory[0]

    sel = selt.sel_lists(molecule, sel, resids, segids, filter_str)
    uni = molecule.mda_object
    uni.trajectory[0]

    box = uni.dimensions[:3]

    for k, s in enumerate(sel):
        vr = s.positions
        i0 = vft.sort_by_dist(vr)
        sel[k] = sel[k][i0]

    vref = list()
    for s in sel:
        v0 = vft.pbc_pos(s.positions.T, box)
        vref.append(vft.principle_axis_MOI(v0)[:, 0])

    def MOIsub():
        v = list()
        box = uni.dimensions[:3]
        for s, vr in zip(sel, vref):
            v0 = vft.pbc_pos(s.positions.T, box)
            v1 = vft.principle_axis_MOI(v0)[:, 0]
            v.append(v1 * np.sign(np.dot(v1, vr)))
        return np.array(v).T

#    for k,s in enumerate(sel):
#        vr=s.positions
#        i0=vft.sort_by_dist(vr)
#        sel[k]=sel[k][i0]

    if index is None:
        if len(sel) == 1:
            index = np.zeros(sel1.n_atoms, dtype=int)
        elif len(sel) == sel1.n_atoms:
            index = np.arange(sel1.n_atoms, dtype=int)
        else:
            print('index must be defined')
            return

#    vref=list()
#    box=uni.dimensions[:3]
#    for s in sel:
#        v0=vft.pbc_pos(s.positions.T,box)
#        vref.append(vft.principle_axis_MOI(v0)[:,0])

    def sub():
        vnorm = MOIsub()
        #Pre-allocate output vector, to point along z
        vZ = np.zeros([3, sel1.n_atoms])
        vZ[2] = 1  #If a bond not in index, then vZ just on Z
        #        vXZ=np.zeros([3,sel1.n_atoms])
        #        vXZ[0]=1    #If a bond not in index, then vXZ just along x

        sc = np.array(vft.getFrame(vnorm)).T
        v00 = vft.norm(vft.pbc_corr((sel1.positions - sel2.positions).T, box))

        for k, (vn, sc0) in enumerate(zip(vnorm.T, sc)):
            v0 = v00[:, k == index]
            cb = v0[0] * vn[0] + v0[1] * vn[1] + v0[2] * vn[
                2]  #Angle between MOI and bond
            cb[cb > 1] = 1.0
            sb = np.sqrt(1 - cb**2)
            v0 = np.concatenate(([sb], [np.zeros(sb.shape)], [cb]), axis=0)
            "Here, we keep the vector fixed in the xz plane of the MOI frame"
            vZ[:, k == index] = vft.R(v0, *sc0)
#            vZ[:,k==index]=v0
#            vXZ[:,k==index]=np.atleast_2d(vn).T.repeat(v0.shape[1],1)

        return vZ

    return sub
示例#7
0
def MOIxy(molecule,
          sel,
          sel1=None,
          sel2=None,
          Nuc=None,
          index=None,
          resids=None,
          segids=None,
          filter_str=None):
    """
    Separates out rotation within the moment of inertia frame (should be used in
    conjunction with MOIz). That is, we identify rotational motion, where the z-axis
    is the direction of the Moment of Inertia vector. 
    
    The user must provide one or more selections to define the moment of inertia 
    (sel). The user must also provide the selections to which the MOI is applied
    (sel1 and sel2, or Nuc). Additional filters will be used as normal, applied 
    to all selections (resids,segids,filter_str). In case multiple MOI selections
    are provided (in a list), the user must provide an index, to specifify which
    bond goes with which MOI selection. This should usually be the same variable
    as provided for the frame_index when using MOIz (and one will usually not
    use a frame_index when setting up this frame)
    
    MOIxy(sel,sel1=None,sel2=None,Nuc=None,index=None,resids=None,segids=None,filter_str=None)
    """

    sel = selt.sel_lists(molecule, sel, resids, segids, filter_str)

    if Nuc is not None:
        sel1, sel2 = selt.protein_defaults(Nuc, molecule, resids, segids,
                                           filter_str)
    else:
        sel1 = selt.sel_simple(molecule, sel1, resids, segids, filter_str)
        sel2 = selt.sel_simple(molecule, sel2, resids, segids, filter_str)

    uni = molecule.mda_object
    uni.trajectory[0]

    for k, s in enumerate(sel):
        vr = s.positions
        i0 = vft.sort_by_dist(vr)
        sel[k] = sel[k][i0]

    if index is None:
        if len(sel) == 1:
            index = np.zeros(sel1.n_atoms, dtype=int)
        elif len(sel) == sel1.n_atoms:
            index = np.arange(sel1.n_atoms, dtype=int)
        else:
            print('index must be defined')
            return

    def sub():
        vnorm = list()
        box = uni.dimensions[:3]
        for s in sel:
            v0 = vft.pbc_pos(s.positions.T, box)
            vnorm.append(vft.principle_axis_MOI(v0)[:, 0])
        vnorm = np.array(vnorm)

        #Pre-allocate output vector, to point along z
        v1 = np.zeros([3, sel1.n_atoms])
        v1[2] = 1
        v2 = v1.copy()

        v0 = vft.pbc_corr((sel1.positions - sel2.positions).T, box)
        for k, vn in enumerate(vnorm):
            v1[:, k == index] = vft.projXY(v0[:, k == index], vn)
            v2[:, k == index] = np.array([vn]).T.repeat((k == index).sum(),
                                                        axis=1)

        return v1, v2

    return sub
示例#8
0
def librations0(molecule,
                sel1=None,
                sel2=None,
                Nuc=None,
                resids=None,
                segids=None,
                filter_str=None):
    """
    Defines a frame for which librations are visible. That is, for a given bond,
    defined by sel1 and sel2, we search for two other atoms bound to the 
    heteroatom (by distance). The reference frame is then defined by the 
    heteroatom and the additional two atoms, leaving primarily librational
    motion of the bond. We preferentially select the other two atoms for larger
    masses, but they may also be protons (for example, a methyl H–C bond will 
    be referenced to the next carbon but also another one of the protons of 
    the methyl group)
    
    In case the heteroatom only has two bound partners, the second atom in the
    bond will also be used for alignment, reducing the effect motion
    (not very common in biomolecules)
    
    librations(sel1,sel2,Nuc,resids,segids,filter_str)
    """
    if Nuc is not None:
        sel1, sel2 = selt.protein_defaults(Nuc, molecule, resids, segids,
                                           filter_str)
    else:
        sel1 = selt.sel_simple(molecule, sel1, resids, segids, filter_str)
        sel2 = selt.sel_simple(molecule, sel2, resids, segids, filter_str)

    if sel1.masses.sum() < sel2.masses.sum():
        sel1, sel2 = sel2, sel1  #Make sel1 the heteroatom

    resids = np.unique(sel1.resids)
    i = np.isin(sel1.universe.residues.resids,
                resids)  #Filter for atoms in the same residues
    sel0 = sel1.universe.residues[i].atoms
    sel2, sel3, sel4, sel5 = selt.find_bonded(sel1, sel0, n=4, sort='mass')

    def vfun():
        v = list()
        for v1, v2, v3, v4, v5 in zip(sel1, sel2, sel3, sel4, sel5):
            v0 = np.array([
                v2.position - v1.position, v3.position - v1.position,
                v4.position - v1.position, v5.position - v1.position
            ])
            box = uni.dimensions[:3]
            v.append(vft.pbc_corr(v0.T, box))
        return v

    uni = molecule.mda_object
    uni.trajectory.rewind()

    vref = vfun()

    def sub():
        R = list()
        vecs = vfun()
        R = [vft.RMSalign(vr, v) for v, vr in zip(vecs, vref)]
        return vft.R2vec(R)

    return sub
示例#9
0
def membrane_grid(molecule,
                  grid_pts,
                  sigma=25,
                  sel0=None,
                  sel='type P',
                  resids=None,
                  segids=None,
                  filter_str=None):
    """
    Calculates motion of the membrane normal, defined by a grid of points spread about
    the simulation. For each grid point, a normal vector is returned. The grid
    is spread uniformly around some initial selection (sel0 is a single atom!)
    in the xy dimensions (currently, if z is not approximately the membrane 
    normal, this function will fail).
    
    The membrane normal is defined by a set of atoms (determined with some 
    combination of the arguments sel, resids, segids, filter_str, with sel_simple)
    
    At each grid point, atoms in the selection will be fit to a plane. However,
    the positions will be weighted depending on how far they are away from that
    grid point in the xy dimensions. Weighting is performed with a normal 
    distribution. sigma, by default, has a width approximately equal to the 
    grid spacing (if x and y box lengths are different, we have to round off the
    spacing)
    
    The number of points is given by grid_pts. These points will be distributed
    automatically in the xy dimensions, to have approximately the same spacing
    in both dimensions. grid_pts will be changed to be the product of the exact
    number of points used (we will always distribute an odd number of points
    in each dimension, so the reference point is in the center of the grid)
    
    if sel0, defining the reference atom, is omitted, then the center of the
    box will be used. Otherwise, the grid will move around with the reference
    atom
    
    membrane_grid(molecule,grid_pts,sigma,sel0,sel,resids,segids,filter_str)
      
    """

    uni = molecule.mda_object

    X, Y, Z = uni.dimensions[:3]
    nX, nY = 1 + 2 * np.round(
        (np.sqrt(grid_pts) - 1) / 2 * np.array([X / Y, Y / X]))
    dX, dY = X / nX, Y / nY

    print(
        '{0:.0f} pts in X, {1:.0f} pts in Y, for {2:.0f} total points'.format(
            nX, nY, nX * nY))
    print('Spacing is {0:.2f} A in X, {0:.2f} A in Y'.format(dX, dY))
    print('Center of grid is found at index {0:.0f}'.format(nX * (nY - 1) / 2 +
                                                            (nX - 1) / 2))
    print('sigma = {0:.2f} A'.format(sigma))

    if sel0 is not None:
        sel0 = selt.sel_simple(molecule,
                               sel0)  #Make sure this is an atom group
        if hasattr(sel0, 'n_atoms'):
            if sel0.n_atoms != 1:
                print(
                    'Only one atom should be selected as the membrane grid reference point'
                )
                print('Setup failed')
                return
            else:
                sel0 = sel0[0]  #Make sure we have an atom, not an atom group

        tophalf = sel0.position[2] > Z / 2  #Which side of the membrane is this?
    else:
        tophalf = True

    "Atoms defining the membrance surface"
    sel = selt.sel_simple(molecule, sel, resids, segids, filter_str)

    "Filter for only atoms on the same side of the membrane"
    sel = sel[sel.positions[:, 2] > Z /
              2] if tophalf else sel[sel.positions[:, 2] < Z / 2]

    def grid():
        "Subfunction, calculates the grid"
        X0, Y0 = (
            X / 2, Y / 2
        ) if sel0 is None else sel0.position[:
                                             2]  #Grid at center, or at position of sel0
        Xout = np.transpose([X0 + (np.arange(nX) - (nX - 1) / 2) * dX
                             ]).repeat(nY, axis=1).reshape(int(nX * nY))
        Yout = np.array([Y0 + (np.arange(nY) - (nY - 1) / 2) * dY
                         ]).repeat(nY, axis=0).reshape(int(nX * nY))
        return Xout, Yout

    def sub():
        "Calculate planes for each element in grid"
        X, Y = grid()
        v = list()
        box = uni.dimensions[:3]
        for x, y in zip(X, Y):
            v0 = vft.pbc_corr(np.transpose(sel.positions - [x, y, 0]), box)
            d2 = v0[0]**2 + v0[1]**2
            i = d2 > 3 * sigma
            weight = np.exp(-d2[i] / (2 * sigma**2))

            v.append(vft.RMSplane(v0[:, i], np.sqrt(weight)))
        v = np.transpose(v)
        return v / np.sign(v[2])

    return sub