y2
u=0.5?0.5× 0.02这个公式说明在壁面上速度为0,圆管中心线的速度为0.5m/s,而圆管入口面上的速度符合抛物线分布。
下面的UDF就是把上述的抛物线分布的入口速度与Fluent求解器结合起来,从而可以在Fluent求解器中把入口速度指定为抛物线形。 此例的C源代码如下:
//********************************************************// //***********抛物线入口速度的定义*************************// //********************************************************// #include”udf.h” //这里一定要调用这个头文件 DEFINE_PROFILE(velocity_inlet,thread,position)
{
real x[ND_ND]; //定义质心的坐标所在的数组 real y; //定义质心y坐标变量 face_t f; //定义face_t类型的变量f
begin_f_loop(f,thread) {
F_CENTROID(x,f,thread); //从Fluent函数得到各网格质心坐标,并且赋给矢量x y=x[1]; //x[0]代表质心的横坐标,x[1]代表质心的纵坐标 F_PROFILE(f,thread,position)=0.5-y*y/(0.02*0.02)*0.5; //定义抛物线速度 }
end_f_loop(f,thread) }
杂=待整理
宏定义
1.DEFINE_ON_DEMAND 异步执行,自动存储温度函数 2.DEFINE_ADJUST 在整个区域对湍流离散率进行积分
3.DEFINE_ADJUST 自定义一个标量是另外一个自定义标量的函数 4.DEFINE_INIT 初始化流场变量
5.DEFINE_RW_FILE 将自定义函数写入data文件中再读出 6.DEFINE_DELTAT 更改时间步长
7.DEFINE_DIFFUSIVITY 应用自定义标量计算空气的平均扩散率 8.DEFINE_HEAT_FLUX 在 P-1辐射模型中的应用(10.5.2)
9.DEFINE_NOX_RATE 计算NOx的产生率和reduction rates (4.3.4) 10.DEFINE_PROFILE 根据压力函数产生压力剖面(4.3.5
11.DEFINE_PROFILE 自定义x方向速度剖面,湍流动能,离散率(4.3.5) 12. DEFINE_PROFILE 叶轮计算中用来fix flow variables
13. DEFINE_PROPERTY 自定义粘度
14.DEFINE_SCAT_PHASE_FUNC 多个UDF连接在一个源程序中(4.3.7)
15.DEFINE_SOURCE 自定义源项(4.3.8) 16.DEFINE_SR_RATE 自定义表面反应速率
17.DEFINE_TURB_PREMIX_SOURCE 在预混燃烧模型中自定义湍流火焰速度和源项 18.DEFINE_TURBULENT_VISCOSITY 标准k-e模型中自定义湍流粘度 19.DEFINE_UDS_FLUX 返回给定面上的质量流率
20.DEFINE_UDS_UNSTEADY 修改自定义的标量time derivatives 21.DEFINE_VR_RATE 定义体积反应率
22.DEFINE_VR_RATE 涉及到多个反应时的应用
23.DEFINE_CAVITATION_RATE 多相混合物中液相和气相之间的传质(4.4.1) 24.DEFINE_EXCHANGE_PROPERTY 自定义Syamlal drag law (4.4.2) 25.DEFINE_VECTOR_EXCHANGE_PROPERTY 两相混合时自定义滑移速度 26.DEFINE_DPM_BODY_FORCE 计算磁力magnetic force 27. DEFINE_DPM_DRAG 计算颗粒的曳力drag force 28.DEFINE_DPM_EROSION 壁面作用的后处理
29.DEFINE_DPM_INJECTION_INIT 初始化颗粒的粒径
30.DEFINE_DPM_LAW 自定义 the evaporation swelling of particles的定律 31.DEFINE_DPM_OUTPUT 计算沿粒子轨迹的熔融指数melting index 32.DEFINE_DPM_PROPERTY 两种离散相物理特性连接在一个C程序中 33.DEFINE_DPM_SOURCE 应用评判标准在不同的 DPM laws 中转化 34.sub_domain_loop 对某一特定相体积分数赋初值 35.Get_Domain 在单相中的应用
36.Lookup_Thread 找到指向给定区域thread的指针,此指针用于begin_f_loop and F_CENTROID
菜鸟学UDF的感觉,希望对UDFers有用。
光看书,感觉UDF不难。看例子,有些看个四五遍之后才能差不多看懂。原来,得靠UDF帮助。我主要用的是fluent v6.3自带的html格式的帮助,里面东西很全,当然也包括UDF Manual。里面自带的search功能相当好,只是要注意用好+或-号(逻辑符号),另外,这个
功能似乎有些浏览器支持不太好,不过基本上用IE不太容易出问题。
对于从零开始学习UDF,建议还是先看一下UDF中文帮助,我估计大家知道的都是马世虎翻译的那本吧,感觉挺好。(没想到马世虎跟我是校友,去年给安世亚太投过一份简历,他给我打过电话,当时一阵兴奋,呵呵。)
对于只涉及到边界条件或物性等的UDF,一般用interpret就可以的,这些我觉得只需要根据例子改一下就是了。
$$ 对于要添加UDS方程的,相对难一点。我编程用的是三到五个UDS,几十个UDM。一开始编程时,没有头绪,后来看别人编的,才慢慢发现了一些基本思路。比如,可以用枚举定义UDS或UDM,这样用起来方便。 enum{ NP,
RHOH2O_Y_UP_X, RHOH2O_Y_UP_Y, RHOH2O_Y_UP_Z, N_REQUIRED_UDS };//枚举UDS变量名
对于UDM,则用N_REQUIRED_UDM代表个数。
然后在INIT与ADJUST函数中,检查变量个数时则比较方便,如: DEFINE_INIT(init_parameter,domain) {
if (n_uds < N_REQUIRED_UDS)
Error(”Not enough user defined scalars!(init)\\n”); if (n_udm Error(”Not enough user defined memories(init)!\\n”); initialise(domain);//代表初始化 } DEFINE_ADJUST(adjust_compute,domain) { if (n_uds < N_REQUIRED_UDS) Error(”Not enough user defined scalars!(adjust)\\n”); if (n_udm Error(”Not enough user defined memories(adjust)!\\n”); update_parameter(domain);//代表主函数 } 初始化时,则可: cell_t c; Thread *t; int i; thread_loop_c(t,d) { if(NNULLP(THREAD_STORAGE(t,SV_UDS_I(NP)))&&NNULLP(THREAD_STORAGE(t,SV_UDS_I(NP_R)))) //为各UDS提供存储空间 { begin_c_loop(c, t) { for (i=0; i end_c_loop(c, t); } if(NNULLP(THREAD_STORAGE(t,SV_UDM_I))) { begin_c_loop(c, t) { for (i=0; i end_c_loop(c, t); } } 对于各UDM量,则可: real udm_v; udm_v=0;//用之前对变量进行初始化 …//UDM相关运行 C_UDMI(c,t,UDM_V)=udm_v;//把值输入给UDM,当然之前要对UDM_V进行定义 用UDM有个好处,一是可以在后处理中显示,二是传递变量相当方便,比如在ADJUST中计算的量用于源项或对流项等,用UDM可以直接调用。 对于invalid number错误,很多时候是因为分母为零,如果习惯UDM初始化为零,则要注意避免零作分母,可以令其初始化不为零或为零时不运算(第二种方法比较好)。 方程与计算 我编程计算的是两相流中一相凝结成核,需要用UDS方程来模拟其成核有关变量(不要来问我程序代码,呵呵)。我觉得对UDS变量控制方程搞清楚之后,这块一点儿也不难。一般变量的控制方程(Fluent能认识的),就是含有瞬态项(时间项),对流项,扩散项与源项。(方程如何处理fluent会自己弄的)对第一项,都有相应的宏来处理。 对于对流项,比如关于phi的方程中的:rho*U*phi的散度,其中U为速度矢量,则fluent中需要知道的对流项则为rho*U.A,其中U.A代表U与A的点积,A代表单元格的面积向量。对流是对面而言(2D的话则对线而言),对于边界,只有一边有单元格,而对于内部surface,则两边都有单元格,这时单元格编号从0到1。比如,对于内部边界,代码可以与下面类似: real NV_VEC(psi),NV_VEC(A),flux1;//声明向量操作 c0=F_C0(f,t); t0=F_C0_THREAD(f,t); F_AREA(A,f,t);//A的获得 c1=F_C1(f,t); t1=F_C1_THREAD(f,t); NV_D(psi,=,C_UDMI(c0,t0,UPX),C_UDMI(c0,t0,UPY),C_UDMI(c0,t0,UPZ)); NV_D(psi,+=,C_UDMI(c1,t1,UPX),C_UDMI(c1,t1,UPY),C_UDMI(c1,t1,UPZ)); flux1=NV_DOT(psi,A)/2.0; 对于边界上,则可以只用单元格c0,t0处的值或用f,t处的值(代表直接在边界面上取值,前提是边界上有存储值)。 对于扩散项,一般比较简单,直接用宏DEFINE_DIFFUSIVITY定义扩散系数即可。注意它在fluent软件中的加入方式,一般是在material菜单中。species中加入的扩散项与UDS扩散项的加入不在同一处。 对于源项,如果不容易线性化,不如索性定义dS[eqn]=0,这样倒简单。 其它 一开始学fluent时,把松弛因子设为0.1就感觉挺小了。没想到刚用UDF计算时,得从1e-5开始,慢慢调到1e-1数量级上。想想挺可怕的。另外,很多CFD或多相流的东西都是理论与经验数据结合构成的公式,不一定完全准确,再加上数值解法的多样性,使算题时容易出各种各样的问题。而且UDF有个特点,不能单独运行的,调试也必须放到fluent中,这样当要加入的宏比较多时,就会变得很麻烦,常常一天要建十几个library,然后分别选择不同的宏加载到fluent,然后就是不停的调试。记得刚开始,我跟我师姐一块,她主要负责物理理论,我主要负责编程,经常因为一个小问题,要调试上大半天,因为编程中,任何一个小问题也必须解决掉。当然,也经常烦得很,还好,男女搭配干活不太累。 我觉得visual assistX这个软件相当不错,它是为了方便vc环境下的编程。而用udf编程时,大家感觉不爽的是,没有提示能力,因为它的宏VC环境下是不认识的,所以一片黑颜色不好看,主要的是不利于查错与写代码。而用visual assistX(网上有破解版的),将fluent udf常用的几个头方便的目录加入visual assistX中后,就方便多了。 比如,输入C_时,可能就会提示C_UDMI,C_UDSI,C_U,C_R等,因为以C开头的宏太多了,所以只提示最近用过的几个。 又比如,各种枚举量,各种宏等,这时都会以不同的颜色表示,一般常见的拼写错误,一下子就能看出来,因为颜色不同,方便多了。visual assistX本身也带有spell check功能,不过最好关掉,因为我们定义变量时常常不是以完整的单词命名,而这时常常很多代码下面都有浅浅的红色的波浪线,很是不爽。 又如,visual assistX提供查询功能,比如,某个宏或某个系统自带的变量名等,会自动显示该名称的出处,点一下go即可查得源文件。这样可以很方便地查询自己不认识的宏或变量等。 一开始编程时常犯一个错误,就是这样写:powl(C_U(c,t),1/3),实际应该写为powl(C_U(c,t),1.0/3),因为1/3默认是int型的,所以值为0,显然不是我想要的。 一开始用fluent6.1编程,它对于某个UDF面板处一般只允许一个宏的存在,所以,当你改了一下代码,重新build一个library后,宏会默认地替换掉原来的,这样,对调试是有好处的。后来用fluent6.3,它允许一个地方加入多个宏,这样,build一个新的library后,需要自己改相关的宏(不变的宏可以不改),一开始觉得这样不如6.1,慢慢发现,它可以同时使用几个不同的library,比如某个量,可以用A方法求一会儿后再用B方法求,总之可以调的东西多了。 感觉,UDF编程除了需要专业知识与一定的C语言基础,更需要耐心。 时间项例子 #include \ //这里一定要调用这个头文件 DEFINE_PROFILE(velocity_inlet,thread,index) { real x[ND_ND]; //定义质心的坐标所在的数组 float t; face_t f; //定义face_t类型的变量 begin_f_loop(f,thread) { F_CENTROID(x,f,thread); //从Fluent函数得到各网格质心坐标,并且赋给矢量x t = RP_Get_Real(\ F_PROFILE(f,thread,index)=0.5+0.5*t; //定义抛物线速度 } end_f_loop(f,thread) }