def read_basis(self): """ Returns data associated with the ion.xml file """ # Get the element-tree root = xml_parse(self.file).getroot() # Get number of orbitals label = root.find('label').text.strip() Z = int(root.find('z').text) # atomic number, negative for floating 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_fermi_level(self): """ Returns the fermi-level """ # Get the element-tree root = xml_parse(self.file).getroot() # Try and find the fermi-level Ef = root.find('fermi_energy') if Ef is None: warn( f"{self!s}.read_data could not locate the Fermi-level in the XML tree" ) return Ef
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. E : the energies at which the PDOS has been evaluated at (if Fermi-level present in file energies are shifted to :math:`E - E_F = 0`). PDOS : an array of DOS with dimensions ``(nspin, geom.no, len(E))`` (with different spin-components) or ``(geom.no, len(E))`` (spin-symmetric). 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 root = xml_parse(self.file).getroot() # Get number of orbitals nspin = int(root.find('nspin').text) # Try and find the fermi-level Ef = root.find('fermi_energy') E = arrayd(root.find('energy_values').text.split()) if Ef is None: warn( str(self) + '.read_data could not locate the Fermi-level in the XML tree, using E_F = 0. eV' ) else: 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.zeta], [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] = arrayd(orb.get('position').split()) atom_species[ia] = Z # Construct the atomic orbital O = AtomicOrbital(n=oi('n'), l=oi('l'), m=oi('m'), zeta=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(orb.find('data').text.split()).reshape(-1, nspin) D.append(to(O, 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(map(Atom, atom_species, atoms)) geom = Geometry(arrayd(xyz) * Bohr2Ang, atoms) if as_dataarray: # Create a new dimension without coordinates (orbital index) D = xr.concat(D, 'orbital') # Add attributes D.attrs['geometry'] = geom D.attrs['unit'] = '1/eV' if Ef is None: D.attrs['Ef'] = 'Unknown' else: D.attrs['Ef'] = Ef return D D = np.moveaxis(np.stack(D, axis=0), 2, 0) if nspin == 1: return geom, E, D[0] return geom, E, D