Exemple #1
0
def frames2data(mol,frame_funs=None,frame_index=None,ffavg='direction',n=100,nr=10,label=None,avg_tensors=True,**kwargs):
    """
    Loads a set of frame functions into several data objects (the number of data
    objects produced is equal to the number of frames)
    
    Frames may be manually produced and included in a list (frame_funs), or 
    alternatively can be loaded into the molecule object itself (mol.new_frame)
    """
    
    
    
    vecs,avgs,tensors=get_vecs(mol,frame_funs,frame_index,ffavg,n,nr,**kwargs)
    if label is None and frame_funs is None:
        label=mol._frame_info['label']
    
    data=list()
    
    for q,(v,a,t) in enumerate(zip(vecs,avgs,tensors)):
        data.append(vec2data(v,molecule=mol,**kwargs))
        if avg_tensors:
            if frame_funs is None:frame_funs=mol._vf
            if frame_index is None:frame_index=mol._frame_info['frame_index']
            
            if q>0:
                "Extract the frame for the first point in the trajectory (index=np.array([0]))"
                v0=ini_vec_load(mol.mda_object.trajectory,[frame_funs[q-1]],[frame_index[q-1]],np.array([0]))['v'][0]
                if len(v0)==2:
                    v1,v2=v0
                else:
                    v1,v2=v0,None
                v1,inan=apply_index(v1,frame_index[q-1])
                v2,_=apply_index(v2,frame_index[q-1])
                
                sc=vft.getFrame(v1,v2)  #Angles to rotate first frame
                sc=[sc0.squeeze() for sc0 in sc]    

                "Apply to D2 evaluated at t=infty (D2inf)"                
                rho1=vft.pars2Spher(a['delta'],a['eta'],*a['euler'])
                rho=vft.Rspher(rho1,*sc)
                
                out=vft.Spher2pars(rho,return_angles=True)
                data[-1].vars['D2inf']={'delta':out[0],'eta':out[1],'euler':out[2:],'rho':a['rho']}
                
                "Apply to the averaged tensors"
                rho1=vft.pars2Spher(t['delta'],t['eta'],*t['euler'])
                rho=vft.Rspher(rho1,*sc)

                out=vft.Spher2pars(rho,return_angles=True)
                data[-1].vars['avg_tensors']={'delta':out[0],'eta':out[1],'euler':out[2:],'A0':t['rho']}
            else:
                data[-1].vars['D2inf']=a
                data[-1].vars['avg_tensors']=t
        if label is not None:
            data[-1].label=label
        
    return data
Exemple #2
0
def remove_frame(molecule, frame, sel, filename, steps=[0, -1, 1]):
    """
    Calculates and saves a trajectory for which a given frame is aligned 
    throughout the trajectory. Allows visualization of motion within the frame,
    in the absence of the frame motion itself.
    Arguments are the molecule object, frame, which specifies which frame,
    stored in the molecule object (molecule._vf) should be used (alternatively,
    one may give the function directly), sel, an MDAnalysis atom group that 
    includes all atoms to be stored, filename, which determines where to store
    the trajectory, and steps, which determines the start,stop, and step size 
    over the trajectory (as indices)
    
    Note, the frame should return only a single frame (defined by one or two
    vectors)
    """

    uni = molecule.mda_object

    if frame is None:

        def f():
            return np.atleast_2d([0, 0, 1]).T
    else:
        f = frame if hasattr(frame, '__call__') else molecule._vf[frame]

    nv = 2 if len(f()) == 2 else 1

    if steps[1] == -1: steps[1] = uni.trajectory.n_frames
    index = np.arange(steps[0], steps[1], steps[2])

    def pos_fun():
        return (sel.positions - sel.positions.mean(axis=0)).T

    vec = ini_vec_load(uni.trajectory, [f, pos_fun], index=index)

    v1, v2 = vec['v'][0] if nv == 2 else vec['v'][0], None
    v1 = apply_index(v1, np.zeros(sel.n_atoms))
    if v2 is not None: v2 = apply_index(v1, np.zeros(sel.n_atoms))
    v0 = vec['v'][1]
    sc = vft.getFrame(v1, v2)
    v0 = vft.R(v0, *vft.pass2act(*sc)).T

    with mda.Writer(os.path.splitext(filename)[0] + '.pdb', sel.n_atoms) as W:
        sel.positions = v0[0]
        W.write(sel)

    with mda.Writer(filename, sel.n_atoms) as W:
        for v in v0:
            sel.positions = v
            W.write(sel)
def applyFrame(*vecs,nuZ_F=None,nuXZ_F=None):
    """
    Applies a frame, F, to a set of vectors, *vecs, by rotating such that the
    vector nuZ_F lies along the z-axis, and nuXZ_F lies in the xz-plane. Input
    is the vectors (as *vecs, so list separately, don't collect in a list), and
    the frame, defined by nuZ_F (a vector on the z-axis of the frame), and 
    optionally nuXZ_F (a vector on xy-axis of the frame). These must be given
    as keyword arguments.
    
    vecs_F = applyFrame(*vecs,nuZ_F=nuZ_F,nuXZ_F=None,frame_index=None)
    
    Note, one may also omit the frame application and just apply a frame index
    """
    if nuZ_F is None:
        out=vecs
    else:
        sc=vft.pass2act(*vft.getFrame(nuZ_F,nuXZ_F))
        out=[None if v is None else vft.R(v,*sc) for v in vecs]
        
    if len(vecs)==1:
        return out[0]
    else:
        return out
Exemple #4
0
def applyFrame(vecs, mode='full'):
    """
    Calculates vectors, which may then be used to determine the influence of
    each frame on the overall correlation function (via detector analysis)
    
    For each frame, we will calculate its trajectory with respect to the previous
    frame (for example, usually the first frame should be some overall motion
    and the last frame should be the bonds themselves. The overall motion is 
    calculated w.r.t. the lab frame)
    
    ffavg is "Faster-frame averaging", which determines how we take into account
    the influence of outer frames on the inner frames. 
     ffavg='off' :      Neglect influence of outer frames, only calculate behavior
                        of frames with respect to the previous frame
     ffavg='direction': Apply motion of a given frame onto a vector pointing
                        in the same direction as the tensor obtained by averaging
                        the tensor in the outer frame
     ffavg='full' :     Apply motion using direction, and also scale
                        (currently unavailable...)
    
    """

    v0 = vecs['v']
    nf = len(v0)
    nt = vecs['t'].size

    avgs = list()

    fi = vecs['frame_index']
    nb = fi[0].size

    "Make sure all vectors in v0 have two elements"
    v0 = [v if len(v) == 2 else [v, None] for v in v0]

    "Start by updating vectors with the frame index"
    #    v0=[[apply_index(v0[k][0],fi[k]),apply_index(v0[k][1],fi[k])] for k in range(nf)]
    inan = list()
    v1 = list()
    for k in range(nf):
        a, b = apply_index(v0[k][0], fi[k])
        c, _ = apply_index(v0[k][1], fi[k])
        inan.append(b)
        v1.append([a, c])
    v0 = v1

    vec_out = list()
    avgs = list()
    tensors = list()

    "This is simply the averaged tensors– Not used, just for storage and later plotting"
    v1, v2 = v0[-1]
    sc0 = vft.getFrame(v1, v2)
    Davg = vft.D2(*sc0).mean(axis=-1)  #Average of tensor components of frame 0
    out = vft.Spher2pars(Davg)  #Convert into delta,eta,euler angles
    tensors.append({
        'delta': out[0],
        'eta': out[1],
        'euler': out[2:]
    })  #Returns in a dict
    tensors[-1].update({'A0': Davg[2]})
    """These are the expectation values for the correlation functions 
    of the individual components (m=-2->2) evaluated at infinite time
    """
    v1, _ = v0[-1]  #This is the bond direction in the lab frame
    #    Davg=vft.getD2inf(v1)
    Davg = vft.D2inf_v2(v1)
    out = vft.Spher2pars(Davg)
    avgs.append({'delta': out[0], 'eta': out[1], 'euler': out[2:]})
    avgs[-1].update({'rho': Davg})

    "Next sweep from outer frame in"
    for k in range(nf - 1):
        v1, v2 = v0.pop(
            0
        )  #Here we import the outermost frame (that is, the slowest frame)
        i = inan.pop(
            0)  #Here we remove motion from positions in this frame for NaN
        v1[0][i] = 0  #Frames with NaN here point along z for all times
        v1[1][i] = 0
        v1[2][i] = 1
        if v2 is not None:  #Frames with NaN here point along x for all times
            v2[0][i] = 1
            v2[1][i] = 0
            v2[2][i] = 0

        "Get the euler angles of the outermost frame"
        sc = vft.getFrame(
            v1, v2
        )  #Cosines and sines of alpha,beta,gamma (6 values ca,sa,cb,sb,cg,sg)
        """Apply rotations to all inner frames- that is, all inner frames are given in the outer frame
        Keep in mind that averaged tensors inside this frame are no longer expressed in the lab frame"
        This is later taken care of in 
        """
        vnew = list()
        for v in v0:
            v1, v2 = v
            "Switch active to passive, apply rotation to all inner vectors"
            sci = vft.pass2act(*sc)
            vnew.append([vft.R(v1, *sci), vft.R(v2, *sci)])

        scx = np.array(vft.getFrame(v0[-1][0])).swapaxes(1, 2)
        v0 = vnew  #Replace v0 for the next step

        v1, _ = v0[-1]  #This is the bond direction in the current frame
        "Averaged tensors from last frame- Not using this, just for record keeping"
        sc0 = vft.getFrame(v1)
        Davg = vft.D2(*sc0).mean(
            axis=-1)  #Average of tensor components of frame 0
        out = vft.Spher2pars(Davg)  #Convert into delta,eta,euler angles
        tensors.append({
            'delta': out[0],
            'eta': out[1],
            'euler': out[2:]
        })  #Returns in a dict
        tensors[-1].update({'rho': Davg})

        "D2 evaluated at infinite time of last frame"
        #        D2inf=vft.getD2inf(v1)   #Average D2 components in current frame at infinite time
        D2inf = vft.D2inf_v2(v1)
        out = vft.Spher2pars(D2inf)

        avgs.append({'delta': out[0], 'eta': out[1], 'euler': out[2:]})
        avgs[-1].update({'rho': D2inf})

        if mode[0].lower() == 'z':
            vZ = vft.R(
                np.array([0, 0, 1]),
                *avgs[-1]['euler'])  #Average of outer frame (usually bond)
            vZ = vft.R(np.atleast_3d(vZ).repeat(nt, axis=2),
                       *sc)  #Apply rotation of current frame to outer frame
            vZ = vZ.swapaxes(1, 2)
            vec_out.append({'vZ': vZ, 't': vecs['t'], 'index': vecs['index']})
        else:
            #            vX=vft.R(np.array([1,0,0]),*sc)
            #            vY=vft.R(np.array([0,1,0]),*sc)
            #            vZ=vft.R(np.array([0,0,1]),*sc)
            #            vX=vX.swapaxes(1,2)
            #            vY=vY.swapaxes(1,2)
            #            vZ=vZ.swapaxes(1,2)
            #
            #            sci=np.array(vft.euler_prod([sc,vft.pass2act(*sc0),vft.pass2act(*sc)])).swapaxes(1,2)
            #

            #            vec_out.append({'vZ':vZ,'vX':vX,'vY':vY,'t':vecs['t'],'sci':sci,'index':vecs['index']})

            sc = np.array(vft.pass2act(*sc)).swapaxes(1, 2)
            #            sc0=np.array(sc0).swapaxes(1,2)
            vZ = vft.R([0, 0, 1], *scx)
            vX = vft.R([1, 0, 0], *scx)
            vY = vft.R([0, 1, 0], *scx)
            vec_out.append({
                'sc': sc,
                'vZ': vZ,
                'vX': vX,
                'vY': vY,
                't': vecs['t'],
                'index': vecs['index']
            })

    v, _ = v0.pop(
        0
    )  #Operations on all but bond frame result in normalization, but here we have to enforce length
    v = vft.norm(v)

    v = v.swapaxes(1, 2)

    vec_out.append({'vZ': v, 't': vecs['t'], 'index': vecs['index']})

    return vec_out, avgs, tensors
Exemple #5
0
def frames2data(mol,
                frame_funs=None,
                frame_index=None,
                mode='full',
                n=100,
                nr=10,
                label=None,
                dt=None):
    """
    Loads a set of frame functions into several data objects (the number of data
    objects produced is equal to the number of frames)
    
    Frames may be manually produced and included in a list (frame_funs), or 
    alternatively can be loaded into the molecule object itself (mol.new_frame)
    """

    if mode.lower() == 'both':
        vecs, vecs_z, avgs, tensors = get_vecs(mol,
                                               frame_funs=frame_funs,
                                               frame_index=frame_index,
                                               mode=mode,
                                               n=n,
                                               nr=nr,
                                               dt=dt)
    else:
        vecs, avgs, tensors = get_vecs(mol,
                                       frame_funs=frame_funs,
                                       frame_index=frame_index,
                                       mode=mode,
                                       n=n,
                                       nr=nr,
                                       dt=dt)
    if label is None and frame_funs is None:
        label = mol._frame_info['label']

    data = list()

    "Here, we get the orientation of all frames at t=0"
    if frame_funs is None: frame_funs = mol._vf
    if frame_index is None: frame_index = mol._frame_info['frame_index']
    out = ini_vec_load(mol.mda_object.trajectory,
                       frame_funs,
                       frame_index,
                       index=np.array([0]))

    for q, (v, a, t) in enumerate(zip(vecs, avgs, tensors)):
        if mode.lower() == 'both':
            data.append([vec2data(v,a,mode='full',molecule=mol),\
                         vec2data(vecs_z[q],mode='z',molecule=mol)])
        else:
            data.append(vec2data(v, a, mode=mode, molecule=mol))

        if q > 0:
            v0 = out['v'][q - 1]
            v1, v2 = v0 if len(v0) == 2 else (v0, None)

            v1, inan = apply_index(v1, frame_index[q - 1])
            v2, _ = apply_index(v2, frame_index[q - 1])

            sc1 = vft.getFrame(v1, v2)
            sc1 = [sc0.squeeze() for sc0 in sc1]

            rho = vft.Rspher(t['rho'], *sc1)
            new = vft.Spher2pars(rho, return_angles=True)
            avg_tensors = {
                'delta': new[0],
                'eta': new[1],
                'euler': new[2:],
                'rho': t['rho']
            }
        else:
            avg_tensors = t

        out1 = out.copy()
        i = np.concatenate((np.arange(q), [len(vecs) - 1]))
        out1['v'] = [out1['v'][k] for k in i]
        out1['frame_index'] = [out1['frame_index'][k] for k in i]
        vecs1, _, _ = applyFrame(out1)
        #        sc=[vft.getFrame(v['vZ'],v['vX']) for v in vecs1[:-1]]
        sc = [vft.pass2act(*v['sc']) for v in vecs1[:-1]]
        sc.append(vft.getFrame(vecs1[-1]['vZ']))

        rho = a['rho']
        for sc0 in sc[::-1]:
            rho = vft.Rspher(rho, *sc0)

        new = vft.Spher2pars(rho.squeeze(), return_angles=True)

        D2inf = {
            'delta': new[0],
            'eta': new[1],
            'euler': new[2:],
            'rho': a['rho']
        }

        if mode.lower() == 'both':
            if label is not None:
                data[-1][0].label = label
                data[-1][1].label = label
            data[-1][0].vars['D2inf'] = D2inf
            data[-1][1].vars['D2inf'] = D2inf
            data[-1][0].vars['avg_tensors'] = avg_tensors
            data[-1][1].vars['avg_tensors'] = avg_tensors
        else:
            if label is not None:
                data[-1].label = label
            data[-1].vars['D2inf'] = D2inf
            data[-1].vars['avg_tensors'] = avg_tensors

    if mode.lower() == 'both':
        data = [[d[0] for d in data], [d[1] for d in data]]
    return data
Exemple #6
0
def applyFrame2(vecs,return_avgs=True,tensor_avgs=True):
    """
    Calculates vectors, which may then be used to determine the influence of
    each frame on the overall correlation function (via detector analysis)
    
    For each frame, we will calculate its trajectory with respect to the previous
    frame (for example, usually the first frame should be some overall motion
    and the last frame should be the bonds themselves. The overall motion is 
    calculated w.r.t. the lab frame)
    
    ffavg is "Faster-frame averaging", which determines how we take into account
    the influence of outer frames on the inner frames. 
     ffavg='off' :      Neglect influence of outer frames, only calculate behavior
                        of frames with respect to the previous frame
     ffavg='direction': Apply motion of a given frame onto a vector pointing
                        in the same direction as the tensor obtained by averaging
                        the tensor in the outer frame
     ffavg='full' :     Apply motion using direction, and also scale
                        (currently unavailable...)
    
    """

    v0=vecs['v']
    nf=len(v0)
    nt=vecs['t'].size
    
    avgs=list()
    
    fi=vecs['frame_index']
    nb=fi[0].size
    
    "Make sure all vectors in v0 have two elements"
    v0=[v if len(v)==2 else [v,None] for v in v0]
    
    "Start by updating vectors with the frame index"
#    v0=[[apply_index(v0[k][0],fi[k]),apply_index(v0[k][1],fi[k])] for k in range(nf)]
    inan=list()
    v1=list()
    for k in range(nf):
        a,b=apply_index(v0[k][0],fi[k])
        c,_=apply_index(v0[k][1],fi[k])
        inan.append(b)
        v1.append([a,c])
    v0=v1


    
    vec_out=list()
    avgs=list()
    tensors=list()

    if tensor_avgs:
        "This is simply the averaged tensors"
        v1,v2=v0[-1]
        sc0=vft.getFrame(v1,v2)
        Davg=vft.D2(*sc0).mean(axis=-1) #Average of tensor components of frame 0
        out=vft.Spher2pars(Davg)    #Convert into delta,eta,euler angles
        tensors.append({'delta':out[0],'eta':out[1],'euler':out[2:]}) #Returns in a dict
        tensors[-1].update({'rho':Davg})
    if return_avgs:
        """These are the expectation values for the correlation functions 
        of the individual components (m=-2->2) evaluated at infinite time
        """
        v1,_=v0[-1]
        D2inf=vft.getD2inf(v1)
        sc0=[sc0.squeeze() for sc0 in vft.getFrame(v1[:,:,:1])]
        sc0[0]=1    #Set alpha to zero (not -gamma)
        sc0[1]=0
        A0=D2inf[2]
        D2inf=vft.Rspher(D2inf,*sc0)
        out=vft.Spher2pars(D2inf)
        avgs.append({'delta':out[0],'eta':out[1],'euler':out[2:]})
        avgs[-1].update({'rho':D2inf})
    
    "Next sweep from outer frame in"
    for k in range(nf-1):
        v1,v2=v0.pop(0) #Should have two vectors (or v1 and None)
        i=inan.pop(0)   #Here we remove motion from positions in this frame for NaN
        v1[0][i]=0
        v1[1][i]=0
        v1[2][i]=1
        if v2 is not None:
            v2[0][i]=1
            v2[1][i]=0
            v2[2][i]=0
        
        "Get the euler angles"
        sc=vft.getFrame(v1,v2) #Cosines and sines of alpha,beta,gamma (6 values ca,sa,cb,sb,cg,sg)
        
        "Apply rotations to all inner frames"
        vnew=list()
        for v in v0:
            v1,v2=v
            "Switch active to passive, apply rotation to all inner vectors"
            sci=vft.pass2act(*sc)
            vnew.append([vft.R(v1,*sci),vft.R(v2,*sci)])
        
        v0=vnew #Replace v0 for the next step
        


        "Averaged tensor from LAST frame"

        
        if tensor_avgs:
            v1,v2=v0[-1]
            sc0=vft.getFrame(v1,v2)
            Davg=vft.D2(*sc0).mean(axis=-1) #Average of tensor components of frame 0
            out=vft.Spher2pars(Davg)    #Convert into delta,eta,euler angles
            tensors.append({'delta':out[0],'eta':out[1],'euler':out[2:]}) #Returns in a dict
            tensors[-1].update({'rho':D2inf})

        v1,_=v0[-1]
        D2inf=vft.getD2inf(v1)
        sc0=[sc0.squeeze() for sc0 in vft.getFrame(v1[:,:,:1])]
        sc0[0]=1    #Set alpha to 0 (as opposed to -gamma)
        sc0[1]=0
        A0=D2inf[2]
        D2inf=vft.Rspher(D2inf,*sc0)
        out=vft.Spher2pars(D2inf)
        avgs.append({'delta':out[0],'eta':out[1],'euler':out[2:]})
        avgs[-1].update({'rho':D2inf})
        
        vZ=vft.R(np.array([0,0,1]),*avgs[-1]['euler'])   #Average of outer frame (usually bond)
        vZ=vft.R(np.atleast_3d(vZ).repeat(nt,axis=2),*sc)    #Apply rotation of current frame to outer frame
        vX=vft.R(np.array([1,0,0]),*avgs[-1]['euler'])   #Average of outer frame (usually bond)
        vX=vft.R(np.atleast_3d(vX).repeat(nt,axis=2),*sc)    #Apply rotation of current frame to outer frame
        vec_out.append({'X':{'X':vX[0].T,'Y':vX[1].T,'Z':vX[2].T},\
                        'Z':{'X':vZ[0].T,'Y':vZ[1].T,'Z':vZ[2].T},\
                             't':vecs['t'],'index':vecs['index'],'eta':avgs[-1]['eta']})
    
    "Apply to last frame"
    v,_=v0.pop(0)
    l=np.sqrt(v[0]**2+v[1]**2+v[2]**2)
    v=v/l
            
    vec_out.append({'X':v[0].T,'Y':v[1].T,'Z':v[2].T,'t':vecs['t'],'index':vecs['index']})
    
    if return_avgs and tensor_avgs:
        for a in avgs:  #Convert to angles
            e=a['euler']
            alpha,beta,gamma=np.arctan2(e[1],e[0]),np.arctan2(e[3],e[2]),np.arctan2(e[5],e[4])
            a['euler']=np.concatenate(([alpha],[beta],[gamma]),axis=0)
        for a in tensors:  #Convert to angles
            e=a['euler']
            alpha,beta,gamma=np.arctan2(e[1],e[0]),np.arctan2(e[3],e[2]),np.arctan2(e[5],e[4])
            a['euler']=np.concatenate(([alpha],[beta],[gamma]),axis=0)
        return vec_out,avgs,tensors
    elif tensor_avgs:
        for a in tensors:  #Convert to angles
            e=a['euler']
            alpha,beta,gamma=np.arctan2(e[1],e[0]),np.arctan2(e[3],e[2]),np.arctan2(e[5],e[4])
            a['euler']=np.concatenate(([alpha],[beta],[gamma]),axis=0)
        return vec_out,tensors
    elif return_avgs:
        for a in avgs:  #Convert to angles
            e=a['euler']
            alpha,beta,gamma=np.arctan2(e[1],e[0]),np.arctan2(e[3],e[2]),np.arctan2(e[5],e[4])
            a['euler']=np.concatenate(([alpha],[beta],[gamma]),axis=0)
        return vec_out,avgs
    else:
        return vec_out
Exemple #7
0
def frames2data(mol,
                frame_funs=None,
                frame_index=None,
                mode='full',
                n=100,
                nr=10,
                label=None,
                dt=None):
    """
    Loads a set of frame functions into several data objects (the number of data
    objects produced is equal to the number of frames)
    
    Frames may be manually produced and included in a list (frame_funs), or 
    alternatively can be loaded into the molecule object itself (mol.new_frame)
    """

    vecs, avgs, tensors = get_vecs(mol,
                                   frame_funs=frame_funs,
                                   frame_index=frame_index,
                                   mode=mode,
                                   n=n,
                                   nr=nr,
                                   dt=dt)
    if label is None and frame_funs is None:
        label = mol._frame_info['label']

    data = list()

    "Here, we get the orientation of all frames at t=0"
    if frame_funs is None: frame_funs = mol._vf
    if frame_index is None: frame_index = mol._frame_info['frame_index']
    out = ini_vec_load(mol.mda_object.trajectory, frame_funs, frame_index,
                       np.array([0]))

    for q, (v, a, t) in enumerate(zip(vecs, avgs, tensors)):
        data.append(vec2data(v, a, mode=mode, molecule=mol))

        if q > 0:
            v0 = out['v'][q - 1]
            v1, v2 = v0 if len(v0) == 2 else (v0, None)

            v1, inan = apply_index(v1, frame_index[q - 1])
            v2, _ = apply_index(v2, frame_index[q - 1])

            sc1 = vft.getFrame(v1, v2)
            sc1 = [sc0.squeeze() for sc0 in sc1]

            rho = vft.Rspher(t['rho'], *sc1)
            new = vft.Spher2pars(rho, return_angles=True)
            data[-1].vars['avg_tensors'] = {
                'delta': new[0],
                'eta': new[1],
                'euler': new[2:],
                'rho': rho
            }
        else:
            data[-1].vars['avg_tensors'] = t

        out1 = out.copy()
        i = np.concatenate((np.arange(q), [len(vecs) - 1]))
        out1['v'] = [out1['v'][k] for k in i]
        out1['frame_index'] = [out1['frame_index'][k] for k in i]
        vecs1, _, _ = applyFrame(out1)
        sc = [vft.getFrame(v['vZ'], v['vX']) for v in vecs1[:-1]]
        sc.append(vft.getFrame(vecs1[-1]['vZ']))

        rho = a['rho']
        for sc0 in sc[::-1]:
            rho = vft.Rspher(rho, *sc0)

        new = vft.Spher2pars(rho.squeeze(), return_angles=True)

        data[-1].vars['D2inf'] = {
            'delta': new[0],
            'eta': new[1],
            'euler': new[2:],
            'rho': rho
        }

        #        if q>0:
        #            "Extract the frame for the first point in the trajectory (index=np.array([0]))"
        #            out=ini_vec_load(mol.mda_object.trajectory,[frame_funs[q-1],frame_funs[-1]],[frame_index[q-1],frame_funs[-1]],np.array([0]))['v']
        #            v0=out[0]
        #            if len(v0)==2:
        #                v1,v2=v0
        #            else:
        #                v1,v2=v0,None
        #            v1,inan=apply_index(v1,frame_index[q-1])
        #            v2,_=apply_index(v2,frame_index[q-1])
        #
        #            sc=vft.getFrame(v1,v2)  #Angles to rotate first frame
        #            sc=[sc0.squeeze() for sc0 in sc]
        #
        #            v0=out[1]
        #            if len(v0)==2:
        #                v1,v2=v0
        #            else:
        #                v1,v2=v0,None
        #
        #            sc1=vft.getFrame(v1,v2)
        #            sc1=[sc0.squeeze() for sc0 in sc1]
        #
        #            "Apply to D2 evaluated at t=infty (D2inf)"
        #            rho1=vft.pars2Spher(a['delta'],a['eta'],*a['euler'])
        #            rho=vft.Rspher(vft.Rspher(rho1,*vft.pass2act(*sc)),*sc1)
        ##            rho=vft.Rspher(rho1,*sc1)
        #
        #            out=vft.Spher2pars(rho,return_angles=True)
        #            data[-1].vars['D2inf']={'delta':out[0],'eta':out[1],'euler':out[2:],'rho':a['rho']}
        #
        #            "Apply to the averaged tensors"
        #            rho1=vft.pars2Spher(t['delta'],t['eta'],*t['euler'])
        #            rho=vft.Rspher(rho1,*sc)
        #
        #
        #            out=vft.Spher2pars(rho,return_angles=True)
        #            data[-1].vars['avg_tensors']={'delta':out[0],'eta':out[1],'euler':out[2:],'rho':t['rho']}
        #        else:
        #            v0=ini_vec_load(mol.mda_object.trajectory,[frame_funs[-1]],[frame_funs[-1]],np.array([0]))['v'][0]
        #            if len(v0)==2:
        #                v1,v2=v0
        #            else:
        #                v1,v2=v0,None
        #
        #            sc=vft.getFrame(v1,v2)  #Angles to rotate first frame
        #            sc=[sc0.squeeze() for sc0 in sc]
        #
        #            rho1=vft.pars2Spher(a['delta'],a['eta'],*a['euler'])
        #            rho=vft.Rspher(rho1,*sc)
        #
        #            out=vft.Spher2pars(rho,return_angles=True)
        #            data[-1].vars['D2inf']={'delta':out[0],'eta':out[1],'euler':out[2:],'rho':a['rho']}
        #
        #            data[-1].vars['avg_tensors']=t

        if label is not None:
            data[-1].label = label

    return data
def CtPAS(vZ,m=0,mp=0,nuZ_F=None,nuXZ_F=None,index=None):
    """
    Calculates the correlation function for a bond motion, optionally in frame
    F. Note, this is only for use when we are not interetested in motion of the
    bond in frame F induced by frame F, rather only the bond motion itself, 
    aligned by F. Defintion of F is optional, in which case the resulting 
    correlation function is the lab frame correlation function. 
    
    mp is the starting component, and m is the final component (D^2_{mp,m}). One
    or both must be zero.
    
    Currently, only Ct via FT is implemented. Sparse sampling may be used,
    if specified by index
    """
    
    "Some checks of the input data"
    if m!=0 and mp!=0:
        print('m or mp must be 0')
        return

    vZ=applyFrame(vZ,nuZ_F=nuZ_F,nuXZ_F=nuXZ_F)
        
    if not(m==0 and mp==0):
        sc=vft.getFrame(vZ)
        vX=vft.R([1,0,0],*sc)
        vY=vft.R([0,1,0],*sc)
    
    n=vZ.shape[-1]
    if vZ.ndim==2:
        SZ=2*n
    else:
        SZ=[vZ.shape[1],2*n]
    
    if mp==-2 or m==2:
        ft0=np.zeros(SZ,dtype=complex)
        for aX,aY,aZ in zip(vX,vY,vZ):
            for bX,bY,bZ in zip(vX,vY,vZ):
                ftzz=FT(aZ*bZ,index)
                ft0+=np.sqrt(3/8)*FT(aX*bX-aY*bY+1j*2*aX*bY,index).conj()*ftzz
    elif mp==-1 or m==1:
        ft0=np.zeros(SZ,dtype=complex)
        for aX,aY,aZ in zip(vX,vY,vZ):
            for bZ in vZ:
                ftzz=FT(aZ*bZ,index)
                ft0+=np.sqrt(3/2)*FT(aX*bZ-1j*aY*bZ,index).conj()*ftzz
    elif mp==0 and m==0:
        ft0=np.zeros(SZ)   
        for aZ in vZ:
            for bZ in vZ:
                ftzz=FT(aZ*bZ,index)
                ft0+=3/2*(ftzz.conj()*ftzz).real
    elif mp==1 or m==-1:
        ft0=np.zeros(SZ,dtype=complex)
        for aX,aY,aZ in zip(vX,vY,vZ):
            for bZ in vZ:
                ftzz=FT(aZ*bZ,index)
                ft0+=np.sqrt(3/2)*FT(-aX*bZ-1j*aY*bZ,index).conj()*ftzz
    elif mp==2 or m==-2:
        ft0=np.zeros(SZ,dtype=complex)
        for aX,aY,aZ in zip(vX,vY,vZ):
            for bX,bY,bZ in zip(vX,vY,vZ):
                ftzz=FT(aZ*bZ,index)
                ft0+=np.sqrt(3/8)*FT(aX*bX-aY*bY-1j*2*aX*bY,index).conj()*ftzz
                
    "Truncate function to half length"
    if vZ.ndim==3:  
        ct=np.fft.ifft(ft0)[:,:n]
    else:
        ct=np.fft.ifft(ft0)[:n]
    
    "Properly normalize correlation function"
    if index is not None:        
        N=get_count(index)
    else:
        N=np.arange(n,0,-1)
    ct=ct/N    
    
    "Subtract away 1/2 for m,mp=0"
    if mp==0 and m==0:
        ct=ct.real-0.5
    
    return ct
def Ct_finF(vZ,nuZ_f,nuXZ_f=None,m=0,mp=0,nuZ_F=None,nuXZ_F=None,index=None):
    """
    Calculates the correlation function for the motion of a bond due to motion
    of a frame of a frame f in frame F (or in the lab frame)
    """
    
    "Some checks of the input data"
    if m!=0 and mp!=0:
        print('m or mp must be 0')
        return

    "Apply frame F"
    vZF,nuZ_fF,nuXZ_fF=applyFrame(vZ,nuZ_f,nuXZ_f,nuZ_F=nuZ_F,nuXZ_F=nuXZ_F)
    "Apply frame f"
#    vZf=applyFrame(vZ,nuZ_F=nuZ_f,nuXZ_F=nuXZ_f)
  
    "Axes to project a bond in frame f into frame F"
    sc=vft.getFrame(nuZ_fF,nuXZ_fF)
    vZf=vft.R(vZF,*vft.pass2act(*sc))
    eFf=[vft.R([1,0,0],*vft.pass2act(*sc)),\
         vft.R([0,1,0],*vft.pass2act(*sc)),\
         vft.R([0,0,1],*vft.pass2act(*sc))]
    
    
    "Axes of the bond vector in frame F"
    if not(m==0 and mp==0):
        sc=vft.getFrame(vZF)
        vXF=vft.R([1,0,0],*sc)
        vYF=vft.R([0,1,0],*sc)
        
    n=vZ.shape[-1]
    if vZ.ndim==2:
        SZ=2*n
    else:
        SZ=[vZ.shape[1],2*n] 
    ft0=np.zeros(SZ,dtype=complex)
    if mp==-2 or m==2:
        for ea,ax,ay in zip(eFf,vXF,vYF):
            for eb,bx,by in zip(eFf,vXF,vYF):
                for eag,gz in zip(ea,vZf):
                    for ebd,dz in zip(eb,vZf):
                        ftee=FT(eag*ebd,index)
                        ft0+=np.sqrt(3/8)*FT(ax*bx*gz*dz-ay*by*gz*dz-1j*2*ax*by*gz*dz,index).conj()*ftee
    elif mp==-1 or m==1:
        for ea,ax,ay in zip(eFf,vXF,vYF):
            for eb,bz in zip(eFf,vZF):
                for eag,gz in zip(ea,vZf):
                    for ebd,dz in zip(eb,vZf):
                        ftee=FT(eag*ebd,index)
                        ft0+=np.sqrt(3/2)*FT(ax*bz*gz*dz+1j*ay*bz*gz*dz,index).conj()*ftee
    elif mp==0 and m==0:      
        for ea,az in zip(eFf,vZF):
            for eb,bz in zip(eFf,vZF):
                for eag,gz in zip(ea,vZf):
                    for ebd,dz in zip(eb,vZf):
                        ftee=FT(eag*ebd,index)
                        ft0+=3/2*(FT(az*bz*gz*dz,index).conj()*ftee)        
    elif mp==1 or m==-1:
        for ea,ax,ay in zip(eFf,vXF,vYF):
            for eb,bz in zip(eFf,vZF):
                for eag,gz in zip(ea,vZf):
                    for ebd,dz in zip(eb,vZf):
                        ftee=FT(eag*ebd,index)
                        ft0+=np.sqrt(3/2)*FT(-ax*bz*gz*dz+1j*ay*bz*gz*dz,index).conj()*ftee
    elif mp==2 or m==-2:
        for ea,ax,ay in zip(eFf,vXF,vYF):
            for eb,bx,by in zip(eFf,vXF,vYF):
                for eag,gz in zip(ea,vZf):
                    for ebd,dz in zip(eb,vZf):
                        ftee=FT(eag*ebd,index)
                        ft0+=np.sqrt(3/8)*FT(ax*bx*gz*dz-ay*by*gz*dz+1j*2*ax*by*gz*dz,index).conj()*ftee
    
    "Use to properly normalize correlation function"
    if index is not None:        
        N=get_count(index)
    else:
        N=np.arange(n,0,-1)
    i=N!=0
    N=N[i]

    "Truncate function to half length"
    if vZ.ndim==3:  
        ct=np.fft.ifft(ft0)[:,:n]
        ct=ct[:,i]/N
    else:
        ct=np.fft.ifft(ft0)[:n]
        ct=ct[i]/N
    
    
    "Subtract away 1/2 for m,mp=0"
    if mp==0 and m==0:
        ct=ct.real-0.5
    
    return ct                    
def D2infPAS(vZ,nuZ_F=None,nuXZ_F=None,m=None):
    """
    Estimates the Wigner rotation matrix elements of a bond reorientation between
    two times separated by infinite time, that is, we calculate:
    
    Am=lim_t->infty <D_{0m}^2(\Omega_tau,t+tau>
    
    for a bond with direction given by vZ.
    
    nuZ_F and nuXY_F are optional arguments to define frame in which to calculate
    the terms. Otherwise, calculation is performed in the input frame of vZ. 
    A frame_index must also be provided if nuZ_F is not the same size as vZ
    
    Setting m=None (default) results in all terms being returned in an array 
    (outer dimension runs from m=-2 to 2)
    
    Am = D2inf(vZ,vX=None,vY=None,m=None)
    
    """

    "Apply the frame if required"
    vZ=applyFrame(vZ,nuZ_F=nuZ_F,nuXZ_F=nuXZ_F)
    

    if m is None:
        m1=[-2,-1,0]    #Default, get all values (we'll calculate 1 and 2 from -1 and -2)
    else:
        m1=[m]
        
    if m!=0:
        sc=vft.getFrame(vZ)
        vX=vft.R([1,0,0],*sc)
        vY=vft.R([0,1,0],*sc)
        
    A=list()    #Collect the output


    
    for m0 in m1:   #Sweep over the components (or just one in case m is not None)
        if vZ.ndim==2:
            d2inf=0
        else:
            d2inf=np.zeros(vZ.shape[1],dtype=complex)
        if m0==-2:
            for aX,aY,aZ in zip(vX,vY,vZ):  #Loop over x,y,z components of all 3 axes
                for bX,bY,bZ in zip(vX,vY,vZ):  #ditto
                    d2inf+=np.sqrt(3/8)*((aX*bX).mean(-1)-(aY*bY).mean(-1))*(aZ*bZ).mean(-1)\
                            +1j*np.sqrt(3/2)*(aX*bY).mean(-1)*(aZ*bZ).mean(-1)
        elif m0==-1:
            for aX,aY,aZ in zip(vX,vY,vZ):  #Loop over x,y,z components of all 3 axes
                for bZ in vZ:  #ditto
                    d2inf+=np.sqrt(3/2)*(aX*bZ).mean(-1)*(aZ*bZ).mean(-1)\
                            -1j*np.sqrt(3/2)*(aY*aZ).mean(-1)*(aZ*bZ).mean(-1)
        elif m0==0:
            if vZ.ndim==2:
                d2inf=-0.5   #Offset for 0,0 term
            else:
                d2inf[:]=-0.5
            for aZ in vZ:  #Loop over x,y,z components of all 3 axes
                for bZ in vZ:  #ditto
                    d2inf+=3/2*((aZ*bZ).mean(-1))**2 #Real only
        elif m0==1:
            for aX,aY,aZ in zip(vX,vY,vZ):  #Loop over x,y,z components of all 3 axes
                for bZ in vZ:  #ditto
                    d2inf+=-np.sqrt(3/2)*(aX*bZ).mean(-1)*(aZ*bZ).mean(-1)\
                            -1j*np.sqrt(3/2)*(aY*aZ).mean(-1)*(aZ*bZ).mean(-1)
        elif m0==2:
            for aX,aY,aZ in zip(vX,vY,vZ):  #Loop over x,y,z components of all 3 axes
                for bX,bY,bZ in zip(vX,vY,vZ):  #ditto
                    d2inf+=np.sqrt(3/8)*((aX*bX).mean(-1)-(aY*bY).mean(-1))*(aZ*bZ).mean(-1)\
                            -1j*np.sqrt(3/2)*(aX*bY).mean(-1)*(aZ*bZ).mean(-1)
        A.append(d2inf)
        
    if m is None:
        A.append(-A[1].conj())  #A_1=-A^*_-1
        A.append(A[0].conj())   #A_2=A^*_-2
    else:
        A=A[0]  #Only one element- get rid of list
    
    return np.array(A)  #Return as numpy array
def loop_gen(vZ,vXZ=None,nuZ_f=None,nuXZ_f=None,nuZ_F=None,nuXZ_F=None,m=None):
    """
    Generates the loop items required for calculating correlation functions and
    equilibrium values.
    
    For f in F calculations, vZ and nuZ_f are required, with additional optional
    arguments nuXZ_f, nuZ_F (calc in LF if omitted), nuXZ_F. If nuZ_f is included,
    a dictionary with the following keys is returned for each of 81 iterator 
    elements
    
    Dictionary keys:
        eag:    "gamma" element of the vector representing the "alpha" axis for
                projection from frame f into frame F
        ebd:    "delta" element of the vector representing the "beta" axis for
                projection from frame f into frame F
        gz:     "gamma" element of the bond vector in frame f
        dz:     "delta" element of the bond vector in frame f
        ax:     "alpha" element of the x-axis of the bond axis system in frame F
        ay:     "alpha" element of the y-axis of the bond axis system in frame F
        az:     "alpha" element of the z-axis of the bond axis system in frame F
        bx:     "beta" element of the x-axis of the bond axis system in frame F
        by:     "beta" element of the y-axis of the bond axis system in frame F
        bz:     "beta" element of the z-axis of the bond axis system in frame F
        
    For PAS calculations (optionally in frame F), vZ is required, with additional
    optional arguments nuZ_F (calc in LF if omitted) and nuXZ_F. In this case,
    a dictionary is returned with the following keys for each of 9 iterator 
    elements
    
    Dictionary keys:
        ax:     "alpha" element of the x-axis of the bond axis system
        ay:     "alpha" element of the y-axis of the bond axis system
        az:     "alpha" element of the z-axis of the bond axis system
        bx:     "beta" element of the x-axis of the bond axis system
        by:     "beta" element of the y-axis of the bond axis system
        bz:     "beta" element of the z-axis of the bond axis system
    
    If m is provided, only the dictionary elements required for calculating the mth
    component will be returned
    
    loop_gen(vZ,nuZ_f=None,nuXZ_f=None,nuZ_F=None,nuXZ_F=None,m=None)
    """
    
    "Apply frame F"
    vZF,vXZF,nuZ_fF,nuXZ_fF=applyFrame(vZ,vXZ,nuZ_f,nuXZ_f,nuZ_F=nuZ_F,nuXZ_F=nuXZ_F)
    
    if m!=0: 
        sc=vft.getFrame(vZF,vXZF)
        vXF=vft.R([1,0,0],*sc)  #x and y axes of the bond axes in F
        vYF=vft.R([0,1,0],*sc)

    if nuZ_f is None:
        if m is None or m==-2 or m==2:
            for ax,ay,az in zip(vXF,vYF,vZF):
                for bx,by,bz in zip(vXF,vYF,vZF):
                    out={'ax':ax,'ay':ay,'az':az,'bx':bx,'by':by,'bz':bz}
                    yield out
        elif m==-1 or m==1:
            for ax,ay,az in zip(vXF,vYF,vZF):
                for bz in vZF:
                    out={'ax':ax,'ay':ay,'az':az,'bz':bz}
                    yield out
        elif m==0:
            for az in vZF:
                for bz in vZF:
                    out={'az':az,'bz':bz}
                    yield out
                    
    else:
        sc=vft.getFrame(nuZ_fF,nuXZ_fF)
        vZf=vft.R(vZF,*vft.pass2act(*sc))
        eFf=[vft.R([1,0,0],*vft.pass2act(*sc)),\
             vft.R([0,1,0],*vft.pass2act(*sc)),\
             vft.R([0,0,1],*vft.pass2act(*sc))]
            
        if m is None:
            for ea,ax,ay,az in zip(eFf,vXF,vYF,vZF):
                for eb,bx,by,bz in zip(eFf,vXF,vYF,vZF):
                    for eag,gz in zip(ea,vZf):
                        for ebd,dz in zip(eb,vZf):
                            out={'eag':eag,'ebd':ebd,'ax':ax,'ay':ay,'az':az,\
                                 'bx':bx,'by':by,'bz':bz,'gz':gz,'dz':dz}
                            yield out
        elif m==-2 or m==2:
            for ea,ax,ay in zip(eFf,vXF,vYF):
                for eb,bx,by in zip(eFf,vXF,vYF):
                    for eag,gz in zip(ea,vZf):
                        for ebd,dz in zip(ea,vZf):
                            out={'eag':eag,'ebd':ebd,'ax':ax,'ay':ay,\
                                 'bx':bx,'by':by,'gz':gz,'dz':dz}
                            yield out
        elif m==-1 or m==1:
            for ea,ax,ay in zip(eFf,vXF,vYF):
                for eb,bz in zip(eFf,vZF):
                    for eag,gz in zip(ea,vZf):
                        for ebd,dz in zip(eb,vZf):
                            out={'eag':eag,'ebd':ebd,'ax':ax,'ay':ay,\
                                 'bz':bz,'gz':gz,'dz':dz}
                            yield out
        elif m==0:      
            for ea,az in zip(eFf,vZF):
                for eb,bz in zip(eFf,vZF):
                    for eag,gz in zip(ea,vZf):
                        for ebd,dz in zip(eb,vZf):
                            out={'eag':eag,'ebd':ebd,'az':az,\
                                 'bz':bz,'gz':gz,'dz':dz}
                            yield out