Hoomd-blue 一款分子动力学模拟软件,由University of Michigan开发,开源于github ,学习其详细内容可参考它的官方文档 和github实例 。
开端 题外话 Ethan在此之前计算机只有python基础课水平,没有独立参加过项目,所以学习了较长时间(断断续续有一个月),才将其基础搞懂,开始复现一篇论文中的结果,这篇论文(A geometry-originated universal relation for arbitrary convex hard particles )是导师要求学习的,但是没有想到看似简单的文章要学习和复现难度也如此大。frustrating
学习 论文主体上围绕一个公式展开
我也大概理解公式的意思,不过可能这也足够。左右侧都表征某种密度分布,左侧是用插入粒子来体现,右侧用多少粒子靠拢的程度来表征,公式的详细推导在论文里有。
复现准备 经过对官方文档孜孜不倦的学习,开始尝试写代码,(Hoomd文档没有类似的例子,这代码是真难写啊)。自认为代码非常粗糙,是Ethan在半知半解的调用函数和疯狂骚扰ChatGpt之后弄出来的结果:>
插入成功几率函数 为了得到插入成功的概率就需要一个能连续向体系中插入单个粒子的函数,不懈努力后,成功生造了一个在体系中插入粒子的函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 def insert_triangle (simulation, mc,times=100 ): """ 在现有的 HOOMD 模拟中随机插入指定数量的三角形粒子,并检测与已有粒子是否重叠。 参数: simulation: hoomd.Simulation 对象,表示现有的模拟。 mc: hoomd的hpmc,蒙特卡洛积分器 times:重复插入的次数,次数越大准确度越高 """ new_mc = hoomd.hpmc.integrate.ConvexPolygon() new_mc.shape["A" ] = dict ( vertices = [ (-0.5 , -0.5 ), (0.5 , -0.5 ), (0 , 0.5 ), ] ) simulation.operations.integrator = mc box = simulation.state.box Lx = box.Lx Ly = box.Ly type_id = simulation.state.particle_types.index('A' ) inserted_recorder=0 attempts = 0 old_snap = simulation.state.get_snapshot() while attempts < times: attempts += 1 x = np.random.uniform(-Lx/2 , Lx/2 ) y = np.random.uniform(-Ly/2 , Ly/2 ) if old_snap.communicator.rank == 0 : N_old = old_snap.particles.N N_new = N_old + 1 theta = np.random.uniform(0 , 2 *np.pi) new_snap = hoomd.Snapshot() new_snap.particles.N = N_new new_snap.configuration.box = old_snap.configuration.box new_snap.particles.types = old_snap.particles.types new_snap.particles.position[:] = np.zeros((N_new, 3 ), dtype=float ) new_snap.particles.orientation[:] = np.zeros((N_new, 4 ), dtype=float ) new_snap.particles.typeid[:N_old] = old_snap.particles.typeid[:] new_snap.particles.position[:N_old] = old_snap.particles.position[:] new_snap.particles.orientation[:N_old] = old_snap.particles.orientation[:] new_snap.particles.typeid[:N_old] = old_snap.particles.typeid[:] new_snap.particles.position[N_old] = [x, y, 0 ] new_snap.particles.orientation[N_old] = [np.cos(theta/2 ), 0 , 0 , np.sin(theta/2 )] new_snap.particles.typeid[N_old] = 0 else : new_snap = None cpu = hoomd.device.CPU() new_simulation = hoomd.Simulation(device=cpu,seed=1 ) new_simulation.create_state_from_snapshot(new_snap) new_simulation.operations.integrator = new_mc new_simulation.run(0 ) if new_mc.overlaps > 0 : continue else : inserted_recorder += 1 simulation.state.set_snapshot(old_snap) return inserted_recorder
需要了解的是,在hoomd中一般有两种储存粒子状态的方式,一种是GSD文件,方便更改,可以从外部轻松访问;另一种是snapshot,多用于在代码环境中储存系统信息,方便调用和删除释放内存。
SDF函数 SDF是scale distribution function的简称,中文尺度分布函数,毫无头绪。它是怎么一回事呢,假如体系粒子间的间隔同等乘以一个系数$1-x$(粒子之间的距离是中心与中心的距离),那么有一定数量的粒子相碰,就可以计正好相碰(只有正好在相碰的粒子才算数)的粒子数为,好在这个函数是hoomd官方有的函数,不用现成写,只需要调用就行了
1 2 3 sdf_compute = hoomd.hpmc.compute.SDF(xmax=xmax, dx=dx) sdf_xcompression = sdf_compute.x_compression sdf_compression = sdf_compute.sdf_compression
此处SDF有两个重要的量可以通过类访问,x_compression和sdf_compression,它们分别是收缩时每一步的x和该x对应的sdf(x),两者都是numpy的array,所以我们后续很方便的就可以进行绘图
系统初始化 这一部分较简单且在hoomd的文档中花大篇幅和多个例子进行说明了,这里就不赘述原理,只针对本论文要求给出初始化函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 def generate_particle (N_particles ): spacing = 2 K = math.ceil(N_particles ** (1 / 2 )) L = K * spacing x = np.linspace(-L / 2 , L / 2 , K, endpoint=False ) position_2d = list (itertools.product(x, repeat=2 )) positions_2d = np.array(position_2d[0 :N_particles]) z_coordinates = np.zeros((N_particles, 1 )) positions_3d = np.hstack((positions_2d, z_coordinates)) frame = gsd.hoomd.Frame() frame.particles.N = N_particles frame.particles.position = positions_3d frame.particles.typeid = [0 ] * N_particles frame.configuration.box = [L, L, 0 , 0 , 0 , 0 ] snapshot=hoomd.Snapshot() snapshot.particles.N = N_particles snapshot.particles.position[:] = positions_3d snapshot.particles.typeid[:] = [0 ] * N_particles snapshot.particles.types=['A' ] snapshot.configuration.box=[L, L, 0 , 0 , 0 , 0 ] return snapshot
生成一个具有N_particles个粒子的体系,密度恒定
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 def randomizing_particles (snapshot,times=10e3 ): cpu = hoomd.device.CPU() simulation = hoomd.Simulation(device=cpu, seed=29 ) mc = hoomd.hpmc.integrate.ConvexPolygon(default_d=0.3 ,default_a=0.4 ) mc.shape["A" ] = dict ( vertices = [ (-0.5 , -0.5 ), (0.5 , -0.5 ), (0 , 0.5 ), ] ) simulation.operations.integrator = mc simulation.create_state_from_snapshot(snapshot) initial_snapshot = simulation.state.get_snapshot() simulation.run(times) final_snapshot = simulation.state.get_snapshot() return final_snapshot,mc,simulation
随机化这个粒子体系,times是MC随机化的步数
顺便一提,hoomd的官方文档的图像生成函数是基于 fresnel 的三维图像生成函数。功能很强,但是我要做二维的体系,用不上:<
我就自己基于matplotlib 写了一个二维图像生成函数(目前只支持展现粒子的位置,都是圆形的粒子)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 def render (snapshot, dims=2 ): """ 从gsd文件生成一个二维的粒子群图像,目前仅支持圆粒子图. """ positions = np.asarray(snapshot.particles.position) if dims != 2 : print ("Only 2D rendering is supported in this function." ) return plt.figure(figsize=(8 , 8 )) plt.scatter(positions[:, 0 ], positions[:, 1 ], s=10 , alpha=0.6 ) plt.xlabel('x' ) plt.ylabel('y' ) plt.title('Particle positions from GSD snapshot' ) plt.gca().set_aspect('equal' , adjustable='box' ) plt.show()
模拟与调教 进行中!!