How-To 指南
本章提供常见任务的实用代码模板和解决方案。
计算特定材料的弹性常数
铝的完整计算
from thermoelasticsim.elastic.benchmark import run_zero_temp_benchmark
from thermoelasticsim.elastic.materials import ALUMINUM_FCC
from thermoelasticsim.potentials.eam import EAMAl1Potential
# 标准计算
results = run_zero_temp_benchmark(
material_params=ALUMINUM_FCC,
potential=EAMAl1Potential(),
supercell_size=(3, 3, 3),
output_dir='results/Al',
save_json=True,
precision=False
)
# 打印结果
print(f"铝的弹性常数 (0K):")
print(f" C11 = {results['elastic_constants']['C11']:.1f} GPa")
print(f" C12 = {results['elastic_constants']['C12']:.1f} GPa")
print(f" C44 = {results['elastic_constants']['C44']:.1f} GPa")
print(f" 体模量 B = {results['elastic_moduli']['bulk_modulus']:.1f} GPa")
铜的完整计算
from thermoelasticsim.elastic.materials import COPPER_FCC
from thermoelasticsim.potentials.eam import EAMCu1Potential
# 高精度计算
results = run_zero_temp_benchmark(
material_params=COPPER_FCC,
potential=EAMCu1Potential(),
supercell_size=(3, 3, 3),
output_dir='results/Cu',
save_json=True,
precision=True # 使用高精度模式
)
自定义材料参数
from thermoelasticsim.elastic.materials import MaterialParameters
from thermoelasticsim.potentials.eam import EAMAl1Potential
# 创建自定义材料(以 Al 为例,便于直接使用 EAMAl1 势)
my_material = MaterialParameters(
name="CustomAl",
symbol="Al",
mass_amu=26.9815, # amu
lattice_constant=4.06, # Å(略作调整)
structure="fcc",
literature_elastic_constants={"C11": 110.0, "C12": 61.0, "C44": 33.0}
)
# 使用自定义材料 + 对应势函数
results = run_zero_temp_benchmark(
material_params=my_material,
potential=EAMAl1Potential(),
supercell_size=(3, 3, 3)
)
LJ体系最小示例
完整的LJ计算流程
import numpy as np
from thermoelasticsim.core.crystalline_structures import CrystallineStructureBuilder
from thermoelasticsim.potentials.lennard_jones import LennardJonesPotential
from thermoelasticsim.utils.utils import NeighborList, EV_TO_GPA
from thermoelasticsim.elastic.deformation import Deformer
from thermoelasticsim.elastic.deformation_method.zero_temp import StructureRelaxer
# 1. 创建氩晶体(使用较小的 3×3×3 超胞,示例足够)
builder = CrystallineStructureBuilder()
cell = builder.create_fcc('Ar', lattice_constant=5.26, supercell=(3, 3, 3))
# 2. 设置LJ势(以邻居列表驱动)
lj = LennardJonesPotential(
epsilon=0.0104, # eV
sigma=3.40, # Å
cutoff=12.5 # Å (约3.7σ)
)
neighbor_list = NeighborList(cutoff=12.5, skin=0.5)
# 3. 包装势函数:自动维护邻居列表
class LJWithNeighborList:
def __init__(self, lj_potential, neighbor_list):
self.lj = lj_potential
self.nl = neighbor_list
def calculate_forces(self, cell):
self.nl.build(cell)
self.lj.calculate_forces(cell, self.nl)
def calculate_energy(self, cell):
self.nl.build(cell)
return self.lj.calculate_energy(cell, self.nl)
potential_wrapped = LJWithNeighborList(lj, neighbor_list)
# 4. 形变与弛豫器(API 参数名对齐源码)
deformer = Deformer(delta=0.01, num_steps=5)
relaxer = StructureRelaxer(
optimizer_type='L-BFGS',
optimizer_params={'gtol': 1e-5, 'maxiter': 5000}
)
# 5. 计算 C11 与 C12(单轴 σ_xx/ε、σ_yy/ε 的斜率)
strains = np.array([-0.01, -0.005, 0.0, 0.005, 0.01])
stresses_xx = [] # σ_xx (eV/ų)
stresses_yy = [] # σ_yy (eV/ų)
for strain in strains:
# 施加形变 F = diag(1+ε, 1, 1)
F = np.eye(3)
F[0, 0] = 1.0 + strain
deformed = cell.copy()
deformer.apply_deformation(deformed, F)
# 形变后内部弛豫(固定晶格,优化内部坐标)
relaxer.internal_relax(deformed, potential_wrapped)
# 应力(维里项;单位 eV/ų),取 σ_xx
stress_tensor = deformed.calculate_stress_tensor(potential_wrapped)
stresses_xx.append(stress_tensor[0, 0])
stresses_yy.append(stress_tensor[1, 1])
print(f"应变 {strain:+.3f}: σ_xx = {stress_tensor[0,0]:.6f} eV/ų, σ_yy = {stress_tensor[1,1]:.6f} eV/ų")
# 6. 线性拟合与换算到 GPa
coeffs_xx = np.polyfit(strains, stresses_xx, 1)
coeffs_yy = np.polyfit(strains, stresses_yy, 1)
C11 = coeffs_xx[0] * EV_TO_GPA
C12 = coeffs_yy[0] * EV_TO_GPA
# 计算 R²(决定系数)
ypred_xx = np.polyval(coeffs_xx, strains)
ypred_yy = np.polyval(coeffs_yy, strains)
ss_res_xx = np.sum((np.array(stresses_xx) - ypred_xx) ** 2)
ss_tot_xx = np.sum((np.array(stresses_xx) - np.mean(stresses_xx)) ** 2)
ss_res_yy = np.sum((np.array(stresses_yy) - ypred_yy) ** 2)
ss_tot_yy = np.sum((np.array(stresses_yy) - np.mean(stresses_yy)) ** 2)
r2_xx = 1.0 - ss_res_xx / ss_tot_xx if ss_tot_xx != 0 else 1.0
r2_yy = 1.0 - ss_res_yy / ss_tot_yy if ss_tot_yy != 0 else 1.0
print(f"\nLJ氩体系:")
print(f"C11 ≈ {C11:.2f} GPa (R²={r2_xx:.6f})")
print(f"C12 ≈ {C12:.2f} GPa (R²={r2_yy:.6f})")
处理周期性边界
# 确保LJ势正确处理PBC
def check_pbc_consistency(cell, potential):
"""检查PBC实现的一致性"""
# 保存原始位置
orig_pos = cell.atoms[0].position.copy()
# 计算原始能量
E1 = potential.calculate_energy(cell)
# 移动原子跨越边界
cell.atoms[0].position += cell.lattice_vectors[0]
E2 = potential.calculate_energy(cell)
# 恢复
cell.atoms[0].position = orig_pos
# 能量应该相同
assert abs(E1 - E2) < 1e-10, f"PBC错误: {E1} != {E2}"
print("PBC一致性检查通过")
尺寸收敛测试
系统尺寸扫描
import numpy as np
def size_convergence_study(material='Al', sizes=None):
"""研究超胞尺寸对弹性常数的影响"""
if sizes is None:
# 示例范围不超过 4×4×4;555 对含截断的势并不优于 4×4×4
sizes = [(2,2,2), (3,3,3), (4,4,4)]
from thermoelasticsim.elastic.materials import ALUMINUM_FCC
from thermoelasticsim.potentials.eam import EAMAl1Potential
results = []
for size in sizes:
print(f"\n计算 {size} 超胞...")
res = run_zero_temp_benchmark(
material_params=ALUMINUM_FCC,
potential=EAMAl1Potential(),
supercell_size=size,
save_json=False
)
n_atoms = np.prod(size) * 4 # FCC
results.append({
'size': size,
'n_atoms': n_atoms,
'C11': res['elastic_constants']['C11'],
'C12': res['elastic_constants']['C12'],
'C44': res['elastic_constants']['C44'],
'time': res.get('computation_time', 0)
})
# 简要输出结果(避免外部依赖如 pandas/matplotlib)
print("\n尺寸收敛结果:")
for row in results:
size = row['size']
print(f" {size}: C11={row['C11']:.2f} GPa, C12={row['C12']:.2f} GPa, C44={row['C44']:.2f} GPa, N={row['n_atoms']} | "
f"文献: C11={ALUMINUM_FCC.literature_elastic_constants['C11']:.1f}, "
f"C12={ALUMINUM_FCC.literature_elastic_constants['C12']:.1f}, "
f"C44={ALUMINUM_FCC.literature_elastic_constants['C44']:.1f} GPa")
return results
# 运行收敛测试
_ = size_convergence_study('Al')
误差条估计
from dataclasses import replace
from thermoelasticsim.potentials.eam import EAMAl1Potential
def calculate_with_error_bars(material, size=(4,4,4), n_runs=5):
"""多次运行估计统计误差"""
all_results = []
for run in range(n_runs):
print(f"运行 {run+1}/{n_runs}...")
# 添加随机扰动(对 MaterialParameters 使用 dataclasses.replace)
perturbed_material = replace(
material,
lattice_constant=material.lattice_constant * (1 + np.random.normal(0, 1e-5))
)
res = run_zero_temp_benchmark(
material_params=perturbed_material,
potential=EAMAl1Potential(),
supercell_size=size,
save_json=False
)
all_results.append(res['elastic_constants'])
# 统计分析
C11_values = [r['C11'] for r in all_results]
C12_values = [r['C12'] for r in all_results]
C44_values = [r['C44'] for r in all_results]
print(f"\n统计结果 ({n_runs}次运行):")
print(f"C11 = {np.mean(C11_values):.1f} ± {np.std(C11_values):.1f} GPa")
print(f"C12 = {np.mean(C12_values):.1f} ± {np.std(C12_values):.1f} GPa")
print(f"C44 = {np.mean(C44_values):.1f} ± {np.std(C44_values):.1f} GPa")
回归测试
验证计算正确性
def regression_test():
"""回归测试确保结果一致性"""
# 预期值(来自之前的验证运行)
expected = {
'Al_C11': 114.3, # GPa
'Al_C12': 61.9,
'Al_C44': 31.6,
'Cu_C11': 176.0,
'Cu_C12': 124.0,
'Cu_C44': 82.0
}
tolerance = 1.0 # GPa
# 测试铝
al_results = run_zero_temp_benchmark(
ALUMINUM_FCC,
EAMAl1Potential(),
(3, 3, 3),
save_json=False
)
assert abs(al_results['elastic_constants']['C11'] - expected['Al_C11']) < tolerance
assert abs(al_results['elastic_constants']['C12'] - expected['Al_C12']) < tolerance
assert abs(al_results['elastic_constants']['C44'] - expected['Al_C44']) < tolerance
print("✓ 铝回归测试通过")
# 测试铜
cu_results = run_zero_temp_benchmark(
COPPER_FCC,
EAMCu1Potential(),
(3, 3, 3),
save_json=False
)
assert abs(cu_results['elastic_constants']['C11'] - expected['Cu_C11']) < tolerance * 2
assert abs(cu_results['elastic_constants']['C12'] - expected['Cu_C12']) < tolerance * 2
assert abs(cu_results['elastic_constants']['C44'] - expected['Cu_C44']) < tolerance * 2
print("✓ 铜回归测试通过")
print("\n所有回归测试通过!")
# 运行测试
regression_test()
单元测试模板
import unittest
import numpy as np
from thermoelasticsim.core.crystalline_structures import CrystallineStructureBuilder
from thermoelasticsim.elastic.materials import ALUMINUM_FCC, COPPER_FCC
from thermoelasticsim.potentials.eam import EAMAl1Potential, EAMCu1Potential
from thermoelasticsim.elastic.deformation_method.zero_temp import StructureRelaxer
from thermoelasticsim.elastic.benchmark import run_zero_temp_benchmark
class TestElasticConstants(unittest.TestCase):
"""弹性常数计算的单元测试"""
def setUp(self):
"""设置测试环境"""
self.builder = CrystallineStructureBuilder()
self.potential = EAMAl1Potential()
def test_cubic_symmetry(self):
"""测试立方对称性"""
cell = self.builder.create_fcc('Al', 4.05, (2, 2, 2))
# 计算应力张量
stress = cell.calculate_stress_tensor(self.potential)
# 检查对称性
np.testing.assert_allclose(stress[0,1], stress[1,0], rtol=1e-10)
np.testing.assert_allclose(stress[0,2], stress[2,0], rtol=1e-10)
np.testing.assert_allclose(stress[1,2], stress[2,1], rtol=1e-10)
def test_zero_strain_zero_stress(self):
"""测试零应变零应力"""
cell = self.builder.create_fcc('Al', 4.05, (3, 3, 3))
# 完全弛豫到近零应力基态
relaxer = StructureRelaxer(
optimizer_type='L-BFGS',
optimizer_params={'gtol': 1e-6},
supercell_dims=(3, 3, 3)
)
ok = relaxer.full_relax(cell, self.potential)
self.assertTrue(ok)
# 应力应该接近零
stress = cell.calculate_stress_tensor(self.potential)
np.testing.assert_allclose(stress.diagonal(), 0, atol=1e-4)
def test_born_stability(self):
"""测试Born稳定性条件"""
results = run_zero_temp_benchmark(
ALUMINUM_FCC,
self.potential,
(3, 3, 3)
)
C11 = results['elastic_constants']['C11']
C12 = results['elastic_constants']['C12']
C44 = results['elastic_constants']['C44']
# Born条件
self.assertGreater(C11 - C12, 0)
self.assertGreater(C11 + 2*C12, 0)
self.assertGreater(C44, 0)
if __name__ == '__main__':
unittest.main()
批处理脚本
参数扫描
import itertools
import json
from thermoelasticsim.elastic.benchmark import run_zero_temp_benchmark
from thermoelasticsim.elastic.materials import ALUMINUM_FCC, COPPER_FCC
from thermoelasticsim.potentials.eam import EAMAl1Potential, EAMCu1Potential
def parameter_scan():
"""扫描不同参数组合(无第三方依赖)"""
materials = ['Al', 'Cu']
sizes = [(3,3,3), (4,4,4)]
precisions = [False, True]
all_results = []
for mat, size, prec in itertools.product(materials, sizes, precisions):
print(f"\n处理: {mat}, {size}, precision={prec}")
material = ALUMINUM_FCC if mat == 'Al' else COPPER_FCC
potential = EAMAl1Potential() if mat == 'Al' else EAMCu1Potential()
try:
res = run_zero_temp_benchmark(
material_params=material,
potential=potential,
supercell_size=size,
precision=prec,
save_json=False
)
all_results.append({
'material': mat,
'size': size,
'precision': prec,
'C11': res['elastic_constants']['C11'],
'C12': res['elastic_constants']['C12'],
'C44': res['elastic_constants']['C44'],
'time': res.get('computation_time', 0)
})
except Exception as e:
print(f" 错误: {e}")
continue
with open('parameter_scan_results.json', 'w') as f:
json.dump(all_results, f, indent=2)
print("\n参数扫描结果:")
for r in all_results:
print(f" {r['material']} {r['size']} precision={r['precision']}: C11={r['C11']:.1f}, C12={r['C12']:.1f}, C44={r['C44']:.1f} GPa")
return all_results
并行计算
说明:并行化示例依赖额外的序列化工具函数,后续将以完整工具提供。当前版本推荐使用单进程示例以保证可运行性。
数据后处理
结果可视化
说明:可视化示例依赖 matplotlib/seaborn,安装后可按需绘制。为确保示例可运行性,本节省略具体代码。
导出为其他格式
说明:CSV/LaTeX/图形导出属于配套工具,后续将以 utils 子模块提供。当前版本建议直接打印或保存 JSON。
小结
本章提供了实用的代码模板:
材料计算:Al、Cu及自定义材料
LJ体系:完整的计算流程
收敛测试:尺寸和误差分析
回归测试:确保计算正确性
批处理:参数扫描和并行计算
数据处理:可视化和导出
这些模板可直接用于实际计算任务。