Esempio n. 1
0
    def d2V(self, p, max_process_p=20):
        """Potential Hessian

        Compute electrostatic potential Hessian at a point or list of points,
        total and split by electronic and ionic contributions.

        Arguments:
            p {np.ndarray} -- List of points to compute potential Hessian at.

        Keyword Arguments:
            max_process_p {number} -- Max number of points processed at once.
                                      Lower to trade off speed for memory
                                      (default: {20})

        Returns:
            np.ndarray -- Total potential Hessian
            np.ndarray -- Electronic potential Hessian
            np.ndarray -- Ionic potential Hessian
        """

        # Return potential Hessian  at a point or a list of points

        p = np.array(p)
        if len(p.shape) == 1:
            p = p[None, :]  # Make it into a list of points

        # The point list is sliced for convenience, to avoid taking too much
        # memory
        N = p.shape[0]
        d2Ve = np.zeros((N, 3, 3))
        d2Vi = np.zeros((N, 3, 3))

        slices = make_process_slices(N, max_process_p)
        g2_mat = self._g_grid[:,
                              None, :, :, :] * self._g_grid[None, :, :, :, :]

        for s in slices:
            # Fourier transform kernel
            ftk = np.exp(1.0j *
                         np.tensordot(self._g_grid, p[s].T, axes=(0, 0)))
            d2ftk = -g2_mat[:, :, :, :, :, None] * ftk[None, None, :, :, :, :]
            # Compute the electronic potential
            d2Ve[s] = np.real(
                np.sum(
                    self._Ve_G[None, None, :, :, :, None] * d2ftk,
                    axis=(2, 3, 4),
                )).T
            # Now add the ionic one
            d2Vi[s] = np.real(
                np.sum(
                    self._Vi_G[None, None, :, :, :, None] * d2ftk,
                    axis=(2, 3, 4),
                )).T

        d2Ve *= _cK * cnst.e * 1e30  # Moving to SI units
        d2Vi *= _cK * cnst.e * 1e30

        d2V = d2Ve + d2Vi

        return d2V, d2Ve, d2Vi
Esempio n. 2
0
    def Hfine(self, p, contact=False, max_process_p=20):
        # Return hyperfine tensors at a point or a list of points
        p = np.array(p)
        if len(p.shape) == 1:
            p = p[None, :]  # Make it into a list of points

        # The point list is sliced for convenience, to avoid taking too much
        # memory
        N = p.shape[0]
        HT = np.zeros((N, 3, 3))

        slices = make_process_slices(N, max_process_p)

        for s in slices:
            # Fourier transform kernel
            ftk = np.exp(1.0j *
                         np.tensordot(self._g_grid, p[s].T, axes=(0, 0)))
            # Compute the electronic potential
            HT[s] = np.real(
                np.sum(self._dip_G[:, :, :, :, :, None] * ftk[None, None],
                       axis=(2, 3, 4))).T
            # And Fermi contact term
            if contact:
                fermi = np.real(
                    np.sum(self._spin_G[:, :, :, None] * ftk, axis=(0, 1, 2)))
                fermi *= _fermiT / (self._vol * np.prod(self._spin.shape))
                HT[s] += np.eye(3)[None, :, :] * fermi[:, None, None]

        return HT
Esempio n. 3
0
    def V(self, p, max_process_p=20):
        # Return potential at a point or list of points
        p = np.array(p)
        if len(p.shape) == 1:
            p = p[None, :]  # Make it into a list of points

        # The point list is sliced for convenience, to avoid taking too much
        # memory
        N = p.shape[0]
        Ve = np.zeros(N)
        Vi = np.zeros(N)

        slices = make_process_slices(N, max_process_p)

        for s in slices:
            # Fourier transform kernel
            ftk = np.exp(1.0j *
                         np.tensordot(self._g_grid, p[s].T, axes=(0, 0)))
            # Compute the electronic potential
            Ve[s] = np.real(
                np.sum(self._Ve_G[:, :, :, None] * ftk, axis=(0, 1, 2)))
            # Now add the ionic one
            Vi[s] = np.real(
                np.sum(self._Vi_G[:, :, :, :, None] * ftk[:, :, :, None],
                       axis=(0, 1, 2, 3)))

        Ve *= _cK * cnst.e * 1e10  # Moving to SI units
        Vi *= _cK * cnst.e * 1e10

        V = Ve + Vi

        return V, Ve, Vi
Esempio n. 4
0
    def rho(self, p, max_process_p=20):
        # Return charge density at a point or list of points
        p = np.array(p)
        if len(p.shape) == 1:
            p = p[None, :]  # Make it into a list of points

        # The point list is sliced for convenience, to avoid taking too much
        # memory
        N = p.shape[0]
        rhoe = np.zeros(N)
        rhoi = np.zeros(N)

        slices = make_process_slices(N, max_process_p)

        for s in slices:
            # Fourier transform kernel
            ftk = np.exp(1.0j *
                         np.tensordot(self._g_grid, p[s].T, axes=(0, 0)))
            rhoe[s] = np.real(
                np.sum(self._rhoe_G[:, :, :, None] * ftk, axis=(0, 1, 2)))
            rhoi[s] = np.real(
                np.sum(self._rhoi_G[:, :, :, :, None] * ftk[:, :, :, None],
                       axis=(0, 1, 2, 3)))

        # Convert units to e/Ang^3
        rhoe /= self._vol
        rhoi /= self._vol
        rho = rhoe + rhoi

        return rho, rhoe, rhoi
Esempio n. 5
0
    def Hfine(self, p, contact=False, max_process_p=20):
        """Hyperfine tensor

        Compute hyperfine tensor at a point or list of points. Only possible
        for electronic densities including spin polarisation.

        Arguments:
            p {np.ndarray} -- List of points to compute hyperfine tensor at.

        Keyword Arguments:
            contact {bool} -- If True, include Fermi contact term
                              (default: {False})
            max_process_p {number} -- Max number of points processed at once.
                                      Lower to trade off speed for memory
                                      (default: {20})

        Returns:
            np.ndarray -- Total hyperfine tensor
            np.ndarray -- Electronic hyperfine tensor
            np.ndarray -- Ionic hyperfine tensor

        Raises:
            RuntimeError -- If the electronic density is not spin polarised.
        """

        if not self.has_spin():
            raise RuntimeError("Can not compute hyperfine tensor without"
                               " spin polarised electronic density")

        # Return hyperfine tensors at a point or a list of points
        p = np.array(p)
        if len(p.shape) == 1:
            p = p[None, :]  # Make it into a list of points

        # The point list is sliced for convenience, to avoid taking too much
        # memory
        N = p.shape[0]
        HT = np.zeros((N, 3, 3))

        slices = make_process_slices(N, max_process_p)

        for s in slices:
            # Fourier transform kernel
            ftk = np.exp(1.0j *
                         np.tensordot(self._g_grid, p[s].T, axes=(0, 0)))
            # Compute the electronic potential
            HT[s] = np.real(
                np.sum(
                    self._dip_G[:, :, :, :, :, None] * ftk[None, None],
                    axis=(2, 3, 4),
                )).T
            # And Fermi contact term
            if contact:
                fermi = np.real(
                    np.sum(self._spin_G[:, :, :, None] * ftk, axis=(0, 1, 2)))
                fermi *= _fermiT / (self._vol * np.prod(self._spin.shape))
                HT[s] += np.eye(3)[None, :, :] * fermi[:, None, None]

        return HT
Esempio n. 6
0
    def d2Vpart(self, p, dr=None, max_process_p=20):
        # Return potential at a point or list of points
        p = np.array(p)
        if len(p.shape) == 1:
            p = p[None, :]  # Make it into a list of points

        # The point list is sliced for convenience, to avoid taking too much
        # memory
        N = p.shape[0]
        I = len(self.positions)
        d2Ve = np.zeros((3, 3, I, N))
        d2Vi = np.zeros((3, 3, I, N))

        if dr is None:
            dr = np.zeros((I, 3))
        else:
            dr = np.array(dr)
            if dr.shape != (I, 3):
                raise ValueError('Invalid ionic displacement vector')
        edr = np.exp(-1.0j * np.tensordot(self._g_grid, dr.T, axes=(0, 0)))

        slices = make_process_slices(N, max_process_p)

        for s in slices:
            # Fourier transform kernel
            ftk = np.exp(1.0j *
                         np.tensordot(self._g_grid, p[s].T, axes=(0, 0)))
            dftk = 1.0j * self._g_grid[:, :, :, :,
                                       None] * ftk[None, :, :, :, :]
            g2_mat = (self._g_grid[:, None, :, :, :] *
                      self._g_grid[None, :, :, :, :])
            d2ftk = -g2_mat[:, :, :, :, :, None] * ftk[None, None, :, :, :, :]
            # Compute the electronic potential
            d2Ve[:, :, :, s] = np.real(
                np.sum(self._Vpart_G[None, None, :, :, :, :, None] *
                       d2ftk[:, :, :, :, :, None, :] *
                       edr[None, None, :, :, :, :, None],
                       axis=(2, 3, 4)))
            # Now add the ionic one
            d2Vi[:, :, :, s] = np.real(
                np.sum(self._Vi_G[None, None, :, :, :, :, None] *
                       d2ftk[:, :, :, :, :, None] *
                       edr[None, None, :, :, :, :, None],
                       axis=(2, 3, 4)))

        # Swap axes for convenience
        d2Ve = np.moveaxis(d2Ve, 2, 0)
        d2Vi = np.moveaxis(d2Vi, 2, 0)

        d2Ve *= _cK * cnst.e * 1e30  # Moving to SI units
        d2Vi *= _cK * cnst.e * 1e30

        d2V = d2Ve + d2Vi

        return d2V, d2Ve, d2Vi
Esempio n. 7
0
    def V(self, p, max_process_p=20):
        """Potential

        Compute electrostatic potential at a point or list of points,
        total and split by electronic and ionic contributions.

        Arguments:
            p {np.ndarray} -- List of points to compute potential at.

        Keyword Arguments:
            max_process_p {number} -- Max number of points processed at once.
                                      Lower to trade off speed for memory
                                      (default: {20})

        Returns:
            np.ndarray -- Total potential
            np.ndarray -- Electronic potential
            np.ndarray -- Ionic potential
        """

        # Return potential at a point or list of points
        p = np.array(p)
        if len(p.shape) == 1:
            p = p[None, :]  # Make it into a list of points

        # The point list is sliced for convenience, to avoid taking too much
        # memory
        N = p.shape[0]
        Ve = np.zeros(N)
        Vi = np.zeros(N)

        slices = make_process_slices(N, max_process_p)

        for s in slices:
            # Fourier transform kernel
            ftk = np.exp(1.0j *
                         np.tensordot(self._g_grid, p[s].T, axes=(0, 0)))
            # Compute the electronic potential
            Ve[s] = np.real(
                np.sum(self._Ve_G[:, :, :, None] * ftk, axis=(0, 1, 2)))
            # Now add the ionic one
            Vi[s] = np.real(
                np.sum(self._Vi_G[:, :, :, None] * ftk, axis=(0, 1, 2)))

        Ve *= _cK * cnst.e * 1e10  # Moving to SI units
        Vi *= _cK * cnst.e * 1e10

        V = Ve + Vi

        return V, Ve, Vi
Esempio n. 8
0
    def rho(self, p, max_process_p=20):
        """Charge density

        Compute charge density at a point or list of points, total and
        split by electronic and ionic contributions.

        Arguments:
            p {np.ndarray} -- List of points to compute charge density at.

        Keyword Arguments:
            max_process_p {number} -- Max number of points processed at once.
                                      Lower to trade off speed for memory
                                      (default: {20})

        Returns:
            np.ndarray -- Total charge density
            np.ndarray -- Electronic charge density
            np.ndarray -- Ionic charge density
        """

        # Return charge density at a point or list of points
        p = np.array(p)
        if len(p.shape) == 1:
            p = p[None, :]  # Make it into a list of points

        # The point list is sliced for convenience, to avoid taking too much
        # memory
        N = p.shape[0]
        rhoe = np.zeros(N)
        rhoi = np.zeros(N)

        slices = make_process_slices(N, max_process_p)

        for s in slices:
            # Fourier transform kernel
            ftk = np.exp(1.0j *
                         np.tensordot(self._g_grid, p[s].T, axes=(0, 0)))
            rhoe[s] = np.real(
                np.sum(self._rhoe_G[:, :, :, None] * ftk, axis=(0, 1, 2)))
            rhoi[s] = np.real(
                np.sum(self._rhoi_G[:, :, :, None] * ftk, axis=(0, 1, 2)))

        # Convert units to e/Ang^3
        rhoe /= self._vol
        rhoi /= self._vol
        rho = rhoe + rhoi

        return rho, rhoe, rhoi
Esempio n. 9
0
    def Vpart(self, p, dr=None, max_process_p=20):
        # Return potential at a point or list of points
        p = np.array(p)
        if len(p.shape) == 1:
            p = p[None, :]  # Make it into a list of points

        # The point list is sliced for convenience, to avoid taking too much
        # memory
        N = p.shape[0]
        I = len(self.positions)
        Ve = np.zeros((I, N))
        Vi = np.zeros((I, N))

        if dr is None:
            dr = np.zeros((I, 3))
        else:
            dr = np.array(dr)
            if dr.shape != (I, 3):
                raise ValueError('Invalid ionic displacement vector')
        edr = np.exp(-1.0j * np.tensordot(self._g_grid, dr.T, axes=(0, 0)))

        slices = make_process_slices(N, max_process_p)

        for s in slices:
            # Fourier transform kernel
            ftk = np.exp(1.0j *
                         np.tensordot(self._g_grid, p[s].T, axes=(0, 0)))
            # Compute the electronic potential
            Ve[:, s] = np.real(
                np.sum(self._Vpart_G[:, :, :, :, None] *
                       ftk[:, :, :, None, :] * edr[:, :, :, :, None],
                       axis=(0, 1, 2)))
            # Now add the ionic one
            Vi[:, s] = np.real(
                np.sum(self._Vi_G[:, :, :, :, None] * ftk[:, :, :, None, :] *
                       edr[:, :, :, :, None],
                       axis=(0, 1, 2)))

        # Coulomb constant
        Ve *= _cK * cnst.e * 1e10  # Moving to SI units
        Vi *= _cK * cnst.e * 1e10

        V = Ve + Vi

        return V, Ve, Vi
Esempio n. 10
0
    def rhopart(self, p, dr=None, max_process_p=20):
        # Return partitioned charge density at a point or list of points
        p = np.array(p)
        if len(p.shape) == 1:
            p = p[None, :]  # Make it into a list of points

        # The point list is sliced for convenience, to avoid taking too much
        # memory
        N = p.shape[0]
        I = len(self.positions)
        rhoe = np.zeros((I, N))
        rhoi = np.zeros((I, N))

        if dr is None:
            dr = np.zeros((I, 3))
        else:
            dr = np.array(dr)
            if dr.shape != (I, 3):
                raise ValueError('Invalid ionic displacement vector')
        edr = np.exp(-1.0j * np.tensordot(self._g_grid, dr.T, axes=(0, 0)))

        slices = make_process_slices(N, max_process_p)

        for s in slices:
            # Fourier transform kernel
            ftk = np.exp(1.0j *
                         np.tensordot(self._g_grid, p[s].T, axes=(0, 0)))
            rhoe[:, s] = np.real(
                np.sum(self._rhopart_G[:, :, :, :, None] * ftk[:, :, :, None] *
                       edr[:, :, :, :, None],
                       axis=(0, 1, 2)))
            rhoi[:, s] = np.real(
                np.sum(self._rhoipart_G[:, :, :, :, None] *
                       ftk[:, :, :, None] * edr[:, :, :, :, None],
                       axis=(0, 1, 2)))
        # Convert units to e/Ang^3
        rhoe /= self._vol
        rhoi /= self._vol
        rho = rhoe + rhoi

        return rho, rhoe, rhoi
Esempio n. 11
0
    def d2V(self, p, max_process_p=20):
        # Return potential Hessian at a point or a list of points

        p = np.array(p)
        if len(p.shape) == 1:
            p = p[None, :]  # Make it into a list of points

        # The point list is sliced for convenience, to avoid taking too much
        # memory
        N = p.shape[0]
        d2Ve = np.zeros((N, 3, 3))
        d2Vi = np.zeros((N, 3, 3))

        slices = make_process_slices(N, max_process_p)
        g2_mat = (self._g_grid[:, None, :, :, :] *
                  self._g_grid[None, :, :, :, :])

        for s in slices:
            # Fourier transform kernel
            ftk = np.exp(1.0j *
                         np.tensordot(self._g_grid, p[s].T, axes=(0, 0)))
            d2ftk = -g2_mat[:, :, :, :, :, None] * ftk[None, None, :, :, :, :]
            # Compute the electronic potential
            d2Ve[s] = np.real(
                np.sum(self._Ve_G[None, None, :, :, :, None] * d2ftk,
                       axis=(2, 3, 4))).T
            # Now add the ionic one
            d2Vi[s] = np.real(
                np.sum(self._Vi_G[None, None, :, :, :, :, None] *
                       d2ftk[:, :, :, :, :, None],
                       axis=(2, 3, 4, 5))).T

        d2Ve *= _cK * cnst.e * 1e30  # Moving to SI units
        d2Vi *= _cK * cnst.e * 1e30

        d2V = d2Ve + d2Vi

        return d2V, d2Ve, d2Vi