Пример #1
0
    def psi(self, r, m=0):
        r""" Calculate :math:`\phi(\mathbf R)` at a given point (or more points)

        The position `r` is a vector from the origin of this orbital.

        Parameters
        -----------
        r : array_like of (:, 3)
           the vector from the orbital origin
        m : int, optional
           magnetic quantum number, must be in range ``-self.l <= m <= self.l``

        Returns
        -------
        numpy.ndarray
             basis function value at point `r`
        """
        r = _a.asarrayd(r)
        s = r.shape[:-1]
        # Convert to spherical coordinates
        n, idx, r, theta, phi = cart2spher(r,
                                           theta=m != 0,
                                           cos_phi=True,
                                           maxR=self.R)
        p = _a.zerosd(n)
        if len(idx) > 0:
            p[idx] = self.psi_spher(r, theta, phi, m, cos_phi=True)
            # Reduce memory immediately
            del idx, r, theta, phi
        p.shape = s
        return p
Пример #2
0
def test_spherical():
    rad2 = np.pi / 45
    r, theta, phi = np.ogrid[0.1:10:0.2, -np.pi:np.pi:rad2, 0:np.pi:rad2]
    xyz = spher2cart(r, theta, phi)
    s = xyz.shape[:-1]
    r1, theta1, phi1 = cart2spher(xyz)
    r1.shape = s
    theta1.shape = s
    phi1.shape = s
    assert np.allclose(r, r1)
    assert np.allclose(theta, theta1[1:2, :, 1:2])
    assert np.allclose(phi, phi1[0:1, 0:1, :])
Пример #3
0
        def _call(self, *args, **kwargs):
            data_axis = kwargs.pop('data_axis', None)
            grid_unit = kwargs.pop('grid_unit', 'b')

            func = getattr(self.parent, self._bz_attr)
            wrap = allow_kwargs('parent', 'k', 'weight')(kwargs.pop('wrap', _do_nothing))
            eta = tqdm_eta(len(self), self.__class__.__name__ + '.asgrid',
                           'k', kwargs.pop('eta', False))
            parent = self.parent
            k = self.k
            w = self.weight

            # Extract information from the MP grid, these values
            # define the Grid size, etc.
            diag = self._diag.copy()
            if not np.all(self._displ == 0):
                raise SislError(self.__class__.__name__ + '.{} requires the displacement to be 0 for all k-points.'.format(self._bz_attr))
            displ = self._displ.copy()
            size = self._size.copy()
            steps = size / diag
            if self._centered:
                offset = np.where(diag % 2 == 0, steps, steps / 2)
            else:
                offset = np.where(diag % 2 == 0, steps / 2, steps)

            # Instead of doing
            #    _in_primitive(k) + 0.5 - offset
            # we can do it here
            #    _in_primitive(k) + offset'
            offset -= 0.5

            # Check the TRS direction
            trs_axis = self._trs
            _in_primitive = self.in_primitive
            _rint = np.rint
            _int32 = np.int32
            def k2idx(k):
                # In case TRS is applied two indices may be returned
                return _rint((_in_primitive(k) - offset) / steps).astype(_int32)
                # To find the opposite k-point, do this
                #  idx[i] = [diag[i] - idx[i] - 1, idx[i]
                # with i in [0, 1, 2]

            # Create cell from the reciprocal cell.
            if grid_unit == 'b':
                cell = np.diag(self._size)
            else:
                cell = parent.sc.rcell * self._size.reshape(1, -1) / units('Ang', grid_unit)

            # Find the grid origo
            origo = -(cell * 0.5).sum(0)

            # Calculate first k-point (to get size and dtype)
            v = wrap(func(*args, k=k[0], **kwargs), parent=parent, k=k[0], weight=w[0])

            if data_axis is None:
                if v.size != 1:
                    raise SislError(self.__class__.__name__ + '.{} requires one value per-kpoint because of the 3D grid values'.format(self._bz_attr))

            else:

                # Check the weights
                weights = self.grid(diag[data_axis], displ[data_axis], size[data_axis],
                                    centered=self._centered, trs=trs_axis == data_axis)[1]

                # Correct the Grid size
                diag[data_axis] = len(v)
                # Create the orthogonal cell direction to ensure it is orthogonal
                # Since array axis is cyclic for negative numbers, we simply do this
                cell[data_axis, :] = cross(cell[data_axis-1, :], cell[data_axis-2, :])
                # Check whether we should rotate it
                if cart2spher(cell[data_axis, :])[2] > pi / 4:
                    cell[data_axis, :] *= -1

            # Correct cell for the grid
            if trs_axis >= 0:
                origo[trs_axis] = 0.
                # Correct offset since we only have the positive halve
                if self._diag[trs_axis] % 2 == 0 and not self._centered:
                    offset[trs_axis] = steps[trs_axis] / 2
                else:
                    offset[trs_axis] = 0.

                # Find number of points
                if trs_axis != data_axis:
                    diag[trs_axis] = len(self.grid(diag[trs_axis], displ[trs_axis], size[trs_axis],
                                                   centered=self._centered, trs=True)[1])

            # Create the grid in the reciprocal cell
            sc = SuperCell(cell, origo=origo)
            grid = Grid(diag, sc=sc, dtype=v.dtype)
            if data_axis is None:
                grid[k2idx(k[0])] = v
            else:
                idx = k2idx(k[0]).tolist()
                weight = weights[idx[data_axis]]
                idx[data_axis] = slice(None)
                grid[idx] = v * weight

            del v

            # Now perform calculation
            if data_axis is None:
                for i in range(1, len(k)):
                    grid[k2idx(k[i])] = wrap(func(*args, k=k[i], **kwargs),
                                             parent=parent, k=k[i], weight=w[i])
                    eta.update()
            else:
                for i in range(1, len(k)):
                    idx = k2idx(k[i]).tolist()
                    weight = weights[idx[data_axis]]
                    idx[data_axis] = slice(None)
                    grid[idx] = wrap(func(*args, k=k[i], **kwargs),
                                     parent=parent, k=k[i], weight=w[i]) * weight
                    eta.update()
            eta.close()
            return grid
Пример #4
0
    def param_circle(self, sc, N_or_dk, kR, normal, origo, loop=False):
        r""" Create a parameterized k-point list where the k-points are generated on a circle around an origo

        The generated circle is a perfect circle in the reciprocal space (Cartesian coordinates).
        To generate a perfect circle in units of the reciprocal lattice vectors one can
        generate the circle for a diagonal supercell with side-length :math:`2\pi`, see
        example below.

        Parameters
        ----------
        sc : SuperCell, or SuperCellChild
           the supercell used to construct the k-points
        N_or_dk : int
           number of k-points generated using the parameterization (if an integer),
           otherwise it specifies the discretization length on the circle (in 1/Ang),
           If the latter case will use less than 4 points a warning will be raised and
           the number of points increased to 4.
        kR : float
           radius of the k-point. In 1/Ang
        normal : array_like of float
           normal vector to determine the circle plane
        origo : array_like of float
           origo of the circle used to generate the circular parameterization
        loop : bool, optional
           whether the first and last point are equal

        Examples
        --------

        >>> sc = SuperCell([1, 1, 10, 90, 90, 60])
        >>> bz = BrillouinZone.param_circle(sc, 10, 0.05, [0, 0, 1], [1./3, 2./3, 0])

        To generate a circular set of k-points in reduced coordinates (reciprocal

        >>> sc = SuperCell([1, 1, 10, 90, 90, 60])
        >>> bz = BrillouinZone.param_circle(sc, 10, 0.05, [0, 0, 1], [1./3, 2./3, 0])
        >>> bz_rec = BrillouinZone.param_circle(2*np.pi, 10, 0.05, [0, 0, 1], [1./3, 2./3, 0])
        >>> bz.k[:, :] = bz_rec.k[:, :]

        Returns
        -------
        BrillouinZone : with the parameterized k-points.
        """
        if isinstance(N_or_dk, Integral):
            N = N_or_dk
        else:
            # Calculate the required number of points
            N = int(kR ** 2 * np.pi / N_or_dk + 0.5)
            if N < 4:
                N = 4
                info('BrillouinZone.param_circle increased the number of circle points to 4.')

        # Conversion object
        bz = BrillouinZone(sc)

        normal = _a.arrayd(normal)
        origo = _a.arrayd(origo)
        k_n = bz.tocartesian(normal)
        k_o = bz.tocartesian(origo)

        # Generate a preset list of k-points on the unit-circle
        if loop:
            radians = _a.aranged(N) / (N-1) * 2 * np.pi
        else:
            radians = _a.aranged(N) / N * 2 * np.pi
        k = _a.emptyd([N, 3])
        k[:, 0] = np.cos(radians)
        k[:, 1] = np.sin(radians)
        k[:, 2] = 0.

        # Now generate the rotation
        _, theta, phi = cart2spher(k_n)
        if theta != 0:
            pv = _a.arrayd([k_n[0], k_n[1], 0])
            pv /= fnorm(pv)
            q = Quaternion(phi, pv, rad=True) * Quaternion(theta, [0, 0, 1], rad=True)
        else:
            q = Quaternion(0., [0, 0, k_n[2] / abs(k_n[2])], rad=True)

        # Calculate k-points
        k = q.rotate(k)
        k *= kR / fnorm(k).reshape(-1, 1)
        k = bz.toreduced(k + k_o)

        # The sum of weights is equal to the BZ area
        W = np.pi * kR ** 2
        w = np.repeat([W / N], N)

        return BrillouinZone(sc, k, w)