电导率及扩散计算
This commit is contained in:
35963
data/log.lammps
Normal file
35963
data/log.lammps
Normal file
File diff suppressed because it is too large
Load Diff
1545
data/msd_li.dat
Normal file
1545
data/msd_li.dat
Normal file
File diff suppressed because it is too large
Load Diff
18
main.py
Normal file
18
main.py
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
from utils.MSD import *
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
# file_path_li = 'data/msd_li.dat'
|
||||||
|
# final_msd = plot_and_get_final_msd(file_path_li, ion_name='Li⁺')
|
||||||
|
|
||||||
|
num_li_ions = 144 # !! 请务必用您体系的真实值替换此示例值 !!
|
||||||
|
|
||||||
|
# 调用新函数
|
||||||
|
# 它会自动从 log.lammps 读取温度和体积
|
||||||
|
results = calculate_conductivity_from_msd(
|
||||||
|
msd_file_path='data/msd_li.dat',
|
||||||
|
log_file_path='data/log.lammps',
|
||||||
|
ion_name='Li⁺',
|
||||||
|
charge=1,
|
||||||
|
num_ions=num_li_ions,
|
||||||
|
fit_fraction=0.5 # 可以根据图像调整此值
|
||||||
|
)
|
||||||
272
utils/MSD.py
272
utils/MSD.py
@@ -0,0 +1,272 @@
|
|||||||
|
import numpy as np
|
||||||
|
import matplotlib.pyplot as plt
|
||||||
|
|
||||||
|
import numpy as np
|
||||||
|
import matplotlib.pyplot as plt
|
||||||
|
from scipy.stats import linregress
|
||||||
|
|
||||||
|
|
||||||
|
def plot_and_get_final_msd(file_path, ion_name='Ion',timestep_ps = 0.2):
|
||||||
|
"""
|
||||||
|
读取LAMMPS输出的MSD数据文件,绘制MSD-时间图,并返回最后一个MSD值。
|
||||||
|
|
||||||
|
参数:
|
||||||
|
file_path (str): msd_*.dat文件的路径。
|
||||||
|
ion_name (str, optional): 离子的名称,用于图表标题和标签。默认为'Ion'。
|
||||||
|
|
||||||
|
返回:
|
||||||
|
float: 文件中记录的最后一个MSD值。
|
||||||
|
"""
|
||||||
|
# LAMMPS输出的MSD文件通常会跳过前几行注释,使用np.loadtxt可以轻松处理
|
||||||
|
try:
|
||||||
|
# 尝试直接读取,通常第一列是Timestep,第四列是总MSD
|
||||||
|
# 文件格式: Timestep, Time, MSD_x, MSD_y, MSD_z, MSD_total
|
||||||
|
# 但fix ave/time的输出是: Timestep, c_msd[1], c_msd[2], c_msd[3], c_msd[4]
|
||||||
|
# c_msd[4] 就是总MSD
|
||||||
|
data = np.loadtxt(file_path, comments='#')
|
||||||
|
timesteps = data[:, 0]
|
||||||
|
msd_values = data[:, -1] # 最后一列通常是总MSD
|
||||||
|
except Exception as e:
|
||||||
|
print(f"读取文件时出错: {e}")
|
||||||
|
print("请检查文件格式是否正确。")
|
||||||
|
return None
|
||||||
|
|
||||||
|
# 从in.lmp文件我们知道时间步长是0.001 ps
|
||||||
|
|
||||||
|
time_ps = timesteps * timestep_ps
|
||||||
|
|
||||||
|
# --- 绘图 ---
|
||||||
|
plt.figure(figsize=(10, 6))
|
||||||
|
plt.plot(time_ps/1000, msd_values, label=f'{ion_name} MSD')
|
||||||
|
|
||||||
|
plt.xlabel('Time (ps)', fontsize=14)
|
||||||
|
plt.ylabel('MSD (Ų)', fontsize=14)
|
||||||
|
plt.title(f'Mean Squared Displacement (MSD) of {ion_name}', fontsize=16)
|
||||||
|
plt.grid(True)
|
||||||
|
plt.legend()
|
||||||
|
plt.show()
|
||||||
|
|
||||||
|
# --- 输出并返回最终值 ---
|
||||||
|
final_msd_value = msd_values[-1]
|
||||||
|
final_time = time_ps[-1]
|
||||||
|
|
||||||
|
print(f"文件路径: {file_path}")
|
||||||
|
print(f"总模拟时间: {final_time/1000:.2f} ps")
|
||||||
|
print(f"最终MSD值: {final_msd_value:.4f} Ų")
|
||||||
|
|
||||||
|
return final_msd_value
|
||||||
|
|
||||||
|
|
||||||
|
def calculate_conductivity_from_msd(
|
||||||
|
msd_file_path,
|
||||||
|
log_file_path,
|
||||||
|
ion_name,
|
||||||
|
charge,
|
||||||
|
num_ions,
|
||||||
|
fit_fraction=0.5
|
||||||
|
):
|
||||||
|
"""
|
||||||
|
从MSD数据计算电导率 (v2)。
|
||||||
|
自动从 log.lammps 文件提取温度和体积。
|
||||||
|
|
||||||
|
参数:
|
||||||
|
msd_file_path (str): msd_*.dat 文件的路径。
|
||||||
|
log_file_path (str): log.lammps 文件的路径。
|
||||||
|
ion_name (str): 离子名称 (例如 'Li⁺')。
|
||||||
|
charge (int): 离子的电荷数 (例如 Li⁺ 为 1)。
|
||||||
|
num_ions (int): 模拟盒子中该离子的总数量。
|
||||||
|
fit_fraction (float): 用于线性拟合的数据比例 (使用最后部分)。
|
||||||
|
|
||||||
|
返回:
|
||||||
|
dict: 包含计算结果的字典。
|
||||||
|
"""
|
||||||
|
# --- 1. 从 log 文件自动提取参数 ---
|
||||||
|
sim_params = extract_params_from_log(log_file_path)
|
||||||
|
if not sim_params or not sim_params["avg_volume"] or not sim_params["avg_temp"]:
|
||||||
|
print("无法从 log 文件获取必要参数,计算中止。")
|
||||||
|
return None
|
||||||
|
|
||||||
|
temp_K = sim_params["avg_temp"]
|
||||||
|
volume_A3 = sim_params["avg_volume"]
|
||||||
|
|
||||||
|
# --- 2. 读取和处理 MSD 数据 ---
|
||||||
|
try:
|
||||||
|
data = np.loadtxt(msd_file_path, comments='#')
|
||||||
|
timesteps = data[:, 0]
|
||||||
|
msd_values = data[:, -1] # 总MSD (Ų)
|
||||||
|
except Exception as e:
|
||||||
|
print(f"读取文件 '{msd_file_path}' 时出错: {e}")
|
||||||
|
return None
|
||||||
|
|
||||||
|
timestep_ps = 0.001 # 时间步长 (ps)
|
||||||
|
time_ps = timesteps * timestep_ps
|
||||||
|
|
||||||
|
# --- 3. 线性拟合计算扩散系数 ---
|
||||||
|
fit_start_index = int(len(time_ps) * (1 - fit_fraction))
|
||||||
|
fit_time_ps = time_ps[fit_start_index:]
|
||||||
|
fit_msd_values = msd_values[fit_start_index:]
|
||||||
|
|
||||||
|
if len(fit_time_ps) < 2:
|
||||||
|
print("错误: 用于拟合的数据点不足 (少于2个),无法进行线性回归。")
|
||||||
|
return None
|
||||||
|
|
||||||
|
slope, intercept, r_value, _, _ = linregress(fit_time_ps, fit_msd_values)
|
||||||
|
|
||||||
|
diff_coeff_A2_ps = slope / 6
|
||||||
|
diff_coeff_cm2_s = diff_coeff_A2_ps * 1e-4
|
||||||
|
diff_coeff_m2_s = diff_coeff_A2_ps * 1e-8
|
||||||
|
|
||||||
|
# --- 4. 计算电导率 (Nernst-Einstein) ---
|
||||||
|
e_charge = 1.60217663e-19 # (C)
|
||||||
|
kB = 1.380649e-23 # (J/K)
|
||||||
|
|
||||||
|
q = charge * e_charge
|
||||||
|
n = num_ions / (volume_A3 * 1e-30) # (m⁻³)
|
||||||
|
|
||||||
|
conductivity_S_m = (n * q ** 2 * diff_coeff_m2_s) / (kB * temp_K)
|
||||||
|
|
||||||
|
# --- 5. 可视化 ---
|
||||||
|
plt.figure(figsize=(12, 7))
|
||||||
|
plt.plot(time_ps, msd_values, 'b.', markersize=4, label='MSD Data')
|
||||||
|
fit_line = slope * fit_time_ps + intercept
|
||||||
|
plt.plot(fit_time_ps, fit_line, 'r-', linewidth=3, label=f'Linear Fit (R² = {r_value ** 2:.4f})')
|
||||||
|
|
||||||
|
plt.xlabel('Time (ps)', fontsize=14)
|
||||||
|
plt.ylabel('MSD (Ų)', fontsize=14)
|
||||||
|
plt.title(f'MSD Analysis for {ion_name}', fontsize=16)
|
||||||
|
plt.legend(fontsize=12)
|
||||||
|
plt.grid(True)
|
||||||
|
|
||||||
|
info_text = (
|
||||||
|
f"Diffusion Coefficient (D): {diff_coeff_cm2_s:.2e} cm²/s\n"
|
||||||
|
f"Conductivity (σ): {conductivity_S_m:.4f} S/m\n"
|
||||||
|
f"Avg Temp: {temp_K:.2f} K\n"
|
||||||
|
f"Avg Volume: {volume_A3:.2f} ų"
|
||||||
|
)
|
||||||
|
plt.text(0.05, 0.95, info_text, transform=plt.gca().transAxes, fontsize=12,
|
||||||
|
verticalalignment='top', bbox=dict(boxstyle='round,pad=0.5', fc='wheat', alpha=0.5))
|
||||||
|
|
||||||
|
plt.show()
|
||||||
|
|
||||||
|
# --- 6. 返回结果 ---
|
||||||
|
results = {
|
||||||
|
"ion": ion_name,
|
||||||
|
"diffusion_coefficient_cm2/s": diff_coeff_cm2_s,
|
||||||
|
"conductivity_S/m": conductivity_S_m,
|
||||||
|
"fit_R2": r_value ** 2,
|
||||||
|
"avg_temp_K": temp_K,
|
||||||
|
"avg_volume_A3": volume_A3
|
||||||
|
}
|
||||||
|
|
||||||
|
print("--- 最终计算结果 ---")
|
||||||
|
for key, value in results.items():
|
||||||
|
print(f"{key}: {value}")
|
||||||
|
|
||||||
|
return results
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def extract_params_from_log(log_file_path):
|
||||||
|
"""
|
||||||
|
从 log.lammps 文件中提取 NVT 生产阶段的平均参数。
|
||||||
|
|
||||||
|
参数:
|
||||||
|
log_file_path (str): log.lammps 文件的路径。
|
||||||
|
|
||||||
|
返回:
|
||||||
|
dict: 包含提取参数的字典 (avg_temp, avg_volume, total_atoms),如果失败则返回 None。
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
with open(log_file_path, 'r',encoding='utf-8',errors='ignore') as f:
|
||||||
|
lines = f.readlines()
|
||||||
|
except FileNotFoundError:
|
||||||
|
print(f"错误: log 文件 '{log_file_path}' 未找到。")
|
||||||
|
return None
|
||||||
|
|
||||||
|
# 初始化变量
|
||||||
|
params = {
|
||||||
|
"avg_temp": None,
|
||||||
|
"avg_volume": None,
|
||||||
|
"total_atoms": None
|
||||||
|
}
|
||||||
|
|
||||||
|
# --- 1. 提取总原子数 ---
|
||||||
|
# 通常在文件开头附近
|
||||||
|
for line in lines:
|
||||||
|
if "atoms" in line and "atom types" in line:
|
||||||
|
parts = line.split()
|
||||||
|
params["total_atoms"] = int(parts[0])
|
||||||
|
break # 找到后就停止
|
||||||
|
|
||||||
|
# --- 2. 定位并提取 NVT 生产阶段的热力学数据 ---
|
||||||
|
# 我们通过寻找 "run 2000000" 来定位生产阶段的开始
|
||||||
|
# 这是根据您提供的 in.lmp 文件
|
||||||
|
nvt_data_lines = []
|
||||||
|
in_nvt_section = False
|
||||||
|
|
||||||
|
for i, line in enumerate(lines):
|
||||||
|
# 寻找 NVT 生产阶段的开始标志
|
||||||
|
if "fix nvt_prod all nvt" in line:
|
||||||
|
# 找到 fix 命令后,热力学数据头通常在几行之后
|
||||||
|
# 我们从下一行的 "run" 命令开始找
|
||||||
|
if "run 2000000" in lines[i + 1]:
|
||||||
|
# 再往下找 "Step Time Temp..." 这样的表头
|
||||||
|
for j in range(i + 2, len(lines)):
|
||||||
|
if lines[j].strip().startswith("Step"):
|
||||||
|
header = lines[j].split()
|
||||||
|
in_nvt_section = True
|
||||||
|
break
|
||||||
|
if in_nvt_section:
|
||||||
|
continue # 跳转到下一个循环,开始读取数据
|
||||||
|
|
||||||
|
# 如果在 NVT 区域,并且行看起来像数据行,则读取
|
||||||
|
if in_nvt_section:
|
||||||
|
# 数据行以数字开头,并且列数与表头匹配
|
||||||
|
parts = line.strip().split()
|
||||||
|
if parts and parts[0].isdigit() and len(parts) == len(header):
|
||||||
|
nvt_data_lines.append(parts)
|
||||||
|
# "Loop time" 标志着一个 run 命令的结束
|
||||||
|
elif line.strip().startswith("Loop time"):
|
||||||
|
in_nvt_section = False
|
||||||
|
break # 生产阶段数据读取完毕
|
||||||
|
|
||||||
|
if not nvt_data_lines:
|
||||||
|
print("警告: 未能在 log 文件中找到 NVT 生产阶段的热力学数据。")
|
||||||
|
print("请检查 in.lmp 中的 run 命令是否为 'run 2000000'。")
|
||||||
|
return params # 即使没找到,也可能返回原子数
|
||||||
|
|
||||||
|
# --- 3. 计算平均值 ---
|
||||||
|
# 将数据转换为 numpy 数组以便于计算
|
||||||
|
try:
|
||||||
|
nvt_data = np.array(nvt_data_lines).astype(float)
|
||||||
|
except ValueError as e:
|
||||||
|
print("错误:在将日志数据转换为数字时失败。")
|
||||||
|
print("这通常意味着数据行中混入了非数字字符。")
|
||||||
|
print(f"原始错误信息: {e}")
|
||||||
|
# 可以打印出有问题的数据行来帮助调试
|
||||||
|
# for i, row in enumerate(nvt_data_lines):
|
||||||
|
# try:
|
||||||
|
# [float(x) for x in row]
|
||||||
|
# except ValueError:
|
||||||
|
# print(f"问题行号 {i}: {row}")
|
||||||
|
# break
|
||||||
|
return params
|
||||||
|
# 根据表头找到 Temp 和 Volume 所在的列索引
|
||||||
|
try:
|
||||||
|
temp_col = header.index("Temp")
|
||||||
|
vol_col = header.index("Volume")
|
||||||
|
except ValueError as e:
|
||||||
|
print(f"错误: 在日志表头中找不到列: {e}")
|
||||||
|
return params
|
||||||
|
print(nvt_data[1:5,temp_col])
|
||||||
|
print(nvt_data[1:5,vol_col])
|
||||||
|
params["avg_temp"] = np.mean(nvt_data[1:20, temp_col])
|
||||||
|
params["avg_volume"] = np.mean(nvt_data[1:20, vol_col])
|
||||||
|
|
||||||
|
print("--- 从 log.lammps 提取的参数 ---")
|
||||||
|
print(f"总原子数: {params['total_atoms']}")
|
||||||
|
print(f"NVT阶段平均温度: {params['avg_temp']:.2f} K")
|
||||||
|
print(f"NVT阶段平均体积: {params['avg_volume']:.4f} ų")
|
||||||
|
print("------------------------------------")
|
||||||
|
|
||||||
|
return params
|
||||||
|
|||||||
Reference in New Issue
Block a user