Exemplo n.º 1
0
    def unfold_points(self, k):
        r""" Return a list of k-points to be evaluated for this objects unfolding

        The k-point `k` is with respect to the unfolded geometry.
        The return list of `k` points are the k-points required to be sampled in the
        folded geometry.

        Parameters
        ----------
        k : (3,) of float
           k-point evaluation corresponding to the unfolded unit-cell

        Returns
        -------
        k_unfold
            a list of ``np.prod(self.bloch)`` k-points used for the unfolding
        """
        k = _a.arrayd(k)

        # Create expansion points
        B = self._bloch
        unfold = _a.emptyd([B[2], B[1], B[0], 3])
        # Use B-casting rules (much simpler)
        unfold[:, :, :, 0] = (aranged(B[0]).reshape(1, 1, -1) + k[0]) / B[0]
        unfold[:, :, :, 1] = (aranged(B[1]).reshape(1, -1, 1) + k[1]) / B[1]
        unfold[:, :, :, 2] = (aranged(B[2]).reshape(-1, 1, 1) + k[2]) / B[2]
        # Back-transform shape
        return unfold.reshape(-1, 3)
Exemplo n.º 2
0
def test_oplist_math(op, key1, key2):
    d = {
        1: oplist([ar.aranged(1, 10), ar.aranged(1, 10)]),
        2: 2,
    }

    l1 = d[key1]
    l2 = d[key2]
    op(l1, l2)
Exemplo n.º 3
0
def test_oplist_imath(op, key):
    d = {
        1: oplist([ar.aranged(1, 10), ar.aranged(1, 10)]),
        2: 2,
    }

    l2 = d[key]
    try:
        l1 = oplist([data * 2 for data in l2])
    except:
        l1 = oplist([ar.aranged(1, 10), ar.aranged(2, 3)])
    op(l1, l2)
Exemplo n.º 4
0
    def grid(n, displ=0., size=1., trs=False):
        r""" Create a grid of `n` points with an offset of `displ` and sampling `size` around `displ`

        The :math:`k`-points are :math:`\Gamma` centered.

        Parameters
        ----------
        n : int
           number of points in the grid. If `trs` is ``True`` this may be smaller than `n`
        displ : float, optional
           the displacement of the grid
        size : float, optional
           the total size of the Brillouin zone to sample
        trs : bool, optional
           whether time-reversal-symmetry is applied

        Returns
        -------
        k : np.ndarray
           the list of k-points in the Brillouin zone to be sampled
        w : np.ndarray
           weights for the k-points
        """
        # First ensure that displ is in the Brillouin
        displ = displ % 1.
        if displ > 0.5:
            displ -= 1.
        if trs and displ == 0.:
            n_half = n // 2
            if n % 2 == 1:
                # Odd case, we have Gamma and remove all negative values
                k = _a.aranged(n_half + 1) * size / n + displ
                # Weights are all twice (except Gamma)
                w = _a.onesd(len(k)) / n * size
                w[1:] *= 2
            else:
                # Even case, we do not have Gamma, but we shift to Gamma
                # All points except Gamma and edge have weights doubled
                k = _a.aranged(n_half + 1) * size / n + displ
                # Weights are all twice (except Gamma and band-edge)
                w = _a.onesd(len(k)) / n * size
                w[1:-1] *= 2
        else:
            # Not TRS
            if n % 2 == 0:
                k = (_a.aranged(n) * 2 - n) * size / (2 * n) + displ
            else:
                k = (_a.aranged(n) * 2 - n + 1) * size / (2 * n) + displ
            w = _a.onesd(n) * size / n
        # Return values
        return k, w
Exemplo n.º 5
0
    def read_basis(self):
        """ Returns data associated with the ion.xml file """
        # Get the element-tree
        ET = ElementTree(None, self.file)
        root = ET.getroot()

        # Get number of orbitals
        label = root.find('label').text.strip()
        Z = int(root.find('z').text)  # atomic number
        mass = float(root.find('mass').text)

        # Read in the PAO's
        paos = root.find('paos')

        # Now loop over all orbitals
        orbital = []

        # All orbital data
        Bohr2Ang = unit_convert('Bohr', 'Ang')
        for orb in paos:

            n = int(orb.get('n'))
            l = int(orb.get('l'))
            z = int(orb.get('z'))  # zeta

            q0 = float(orb.get('population'))

            P = not int(orb.get('ispol')) == 0

            # Radial components
            rad = orb.find('radfunc')
            npts = int(rad.find('npts').text)

            # Grid spacing in Bohr (conversion is done later
            # because the normalization is easier)
            delta = float(rad.find('delta').text)

            # Read in data to a list
            dat = list(map(float, rad.find('data').text.split()))

            # Since the readed data has fewer significant digits we
            # might as well re-create the table of the radial component.
            r = aranged(npts) * delta

            # To get it per Ang**3
            # TODO, check that this is correct.
            # The fact that we have to have it normalized means that we need
            # to convert psi /sqrt(Bohr**3) -> /sqrt(Ang**3)
            # \int psi^\dagger psi == 1
            psi = arrayd(dat[1::2]) * r**l / Bohr2Ang**(3. / 2.)

            # Create the sphericalorbital and then the atomicorbital
            sorb = SphericalOrbital(l, (r * Bohr2Ang, psi), q0)

            # This will be -l:l (this is the way siesta does it)
            orbital.extend(sorb.toAtomicOrbital(n=n, Z=z, P=P))

        # Now create the atom and return
        return Atom(Z, orbital, mass=mass, tag=label)
Exemplo n.º 6
0
    def _index_shape_cuboid(self, cuboid):
        """ Internal routine for cuboid shape-indices """
        # Construct all points on the outer rim of the cuboids
        min_d = fnorm(self.dcell).min()

        # Retrieve cuboids edge-lengths
        v = cuboid.edge_length
        # Create normalized cuboid vectors (because we expan via the lengths below
        vn = cuboid._v / fnorm(cuboid._v).reshape(-1, 1)
        LL = (cuboid.center - cuboid._v.sum(0) / 2).reshape(1, 3)
        UR = (cuboid.center + cuboid._v.sum(0) / 2).reshape(1, 3)

        # Create coordinates
        a = vn[0, :].reshape(1, -1) * _a.aranged(0, v[0] + min_d, min_d).reshape(-1, 1)
        b = vn[1, :].reshape(1, -1) * _a.aranged(0, v[1] + min_d, min_d).reshape(-1, 1)
        c = vn[2, :].reshape(1, -1) * _a.aranged(0, v[2] + min_d, min_d).reshape(-1, 1)

        # Now create all sides
        sa = a.shape[0]
        sb = b.shape[0]
        sc = c.shape[0]

        def plane(v1, v2):
            return (v1.reshape(-1, 1, 3) + v2.reshape(1, -1, 3)).reshape(1, -1, 3)

        # Allocate for the 6 faces of the cuboid
        rxyz = _a.emptyd([2, sa * sb + sa * sc + sb * sc, 3])
        # Define the LL and UR
        rxyz[0, :, :] = LL
        rxyz[1, :, :] = UR

        i = 0
        rxyz[:, i:i + sa * sb, :] += plane(a, b)
        i += sa * sb
        rxyz[:, i:i + sa * sc, :] += plane(a, c)
        i += sa * sc
        rxyz[:, i:i + sb * sc, :] += plane(b, c)
        del a, b, c, sa, sb, sc
        rxyz.shape = (-1, 3)

        # Get all indices of the cuboid planes
        return self.index(rxyz)
Exemplo n.º 7
0
    def _index_shape(self, shape):
        """ Internal routine for shape-indices """
        # First grab the sphere, subsequent indices will be reduced
        # by the actual shape
        cuboid = shape.toCuboid()
        ellipsoid = shape.toEllipsoid()
        if ellipsoid.volume() > cuboid.volume():
            idx = self._index_shape_cuboid(cuboid)
        else:
            idx = self._index_shape_ellipsoid(ellipsoid)

        # Get min/max
        imin = idx.min(0)
        imax = idx.max(0)
        del idx

        dc = self.dcell

        # Now to find the actual points inside the shape
        # First create all points in the square and then retrieve all indices
        # within.
        ix = _a.aranged(imin[0], imax[0] + 0.5)
        iy = _a.aranged(imin[1], imax[1] + 0.5)
        iz = _a.aranged(imin[2], imax[2] + 0.5)
        output_shape = (ix.size, iy.size, iz.size, 3)
        rxyz = _a.emptyd(output_shape)
        ao = add.outer
        ao(ao(ix * dc[0, 0], iy * dc[1, 0]), iz * dc[2, 0], out=rxyz[:, :, :, 0])
        ao(ao(ix * dc[0, 1], iy * dc[1, 1]), iz * dc[2, 1], out=rxyz[:, :, :, 1])
        ao(ao(ix * dc[0, 2], iy * dc[1, 2]), iz * dc[2, 2], out=rxyz[:, :, :, 2])
        idx = shape.within_index(rxyz.reshape(-1, 3))
        del rxyz
        i = _a.emptyi(output_shape)
        i[:, :, :, 0] = ix.reshape(-1, 1, 1)
        i[:, :, :, 1] = iy.reshape(1, -1, 1)
        i[:, :, :, 2] = iz.reshape(1, 1, -1)
        del ix, iy, iz
        i.shape = (-1, 3)
        i = take(i, idx, axis=0)
        del idx

        return i
Exemplo n.º 8
0
    def read_basis(self):
        """ Returns data associated with the ion.xml file """
        no = len(self._dimension('norbs'))

        # Get number of orbitals
        label = self.Label.strip()
        Z = int(self.Atomic_number)
        mass = float(self.Mass)

        # Retrieve values
        orb_l = self._variable('orbnl_l')[:]  # angular quantum number
        orb_n = self._variable('orbnl_n')[:]  # principal quantum number
        orb_z = self._variable('orbnl_z')[:]  # zeta
        orb_P = self._variable(
            'orbnl_ispol')[:] > 0  # polarization shell, or not
        orb_q0 = self._variable('orbnl_pop')[:]  # q0 for the orbitals
        orb_delta = self._variable('delta')[:]  # delta for the functions
        orb_psi = self._variable('orb')[:, :]

        # Now loop over all orbitals
        orbital = []

        # All orbital data
        Bohr2Ang = unit_convert('Bohr', 'Ang')
        for io in range(no):

            n = orb_n[io]
            l = orb_l[io]
            z = orb_z[io]
            P = orb_P[io]

            # Grid spacing in Bohr (conversion is done later
            # because the normalization is easier)
            delta = orb_delta[io]

            # Since the readed data has fewer significant digits we
            # might as well re-create the table of the radial component.
            r = aranged(orb_psi.shape[1]) * delta

            # To get it per Ang**3
            # TODO, check that this is correct.
            # The fact that we have to have it normalized means that we need
            # to convert psi /sqrt(Bohr**3) -> /sqrt(Ang**3)
            # \int psi^\dagger psi == 1
            psi = orb_psi[io, :] * r**l / Bohr2Ang**(3. / 2.)

            # Create the sphericalorbital and then the atomicorbital
            sorb = SphericalOrbital(l, (r * Bohr2Ang, psi), orb_q0[io])

            # This will be -l:l (this is the way siesta does it)
            orbital.extend(sorb.toAtomicOrbital(n=n, Z=z, P=P))

        # Now create the atom and return
        return Atom(Z, orbital, mass=mass, tag=label)
Exemplo n.º 9
0
    def read_basis(self):
        """ Returns a set of atoms corresponding to the basis-sets in the nc file """
        if 'BASIS' not in self.groups:
            return None

        basis = self.groups['BASIS']
        atom = [None] * len(basis.groups)

        for a_str in basis.groups:
            a = basis.groups[a_str]

            if 'orbnl_l' not in a.variables:

                # Do the easy thing.

                # Get number of orbitals
                label = a.Label.strip()
                Z = int(a.Atomic_number)
                mass = float(a.Mass)

                i = int(a.ID) - 1
                atom[i] = Atom(Z, [-1] * a.Number_of_orbitals,
                               mass=mass,
                               tag=label)
                continue

            # Retrieve values
            orb_l = a.variables['orbnl_l'][:]  # angular quantum number
            orb_n = a.variables['orbnl_n'][:]  # principal quantum number
            orb_z = a.variables['orbnl_z'][:]  # zeta
            orb_P = a.variables[
                'orbnl_ispol'][:] > 0  # polarization shell, or not
            orb_q0 = a.variables['orbnl_pop'][:]  # q0 for the orbitals
            orb_delta = a.variables['delta'][:]  # delta for the functions
            orb_psi = a.variables['orb'][:, :]

            # Now loop over all orbitals
            orbital = []

            # Number of basis-orbitals (before m-expansion)
            no = len(a.dimensions['norbs'])

            # All orbital data
            for io in range(no):

                n = orb_n[io]
                l = orb_l[io]
                z = orb_z[io]
                P = orb_P[io]

                # Grid spacing in Bohr (conversion is done later
                # because the normalization is easier)
                delta = orb_delta[io]

                # Since the readed data has fewer significant digits we
                # might as well re-create the table of the radial component.
                r = aranged(orb_psi.shape[1]) * delta

                # To get it per Ang**3
                # TODO, check that this is correct.
                # The fact that we have to have it normalized means that we need
                # to convert psi /sqrt(Bohr**3) -> /sqrt(Ang**3)
                # \int psi^\dagger psi == 1
                psi = orb_psi[io, :] * r**l / Bohr2Ang**(3. / 2.)

                # Create the sphericalorbital and then the atomicorbital
                sorb = SphericalOrbital(l, (r * Bohr2Ang, psi), orb_q0[io])

                # This will be -l:l (this is the way siesta does it)
                orbital.extend(sorb.toAtomicOrbital(n=n, Z=z, P=P))

            # Get number of orbitals
            label = a.Label.strip()
            Z = int(a.Atomic_number)
            mass = float(a.Mass)

            i = int(a.ID) - 1
            atom[i] = Atom(Z, orbital, mass=mass, tag=label)
        return atom
Exemplo n.º 10
0
    def tile(self, reps, axis, normalize=False, offset=0):
        r"""Tile the state vectors for a new supercell

        Tiling a state vector makes use of the Bloch factors for a state by utilizing

        .. math::

           \psi_{\mathbf k}(\mathbf r + \mathbf T) \propto e^{i\mathbf k\cdot \mathbf T}

        where :math:`\mathbf T = i\mathbf a_0 + j\mathbf a_1 + l\mathbf a_2`. Note that `axis`
        selects which of the :math:`\mathbf a_i` vectors that are translated and `reps` corresponds
        to the :math:`i`, :math:`j` and :math:`l` variables. The `offset` moves the individual states
        by said amount, i.e. :math:`i\to i+\mathrm{offset}`.

        Parameters
        ----------
        reps : int
           number of repetitions along a specific lattice vector
        axis : int
           lattice vector to tile along
        normalize: bool, optional
           whether the states are normalized upon return, may be useful for
           eigenstates
        offset: float, optional
           the offset for the phase factors

        See Also
        --------
        Geometry.tile
        """
        # the parent gets tiled
        parent = self.parent.tile(reps, axis)
        # the k-point gets reduced
        k = _a.asarrayd(self.info.get("k", [0] * 3))

        # now tile the state vectors
        state = np.tile(self.state, (1, reps)).astype(np.complex128,
                                                      copy=False)
        # re-shape to apply phase-factors
        state.shape = (len(self), reps, -1)

        # Tiling stuff is trivial since we simply
        # translate the bloch coefficients with:
        #   exp(i k.T)
        # with T being
        #   i * a_0 + j * a_1 + k * a_2
        # We can leave out the lattice vectors entirely
        phase = exp(2j * _pi * k[axis] * (_a.aranged(reps) + offset))

        state *= phase.reshape(1, -1, 1)
        state.shape = (len(self), -1)

        # update new k; when we double the system, we halve the periodicity
        # and hence we need to account for this
        k[axis] = (k[axis] * reps % 1)
        while k[axis] > 0.5:
            k[axis] -= 1
        while k[axis] <= -0.5:
            k[axis] += 1

        # this allows us to make the same usable for StateC classes
        s = self.copy()
        s.parent = parent
        s.state = state
        # update the k-point
        s.info = dict(**self.info)
        s.info.update({'k': k})

        if normalize:
            return s.normalize()
        return s
Exemplo n.º 11
0
    def unfold(self, M, k_unfold):
        r""" Unfold the matrix list of matrices `M` into a corresponding k-point (unfolding k-points are `k_unfold`)

        Parameters
        ----------
        M : list of numpy arrays
            matrices used for unfolding
        k_unfold : (*, 3) of float
            unfolding k-points as returned by `Bloch.unfold_points`

        Returns
        -------
        M_unfold : unfolded matrix of size ``M[0].shape * k_unfold.shape[0] ** 2``
        """
        Bi, Bj, Bk = self.bloch
        # Retrieve shapes
        M0, M1 = M[0].shape
        shape = (Bk, Bj, Bi, M0, Bk, Bj, Bi, M1)
        Mshape = (M0, 1, 1, 1, M1)

        # Allocate the unfolded matrix
        Mu = zeros(shape, dtype=dtype_real_to_complex(M[0].dtype))

        # Use B-casting rules (much simpler)

        # Perform unfolding
        N = len(self)
        w = 1 / N
        if Bi == 1:
            if Bj == 1:
                K = aranged(Bk).reshape(1, Bk, 1, 1, 1)
                for T in range(N):
                    m = M[T].reshape(Mshape) * w
                    kjpi = 2j * pi * k_unfold[T, 2]
                    for k in range(Bk):
                        add(Mu[k, 0, 0], m * exp(kjpi * (K - k)), out=Mu[k, 0, 0])

            elif Bk == 1:
                J = aranged(Bj).reshape(1, 1, Bj, 1, 1)
                for T in range(N):
                    m = M[T].reshape(Mshape) * w
                    kjpi = 2j * pi * k_unfold[T, 1]
                    for j in range(Bj):
                        add(Mu[0, j, 0], m * exp(kjpi * (J - j)), out=Mu[0, j, 0])

            else:
                J = aranged(Bj).reshape(1, 1, Bj, 1, 1)
                K = aranged(Bk).reshape(1, Bk, 1, 1, 1)
                for T in range(N):
                    m = M[T].reshape(Mshape) * w
                    kpi = pi * k_unfold[T, :]
                    for k in range(Bk):
                        Kk = (K - k) * kpi[2]
                        for j in range(Bj):
                            add(Mu[k, j, 0], m * exp(2j * (Kk + kpi[1] * (J - j))), out=Mu[k, j, 0])
        elif Bj == 1:
            if Bk == 1:
                I = aranged(Bi).reshape(1, 1, 1, Bi, 1)
                for T in range(N):
                    m = M[T].reshape(Mshape) * w
                    kjpi = 2j * pi * k_unfold[T, 0]
                    for i in range(Bi):
                        add(Mu[0, 0, i], m * exp(kjpi * (I - i)), out=Mu[0, 0, i])

            else:
                I = aranged(Bi).reshape(1, 1, 1, Bi, 1)
                K = aranged(Bk).reshape(1, Bk, 1, 1, 1)
                for T in range(N):
                    m = M[T].reshape(Mshape) * w
                    kpi = pi * k_unfold[T, :]
                    for k in range(Bk):
                        Kk = (K - k) * kpi[2]
                        for i in range(Bi):
                            add(Mu[k, 0, i], m * exp(2j * (Kk + kpi[0] * (I - i))), out=Mu[k, 0, i])

        elif Bk == 1:
            I = aranged(Bi).reshape(1, 1, 1, Bi, 1)
            J = aranged(Bj).reshape(1, 1, Bj, 1, 1)
            for T in range(N):
                m = M[T].reshape(Mshape) * w
                kpi = pi * k_unfold[T, :]
                for j in range(Bj):
                    Jj = (J - j) * kpi[1]
                    for i in range(Bi):
                        add(Mu[0, j, i], m * exp(2j * (Jj + kpi[0] * (I - i))), out=Mu[0, j, i])

        else:
            I = aranged(Bi).reshape(1, 1, 1, Bi, 1)
            J = aranged(Bj).reshape(1, 1, Bj, 1, 1)
            K = aranged(Bk).reshape(1, Bk, 1, 1, 1)
            for T in range(N):
                m = M[T].reshape(Mshape) * w
                kpi = pi * k_unfold[T, :]
                for k in range(Bk):
                    Kk = (K - k) * kpi[2]
                    for j in range(Bj):
                        KkJj = Kk + (J - j) * kpi[1]
                        for i in range(Bi):
                            # Calculate phases and add for all expansions
                            add(Mu[k, j, i], m * exp(2j * (KkJj + kpi[0] * (I - i))), out=Mu[k, j, i])

        return Mu.reshape(N * M0, N * M1)
Exemplo n.º 12
0
Arquivo: basis.py Projeto: juijan/sisl
    def ArgumentParser(self, p=None, *args, **kwargs):
        """ Returns the arguments that is available for this Sile """
        #limit_args = kwargs.get('limit_arguments', True)
        short = kwargs.get('short', False)

        def opts(*args):
            if short:
                return args
            return [args[0]]

        # We limit the import to occur here
        import argparse

        Bohr2Ang = unit_convert('Bohr', 'Ang')
        Ry2eV = unit_convert('Bohr', 'Ang')

        # The first thing we do is adding the geometry to the NameSpace of the
        # parser.
        # This will enable custom actions to interact with the geometry in a
        # straight forward manner.
        # convert netcdf file to a dictionary
        ion_nc = PropertyDict()
        ion_nc.n = self._variable('orbnl_n')[:]
        ion_nc.l = self._variable('orbnl_l')[:]
        ion_nc.zeta = self._variable('orbnl_z')[:]
        ion_nc.pol = self._variable('orbnl_ispol')[:]
        ion_nc.orbital = self._variable('orb')[:]

        # this gets converted later
        delta = self._variable('delta')[:]
        r = aranged(ion_nc.orbital.shape[1]).reshape(1, -1) * delta.reshape(-1, 1)
        ion_nc.orbital *= r ** ion_nc.l.reshape(-1, 1) / Bohr2Ang * (3./2.)
        ion_nc.r = r * Bohr2Ang
        ion_nc.kb = PropertyDict()
        ion_nc.kb.n = self._variable('pjnl_n')[:]
        ion_nc.kb.l = self._variable('pjnl_l')[:]
        ion_nc.kb.e = self._variable('pjnl_ekb')[:] * Ry2eV
        ion_nc.kb.proj = self._variable('proj')[:]
        delta = self._variable('kbdelta')[:]
        r = aranged(ion_nc.kb.proj.shape[1]).reshape(1, -1) * delta.reshape(-1, 1)
        ion_nc.kb.proj *= r ** ion_nc.kb.l.reshape(-1, 1) / Bohr2Ang * (3./2.)
        ion_nc.kb.r = r * Bohr2Ang

        vna = self._variable('vna')
        r = aranged(vna[:].size) * vna.Vna_delta
        ion_nc.vna = PropertyDict()
        ion_nc.vna.v = vna[:] * Ry2eV * r / Bohr2Ang ** 3
        ion_nc.vna.r = r * Bohr2Ang

        # this is charge (not 1/sqrt(charge))
        chlocal = self._variable('chlocal')
        r = aranged(chlocal[:].size) * chlocal.Chlocal_delta
        ion_nc.chlocal = PropertyDict()
        ion_nc.chlocal.v = chlocal[:] * r / Bohr2Ang ** 3
        ion_nc.chlocal.r = r * Bohr2Ang

        vlocal = self._variable('reduced_vlocal')
        r = aranged(vlocal[:].size) * vlocal.Reduced_vlocal_delta
        ion_nc.vlocal = PropertyDict()
        ion_nc.vlocal.v = vlocal[:] * r / Bohr2Ang ** 3
        ion_nc.vlocal.r = r * Bohr2Ang

        if "core" in self.variables:
            # this is charge (not 1/sqrt(charge))
            core = self._variable('core')
            r = aranged(core[:].size) * core.Core_delta
            ion_nc.core = PropertyDict()
            ion_nc.core.v = core[:] * r / Bohr2Ang ** 3
            ion_nc.core.r = r * Bohr2Ang

        d = {
            "_data": ion_nc,
            "_kb_proj": False,
            "_l": True,
            "_n": True,
        }
        namespace = default_namespace(**d)

        # l-quantum number
        class lRange(argparse.Action):

            def __call__(self, parser, ns, value, option_string=None):
                value = (value
                         .replace("s", 0)
                         .replace("p", 1)
                         .replace("d", 2)
                         .replace("f", 3)
                         .replace("g", 4)
                )
                ns._l = strmap(int, value)[0]
        p.add_argument('-l',
                       action=lRange,
                       help='Denote the sub-section of l-shells that are plotted: "s,f"')

        # n quantum number
        class nRange(argparse.Action):

            def __call__(self, parser, ns, value, option_string=None):
                ns._n = strmap(int, value)[0]
        p.add_argument('-n',
                       action=nRange,
                       help='Denote the sub-section of n quantum numbers that are plotted: "2-4,6"')

        class Plot(argparse.Action):

            def __call__(self, parser, ns, value, option_string=None):
                import matplotlib.pyplot as plt

                # Retrieve values
                data = ns._data

                # We have these plots:
                #  - orbitals
                #  - projectors
                #  - chlocal
                #  - vna
                #  - vlocal
                #  - core (optional)

                # We'll plot them like this:
                #  orbitals | projectors
                #  vna + vlocal | chlocal + core
                #
                # Determine different n, l
                fig, axs = plt.subplots(2, 2)

                # Now plot different orbitals
                for n, l, zeta, pol, r, orb in zip(data.n, data.l, data.zeta,
                                                   data.pol, data.r, data.orbital):
                    if pol == 1:
                        pol = 'P'
                    else:
                        pol = ''
                    axs[0][0].plot(r, orb, label=f"n{n}l{l}Z{zeta}{pol}")
                axs[0][0].set_title("Orbitals")
                axs[0][0].set_xlabel("Distance [Ang]")
                axs[0][0].set_ylabel("Value [a.u.]")
                axs[0][0].legend()

                # plot projectors
                for n, l, e, r, proj in zip(
                        data.kb.n, data.kb.l, data.kb.e, data.kb.r, data.kb.proj):
                    axs[0][1].plot(r, proj, label=f"n{n}l{l} e={e:.5f}")
                axs[0][1].set_title("KB projectors")
                axs[0][1].set_xlabel("Distance [Ang]")
                axs[0][1].set_ylabel("Value [a.u.]")
                axs[0][1].legend()

                axs[1][0].plot(data.vna.r, data.vna.v, label='Vna')
                axs[1][0].plot(data.vlocal.r, data.vlocal.v, label='Vlocal')
                axs[1][0].set_title("Potentials")
                axs[1][0].set_xlabel("Distance [Ang]")
                axs[1][0].set_ylabel("Potential [eV]")
                axs[1][0].legend()

                axs[1][1].plot(data.chlocal.r, data.chlocal.v, label='Chlocal')
                if "core" in data:
                    axs[1][1].plot(data.core.r, data.core.v, label='core')
                axs[1][1].set_title("Charge")
                axs[1][1].set_xlabel("Distance [Ang]")
                axs[1][1].set_ylabel("Charge [Ang^3]")
                axs[1][1].legend()

                if value is None:
                    plt.show()
                else:
                    plt.savefig(value)
        p.add_argument(*opts('--plot', '-p'), action=Plot, nargs='?', metavar='FILE',
                       help='Plot the content basis set file, possibly saving plot to a file.')

        return p, namespace
Exemplo n.º 13
0
def test_oplist_single(op):
    d = oplist([ar.aranged(1, 10), ar.aranged(1, 10)])
    op(d)
Exemplo n.º 14
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)
Exemplo n.º 15
0
    def grid(cls, n, displ=0., size=1., centered=True, trs=False):
        r""" Create a grid of `n` points with an offset of `displ` and sampling `size` around `displ`

        The :math:`k`-points are :math:`\Gamma` centered.

        Parameters
        ----------
        n : int
           number of points in the grid. If `trs` is ``True`` this may be smaller than `n`
        displ : float, optional
           the displacement of the grid
        size : float, optional
           the total size of the Brillouin zone to sample
        centered : bool, optional
           if the points are centered
        trs : bool, optional
           whether time-reversal-symmetry is applied

        Returns
        -------
        k : np.ndarray
           the list of k-points in the Brillouin zone to be sampled
        w : np.ndarray
           weights for the k-points
        """
        # First ensure that displ is in the Brillouin
        displ = displ % 1.
        if displ > 0.5:
            displ -= 1.
        if displ < -0.5:
            displ += 1.

        # Centered _only_ has effect IFF
        #  displ == 0. and size == 1
        # Otherwise we resort to other schemes
        if displ != 0. or size != 1.:
            centered = False

        # We create the full grid, then afterwards we figure out TRS
        n_half = n // 2
        if n % 2 == 1:
            k = _a.aranged(-n_half, n_half + 1) * size / n + displ
        else:
            k = _a.aranged(-n_half, n_half) * size / n + displ
            if not centered:
                # Shift everything by halve the size each occupies
                k += size / (2 * n)

        # Move k to the primitive cell and generate weights
        k = cls.in_primitive(k)
        w = _a.onesd(n) * size / n

        # Check for TRS points
        if trs and np.any(k < 0.):
            # Make all positive to remove the double conting terms
            k_pos = np.abs(k)

            # Sort k-points and weights
            idx = np.argsort(k_pos)

            # Re-arange according to k value
            k_pos = k_pos[idx]
            w = w[idx]

            # Find indices of all equivalent k-points (tolerance of 1e-10 in reciprocal units)
            #  1e-10 ~ 1e10 k-points (no body will do this!)
            idx_same = (np.diff(k_pos) < 1e-10).nonzero()[0]

            # The above algorithm should never create more than two duplicates.
            # Hence we can simply remove all idx_same and double the weight for all
            # idx_same + 1.
            w[idx_same + 1] *= 2
            # Delete the duplicated k-points (they are already sorted)
            k = np.delete(k_pos, idx_same, axis=0)
            w = np.delete(w, idx_same)
        else:
            # Sort them, because it makes more visual sense
            idx = np.argsort(k)
            k = k[idx]
            w = w[idx]

        # Return values
        return k, w