前述两节介绍了UDF中常用的网格节点、网格面及网格单元数据访问宏,除此之外,UDF的编写常常需要配合一些特殊的数据访问宏,本节继续介绍一些UDF常用的数据访问宏。
Lookup_Thread
THREAD_ID
Get_Domain
F_PROFILE
THREAD_SHADOW
4.1 获取线程指针 (Lookup_Thread)
如果 UDF 需要在特定线程上操作(而不是遍历所有线程),并且 DEFINE
宏没有从求解器传递线程指针(如 DEFINE_ADJUST
),则可以使用 Lookup_Thread
获取所需的线程指针。将 zone_ID
作为参数传递给 Lookup_Thread
,它返回与该区域 ID 关联的线程指针。
宏 | 参数类型 | 返回值 |
---|---|---|
Lookup_Thread(domain,zone_ID) | Domain *domain , int Zone_ID | Thread *t |
参数和用法:
int zone_ID = 16;
Thread *thread_name = Lookup_Thread(domain, zone_ID);
domain
:指向 domain 的指针,由 Ansys Fluent 传递。zone_ID
:Ansys Fluent 自动分配的区域 ID 号。
Zone ID可由对应的边界条件或单元区域条件面板中查看:
UDF示例:
#include "udf.h"
// 定义 ADJUST 宏,每次迭代执行
DEFINE_ADJUST(face_centroid, domain)
{
real FC[ND_ND]; // 定义一个数组用于存储坐标
face_t face; // 定义一个 face_t 类型的变量,用于存储面索引
int FACE_ID = 16; // 定义面 ID,在 Fluent 中查看,可以用宏常量定义:#define FACE_ID 16
real fx, fy; // 定义变量存储坐标值
// 查找在domain中ID为FACE_ID的面线程face_thread
Thread *face_thread;
face_thread = Lookup_Thread(domain, FACE_ID);
// 检查face_thread是否为空
if (face_thread == NULL)
{
Message("Error: face_thread is NULL. Check FACE_ID.\n");
return;
}
// 遍历 face_thread 中的所有面face
begin_f_loop(face, face_thread)
{
F_CENTROID(FC, face, face_thread); // 获取面 face 的质心坐标,并存储在 FC 数组中
fx = FC[0]; // 获取 x 坐标
fy = FC[1]; // 获取 y 坐标
// 打印坐标信息
Message("Centroid Coordinates: x = %f, y = %f\n", fx, fy);
}
end_f_loop(face, face_thread) // 结束面循环
}
该UDF的使用与详解参见本专栏第三节面数据访问宏-面心坐标
4.2 获取 Zone ID (THREAD_ID)
THREAD_ID
宏用于在已知线程指针 t
的情况下,获取与该线程关联的Zone ID ,该宏的功能与上述获取线程指针宏 Lookup_Thread
相反。THREAD_ID
宏接收一个线程指针 t
并返回该线程所关联的Zone ID 。
宏 | 参数类型 | 返回值 |
---|---|---|
THREAD_ID(t) | Thread *t | int Zone_ID |
int zone_ID = THREAD_ID(t);
4.3 获取域指针 (Get_Domain)
Get_Domain
宏用于获取域指针。在某些情况下,求解器不会将域指针自动传给UDF,则需要Get_Domain
宏获取域指针以便进行后续操作。
例如在
DEFINE_ON_DEMAND
宏中特别常用,因为DEFINE_ON_DEMAND
函数不会从 Ansys Fluent 求解器中传入任何参数。
Get_Domain(domain_id);
对于单相流,domain_id
为 1,Get_Domain(1)
将返回流体域指针。
DEFINE_ON_DEMAND(my_udf)
{
Domain *domain; /* 声明域指针变量 */
domain = Get_Domain(1); /* 返回流体域指针 */
...
}
对于多相流,Get_Domain
返回的值可以是混合级域、相级域或交互相级域指针。domain_id
的值对于混合域始终为 1。可以通过 Fluent 图形用户界面获取 domain_id
,获取方法与获取 Zone ID 类似。只需转到 “Phases” 对话框并选择所需的相,即可显示 domain_id
。
Setup → Models → Multiphase → Phases → Edit…
DEFINE_ON_DEMAND(my_udf)
{
Domain *mixture_domain;
mixture_domain = Get_Domain(1); /* 返回混合域指针 */
Domain *subdomain;
subdomain = Get_Domain(2); /* 返回 ID 为 2 的相域指针 */
...
}
UDF示例:构建名为 get_coords
的 UDF,打印两个指定线程 ID 的线程面质心。自定义函数 Print_Thread_Face_Centroids
使用 Lookup_Thread
宏确定线程指针,然后将指定线程中所有面的面质心写入文件。Get_Domain(1)
函数调用返回域指针(在多相流中返回混合域指针),此参数没有传递给 DEFINE_ON_DEMAND
。
#include "udf.h"
FILE *fout; // 定义一个文件指针,用于输出文件
// 定义函数,打印指定线程的面质心
void Print_Thread_Face_Centroids(Domain *domain, int id)
{
real FC[3]; // 用于存储面质心坐标的数组
face_t f; // 定义面标识符
Thread *t = Lookup_Thread(domain, id); // 通过线程 ID 查找线程指针
fprintf(fout,"thread id %d\n", id); // 输出线程 ID 到文件
// 开始循环遍历线程中的每个面
begin_f_loop(f, t)
{
F_CENTROID(FC, f, t); // 获取当前面的质心坐标并存储在 FC 数组中
// 将当前面的 ID 及其质心坐标输出到文件
fprintf(fout, "f%d %g %g %g\n", f, FC[0], FC[1], FC[2]);
}
end_f_loop(f, t) // 结束循环遍历
fprintf(fout, "\n"); // 在文件中添加一个换行符,用于分隔不同线程的数据
}
// 定义按需执行的 UDF 函数
DEFINE_ON_DEMAND(get_coords)
{
Domain *domain; // 声明一个域指针变量
domain = Get_Domain(1); // 获取域指针,1 表示单相流的流体域
fout = fopen("faces.out", "w"); // 打开文件 faces.out 以写入模式
// 打印 ID 为 2 的线程的面质心
Print_Thread_Face_Centroids(domain, 2);//调用函数
// 打印 ID 为 4 的线程的面质心
Print_Thread_Face_Centroids(domain, 4);
fclose(fout); // 关闭文件
}
在后续多相流专栏中还会重点讲解
Get_Domain
宏的应用。
4.4 边界条件设置 (F_PROFILE)
F_PROFILE
通常用于 DEFINE_PROFILE
宏中为给定的面和线程设置边界条件值。
宏 | 参数类型 | 返回值 |
---|---|---|
F_PROFILE(f,t,i) | face_t f,Thread *t,int i | void |
face_t f
:面标识符Thread *t
:线程指针int i
:需要设置的特定面变量的整数索引。在将DEFINE_PROFILE
UDF 挂接到边界条件对话框中的特定变量(例如,压力、温度、速度)时,Ansys Fluent 会定义该索引,并将其传递给 UDF,使函数知道要操作哪个变量。
F_PROFILE
宏使用框架如下:
#include "udf.h"
DEFINE_PROFILE(boundary_condition,t,i)
{
/* 定义其他需要的变量 */
face_t f;
begin_f_loop(f,t)
{
//对 F_PROFILE(f,t,i) 进行赋值或其他操作
}
end_f_loop(f,t)
}
UDF示例:案例链接
1.案例描述:如下图所示二维矩形流场,矩形尺寸
40
×
20
mm
40×20\text{mm}
40×20mm,上下边界类型为wall,两侧分别为速度入口和压力出口,现采用UDF使入口速度呈抛物线分布:
v
(
y
)
=
V
max
(
1
−
y
2
y
max
2
)
v(y)=V_{\max}\left(1-\frac{y^2}{y_{\max}^2}\right)
v(y)=Vmax(1−ymax2y2)
- V m a x V_{max} Vmax 为轴心处最大速度,本案例为 0.01 m/s 0.01\text{m/s} 0.01m/s
-
y
m
a
x
y_{max}
ymax 为流域半宽度,本案例为
10
mm
10\text{mm}
10mm
2.UDF文件:
#include "udf.h"
// 定义入口速度分布为抛物线形状的 UDF
DEFINE_PROFILE(velocity_profile, t, i)
{
real x[ND_ND]; // 数组,用于存储质心坐标
real y; // 变量,用于存储 y 坐标值
face_t f; // 面标识符
real max_velocity = 0.01; // 最大速度值
real y_max = 0.01; // y 方向的最大值,用于归一化
// 开始循环遍历线程中的每个面
begin_f_loop(f, t)
{
// 获取当前面的质心坐标并存储在 x 数组中
F_CENTROID(x, f, t);
// 提取 y 坐标
y = x[1];
// 计算抛物线形状的速度值并将其存储到当前面
F_PROFILE(f, t, i) = max_velocity * (1.0 - (y*y)/(y_max*y_max));
}
end_f_loop(f, t) // 结束循环遍历
}
将UDF保存为.c
文件类型并储存在案例文件夹下。
3.案例设置:本案例仅为演示抛物线入口速度分布,除入口速度采用UDF外,其余设置保持默认。
4.求解查看:初始化完成后,进行迭代求解(本案例迭代次数为10)
速度云图如下:
入口速度分布:
4.5 影子面 (THREAD_SHADOW)
在 Ansys Fluent 中,薄壁通常由两个面线程组成:一个是实际的面线程,另一个是对应的影子线程。THREAD_SHADOW(t)
宏可以在 UDF 中获取影子线程,以便在薄壁的两侧进行操作。通过检查 THREAD_SHADOW(t)
返回值是否为 NULL,可确定一个线程是否是薄壁的一部分,并进行相应的处理。
if (!NULLP(ts = THREAD_SHADOW(t)))
{
/* 在此处使用影子壁面线程 (ts) 进行操作 */
}
这行代码检查线程 t
是否有影子线程。如果有,THREAD_SHADOW(t)
会返回影子线程指针,并将其赋值给 ts
。NULLP(ts)
用于检查 ts
是否为 NULL。
UDF示例:假设有一个薄壁,需要在 UDF 中分别对薄壁两侧进行操作,可以使用以下代码:
#include "udf.h"
DEFINE_ADJUST(adjust_thin_wall, domain)
{
Thread *t;
Thread *ts;
face_t f;
thread_loop_f(t, domain)
{
if (!NULLP(ts = THREAD_SHADOW(t)))
{
begin_f_loop(f, t)
{
/* 在实际壁面线程 t 上进行操作 */
}
end_f_loop(f, t)
begin_f_loop(f, ts)
{
/* 在影子壁面线程 ts 上进行操作 */
}
end_f_loop(f, ts)
}
}
}
本节介绍了几个UDF中特殊的数据获取宏,结合前两节内容。常用的数据获取宏介绍完毕,剩下一些多相流模型、DPM模型、梯度与导数数据获取宏后续有需求再补充。
欢迎评论区留言或私信讨论交流,不仅仅是CFD。