Exemple #1
0
Fichier : hf.py Projet : diffqc/dqc
 def __dm2vhf(self, dm):
     # from density matrix, returns the linear operator on electron-electron
     # coulomb and exchange
     elrep = self._hamilton.get_elrep(SpinParam.sum(dm))
     exch = self._hamilton.get_exchange(dm)
     vhf = SpinParam.apply_fcn(lambda exch: elrep + exch, exch)
     return vhf
Exemple #2
0
    def get_vxc(self, densinfo):
        """
        Returns the ValGrad for the xc potential given the density info
        for unpolarized case.
        """
        # This is the default implementation of vxc if there is no implementation
        # in the specific class of XC.

        # densinfo.value & lapl: (*BD, nr)
        # densinfo.grad: (*BD, ndim, nr)
        # return:
        # potentialinfo.value & lapl: (*BD, nr)
        # potentialinfo.grad: (*BD, ndim, nr)

        # mark the densinfo components as requiring grads
        with self._enable_grad_densinfo(densinfo):
            with torch.enable_grad():
                edensity = self.get_edensityxc(densinfo)  # (*BD, nr)
            grad_outputs = torch.ones_like(edensity)
            grad_enabled = torch.is_grad_enabled()

            if not isinstance(densinfo, ValGrad):  # polarized case
                if self.family == 1:  # LDA
                    params = (densinfo.u.value, densinfo.d.value)
                    dedn_u, dedn_d = torch.autograd.grad(
                        edensity, params, create_graph=grad_enabled, grad_outputs=grad_outputs)

                    return SpinParam(u=ValGrad(value=dedn_u), d=ValGrad(value=dedn_d))

                elif self.family == 2:  # GGA
                    params = (densinfo.u.value, densinfo.d.value, densinfo.u.grad, densinfo.d.grad)
                    dedn_u, dedn_d, dedg_u, dedg_d = torch.autograd.grad(
                        edensity, params, create_graph=grad_enabled, grad_outputs=grad_outputs)

                    return SpinParam(
                        u=ValGrad(value=dedn_u, grad=dedg_u),
                        d=ValGrad(value=dedn_d, grad=dedg_d))

                else:
                    raise NotImplementedError(
                        "Default polarized vxc for family %s is not implemented" % self.family)
            else:  # unpolarized case
                if self.family == 1:  # LDA
                    dedn, = torch.autograd.grad(
                        edensity, densinfo.value, create_graph=grad_enabled,
                        grad_outputs=grad_outputs)

                    return ValGrad(value=dedn)

                elif self.family == 2:  # GGA
                    dedn, dedg = torch.autograd.grad(
                        edensity, (densinfo.value, densinfo.grad), create_graph=grad_enabled,
                        grad_outputs=grad_outputs)

                    return ValGrad(value=dedn, grad=dedg)

                else:
                    raise NotImplementedError("Default vxc for family %d is not implemented" % self.family)
Exemple #3
0
 def dm2params(dm: Union[torch.Tensor, SpinParam[torch.Tensor]]) -> \
         Tuple[torch.Tensor, torch.Tensor]:
     pc = SpinParam.apply_fcn(
         lambda dm, norb: h.dm2ao_orb_params(SpinParam.sum(dm),
                                             norb=norb), dm, norb)
     p = SpinParam.apply_fcn(lambda pc: pc[0], pc)
     c = SpinParam.apply_fcn(lambda pc: pc[1], pc)
     params = self._engine.pack_aoparams(p)
     coeffs = self._engine.pack_aoparams(c)
     return params, coeffs
Exemple #4
0
 def get_e_exchange(
         self, dm: Union[torch.Tensor,
                         SpinParam[torch.Tensor]]) -> torch.Tensor:
     # get the energy from two electron exchange operator
     exc_mat = self.get_exchange(dm)
     ene = SpinParam.apply_fcn(
         lambda exc_mat, dm: 0.5 * torch.einsum(
             "...kij,...kji,k->...", exc_mat.fullmatrix(), dm, self._wkpts),
         exc_mat, dm)
     enetot = SpinParam.sum(ene)
     return enetot
Exemple #5
0
Fichier : ks.py Projet : diffqc/dqc
    def __dm2fock(self, dm):
        elrep = self.hamilton.get_elrep(SpinParam.sum(dm))  # (..., nao, nao)
        core_coul = self.knvext_linop + elrep

        if self.xc is not None:
            vxc = self.hamilton.get_vxc(
                dm)  # spin param or tensor (..., nao, nao)
            return SpinParam.apply_fcn(lambda vxc_: vxc_ + core_coul, vxc)
        else:
            if isinstance(dm, SpinParam):
                return SpinParam(u=core_coul, d=core_coul)
            else:
                return core_coul
Exemple #6
0
 def get_ene(orb_params, orb_coeffs):
     if polarized:
         orb_p = SpinParam(u=orb_params[..., :norb.u],
                           d=orb_params[..., norb.u:])
         orb_c = SpinParam(u=orb_coeffs[..., :norb.u],
                           d=orb_coeffs[..., norb.u:])
     else:
         orb_p = orb_params
         orb_c = orb_coeffs
     dm2 = SpinParam.apply_fcn(
         lambda orb_p, orb_c, orb_weights: h.ao_orb_params2dm(
             orb_p, orb_c, orb_weights), orb_p, orb_c, orb_weights)
     ene = qc.dm2energy(dm2)
     return ene
Exemple #7
0
def test_libxc_ldac_value():
    # check if the value of lda_c_pw is consistent
    xc = get_libxc("lda_c_pw")
    assert xc.family == 1
    assert xc.family == 1

    torch.manual_seed(123)
    n = 100
    rho_1 = torch.rand((n,), dtype=torch.float64)
    rho_2 = torch.rand((n,), dtype=torch.float64)
    rho_u = torch.maximum(rho_1, rho_2)
    rho_d = torch.minimum(rho_1, rho_2)
    rho_tot = rho_u + rho_d
    xi = (rho_u - rho_d) / rho_tot

    densinfo_u = ValGrad(value=rho_u)
    densinfo_d = ValGrad(value=rho_d)
    densinfo = SpinParam(u=densinfo_u, d=densinfo_d)
    densinfo_tot = ValGrad(value=rho_tot)

    # calculate the energy and compare with analytic
    edens_unpol = xc.get_edensityxc(densinfo_tot)
    edens_unpol_true = ldac_e_true(rho_tot, rho_tot * 0)
    assert torch.allclose(edens_unpol, edens_unpol_true)

    edens_pol = xc.get_edensityxc(densinfo)
    edens_pol_true = ldac_e_true(rho_tot, xi)
    assert torch.allclose(edens_pol, edens_pol_true)
Exemple #8
0
    def get_vxc(self, densinfo):
        avxc = self.a.get_vxc(densinfo)

        if isinstance(densinfo, ValGrad):
            return avxc * self.b
        else:
            return SpinParam(u=avxc.u * self.b, d=avxc.d * self.b)
Exemple #9
0
    def get_vxc(self, densinfo):
        # densinfo.value: (*BD, nr)
        # return:
        # potentialinfo.value: (*BD, nr)

        # polarized case
        if not isinstance(densinfo, ValGrad):
            assert _all_same_shape(densinfo.u, densinfo.d), ERRMSG
            rho_u = densinfo.u.value
            rho_d = densinfo.d.value

            # calculate the dE/dn
            dedn = CalcLDALibXCPol.apply(rho_u.reshape(-1), rho_d.reshape(-1),
                                         1, self.libxc_pol)  # (2, ninps)
            dedn = dedn.reshape(N_VRHO, *rho_u.shape)

            # split dE/dn into 2 different potential info
            potinfo_u = ValGrad(value=dedn[0])
            potinfo_d = ValGrad(value=dedn[1])
            return SpinParam(u=potinfo_u, d=potinfo_d)

        # unpolarized case
        else:
            rho = densinfo.value  # (*BD, nr)
            dedn = CalcLDALibXCUnpol.apply(rho.reshape(-1), 1,
                                           self.libxc_unpol)
            dedn = dedn.reshape(rho.shape)
            potinfo = ValGrad(value=dedn)
            return potinfo
Exemple #10
0
Fichier : hf.py Projet : diffqc/dqc
 def __fock2dm(self, fock):
     # diagonalize the fock matrix and obtain the density matrix
     eigvals, eigvecs = self.diagonalize(fock, self._norb)
     dm = SpinParam.apply_fcn(
         lambda eivecs, orb_weights: self._hamilton.ao_orb2dm(
             eivecs, orb_weights), eigvecs, self._orb_weight)
     return dm
Exemple #11
0
def test_libxc_gga_value():
    # compare the calculated value of GGA potential
    dtype = torch.float64
    xc = get_libxc("gga_x_pbe")
    assert xc.family == 2
    assert xc.family == 2

    torch.manual_seed(123)
    n = 100
    rho_u = torch.rand((n, ), dtype=dtype)
    rho_d = torch.rand((n, ), dtype=dtype)
    rho_tot = rho_u + rho_d
    gradn_u = torch.rand((n, 3), dtype=dtype) * 0
    gradn_d = torch.rand((n, 3), dtype=dtype) * 0
    gradn_tot = gradn_u + gradn_d

    densinfo_u = ValGrad(value=rho_u, grad=gradn_u)
    densinfo_d = ValGrad(value=rho_d, grad=gradn_d)
    densinfo_tot = densinfo_u + densinfo_d
    densinfo = SpinParam(u=densinfo_u, d=densinfo_d)

    # calculate the energy and compare with analytical expression
    edens_unpol = xc.get_edensityxc(densinfo_tot)
    edens_unpol_true = pbe_e_true(rho_tot, gradn_tot)
    assert torch.allclose(edens_unpol, edens_unpol_true)

    edens_pol = xc.get_edensityxc(densinfo)
    edens_pol_true = 0.5 * (pbe_e_true(2 * rho_u, 2 * gradn_u) +
                            pbe_e_true(2 * rho_d, 2 * gradn_d))
    assert torch.allclose(edens_pol, edens_pol_true)
Exemple #12
0
def test_libxc_lda_value():
    # check if the value is consistent
    xc = get_libxc("lda_x")
    assert xc.family == 1
    assert xc.family == 1

    torch.manual_seed(123)
    n = 100
    rho_u = torch.rand((n, ), dtype=torch.float64)
    rho_d = torch.rand((n, ), dtype=torch.float64)
    rho_tot = rho_u + rho_d

    densinfo_u = ValGrad(value=rho_u)
    densinfo_d = ValGrad(value=rho_d)
    densinfo = SpinParam(u=densinfo_u, d=densinfo_d)
    densinfo_tot = ValGrad(value=rho_tot)

    # calculate the energy and compare with analytic
    edens_unpol = xc.get_edensityxc(densinfo_tot)
    edens_unpol_true = lda_e_true(rho_tot)
    assert torch.allclose(edens_unpol, edens_unpol_true)

    edens_pol = xc.get_edensityxc(densinfo)
    edens_pol_true = 0.5 * (lda_e_true(2 * rho_u) + lda_e_true(2 * rho_d))
    assert torch.allclose(edens_pol, edens_pol_true)

    vxc_unpol = xc.get_vxc(densinfo_tot)
    vxc_unpol_value_true = lda_v_true(rho_tot)
    assert torch.allclose(vxc_unpol.value, vxc_unpol_value_true)

    vxc_pol = xc.get_vxc(densinfo)
    vxc_pol_u_value_true = lda_v_true(2 * rho_u)
    vxc_pol_d_value_true = lda_v_true(2 * rho_d)
    assert torch.allclose(vxc_pol.u.value, vxc_pol_u_value_true)
    assert torch.allclose(vxc_pol.d.value, vxc_pol_d_value_true)
Exemple #13
0
    def _dm2densinfo(self, dm, family):
        # dm: (*BD, nao, nao)
        # family: 1 for LDA, 2 for GGA, 3 for MGGA
        # self.basis: (nao, ngrid)
        if isinstance(dm, SpinParam):
            res_u = self._dm2densinfo(dm.u, family)
            res_d = self._dm2densinfo(dm.d, family)
            return SpinParam(u=res_u, d=res_d)
        else:
            dens = self._get_dens_at_grid(dm)

            # calculate the density gradient
            gdens = None
            if family >= 2:  # requires gradient
                # (*BD, ngrid, ndim)
                # dm is multiplied by 2 because n(r) = sum (D_ij * phi_i * phi_j), thus
                # d.n(r) = sum (D_ij * d.phi_i * phi_j + D_ij * phi_i * d.phi_j)
                gdens = self._get_grad_dens_at_grid(dm)

            # TODO: implement the density laplacian

            # dens: (*BD, ngrid)
            # gdens: (*BD, ngrid, ndim)
            res = ValGrad(value=dens, grad=gdens)
            return res
Exemple #14
0
    def get_vxc(self, dm):
        # dm: (*BD, nao, nao)
        assert self.xc is not None, "Please call .setup_grid with the xc object"

        densinfo = self._dm2densinfo(dm, self.xc.family)  # value: (*BD, nr)
        potinfo = self.xc.get_vxc(densinfo)  # value: (*BD, nr)

        if isinstance(dm, torch.Tensor):  # unpolarized case
            # get the linear operator from the potential
            vxc_linop = self.get_vext(potinfo.value)
            if self.xcfamily >= 2:  # GGA or MGGA
                assert potinfo.grad is not None
                vxc_linop = vxc_linop + self.get_grad_vext(potinfo.grad)
            if self.xcfamily >= 3:  # MGGA
                assert potinfo.lapl is not None
                vxc_linop = vxc_linop + self.get_lapl_vext(potinfo.lapl)

            return vxc_linop

        else:  # polarized case
            # get the linear operator from the potential
            vxc_linop_u = self.get_vext(potinfo.u.value)
            vxc_linop_d = self.get_vext(potinfo.d.value)
            if self.xcfamily >= 2:  # GGA or MGGA
                assert potinfo.u.grad is not None
                assert potinfo.d.grad is not None
                vxc_linop_u = vxc_linop_u + self.get_grad_vext(potinfo.u.grad)
                vxc_linop_d = vxc_linop_d + self.get_grad_vext(potinfo.d.grad)
            if self.xcfamily >= 3:  # MGGA
                assert potinfo.u.lapl is not None
                assert potinfo.d.lapl is not None
                vxc_linop_u = vxc_linop_u + self.get_lapl_vext(potinfo.u.lapl)
                vxc_linop_d = vxc_linop_d + self.get_lapl_vext(potinfo.d.lapl)

            return SpinParam(u=vxc_linop_u, d=vxc_linop_d)
Exemple #15
0
Fichier : hf.py Projet : diffqc/dqc
    def __init__(self,
                 system: BaseSystem,
                 restricted: Optional[bool] = None,
                 build_grid_if_necessary: bool = False):

        # decide if this is restricted or not
        if restricted is None:
            self._polarized = bool(system.spin != 0)
        else:
            self._polarized = not restricted

        # construct the grid if the system requires it
        if build_grid_if_necessary and system.requires_grid():
            system.setup_grid()
            system.get_hamiltonian().setup_grid(system.get_grid())

        # build the basis
        self._hamilton = system.get_hamiltonian().build()
        self._system = system

        # get the orbital info
        self._orb_weight = system.get_orbweight(
            polarized=self._polarized)  # (norb,)
        self._norb = SpinParam.apply_fcn(
            lambda orb_weight: int(orb_weight.shape[-1]), self._orb_weight)

        # set up the 1-electron linear operator
        self._core1e_linop = self._hamilton.get_kinnucl(
        )  # kinetic and nuclear
Exemple #16
0
 def get_orbweight(
     self,
     polarized: bool = False
 ) -> Union[torch.Tensor, SpinParam[torch.Tensor]]:
     if not polarized:
         return self._orb_weights
     else:
         return SpinParam(u=self._orb_weights_u, d=self._orb_weights_d)
Exemple #17
0
    def get_vxc(self, densinfo):
        avxc = self.a.get_vxc(densinfo)
        bvxc = self.b.get_vxc(densinfo)

        if isinstance(densinfo, ValGrad):
            return avxc + bvxc
        else:
            return SpinParam(u=avxc.u + bvxc.u, d=avxc.d + bvxc.d)
Exemple #18
0
Fichier : hf.py Projet : diffqc/dqc
 def dm2energy(
         self, dm: Union[torch.Tensor,
                         SpinParam[torch.Tensor]]) -> torch.Tensor:
     # calculate the energy given the density matrix
     dmtot = SpinParam.sum(dm)
     e_core = self._hamilton.get_e_hcore(dmtot)
     e_elrep = self._hamilton.get_e_elrep(dmtot)
     e_exch = self._hamilton.get_e_exchange(dm)
     return e_core + e_elrep + e_exch + self._system.get_nuclei_energy()
Exemple #19
0
Fichier : hf.py Projet : diffqc/dqc
 def unpack_aoparams(
     self, aoparams: torch.Tensor
 ) -> Union[torch.Tensor, SpinParam[torch.Tensor]]:
     # if polarized, then construct the SpinParam (reverting the pack_aoparams)
     if isinstance(self._norb, SpinParam):
         return SpinParam(u=aoparams[..., :self._norb.u],
                          d=aoparams[..., self._norb.u:])
     else:
         return aoparams
Exemple #20
0
Fichier : hf.py Projet : diffqc/dqc
 def diagonalize(self, fock, norb):
     ovlp = self._hamilton.get_overlap()
     if isinstance(fock, SpinParam):
         assert isinstance(self._norb, SpinParam)
         eivals_u, eivecs_u = xitorch.linalg.lsymeig(A=fock.u,
                                                     neig=norb.u,
                                                     M=ovlp,
                                                     **self.eigen_options)
         eivals_d, eivecs_d = xitorch.linalg.lsymeig(A=fock.d,
                                                     neig=norb.d,
                                                     M=ovlp,
                                                     **self.eigen_options)
         return SpinParam(u=eivals_u, d=eivals_d), SpinParam(u=eivecs_u,
                                                             d=eivecs_d)
     else:
         return xitorch.linalg.lsymeig(A=fock,
                                       neig=norb,
                                       M=ovlp,
                                       **self.eigen_options)
Exemple #21
0
def _postproc_libxc_voutput(densinfo: Union[SpinParam[ValGrad], ValGrad],
                            vrho: torch.Tensor,
                            vsigma: Optional[torch.Tensor] = None,
                            vlapl: Optional[torch.Tensor] = None,
                            vkin: Optional[torch.Tensor] = None) -> Union[SpinParam[ValGrad], ValGrad]:
    # postprocess the output from libxc's 1st derivative into derivative
    # suitable for valgrad
    # densinfo.value: (..., nr)
    # densinfo.grad: (..., ndim, nr)

    # polarized case
    if isinstance(densinfo, SpinParam):
        # vrho: (2, *BD, nr)
        # vsigma: (3, *BD, nr)
        # vlapl: (2, *BD, nr)
        # vkin: (2, *BD, nr)
        vrho_u = vrho[0]
        vrho_d = vrho[1]

        # calculate the gradient potential
        vgrad_u: Optional[torch.Tensor] = None
        vgrad_d: Optional[torch.Tensor] = None
        if vsigma is not None:
            # calculate the grad_vxc
            vgrad_u = 2 * vsigma[0].unsqueeze(-2) * densinfo.u.grad + \
                vsigma[1].unsqueeze(-2) * densinfo.d.grad  # (..., 3)
            vgrad_d = 2 * vsigma[2].unsqueeze(-2) * densinfo.d.grad + \
                vsigma[1].unsqueeze(-2) * densinfo.u.grad

        vlapl_u: Optional[torch.Tensor] = None
        vlapl_d: Optional[torch.Tensor] = None
        if vlapl is not None:
            vlapl_u = vlapl[0]
            vlapl_d = vlapl[1]

        vkin_u: Optional[torch.Tensor] = None
        vkin_d: Optional[torch.Tensor] = None
        if vkin is not None:
            vkin_u = vkin[0]
            vkin_d = vkin[1]

        potinfo_u = ValGrad(value=vrho_u, grad=vgrad_u, lapl=vlapl_u, kin=vkin_u)
        potinfo_d = ValGrad(value=vrho_d, grad=vgrad_d, lapl=vlapl_d, kin=vkin_d)
        return SpinParam(u=potinfo_u, d=potinfo_d)

    # unpolarized case
    else:
        # all are: (*BD, nr)

        # calculate the gradient potential
        if vsigma is not None:
            vsigma = 2 * vsigma.unsqueeze(-2) * densinfo.grad  # (*BD, ndim, nr)

        potinfo = ValGrad(value=vrho, grad=vsigma, lapl=vlapl, kin=vkin)
        return potinfo
Exemple #22
0
Fichier : hf.py Projet : diffqc/dqc
 def aoparams2dm(self, aoparams: torch.Tensor, aocoeffs: torch.Tensor,
                 with_penalty: Optional[float] = None) -> \
         Tuple[Union[torch.Tensor, SpinParam[torch.Tensor]], Optional[torch.Tensor]]:
     # convert the aoparams to density matrix and penalty factor
     aop = self.unpack_aoparams(aoparams)  # tensor or SpinParam of tensor
     aoc = self.unpack_aoparams(aocoeffs)  # tensor or SpinParam of tensor
     dm_penalty = SpinParam.apply_fcn(
         lambda aop, aoc, orb_weight: self._hamilton.ao_orb_params2dm(
             aop, aoc, orb_weight, with_penalty=with_penalty), aop, aoc,
         self._orb_weight)
     if with_penalty is not None:
         dm = SpinParam.apply_fcn(lambda dm_penalty: dm_penalty[0],
                                  dm_penalty)
         penalty: Optional[torch.Tensor] = SpinParam.sum(
             SpinParam.apply_fcn(lambda dm_penalty: dm_penalty[1],
                                 dm_penalty))
     else:
         dm = dm_penalty
         penalty = None
     return dm, penalty
Exemple #23
0
Fichier : hf.py Projet : diffqc/dqc
 def scp2dm(
         self,
         scp: torch.Tensor) -> Union[torch.Tensor, SpinParam[torch.Tensor]]:
     # scp is like KS, using the concatenated Fock matrix
     if not self._polarized:
         fock = xt.LinearOperator.m(_symm(scp), is_hermitian=True)
         return self.__fock2dm(fock)
     else:
         fock_u = xt.LinearOperator.m(_symm(scp[0]), is_hermitian=True)
         fock_d = xt.LinearOperator.m(_symm(scp[1]), is_hermitian=True)
         return self.__fock2dm(SpinParam(u=fock_u, d=fock_d))
Exemple #24
0
    def __dm2fock(self, dm):
        if isinstance(dm, torch.Tensor):
            # construct the fock matrix from the density matrix
            elrep = self.hamilton.get_elrep(dm)  # (..., nao, nao)
            vxc = self.hamilton.get_vxc(dm)
            return self.knvext_linop + elrep + vxc
        else:
            elrep = self.hamilton.get_elrep(dm.u + dm.d)  # (..., nao, nao)
            vext_elrep = self.knvext_linop + elrep

            vxc_ud = self.hamilton.get_vxc(dm)
            return SpinParam(u=vext_elrep + vxc_ud.u, d=vext_elrep + vxc_ud.d)
Exemple #25
0
 def __fock2dm(self, fock):
     # diagonalize the fock matrix and obtain the density matrix
     eigvals, eigvecs = self.__diagonalize(fock)
     if isinstance(eigvecs, torch.Tensor):  # unpolarized
         assert isinstance(self.orb_weight, torch.Tensor)
         dm = self.hamilton.ao_orb2dm(eigvecs, self.orb_weight)
         return dm
     else:  # polarized
         assert isinstance(self.orb_weight, SpinParam)
         dm_u = self.hamilton.ao_orb2dm(eigvecs.u, self.orb_weight.u)
         dm_d = self.hamilton.ao_orb2dm(eigvecs.d, self.orb_weight.d)
         return SpinParam(u=dm_u, d=dm_d)
Exemple #26
0
    def run(self, dm0: Optional[Union[torch.Tensor, SpinParam[torch.Tensor]]] = None,  # type: ignore
            eigen_options: Optional[Mapping[str, Any]] = None,
            fwd_options: Optional[Mapping[str, Any]] = None,
            bck_options: Optional[Mapping[str, Any]] = None) -> BaseQCCalc:

        # setup the default options
        if eigen_options is None:
            eigen_options = {
                "method": "exacteig"
            }
        if fwd_options is None:
            fwd_options = {
                "method": "broyden1",
                "alpha": -0.5,
                "maxiter": 50,
                # "verbose": True,
            }
        if bck_options is None:
            bck_options = {
                # NOTE: it seems like in most cases the jacobian matrix is posdef
                # if it is not the case, we can just remove the line below
                "posdef": True,
                # "verbose": True,
            }

        # save the eigen_options for use in diagonalization
        self.engine.set_eigen_options(eigen_options)

        # set up the initial self-consistent param guess
        if dm0 is None:
            if not self.polarized:
                dm0 = torch.zeros(self.shape, dtype=self.dtype,
                                  device=self.device)
            else:
                dm0_u = torch.zeros(self.shape, dtype=self.dtype,
                                    device=self.device)
                dm0_d = torch.zeros(self.shape, dtype=self.dtype,
                                    device=self.device)
                dm0 = SpinParam(u=dm0_u, d=dm0_d)

        scp0 = self.engine.dm2scp(dm0)

        # do the self-consistent iteration
        scp = xitorch.optimize.equilibrium(
            fcn=self.engine.scp2scp,
            y0=scp0,
            bck_options={**bck_options},
            **fwd_options)

        # post-process parameters
        self._dm = self.engine.scp2dm(scp)
        self.has_run = True
        return self
Exemple #27
0
Fichier : ks.py Projet : diffqc/dqc
 def dm2energy(
         self, dm: Union[torch.Tensor,
                         SpinParam[torch.Tensor]]) -> torch.Tensor:
     # calculate the energy given the density matrix
     dmtot = SpinParam.sum(dm)
     e_core = self.hamilton.get_e_hcore(dmtot)
     e_elrep = self.hamilton.get_e_elrep(dmtot)
     if self.xc is not None:
         e_xc: Union[torch.Tensor, float] = self.hamilton.get_e_xc(dm)
     else:
         e_xc = 0.0
     return e_core + e_elrep + e_xc + self._system.get_nuclei_energy()
Exemple #28
0
    def scp2dm(self, scp: torch.Tensor) -> Union[torch.Tensor, SpinParam[torch.Tensor]]:
        # convert the self-consistent parameter (scp) to the density matrix
        def _symm(scp: torch.Tensor):
            # forcely symmetrize the tensor
            return (scp + scp.transpose(-2, -1)) * 0.5

        if not self._polarized:
            fock = xt.LinearOperator.m(_symm(scp), is_hermitian=True)
            return self.__fock2dm(fock)
        else:
            fock_u = xt.LinearOperator.m(_symm(scp[0]), is_hermitian=True)
            fock_d = xt.LinearOperator.m(_symm(scp[1]), is_hermitian=True)
            return self.__fock2dm(SpinParam(u=fock_u, d=fock_d))
Exemple #29
0
 def _get_zero_dm(self) -> Union[SpinParam[torch.Tensor], torch.Tensor]:
     # get the initial dm that are all zeros
     if not self._polarized:
         return torch.zeros(self._shape,
                            dtype=self.dtype,
                            device=self.device)
     else:
         dm0_u = torch.zeros(self._shape,
                             dtype=self.dtype,
                             device=self.device)
         dm0_d = torch.zeros(self._shape,
                             dtype=self.dtype,
                             device=self.device)
         return SpinParam(u=dm0_u, d=dm0_d)
Exemple #30
0
def test_xc_default_vxc(xccls, libxcname):
    # test if the default vxc implementation is correct, compared to libxc

    dtype = torch.float64
    xc = xccls()
    libxc = get_libxc(libxcname)

    torch.manual_seed(123)
    n = 100
    rho_u = torch.rand((n, ), dtype=dtype)
    rho_d = torch.rand((n, ), dtype=dtype)
    rho_tot = rho_u + rho_d
    gradn_u = torch.rand((n, 3), dtype=dtype) * 0
    gradn_d = torch.rand((n, 3), dtype=dtype) * 0
    gradn_tot = gradn_u + gradn_d

    densinfo_u = ValGrad(value=rho_u, grad=gradn_u)
    densinfo_d = ValGrad(value=rho_d, grad=gradn_d)
    densinfo_tot = densinfo_u + densinfo_d
    densinfo = SpinParam(u=densinfo_u, d=densinfo_d)

    def assert_valgrad(vg1, vg2):
        assert torch.allclose(vg1.value, vg2.value)
        assert (vg1.grad is None) == (vg2.grad is None)
        assert (vg1.lapl is None) == (vg2.lapl is None)
        if vg1.grad is not None:
            assert torch.allclose(vg1.grad, vg2.grad)
        if vg1.lapl is not None:
            assert torch.allclose(vg1.lapl, vg2.lapl)

    # check if the energy is the same (implementation check)
    xc_edens_unpol = xc.get_edensityxc(densinfo_tot)
    lxc_edens_unpol = libxc.get_edensityxc(densinfo_tot)
    assert torch.allclose(xc_edens_unpol, lxc_edens_unpol)

    xc_edens_pol = xc.get_edensityxc(densinfo)
    lxc_edens_pol = libxc.get_edensityxc(densinfo)
    assert torch.allclose(xc_edens_pol, lxc_edens_pol)

    # calculate the potential and compare with analytical expression
    xcpotinfo_unpol = xc.get_vxc(densinfo_tot)
    lxcpotinfo_unpol = libxc.get_vxc(densinfo_tot)
    assert_valgrad(xcpotinfo_unpol, lxcpotinfo_unpol)

    xcpotinfo_pol = xc.get_vxc(densinfo)
    lxcpotinfo_pol = libxc.get_vxc(densinfo)
    # print(type(xcpotinfo_pol), type(lxcpotinfo_unpol))
    assert_valgrad(xcpotinfo_pol.u, lxcpotinfo_pol.u)
    assert_valgrad(xcpotinfo_pol.d, lxcpotinfo_pol.d)