def run_task(self, fw_spec): directory = self.get("directory", os.getcwd()) multiplier = self.get("multiplier", 3) os.chdir(directory) nelect_written = False try: with open("OUTCAR", "r") as file: if "NELECT" in file.read(): nelect_written = True except FileNotFoundError: pass if not nelect_written: # Do a trial run to figure out the number of standard bands stdout_file = "temp.out" stderr_file = "temp.out" vasp_cmd = fw_spec["_fw_env"]["vasp_cmd"].split(" ") with open(stdout_file, 'w') as f_std, \ open(stderr_file, "w", buffering=1) as f_err: p = subprocess.Popen(vasp_cmd, stdout=f_std, stderr=f_err, preexec_fn=os.setsid) while not nelect_written: try: with open("OUTCAR", "r") as file: if "NELECT" in file.read(): nelect_written = True except FileNotFoundError: pass time.sleep(1) os.killpg(os.getpgid(p.pid), signal.SIGTERM) time.sleep(3) os.remove(os.path.join(directory, "temp.out")) outcar = Outcar("OUTCAR") incar = Incar.from_file("INCAR") pattern = r"\s+NELECT\s=\s+(\d+).\d+\s+total\snumber\sof\selectrons" outcar.read_pattern({"nelect": pattern}) nions = len(Structure.from_file("POSCAR")) nelect = int(outcar.data["nelect"][0][0]) ispin = int(incar.get("ISPIN", 1)) if ispin == 1: nbands = int(round(nelect / 2 + nions / 2)) * multiplier elif ispin == 2: nbands = int(nelect * 3 / 5 + nions) * multiplier else: raise ValueError("ISPIN Value is not set to 1 or 2!") incar.update({"NBANDS": nbands}) incar.write_file("INCAR")
def load_polarization(self, path): """ This function tries to read Berry phase calculations from three files: filename+'_berry_1' filename+'_berry_2' filename+'_berry_3' If the files are not readable, no polarization attribute is set. On successful run this method fills the following attributes: - self.polarization - polarization as calculated from the three files, in cartesian coordinates (3 e/A**2) - self.ev_phase - expectation value reported by VASP for each of the three directions (3x3 -e*A) - self.ev_recip - the mean of the expectation value projected to internal coordinates (3 -e) - self.berry_phase - berry phases of all strings in each of the directions (3xN 1) - self.polarization_quant - polarization quant for the calculation (3x3 e/A**2) - self.i_polarization_quant - inverse of polarization quant """ self.berry_phase = 3 * [None] self.berry_ev = 3 * [None] self.berry_ion = 3 * [None] self.num_per_type = [] # Noncollinear calculations are officially numspin==1, but they do contain separate electrons, not electron pairs, # Therefore, they should be treated as numspin==2 for calculation of polarizations. self.numspin = 2 # Extract information from OUTCAR file in each of 3 directions. for kdirection in range(1, 4): fname = os.path.join(path, 'Berry_%d/OUTCAR' % kdirection) if os.access(fname, os.R_OK): # open and read OUTCAR file outcar = Outcar(fname) # berry phase outcar.read_pattern( { 'berry_phase': r"Im\s+ln\[Det\|M_k\|\]=\s+([+-]?\d+\.\d+)\s" }, terminate_on_match=False, postprocess=float) bphase = [ item for sublist in outcar.data.get("berry_phase") for item in sublist ] # k-point weights outcar.read_pattern( { 'kpoint_weights': r"K-point string #\s+\S+\s+weight=\s+([+-]?\d+\.\d+)\s" }, terminate_on_match=False, postprocess=float) kpoint_weights = [ item for sublist in outcar.data.get("kpoint_weights") for item in sublist ] self.berry_phase[kdirection - 1] = np.array( [list(x) for x in zip(bphase, kpoint_weights)]) #self.berry_ev[kdirection-1] = self.read_berry_ev(fname) # alternative way to read berry_ev from Outcar self.berry_ev[kdirection - 1] = self.read_berry_ev_old(fname) outcar.read_lcalcpol() else: # Make sure, we have 0,0,0 as polarisation in case there is no berry phase output available print('No file with polarization') self.berry_phase = 3 * [np.zeros((1, 2))] self.berry_ev = 3 * [np.zeros((3))] self.berry_ion = 3 * [[[0., 0., 0.]]] self.polarization_quant = self.unit_cell.copy() / self.volume self.berry_multiplier = 1. self.polarization_quant *= self.berry_multiplier self.i_polarization_quant = np.linalg.inv( self.polarization_quant) self.ev_recip = np.dot(np.mean(self.berry_ev, axis=0), self.recip_cell) self.recalculate_polarization() return self.polarization_quant = self.unit_cell.copy() / self.volume # In case of nonpolarised calculations, we have two electrons per each Berry string # So the quantum needs to be doubled. self.berry_multiplier = 3 - self.numspin self.polarization_quant *= self.berry_multiplier self.i_polarization_quant = np.linalg.inv(self.polarization_quant) self.ev_recip = np.dot(np.mean(self.berry_ev, axis=0), self.recip_cell) self.recalculate_polarization()
def load_from_outcar(self, path, structure, mode): """ This function reads atom positions, unit cell, forces and stress tensor from the VASP OUTCAR file path: structure: """ if mode == 'scf': filename = os.path.join(path, 'OUTCAR') self.name = filename.split('/')[-2] elif mode == 'nscf': filename = os.path.join(path, 'nscf_SOC/OUTCAR') self.name = filename.split('/')[-3] else: raise Exception( f"Calculation mode {mode} is wrong. Must be scf or nscf") if os.access(filename, os.R_OK): outcar = Outcar(filename) else: raise Exception("Missing OUTCAR file in {}".format(filename)) print(self.name) # ATOMIC PROPERTIES self.num_atoms = int( structure.composition.num_atoms) # total number of atoms self.atoms_frac = structure.frac_coords # fraction coordinates self.atoms_cart = structure.cart_coords # cartesian coordinates self.num_per_type = [ int(x) for x in structure.composition.get_el_amt_dict().values() ] # number of atoms per type self.species = [x.symbol for x in structure.species ] # list with atomic symbols for each specie self.pomass = [ Element(x.symbol).atomic_mass for x in structure.species ] # list with atomic masses for each specie self.charges = [x['tot'] for x in outcar.charge] # list with charges # read zval dict and create a mapping outcar.read_pseudo_zval() zval_dict = outcar.zval_dict self.zvals = [zval_dict[x.symbol] for x in structure.species] # UNIT CELL self.unit_cell = structure.lattice.matrix # unit cell self.recip_cell = structure.lattice.inv_matrix # inverse unit cell self.volume = structure.lattice.volume # total energy in eV self.energy = outcar.final_energy # atomic positions read from Outcar self.atoms = np.array( outcar.read_table_pattern( header_pattern=r"\sPOSITION\s+TOTAL-FORCE \(eV/Angst\)\n\s-+", row_pattern= r"\s+([+-]?\d+\.\d+)\s+([+-]?\d+\.\d+)\s+([+-]?\d+\.\d+)\s+[+-]?\d+\.\d+\s+[+-]?\d+\.\d+\s+[+-]?\d+\.\d+", footer_pattern=r"\s--+", postprocess=lambda x: float(x), last_one_only=False)[0]) # forces acting on atoms from Outcar self.forces = np.array( outcar.read_table_pattern( header_pattern=r"\sPOSITION\s+TOTAL-FORCE \(eV/Angst\)\n\s-+", row_pattern= r"\s+[+-]?\d+\.\d+\s+[+-]?\d+\.\d+\s+[+-]?\d+\.\d+\s+([+-]?\d+\.\d+)\s+([+-]?\d+\.\d+)\s+([+-]?\d+\.\d+)", footer_pattern=r"\s--+", postprocess=lambda x: float(x), last_one_only=False)[0]) # READ STRESS: (XX, YY, ZZ, XY, YZ, ZX) and convert values to array outcar.read_pattern( { 'stress': r"in kB\s+([\.\-\d]+)\s+([\.\-\d]+)\s+([\.\-\d]+)\s+([\.\-\d]+)\s+([\.\-\d]+)\s+([\.\-\d]+)" }, terminate_on_match=False, postprocess=float) stress = outcar.data.get("stress")[0] self.stress = np.array([[stress[0], stress[3], stress[5]], [stress[3], stress[1], stress[4]], [stress[5], stress[4], stress[2]]]) # Conversion from kbar to ev/A^3. self.stress *= KBAR_TO_EVA3 # MAGNETIZATION and its PROJECTION ON EVERY ATOM try: # non-collinear outcar.read_pattern( { 'total_mag': r"number of electron\s+\S+\s+magnetization\s+([\.\-\d]+)\s+([\.\-\d]+)\s+([\.\-\d]+)\s" }, terminate_on_match=False, postprocess=float) self.__magnetization = outcar.data.get("total_mag")[-1] self.proj_magn = np.array([ mag['tot'].moment for mag in outcar.magnetization ]) # 2-D array except: # collinear outcar.read_pattern( { 'total_mag': r"number of electron\s+\S+\s+magnetization\s+(" r"\S+)" }, terminate_on_match=False, postprocess=float) self.__magnetization = np.zeros(3) self.__magnetization[2] = outcar.data.get("total_mag")[-1][0] self.proj_magn = np.zeros((self.num_atoms, 3)) for i in range(self.num_atoms): self.proj_magn[i, 2] = outcar.magnetization[i]['tot'] # 2-D array # NOTE: sum of magnetization projected on atoms differs from the total magnetization self.proj_magn_sum = np.sum(self.proj_magn, axis=0) self.get__magnetization() # POLARIZATION self.load_polarization(path) self.fileID = filename
def dos(relax_dir, k_product, hse_calc=False): """ Set up the work function calculation based on the output of the geometry optimization. """ relax_dir = os.path.abspath(relax_dir) try: # Try reading the vasprun.xml file relax_vasprun = Vasprun(os.path.join(relax_dir, "vasprun.xml")) # Triple the amount of bands compared to the minimum nbands = relax_vasprun.parameters["NBANDS"] * 3 except FileNotFoundError: # In case this file is not present, try the OUTCAR file. relax_outcar = Outcar(os.path.join(relax_dir, "OUTCAR")) relax_outcar.read_pattern( {"nbands": r"\s+k-points\s+NKPTS =\s+[0123456789]+\s+k-points " r"in BZ\s+NKDIM =\s+[0123456789]+\s+number of " r"bands\s+NBANDS=\s+([\.\-\d]+)"}, postprocess=int) # Include a significant number of empty bands nbands = relax_outcar.data['nbands'][0][0] * 3 # Add some typical extra settings for the DOS calculation dos_incar = {"NEDOS": 2000, "NBANDS": nbands} if hse_calc: # Set up the calculation dos_calc = slabWorkFunctionHSESet.from_relax_calc( relax_dir=relax_dir, k_product=k_product, user_incar_settings=dos_incar ) # Set up the calculation directory calculation_dir = os.path.join(os.path.split(relax_dir)[0], "hse_dos") else: # Use the charge density from the geometry optimization # dos_incar["ICHARG"] = 11 # Set up the calculation dos_calc = slabWorkFunctionSet.from_relax_calc( relax_dir=relax_dir, k_product=k_product, user_incar_settings=dos_incar ) # Set up the calculation directory calculation_dir = os.path.join(os.path.split(relax_dir)[0], "dftu_dos") if os.path.exists(calculation_dir): clean_dir(calculation_dir) # Write the input files of the calculation dos_calc.write_input(calculation_dir) # Return the calculation director for workflow purposes return calculation_dir