上一章节介绍了UDF数据结构,本节将继续介绍UDF常用的循环操作宏。
在编写自定义函数UDF时,常常需要对边界和计算域内的节点、单元、单元面和线程上执行重复操作(简单理解为C/C++编程中对数组元素进行循环遍历),Fluent 提供的循环宏能够帮助用户有效地遍历网格中的各个组件,以实现特定的操作和数据访问,循环宏的使用需要结合适当的线程指针和索引变量来确保对正确的网格组件进行操作。
循环宏通常应用于以下场景:
-
数据采集和统计:遍历所有单元或面以收集各种物理量,如温度、压力、速度等,然后进行统计分析(例如计算平均值、最大值、最小值)。
-
边界条件的应用:在自定义边界条件时,遍历所有边界面以设置或修改特定的边界条件。
-
流场的调整和修正:在模拟过程中,根据特定的条件对流场进行动态调整,遍历所有单元或面以修改特定的流动参数或物性参数。
-
后处理和结果输出:遍历所有单元或面,提取和输出模拟结果以进行后续分析或可视化。
-
网格适应和优化:在自适应网格生成或优化过程中,遍历网格以确定需要细化或粗化的区域。
-
源项的计算和添加:在求解方程过程中,遍历单元以计算并添加源项或其它特定项。
举个例子:下列 UDF 遍历一个指定的面线程,获取每个面的质心坐标,并打印坐标信息。代码通过定义一个 DEFINE_ADJUST
宏来实现这个功能,使用 Lookup_Thread
查找面线程,并使用 begin_f_loop
和 end_f_loop
遍历线程中的所有面。
注:该示例仅为体现循环宏的用途,其中涉及的通用宏和其他数据获取与存储函数将在后续章节详细介绍。
#include "udf.h"
// 定义 ADJUST 宏,每次迭代执行
DEFINE_ADJUST(face_centroid, domain)
{
real FC[ND_ND]; // 定义一个数组用于存储坐标
face_t face; // 定义一个 face_t 类型的变量,用于存储面索引
int FACE_ID = 1; // 定义面 ID,在 Fluent 中查看,可以用宏常量定义:#define FACE_ID 1
real fx, fy; // 定义变量存储坐标值
// 查找在domain中ID为FACE_ID的面线程face_thread
Thread *face_thread;
face_thread= Lookup_Thread(domain, FACE_ID);
// 遍历 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) // 结束面循环
}
循环嵌套的过程包括多个层次的循环操作。在第一层循环中,通过遍历域 (Domain) 来获取单元 (Cell) 或面的线程 (Thread)。在第二层循环中,对获取到的单元或面线程进行循环,以访问具体的单元或面。在第三层循环中,对具体的单元或面进行循环操作,以获取相应的节点 (Node)。这种嵌套循环结构有助于逐层访问网格数据,确保对每个元素进行细致操作。
下面逐一介绍循环宏:
一、Domain中遍历cell thread(thread_loop_c)
thread_loop_c
遍历指定Domain内的所有cell threads,c_thread
变量用于引用当前循环到的单元线程,可以在循环体内添加需要对每个单元线程执行的具体操作。
Domain *domain;//给定的域
Thread *c_thread;
thread_loop_c(c_thread, domain) /* 遍历域内所有单元线程 */
{
/* 对每个单元线程执行的操作 */
}
二、Cell thread中遍历cells (begin…end_c_loop)
使用 begin_c_loop
和 end_c_loop
在一个给定的Cell thread中来遍历所有cells。其包含开始和结束循环语句,并在大括号内对单元线程中的每个单元执行操作。
cell_t c;
Thread *c_thread;//给定的单元线程
begin_c_loop(c, c_thread) /* 遍历单元线程内所有单元 */
{
/* 对每个单元执行的操作 */
}
end_c_loop(c, c_thread)
通常,需要遍历Domain中所有cell threads中的所有cells,将begin...end_c_loop
循环嵌套在 thread_loop_c
中。
Domain *domain;//给定的域
Thread *c_thread;//thread_loop_c循环获得,用于begin...end_c_loop循环
cell_t c;
thread_loop_c(c_thread,domain)
{
begin_c_loop(c,c_thread)
{
.../*对单元进行操作*/
}
end_c_loop(c,c_thread)
}
举例:循环遍历指定域中所有单元线程中的所有单元,并累积单元温度:
#include "udf.h"
DEFINE_EXECUTE_AT_END(loop_over_cells)
{
Domain *domain; /* Fluent 域指针 */
Thread *c_thread; /* 单元线程指针 */
cell_t c; /* 单元变量,用于标记单元索引 */
real temp = 0.0; /* 用于累积温度的变量 */
/* 获取域指针,后续章节介绍 */
domain = Get_Domain(1);
/* 检查域指针是否为空 */
if (domain == NULL)
{
Message("Error: domain pointer is NULL.\n");
return;
}
/* 在域中循环所有单元线程 */
thread_loop_c(c_thread, domain)
{
/* 在单元线程中循环所有单元 */
begin_c_loop(c, c_thread)
{
/* 获取单元温度并累加 */
temp += C_T(c, c_thread);
}
end_c_loop(c, c_thread)
}
/* 打印累积的温度值 */
Message("Total temperature: %f\n", temp);
}
三、Domain中遍历face thread(thread_loop_f)
与上述 thread_loop_c
宏类似,可使用thread_loop_f
遍历指定Domain内的所有face threads,f_thread
变量用于引用当前循环到的单元面线程,可以在循环体内添加需要对每个单元面线程执行的具体操作。
Domain *domain;//给定的域
Thread *f_thread;
thread_loop_f(f_thread, domain) /* 遍历域内所有面线程 */
{
/* 对每个面线程执行的操作 */
}
四、Face thread中遍历faces (begin…end_f_loop)
与begin...end_c_loop
宏类似,使用 begin_f_loop
和 end_f_loop
在一个给定的face thread中来遍历所有faces。其包含开始和结束循环语句,并在大括号内对单元面线程中的每个单元面执行操作。
face_t f;
Thread *f_thread;//给定的单元线程
begin_f_loop(f, f_thread) /* 遍历单元线程内所有单元 */
{
/* 对每个单元执行的操作 */
}
end_f_loop(f, f_thread)
通常,需要遍历Domain中所有face threads中的所有faces,将begin...end_c_loop
循环嵌套在 thread_loop_c
中。
Domain *domain;//给定的域
Thread *f_thread;//thread_loop_f循环获得,用于begin...end_f_loop循环
face_t f;
thread_loop_f(f_thread,domain)
{
begin_f_loop(f,f_thread)
{
.../*对面进行操作*/
}
end_f_loop(f,f_thread)
}
举例:给定域中循环遍历所有面线程,然后在每个面线程中循环遍历所有面,获取并打印面的中心坐标:
#include "udf.h"
DEFINE_EXECUTE_AT_END(loop_over_faces)
{
Domain *domain; /* Fluent 域指针 */
Thread *f_thread; /* 面线程指针 */
face_t f; /* 面变量,用于标记面索引 */
/* 获取域指针,1表示默认的流体域 */
domain = Get_Domain(1);
/* 检查域指针是否为空 */
if (domain == NULL)
{
Message("Error: domain pointer is NULL.\n");
return;
}
/* 在域中循环所有面线程 */
thread_loop_f(f_thread, domain)
{
/* 在面线程中循环所有面 */
begin_f_loop(f, f_thread)
{
/* 对面进行操作,例如获取面中心坐标 */
real face_centroid[ND_ND];
F_CENTROID(face_centroid, f, f_thread);
/* 打印面中心坐标 */
Message("Face centroid: (%f, %f)\n", face_centroid[0], face_centroid[1]);
#if ND_ND == 3
Message(", %f", face_centroid[2]);
#endif
}
end_f_loop(f, f_thread)
}
}
五、Cell中遍历faces (c_face_loop)
利用c_face_loop(c,t,n)
遍历给定单元中的所有面,并在{…}
中对每个面执行特定操作。
/* 定义用于存储单元、单元线程、面、面线程和局部面索引的变量 */
cell_t c;
Thread *t;
face_t f;
Thread *tf;
int n;
c_face_loop(c, t, n) /* 遍历一个单元的所有面 */
{
/* 获取全局面索引 */
f = C_FACE(c, t, n);
/* 获取相关的面线程 */
tf = C_FACE_THREAD(c, t, n);
/* 对每个面执行操作 */
/* ... */
}
使用 c_face_loop
宏遍历给定单元 c
在线程 t
中的所有面,局部面索引存储在变量 n
中。在循环体内,使用 C_FACE
宏和 C_FACE_THREAD
宏获取全局面索引和相关的面线程。
六、Cell中遍历nodes (c_node_loop)
利用c_node_loop(c,t,n)
遍历给定单元中的所有节点,并在{…}
中对每个节点执行特定操作。
/* 定义用于存储单元、单元线程、局部节点索引和节点指针的变量。 */
cell_t c;
Thread *t;
int n;
Node *node;
c_node_loop(c,t,n) // 遍历给定单元c的所有节点
{
// 获取单元的全局节点号
node = C_NODE(c,t,n);
// 在这里对每个节点执行操作
// ...
}
使用 c_node_loop
宏遍历一个单元 c
在线程 t
中的所有节点。在循环体内,使用 C_NODE
宏获取全局节点号,并将其存储在变量 node
中。
七、Face中遍历nodes (f_node_loop)
利用f_node_loop(f,t,n)
遍历给定面的所有节点,并在{…}
中对每个节点执行特定操作。
示例:
face_t f; // 定义面变量f,用于标记面索引
Thread *t; // 定义线程指针t,指向包含面的线程
int n; // 定义局部节点索引变量n
Node *node; // 定义节点指针node,用于存储全局节点号
f_node_loop(f,t,n) // 遍历给定面f的所有节点
{
// 获取面的全局节点号
node = F_NODE(f,t,n);
// 在这里对每个节点执行操作
// ...
}
使用 f_node_loop
宏遍历一个面 f
在线程 t
中的所有节点。在循环体内,使用 F_NODE
宏获取全局节点索引,并将其存储在变量 node
中。
除上述七个常用的循环宏外,Fluent UDF还定义了一些网格循环宏,在后续UDF学习中需要使用再补充说明;此外,还有一些特定的多相流循环宏,包括前述数据结构章节未介绍多相流的数据结构,其将在后续多相流专题详细介绍。
本章节介绍了UDF常用的数据循环宏,后续章节将介绍UDF数据获取宏。
能力有限,如有错误之处,请批评指正!