本文为STAR CCM+二次开发系列的最后一篇文章。
User Code 是用户自己用编译语言(例如 C,C++或 Fortran)编写的函数,用来和 Simcenter STAR-CCM+动态链接,实现标准界面无法完成的一些需求(比如自定义边界,自定义初始化等)。
下面我们通过将入口边界设置为层流充分发展边界这样一个简单的例子来介绍User Code的基本使用方法。
注:
1. 本文内容收集自网络,有少量内容因软件版本不同而进行了修改
2. 原文地址:https://www.cnblogs.com/liusuanyatong/p/122882458.html
3. 原文作者:硫酸亚铜
管道模型如下:
截面直径0.2 m。
物性参数:
管道层流入口充分发展满足下面的关系:
其中:Um表示入口的平均速度,r0表示管径
User Code的编写步骤如下:
新建一个头文件,命名为uclib.h,内容如下:
#ifndef UCLIB_H
#define UCLIB_H
#ifdef DOUBLE_PRECISION
typedef double Real;
#else
typedef float Real;
#endif
typedef double CoordReal;
#ifdef __cplusplus
extern "C" {
#endif
#if defined(WIN32) || defined(_WINDOWS) || defined(_WINNT)
# define USERFUNCTION_EXPORT __declspec(dllexport)
# define USERFUNCTION_IMPORT __declspec(dllimport)
#else
# define USERFUNCTION_EXPORT
# define USERFUNCTION_IMPORT
#endif
extern void USERFUNCTION_IMPORT ucarg(void *, char *, char *, int);
extern void USERFUNCTION_IMPORT ucfunc(void *, char *, char *);
extern void USERFUNCTION_IMPORT ucfunction(void *, char *, char *, int, ...);
void USERFUNCTION_EXPORT uclib();
#ifdef __cplusplus
}
#endif
#endif
注:上面头文件的名称和格式都是固定的,照着写就行了,不要改动。
参阅STAR CCM+文档:主页 → 用户界面 → 使用用户程序 → 用户函数接口引用 → 类型定义。
”
编写 User Code 的源码,新建一个.c 或者.cpp 文件都可以,这里新建一个 demo.cpp 文件。
内容如下:
#include <cmath>
#include "uclib.h"
const Real R = 0.1;
void USERFUNCTION_EXPORT ParabolicVelocity(Real *result, int size, CoordReal (*centroid)[3])
{
CoordReal origin[3]{};
Real r;
for (int i = 0; i != size; ++i)
{
r = sqrt((centroid[i][0] - origin[0]) * (centroid[i][0] - origin[0]) +
(centroid[i][1] - origin[1]) * (centroid[i][1] - origin[1]) +
(centroid[i][2] - origin[2]) * (centroid[i][2] - origin[2]));
result[i] = 2 * (1 - (r / R) * (r / R));
}
}
void USERFUNCTION_EXPORT uclib()
{
/*Register user functions here*/
ucfunc(ParabolicVelocity, "BoundaryProfile", "Parabolic velocity");
ucarg(ParabolicVelocity, "Face", "centroid", sizeof(CoordReal[3]));
}
上面的源码主要分为三个部分。
1、头文件
#include <cmath>
#include "uclib.h"
这部分是头文件,注意uclib.h这个头文件必须要包含,其他的都是c语言或者c++的头文件了。
2、主体函数
主体函数可写为:
void ParabolicVelocity(Real *result, int size, CoordReal (*centroid)[3])
{
CoordReal origin[3]{};
Real r;
for (int i = 0; i != size; ++i)
{
r = sqrt((centroid[i][0] - origin[0]) * (centroid[i][0] - origin[0]) +
(centroid[i][1] - origin[1]) * (centroid[i][1] - origin[1]) +
(centroid[i][2] - origin[2]) * (centroid[i][2] - origin[2]));
result[i] = 2 * (1 - (r / R) * (r / R));
}
}
主体函数的格式是固定的,其格式为:
#include "uclib.h"
void name(result, int size, args ...)
{
//用户自定义函数内容
}
其中:
name
表示函数名,名称可以随便,只要符合c或者c++的命名规则即可。Real *result
,这里可以将result理解为一个数组。对于返回值是矢量(比如速度的三个分量)的时候,result就写为real (*result)[3]
,这里可以将result理解为一个二维数组,(*result)[0]、(*result)[1]、(*result)[2]
分别是一个一维数组,存储不同的量,比如速度三个分量就分别放在三个一维数组当中。size
是前面result的维数,对应的单元(面单元,体单元)的数量,比如以本例来说,入口面有4263个面单元,那么这里的size就等于4263,那么这里的Real *result
就相当于Real result[4263]
。args...
表示不定参数,表示后面跟的参数可能不止一个,类似printf,下图将固定格式和对应例子结合说明一下。本例中args表示一个参数,当然后面还可以有更多的参数,比如:
就跟着两个参数。args声明参数的方法和对应方式参考下表
这么声明的方法,基本上和上面的result参数理解类似,比如本例我们需要获取入口面单元的坐标信息,因为前面有size参数,在本例中这个size参数等于4263,那么这里的CoordReal (*centroid)[3]
就相当于CoordReal centroid[4263][3]
。
4263表示单元数目,后面的3表示x,y,z的索引,那么centroid[0][0]
就表示第一个单元的x坐标,centroid[0][1]
就表示第一个单元的y坐标,centroid[0][2]
就表示第一个单元的z坐标,所以centroid[xxx][0]
就表示第xxx个单元的x坐标,centroid[xxx
][1]就表示第xxx个单元的y坐标,centroid[xxx
][2]就表示第xxx个单元的z坐标,其他类似的理解。
当然用户自定义函数可以不止一个,有多少个就写多少即可:
void userFunction_1(result,int size,args...)
{
}
void userFunction_2(result,int size,args...)
{
}
void userFunction_3(result,int size,args...)
{
}
void userFunction_n(result,int size,args...)
{
}
4、注册函数
如前面示例部分的代码:
void uclib()
{
/*Register user functions here*/
ucfunc(ParabolicVelocity, "BoundaryProfile", "Parabolic velocity");
ucarg(ParabolicVelocity, "Face", "centroid", sizeof(CoordReal[3]));
}
为注册函数。
注册函数的格式是固定的:
#include "uclib.h"
void uclib()
{
}
每个用户自定义函数都需要注册,每个用户自定义函数传入的不定参数也需要注册。
注册用户的自定义函数的函数格式为:
ucfunc(void *func, char *type, char *name);
其中:
func
是用户自定义函数的函数名,比如本例中我们的函数名为 ParabolicVelocitytype
必须是"BoundaryProfile"、"RegionProfile"、"ScalarFieldFunction"、"VectorFieldFunction"这四种当中的一种,比如本例中我们是设置边界条件,故选择 BoundaryProfile。name
则表示为在STAR-CCM+中模型树的Tools->User Code->libuser.dll显示名称和调用时选取的名称,比如本例中我们设置的是边界条件,那么在模型树和边界条件设置的名称就应该是这里的name,见下图注意:有多少个用户自定义函数就需要写多少个这样的变量注册用户自定义函数的函数
”
注册用户自定义函数传入的不定参数的函数格式为
ucarg(void *func, char *type, char *variable, int size);
其中:
func
参数含义与上面 ucfunc 函数中 func 参数的含义是一致的type
参数必须是"Cell"、"Face"、"Vertex"这三种当中的一种,Cell表示获取体单元的属性,Face表示获取面单元的属性,Vertex表示获取点的属性。比如本例中我们是设置边界条件,故选择Facevariable
参数必须是STAR-CCM+中可用的变量,比如温度(Temperature)、中心(Centroid)等等总结一下上面注册函数的内容,由于本例当中我们只编写了1个用户自定义函数,不定参数只传入了1个参数,所以写为下面的形式
void uclib()
{
/*Register user functions here*/
ucfunc(ParabolicVelocity, "BoundaryProfile", "Parabolic velocity");
ucarg(ParabolicVelocity, "Face", "centroid", sizeof(CoordReal[3]));
}
如果多个用户自定函数和不定参数传入多个参数的情况就参照Simcenter STAR-CCM+ User Guide当中的写法
接下来就是编译User Code
UserFunctions.lib
,如本机路径为:C:\Program Files\Siemens\18.02.008-R8\STAR-CCM+18.02.008-R8\star\lib\win64\clang11.1vc14.2-r8\lib\UserFunctions.lib
。把路径记下来,后面要用x64 Native Tools Command Prompt for VS 2022
,点击鼠标右键,选择以管理员身份运行
启动命令行对话框cd /d "C:\Users\Administrator\Desktop\MAPDL\UserCode"
进入到User Code源代码所在的目录(如本案例存储在C:\Users\Administrator\Desktop\MAPDL\UserCode
文件夹中),如下图所示cl /MD /EHsc /DDOUBLE_PRECISION /D_WINDOWS -c *.cpp
注:若STAR CCM+不是双精度版本,则不需要添加/DDOUBLE_PRECISION
”
link -dll /out:libuser.dll *.obj "C:\Program Files\Siemens\18.02.008-R8\STAR-CCM+18.02.008-R8\star\lib\win64\clang11.1vc14.2-r8\lib\UserFunctions.lib"
编译成功后在当前路径下会生成一个名为libuser.dll的文件。
上面命令汇总一下:
cl /MD /EHsc /DDOUBLE_PRECISION /D_WINDOWS -c *.cpp
link -dll /out:libuser.dll *.obj "C:\Program Files\Siemens\18.02.008-R8\STAR-CCM+18.02.008-R8\star\lib\win64\clang11.1vc14.2-r8\lib\UserFunctions.lib"
从上面的结果可以看出User Code的设置满足我们的需求。
模型、网格、User Code源码、STAR-CCM+结果文件链接:
见附件
”
(完)