定义基于图论的离子团簇分析方法与实现细节,涵盖距离判据、最近邻规则、团簇识别、结构判定、边界条件、验证规则与输出数据结构。 以下通过LiTFSI分子(包含O和N原子)与Li+的案例进行分析。
- 判据来源:基于径向分布函数(RDF)的第一配位壳层。
- 物理意义:第一峰后的最小值代表第一配位壳层的边界,超过此距离的离子对不再有强相互作用。
- Li⁺-O(TFSI⁻) 判据:
- 阈值:d_Li-O < 3.00 Å
- 基于:RDF_Li-O(r) 第一峰后最小值位置
- Li⁺-N(TFSI⁻) 判据:
- 阈值:d_Li-N < 5.26 Å
- 基于:RDF_Li-N(r) 第一峰后最小值位置
函数 计算离子间最短距离(Li离子, TFSI离子):
最短距离 = 无穷大
对于 每个O原子 in TFSI离子.O原子列表:
d = 欧几里得距离(Li离子.位置, O原子.位置)
如果 d < 最短距离:
最短距离 = d
最短距离类型 = "Li-O"
N原子距离 = 欧几里得距离(Li离子.位置, TFSI离子.N原子.位置)
如果 N原子距离 < 最短距离:
最短距离 = N原子距离
最短距离类型 = "Li-N"
返回 最短距离, 最短距离类型
根据 Figure S3 的说明,存在以下几种情况:
函数 判断边类型(节点A, 节点B):
// A和B必须是不同类型的离子
如果 (A.类型 == B.类型):
返回 "无边"
// 检查A到B的最近邻关系
A的最近反离子 = 寻找最近反离子(A, 所有反离子)
B的最近反离子 = 寻找最近反离子(B, 所有反离子)
如果 (A的最近反离子 == B) 且 (B的最近反离子 == A):
返回 "强边_互为最近" // 最强的连接
否则如果 (A的最近反离子 == B) 或 (B的最近反离子 == A):
返回 "边_单向最近" // 较强的连接
否则:
返回 "无边" // 不形成边
推荐使用深度优先搜索(DFS)或广度优先搜索(BFS):
函数 DFS查找连通分量(图G):
已访问 = 空集
连通分量列表 = []
对于 每个节点v in G.所有节点:
如果 v 不在 已访问中:
当前分量 = []
栈 = [v]
当 栈非空:
当前节点 = 栈.弹出()
如果 当前节点 不在 已访问中:
已访问.添加(当前节点)
当前分量.添加(当前节点)
对于 每个邻居 in G.获取邻居(当前节点):
如果 邻居 不在 已访问中:
栈.添加(邻居)
连通分量列表.添加(当前分量)
返回 连通分量列表
{
"团簇类型定义": {
"SSIP": {
"定义": "完全被溶剂分离的单个离子",
"判据": "连通分量大小 == 1",
"子类型": ["SSIP_Li+", "SSIP_TFSI-"]
},
"CIP": {
"定义": "接触离子对",
"判据": "连通分量大小 == 2 且 包含1个Li+ 和 1个TFSI-",
"化学式": "Li·TFSI"
},
"AGG": {
"定义": "聚集体",
"判据": "连通分量大小 >= 3",
"子类型": {
"小型AGG": {
"大小": "3-5个离子",
"示例": ["Li₂TFSI⁺", "LiTFSI₂⁻", "Li₂TFSI₂", "Li₃TFSI₂⁺"]
},
"中型AGG": {
"大小": "6-10个离子"
},
"大型AGG": {
"大小": ">10个离子",
"特征": "可能形成离子网络"
}
}
}
}
}AGG 必须满足以下条件:
- 最小尺寸要求:至少包含 3 个离子
- 连通性要求:所有离子通过边连接形成单一连通图
- 稳定性要求:内部至少存在一条“强边_互为最近”
{
"函数": "计算AGG属性(AGG团簇)",
"实现": [
"属性 = {",
" \"总离子数\": AGG团簇.节点数量,",
" \"Li+数量\": 统计(AGG团簇, 类型=\"Li+\"),",
" \"TFSI-数量\": 统计(AGG团簇, 类型=\"TFSI-\"),",
" \"净电荷\": Li+数量 - TFSI-数量,",
" \"电荷密度\": 净电荷 / 总离子数,",
" \"化学式\": 生成化学式(Li+数量, TFSI-数量)",
"}",
"",
"// 判断AGG类型",
"如果 属性.净电荷 == 0:",
" 属性[\"类型\"] = \"中性AGG\"",
"否则如果 属性.净电荷 > 0:",
" 属性[\"类型\"] = \"阳离子AGG\"",
"否则:",
" 属性[\"类型\"] = \"阴离子AGG\"",
"",
"返回 属性"
]
}函数 识别特殊AGG模式(AGG):
// 线性链状结构
如果 是线性链(AGG):
返回 "链状AGG"
// 环状结构
如果 包含环(AGG):
环大小 = 获取最小环大小(AGG)
返回 f"环状AGG_环大小{环大小}"
// 分支结构
如果 存在分支点(AGG):
分支度 = 计算最大分支度(AGG)
返回 f"分支AGG_度{分支度}"
返回 "不规则AGG"
考虑轨迹的输出格式为unwrap的坐标,因此这一步为可选项
{
"函数": "考虑周期性边界(位置1, 位置2, 盒子尺寸)",
"说明": "最小镜像约定",
"实现": [
"delta = 位置2 - 位置1",
"对于 每个维度i in [x, y, z]:",
" 如果 abs(delta[i]) > 盒子尺寸[i] / 2:",
" 如果 delta[i] > 0: delta[i] -= 盒子尺寸[i]",
" 否则: delta[i] += 盒子尺寸[i]",
"返回 sqrt(delta.x² + delta.y² + delta.z²)"
]
}{
"函数": "团簇生存时间分析(轨迹)",
"实现": [
"团簇历史 = {}",
"对于 每个时间步t in 轨迹:",
" 当前团簇 = 识别所有团簇(轨迹[t])",
" 对于 每个团簇 in 当前团簇:",
" 团簇ID = 生成唯一ID(团簇)",
" 如果 团簇ID in 团簇历史:",
" 团簇历史[团簇ID].生存时间 += 时间步长",
" 否则:",
" 团簇历史[团簇ID] = {",
" \"起始时间\": t,",
" \"生存时间\": 时间步长,",
" \"类型\": 团簇.类型",
" }",
"返回 团簇历史"
]
}函数 验证团簇划分(所有团簇, 总离子数):
// 检查1:所有离子都被分配
已分配离子 = 0
对于 每个团簇 in 所有团簇:
已分配离子 += 团簇.离子数
断言(已分配离子 == 总离子数, "存在未分配的离子")
// 检查2:无重复分配
离子集合 = 空集
对于 每个团簇 in 所有团簇:
对于 每个离子 in 团簇.离子列表:
断言(离子 不在 离子集合中, f"离子{离子}被重复分配")
离子集合.添加(离子)
// 检查3:电荷守恒
总电荷 = 0
对于 每个团簇 in 所有团簇:
总电荷 += 团簇.净电荷
断言(总电荷 == 系统总电荷, "电荷不守恒")
返回 True
函数 检查物理合理性(团簇):
// 检查最大配位数
对于 每个Li离子 in 团簇.Li离子列表:
配位数 = 计算配位数(Li离子)
断言(配位数 <= 6, f"Li+配位数{配位数}超过物理极限")
// 检查TFSI的配位模式
对于 每个TFSI in 团簇.TFSI列表:
配位Li数 = 计算配位Li数(TFSI)
断言(配位Li数 <= 4, f"TFSI配位{配位Li数}个Li+超过极限")
返回 True
{
"时间步": 1000,
"系统信息": {
"Li+总数": 180,
"TFSI-总数": 180,
"浓度": "10 mol/kg"
},
"团簇统计": {
"SSIP": {
"Li+": {
"数量": 15,
"离子ID": [1, 5, 12],
"百分比": 8.33
},
"TFSI-": {
"数量": 10,
"离子ID": [2, 7, 19],
"百分比": 5.56
}
},
"CIP": {
"数量": 45,
"离子对": [
{ "Li": 3, "TFSI": 4, "距离": 2.85, "连接类型": "Li-O" }
],
"百分比": 50.0
},
"AGG": {
"总数": 25,
"详细信息": [
{
"ID": "AGG_001",
"尺寸": 5,
"组成": "Li3TFSI2",
"净电荷": 1,
"类型": "阳离子AGG",
"结构类型": "链状",
"离子列表": [6, 8, 11, 15, 20]
}
],
"尺寸分布": {
"3-5": 15,
"6-10": 8,
">10": 2
},
"平均尺寸": 5.8,
"最大尺寸": 15
}
}
}- 距离计算必须考虑 PBC(周期性边界条件)的最小镜像约定。
- 最近邻的判定应在满足距离阈值的前提下进行;可先构建候选边集(满足阈值),再应用最近邻规则筛边。