简介:FLAC3D 5.01插件文件包完整提供了FISH脚本、interface接口、lib函数库及多种材料模型的源代码,专为希望深入内核并进行二次开发的工程师与科研人员设计。压缩包共317个文件,以232个h头文件和26个cpp源文件为主体,配合10个dll、8个lib动态/静态库以及dat数据文件、PDF说明和Visual Studio工程文件,整体仅4.99MB,目录结构非常清晰。FISH源码能够帮助读者掌握自定义脚本与内核交互的方法,interface代码展示了如何对接GIS或第三方后处理工具,而models源码则涵盖线弹性、弹塑性、粘塑性等典型本构模型的实现细节,便于按实际工程调整参数或开发新模型。对于涉及隧道支护、边坡稳定、矿井开采等数值模拟任务的高级用户,这些源代码既是调试和优化程序的工具,也是构建自定义插件、扩展FLAC3D功能的重要参考资料。目前已有553人学习下载,适合具备编程基础、熟悉FLAC3D基本操作的读者进一步深入学习。 做岩土数值模拟的同行,V5.0以后的FLAC3D用户应该都见过安装目录下那个叫pluginfiles的文件夹。很多人在里面翻过一阵子,看到一堆dll、xml、fis文件,觉得看不懂就关掉了;也有人试图改里面的东西,结果把软件搞崩了又重新装。其实这个目录是整个FLAC3D 5.01体系里信息密度最高、也最容易被低估的地方——它不只是插件加载目录,而是把FISH函数库、interface(接触面单元)、底层链接库和几十种本构模型的源码(或者接近源码级别的声明文件)一起暴露给了用户。这篇文章我把这个目录从结构到用途到二次开发实操完整拆一遍,包括怎么用它做UMT自定义模型开发,以及编译加载时最容易踩的那些坑。
1. pluginfiles目录的整体结构与设计逻辑
1.1 目录里到底放了什么东西
FLAC3D 5.01安装完成之后,pluginfiles目录下一般长这样(部分版本会有细微差异):
pluginfiles/ ├── fish/ │ ├── Library/ │ ├── libfunctions.fis │ └── ... ├── interface/ │ ├── libinterface.dll │ ├── interface_link.h │ └── ... ├── lib/ │ ├── libflac3d.dll │ ├── libflac3d5.dll │ ├── ... └── models/ ├── mohrcoulomb/ ├── doubleyield/ ├── cysoil/ ├── burgers/ └── ...从目录划分就能看出ITASCA在5.0版本做的架构调整——把原本铁板一块的FLAC3D核心给模块化了。插件不再只是“附加功能”,而是整个程序机制的一部分:fish目录里是以FISH脚本形式写的库函数;interface里是接触面单元的实现;lib里是底层的动态链接库;models里是各种内置本构模型的源码文件。这几个目录结合起来,基本构成了FLAC3D的主体能力。
这种设计逻辑的转变,本质上是因为FLAC3D当时要跟自家的FLAC2D、3DEC以及后来的Griddle、OptumG2等软件共享一套底层代码框架,为了复用,把核心功能全部插件化。所以5.0以后,FLAC3D的启动时间变长了,但是加载插件机制统一了——每一个plugin都是一个独立的dll,通过plugin.xml清单文件声明自己的身份。知道了这个逻辑,你再去看目录里的文件,思路就顺了:每个模块都是自治的,彼此通过接口通信,而不是像老版本那样一个大exe包打天下。
1.2 plugin.xml:插件与主程序之间的“身份证”
在每个插件子目录(比如models/mohrcoulomb)里,一定有一个plugin.xml文件。这个文件内容非常简短,但作用极其关键,它告诉FLAC3D主程序:我这个dll叫什么、版本号是多少、在程序菜单和命令文档中怎么显示。以mohrcoulomb的plugin.xml为例,核心内容大致是:
<plugin> <name>MohrCoulomb</name> <version>5.0</version> <library>models\mohrcoulomb\mohrcoulomb.dll</library> ... </plugin>这里有个细节值得注意:<library>标签指向的是dll的相对路径,是相对于plugin.xml所在位置来的。很多人在自定义模型开发时,只改了dll路径,但忘了插件目录的名字也要跟<name>对应,结果控制台一直报告找不到插件。这个下面实操部分细讲,这里先有个印象。
1.3 为什么官方要把源码暴露给用户
这是很多新手百思不得其解的问题——商业软件,源码不是核心机密吗?其实是两码事。FLAC3D 5.01里的“源码”,并不是整套程序源代码,而是本构模型的用户接口实现。ITASCA把模型的骨架留给了用户:类的声明、成员函数结构、属性注册方式都是公开的,但实际的数值算法(隐式积分、应力更新等)在dll里是以加密函数方式调用的。用户拿到的源码,更像是“API文档+示例工程”的合体,目的是让你在开发自定义模型时有一份标准范式可以对照,而不是闭门造车。
换句话说,ITASCA做的是“半开源”——把与用户二次开发直接相关的部分暴露出来,既降低技术支持成本,又让高手能扩展软件的边界。所以不要一看到models下面的代码就想着“破解”或“反向编译”,那不是这个目录设计的目的。它的真实价值有两个:一是给你一个开发UMT(User-defined Material Model,用户自定义本构模型)的参考模板;二是让你在调试模型参数时有据可查,比如看书里的公式,知道它在源码里对应的变量名是什么。
2. fish库、interface源码与lib链接库逐个拆解
2.1 fish目录:看似是脚本,其实是“官方外挂”
fish目录下的内容最容易被人忽视,因为它不是dll,而是一堆.fis文本文件。这里我不建议你直接双击打开看——有经验的用户都知道,.fis文件本质上就是FISH语言的库文件,里面定义了一堆现成的函数和变量,供主程序调用。它跟你在命令窗口手写的FISH函数,区别只在于它由系统默认加载,属于“官方外挂”。
打开libfunctions.fis,你会看到类似这样的声明:
; 定义模型中常用材料参数的默认值 def model_parameters bulk = 1e8 shear = 3e7 ... end这里的关键不是这些值,而是调用机制——FLAC3D 5.01在启动时会按照一定顺序加载插件目录下的库文件,全局的FISH符号表里就有了这些函数和变量。所以你在命令行直接敲model_parameters,软件能识别;但是你自己新建一个.fis文件放同样的函数,如果不显式用call加载,就永远是“未定义”。
有个实操经验:如果你改动了pluginfiles/fish目录下的.fis文件,必须重启FLAC3D才生效。因为FISH库的加载发生在程序启动阶段,后面不会再刷新。我见过有人改完fis文件后在命令行反复call,控制台一直报错,其实是路径写错了——call只能加载你自己写的脚本,系统库的文件索引不会因为你在命令行手动call而改变。
2.2 interface单元源码:认识FLAC3D中的接触面
interface目录下通常有libinterface.dll,以及对应的头文件或声明文件。接触面单元在FLAC3D中的作用,是模拟不同网格块体之间的滑移、张开、闭合以及摩擦行为,在岩土工程里最典型的就是模拟岩体结构面、桩土接触、锚杆与岩体的协同变形。
这个目录里的源码,核心是接触面本构行为的接口定义。常用的接触面模型是Coulomb滑动模型,它的切向和法向行为分别由ks(切向刚度)和kn(法向刚度)控制,同时还有一个内聚力c和摩擦角fric。在interface源码的头文件里,你能看到这些属性的变量名定义:
// 接触面属性 double kn; // 法向刚度 double ks; // 切向刚度 double cohesion; // 内聚力 double friction; // 摩擦角看懂这套源码对搞数值模拟的人最大的价值是:当你的模型收敛困难,或者接触面出现穿透/回弹异常时,你能判断是不是interface刚度的量级取小了。很多用户是“照着论文填参数”,论文给的参数未必设置合理,这时候源码能帮你从底层理解接触面是怎么计算的,而不是盲目调数。
2.3 lib目录与动态链接库加载机制
lib目录里放的是一堆dll,有经验的用户会发现这些dll之间存在依赖关系。比如libflac3d.dll被mohrcoulomb.dll依赖,如果主程序找不到这个基础库,所有使用该库的模型插件都会加载失败,控制台报错是“无法定位程序输入点于动态链接库”之类的提示。
这个机制在Windows下有个特别麻烦的地方:dll搜索顺序。FLAC3D主程序在加载插件时,搜索路径首先是主程序所在目录,然后是系统PATH,最后才是插件目录。ITASCA在安装时会把基础库放到系统目录或主程序目录,但如果你手动移动了pluginfiles的位置(比如为了“精简安装”把pluginfiles拷到其他盘),各种依赖就会断裂。
所以建议:不要动pluginfiles目录的默认位置。如果确实要迁移(比如C盘空间不够),务必保持整个pluginfiles目录的结构完整性,并且使用环境变量FLAC3D_PLUGIN_PATH来指向新位置,而不是手动改注册表。
2.4 models目录:本构模型源码的“完整教科书”
models目录是pluginfiles里价值最高的部分。这里有FLAC3D 5.01内置的几十个本构模型源码:Mohr-Coulomb、应变硬化/软化模型、双屈服模型、CYSoil、修正剑桥、蠕变模型(Burgers、Power、WIPP)等等。任何一个你打算做二次开发的用户,都应该先花时间研究这个目录里的两三个成熟模型,再动笔写自己的。
以models/mohrcoulomb/为例,核心源码文件是mohrcoulomb.cpp,打开后能看到这个模型类的基本骨架:
namespace itasca { class MohrCoulomb : public ConstitutiveModel { public: // 返回模型名称 virtual const char *getName() const; // 返回模型属性数量 virtual unsigned int getProperties() const; // 设置模型属性 virtual void setProperty(unsigned int prop, double value); // 核心应力更新算法 virtual void run(unsigned int state, double *stress, double *strainIncr, double *strainRate, double dt); ... }; }注意这个run函数——这就是FLAC3D每一次时步循环中,本构模型执行应力更新的入口。你的自定义模型要做的就是继承这个类,重写run函数,在里面实现你自己的应力-应变积分算法。源码给了一个引子:mohrcoulomb.cpp里run的实现清晰地展示了弹性预测-塑性修正的流程,这个流程是理解FLAC3D所有内置模型算法的基础。
3. 以UMT为例:拿源码做一套自定义本构模型
3.1 UMT二次开发环境准备
如果你要用pluginfiles里的源码做自定义模型,5.01版比较成熟的配置是:
| 工具 | 推荐版本 | 说明 |
|---|---|---|
| 操作系统 | Windows 7/10 x64 | 32位系统跑5.01插件会有兼容问题 |
| 编译器 | Microsoft Visual Studio 2013 | ITASCA官方测试环境 |
| Fortran编译器 | Intel Visual Fortran XE 2015 | 该版本与VS2013的集成最稳定 |
| FLAC3D | 5.01(含UDM开发包) | 需要在安装时勾选UDM组件 |
建议直接用支持C++的VS2013,Intel Visual Fortran单独装好并集成。经常有人问:“我装的是VS2017/VS2019,能不能编译?”理论上代码是标准的C++,新版本编译器不一定报错,但—链接时极易出错,因为dll编译用的运行库版本不同,而插件是动态加载的,运行时一旦方法签名对不上,主程序直接闪退。不建议一开始就在非官方版本上折腾。
3.2 快速识别内置模型源码的结构
拿到源码后,先不要急着写自己的代码,先看内置模型的结构。以mohrcoulomb.cpp为例,我的经验是分四步浏览:
- 第一步,看类的继承关系。FLAC3D 5.x里所有本构模型都继承自
ConstitutiveModel,这个基类在lib目录的头文件里。理解基类的虚函数(run、getProperties、setProperty等)是开发的基础。 - 第二步,看
setProperty和getProperty。这两个函数决定了在命令行里prop命令能识别哪些属性名,以及属性名的索引号。比如setProperty(0, bulk)就对应prop bulk。 - 第三步,看
run函数的算法流程。这一步最关键,能看懂内置模型如何把应变增量转化为应力增量,你自己写时照葫芦画瓢就行。 - 第四步,看模型的
getVersion和getName函数。这决定插件加载后模型显示的名称,也决定plugin.xml与模型类是否匹配。
3.3 编译环境的配置与dll输出
在VS2013里新建一个dll工程,把models/mohrcoulomb目录下的mohrcoulomb.cpp加入工程,然后在工程属性中设置:
- 字符集:使用多字节字符集(不要用Unicode,插件接口是char*)
- 运行库:多线程DLL(/MD)
- 输出路径:直接设置到
pluginfiles/models/mohrcoulomb/下,覆盖原有的mohrcoulomb.dll
编译成功后,最好先做一步验证——不要急着加载自己的模型,先把官方库文件备份一份,把编译产物拷过去,启动FLAC3D后运行config cppudm,再执行model cmodel list,看列表里MohrCoulomb是否依然正常显示。这一步能快速验证编译环境是否和软件匹配,不匹配的直接报错。
3.4 插件加载命令的完整流程
自定义模型开发完成后,加载流程大概分这几步:
- 启动FLAC3D,先执行
config cppudm——这行命令会启用用户自定义本构模型的加载机制,让后续plugin load命令可以正常工作。 - 执行
plugin load models\mohrcoulomb\mohrcoulomb.dll,按实际路径指向你的dll。注意路径分隔符用反斜杠就行。 - 执行
model cmodel list,看列表里是否出现模型名。如果没出现,排查方向:dll是否被占用、plugin.xml路径是否正确、编译是否成功。 - 在zone上指定模型,设置材料参数,开始计算。
提示:FLAC3D 5.01中另有基于FISH的二次开发方式(
model configure),但与上述插件机制无关。若你同时用了两种机制,注意config cppudm必须在model configure之前执行,否则自定义模型不生效。
3.5 用自己的dll替换官方dll时,三个容易踩的坑
- 坑一:叫你备份你不备份。替换官方dll前务必把原dll改名留存或复制到另一个目录。我第一次做UMT开发时直接把mohrcoulomb.cpp改成了自己的模型,编译后覆盖了官方dll,结果原厂模型没了,后续想回退只能重装软件。
- 坑二:保留plugin.xml中的模型名。如果你只改了代码里类的
getName()返回的名字,但plugin.xml里的<name>没改,加载时核心逻辑会混乱——插件系统会按plugin.xml里的名字加载,但这个类已经改了名字,导致链接失败。 - 坑三:sln工程名与dll输出名不一致。VS工程的名字不一定要和dll输出名一致,但dll名最好和plugin.xml里的
<library>指向保持一致,否则加载时报“找不到文件”。
4. 源码目录下的常见问题与排查技巧实录
4.1 加载插件总是报“无法定位程序输入点”
这是最常见的错误,几乎每个人第一次做插件开发都会遇到。出现这个错误的原因多半是编译器的运行库版本与主程序不一致。举个例子:你用VS2013编译的dll,跑在一个用VS2019编译的主程序上(如果你软件装的是高版本),绝对会报类似的错误。
处理办法:
- 卸载当前VS,换用ITASCA官方推荐的VS2013 + IVFXE 2015组合。
- 如果确实不想换,至少把工程属性中的
Platform Toolset改成v120(对应VS2013)。 - 检查dll依赖——用Dependency Walker打开你的dll,看是否缺少某个系统dll或FLAC3D基础库dll。
4.2 模型能在plugin load后加载成功,但model cmodel list里不显示
这个问题的解法已经提过:检查plugin.xml里的<name>标签和dll内类的getName()返回的内容是否一致。ITASCA的插件加载器会把<name>和类名做匹配,不一致就静默跳过(这是最坑的一点,不报错,只在列表里缺失)。
我的经验是:改代码时只改动getProperties、setProperty和run函数,不要随意动getName和getVersion。如果不是从零开发新模型,而是基于已有模型改参数,保留原名称反而更省事。
4.3 plugin load加载成功,但计算时报“未知属性”
这种情况一般是setProperty函数里的属性索引映射出错了。比如你想通过prop bulk 2e8设置体积模量,但你的setProperty里把索引0映射成了bulk之外的其他值,而getProperty返回的属性列表长度与索引不匹配。
排查技巧:
- 在FLAC3D命令行执行
prop不带参数,让软件列出当前模型的所有属性名,对照源码里setProperty的实现。 - 在
setProperty函数里临时加一行printf输出属性索引和属性名,编译加载后观察命令行输出,很快能定位。
4.4 模型加载成功、计算也能跑,但结果与官方模型不一致
这个现象有两种原因。一是数值算法没有按照官方模型的写法来实现——比如应力更新顺序、弹性预测的修正逻辑在源码里是有一套标准流程的,如果你为了“简化”改了计算路径,结果自然不同。二是你替换了官方dll但算了不同的初始条件,这种情况下对比的基准就不对。
想确认是哪种原因,最简单的办法是:把你的dll恢复成官方原版dll,用完全相同的命令文件跑一遍,得到基线结果;再替换成你的自定义dll跑一遍,对比两者在同样工况下的应力应变曲线。这个对照试验一定要做。
4.5 调试技巧:如何在FLAC3D里断点调试自定义本构
插件dll本质上是一个被主程序加载的动态库,所以可以在VS里直接对它进行断点调试。方法是:
- 在VS中打开你的dll工程,在
run函数里设置断点。 - 在“调试”菜单中选择“附加到进程”,选中正在运行的FLAC3D.exe进程。
- 在FLAC3D里跑一个简单的单zone模型,触发dll中的
run函数,断点就会命中。
注意:调试时FLAC3D主程序的编译模式(Debug/Release)和dll的编译模式要匹配。主程序是Release版本,dll如果用了Debug编译,附加调试时会提示符号不匹配;一般建议dll直接用Release编译,调试时只靠断点看变量值,也能满足绝大多数需求。
5. 从pluginfiles源码里能学到的深层经验
我把这个目录翻了很多遍之后最大的体会是:源码比手册更有说服力。FLAC3D的在线手册虽然详细,但很多公式的变量定义和软件内实际用的一致程度,只有看源码才能确认。举个例子,很多人在做应变软化模型时,手册里说的是“内聚力随塑性应变线性衰减”,但具体衰减斜率怎么定义、衰减到多少重置,手册语焉不详。看双屈服模型或应变软化模型的源码,你会发现它用的是table机制——给c和fric各绑定一张表,然后根据塑性应变查表取值。这个细节决定了你在命令行里到底该怎么写那个模型的参数。
另外,如果你打算长期做FLAC3D二次开发,不要只盯着models目录下的单一模型看,建议把interface目录和lib目录里的头文件一起读一遍。它们能帮你理解主程序和插件之间的通信协议——比如State枚举表示单元当前是否处于塑性状态,run函数里的state参数决定了你是做弹性预测还是塑性修正。这些底层知识,对调试复杂模型时定位问题极有帮助。
再分享一个“傻办法”:碰到看不懂的代码,把内置模型的状态变量输出出来。在run函数里临时加几行代码,把关键状态量(比如塑性应变增量、应力张量分量)写到文本文件里,加载后跑一个简单拉伸试验,把输出结果和手算的理论值对比。做完这一步,你对这个模型的理解会远超“会用prop填参数”的层面。
FLAC3D 5.01的pluginfiles,是我接触过的商业数值软件里,把“用户可扩展性”和“用户友好度”平衡得相当好的一个设计。花点时间把里面的代码读懂,收益不只是能做自定义模型,更是对整个软件计算内核建立一种直觉——以后模型出了问题,你能很快判断问题出在网格、接触还是本构上,而不是靠反复试参数碰运气。有条件的话,建议副屏开着vs,主屏开着FLAC3D,逐行跟一下内置模型的run函数,那种感觉会很不一样。
本文还有配套的精品资源,点击获取