Exemplo n.º 1
0
 def comp_apair_pp_libint(self, a1,a2):
   """ Get's the vertex coefficient and conversion coefficients for a pair of atoms given by their atom indices """
   from operator import mul
   from pyscf.nao.m_prod_biloc import prod_biloc_c
   if not hasattr(self, 'sv_pbloc_data') : raise RuntimeError('.sv_pbloc_data is absent')
   assert a1>=0
   assert a2>=0
   
   t1 = timer()
   sv = self.sv
   aos = self.sv.ao_log
   sp12 = np.require( np.array([sv.atom2sp[a] for a in (a1,a2)], dtype=c_int64), requirements='C')
   rc12 = np.require( np.array([sv.atom2coord[a,:] for a in (a1,a2)]), requirements='C')
   icc2a = np.require( np.array(self.ls_contributing(a1,a2), dtype=c_int64), requirements='C')
   npmx = aos.sp2norbs[sv.atom2sp[a1]]*aos.sp2norbs[sv.atom2sp[a2]]
   npac = sum([self.prod_log.sp2norbs[sv.atom2sp[ia]] for ia in icc2a ])
   nout = c_int64(npmx**2+npmx*npac+10)
   dout = np.require( zeros(nout.value), requirements='CW')
   
   libnao.vrtx_cc_apair( sp12.ctypes.data_as(POINTER(c_int64)), rc12.ctypes.data_as(POINTER(c_double)), icc2a.ctypes.data_as(POINTER(c_int64)), c_int64(len(icc2a)), dout.ctypes.data_as(POINTER(c_double)), nout )    
   if dout[0]<1: return None
   
   nnn = np.array(dout[0:3], dtype=int)
   nnc = np.array([dout[8],dout[7]], dtype=int)
   ncc = int(dout[9])
   if ncc!=len(icc2a): raise RuntimeError('ncc!=len(icc2a)')
   s = 10; f=s+np.prod(nnn); vrtx  = dout[s:f].reshape(nnn)
   s = f;  f=s+np.prod(nnc); ccoe  = dout[s:f].reshape(nnc)
   icc2s = np.zeros(len(icc2a)+1, dtype=np.int64)
   for icc,a in enumerate(icc2a): icc2s[icc+1] = icc2s[icc] + self.prod_log.sp2norbs[sv.atom2sp[a]]
   pbiloc = prod_biloc_c(atoms=array([a2,a1]),vrtx=vrtx,cc2a=icc2a,cc2s=icc2s,cc=ccoe)
   
   return pbiloc
Exemplo n.º 2
0
 def comp_apair_pp_libint(self, a1,a2):
   """ Get's the vertex coefficient and conversion coefficients for a pair of atoms given by their atom indices """
   from operator import mul
   from pyscf.nao.m_prod_biloc import prod_biloc_c
   if not hasattr(self, 'sv_pbloc_data') : raise RuntimeError('.sv_pbloc_data is absent')
   assert a1>=0
   assert a2>=0
   
   t1 = timer()
   sv = self.sv
   aos = self.sv.ao_log
   sp12 = np.require( np.array([sv.atom2sp[a] for a in (a1,a2)], dtype=c_int64), requirements='C')
   rc12 = np.require( np.array([sv.atom2coord[a,:] for a in (a1,a2)]), requirements='C')
   icc2a = np.require( np.array(self.ls_contributing(a1,a2), dtype=c_int64), requirements='C')
   npmx = aos.sp2norbs[sv.atom2sp[a1]]*aos.sp2norbs[sv.atom2sp[a2]]
   npac = sum([self.prod_log.sp2norbs[sv.atom2sp[ia]] for ia in icc2a ])
   nout = c_int64(npmx**2+npmx*npac+10)
   dout = np.require( zeros(nout.value), requirements='CW')
   
   libnao.vrtx_cc_apair( sp12.ctypes.data_as(POINTER(c_int64)), rc12.ctypes.data_as(POINTER(c_double)), icc2a.ctypes.data_as(POINTER(c_int64)), c_int64(len(icc2a)), dout.ctypes.data_as(POINTER(c_double)), nout )    
   if dout[0]<1: return None
   
   nnn = np.array(dout[0:3], dtype=int)
   nnc = np.array([dout[8],dout[7]], dtype=int)
   ncc = int(dout[9])
   if ncc!=len(icc2a): raise RuntimeError('ncc!=len(icc2a)')
   s = 10; f=s+np.prod(nnn); vrtx  = dout[s:f].reshape(nnn)
   s = f;  f=s+np.prod(nnc); ccoe  = dout[s:f].reshape(nnc)
   icc2s = np.zeros(len(icc2a)+1, dtype=np.int64)
   for icc,a in enumerate(icc2a): icc2s[icc+1] = icc2s[icc] + self.prod_log.sp2norbs[sv.atom2sp[a]]
   pbiloc = prod_biloc_c(atoms=array([a2,a1]),vrtx=vrtx,cc2a=icc2a,cc2s=icc2s,cc=ccoe)
   
   return pbiloc
Exemplo n.º 3
0
    def init_prod_basis_pp_batch(self, nao, **kw):
        """ Talman's procedure should be working well with Pseudo-Potential starting point."""
        from pyscf.nao import prod_log_c
        from pyscf.nao.m_prod_biloc import prod_biloc_c

        sv = nao
        t1 = timer()
        self.norbs = sv.norbs
        self.init_inp_param_prod_log_dp(sv, **kw)
        #t2 = timer(); print(' after init_inp_param_prod_log_dp ', t2-t1); t1=timer()
        data = self.chain_data()
        libnao.init_vrtx_cc_batch(data.ctypes.data_as(POINTER(c_double)),
                                  c_int64(len(data)))
        self.sv_pbloc_data = True

        aos = sv.ao_log
        p2srncc, p2npac, p2atoms = [], [], []
        for a1, [sp1, ra1] in enumerate(zip(sv.atom2sp, sv.atom2coord)):
            rc1 = aos.sp2rcut[sp1]
            for a2, [sp2, ra2] in enumerate(
                    zip(sv.atom2sp[a1 + 1:], sv.atom2coord[a1 + 1:])):
                a2 += a1 + 1
                rc2, dist = aos.sp2rcut[sp2], sqrt(((ra1 - ra2)**2).sum())
                if dist > rc1 + rc2: continue
                cc2atom = self.ls_contributing(a1, a2)
                p2atoms.append([a1, a2])
                p2srncc.append([sp1, sp2] + list(ra1) + list(ra2) +
                               [len(cc2atom)] + list(cc2atom))
                p2npac.append(
                    sum([
                        self.prod_log.sp2norbs[sv.atom2sp[ia]]
                        for ia in cc2atom
                    ]))

        #print(np.asarray(p2srncc))
        p2ndp = np.require(zeros(len(p2srncc), dtype=np.int64),
                           requirements='CW')
        p2srncc_cp = np.require(np.asarray(p2srncc), requirements='C')
        npairs = p2srncc_cp.shape[0]
        self.npairs = npairs
        self.bp2info = [
        ]  # going to be indices of atoms, list of contributing centres, conversion coefficients
        if npairs > 0:  # Conditional fill of the self.bp2info if there are bilocal pairs (natoms>1)
            ld = p2srncc_cp.shape[1]
            #print('npairs  p2srncc_cp.shape', npairs, p2srncc_cp.shape)
            if nao.verbosity > 0:
                t2 = timer()
                print('call vrtx_cc_batch ', t2 - t1, 'npairs ', npairs)
                t1 = timer()
            libnao.vrtx_cc_batch(c_int64(npairs),
                                 p2srncc_cp.ctypes.data_as(POINTER(c_double)),
                                 c_int64(ld),
                                 p2ndp.ctypes.data_as(POINTER(c_int64)))
            if nao.verbosity > 0:
                t2 = timer()
                print('after vrtx_cc_batch ', t2 - t1)
                t1 = timer()
            nout = 0
            sp2norbs = sv.ao_log.sp2norbs
            for srncc, ndp, npac in zip(p2srncc, p2ndp, p2npac):
                sp1, sp2 = srncc[0], srncc[1]
                nout = nout + ndp * sp2norbs[sp1] * sp2norbs[sp2] + npac * ndp

            dout = np.require(zeros(nout), requirements='CW')
            libnao.get_vrtx_cc_batch(c_int64(0), c_int64(npairs),
                                     dout.ctypes.data_as(POINTER(c_double)),
                                     c_int64(nout))

            f = 0
            for srncc, ndp, npac, [a1, a2] in zip(p2srncc, p2ndp, p2npac,
                                                  p2atoms):
                if ndp < 1: continue
                sp1, sp2, ncc = srncc[0], srncc[1], srncc[8]
                icc2a = array(srncc[9:9 + ncc], dtype=int64)
                nnn = np.array((ndp, sp2norbs[sp2], sp2norbs[sp1]),
                               dtype=int64)
                nnc = np.array([ndp, npac], dtype=int64)
                s = f
                f = s + np.prod(nnn)
                vrtx = dout[s:f].reshape(nnn)
                s = f
                f = s + np.prod(nnc)
                ccoe = dout[s:f].reshape(nnc)
                icc2s = np.zeros(len(icc2a) + 1, dtype=int64)
                for icc, a in enumerate(icc2a):
                    icc2s[
                        icc +
                        1] = icc2s[icc] + self.prod_log.sp2norbs[sv.atom2sp[a]]
                pbiloc = prod_biloc_c(atoms=array([a2, a1]),
                                      vrtx=vrtx,
                                      cc2a=icc2a,
                                      cc2s=icc2s,
                                      cc=ccoe)
                self.bp2info.append(pbiloc)

        #t2 = timer(); print('after loop ', t2-t1); t1=timer()
        self.dpc2s, self.dpc2t, self.dpc2sp = self.init_c2s_domiprod(
        )  # dominant product's counting
        self.npdp = self.dpc2s[-1]
        #t2 = timer(); print('after init_c2s_domiprod ', t2-t1); t1=timer()
        return self
Exemplo n.º 4
0
def pb_ae(self, sv, tol_loc=1e-5, tol_biloc=1e-6, ac_rcut_ratio=1.0):
    """ It should work with GTOs as well."""
    from pyscf.nao import coulomb_am, get_atom2bas_s, conv_yzx2xyz_c, prod_log_c, ls_part_centers, comp_coulomb_den
    from pyscf.nao.m_overlap_coo import overlap_coo
    from pyscf.nao.m_prod_biloc import prod_biloc_c
    from scipy.sparse import csr_matrix
    from pyscf import gto

    self.sv = sv
    self.tol_loc = tol_loc
    self.tol_biloc = tol_biloc
    self.ac_rcut_ratio = ac_rcut_ratio
    self.ac_rcut = ac_rcut_ratio * max(sv.ao_log.sp2rcut)

    self.prod_log = prod_log_c().init_prod_log_dp(
        sv.ao_log, tol_loc)  # local basis (for each specie)
    self.hkernel_csr = csr_matrix(
        overlap_coo(sv, self.prod_log,
                    coulomb_am))  # compute local part of Coulomb interaction
    self.c2s = zeros(
        (sv.natm + 1), dtype=int64
    )  # global product Center (atom) -> start in case of atom-centered basis
    for gc, sp in enumerate(sv.atom2sp):
        self.c2s[gc + 1] = self.c2s[gc] + self.prod_log.sp2norbs[sp]  #
    c2s = self.c2s  # What is the meaning of this copy ?? ... This is a pointer to self.c2s

    self.bp2info = [
    ]  # going to be some information including indices of atoms, list of contributing centres, conversion coefficients

    for ia1, n1 in enumerate(sv.atom2s[1:] - sv.atom2s[0:-1]):
        for ia2, n2 in enumerate(sv.atom2s[ia1 + 2:] - sv.atom2s[ia1 + 1:-1]):
            ia2 += ia1 + 1
            mol2 = gto.Mole(atom=[sv._atom[ia1], sv._atom[ia2]],
                            basis=sv.basis,
                            unit='bohr').build()
            bs = get_atom2bas_s(mol2._bas)
            ss = (bs[0], bs[1], bs[1], bs[2], bs[0], bs[1], bs[1], bs[2])
            eri = mol2.intor('cint2e_sph',
                             shls_slice=ss).reshape(n1, n2, n1, n2)
            eri = conv_yzx2xyz_c(mol2).conv_yzx2xyz_4d(eri, 'pyscf2nao',
                                                       ss).reshape(
                                                           n1 * n2, n1 * n2)
            ee, xx = np.linalg.eigh(
                eri
            )  # This the simplest way. TODO: diag in each m-channel separately
            mu2d = [domi for domi, eva in enumerate(ee) if eva > tol_biloc
                    ]  # The choice of important linear combinations is here
            nprod = len(mu2d)
            if nprod < 1:
                continue  # Skip the rest of operations in case there is no large linear combinations.

            # add new vertex
            vrtx = zeros([nprod, n1, n2])
            for p, d in enumerate(mu2d):
                vrtx[p, :, :] = xx[:, d].reshape(n1, n2)

            #print(ia1,ia2,nprod,abs(einsum('pab,qab->pq', lambdx, lambdx).reshape(nprod,nprod)-np.identity(nprod)).sum())

            lc2c = ls_part_centers(
                sv, ia1, ia2, ac_rcut_ratio)  # list of participating centers
            lc2s = zeros(
                (len(lc2c) + 1), dtype=int64
            )  # local product center -> start for the current bilocal pair
            for lc, c in enumerate(lc2c):
                lc2s[lc + 1] = lc2s[lc] + self.prod_log.sp2norbs[sv.atom2sp[c]]

            npbp = lc2s[
                -1]  # size of the functions which will contribute to the given pair ia1,ia2
            hkernel_bp = np.zeros(
                (npbp,
                 npbp))  # this is local kernel for the current bilocal pair
            for lc1, c1 in enumerate(lc2c):
                for lc2, c2 in enumerate(lc2c):
                    for i1 in range(lc2s[lc1 + 1] - lc2s[lc1]):
                        for i2 in range(lc2s[lc2 + 1] - lc2s[lc2]):
                            hkernel_bp[i1 + lc2s[lc1], i2 + lc2s[
                                lc2]] = self.hkernel_csr[i1 + c2s[c1], i2 + c2s[
                                    c2]]  # element-by-element construction here
            inv_hk = np.linalg.inv(hkernel_bp)

            llp = np.zeros((npbp, nprod))
            for c, s, f in zip(lc2c, lc2s, lc2s[1:]):
                n3 = sv.atom2s[c + 1] - sv.atom2s[c]
                lcd = self.prod_log.sp2lambda[sv.atom2sp[c]]
                mol3 = gto.Mole(
                    atom=[sv._atom[ia1], sv._atom[ia2], sv._atom[c]],
                    basis=sv.basis,
                    unit='bohr',
                    spin=1).build()
                bs = get_atom2bas_s(mol3._bas)
                ss = (bs[2], bs[3], bs[2], bs[3], bs[0], bs[1], bs[1], bs[2])
                tci_ao = mol3.intor('cint2e_sph',
                                    shls_slice=ss).reshape(n3, n3, n1, n2)
                tci_ao = conv_yzx2xyz_c(mol3).conv_yzx2xyz_4d(
                    tci_ao, 'pyscf2nao', ss)
                lp = einsum('lcd,cdp->lp', lcd,
                            einsum('cdab,pab->cdp', tci_ao, vrtx))
                llp[s:f, :] = lp

            cc = einsum('ab,bc->ac', inv_hk, llp)
            pbiloc = prod_biloc_c(atoms=array([ia1, ia2]),
                                  vrtx=vrtx,
                                  cc2a=lc2c,
                                  cc2s=lc2s,
                                  cc=cc.T)

            self.bp2info.append(pbiloc)
            #print(ia1, ia2, len(mu2d), lc2c, hkernel_bp.sum(), inv_hk.sum())
    self.dpc2s, self.dpc2t, self.dpc2sp = self.init_c2s_domiprod(
    )  # dominant product's counting
    return self
Exemplo n.º 5
0
  def init_prod_basis_pp_batch(self, nao, **kw):
    """ Talman's procedure should be working well with Pseudo-Potential starting point."""
    from pyscf.nao import prod_log_c
    from pyscf.nao.m_prod_biloc import prod_biloc_c

    sv = nao
    t1 = timer()
    self.norbs = sv.norbs
    self.init_inp_param_prod_log_dp(sv, **kw)
    #t2 = timer(); print(' after init_inp_param_prod_log_dp ', t2-t1); t1=timer()
    data = self.chain_data()
    libnao.init_vrtx_cc_batch(data.ctypes.data_as(POINTER(c_double)), c_int64(len(data)))
    self.sv_pbloc_data = True

    aos = sv.ao_log
    p2srncc,p2npac,p2atoms = [],[],[]
    for a1,[sp1,ra1] in enumerate(zip(sv.atom2sp, sv.atom2coord)):
      rc1 = aos.sp2rcut[sp1]
      for a2,[sp2,ra2] in enumerate(zip(sv.atom2sp[a1+1:], sv.atom2coord[a1+1:])):
        a2+=a1+1
        rc2,dist = aos.sp2rcut[sp2], sqrt(((ra1-ra2)**2).sum())
        if dist>rc1+rc2 : continue
        cc2atom = self.ls_contributing(a1,a2)
        p2atoms.append([a1,a2])
        p2srncc.append([sp1,sp2]+list(ra1)+list(ra2)+[len(cc2atom)]+list(cc2atom))
        p2npac.append( sum([self.prod_log.sp2norbs[sv.atom2sp[ia]] for ia in cc2atom ]))

    #print(np.asarray(p2srncc))
    p2ndp = np.require( zeros(len(p2srncc), dtype=np.int64), requirements='CW')
    p2srncc_cp = np.require(  np.asarray(p2srncc), requirements='C')
    npairs = p2srncc_cp.shape[0]
    self.npairs = npairs
    self.bp2info = [] # going to be indices of atoms, list of contributing centres, conversion coefficients
    if npairs>0 : # Conditional fill of the self.bp2info if there are bilocal pairs (natoms>1)
      ld = p2srncc_cp.shape[1]
      #print('npairs  p2srncc_cp.shape', npairs, p2srncc_cp.shape)
      #t2 = timer(); print('call vrtx_cc_batch ', t2-t1, 'npairs ', npairs); t1=timer()
      libnao.vrtx_cc_batch( c_int64(npairs), p2srncc_cp.ctypes.data_as(POINTER(c_double)), 
        c_int64(ld), p2ndp.ctypes.data_as(POINTER(c_int64)))
      #t2 = timer(); print('after vrtx_cc_batch ', t2-t1); t1=timer()
      nout = 0
      sp2norbs = sv.ao_log.sp2norbs
      for srncc,ndp,npac in zip(p2srncc,p2ndp,p2npac):
        sp1,sp2 = srncc[0],srncc[1]
        nout = nout + ndp*sp2norbs[sp1]*sp2norbs[sp2]+npac*ndp
      
      dout = np.require( zeros(nout), requirements='CW')
      libnao.get_vrtx_cc_batch(c_int64(0),c_int64(npairs),dout.ctypes.data_as(POINTER(c_double)),c_int64(nout))

      f = 0
      for srncc,ndp,npac,[a1,a2] in zip(p2srncc,p2ndp,p2npac,p2atoms):
        if ndp<1 : continue
        sp1,sp2,ncc = srncc[0],srncc[1],srncc[8]
        icc2a = array(srncc[9:9+ncc], dtype=int64)
        nnn = np.array((ndp,sp2norbs[sp2],sp2norbs[sp1]), dtype=int64)
        nnc = np.array([ndp,npac], dtype=int64)
        s = f;  f=s+np.prod(nnn); vrtx  = dout[s:f].reshape(nnn)
        s = f;  f=s+np.prod(nnc); ccoe  = dout[s:f].reshape(nnc)
        icc2s = np.zeros(len(icc2a)+1, dtype=int64)
        for icc,a in enumerate(icc2a): icc2s[icc+1] = icc2s[icc] + self.prod_log.sp2norbs[sv.atom2sp[a]]
        pbiloc = prod_biloc_c(atoms=array([a2,a1]),vrtx=vrtx,cc2a=icc2a,cc2s=icc2s,cc=ccoe)
        self.bp2info.append(pbiloc)

    #t2 = timer(); print('after loop ', t2-t1); t1=timer()
    self.dpc2s,self.dpc2t,self.dpc2sp = self.init_c2s_domiprod() # dominant product's counting
    self.npdp = self.dpc2s[-1]
    #t2 = timer(); print('after init_c2s_domiprod ', t2-t1); t1=timer()
    return self
Exemplo n.º 6
0
def pb_ae(self, sv, tol_loc=1e-5, tol_biloc=1e-6, ac_rcut_ratio=1.0):
  """ It should work with GTOs as well."""
  from pyscf.nao import coulomb_am, get_atom2bas_s, conv_yzx2xyz_c, prod_log_c, ls_part_centers, comp_coulomb_den
  from pyscf.nao.m_overlap_coo import overlap_coo
  from pyscf.nao.m_prod_biloc import prod_biloc_c
  from scipy.sparse import csr_matrix
  from pyscf import gto
    
  self.sv = sv
  self.tol_loc = tol_loc
  self.tol_biloc = tol_biloc
  self.ac_rcut_ratio = ac_rcut_ratio
  self.ac_rcut = ac_rcut_ratio*max(sv.ao_log.sp2rcut)
   
  self.prod_log = prod_log_c().init_prod_log_dp(sv.ao_log, tol_loc) # local basis (for each specie) 
  self.hkernel_csr  = csr_matrix(overlap_coo(sv, self.prod_log, coulomb_am)) # compute local part of Coulomb interaction
  self.c2s = zeros((sv.natm+1), dtype=int64) # global product Center (atom) -> start in case of atom-centered basis
  for gc,sp in enumerate(sv.atom2sp): self.c2s[gc+1]=self.c2s[gc]+self.prod_log.sp2norbs[sp] # 
  c2s = self.c2s      # What is the meaning of this copy ?? ... This is a pointer to self.c2s
   
  self.bp2info   = [] # going to be some information including indices of atoms, list of contributing centres, conversion coefficients
  
  for ia1,n1 in enumerate(sv.atom2s[1:]-sv.atom2s[0:-1]):
    for ia2,n2 in enumerate(sv.atom2s[ia1+2:]-sv.atom2s[ia1+1:-1]):
      ia2 += ia1+1
      mol2 = gto.Mole(atom=[sv._atom[ia1], sv._atom[ia2]], basis=sv.basis, unit='bohr').build()
      bs = get_atom2bas_s(mol2._bas)
      ss = (bs[0],bs[1], bs[1],bs[2], bs[0],bs[1], bs[1],bs[2])
      eri = mol2.intor('cint2e_sph', shls_slice=ss).reshape(n1,n2,n1,n2)
      eri = conv_yzx2xyz_c(mol2).conv_yzx2xyz_4d(eri, 'pyscf2nao', ss).reshape(n1*n2,n1*n2)
      ee,xx = np.linalg.eigh(eri)   # This the simplest way. TODO: diag in each m-channel separately
      mu2d = [domi for domi,eva in enumerate(ee) if eva>tol_biloc] # The choice of important linear combinations is here
      nprod=len(mu2d)
      if nprod<1: continue # Skip the rest of operations in case there is no large linear combinations.

      # add new vertex
      vrtx = zeros([nprod,n1,n2])
      for p,d in enumerate(mu2d): vrtx[p,:,:] = xx[:,d].reshape(n1,n2)
        
      #print(ia1,ia2,nprod,abs(einsum('pab,qab->pq', lambdx, lambdx).reshape(nprod,nprod)-np.identity(nprod)).sum())

      lc2c = ls_part_centers(sv, ia1, ia2, ac_rcut_ratio) # list of participating centers
      lc2s = zeros((len(lc2c)+1), dtype=int64) # local product center -> start for the current bilocal pair
      for lc,c in enumerate(lc2c): lc2s[lc+1]=lc2s[lc]+self.prod_log.sp2norbs[sv.atom2sp[c]]

      npbp = lc2s[-1] # size of the functions which will contribute to the given pair ia1,ia2
      hkernel_bp = np.zeros((npbp, npbp)) # this is local kernel for the current bilocal pair
      for lc1,c1 in enumerate(lc2c):
        for lc2,c2 in enumerate(lc2c):
          for i1 in range(lc2s[lc1+1]-lc2s[lc1]):
            for i2 in range(lc2s[lc2+1]-lc2s[lc2]):
              hkernel_bp[i1+lc2s[lc1],i2+lc2s[lc2]] = self.hkernel_csr[i1+c2s[c1],i2+c2s[c2]] # element-by-element construction here
      inv_hk = np.linalg.inv(hkernel_bp)

      llp = np.zeros((npbp, nprod))
      for c,s,f in zip(lc2c,lc2s,lc2s[1:]):
        n3 = sv.atom2s[c+1]-sv.atom2s[c]
        lcd = self.prod_log.sp2lambda[sv.atom2sp[c]]
        mol3 = gto.Mole(atom=[sv._atom[ia1], sv._atom[ia2], sv._atom[c]], basis=sv.basis, unit='bohr', spin=1).build()
        bs = get_atom2bas_s(mol3._bas)
        ss = (bs[2],bs[3], bs[2],bs[3], bs[0],bs[1], bs[1],bs[2])
        tci_ao = mol3.intor('cint2e_sph', shls_slice=ss).reshape(n3,n3,n1,n2)
        tci_ao = conv_yzx2xyz_c(mol3).conv_yzx2xyz_4d(tci_ao, 'pyscf2nao', ss)
        lp = einsum('lcd,cdp->lp', lcd,einsum('cdab,pab->cdp', tci_ao, vrtx))
        llp[s:f,:] = lp

      cc = einsum('ab,bc->ac', inv_hk, llp)
      pbiloc = prod_biloc_c(atoms=array([ia1,ia2]), vrtx=vrtx, cc2a=lc2c, cc2s=lc2s, cc=cc.T)
      
      self.bp2info.append(pbiloc)
      #print(ia1, ia2, len(mu2d), lc2c, hkernel_bp.sum(), inv_hk.sum())
  self.dpc2s,self.dpc2t,self.dpc2sp = self.init_c2s_domiprod() # dominant product's counting 
  return self