Fluent UDF中调用变量的梯度及其注意点
Fluent UDF中有时候需要调用变量的梯度,例如温度梯度,压力梯度,VOF梯度等等,一般是在C_T,C_P,C_VOF后面加上“_G”来获取,例如C_T_G,C_VOF_G。看似简单,实际上里面有比较多的“坑”,现以如下实例来提请各位看官注意。
首先,我们利用VC++ UDF Studio插件(https://vcudfstudio.github.io)启动Fluent,然后再点击Fluent中的“启动Visual Studio”菜单,这样我们就可以在Visual Studio中输入源码并编译UDF了。



在Visual Studio项目中的udf_source.cpp文件中输入如下源码。
#include "udf.h" #include "SuperUdfExtension.h" //VC++ UDF Studio自带的扩展库头文件,具体参考该软件中的编程手册 #pragma comment(lib, "SuperUdfExtension.lib") //VC++ UDF Studio自带的扩展库的lib文件 int GetZoneIdByName(CString zoneName); //函数声明 DEFINE_ADJUST(show_gradient, domain) { face_t f; real T_gradient[ND_ND]; int theInletID = GetZoneIdByName("inlet"); //根据边界名字获取其ID,如果返回-1表示找不到 if (-1 == theInletID) { Message("Cannot find the boundary name 'inlet'. Please modify!\n"); return; } Thread * wall_thread = Lookup_Thread(domain, theInletID); //根据inlet的ID获得其Thread Thread *fluid_thread = THREAD_T0(wall_thread); //获得和边界face邻接的cell的thread begin_f_loop(f, wall_thread) //对inlet的面进行循环 { cell_t c0 = F_C0(f, wall_thread); //获得和inlet面邻接的cell NV_V(T_gradient, =, C_T_G(c0, fluid_thread)); //将温度梯度赋值给T_gradient Message("c=%d, Temperature X gradient is %g\n", c0, T_gradient[0]); //打印x方向的梯度 } end_f_loop(f, wall_thread) } int GetZoneIdByName(CString zoneName) //适用于所有Fluent版本 { int returnID = -1; Domain*domain = Get_Domain(1); CString strCurrentFluentVersion; strCurrentFluentVersion.Format("%d.%d", RampantReleaseMajor, RampantReleaseMinor);//格式化当前Fluent版本为字符串形式 double fCurrentFluentVersion = atof(strCurrentFluentVersion.GetBuffer()); //当前Fluent版本转为double类型 if (fCurrentFluentVersion <= 19.2) // 对于Fluent6.3-19.2,只能调用VC++ UDF Studio扩展库 { SuperUdf_Initialize(AfxGetInstanceHandle()); //调用VC++ UDF Studio扩展库中任何函数之前必须调用此初始化函数,具体参考该软件中的编程手册 #if !RP_NODE returnID = SuperUdf_GetZoneIdByName(zoneName.GetBuffer()); //调用VC++ UDF Studio扩展库中的SuperUdf_GetZoneIdByName函数,具体参考该软件中的编程手册 #endif host_to_node_int_1(returnID); } else // 对于Fluent version >=19.3,有直接UDF函数可以实现 { Thread*tf; thread_loop_f(tf, domain) //对所有面的thread进行循环查找 { if (0 == zoneName.CompareNoCase(THREAD_NAME(tf))) //对比名字是否相同 { returnID = THREAD_ID(tf); break; } } if (-1 == returnID) //如果面的thread中无法找到匹配名字 { Thread*tc; thread_loop_c(tc, domain) //对所有网格的thread进行循环查找 { if (0 == zoneName.CompareNoCase(THREAD_NAME(tc))) //对比名字是否相同 { returnID = THREAD_ID(tc); break; } } } } return returnID; }
以上程序是想要在每次迭代之前显示x方向的温度梯度的值。其中,GetZoneIdByName函数是根据边界的名字来获得其ID,可以参考博文《Fluent UDF中根据zone的名字获取ID》(https://www.cnblogs.com/SuperUDF/articles/15886289.html)。
注意:请确保有名字为inlet的入口边界,否则加载UDF的时候就会报告“Cannot find the boundary name 'inlet'. Please modify!”,说明找不到名字为inlet的边界。
源码输入完毕以后,点击“编译UDF”按钮就可以编译UDF了,编译成功后点击“UDF库加载到Fluent”按钮就可以加载UDF库了。

加载成功以后,我们就需要手动将DEFINE_ADJUST宏hook到Fluent。这样才能每次迭代之前执行我们的Adjust宏。


初始化然后开始迭代计算,但是计算第一步就直接报错,说明我们已经顺利“入坑”。

然而,面对这个毫无头绪的错误提示,可能连坑在哪里都不知道,那怎么办呢?可以启用VC++ UDF Studio插件的调试功能。即在宏内第一行鼠标右键然后菜单选择“Insert Breakpoint”,此时该行前面就会出现一个圆球,表示断点已经插入。 一旦程序运行到该行,就会中断。点击三角形的按钮进入调试模式,然后重新初始化开始迭代计算。

开始迭代计算后,程序马上就中断在圆球的断点处,并显示一个黄色箭头,这就说明fluent执行到该行被中断暂停了(但尚未执行该行),然后我们就可以手动一步一步跟踪后面每一行是否能正确运行。

单击Debug菜单中的“Step Over”项,或者快捷键F10,就能手动执行一行程序,如果没有出现错误提示,那说明改行能正确运行,同时我们还可以在变量值显示区查看执行该行的变量值有什么变化,检查变量值是不是如我们所设想的那样,从而可以发现潜在错误。

当我们一步一步执行到C_T_G这一行时,前面那个错误就跳出来了,那就说明错误就发生在这一行,需要我们仔细考虑如何修改。

首先,我们知道这个是温度梯度,可是我们连能量方程都没有开,哪来的温度呢?所以第一坑就是要对什么方程求解什么传输变量要有理解,例如取温度梯度必须开能量方程,VOF梯度也必须开多相流模型。这样,我们先把第一个坑给填平了,打开能量控制方程。

重复前面的调试过程,然而错误提示依旧出现,而且还是这一行出问题。我们通过查阅UDF手册发现里面有这么一句话,“默认情况下求解器会不断移除梯度数据”。如果需要保留梯度数据,需要使用“solver/set/expert”的TUI命令回答“yes”来保留。原来,第二个坑在这里。

OK,那我们继续填平第二个“坑”,在控制台里面输入“solver/set/expert”的TUI命令,并对于“Keep temporary solver memory from being freed?”回答yes。

然后重复前面的调试过程,然而错误提示依旧出现,还是定位在C_T_G这一行。这个可够让人郁闷的,怎么回事?经过笔者的研究,原来这最大的“坑”是因为迭代第一步迭代之前梯度还没有建立,而adjust恰恰是每一步迭代前调用的,所以第一步迭代之前是取不到梯度的,需要迭代第二步迭代前才有值。这样,刚开始迭代第一步就跳出错误。为了避免这个问题,我们需要写一个判断语句。
if(NULL!=T_STORAGE_R_NV(fluid_thread, SV_T_G)) // 如果温度梯度已经建立,一般迭代第二步以后就有梯度值了 NV_V(T_gradient, =, C_T_G(c0, fluid_thread)); else // 如果温度梯度尚未建立,一般是迭代第一步的开始 NV_D(T_gradient,=,0,0,0);
其中,SV_T_G是温度梯度的存储序号。类似地,如果是VOF梯度,其序号为SV_VOF_G。整个语句结构的意思就是:对于梯度尚未建立的情况(一般是迭代第一步的开始),直接赋值零梯度。后面当梯度有存储值以后,就可以调用梯度了。正确的完整程序如下:
#include "udf.h" #include "SuperUdfExtension.h" //VC++ UDF Studio自带的扩展库头文件,具体参考该软件中的编程手册 #pragma comment(lib, "SuperUdfExtension.lib") //VC++ UDF Studio自带的扩展库的lib文件 int GetZoneIdByName(CString zoneName); //函数声明 DEFINE_ADJUST(show_gradient, domain) { face_t f; real T_gradient[ND_ND]; int theInletID = GetZoneIdByName("inlet"); //根据边界名字获取其ID,如果返回-1表示找不到 if (-1 == theInletID) { Message("Cannot find the boundary name 'inlet'. Please modify!\n"); return; } Thread * wall_thread = Lookup_Thread(domain, theInletID); Thread *fluid_thread = THREAD_T0(wall_thread); begin_f_loop(f, wall_thread) { cell_t c0 = F_C0(f, wall_thread); if(NULL!=T_STORAGE_R_NV(fluid_thread, SV_T_G)) // 如果温度梯度已经建立,一般迭代第二步以后就有梯度值了 NV_V(T_gradient, =, C_T_G(c0, fluid_thread)); else // 如果温度梯度尚未建立,一般是迭代第一步的开始 NV_D(T_gradient,=,0,0,0); Message("c=%d, Temperature X gradient is %g\n", c0, T_gradient[0]); } end_f_loop(f, wall_thread) } int GetZoneIdByName(CString zoneName) //适用于所有Fluent版本 { int returnID = -1; Domain*domain = Get_Domain(1); CString strCurrentFluentVersion; strCurrentFluentVersion.Format("%d.%d", RampantReleaseMajor, RampantReleaseMinor);//格式化当前Fluent版本为字符串形式 double fCurrentFluentVersion = atof(strCurrentFluentVersion.GetBuffer()); //当前Fluent版本转为double类型 if (fCurrentFluentVersion <= 19.2) // 对于Fluent6.3-19.2,只能调用VC++ UDF Studio扩展库 { SuperUdf_Initialize(AfxGetInstanceHandle()); //调用VC++ UDF Studio扩展库中任何函数之前必须调用此初始化函数,具体参考该软件中的编程手册 #if !RP_NODE returnID = SuperUdf_GetZoneIdByName(zoneName.GetBuffer()); //调用VC++ UDF Studio扩展库中的SuperUdf_GetZoneIdByName函数,具体参考该软件中的编程手册 #endif host_to_node_int_1(returnID); } else // 对于Fluent version >=19.3,有直接UDF函数可以实现 { Thread*tf; thread_loop_f(tf, domain) //对所有面的thread进行循环查找 { if (0 == zoneName.CompareNoCase(THREAD_NAME(tf))) //对比名字是否相同 { returnID = THREAD_ID(tf); break; } } if (-1 == returnID) //如果面的thread中无法找到匹配名字 { Thread*tc; thread_loop_c(tc, domain) //对所有网格的thread进行循环查找 { if (0 == zoneName.CompareNoCase(THREAD_NAME(tc))) //对比名字是否相同 { returnID = THREAD_ID(tc); break; } } } } return returnID; }
执行结果如下,可以看到第一次迭代前的梯度因为没有存储值,所以取零值,第二步开始就有具体的值了。


浙公网安备 33010602011771号