0. 写得好好的UDF,为什么并行一跑就炸了?
很多 Fluent 用户在写 UDF 时,都会掉进这样一个坑:
“串行模式下一切正常,一切换到并行就报错、结果乱、甚至界面崩溃?”
放心,不是你UDF写得差,而是你还没掌握 UDF并行机制的底层逻辑。
本文就带你彻底搞懂:
UDF并行到底怎么回事?你又该怎么写出“并行安全”的UDF代码?”
1. 为什么UDF在并行下就出问题了?
先来看看 Fluent 并行的工作方式。
Fluent 在并行运行时采用的是 分布式计算模型:它会将整个计算域分成多个子域,交由不同 CPU 核分别处理。
打个比方,如果模拟是一栋房子,那就是一队工人分头施工,每人干自己那一块,互不打扰。数据也是局部的,不主动通信,谁也不知道别人干了什么。
于是问题就来了:你写的那段UDF代码,其实是每个核都会各自执行一遍。它们彼此之间完全不知道其他核的执行结果,也不会共享全局变量,除非你显式地处理!
如果你使用了跨子域数据访问、全局变量共享、数组累加、文件写入等操作,而又没有同步处理,就容易出问题!
2. 典型错误写法:并行下总能量统计
来看一个常见的串行UDF写法:
#include "udf.h"
DEFINE_EXECUTE_AT_END(total_energy_serial)
{
real total_energy = 0.0;
Thread *t;
cell_t c;
thread_loop_c(t, domain)
{
begin_c_loop(c, t)
{
total_energy += C_T(c, t) * C_VOLUME(c, t);
}
end_c_loop(c, t)
}
printf("Total Energy = %f\n", total_energy);
}
这段代码在串行运行时完全没问题,但一到并行:
每个核都独立计算一遍 total_energy
多个核同时 printf()
,输出会混乱,甚至导致 Fluent 崩溃!
3. 并行安全的正确写法
我们使用 Fluent 提供的并行归约宏 PRF_GRSUM1()
来进行跨核求和,并在 主节点打印结果:
DEFINE_EXECUTE_AT_END(total_energy_parallel)
{
real total_energy = 0.0;
Thread *t;
cell_t c;
thread_loop_c(t, domain)
{
begin_c_loop_int(c, t)
{
total_energy += C_T(c, t) * C_VOLUME(c, t);
}
end_c_loop_int(c, t)
}
// 所有核之间进行并行求和
total_energy = PRF_GRSUM1(total_energy);
// 仅在主节点输出结果
printf("Total Energy = %f\n", total_energy);
}
这段代码在并行下就完全稳定,结果也正确!
4. 还有哪些情况需要并行处理?
以下这些典型UDF场景都要特别注意:
4.1 使用了条件编译指令
// 执行从节点的逻辑
// 仅主节点执行,比如输出、写文件等
4.2 涉及全局变量累加 / 统计
例如在 DEFINE_ADJUST
或 DEFINE_EXECUTE_AT_END
中累加变量:
total_heat += C_T(c, t) * C_VOLUME(c, t);
必须使用 PRF_GRSUM1()
规约
4.3 读取或写入UDF
错误写法(多个核同时写):
FILE *fp = fopen("data.txt", "w");
fprintf(fp, "Wrong output\n");
fclose(fp);
正确写法(只在主核写):
FILE *fp = fopen("data.txt", "w");
fprintf(fp, "Correct output\n");
fclose(fp);
5. 怎么判断你的UDF是否需要并行处理?
用到了 DEFINE_EXECUTE_AT_END、DEFINE_ADJUST 等宏?
涉及全局统计/求和/写文件?
使用了 #if RP_HOST / RP_NODE 条件?