PFC 学习笔记-003

本文最后更新于:2022年9月15日 晚上

理解篇,PFC 6.0 编程

概述

上篇笔记说到PFC 5.0后的版本支持命令流、FishPython三种方式进行编程,这篇笔记就来谈谈我对PFC 6.0中这三种编程方式的理解。PFC建模其实就是通过编程建立不同类型的对象,这些对象有的是itasca系列软件通用的,如modelgeometrygroup等,有的则是PFC独有的,如ballclumpcontact等。通用的对象一般具有工具属性,用于辅助模型建立或者模型数据记录和导出等,PFC独有的对象则是我们建模的主体,它们的属性、行为和相互作用是我们编程的重点。

对于建模方式,通过在 PFC 默认的控制台console中一条一条地输入命令的方式显然不实际,一般是将命令按顺序写入文本文件(脚本)中,然后程序再自动按顺序运行;默认的控制台只能用于输入命令或者嵌入的Fish语句,对于pythonPFC 6.0内置了ipython控制台,支持tab命令自动补全,使用起来很方便。

命令流建模

基本语法格式

命令流是PFC建模的核心方式,PFC中的通用对象和大部分独有对象只能通过命令流的方式来创建。PFC的命令在手册里都有详细的描述,使用时可以提前查阅。我个人认为这些命令可以粗略地分为两类,一类为操作类命令,即对一个对象进行某种操作,语法格式为 “对象 + 操作”,如:

1
2
ball delete ; 删除所有 ball
program return ; 退出程序

另一类为赋值类命令,即对对象的某一属性进行赋值,语法格式为 “对象 + 属性 + 值”,如:

1
2
ball attribute velocity 0 0 0 ; 设置 ball 的速度为 (0, 0, 0)
ball group "sand" ; 将 ball 分到 "sand" 组中

除此之外,PFC的大多数命令都具有层级结构,即一个命令下还有二级命令,典型的如:

1
2
3
contact cmat default model xxx ... ; 这里 xxx 为占位
method defomability emod xxx kratio xxx &
property fric xxx

可以看到,model命令下有二级命令methodpropertymethod又有二级命令deformability,二级命令还有自己的下一级命令,具体的层级关系可以查阅手册中的命令描述。这句命令可以这样理解:首先指定操作对象,即接触模型赋值表cmatdefault槽,设置该槽的接触模型model;然后再将model作为操作对象,先对其defomability方法 (method) 进行设置,包括对emodkratio进行赋值,接着对其fric属性 (property) 进行赋值。PFC 6.0支持行内代码提示,英文输入状态下,按下Ctrl+Space就会弹出命令提示。

PFC中,命令流和Fish语言使用分号 “;” 进行注释;当一条命令用一行写不下时,可以在该行的末尾写上 “…” 或 “&”,然后在紧接着的下一行写该命令剩余的部分,也可以用这种方式来提升代码的可读性。

PFC代码中的符号必须是英文半角符号;”…” 或 “&” 只连接相邻的两行命令,不能跨越空行。

常用命令

group

为了方便操作和管理对象,通常会对具有某一共同性质的对象进行分组,这就是group命令的作用,group下还有二级命令slot,可以将同一组 (group) 中的部分对象分入不同的槽 (slot),进行更加细致的区分。

1
2
ball group "sand" slot "fines"  ; 粉砂
ball group "sand" slot "coarse" ; 粗砂

range

在建模时,常常需要对一类对象中满足某一条件的对象进行操作,而不是对一类对象中的所有对象进行操作,仅限定要操作的对象范围,这就是range命令的作用,range命令在建模的过程中非常常用,使用时在需要限定范围的命令后加上range关键字和限定条件即可。range命令后的限定条件通常是某些属性范围,可以在手册中查看详细的描述,简单示例如下:

1
2
3
4
range group "g1"     ; 对属于 "g1" 组的对象进行操作
range group "g1" not ; 对不属于 "g1" 组的对象进行操作
range position-x 0 1 ; 对 "x" 坐标在 (0, 1) 的对象进行操作
range union group "g2" position-z 0 1 ; 对属于 "g2" 组或 "z" 坐标在 (0, 1) 的对象进行操作

history

为了观察模型的变化,我们需要在模型执行的过程中对我们关心的状态量进行监测,如颗粒或墙体的受力情况和位移情况等等。PFC除了提供通用的history命令外,还为每个对象提供了history命令,在监测时推荐后者,更加符合直觉,前者更适合用来对已有的history进行整体操作,简单示例如下:

1
2
3
4
history name xx xxx  ; 通用的 history 命令进行监测
history interval 50 ; 使用通用的 history 命令设置每隔 50 时步记录一次数据
history delete ; 删除所有 history
ball history id 1 velocity-z id 2 ; 使用 ball 对象的 history 命令监测 id 为 2 的颗粒的 z 向速度,该 history 的 id 为 1

plot

plot是用于绘图的命令,但通过代码编写绘图程序比较繁琐,不直观,一般直接通过PFC的图形化界面来绘制history命令监测的数据的图像,plot命令通常用来导出数据。

1
2
plot "xxx" export csv filename "yyy.csv"    ; 将 plot xxx 中的数据导出到名为 yyy.csv 的 csv 文件中
plot "xxx" export bitmap filename "yyy.png" ; 将 plot xxx 中的图像导出到名为 yyy.png 的 png 图片中

program

program命令用于控制程序的运行和调用:

1
2
3
4
5
program call "xxx.dat" suppress ; 调用 "xxx.dat" 文件
program system "mkdir a" ; 执行系统命令,对于 windows 系统,相当于在命令行中执行 "mkdir a",即创建文件夹 "a"
program pasue ; 暂停程序,控制台输入 program continue 继续运行
program pasue key ; 暂停程序,按任意键继续
program return ; 结束程序

suppress关键字表示关闭命令回显,即被调用的文件内的代码不会在控制台中打印。

Fish 建模

Fish语言是PFC默认的脚本语言,可以与命令流进行相互嵌套,需要注意的是PFC中的这个Fish语言和命令行Fish Shell并不是一个东西,Fish语言的特点为:

  • 1.大多数命令成对出现
  • 2.变量声明无类型限制,且默认为全局变量
  • 3.可以与命令流相互嵌套
  • 4.无法创建Itasca系列软件通用对象和大部分PFC独有对象,但基本可以读取和操作这些对象

基本语法

Fish语言的语法比较简单,容易上手,和Python有一点类似:

1
2
3
4
5
6
7
8
9
10
11
12
fish define foo(a, b) ; 带参数函数定义,也可以去掉括号定义无参函数
a = 1 ; 声明和定义全局变量,等价于 global a = 1
global b = 2

if a # b then ; Fish 中,不等于用 "#" 号表示
local c = a + b ; 声明和定义局部变量,作用域为其所在的命令对,这里是 if ··· endif
io.out("a != b")
endif

foo = a + b ; 函数名可以赋值,相当于函数返回值,需要注意不能多次赋值,那样相当于循环调用 foo 函数
end
@foo(1, 2) ; 在函数外需要使用 "@" 符号调用 Fish 函数

Fish 方法调用

与常见的面向对象语言不同,Fish语言中某类对象调用其方法的方式为 “类名.方法(对象指针/id/name)”:

1
2
[mp1 = measure.find(1)] ; 查找 id 为 1 的测量圆
measure.porosity(mp1) ; 调用 measure 类的 porosity 方法查看 mp1 的孔隙率

Fish 语句与命令流嵌套

PFC中,新建了一个Data file后,默认是写入命令流语句或者定义Fish函数,想要写入单独的Fish语句则需要将该语句用 “[]” 括起来;在Fish函数中使用命令流时,需要将命令流写在command ··· endcommand中。以下为简单的示例,仅作说明,实际使用时会复杂一些:

1
2
3
4
5
6
7
[rad = 1.0] ; Fish 函数外的 Fish 变量/语句需要用 "[]" 包起来
[pos = vector((0, 0, 0))] ; 这里调用了 vector 类的构造函数,传入参数为 (0, 0, 0)
def foo ; fish define 可简写为 def
command ; 命令流与 Fish 函数嵌套
ball create id 1 radius [rad] position [pos] ; Fish 变量与命令嵌套
endcommand
end

此外,Fish函数中嵌套的命令不能是model restore或者model new,因为前者会用.sav文件中的Fish状态(函数、变量等)覆盖当前的Fish状态,后者则会清空模型内存,包括当前的Fish状态,两者都会和现有Fish状态产生冲突,因此不能在Fish函数中使用,这有点穿越到过去杀了自己的感觉 🤪。

不建议Fish语言与命令流多层嵌套,那样会降低程序的可读性和易维护性。

Fish 变量变化监测

前面说过可以用History命令监测PFC中对象的属性,但有时候我们需要用Fish函数来计算模型中的各种参数,最后得到结果也保存在Fish变量中,这时就需要对该变量进行监测,对此,PFC提供了fish history命令,简单示例如下:

1
2
fish history @a ; 监测变量 a
fish history name "b" @b ; 监测变量 a,并将该 history 命名为 "b"

常用 Fish 方法

PFC中,每一类对象都有自己的专属Fish方法,此外PFC还提供了一系列工具类库,如matharrayvector等,所有Fish函数在PFC的手册中都有详细的说明,使用时可以根据需求去查找参考。

1
2
3
4
5
6
ball.find()    ; 查找类函数
ball.group(bp) = "sand" ; 设置属性类函数
gp = ball.group(bp) ; 读取属性类函数
contact.list ; 获取属性类方法
math.sqrt() ; 计算类函数
array / vector ; 数据结构类

PFC独有对象的Fish函数大多是读写一致的,如ball.group()方法,它在等号左边就是写,即设置分组,在等号右边就是读,即读取分组。

Python 建模

PFC 6.0开始支持Python脚本,就个人使用经验来看,Python脚本适合用于程序的自动化运行以及程序运行结果数据的处理,对于模型的生成和模型的各种细节配置,还是得用命令流和Fish语言。和Fish语言一样,使用Python同样无法创建Itasca系列软件通用对象和大部分PFC独有对象,但基本可以读取和操作这些对象。

在 PFC 中使用 Python

Itasca 将其提供的对象和方法都封装在itasca这个包里,因此使用时需要先import这个包:

1
2
# pfc.py
import itasca as it # 导入包,设置别名为 it,方便后续使用

当前的 Python 状态在执行了model restore或者model new命令后会被清空,要防止这种情况需要设置:

1
it.command("python-reset-state false")

这样就可以在使用model restoremodel new命令的同时保留Python状态了,个人猜测Python状态可以通过以上命令独立于模型数据之外,而Fish状态始终和模型数据绑定,所以前者可以使用model restoremodel new命令,后者则不行。

itasca 包中的类和方法

虽然Python不能创建大多数PFC中的对象,但itasca包中提供了一系列方法来获取和操作这些对象,这些方法可以分为类方法和对象方法,前者是对属于某个类的所有对象进行操作,后者则是对某个类的某个对象进行操作。

1
2
3
4
it.ball.count()  # 类方法,获取 ball 的数目
ball = it.ball.find(1) # 类方法,查找指定 ball
ball.density() # 对象方法,获取属性
ball.set_density() # 对象方法,设置属性

Fish不同,Python中提供的方法不是读写一致的,写命令通常以 “set” 开头。

Python 与命令流和 Fish 交互

在 PFC 中,Python 与命令流交互很简单,用一个command()方法就可以了,与Fish进行交互则通过fish这个类来实现:

1
2
3
4
5
6
7
8
9
10
11
12
it.command("""
model new
model domain extent -5e-2 6e-2 -6e-2 5e-2 -5e-2 5e-2
model cmat default model linear property kn 1e1 dp_nratio 0.2

[den = 2600]
ball generate cubic box -0.02375 0.02375 rad 1.25e-3
ball attr dens [den]
""")
it.fish.get("a") # 读取 Fish 变量 a
it.fish.set("a", 11) # 设置 Fish 变量 a 的值为 11
it.fish.call_function() # 调用 Fish 函数

使用第三方库

Python 的强大除了语法简单外,还在于其丰富的第三方库,在ipython控制台中使用pip命令安装即可,如:

1
pip install pandas # 安装数据处理库 pandas

PFC 6.0还内置了PySide2库,可以借助其编写图形化的交互窗口,自由度非常高。

Python 的使用场景

对于PFC提供的三种建模方式,个人认为命令流和Fish语言适合用来直接建模,而Python语言则适合对模型中的各种数据进行处理或者进行流程控制。我觉得典型的使用场景如下:

  • 1.命令流或Fish建模
  • 2.history/fish history监测数据
  • 3.plot export导出数据
  • 4.Python获取导出的数据并处理
  • 5.根据处理的结果决定下一步操作

PFC模拟中,通常是设定一组模拟参数,然后等这个模拟跑完后,再手动改另一组模拟参数,接着继续跑。两次模拟不是连续的,而且第一次模拟结束的时间是不确定的,这样在需要跑大量模拟时就比较费时间。

而通过Python就可以使用循环在一次模拟结束后根据设定的参数自动更改模拟参数,再接着跑,使不同的模拟过程连续。而且第二组模拟的参数也可以通过第一组模拟的结果数据来进行确定,这可以大大提高参数标定的效率。

总结

总的来说,PFC的三种建模方式各有优劣,需根据需要综合起来使用,比如生成位置随机的颗粒簇时,通过命令流就可以方便的实现,而通过Fish或者Python语言实现就比较麻烦;但对于需要一个一个地生成特定位置排列的颗粒时,使用命令的生成速度就远不如FishPython语言的生成速度了。

另外,学习这三种建模方式最好的方式就是阅读程序自带的手册,它是与软件配套的,一般不会出现命令不适配的情况,并且手册里提供了各种命令的详细用法,也有一些示例可以参考,这样上手会比上网找教程快很多,程序自带手册打开方式为点击软件菜单栏:

1
Help > Help # 在软件内部右侧面板打开手册

默认是在软件内部打开手册,可以通过以下设置改为在浏览器中打开手册:

1
Tools > General > show help in default web browser # 设置为在浏览器里打开手册

最后,学习PFC建议参考官方在线手册[1]、关注一些技术博主[2]、购买相关的书籍[3]和加入相关的学习群[4],当然最推荐的还是学习官方的手册。

学习资料

  1. PFC 官方在线手册,如PFC 7.0 Documentation,注意版本对应
  2. 推荐微信公众号:超级大的 lobby
  3. PFC 相关书籍:颗粒流(PFC5.0)数值模拟技术及应用
  4. QQ 学习群:305143751221013772

PFC 学习笔记-003
https://wanghao6736.github.io/2022/09/07/PFC-Notes-003/
作者
Wang Hao
发布于
2022年9月7日
许可协议