- ANSYS Fluent 二次開發指南
- 胡坤編著
- 3428字
- 2022-01-13 14:56:43
1.6 C語言基礎
Fluent UDF采用C語言進行編寫,本節簡單介紹在UDF中經常會用到的C語言知識,本節部分內容來自UDF手冊。
1.6.1 C語言中的注釋
C語言中的注釋利用/*及*/來實現。例如:
/*這是一個注釋*/
注釋也可以跨行實現,如:
/*這是一個 跨行注釋*/
注 意
在編寫UDF的過程中,不能把DEFINE宏(如DEFINE_PROFILE)放置在注釋中,否則會引起編譯錯誤。
1.6.2 基本數據類型
Fluent UDF解釋器支持的標準C數據類型如下:
Int:整型,存儲形如1、2、3之類的整數。
long:長整型,存儲數據與int類似,但范圍更廣。
float:浮點型,存儲小數,如1.234等。
double:雙精度浮點型,與float類似。
char:字符型,如’a’、’b’、’c’等。
Fluent UDF中還有real型,其實這是Fluent自定義的數據類型,在雙精度求解器中,real類型與double類型相同,而在單精度求解器中,real類型等同于float類型。UDF自動進行轉換,因此在需要浮點數時,可以全部采用real類型。
1.6.3 常數
在C語言中可以利用#define來定義常數。需要注意的是,定義為常數類型后,該變量的值不能改變。如:
#define WALL_ID 5 #define YMIN 0.0 #define YMAX 0.4
這樣定義完畢后,WALL_ID的值不能再發生改變,因此如下的語句會引發編譯錯誤:
WALL_ID = WALL_ID +1;
1.6.4 全局變量和局部變量
變量用于存儲數據。所有變量都包含類型、名稱以及值,有時候還包含存儲標記,如靜態變量和外部變量。C語言中所有的變量在使用之前都必須聲明,這樣C編譯器才會知道該如何為此變量分配內存。
C語言中的全局變量定義在函數的外部,該變量可以被源文件中所有的函數引用。全局變量如果未被聲明為靜態變量,還可以被外部函數引用。如下面程序中的全局變量聲明:
#include ”udf.h” real volume; /*此處定義的是全局變量*/ DEFINE_ADJUST(vol,domain) { /*此處可以訪問變量volume*/ }
局部變量一般定義在函數體內,其只在函數體內起作用,在函數體外無法被訪問到。如下面程序中的局部變量定義:
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; }
1.6.5 外部變量
當在某個源文件中定義了一個未加static的全局變量后,若想在另一個源文件中調用此變量,此時可以使用外部變量聲明來實現。采用如下聲明:
extern real volume;
注 意
extern聲明只能用于編譯型UDF中。
以下是一個利用extern的案例。
假設在源文件file1.c中定義了全局變量:
#include ”udf.h” real volume; DEFINE_ADJUST(compute_volume, domain) { volume = .... }
若其他的源文件想要利用此全局變量 volume,此時可以創建頭文件,并將變量 volume聲明為extern變量,如創建頭文件extfile.h,寫入內容:
extern real volume;
之后就可以在其他的源文件中使用此變量volume了,如在源文件file2.c中:
#include ”udf.h” #include ”extfile.h” DEFINE_SOURCE(heat_source,c,t,ds,eqn) { real total_source = ...; real source; source = total_source/volume; return source; }
◎ 提示:外部變量使用起來很麻煩,也很容易出錯,如果對其不甚了解,建議不要使用。
1.6.6 靜態變量
靜態變量(聲明時添加static關鍵字)在用于局部變量或全局變量時具有不同的作用。局部變量被聲明為static時,當函數返回后變量并不銷毀,變量的值依舊被保留。全局變量被聲明為static時,該變量能夠被此源文件中的所有函數調用,但不能被其他源文件中的函數調用。實際上是變量被隱藏了。
例如在文件mysource.c中有如下代碼:
#include ”udf.h” static real abs_coeff = 1.0; /*靜態全局變量*/ /* 此變量只能被本文件中的其他函數調用 */ DEFINE_SOURCE(energy_source, c, t, dS, eqn) { real source; /* 局部變量*/ int P1 = ....; /* 局部變量*/ /*變量只能被當前函數調用,但在函數返回時變量并不釋放 */ dS[eqn] = -16.* abs_coeff; source =-abs_coeff *(4.* SIGMA_SBC ); return source; } DEFINE_SOURCE(p1_source, c, t, dS, eqn) { real source; int P1 = ...; dS[eqn] = -abs_coeff; source = abs_coeff *(4.* SIGMA_SBC); return source; }
◎ 提示:與全局變量類似,靜態變量也盡量少用,容易造成不必要的麻煩。
1.6.7 用戶自定義數據類型
C語言允許用戶自己定義數據類型,通過使用結構體及typedef關鍵字。如定義類型:
typedef struct list { int a; real b; int c; }mylist; mylist x,y,z;
上例定義了一個結構體類型mylist,并定義了三個結構體變量x、y、z。
1.6.8 強制轉換
在C語言中,有時需要對類型進行強制轉換,如將浮點型強制轉換為整型,如下例程序:
int x =1; real y=3.1415926; int z=x+(int)y;
計算完畢后,z=4。
1.6.9 函數
C語言中的函數執行獨立的任務。函數能夠被同一源文件中的其他函數調用,也可以由源文件之外的函數調用。
函數定義包含函數名以及被傳遞給函數的零個或多個參數列表。函數包含一個包含在大括號內的主體,主體中包含執行任務的指令。函數可以返回特定類型的值。
函數返回特定數據類型的值(例如,實數),如果類型為void,則不返回任何值。要確定DEFINE宏的返回數據類型,可查看udf.h文件中宏的相應#define語句。
1.6.10 數組
C語言中數組變量定義為name[size],其中name為數組變量的名稱,size為數組中存儲的單元數量。C語言中數組索引從0開始。
int a[10], b[10][10]; real rad[5]; a[0] = 1; rad[4] = 3.14159265; b[10][10] = 4;
1.6.11 指針
指針是一種存儲變量內存地址的變量。換句話說,指針是一個變量,這個變量指向另外一個變量的內存地址。指針變量的聲明:
int *ip;/*定義指針變量ip*/
定義了指針變量后,可以利用取址運算符將其他變量的地址賦予指針變量,如:
int *ip; ip =&a;
也可以為指針變量賦值,如:
*ip =4;
當指針作為函數的參數,此時為傳址調用,在函數體內修改指針參數的值,會改變調用函數時傳遞的參數的值。此功能可以實現一個函數返回多個值。
如下的C程序:
#include <stdio.h> int add(int *a,int b) { int sum = 0; sum = *a + b; *a = 5; return sum; } int main() { int *ip; int a = 1; int b = 2; int sum = 0; ip = &a; sum = add(ip,b); printf(“sum=%d,a=%d\n”,sum,a); return 0; }
輸出結果:
sum=3,a=5
傳遞的參數值被函數體內的程序改變。
1.6.12 流程控制
C語言中可以用邏輯判斷和循環來進行流程控制。
(1)if語句
if語句用于邏輯判斷。可寫成:
if(邏輯判斷表達式) { 語句塊; }
例如:
if(q!=1) { a=0; b=1; }
若邏輯判斷存在多個分支,可以采用if-else結構,如:
if(x<0) { y = x/50; } else(x>=0 && x<3) { x=-x; y = x/25; } else { x= 0; y = 0; }
(2)for循環
for語句常用于循環表達。
int i,j,n=10; for(i=1;i<n;i++) { j = i*i; printf(“%d%d\n”,i,j) }
除此以外,C語言中還包含while、do…while循環,以及switch開關判斷等流程控制。關于此方面更詳細內容可參閱專業的C語言類圖書。
1.6.13 操作符
(1)常用的代數操作符(表1-1)
表1-1 常用的代數操作符

(2)常用的邏輯操作符(表1-2)
表1-2 常用的邏輯操作符

1.6.14 C語言庫函數
C語言中包含了一些常用的庫函數,這些函數可以在UDF中直接調用。
(1)常用的三角函數
double acos (double x); double asin (double x); double atan (double x); double atan2 (double x, double y); double cos (double x); double sin (double x); double tan (double x); double cosh (double x); double sinh (double x); double tanh (double x);
(2)常用的數學函數
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); double floor (double x);
(3)常用的標準輸入輸出函數
FILE *fopen(char *filename, char *mode); int fclose(FILE *fp); int printf(char *format,...); int fprintf(FILE *fp, const char *format,...); int fscanf(FILE *fp, char *format,...);
1.6.15 預處理命令
在UDF的各種頭文件中(文件路徑D:\Program Files\ANSYS Inc\v180\fluent\fluent18.0.0\src),存在各種以#開頭的語句,如圖1-3所示。
#ifndef _FLUENT_UDF_H
#define _FLUENT_UDF_H
#ifdef __cplusplus
extern ”C” {
#endif
#define _UDF 1
#define _CRT_SECURE_NO_DEPRECATE 1
#define _CRT_NONSTDC_NO_DEPRECATE 1
#ifdef UDFCONFIG_H
# include UDFCONFIG_H
#endif
#include ”global.h”
圖1-3 頭文件示例
這些以#開頭的語句就是C語言的預處理命令。
C語言的預處理工作由一個預處理程序來完成。任何C系統都有一個預處理程序,負責處理源程序中的所有預處理命令,從而生成不含預處理命令的源程序。C語言的預處理目的是為了方便編程。
預處理命令以獨立的預處理命令行的形式出現在源程序中,# 是其特殊的引導符號。如果源程序中某一行的第一個非空格符號是#,這就是一個預處理命令行。預處理命令的作用是要求預處理程序完成一些操作。
(1)文件包含命令
文件包含命令是以#include開始的行,其作用是把特定文件的內容復制到當前源文件中。其存在兩種形式如下:
# include <文件名> # include ”文件名”
兩者的差異在于文件搜索方式的不同。
第一種形式,預處理程序直接到系統指定的某些目錄中去查找所需文件,目錄指定方式由具體系統確定,通常指定幾個系統目錄。
第二種形式,預處理程序先在源文件所在目錄中查找,若沒找到文件,則再到系統指定的目錄中去查找。
文件包含命令的處理過程:首先查找所需文件,找到后就用該文件的內容取代這個包含命令行。替換進來的文件中若有預處理命令,也將被處理。
(2)宏定義和宏替換
以#define開始的行稱為宏定義命令行。宏定義包含兩種形式:簡單宏定義;帶參數的宏定義。
① 簡單宏定義
簡單宏定義的形式為:
#define 宏名字 替代文本
其中宏名字是任意標識符,替代文本可以是任意一段正文,其中可以包括程序中能出現的任何字符(包括空格等),一直延續到本行結束。如果需要寫多行的替代文本,可以在行末寫一個反斜杠\,這將使下一行內容繼續被當作替代文本。
宏定義的作用就是為宏名字定義替代。
如果一個宏名字的替代文本是數值或可以靜態求值的表達式,當這個宏名字在程序某處出現,就相當于在那里寫了這個數值或表達式。
例如,如果進行了如下定義:
#define SLD static long double
此后,宏名字SLD就代表static long double。若程序中出現:
SLD x=2.4, y=9.16;
經過預處理后,源代碼被翻譯為:
static long double x=2.4,y=9.16;
預處理并不檢查宏定義中的替代文本是否為合法的C語言結構,也不檢查替換之后的結果是否為正確的C語言程序段,其只是簡單地完成文本替換工作。
② 帶參數的宏定義
帶參數的宏定義形式為:
#define 宏名字(參數列表)替代正文
使用帶參數的宏定義時,不但要給出宏的名字,還要用類似函數實參的形式給出各宏參數的替代段,多個替代段之間用逗號分隔。這種形式也成為一個宏調用。
對宏調用的替換分兩部分進行:首先用替代代碼段填充宏參數,然后將替換的結果(展開后的替代正文)代入到主程序中實現程序代碼的替換。
例如,定義求兩個數據中較小數,可定義宏:
#define min(A,B) ((A)<(B)?(A):(B))
若程序中出現如下語句:
z = min(x+y,x*y)
則宏展開后為:
z = ((x+y)<(x*y)?(x+y):(x*y));
帶參數的宏定義與函數看起來很類似,但實際上有很大的不同。切記宏定義只是簡單的文本替換。
(3)條件編譯命令
條件編譯的作用是在源程序中劃出一些片段,使預處理程序可根據條件保留或丟掉一段,或從幾段中選擇一段保留。實現條件編譯的預處理命令有四個,分別是:
#if #else #elif #endif
其中,#if和#elif命令以一個能靜態求出整型值的表達式為參數,另外兩個沒有參數。條件編譯命令的常見使用形式有三種。
① 形式一
#if 整型表達式 …… /*代碼片段,條件成立時保留*/ #endif
② 形式二
#if 整型表達式 …… /*條件成立時保留*/ #else …… /*條件不成立時保留*/ #endif
③ 形式三
#if 整型表達式 …… /*條件成立時保留*/ #elif 整型表達式 …… /*elif部分,可以有多個*/ #elif 整型表達式 …… #else …… /*條件都不成立時保留*/ #endif
其中整型表達式是預處理條件,值為0表示條件不成立,否則條件成立。這里常用==、!=等做判斷,例如判斷宏定義的符號是不是等于某個值等。
為了方便,C語言提供了一個特殊謂詞define,其使用形式有兩種:
define 標識符 define (標識符)
當標識符是有定義的宏名字時,define(標識符)將得到1,否則得到0。這種表達式常被作為條件編譯的條件。此外還有兩個預處理命令#ifdef和ifndef,它們相當于#if和#define混合的簡寫形式。
#ifdef 標識符 /*相當于#if define(標識符)*/ #ifndef 標識符 /*相當于#if !define(標識符)*/