Example #1
0
    def _int2c(self) -> torch.Tensor:
        # 2-centre integral
        # this function works mostly in numpy
        # no gradients propagated in this function (and it's OK)
        # this function mostly replicate the `ft_aopair_kpts` function in pyscf
        # https://github.com/pyscf/pyscf/blob/master/pyscf/pbc/df/ft_ao.py
        # https://github.com/pyscf/pyscf/blob/c9aa2be600d75a97410c3203abf35046af8ca615/pyscf/pbc/df/ft_ao.py#L52
        assert len(self.wrappers) == 2

        # if the ls is too big, it might produce segfault
        if (self.ls.shape[0] > 1e6):
            warnings.warn("The number of neighbors in the integral is too many, "
                          "it might causes segfault")

        # libpbc will do in-place shift of the basis of one of the wrappers, so
        # we need to make a concatenated copy of the wrapper's atm_bas_env
        atm, bas, env, ao_loc = _concat_atm_bas_env(self.wrappers[0], self.wrappers[1])
        i0, i1 = self.wrappers[0].shell_idxs
        j0, j1 = self.wrappers[1].shell_idxs
        nshls0 = len(self.wrappers[0].parent)
        shls_slice = (i0, i1, j0 + nshls0, j1 + nshls0)

        # get the lattice translation vectors and the exponential factors
        expkl = np.asarray(np.exp(1j * np.dot(self.kpts_inp_np, self.ls.T)), order='C')

        # prepare the output
        nGv = self.GvT.shape[-1]
        nkpts = len(self.kpts_inp_np)
        outshape = (nkpts,) + self.comp_shape + tuple(w.nao() for w in self.wrappers) + (nGv,)
        out = np.empty(outshape, dtype=np.complex128)

        # do the integration
        cintor = getattr(CGTO(), self.opname)
        eval_gz = CPBC().GTO_Gv_general
        fill = CPBC().PBC_ft_fill_ks1
        drv = CPBC().PBC_ft_latsum_drv
        p_gxyzT = c_null_ptr()
        p_mesh = (ctypes.c_int * 3)(0, 0, 0)
        p_b = (ctypes.c_double * 1)(0)
        drv(cintor, eval_gz, fill,
            np2ctypes(out),  # ???
            int2ctypes(nkpts),
            int2ctypes(self.ncomp),
            int2ctypes(len(self.ls)),
            np2ctypes(self.ls),
            np2ctypes(expkl),
            (ctypes.c_int * len(shls_slice))(*shls_slice),
            np2ctypes(ao_loc),
            np2ctypes(self.GvT),
            p_b, p_gxyzT, p_mesh,
            int2ctypes(nGv),
            np2ctypes(atm), int2ctypes(len(atm)),
            np2ctypes(bas), int2ctypes(len(bas)),
            np2ctypes(env))

        out_tensor = torch.as_tensor(out, dtype=get_complex_dtype(self.dtype),
                                     device=self.device)
        return out_tensor
Example #2
0
    def _int2c(self) -> torch.Tensor:
        # 2-centre integral
        # this function works mostly in numpy
        # no gradients propagated in this function (and it's OK)
        # this function mostly replicate the `intor_cross` function in pyscf
        # https://github.com/pyscf/pyscf/blob/master/pyscf/pbc/gto/cell.py
        # https://github.com/pyscf/pyscf/blob/f1321d5dd4fa103b5b04f10f31389c408949269d/pyscf/pbc/gto/cell.py#L345
        assert len(self.wrappers) == 2

        # libpbc will do in-place shift of the basis of one of the wrappers, so
        # we need to make a concatenated copy of the wrapper's atm_bas_env
        atm, bas, env, ao_loc = _concat_atm_bas_env(self.wrappers[0],
                                                    self.wrappers[1])
        i0, i1 = self.wrappers[0].shell_idxs
        j0, j1 = self.wrappers[1].shell_idxs
        nshls0 = len(self.wrappers[0].parent)
        shls_slice = (i0, i1, j0 + nshls0, j1 + nshls0)

        # prepare the output
        nkpts = len(self.kpts_inp_np)
        outshape = (nkpts, ) + self.comp_shape + tuple(w.nao()
                                                       for w in self.wrappers)
        out = np.empty(outshape, dtype=np.complex128)

        # TODO: add symmetry here
        fill = CPBC().PBCnr2c_fill_ks1
        fintor = getattr(CGTO(), self.opname)
        # TODO: use proper optimizers
        cintopt = _get_intgl_optimizer(self.opname, atm, bas, env)
        cpbcopt = c_null_ptr()

        # get the lattice translation vectors and the exponential factors
        expkl = np.asarray(np.exp(1j * np.dot(self.kpts_inp_np, self.ls.T)),
                           order='C')

        # if the ls is too big, it might produce segfault
        if (self.ls.shape[0] > 1e6):
            warnings.warn(
                "The number of neighbors in the integral is too many, "
                "it might causes segfault")

        # perform the integration
        drv = CPBC().PBCnr2c_drv
        drv(fintor, fill,
            out.ctypes.data_as(ctypes.c_void_p), int2ctypes(nkpts),
            int2ctypes(self.ncomp), int2ctypes(len(self.ls)),
            np2ctypes(self.ls),
            np2ctypes(expkl), (ctypes.c_int * len(shls_slice))(*shls_slice),
            np2ctypes(ao_loc), cintopt, cpbcopt, np2ctypes(atm),
            int2ctypes(atm.shape[0]), np2ctypes(bas), int2ctypes(bas.shape[0]),
            np2ctypes(env), int2ctypes(env.size))

        out_tensor = torch.as_tensor(out,
                                     dtype=get_complex_dtype(self.dtype),
                                     device=self.device)
        return out_tensor
Example #3
0
    def _int3c(self) -> torch.Tensor:
        # performing 3-centre integrals with libcint
        drv = CGTO().GTOnr3c_drv
        fill = CGTO().GTOnr3c_fill_s1
        # TODO: create optimizer without the 3rd index like in
        # https://github.com/pyscf/pyscf/blob/e833b9a4fd5fb24a061721e5807e92c44bb66d06/pyscf/gto/moleintor.py#L538
        outsh = self.outshape
        out = np.empty((*outsh[:-3], outsh[-1], outsh[-2], outsh[-3]), dtype=np.float64)
        drv(self.op, fill,
            out.ctypes.data_as(ctypes.c_void_p),
            int2ctypes(self.ncomp),
            (ctypes.c_int * len(self.shls_slice))(*self.shls_slice),
            np2ctypes(self.wrapper0.full_shell_to_aoloc),
            self.optimizer,
            np2ctypes(self.atm), int2ctypes(self.atm.shape[0]),
            np2ctypes(self.bas), int2ctypes(self.bas.shape[0]),
            np2ctypes(self.env))

        out = np.swapaxes(out, -3, -1)
        return self._to_tensor(out)
Example #4
0
    def _int4c(self) -> torch.Tensor:
        # performing 4-centre integrals with libcint
        symm = self.int_nmgr.get_intgl_symmetry(self.wrapper_uniqueness)
        outshape = symm.get_reduced_shape(self.outshape)

        out = np.empty(outshape, dtype=np.float64)

        drv = CGTO().GTOnr2e_fill_drv
        fill = getattr(CGTO(), "GTOnr2e_fill_%s" % symm.code)
        prescreen = ctypes.POINTER(ctypes.c_void_p)()
        drv(self.op, fill, prescreen,
            out.ctypes.data_as(ctypes.c_void_p),
            ctypes.c_int(self.ncomp),
            (ctypes.c_int * 8)(*self.shls_slice),
            np2ctypes(self.wrapper0.full_shell_to_aoloc),
            self.optimizer,
            np2ctypes(self.atm), int2ctypes(self.atm.shape[0]),
            np2ctypes(self.bas), int2ctypes(self.bas.shape[0]),
            np2ctypes(self.env))

        out = symm.reconstruct_array(out, self.outshape)
        return self._to_tensor(out)
Example #5
0
def gto_evaluator(wrapper: LibcintWrapper, shortname: str, rgrid: torch.Tensor,
                  to_transpose: bool):
    # NOTE: this function do not propagate gradient and should only be used
    # in this file only

    # rgrid: (ngrid, ndim)
    # returns: (*, nao, ngrid) if not to_transpose else (*, ngrid, nao)

    ngrid = rgrid.shape[0]
    nshells = len(wrapper)
    nao = wrapper.nao()
    opname = _get_evalgto_opname(shortname, wrapper.spherical)
    outshape = _get_evalgto_compshape(shortname) + (nao, ngrid)

    out = np.empty(outshape, dtype=np.float64)
    non0tab = np.ones(((ngrid + BLKSIZE - 1) // BLKSIZE, nshells),
                      dtype=np.int8)

    # TODO: check if we need to transpose it first?
    rgrid = rgrid.contiguous()
    coords = np.asarray(rgrid, dtype=np.float64, order='F')
    ao_loc = np.asarray(wrapper.full_shell_to_aoloc, dtype=np.int32)

    c_shls = (ctypes.c_int * 2)(*wrapper.shell_idxs)
    c_ngrid = ctypes.c_int(ngrid)

    # evaluate the orbital
    operator = getattr(CGTO(), opname)
    operator.restype = ctypes.c_double
    atm, bas, env = wrapper.atm_bas_env
    operator(c_ngrid, c_shls, np2ctypes(ao_loc), np2ctypes(out),
             np2ctypes(coords), np2ctypes(non0tab), np2ctypes(atm),
             int2ctypes(atm.shape[0]), np2ctypes(bas),
             int2ctypes(bas.shape[0]), np2ctypes(env))

    if to_transpose:
        out = np.ascontiguousarray(np.moveaxis(out, -1, -2))

    out_tensor = torch.as_tensor(out,
                                 dtype=wrapper.dtype,
                                 device=wrapper.device)
    return out_tensor
Example #6
0
    def _int2c(self) -> torch.Tensor:
        # performing 2-centre integrals with libcint
        drv = CGTO().GTOint2c
        outshape = self.outshape
        out = np.empty((*outshape[:-2], outshape[-1], outshape[-2]), dtype=np.float64)
        drv(self.op,
            out.ctypes.data_as(ctypes.c_void_p),
            ctypes.c_int(self.ncomp),
            ctypes.c_int(0),  # do not assume hermitian
            (ctypes.c_int * len(self.shls_slice))(*self.shls_slice),
            np2ctypes(self.wrapper0.full_shell_to_aoloc),
            self.optimizer,
            np2ctypes(self.atm), int2ctypes(self.atm.shape[0]),
            np2ctypes(self.bas), int2ctypes(self.bas.shape[0]),
            np2ctypes(self.env))

        out = np.swapaxes(out, -2, -1)
        # TODO: check if we need to do the lines below for 3rd order grad and higher
        # if out.ndim > 2:
        #     out = np.moveaxis(out, -3, 0)
        return self._to_tensor(out)
Example #7
0
 def __del__(self):
     try:
         CGTO.CINTdel_optimizer(ctypes.byref(self))
     except AttributeError:
         pass
Example #8
0
def gto_ft_evaluator(wrapper: LibcintWrapper,
                     gvgrid: torch.Tensor) -> torch.Tensor:
    # evaluate Fourier Transform of the basis which is defined as
    # FT(f(r)) = integral(f(r) * exp(-ik.r) dr)

    # NOTE: this function do not propagate gradient and should only be used
    # in this file only
    # this is mainly from PySCF
    # https://github.com/pyscf/pyscf/blob/c9aa2be600d75a97410c3203abf35046af8ca615/pyscf/gto/ft_ao.py#L107

    assert gvgrid.ndim == 2
    assert gvgrid.shape[-1] == NDIM

    # gvgrid: (ngrid, ndim)
    # returns: (nao, ngrid)
    dtype = wrapper.dtype
    device = wrapper.device

    fill = CGTO().GTO_ft_fill_s1
    if wrapper.spherical:
        intor = CGTO().GTO_ft_ovlp_sph
    else:
        intor = CGTO().GTO_ft_ovlp_cart
    fn = CGTO().GTO_ft_fill_drv

    eval_gz = CGTO().GTO_Gv_general
    p_gxyzT = c_null_ptr()
    p_gs = (ctypes.c_int * 3)(0, 0, 0)
    p_b = (ctypes.c_double * 1)(0)

    # add another dummy basis to provide the multiplier
    c = np.sqrt(4 * np.pi)  # s-type normalization
    ghost_basis = CGTOBasis(
        angmom=0,
        alphas=torch.tensor([0.], dtype=dtype, device=device),
        coeffs=torch.tensor([c], dtype=dtype, device=device),
        normalized=True,
    )
    ghost_atom_basis = AtomCGTOBasis(atomz=0,
                                     bases=[ghost_basis],
                                     pos=torch.tensor([0.0, 0.0, 0.0],
                                                      dtype=dtype,
                                                      device=device))
    ghost_wrapper = LibcintWrapper([ghost_atom_basis],
                                   spherical=wrapper.spherical,
                                   lattice=wrapper.lattice)
    wrapper, ghost_wrapper = LibcintWrapper.concatenate(wrapper, ghost_wrapper)
    shls_slice = (*wrapper.shell_idxs, *ghost_wrapper.shell_idxs)
    ao_loc = wrapper.full_shell_to_aoloc
    atm, bas, env = wrapper.atm_bas_env

    # prepare the gvgrid
    GvT = np.asarray(gvgrid.detach().numpy().T, order="C")
    nGv = gvgrid.shape[0]

    # prepare the output matrix
    outshape = (wrapper.nao(), nGv)
    out = np.zeros(outshape, dtype=np.complex128, order="C")

    fn(intor, eval_gz, fill, np2ctypes(out), int2ctypes(1),
       (ctypes.c_int * len(shls_slice))(*shls_slice), np2ctypes(ao_loc),
       ctypes.c_double(0), np2ctypes(GvT), p_b, p_gxyzT, p_gs, int2ctypes(nGv),
       np2ctypes(atm), int2ctypes(len(atm)), np2ctypes(bas),
       int2ctypes(len(bas)), np2ctypes(env))

    return torch.as_tensor(out, dtype=get_complex_dtype(dtype), device=device)