注:
本文所需的所有文件包含python代码、UDF代码、cas和dat文件、gif图文件都附在文末的链接中
1. 云图表情包说明
本云图表情包有一定的局限性,即只适用于黑白线条的表情包,在云图中红色表示黑色,蓝色表示白色。这是因为云图颜色从蓝色到红色变化,而表情包颜色可能RGB全覆盖。-我不知道怎么能在云图中显示RGB颜色。
当然,我尝试过RGB直接相加,确实能得到有颜色的云图,但结果却像热成像照片。
2. 云图表情包生成思路
这个云图的生成其实有几种方法,使用profile文件也可实现部分的效果。但最终确定比较简单的实现方式还是python结合UDF。
思路与步骤:我们都知道动图是由很多图片叠加实现的,云图表情包也是这个原理。
2.1获得表情包的关键帧
首先我们需要获得表情包的关键帧,这一操作可以通过WPS的图片编辑器实现。
通过WPS图片编辑器将表情包的gif文件生成很多图片。
2.2python识别像素点
这一操作由python来完成。通过python将表情包的图片文件识别成像素点,并将每个图片的像素点写入一个txt数据。
比如一个表情包gif图片可以生成20张png图片,那么通过python就可以生成20个profile文件。每个proflie代表一张图片的像素点信息
2.3UDF动态导入像素点数据
我们已经获取了图片的像素点信息了,现在需要将这些信息导入Fluent。如何导入呢?可以通过profile和UDF两种方式。但是UDF方式更加简单且流程化。
为了实现动画效果,我们必须在每迭代步都导入一个profile,然后强制让温度或者其他变量根据像素点信息改变数值。每个迭代步画一张图,20张图就20个迭代步,连接起来就会实现动画效果了。
2.4Fluent动画制作
就是很常规的Fluent生成动画操作,在生成动画之前,要注意必须要勾选能量方程,然后在control里面不选择能量方程。
这样做的目的是不让Fluent自带的求解器影响到生成的图片。
3. 表情包案例步骤
上面是整个思路和步骤,下面我们来通过一个例子详细详解。
3.1 获得表情包的关键帧
首先搜索表情包,然后保存下来
用WPS打开表情包gif图,点击保存所有帧
可以在表情包图片所在的文件夹下看到所有帧的文件夹,里面是每帧的图片。这就是我们要生成云图动画的源图片
3.2 python识别像素点
基本逻辑:python读取图片像素,如果像素颜色小于某个值,则记录此处的坐标,否则,不记录。
下面为完整的python代码:
from PIL import Image
import matplotlib.pyplot as plt
def get_coordinate(path):
# 打开图片
image = Image.open(path)
width, height = image.size
length_x=4 #Fluent中模型尺寸,x方向,根据云图表情包尺寸调整
length_y=3 #Fluent中模型尺寸,y方向,根据云图表情包尺寸调整
x_list=[] #x坐标
y_list=[] #y坐标
out = image.convert("RGB")
num=0
for i in range(width):
for j in range(height):
num+=1
if (out.getpixel((i, j)) < (50, 50, 50)): #RGB数小于次数值,值越大可捕捉更多的像素
x=i/width*length_x
y=(height-j)/height*length_y
if (num%2==0): #跳过多少数据
x_list.append(x)
y_list.append(y)
return x_list,y_list
def write_file(x_list,y_list,path,UDF=0,draw=1): # 打开文件用于写入,如果文件不存在则创建
if(UDF==1): #如果使用UDF,则UDF=1,生成UDF使用的txt数据,否则生成profile文件
with open(path, 'w') as file:
for i,x in enumerate(x_list):
file.write("{:.5f} {:.5f}\n".format(x,y_list[i]))
if(draw==1):#是否显示图片,1表示显示
plt.scatter(x_list, y_list, marker='o')
plt.grid(True)
plt.xlabel('1st')
plt.ylabel('2nd')
plt.title('Scatter Plot')
plt.show()
if __name__ == '__main__':
"""----------------------------系统动图------------------------"""
MAX_DATA_POINTS=[]
for i in range(1,141):#141表示帧数,根据实际情况更改
path=r"G:\ANSYS Test\wechat\BIG\Chapter94\gif\12\12\\"+"12_wps图片_{:d}.jpg".format(i)#帧图片地址,根据实际情况更改地址
x_list,y_list=get_coordinate(path)
print("The number of data item:{:d}".format(len(x_list)))
MAX_DATA_POINTS.append(len(x_list))
path1 = r"G:\ANSYS Test\wechat\BIG\Chapter94\gif\12\12\\" + "{:d}.txt".format(i)#UDF文件地址,根据实际情况更改地址,保存和帧图片地址相同即可
if i % 50==0: #每隔多少次画一次图
draw =1
else:
draw=0
write_file(x_list,y_list,path1,UDF=1,draw=draw)
print(MAX_DATA_POINTS)
可以分为四个主要步骤:图片处理、坐标提取、数据导出、可视化。整体流程如下:
a. 图片处理和打开
代码从指定的图片路径中逐帧加载图像。使用 PIL.Image.open 函数打开图片,转换为 RGB 格式。
b. 坐标提取
通过遍历图片的每一个像素,提取颜色值较暗的像素(RGB值低于设定的阈值 (50, 50, 50))。
c. 数据导出
对每一帧图像处理后,提取到的坐标数据可以根据需要选择保存为UDF格式:如果 UDF=1,将坐标以文本格式保存,用于Fluent的自定义用户定义函数 (UDF)。
d. 可视化
每隔50帧,代码会根据参数 draw=1,调用 matplotlib.pyplot 绘制并显示一张散点图,展示提取出的坐标数据,便于可视化验证。
e. 主程序控制流程
主函数处理 141 帧图片,每一帧都会调用 get_coordinate 函数提取坐标,并调用 write_file 保存数据。
图片帧地址和输出文件的路径根据每一帧图片编号动态生成,最终每帧的UDF数据以 .txt 文件保存。
经过上面的操作之后,会生成大量的像素坐标txt文件,一张图片生成一个,这些文件即是UDF需要读取的文件。
需要调整的代码行数为:8、9、17、20、43、44、48、49
3.3 UDF动态导入像素点数据
下面就需要UDF读取这些txt文件
基本逻辑:每个迭代步读取一次txt文件,然后对整个计算域网格进行遍历循环,判断txt文件坐标是否满足当前的网格坐标位置,如果满足则改变温度;下个迭代步再次读取下一个txt文件。
下面为完整的UDF代码:
DEFINE_EXECUTE_AT_END(on_demand_calc)
{
cell_t c;
Domain *d;
Thread *t;
d = Get_Domain(1);
real xc[ND_ND];
int j = 0;
int i = 0;
//每个文件保存的像素点个数,根据情况更改
int MAX_DATA_POINTS[140] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3002, 4466, 6010, 7999, 8949, 10528, 11381, 11364, 11769, 11775, 11792, 11767, 11775, 11767, 11767, 11767, 11767, 11767, 11767, 11767, 11767, 11767, 11767, 11767, 11767, 11767, 11767, 11767, 11767, 11767, 12440, 14092, 15473, 17368, 18150, 20611, 21697, 23227, 23785, 24214, 24214, 24203, 24206, 24214, 24216, 24216, 24216, 24216, 24216, 24216, 24216, 24216, 24216, 24216, 24216, 24431, 26627, 28899, 30957, 31855, 33930, 35290, 36923, 38362, 40050, 40983, 42576, 43869, 45743, 46371, 46594, 46771, 46754, 46771, 46754, 46754, 46754, 46754, 46754, 46754, 46754, 46754, 46754, 46754, 46754, 46754, 46754, 46754, 46754, 46754, 46754, 48170, 49763, 51614, 52923, 54112, 55552, 57157, 58301, 58559, 58550, 58559, 58559, 58559, 58559, 58559, 58559, 58559, 58559, 58559, 58559, 58559, 58559, 58559, 58559, 58559, 58559, 58559, 58559, 58559, 58559 };
real *data_x = NULL;
real *data_y = NULL;
//// 动态分配内存
data_x = (real*)malloc(sizeof(real) * MAX_DATA_POINTS[N_ITER-1]);
data_y = (real*)malloc(sizeof(real) * MAX_DATA_POINTS[N_ITER-1]);
char filename[] = "G:/ANSYS Test/wechat/BIG/Chapter94/gif/12/12/";//数据点txt文件
char index[20];
sprintf(index, "%d", N_ITER);//int类型转换为字符串类型
strcat(filename, index);//字符串拼接
strcat(filename, ".txt");//字符串拼接
FILE *fp = NULL;
fp = fopen(filename, "r");
if (fp == NULL)
{
Message("Error: Cannot open file %s\n", filename);//如果读取文件失败,则输出信息
return;
}
else
{
Message("Successfully opened file %s %d \n", filename, N_ITER);//读取成功,输出文件名
}
while ((fscanf(fp, "%lf %lf", &data_x[i], &data_y[i]) != EOF && i < MAX_DATA_POINTS[N_ITER-1]))//读取数据
{
//Message("Value %d: %lf %lf\n", i + 1, data_x[i], data_y[i]);
i++;
}
fclose(fp);
thread_loop_c(t, d)
{
begin_c_loop(c, t)
{
C_T(c, t) = 100;
C_CENTROID(xc, c, t);
for (j = 0; j < MAX_DATA_POINTS[N_ITER-1]; j++)//循环遍历一组数据
{
if (((xc[0] - data_x[j])*(xc[0] - data_x[j]) + (xc[1] - data_y[j])*(xc[1] - data_y[j])) < 0.0001)//判断是否满足位置,此值越小,则图形越细,可调整
{
C_T(c, t) = 1000;//满足像素点位置,令温度为1000K
}
}
}
end_c_loop(c, t)
}
//// 释放内存
free(data_x);
free(data_y);
}
UDF的代码逻辑很简单,要更改的代码:
a. 14行的数组,数组个数为txt文件个数,数组值为python代码的MAX_DATA_POINTS的值。
b. 20行的txt文件地址,你保存的txt文件地址即可,要注意不要有中文路径
3.4 Fluent中的操作
a
打开Fluent,读取cas和dat
打开Fluent,读取cas和dat,cas和dat最基本二维矩形计算域就可以,这里给大家提供一个。
b
编译UDF
c
不计算能量方程
d
标准初始化
e
生成温度云图动画
f
迭代步数改成帧数
i
计算过程
g
最后生成动画
通过百度网盘分享的文件:Chapter94
链接:https://pan.baidu.com/s/1--r4CGH94ef-9SIyGbFceQ?pwd=k2us
提取码:k2us
来源:Fluent学习笔记