Source code for dpgen.auto_test.lib.vasp

#!/usr/bin/python3
import os
import warnings

import numpy as np
from pymatgen.io.vasp import Incar, Kpoints

import dpgen.auto_test.lib.util as util
from dpgen.generator.lib.vasp import incar_upper


[docs] class OutcarItemError(Exception): pass
# def get_poscar(conf_dir) : # conf_path = os.path.abspath(conf_dir) # poscar_out = os.path.join(conf_path, 'POSCAR') # contcar = os.path.join(conf_path, 'POSCAR') # lmp_dump = glob.glob(os.path.join(conf_path, 'dump.*')) # lmp_dump.sort() # if os.path.isfile(poscar_out) : # pass # elif os.path.isfile(contcar) : # os.symlink(contcar, poscar_out) # elif len(lmp_dump) == 1 : # dump_file = lmp_dump[0] # lammps.poscar_from_last_dump(dump_file, task_poscar, deepmd_type_map)
[docs] def regulate_poscar(poscar_in, poscar_out): with open(poscar_in) as fp: lines = fp.read().split("\n") names = lines[5].split() counts = [int(ii) for ii in lines[6].split()] assert len(names) == len(counts) uniq_name = [] for ii in names: if ii not in uniq_name: uniq_name.append(ii) uniq_count = np.zeros(len(uniq_name), dtype=int) for nn, cc in zip(names, counts): uniq_count[uniq_name.index(nn)] += cc natoms = np.sum(uniq_count) posis = lines[8 : 8 + natoms] all_lines = [] for ele in uniq_name: ele_lines = [] for ii in posis: ele_name = ii.split()[-1] if ele_name == ele: ele_lines.append(ii) all_lines += ele_lines all_lines.append("") ret = lines[0:5] ret.append(" ".join(uniq_name)) ret.append(" ".join([str(ii) for ii in uniq_count])) ret.append("Direct") ret += all_lines with open(poscar_out, "w") as fp: fp.write("\n".join(ret))
[docs] def sort_poscar(poscar_in, poscar_out, new_names): with open(poscar_in) as fp: lines = fp.read().split("\n") names = lines[5].split() counts = [int(ii) for ii in lines[6].split()] new_counts = np.zeros(len(counts), dtype=int) for nn, cc in zip(names, counts): new_counts[new_names.index(nn)] += cc natoms = np.sum(new_counts) posis = lines[8 : 8 + natoms] all_lines = [] for ele in new_names: ele_lines = [] for ii in posis: ele_name = ii.split()[-1] if ele_name == ele: ele_lines.append(ii) all_lines += ele_lines all_lines.append("") ret = lines[0:5] ret.append(" ".join(new_names)) ret.append(" ".join([str(ii) for ii in new_counts])) ret.append("Direct") ret += all_lines with open(poscar_out, "w") as fp: fp.write("\n".join(ret))
[docs] def perturb_xz(poscar_in, poscar_out, pert=0.01): with open(poscar_in) as fp: lines = fp.read().split("\n") zz = lines[4] az = [float(ii) for ii in zz.split()] az[0] += pert zz = [str(ii) for ii in az] zz = " ".join(zz) lines[4] = zz with open(poscar_out, "w") as fp: fp.write("\n".join(lines))
[docs] def reciprocal_box(box): rbox = np.linalg.inv(box) rbox = rbox.T # rbox = rbox / np.linalg.det(box) # print(np.matmul(box, rbox.T)) # print(rbox) return rbox
[docs] def make_kspacing_kpoints(poscar, kspacing, kgamma): if not isinstance(kspacing, list): kspacing = [kspacing, kspacing, kspacing] with open(poscar) as fp: lines = fp.read().split("\n") scale = float(lines[1]) box = [] for ii in range(2, 5): box.append([float(jj) for jj in lines[ii].split()[0:3]]) box = np.array(box) box *= scale rbox = reciprocal_box(box) kpoints = [ max(1, (np.ceil(2 * np.pi * np.linalg.norm(ii) / ks).astype(int))) for ii, ks in zip(rbox, kspacing) ] ret = make_vasp_kpoints(kpoints, kgamma) return ret
[docs] def get_energies(fname): if not check_finished(fname): warnings.warn("incomplete outcar: " + fname) with open(fname) as fp: lines = fp.read().split("\n") try: ener = _get_energies(lines) return ener except OutcarItemError: return None
[docs] def get_boxes(fname): if not check_finished(fname): warnings.warn("incomplete outcar: " + fname) with open(fname) as fp: lines = fp.read().split("\n") try: ener = _get_boxes(lines) return ener except OutcarItemError: return None
[docs] def get_nev(fname): if not check_finished(fname): warnings.warn("incomplete outcar: " + fname) with open(fname) as fp: lines = fp.read().split("\n") try: natoms = _get_natoms(lines) vol = _get_volumes(lines)[-1] ener = _get_energies(lines)[-1] return natoms, ener / natoms, vol / natoms except OutcarItemError: raise OutcarItemError("cannot find the result, please check the OUTCAR")
# print(fname, natoms, vol, ener)
[docs] def get_stress(fname): if not check_finished(fname): warnings.warn("incomplete outcar: " + fname) with open(fname) as fp: lines = fp.read().split("\n") try: stress = _get_stress(lines)[-1] return stress except OutcarItemError: return None
[docs] def check_finished(fname): with open(fname) as fp: return "Elapsed time (sec):" in fp.read()
def _get_natoms(lines): ipt = None for ii in lines: if "ions per type" in ii: ipt = [int(jj) for jj in ii.split()[4:]] return sum(ipt) raise OutcarItemError("cannot find item 'ions per type'") def _get_energies(lines): items = [] for ii in lines: if "free energy TOTEN" in ii: items.append(float(ii.split()[4])) if len(items) == 0: raise OutcarItemError("cannot find item 'free energy TOTEN'") return items def _split_box_line(line): return [float(line[0:16]), float(line[16:29]), float(line[29:42])] def _get_boxes(lines): items = [] for idx, ii in enumerate(lines): tmp_box = [] if "direct lattice vectors" in ii: tmp_box.append(_split_box_line(lines[idx + 1])) tmp_box.append(_split_box_line(lines[idx + 2])) tmp_box.append(_split_box_line(lines[idx + 3])) items.append(tmp_box) return np.array(items) def _get_volumes(lines): items = [] for ii in lines: if "volume of cell" in ii: items.append(float(ii.split()[4])) if len(items) == 0: raise OutcarItemError("cannot find item 'volume of cell'") return items def _get_stress(lines): items = [] for ii in lines: if "in kB" in ii: sv = [float(jj) for jj in ii.split()[2:8]] tmp = sv[4] sv[4] = sv[5] sv[5] = tmp items.append(util.voigt_to_stress(sv)) if len(items) == 0: raise OutcarItemError("cannot find item 'in kB'") return items def _compute_isif(relax_ions, relax_shape, relax_volume): if (relax_ions) and (not relax_shape) and (not relax_volume): isif = 2 elif (relax_ions) and (relax_shape) and (relax_volume): isif = 3 elif (relax_ions) and (relax_shape) and (not relax_volume): isif = 4 elif (not relax_ions) and (relax_shape) and (not relax_volume): isif = 5 elif (not relax_ions) and (relax_shape) and (relax_volume): isif = 6 elif (not relax_ions) and (not relax_shape) and (relax_volume): isif = 7 else: raise ValueError("unknow relax style") return isif
[docs] def make_vasp_static_incar( ecut, ediff, npar, kpar, kspacing=0.5, kgamma=True, ismear=1, sigma=0.2 ): isif = 2 ret = "" ret += "PREC=A\n" ret += "ENCUT=%d\n" % ecut ret += "# ISYM=0\n" ret += "ALGO=normal\n" ret += "EDIFF=%e\n" % ediff ret += "EDIFFG=-0.01\n" ret += "LREAL=A\n" ret += "NPAR=%d\n" % npar ret += "KPAR=%d\n" % kpar ret += "\n" ret += "ISMEAR=%d\n" % ismear ret += "SIGMA=%f\n" % sigma ret += "\n" ret += "ISTART=0\n" ret += "ICHARG=2\n" ret += "NELMIN=6\n" ret += "ISIF=%d\n" % isif ret += "IBRION=-1\n" ret += "\n" ret += "NSW=0\n" ret += "\n" ret += "LWAVE=F\n" ret += "LCHARG=F\n" ret += "PSTRESS=0\n" ret += "\n" if kspacing is not None: ret += "KSPACING=%f\n" % kspacing if kgamma is not None: if kgamma: ret += "KGAMMA=T\n" else: ret += "KGAMMA=F\n" return ret
[docs] def make_vasp_relax_incar( ecut, ediff, relax_ion, relax_shape, relax_volume, npar, kpar, kspacing=0.5, kgamma=True, ismear=1, sigma=0.22, ): isif = _compute_isif(relax_ion, relax_shape, relax_volume) ret = "" ret += "PREC=A\n" ret += "ENCUT=%d\n" % ecut ret += "# ISYM=0\n" ret += "ALGO=normal\n" ret += "EDIFF=%e\n" % ediff ret += "EDIFFG=-0.01\n" ret += "LREAL=A\n" ret += "NPAR=%d\n" % npar ret += "KPAR=%d\n" % kpar ret += "\n" ret += "ISMEAR=%d\n" % ismear ret += "SIGMA=%f\n" % sigma ret += "\n" ret += "ISTART=0\n" ret += "ICHARG=2\n" ret += "NELM=100\n" ret += "NELMIN=6\n" ret += "ISIF=%d\n" % isif ret += "IBRION=2\n" ret += "\n" ret += "NSW=50\n" ret += "\n" ret += "LWAVE=F\n" ret += "LCHARG=F\n" ret += "PSTRESS=0\n" ret += "\n" if kspacing is not None: ret += "KSPACING=%f\n" % kspacing if kgamma is not None: if kgamma: ret += "KGAMMA=T\n" else: ret += "KGAMMA=F\n" return ret
[docs] def make_vasp_phonon_incar( ecut, ediff, npar, kpar, kspacing=0.5, kgamma=True, ismear=1, sigma=0.2 ): isif = 2 ret = "" ret += "PREC=A\n" ret += "ENCUT=%d\n" % ecut ret += "# ISYM=0\n" ret += "ALGO=normal\n" ret += "EDIFF=%e\n" % ediff ret += "EDIFFG=-0.01\n" ret += "LREAL=A\n" # ret += 'NPAR=%d\n' % npar ret += "KPAR=%d\n" % kpar ret += "\n" ret += "ISMEAR=%d\n" % ismear ret += "SIGMA=%f\n" % sigma ret += "\n" ret += "ISTART=0\n" ret += "ICHARG=2\n" ret += "NELMIN=4\n" ret += "ISIF=%d\n" % isif ret += "IBRION=8\n" ret += "\n" ret += "NSW=1\n" ret += "\n" ret += "LWAVE=F\n" ret += "LCHARG=F\n" ret += "PSTRESS=0\n" ret += "\n" if kspacing is not None: ret += "KSPACING=%f\n" % kspacing if kgamma is not None: if kgamma: ret += "KGAMMA=T\n" else: ret += "KGAMMA=F\n" return ret
[docs] def get_poscar_types(fname): with open(fname) as fp: lines = fp.read().split("\n") return lines[5].split()
[docs] def get_poscar_natoms(fname): with open(fname) as fp: lines = fp.read().split("\n") return [int(ii) for ii in lines[6].split()]
def _poscar_natoms(lines): numb_atoms = 0 for ii in lines[6].split(): numb_atoms += int(ii) return numb_atoms def _poscar_scale_direct(str_in, scale): lines = str_in.copy() numb_atoms = _poscar_natoms(lines) pscale = float(lines[1]) pscale = pscale * scale lines[1] = str(pscale) + "\n" return lines def _poscar_scale_cartesian(str_in, scale): lines = str_in.copy() numb_atoms = _poscar_natoms(lines) # scale box for ii in range(2, 5): boxl = lines[ii].split() boxv = [float(ii) for ii in boxl] boxv = np.array(boxv) * scale lines[ii] = f"{boxv[0]:.16e} {boxv[1]:.16e} {boxv[2]:.16e}\n" # scale coord for ii in range(8, 8 + numb_atoms): cl = lines[ii].split() cv = [float(ii) for ii in cl] cv = np.array(cv) * scale lines[ii] = f"{cv[0]:.16e} {cv[1]:.16e} {cv[2]:.16e}\n" return lines
[docs] def poscar_natoms(poscar_in): with open(poscar_in) as fin: lines = list(fin) return _poscar_natoms(lines)
[docs] def poscar_scale(poscar_in, poscar_out, scale): with open(poscar_in) as fin: lines = list(fin) if "D" == lines[7][0] or "d" == lines[7][0]: lines = _poscar_scale_direct(lines, scale) elif "C" == lines[7][0] or "c" == lines[7][0]: lines = _poscar_scale_cartesian(lines, scale) else: raise RuntimeError("Unknow poscar coord style at line 7: %s" % lines[7]) with open(poscar_out, "w") as fout: fout.write("".join(lines))
[docs] def poscar_vol(poscar_in): with open(poscar_in) as fin: lines = list(fin) box = [] for ii in range(2, 5): words = lines[ii].split() vec = [float(jj) for jj in words] box.append(vec) scale = float(lines[1].split()[0]) box = np.array(box) box *= scale return np.linalg.det(box)
def _make_vasp_kp_gamma(kpoints): ret = "" ret += "Automatic mesh\n" ret += "0\n" ret += "Gamma\n" ret += "%d %d %d\n" % (kpoints[0], kpoints[1], kpoints[2]) ret += "0 0 0\n" return ret def _make_vasp_kp_mp(kpoints): ret = "" ret += "K-Points\n" ret += " 0\n" ret += "Monkhorst Pack\n" ret += "%d %d %d\n" % (kpoints[0], kpoints[1], kpoints[2]) ret += " 0 0 0\n" return ret
[docs] def make_vasp_kpoints(kpoints, kgamma=False): if kgamma: ret = _make_vasp_kp_gamma(kpoints) else: ret = _make_vasp_kp_mp(kpoints) return ret
[docs] def make_vasp_kpoints_from_incar(work_dir, jdata): cwd = os.getcwd() fp_aniso_kspacing = jdata.get("fp_aniso_kspacing") os.chdir(work_dir) # get kspacing and kgamma from incar assert os.path.exists("INCAR") with open("INCAR") as fp: incar = fp.read() try: standard_incar = incar_upper(Incar.from_string(incar)) except AttributeError: standard_incar = incar_upper(Incar.from_str(incar)) if fp_aniso_kspacing is None: try: kspacing = standard_incar["KSPACING"] except KeyError: raise RuntimeError("KSPACING must be given in INCAR") else: kspacing = fp_aniso_kspacing try: gamma = standard_incar["KGAMMA"] if isinstance(gamma, bool): pass else: if gamma[0].upper() == "T": gamma = True else: gamma = False except KeyError: raise RuntimeError("KGAMMA must be given in INCAR") # check poscar assert os.path.exists("POSCAR") # make kpoints ret = make_kspacing_kpoints("POSCAR", kspacing, gamma) try: kp = Kpoints.from_string(ret) except AttributeError: kp = Kpoints.from_str(ret) kp.write_file("KPOINTS") os.chdir(cwd)