ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

LAMMPS 插件开发指南:利用 DSO 动态加载扩展 pair、fix、command 等样式

LAMMPS 插件开发指南:利用 DSO 动态加载扩展 pair、fix、command 等样式 科研科学计算高性能计算【免费下载链接】lammpsPublic development project of the LAMMPS MD software package项目地址https://gitcode.com/gh_mirrors/la/lammps点击查看免费下载导读本文是 LAMMPS 开发者指南中“Writing plugins”章节的深入讲解面向希望在不重编译、不重新链接整个可执行文件的前提下为 LAMMPS 增加新样式pair、bond、fix、compute、command 等的开发者。读完本文你将掌握插件 DSO 的结构约定、lammpsplugin_init初始化函数与lammpsplugin_t注册结构体的用法、pair/fix/command 三类工厂函数的编写差异以及如何用 CMake 或 GNU make 编译插件并借助LAMMPS_PLUGIN_PATH实现启动时自动加载。插件机制概述为 LAMMPS 二进制动态注入新样式插件plugin是 LAMMPS 提供的一种在不重新编译 LAMMPS 可执行文件的前提下添加新功能的机制。其实现位于 PLUGIN 包中源码见 src/PLUGIN/plugin.cpp 与 src/PLUGIN/plugin.h因此要使用插件功能LAMMPS 必须编译安装了该包。插件本质上是利用操作系统加载动态共享对象DSODynamic Shared Object文件的能力DSO 在运行时被加载进进程其中特定的导出符号可以被解析并调用。任何包含插件的 DSO 文件都必须导出一个固定名称的初始化函数lammpsplugin_init它遵循下文描述的特定规则。当通过plugin命令加载该 DSO 时LAMMPS 会查找并调用这个函数由它把 DSO 中封装的插件注册进 LAMMPS。从实现角度看这一机制之所以可行源于 LAMMPS 的面向对象设计所有 pair 样式类都继承自Pair基类所有 fix 样式类都继承自Fix基类其余样式bond、angle、dihedral、improper、kspace、compute、region、command、run、min 等同样如此调用方通常只直接调用这些基类中存在的成员函数。当执行pair_style、fix等命令时LAMMPS 通过一个与样式名映射的工厂函数创建派生类实例。例如处理pair_style lj/cut 2.5时LAMMPS 会查找创建PairLJCut类的工厂函数并执行返回值是一个Pair *指针被赋给当前激活的 pair 样式位置。因此插件 DSO 的核心任务就是实现这样一个工厂函数并通过注册把它挂到 LAMMPS 对应样式类别的全局注册表中。加载插件后该样式对 LAMMPS 的行为与把对应代码静态编译进二进制中的包完全一致。安装 PLUGIN 包与 plugin 命令基础在使用插件前需要先编译带 PLUGIN 包的 LAMMPS然后才能使用 plugin 命令 管理插件。其语法为plugin command args支持五个子命令子命令作用load file从指定 DSO 文件加载其中包含的所有插件单个 DSO 可包含多个插件已加载的插件会被跳过unload style name卸载指定样式pair、bond、angle、dihedral、improper、kspace、compute、fix、region、command、run、min下指定名称的插件若该样式当前正在使用其实例会先被删除并替换为该类样式的默认设置list打印当前已加载插件及其样式与名称clear卸载所有已加载插件restore兼容性保留命令在新版中不再需要、不做任何事见下文进程全局注册表典型用法示例见 plugin 命令文档plugin load morse2plugin.so plugin unload pair morse2/omp plugin unload command hello plugin list plugin clear加载单个 DSO 的完整调用链可以在 src/PLUGIN/plugin.cpp 的plugin_load()中看到platform::dlopen()打开 DSOplatform::dlsym()按名称查找lammpsplugin_init符号随后以三个参数调用它——指向当前 LAMMPS 实例的指针、DSO 句柄、注册函数指针。lammpsplugin_init插件 DSO 的入口约定所有插件 DSO 都必须实现并导出如下签名的初始化函数C 绑定因此没有 C 名称修饰void lammpsplugin_init(void *lmp, void *handle, void *regfunc);三个void *参数分别是lmp当前调用它的 LAMMPS 实例指针需要原样传给注册函数handleDSO 文件的内部句柄必须写入插件信息结构体以便在 DSO 中所有插件都被卸载后由 LAMMPS 关闭并释放该 DSOregfunc注册函数的函数指针需要先转换为lammpsplugin_regfunc类型再以lammpsplugin_t *结构体指针和 LAMMPS 实例指针为参数调用完成单个插件的注册。同一个初始化函数内部可以多次调用注册函数从而在一个 DSO 中注册多个插件。类型定义见 src/lammpsplugin.htypedef void *(lammpsplugin_factory1) (void *); typedef void *(lammpsplugin_factory2) (void *, int, char **); typedef struct { const char *version; const char *style; const char *name; const char *info; const char *author; union { lammpsplugin_factory1 *v1; lammpsplugin_factory2 *v2; } creator; void *handle; } lammpsplugin_t; typedef void (*lammpsplugin_regfunc)(lammpsplugin_t *, void *); typedef void (*lammpsplugin_initfunc)(void *, void *, void *);lammpsplugin_t 结构体成员详解注册插件时需要填充一个lammpsplugin_t结构体各成员含义如下与原文档成员表一致成员含义version插件编译时对应的 LAMMPS 版本字符串style插件的样式类别pair、bond、fix、command 等name插件样式名info描述插件的信息字符串author作者姓名与邮箱字符串creator.v1指向 pair、bond、angle、dihedral、improper、kspace、command 或 minimize 样式的工厂函数creator.v2指向 compute、fix、region 或 run 样式的工厂函数handle打开的 DSO 文件句柄注册时lammpsplugin_t被放入 LAMMPS 内部维护的全局插件列表中对应 DSO 句柄的引用计数加一见 src/PLUGIN/plugin.cpp 中的pluginlist与dso_refcounter卸载时引用计数归零才会真正dlclose()释放 DSO。注册函数还会把工厂函数以给定的名字加入对应样式类别的注册表。creator.v1 与 creator.v2两类工厂函数的区分creator是联合体同一时刻只能使用其中一个成员由插件style决定creator.v1适用于计算受力的样式pair、bond、angle、dihedral、improper、command 样式和 minimize 样式。工厂函数只接收一个参数——LAMMPS 实例指针赋值前需将函数指针转换为lammpsplugin_factory1类型typedef void *(lammpsplugin_factory1) (void *);creator.v2适用于 fix、compute、region 和 run 样式。工厂函数接收三个参数——LAMMPS 实例指针、参数列表长度int、char **参数列表指针赋值前需转换为lammpsplugin_factory2类型typedef void *(lammpsplugin_factory2) (void *, int, char **);这种区分与 LAMMPS 内部创建各类样式的调用方式一一对应pair/fix 等类的构造函数签名不同fix、compute这类命令创建实例时需要透传命令行参数。示例一Pair 样式插件factory1原文档以假想的morse2pair 样式为例其实现位于pair_morse2.h/pair_morse2.cpp类名为PairMorse2。工厂函数与初始化函数如下#include lammpsplugin.h #include version.h #include pair_morse2.h using namespace LAMMPS_NS; static Pair *morse2creator(LAMMPS *lmp) { return new PairMorse2(lmp); } extern C void lammpsplugin_init(void *lmp, void *handle, void *regfunc) { lammpsplugin_regfunc register_plugin (lammpsplugin_regfunc) regfunc; lammpsplugin_t plugin; plugin.version LAMMPS_VERSION; plugin.style pair; plugin.name morse2; plugin.info Morse2 variant pair style v1.0; plugin.author Axel Kohlmeyer (akohlmeygmail.com); plugin.creator.v1 (lammpsplugin_factory1 *) morse2creator; plugin.handle handle; (*register_plugin)(plugin,lmp); }要点工厂函数morse2creator()只接收一个 LAMMPS 指针因此必须赋给creator.v1并转换为lammpsplugin_factory1类型返回值是Pair派生类实例的指针工厂函数可以声明为static避免与其他插件发生符号冲突派生类的类名PairMorse2在整个 LAMMPS 可执行文件中必须唯一插件之间不能重名注册见下文。仓库中 examples/plugins/morse2plugin.cpp 给出了真实的同款示例并且它在同一个lammpsplugin_init中连续注册了morse2与morse2/omp两个样式——注册完第一个后只更新name、info、creator.v1再调用一次注册函数这正好演示了单个 DSO 可注册多个插件的用法。示例二Fix 样式插件factory2若工厂函数针对 fix 或 compute接收 LAMMPS 指针、参数个数与参数串列表三个参数指针类型必须是lammpsplugin_factory2赋给creator.v2成员#include lammpsplugin.h #include version.h #include fix_nve2.h using namespace LAMMPS_NS; static Fix *nve2creator(LAMMPS *lmp, int argc, char **argv) { return new FixNVE2(lmp,argc,argv); } extern C void lammpsplugin_init(void *lmp, void *handle, void *regfunc) { lammpsplugin_regfunc register_plugin (lammpsplugin_regfunc) regfunc; lammpsplugin_t plugin; plugin.version LAMMPS_VERSION; plugin.style fix; plugin.name nve2; plugin.info NVE2 variant fix style v1.0; plugin.author Axel Kohlmeyer (akohlmeygmail.com); plugin.creator.v2 (lammpsplugin_factory2 *) nve2creator; plugin.handle handle; (*register_plugin)(plugin,lmp); }示例三Command 样式插件factory1 单文件实现Command 样式同样使用第一类工厂函数。下面的示例还展示了插件类实现可以与插件接口代码放在同一个源文件中#include lammpsplugin.h #include comm.h #include error.h #include command.h #include version.h #include cstring namespace LAMMPS_NS { class Hello : public Command { public: Hello(class LAMMPS *lmp) : Command(lmp) {}; void command(int, char **); }; } using namespace LAMMPS_NS; void Hello::command(int argc, char **argv) { if (argc ! 1) error-all(FLERR,Illegal hello command); if (comm-me 0) utils::logmesg(lmp,fmt::format(Hello, {}!\n,argv[0])); } static void hellocreator(LAMMPS *lmp) { return new Hello(lmp); } extern C void lammpsplugin_init(void *lmp, void *handle, void *regfunc) { lammpsplugin_t plugin; lammpsplugin_regfunc register_plugin (lammpsplugin_regfunc) regfunc; plugin.version LAMMPS_VERSION; plugin.style command; plugin.name hello; plugin.info Hello world command v1.1; plugin.author Axel Kohlmeyer (akohlmeygmail.com); plugin.creator.v1 (lammpsplugin_factory1 *) hellocreator; plugin.handle handle; (*register_plugin)(plugin,lmp); }加载该插件后即可在输入脚本中直接使用hello name命令。真实实现见 examples/plugins/helloplugin.cpp。附加细节注册规则、样式覆盖与多插件注册原文档强调了几条必须遵守的规则初始化函数名必须叫lammpsplugin_init必须有 C 绑定且只接收三个void *参数第一个参数LAMMPS 实例指针必须传给注册函数第二个参数DSO 句柄必须写入插件结构体供引用计数与卸载使用第三个参数注册函数指针必须先存入lammpsplugin_regfunc类型变量再调用同一个初始化函数可以多次调用注册函数注册多个插件插件 DSO 中的样式类本身如PairMorse2可以像普通 LAMMPS 内置样式一样编写只是不再需要#ifdef PAIR_CLASS保护的PairStyle宏——类名到样式名的映射由插件注册函数根据lammpsplugin_t中的信息完成。当然如果希望新代码将来并入 LAMMPS 主分支仍然可以保留该宏插件可以用已有样式名注册此时插件会覆盖已有代码。这可以用来修改既有样式的行为或在不必重编译、重装整个 LAMMPS 的情况下调试新版本样式。在 src/PLUGIN/plugin.cpp 的plugin_register()中可以看到覆盖内置样式时会在根进程打印Overriding built-in ... style ... from plugin警告。进程全局注册表插件生命周期与版本行为变化关于插件生命周期原文档记录了两个重要版本变化12Jun2025 起执行clear命令时插件不再被卸载2Sep2026 起样式工厂函数从每个 LAMMPS 实例独立的样式映射迁移为进程全局注册表。其直接后果是已加载插件跨越clear命令保持有效无需重新加载同一进程中任何 LAMMPS 实例包括通过 C 库接口、Python 或 Fortran 模块创建的多实例加载的插件对所有其他实例立即可用不再需要显式的 restore 步骤plugin restore命令仅为向后兼容保留、什么都不做plugin load可以重复发出但已加载的插件会被跳过src/PLUGIN/plugin.cpp 的plugin_register()中通过plugin_find()检查并打印 Ignoring load of ... must unload existing ... plugin first替换插件必须先显式plugin unload插件只会在被显式卸载、或 LAMMPS 库接口被 finalize如 C 库接口中的lammps_plugin_finalize()见 unittest/c-library/test_main.cpp时移除多个实例并发的插件加载/卸载操作在内部通过互斥锁plugin_mutex串行化保证共享的插件注册表不会被破坏info styles命令见 info 命令文档和-help命令行输出会对当前由插件提供的样式名附加一个尾部星号*标记。编译插件环境一致性与构建方式插件必须使用与 LAMMPS 可执行文件和库相同的编译器、相同的库如 MPI和相同的编译设置MPI 开关、OpenMP、整数位宽等来编译。否则插件很可能无法加载——LAMMPS 是 C函数签名的作用域、类型和参数个数都被编码进符号名name mangling任何不一致都会导致plugin load失败。对此 plugin 命令文档 还提醒插件依赖 LAMMPS 的二进制接口ABI尤其是 MPI 库用不同 MPI 库或不同编译设置编译出的插件不能保证工作且 LAMMPS 不做一致性检查不匹配时可能加载失败甚至可能造成数据损坏或崩溃。插件的编译有两种方式方式一CMakeexamples/plugins/CMakeLists.txt 是一份可以直接作为模板使用的 CMake 构建脚本其中几个值得注意的技术点要求 CMake 3.20 与 C17 标准与当前 LAMMPS 的 C 标准一致独立构建时通过LAMMPS_HEADER_DIR指定 LAMMPS 头文件目录默认安装前缀设为$HOME/.local避免需要 root 权限每个插件用add_library(... MODULE ...)声明链接到 LAMMPS 库并通过set_target_properties(... PROPERTIES PREFIX SUFFIX .so)去掉 CMake 默认的前缀/后缀使产物形如helloplugin.so平台差异处理macOS 使用-Wl,-undefined,dynamic_lookupWindows 使用WINDOWS_EXPORT_ALL_SYMBOLS导出 DLL 符号Linux/BSD 等 ELF 平台使用-rdynamic当在静态 LAMMPS 库构建中内嵌编译插件时需要 CMake 3.27 并使用$COMPILE_ONLY:lammps生成器表达式只导入编译期设置避免把整份 LAMMPS 副本链接进插件导致符号二次构造/析构、进程退出时崩溃Windows 例外DLL 不允许未定义符号必须完整链接库。方式二GNU makeexamples/plugins/Makefile 展示了传统的 make 方式CXXmpicxx编译标志包含-fPIC生成位置无关代码DSO 必需链接时用-shared -rdynamic生成.so具体目标规则在 Makefile.common 中。-fopenmp与 OPENMP 头文件路径是为了编译morse2/omp这类 OpenMP 变体样式。更复杂的插件实例从既有包构建插件原文档给出了两个把整个包变成插件的进阶实例examples/kim/pluginKIM 包插件把 KIM 包转换为插件KIM 包源码本身无需任何改动只需额外添加插件接口与加载器代码。该示例仅支持 CMake 构建需要以-DLAMMPS_SOURCE_DIRpath/to/lammps/src/folder运行 CMake其余配置与编译 LAMMPS 相同examples/PACKAGES/pace/pluginML-PACE 包插件从 ML-PACE 包创建插件。主体代码位于先下载并编译的静态外部库中再与 pair 样式包装器和插件加载器组合。由于外部库与 LAMMPS 的许可证在分发二进制时存在冲突ML-PACE 不能静态链接而构建插件所需的 LAMMPS 头文件以更宽松的许可证提供因此该示例还包含一个 NSIS 脚本用于生成 Windows 安装包安装时会自动设置所需环境变量之后启动兼容的 LAMMPS 二进制即可自动加载并注册该插件ML-PACE 包用法与直接静态链接进 LAMMPS 时完全一致。启动时自动加载LAMMPS_PLUGIN_PATH设置环境变量LAMMPS_PLUGIN_PATH后LAMMPS 会在启动时自动搜索该路径所列目录可多个路径分隔符与系统 PATH 一致中所有以plugin.so结尾的文件例如helloplugin.so并尝试自动加载其中包含的插件。实现位于 src/PLUGIN/plugin.cpp 的plugin_auto_load()它遍历目录、用正则匹配\plugin.so$文件名并逐个plugin_load()。按此方式加载的插件其行为与对应代码被静态编译进二进制完全一致。测试与验证仓库的单测覆盖了插件的主要行为可作为开发插件时的验证参考。在 unittest/commands/test_simple_commands.cpp 中可以看到加载helloplugin.so后检查日志中出现 Loading plugin: Hello world command加载nve2plugin.so后检查 NVE2 variant fix styleplugin list输出1: command style plugin hello、2: fix style plugin nve2等条目重复加载同一插件会被拒绝并提示必须先卸载已有插件卸载不存在对应插件的样式如plugin unload pair nve2会提示 Ignoring unload ... not from a plugin。此外 unittest/c-library/CMakeLists.txt 展示了把 examples/COUPLE/plugin 目录作为插件构建并与 C 库接口联调的测试组织方式而静态库构建场景下插件必须在不嵌入 LAMMPS 副本的前提下构建见 unittest/commands/CMakeLists.txt 中的说明。小结LAMMPS 的插件机制把新增样式从重编译整个 LAMMPS中解放出来只需按约定编写实现类 工厂函数 lammpsplugin_init注册函数编译成*plugin.soDSO即可在任意兼容的 LAMMPS 二进制中通过plugin load或LAMMPS_PLUGIN_PATH自动加载使用。编写时的核心要点可归纳为工厂函数签名必须与样式类别匹配force/command/min 用 factory1fix/compute/region/run 用 factory2、初始化函数必须为 C 绑定、编译环境必须与宿主 LAMMPS 一致、样式名在进程内全局唯一。以 examples/plugins 目录中的六个插件为模板pair、fix、command、zero 系列、kspace、run/min再参考 KIM 与 ML-PACE 的包级插件实例即可快速上手自己的插件开发。赞分享科研科学计算高性能计算【免费下载链接】lammpsPublic development project of the LAMMPS MD software package项目地址https://gitcode.com/gh_mirrors/la/lammps点击查看免费下载相关推荐Ponytail全面概览一个让AI平均少写54%代码的免费开源神器Ponytail全面概览一个让AI平均少写54%代码的免费开源神器 Ponytail 是一款免费开源的 AI Agent 技能包 它让 Claude人工智能AI 技能AI 插件提示工程AI 评测ASP.NET Boilerplate 插件系统开发指南动态加载与功能扩展终极教程ASP.NET Boilerplate 插件系统开发指南动态加载与功能扩展终极教程 ASP.NET Boilerplate 是一个强大的企业级应用程序框架提后端Web框架依赖注入认证鉴权如何在5分钟内快速上手MAVLink新手入门完整教程如何在5分钟内快速上手MAVLink新手入门完整教程 MAVLinkMicro Air Vehicle Message Marshalling Librar通信序列化嵌入式上一篇眼动追踪实战指南eyetracker 开源项目从搭建到视线控制的完整上手下一篇抖音无水印下载3步上手douyin-downloader 批量下载实战创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表