<commit_before>from __future__ import print_function

import numpy as np

from sisl.physics import get_distribution
from sisl.utils import strmap
from sisl.utils.cmd import default_ArgumentParser, default_namespace
from sisl.unit.siesta import units
from ..sile import add_sile, sile_fh_open, SileError
from .sile import *
from .kp import kpSileSiesta


__all__ = ['eigSileSiesta']


class eigSileSiesta(SileSiesta):
    """ Eigenvalues as calculated in the SCF loop, easy plots using `sdata`

    The .EIG file from Siesta contains the eigenvalues for k-points used during the SCF.
    Using the command-line utility `sdata` one may plot the eigenvalue spectrum to visualize the
    spread of eigenvalues.

    .. code:: bash

        sdata siesta.EIG --plot
        # or to save to png file
        sdata siesta.EIG --plot eig_spread.png


    One may also plot the DOS using the eigenvalues and the k-point weights:

    .. code:: bash

        sdata siesta.EIG --dos
        # or to save to png file
        sdata siesta.EIG --dos dos.png

    This will default to plot the DOS using these parameters:
    dE = 5 meV, kT = 300 K (25 meV), Gaussian distribution and in the full energy-range of the eigenvalue spectrum.
    By default the k-point weights will be read in the *.KP file, however if the file does not
    exist one may use the option ``--kp-file FILE`` to read in the weights from ``FILE``.

    To limit the shown energy region, simply use:

    .. code:: bash

        sdata siesta.EIG -E -10:10 --dos

    to reduce to the -10 eV to 10 eV energy range.

    One may optionally choose the temperature smearing and the used distribution function:

    .. code:: bash

        sdata siesta.EIG -E -10:10 --dos 0.01 0.1 lorentzian

    which will calculate the DOS in steps of 10 meV, the temperature smearing is 0.1 eV and
    the used distribution is a Lorentzian.
    """

    @sile_fh_open()
    def read_data(self):
        r""" Read eigenvalues, as calculated and written by Siesta

        Returns
        -------
        numpy.ndarray : all eigenvalues, shifted to :math:`E_F = 0`, shape ``(ns, nk, no)``
                        where ``ns`` number of spin-components, ``nk`` number of k-points and
                        ``no`` number of orbitals.
        """

        # Luckily the data is in eV
        Ef = float(self.readline())

        # Read the total length of the path
        no, ns, nk = map(int, self.readline().split())
        if ns > 2:
            # This is simply a NC/SOC calculation which is irrelevant in
            # regards of the eigenvalues.
            ns = 1
        # Now we may read the eigenvalues

        # Allocate
        eigs = np.empty([ns, nk, no], np.float32)

        for ik in range(nk):
            # The first line is special
            E = list(map(float, self.readline().split()[1:]))
            s = 0
            e = len(E)
            tmp_E = np.empty([ns*no], np.float32)
            tmp_E[s:e] = E
            for _ in range(ns):
                while e < ns*no:
                    E = list(map(float, self.readline().split()))
                    s = e
                    e += len(E)
                    tmp_E[s:e] = E
            tmp_E.shape = (ns, no)
            eigs[:, ik, :] = tmp_E

        return eigs - Ef

    @default_ArgumentParser(description="Manipulate Siesta EIG file.")
    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

        # 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.
        d = {
            "_eigs": self.read_data(),
            "_Emap": None,
        }
        namespace = default_namespace(**d)<commit_msg>maint: small performance improvement in eig reads<commit_after>from __future__ import print_function

import numpy as np

from sisl.physics import get_distribution
from sisl.utils import strmap
from sisl.utils.cmd import default_ArgumentParser, default_namespace
from sisl.unit.siesta import units
from ..sile import add_sile, sile_fh_open, SileError
from .sile import *
from .kp import kpSileSiesta


__all__ = ['eigSileSiesta']


class eigSileSiesta(SileSiesta):
    """ Eigenvalues as calculated in the SCF loop, easy plots using `sdata`

    The .EIG file from Siesta contains the eigenvalues for k-points used during the SCF.
    Using the command-line utility `sdata` one may plot the eigenvalue spectrum to visualize the
    spread of eigenvalues.

    .. code:: bash

        sdata siesta.EIG --plot
        # or to save to png file
        sdata siesta.EIG --plot eig_spread.png


    One may also plot the DOS using the eigenvalues and the k-point weights:

    .. code:: bash

        sdata siesta.EIG --dos
        # or to save to png file
        sdata siesta.EIG --dos dos.png

    This will default to plot the DOS using these parameters:
    dE = 5 meV, kT = 300 K (25 meV), Gaussian distribution and in the full energy-range of the eigenvalue spectrum.
    By default the k-point weights will be read in the *.KP file, however if the file does not
    exist one may use the option ``--kp-file FILE`` to read in the weights from ``FILE``.

    To limit the shown energy region, simply use:

    .. code:: bash

        sdata siesta.EIG -E -10:10 --dos

    to reduce to the -10 eV to 10 eV energy range.

    One may optionally choose the temperature smearing and the used distribution function:

    .. code:: bash

        sdata siesta.EIG -E -10:10 --dos 0.01 0.1 lorentzian

    which will calculate the DOS in steps of 10 meV, the temperature smearing is 0.1 eV and
    the used distribution is a Lorentzian.
    """

    @sile_fh_open()
    def read_data(self):
        r""" Read eigenvalues, as calculated and written by Siesta

        Returns
        -------
        numpy.ndarray : all eigenvalues, shifted to :math:`E_F = 0`, shape ``(ns, nk, no)``
                        where ``ns`` number of spin-components, ``nk`` number of k-points and
                        ``no`` number of orbitals.
        """

        # Luckily the data is in eV
        Ef = float(self.readline())

        # Read the total length of the path
        no, ns, nk = map(int, self.readline().split())
        if ns > 2:
            # This is simply a NC/SOC calculation which is irrelevant in
            # regards of the eigenvalues.
            ns = 1

        # Allocate
        eigs = np.empty([ns, nk, no], np.float32)

        readline = self.readline
        for ik in range(nk):
            # The first line is special
            E_list = list(map(float, readline().split()[1:]))
            for _ in range(ns):
                while len(E_list) < ns*no:
                    E_list.extend(list(map(float, readline().split())))
            eigs[:, ik, :] = np.asarray(E_list, np.float32).reshape(ns, no)

        return eigs - Ef

    @default_ArgumentParser(description="Manipulate Siesta EIG file.")
    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

        # 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.
        d = {
            "_eigs": self.read_data(),
            "_Emap": None,
        }
        namespace = default_namespace(**d)
{'fraction_non_alphanumeric': 0.08766716196136701, 'fraction_numerical': 0.00903912828132739, 'mean_word_length': 2.072270825408901, 'pattern_counts': {'":': 4, '<': 5, '<?xml version=': 0, '>': 5, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2603647', 'n_tokens_mistral': 2447, 'n_tokens_neox': 2253, 'n_words': 928}