本文描述并行UDF的一些限制、处理器标识以及并行文件读写操作。
注:本文内容来自Fluent UDF手册。
1 并行UDF宏限制
-
宏PRINCIPAL_FACE_P只能用于编译型UDF中 -
PRF_GRSUM1及类似的全局约简宏不能在诸如DEFINE_SOURCE和DEFINE_PROPERTY之类的宏中使用,这些宏通常在全局网格单元(或面)上调用,因此在每个计算节点上调用的次数不同的。这里提供一种变通的方法:用户可以使用在每个计算节点上只调用一次的宏,如DEFINE_ADJUST、DEFINE_ON_DEMAND和DEFINE_EXECUTE_AT_END ,如可以编写DEFINE_ADJUST UDF来计算调整函数中的全局求和值,然后将变量保存在用户定义内存(UDM)中,随后可以从用户定义的内存中检索存储的变量,并在DEFINE_SOURCE中使用它。如下所示
在下面的示例中,在DEFINE_ADJUST函数中计算spark体积,并使用C_UDMI将该值存储在用户定义的内存中。然后从用户定义的内存中检索体积并在DEFINE_SOURCE中使用。
#include "udf.h"
/* These variables will be passed between the ADJUST and SOURCE UDFs */
static real spark_center[ND_ND] = {ND_VEC(20.0e-3, 1.0e-3, 0.0)};
static real spark_start_angle = 0.0, spark_end_angle = 0.0;
static real spark_energy_source = 0.0;
static real spark_radius = 0.0;
static real crank_angle = 0.0;
DEFINE_ADJUST(adjust, domain)
{
#if !RP_HOST
const int FLUID_CHAMBER_ID = 2;
real cen[ND_ND], dis[ND_ND];
real crank_start_angle;
real spark_duration, spark_energy;
real spark_volume;
real rpm;
cell_t c;
Thread *ct;
rpm = RP_Get_Real("dynamesh/in-cyn/crank-rpm");
crank_start_angle = RP_Get_Real("dynamesh/in-cyn/crank-start-angle");
spark_start_angle = RP_Get_Real("spark/start-ca");
spark_duration = RP_Get_Real("spark/duration");
spark_radius = RP_Get_Real("spark/radius");
spark_energy = RP_Get_Real("spark/energy");
/* Set the global angle variables [deg] here for use in the SOURCE UDF */
crank_angle = crank_start_angle + (rpm * CURRENT_TIME * 6.0);
spark_end_angle = spark_start_angle + (rpm * spark_duration * 6.0);
ct = Lookup_Thread(domain, FLUID_CHAMBER_ID);
spark_volume = 0.0;
begin_c_loop_int(c, ct)
{
C_CENTROID(cen, c, ct);
NV_VV(dis,=,cen,-,spark_center);
if (NV_MAG(dis) < spark_radius)
{
spark_volume += C_VOLUME(c, ct);
}
}
end_c_loop_int(c, ct)
spark_volume = PRF_GRSUM1(spark_volume);
spark_energy_source = spark_energy/(spark_duration*spark_volume);
Message0("nSpark energy source = %g [W/m3].n", spark_energy_source);
#endif
}
DEFINE_SOURCE(energy_source, c, ct, dS, eqn)
{
/* Don't need to mark with #if !RP_HOST as DEFINE_SOURCE is only executed
on nodes as indicated by the arguments "c" and "ct" */
real cen[ND_ND], dis[ND_ND];
if((crank_angle >= spark_start_angle) && (crank_angle < spark_end_angle))
{
C_CENTROID(cen, c, ct);
NV_VV(dis,=,cen,-,spark_center);
if (NV_MAG(dis) < spark_radius)
{
return spark_energy_source;
}
}
return 0.0;
}
注:解释udf不能与infiniband互连一起使用。这种情况下应该使用编译UDF方法。
2 处理器标识
并行ANSYS Fluent中的每个计算节点都有一个唯一的整数标识符,该标识符存储为全局变量myid。当在并行UDF中使用myid时,它将返回当前计算节点(包括主机)的整数ID。host节点的ID为host(=999999),并存储为全局变量host。node-0节点的ID为0,并被分配给全局变量node_zero。下面是并行ANSYS Fluent中的全局变量列表。
int node_zero = 0;
int host = 999999;
int node_one = 1;
int node_last; /* 返回最后一个计算节点的ID值*/
int compute_node_count; /* 返回计算节点的数量 */
int myid; /*返回当前节点的ID值 */
myid通常用于并行UDF代码中的条件-if语句。下面是一些使用全局变量myid的示例代码。在本例中,首先通过累加计算面thread中面的总数。然后,如果myid不是node-0节点,则使用传递宏PRF_CSEND_INT的消息将面数从所有计算节点传递到node-0节点。
int noface=0;
begin_f_loop(f, tf) /* loops over faces in a face thread and computes number of faces */
{
noface++;
}
end_f_loop(f, tf)
/* Pass the number of faces from node 1,2, ... to node 0 */
#if RP_NODE if(myid!=node_zero)
{
PRF_CSEND_INT(node_zero, &noface, 1, myid);
}
#endif
3 并行UDF中的文件读写
计算当Fluent以并行模式运行时,计算节点可以同时对数据执行计算。但当数据从一个普通文件读取或写入该文件时,操作必须是连续的,文件必须由访问文件系统的处理器打开和读写。通常情况下,计算节点在没有磁盘空间的专用并行计算机上运行,这意味着所有的数据都必须从host节点读取和/或写入,host节点总是在具有文件系统访问权限的机器上运行,因为其需要读取和写入cas和dat文件。这意味着,现在必须将数据从所有计算节点传递给node-0节点,然后node-0节点将数据传递给host节点,再利用host节点将数据写入文件。这个过程称为“marshalling”。
3.1 读取文件
在并行UDF中读取文件之前, 要将文件从host节点复制到计算节点,可以使用以下函数:
host_to_node_sync_file(const char* filename)
这可以处理当前工作路径在host与node之间不共享时。对于host节点,输入参数filename为要复制到node节点的文件路径,而对于node节点,输入参数是复制文件的节点上的路径。函数执行完成后,host_to_node_sync_file()返回复制的字节数,否则返回-1。
在下面的示例中,Windows上的host节点将文件从其本地目录e:udfstest.bat复制到远程Linux节点上的目录/tmp。
DEFINE_ON_DEMAND(host_to_node_sync)
{
#if RP_HOST
int total_bytes_copied = host_to_node_sync_file("e:\udfs\test.dat.h5");
#endif
#if RP_NODE
int total_bytes_copied = host_to_node_sync_file("/tmp");
#endif
printf("Total number of bytes copied is %dn", total_bytes_copied);
}
3.2 写入文件
在并行模式下写入文件过程较为复杂,其基本步骤为:
-
计算节点将数据发送给node-0节点 -
node-0节点将数据发送给host节点 -
host节点打开文件 -
host节点将数据写入文件 -
host节点关闭文件
下面的示例描述了文件写入过程。
#include "udf.h"
# define FLUID_ID 2
DEFINE_ON_DEMAND(pressures_to_file)
{
/* Different variables are needed on different nodes */
#if !RP_HOST
Domain *domain=Get_Domain(1);
Thread *thread;
cell_t c;
#else
int i;
#endif
#if !RP_NODE
FILE *fp = NULL;
char filename[]="press_out.txt";
#endif
int size; /* data passing variables */
real *array;
int pe;
#if !RP_HOST
thread=Lookup_Thread(domain,FLUID_ID);
#endif
#if !RP_NODE
if ((fp = fopen(filename, "w"))==NULL)
Message("n Warning: Unable to open %s for writingn",filename);
else
Message("nWriting Pressure to %s...",filename);
#endif
/* UDF Now does 2 different things depending on NODE or HOST */
#if RP_NODE
/* Each Node loads up its data passing array */
size=THREAD_N_ELEMENTS_INT(thread);
array = (real *)malloc(size * sizeof(real));
begin_c_loop_int(c,thread)
array[c]= C_P(c,thread);
end_c_loop_int(c,thread)
/* Set pe to destination node */
/* If on node_0 send data to host */
/* Else send to node_0 because */
/* compute nodes connect to node_0 & node_0 to host */
pe = (I_AM_NODE_ZERO_P) ? host : node_zero;
PRF_CSEND_INT(pe, &size, 1, myid);
PRF_CSEND_REAL(pe, array, size, myid);
free(array);/* free array on nodes after data sent */
/* node_0 now collect data sent by other compute nodes */
/* and sends it straight on to the host */
if (I_AM_NODE_ZERO_P)
compute_node_loop_not_zero (pe)
{
PRF_CRECV_INT(pe, &size, 1, pe);
array = (real *)malloc(size * sizeof(real));
PRF_CRECV_REAL(pe, array, size, pe);
PRF_CSEND_INT(host, &size, 1, myid);
PRF_CSEND_REAL(host, array, size, myid);
free((char *)array);
}
#endif /* RP_NODE */
#if RP_HOST
compute_node_loop (pe) /* only acts as a counter in this loop */
{
/* Receive data sent by each node and write it out to the file */
PRF_CRECV_INT(node_zero, &size, 1, node_zero);
array = (real *)malloc(size * sizeof(real));
PRF_CRECV_REAL(node_zero, array, size, node_zero);
for (i=0; i fprintf(fp, "%gn", array[i]);
free(array);
}
#endif /* RP_HOST */
#if !RP_NODE
fclose(fp);
Message("Donen");
#endif
}
并行UDF的编写要远比串行程序复杂,在工程应用时一定要仔细设计。大多数串行代码在并行模式下也能够很好的工作,千万不要以为把串行代码改造成了并行代码就一定能够提高计算效率。
本篇文章来源于微信公众号: CFD之道
评论前必须登录!
注册