Source code for dpgen.data.tools.create_random_disturb

#!/usr/bin/env python3

import argparse
import glob
import os

import ase.io
import numpy as np

import dpgen.data.tools.io_lammps as io_lammps
from dpgen.generator.lib.abacus_scf import get_abacus_STRU, make_abacus_scf_stru


[docs] def create_disturbs_atomsk(fin, nfile, dmax=1.0, ofmt="lmp"): # removing the exists files flist = glob.glob("*." + ofmt) for f in flist: os.remove(f) # Based on our tests, we find it always creates a disturb by # constant value of dmax for atomsk for i in range(1, nfile + 1): fout = fin + str(i) + "." + ofmt cmd = "atomsk " + fin + " -disturb " + str(dmax) + " -wrap -ow " + fout os.system(cmd) return
[docs] def random_range(a, b, ndata=1): data = np.random.random(ndata) * (b - a) + a return data
[docs] def gen_random_disturb(dmax, a, b, dstyle="uniform"): d0 = np.random.rand(3) * (b - a) + a dnorm = np.linalg.norm(d0) if dstyle == "normal": dmax = np.random.standard_normal(0, 0.5) * dmax elif dstyle == "constant": pass else: # use if we just wanna a disturb in a range of [0, dmax), dmax = np.random.random() * dmax dr = dmax / dnorm * d0 return dr
[docs] def create_disturbs_ase( fin, nfile, dmax=1.0, ofmt="lmp", dstyle="uniform", write_d=False ): # removing the exists files flist = glob.glob("*." + ofmt) for f in flist: os.remove(f) # read-in by ase atoms = ase.io.read(fin) natoms = atoms.get_global_number_of_atoms() pos0 = atoms.get_positions() # creat nfile ofmt files. for fid in range(1, nfile + 1): dpos = np.zeros((natoms, 3)) atoms_d = atoms.copy() if write_d: fw = open("disp-" + str(fid) + ".dat", "w") for i in range(natoms): # Use copy(), otherwise it will modify the input atoms every time. dr = gen_random_disturb(dmax, -0.5, 0.5, dstyle) """ if i == 1: print(dr) print(np.linalg.norm(dr)) """ dpos[i, :] = dr if write_d: dnorm = np.linalg.norm(dr) fw.write("%d\t%f\t%f\t%f\t%f\n" % (i + 1, dr[0], dr[1], dr[2], dnorm)) fw.flush() pos = pos0 + dpos atoms_d.set_positions(pos) fout = fin + str(fid) + "." + ofmt print("Creating %s ..." % fout) if ofmt in ["lmp", "lammps_data"]: # for lammps, use my personal output functions io_lammps.ase2lammpsdata(atoms_d, fout) else: ase.io.write(fout, atoms_d, ofmt, vasp5=True) if write_d: fw.close() return
[docs] def gen_random_emat(etmax, diag=0): if np.abs(etmax) >= 1e-6: e = random_range(-etmax, etmax, 6) else: e = np.zeros(6) if diag != 0: # isotropic behavior e[3], e[4], e[5] = 0, 0, 0 emat = np.array( [ [e[0], 0.5 * e[5], 0.5 * e[4]], [0.5 * e[5], e[1], 0.5 * e[3]], [0.5 * e[4], 0.5 * e[3], e[2]], ] ) emat = emat + np.eye(3) return emat
[docs] def create_disturbs_ase_dev( fin, nfile, dmax=1.0, etmax=0.1, ofmt="lmp", dstyle="uniform", write_d=False, diag=0 ): # removing the exists files flist = glob.glob("*." + ofmt) for f in flist: os.remove(f) # read-in by ase atoms = ase.io.read(fin) natoms = atoms.get_global_number_of_atoms() cell0 = atoms.get_cell() # creat nfile ofmt files. for fid in range(1, nfile + 1): # Use copy(), otherwise it will modify the input atoms every time. atoms_d = atoms.copy() # random flux for atomic positions if write_d: fw = open("disp-" + str(fid) + ".dat", "w") dpos = np.zeros((natoms, 3)) for i in range(natoms): dr = gen_random_disturb(dmax, -0.5, 0.5, dstyle) dpos[i, :] = dr if write_d: dnorm = np.linalg.norm(dr) fw.write("%d\t%f\t%f\t%f\t%f\n" % (i + 1, dr[0], dr[1], dr[2], dnorm)) fw.flush() # random flux for volumes cell = np.dot(cell0, gen_random_emat(etmax, diag)) atoms_d.set_cell(cell, scale_atoms=True) if write_d: fout_c = "cell-" + str(fid) + ".dat" np.savetxt(fout_c, cell, "%f") # determine new cell & atomic positions randomiziations pos = atoms_d.get_positions() + dpos atoms_d.set_positions(pos) # pre-converting the Atoms to be in low tri-angular cell matrix cell_new = io_lammps.convert_cell(cell) # pos_new = io_lammps.convert_positions(pos, cell, cell_new) atoms_d.set_cell(cell_new, scale_atoms=True) # atoms_d.set_positions(pos_new) # Writing it fout = fin + str(fid) + "." + ofmt print("Creating %s ..." % fout) if ofmt in ["lmp", "lammps_data"]: # for lammps, use my personal output functions io_lammps.ase2lammpsdata(atoms_d, fout=fout) else: ase.io.write(fout, atoms_d, ofmt, vasp5=True) if write_d: fw.close() return
[docs] def create_disturbs_abacus_dev( fin, nfile, dmax=1.0, etmax=0.1, ofmt="abacus", dstyle="uniform", write_d=False, diag=0, ): # removing the exists files flist = glob.glob("*." + ofmt) for f in flist: os.remove(f) # read-in by ase # atoms = ase.io.read(fin) # natoms = atoms.get_global_number_of_atoms() # cell0 = atoms.get_cell() stru = get_abacus_STRU(fin) natoms = sum(stru["atom_numbs"]) cell0 = stru["cells"] # creat nfile ofmt files. for fid in range(1, nfile + 1): # Use copy(), otherwise it will modify the input atoms every time. stru_d = stru.copy() # random flux for atomic positions if write_d: fw = open("disp-" + str(fid) + ".dat", "w") dpos = np.zeros((natoms, 3)) for i in range(natoms): dr = gen_random_disturb(dmax, -0.5, 0.5, dstyle) dpos[i, :] = dr if write_d: dnorm = np.linalg.norm(dr) fw.write("%d\t%f\t%f\t%f\t%f\n" % (i + 1, dr[0], dr[1], dr[2], dnorm)) fw.flush() # random flux for volumes cell = np.dot(cell0, gen_random_emat(etmax, diag)) stru_d["cells"] = cell if write_d: fout_c = "cell-" + str(fid) + ".dat" np.savetxt(fout_c, cell, "%f") # determine new cell & atomic positions randomiziations stru_d["coords"] += dpos # pre-converting the Atoms to be in low tri-angular cell matrix cell_new = io_lammps.convert_cell(cell) # pos_new = io_lammps.convert_positions(pos, cell, cell_new) stru_d["cells"] = cell_new convert_mat = np.linalg.inv(cell).dot(cell_new) stru_d["coords"] = np.matmul(stru_d["coords"], convert_mat) # Writing it fout = fin + str(fid) + "." + ofmt print("Creating %s ..." % fout) ret = make_abacus_scf_stru( stru_d, stru_d["pp_files"], stru_d["orb_files"], stru_d["dpks_descriptor"] ) with open(fout, "w") as fp: fp.write(ret) if write_d: fw.close() return
[docs] def create_random_alloys(fin, alloy_dist, ifmt="vasp", ofmt="vasp"): """In fact, atomsk also gives us the convinient tool to do this.""" # alloy_dist = {'Zr': 0.80, 'Nb': 0.20} atomic_symbols = alloy_dist.keys() atomic_ratios = alloy_dist.values() # renormalize the atomic_ratio atomic_ratios = atomic_ratios / atomic_ratios.sum() atoms = ase.io.read(fin, format=ifmt) # decide the exactly number of atoms for each types. natoms = atoms.num_of_numbers() num_for_each = [] for r in atomic_ratios: num_for_each.append(int(r * natoms)) # ========= decide which atom-ID to be substituted. =========== # a natoms vector with random sequence all_ids = np.random.permutation(natoms) ids_for_each = [] for i in range(num_for_each): num = num_for_each[i] if i == 0: id_start = 0 else: id_start = num_for_each[i - 1] ids_for_each.append(all_ids[id_start, id_start + num]) # subsitute them, by replacing their chemical symbols of A by B new_chemical_symbols = atoms.get_chemical_symbols() for i in range(1, num_for_each): # ignore the 0-th element (it is itself) symbol = atomic_symbols[i] ids = ids_for_each[i] for idx in ids: new_chemical_symbols[idx] = symbol # set the new chemical_symbols to the atoms atoms.set_chemical_symbols(new_chemical_symbols) # write it as ofmt fout = fin.split(",")[0] + "_random" + ofmt ase.io.write(fout, atoms, format=ofmt, vasp5=True) return
[docs] def RandomDisturbParser(): parser = argparse.ArgumentParser( description="Script to generate random disturb configurations" ) parser.add_argument("fin", type=str, help="input file name") parser.add_argument("nfile", type=int, help="number of files to be created") parser.add_argument("dmax", type=float, help="dmax") parser.add_argument( "-etmax", type=float, default=0, help="etmax for random strain tensor generations", ) parser.add_argument( "-diag", type=int, default=0, help="only diagonal elements of strain tensors are randomized?", ) parser.add_argument("-ofmt", type=str, default="lmp", help="output fileformat") parser.add_argument( "-dstyle", type=str, default="uniform", help="random distribution style [uniform?]", ) parser.add_argument( "-wd", "--write_disp", type=int, default=0, help="write displacement information?", ) return parser.parse_args()
# if __name__ == "__main__": args = RandomDisturbParser() fin = args.fin nfile = args.nfile dmax = args.dmax ofmt = args.ofmt dstyle = args.dstyle write_d = args.write_disp etmax = args.etmax diag = args.diag if write_d == 0: write_d = False else: write_d = True # main program # create_disturbs_atomsk(fin, nfile, dmax, ofmt) # create_disturbs_ase(fin, nfile, dmax, ofmt, dstyle, write_d) if ofmt == "vasp": create_disturbs_ase_dev(fin, nfile, dmax, etmax, ofmt, dstyle, write_d, diag) elif ofmt == "abacus": create_disturbs_abacus_dev(fin, nfile, dmax, etmax, ofmt, dstyle, write_d, diag)