一、UDF基础
1、Fluent的求解次序
了解fluent的求解过程有助于理解UDF的调用过程,确定在给定的任意时间内哪些数据是当前的和有效的。对于不同的求解器,其求解次序是不一样的。
在分离式求解器求解过程中,用户定义的初始化函数(使用DEFINE_INIT定义的)在迭代循环开始之前执行。然后迭代循环开始执行用户定义的调整函数(使用DEFINE_ADJUST定义的)。接着,求解守恒方程,顺序是从动量方程和后来的压力修正方程到与特定计算相关的附加标量方程。守恒方程之后,属性被更新(包含用户定义属性)。这样,如果模型涉及气体定律,这时,密度将随更新的温度(和压力、物质质量分数)而被更新,进行收敛或者附加要求的迭代的检查、循环或者继续或者停止。
在耦合求解器求解过程中,用户定义的初始化函数(使用DEFINE_INIT定义的)在迭代循环开始之前执行;然后迭代循环开始执行用户定义的调整函数(使用DEFINE_ADJUST定义的);接着,Fluent求解连续、动量和(适合的地方)能量的控制方程及相关的物质输运或矢量方程。其余的求解步骤与分离式求解器相同。 2、Fluent网格拓扑
① 单元(cell):区域被分割成的控制体积 ② 单元中心(cell center):Fluent中数据存储的地方 ③ 面(face):单元(二维或三维)的边界 ④ 边(edge):面(三维)的边界 ⑤ 节点(node):网格点 ⑥ 单元线索(cell thread):在其中分配了材料数据和源项的单元组 ⑦ 面线索(face thread):在其中分配了边界数据的面组 ⑧ 节点线索(node thread):节点组 ⑨ 区域(domain):由网格定义的所有节点、面和单元线索的组合 3、Fluent的数据类型
在编写UDF时,除了可以使用C语言数据类型外,还可以直接使用Fluent指定的与求解器数据相关的数据类型。常用的Fluent数据类型如下。
cell_t是线索内单元标示符的数据类型,是一个识别给定线索内单元的整数索引。face_t是线索内面标示符的数据类型,是一个识别给定线索内面的整数索引。Thread是单元或面的组合相关的数据容器。Node是单元或面的拐角相关的数据容器。Domain是Fluent中最高水平的数据结构,是一个与网格中所有节点、面和单元线索组合相关的数据容器。
二、UDF中访问Fluent变量的宏
(一)访问单元的宏
1、访问单元流体变量的宏
在Fluent中可以用来访问单元上流体变量的宏在表1中列出,注意加了_G、_RG、_M1和_M2这些下标的单元格温度的宏,可以应用于表1中除单元格压力(C_P)的所有求解器的变量中。这些下标表示的是矢量梯度、改造的矢量梯度、前一次的步长和前两次的步长。而对于单元格压力,它的矢量梯度和相应的分量是使用C_DP得到的,而不是C_P_G。
表1 在mem.h文件中的流体标量宏 名称(参数) 参数类型 返回值 C_T(c,t) cell_t c,Thread*t 温度 C_T_G(c,t) cell_t c,Thread*t 温度梯度矢量 C_T_G(c,t)[i] cell_t c,Thread*t,inti 温度梯度矢量的分量 C_T_RG(c,t) cell_t c,Thread*t 改造后的温度梯度矢量 C_T_RG(c,t)[i] cell_t c,Thread*t,inti 改造后的温度梯度矢量的分量 C_T_M1(c,t) cell_t c,Thread*t 温度的前一次步长 C_T_M2(c,t) cell_t c,Thread*t 温度的前二次步长 C_P(c,t) cell_t c,Thread*t 压力 C_DP(c,t) cell_t c,Thread*t 压力梯度矢量 C_DP(c,t)[i] cell_t c,Thread*t,inti 压力梯度矢量的分量 C_U(c,t) cell_t c,Thread*t u方向的速度 C_V(c,t) cell_t c,Thread*t v方向的速度 C_W(c,t) cell_t c,Thread*t w方向的速度 C_YI(c,t,i) cell_t c,Thread*t,inti 物质质量分数 C_K(c,t) cell_t c,Thread*t 湍流运动能 C_D(c,t) cell_t c,Thread*t 湍流运动能的分散速率 (1)可以在宏中加入下标_G来得到梯度矢量和其分量。例如,C_T_G(c,t)就是返回单元格的温度梯度矢量。注意:只有当已经求解出包含这个变量的方程时才能得到梯度变量。例如,如果定义了一个关于能量的源程序,那么所编的UDF可以使用C_T_G读写单元格的温度梯度,而不能使用C_T_G读写X方向的速度分量。而且,如果建立一个由使用者确定的方式转移方程,那么就不能得到一部分的梯度了。这是因为求解器不断地移走它不需要的数据。可以使用下面的方法来阻止存储器释放记忆:发出文本命令save/set/expert,然后对计算机提出的‘是否阻止暂时的求解器记忆释放’这一提问回答‘是’,按照这种做法就可以保留所有的梯度数据,但是这种计算需要更多的内存。 (2)在调用梯度矢量时把某一分量作为参数,这样就可以得到梯度分量了,参数0代表X方向的分量,1代表Y方向的分量,2代表Z方向的分量。例如,C_T_G(c,t)[0]就是返回温度梯度X方向的分量.注意:在表1中,虽然只列出了温度梯度和其分量求解的宏,但是却可以扩展到除了压力以外的所有变量中去,对于压力只能按照表1中的方法使用C_DP来得到压力梯度和其分量。 (3)和梯度一样的方式,可以通过加RG的下标在宏中得到梯度向量和其分量。通过使用恰当的整数作为参数来获得想要的矢量分量,参数0代表X方向的分量,1代表Y方向的分量,2代表Z方向的分量。当完成自己的插补计划时可以使用改造过的梯度。改造过的温度梯度和其分量在表1中列出了,同样可以推广到所有的变量。注意:改造过的梯度矢量和梯度矢量一样都只有在梯度方程被求解出来时才可以得到。
(4)在表1中的宏中加入下标_M1就可以得到前一次步长时间下(tt)的变量的值,得到的这些数据可以在瞬态的模拟中使用。例如,C_T_M1(c,t),返回前一步时间下的单元格温度的值。若在表1里宏的后面加上下标_M2就可以得到前两次步长下的时间(t2t),这些数据可用于瞬态的模拟计算中。在表1中仅列出了温度的前一次步长的求法,也可以扩展到其他的变量中去。 2、访问导数的宏
表2 在mem.h文件中访问导数个宏 名称(参数) 参数类型 返回值 C_DUDX(c,t) cell_t c,Thread*t U速度对x方向的导数 C_DUDY(c,t) cell_t c,Thread*t U速度对y方向的导数 C_DUDZ(c,t) cell_t c,Thread*t U速度对z方向的导数 C_DVDX(c,t) cell_t c,Thread*t V速度对x方向的导数 C_DVDY(c,t) cell_t c,Thread*t V速度对y方向的导数 C_DVDZ(c,t) cell_t c,Thread*t V速度对z方向的导数 C_DWDX(c,t) cell_t c,Thread*t W速度对x方向的导数 C_DWDY(c,t) cell_t c,Thread*t W速度对y方向的导数 C_DWDZ(c,t) cell_t c,Thread*t W速度对z方向的导数 3、访问材料性质的宏
表3 在mem.h文件中访问材料性质的宏 名称(参数) 参数类型 返回值 C_FMEAN(c,t) cell_t c,Thread*t 第一次混合分数的平均值 C_FMEAN2(c,t) cell_t c,Thread*t 第二次混合分数的平均值 C_FVAR(c,t) cell_t c,Thread*t 第一次混合分数变量 C_FVAR2(c,t) cell_t c,Thread*t 第二次混合分数变量 C_PREMIXC(c,t) cell_t c,Thread*t 反应过程变量 C_LAMFLAME SPEED(c,t) cell_t c,Thread*t 层流焰速度 C_CRITICAL STRAIN cell_t c,Thread*t 临界应变速度 RATE(c,t) C_POLLUT(c,t,i) cell_t c,Thread*t,inti 第i个污染物质的质量分数 C_R(c,t) cell_t c,Thread*t 密度 C_MU L(c,t) cell_t c,Thread*t 层流速度 C_MU T(c,t) cell_t c,Thread*t 湍流速度 C_MU EFF(c,t) cell_t c,Thread*t 有效粘度 C_K_L(c,t) cell_t c,Thread*t 热传导系数 C_K_T(c,t) cell_t c,Thread*t 湍流热传导系数 C_K_EFF(c,t) cell_t c,Thread*t 有效热传导系数 C_CP(c,t) cell_t c,Thread*t 确定的热量 C_RGAS(c,t) cell_t c,Thread*t 气体常数 C_DIFF L(c,t,i,j) cell_t c,Thread*t,int 层流物质的扩散率 i,int j C_DIFF EFF(c,t,i) cell_t c,Thread*t,int 物质的有效扩散率 i C_ABS COEFF(c,t) cell_t c,Thread*t 吸附系数 C_SCAT COEFF(c,t) cell_t c,Thread*t 扩散系数 C_NUT(c,t) cell_t c,Thread*t 湍流速度 4、访问用户自定义的单元标量和存储器的宏
表4 在mem.h文件中的可以为单元格访问用户定义的标量和存储器的宏
名称(参数) 参数类型 返回值 C_UDSI(c,t,i) cell_t c,Thread*t,int 用户定义的单元格标量 i C_UDSI M(c,t,i) cell_t c,Thread*t,int 前一次步长下用户定义i 的单元格标量 C_UDSI_DIFF(c,t,i) cell_t c,Thread*t,int 用户定义的标量的单元i 格分散率 C_UDMI(c,t,i) cell_t c,Thread*t,int 用户定义的单元格存储i 器 5、访问雷诺兹压力模型的宏
表5 在metric.h文件中的可以给雷诺兹压力模型读写标量的宏 名称(参数) 参数类型 返回值 C_RUU(c,t) cell_t c,Thread*t uu雷诺兹压力 C_RVV(c,t) cell_t c,Thread*t vv雷诺兹压力 C_RWW(c,t) cell_t c,Thread*t ww雷诺兹压力 C_RUV(c,t) cell_t c,Thread*t uv雷诺兹压力 C_RVW(c,t) cell_t c,Thread*t vw雷诺兹压力 C_RUW(c,t) cell_t c,Thread*t uw雷诺兹压力
(二)访问面的宏
1、访问面流体标量的宏
注意:如果表面在边界上,那么流体的方向是由F_FLUX决定的点指向外围空间。
表6 在mem.h文件中可以在边界面读写流体标量的宏 名称(参数) 参数类型 返回值 F_R(f,t) face_t f,Thread*t 密度 F_P(f,t) face_t f,Thread*t 压力 F_U(f,t) face_t f,Thread*t u方向的速度 F_V(f,t) face_t f,Thread*t v方向的速度 F_W(f,t) face_t f,Thread*t w方向的速度 F_T(f,t) face_t f,Thread*t 温度 F_H(f,t) face_t f,Thread*t 焓 F_K(f,t) face_t f,Thread*t 湍流运动能 F_D(f,t) face_t f,Thread*t 湍流运动能的分散速率 F_YI(f,t,i) face_t f,Thread*t,int 物质的质量分数 i F_FLUX(f,t) face_t f,Thread*t 通过边界表面的质量流速 2、读写用户定义的面标量和存储器的宏
表7 在mem.h文件中的表面读写用户定义的标量和存储器的宏 名称(参数) 参数类型 返回值 F_UDSI(f,t,i) face_t f,Thread*t,int 用户确定的表面标量 i F_UDMI(f,t,i) face_t f,Thread*t,int 用户定义的表面存储器 i 3、访问混合面标量的宏
表8 在mem.h文件中的访问混合面的宏 名称(参数) 参数类型 返回值 F_C0(f,t) face_t f,Thread*t 访问表面\\0边上的单元变量 F_C0_THREAD(f,t) face_t f,Thread*t 访问表面\\0边上的单元线索 F_C1(f,t) face_t f,Thread*t 访问表面\\1边上的单元变量 F_C1_THREAD(f,t) face_t f,Thread*t 访问表面\\1边上的单元线索 (三)访问几何的宏
1、节点和面的数量
宏C_NNODES和C_NFACES返回相应的节点和面的整数值。对于一个给定的单元格,F_NNODES返回与某个表面相关的节点的整数个数。
表9 在mem.h文件中的节点和表面的宏 名称(参数) 参数类型 返回值 C_NNODES(c,t) cell_t c,,Thread*t 一个单元格的节点数 C_NFACES(c,t) cell_t c,,Thread*t 一个单元格中的表面数 F_NNODES(f,t) cell_t f,Thread*t 一个表面中的节点数 2、单元格和表面的重心
表10列出的宏可以获得一个单元格或是表面的真实重心。C_CENTROID找到单元格的重心坐标,并把它的坐标存储在矩阵X中。F_CENTROID找到表面的重心的坐标,并把它的坐标存储在矩阵X中。注意,矩阵X可以是一维、二维或是三维的。
表10 在metric.h文件中的变量重心宏 名称(参数) 参数类型 返回值 C_CENTROID(x,c,t) realx[ND ND],cell_t 单元格重心的x值 c,,Thread*t F_CENTROID(x,f,t) realx[ND ND],face_t 表面中心的x值 f,Thread*t 3、表面积
宏F_AREA可以被用于返回一个实数的面积向量。对于内部的表面,标准的面积向量的方向是从单元格C0指向单元格C1。标准的方向总是从边界面指向外(范围之外)的。
表11 在metric.h文件中的表面积宏 名称(参数) 参数类型 返回值 F_AREA(A,f,t) A[ND ND],face_t 面积向量A f,Thread*t 4、单元格体积
表12列出的宏可以用于获得二维、三维和轴对称的模型的单元格的真实体积
表12 在mem.h文件中的单元格体积宏 名称(参数) 参数类型 返回值 C_VOLUME(c,t) cell_t c,,Thread*t 二维或是三维的单元格体积 单元格体积/2是轴对称模型的体积 (四)访问节点的宏
表13和表14列出的宏返回单元格节点的实数直角坐标(在单元格的拐角)和相应的节点速度的分量。例如,在移动的网格模拟中节点速度是相对应的,每个变量的Node*node的参数定义了一个节点。
表13 在mem.h文件中变量的节点坐标 名称(参数) 参数类型 返回值 NODE X(node) Node*node 节点的X坐标 NODE Y(node) Node*node 节点的Y坐标 NODE Z(node) Node*node 节点的Z坐标 表14 在mem.h文件中的节点速度变量宏 名称(参数) 参数类型 返回值 NODE GX(node) Node*node 节点速度的X分量 NODE GX(node) Node*node 节点速度的Y分量 NODE GX(node) Node*node 节点速度的Z分量 (五)访问多相的宏
表15列出的宏返回一个与整体多相节点相连的实数变量。这些变量的定义在sg_mphase.h文件中可以找到,这些包含在udf.h文件中。
表15 在sg_mphase.h文件中的变量宏 名称(参数) 参数类型 返回值 C_VOF(c,pt[0]) cell_t c,,Thread**pt 主要相的体积分数 C_VOF(c,pt[n]) cell_t c,,Thread**pt 第n个辅助相的体积分数 三、一般循环宏
1、查询控制区的单元线
当查询给定控制区的单元线索时,可以用thread_loop_c。它包含单独的说明,后面是对控制区的单元线索所做的操作,正如下面显示的包含在{}中。注意:thread_loop_c在执行上和下面的thread_loop_c相似。 Domain *domain; Thread *c_thread;
Thread_loop_c(c_thread,domain)/*loops over all cell threads in domain*/
{ }
2、查询控制区的面
当查询给定控制区的面时,可以用thread_loop_f。它包含单独的说明,后面是对控制区的面所做的操作,正如下面显示的包含在{}中。注意:thread_loop_f在执行上和上面的thread_loop_c相似。
Thread *f_thread; Domain *domain;
Thread_loop_f(f_thread,domain)/*loops over all face threads in a domain*/
{ }
3、查询单元线索中的单元
当要查询给定单元线索c_thread上所有的单元时,可以用begin_c_loop和end_c_loop。它包含begin和end loop的说明,完成对单元线索所做的操作,定义包含在{}中。当查找控制区单元线索的单元时,应用loop全嵌套在thread_loop_c中。 cell_t c;
Thread *c_thread;
Begin_c_loop(c,c_thread)/*loops over cells in a cell thread*/
{ }
end_c_loop(c,c_thread) 4、查询面线索中的面
当要查找给定面线索f_thread的所有面时,可以用begin_f_loop和end_f_loop。它包含begin和end loop的说明,完成对面线索中面单元所做的操作,定义包含在{}中。当查找控制区面线索的所有面时,应用loop全嵌套在thread_loop_f中。 face_t f;
Thread *f_thread;
Begin_f_loop(f,f_thread)/*loops over faces in a face thread*/
{ }
end_f_loop(f,f_thread) 5、查询单元中的面
下面函数用以查询给定单元中所有面。它包含单独的查询说明,后面是所做的操作,意义包含在{}中。
face_t f; Thread *tf; int n;
c_face_loop(c,t,n)/*loops over all faces on a cell*/ { ……
F=C_FACE(c,t,n);
tf=C_FACE_THREAD(c,t,n); …… }
这里n是当前面的索引号。当前面的索引号用在C_FACE宏中以获得所有面的数量。例如,f=C_FACE(c,t,n).
另一个在c_face_loop中有用的宏是C_FACE_THREAD。这个宏用于合并两个面线索,例如,tf=C_FACE_THREAD(c,t,n). 6、查询单元节点
C_node_loop函数用以查询给定单元中所有节点。它包含单独的查询说明,后面是所做的操作,定义包含在{}中。
cell_t c; Thread *t; int n;
c_node_loop(c,t,n) { ……
node=C_NODE(c,t,n); …… }
这里n是当前节点的索引号。当前节点的索引号用在C_NODE宏中以获得所有节点的数量。例如,node=C_NODE(c,t,n).
四、查询多相组分的宏
1、查询混合物中相的控制区
sub_domain_loop宏用于查询混合物控制区的所有相的子区。这个宏查询并在混合物控制区,给每个相区定义指针以及相关的phase_domain_index。控制区需要指针,在每个相中都有权访问部分数据。注意:sub_domain_loop宏在执行中和下面的sub_thread_loop宏是相似的。
int phase_domain_index;/*index of subdomain pointers*/ Domain *mixture_domain; Domain *subdomain;
sub_domain_loop(subdomain,mixture_domain,phase_domain_index)
sub_domain_loop的变量是subdomain、mixture_domain和phase_domain_index。 subdomain是phase_level domain的指针,mixture_domain是mixture_level domain的指针。当想用DEFINE宏时,mixture_domain(包含控制区变量,如DEFINE_ADJUST)通过Fluent求解器自动传递给UDF,混合物就和UDF相关了。如果mixture_domain没有显式地址传递给UDF,应用另
外一个宏来恢复它,如在sub_domain_loop前使用Get_Domain(1)。
phase_domain_index是子区指针索引号,phase_domain_index指向初始相时,其索引号为0,混合物中其他相一次加1,注意:subdomain和phase_domain_index是在sub_domain_loop宏定义中初始化的。
下面被集成在UDF中的语句在求解过程中补充说明一个相的体积分数,它在求解过程的开始执行。这个函数建立一个中心在点(0.5,0.5,0.5),半径为0.25的球形体。第二相的体积分数1被补充说明到球形体内的单元中,但是第二个相在其他单元中的体积分数为0。
#include “udf.h”
/*domain pointer that is passed by INIT function is mixture domain */ DEFINE_INIT(my_init_function,mixture_domain) {
int phase_domain_index;
cell_t cell;
Thread *cell_thread; Domain *subdomain; Real xc[ND_ND];
/*loop over all subdomains (phase) in the superdomain (mixture)*/ sub_domain_loop(subdomain,mixture_domain,pgase_domain_index) {
/*loop if secondary phase*/ if(domain_ID(sunmain)==2)
/*loop over all cell threads in the secondary phase domain*/ thread_loop_c(cell_thread,subdomain) {
/*loop over all cells in secdonary phase cell threads*/ Begin_c_loop_all(cell,cell_thread) {
C_CENTROID(xc,cell,cell_thread); if (sqrt(ND_SUM(pow(xc[0]-0.5),2.), pow(xc[1]-0.5,2.),
pow(xc[2]-0.5,2.)))<0.25)
/*set volume fraction to 1 for centroid*/ C_VOF(cell,cell_thread)=1.; else
/*otherwise initialize to zero*/ C_VOF(cell,cell_thread)=0.; }
end_c_loop_all(cell,cell_thread) } } }
因篇幅问题不能全部显示,请点此查看更多更全内容