Files
screen/py/Occupation.py
2025-12-07 13:56:33 +08:00

330 lines
12 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

from fontTools.misc.plistlib import end_string
from pymatgen.core import Structure
import spglib
from fractions import Fraction
import random
from pymatgen.core.sites import PeriodicSite, Species,Element,Lattice
import numpy as np
import re
def typejudge(number):
if number in [1, 2]:
return "Triclinic"
elif 3 <= number <= 15:
return "Monoclinic"
elif 16 <= number <= 74:
return "Orthorhombic"
elif 75 <= number <= 142:
return "Tetragonal"
elif 143 <= number <= 167:
return "Trigonal"
elif 168 <= number <= 194:
return "Hexagonal"
elif 195 <= number <= 230:
return "Cubic"
else:
return "Unknown"
def extract_oxi_state(species_string):
"""
从 species_string"Li+:0.645")提取氧化态
Args:
species_string: 包含元素和氧化态的字符串(如 "Li+:0.645""Fe2-:0.5"
Returns:
int: 氧化态数值(如 "+" -> 1, "2-" -> -2默认返回 0 如果解析失败
"""
# 使用正则表达式匹配化学符号和氧化态(如 Li+, Fe2-
match = re.search(r"([A-Za-z]+)([+-]?\d*[+-])", species_string)
if not match:
return 0 # 默认中性
# 提取氧化态部分(如 "+", "2-", "3+"
oxi_str = match.group(2)
# 处理单符号情况(如 "+" -> 1, "-" -> -1
if oxi_str in ("+", "-"):
return 1 if oxi_str == "+" else -1
# 处理多数字情况(如 "2+" -> 2, "3-" -> -3
try:
num = int(oxi_str[:-1]) # 提取数字部分
sign = 1 if oxi_str[-1] == "+" else -1
return num * sign
except (ValueError, IndexError):
return 0 # 解析失败时返回中性
def process_cif_file(cif_file_path,explict_element):
structure = Structure.from_file(cif_file_path)
result_list = []
for index, site in enumerate(structure.sites, start=1):
occupancy = site.species.element_composition.num_atoms
species = site.species.chemical_system
if species in explict_element:
break
if occupancy < 1.0:
entry = next((r for r in result_list if
r["species"] == species and r["occupancy"] == occupancy), None)
if entry:
entry["number"].append(index)
else:
result_list.append({
"species": species,
"number": [index],
"occupancy": occupancy
})
return result_list
def factorize_to_three_factors(n,type_sym=None):
factors = []
# 遍历可能的x值
if type_sym == None:
for x in range(1, n + 1):
if n % x == 0:
remaining_n = n // x
# 遍历可能的y值
for y in range(1, remaining_n + 1):
if remaining_n % y == 0:
z = remaining_n // y
factors.append({'x': x, 'y': y, 'z': z})
if type_sym == "xyz":
for x in range(1, n + 1):
if n % x == 0:
remaining_n = n // x
# 遍历可能的y值
for y in range(1, remaining_n + 1):
if remaining_n % y == 0 and y <= x:
z = remaining_n // y
if z <= y:
factors.append({'x': x, 'y': y, 'z': z})
def sum_score(factor):
x, y, z = factor['x'], factor['y'], factor['z']
return x + y + z
# 按照sum_score从小到大排序
sorted_factors = sorted(factors, key=sum_score)
return sorted_factors
def calculate_supercell_factor(occupancy):
# 将浮点数转换为分数形式
fraction = Fraction(occupancy).limit_denominator()
# 获取分子和分母
numerator = fraction.numerator
denominator = fraction.denominator
return numerator,denominator
def mark_atoms_randomly(factors, atom_number):
"""
根据扩胞因子和占据数量生成随机占据字典
Args:
factors: 扩胞因子字典 {'x': int, 'y': int, 'z': int}
atom_number: 需要占据的副本数量
Returns:
字典 {0: 1或0, 1: 1或0, ..., total_copies-1: 1或0}
"""
x, y, z = factors['x'], factors['y'], factors['z']
total_copies = x * y * z
if atom_number > total_copies:
raise ValueError(f"atom_number ({atom_number}) 不能超过扩胞总数 (x*y*z = {total_copies})")
# 生成所有副本索引 [0, 1, 2, ..., total_copies-1]
atom_dice = list(range(total_copies))
# 随机选择 atom_number 个副本占据
selected_atoms = random.sample(atom_dice, atom_number)
# 创建结果字典 {0: 1或0, 1: 1或0, ...}
result = {atom: 1 if atom in selected_atoms else 0 for atom in atom_dice}
return result
def generate_random_list(total_elements, atom_number):
# 确保 atom_number 不超过 total_elements
if atom_number > total_elements:
raise ValueError("atom_number cannot be greater than the total number of elements (x * y * z)")
# 创建一个全0的列表
result = [0] * total_elements
# 随机选择 atom_number 个位置并将这些位置的值设为1
indices = random.sample(range(total_elements), atom_number)
for index in indices:
result[index] = 1
return result
def merge_structures(struct_copies, factors):
"""
将多个副本结构按三维顺序合并为扩胞后的结构
Args:
struct_copies: 副本结构列表(长度 = x*y*z
factors: 扩胞因子字典 {"x": int, "y": int, "z": int}
Returns:
合并后的扩胞结构
"""
x, y, z = factors["x"], factors["y"], factors["z"]
total_copies = x * y * z
if len(struct_copies) != total_copies:
raise ValueError("副本数量与扩胞因子不匹配")
# 获取原结构的晶格
original_lattice = struct_copies[0].lattice
# 创建扩胞后的新晶格(直接按倍数缩放)
new_lattice_matrix = np.dot(original_lattice.matrix, np.diag([x, y, z]))
new_lattice = Lattice(new_lattice_matrix)
# 初始化合并后的结构
merged_structure = Structure(
lattice=new_lattice,
species=[],
coords=[],
coords_are_cartesian=False
)
# 按三维顺序填充每个副本的原子
for copy_idx in range(total_copies):
# 计算当前副本的分数坐标偏移量
offset = np.array([
copy_idx // (y * z), # x方向偏移
(copy_idx % (y * z)) // z, # y方向偏移
copy_idx % z # z方向偏移
])
# 将当前副本的原子添加到合并结构中(考虑偏移)
for site in struct_copies[copy_idx]:
if site.species: # 跳过空位
merged_structure.append(
species=site.species,
coords=site.frac_coords + offset,
coords_are_cartesian=False,
properties=site.properties
)
return merged_structure
def expand_structure(structure, factors, atom_indices, atom_number):
# 参数检查
x, y, z = factors['x'], factors['y'], factors['z']
total_copies = x * y * z
if not all(1 <= idx <= len(structure.sites) for idx in atom_indices):
raise ValueError("atom_indices包含无效原子索引")
# 生成独立副本
struct_copies = [structure.copy() for _ in range(total_copies)]
atom_dice = list(range(total_copies)) # 所有副本索引 [0,1,2,...]
# 处理每个目标原子
for atom_idx in atom_indices:
original_site = structure.sites[atom_idx - 1]
element = original_site.species.chemical_system
# 生成当前原子的占据字典(如{0:1, 1:0, 2:1,...}
occupancy_dict = mark_atoms_randomly(factors,atom_number)
# 修改每个副本
for copy_idx, occupy in occupancy_dict.items():
# 或者方法2使用remove/insert
struct_copies[copy_idx].remove_sites([atom_idx - 1])
oxi_state = extract_oxi_state(original_site.species_string)
if occupy:
new_site = PeriodicSite(
species=Species(element, oxi_state),
coords=original_site.frac_coords,
lattice=struct_copies[copy_idx].lattice,
to_unit_cell=True,
label=original_site.label
)
struct_copies[copy_idx].sites.insert(atom_idx - 1, new_site)
else:
species_dict = {Species(element, oxi_state): 0.0}
new_site = PeriodicSite(
species = species_dict,
coords=original_site.frac_coords,
lattice=struct_copies[copy_idx].lattice,
to_unit_cell=True,
label=original_site.label
)
struct_copies[copy_idx].sites.insert(atom_idx - 1, new_site)
# 合并副本
expanded_structure = Structure(
lattice=np.dot(structure.lattice.matrix, np.diag([x, y, z])),
species=[],
coords=[],
coords_are_cartesian=False
)
for copy_idx in range(total_copies):
offset = np.array([
copy_idx // (y * z),
(copy_idx % (y * z)) // z,
copy_idx % z
])
for site in struct_copies[copy_idx]:
if site.species: # 只添加非空位
expanded_structure.append(
species=site.species,
coords=site.frac_coords + offset,
coords_are_cartesian=False,
properties=site.properties
)
expanded_structure = merge_structures(struct_copies,factors)
return expanded_structure
def process_occupation(input_file,output_file,explict_element = ["Li"],expect_cifnumber = 10,random_time=1):
struct = Structure.from_file(input_file)
space_group_info = struct.get_space_group_info()
space_group_symbol = space_group_info[0]
all_spacegroup_symbols = [spglib.get_spacegroup_type(i) for i in range(1, 531)]
symbol = all_spacegroup_symbols[0]
for symbol_i in all_spacegroup_symbols:
if space_group_symbol == symbol_i.international_short:
symbol = symbol_i
space_type = typejudge(symbol.number)
print(f"当前空间群符号为{space_group_symbol},序号为{symbol.number},对应的晶体体系为{space_type}")
occupation_list = process_cif_file(cif_file_path=input_file,explict_element=explict_element)
print(occupation_list)
for occupation in occupation_list:
atom_number, target_multiplier=calculate_supercell_factor(occupation["occupancy"])
divides = []
if space_type == "Hexagonal":
print('当前为六方晶系,暂不处理')
if space_type == "Cubic":
print("当前为立方晶体,三个方向同步")
divides = factorize_to_three_factors(target_multiplier,"xyz")
else:
print("为其他晶系,假设三个方向不同")
divides = factorize_to_three_factors(target_multiplier,)
print(divides)
for it in divides:
end_str = f'x{it["x"]}y{it["y"]}z{it["z"]}'
for i in range(random_time):
expand_struct=expand_structure(struct,it,occupation["number"],atom_number)
expand_struct.to_file(output_file)
print(it)
else:
print(f"不存在除{explict_element}以外的共占位原子")
process_occupation("../data/input_pre/ICSD_1234.cif", "haha.cif", explict_element=[], expect_cifnumber=1, random_time=1)