import numpy as np
from functools import reduce


def memoize(f):
    memo = {}
    def helper(x):
        if x not in memo:
            memo[x] = f(x)
        return memo[x]
    return helper

@memoize
def idx2_tri(iijj):
    '''
    iijj should be a 2-tuple
    return triangular compound index for (0-indexed counting)
    '''
    ij1=min(iijj)
    ij2=max(iijj)
    return ij1+(ij2*(ij2+1))//2
#    return ij1+(ij2*(ij2-1))//2

def pad(arr_in,outshape):
    arr_out = np.zeros(outshape,dtype=np.complex128)
    dataslice = tuple(slice(0,arr_in.shape[dim]) for dim in range(len(outshape)))
    arr_out[dataslice] = arr_in
    return arr_out

def makesq(vlist,n1,n2):
    '''
    make hermitian matrices of size (n2 x n2) from from lower triangles
    vlist is n1 lower triangles in flattened form
    given: ([a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t],2,4)
           output a 2x4x4 array, where each 4x4 is the square constructed from the lower triangle
    [
     [
      [a  b* d* g*]
      [b  c  e* h*]
      [d  e  f  i*]
      [g  h  i  j ]
     ],
     [
      [k  l* n* q*]
      [l  m  o* r*]
      [n  o  p  s*]
      [q  r  s  t ]
     ]
    ]
    '''
    out=np.zeros([n1,n2,n2],dtype=np.complex128)
    n0 = vlist.shape[0]
    lmask=np.tri(n2,dtype=bool)
    for i in range(n0):
        out[i][lmask] = vlist[i].conj()
    out2=out.transpose([0,2,1])
    for i in range(n0):
        out2[i][lmask] = vlist[i]
    return out2


def makesq3(vlist,n2):
    '''
    make hermitian matrices of size (n2 x n2) from from lower triangles
    vlist is n1 lower triangles in flattened form
    given: ([a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t],2,4)
           output a 2x4x4 array, where each 4x4 is the square constructed from the lower triangle
    [
     [
      [a  b* d* g*]
      [b  c  e* h*]
      [d  e  f  i*]
      [g  h  i  j ]
     ],
     [
      [k  l* n* q*]
      [l  m  o* r*]
      [n  o  p  s*]
      [q  r  s  t ]
     ]
    ]
    '''
    n0 = vlist.shape[0]
    out=np.zeros([n0,n2,n2],dtype=np.complex128)
    lmask=np.tri(n2,dtype=bool)
    for i in range(n0):
        out[i][lmask] = vlist[i].conj()
    out2=out.transpose([0,2,1])
    for i in range(n0):
        out2[i][lmask] = vlist[i]
    return out2

def makesq2(vlist,n1,n2):
    out=np.zeros([n1,n2,n2],dtype=np.complex128)
    lmask=np.tri(n2,dtype=bool)
    tmp=np.zeros([n2,n2],dtype=np.complex128)
    tmp2=np.zeros([n2,n2],dtype=np.complex128)
    for i in range(n1):
        tmp[lmask] = vlist[i].conj()
        tmp2=tmp.T
        tmp2[lmask] = vlist[i]
        out[i] = tmp2.copy()
    return out


def get_phase(cell, kpts, kmesh=None):
    '''
    The unitary transformation that transforms the supercell basis k-mesh
    adapted basis.
    '''
    from pyscf.pbc import tools
    from pyscf import lib

    latt_vec = cell.lattice_vectors()
    if kmesh is None:
        # Guess kmesh
        scaled_k = cell.get_scaled_kpts(kpts).round(8)
        kmesh = (len(np.unique(scaled_k[:,0])),
                 len(np.unique(scaled_k[:,1])),
                 len(np.unique(scaled_k[:,2])))

    R_rel_a = np.arange(kmesh[0])
    R_rel_b = np.arange(kmesh[1])
    R_rel_c = np.arange(kmesh[2])
    R_vec_rel = lib.cartesian_prod((R_rel_a, R_rel_b, R_rel_c))
    R_vec_abs = np.einsum('nu, uv -> nv', R_vec_rel, latt_vec)

    NR = len(R_vec_abs)
    phase = np.exp(1j*np.einsum('Ru, ku -> Rk', R_vec_abs, kpts))
    phase /= np.sqrt(NR)  # normalization in supercell

    # R_rel_mesh has to be construct exactly same to the Ts in super_cell function
    scell = tools.super_cell(cell, kmesh)
    return scell, phase

def mo_k2gamma(cell, mo_energy, mo_coeff, kpts, kmesh=None):
    '''
    Transform MOs in Kpoints to the equivalents supercell
    '''
    from pyscf import lib
    import scipy.linalg as la
    scell, phase = get_phase(cell, kpts, kmesh)

    E_g = np.hstack(mo_energy)
    C_k = np.asarray(mo_coeff)
    Nk, Nao, Nmo = C_k.shape
    NR = phase.shape[0]

    # Transform AO indices
    C_gamma = np.einsum('Rk, kum -> Rukm', phase, C_k)
    C_gamma = C_gamma.reshape(Nao*NR, Nk*Nmo)

    E_sort_idx = np.argsort(E_g)
    E_g = E_g[E_sort_idx]
    C_gamma = C_gamma[:,E_sort_idx]
    s = scell.pbc_intor('int1e_ovlp')
    assert(abs(reduce(np.dot, (C_gamma.conj().T, s, C_gamma))
               - np.eye(Nmo*Nk)).max() < 1e-7)

    # Transform MO indices
    E_k_degen = abs(E_g[1:] - E_g[:-1]).max() < 1e-5
    if np.any(E_k_degen):
        degen_mask = np.append(False, E_k_degen) | np.append(E_k_degen, False)
        shift = min(E_g[degen_mask]) - .1
        f = np.dot(C_gamma[:,degen_mask] * (E_g[degen_mask] - shift),
                   C_gamma[:,degen_mask].conj().T)
        assert(abs(f.imag).max() < 1e-5)

        e, na_orb = la.eigh(f.real, s, type=2)
        C_gamma[:,degen_mask] = na_orb[:, e>0]

    if abs(C_gamma.imag).max() < 1e-7:
        print('!Warning  Some complexe pollutions in MOs are present')

    C_gamma = C_gamma.real
    if  abs(reduce(np.dot, (C_gamma.conj().T, s, C_gamma)) - np.eye(Nmo*Nk)).max() < 1e-7:
        print('!Warning  Some complexe pollutions in MOs are present')

    s_k = cell.pbc_intor('int1e_ovlp', kpts=kpts)
    # overlap between k-point unitcell and gamma-point supercell
    s_k_g = np.einsum('kuv,Rk->kuRv', s_k, phase.conj()).reshape(Nk,Nao,NR*Nao)
    # The unitary transformation from k-adapted orbitals to gamma-point orbitals
    mo_phase = lib.einsum('kum,kuv,vi->kmi', C_k.conj(), s_k_g, C_gamma)

    return mo_phase

def qp2rename():
    import shutil
    qp2names={}
    qp2names['mo_coef_complex'] = 'C.qp'
    qp2names['bielec_ao_complex'] = 'W.qp'

    qp2names['kinetic_ao_complex'] = 'T.qp'
    qp2names['ne_ao_complex'] = 'V.qp'
    qp2names['overlap_ao_complex'] = 'S.qp'


    for old,new in qp2names.items():
        shutil.move(old,new)
    shutil.copy('e_nuc','E.qp')

def pyscf2QP(cell,mf, kpts, kmesh=None, cas_idx=None, int_threshold = 1E-8, 
        print_ao_ints_bi=False, 
        print_mo_ints_bi=False, 
        print_ao_ints_df=True, 
        print_mo_ints_df=False, 
        print_ao_ints_mono=True, 
        print_mo_ints_mono=False):
    '''
    kpts = List of kpoints coordinates. Cannot be null, for gamma is other script
    kmesh = Mesh of kpoints (optional)
    cas_idx = List of active MOs. If not specified all MOs are actives
    int_threshold = The integral will be not printed in they are bellow that
    '''
  
    from pyscf.pbc import ao2mo
    from pyscf.pbc import tools
    from pyscf.pbc.gto import ecp
    import h5py
    
    mo_coef_threshold = int_threshold
    ovlp_threshold = int_threshold
    kin_threshold = int_threshold
    ne_threshold = int_threshold
    bielec_int_threshold = int_threshold

    natom = len(cell.atom_coords())
    print('n_atom per kpt',   natom)
    print('num_elec per kpt', cell.nelectron)
  
    mo_coeff = mf.mo_coeff
    # Mo_coeff actif
    mo_k = np.array([c[:,cas_idx] for c in mo_coeff] if cas_idx is not None else mo_coeff)
    e_k =  np.array([e[cas_idx] for e in mf.mo_energy] if cas_idx is not None else mf.mo_energy)
  
    Nk, nao, nmo = mo_k.shape
    print("n Kpts", Nk)
    print("n active Mos per kpt", nmo)
    print("n AOs per kpt", nao)

    naux = mf.with_df.auxcell.nao
    print("n df fitting functions", naux)
    with open('num_df','w') as f:
        f.write(str(naux))
  
    # Write all the parameter need to creat a dummy EZFIO folder who will containt the integral after.
    # More an implentation detail than a real thing
    with open('param','w') as f:
    # Note the use of nmo_tot
        f.write(' '.join(map(str,(cell.nelectron*Nk, Nk*nmo, natom*Nk))))
  
    with open('num_ao','w') as f:
        f.write(str(nao*Nk))
    with open('kpt_num','w') as f:
        f.write(str(Nk))
    #                             _                             
    # |\ |      _ |  _   _. ._   |_)  _  ._      |  _ o  _  ._  
    # | \| |_| (_ | (/_ (_| |    | \ (/_ |_) |_| | _> | (_) | | 
    #                                    |                      
    
    #Total energy shift due to Ewald probe charge = -1/2 * Nelec*madelung/cell.vol =
    shift = tools.pbc.madelung(cell, kpts)*cell.nelectron * -.5 
    e_nuc = (cell.energy_nuc() + shift)*Nk
  
    print('nucl_repul', e_nuc)
    with open('e_nuc','w') as f:
        f.write(str(e_nuc))
  
  
  
    #       __    __          _                                 
    # |\/| |  |  |    _   _  |_  _ 
    # |  | |__|  |__ (_) (/_ |  _> 
    #                                               
    with open('mo_coef_complex','w') as outfile:
        c_kpts = np.reshape(mo_k,(Nk,nao,nmo))

        for ik in range(Nk):
            shift1=ik*nao+1
            shift2=ik*nmo+1
            for i in range(nao):
                for j in range(nmo):
                    cij = c_kpts[ik,i,j]
                    if abs(cij) > mo_coef_threshold:
                        outfile.write('%s %s %s %s\n' % (i+shift1, j+shift2, cij.real, cij.imag))
    
    # ___                                              
    #  |  ._ _|_  _   _  ._ _. |  _   |\/|  _  ._   _  
    # _|_ | | |_ (/_ (_| | (_| | _>   |  | (_) | | (_) 
    #                 _|                              
   
    if mf.cell.pseudo:
        v_kpts_ao = np.reshape(mf.with_df.get_pp(kpts=kpts),(Nk,nao,nao))
    else:
        v_kpts_ao = np.reshape(mf.with_df.get_nuc(kpts=kpts),(Nk,nao,nao))
    if len(cell._ecpbas) > 0:
        v_kpts_ao += np.reshape(ecp.ecp_int(cell, kpts),(Nk,nao,nao))

    ne_ao = ('ne',v_kpts_ao,ne_threshold)
    ovlp_ao = ('overlap',np.reshape(mf.get_ovlp(cell=cell,kpts=kpts),(Nk,nao,nao)),ovlp_threshold)
    kin_ao = ('kinetic',np.reshape(cell.pbc_intor('int1e_kin',1,1,kpts=kpts),(Nk,nao,nao)),kin_threshold)

    for name, intval_kpts_ao, thresh in (ne_ao, ovlp_ao, kin_ao):
        if print_ao_ints_mono:
            with open('%s_ao_complex' % name,'w') as outfile:
                for ik in range(Nk):
                    shift=ik*nao+1
                    for i in range(nao):
                        for j in range(i,nao):
                            int_ij = intval_kpts_ao[ik,i,j]
                            if abs(int_ij) > thresh:
                                outfile.write('%s %s %s %s\n' % (i+shift, j+shift, int_ij.real, int_ij.imag))
        if print_mo_ints_mono:
            intval_kpts_mo = np.einsum('kim,kij,kjn->kmn',mo_k.conj(),intval_kpts_ao,mo_k)
            with open('%s_mo_complex' % name,'w') as outfile:
                for ik in range(Nk):
                    shift=ik*nmo+1
                    for i in range(nmo):
                        for j in range(i,nmo):
                            int_ij = intval_kpts_mo[ik,i,j]
                            if abs(int_ij) > thresh:
                                outfile.write('%s %s %s %s\n' % (i+shift, j+shift, int_ij.real, int_ij.imag))

  
    # ___                              _    
    #  |  ._ _|_  _   _  ._ _. |  _   |_) o 
    # _|_ | | |_ (/_ (_| | (_| | _>   |_) | 
    #                 _|                    
    #
    kconserv = tools.get_kconserv(cell, kpts)

    with open('kconserv_complex','w') as outfile:
        for a in range(Nk):
            for b in range(Nk):
                for c in range(Nk):
                    d = kconserv[a,b,c]
                    outfile.write('%s %s %s %s\n' % (a+1,c+1,b+1,d+1))
    

    intfile=h5py.File(mf.with_df._cderi,'r')

    j3c = intfile.get('j3c')
    naosq = nao*nao
    naotri = (nao*(nao+1))//2
    j3ckeys = list(j3c.keys())
    j3ckeys.sort(key=lambda strkey:int(strkey))

    # in new(?) version of PySCF, there is an extra layer of groups before the datasets
    # datasets used to be [/j3c/0,   /j3c/1,   /j3c/2,   ...]
    # datasets now are    [/j3c/0/0, /j3c/1/0, /j3c/2/0, ...]
    j3clist = [j3c.get(i+'/0') for i in j3ckeys]
    if j3clist==[None]*len(j3clist):
    # if using older version, stop before last level
        j3clist = [j3c.get(i) for i in j3ckeys]

    nkinvsq = 1./np.sqrt(Nk)

    # dimensions are (kikj,iaux,jao,kao), where kikj is compound index of kpts i and j
    # output dimensions should be reversed (nao, nao, naux, nkptpairs)
    j3arr=np.array([(i.value.reshape([-1,nao,nao]) if (i.shape[1] == naosq) else makesq3(i.value,nao)) * nkinvsq for i in j3clist])

    nkpt_pairs = j3arr.shape[0]

    if print_ao_ints_df:
        with open('df_ao_integral_array','w') as outfile:
            pass
        with open('df_ao_integral_array','a') as outfile:
            for k,kpt_pair in enumerate(j3arr):
                for iaux,dfbasfunc in enumerate(kpt_pair):
                    for i,i0 in enumerate(dfbasfunc):
                        for j,v in enumerate(i0):
                            if (abs(v) > bielec_int_threshold):
                                outfile.write('%s %s %s %s %s %s\n' % (i+1,j+1,iaux+1,k+1,v.real,v.imag))

    if print_mo_ints_df:
        kpair_list=[]
        for i in range(Nk):
            for j in range(Nk):
                if(i>=j):
                    kpair_list.append((i,j,idx2_tri((i,j))))
        j3mo = np.array([np.einsum('mij,ik,jl->mkl',j3arr[kij],mo_k[ki].conj(),mo_k[kj]) for ki,kj,kij in kpair_list])
        with open('df_mo_integral_array','w') as outfile:
            pass
        with open('df_mo_integral_array','a') as outfile:
            for k,kpt_pair in enumerate(j3mo):
                for iaux,dfbasfunc in enumerate(kpt_pair):
                    for i,i0 in enumerate(dfbasfunc):
                        for j,v in enumerate(i0):
                            if (abs(v) > bielec_int_threshold):
                                outfile.write('%s %s %s %s %s %s\n' % (i+1,j+1,iaux+1,k+1,v.real,v.imag))




#    eri_4d_ao = np.zeros((Nk,nao,Nk,nao,Nk,nao,Nk,nao), dtype=np.complex)
#    for d, kd in enumerate(kpts):
#        for c, kc in enumerate(kpts):
#            if c > d: break
#            idx2_cd = idx2_tri(c,d)
#            for b, kb in enumerate(kpts):
#                if b > d: break
#                a = kconserv[b,c,d]
#                if idx2_tri(a,b) > idx2_cd: continue
#                if ((c==d) and (a>b)): continue
#                ka = kpts[a]
#                v = mf.with_df.get_ao_eri(kpts=[ka,kb,kc,kd],compact=False).reshape((nao,)*4)
#                v *= 1./Nk
#                eri_4d_ao[a,:,b,:,c,:,d] = v
#    
#    eri_4d_ao = eri_4d_ao.reshape([Nk*nao]*4)


    if (print_ao_ints_bi or print_mo_ints_bi):
        if print_ao_ints_bi:
            with open('bielec_ao_complex','w') as outfile: 
                pass
        if print_mo_ints_bi:
            with open('bielec_mo_complex','w') as outfile: 
                pass
        for d, kd in enumerate(kpts):
            for c, kc in enumerate(kpts):
                if c > d: break
                idx2_cd = idx2_tri((c,d))
                for b, kb in enumerate(kpts):
                    if b > d: break
                    a = kconserv[b,c,d]
                    if idx2_tri((a,b)) > idx2_cd: continue
                    if ((c==d) and (a>b)): continue
                    ka = kpts[a]

                    if print_ao_ints_bi:
                        with open('bielec_ao_complex','a') as outfile:
                            eri_4d_ao_kpt = mf.with_df.get_ao_eri(kpts=[ka,kb,kc,kd],compact=False).reshape((nao,)*4)
                            eri_4d_ao_kpt *= 1./Nk
                            for l in range(nao):
                                ll=l+d*nao
                                for j in range(nao):
                                    jj=j+c*nao
                                    if jj>ll: break
                                    idx2_jjll = idx2_tri((jj,ll))
                                    for k in range(nao):
                                        kk=k+b*nao
                                        if kk>ll: break
                                        for i in range(nao):
                                            ii=i+a*nao
                                            if idx2_tri((ii,kk)) > idx2_jjll: break
                                            if ((jj==ll) and (ii>kk)): break
                                            v=eri_4d_ao_kpt[i,k,j,l]
                                            if (abs(v) > bielec_int_threshold):
                                                outfile.write('%s %s %s %s %s %s\n' % (ii+1,jj+1,kk+1,ll+1,v.real,v.imag))
            
                    if print_mo_ints_bi:
                        with open('bielec_mo_complex','a') as outfile:
                            eri_4d_mo_kpt = mf.with_df.ao2mo([mo_k[a], mo_k[b], mo_k[c], mo_k[d]],
                                                              [ka,kb,kc,kd],compact=False).reshape((nmo,)*4)
                            eri_4d_mo_kpt *= 1./Nk
                            for l in range(nmo):
                                ll=l+d*nmo
                                for j in range(nmo):
                                    jj=j+c*nmo
                                    if jj>ll: break
                                    idx2_jjll = idx2_tri((jj,ll))
                                    for k in range(nmo):
                                        kk=k+b*nmo
                                        if kk>ll: break
                                        for i in range(nmo):
                                            ii=i+a*nmo
                                            if idx2_tri((ii,kk)) > idx2_jjll: break
                                            if ((jj==ll) and (ii>kk)): break
                                            v=eri_4d_mo_kpt[i,k,j,l]
                                            if (abs(v) > bielec_int_threshold):
                                                outfile.write('%s %s %s %s %s %s\n' % (ii+1,jj+1,kk+1,ll+1,v.real,v.imag))


def testprintbi(cell,mf, kpts, kmesh=None, cas_idx=None, int_threshold = 1E-8): 
    '''
    kpts = List of kpoints coordinates. Cannot be null, for gamma is other script
    kmesh = Mesh of kpoints (optional)
    cas_idx = List of active MOs. If not specified all MOs are actives
    int_threshold = The integral will be not printed in they are bellow that
    '''
  
    from pyscf.pbc import ao2mo
    from pyscf.pbc import tools
    from pyscf.pbc.gto import ecp
    from pyscf.data import nist
    import h5py
    import scipy

    
    bielec_int_threshold = int_threshold

    natom = cell.natm
    nelec = cell.nelectron
    neleca,nelecb = cell.nelec
    atom_xyz = mf.cell.atom_coords()
    if not(mf.cell.unit.startswith(('B','b','au','AU'))):
        atom_xyz /= nist.BOHR # always convert to au
    
    strtype=h5py.special_dtype(vlen=str)
    atom_dset=qph5.create_dataset('nuclei/nucl_label',(natom,),dtype=strtype)
    for i in range(natom):
        atom_dset[i] = mf.cell.atom_pure_symbol(i)
    qph5.create_dataset('nuclei/nucl_coord',data=atom_xyz)
    qph5.create_dataset('nuclei/nucl_charge',data=mf.cell.atom_charges())


    print('n_atom per kpt',   natom)
    print('num_elec per kpt', nelec)

    mo_coeff = mf.mo_coeff
    # Mo_coeff actif
    mo_k = np.array([c[:,cas_idx] for c in mo_coeff] if cas_idx is not None else mo_coeff)
    e_k =  np.array([e[cas_idx] for e in mf.mo_energy] if cas_idx is not None else mf.mo_energy)
  
    Nk, nao, nmo = mo_k.shape
    print("n Kpts", Nk)
    print("n active Mos per kpt", nmo)
    print("n AOs per kpt", nao)

    naux = mf.with_df.auxcell.nao
    print("n df fitting functions", naux)
  
    #in old version: param << nelec*Nk, nmo*Nk, natom*Nk
  

  
    # ___                              _    
    #  |  ._ _|_  _   _  ._ _. |  _   |_) o 
    # _|_ | | |_ (/_ (_| | (_| | _>   |_) | 
    #                 _|                    
    #
    kconserv = tools.get_kconserv(cell, kpts)
    qph5.create_dataset('nuclei/kconserv',data=np.transpose(kconserv+1,(0,2,1)))
    kcon_test = np.zeros((Nk,Nk,Nk),dtype=int)
    for a in range(Nk):
        for b in range(Nk):
            for c in range(Nk):
                kcon_test[a,c,b] = kconserv[a,b,c]+1
    qph5.create_dataset('nuclei/kconserv_test',data=kcon_test)

    
    with open('K.qp','w') as outfile:
        for a in range(Nk):
            for b in range(Nk):
                for c in range(Nk):
                    d = kconserv[a,b,c]
                    outfile.write('%s %s %s %s\n' % (a+1,c+1,b+1,d+1))
    

    intfile=h5py.File(mf.with_df._cderi,'r')

    j3c = intfile.get('j3c')
    naosq = nao*nao
    naotri = (nao*(nao+1))//2
    j3ckeys = list(j3c.keys())
    j3ckeys.sort(key=lambda strkey:int(strkey))

    # in new(?) version of PySCF, there is an extra layer of groups before the datasets
    # datasets used to be [/j3c/0,   /j3c/1,   /j3c/2,   ...]
    # datasets now are    [/j3c/0/0, /j3c/1/0, /j3c/2/0, ...]
    j3clist = [j3c.get(i+'/0') for i in j3ckeys]
    if j3clist==[None]*len(j3clist):
    # if using older version, stop before last level
        j3clist = [j3c.get(i) for i in j3ckeys]

    nkinvsq = 1./np.sqrt(Nk)

    # dimensions are (kikj,iaux,jao,kao), where kikj is compound index of kpts i and j
    # output dimensions should be reversed (nao, nao, naux, nkptpairs)
    j3arr=np.array([(i.value.reshape([-1,nao,nao]) if (i.shape[1] == naosq) else makesq3(i.value,nao)) * nkinvsq for i in j3clist])

    nkpt_pairs = j3arr.shape[0]
    df_ao_tmp = np.zeros((nao,nao,naux,nkpt_pairs),dtype=np.complex128)

    if print_ao_ints_df:
        with open('D.qp','w') as outfile:
            pass
        with open('D.qp','a') as outfile:
            for k,kpt_pair in enumerate(j3arr):
                for iaux,dfbasfunc in enumerate(kpt_pair):
                    for i,i0 in enumerate(dfbasfunc):
                        for j,v in enumerate(i0):
                            if (abs(v) > bielec_int_threshold):
                                outfile.write('%s %s %s %s %s %s\n' % (i+1,j+1,iaux+1,k+1,v.real,v.imag))
                                df_ao_tmp[i,j,iaux,k]=v
        
        qph5.create_dataset('ao_two_e_ints/df_ao_integrals_real',data=df_ao_tmp.real)
        qph5.create_dataset('ao_two_e_ints/df_ao_integrals_imag',data=df_ao_tmp.imag)

    if print_mo_ints_df:
        kpair_list=[]
        for i in range(Nk):
            for j in range(Nk):
                if(i>=j):
                    kpair_list.append((i,j,idx2_tri((i,j))))
        j3mo = np.array([np.einsum('mij,ik,jl->mkl',j3arr[kij],mo_k[ki].conj(),mo_k[kj]) for ki,kj,kij in kpair_list])
        df_mo_tmp = np.zeros((nmo,nmo,naux,nkpt_pairs),dtype=np.complex128)
        with open('D_mo.qp','w') as outfile:
            pass
        with open('D_mo.qp','a') as outfile:
            for k,kpt_pair in enumerate(j3mo):
                for iaux,dfbasfunc in enumerate(kpt_pair):
                    for i,i0 in enumerate(dfbasfunc):
                        for j,v in enumerate(i0):
                            if (abs(v) > bielec_int_threshold):
                                outfile.write('%s %s %s %s %s %s\n' % (i+1,j+1,iaux+1,k+1,v.real,v.imag))
                                df_mo_tmp[i,j,iaux,k]=v
        qph5.create_dataset('mo_two_e_ints/df_mo_integrals_real',data=df_mo_tmp.real)
        qph5.create_dataset('mo_two_e_ints/df_mo_integrals_imag',data=df_mo_tmp.imag)



#    eri_4d_ao = np.zeros((Nk,nao,Nk,nao,Nk,nao,Nk,nao), dtype=np.complex)
#    for d, kd in enumerate(kpts):
#        for c, kc in enumerate(kpts):
#            if c > d: break
#            idx2_cd = idx2_tri(c,d)
#            for b, kb in enumerate(kpts):
#                if b > d: break
#                a = kconserv[b,c,d]
#                if idx2_tri(a,b) > idx2_cd: continue
#                if ((c==d) and (a>b)): continue
#                ka = kpts[a]
#                v = mf.with_df.get_ao_eri(kpts=[ka,kb,kc,kd],compact=False).reshape((nao,)*4)
#                v *= 1./Nk
#                eri_4d_ao[a,:,b,:,c,:,d] = v
#    
#    eri_4d_ao = eri_4d_ao.reshape([Nk*nao]*4)


        with open('W.qp','w') as outfile: 
            pass
        for d, kd in enumerate(kpts):
            for c, kc in enumerate(kpts):
                if c > d: break
                idx2_cd = idx2_tri((c,d))
                for b, kb in enumerate(kpts):
                    if b > d: break
                    a = kconserv[b,c,d]
                    #if idx2_tri((a,b)) > idx2_cd: continue
                    if a>d: continue
                    #if ((c==d) and (a>b)): continue
                    ka = kpts[a]

                    with open('W.qp','a') as outfile:
                        eri_4d_ao_kpt = mf.with_df.get_ao_eri(kpts=[ka,kb,kc,kd],compact=False).reshape((nao,)*4)
                        eri_4d_ao_kpt *= 1./Nk
                        for l in range(nao):
                            ll=l+d*nao
                            for j in range(nao):
                                jj=j+c*nao
                                if jj>ll: break
                                idx2_jjll = idx2_tri((jj,ll))
                                for k in range(nao):
                                    kk=k+b*nao
                                    if kk>ll: break
                                    for i in range(nao):
                                        ii=i+a*nao
                                        if idx2_tri((ii,kk)) > idx2_jjll: break
                                        if ((jj==ll) and (ii>kk)): break
                                        v=eri_4d_ao_kpt[i,k,j,l]
                                        if (abs(v) > bielec_int_threshold):
                                            outfile.write('%s %s %s %s %s %s\n' % (ii+1,jj+1,kk+1,ll+1,v.real,v.imag))
            
    
  
def pyscf2QP2(cell,mf, kpts, kmesh=None, cas_idx=None, int_threshold = 1E-8, 
        print_ao_ints_bi=False, 
        print_mo_ints_bi=False, 
        print_ao_ints_df=True, 
        print_mo_ints_df=False, 
        print_ao_ints_mono=True, 
        print_mo_ints_mono=False):
    '''
    kpts = List of kpoints coordinates. Cannot be null, for gamma is other script
    kmesh = Mesh of kpoints (optional)
    cas_idx = List of active MOs. If not specified all MOs are actives
    int_threshold = The integral will be not printed in they are bellow that
    '''
  
    from pyscf.pbc import ao2mo
    from pyscf.pbc import tools
    from pyscf.pbc.gto import ecp
    from pyscf.data import nist
    import h5py
    import scipy

    qph5=h5py.File('qpdat.h5')
    qph5.create_group('nuclei')
    qph5.create_group('electrons')
    qph5.create_group('ao_basis')
    qph5.create_group('mo_basis')

    
    mo_coef_threshold = int_threshold
    ovlp_threshold = int_threshold
    kin_threshold = int_threshold
    ne_threshold = int_threshold
    bielec_int_threshold = int_threshold

    natom = cell.natm
    nelec = cell.nelectron
    neleca,nelecb = cell.nelec
    atom_xyz = mf.cell.atom_coords()
    if not(mf.cell.unit.startswith(('B','b','au','AU'))):
        atom_xyz /= nist.BOHR # always convert to au
    
    strtype=h5py.special_dtype(vlen=str)
    atom_dset=qph5.create_dataset('nuclei/nucl_label',(natom,),dtype=strtype)
    for i in range(natom):
        atom_dset[i] = mf.cell.atom_pure_symbol(i)
    qph5.create_dataset('nuclei/nucl_coord',data=atom_xyz)
    qph5.create_dataset('nuclei/nucl_charge',data=mf.cell.atom_charges())


    print('n_atom per kpt',   natom)
    print('num_elec per kpt', nelec)

    mo_coeff = mf.mo_coeff
    # Mo_coeff actif
    mo_k = np.array([c[:,cas_idx] for c in mo_coeff] if cas_idx is not None else mo_coeff)
    e_k =  np.array([e[cas_idx] for e in mf.mo_energy] if cas_idx is not None else mf.mo_energy)
  
    Nk, nao, nmo = mo_k.shape
    print("n Kpts", Nk)
    print("n active Mos per kpt", nmo)
    print("n AOs per kpt", nao)

    naux = mf.with_df.auxcell.nao
    print("n df fitting functions", naux)
  
    #in old version: param << nelec*Nk, nmo*Nk, natom*Nk
    qph5['electrons'].attrs['elec_alpha_num']=neleca*Nk 
    qph5['electrons'].attrs['elec_beta_num']=nelecb*Nk
    qph5['mo_basis'].attrs['mo_num']=Nk*nmo
    qph5['ao_basis'].attrs['ao_num']=Nk*nao
    qph5['nuclei'].attrs['nucl_num']=Nk*natom
    qph5['nuclei'].attrs['kpt_num']=Nk
    qph5.create_group('ao_two_e_ints')
    qph5['ao_two_e_ints'].attrs['df_num']=naux

    qph5['ao_basis'].attrs['ao_basis']=mf.cell.basis
    ao_nucl=[mf.cell.bas_atom(i)+1 for i in range(nao)]
    qph5.create_dataset('ao_basis/ao_nucl',data=Nk*ao_nucl)


    #                             _                             
    # |\ |      _ |  _   _. ._   |_)  _  ._      |  _ o  _  ._  
    # | \| |_| (_ | (/_ (_| |    | \ (/_ |_) |_| | _> | (_) | | 
    #                                    |                      
    
    #Total energy shift due to Ewald probe charge = -1/2 * Nelec*madelung/cell.vol =
    shift = tools.pbc.madelung(cell, kpts)*cell.nelectron * -.5 
    e_nuc = (cell.energy_nuc() + shift)*Nk
  
    print('nucl_repul', e_nuc)
    qph5['nuclei'].attrs['nuclear_repulsion']=e_nuc
  
    #       __    __          _                                 
    # |\/| |  |  |    _   _  |_  _ 
    # |  | |__|  |__ (_) (/_ |  _> 
    #                                               
    mo_coef_blocked=scipy.linalg.block_diag(*mo_k)
    qph5.create_dataset('mo_basis/mo_coef_real',data=mo_coef_blocked.real)
    qph5.create_dataset('mo_basis/mo_coef_imag',data=mo_coef_blocked.imag)
    qph5.create_dataset('mo_basis/mo_coef_kpts_real',data=mo_k.real)
    qph5.create_dataset('mo_basis/mo_coef_kpts_imag',data=mo_k.imag)
    
    with open('C.qp','w') as outfile:
        c_kpts = np.reshape(mo_k,(Nk,nao,nmo))

        for ik in range(Nk):
            shift1=ik*nao+1
            shift2=ik*nmo+1
            for i in range(nao):
                for j in range(nmo):
                    cij = c_kpts[ik,i,j]
                    if abs(cij) > mo_coef_threshold:
                        outfile.write('%s %s %s %s\n' % (i+shift1, j+shift2, cij.real, cij.imag))

    # ___                                              
    #  |  ._ _|_  _   _  ._ _. |  _   |\/|  _  ._   _  
    # _|_ | | |_ (/_ (_| | (_| | _>   |  | (_) | | (_) 
    #                 _|                              
   
    if mf.cell.pseudo:
        v_kpts_ao = np.reshape(mf.with_df.get_pp(kpts=kpts),(Nk,nao,nao))
    else:
        v_kpts_ao = np.reshape(mf.with_df.get_nuc(kpts=kpts),(Nk,nao,nao))
    if len(cell._ecpbas) > 0:
        v_kpts_ao += np.reshape(ecp.ecp_int(cell, kpts),(Nk,nao,nao))
      
    ne_ao = ('V',v_kpts_ao,ne_threshold)
    ovlp_ao = ('S',np.reshape(mf.get_ovlp(cell=cell,kpts=kpts),(Nk,nao,nao)),ovlp_threshold)
    kin_ao = ('T',np.reshape(cell.pbc_intor('int1e_kin',1,1,kpts=kpts),(Nk,nao,nao)),kin_threshold)
    
    kin_ao_blocked=scipy.linalg.block_diag(*kin_ao[1])
    ovlp_ao_blocked=scipy.linalg.block_diag(*ovlp_ao[1])
    ne_ao_blocked=scipy.linalg.block_diag(*v_kpts_ao)
    qph5.create_dataset('ao_one_e_ints/ao_integrals_kinetic_real',data=kin_ao_blocked.real)
    qph5.create_dataset('ao_one_e_ints/ao_integrals_kinetic_imag',data=kin_ao_blocked.imag)
    qph5.create_dataset('ao_one_e_ints/ao_integrals_overlap_real',data=ovlp_ao_blocked.real)
    qph5.create_dataset('ao_one_e_ints/ao_integrals_overlap_imag',data=ovlp_ao_blocked.imag)
    qph5.create_dataset('ao_one_e_ints/ao_integrals_n_e_real',    data=ne_ao_blocked.real)
    qph5.create_dataset('ao_one_e_ints/ao_integrals_n_e_imag',    data=ne_ao_blocked.imag)
    


    for name, intval_kpts_ao, thresh in (ne_ao, ovlp_ao, kin_ao):
        if print_ao_ints_mono:
            with open('%s.qp' % name,'w') as outfile:
                for ik in range(Nk):
                    shift=ik*nao+1
                    for i in range(nao):
                        for j in range(i,nao):
                            int_ij = intval_kpts_ao[ik,i,j]
                            if abs(int_ij) > thresh:
                                outfile.write('%s %s %s %s\n' % (i+shift, j+shift, int_ij.real, int_ij.imag))
        if print_mo_ints_mono:
            intval_kpts_mo = np.einsum('kim,kij,kjn->kmn',mo_k.conj(),intval_kpts_ao,mo_k)
            with open('%s_mo.qp' % name,'w') as outfile:
                for ik in range(Nk):
                    shift=ik*nmo+1
                    for i in range(nmo):
                        for j in range(i,nmo):
                            int_ij = intval_kpts_mo[ik,i,j]
                            if abs(int_ij) > thresh:
                                outfile.write('%s %s %s %s\n' % (i+shift, j+shift, int_ij.real, int_ij.imag))

  
    # ___                              _    
    #  |  ._ _|_  _   _  ._ _. |  _   |_) o 
    # _|_ | | |_ (/_ (_| | (_| | _>   |_) | 
    #                 _|                    
    #
    kconserv = tools.get_kconserv(cell, kpts)
    qph5.create_dataset('nuclei/kconserv',data=np.transpose(kconserv+1,(0,2,1)))
    kcon_test = np.zeros((Nk,Nk,Nk),dtype=int)
    for a in range(Nk):
        for b in range(Nk):
            for c in range(Nk):
                kcon_test[a,c,b] = kconserv[a,b,c]+1
    qph5.create_dataset('nuclei/kconserv_test',data=kcon_test)

    
    with open('K.qp','w') as outfile:
        for a in range(Nk):
            for b in range(Nk):
                for c in range(Nk):
                    d = kconserv[a,b,c]
                    outfile.write('%s %s %s %s\n' % (a+1,c+1,b+1,d+1))
    

    intfile=h5py.File(mf.with_df._cderi,'r')

    j3c = intfile.get('j3c')
    naosq = nao*nao
    naotri = (nao*(nao+1))//2
    j3ckeys = list(j3c.keys())
    j3ckeys.sort(key=lambda strkey:int(strkey))

    # in new(?) version of PySCF, there is an extra layer of groups before the datasets
    # datasets used to be [/j3c/0,   /j3c/1,   /j3c/2,   ...]
    # datasets now are    [/j3c/0/0, /j3c/1/0, /j3c/2/0, ...]
    j3clist = [j3c.get(i+'/0') for i in j3ckeys]
    if j3clist==[None]*len(j3clist):
    # if using older version, stop before last level
        j3clist = [j3c.get(i) for i in j3ckeys]

    nkinvsq = 1./np.sqrt(Nk)

    # dimensions are (kikj,iaux,jao,kao), where kikj is compound index of kpts i and j
    # output dimensions should be reversed (nao, nao, naux, nkptpairs)
    j3arr=np.array([(i.value.reshape([-1,nao,nao]) if (i.shape[1] == naosq) else makesq3(i.value,nao)) * nkinvsq for i in j3clist])

    nkpt_pairs = j3arr.shape[0]
    df_ao_tmp = np.zeros((nao,nao,naux,nkpt_pairs),dtype=np.complex128)

    if print_ao_ints_df:
        with open('D.qp','w') as outfile:
            pass
        with open('D.qp','a') as outfile:
            for k,kpt_pair in enumerate(j3arr):
                for iaux,dfbasfunc in enumerate(kpt_pair):
                    for i,i0 in enumerate(dfbasfunc):
                        for j,v in enumerate(i0):
                            if (abs(v) > bielec_int_threshold):
                                outfile.write('%s %s %s %s %s %s\n' % (i+1,j+1,iaux+1,k+1,v.real,v.imag))
                                df_ao_tmp[i,j,iaux,k]=v
        
        qph5.create_dataset('ao_two_e_ints/df_ao_integrals_real',data=df_ao_tmp.real)
        qph5.create_dataset('ao_two_e_ints/df_ao_integrals_imag',data=df_ao_tmp.imag)

    if print_mo_ints_df:
        kpair_list=[]
        for i in range(Nk):
            for j in range(Nk):
                if(i>=j):
                    kpair_list.append((i,j,idx2_tri((i,j))))
        j3mo = np.array([np.einsum('mij,ik,jl->mkl',j3arr[kij],mo_k[ki].conj(),mo_k[kj]) for ki,kj,kij in kpair_list])
        df_mo_tmp = np.zeros((nmo,nmo,naux,nkpt_pairs),dtype=np.complex128)
        with open('D_mo.qp','w') as outfile:
            pass
        with open('D_mo.qp','a') as outfile:
            for k,kpt_pair in enumerate(j3mo):
                for iaux,dfbasfunc in enumerate(kpt_pair):
                    for i,i0 in enumerate(dfbasfunc):
                        for j,v in enumerate(i0):
                            if (abs(v) > bielec_int_threshold):
                                outfile.write('%s %s %s %s %s %s\n' % (i+1,j+1,iaux+1,k+1,v.real,v.imag))
                                df_mo_tmp[i,j,iaux,k]=v
        qph5.create_dataset('mo_two_e_ints/df_mo_integrals_real',data=df_mo_tmp.real)
        qph5.create_dataset('mo_two_e_ints/df_mo_integrals_imag',data=df_mo_tmp.imag)



#    eri_4d_ao = np.zeros((Nk,nao,Nk,nao,Nk,nao,Nk,nao), dtype=np.complex)
#    for d, kd in enumerate(kpts):
#        for c, kc in enumerate(kpts):
#            if c > d: break
#            idx2_cd = idx2_tri(c,d)
#            for b, kb in enumerate(kpts):
#                if b > d: break
#                a = kconserv[b,c,d]
#                if idx2_tri(a,b) > idx2_cd: continue
#                if ((c==d) and (a>b)): continue
#                ka = kpts[a]
#                v = mf.with_df.get_ao_eri(kpts=[ka,kb,kc,kd],compact=False).reshape((nao,)*4)
#                v *= 1./Nk
#                eri_4d_ao[a,:,b,:,c,:,d] = v
#    
#    eri_4d_ao = eri_4d_ao.reshape([Nk*nao]*4)


    if (print_ao_ints_bi or print_mo_ints_bi):
        if print_ao_ints_bi:
            with open('W.qp','w') as outfile: 
                pass
        if print_mo_ints_bi:
            with open('W_mo.qp','w') as outfile: 
                pass
        for d, kd in enumerate(kpts):
            for c, kc in enumerate(kpts):
                if c > d: break
                idx2_cd = idx2_tri((c,d))
                for b, kb in enumerate(kpts):
                    if b > d: break
                    a = kconserv[b,c,d]
                    #if idx2_tri((a,b)) > idx2_cd: continue
                    if a > d: continue
                    #if ((c==d) and (a>b)): continue
                    ka = kpts[a]

                    if print_ao_ints_bi:
                        with open('W.qp','a') as outfile:
                            eri_4d_ao_kpt = mf.with_df.get_ao_eri(kpts=[ka,kb,kc,kd],compact=False).reshape((nao,)*4)
                            eri_4d_ao_kpt *= 1./Nk
                            for l in range(nao):
                                ll=l+d*nao
                                for j in range(nao):
                                    jj=j+c*nao
                                    if jj>ll: break
                                    idx2_jjll = idx2_tri((jj,ll))
                                    for k in range(nao):
                                        kk=k+b*nao
                                        if kk>ll: break
                                        for i in range(nao):
                                            ii=i+a*nao
                                            if idx2_tri((ii,kk)) > idx2_jjll: break
                                            if ((jj==ll) and (ii>kk)): break
                                            v=eri_4d_ao_kpt[i,k,j,l]
                                            if (abs(v) > bielec_int_threshold):
                                                outfile.write('%s %s %s %s %s %s\n' % (ii+1,jj+1,kk+1,ll+1,v.real,v.imag))
            
                    if print_mo_ints_bi:
                        with open('W_mo.qp','a') as outfile:
                            eri_4d_mo_kpt = mf.with_df.ao2mo([mo_k[a], mo_k[b], mo_k[c], mo_k[d]],
                                                              [ka,kb,kc,kd],compact=False).reshape((nmo,)*4)
                            eri_4d_mo_kpt *= 1./Nk
                            for l in range(nmo):
                                ll=l+d*nmo
                                for j in range(nmo):
                                    jj=j+c*nmo
                                    if jj>ll: break
                                    idx2_jjll = idx2_tri((jj,ll))
                                    for k in range(nmo):
                                        kk=k+b*nmo
                                        if kk>ll: break
                                        for i in range(nmo):
                                            ii=i+a*nmo
                                            if idx2_tri((ii,kk)) > idx2_jjll: break
                                            if ((jj==ll) and (ii>kk)): break
                                            v=eri_4d_mo_kpt[i,k,j,l]
                                            if (abs(v) > bielec_int_threshold):
                                                outfile.write('%s %s %s %s %s %s\n' % (ii+1,jj+1,kk+1,ll+1,v.real,v.imag))

    
  
#def testpyscf2QP(cell,mf, kpts, kmesh=None, cas_idx=None, int_threshold = 1E-8):
#    '''
#    kpts = List of kpoints coordinates. Cannot be null, for gamma is other script
#    kmesh = Mesh of kpoints (optional)
#    cas_idx = List of active MOs. If not specified all MOs are actives
#    int_threshold = The integral will be not printed in they are bellow that
#    '''
#
#    from pyscf.pbc import ao2mo
#    from pyscf.pbc import tools
#    from pyscf.pbc.gto import ecp
#
#    mo_coef_threshold = int_threshold
#    ovlp_threshold = int_threshold
#    kin_threshold = int_threshold
#    ne_threshold = int_threshold
#    bielec_int_threshold = int_threshold
#
#    natom = len(cell.atom_coords())
#    print('n_atom per kpt',   natom)
#    print('num_elec per kpt', cell.nelectron)
#
#    mo_coeff = mf.mo_coeff
#    # Mo_coeff actif
#    mo_k = np.array([c[:,cas_idx] for c in mo_coeff] if cas_idx is not None else mo_coeff)
#    e_k =  np.array([e[cas_idx] for e in mf.mo_energy] if cas_idx is not None else mf.mo_energy)
#
#    Nk, nao, nmo = mo_k.shape
#    print("n Kpts", Nk)
#    print("n active Mos per kpt", nmo)
#    print("n AOs per kpt", nao)
#
#    naux = mf.with_df.get_naoaux()
#    print("n df fitting functions", naux)
#
#    #                             _
#    # |\ |      _ |  _   _. ._   |_)  _  ._      |  _ o  _  ._
#    # | \| |_| (_ | (/_ (_| |    | \ (/_ |_) |_| | _> | (_) | |
#    #                                    |
#
#    #Total energy shift due to Ewald probe charge = -1/2 * Nelec*madelung/cell.vol =
#    shift = tools.pbc.madelung(cell, kpts)*cell.nelectron * -.5
#    e_nuc = (cell.energy_nuc() + shift)*Nk
#
#    print('nucl_repul', e_nuc)
#
#
#    # ___
#    #  |  ._ _|_  _   _  ._ _. |  _   |\/|  _  ._   _  
#    # _|_ | | |_ (/_ (_| | (_| | _>   |  | (_) | | (_) 
#    #                 _|                              
#   
#    if mf.cell.pseudo:
#        v_kpts_ao = np.reshape(mf.with_df.get_pp(kpts=kpts),(Nk,nao,nao))
#    else:
#        v_kpts_ao = np.reshape(mf.with_df.get_nuc(kpts=kpts),(Nk,nao,nao))
#    if len(cell._ecpbas) > 0:
#        v_kpts_ao += np.reshape(ecp.ecp_int(cell, kpts),(Nk,nao,nao))
#
#    ne_ao = ('ne',v_kpts_ao,ne_threshold)
#    ovlp_ao = ('overlap',np.reshape(mf.get_ovlp(cell=cell,kpts=kpts),(Nk,nao,nao)),ovlp_threshold)
#    kin_ao = ('kinetic',np.reshape(cell.pbc_intor('int1e_kin',1,1,kpts=kpts),(Nk,nao,nao)),kin_threshold)
#
#  
#    # ___                              _    
#    #  |  ._ _|_  _   _  ._ _. |  _   |_) o 
#    # _|_ | | |_ (/_ (_| | (_| | _>   |_) | 
#    #                 _|                    
#    #
#    kconserv = tools.get_kconserv(cell, kpts)
#
#    
#    import h5py
#
#    intfile=h5py.File(mf.with_df._cderi,'r')
#
#    j3c = intfile.get('j3c')
#    naosq = nao*nao
#    naotri = (nao*(nao+1))//2
#    j3keys = list(j3c.keys())
#    j3keys.sort(key=lambda x:int(x))
#    j3clist = [j3c.get(i) for i in j3keys]
#    nkinvsq = 1./np.sqrt(Nk)
#    
#    # dimensions are (kikj,iaux,jao,kao), where kikj is compound index of kpts i and j
#    # output dimensions should be reversed (nao, nao, naux, nkptpairs)
#    j3arr=np.array([(pad(i.value.reshape([-1,nao,nao]),[naux,nao,nao]) if (i.shape[1] == naosq) else makesq(i.value,naux,nao)) * nkinvsq for i in j3clist])
#
#    nkpt_pairs = j3arr.shape[0]
#
#    kpair_list=[]
#    for i in range(Nk):
#        for j in range(Nk):
#            if(i>=j):
#                kpair_list.append((i,j,idx2_tri((i,j))))
#    j3mo = np.array([np.einsum('mij,ik,jl->mkl',j3arr[kij,:,:,:],mo_k[ki,:,:].conj(),mo_k[kj,:,:]) for ki,kj,kij in kpair_list])
#
#
#
#    eri_mo = np.zeros(4*[nmo*Nk],dtype=np.complex128)
#    eri_ao = np.zeros(4*[nao*Nk],dtype=np.complex128)
#
#    for d, kd in enumerate(kpts):
#        for c, kc in enumerate(kpts):
#            for b, kb in enumerate(kpts):
#                a = kconserv[b,c,d]
#                ka = kpts[a]
#                eri_4d_ao_kpt = mf.with_df.get_ao_eri(kpts=[ka,kb,kc,kd],compact=False).reshape((nao,)*4)
#                eri_4d_ao_kpt *= 1./Nk
#                for l in range(nao):
#                    ll=l+d*nao
#                    for j in range(nao):
#                        jj=j+c*nao
#                        for k in range(nao):
#                            kk=k+b*nao
#                            for i in range(nao):
#                                ii=i+a*nao
#                                v=eri_4d_ao_kpt[i,k,j,l]
#                                eri_ao[ii,kk,jj,ll]=v
#        
#                eri_4d_mo_kpt = mf.with_df.ao2mo([mo_k[a], mo_k[b], mo_k[c], mo_k[d]],
#                                                  [ka,kb,kc,kd],compact=False).reshape((nmo,)*4)
#                eri_4d_mo_kpt *= 1./Nk
#                for l in range(nmo):
#                    ll=l+d*nmo
#                    for j in range(nmo):
#                        jj=j+c*nmo
#                        for k in range(nmo):
#                            kk=k+b*nmo
#                            for i in range(nmo):
#                                ii=i+a*nmo
#                                v=eri_4d_mo_kpt[i,k,j,l]
#                                eri_mo[ii,kk,jj,ll]=v
#        
#    return (mo_k,j3arr,j3mo,eri_ao,eri_mo,kpair_list)
    
  
