详细内容

LAMMPS讲解79-LAMMPS的一个时间步是如何工作的

理解LAMMPS代码工作方式的第一也是最基础的方法就是明白一个时间步使如何执行的。一个时间步是通过调用Updata类中的积分类实例的方法来实现。由于积分是一个基类,它指向一个导出类的实例,该导出类与in文件中run_style命令的设置相对应。这里介绍由Verlet类所执行的时间步。对于r-RESPA多级时间步方法,Respa类执行了相似的过程。Min类用来执行能量最小化,因此并未执行字面意思的时间步。但是其与这里描述的过程是相似的,即在每个能量最小化迭代中计算受力和调用fix。时间积分和能量最小化的区别将在本节最后进行强调。

Verlet类写在src/verlet.cppverlet.h文件中。它执行速度-Verlet时间步算法。具体执行这一算法的方法是Verlet::run()方法。在介绍这个方法前,首先要介绍该类中的几个其他方法。

init()方法在每个run之前都会调用。该方法基于用户在其他代码部分的设置,初始化了一些内部的flag

setup()setup_minimal()方法在run前也会调动。速度-Verlet方法要求当前受力在第一个时间步之前计算,这些根据原子相互作用计算受力的过程与接下来描述的逻辑是相同的。一些fix采用下面描述的机制被调用。各种counters也会在run前被初始化。setup_minimal是一个变体,其有一些用来执行较少设置的flag。当之前有多个run连续调用,并且之前run的信息在后续依旧成立,那么这个方法会被调用。比如,在in文件中通过pre noeveryrun选项设置多个重复的较短的run(这些run之间有其他命令),那么setup_minimal()方法会被使用

force_clear()方法在每个时间步之前初始化受力和其他数组,这样受力以及扭矩等可以被累积。

下面介绍Verlet::run()方法。该方法的基本结构以伪代码的形式进行描述。在实际src/verlet.cpp的代码中这些操作会被有选择的调用。


loop over N timesteps:

  if timeout condition: break

  ev_set()

 

  fix->initial_integrate()

  fix->post_integrate()

 

  nflag = neighbor->decide()

  if nflag:

    fix->pre_exchange()

    domain->pbc()

    domain->reset_box()

    comm->setup()

    neighbor->setup_bins()

    comm->exchange()

    comm->borders()

    fix->pre_neighbor()

    neighbor->build()

    fix->post_neighbor()

  else:

    comm->forward_comm()

 

  force_clear()

  fix->pre_force()




LAMMPS一个时间步执行的伪代码

ev_set()方法(在Integrate父类中),设置两个flageflagvflag),这两个flag用来控制能量和维里的计算。每个flag控制globalper-atom能量和维里是否在当前时间步进行计算,这是因为能量和维里可能会被某些fixvariableoutput需要。这些flag会被传递给计算粒子相互作用的各种方法中,这样如果能量和维里不需要的时候一些computetally相应的数据可以跳过额外的计算。查看Integrate::ev_set()方法中对这些flag取值的描述。

在时间步的各个阶段,不同的fix会被调用,比如fix->initial_intergrate()。在代码中,这实际上是通过Modify类来实现。Modify类存储了所有的Fix对象,以及一个列表来记录在时间步的哪个阶段调用哪个fixFixLAMMPS中用来针对一个模拟来在一个时间步内实现某种操作。每一个fix都有一个或多个方法,每一个方法都会在时间步的特定阶段被调用,具体见时间步的伪代码。所有在in文件中定义的fix都有一个initial_integrate()方法,该方法在每个时间步开始时调用。如fix nvefix nvtfix npt执行时间步起始的速度- Verlet积分操作来以半个时间步长更新速度,以整个时间步长更新位置。post_integrate()方法在这些更新后会被立即调用。只有少数一些fix使用这个方法,如在FixWallRefect类将例子反射回盒子。

Neughbor类中decide()方法决定是否需要在当前时间步重建邻居列表(重建条件可以使用neigh_modify中的every/delay/check参数进行修改)。如果不需要重建,则通过Comm类中的forward_comm()方法获取每个处理器的ghost原子的坐标。如果邻居列表需要被重建,在伪代码中if条件语句中的一些操作会首先被调用。任何定义的fix中的pre_exchange()方法先被调用。通常这会往系统中插入或从系统中删除粒子。

周期性边界条件则通过Domain类中的pbc()方法来实现,即将那些跑出模拟盒子的粒子重新调整回盒子。注意这不是每个时间步都会执行,只有在邻居列表重建时执行。这样每个处理所负责的子区域对于owned原子和ghost原子将会具有一致的原子坐标。这也就是为什么当dump不在邻居列表重建时间步输出时,dump出的原子坐标会稍微处于模拟盒子外部的原因。

接下来,如果需要,盒子边界会通过Domian类中的reset_box()进行重新调整,如盒子边界缩放至当前粒子的坐标。盒子尺寸或形状的改变要求通信ghost原子(Comm类)和邻居列表bin(邻居类)的内部信息被更新。Comm类中的setup()方法和Neighbor类中的setup_bins()方法来执行这些更新。

这时,代码已经准备来将原子从一个处理器的几何区域迁移至另一个处理器中。这个操作通过Comm类中的exchange()方法进行实现。接着,Comm类中的borders()方法识别出围绕在处理器子区域周围的ghost原子并且和周围处理器通信ghost原子信息。这通过循环处理器所有owned原子来构建这些原子的列表并发送给处理器的每个邻居处理器。在接下来的时间步中,这些列表会被Comm::forward_comm()方法使用。

具有pre_neighbor()方法的fix会被接着调用。这通常会重建一些由fix所存储的数据结构,之所以这样是因为这些fix取决于每个处理器当前owned用字。

现在每个处理器都有了其当前owned原子和ghost原子的列表。LAMMPS会通过Neighbor类中的build()方法来重建邻居列表。这通常通过将处理器owned原子和ghost原子进行binning,并扫描每个原子的bin周围的bin模板来在受力截断半径+skin距离之内构建邻居原子的Verlet列表。

在接下里时间步部分,在使用force_clear()置零每个原子的力矢量后,计算原子之间所有的相互作用力。如果newton flag通过newton命令被设置为on,受力会被加到处理器owned原子和ghost原子上,否则只加到owned(即当地)原子上。

对相互作用先会被计算,这允许全局维里(在Pair::compute()方法末尾以O(N)而不是O(N**2))被便宜的计算。维里通过点乘原子坐标和受力来计算。通过在点乘中包含ownedghost原子,周期性边界条件可以被正确的考虑。分子拓扑相互作用(键,键角,二面角,离平面)被接着计算。最有通过Kspace类计算来自长程库伦相互作用的贡献。

fix中的pre_exchange()方法被用来执行那些在反向通信(比如在受力计算和存储的ghost原子期间执行额外的数据传递或数据减少)之前的操作。

如果newton flagonghost原子的受力会被通信并被累加回它们所对应的owned原子上。Comm类中的reverse_comm()来执行这个操作。这其实就是发送owned原子的坐标到其他处理器ghost原子的反向操作。

在时间步的这个阶段,每个原子的受力都知道了。通过fix(具有post_force())所施加的额外受力约束(外部力,SHAKE等)被执行。速度-Verlet的第二阶段通过类似fix nvenvtnpt中的final_integrate()方法进行执行(即,更新第二个半步长的速度)

在时间步的末尾,具有end_of_step()方法的类会被调用。这通常执行后处理计算,如fix ave/timeave/chunk。时间步的最后操作是执行任何输出的设置,即通过Output类的write()方法。在LAMMPS中有三类输出:输出到屏幕或日志文件中的热力学信息,输出到dump文件中的原子快照和restart文件。查看thermo_styledumprestart命令获取详细信息。

能量最小化迭代的流程控制和一个动力学时间步的是相似的。受力被计算,邻居列表按需建立,原子迁移到新的处理器,原子坐标和受力被通信到邻居处理器。唯一不同的是调用Fix类的不同。只有有限的一些fix在能量最小化中是有用的。相关的Fix类中的方法是min_pre_exchange()min_pre_force()min_post_force()。每个fix在能量最小化迭代的适当阶段被调用。如,min_post_force()和动力学中的post_force()是类似的。它用来改变或这约束每个原子的受力,这会影响能量最小化的过程。

在所有迭代完成后会有一个cleanup步骤来调用fix中的post_run方法来执行只有在计算末尾才需要的操作,如释放临时存储或者创建最终输出。

 

 

感谢鲍路瑶老师的分享,内容来自于鲍老师分享出来的资料

如有需要添加微信:lmp_zhushou  进入微信群,帮助他人,共建社区

获取完整版lammps讲义可以加微信lmp_zhushou或加入QQ994359511


最新评论
请先登录才能进行回复登录
技术支持: CLOUD | 管理登录
seo seo