def get_band_info(self,b_i,k_i=None): """Get band information for given band index `b_i`. If `k_i` is given, returns info at that point Fermi energy is subtracted from all energies. When a plot commnad is called, the Fermi energy is updated if provided. """ def at_minmax(_bands,_pros,func,k_i=None): _bands_ = _bands.flatten() - self._efermi # subtract fermi energy if isinstance(k_i,int): extrema = _bands_[k_i] k = float(self._kpath[k_i]) kp = self._data.kpoints[k_i] pros = _pros[:,k_i,:].sum(axis=0).flatten() else: extrema = func(_bands_) where, = np.where(_bands_ == extrema) # unpack singelton k, kp = [float(self._kpath[w]) for w in where], self._data.kpoints[where] pros = _pros[:,where[0],:].sum(axis=0).flatten() return vp.Dict2Data({'e':float(extrema),'k':k,'kp':kp.tolist(), 'pros':{l.replace('-',''):float(p) for p,l in zip(pros,self._data.pro_bands.labels)}}) if self._data.bands.ISPIN == 1: b = self._data.bands.evals[:,b_i] p = self._data.pro_bands.pros[:,:,b_i,:] if isinstance(k_i,int): # single kpoint return at_minmax(b,p,np.min,k_i=k_i) return vp.Dict2Data({'min':at_minmax(b,p,np.min,k_i=k_i),'max':at_minmax(b,p,np.max,k_i=k_i)}) else: # spin-polarized bu = self._data.bands.evals.SpinUp[:,b_i] pu = self._data.pro_bands.pros.SpinUp[:,:,b_i,:] _minu = at_minmax(bu,pu,np.min,k_i=k_i) _maxu = at_minmax(bu,pu,np.max,k_i=k_i) bd = self._data.bands.evals.SpinDown[:,b_i] pd = self._data.pro_bands.pros.SpinDown[:,:,b_i,:] _mind = at_minmax(bd,pd,np.min,k_i=k_i) _maxd = at_minmax(bd,pd,np.max,k_i=k_i) if isinstance(k_i,int): # single kpoint return vp.Dict2Data({'SpinUp':_minu,'SpinDown':_mind}) return vp.Dict2Data({'SpinUp':{'min':_minu,'max':_maxu},'SpinDown':{'min':_mind,'max':_maxd}})
def export_outcar(path=None): """ - Read potential at ionic sites from OUTCAR. """ if path is None: path = './OUTCAR' if not os.path.isfile(path): return print("{} does not exist!".format(path)) # Raeding it with open(r'{}'.format(path), 'r') as f: lines = f.readlines() # Processing for i, l in enumerate(lines): if 'NIONS' in l: N = int(l.split()[-1]) nlines = np.ceil(N / 5).astype(int) if 'electrostatic' in l: start_index = i + 3 stop_index = start_index + nlines if 'fractional' in l: first = i + 1 if 'vectors are now' in l: b_first = i + 5 if 'NION' in l: ion_line = l if 'NKPTS' in l: kpt_line = l NKPTS, NKDIMS, NBANDS = [int(v) for v in re.findall(r"\d+", kpt_line)] NEDOS, NIONS = [int(v) for v in re.findall(r"\d+", ion_line)] n_kbi = (NKPTS, NBANDS, NIONS) # Data manipulation # Potential data = lines[start_index:stop_index] initial = np.loadtxt(StringIO(''.join(data[:-1]))).reshape((-1)) last = np.loadtxt(StringIO(data[-1])) pot_arr = np.hstack([initial, last]).reshape((-1, 2)) pot_arr[:, 0] = pot_arr[:, 0] - 1 # Ion index fixing # Nearest neighbors pos = lines[first:first + N] pos_arr = np.loadtxt(StringIO('\n'.join(pos))) pos_arr[ pos_arr > 0.98] = pos_arr[pos_arr > 0.98] - 1 # Fixing outer layers # positions and potential pos_pot = np.hstack([pos_arr, pot_arr[:, 1:]]) basis = np.loadtxt(StringIO(''.join(lines[b_first:b_first + 3]))) final_dict = { 'ion_pot': pot_arr, 'positions': pos_arr, 'site_pot': pos_pot, 'basis': basis[:, :3], 'rec_basis': basis[:, 3:], 'n_kbi': n_kbi } return vp.Dict2Data(final_dict)
def at_minmax(_bands,_pros,func,k_i=None): _bands_ = _bands.flatten() - self._efermi # subtract fermi energy if isinstance(k_i,int): extrema = _bands_[k_i] k = float(self._kpath[k_i]) kp = self._data.kpoints[k_i] pros = _pros[:,k_i,:].sum(axis=0).flatten() else: extrema = func(_bands_) where, = np.where(_bands_ == extrema) # unpack singelton k, kp = [float(self._kpath[w]) for w in where], self._data.kpoints[where] pros = _pros[:,where[0],:].sum(axis=0).flatten() return vp.Dict2Data({'e':float(extrema),'k':k,'kp':kp.tolist(), 'pros':{l.replace('-',''):float(p) for p,l in zip(pros,self._data.pro_bands.labels)}})
def download_structure(formula, mp_id=None, max_sites=None,min_sites=None, api_key=None,save_key = False): """Download structure data from Materials project website. - **Parameters** - formula: chemical formula of the material. - mp_id: Materials project id of material. - max_sites: maximum number of sites in structure to download. - min_sites: minimum number of sites in structure to download. > max_sites and min_sites are used to filter the number of sites in structure, or use mp_id to download a specific structure. - **One Time API Key** - api_key: API key from Materials project websit, if you use save_key=True, never required again. - save_key: Save API key to file. You can save any time of key or device changed. - **Return** - structure: Structure data containing attributes `poscars` and `cifs` as list. Each item in list has attributes `content` and `write` to write to file. """ mp = sio.InvokeMaterialsProject(api_key= api_key) mp.request(formula=formula,mp_id=mp_id,max_sites=max_sites,min_sites=min_sites) # make a request if save_key and isinstance(api_key,str): mp.save_api_key(api_key) if mp.success: return vp.Dict2Data({'poscars':mp.poscars,'cifs':mp.cifs}) else: return print('Connection was not sccessful. Try again later.')
def export_potential(locpot=None, e=True, m=False): """ - Returns Data from LOCPOT and similar structure files like CHG. Loads only single set out of 2/4 magnetization data to avoid performance/memory cost while can load electrostatic and one set of magnetization together. - **Parameters** - locpot: path/to/LOCPOT or similar stuructured file like CHG. LOCPOT is auto picked in CWD. - e : Electric potential/charge density. Default is True. - m : Magnetization density m. Default is False. If True, picks `m` for spin polarized case, and `m_x` for non-colinear case. Additionally it can take 'x','y' and 'z' in case of non-colinear calculations. - **Exceptions** - Would raise index error if magnetization density set is not present in LOCPOT/CHG in case `m` is not False. """ if locpot is None: if os.path.isfile('LOCPOT'): locpot = 'LOCPOT' else: return print('./LOCPOT not found.') else: if not os.path.isfile(locpot): return print("File {!r} does not exist!".format(locpot)) if m not in [True, False, 'x', 'y', 'z']: return print( "m expects one of [True,False,'x','y','z'], got {}".format(e)) # data fixing after reading islice from file. def fix_data(islice_gen, shape): new_gen = (float(l) for line in islice_gen for l in line.split()) COUNT = np.prod(shape).astype(int) data = np.fromiter(new_gen, dtype=float, count=COUNT) # Count is must for performance # data written on LOCPOT is in shape of (NGz,NGy,NGx) N_reshape = [shape[2], shape[1], shape[0]] data = data.reshape(N_reshape).transpose([2, 1, 0]) return data # Reading File with open(locpot, 'r') as f: lines = [] f.seek(0) for i in range(8): lines.append(f.readline()) N = sum([int(v) for v in lines[6].split()]) f.seek(0) poscar = [] for i in range(N + 8): poscar.append(f.readline()) f.readline() # Empty one Nxyz = [int(v) for v in f.readline().split()] # Grid line read nlines = np.ceil(np.prod(Nxyz) / 5).astype(int) #islice is faster generator for reading potential pot_dict = {} if e == True: pot_dict.update({'e': fix_data(islice(f, nlines), Nxyz)}) ignore_set = 0 # Pointer already ahead. else: ignore_set = nlines # Needs to move pointer to magnetization #reading Magnetization if True ignore_n = np.ceil(N / 5).astype(int) + 1 #Some kind of useless data if m == True: print( "m = True would pick m_x for non-colinear case, and m for ISPIN=2.\nUse m='x' for non-colinear or keep in mind that m will refer to m_x." ) start = ignore_n + ignore_set pot_dict.update( {'m': fix_data(islice(f, start, start + nlines), Nxyz)}) elif m == 'x': start = ignore_n + ignore_set pot_dict.update( {'m_x': fix_data(islice(f, start, start + nlines), Nxyz)}) elif m == 'y': start = 2 * ignore_n + nlines + ignore_set pot_dict.update( {'m_y': fix_data(islice(f, start, start + nlines), Nxyz)}) elif m == 'z': start = 3 * ignore_n + 2 * nlines + ignore_set pot_dict.update( {'m_z': fix_data(islice(f, start, start + nlines), Nxyz)}) # Read Info basis = np.loadtxt(StringIO(''.join(poscar[2:5]))) * float( poscar[1].strip()) system = poscar[0].strip() ElemName = poscar[5].split() ElemIndex = [int(v) for v in poscar[6].split()] ElemIndex.insert(0, 0) ElemIndex = list(np.cumsum(ElemIndex)) positions = np.loadtxt(StringIO(''.join(poscar[8:N + 9]))) final_dict = dict(SYSTEM=system, ElemName=ElemName, ElemIndex=ElemIndex, basis=basis, positions=positions) final_dict = {**final_dict, **pot_dict} return vp.Dict2Data(final_dict)
def get_en_diff(self,b1_i,b2_i,k1_i=None,k2_i=None): """Get energy difference between two bands at given two kpoints indices. Index 2 is considered at higher energy. - b1_i, b2_i : band indices of the two bands, minimum energy difference is calculated. - k1_i, k2_i : k-point indices of the two bands. > If k1_i and k2_i are not provided, `min(b2_i) - max(b1_i)` is calculated which is equivalent to band gap. Returns: Data with follwoing attributes which can be used to annotate the difference on plot. de : energy difference coords : np.array([[k1,e1],[k2,e2]]) #E_Fermi is subtracted either from system or when user provides in a plot command. eqv_coords: list(coords) at equivalent k-points if exit. Do not appear if k1_i and k2_i are provided. For spin-polarized case, 4 blocks of above data are returned which are accessible by `u1u2, u1d2, d1u2, d1d2` and they collects energy difference between 2 given bands at 2 different spin. """ if k1_i and k2_i == None: raise ValueError('When you provide `k1_i`, `k2_i` cannot be None. They both can be None at same time.') if k1_i == None and k2_i: raise ValueError('When you provide `k2_i`, `k1_i` cannot be None. They both can be None at same time.') def format_coords(b1_max,b2_min): "maximum of b1 and min of b2 is taken for energy difference of two bands when kpoint not given." combs = [] for k1 in b1_max.k: for k2 in b2_min.k: combs.append(np.array([[k1,b1_max.e],[k2,b2_min.e]])) _out = {'coords':combs[0]} if combs[1:]: _out['eqv_coords'] = combs[1:] return _out if self._data.bands.ISPIN == 1: if isinstance(k1_i,int): b1 = self.get_band_info(b1_i,k_i=k1_i) b2 = self.get_band_info(b2_i,k_i=k2_i) return vp.Dict2Data({'de':b2.e - b1.e, 'coords': np.array([[b1.k,b1.e],[b2.k, b2.e]])}) else: b1 = self.get_band_info(b1_i,k_i=None).max b2 = self.get_band_info(b2_i,k_i=None).min return vp.Dict2Data({'de':b2.e - b1.e, **format_coords(b1,b2)}) else: if isinstance(k1_i,int): b1u = self.get_band_info(b1_i,k_i=k1_i).SpinUp b1d = self.get_band_info(b1_i,k_i=k1_i).SpinDown b2u = self.get_band_info(b2_i,k_i=k2_i).SpinUp b2d = self.get_band_info(b2_i,k_i=k2_i).SpinDown return vp.Dict2Data({ 'u1u2':{'de':b2u.e - b1u.e, 'coords':np.array([[b1u.k, b1u.e], [b2u.k, b2u.e]])}, 'd1d2':{'de':b2d.e - b1d.e, 'coords':np.array([[b1d.k, b1d.e], [b2d.k, b2d.e]])}, 'd1u2':{'de':b2u.e - b1d.e, 'coords':np.array([[b1d.k, b1d.e], [b2u.k, b2u.e]])}, 'u1d2':{'de':b2d.e - b1u.e, 'coords':np.array([[b1u.k, b1u.e], [b2d.k, b2d.e]])} }) else: b1u = self.get_band_info(b1_i,k_i=None).SpinUp.max # max in lower band b1d = self.get_band_info(b1_i,k_i=None).SpinDown.max b2u = self.get_band_info(b2_i,k_i=None).SpinUp.min # min in upper band b2d = self.get_band_info(b2_i,k_i=None).SpinDown.min return vp.Dict2Data({ 'u1u2':{'de':b2u.e - b1u.e, **format_coords(b1u,b2u)}, 'd1d2':{'de':b2d.e - b1d.e, **format_coords(b1d,b2d)}, 'd1u2':{'de':b2u.e - b1d.e, **format_coords(b1d,b2u)}, 'u1d2':{'de':b2d.e - b1u.e, **format_coords(b1u,b2d)} })