├── README.md ├── configura ├── MD_LJP.ipynb ├── MD_LJP.py └── Rap_2_LJP.in └── exmple_in_jupyter notebook ├── .ipynb_checkpoints └── MD_LJP-checkpoint.ipynb ├── MD_LJP.ipynb ├── Rap_2_LJP.in ├── coo └── coordinates.mp4 └── plot.jpg /README.md: -------------------------------------------------------------------------------- 1 | # MD_LJP 2 | An elementary MD simulation program written in python 3 | -------------------------------------------------------------------------------- /configura/MD_LJP.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": null, 6 | "id": "aea0a6c1", 7 | "metadata": {}, 8 | "outputs": [], 9 | "source": [ 10 | "# 代码1:..........................................................................................\n", 11 | "import pandas as pd\n", 12 | "import math\n", 13 | "import os\n", 14 | "import matplotlib.pyplot as plt\n", 15 | "plt.style.use('seaborn-whitegrid')\n", 16 | "import numpy as np\n", 17 | "from PIL import Image\n", 18 | "import glob\n", 19 | "import moviepy.editor as mp\n", 20 | "from datetime import datetime\n", 21 | "import time\n", 22 | "import os.path\n", 23 | "from os import path\n", 24 | "import shutil\n", 25 | "from IPython.display import display\n", 26 | " \n", 27 | "\n", 28 | "# 代码2:..........................................................................................\n", 29 | "class Mol():\n", 30 | " def __init__(self,r,rv,ra):\n", 31 | " \"\"\"\n", 32 | " 每一分子对象(mol)所具有的属性如下:\n", 33 | " r:原子坐标\n", 34 | " rv:原子速度\n", 35 | " ra:原子加速度向量\n", 36 | " \"\"\"\n", 37 | " #初始化以上属性的值\n", 38 | " self.r=np.asarray([0.0,0.0])\n", 39 | " self.rv=np.asarray([0.0,0.0])\n", 40 | " self.ra=np.asarray([0.0,0.0])\n", 41 | " \n", 42 | "# 体系属性类,主要用来求体系属性(测定量)的平均值和标准差 \n", 43 | "class Prop():\n", 44 | " def __init__(self, val, sum1, sum2 ):\n", 45 | " self.val=val #体系测定量的值\n", 46 | " self.sum1=sum1 #体系测定量的值的和\n", 47 | " self.sum2=sum2 #体系测定量的值的平方和\n", 48 | "\n", 49 | " \n", 50 | "# 代码3:..........................................................................................\n", 51 | "def Sqr(x):\n", 52 | " return (x * x)\n", 53 | "\n", 54 | "def Cube(x):\n", 55 | " return ((x) * (x) * (x))\n", 56 | "\n", 57 | "# 用于生成 (0,1) 范围内的均匀分布的随机数,见18.4\n", 58 | "def RandR():\n", 59 | " global randSeedP\n", 60 | " randSeedP=(randSeedP * IMUL + IADD) & MASK\n", 61 | " return (randSeedP*SCALE)\n", 62 | "\n", 63 | "# 用于在二维空间产生具有均匀分布的随机方向的单位向量\n", 64 | "def VRand(p):\n", 65 | " s: float\n", 66 | " s = 2. * math.pi * RandR()\n", 67 | " p[0] = math.cos(s)\n", 68 | " p[1] = math.sin(s)\n", 69 | " return p\n", 70 | "\n", 71 | "# 周期性边界(PBC)算法:\n", 72 | "# PBC算法第一部分:对体系内粒子坐标的约束\n", 73 | "def VWrapAll(v):\n", 74 | " #对元胞x方向的坐标约束\n", 75 | " if v[0]>=0.5*region[0]: \n", 76 | " v[0]-=region[0]\n", 77 | " elif v[0]<-0.5*region[0]:\n", 78 | " v[0] +=region[0]\n", 79 | " \n", 80 | " #对元胞y方向的坐标约束\n", 81 | " if v[1] >= 0.5 * region[1]:\n", 82 | " v[1] -= region[1]\n", 83 | " elif v[1] < -0.5 * region[1]:\n", 84 | " v[1] += region[1]\n", 85 | " \n", 86 | " \n", 87 | "# 代码5:.......................................................................................... \n", 88 | "def SetParams():\n", 89 | " global rCut #截断距离\n", 90 | " global region #区域\n", 91 | " global velMag #速度幅度\n", 92 | " \n", 93 | " #截断值为势阱底部时的原子间距离,即rmin=rCut=2^1/6 *sigma\n", 94 | " rCut=math.pow(2.,1./6. * sigma)\n", 95 | " region=np.multiply(1./math.sqrt(density),initUcell) \n", 96 | " nMol=len(mol)\n", 97 | " \n", 98 | " #速度的变化幅度取决于温度的高低\n", 99 | " velMag = math.sqrt(NDIM * (1. -1. /nMol) * temperature)\n", 100 | " \n", 101 | " \n", 102 | "# 代码7:..........................................................................................\n", 103 | "def InitCoords():\n", 104 | " c=np.asarray([0.0,0.0]) #初始坐标值\n", 105 | " gap=np.divide(region,initUcell) #单个粒子所在元胞的大小\n", 106 | " n=0 #给粒子计数\n", 107 | " \n", 108 | " #将400个原子分别放到元胞中(这些元胞向x,y方向扩胞成了region区域)\n", 109 | " for ny in range(0, int(initUcell[1])):\n", 110 | " for nx in range(0, int(initUcell[0])):\n", 111 | " c = np.asarray([nx+0.5, ny+0.5])\n", 112 | " c = np.multiply(c, gap)\n", 113 | " c = np.add(c, np.multiply(-0.5, region))\n", 114 | " mol[n].r = c\n", 115 | " n=n+1\n", 116 | " \n", 117 | " \n", 118 | "def InitVels():\n", 119 | " global vSum\n", 120 | " vSum=np.zeros(vSum.shape) #返回特定大小,以0填充新的数组:[0,0],这里是形状\n", 121 | " \n", 122 | " for n in range(nMol):\n", 123 | " VRand(mol[n].rv) #产生随机随机速度向量[x,y]\n", 124 | " mol[n].rv=np.multiply(mol[n].rv,velMag) #根据温度,生成新的速度\n", 125 | " vSum=np.add(vSum,mol[n].rv) #质心的速度,系统总速度各质点速度的总和\n", 126 | " \n", 127 | " #调整度方向,以确保质心是静止的,这里取400个粒子在,x,y方向的平均速度1/400*Vsum+每一个原子的速度\n", 128 | " for n in range (nMol):\n", 129 | " mol[n].rv=np.add(mol[n].rv,np.multiply((- 1.0 / nMol),vSum))\n", 130 | " \n", 131 | " \n", 132 | "def InitAccels():\n", 133 | " for n in range(nMol):\n", 134 | " mol[n].ra=np.zeros(mol[n].ra.shape)\n", 135 | " \n", 136 | "\n", 137 | "# 代码8:..........................................................................................\n", 138 | "def PropZero(v):\n", 139 | " v.sum1 = v.sum2 = 0.\n", 140 | " return v \n", 141 | " \n", 142 | "def PropAccum(v):\n", 143 | " v.sum1 += v.val\n", 144 | " v.sum2 += Sqr(v.val)\n", 145 | " return v \n", 146 | "\n", 147 | "def PropAvg(v, n):\n", 148 | " v.sum1 /= n\n", 149 | " v.sum2 = math.sqrt(max(v.sum2 / n - Sqr(v.sum1), 0.)) \n", 150 | " return v \n", 151 | "\n", 152 | "\n", 153 | "def AccumProps(icode):\n", 154 | " \n", 155 | " if icode == 0: # 0:初始化 \n", 156 | " PropZero(totEnergy)\n", 157 | " PropZero(kinEnergy)\n", 158 | " PropZero(pressure) \n", 159 | " if icode == 1: # 1:求和 \n", 160 | " PropAccum(totEnergy)\n", 161 | " PropAccum(kinEnergy)\n", 162 | " PropAccum(pressure) \n", 163 | " if icode == 2: # 2:求平均值和标准差\n", 164 | " PropAvg(totEnergy, stepAvg)\n", 165 | " PropAvg(kinEnergy, stepAvg)\n", 166 | " PropAvg(pressure, stepAvg) \n", 167 | " \n", 168 | " \n", 169 | "# 代码6:..........................................................................................\n", 170 | "def SetupJob(): \n", 171 | " global stepCount #步长计数\n", 172 | " \n", 173 | " stepCount = 0 #初始化步长计数变量的值\n", 174 | " InitCoords() #初始化坐标\n", 175 | " InitVels() #初始化速度\n", 176 | " InitAccels() #初始化加速度\n", 177 | " AccumProps(0) #初始化系统属性值(总能量,动能,压强)\n", 178 | " \n", 179 | "\n", 180 | "# 代码10:.........................................................................................\n", 181 | "def LeapfrogStep(part):\n", 182 | " if part == 1:\n", 183 | " for n in range (nMol):\n", 184 | " mol[n].rv=np.add(mol[n].rv,np.multiply(0.5 * deltaT,mol[n].ra))\n", 185 | " mol[n].r=np.add(mol[n].r,np.multiply(deltaT,mol[n].rv))\n", 186 | " else :\n", 187 | " for n in range(nMol):\n", 188 | " mol[n].rv=np.add(mol[n].rv,np.multiply(0.5 * deltaT,mol[n].ra))\n", 189 | "\n", 190 | " \n", 191 | "# 代码11:...................................................................................\n", 192 | "def ApplyBoundaryCond():\n", 193 | " for n in range(nMol):\n", 194 | " VWrapAll(mol[n].r)\n", 195 | " \n", 196 | "# 代码12:...................................................................................\n", 197 | "def ComputeForces():\n", 198 | " global virSum #用于计算压强的中间变量(fij*rij)和\n", 199 | " global uSum #LJ势能和\n", 200 | " fcVal=0 # 原子j对原子i施加的力\n", 201 | " rrCut=Sqr(rCut) # rCut:Rc,截断半径的平方\n", 202 | " \n", 203 | " #初始化分子加速度\n", 204 | " for n in range (nMol):\n", 205 | " mol[n].ra=np.zeros(mol[n].ra.shape)\n", 206 | " \n", 207 | " uSum=0. #初始化LJ势能和值\n", 208 | " virSum=0.\n", 209 | " n=0\n", 210 | " for j1 in range(nMol-1):\n", 211 | " for j2 in range(j1+1,nMol):\n", 212 | " \n", 213 | " # 使Delta Rij。(RJ1-RJ2的平方之和)\n", 214 | " dr=np.subtract(mol[j1].r,mol[j2].r) # dr包含Rj1和Rj2之间的x,y坐标差值\n", 215 | " VWrapAll(dr) #应用PBC约束原子在元胞内,更新了dr[0],dr[1]\n", 216 | " rr=(dr[0] * dr[0] + dr[1] * dr[1]) #两原子距离的平方,这里并未求两原子间距离的绝对值,没必要,因为与截断半径只是比大小,省去了平方根的计算\n", 217 | " r=np.sqrt(rr) #r两为原子间的距离 \n", 218 | " \n", 219 | " # 这里是使用原子距离的平方来 dr2 < Rc^2 来判断两原子键相互作用力,\n", 220 | " if(rr < rrCut):\n", 221 | " fcVal = 48 * epsilon * np.power(sigma, 12) / np.power(r, 13) - 24 * epsilon * np.power(sigma, 6) / np.power(r, 7)\n", 222 | " \n", 223 | " # 更新加速度\n", 224 | " mol[j1].ra = np.add(mol[j1].ra, np.multiply(fcVal, dr))\n", 225 | " mol[j2].ra = np.add(mol[j2].ra, np.multiply(-fcVal, dr))\n", 226 | " \n", 227 | " #LJ势能计算\n", 228 | " uSum += 4 * epsilon * np.power(sigma/r, 12)/r - np.power(sigma/r, 6) \n", 229 | " virSum += fcVal * rr\n", 230 | " \n", 231 | "# 代码13:...................................................................................\n", 232 | "def EvalProps():\n", 233 | " global vSum\n", 234 | " vvSum = 0. #系统速度平方的总和\n", 235 | " vSum = np.zeros(vSum.shape) #质心速度,为系统速度=各质点速度总和,初始化为[0,0]\n", 236 | " \n", 237 | " global kinEnergy #动能\n", 238 | " global totEenergy #总能量\n", 239 | " global pressure #压强\n", 240 | " \n", 241 | " #求得质心速度\n", 242 | " for n in range(nMol):\n", 243 | " vSum =np.add(vSum,mol[n].rv) \n", 244 | " vv = (mol[n].rv[0] * mol[n].rv[0] + mol[n].rv[1] * mol[n].rv[1]) #xv^2+yv^2\n", 245 | " vvSum += vv\n", 246 | " \n", 247 | " # 在二维分子体系中,热力学属性值的数值计算方法\n", 248 | " kinEnergy.val = (0.5 * vvSum) / nMol #单个原子动能,nMol代表原子数\n", 249 | " totEnergy.val = kinEnergy.val + (uSum / nMol) #总能量:单个原子的动能+单个原子的势能\n", 250 | " pressure.val = density * (vvSum + virSum) / (nMol * NDIM) #体系压强\n", 251 | " \n", 252 | "\n", 253 | "# 代码14:...................................................................................\n", 254 | "def PrintSummary():\n", 255 | "\n", 256 | " print(stepCount, \\\n", 257 | " \"{0:.4f}\".format(timeNow), \\\n", 258 | " \"{0:.4f}\".format(vSum[0] / nMol) ,\\\n", 259 | " \"{0:.4f}\".format(totEnergy.sum1),\\\n", 260 | " \"{0:.4f}\".format(totEnergy.sum2), \\\n", 261 | " \"{0:.4f}\".format(kinEnergy.sum1), \\\n", 262 | " \"{0:.4f}\".format(kinEnergy.sum2),\\\n", 263 | " \"{0:.4f}\".format(pressure.sum1),\\\n", 264 | " \"{0:.4f}\".format(pressure.sum2))\n", 265 | " \n", 266 | " return (stepCount, \\\n", 267 | " timeNow, \\\n", 268 | " (vSum[0] / nMol) ,\\\n", 269 | " totEnergy.sum1,\\\n", 270 | " totEnergy.sum2, \\\n", 271 | " kinEnergy.sum1, \\\n", 272 | " kinEnergy.sum2,\\\n", 273 | " pressure.sum1,\\\n", 274 | " pressure.sum2) \n", 275 | "\n", 276 | "# 代码9:------------------------------------------------------------------------------------------\n", 277 | "def SingleStep():\n", 278 | " \n", 279 | " global stepCount # 步长计数\n", 280 | " global timeNow # 模拟运行时间\n", 281 | "\n", 282 | " stepCount +=1 \n", 283 | " timeNow = stepCount * deltaT #模拟运行的时间=步数x步长\n", 284 | " LeapfrogStep(1) #求解运动方程积分\n", 285 | " ApplyBoundaryCond() #应用周期性边界条件\n", 286 | " ComputeForces() # 计算原间相互作用力\n", 287 | " LeapfrogStep(2) # 坐标和速度的积分\n", 288 | " EvalProps() #计算系统属性值(速度,,速度平方和,总能量,动能,压力)\n", 289 | " AccumProps(1) #系统属性值求和\n", 290 | " \n", 291 | " #每一百步统计系统的属性值(0,100,200,300,400,500),可以设置stepAvg的值进行自定义\n", 292 | " if (stepCount % stepAvg == 0):\n", 293 | " AccumProps(2) #求系统的属性值的平均值和标准差\n", 294 | " systemParams.append(PrintSummary()) #将结果加入到 systemParams列表中\n", 295 | " AccumProps(0) # 重置系统属性值用来下一次的统计\n", 296 | " \n", 297 | "\n", 298 | "# 代码15:...................................................................................\n", 299 | "def plotMolCoo(mol,workdir,n):\n", 300 | " import matplotlib.patches as mpatches\n", 301 | " import matplotlib.pyplot as plt\n", 302 | " \n", 303 | " Time=timeNow #模拟时长\n", 304 | " Sigma_v = \"{0:.4f}\".format(vSum[0] / nMol)\n", 305 | " E = \"{0:.4f}\".format(totEnergy.sum1)\n", 306 | " Sigma_E = \"{0:.4f}\".format(totEnergy.sum2)\n", 307 | " Ek = \"{0:.4f}\".format(kinEnergy.sum1)\n", 308 | " Sigma_Ek = \"{0:.4f}\".format(kinEnergy.sum2)\n", 309 | " P_1 = \"{0:.4f}\".format(pressure.sum1)\n", 310 | " P_2 = \"{0:.4f}\".format(pressure.sum2) \n", 311 | " \n", 312 | " %matplotlib inline \n", 313 | " TileName = (workdir+'coo/'+str(n)+'.png') #传入的n表示n步,这里用来给生成的图像命名\n", 314 | " x = [] #新建列表保存粒子x的坐标\n", 315 | " y = [] #新建列表保存粒子y的坐标\n", 316 | " \n", 317 | " # 遍历0-400号原子的坐标,加入到列表中去\n", 318 | " for n in range(len(mol)):\n", 319 | " x.append(mol[n].r[0])\n", 320 | " y.append(mol[n].r[1])\n", 321 | " \n", 322 | " # 标记400个原子中两个位置较为居中的两相邻原子mol[250]和mol[251],用于观察\n", 323 | " mark_1 = int(len(mol)/2 + len(mol)/8)\n", 324 | " mark_2 = int(len(mol)/2 + len(mol)/8 + 1)\n", 325 | " \n", 326 | " # 根据原子坐标绘制图形\n", 327 | " plt.plot(x, y, 'o', color='blue') #每个原子均为蓝色圆形\n", 328 | " plt.plot(x[mark_1], y[mark_1], 'o', color='red') #标记mark_1为红色\n", 329 | " plt.plot(x[mark_2], y[mark_2], 'o', color='cyan') #标记mark_2为青色\n", 330 | " \n", 331 | " # 绘制图标的标题\n", 332 | " plt.title('timestep:'+\"{0:.4f}\".format(timeNow)+'; '+\\\n", 333 | " '$\\Sigma v$:'+Sigma_v+'; '+\\\n", 334 | " 'E:'+E+'; '+\\\n", 335 | " '$\\sigma E$:'+Sigma_E+';\\n'+\\\n", 336 | " 'Ek:'+Ek+'; ' +\\\n", 337 | " '$\\sigma Ek$:'+Sigma_Ek+'; '+\\\n", 338 | " 'P.sum1:'+P_1+'; '+\\\n", 339 | " 'P.sum2:'+P_2+'; ', loc='left')\n", 340 | " \n", 341 | " plt.savefig(TileName, dpi=100) #保存为坐标图为.png图片\n", 342 | " \n", 343 | "\n", 344 | "# 代码16:...................................................................................\n", 345 | "def makeMov():\n", 346 | " \n", 347 | " # 将多张.png图像转为gif图的片段\n", 348 | " frames = []\n", 349 | " imgs = sorted(glob.glob('coo/*.png'), key=os.path.getmtime)\n", 350 | " for i in imgs:\n", 351 | " temp = Image.open(i)\n", 352 | " keep = temp.copy()\n", 353 | " frames.append(keep)\n", 354 | " temp.close()\n", 355 | " \n", 356 | " # 删除全部的.png图\n", 357 | " for i in imgs:\n", 358 | " os.remove(i) \n", 359 | "\n", 360 | " # 片段合并保存为GIF文件,永远循环播放\n", 361 | " frames[0].save('coo/coordinates.gif', format='GIF',\n", 362 | " append_images=frames[1:],\n", 363 | " save_all=True,\n", 364 | " duration=30, loop=0)\n", 365 | "\n", 366 | " # 将GIF图转换成MP4视频文件\n", 367 | " clip = mp.VideoFileClip(\"coo/coordinates.gif\")\n", 368 | " clip.write_videofile(\"coo/\"+\"coordinates\"+\".mp4\")\n", 369 | " \n", 370 | " # 删除gif图\n", 371 | " os.remove(\"coo/coordinates.gif\")\n", 372 | " \n", 373 | "\n", 374 | "# 代码17:...................................................................................\n", 375 | "def GraphOutput():\n", 376 | " \n", 377 | " ax = \\\n", 378 | " df_systemParams.plot(x=\"timestep\", y='$\\Sigma v$', kind=\"line\")\n", 379 | " df_systemParams.plot(x=\"timestep\", y='E', kind=\"line\", ax=ax, color=\"C1\")\n", 380 | " df_systemParams.plot(x=\"timestep\", y='$\\sigma E$', kind=\"line\", ax=ax, color=\"C2\")\n", 381 | " df_systemParams.plot(x=\"timestep\", y='Ek', kind=\"line\", ax=ax, color=\"C3\")\n", 382 | " df_systemParams.plot(x=\"timestep\", y='$\\sigma Ek$', kind=\"line\", ax=ax, color=\"C4\")\n", 383 | " df_systemParams.plot(x=\"timestep\", y='P_1', kind=\"line\", ax=ax, color=\"C9\")\n", 384 | " df_systemParams.plot(x=\"timestep\", y='P_2', kind=\"line\", ax=ax, color=\"C9\")\n", 385 | " \n", 386 | " plt.savefig('plot.jpg', dpi=300)\n", 387 | " \n", 388 | " \n", 389 | "# 代码3:************************************Main************************************************\n", 390 | "mov = 1 # 如果你想做一个视频的话,设置 mov=1\n", 391 | "workdir = str(os.getcwd()+'/') # 为所有png和视频设置一个工作目录\n", 392 | "\n", 393 | "if path.exists(str(workdir+'coo'))==False:\n", 394 | " os.makedirs(str(workdir+'coo'))\n", 395 | "else:\n", 396 | " shutil.rmtree(str(workdir+'coo'))\n", 397 | " os.makedirs(str(workdir+'coo'))\n", 398 | "\n", 399 | "# 加载输入参数文件\n", 400 | "df_params = pd.read_csv('Rap_2_LJP.in', sep='\\t', header=None, names=['parameter', 'value'])\n", 401 | "\n", 402 | "#初始该模拟系统热力学属性值\n", 403 | "NDIM = 2 #二维度设置\n", 404 | "vSum = np.asarray([0.0, 0.0]) #速度之和\n", 405 | "kinEnergy =Prop(0.0, 0.0, 0.0) #动能\n", 406 | "totEnergy =Prop(0.0, 0.0, 0.0) #势能\n", 407 | "pressure =Prop(0.0, 0.0, 0.0) #压强\n", 408 | "\n", 409 | "systemParams = [] #初始化系统参数列表\n", 410 | "\n", 411 | "#用于产生随机数算法函数的变量\n", 412 | "IADD = 453806245\n", 413 | "IMUL = 314159269\n", 414 | "MASK = 2147483647\n", 415 | "SCALE = 0.4656612873e-9\n", 416 | "randSeedP = 17\n", 417 | "\n", 418 | "#初始化参数:把Rap_2_LJP.文件中的参数值传递给MD模拟所需要的相关变量\n", 419 | "deltaT = float(df_params.values[0][1]) #模拟步长\n", 420 | "density = float(df_params.values[1][1]) #体系密度\n", 421 | "\n", 422 | "\n", 423 | "initUcell = np.asarray([0.0, 0.0]) #初始化元胞,具体大小由输入文件密度决定\n", 424 | "initUcell[0] = int(df_params.values[2][1]) #元胞X方向长度=20\n", 425 | "initUcell[1] = int(df_params.values[3][1]) #元胞X方向长度=20\n", 426 | " \n", 427 | "stepAvg = int(df_params.values[4][1]) #抽样平均数\n", 428 | "stepEquil = float(df_params.values[5][1]) #平衡步长\n", 429 | "stepLimit = float(df_params.values[6][1]) #限制的步数\n", 430 | "temperature = float(df_params.values[7][1]) #体系温度\n", 431 | "\n", 432 | "#定义了一个Mol类对象的数组,大小为20*20=400. mol[0]~mol[399],共400个氩原子分子\n", 433 | "mol = [Mol(np.asarray([0.0, 0.0]), \\\n", 434 | " np.asarray([0.0, 0.0]), \\\n", 435 | " np.asarray([0.0, 0.0])) for i in range(int(initUcell[0]*initUcell[1]))]\n", 436 | "\n", 437 | "global nMol #定义分子的数量变量\n", 438 | "nMol = len(mol) #为mol数组的长度为400\n", 439 | "\n", 440 | "# 氩-氩相互作用的LJ势参数:\n", 441 | "epsilon = 1\n", 442 | "sigma = 1\n", 443 | "\n", 444 | "# 执行体系初始化相关功能函数\n", 445 | "SetParams() #设置主程序内部模拟所需参数\n", 446 | "SetupJob() #初始化粒子的坐标,速度,加速\n", 447 | "moreCycles = 1 #MD循环控制开关变量1:run; 0:stop\n", 448 | "\n", 449 | "n = 0 #记录步数,用来给每一步输出的坐标图命名 \n", 450 | "while moreCycles: #MD循环\n", 451 | " SingleStep() #求解每一步的运动方程\n", 452 | " if mov==1:\n", 453 | " plotMolCoo(mol, workdir, n) #制作单个步长的粒子坐标图\n", 454 | " n += 1\n", 455 | " if stepCount >= stepLimit:\n", 456 | " moreCycles = 0\n", 457 | " \n", 458 | "#输出模拟过程中生成的参数\n", 459 | "columns = ['timestep','timeNow', '$\\Sigma v$', 'E', '$\\sigma E$', 'Ek', '$\\sigma Ek$', 'P_1', 'P_2']\n", 460 | "df_systemParams = pd.DataFrame(systemParams, columns=columns) \n", 461 | "\n", 462 | "#绘制表格\n", 463 | "display(df_systemParams) \n", 464 | "if mov==1: \n", 465 | " makeMov() # 制作视频\n", 466 | "GraphOutput() #输出体系测定量随模拟时间变化的图像" 467 | ] 468 | } 469 | ], 470 | "metadata": { 471 | "kernelspec": { 472 | "display_name": "Python 3 (ipykernel)", 473 | "language": "python", 474 | "name": "python3" 475 | }, 476 | "language_info": { 477 | "codemirror_mode": { 478 | "name": "ipython", 479 | "version": 3 480 | }, 481 | "file_extension": ".py", 482 | "mimetype": "text/x-python", 483 | "name": "python", 484 | "nbconvert_exporter": "python", 485 | "pygments_lexer": "ipython3", 486 | "version": "3.9.6" 487 | }, 488 | "toc": { 489 | "base_numbering": 1, 490 | "nav_menu": {}, 491 | "number_sections": true, 492 | "sideBar": true, 493 | "skip_h1_title": false, 494 | "title_cell": "Table of Contents", 495 | "title_sidebar": "Contents", 496 | "toc_cell": false, 497 | "toc_position": {}, 498 | "toc_section_display": true, 499 | "toc_window_display": false 500 | } 501 | }, 502 | "nbformat": 4, 503 | "nbformat_minor": 5 504 | } 505 | -------------------------------------------------------------------------------- /configura/MD_LJP.py: -------------------------------------------------------------------------------- 1 | # 代码1:.......................................................................................... 2 | import pandas as pd 3 | import math 4 | import os 5 | import matplotlib.pyplot as plt 6 | plt.style.use('seaborn-whitegrid') 7 | import numpy as np 8 | from PIL import Image 9 | import glob 10 | import moviepy.editor as mp 11 | from datetime import datetime 12 | import time 13 | import os.path 14 | from os import path 15 | import shutil 16 | 17 | 18 | # 代码2:.......................................................................................... 19 | class Mol(): 20 | def __init__(self,r,rv,ra): 21 | """ 22 | 每一分子对象(mol)所具有的属性如下: 23 | r:原子坐标 24 | rv:原子速度 25 | ra:原子加速度向量 26 | """ 27 | #初始化以上属性的值 28 | self.r=np.asarray([0.0,0.0]) 29 | self.rv=np.asarray([0.0,0.0]) 30 | self.ra=np.asarray([0.0,0.0]) 31 | 32 | # 体系属性类,主要用来求体系属性(测定量)的平均值和标准差 33 | class Prop(): 34 | def __init__(self, val, sum1, sum2 ): 35 | self.val=val #体系测定量的值 36 | self.sum1=sum1 #体系测定量的值的和 37 | self.sum2=sum2 #体系测定量的值的平方和 38 | 39 | 40 | # 代码3:.......................................................................................... 41 | def Sqr(x): 42 | return (x * x) 43 | 44 | def Cube(x): 45 | return ((x) * (x) * (x)) 46 | 47 | # 用于生成 (0,1) 范围内的均匀分布的随机数,见18.4 48 | def RandR(): 49 | global randSeedP 50 | randSeedP=(randSeedP * IMUL + IADD) & MASK 51 | return (randSeedP*SCALE) 52 | 53 | # 用于在二维空间产生具有均匀分布的随机方向的单位向量 54 | def VRand(p): 55 | s: float 56 | s = 2. * math.pi * RandR() 57 | p[0] = math.cos(s) 58 | p[1] = math.sin(s) 59 | return p 60 | 61 | # 周期性边界(PBC)算法: 62 | # PBC算法第一部分:对体系内粒子坐标的约束 63 | def VWrapAll(v): 64 | #对元胞x方向的坐标约束 65 | if v[0]>=0.5*region[0]: 66 | v[0]-=region[0] 67 | elif v[0]<-0.5*region[0]: 68 | v[0] +=region[0] 69 | 70 | #对元胞y方向的坐标约束 71 | if v[1] >= 0.5 * region[1]: 72 | v[1] -= region[1] 73 | elif v[1] < -0.5 * region[1]: 74 | v[1] += region[1] 75 | 76 | 77 | # 代码5:.......................................................................................... 78 | def SetParams(): 79 | global rCut #截断距离 80 | global region #区域 81 | global velMag #速度幅度 82 | 83 | #截断值为势阱底部时的原子间距离,即rmin=rCut=2^1/6 *sigma 84 | rCut=math.pow(2.,1./6. * sigma) 85 | region=np.multiply(1./math.sqrt(density),initUcell) 86 | nMol=len(mol) 87 | 88 | #速度的变化幅度取决于温度的高低 89 | velMag = math.sqrt(NDIM * (1. -1. /nMol) * temperature) 90 | 91 | 92 | # 代码7:.......................................................................................... 93 | def InitCoords(): 94 | c=np.asarray([0.0,0.0]) #初始坐标值 95 | gap=np.divide(region,initUcell) #单个粒子所在元胞的大小 96 | n=0 #给粒子计数 97 | 98 | #将400个原子分别放到元胞中(这些元胞向x,y方向扩胞成了region区域) 99 | for ny in range(0, int(initUcell[1])): 100 | for nx in range(0, int(initUcell[0])): 101 | c = np.asarray([nx+0.5, ny+0.5]) 102 | c = np.multiply(c, gap) 103 | c = np.add(c, np.multiply(-0.5, region)) 104 | mol[n].r = c 105 | n=n+1 106 | 107 | 108 | def InitVels(): 109 | global vSum 110 | vSum=np.zeros(vSum.shape) #返回特定大小,以0填充新的数组:[0,0],这里是形状 111 | 112 | for n in range(nMol): 113 | VRand(mol[n].rv) #产生随机随机速度向量[x,y] 114 | mol[n].rv=np.multiply(mol[n].rv,velMag) #根据温度,生成新的速度 115 | vSum=np.add(vSum,mol[n].rv) #质心的速度,系统总速度各质点速度的总和 116 | 117 | #调整度方向,以确保质心是静止的,这里取400个粒子在,x,y方向的平均速度1/400*Vsum+每一个原子的速度 118 | for n in range (nMol): 119 | mol[n].rv=np.add(mol[n].rv,np.multiply((- 1.0 / nMol),vSum)) 120 | 121 | 122 | def InitAccels(): 123 | for n in range(nMol): 124 | mol[n].ra=np.zeros(mol[n].ra.shape) 125 | 126 | 127 | # 代码8:.......................................................................................... 128 | def PropZero(v): 129 | v.sum1 = v.sum2 = 0. 130 | return v 131 | 132 | def PropAccum(v): 133 | v.sum1 += v.val 134 | v.sum2 += Sqr(v.val) 135 | return v 136 | 137 | def PropAvg(v, n): 138 | v.sum1 /= n 139 | v.sum2 = math.sqrt(max(v.sum2 / n - Sqr(v.sum1), 0.)) 140 | return v 141 | 142 | 143 | def AccumProps(icode): 144 | 145 | if icode == 0: # 0:初始化 146 | PropZero(totEnergy) 147 | PropZero(kinEnergy) 148 | PropZero(pressure) 149 | if icode == 1: # 1:求和 150 | PropAccum(totEnergy) 151 | PropAccum(kinEnergy) 152 | PropAccum(pressure) 153 | if icode == 2: # 2:求平均值和标准差 154 | PropAvg(totEnergy, stepAvg) 155 | PropAvg(kinEnergy, stepAvg) 156 | PropAvg(pressure, stepAvg) 157 | 158 | 159 | # 代码6:.......................................................................................... 160 | def SetupJob(): 161 | global stepCount #步长计数 162 | 163 | stepCount = 0 #初始化步长计数变量的值 164 | InitCoords() #初始化坐标 165 | InitVels() #初始化速度 166 | InitAccels() #初始化加速度 167 | AccumProps(0) #初始化系统属性值(总能量,动能,压强) 168 | 169 | 170 | # 代码10:......................................................................................... 171 | def LeapfrogStep(part): 172 | if part == 1: 173 | for n in range (nMol): 174 | mol[n].rv=np.add(mol[n].rv,np.multiply(0.5 * deltaT,mol[n].ra)) 175 | mol[n].r=np.add(mol[n].r,np.multiply(deltaT,mol[n].rv)) 176 | else : 177 | for n in range(nMol): 178 | mol[n].rv=np.add(mol[n].rv,np.multiply(0.5 * deltaT,mol[n].ra)) 179 | 180 | 181 | # 代码11:................................................................................... 182 | def ApplyBoundaryCond(): 183 | for n in range(nMol): 184 | VWrapAll(mol[n].r) 185 | 186 | # 代码12:................................................................................... 187 | def ComputeForces(): 188 | global virSum #用于计算压强的中间变量(fij*rij)和 189 | global uSum #LJ势能和 190 | fcVal=0 # 原子j对原子i施加的力 191 | rrCut=Sqr(rCut) # rCut:Rc,截断半径的平方 192 | 193 | #初始化分子加速度 194 | for n in range (nMol): 195 | mol[n].ra=np.zeros(mol[n].ra.shape) 196 | 197 | uSum=0. #初始化LJ势能和值 198 | virSum=0. 199 | n=0 200 | for j1 in range(nMol-1): 201 | for j2 in range(j1+1,nMol): 202 | 203 | # 使Delta Rij。(RJ1-RJ2的平方之和) 204 | dr=np.subtract(mol[j1].r,mol[j2].r) # dr包含Rj1和Rj2之间的x,y坐标差值 205 | VWrapAll(dr) #应用PBC约束原子在元胞内,更新了dr[0],dr[1] 206 | rr=(dr[0] * dr[0] + dr[1] * dr[1]) #两原子距离的平方,这里并未求两原子间距离的绝对值,没必要,因为与截断半径只是比大小,省去了平方根的计算 207 | r=np.sqrt(rr) #r两为原子间的距离 208 | 209 | # 这里是使用原子距离的平方来 dr2 < Rc^2 来判断两原子键相互作用力, 210 | if(rr < rrCut): 211 | fcVal = 48 * epsilon * np.power(sigma, 12) / np.power(r, 13) - 24 * epsilon * np.power(sigma, 6) / np.power(r, 7) 212 | 213 | # 更新加速度 214 | mol[j1].ra = np.add(mol[j1].ra, np.multiply(fcVal, dr)) 215 | mol[j2].ra = np.add(mol[j2].ra, np.multiply(-fcVal, dr)) 216 | 217 | #LJ势能计算 218 | uSum += 4 * epsilon * np.power(sigma/r, 12)/r - np.power(sigma/r, 6) 219 | virSum += fcVal * rr 220 | 221 | # 代码13:................................................................................... 222 | def EvalProps(): 223 | global vSum 224 | vvSum = 0. #系统速度平方的总和 225 | vSum = np.zeros(vSum.shape) #质心速度,为系统速度=各质点速度总和,初始化为[0,0] 226 | 227 | global kinEnergy #动能 228 | global totEenergy #总能量 229 | global pressure #压强 230 | 231 | #求得质心速度 232 | for n in range(nMol): 233 | vSum =np.add(vSum,mol[n].rv) 234 | vv = (mol[n].rv[0] * mol[n].rv[0] + mol[n].rv[1] * mol[n].rv[1]) #xv^2+yv^2 235 | vvSum += vv 236 | 237 | # 在二维分子体系中,热力学属性值的数值计算方法 238 | kinEnergy.val = (0.5 * vvSum) / nMol #单个原子动能,nMol代表原子数 239 | totEnergy.val = kinEnergy.val + (uSum / nMol) #总能量:单个原子的动能+单个原子的势能 240 | pressure.val = density * (vvSum + virSum) / (nMol * NDIM) #体系压强 241 | 242 | 243 | # 代码14:................................................................................... 244 | def PrintSummary(): 245 | 246 | print(stepCount, \ 247 | "{0:.4f}".format(timeNow), \ 248 | "{0:.4f}".format(vSum[0] / nMol) ,\ 249 | "{0:.4f}".format(totEnergy.sum1),\ 250 | "{0:.4f}".format(totEnergy.sum2), \ 251 | "{0:.4f}".format(kinEnergy.sum1), \ 252 | "{0:.4f}".format(kinEnergy.sum2),\ 253 | "{0:.4f}".format(pressure.sum1),\ 254 | "{0:.4f}".format(pressure.sum2)) 255 | 256 | return (stepCount, \ 257 | timeNow, \ 258 | (vSum[0] / nMol) ,\ 259 | totEnergy.sum1,\ 260 | totEnergy.sum2, \ 261 | kinEnergy.sum1, \ 262 | kinEnergy.sum2,\ 263 | pressure.sum1,\ 264 | pressure.sum2) 265 | 266 | # 代码9:------------------------------------------------------------------------------------------ 267 | def SingleStep(): 268 | 269 | global stepCount # 步长计数 270 | global timeNow # 模拟运行时间 271 | 272 | stepCount +=1 273 | timeNow = stepCount * deltaT #模拟运行的时间=步数x步长 274 | LeapfrogStep(1) #求解运动方程积分 275 | ApplyBoundaryCond() #应用周期性边界条件 276 | ComputeForces() # 计算原间相互作用力 277 | LeapfrogStep(2) # 坐标和速度的积分 278 | EvalProps() #计算系统属性值(速度,,速度平方和,总能量,动能,压力) 279 | AccumProps(1) #系统属性值求和 280 | 281 | #每一百步统计系统的属性值(0,100,200,300,400,500),可以设置stepAvg的值进行自定义 282 | if (stepCount % stepAvg == 0): 283 | AccumProps(2) #求系统的属性值的平均值和标准差 284 | systemParams.append(PrintSummary()) #将结果加入到 systemParams列表中 285 | AccumProps(0) # 重置系统属性值用来下一次的统计 286 | 287 | 288 | # 代码15:................................................................................... 289 | def plotMolCoo(mol,workdir,n): 290 | import matplotlib.patches as mpatches 291 | import matplotlib.pyplot as plt 292 | 293 | Time=timeNow #模拟时长 294 | Sigma_v = "{0:.4f}".format(vSum[0] / nMol) 295 | E = "{0:.4f}".format(totEnergy.sum1) 296 | Sigma_E = "{0:.4f}".format(totEnergy.sum2) 297 | Ek = "{0:.4f}".format(kinEnergy.sum1) 298 | Sigma_Ek = "{0:.4f}".format(kinEnergy.sum2) 299 | P_1 = "{0:.4f}".format(pressure.sum1) 300 | P_2 = "{0:.4f}".format(pressure.sum2) 301 | 302 | 303 | #%matplotlib inline 304 | TileName = (workdir+'coo/'+str(n)+'.png') #传入的n表示n步,这里用来给生成的图像命名 305 | x = [] #新建列表保存粒子x的坐标 306 | y = [] #新建列表保存粒子y的坐标 307 | 308 | # 遍历0-400号原子的坐标,加入到列表中去 309 | for n in range(len(mol)): 310 | x.append(mol[n].r[0]) 311 | y.append(mol[n].r[1]) 312 | 313 | # 标记400个原子中两个位置较为居中的两相邻原子mol[250]和mol[251],用于观察 314 | mark_1 = int(len(mol)/2 + len(mol)/8) 315 | mark_2 = int(len(mol)/2 + len(mol)/8 + 1) 316 | 317 | # 根据原子坐标绘制图形 318 | plt.plot(x, y, 'o', color='blue') #每个原子均为蓝色圆形 319 | plt.plot(x[mark_1], y[mark_1], 'o', color='red') #标记mark_1为红色 320 | plt.plot(x[mark_2], y[mark_2], 'o', color='cyan') #标记mark_2为青色 321 | 322 | # 绘制图标的标题 323 | plt.title('timestep:'+"{0:.4f}".format(timeNow)+'; '+\ 324 | '$\Sigma v$:'+Sigma_v+'; '+\ 325 | 'E:'+E+'; '+\ 326 | '$\sigma E$:'+Sigma_E+';\n'+\ 327 | 'Ek:'+Ek+'; ' +\ 328 | '$\sigma Ek$:'+Sigma_Ek+'; '+\ 329 | 'P.sum1:'+P_1+'; '+\ 330 | 'P.sum2:'+P_2+'; ', loc='left') 331 | 332 | plt.savefig(TileName, dpi=100) #保存为坐标图为.png图片 333 | 334 | 335 | # 代码16:................................................................................... 336 | def makeMov(): 337 | 338 | # 将多张.png图像转为gif图的片段 339 | frames = [] 340 | imgs = sorted(glob.glob('coo/*.png'), key=os.path.getmtime) 341 | for i in imgs: 342 | temp = Image.open(i) 343 | keep = temp.copy() 344 | frames.append(keep) 345 | temp.close() 346 | 347 | # 删除全部的.png图 348 | for i in imgs: 349 | os.remove(i) 350 | 351 | # 片段合并保存为GIF文件,永远循环播放 352 | frames[0].save('coo/coordinates.gif', format='GIF', 353 | append_images=frames[1:], 354 | save_all=True, 355 | duration=30, loop=0) 356 | 357 | # 将GIF图转换成MP4视频文件 358 | clip = mp.VideoFileClip("coo/coordinates.gif") 359 | clip.write_videofile("coo/"+"coordinates"+".mp4") 360 | 361 | # 删除gif图 362 | os.remove("coo/coordinates.gif") 363 | 364 | 365 | # 代码17:................................................................................... 366 | def GraphOutput(): 367 | 368 | ax = \ 369 | df_systemParams.plot(x="timestep", y='$\Sigma v$', kind="line") 370 | df_systemParams.plot(x="timestep", y='E', kind="line", ax=ax, color="C1") 371 | df_systemParams.plot(x="timestep", y='$\sigma E$', kind="line", ax=ax, color="C2") 372 | df_systemParams.plot(x="timestep", y='Ek', kind="line", ax=ax, color="C3") 373 | df_systemParams.plot(x="timestep", y='$\sigma Ek$', kind="line", ax=ax, color="C4") 374 | df_systemParams.plot(x="timestep", y='P_1', kind="line", ax=ax, color="C9") 375 | df_systemParams.plot(x="timestep", y='P_2', kind="line", ax=ax, color="C9") 376 | 377 | plt.savefig('plot.jpg', dpi=300) 378 | 379 | 380 | # 代码3:************************************Main************************************************ 381 | mov = 1 # 如果你想做一个视频的话,设置 mov=1 382 | workdir = str(os.getcwd()+'/') # 为所有png和视频设置一个工作目录 383 | 384 | if path.exists(str(workdir+'coo'))==False: 385 | os.makedirs(str(workdir+'coo')) 386 | else: 387 | shutil.rmtree(str(workdir+'coo')) 388 | os.makedirs(str(workdir+'coo')) 389 | 390 | # 加载输入参数文件 391 | df_params = pd.read_csv('Rap_2_LJP.in', sep='\t', header=None, names=['parameter', 'value']) 392 | 393 | #初始该模拟系统热力学属性值 394 | NDIM = 2 #二维度设置 395 | vSum = np.asarray([0.0, 0.0]) #速度之和 396 | kinEnergy =Prop(0.0, 0.0, 0.0) #动能 397 | totEnergy =Prop(0.0, 0.0, 0.0) #势能 398 | pressure =Prop(0.0, 0.0, 0.0) #压强 399 | 400 | systemParams = [] #初始化系统参数列表 401 | 402 | #用于产生随机数算法函数的变量 403 | IADD = 453806245 404 | IMUL = 314159269 405 | MASK = 2147483647 406 | SCALE = 0.4656612873e-9 407 | randSeedP = 17 408 | 409 | #初始化参数:把Rap_2_LJP.文件中的参数值传递给MD模拟所需要的相关变量 410 | deltaT = float(df_params.values[0][1]) #模拟步长 411 | density = float(df_params.values[1][1]) #体系密度 412 | 413 | 414 | initUcell = np.asarray([0.0, 0.0]) #初始化元胞,具体大小由输入文件密度决定 415 | initUcell[0] = int(df_params.values[2][1]) #元胞X方向长度=20 416 | initUcell[1] = int(df_params.values[3][1]) #元胞X方向长度=20 417 | 418 | stepAvg = int(df_params.values[4][1]) #抽样平均数 419 | stepEquil = float(df_params.values[5][1]) #平衡步长 420 | stepLimit = float(df_params.values[6][1]) #限制的步数 421 | temperature = float(df_params.values[7][1]) #体系温度 422 | 423 | #定义了一个Mol类对象的数组,大小为20*20=400. mol[0]~mol[399],共400个氩原子分子 424 | mol = [Mol(np.asarray([0.0, 0.0]), \ 425 | np.asarray([0.0, 0.0]), \ 426 | np.asarray([0.0, 0.0])) for i in range(int(initUcell[0]*initUcell[1]))] 427 | 428 | global nMol #定义分子的数量变量 429 | nMol = len(mol) #为mol数组的长度为400 430 | 431 | # 氩-氩相互作用的LJ势参数: 432 | epsilon = 1 433 | sigma = 1 434 | 435 | # 执行体系初始化相关功能函数 436 | SetParams() #设置主程序内部模拟所需参数 437 | SetupJob() #初始化粒子的坐标,速度,加速 438 | moreCycles = 1 #MD循环控制开关变量1:run; 0:stop 439 | 440 | n = 0 #记录步数,用来给每一步输出的坐标图命名 441 | while moreCycles: #MD循环 442 | SingleStep() #求解每一步的运动方程 443 | if mov==1: 444 | plotMolCoo(mol, workdir, n) #制作单个步长的粒子坐标图 445 | n += 1 446 | if stepCount >= stepLimit: 447 | moreCycles = 0 448 | 449 | #输出模拟过程中生成的参数 450 | columns = ['timestep','timeNow', '$\Sigma v$', 'E', '$\sigma E$', 'Ek', '$\sigma Ek$', 'P_1', 'P_2'] 451 | df_systemParams = pd.DataFrame(systemParams, columns=columns) 452 | print(df_systemParams) 453 | if mov==1: 454 | makeMov() # 制作视频 455 | GraphOutput() #输出体系测定量随模拟时间变化的图像 456 | -------------------------------------------------------------------------------- /configura/Rap_2_LJP.in: -------------------------------------------------------------------------------- 1 | deltaT 0.005 2 | density 0.8 3 | initUcell_x 20 4 | initUcell_y 20 5 | stepAvg 100 6 | stepEquil 0 7 | stepLimit 500 8 | temperature 1. 9 | -------------------------------------------------------------------------------- /exmple_in_jupyter notebook/.ipynb_checkpoints/MD_LJP-checkpoint.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": null, 6 | "id": "33f7674e", 7 | "metadata": { 8 | "ExecuteTime": { 9 | "end_time": "2021-09-27T09:50:56.152591Z", 10 | "start_time": "2021-09-27T09:50:42.955752Z" 11 | }, 12 | "scrolled": false 13 | }, 14 | "outputs": [], 15 | "source": [ 16 | "# 代码1:..........................................................................................\n", 17 | "import pandas as pd\n", 18 | "import math\n", 19 | "import os\n", 20 | "import matplotlib.pyplot as plt\n", 21 | "plt.style.use('seaborn-whitegrid')\n", 22 | "import numpy as np\n", 23 | "from PIL import Image\n", 24 | "import glob\n", 25 | "import moviepy.editor as mp\n", 26 | "from datetime import datetime\n", 27 | "import time\n", 28 | "import os.path\n", 29 | "from os import path\n", 30 | "import shutil\n", 31 | "%matplotlib inline \n", 32 | "\n", 33 | "# 代码2:..........................................................................................\n", 34 | "class Mol():\n", 35 | " def __init__(self,r,rv,ra):\n", 36 | " \"\"\"\n", 37 | " 每一分子对象(mol)所具有的属性如下:\n", 38 | " r:原子坐标\n", 39 | " rv:原子速度\n", 40 | " ra:原子加速度向量\n", 41 | " \"\"\"\n", 42 | " #初始化以上属性的值\n", 43 | " self.r=np.asarray([0.0,0.0])\n", 44 | " self.rv=np.asarray([0.0,0.0])\n", 45 | " self.ra=np.asarray([0.0,0.0])\n", 46 | " \n", 47 | "# 体系属性类,主要用来求体系属性(测定量)的平均值和标准差 \n", 48 | "class Prop():\n", 49 | " def __init__(self, val, sum1, sum2 ):\n", 50 | " self.val=val #体系测定量的值\n", 51 | " self.sum1=sum1 #体系测定量的值的和\n", 52 | " self.sum2=sum2 #体系测定量的值的平方和\n", 53 | "\n", 54 | " \n", 55 | "# 代码3:..........................................................................................\n", 56 | "def Sqr(x):\n", 57 | " return (x * x)\n", 58 | "\n", 59 | "def Cube(x):\n", 60 | " return ((x) * (x) * (x))\n", 61 | "\n", 62 | "# 用于生成 (0,1) 范围内的均匀分布的随机数,见18.4\n", 63 | "def RandR():\n", 64 | " global randSeedP\n", 65 | " randSeedP=(randSeedP * IMUL + IADD) & MASK\n", 66 | " return (randSeedP*SCALE)\n", 67 | "\n", 68 | "# 用于在二维空间产生具有均匀分布的随机方向的单位向量\n", 69 | "def VRand(p):\n", 70 | " s: float\n", 71 | " s = 2. * math.pi * RandR()\n", 72 | " p[0] = math.cos(s)\n", 73 | " p[1] = math.sin(s)\n", 74 | " return p\n", 75 | "\n", 76 | "# 周期性边界(PBC)算法:\n", 77 | "# PBC算法第一部分:对体系内粒子坐标的约束\n", 78 | "def VWrapAll(v):\n", 79 | " #对元胞x方向的坐标约束\n", 80 | " if v[0]>=0.5*region[0]: \n", 81 | " v[0]-=region[0]\n", 82 | " elif v[0]<-0.5*region[0]:\n", 83 | " v[0] +=region[0]\n", 84 | " \n", 85 | " #对元胞y方向的坐标约束\n", 86 | " if v[1] >= 0.5 * region[1]:\n", 87 | " v[1] -= region[1]\n", 88 | " elif v[1] < -0.5 * region[1]:\n", 89 | " v[1] += region[1]\n", 90 | " \n", 91 | " \n", 92 | "# 代码5:.......................................................................................... \n", 93 | "def SetParams():\n", 94 | " global rCut #截断距离\n", 95 | " global region #区域\n", 96 | " global velMag #速度幅度\n", 97 | " \n", 98 | " #截断值为势阱底部时的原子间距离,即rmin=rCut=2^1/6 *sigma\n", 99 | " rCut=math.pow(2.,1./6. * sigma)\n", 100 | " region=np.multiply(1./math.sqrt(density),initUcell) \n", 101 | " nMol=len(mol)\n", 102 | " \n", 103 | " #速度的变化幅度取决于温度的高低\n", 104 | " velMag = math.sqrt(NDIM * (1. -1. /nMol) * temperature)\n", 105 | " \n", 106 | " \n", 107 | "# 代码7:..........................................................................................\n", 108 | "def InitCoords():\n", 109 | " c=np.asarray([0.0,0.0]) #初始坐标值\n", 110 | " gap=np.divide(region,initUcell) #单个粒子所在元胞的大小\n", 111 | " n=0 #给粒子计数\n", 112 | " \n", 113 | " #将400个原子分别放到元胞中(这些元胞向x,y方向扩胞成了region区域)\n", 114 | " for ny in range(0, int(initUcell[1])):\n", 115 | " for nx in range(0, int(initUcell[0])):\n", 116 | " c = np.asarray([nx+0.5, ny+0.5])\n", 117 | " c = np.multiply(c, gap)\n", 118 | " c = np.add(c, np.multiply(-0.5, region))\n", 119 | " mol[n].r = c\n", 120 | " n=n+1\n", 121 | " \n", 122 | " \n", 123 | "def InitVels():\n", 124 | " global vSum\n", 125 | " vSum=np.zeros(vSum.shape) #返回特定大小,以0填充新的数组:[0,0],这里是形状\n", 126 | " \n", 127 | " for n in range(nMol):\n", 128 | " VRand(mol[n].rv) #产生随机随机速度向量[x,y]\n", 129 | " mol[n].rv=np.multiply(mol[n].rv,velMag) #根据温度,生成新的速度\n", 130 | " vSum=np.add(vSum,mol[n].rv) #质心的速度,系统总速度各质点速度的总和\n", 131 | " \n", 132 | " #调整度方向,以确保质心是静止的,这里取400个粒子在,x,y方向的平均速度1/400*Vsum+每一个原子的速度\n", 133 | " for n in range (nMol):\n", 134 | " mol[n].rv=np.add(mol[n].rv,np.multiply((- 1.0 / nMol),vSum))\n", 135 | " \n", 136 | " \n", 137 | "def InitAccels():\n", 138 | " for n in range(nMol):\n", 139 | " mol[n].ra=np.zeros(mol[n].ra.shape)\n", 140 | " \n", 141 | "\n", 142 | "# 代码8:..........................................................................................\n", 143 | "def PropZero(v):\n", 144 | " v.sum1 = v.sum2 = 0.\n", 145 | " return v \n", 146 | " \n", 147 | "def PropAccum(v):\n", 148 | " v.sum1 += v.val\n", 149 | " v.sum2 += Sqr(v.val)\n", 150 | " return v \n", 151 | "\n", 152 | "def PropAvg(v, n):\n", 153 | " v.sum1 /= n\n", 154 | " v.sum2 = math.sqrt(max(v.sum2 / n - Sqr(v.sum1), 0.)) \n", 155 | " return v \n", 156 | "\n", 157 | "\n", 158 | "def AccumProps(icode):\n", 159 | " \n", 160 | " if icode == 0: # 0:初始化 \n", 161 | " PropZero(totEnergy)\n", 162 | " PropZero(kinEnergy)\n", 163 | " PropZero(pressure) \n", 164 | " if icode == 1: # 1:求和 \n", 165 | " PropAccum(totEnergy)\n", 166 | " PropAccum(kinEnergy)\n", 167 | " PropAccum(pressure) \n", 168 | " if icode == 2: # 2:求平均值和标准差\n", 169 | " PropAvg(totEnergy, stepAvg)\n", 170 | " PropAvg(kinEnergy, stepAvg)\n", 171 | " PropAvg(pressure, stepAvg) \n", 172 | " \n", 173 | " \n", 174 | "# 代码6:..........................................................................................\n", 175 | "def SetupJob(): \n", 176 | " global stepCount #步长计数\n", 177 | " \n", 178 | " stepCount = 0 #初始化步长计数变量的值\n", 179 | " InitCoords() #初始化坐标\n", 180 | " InitVels() #初始化速度\n", 181 | " InitAccels() #初始化加速度\n", 182 | " AccumProps(0) #初始化系统属性值(总能量,动能,压强)\n", 183 | " \n", 184 | "\n", 185 | "# 代码10:.........................................................................................\n", 186 | "def LeapfrogStep(part):\n", 187 | " if part == 1:\n", 188 | " for n in range (nMol):\n", 189 | " mol[n].rv=np.add(mol[n].rv,np.multiply(0.5 * deltaT,mol[n].ra))\n", 190 | " mol[n].r=np.add(mol[n].r,np.multiply(deltaT,mol[n].rv))\n", 191 | " else :\n", 192 | " for n in range(nMol):\n", 193 | " mol[n].rv=np.add(mol[n].rv,np.multiply(0.5 * deltaT,mol[n].ra))\n", 194 | "\n", 195 | " \n", 196 | "# 代码11:...................................................................................\n", 197 | "def ApplyBoundaryCond():\n", 198 | " for n in range(nMol):\n", 199 | " VWrapAll(mol[n].r)\n", 200 | " \n", 201 | "# 代码12:...................................................................................\n", 202 | "def ComputeForces():\n", 203 | " global virSum #用于计算压强的中间变量(fij*rij)和\n", 204 | " global uSum #LJ势能和\n", 205 | " fcVal=0 # 原子j对原子i施加的力\n", 206 | " rrCut=Sqr(rCut) # rCut:Rc,截断半径的平方\n", 207 | " \n", 208 | " #初始化分子加速度\n", 209 | " for n in range (nMol):\n", 210 | " mol[n].ra=np.zeros(mol[n].ra.shape)\n", 211 | " \n", 212 | " uSum=0. #初始化LJ势能和值\n", 213 | " virSum=0.\n", 214 | " n=0\n", 215 | " for j1 in range(nMol-1):\n", 216 | " for j2 in range(j1+1,nMol):\n", 217 | " \n", 218 | " # 使Delta Rij。(RJ1-RJ2的平方之和)\n", 219 | " dr=np.subtract(mol[j1].r,mol[j2].r) # dr包含Rj1和Rj2之间的x,y坐标差值\n", 220 | " VWrapAll(dr) #应用PBC约束原子在元胞内,更新了dr[0],dr[1]\n", 221 | " rr=(dr[0] * dr[0] + dr[1] * dr[1]) #两原子距离的平方,这里并未求两原子间距离的绝对值,没必要,因为与截断半径只是比大小,省去了平方根的计算\n", 222 | " r=np.sqrt(rr) #r两为原子间的距离 \n", 223 | " \n", 224 | " # 这里是使用原子距离的平方来 dr2 < Rc^2 来判断两原子键相互作用力,\n", 225 | " if(rr < rrCut):\n", 226 | " fcVal = 48 * epsilon * np.power(sigma, 12) / np.power(r, 13) - 24 * epsilon * np.power(sigma, 6) / np.power(r, 7)\n", 227 | " \n", 228 | " # 更新加速度\n", 229 | " mol[j1].ra = np.add(mol[j1].ra, np.multiply(fcVal, dr))\n", 230 | " mol[j2].ra = np.add(mol[j2].ra, np.multiply(-fcVal, dr))\n", 231 | " \n", 232 | " #LJ势能计算\n", 233 | " uSum += 4 * epsilon * np.power(sigma/r, 12)/r - np.power(sigma/r, 6) \n", 234 | " virSum += fcVal * rr\n", 235 | " \n", 236 | "# 代码13:...................................................................................\n", 237 | "def EvalProps():\n", 238 | " global vSum\n", 239 | " vvSum = 0. #系统速度平方的总和\n", 240 | " vSum = np.zeros(vSum.shape) #质心速度,为系统速度=各质点速度总和,初始化为[0,0]\n", 241 | " \n", 242 | " global kinEnergy #动能\n", 243 | " global totEenergy #总能量\n", 244 | " global pressure #压强\n", 245 | " \n", 246 | " #求得质心速度\n", 247 | " for n in range(nMol):\n", 248 | " vSum =np.add(vSum,mol[n].rv) \n", 249 | " vv = (mol[n].rv[0] * mol[n].rv[0] + mol[n].rv[1] * mol[n].rv[1]) #xv^2+yv^2\n", 250 | " vvSum += vv\n", 251 | " \n", 252 | " # 在二维分子体系中,热力学属性值的数值计算方法\n", 253 | " kinEnergy.val = (0.5 * vvSum) / nMol #单个原子动能,nMol代表原子数\n", 254 | " totEnergy.val = kinEnergy.val + (uSum / nMol) #总能量:单个原子的动能+单个原子的势能\n", 255 | " pressure.val = density * (vvSum + virSum) / (nMol * NDIM) #体系压强\n", 256 | " \n", 257 | "\n", 258 | "# 代码14:...................................................................................\n", 259 | "def PrintSummary():\n", 260 | "\n", 261 | " print(stepCount, \\\n", 262 | " \"{0:.4f}\".format(timeNow), \\\n", 263 | " \"{0:.4f}\".format(vSum[0] / nMol) ,\\\n", 264 | " \"{0:.4f}\".format(totEnergy.sum1),\\\n", 265 | " \"{0:.4f}\".format(totEnergy.sum2), \\\n", 266 | " \"{0:.4f}\".format(kinEnergy.sum1), \\\n", 267 | " \"{0:.4f}\".format(kinEnergy.sum2),\\\n", 268 | " \"{0:.4f}\".format(pressure.sum1),\\\n", 269 | " \"{0:.4f}\".format(pressure.sum2))\n", 270 | " \n", 271 | " return (stepCount, \\\n", 272 | " timeNow, \\\n", 273 | " (vSum[0] / nMol) ,\\\n", 274 | " totEnergy.sum1,\\\n", 275 | " totEnergy.sum2, \\\n", 276 | " kinEnergy.sum1, \\\n", 277 | " kinEnergy.sum2,\\\n", 278 | " pressure.sum1,\\\n", 279 | " pressure.sum2) \n", 280 | "\n", 281 | "# 代码9:------------------------------------------------------------------------------------------\n", 282 | "def SingleStep():\n", 283 | " \n", 284 | " global stepCount # 步长计数\n", 285 | " global timeNow # 模拟运行时间\n", 286 | "\n", 287 | " stepCount +=1 \n", 288 | " timeNow = stepCount * deltaT #模拟运行的时间=步数x步长\n", 289 | " LeapfrogStep(1) #求解运动方程积分\n", 290 | " ApplyBoundaryCond() #应用周期性边界条件\n", 291 | " ComputeForces() # 计算原间相互作用力\n", 292 | " LeapfrogStep(2) # 坐标和速度的积分\n", 293 | " EvalProps() #计算系统属性值(速度,,速度平方和,总能量,动能,压力)\n", 294 | " AccumProps(1) #系统属性值求和\n", 295 | " \n", 296 | " #每一百步统计系统的属性值(0,100,200,300,400,500),可以设置stepAvg的值进行自定义\n", 297 | " if (stepCount % stepAvg == 0):\n", 298 | " AccumProps(2) #求系统的属性值的平均值和标准差\n", 299 | " systemParams.append(PrintSummary()) #将结果加入到 systemParams列表中\n", 300 | " AccumProps(0) # 重置系统属性值用来下一次的统计\n", 301 | " \n", 302 | "\n", 303 | "# 代码15:...................................................................................\n", 304 | "def plotMolCoo(mol,workdir,n):\n", 305 | " import matplotlib.patches as mpatches\n", 306 | " import matplotlib.pyplot as plt\n", 307 | " \n", 308 | " Time=timeNow #模拟时长\n", 309 | " Sigma_v = \"{0:.4f}\".format(vSum[0] / nMol)\n", 310 | " E = \"{0:.4f}\".format(totEnergy.sum1)\n", 311 | " Sigma_E = \"{0:.4f}\".format(totEnergy.sum2)\n", 312 | " Ek = \"{0:.4f}\".format(kinEnergy.sum1)\n", 313 | " Sigma_Ek = \"{0:.4f}\".format(kinEnergy.sum2)\n", 314 | " P_1 = \"{0:.4f}\".format(pressure.sum1)\n", 315 | " P_2 = \"{0:.4f}\".format(pressure.sum2) \n", 316 | " \n", 317 | " \n", 318 | " TileName = (workdir+'coo/'+str(n)+'.png') #传入的n表示n步,这里用来给生成的图像命名\n", 319 | " x = [] #新建列表保存粒子x的坐标\n", 320 | " y = [] #新建列表保存粒子y的坐标\n", 321 | " \n", 322 | " # 遍历0-400号原子的坐标,加入到列表中去\n", 323 | " for n in range(len(mol)):\n", 324 | " x.append(mol[n].r[0])\n", 325 | " y.append(mol[n].r[1])\n", 326 | " \n", 327 | " # 标记400个原子中两个位置较为居中的两相邻原子mol[250]和mol[251],用于观察\n", 328 | " mark_1 = int(len(mol)/2 + len(mol)/8)\n", 329 | " mark_2 = int(len(mol)/2 + len(mol)/8 + 1)\n", 330 | " \n", 331 | " # 根据原子坐标绘制图形\n", 332 | " plt.plot(x, y, 'o', color='blue') #每个原子均为蓝色圆形\n", 333 | " plt.plot(x[mark_1], y[mark_1], 'o', color='red') #标记mark_1为红色\n", 334 | " plt.plot(x[mark_2], y[mark_2], 'o', color='cyan') #标记mark_2为青色\n", 335 | " \n", 336 | " # 绘制图标的标题\n", 337 | " plt.title('timestep:'+\"{0:.4f}\".format(timeNow)+'; '+\\\n", 338 | " '$\\Sigma v$:'+Sigma_v+'; '+\\\n", 339 | " 'E:'+E+'; '+\\\n", 340 | " '$\\sigma E$:'+Sigma_E+';\\n'+\\\n", 341 | " 'Ek:'+Ek+'; ' +\\\n", 342 | " '$\\sigma Ek$:'+Sigma_Ek+'; '+\\\n", 343 | " 'P.sum1:'+P_1+'; '+\\\n", 344 | " 'P.sum2:'+P_2+'; ', loc='left')\n", 345 | " \n", 346 | " plt.savefig(TileName, dpi=100) #保存为坐标图为.png图片\n", 347 | " \n", 348 | "\n", 349 | "# 代码16:...................................................................................\n", 350 | "def makeMov():\n", 351 | " \n", 352 | " # 将多张.png图像转为gif图的片段\n", 353 | " frames = []\n", 354 | " imgs = sorted(glob.glob('coo/*.png'), key=os.path.getmtime)\n", 355 | " for i in imgs:\n", 356 | " temp = Image.open(i)\n", 357 | " keep = temp.copy()\n", 358 | " frames.append(keep)\n", 359 | " temp.close()\n", 360 | " \n", 361 | " # 删除全部的.png图\n", 362 | " for i in imgs:\n", 363 | " os.remove(i) \n", 364 | "\n", 365 | " # 片段合并保存为GIF文件,永远循环播放\n", 366 | " frames[0].save('coo/coordinates.gif', format='GIF',\n", 367 | " append_images=frames[1:],\n", 368 | " save_all=True,\n", 369 | " duration=30, loop=0)\n", 370 | "\n", 371 | " # 将GIF图转换成MP4视频文件\n", 372 | " clip = mp.VideoFileClip(\"coo/coordinates.gif\")\n", 373 | " clip.write_videofile(\"coo/\"+\"coordinates\"+\".mp4\")\n", 374 | " \n", 375 | " # 删除gif图\n", 376 | " os.remove(\"coo/coordinates.gif\")\n", 377 | " \n", 378 | "\n", 379 | "# 代码17:...................................................................................\n", 380 | "def GraphOutput():\n", 381 | " \n", 382 | " ax = \\\n", 383 | " df_systemParams.plot(x=\"timestep\", y='$\\Sigma v$', kind=\"line\")\n", 384 | " df_systemParams.plot(x=\"timestep\", y='E', kind=\"line\", ax=ax, color=\"C1\")\n", 385 | " df_systemParams.plot(x=\"timestep\", y='$\\sigma E$', kind=\"line\", ax=ax, color=\"C2\")\n", 386 | " df_systemParams.plot(x=\"timestep\", y='Ek', kind=\"line\", ax=ax, color=\"C3\")\n", 387 | " df_systemParams.plot(x=\"timestep\", y='$\\sigma Ek$', kind=\"line\", ax=ax, color=\"C4\")\n", 388 | " df_systemParams.plot(x=\"timestep\", y='P_1', kind=\"line\", ax=ax, color=\"C9\")\n", 389 | " df_systemParams.plot(x=\"timestep\", y='P_2', kind=\"line\", ax=ax, color=\"C9\")\n", 390 | " \n", 391 | " plt.savefig('plot.jpg', dpi=300)\n", 392 | " \n", 393 | " \n", 394 | "# 代码3:************************************Main************************************************\n", 395 | "mov = 1 # 如果你想做一个视频的话,设置 mov=1\n", 396 | "workdir = str(os.getcwd()+'/') # 为所有png和视频设置一个工作目录\n", 397 | "\n", 398 | "if path.exists(str(workdir+'coo'))==False:\n", 399 | " os.makedirs(str(workdir+'coo'))\n", 400 | "else:\n", 401 | " shutil.rmtree(str(workdir+'coo'))\n", 402 | " os.makedirs(str(workdir+'coo'))\n", 403 | "\n", 404 | "# 加载输入参数文件\n", 405 | "df_params = pd.read_csv('Rap_2_LJP.in', sep='\\t', header=None, names=['parameter', 'value'])\n", 406 | "\n", 407 | "#初始该模拟系统热力学属性值\n", 408 | "NDIM = 2 #二维度设置\n", 409 | "vSum = np.asarray([0.0, 0.0]) #速度之和\n", 410 | "kinEnergy =Prop(0.0, 0.0, 0.0) #动能\n", 411 | "totEnergy =Prop(0.0, 0.0, 0.0) #势能\n", 412 | "pressure =Prop(0.0, 0.0, 0.0) #压强\n", 413 | "\n", 414 | "systemParams = [] #初始化系统参数列表\n", 415 | "\n", 416 | "#用于产生随机数算法函数的变量\n", 417 | "IADD = 453806245\n", 418 | "IMUL = 314159269\n", 419 | "MASK = 2147483647\n", 420 | "SCALE = 0.4656612873e-9\n", 421 | "randSeedP = 17\n", 422 | "\n", 423 | "#初始化参数:把Rap_2_LJP.文件中的参数值传递给MD模拟所需要的相关变量\n", 424 | "deltaT = float(df_params.values[0][1]) #模拟步长\n", 425 | "density = float(df_params.values[1][1]) #体系密度\n", 426 | "\n", 427 | "\n", 428 | "initUcell = np.asarray([0.0, 0.0]) #初始化元胞,具体大小由输入文件密度决定\n", 429 | "initUcell[0] = int(df_params.values[2][1]) #元胞X方向长度=20\n", 430 | "initUcell[1] = int(df_params.values[3][1]) #元胞X方向长度=20\n", 431 | " \n", 432 | "stepAvg = int(df_params.values[4][1]) #抽样平均数\n", 433 | "stepEquil = float(df_params.values[5][1]) #平衡步长\n", 434 | "stepLimit = float(df_params.values[6][1]) #限制的步数\n", 435 | "temperature = float(df_params.values[7][1]) #体系温度\n", 436 | "\n", 437 | "#定义了一个Mol类对象的数组,大小为20*20=400. mol[0]~mol[399],共400个氩原子分子\n", 438 | "mol = [Mol(np.asarray([0.0, 0.0]), \\\n", 439 | " np.asarray([0.0, 0.0]), \\\n", 440 | " np.asarray([0.0, 0.0])) for i in range(int(initUcell[0]*initUcell[1]))]\n", 441 | "\n", 442 | "global nMol #定义分子的数量变量\n", 443 | "nMol = len(mol) #为mol数组的长度为400\n", 444 | "\n", 445 | "# 氩-氩相互作用的LJ势参数:\n", 446 | "epsilon = 1\n", 447 | "sigma = 1\n", 448 | "\n", 449 | "# 执行体系初始化相关功能函数\n", 450 | "SetParams() #设置主程序内部模拟所需参数\n", 451 | "SetupJob() #初始化粒子的坐标,速度,加速\n", 452 | "moreCycles = 1 #MD循环控制开关变量1:run; 0:stop\n", 453 | "\n", 454 | "n = 0 #记录步数,用来给每一步输出的坐标图命名 \n", 455 | "while moreCycles: #MD循环\n", 456 | " SingleStep() #求解每一步的运动方程\n", 457 | " if mov==1:\n", 458 | " plotMolCoo(mol, workdir, n) #制作单个步长的粒子坐标图\n", 459 | " n += 1\n", 460 | " if stepCount >= stepLimit:\n", 461 | " moreCycles = 0\n", 462 | " \n", 463 | "#输出模拟过程中生成的参数\n", 464 | "columns = ['timestep','timeNow', '$\\Sigma v$', 'E', '$\\sigma E$', 'Ek', '$\\sigma Ek$', 'P_1', 'P_2']\n", 465 | "df_systemParams = pd.DataFrame(systemParams, columns=columns) \n", 466 | "df_systemParamssy.\n", 467 | "if mov==1: \n", 468 | " makeMov() # 制作视频\n", 469 | "GraphOutput() #输出体系测定量随模拟时间变化的图像" 470 | ] 471 | } 472 | ], 473 | "metadata": { 474 | "celltoolbar": "编辑元数据", 475 | "kernelspec": { 476 | "display_name": "Python 3 (ipykernel)", 477 | "language": "python", 478 | "name": "python3" 479 | }, 480 | "language_info": { 481 | "codemirror_mode": { 482 | "name": "ipython", 483 | "version": 3 484 | }, 485 | "file_extension": ".py", 486 | "mimetype": "text/x-python", 487 | "name": "python", 488 | "nbconvert_exporter": "python", 489 | "pygments_lexer": "ipython3", 490 | "version": "3.9.6" 491 | }, 492 | "toc": { 493 | "base_numbering": 1, 494 | "nav_menu": {}, 495 | "number_sections": true, 496 | "sideBar": true, 497 | "skip_h1_title": false, 498 | "title_cell": "Table of Contents", 499 | "title_sidebar": "Contents", 500 | "toc_cell": false, 501 | "toc_position": {}, 502 | "toc_section_display": true, 503 | "toc_window_display": false 504 | } 505 | }, 506 | "nbformat": 4, 507 | "nbformat_minor": 5 508 | } 509 | -------------------------------------------------------------------------------- /exmple_in_jupyter notebook/MD_LJP.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": 8, 6 | "id": "33f7674e", 7 | "metadata": { 8 | "ExecuteTime": { 9 | "end_time": "2021-09-29T06:04:28.618922Z", 10 | "start_time": "2021-09-29T05:56:19.226429Z" 11 | }, 12 | "scrolled": true 13 | }, 14 | "outputs": [ 15 | { 16 | "name": "stdout", 17 | "output_type": "stream", 18 | "text": [ 19 | "100 0.5000 -0.0000 2.2080 0.1555 0.6488 0.0920 4.6033 0.8406\n", 20 | "200 1.0000 -0.0000 2.1828 0.0220 0.6425 0.0124 4.5977 0.0897\n", 21 | "300 1.5000 0.0000 2.1793 0.0251 0.6352 0.0192 4.6146 0.1336\n", 22 | "400 2.0000 0.0000 2.1583 0.0219 0.6410 0.0173 4.5456 0.1210\n", 23 | "500 2.5000 0.0000 2.1602 0.0281 0.6447 0.0148 4.5389 0.1260\n" 24 | ] 25 | }, 26 | { 27 | "data": { 28 | "text/html": [ 29 | "
\n", 30 | "\n", 43 | "\n", 44 | " \n", 45 | " \n", 46 | " \n", 47 | " \n", 48 | " \n", 49 | " \n", 50 | " \n", 51 | " \n", 52 | " \n", 53 | " \n", 54 | " \n", 55 | " \n", 56 | " \n", 57 | " \n", 58 | " \n", 59 | " \n", 60 | " \n", 61 | " \n", 62 | " \n", 63 | " \n", 64 | " \n", 65 | " \n", 66 | " \n", 67 | " \n", 68 | " \n", 69 | " \n", 70 | " \n", 71 | " \n", 72 | " \n", 73 | " \n", 74 | " \n", 75 | " \n", 76 | " \n", 77 | " \n", 78 | " \n", 79 | " \n", 80 | " \n", 81 | " \n", 82 | " \n", 83 | " \n", 84 | " \n", 85 | " \n", 86 | " \n", 87 | " \n", 88 | " \n", 89 | " \n", 90 | " \n", 91 | " \n", 92 | " \n", 93 | " \n", 94 | " \n", 95 | " \n", 96 | " \n", 97 | " \n", 98 | " \n", 99 | " \n", 100 | " \n", 101 | " \n", 102 | " \n", 103 | " \n", 104 | " \n", 105 | " \n", 106 | " \n", 107 | " \n", 108 | " \n", 109 | " \n", 110 | " \n", 111 | " \n", 112 | " \n", 113 | " \n", 114 | " \n", 115 | " \n", 116 | " \n", 117 | " \n", 118 | " \n", 119 | " \n", 120 | "
timesteptimeNow$\\Sigma v$E$\\sigma E$Ek$\\sigma Ek$P_1P_2
01000.5-1.776357e-172.2079950.1555260.6487730.0920444.6033410.840562
12001.0-2.942091e-172.1828440.0220060.6424840.0124094.5976680.089659
23001.51.221245e-172.1792750.0251340.6351850.0191704.6145880.133551
34002.01.706968e-172.1583080.0219440.6410050.0173184.5456010.120993
45002.51.457168e-172.1602340.0281120.6446720.0148384.5389440.126002
\n", 121 | "
" 122 | ], 123 | "text/plain": [ 124 | " timestep timeNow $\\Sigma v$ E $\\sigma E$ Ek \\\n", 125 | "0 100 0.5 -1.776357e-17 2.207995 0.155526 0.648773 \n", 126 | "1 200 1.0 -2.942091e-17 2.182844 0.022006 0.642484 \n", 127 | "2 300 1.5 1.221245e-17 2.179275 0.025134 0.635185 \n", 128 | "3 400 2.0 1.706968e-17 2.158308 0.021944 0.641005 \n", 129 | "4 500 2.5 1.457168e-17 2.160234 0.028112 0.644672 \n", 130 | "\n", 131 | " $\\sigma Ek$ P_1 P_2 \n", 132 | "0 0.092044 4.603341 0.840562 \n", 133 | "1 0.012409 4.597668 0.089659 \n", 134 | "2 0.019170 4.614588 0.133551 \n", 135 | "3 0.017318 4.545601 0.120993 \n", 136 | "4 0.014838 4.538944 0.126002 " 137 | ] 138 | }, 139 | "metadata": {}, 140 | "output_type": "display_data" 141 | }, 142 | { 143 | "name": "stdout", 144 | "output_type": "stream", 145 | "text": [ 146 | "Moviepy - Building video coo/coordinates.mp4.\n", 147 | "Moviepy - Writing video coo/coordinates.mp4\n", 148 | "\n" 149 | ] 150 | }, 151 | { 152 | "name": "stderr", 153 | "output_type": "stream", 154 | "text": [ 155 | " \r" 156 | ] 157 | }, 158 | { 159 | "name": "stdout", 160 | "output_type": "stream", 161 | "text": [ 162 | "Moviepy - Done !\n", 163 | "Moviepy - video ready coo/coordinates.mp4\n" 164 | ] 165 | }, 166 | { 167 | "data": { 168 | "image/png": "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\n", 169 | "text/plain": [ 170 | "
" 171 | ] 172 | }, 173 | "metadata": { 174 | "needs_background": "light" 175 | }, 176 | "output_type": "display_data" 177 | }, 178 | { 179 | "data": { 180 | "image/png": "iVBORw0KGgoAAAANSUhEUgAAAWYAAAEBCAYAAABL1w/0AAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjQuMywgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy/MnkTPAAAACXBIWXMAAAsTAAALEwEAmpwYAAAoHElEQVR4nO3de3xcdZ3/8dc5k0nSpGma3ktboBf67WW5GaAVkIsLiqC2oqUra3cF3F0XWekPfivgivIT+SE8ttxEF0UooiiK9QK4sriigtJyCeACTU9LS0svpG3aJG1zmUkyZ/84ZyZnJjNJJslkju37CXnMzPf7Pd/zmdOZz/nOd86cY7mui4iIhIdd7ABERCSdErOISMgoMYuIhIwSs4hIyCgxi4iEjBKziEjIlAy1g7q6Oh1vJyIyCLW1tVa28iEnZr/zQS9bX1/P/PnzhyOMYaW48qO48qO48nM4xlVXV5ezTlMZIiIho8QsIhIySswiIiGjxCwiEjJKzCIiIaPELCISMsNyuJxI2Liuiw6wl79URUvM8USC1TsbcQ52MXHLuwBY/h+AZQXuY3l1aWV+eaAsVW5Z6X0Flk0uk9beXyb4uKGtm5d3Naav32/QVwzB/oIx9DwnK0dcgeea0R+B/rZ1dLO9sYVu1yUBdLuQ8O8nUmUurgvduCRcUm0Trvc41ca/Tfht3bS2vcuSyyRc11svPf01tXQy+o2tgTK3J7bg42R91jKXbtKXSY+jZxk32H/Gc0/GBBABKvb+D6MiNqNsO8ut1bssV3narVdfYduUR2wiwReVyBAVLTG3dif4j3f2sjfeDW27AXBdcCE8I50DO4odQXbNbxekWxuwLYhYFjbeziAC2JZFxN+pRCyw/VsrcL+r06W8td1r6y9j+/W2BRH/1ragxLKI2GBj+2Xe+iKZy/jlmWUWPXURy9uRpfoPlNlYvLt3L5U142hPJGjvTtCeSNDW7d1v7OykvcNNq2vvTqSSej7KbKtXwi+3A4k8I6m3HuxixrbdqeSeLekHy8ptm3LbwtYO4IhQtMRcEy3htTMW9vvLmeRHUhcvcZO8j5sqSybyZDm9ytL7IdBfapnU+ryyjZs2cdxxx3nrCewwUssHPiq7GcsGY0uLwe8LMsr9QjetLBhrz3PatnUrs2fN7ElA9CS0ZHLKLLOs9ESWSrrBJEvPp4bBCO0vszqamH/ctAG3d12XuOsGErWXuDuCST2VyP12aWWJXsu2xDqzluN/UsxHKmnnGv3nGuEHE37WHYa3bFvCpbWr2/9kmP4pMu1TJ+mf9oby2gmLBQsWMGPGDMrLywE4//zzueqqq4oSS+jnmJMf4b0HaTUFXe/+iMWUsmhB1zEYFaU286sqih3GYcuyLMosizLbZmwB17N+/Xpmmnm9E3ow4ftlbb2Sfnp9RyJBa3c3jZ09ST9ZH0sM4vPnntcH/bwGksQJTNf1ntaz0vrwW5Po7qJk/xs9ddn6yLJ8si0ZZWTpo/KSv6NhUz0Trr4B27J4HHj8ZSfn87Ati49Y3RRiOBL6xCxyOLKsnukKCrj/73ZdOtKSu9vHCD/Brt27mThpcq8vT3s+xbm9PtXl/IQY/LSbZXkyPhWm95neR1NTE2NrqgcVQ9rjjDgI9vHxS9h4123w5E+Z8olLcYFDmzex/eH7mXvTbQC0bdvKO/d/g+Nu/negcAlUiVnkMBaxLCpLIlQSGVD7+kP7mH/0pAJHlduauh385OXtvcrb2to4UNE6qD4vOWUGH6+dPqC2Hd+8myVLlnD1Ry/glFNOof24qZy/6mZ+dOJsAK66/06uu+ELnH7SHMCbwisEJWYREd/mzZvp7u5m3rx5AIwaNYry8nIOHDjA9u3baWlp4fTTTy94HErMIhIaH6+dnnV0OxJfLsfjca6//npuuukmRo8enSqfM2cOW7Zs4Vvf+hYrV64saAxJ+uWfiAhw9913c9JJJ3HmmWemlc+ZM4c1a9bguu6Qzj2fD42YRUSABx98kKOPPpolS5YAcOqpp/KlL32JOXPmcP3117NmzZoRi0WJWUSE3F/kLV26lKVLl45oLJrKEBEJGSVmEZGQUWIWEQkZJWYRkZBRYhYRCRklZhGRkNHhciJyxHvhhRdYuXIlc+bMSZXV1NRwzz33FCUeJWYREWDx4sXceeedxQ4DUGIWkTB57Ufw6g96FR/d1grrKgfX58mfgpM+OcTARpYSs4gIsG7dOlasWJF6fPbZZ/OZz3ymKLEoMYtIeJz0yayj23dG4OxyYZrK0FEZIiIhoxGziAi9pzIA7r///tTFWUfSgBKzMWYSUAec7zjOhsKGJCIyshYtWsTatWuLHUZKv1MZxpgo8G2gvfDhiIjIQOaY/x24D9hV4FhERASwkpf3zsYY82lguuM4XzPG/B74bOZURl1dnVtRUTHoADo6Oooyh9MfxZUfxZUfxZWfwzGutrY2amtrrWx1/c0xXw64xpjzgJOAh40xH3UcpyHYaCiHsYzERRYHQ3HlR3HlR3Hl53CMq66uLmddn4nZcZyzkvcDI+aG3EuIiMhQ6ThmEZGQGfBxzI7jnFPAOERExKcRs4hIyOiXfyIifXj00Ue59957GT9+fKrs9ttvxxhTsHUqMYuI9GHjxo1cffXVLFu2bMTWqcQsIgJs2LCBm2++maamJrZs2YLrulx55ZU4jsPFF188orEoMYtIaDy++XF+vunnvcrb2tqo2Da4H7J97LiP8dHZH+2zTSwWY+XKldx+++2ccMIJ3HXXXcRiMT7/+c+zePFibrjhBmzb+0ru0ksvZfny5YOKZaCUmEXkiPf888+zcOFCTjjhBACMMTz33HM0NDRQU1PDE088MaLxKDGLSGh8dPZHs45uC/3Lv02bNjF37tzU4/Xr17Nw4UI2btzI7NmzC7beXHS4nIgc8caOHYvjOAC8/fbbPP3001x44YU4jsOsWbNGPB6NmEXkiHfRRRfxzDPP8OEPf5iamhruuOMOampqcByHl156iWeffRYAy7J45JFHqKwc5IVhB0iJWUSOeJWVldx33329yletWlWEaDSVISISOkrMIiIho8QsIhIySswiIiGjxCwiEjJKzCIiIaPD5UTkiPfCCy+wcuVK5syZkyqrqamhqamJm266acR//afELCICLF68mDvvvDOtbMWKFUWJRYlZREKj+Re/oGXNz3pXtLWxrWJwZ5er/vjFjF26dEhxPfPMM6xevZpvfvObjBkzZkh9DYQSs4gIsG7durQR8tlnnw3Ab37zG1566SW+/e1vUzHInUO+lJhFJDTGLl2adXRbX1/PMQU8uxxkn8r4wx/+wNq1azl06BAlJSOXLnVUhohIH7785S9z5plncs8994zYOjViFhGh91QGQEdHBwCf+9znWLZsGeeccw6nnHJKwWNRYhaRI96iRYtYu3Ztn21++ctfjlA0msoQEQkdJWYRkZBRYhYRCRklZhGRkFFiFhEJGSVmEZGQUWIWEQkZJWYRkZDRD0xERPrw6KOPcu+99zJ+/PhU2e23387+/ftZvXo13/nOd4Z9nUrMIiJ92LhxI1dffTXLli1LK1+9ejWzZs0qyDo1lSEiAmzYsIG//du/5cILL2TevHkYY7j77rtxHIf5Wc5st2HDBmbNmkU8Huf666/njjvuwHXdYYml3xGzMSYC3A8YwAU+6zjOG8OydhGRgA3r3qX+T+/2Km9ra2NDxSuD6nP+GVOZt3hqn21isRgrV67k9ttv54QTTuCuu+4iFovx+c9/nsWLF3PDDTdg29449tJLL2X58uU4jsP73/9+rrjiCj7xiU+wZMmSQcWXzUCmMj4C4DjOGcaYc4BbgOGLQESkyJ5//nkWLlzICSecAIAxhueee46GhgZqamp44okn0tp3dnayfft2Vq1axW233cbJJ588rPH0m5gdx/mFMeZJ/+ExQPOwRiAi4pu3OPvotr6+Put0wnDZtGkTc+fOTT1ev349CxcuZOPGjVkvxLp582aOP/54du/eTSQSGfZ4BvTln+M4XcaY7wEfAz6RWV9fXz/oADo6Ooa0fKEorvworvworvwUOq729nZef/11zjrrLHbu3MmTTz7J17/+dX7zm99QXV3da92/+93vmDFjBsuWLeOaa67h5ptvZuzYscMWj5XPZLUxZgrwArDAcZxWgLq6Ore2tnbQARR6TzhYiis/iis/iis/hY6rtbWVa6+9lh07dlBTU8P111/PwoULufbaa3nppZeoqakBwLIsHnnkEe69916OP/54Zs6cSUNDAw888ACrV68mGo0OeJ11dXXU1tZa2eoG8uXfCmC64zi3Am1Awv8TETksVFZWct999/UqX7VqVdb21113HeDtMM4991zOPffcYY1nIFMZPwNWG2OeBaLASsdx2oc1ChERSRnIl3+twCUjEIuIiKAfmIiIhI4Ss4hIyCgxi4iEjBKziEjIKDGLiISMTvspIke8F154gZUrVzJnzhzAO6nRRz7yEVasWJFzmf3793PllVfy1FNPUVZWNqzxKDGLSGj8pGE/P3p3X6/yttY4Fa9uGlSfn5w6nkumjOu33eLFi7nzzjsBiMfjXHDBBSxZsoQxY8b0avvcc8+xatUqmpqaBhVTfzSVISKS4dChQ9i2nfMERbZts3r1akaPHl2Q9WvELCKhccmUcVlHt965Mo4r6LrXrVvHihUrsCyLaDTKjTfeSGVlZda2Z5xxRkFjUWIWESF9KqPYNJUhIhIySswiIiGjqQwROeItWrSIRYsW5b3c/fffP+yHyoESs4hIVj/+8Y958skne5Vf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\n", 181 | "text/plain": [ 182 | "
" 183 | ] 184 | }, 185 | "metadata": { 186 | "needs_background": "light" 187 | }, 188 | "output_type": "display_data" 189 | } 190 | ], 191 | "source": [ 192 | "# 代码1:..........................................................................................\n", 193 | "import pandas as pd\n", 194 | "import math\n", 195 | "import os\n", 196 | "import matplotlib.pyplot as plt\n", 197 | "plt.style.use('seaborn-whitegrid')\n", 198 | "import numpy as np\n", 199 | "from PIL import Image\n", 200 | "import glob\n", 201 | "import moviepy.editor as mp\n", 202 | "from datetime import datetime\n", 203 | "import time\n", 204 | "import os.path\n", 205 | "from os import path\n", 206 | "import shutil\n", 207 | "from IPython.display import display\n", 208 | " \n", 209 | "\n", 210 | "# 代码2:..........................................................................................\n", 211 | "class Mol():\n", 212 | " def __init__(self,r,rv,ra):\n", 213 | " \"\"\"\n", 214 | " 每一分子对象(mol)所具有的属性如下:\n", 215 | " r:原子坐标\n", 216 | " rv:原子速度\n", 217 | " ra:原子加速度向量\n", 218 | " \"\"\"\n", 219 | " #初始化以上属性的值\n", 220 | " self.r=np.asarray([0.0,0.0])\n", 221 | " self.rv=np.asarray([0.0,0.0])\n", 222 | " self.ra=np.asarray([0.0,0.0])\n", 223 | " \n", 224 | "# 体系属性类,主要用来求体系属性(测定量)的平均值和标准差 \n", 225 | "class Prop():\n", 226 | " def __init__(self, val, sum1, sum2 ):\n", 227 | " self.val=val #体系测定量的值\n", 228 | " self.sum1=sum1 #体系测定量的值的和\n", 229 | " self.sum2=sum2 #体系测定量的值的平方和\n", 230 | "\n", 231 | " \n", 232 | "# 代码3:..........................................................................................\n", 233 | "def Sqr(x):\n", 234 | " return (x * x)\n", 235 | "\n", 236 | "def Cube(x):\n", 237 | " return ((x) * (x) * (x))\n", 238 | "\n", 239 | "# 用于生成 (0,1) 范围内的均匀分布的随机数,见18.4\n", 240 | "def RandR():\n", 241 | " global randSeedP\n", 242 | " randSeedP=(randSeedP * IMUL + IADD) & MASK\n", 243 | " return (randSeedP*SCALE)\n", 244 | "\n", 245 | "# 用于在二维空间产生具有均匀分布的随机方向的单位向量\n", 246 | "def VRand(p):\n", 247 | " s: float\n", 248 | " s = 2. * math.pi * RandR()\n", 249 | " p[0] = math.cos(s)\n", 250 | " p[1] = math.sin(s)\n", 251 | " return p\n", 252 | "\n", 253 | "# 周期性边界(PBC)算法:\n", 254 | "# PBC算法第一部分:对体系内粒子坐标的约束\n", 255 | "def VWrapAll(v):\n", 256 | " #对元胞x方向的坐标约束\n", 257 | " if v[0]>=0.5*region[0]: \n", 258 | " v[0]-=region[0]\n", 259 | " elif v[0]<-0.5*region[0]:\n", 260 | " v[0] +=region[0]\n", 261 | " \n", 262 | " #对元胞y方向的坐标约束\n", 263 | " if v[1] >= 0.5 * region[1]:\n", 264 | " v[1] -= region[1]\n", 265 | " elif v[1] < -0.5 * region[1]:\n", 266 | " v[1] += region[1]\n", 267 | " \n", 268 | " \n", 269 | "# 代码5:.......................................................................................... \n", 270 | "def SetParams():\n", 271 | " global rCut #截断距离\n", 272 | " global region #区域\n", 273 | " global velMag #速度幅度\n", 274 | " \n", 275 | " #截断值为势阱底部时的原子间距离,即rmin=rCut=2^1/6 *sigma\n", 276 | " rCut=math.pow(2.,1./6. * sigma)\n", 277 | " region=np.multiply(1./math.sqrt(density),initUcell) \n", 278 | " nMol=len(mol)\n", 279 | " \n", 280 | " #速度的变化幅度取决于温度的高低\n", 281 | " velMag = math.sqrt(NDIM * (1. -1. /nMol) * temperature)\n", 282 | " \n", 283 | " \n", 284 | "# 代码7:..........................................................................................\n", 285 | "def InitCoords():\n", 286 | " c=np.asarray([0.0,0.0]) #初始坐标值\n", 287 | " gap=np.divide(region,initUcell) #单个粒子所在元胞的大小\n", 288 | " n=0 #给粒子计数\n", 289 | " \n", 290 | " #将400个原子分别放到元胞中(这些元胞向x,y方向扩胞成了region区域)\n", 291 | " for ny in range(0, int(initUcell[1])):\n", 292 | " for nx in range(0, int(initUcell[0])):\n", 293 | " c = np.asarray([nx+0.5, ny+0.5])\n", 294 | " c = np.multiply(c, gap)\n", 295 | " c = np.add(c, np.multiply(-0.5, region))\n", 296 | " mol[n].r = c\n", 297 | " n=n+1\n", 298 | " \n", 299 | " \n", 300 | "def InitVels():\n", 301 | " global vSum\n", 302 | " vSum=np.zeros(vSum.shape) #返回特定大小,以0填充新的数组:[0,0],这里是形状\n", 303 | " \n", 304 | " for n in range(nMol):\n", 305 | " VRand(mol[n].rv) #产生随机随机速度向量[x,y]\n", 306 | " mol[n].rv=np.multiply(mol[n].rv,velMag) #根据温度,生成新的速度\n", 307 | " vSum=np.add(vSum,mol[n].rv) #质心的速度,系统总速度各质点速度的总和\n", 308 | " \n", 309 | " #调整度方向,以确保质心是静止的,这里取400个粒子在,x,y方向的平均速度1/400*Vsum+每一个原子的速度\n", 310 | " for n in range (nMol):\n", 311 | " mol[n].rv=np.add(mol[n].rv,np.multiply((- 1.0 / nMol),vSum))\n", 312 | " \n", 313 | " \n", 314 | "def InitAccels():\n", 315 | " for n in range(nMol):\n", 316 | " mol[n].ra=np.zeros(mol[n].ra.shape)\n", 317 | " \n", 318 | "\n", 319 | "# 代码8:..........................................................................................\n", 320 | "def PropZero(v):\n", 321 | " v.sum1 = v.sum2 = 0.\n", 322 | " return v \n", 323 | " \n", 324 | "def PropAccum(v):\n", 325 | " v.sum1 += v.val\n", 326 | " v.sum2 += Sqr(v.val)\n", 327 | " return v \n", 328 | "\n", 329 | "def PropAvg(v, n):\n", 330 | " v.sum1 /= n\n", 331 | " v.sum2 = math.sqrt(max(v.sum2 / n - Sqr(v.sum1), 0.)) \n", 332 | " return v \n", 333 | "\n", 334 | "\n", 335 | "def AccumProps(icode):\n", 336 | " \n", 337 | " if icode == 0: # 0:初始化 \n", 338 | " PropZero(totEnergy)\n", 339 | " PropZero(kinEnergy)\n", 340 | " PropZero(pressure) \n", 341 | " if icode == 1: # 1:求和 \n", 342 | " PropAccum(totEnergy)\n", 343 | " PropAccum(kinEnergy)\n", 344 | " PropAccum(pressure) \n", 345 | " if icode == 2: # 2:求平均值和标准差\n", 346 | " PropAvg(totEnergy, stepAvg)\n", 347 | " PropAvg(kinEnergy, stepAvg)\n", 348 | " PropAvg(pressure, stepAvg) \n", 349 | " \n", 350 | " \n", 351 | "# 代码6:..........................................................................................\n", 352 | "def SetupJob(): \n", 353 | " global stepCount #步长计数\n", 354 | " \n", 355 | " stepCount = 0 #初始化步长计数变量的值\n", 356 | " InitCoords() #初始化坐标\n", 357 | " InitVels() #初始化速度\n", 358 | " InitAccels() #初始化加速度\n", 359 | " AccumProps(0) #初始化系统属性值(总能量,动能,压强)\n", 360 | " \n", 361 | "\n", 362 | "# 代码10:.........................................................................................\n", 363 | "def LeapfrogStep(part):\n", 364 | " if part == 1:\n", 365 | " for n in range (nMol):\n", 366 | " mol[n].rv=np.add(mol[n].rv,np.multiply(0.5 * deltaT,mol[n].ra))\n", 367 | " mol[n].r=np.add(mol[n].r,np.multiply(deltaT,mol[n].rv))\n", 368 | " else :\n", 369 | " for n in range(nMol):\n", 370 | " mol[n].rv=np.add(mol[n].rv,np.multiply(0.5 * deltaT,mol[n].ra))\n", 371 | "\n", 372 | " \n", 373 | "# 代码11:...................................................................................\n", 374 | "def ApplyBoundaryCond():\n", 375 | " for n in range(nMol):\n", 376 | " VWrapAll(mol[n].r)\n", 377 | " \n", 378 | "# 代码12:...................................................................................\n", 379 | "def ComputeForces():\n", 380 | " global virSum #用于计算压强的中间变量(fij*rij)和\n", 381 | " global uSum #LJ势能和\n", 382 | " fcVal=0 # 原子j对原子i施加的力\n", 383 | " rrCut=Sqr(rCut) # rCut:Rc,截断半径的平方\n", 384 | " \n", 385 | " #初始化分子加速度\n", 386 | " for n in range (nMol):\n", 387 | " mol[n].ra=np.zeros(mol[n].ra.shape)\n", 388 | " \n", 389 | " uSum=0. #初始化LJ势能和值\n", 390 | " virSum=0.\n", 391 | " n=0\n", 392 | " for j1 in range(nMol-1):\n", 393 | " for j2 in range(j1+1,nMol):\n", 394 | " \n", 395 | " # 使Delta Rij。(RJ1-RJ2的平方之和)\n", 396 | " dr=np.subtract(mol[j1].r,mol[j2].r) # dr包含Rj1和Rj2之间的x,y坐标差值\n", 397 | " VWrapAll(dr) #应用PBC约束原子在元胞内,更新了dr[0],dr[1]\n", 398 | " rr=(dr[0] * dr[0] + dr[1] * dr[1]) #两原子距离的平方,这里并未求两原子间距离的绝对值,没必要,因为与截断半径只是比大小,省去了平方根的计算\n", 399 | " r=np.sqrt(rr) #r两为原子间的距离 \n", 400 | " \n", 401 | " # 这里是使用原子距离的平方来 dr2 < Rc^2 来判断两原子键相互作用力,\n", 402 | " if(rr < rrCut):\n", 403 | " fcVal = 48 * epsilon * np.power(sigma, 12) / np.power(r, 13) - 24 * epsilon * np.power(sigma, 6) / np.power(r, 7)\n", 404 | " \n", 405 | " # 更新加速度\n", 406 | " mol[j1].ra = np.add(mol[j1].ra, np.multiply(fcVal, dr))\n", 407 | " mol[j2].ra = np.add(mol[j2].ra, np.multiply(-fcVal, dr))\n", 408 | " \n", 409 | " #LJ势能计算\n", 410 | " uSum += 4 * epsilon * np.power(sigma/r, 12)/r - np.power(sigma/r, 6) \n", 411 | " virSum += fcVal * rr\n", 412 | " \n", 413 | "# 代码13:...................................................................................\n", 414 | "def EvalProps():\n", 415 | " global vSum\n", 416 | " vvSum = 0. #系统速度平方的总和\n", 417 | " vSum = np.zeros(vSum.shape) #质心速度,为系统速度=各质点速度总和,初始化为[0,0]\n", 418 | " \n", 419 | " global kinEnergy #动能\n", 420 | " global totEenergy #总能量\n", 421 | " global pressure #压强\n", 422 | " \n", 423 | " #求得质心速度\n", 424 | " for n in range(nMol):\n", 425 | " vSum =np.add(vSum,mol[n].rv) \n", 426 | " vv = (mol[n].rv[0] * mol[n].rv[0] + mol[n].rv[1] * mol[n].rv[1]) #xv^2+yv^2\n", 427 | " vvSum += vv\n", 428 | " \n", 429 | " # 在二维分子体系中,热力学属性值的数值计算方法\n", 430 | " kinEnergy.val = (0.5 * vvSum) / nMol #单个原子动能,nMol代表原子数\n", 431 | " totEnergy.val = kinEnergy.val + (uSum / nMol) #总能量:单个原子的动能+单个原子的势能\n", 432 | " pressure.val = density * (vvSum + virSum) / (nMol * NDIM) #体系压强\n", 433 | " \n", 434 | "\n", 435 | "# 代码14:...................................................................................\n", 436 | "def PrintSummary():\n", 437 | "\n", 438 | " print(stepCount, \\\n", 439 | " \"{0:.4f}\".format(timeNow), \\\n", 440 | " \"{0:.4f}\".format(vSum[0] / nMol) ,\\\n", 441 | " \"{0:.4f}\".format(totEnergy.sum1),\\\n", 442 | " \"{0:.4f}\".format(totEnergy.sum2), \\\n", 443 | " \"{0:.4f}\".format(kinEnergy.sum1), \\\n", 444 | " \"{0:.4f}\".format(kinEnergy.sum2),\\\n", 445 | " \"{0:.4f}\".format(pressure.sum1),\\\n", 446 | " \"{0:.4f}\".format(pressure.sum2))\n", 447 | " \n", 448 | " return (stepCount, \\\n", 449 | " timeNow, \\\n", 450 | " (vSum[0] / nMol) ,\\\n", 451 | " totEnergy.sum1,\\\n", 452 | " totEnergy.sum2, \\\n", 453 | " kinEnergy.sum1, \\\n", 454 | " kinEnergy.sum2,\\\n", 455 | " pressure.sum1,\\\n", 456 | " pressure.sum2) \n", 457 | "\n", 458 | "# 代码9:------------------------------------------------------------------------------------------\n", 459 | "def SingleStep():\n", 460 | " \n", 461 | " global stepCount # 步长计数\n", 462 | " global timeNow # 模拟运行时间\n", 463 | "\n", 464 | " stepCount +=1 \n", 465 | " timeNow = stepCount * deltaT #模拟运行的时间=步数x步长\n", 466 | " LeapfrogStep(1) #求解运动方程积分\n", 467 | " ApplyBoundaryCond() #应用周期性边界条件\n", 468 | " ComputeForces() # 计算原间相互作用力\n", 469 | " LeapfrogStep(2) # 坐标和速度的积分\n", 470 | " EvalProps() #计算系统属性值(速度,,速度平方和,总能量,动能,压力)\n", 471 | " AccumProps(1) #系统属性值求和\n", 472 | " \n", 473 | " #每一百步统计系统的属性值(0,100,200,300,400,500),可以设置stepAvg的值进行自定义\n", 474 | " if (stepCount % stepAvg == 0):\n", 475 | " AccumProps(2) #求系统的属性值的平均值和标准差\n", 476 | " systemParams.append(PrintSummary()) #将结果加入到 systemParams列表中\n", 477 | " AccumProps(0) # 重置系统属性值用来下一次的统计\n", 478 | " \n", 479 | "\n", 480 | "# 代码15:...................................................................................\n", 481 | "def plotMolCoo(mol,workdir,n):\n", 482 | " import matplotlib.patches as mpatches\n", 483 | " import matplotlib.pyplot as plt\n", 484 | " \n", 485 | " Time=timeNow #模拟时长\n", 486 | " Sigma_v = \"{0:.4f}\".format(vSum[0] / nMol)\n", 487 | " E = \"{0:.4f}\".format(totEnergy.sum1)\n", 488 | " Sigma_E = \"{0:.4f}\".format(totEnergy.sum2)\n", 489 | " Ek = \"{0:.4f}\".format(kinEnergy.sum1)\n", 490 | " Sigma_Ek = \"{0:.4f}\".format(kinEnergy.sum2)\n", 491 | " P_1 = \"{0:.4f}\".format(pressure.sum1)\n", 492 | " P_2 = \"{0:.4f}\".format(pressure.sum2) \n", 493 | " \n", 494 | " %matplotlib inline \n", 495 | " TileName = (workdir+'coo/'+str(n)+'.png') #传入的n表示n步,这里用来给生成的图像命名\n", 496 | " x = [] #新建列表保存粒子x的坐标\n", 497 | " y = [] #新建列表保存粒子y的坐标\n", 498 | " \n", 499 | " # 遍历0-400号原子的坐标,加入到列表中去\n", 500 | " for n in range(len(mol)):\n", 501 | " x.append(mol[n].r[0])\n", 502 | " y.append(mol[n].r[1])\n", 503 | " \n", 504 | " # 标记400个原子中两个位置较为居中的两相邻原子mol[250]和mol[251],用于观察\n", 505 | " mark_1 = int(len(mol)/2 + len(mol)/8)\n", 506 | " mark_2 = int(len(mol)/2 + len(mol)/8 + 1)\n", 507 | " \n", 508 | " # 根据原子坐标绘制图形\n", 509 | " plt.plot(x, y, 'o', color='blue') #每个原子均为蓝色圆形\n", 510 | " plt.plot(x[mark_1], y[mark_1], 'o', color='red') #标记mark_1为红色\n", 511 | " plt.plot(x[mark_2], y[mark_2], 'o', color='cyan') #标记mark_2为青色\n", 512 | " \n", 513 | " # 绘制图标的标题\n", 514 | " plt.title('timestep:'+\"{0:.4f}\".format(timeNow)+'; '+\\\n", 515 | " '$\\Sigma v$:'+Sigma_v+'; '+\\\n", 516 | " 'E:'+E+'; '+\\\n", 517 | " '$\\sigma E$:'+Sigma_E+';\\n'+\\\n", 518 | " 'Ek:'+Ek+'; ' +\\\n", 519 | " '$\\sigma Ek$:'+Sigma_Ek+'; '+\\\n", 520 | " 'P.sum1:'+P_1+'; '+\\\n", 521 | " 'P.sum2:'+P_2+'; ', loc='left')\n", 522 | " \n", 523 | " plt.savefig(TileName, dpi=100) #保存为坐标图为.png图片\n", 524 | " \n", 525 | "\n", 526 | "# 代码16:...................................................................................\n", 527 | "def makeMov():\n", 528 | " \n", 529 | " # 将多张.png图像转为gif图的片段\n", 530 | " frames = []\n", 531 | " imgs = sorted(glob.glob('coo/*.png'), key=os.path.getmtime)\n", 532 | " for i in imgs:\n", 533 | " temp = Image.open(i)\n", 534 | " keep = temp.copy()\n", 535 | " frames.append(keep)\n", 536 | " temp.close()\n", 537 | " \n", 538 | " # 删除全部的.png图\n", 539 | " for i in imgs:\n", 540 | " os.remove(i) \n", 541 | "\n", 542 | " # 片段合并保存为GIF文件,永远循环播放\n", 543 | " frames[0].save('coo/coordinates.gif', format='GIF',\n", 544 | " append_images=frames[1:],\n", 545 | " save_all=True,\n", 546 | " duration=30, loop=0)\n", 547 | "\n", 548 | " # 将GIF图转换成MP4视频文件\n", 549 | " clip = mp.VideoFileClip(\"coo/coordinates.gif\")\n", 550 | " clip.write_videofile(\"coo/\"+\"coordinates\"+\".mp4\")\n", 551 | " \n", 552 | " # 删除gif图\n", 553 | " os.remove(\"coo/coordinates.gif\")\n", 554 | " \n", 555 | "\n", 556 | "# 代码17:...................................................................................\n", 557 | "def GraphOutput():\n", 558 | " \n", 559 | " ax = \\\n", 560 | " df_systemParams.plot(x=\"timestep\", y='$\\Sigma v$', kind=\"line\")\n", 561 | " df_systemParams.plot(x=\"timestep\", y='E', kind=\"line\", ax=ax, color=\"C1\")\n", 562 | " df_systemParams.plot(x=\"timestep\", y='$\\sigma E$', kind=\"line\", ax=ax, color=\"C2\")\n", 563 | " df_systemParams.plot(x=\"timestep\", y='Ek', kind=\"line\", ax=ax, color=\"C3\")\n", 564 | " df_systemParams.plot(x=\"timestep\", y='$\\sigma Ek$', kind=\"line\", ax=ax, color=\"C4\")\n", 565 | " df_systemParams.plot(x=\"timestep\", y='P_1', kind=\"line\", ax=ax, color=\"C9\")\n", 566 | " df_systemParams.plot(x=\"timestep\", y='P_2', kind=\"line\", ax=ax, color=\"C9\")\n", 567 | " \n", 568 | " plt.savefig('plot.jpg', dpi=300)\n", 569 | " \n", 570 | " \n", 571 | "# 代码3:************************************Main************************************************\n", 572 | "mov = 1 # 如果你想做一个视频的话,设置 mov=1\n", 573 | "workdir = str(os.getcwd()+'/') # 为所有png和视频设置一个工作目录\n", 574 | "\n", 575 | "if path.exists(str(workdir+'coo'))==False:\n", 576 | " os.makedirs(str(workdir+'coo'))\n", 577 | "else:\n", 578 | " shutil.rmtree(str(workdir+'coo'))\n", 579 | " os.makedirs(str(workdir+'coo'))\n", 580 | "\n", 581 | "# 加载输入参数文件\n", 582 | "df_params = pd.read_csv('Rap_2_LJP.in', sep='\\t', header=None, names=['parameter', 'value'])\n", 583 | "\n", 584 | "#初始该模拟系统热力学属性值\n", 585 | "NDIM = 2 #二维度设置\n", 586 | "vSum = np.asarray([0.0, 0.0]) #速度之和\n", 587 | "kinEnergy =Prop(0.0, 0.0, 0.0) #动能\n", 588 | "totEnergy =Prop(0.0, 0.0, 0.0) #势能\n", 589 | "pressure =Prop(0.0, 0.0, 0.0) #压强\n", 590 | "\n", 591 | "systemParams = [] #初始化系统参数列表\n", 592 | "\n", 593 | "#用于产生随机数算法函数的变量\n", 594 | "IADD = 453806245\n", 595 | "IMUL = 314159269\n", 596 | "MASK = 2147483647\n", 597 | "SCALE = 0.4656612873e-9\n", 598 | "randSeedP = 17\n", 599 | "\n", 600 | "#初始化参数:把Rap_2_LJP.文件中的参数值传递给MD模拟所需要的相关变量\n", 601 | "deltaT = float(df_params.values[0][1]) #模拟步长\n", 602 | "density = float(df_params.values[1][1]) #体系密度\n", 603 | "\n", 604 | "\n", 605 | "initUcell = np.asarray([0.0, 0.0]) #初始化元胞,具体大小由输入文件密度决定\n", 606 | "initUcell[0] = int(df_params.values[2][1]) #元胞X方向长度=20\n", 607 | "initUcell[1] = int(df_params.values[3][1]) #元胞X方向长度=20\n", 608 | " \n", 609 | "stepAvg = int(df_params.values[4][1]) #抽样平均数\n", 610 | "stepEquil = float(df_params.values[5][1]) #平衡步长\n", 611 | "stepLimit = float(df_params.values[6][1]) #限制的步数\n", 612 | "temperature = float(df_params.values[7][1]) #体系温度\n", 613 | "\n", 614 | "#定义了一个Mol类对象的数组,大小为20*20=400. mol[0]~mol[399],共400个氩原子分子\n", 615 | "mol = [Mol(np.asarray([0.0, 0.0]), \\\n", 616 | " np.asarray([0.0, 0.0]), \\\n", 617 | " np.asarray([0.0, 0.0])) for i in range(int(initUcell[0]*initUcell[1]))]\n", 618 | "\n", 619 | "global nMol #定义分子的数量变量\n", 620 | "nMol = len(mol) #为mol数组的长度为400\n", 621 | "\n", 622 | "# 氩-氩相互作用的LJ势参数:\n", 623 | "epsilon = 1\n", 624 | "sigma = 1\n", 625 | "\n", 626 | "# 执行体系初始化相关功能函数\n", 627 | "SetParams() #设置主程序内部模拟所需参数\n", 628 | "SetupJob() #初始化粒子的坐标,速度,加速\n", 629 | "moreCycles = 1 #MD循环控制开关变量1:run; 0:stop\n", 630 | "\n", 631 | "n = 0 #记录步数,用来给每一步输出的坐标图命名 \n", 632 | "while moreCycles: #MD循环\n", 633 | " SingleStep() #求解每一步的运动方程\n", 634 | " if mov==1:\n", 635 | " plotMolCoo(mol, workdir, n) #制作单个步长的粒子坐标图\n", 636 | " n += 1\n", 637 | " if stepCount >= stepLimit:\n", 638 | " moreCycles = 0\n", 639 | " \n", 640 | "#输出模拟过程中生成的参数\n", 641 | "columns = ['timestep','timeNow', '$\\Sigma v$', 'E', '$\\sigma E$', 'Ek', '$\\sigma Ek$', 'P_1', 'P_2']\n", 642 | "df_systemParams = pd.DataFrame(systemParams, columns=columns) \n", 643 | "\n", 644 | "#绘制表格\n", 645 | "display(df_systemParams) \n", 646 | "if mov==1: \n", 647 | " makeMov() # 制作视频\n", 648 | "GraphOutput() #输出体系测定量随模拟时间变化的图像" 649 | ] 650 | }, 651 | { 652 | "cell_type": "code", 653 | "execution_count": null, 654 | "id": "348d912d", 655 | "metadata": {}, 656 | "outputs": [], 657 | "source": [] 658 | } 659 | ], 660 | "metadata": { 661 | "celltoolbar": "编辑元数据", 662 | "kernelspec": { 663 | "display_name": "Python 3 (ipykernel)", 664 | "language": "python", 665 | "name": "python3" 666 | }, 667 | "language_info": { 668 | "codemirror_mode": { 669 | "name": "ipython", 670 | "version": 3 671 | }, 672 | "file_extension": ".py", 673 | "mimetype": "text/x-python", 674 | "name": "python", 675 | "nbconvert_exporter": "python", 676 | "pygments_lexer": "ipython3", 677 | "version": "3.9.6" 678 | }, 679 | "toc": { 680 | "base_numbering": 1, 681 | "nav_menu": {}, 682 | "number_sections": true, 683 | "sideBar": true, 684 | "skip_h1_title": false, 685 | "title_cell": "Table of Contents", 686 | "title_sidebar": "Contents", 687 | "toc_cell": false, 688 | "toc_position": {}, 689 | "toc_section_display": true, 690 | "toc_window_display": false 691 | } 692 | }, 693 | "nbformat": 4, 694 | "nbformat_minor": 5 695 | } 696 | -------------------------------------------------------------------------------- /exmple_in_jupyter notebook/Rap_2_LJP.in: -------------------------------------------------------------------------------- 1 | deltaT 0.005 2 | density 0.8 3 | initUcell_x 20 4 | initUcell_y 20 5 | stepAvg 100 6 | stepEquil 0 7 | stepLimit 500 8 | temperature 1. 9 | -------------------------------------------------------------------------------- /exmple_in_jupyter notebook/coo/coordinates.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/egtai/MD_LJP/31753412cce61bd108417914d1a140715b6cafbf/exmple_in_jupyter notebook/coo/coordinates.mp4 -------------------------------------------------------------------------------- /exmple_in_jupyter notebook/plot.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/egtai/MD_LJP/31753412cce61bd108417914d1a140715b6cafbf/exmple_in_jupyter notebook/plot.jpg --------------------------------------------------------------------------------