固体物理中经常会涉及到两种晶胞的选取方式,一种叫primitive cell(原胞),另一种叫conventional cell(单胞,惯用晶胞)。有些工具已经帮我们实现了这两种晶胞的转换。
晶胞转换方法
phonopy晶胞转换
phonopy --symmetry POSCAR
phonopy判断的阈值比较小,有时候可以设置大一点
phonopy --symmetry --tolerance=1.0e-3 POSCAR
POSCAR是VASP输入格式的晶胞。执行命令后会生成BPOSCAR(conventional cell)和PPOSCAR(primitive cell)。
Avogadro晶胞转换
安装后导入结构,执行Crystallography ->Reduce->Reduce Cell(primitive cell),当然,有时候也要设置阈值 Crystallography ->Settings->Tolerance for symmetry operations。
QEtoolkit
这个网页集成的功能基于spglib的,当然前面的phonopy也是。
AFLOW
这个网站功能比较强大。
Spglib库使用
下面介绍怎么用spglib库的功能实现晶胞转换。
首先是读取结构文件(read_poscar.py)
import spglib
import numpy as np
import os
import linecache
class read_poscar(object):
def __init__(
self,
struct=None,
pos_name=None,
lattice_index=None,
lat=None,
lat_recell=None,
atomname=None,
atomnum=None,
postype=None,
pos=None,
spg_number=None,
):
self.struct = linecache.getlines("POSCAR")
# read POSCAR to get some paramatrics: sys_name; lattice; atom_name; atom_number; atom_position
# and get spacegroup_number
poscar = [line.strip() for line in self.struct]
num = len(poscar)
self.pos_name = poscar[0].split()
self.lat_index = poscar[1].split()
self.lattice_index = float(self.lat_index[0])
# matrics of lattice vector
lat_vector = np.zeros((3, 3))
index = 0
for latt in poscar[2:5]:
latt = latt.split()
lat_vector[index, :] = latt[0:3]
index += 1
self.lattice = lat_vector
self.atomname = poscar[5].split()
self.atomnum = poscar[6].split()
self.postype = poscar[7].split()
atom_len=len(self.atomname)
# matrics of atom position
i = num - 8
position_vector = np.zeros((i, 3))
index = 0
for poss in poscar[8:num]:
poss = poss.split()
#position_vector[index, 0:3] = poss[0:3]
position_vector[index,0] = poss[0]
position_vector[index,1] = poss[1]
position_vector[index,2] = poss[2]
index += 1
self.lat = lat_vector * self.lattice_index
self.pos = position_vector
atom_numbers = [1,] * (int(self.atomnum[0]))#+int(self.atomnum[1]))
cell = (self.lat, self.pos, atom_numbers)
database = spglib.get_symmetry_dataset(
cell, symprec=1e-3
)
self.spg_number = database["number"]
def system_name(self):
return self.pos_name
def latt_index(self):
return self.lattice_index
def latti(self):
return self.lattice
def atom_name(self):
return self.atomname
def atom_number(self):
return self.atomnum
def position_type(self):
return self.postype
def positions(self):
return self.pos
def spacegroup_num(self):
return self.spg_number
然后转换晶胞
import os
import numpy as np
import read_poscar as readpos
import spglib
class recell(object):
def __init__(
self,
spg_num=None,
lattindex=None,
latt=None,
atomname=None,
atomnum=None,
postype=None,
position=None,
cell_lattice=None,
cell_position=None,
cell_atomnum=None,
to_pricell = None
):
# read the origmitive cell
self.spg_num = readpos.read_poscar().spacegroup_num()
self.lattindex = readpos.read_poscar().latt_index()
self.latt = readpos.read_poscar().latti()
self.atomname = readpos.read_poscar().atom_name()
self.atomnum = readpos.read_poscar().atom_number()
self.postype = readpos.read_poscar().position_type()
self.position = readpos.read_poscar().positions()
orignumbers = []
for i in np.arange(0, len(self.atomname)):
for j in np.arange(0, int(self.atomnum[i]), 1):
orignumbers.append(i + 1)
origlattice = self.latt * self.lattindex
origpositon = self.position
origcell = (origlattice, origpositon, orignumbers)
if(to_pricell==False):
# refine cell
self.cell_lattice, re_position, re_numbers = spglib.standardize_cell(
cell=origcell, symprec=1e-3, to_primitive=False
)
else:
self.cell_lattice, re_position, re_numbers = spglib.standardize_cell(
cell=origcell, symprec=1e-3, to_primitive=True
)
# 主要对坐标进行重新排序
re_position_list = list(re_position)
zipped = list(zip(re_numbers, re_position_list))
zipsorted = sorted(zipped, key=lambda x: (x[0]))
re_numbers_sort, re_positon_sort = zip(*zipsorted)
self.cell_position = np.array(re_positon_sort)
self.cell_atomnum = []
for i in np.arange(0, len(self.atomnum), 1):
num = int(self.atomnum[i]) * (len(re_numbers_sort) / len(orignumbers))
self.cell_atomnum.append(num)
# write the refine cell
writepos = open("RECELL", mode="w")
print("recell_poscar", file=writepos)
print("1.0", file=writepos)
for m in np.arange(0, 3, 1):
print(
format(self.cell_lattice[m, 0], ".10f"),
" ",
format(self.cell_lattice[m, 1], ".10f"),
" ",
format(self.cell_lattice[m, 2], ".10f"),
file=writepos,
)
for j in np.arange(0, len(self.atomname), 1):
print(self.atomname[j], file=writepos, end=" ")
print(end="\n", file=writepos)
for l in np.arange(0, len(self.atomname), 1):
print(int(self.cell_atomnum[l]), end=" ", file=writepos)
print(end="\n", file=writepos)
print(self.postype[0], file=writepos)
for n in np.arange(0, self.cell_position.shape[0], 1):
print(
format(self.cell_position[n, 0], ".10f"),
" ",
format(self.cell_position[n, 1], ".10f"),
" ",
format(self.cell_position[n, 2], ".10f"),
file=writepos,
)
writepos.close()
def latti(self):
return self.cell_lattice
def atom_number(self):
return self.cell_atomnum
def positions(self):
return self.cell_position
使用方法
import reposcar
reposcar.recell(to_pricell=False) #转化为惯用晶胞
reposcar.recell(to_pricell=True) #转化为原胞