技术背景

在前面的两篇博客中,我们分别介绍了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. 01-canvas体验

    1 <!DOCTYPE html> 2 <html lang="en"> 3 <head> 4 <meta charset="U ...

  2. [nRF24L01+] 3. Radio Control 无线电控制

    3. Radio Control 无线电控制 nRF24L01+可以配置为:power down, standby, Rx/Tx mode 3.1. 无线控制状态图 当VDD电压大于1.9V时,进入上 ...

  3. 聊一聊SQL优化

    晚上睡不着,脑子里总想着一些问题,试着写一写对于SQL查询优化的见解. 首先,数据库有自己的查询优化器,执行一条查询SQL优化器会选择最优的方式(不走索引.走索引.走哪个索引), 所以索引不是越多越好 ...

  4. C#基础 - Task

    目录 前言 1,Task的分类 2,Task的状态 2.1 TaskStatus枚举 2.2 状态相关属性 2.3 小结 3,Task的等待 3.1 Wait方法 3.2 死锁 3.2.1 死锁形成 ...

  5. ZXING 修复预览页面变形问题

    ZXING之前都用的好好的,结果在一个Android11的设备上翻了车 (基于全屏预览的情况下) 扫码页面预览效果变形, 在网上找了一些代码,修改后发现无效,不适用我这边的设备, 最后想着,预览结果变 ...

  6. 扩展KMP (ex_KMP)

    一些约定: 字符串下标从1开始 s[1,i]表示S的第一个到第i个字符组成的字符串 解决的题型: 给你两个字符串A,B(A.size()=n,B.size()=m),求p数组 p[i]表示最大的len ...

  7. DECL: 针对噪声时间序列的去噪感知对比学习《Denoising-Aware Contrastive Learning for Noisy Time Series》(时间序列、对比学习、去噪)

    今天是2024年9月12日,组会摸鱼,很久没看论文了,在摸鱼看代码,最近IJCAI 2024出来了,找了几篇论文看,首先这是第一篇. 论文:Denoising-Aware Contrastive Le ...

  8. CSS – display, visibility, opacity, transparent 的区别

    前言 要让一个元素"消失", 有 3 种做法. 它们有一点点的不同. 在实战时要清楚什么时候用什么哦. 例子说明 <div class="abc"> ...

  9. CSS – Position

    前言 定位是 CSS 里蛮重要的一课. 图片黑影 (overlay), back to top button, header, footer 紧贴在屏幕上下方等效果都是靠 position 完成的. ...

  10. 面试官:谈谈你对 IoC 和 AOP 的理解!

    本文摘录自笔者开源的 Java 学习&面试指南(Github 收获146k star):JavaGuide . 这篇文章会从下面从以下几个问题展开对 IoC & AOP 的解释 什么是 ...