udf 中文手冊 ANSYS FLUNT
第二章.UDF的C語言基礎
?(翻譯不易,辛苦點贊)
?
本章介紹了UDF的C語言基礎
2.1引言
2.2注釋你的C代碼
2.3FLUENT中的C數(shù)據(jù)類型
2.4常數(shù)
2.5變量
2.6自定義數(shù)據(jù)類型
2.7強制轉換
2.8函數(shù)
2.9數(shù)組
2.10指針
2.11聲明
2.12常用C操作符
2.13C庫函數(shù)
2.14用#define實現(xiàn)宏置換
2.15用#include實現(xiàn)文件包含
2.16與FORTRAN比較
?
2.1引言
本章介紹了C語言的一些基本信息,這些信息對處理FLUENT的UDF很有幫助。本章首先假定你有一些編程經(jīng)驗而不是C語言的初級介紹。本章不會介紹諸如while-do循環(huán),聯(lián)合,遞歸,結構以及讀寫文件的基礎知識。如果你對C語言不熟悉可以參閱C語言的相關書籍。
2.2注釋你的C代碼
熟悉C語言的人都知道,注釋在編寫程序和調(diào)試程序等處理中是很重要的。注釋的每一行以“/*”開始,后面的是注釋的文本行,然后是“*/”結尾
如:/* This is how I put a comment in my C program?? */
2.3FLUENT的C數(shù)據(jù)類型
FLUENT的UDF解釋程序支持下面的C數(shù)據(jù)類型:
Int:整型
Long:長整型
Real:實數(shù)
Float:浮點型
Double:雙精度
Char:字符型
注意:UDF解釋函數(shù)在單精度算法中定義real類型為float型,在雙精度算法宏定義real為double型。因為解釋函數(shù)自動作如此分配,所以使用在UDF中聲明所有的float和double數(shù)據(jù)變量時使用real數(shù)據(jù)類型是很好的編程習慣。
2.4常數(shù)
??????? 常數(shù)是表達式中所使用的絕對值,在C程序中用語句#define來定義。最簡單的常數(shù)是十進制整數(shù)(如:0,1,2)包含小數(shù)點或者包含字母e的十進制數(shù)被看成浮點常數(shù)。按慣例,常數(shù)的聲明一般都使用大寫字母。例如,你可以設定區(qū)域的ID或者定義YMIN和YMAX如下:#define WALL_ID 5#define YMIN 0.0#define YMAX 0.4064
2.5變量
變量或者對象保存在可以存儲數(shù)值的內(nèi)存中。每一個變量都有類型、名字和值。變量在使用之前必須在C程序中聲明。這樣,計算機才會提前知道應該如何分配給相應變量的存儲類型。
2.5.1聲明變量
變量聲明的結構如下:首先是數(shù)據(jù)類型,然后是具有相應類型的一個或多個變量的名字。變量聲明時可以給定初值,最后面用分號結尾。變量名的頭字母必須是C所允許的合法字符,變量名字中可以有字母,數(shù)字和下劃線。需要注意的是,在C程序中,字母是區(qū)分大小寫的。下面是變量聲明的例子:
int n;?????????????????? /*聲明變量n為整型*/int i1, i2;????????????? ??/*聲明變量i1和i2為整型*/float tmax = 0.;???????? ??/* tmax為浮點型實數(shù),初值為0 */real average_temp = 0.0; ??/* average_temp為實數(shù),賦初值為0.1*/
2.5.2局部變量
局部變量只用于單一的函數(shù)中。當函數(shù)調(diào)用時,就被創(chuàng)建了,函數(shù)返回之后,這個變量就不存在了,局部變量在函數(shù)內(nèi)部(大括號內(nèi))聲明。在下面的例子中,mu_lam和temp是局部變量。
DEFINE_PROPERTY(cell_viscosity, cell, thread){? real mu_lam;? real temp = C_T(cell, thread);?? if (temp > 288.)??? mu_lam = 5.5e-3;? else if (temp > 286.)??? mu_lam = 143.2135 - 0.49725 * temp;? else??? mu_lam = 1.;?? return mu_lam;}
2.5.3全局變量
全局變量在你的UDF源文件中是對所有的函數(shù)都起作用的。(調(diào)用一個UDF源文件可能會包括一系列的連接函數(shù)。)它們是在單一函數(shù)的外部定義的。全局變量一般是在預處理程序之后的文件開始處聲明。
2.5.4外部變量
如果全局變量在某一源代碼文件中聲明,但是另一個源代碼的某一文件需要用到它,那么你必須在另一個文件中聲明它是外部變量。外部變量的聲明很簡單,你只需要在變量聲明的最前面加上extern即可。如果有幾個文件涉及到該變量,最方便的處理方法就是在頭文件(.h)中加上extern的定義,然后在所有的.c文件中引用該頭文件即可。只有一個.c文件應該包括沒有extern關鍵字的變量聲明,如下所示。注意:extern只用于編譯過的UDF。
例子:
/* filea.h? *//*包含外部定義的頭文件*/extern real volume;/* filea.c? *//*調(diào)用頭文件filea.h中聲明的volumn的C函數(shù)*/#include "udf.h"#include "filea.h"real volume;DEFINE_ADJUST(compute_volume, domain){?? /*計算某些區(qū)域volumn的代碼*/?? volume = ....}/* fileb.c? *//*調(diào)用頭文件filea.h中聲明的volumn的另一個C函數(shù)*/#include "udf.h"#include "filea.h"DEFINE_SOURCE(heat_source,c,t,ds,eqn){?? /* 用總數(shù)來計算每個單位體積的源項的代碼*/?? /*fliea.c的compute_volum計算出的volume*/?? real total_source = ...;?? real source;?? source = total_source/volume;?? return source;}
2.5.5靜態(tài)變量
static聲明對于全局變量和局部變量的影響是不一樣的。靜態(tài)局部變量在函數(shù)調(diào)用返回之后,該變量不會被破壞。靜態(tài)全局變量則在定義該變量的.c源文件之外對任何函數(shù)保持不可見。靜態(tài)聲明也可以用于函數(shù),使該函數(shù)只對定義它的.c源文件保持可見。下面是靜態(tài)全局變量聲明的例子。注意:extern只用于編譯過的UDF。
例子:
#include "udf.h"static real abs_coeff = 1.0;?? /* 吸收系數(shù)*/real source;DEFINE_SOURCE(energy_source, c, t, dS, eqn){ int P1 = ....; dS[eqn] = -16.* abs_coeff * SIGMA_SBC * pow(C_T(c,t),3.); source =-abs_coeff *(4.* SIGMA_SBC * pow(C_T(c,t),4.) - C_UDSI(c,t,P1)); return source;}DEFINE_SOURCE(p1_source, c, t, dS, eqn){ int P1 = ...; dS[eqn] = -abs_coeff; ?source = abs_coeff *(4.* SIGMA_SBC * pow(C_T(c,t),4.) - C_UDSI(c,t,P1)); return source;}
2.6自定義數(shù)據(jù)類型
C還允許你用結構和typedef創(chuàng)建自定義數(shù)據(jù)類型。下面是一個結構列表的定義。注意:typedef只用于編譯過的UDF。
例子:
typedef struct list{???????????????? int a;???????????????? real b;???????????????? int c;}mylist;?????????????????????????? /* mylist為類型結構列表*/mylist x,y,z;?????????????????????? /* x,y,z為類型結構列表*/
2.7強制轉換
你可以通過強制轉換將某一數(shù)據(jù)類型轉換為另一種。強制由類型來表示,其中的類型包括int,float等等,如下例所示:
int x = 1;real y = 3.14159;int z = x+((int) y);?????? /* z = 4 */
2.8函數(shù)
函數(shù)是用完成一定任務的一系列語句。在定義該函數(shù)的同一源代碼中,這些任務可能對其它的函數(shù)有用,也可能會被用于完成源文件以外的函數(shù)中。每個函數(shù)都包括一個函數(shù)名以及函數(shù)名之后的零行或多行語句,其中有大括號括起來的函數(shù)主體可以完成所需要的任務。函數(shù)可以返回特定類型的數(shù)值。C函數(shù)通過數(shù)值來傳遞數(shù)據(jù)。
函數(shù)有很多數(shù)據(jù)類型,如real,void等,其相應的返回值就是該數(shù)據(jù)類型,如果函數(shù)的類型是void就沒有任何返回值。要確定定義UDF時所使用的DEFINE宏的數(shù)據(jù)類型你可以參閱udf.h文件中關于宏的#define聲明一節(jié),也可以參閱附錄A的列表。
!! C函數(shù)不能改變它們的聲明,但是可以改變這些聲明所指向的變量。
2.9 數(shù)組
數(shù)組的定義格式為:名字[數(shù)組元素個數(shù)],C數(shù)組的下標是從零開始的。變量的數(shù)組可以具有不同的數(shù)據(jù)類型。
例子
int a[10], b[10][10];
real radii[5];
?
a[0] = 1;????????????????? /* 變量a為一個一維數(shù)組*/
radii[4] = 3.14159265;?? ????/*變量radii為一個一維數(shù)組*/
b[10][10] = 4;??????????? ??/*變量b為一個二維數(shù)組*/
2.10指針
指針變量的數(shù)值是其它變量存儲于內(nèi)存中的地址值。C程序中指針變量的聲明必須以*開頭。指針廣泛用于提取結構中存儲的數(shù)據(jù),以及在多個函數(shù)中通過數(shù)據(jù)的地址傳送數(shù)據(jù)。
例如:int *ip;
本語句聲明了一個指向整型變量的指針變量ip。此時你可以為指針變量分配一個地址值了?,F(xiàn)在假定你要將某一地址分配給指針ip,你可以用取地址符&來實現(xiàn)。例如:
ip = &a;
就分配給指針ip變量a的地址值了。
要得到指針變量所指向的單元的值,你可以使用:
*ip
你還可以為指針ip所指向的變量賦值,例如:
*ip = 4;
將4賦給指針ip所指向的變量。下面是使用指針的例子:
int a = 1;
int *ip;
ip = &a;??????????????? /* &a返回了變量a的地址值*/
printf("content of address pointed to by ip = %d\n", *ip);
*ip = 4;??????????????? /* a = 4? */
printf("now a = %d\n", a);
在上面的語句中,整型變量賦初值為1。然后為整型變量聲明一個指針。然后整型變量a的地址值分配給指針ip。然后用*ip來輸出指針ip所指向的值(該值為1)。然后用*ip間接的給變量a賦值為4。然后輸出a的新值。指針還可以指向數(shù)組的起始地址,在C中指針和數(shù)組具有緊密的聯(lián)系。
2.10.1 作為函數(shù)自變量的指針
C函數(shù)可以通過指針進入和修改它們的自變量。在FLUENT中,線程和域指針是UDF常用的自變量。當你在UDF中指定這些自變量時, FLUENT解算器會自動將指針所指向的數(shù)據(jù)傳送給UDF,從而使你的函數(shù)可以存取解算器的數(shù)據(jù)(你不必聲明作為自變量從解算器傳送給UDF的指針)。例如,某一傳送給指定(由DEFINE_PROFILE宏來定義的)自定義輪廓UDF的自變量是一個指向應用于邊界條件的線程的指針。DEFINE_PROFILE函數(shù)會存取線程指針所指向的數(shù)據(jù)。
2.11 控制語句
你可以使用控制語句,如if, if-else和循環(huán)來控制C程序中語句的執(zhí)行順序??刂普Z句決定了程序序列中下一步該執(zhí)行的內(nèi)容
2.11.1 if語句
if語句是條件控制語句的一種。格式為:
?if (邏輯表達式)
???? {語句}
其中邏輯表達式是判斷條件,語句是條件滿足時所要執(zhí)行的代碼行。
例子
if (q != 1)
???? {a = 0; b = 1;}
2.11.2 if-else語句
if-else語句是另一種條件控制語句。格式為:
if (邏輯表達式)
? {語句}
else
? {語句}
如果邏輯表達式條件滿足,則執(zhí)行第一個語句,否則執(zhí)行下面的語句。
例子
if (x < 0.)
? y = x/50.;
else
? {
?? x = -x;
?? y = x/25.;
? }
下面是等價的FORTRAN代碼,大家可以比較一下:
?????? IF (X.LT.0.) THEN
???????? Y = X/50.
?????? ELSE
???????? X = -X
???????? Y = X/25.
?????? ENDIF
2.11.3 for循環(huán)
for循環(huán)是C程序最為基本的循環(huán)控制語句。它和FORTRAN中的do循環(huán)很類似。格式為:
for (起點;終點;增量)
??? {語句}
其中起點是在循環(huán)開始時執(zhí)行的表達式;終點是判斷循環(huán)是否結束的邏輯表達式;增量是循環(huán)迭代一次之后執(zhí)行的表達式(通常是增量計數(shù)器)。
例子:
/* 輸出整數(shù)1-10及它們的平方*/
int i, j, n = 10;
for (i = 1 ; i <= n ; i++)
???????????????? {
?????????????????????????????????? j = i*i;
?????????????????????????????????? printf("%d %d\n",i,j);
???????????????? }
下面是等價的FORTRAN代碼,大家可以做一比較:
????? INTEGER I,J
????? N = 10
????? DO I = 1,10
????? J = I*I
????? WRITE (*,*) I,J
????? ENDDO
2.12常用的C運算符
運算符是內(nèi)部的C函數(shù),當它們對具體數(shù)值運算時會得到一個結果。常用的C運算符是算術運算符和邏輯運算符。
2.12.1 算術運算符
下面是一些常用的算術運算符。
=? 賦值
+? 加
-? 減
*? 乘
/? 除
%? 取模
++ 增量
-- 減量
注意:乘、除和取模運算的優(yōu)先級要高于加、減運算。除法只取結果的整數(shù)部分。取模只取結果的余數(shù)部分。++運算符是增量操作的速記符。
2.12.2 邏輯運算符
下面是一些邏輯運算符。
<?? 小于
<=? 小于或等于
>?? 大于
>=? 大于或等于
==? 等于
!=? 不等于
?
2.13 C庫函數(shù)
當你書寫UDF代碼時,你可以使用C編譯器中包括的標準數(shù)學庫和I/O函數(shù)庫。下面各節(jié)介紹了標準C庫函數(shù)。標準C庫函數(shù)可以在各種頭文件中找到(如:global.h)。這些文件都被包含在udf.h文件中。
2.13.1 三角函數(shù)
下面的三角函數(shù)都是計算變量x(只有一個還計算y)的三角函數(shù)值。函數(shù)和變量都是雙精度實數(shù)變量。具體的意義大家應該都很清楚,就不具體介紹了。
double acos (double x);
返回x的反余弦函數(shù)
double asin (double x);
返回x的反正弦函數(shù)
double atan (double x);
返回x的反正切函數(shù)
double atan2 (double x, double y);
返回x/y的反正切函數(shù)
double cos (double x);
返回x的余弦函數(shù)
double sin (double x);
返回x的正弦函數(shù)
double tan (double x);
返回x的正切函數(shù)
double cosh (double x);
返回x的雙曲余弦函數(shù)
double sinh (double x);
返回x的雙曲正弦函數(shù)
double tanh (double x);
返回x的雙曲正切函數(shù)
2.13.2 各種數(shù)學函數(shù)
下面列表中,左邊是C函數(shù),右邊是對應數(shù)學函數(shù):
double sqrt (double x);
double pow(double x, double y);
double exp (double x);
double log (double x);
double log10 (double x);
double fabs (double x);
double ceil (double x);
不小于x的最小整數(shù)
double floor (double x);
不大于x的最大整數(shù)
2.13.3 標準I/O函數(shù)
C中有大量的標準輸入輸出(I/O)函數(shù)。在很多情況下,這些函數(shù)在指定的文件中工作。下面是一些例子。
FILE *fopen(char *filename, char *type);
打開一個文件
int fclose(FILE *fd);
關閉一個文件
int fprintf(FILE *fd, char *format, ...);
格式化輸出到一個文件
int printf(char *format, ...);
輸出到屏幕
int fscanf(FILE *fd, char *format, ...);
格式化讀入一個文件
函數(shù)fopen和fclose分別打開和關閉一個文件。函數(shù)fprintf以指定的格式寫入文件,函數(shù)fscanf以相同的方式從某一文件中將數(shù)據(jù)讀入。函數(shù)printf是一般的輸出函數(shù)。fd是一個文件指針,它所指向的是包含所要打開文件的信息的C結構。除了fopen之外所有的函數(shù)都聲明為整數(shù),這是因為該函數(shù)所返回的整數(shù)會告訴我們這個文件操作命令是否成功執(zhí)行。
在下面的例子中,需要打開的數(shù)據(jù)文件的名字用雙引號括起來。fopen中的選項r表明該文件是以可讀形式打開的。fscan函數(shù)從fd所指向的文件中讀入兩個浮點數(shù)并將它們存儲為f1和f2。關于C的標準輸入輸出函數(shù)其它更多的信息,你可以查閱相關手冊(如:[2])。
例子:
? FILE *fd;
??? fd = fopen("data.txt","r");?? /*? opens a file named data.txt? */
??? fscanf(fd, "%f ,%f'', &f1, &f2);
??? fclose(fd);
2.14 用#define實現(xiàn)宏置換
UDF解釋程序支持宏置換的C預處理程序命令。當你使用#define宏置換命令,C預處理程序(如,cpp)執(zhí)行了一個簡單的置換,并用替換文本替換宏中定義的每一個自變量。
#define macro replacement-text
如下面的宏置換命令:
#define RAD 1.2345
預處理程序會在UDF中所有的變量RAD出現(xiàn)的地方將RAD替換為1.2345。在你的函數(shù)中可能會有很多涉及到變量RAD的地方,但是你只需要在宏命令中定義一次,預處理程序會在所有的代碼中執(zhí)行替換操作。
在下面這個例子中:
#define AREA_RECTANGLE(X,Y) ((X)*(Y))
你的UDF中所有的AREA_RECTANGLE(X,Y)都會被替換為(X)和(Y)的乘積。
2.15 用#include實現(xiàn)文件包含
UDF解釋程序還支持文件包含的C前處理命令。當你使用#include包含一個文件時,C前處理程序會將#include filename行替換為文件名對應的文件內(nèi)容。
#include " filename "
文件名對應的文件必須在當前目錄中。只有udf.h文件例外,這是因為FLUENT解算器會自動將它讀入。
如下面的文件包含命令:
#include "udf.h"
會將文件udf.h包含進你的源代碼中。
2.16 與FORTRAN 的比較
很多簡單的C函數(shù)和FORTRAN函數(shù)的子程序很相似,例子如下:
簡單的C函數(shù)
等價的FORTRAN函數(shù)
int myfunction(int x)
INTEGER FUNCTION MYFUNCTION(X)
{
?
int x,y,z;
INTEGER X,Y,Z
y = 11;
Y = 11
z = x+y;
Z = X+Y
printf("z = %d",z);
WRITE (*,100) Z
return z;
MYFUNCTION = Z
}
END
?
?