def read_brillouinzone(self, sc): """ Returns K-points from the file (note that these are in reciprocal units) Parameters ---------- sc : SuperCellChild required supercell for the BrillouinZone object Returns ------- bz : BrillouinZone """ k, w = self.read_data(sc) from sisl.physics.brillouinzone import BrillouinZone bz = BrillouinZone(sc) bz._k = k bz._w = w return bz
def _read_from_wfsx(self, root_fdf, wfsx_file, extra_vars=(), need_H=False): """Plots bands from the eigenvalues contained in a WFSX file. It also needs to get a geometry. """ if need_H: self.setup_hamiltonian() if self.H is None: raise ValueError( "Hamiltonian was not setup, and it is needed for the calculations" ) parent = self.H self.geometry = parent.geometry else: # Get the fdf sile fdf = self.get_sile(root_fdf or "root_fdf") # Read the geometry from the fdf sile self.geometry = fdf.read_geometry(output=True) parent = self.geometry # Get the wfsx file wfsx_sile = self.get_sile(wfsx_file or "wfsx_file", parent=parent) # Now read all the information of the k points from the WFSX file k, weights, nwfs = wfsx_sile.read_info() # Get the number of wavefunctions in the file while performing a quick check nwf = np.unique(nwfs) if len(nwf) > 1: raise ValueError( f"File {wfsx_sile.file} contains different number of wavefunctions in some k points" ) nwf = nwf[0] # From the k values read in the file, build a brillouin zone object. # We will use it just to get the linear k values for plotting. bz = BrillouinZone(self.geometry, k=k, weight=weights) # Read the sizes of the file, which contain the number of spin channels # and the number of orbitals and the number of k points. nspin, nou, nk, _ = wfsx_sile.read_sizes() # Find out the spin class of the calculation. self.spin = Spin({ 1: Spin.UNPOLARIZED, 2: Spin.POLARIZED, 4: Spin.NONCOLINEAR, 8: Spin.SPINORBIT }[nspin]) # Now find out how many spin channels we need. Note that if there is only # one spin channel there will be no "spin" dimension on the final dataset. nspin = 2 if self.spin.is_polarized else 1 # Determine whether spin moments will be calculated. spin_moments = False if not self.spin.is_diagonal: # We need to set the parent self.setup_hamiltonian() if self.H is not None: # We could read a hamiltonian, set it as the parent of the wfsx sile wfsx_sile = sisl.get_sile(wfsx_sile.file, parent=self.H) spin_moments = True # Get the wrapper function that we should call on each eigenstate. # This also returns the coordinates and names to build the final dataset. bands_wrapper, all_vars, coords_values = self._get_eigenstate_wrapper( sisl.physics.linspace_bz(bz), extra_vars=extra_vars, spin_moments=spin_moments) # Make sure all coordinates have values so that we can assume the shape # of arrays below. coords_values['band'] = np.arange(0, nwf) coords_values['orb'] = np.arange(0, nou) self.ticks = None # Initialize all the arrays. For each quantity we will initialize # an array of the needed shape. arrays = {} for var in all_vars: # These are all the extra dimensions of the quantity. Note that a # quantity does not need to have extra dimensions. extra_shape = [ len(coords_values[coord]) for coord in var['coords'] ] # First two dimensions will always be the spin channel and the k index. # Then add potential extra dimensions. shape = (nspin, len(bz), *extra_shape) # Initialize the array. arrays[var['name']] = np.empty(shape, dtype=var.get('dtype', np.float64)) # Loop through eigenstates in the WFSX file and add their contribution to the bands ik = -1 for eigenstate in wfsx_sile.yield_eigenstate(): spin = eigenstate.info.get("spin", 0) # Every time we encounter spin 0, we are in a new k point. if spin == 0: ik += 1 if ik == 0: # If this is the first eigenstate we read, get the wavefunction # indices. We will assume that ALL EIGENSTATES have the same indices. # Note that we already checked previously that they all have the same # number of wfs, so this is a fair assumption. coords_values['band'] = eigenstate.info['index'] # Get all the values for this eigenstate. returns = bands_wrapper(eigenstate, spin_index=spin) # And store them in the respective arrays. for var, vals in zip(all_vars, returns): arrays[var['name']][spin, ik] = vals # Now that we have all the values, just build the dataset. self.bands_data = xr.Dataset( data_vars={ var['name']: (("spin", "k", *var['coords']), arrays[var['name']]) for var in all_vars }).assign_coords(coords_values) self.bands_data.attrs = { "ticks": None, "ticklabels": None, "parent": bz }