技术背景

在前面的两篇博客中,我们分别介绍了Ewald算法求解静电势能基于格点拉格朗日插值法的PME算法。在多种计算优化算法(Ewald求和、快速傅里叶变换、格点拉格朗日插值、截断近似)的加持下,使得我们不需要在实空间进行大量的迭代,也可以得到一个近似收敛的静电势能结果。相关的PME计算公式为:

\[\begin{align*}
E&=E^S+E^L-E^{self}\\
&=\sum_{i,j\in \{Neigh\}}\frac{q_iq_j}{4\pi\epsilon_0|\mathbf{r}_j-\mathbf{r}_i|}Erfc\left(\frac{|\mathbf{r}_j-\mathbf{r}_i|}{\sqrt{2}\sigma}\right)\\
&+\frac{V}{2k_xk_yk_z\epsilon_0}\sum_{|\mathbf{k}|>0}\frac{e^{-\frac{\sigma^2 k^2}{2}}}{k^2}|F_{\mathbf{k}}(Q)(m_x,m_y,m_z)|^2\\
&-\frac{1}{4\pi\epsilon_0}\frac{1}{\sqrt{2\pi}\sigma}\sum_{i=0}^{N-1}q_i^2
\end{align*}
\]

以下做一个Python版本的简单测试。

测试思路

为了对比PME算法的收敛性与实空间迭代的收敛性,我们先取一个一维的简单周期性点电荷体系,一方面通过对实空间盒子进行扩张,以得到一个收敛的趋势。另一方面通过PME算法直接截断计算静电势。然后对比两者的结果,按预期来说,PME的结果应该在大量的实空间迭代之后被近似到,也就是\(V_{PME}=V_{real}(N)\)。当然,因为不同的插值算法也有可能也会导致计算结果的差异性,所以这里使用了两种插值阶数来进行测试。

代码示例

import numpy as np
from scipy.special import erfc
from tqdm import trange
import matplotlib.pyplot as plt
np.random.seed(4) num_charges=1000
crd = np.random.random(num_charges) * 10
q = np.random.random(num_charges)
q -= np.sum(q) / q.shape[0]
pbc_box = np.array([10.], np.float64) def energy(crd, q, pbc_box, levels=0, epsilon=1):
qij = np.einsum('i,j->ij', q, q)
if levels == 0:
dis = np.abs(crd[:, None] - crd[None])
energy = np.triu(qij / (dis+1e-08) / 4 / np.pi / epsilon, k=1).sum()
else:
# left box
crd1 = crd - levels * pbc_box
dis = np.abs(crd[:, None] - crd1[None])
energy = np.triu(qij / (dis+1e-08) / 4 / np.pi / epsilon, k=0).sum()
# right box
crd2 = crd + levels * pbc_box
dis = np.abs(crd[:, None] - crd2[None])
energy += np.triu(qij / (dis+1e-08) / 4 / np.pi / epsilon, k=0).sum()
return energy def pme4(crd, q, pbc_box, epsilon=1):
sigma = (pbc_box[0] ** 2 / crd.shape[0]) ** (1/6) / np.sqrt(2*np.pi)
qij = np.einsum('i,j->ij', q, q)
dis = np.abs(crd[:, None] - crd[None])
dis = np.where(dis<pbc_box-dis, dis, pbc_box-dis)
coe = erfc(dis / np.sqrt(2) / sigma)
real_energy = np.sum(coe * np.triu(qij / (dis+1e-08) / 4 / np.pi / epsilon, k=1))
self_energy = np.sum(q ** 2) / np.sqrt(2 * np.pi) / sigma / 4 / np.pi / epsilon
Q = np.zeros(10, dtype=np.float64)
for idx in range(crd.shape[0]):
x_floor = np.floor(crd[idx])
x_shift = np.array([-1.5, -0.5, 0.5, 1.5], np.float32)
x_idx = (np.floor(x_floor + x_shift) % 10).astype(np.int32)
x = x_shift
Q[x_idx[0]] += q[idx]*(-8*x[0]**3+12*x[0]**2+2*x[0]-3)/48
Q[x_idx[1]] += q[idx]*(8*x[1]**3-4*x[1]**2-18*x[1]+9)/16
Q[x_idx[2]] += q[idx]*(-8*x[2]**3-4*x[2]**2+18*x[2]+9)/16
Q[x_idx[3]] += q[idx]*(8*x[3]**3+12*x[3]**2-2*x[3]-3)/48
Q_ifft = np.fft.ifft(Q)
sk = np.abs(Q_ifft) ** 2 * Q.shape[0]
k = np.arange(Q.shape[0]) * 2 * np.pi / Q.shape[0]
res_energy = np.sum(np.exp(-0.5*sigma**2*k[1:]**2)*sk[1:]/k[1:]**2)/2/epsilon/(2*np.pi/pbc_box[0])
return real_energy - self_energy + res_energy def pme6(crd, q, pbc_box, epsilon=1):
sigma = (pbc_box[0] ** 2 / crd.shape[0]) ** (1/6) / np.sqrt(2*np.pi)
qij = np.einsum('i,j->ij', q, q)
dis = np.abs(crd[:, None] - crd[None])
dis = np.where(dis<pbc_box-dis, dis, pbc_box-dis)
coe = erfc(dis / np.sqrt(2) / sigma)
real_energy = np.sum(coe * np.triu(qij / (dis+1e-08) / 4 / np.pi / epsilon, k=1))
self_energy = np.sum(q ** 2) / np.sqrt(2 * np.pi) / sigma / 4 / np.pi / epsilon
Q = np.zeros(10, dtype=np.float64)
for idx in range(crd.shape[0]):
x_floor = np.floor(crd[idx])
x_shift = np.array([-2.5, -1.5, -0.5, 0.5, 1.5, 2.5], np.float32)
x_idx = (np.floor(x_floor + x_shift) % 10).astype(np.int32)
x = x_shift
Q[x_idx[0]] += -q[idx]*(x[0]**5-2.5*x[0]**4-1.5*x[0]**3+3.75*x[0]**2+0.3125*x[0]-0.78125)/120
Q[x_idx[1]] += q[idx]*(x[1]**5-1.5*x[1]**4-6.5*x[1]**3+9.75*x[1]**2+1.5625*x[1]-2.34375)/24
Q[x_idx[2]] += -q[idx]*(x[2]**5-0.5*x[2]**4-8.5*x[2]**3+4.25*x[2]**2+14.0625*x[2]-7.03125)/12
Q[x_idx[3]] += q[idx]*(x[2]**5+0.5*x[2]**4-8.5*x[2]**3-4.25*x[2]**2+14.0625*x[2]+7.03125)/12
Q[x_idx[4]] += -q[idx]*(x[1]**5+1.5*x[1]**4-6.5*x[1]**3-9.75*x[1]**2+1.5625*x[1]+2.34375)/24
Q[x_idx[5]] += q[idx]*(x[0]**5+2.5*x[0]**4-1.5*x[0]**3-3.75*x[0]**2+0.3125*x[0]+0.78125)/120
Q_ifft = np.fft.ifft(Q)
sk = np.abs(Q_ifft) ** 2 * Q.shape[0]
k = np.arange(Q.shape[0]) * 2 * np.pi / Q.shape[0]
res_energy = np.sum(np.exp(-0.5*sigma**2*k[1:]**2)*sk[1:]/k[1:]**2)/2/epsilon/(2*np.pi/pbc_box[0])
return real_energy - self_energy + res_energy N = 100000
e = np.zeros(N)
for i in trange(N):
e[i] = energy(crd, q, pbc_box, levels=i)
e = np.cumsum(e)
print (e[0], e[-1]) e2 = pme4(crd, q, pbc_box)
print (e2) e3 = pme6(crd, q, pbc_box)
print (e3) x = list(range(N))
plt.figure()
plt.xlabel('Box Layers')
plt.ylabel("Energy")
plt.plot(x, e, label='Normal')
plt.plot(x, np.ones_like(x) * e2, label='PME4')
plt.plot(x, np.ones_like(x) * e3, label='PME6')
plt.legend()
plt.savefig('energy.png')

运行输出为:

-6016.973545020364 -6008.267263384039
-6010.335037181866
-6009.780676419067

这个输出结果表示,不加任何额外的Box时,计算得到的电势能为-6016,在左右各加了10万个Box之后,得到的静电势能结果变为-6008。而PME计算使用4阶拉格朗日插值时一步就可以得到-6010,如果使用6阶的插值,一步就可以得到-6009。总体的不同Box Level下的静电势计算结果对比为:

这个结果表示,如果我们使用6阶插值的PME算法,单步的计算就可以得到实空间迭代10000个Box的近似结果。

总结概要

基于前面几篇博客关于PME算法的理论推导,本文给出了一个简单版本的Python代码实现,并且对比了PME算法相比于实空间迭代算法的优越性。从结果上来看,一维的静电势能计算中,PME单步得到的计算结果非常接近于实空间迭代1万个Box的近似结果。

版权声明

本文首发链接为:https://www.cnblogs.com/dechinphy/p/pme-python.html

作者ID:DechinPhy

更多原著文章:https://www.cnblogs.com/dechinphy/

请博主喝咖啡:https://www.cnblogs.com/dechinphy/gallery/image/379634.html

PME算法简单Python实现的更多相关文章

  1. 机器学习算法与Python实践之(四)支持向量机(SVM)实现

    机器学习算法与Python实践之(四)支持向量机(SVM)实现 机器学习算法与Python实践之(四)支持向量机(SVM)实现 zouxy09@qq.com http://blog.csdn.net/ ...

  2. 机器学习算法与Python实践之(三)支持向量机(SVM)进阶

    机器学习算法与Python实践之(三)支持向量机(SVM)进阶 机器学习算法与Python实践之(三)支持向量机(SVM)进阶 zouxy09@qq.com http://blog.csdn.net/ ...

  3. 机器学习算法与Python实践之(二)支持向量机(SVM)初级

    机器学习算法与Python实践之(二)支持向量机(SVM)初级 机器学习算法与Python实践之(二)支持向量机(SVM)初级 zouxy09@qq.com http://blog.csdn.net/ ...

  4. 常用排序算法的python实现和性能分析

    常用排序算法的python实现和性能分析 一年一度的换工作高峰又到了,HR大概每天都塞几份简历过来,基本上一天安排两个面试的话,当天就只能加班干活了.趁着面试别人的机会,自己也把一些基础算法和一些面试 ...

  5. 分类算法——k最近邻算法(Python实现)(文末附工程源代码)

    kNN算法原理 k最近邻(k-Nearest Neighbor)算法是比较简单的机器学习算法.它采用测量不同特征值之间的距离方法进行分类,思想很简单:如果一个样本在特征空间中的k个最近邻(最相似)的样 ...

  6. 机器学习算法与Python实践之(五)k均值聚类(k-means)

    机器学习算法与Python实践这个系列主要是参考<机器学习实战>这本书.因为自己想学习Python,然后也想对一些机器学习算法加深下了解,所以就想通过Python来实现几个比较常用的机器学 ...

  7. 对《禁忌搜索(Tabu Search)算法及python实现》的修改

    这个算法是在听北大人工智能mooc的时候,老师讲的一种局部搜索算法,可是举得例子不太明白.搜索网页后,发现<禁忌搜索(Tabu Search)算法及python实现>(https://bl ...

  8. DES的加密与解密算法(Python实现)

    DES的加密与解密算法(Python实现) 密码学实验:实现了DES的简单的加密和解密算法,DES算法的相关资料网上很多,这里不再赘述,仅仅贴出源代码给大家分享,源码中包含很多汉字注释,相信大家都是可 ...

  9. 机器学习算法与Python实践之(七)逻辑回归(Logistic Regression)

    http://blog.csdn.net/zouxy09/article/details/20319673 机器学习算法与Python实践之(七)逻辑回归(Logistic Regression) z ...

  10. 字符串匹配算法之 kmp算法 (python版)

    字符串匹配算法之 kmp算法 (python版) 1.什么是KMP算法 KMP是三位大牛:D.E.Knuth.J.H.MorriT和V.R.Pratt同时发现的.其中第一位就是<计算机程序设计艺 ...

随机推荐

  1. 简化数据流:Apache SeaTunnel实现多表同步的高效指南

    Apache SeaTunnel除了单表之间的数据同步之外,也支持单表同步到多表,多表同步到单表,以及多表同步到多表,下面简单举例说明如何实现这些功能. 单表 to 单表 一个source,一个sin ...

  2. 金融、支付行业的开发者不得不知道的float、double计算误差问题

    为什么浮点数 float 或 double 运算的时候会有精度丢失的风险呢? <阿里巴巴 Java 开发手册>中提到:"浮点数之间的等值判断,基本数据类型不能用 == 来比较,包 ...

  3. Catlan--卡特兰数--组合数学

    卡特兰数 \(Catlan\) ·赘述 其实发现卡特兰数和之前不同的是,前面的是给你公式,让你去求具体的例子,然而卡特兰数这里是给你大量例子来给你证明和解释什么是卡特兰数. ·定义 对于卡特兰数来说, ...

  4. freertos学习笔记(十)事件标志组

    事件标志组 相当于用户平时定义的Flag,事件标志,不过freertos支持将该标志组作为启动task的条件 概述 分为8位和24位的模式(通过设置宏来配置) 每一位有0和1两个状态 用法 用于平常程 ...

  5. C#整合Ollama实现本地LLMs调用

    前言 近两年AIGC发展的非常迅速,从刚开始的只有ChatGPT到现在的很百家争鸣.从开始的大参数模型,再到后来的小参数模型,从一开始单一的文本模型到现在的多模态模型等等.随着一起进步的不仅仅是模型的 ...

  6. Mongodb入门3

    company数据库下面heros集合里的数据: { "_id" : ObjectId("6100c897d0c9f4158c2b0c9b"), "n ...

  7. opencv equalizeHist

    ''' What are histograms? Histograms are collected counts of data organized into a set of predefined ...

  8. DOM – Web Animation API

    前言 以前写过相关的文章 angular2 学习笔记 ( animation 动画 ).但在项目种很少用到 Web Animation. 体会不到它的精髓,目前的感觉是,它对比 CSS Animati ...

  9. 【QT界面美化】QT界面美化效果截图QSS+QML

    贴几个QT做的界面美化效果截图. 先来一张动图,有一些画面是QT Widgets + QSS实现的:另外一些画面是QT QML实现的. QT界面美化效果图QT QSS QML 补天云QT技术培训专家 ...

  10. Linux板子与ubuntu交互,NFS配置

    第0步:保证你的ubuntu能上网,可以选择NAT方式让ubuntu上网. 第一步:安装NFS服务 sudo apt-get install nfs-kernel-server portmap 第二步 ...