Ejemplo n.º 1
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)
    def read_data(self, as_dataarray=False):
        r""" Returns data associated with the PDOS file

        For spin-polarized calculations the returned values are up/down, orbitals, energy.
        For non-collinear calculations the returned values are sum/x/y/z, orbitals, energy.

        Parameters
        ----------
        as_dataarray: bool, optional
           If True the returned PDOS is a `xarray.DataArray` with energy, spin
           and orbital information as coordinates in the data.
           The geometry, unit and Fermi level are stored as attributes in the
           DataArray.

        Returns
        -------
        geom : Geometry instance with positions, atoms and orbitals. The
               orbitals of these atoms are `AtomicOrbital` instances.
        E : the energies at which the PDOS has been evaluated at (if the Fermi-level is present the energies
            are shifted to :math:`E - E_F = 0`, this will *only* be done from Siesta 4.0.2 and later).
        PDOS : an array of DOS, for non-polarized calculations it has dimension ``(atom.no, len(E))``,
               else it has dimension ``(nspin, atom.no, len(E))``.
        DataArray : if `as_dataarray` is True, only this data array is returned, in this case
               all data can be post-processed using the `xarray` selection routines.
        """
        # Get the element-tree
        ET = ElementTree('pdos', self.file)
        root = ET.getroot()

        # Get number of orbitals
        nspin = int(root.find('nspin').text)
        # Try and find the fermi-level
        Ef = root.find('fermi_energy')
        E = arrayd(list(map(float, root.find('energy_values').text.split())))
        if Ef is not None:
            Ef = float(Ef.text)
            E -= Ef
        ne = len(E)

        # All coordinate, atoms and species data
        xyz = []
        atoms = []
        atom_species = []

        def ensure_size(ia):
            while len(atom_species) <= ia:
                atom_species.append(None)
                xyz.append(None)

        def ensure_size_orb(ia, i):
            while len(atoms) <= ia:
                atoms.append([])
            while len(atoms[ia]) <= i:
                atoms[ia].append(None)

        if nspin == 4:

            def process(D):
                tmp = np.empty(D.shape[0], D.dtype)
                tmp[:] = D[:, 3]
                D[:, 3] = D[:, 0] - D[:, 1]
                D[:, 0] = D[:, 0] + D[:, 1]
                D[:, 1] = D[:, 2]
                D[:, 2] = tmp[:]
                return D
        else:

            def process(D):
                return D

        if as_dataarray:
            import xarray as xr
            if nspin == 1:
                spin = ['sum']
            elif nspin == 2:
                spin = ['up', 'down']
            elif nspin == 4:
                spin = ['sum', 'x', 'y' 'z']

            # Dimensions of the PDOS data-array
            dims = ['E', 'spin', 'n', 'l', 'm', 'zeta', 'polarization']

            shape = (ne, nspin, 1, 1, 1, 1, 1)

            def to(o, DOS):
                # Coordinates for this dataarray
                coords = [E, spin, [o.n], [o.l], [o.m], [o.Z], [o.P]]

                return xr.DataArray(data=process(DOS).reshape(shape),
                                    dims=dims,
                                    coords=coords,
                                    name='PDOS')

        else:

            def to(o, DOS):
                return process(DOS)

        D = []
        for orb in root.findall('orbital'):

            # Short-hand function to retrieve integers for the attributes
            def oi(name):
                return int(orb.get(name))

            # Get indices
            ia = oi('atom_index') - 1
            i = oi('index') - 1

            species = orb.get('species')

            # Create the atomic orbital
            try:
                Z = oi('Z')
            except:
                try:
                    Z = PeriodicTable().Z(species)
                except:
                    # Unknown
                    Z = -1

            try:
                P = orb.get('P') == 'true'
            except:
                P = False

            ensure_size(ia)
            xyz[ia] = list(map(float, orb.get('position').split()))
            atom_species[ia] = Z

            # Construct the atomic orbital
            O = AtomicOrbital(n=oi('n'), l=oi('l'), m=oi('m'), Z=oi('z'), P=P)

            # We know that the index is far too high. However,
            # this ensures a consecutive orbital
            ensure_size_orb(ia, i)
            atoms[ia][i] = O

            # it is formed like : spin-1, spin-2 (however already in eV)
            DOS = arrayd(list(map(float,
                                  orb.find('data').text.split()))).reshape(
                                      -1, nspin)

            if as_dataarray:
                if len(D) == 0:
                    D = to(O, DOS)
                else:
                    D = D.combine_first(to(O, DOS))
            else:
                D.append(process(DOS))

        # Now we need to parse the data
        # First reduce the atom
        atoms = [[o for o in a if o] for a in atoms]
        atoms = Atoms([Atom(Z, os) for Z, os in zip(atom_species, atoms)])
        geom = Geometry(arrayd(xyz) * Bohr2Ang, atoms)

        if as_dataarray:
            # Add attributes
            D.attrs['geometry'] = geom
            D.attrs['units'] = '1/eV'
            if Ef is None:
                D.attrs['Ef'] = 'Unknown'
            else:
                D.attrs['Ef'] = Ef

            return D

        return geom, E, np.moveaxis(np.stack(D, axis=0), 2, 0)