☰
LAMMPS `jump` 命令完全指南:脚本跳转、SELF 重读、标签循环与多分区并行
2026/10/16 2:00:29 网站建设 项目流程
  • 科研
  • 科学计算
  • 高性能计算

【免费下载链接】lammps

Public development project of the LAMMPS MD software package

项目地址:https://gitcode.com/gh_mirrors/la/lammps
点击查看免费下载

jump是 LAMMPS 输入脚本中最重要也最常用的控制流命令之一,它允许脚本在执行过程中关闭当前输入文件、切换到另一个输入文件继续读取命令,配合label、next、if与variable可以构建循环、条件跳出、脚本链式切换甚至多分区并行执行不同模拟等高阶逻辑。本文以 LAMMPS 官方文档 doc/src/jump.rst 为骨架,结合src/input.cpp中的Input::jump()、Input::label()、Input::next_command()实现与src/library.cpp中的命令字符串模拟逻辑,系统讲解jump的语法、语义、典型用法与实现原理。读完本文,你将掌握用jump组织多文件工作流、用标签做循环与条件跳出、用 SELF 做"脚本重放",以及用变量文件名驱动多分区并行的完整实战方案。

语法总览

jump命令的完整语法如下(摘自 doc/src/jump.rst):

jump file label
  • file:要切换到的新的输入脚本文件名(必填)。
  • label:新文件内要跳转到的标签名(可选)。

典型示例:

jump newfile jump in.run2 runloop jump SELF runloop

第一条命令直接切换到newfile从头执行;第二条切换到in.run2并跳到其中的runloop标签处开始执行;第三条使用特殊文件名SELF重新打开当前输入脚本并从指定标签继续。需要说明的是,该命令没有默认值(文档中 Default 一栏为 none),参数缺失或多余会直接报错。

从源码角度看,jump属于 LAMMPS 输入层内建的少量"控制流"命令之一。在 src/input.cpp 的Input::execute_command()分发表中,jump与if、label、next、variable、include、quit等命令并列处理(见 src/input.cpp),其中:

else if (mycmd == "if") ifthenelse(); else if (mycmd == "include") include(); else if (mycmd == "jump") jump(); else if (mycmd == "label") label(); else if (mycmd == "next") next_command();

这组命令共同构成了 LAMMPS 输入脚本的控制流能力:if做条件分支、label/jump做跳转、next推进循环变量、variable定义循环与索引变量。jump本身对参数个数做了严格校验(src/input.cpp):

if (narg < 1 || narg > 2) error->all(FLERR, Error::COMMAND, "Illegal jump command: expected 1 or 2 argument(s) but found {}", narg);

语义:与 include 的区别

jump的核心语义是:关闭当前输入脚本文件,打开指定文件并从该文件继续读取 LAMMPS 命令。

这与include命令有本质区别:include会先完整执行被包含的文件,执行完毕后返回原文件继续向下执行;而jump一旦执行,当前文件不再返回,脚本执行的控制权彻底转移到新文件中。当然,通过多次jump命令可以形成文件间的链式跳转,甚至跳回最初的文件(例如jump in.a从in.main跳到in.a,in.a末尾再jump in.main label跳回)。

这一语义在源码中得到直接体现。查看 src/input.cpp 的Input::jump():

void Input::jump() { if (narg < 1 || narg > 2) error->all(FLERR, Error::COMMAND, "..."); if (jump_skip) { jump_skip = 0; return; } if (me == 0) { output->thermo->set_line(-1); if (strcmp(arg[0],"SELF") == 0) { rewind(infile); } else { if (infile && infile != stdin) fclose(infile); infile = fopen(arg[0],"r"); if (infile == nullptr) error->one(FLERR, Error::ARGZERO, "Cannot open input script {}: {}", arg[0], utils::getsyserror()); inlines[nfile-1] = -1; infiles[nfile-1] = infile; } } if (narg == 2) { label_active = 1; delete[] labelstr; labelstr = utils::strdup(arg[1]); } }

实现中有几个值得注意的细节:

  1. 只有 rank 0 进程实际打开文件:if (me == 0)限定仅主进程执行fclose/fopen/rewind,其他进程通过后续的MPI_Bcast广播获得命令行内容,这是 LAMMPS"串行输入、并行执行"架构的一部分。
  2. 切换文件时会关闭旧文件:if (infile && infile != stdin) fclose(infile);对应文档所述"closes the current input script file"。infiles/inlines栈(声明于 src/input.h)记录了打开的文件与对应行号,用于支持include的嵌套返回以及jump之后的文件栈管理。
  3. 可选 label 参数触发标签扫描模式:label_active = 1并记录目标labelstr。在Input::file()的主循环(src/input.cpp)中,一旦label_active为真,所有非label命令都会被跳过,直到命中与labelstr匹配的label命令:
// if scanning for label, skip command unless it's a label command if (label_active && strcmp(command,"label") != 0) continue; // execute the command if (execute_command() && line) error->all(FLERR, Error::COMMAND, "Unknown command: {}",line);

Input::label()的匹配逻辑见 src/input.cpp:

void Input::label() { if (narg != 1) error->all(FLERR, Error::COMMAND, "..."); if (label_active && strcmp(labelstr,arg[0]) == 0) label_active = 0; }

即:跳转时先"不执行命令"地扫描新文件,找到目标label后label_active归零,从该行起正常执行后续命令。若新文件始终没有出现目标标签,主循环会在读到文件末尾(n == 0)时报错退出:

if (n == 0) { if (label_active) error->all(FLERR, Error::NOLASTLINE, "Label {} wasn't found in input script", labelstr); break; }

这对应文档 Restrictions 一节所述:"If you jump to a file and it does not contain the specified label, LAMMPS will come to the end of the file and exit."

SELF:重新读取当前输入脚本

将文件名参数写为SELF(必须大写),jump会关闭并重新打开当前输入脚本再读一遍。这是实现"循环到脚本开头"的经典手段。

源码中SELF的实现是rewind(infile)(src/input.cpp),即调用 C 标准库的rewind()将文件指针复位到文件开头。

文档特别给出了一个重要警告:当当前输入脚本是通过标准输入(stdin)读取时,例如:

lmp < in.script

SELF并不能保证正常工作,因为rewind()对某些系统或某些 MPI 实现下的 stdin 流可能不受支持。

文档提供了两种可靠的替代方案:

方案一:改用-in命令行开关

lmp -in in.script

这样 LAMMPS 会以常规文件方式打开脚本,rewind()可以正常工作。该开关的完整说明见 doc/src/Run_options.rst 中的-in <file>选项。

方案二:通过-var把脚本名作为变量传入

lmp -var fname in.script < in.script

此时脚本内部用变量fname代替SELF,例如:

jump ${fname} loop

由于文件路径已经通过变量持有,jump走的是普通的fopen(arg[0])路径而非rewind,从而绕开 stdin 的限制。

用 jump + label + next 构建循环

jump最典型的实战场景是与label、next、variable组合实现循环。文档给出第一个完整示例:执行 10 次各 10000 步的模拟,并生成file.1、file.2…file.10共 10 个 dump 文件:

variable a loop 10 label loop dump 1 all atom 100 file.$a run 10000 undump 1 next a jump in.lj loop

执行流程分析:

  1. variable a loop 10定义循环变量a,取值依次为 1~10;
  2. label loop标记循环起点;
  3. 每次迭代执行dump+run 10000,dump 文件名通过file.$a使用即时变量插值($a);
  4. next a将a推进到下一个值;
  5. jump in.lj loop跳回in.lj中的loop标签开始下一次迭代。

这里的关键机制是next与jump的联动:当循环变量a已被第十次递增耗尽后,next会让下一次jump被跳过,从而退出循环。这个"跳过"机制正是源码中的jump_skip标志——Input::next_command()在变量耗尽时返回真并置位jump_skip(src/input.cpp):

void Input::next_command() { if (variable->next(narg,arg)) jump_skip = 1; }

随后Input::jump()入口处检测到jump_skip即直接返回、不执行任何跳转:

if (jump_skip) { jump_skip = 0; return; }

也就是说,"第 10 次递增后跳过下一条jump"并非jump自身的行为,而是next命令主动设置jump_skip、jump命令被动配合的结果。成员声明见 src/input.h:

int label_active; // 0 = no label, 1 = looking for label char *labelstr; // label string being looked for int jump_skip; // 1 if skipping next jump, 0 otherwise

条件跳出:jump SELF + label break

jump与if组合可以在满足条件时跳出循环。文档给出的示例是:每 1000 步检查一次系统温度,若温度低于 300 则跳出循环结束运行:

variable myTemp equal temp label loop variable a loop 1000 run 1000 if "${myTemp} < 300.0" then "jump SELF break" next a jump SELF loop label break print "ALL DONE"

流程:

  1. variable myTemp equal temp将当前系统温度绑定到变量myTemp;
  2. label loop起循环,variable a loop 1000为内层迭代计数(最多 1000 次);
  3. 每跑 1000 步后,if检查myTemp < 300.0:满足则执行jump SELF break,即重新打开当前脚本并跳到break标签,从而退出循环并执行后面的print "ALL DONE";
  4. 不满足则next a推进计数、jump SELF loop回到循环起点。

这里必须注意if命令的语义细节:if条件分支中的"jump SELF break"是一个由ifthenelse()内部再次调用execute_command()执行的内嵌命令(execute_command同时接受来自命令字符串的分发,见 src/input.cpp),因此它和顶层脚本里的jump行为完全一致。

文档还特别提示:任何变量都可以作为循环退出条件,只要在run完成的那个时间步该变量是"当前值"。这一点可以通过把该变量放进热力学输出(thermo 输出)来保证——LAMMPS 会在每个热力学输出步更新所有被引用的变量。关于这一机制的完整说明见 doc/src/variable.rst。

双循环:jump SELF 跳出内层

jump同样适用于嵌套循环。文档给出的双循环示例通过if+jump SELF从内层循环提前跳出,然后继续外层循环:

label loopa variable a loop 5 label loopb variable b loop 5 print "A,B = $a,$b" run 10000 if "$b > 2" then "jump SELF break" next b jump in.script loopb label break variable b delete next a jump SELF loopa

执行要点:

  • 外层a从 1 循环到 5,内层b也从 1 循环到 5;
  • 内层每轮执行print与run 10000,当b > 2时jump SELF break跳到内层循环体外的break标签,结束本次内层循环;
  • 随后variable b delete清理内层变量,next a推进外层,jump SELF loopa回到外层起点。

注意:这里的jump in.script loopb使用了显式文件名in.script跳回内层标签loopb,而跳出时用jump SELF break在当前脚本内跳转——两种写法在同一脚本中混合使用,展示了jump对"文件间跳转"与"文件内跳转"的统一处理能力。

多分区并行:jump + 变量文件名

jump的另一个高级用法是通过变量作为文件名参数,让不同的处理器分区(partition)执行不同的输入脚本。文档示例:在 40 个处理器上运行 4 个分区、每分区 10 进程,每个分区执行不同的模拟:

mpirun -np 40 lmp -partition 4x10 -in in.file

其中in.file内容为:

variable f world script.1 script.2 script.3 script.4 jump $f

工作原理:

  1. variable f world ...定义一个 world 风格变量:LAMMPS 会按分区(world)分别赋值——分区 1 得到script.1,分区 2 得到script.2,依此类推;
  2. jump $f中文件名参数是变量引用,各分区替换后的文件名各不相同,从而每个分区读取并执行各自的输入脚本。

由于jump会关闭当前文件并打开新文件,而各分区可以分别执行完全不同的脚本(各自设置势函数、边界、集成器乃至独立的体系),这一模式是 LAMMPS 多分区并行(如副本交换、参数扫描、批量任务)的基础设施。-partition与-var等开关的完整说明同样见 doc/src/Run_options.rst。

在 LAMMPS 库接口(lammps_command_string)中,jump的语义也做了对应模拟。当通过字符串缓冲执行命令时,src/library.cpp 中的代码会解析jump SELF label形式,并支持与输入脚本一致的jump_skip跳过逻辑:

// need to handle jump command here if ((words.size() == 3) && (words[0] == "jump")) { if (words[1] != "SELF") lmp->error->all(FLERR, Error::NOLASTLINE, "{}(): May only use jump SELF with command string buffer", FNERR); // emulate jump command unless with need to skip it if (!lmp->input->get_jump_skip()) { label = words[2]; cursor = 0; nline = -1; continue; } }

可见:在库模式下jump仅允许SELF形式(配合标签),且同样受next设置的jump_skip控制,保证与输入脚本行为的兼容性。对应接口get_jump_skip()声明于 src/input.h。

实战中的典型文件链式编排

结合jump的特性,一个常见的工作流是把准备、平衡、生产等阶段拆成多个脚本,用jump串联:

# in.prep read_data system.data pair_style lj/cut 2.5 pair_coeff * * 1.0 1.0 jump in.equil relax
# in.equil velocity all create 300.0 4928459 mom yes fix 1 all nvt temp 300.0 300.0 100.0 timestep 0.001 label relax run 50000 if "${step} >= 100000" then "jump in.prod run" jump SELF relax
# in.prod unfix 1 fix 1 all nve label run run 1000000

注意in.prod执行完毕后,由于jump不再返回原文件,LAMMPS 会在该文件末尾结束整个模拟——这正是文档所述"the original file is not returned to"的应用体现。若需要回到主脚本继续,只需在链尾再加一条jump in.prep <label>形式的回跳即可。

限制与注意事项小结

  • 标签缺失即退出:跳转目标文件不含指定标签时,LAMMPS 读到文件末尾后直接退出(对应源码Label {} wasn't found in input script报错,见 src/input.cpp)。
  • stdin 下慎用 SELF:脚本经lmp < in.script重定向读取时,SELF依赖的rewind()可能不受支持;请改用lmp -in in.script或-var fname in.script < in.script。
  • SELF 大小写敏感:必须写作大写SELF。
  • jump_skip的传递性:next置位jump_skip后,紧接着的下一条jump会被静默跳过,这是循环退出的关键,请勿在next与jump之间插入其他jump命令,以免误跳。
  • 库模式限制:通过lammps_command_string执行命令字符串时,jump只允许SELF形式。

关联命令与延伸阅读

jump与以下命令协同工作(相关文档均位于 doc/src 目录):

  • doc/src/variable.rst:循环变量(loop)、索引变量(index)、world 变量(world)、即时变量($a、${a})的定义与取值规则;
  • doc/src/include.rst:与jump语义互补的"执行完返回原文件"机制;
  • doc/src/label.rst:配合jump使用的标签定义命令;
  • doc/src/next.rst:推进循环变量并联动jump_skip跳过跳转;
  • doc/src/Run_options.rst:-in、-var、-partition等命令行开关。

仓库中真实输入脚本也能看到jump的落地实践,例如 examples/PACKAGES/interlayer/lebedeva/2particles.in 等示例文件就使用了jump组织多阶段计算,可结合这些示例体会jump在真实模拟工作流中的编排方式。

  • 科研
  • 科学计算
  • 高性能计算

【免费下载链接】lammps

Public development project of the LAMMPS MD software package

项目地址:https://gitcode.com/gh_mirrors/la/lammps
点击查看免费下载

相关推荐

上一篇:GluonCV YOLOv3 实战指南:从随机形状训练到 INT8 量化部署
下一篇:三步解锁网易云NCM音乐格式:ncmdump让你的音乐收藏重获自由

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询