- 科研
- 科学计算
- 高性能计算
【免费下载链接】lammps
Public development project of the LAMMPS MD software package
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 labelfile:要切换到的新的输入脚本文件名(必填)。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]); } }实现中有几个值得注意的细节:
- 只有 rank 0 进程实际打开文件:
if (me == 0)限定仅主进程执行fclose/fopen/rewind,其他进程通过后续的MPI_Bcast广播获得命令行内容,这是 LAMMPS"串行输入、并行执行"架构的一部分。 - 切换文件时会关闭旧文件:
if (infile && infile != stdin) fclose(infile);对应文档所述"closes the current input script file"。infiles/inlines栈(声明于 src/input.h)记录了打开的文件与对应行号,用于支持include的嵌套返回以及jump之后的文件栈管理。 - 可选 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.scriptSELF并不能保证正常工作,因为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执行流程分析:
variable a loop 10定义循环变量a,取值依次为 1~10;label loop标记循环起点;- 每次迭代执行
dump+run 10000,dump 文件名通过file.$a使用即时变量插值($a); next a将a推进到下一个值;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"流程:
variable myTemp equal temp将当前系统温度绑定到变量myTemp;label loop起循环,variable a loop 1000为内层迭代计数(最多 1000 次);- 每跑 1000 步后,
if检查myTemp < 300.0:满足则执行jump SELF break,即重新打开当前脚本并跳到break标签,从而退出循环并执行后面的print "ALL DONE"; - 不满足则
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工作原理:
variable f world ...定义一个 world 风格变量:LAMMPS 会按分区(world)分别赋值——分区 1 得到script.1,分区 2 得到script.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
相关推荐
LAMMPS if 命令完全指南:在输入脚本中实现条件分支与循环控制
LAMMPS if 命令完全指南:在输入脚本中实现条件分支与循环控制 if 是 LAMMPS 输入脚本中实现"条件分支"的核心命令,它允许你基于模拟的实时状态(
科研科学计算高性能计算LAMMPS clear 命令完全指南:在单个输入脚本中顺序运行多个仿真
LAMMPS clear 命令完全指南:在单个输入脚本中顺序运行多个仿真 clear 是 LAMMPS 输入脚本中用于"重置"仿真状态的核心命令:它删除所有原子
科研科学计算高性能计算LAMMPS 通用命令全览指南:从 Commands_all 文档读懂输入脚本的命令体系
LAMMPS 通用命令全览指南:从 Commands_all 文档读懂输入脚本的命令体系 导读 本文以 LAMMPS 官方文档手册的 Commands_all.
科研科学计算高性能计算
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考