欢迎访问 生活随笔!

生活随笔

当前位置: 首页 > 编程资源 > 编程问答 >内容正文

编程问答

Fluent UDF中调用变量的梯度及其注意点

发布时间:2023/12/29 编程问答 41 豆豆
生活随笔 收集整理的这篇文章主要介绍了 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获得其ThreadThread *fluid_thread = THREAD_T0(wall_thread); //获得和边界face邻接的cell的threadbegin_f_loop(f, wall_thread) //对inlet的面进行循环{cell_t c0 = F_C0(f, wall_thread); //获得和inlet面邻接的cellNV_V(T_gradient, =, C_T_G(c0, fluid_thread)); //将温度梯度赋值给T_gradientMessage("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_NODEreturnID = SuperUdf_GetZoneIdByName(zoneName.GetBuffer()); //调用VC++ UDF Studio扩展库中的SuperUdf_GetZoneIdByName函数,具体参考该软件中的编程手册 #endifhost_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_NODEreturnID = SuperUdf_GetZoneIdByName(zoneName.GetBuffer()); //调用VC++ UDF Studio扩展库中的SuperUdf_GetZoneIdByName函数,具体参考该软件中的编程手册 #endifhost_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; }

执行结果如下,可以看到第一次迭代前的梯度因为没有存储值,所以取零值,第二步开始就有具体的值了。

 

总结

以上是生活随笔为你收集整理的Fluent UDF中调用变量的梯度及其注意点的全部内容,希望文章能够帮你解决所遇到的问题。

如果觉得生活随笔网站内容还不错,欢迎将生活随笔推荐给好友。