不同的主动学习方法

  1. 1. 随机位移的方法
  2. 2. 切割的方法

1. 随机位移的方法

给整个结构中的原子引入随机位移,代替md过程,增加采样

核心脚本

random_distance.py

需要的文件

sub-grade.sh POSCAR inv2

import numpy as np
from sys import argv
from ase.atoms import Atoms
from ase.io import read
import os
import subprocess
import shutil

def write2my(file_path, ene_i, lat_i, ele_i, coo_i, foc_i, vir_i=None):
    lat_i = lat_i.reshape(3, 3)
    coo_i = coo_i.reshape(-1, 3)
    foc_i = foc_i.reshape(-1, 3)
    with open(file_path, 'w') as file:
        file.write(f"# total energy = {ene_i} eV\n\n")
        if vir_i is not None:
            vir_i = np.array(vir_i).reshape(-1)
            file.write(f"VIRIAL\n")
            for i in vir_i:
                file.write(f'{i:20.8f}')
        file.write("\n")

        file.write("CRYSTAL\n")
        file.write("PRIMVEC\n")

        for j in lat_i:
            for k in j:
                file.write(f'{k:20.8f}')
            file.write('\n')

        file.write("PRIMCOORD\n")
        file.write(f"{len(coo_i)} 1\n")

        for j in range(len(coo_i)):
            file.write(f'{ele_i[j]:2}')
            # coo
            for k in coo_i[j]:
                file.write(f"{k:20.8f}")
            # force
            for k in foc_i[j]:
                file.write(f"{k:20.8f}")
            file.write("\n")
    pass
def read_from_ase_atoms(atoms: Atoms):
    try:
        ene = atoms.get_potential_energy()
    except:
        ene = 0.0
    lat = atoms.get_cell()
    pos = atoms.get_positions()
    try:
        foc = atoms.get_forces()
    except:
        foc = np.zeros_like(pos)
    try:
        sts = atoms.get_stress()
        xx, yy, zz, yz, xz, xy = - sts * atoms.get_volume()
        vir = np.array(
            [[xx, xy, xz],
             [xy, yy, yz],
             [xz, yz, zz]]).reshape(-1)
    except:
        vir = np.zeros([3, 3])
    ele = atoms.get_chemical_symbols()
    return ene, lat, pos, foc, vir, ele





def read_poscar(filename):
    with open(filename,'r') as file:
        lines =file.readlines()
    title =lines[0].strip()
    scale =float(lines[1].strip())
    #for i in range(2,5):
    #    lattice_list.append(list(map(float,line[i].split())))
    #lattice_array=np.array(lattice_list)
    lattice_vectors = np.array([list(map(float,lines[i].split())) for i in range(2,5)])
    element_type=lines[5].split()
    atomnumbers=np.array(list(map(int,lines[6].split())))
    atompositions=np.array([list(map(float,lines[i].split()[:3])) for i in range(8,8+sum(atomnumbers))])
    cor_type = lines[7].strip()
    if cor_type.lower() == "direct" or cor_type.lower() == "d":
        atompositions[atompositions < 0]+=1
        atompositions[atompositions >=1]-=1
    elif cor_type.lower() == "cartesian" or cor_type.lower() == "c":
        pass
    return(title,scale,lattice_vectors,element_type,atomnumbers,cor_type,atompositions)
def write_poscar(filename,title,scale,lattice_vectors,element_type,atomnumbers,cor_type,atompositions):
     with open(filename,'w') as file:
        file.write(f"{title}\n")
        file.write(f"{scale}\n")
        for vec in lattice_vectors:
            file.write(f"{vec[0]:.8f}  {vec[1]:.8f}  {vec[2]:.8f}\n")
        #"  ".join用于将可迭代对象的元素以空格连接起来
        file.write("  ".join(element_type)+ "\n")
        file.write("  ".join(map(str,atomnumbers))+ "\n")
        file.write(f"{cor_type}\n")
        for pos in atompositions:
            file.write(f"{pos[0]:.8f}  {pos[1]:.8f}  {pos[2]:.8f}\n")

def change_dir_to_car(filename):
    title,scale,lattice_vectors,element_type,atomnumbers,cor_type,atompositions= read_poscar(filename)
    if cor_type.lower() == "cartesian" or cor_type.lower() == "c":
        pass
    elif cor_type.lower() == "direct" or cor_type.lower() == "d":
        atompositions = np.dot(atompositions,lattice_vectors)
        cor_type = "Cartesian"
    return title,scale,lattice_vectors,element_type,atomnumbers,cor_type,atompositions
def atom_pos(atomnumbers,atompositions):
    atom_pos = np.split(atompositions,np.cumsum(atomnumbers)[:-1])
    return atom_pos


parent_dir = os.path.dirname(os.getcwd())
oos_dir = os.path.join(parent_dir, "oos")
os.makedirs(oos_dir, exist_ok=True)

counter = 0
counter2 = 0
counter3 = 0
exit_flag = False


for itera in range(1, 100):
    if exit_flag:
        break
    mignitude1 = 0.0018*itera
    mignitude2 = 0.0009*itera
    mignitude3 = 0.0017*itera
    mignitude4 = 0.0022*itera
    
    title,scale,lattice_vectors,element_type,atomnumbers,cor_type,atompositions=change_dir_to_car("POSCAR")
    
    atom_pos = atom_pos(atomnumbers,atompositions)
    for i in range(1,10001):
        counter3 +=1
        if exit_flag:
            break
        counter +=1
        rd1=np.random.randn(atomnumbers[0],3)*mignitude1
        rd2=np.random.randn(atomnumbers[1],3)*mignitude1
        rd3=np.random.randn(atomnumbers[2],3)*mignitude1
        rd4=np.random.randn(atomnumbers[3],3)*mignitude1
        atom_pos[0]+=rd1
        atom_pos[1]+=rd2
        atom_pos[2]+=rd3
        atom_pos[3]+=rd4
        atompositions = np.concatenate(atom_pos)
        write_poscar(f"{counter}.vasp",title,scale,lattice_vectors,element_type,atomnumbers,cor_type,atompositions)
        datas = read(f"{counter}.vasp", index=":")
        ene, lat, pos, foc, vir, ele = read_from_ase_atoms(datas[0])
        write2my(
            f"{counter}.xsf",
            ene_i=ene,
            lat_i=lat,
            ele_i=ele,
            coo_i=pos,
            foc_i=foc,
            vir_i=vir)
        os.remove(f"{counter}.vasp")
        print(counter3)
        if counter3 >= 100:
            subprocess.run(["sh", "sub-grade.sh"], check=True)
            with open("grade-log", 'r') as file:
                lines = file.readlines()
            
            for line in lines:
                if exit_flag:
                    break
                if 'grad:' in line:
                    parts = line.split()
                    filename = parts[0]
                    grade_value = float(parts[-1])
                    if 3 < grade_value < 8:
                        counter2 += 1
                        if counter2 >= 50:
                            exit_flag = True
                            break
                        src = os.path.join(os.getcwd(), filename)
                        dst = os.path.join(oos_dir, filename)
                        shutil.move(src, dst)
                    else:
                        os.remove(filename)        
            counter3 = 0            

iter_scheduling_remote.sh 中修改的内容


echo "Simulation ..."
# run local >>>>>>>>>>>>>
mkdir random_vasp
mv sub-grade.sh random_vasp
mv Ap_inv* random_vasp
mv inv2 random_vasp
mv random_distance.py random_vasp
mv POSCAR random_vasp
cd random_vasp
python random_distance.py

2. 切割的方法

切割后的结构grade比较大,不太好用

"""
输入可以有多种形式,输出目前只有writ_poscar可以
"""


import numpy as np
import os
from ase.atoms import Atoms
from ase.io import read
import subprocess
import shutil

def write2my(file_path, ene_i, lat_i, ele_i, coo_i, foc_i, vir_i=None):
    lat_i = lat_i.reshape(3, 3)
    coo_i = coo_i.reshape(-1, 3)
    foc_i = foc_i.reshape(-1, 3)
    with open(file_path, 'w') as file:
        file.write(f"# total energy = {ene_i} eV\n\n")
        if vir_i is not None:
            vir_i = np.array(vir_i).reshape(-1)
            file.write(f"VIRIAL\n")
            for i in vir_i:
                file.write(f'{i:20.8f}')
        file.write("\n")

        file.write("CRYSTAL\n")
        file.write("PRIMVEC\n")

        for j in lat_i:
            for k in j:
                file.write(f'{k:20.8f}')
            file.write('\n')

        file.write("PRIMCOORD\n")
        file.write(f"{len(coo_i)} 1\n")

        for j in range(len(coo_i)):
            file.write(f'{ele_i[j]:2}')
            # coo
            for k in coo_i[j]:
                file.write(f"{k:20.8f}")
            # force
            for k in foc_i[j]:
                file.write(f"{k:20.8f}")
            file.write("\n")
    pass
def read_from_ase_atoms(atoms: Atoms):
    try:
        ene = atoms.get_potential_energy()
    except:
        ene = 0.0
    lat = atoms.get_cell()
    pos = atoms.get_positions()
    try:
        foc = atoms.get_forces()
    except:
        foc = np.zeros_like(pos)
    try:
        sts = atoms.get_stress()
        xx, yy, zz, yz, xz, xy = - sts * atoms.get_volume()
        vir = np.array(
            [[xx, xy, xz],
             [xy, yy, yz],
             [xz, yz, zz]]).reshape(-1)
    except:
        vir = np.zeros([3, 3])
    ele = atoms.get_chemical_symbols()
    return ene, lat, pos, foc, vir, ele


def read_poscar(filename):
    with open(filename,'r') as file:
        lines =file.readlines()
    title =lines[0].strip()
    scale =float(lines[1].strip())
    #for i in range(2,5):
    #    lattice_list.append(list(map(float,line[i].split())))
    #lattice_array=np.array(lattice_list)
    lattice_vectors = np.array([list(map(float,lines[i].split())) for i in range(2,5)])
    element_type=lines[5].split()
    atomnumbers=np.array(list(map(int,lines[6].split())))
    atompositions=np.array([list(map(float,lines[i].split()[:3])) for i in range(8,8+sum(atomnumbers))])
    cor_type = lines[7].strip()
    if cor_type.lower() == "direct" or cor_type.lower() == "d":
        atompositions[atompositions < 0]+=1
        atompositions[atompositions >=1]-=1
    elif cor_type.lower() == "cartesian" or cor_type.lower() == "c":
        pass
    return(title,scale,lattice_vectors,element_type,atomnumbers,cor_type,atompositions)
def write_poscar(filename,title,scale,lattice_vectors,element_type,atomnumbers,cor_type,atompositions):
     with open(filename,'w') as file:
        file.write(f"{title}\n")
        file.write(f"{scale}\n")
        for vec in lattice_vectors:
            file.write(f"{vec[0]:.8f}  {vec[1]:.8f}  {vec[2]:.8f}\n")
        #"  ".join用于将可迭代对象的元素以空格连接起来
        file.write("  ".join(element_type)+ "\n")
        file.write("  ".join(map(str,atomnumbers))+ "\n")
        file.write(f"{cor_type}\n")
        for pos in atompositions:
            file.write(f"{pos[0]:.8f}  {pos[1]:.8f}  {pos[2]:.8f}\n")

#输出不同类型原子的坐标
def atom_pos(atomnumbers,atompositions):
    atom_pos = np.split(atompositions,np.cumsum(atomnumbers)[:-1])
    return atom_pos
        
"""
功能区
"""
#计算体积
def cal_volume(lattice_vectors):
    volume = np.abs(np.dot(lattice_vectors[0],np.cross(lattice_vectors[1],lattice_vectors[2])))
    return volume


#转换分数坐标为笛卡尔坐标
def change_dir_to_car(filename):
    title,scale,lattice_vectors,element_type,atomnumbers,cor_type,atompositions= read_poscar(filename)
    if cor_type.lower() == "cartesian" or cor_type.lower() == "c":
        pass
    elif cor_type.lower() == "direct" or cor_type.lower() == "d":
        atompositions = np.dot(atompositions,lattice_vectors)
        cor_type = "Cartesian"
    return title,scale,lattice_vectors,element_type,atomnumbers,cor_type,atompositions
#笛卡尔坐标转换为分数坐标
def change_car_to_dir(filename):
    title,scale,lattice_vectors,element_type,atomnumbers,cor_type,atompositions = read_poscar(filename)
    if cor_type.lower() == "cartesian" or cor_type.lower() == "c":
        atompositions = np.dot(atompositions,np.linalg.inv(lattice_vectors))
        cor_type = "Direct"
        atompositions[atompositions < 0]+=1
        atompositions[atompositions >=1]-=1
    elif cor_type.lower() == "direct" or cor_type.lower() == "d":
        pass
    return title,scale,lattice_vectors,element_type,atomnumbers,cor_type,atompositions

#晶格向量的变换
def lattice_change(filename,cell_type):
    title,scale,lattice_vectors,element_type,atomnumbers,cor_type,atompositions= change_car_to_dir(filename)
    if cell_type == '211':
        pass
    elif cell_type == "121":
        middle_value = lattice_vectors[0].copy()
        lattice_vectors[0] = lattice_vectors[1]
        lattice_vectors[1] = lattice_vectors[2]
        lattice_vectors[2] = middle_value
        middle_value2 = atompositions[:,0].copy()
        atompositions[:,0] = atompositions[:,1]
        atompositions[:,1] = atompositions[:,2]
        atompositions[:,2] = middle_value2
    elif cell_type == "112":
        middle_value = lattice_vectors[0].copy()
        lattice_vectors[0] = lattice_vectors[2]
        lattice_vectors[2] = lattice_vectors[1]
        lattice_vectors[1] = middle_value
        middle_value2 = atompositions[:,0].copy
        atompositions[:,0] = atompositions[:,2]
        atompositions[:,2] = atompositions[:,1]
        atompositions[:,1] = middle_value2
    return title,scale,lattice_vectors,element_type,atomnumbers,cor_type,atompositions
#将211晶胞分解为两个111晶胞
def split_211(input_file,cell_type,output_file1,output_file2):
    title,scale,lattice_vectors,element_type,atomnumbers,cor_type,atompositions= lattice_change(input_file,cell_type)
    atom_type_pos = atom_pos(atomnumbers,atompositions)
    total_atoms1 = []
    total_atoms_number1 = []
    total_atoms2 = []
    total_atoms_number2 = []
    if cor_type.lower() == "direct" or cor_type.lower() == "d":
        for sub_atom_type_pos in atom_type_pos:
            print(sub_atom_type_pos)
            coord_1=sub_atom_type_pos[sub_atom_type_pos[:,0]<=0.48].copy()
            total_atoms1.append(coord_1)
            total_atoms_number1.append(len(coord_1))        
            coord_2=sub_atom_type_pos[sub_atom_type_pos[:,0]>0.52].copy()
            total_atoms2.append(coord_2)
            total_atoms_number2.append(len(coord_2))
    # elif cor_type.lower() == "cartesian" or cor_type.lower() == "c":

    #     for sub_atom_type_pos in atom_type_pos:
    #         sub_atom_type_pos=np.dot(sub_atom_type_pos,np.linalg.inv(lattice_vectors))
    #         sub_atom_type_pos[sub_atom_type_pos < 0]+=1
    #         sub_atom_type_pos[sub_atom_type_pos >=1]-=1
    #         coord_1=sub_atom_type_pos[sub_atom_type_pos[:,0]<=0.5].copy()
    #         total_atoms1.append(coord_1)
    #         total_atoms_number1.append(len(coord_1))        
    #         coord_2=sub_atom_type_pos[sub_atom_type_pos[:,0]>0.5].copy()
    #         total_atoms2.append(coord_2)
    #         total_atoms_number2.append(len(coord_2))
    #     cor_type = "Direct"
    total_atoms1=np.vstack(total_atoms1)
    total_atoms2=np.vstack(total_atoms2)
    total_atoms1=np.dot(total_atoms1,lattice_vectors)
    total_atoms2=np.dot(total_atoms2,lattice_vectors)
    lattice_vectors[0]/=2
    cor_type ="Cartesian"
    # print(total_atoms1)
    # print(total_atoms_number1)
    write_poscar(output_file1,title,scale,lattice_vectors,element_type,total_atoms_number1,cor_type,total_atoms1)
    write_poscar(output_file2,title,scale,lattice_vectors,element_type,total_atoms_number2,cor_type,total_atoms2)

#POSCAR坐标的修改
def coords_change(axis,value,cor_type,atompositions):
    if cor_type.lower() == "Cartesian" or cor_type.lower() == "c":
        print("use change_car_to_dir to input")
    elif cor_type.lower() == "direct" or cor_type.lower() == "d":
        if axis == 'x':
           atompositions[:,0] += value
        elif axis == 'y':
           atompositions[:,1] += value
        elif axis == 'z':
           atompositions[:,2] += value
        else:
            raise ValueError(f"invalid axis")
    else:
        raise ValueError(f"invalid cor_type")
    return atompositions

#切割x*y*z倍的晶胞为x*y*z个晶胞
def split_cell(input_file,x,y,z):
    input_file_without_ext = os.path.splitext(input_file)[0]
    title,scale,lattice_vectors,element_type,atomnumbers,cor_type,atompositions= change_car_to_dir(input_file)
    atom_type_pos = atom_pos(atomnumbers,atompositions)
    total_cells = [[[{elem:[] for elem in element_type} for _ in range(z)] for _ in range(y)] for _ in range(x)]
    total_atom_numbers = [[[{elem: 0 for elem in element_type} for _ in range(z)] for _ in range(y)] for _ in range(x)]

    if cor_type.lower() == "direct" or cor_type.lower == "d":
        for elem_index,sub_atom_type_pos in enumerate(atom_type_pos):
            elem = element_type[elem_index]
            for atom in sub_atom_type_pos:
                ix = int(atom[0]*x)
                iy = int(atom[1]*y)
                iz = int(atom[2]*z)
                total_cells[ix][iy][iz][elem].append(atom)
                total_atom_numbers[ix][iy][iz][elem] += 1
    cor_type ="Cartesian"
    lattice_vectors_changed = np.zeros((3,3))
    lattice_vectors_changed[0] = lattice_vectors[0]/x
    lattice_vectors_changed[1] = lattice_vectors[1]/y
    lattice_vectors_changed[2] = lattice_vectors[2]/z
    os.makedirs("xsf_output")
    for ix in range(x):
        for iy in range(y):
            for iz in range(z):
                output_file =f"{input_file_without_ext}_{ix}{iy}{iz}.vasp"
                sub_atoms = np.vstack([atom for elem in element_type for atom in total_cells[ix][iy][iz][elem]])
                sub_atoms = np.dot(sub_atoms, lattice_vectors)
                subcell_numbers = [total_atom_numbers[ix][iy][iz][elem] for elem in element_type]
                write_poscar(output_file,title,scale,lattice_vectors_changed,element_type,subcell_numbers,cor_type,sub_atoms)
                datas = read(output_file, index=":")
                ene, lat, pos, foc, vir, ele = read_from_ase_atoms(datas[0])
                output_file_path = os.path.join("xsf_output", f"{output_file}.xsf")
                write2my(
                    output_file_path,
                    ene_i=ene,
                    lat_i=lat,
                    ele_i=ele,
                    coo_i=pos,
                    foc_i=foc,
                    vir_i=vir)
split_cell("POSCAR",2,2,2)
#os.remove("POS")
subprocess.run(["dsub","-s","sub-grade.sh"], check=True)
counter = 0
with open("grade-log", 'r') as file:
    lines = file.readlines()
    for line in lines:
        if 'grad:' in line:
            parts = line.split()
            filename = parts[0]
            filename_first = os.path.splitext(filename)[0]
            grade_value = float(parts[-1])
            if 3 < grade_value < 8:
                os.makedirs(filename_first)
                shutil.move(f"{filename_first}", os.path.join (filename_first, "POSCAR"))
            else:
                os.remove(f"{filename_first}")

转载请注明来源 有问题可通过github提交issue