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