0% found this document useful (0 votes)
57 views128 pages

Complete Course On UDF by Ansys Fluent1

This document serves as training material for using User Defined Functions (UDFs) in Fluent, detailing their purpose, implementation, and the C programming language used for coding them. It explains the structure of UDFs, the data types associated with Fluent's mesh components, and various macros that facilitate UDF programming. The document emphasizes the flexibility and power of UDFs for customizing simulations in Fluent to meet specific user needs.
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
57 views128 pages

Complete Course On UDF by Ansys Fluent1

This document serves as training material for using User Defined Functions (UDFs) in Fluent, detailing their purpose, implementation, and the C programming language used for coding them. It explains the structure of UDFs, the data types associated with Fluent's mesh components, and various macros that facilitate UDF programming. The document emphasizes the flexibility and power of UDFs for customizing simulations in Fluent to meet specific user needs.
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 128

Chapter 1 Customer Training Material

Introduction to Fluent
from the Inside
14. 0 Release

Advanced Fluent
User-Defined Functions
1 © 2012 ANSYS, Inc. March 14, 2012 Release 14.0
Introduction to UDF Programming Customer Training Material

• Why use UDFs?

− Fluent is a general‐purpose tool and cannot anticipate all users needs.

− Addition of new physical models for academic research or customer


proprietary physics.

− Customization of boundary conditions.

2 © 2012 ANSYS, Inc. March 14, 2012 Release 14.0


What is a User Defined Function (UDF)? Customer Training Material

• A UDF is a function or set of functions programmed by the user in C language and


linked to Fluent to perform a wide variety of operations including:

– Initialization
– Custom boundary conditions (both space or time dependent)
– Material properties
– Source terms into any of the flow equations
– Chemical reaction rates
– Post processing and reporting of modified data
– Adding extra flow equations
– Mesh motion
– Discrete phase model modification
– And many more...

3 © 2012 ANSYS, Inc. March 14, 2012 Release 14.0


User Access to the Fluent Solver Customer Training Material

There are many places within the solution cycle where a UDF
can be added:
Segregated PBCS DBCS

User-
Initialize Begin Loop defined Solver? Solve U-Momentum Source terms
ADJUST
Solve Mass,
Source terms Solve V-Momentum Momentum,
Solve Mass Source terms
User Defined Energy,
& Momentum
INITIALIZE Species
Solve W-Momentum

Repeat
Solve Mass Continuity;
Update Velocity

Exit Loop Check Convergence


Solve Energy

Update Properties Solve Species Source


terms
User-Defined Properties Solve Turbulence Equation(s)
User-Defined BCs
Solve Other Transport Equations as required

4 © 2012 ANSYS, Inc. March 14, 2012 Release 14.0


Data Structures Customer Training Material

• Each type of UDF is passed different values and data structures


depending on its function.

• Most UDFs will need geometrical data. This is stored in a hierarchy:

– Domains are collections of threads that make the whole mesh


– Threads (or zones) are collections of cells or faces
– Cells and faces are made of Nodes

5 © 2012 ANSYS, Inc. March 14, 2012 Release 14.0


Geometrical Entities Customer Training Material

• The parts of a mesh are:

6 © 2012 ANSYS, Inc. March 14, 2012 Release 14.0


Data Structures Customer Training Material

• The terms ‘Thread’ and ‘Zone’ are interchangeable. The boundary


and cell zones defined in the User Interface are stored internally as
threads.

– Zones have an associated integer ID seen in the settings panel


– In a UDF, the corresponding thread can be identified using this ID
– Threads are an internal data type which stores information about the mesh,
models, properties, BCs all in one place

• A cell has information about its bounding faces and a face has access to
its neighbouring cells.

7 © 2012 ANSYS, Inc. March 14, 2012 Release 14.0


Customer Training Material

Introduction to the C
Programming Language

14. 0 Release

Advanced Fluent
User-Defined Functions
8 © 2012 ANSYS, Inc. March 14, 2012 Release 14.0
C Programming (1) Customer Training Material

– Basic syntax rules:


• Each statement must be terminated with a semicolon ;
• Comments can be inserted anywhere between /* and */
• All variables must be explicitly declared (unlike in FORTRAN)
• Compound statements can be created by enclosing multiple statements by braces:
{ }
• Function definitions have the following format:
return-value-type function-name(parameter-list)
{ function body }
– Built‐in data types: int, float, double, enum, boolean:
int niter, a; /* Declare ‘niter’ and ‘a’ as integers */
float dx[10]; /* dx is an array of 10 numbers indexed from 0-9:
dx[0], dx[1] … dx[8], dx[9] */

enum {X, Y, Z}; /* X, Y, Z are enumeration constants = 0, 1, 2 */

9 © 2012 ANSYS, Inc. March 14, 2012 Release 14.0


C Programming (2) Customer Training Material

• A Pointer is a special kind of variable that contains an address in memory, not content, of
another variable

• Pointers are declared using the * notation:


int *ip; /* ip is declared as a pointer to integer */

• We can make a Pointer point to the address of predefined variable as follows:


int a = 1;
int *ip; /* (*ip) contains an integer */

ip = &a; /* &a returns the address of variable a */


printf(“ip is a pointer with value %p\n”, ip);
printf(“The integer stored at ip = %d\n”,*ip);

ip is a pointer with value 0x40b2


The integer stored at ip = 1

10 © 2012 ANSYS, Inc. March 14, 2012 Release 14.0


C Programming (3) Customer Training Material

• Pointers and arrays are closely related:

int ar[10]; /* ar is an array of 10 integers */


int *ip; /* (*ip) contains an integer, */

The name of an array without an index can be


used as a pointer to the start of the array

ip = ar; /* ar is the address of its first integer


so this is equivalent to ip = &(ar[0]); */

BUT:
An array name cannot be set to a new value.
It is fixed to the value set when it is defined as an array.

ar = &i; /* This will cause a compilation error */

11 © 2012 ANSYS, Inc. March 14, 2012 Release 14.0


C Programming (4) Customer Training Material

– Operators
= (assignment)
+, -, *, /, % (modulus)
<, >, >=, <= , ==, !=
++ increment; i++ is “post‐increment”: use the current value of i in the
expression, then increment i by 1 (i=i+1)

-- decrement: j-- post‐decrement, use the current value of j, then


decrement j by 1 (j=j-1)

+= addition assignment:
agg += single; /* means: agg = agg + single; */
*= multiplication assignment,
-= subtraction assignment,
/= division assignment
12 © 2012 ANSYS, Inc. March 14, 2012 Release 14.0
C Programming (5) Customer Training Material

– Basic control structures


for loops:
for ( k=0; k < NUM; k++ )
if ( … )
<statement>;
<statement>;
while loops:
if ( …)
<statement>; while ( … )
<statement>;
else
<statement>;

Conditional Operator (? : )
if ( …)
( condition ? operand a : operand b )
<statement>; example:
else if ( … ) real At = (rp_axi ? At*2*M_PI : At );
<statement>;

13 © 2012 ANSYS, Inc. March 14, 2012 Release 14.0


C Programming (6) Customer Training Material

– Compound statements may be placed wherever a simple statement is


allowed:
and in loops:

if ( k > 2 ) for ( k=0; k < NUM; k++ )


{ {
<statement>; <statement>;
<statement>;
<statement>;

… }
}
else while ( k < NUM )
{ {
<statement>;
<statement>;
k++;
<statement>; …
… }
}
14 © 2012 ANSYS, Inc. March 14, 2012 Release 14.0
C Programming (7) Customer Training Material

– Structures
• Structures are collections of data that are of different types.

struct car_struct
{
char model_name[25];
int number_of_wheels;
int engine_cc;
real top_speed;
}; /* Need a ‘;’ as variables can be defined here */

struct car_struct alices_car, bobs_car; /* or here */

/* Set and get values like this: */


alices_car.top_speed = 120.0;
if(bobs_car.top_speed > alices_car.top_speed)

15 © 2012 ANSYS, Inc. March 14, 2012 Release 14.0


C Programming (8) Customer Training Material

– In Fluent, structures are used for many things.


• The most common one is Thread.
• This is actually defined using the typedef statement so we don’t need to
use the word struct when defining new Threads:
typedef struct thread_struct
{
...
int id;
...
} Thread; /* typedef makes a new Thread type */

Thread *ct; /* Usually we use pointers to structures */

/* Macros are defined to access structure elements */


#define THREAD_ID(t)((t)->id)

/* Note ‘->’ operator: t->id is the same as (*t).id */


16 © 2012 ANSYS, Inc. March 14, 2012 Release 14.0
Customer Training Material

Introduction to UDF
Implementation Using C

14. 0 Release

Advanced Fluent
User-Defined Functions
17 © 2012 ANSYS, Inc. March 14, 2012 Release 14.0
Why UDFs are written in the C Customer Training Material

Language
Fluent is written in C because it is a very powerful language. It provides access to high
level operations such as graphics and networking and low level capabilities such as
mathematical functions and memory operations.

It enables the linking of extra functionality using “shared objects” in Unix and Dynamic
Linked Libraries (DLLs) in windows systems.

This is a very convenient mechanism for linking in UDFs which allows a seamless
connection between the main program and the user functions

Users familiar with other “procedural” languages such as FORTRAN will be familiar with
most of the ideas and syntax used in C

18 © 2012 ANSYS, Inc. March 14, 2012 Release 14.0


Mesh Data Types (1) Customer Training Material

• There are several Fluent‐specific data types that are associated with mesh
components.
• Some of the more commonly used ones are:
− Node – stores data associated with a grid point
− face_t – identifies a face within a face thread
− cell_t – identifies a cell within a cell thread
− Thread – stores data that is common to a collection of cells or faces
− Domain – stores data associated with a collection of face and cell threads in a mesh
• Each individual cell can be accessed by using a cell index of type cell_t and the
cell thread (the zone which contains the cell)
• Similarly, each individual face (boundary or internal) can be accessed by using
the face index of type face_t and the face thread (the zone which contains the
face)

19 © 2012 ANSYS, Inc. March 14, 2012 Release 14.0


Mesh Data Types (2) Customer Training Material

• Some mesh or solver data types have a capital letter. These are actually data
structures in the C language and are usually passed between functions as
pointers to these structures.

• Examples of how these data types are defined are shown below:

Type Example Declaration


Domain *d; d is a pointer to a Domain structure

Thread *t; t is a pointer to a Thread structure

cell_t c; c is cell index, a simple integer

face_t f; f is a face index, a simple integer

Node *node; node is pointer to a Node structure

20 © 2012 ANSYS, Inc. March 14, 2012 Release 14.0


Mesh Data Types (3) Customer Training Material

• Every Zone is associated to a single ID available in the BC panel or the TUI using:
/grid/modify-zones/list-zones

• Given the ID, the thread associated with


that zone can be retrieved as:
int t_id = 7;
Thread *ft;
ft = Lookup_Thread(domain, t_id);

• Once we have the thread we can access


its associated data

• Similarly, a thread’s ID can be retrieved using:


t_id = THREAD_ID(ft);
21 © 2012 ANSYS, Inc. March 14, 2012 Release 14.0
C macros and UDF Programming Customer Training Material

• C has a very powerful macro definition capabilities. These are used


extensively in Fluent in many ways. Notable example include
definitions of:

– Data structure looping macros


– Geometry macros
– Field data Macros
– Logic and status control macros
– Safe arithmetic and trigonometry functions
– Complete set of vector arithmetic operations

• The definition of each UDF also uses a specific DEFINE_ macro.

22 © 2012 ANSYS, Inc. March 14, 2012 Release 14.0


Loop Macros in a UDF Customer Training Material

• thread_loop_c(t,d) Loop over the cell threads in a domain d


• thread_loop_f(t,d) Loop over face threads in a domain d

• begin_c_loop(c,t) Loop over the cells in a given cell thread


end_c_loop (c,t)

• begin_f_loop(f,t) Loop over the faces in a given face thread


end_f_loop(f,t)

23 © 2012 ANSYS, Inc. March 14, 2012 Release 14.0


Geometry Macros Customer Training Material

• C_CENTROID(pos,c, t) x, y, z coords of cell centroid


• F_CENTROID(pos, f, t) x, y, z coords of face centroid A Hex cell
• F_AREA(A, f, t) Area vector of a face; Faces
• NV_MAG(A) Vector magnitude
• C_VOLUME(c, t) Volume of a cell Nodes
• C_VOLUME_2D(c, t) Volume of a 2D cell
Location of cell variables
(pos & A are vectors which are discussed later) C_NNODES(c,t) = 8
C_NFACES(c,t) = 6
F_NNODES(f,t) = 4 each
(Depth is 1 m in 2D; 2* radians in axi‐symmetric)

• NODE_X(nn) Node x‐coord; (with Node *nn;)

• NODE_Y(nn) Node x‐coord;


• NODE_Z(nn) Node x‐coord;

Many more are available. See the Fluent UDF Manual

24 © 2012 ANSYS, Inc. March 14, 2012 Release 14.0


Cell‐Face Connectivity Customer Training Material

• The cells either side of a face (f,ft) can be accessed using the macros:

ct0 = THREAD_T0(ft); C0
ct1 = THREAD_T1(ft);
c0 = F_C0(f,ft);
C1
c1 = F_C1(f,ft);

• The macros THREAD_T0(ft)& THREAD_T1(ft)only need the face thread,


not the face index. This is because in Fluent, an interior zone must connect to the
same fluid or solid zones on either side.

• Boundary zones such as inlets will connect to a cell zone on one side with
THREAD_T0(ft) but THREAD_T1(ft) will return NULL , a special pointer
which signifies there is no zone on the other side.

25 © 2012 ANSYS, Inc. March 14, 2012 Release 14.0


Cell‐Face Connectivity (2) Customer Training Material

• A face’s area is obtained as a vector using:

C0
real A[3];
f = F_AREA(A,f,ft); C1

A face’s area vector


points from C0 to C1
The area can be used to calculate the face normal
• The faces that bound a cell can be accessed using this loop :
int nf;
for(nf=0;nf<C_NFACES(c,ct);nf++)
{
f = C_FACE(c,ct,nf);
ft = C_FACE_THREAD(c,ct,nf);
26 © 2012}ANSYS, Inc. March 14, 2012 Release 14.0
Field Variables Macros - Cell Customer Training Material

• C_R(c,t) Density
• C_P(c,t) Pressure
• C_U(c,t)
• C_V(c,t) X, Y & Z Velocity Components
• C_W(c,t)
• C_T(c,t) Temperature
• C_H(c,t) Enthalpy
• C_K(c,t) Turbulent Kinetic Energy
• C_D(c,t) Turbulent Energy Dissipation
• C_YI(c,t,i) i‐th Species Mass Fraction
• C_UDSI(c,t,i) i‐th User Defined Scalar
• C_UDMI(c,t,i) i‐th User Defined Memory
t is a cell‐thread pointer, c is a cell index, and i is an integer for indexing which
species, UDS and UDM require.
27 © 2012 ANSYS, Inc. March 14, 2012 Release 14.0
Macros used for Control Customer Training Material

• Data_Valid_P()
Equals 1 if data is available 0 if not.
if (!Data_Valid_P()) return;
• FLUID_THREAD_P(t0)
Checks to see if thread t0 is a fluid thread

• NULLP(ct)
NULLP checks if a pointer is NULL

• NNULLP(ct)
NNULLP checks if a pointer is NOT NULL
if(NNULLP(THREAD_T1(ft)))
{ ... Do things on neighbouring cell thread.}

28 © 2012 ANSYS, Inc. March 14, 2012 Release 14.0


UDF Defining Macros Customer Training Material

• All UDFs that will be used in Fluent must be defined using a specific DEFINE_
macro
• Some examples are:

 Profiles : DEFINE_PROFILE
 Source terms : DEFINE_SOURCE
 Properties : DEFINE_PROPERTY
 User-defined Scalars : DEFINE_UNSTEADY
DEFINE_FLUX
DEFINE_DIFFUSIVITY
 Initialization : DEFINE_INIT
 Global Functions : DEFINE_ADJUST
DEFINE_ON_DEMAND
DEFINE_RW_FILE

Refer to the UDF Manual for a complete list

29 © 2012 ANSYS, Inc. March 14, 2012 Release 14.0


Additional Information Customer Training Material

• Many additional macros are available to implement different physical models


including:
combustion models
particles‐based models

• It is also possible to develop additional numerical methods, particularly when


using User Defined Scalars. Access to low‐level operations is possible, such as:

flux discretisation
variable reconstruction and clipping

30 © 2012 ANSYS, Inc. March 14, 2012 Release 14.0


The Header Files Customer Training Material

• The UDF macros are defined in the ‘udf.h’ file, for instance:
#define DEFINE_PROFILE(name, t, i) void name(Thread *t, int i)
#define DEFINE_PROPERTY(name,c,t) real name(cell_t c, Thread *t)
#define DEFINE_SOURCE(name, c, t, dS, i) \
real name(cell_t c, Thread *t, real dS[], int i)
#define DEFINE_INIT(name, domain) void name(Domain *domain)
#define DEFINE_ADJUST(name, domain) void name(Domain *domain)

• The file udf.h is a fluent header file in the {Fluent installed


directory}/FluentX.Y/src/ directory
• The line:
include “udf.h”
must be at the top of every file defining any UDFs
− A file may contain more than one UDF function definition
− Multiple files can be used to define different related sets of functions

• There are many more header files stored in the same directory which
can be browsed by users but most are already included automatically
within udf.h
31 © 2012 ANSYS, Inc. March 14, 2012 Release 14.0
Customer Training Material

Chapter 2

Compilation and Interpretation


14. 0 Release

Advanced Fluent
User-Defined Functions
1 © 2011 ANSYS, Inc. March 14, 2012 Release 14.0
How to use UDFs Customer Training Material

UDFs must be compiled and linked to the main code


There are two options:
• Interpreted
– The UDF is translated into assembly code which is executed on a “line‐by‐line”
basis at run time
– Does not need a separate compiler (completely automatic)
– Slower execution and uses more memory
– Only basic language and FLUENT features implemented
– Restricted functionality in parallel cases

• Compiled
– The UDF files are compiled and linked to the main code
– Full C language and all standard libraries
– Access to full FLUENT function set
– Needs a compiler for every OS to be used on

2 © 2012 ANSYS, Inc. March 14, 2012 Release 14.0


Interpreted UDFs Customer Training Material

The interpreter does not have all of the capabilities of the


standard C compiler

Interpreted UDFs cannot contain


• declarations of local structures, unions, pointers to functions,
and arrays of functions
• direct structure references

Can access data stored in a FLUENT structure only via a


limited set of macros

Users are strongly encouraged to use compilation for all


but the most simple UDFs

3 © 2012 ANSYS, Inc. March 14, 2012 Release 14.0


Interpreting the UDFs Customer Training Material

Define → User Defined → Interpreted


Click Interpret

Default stack size might be too small for large


arrays so may need increasing

The assembly language code will be


displayed if no errors are found.

4 © 2012 ANSYS, Inc. March 14, 2012 Release 14.0


Compiling the UDFs Customer Training Material

Define → User Defined → Func on → Compiled


• Click Build
• The compiler will build the shared object files
• Load the shared library

• On Windows to recompile you will need to unload a previously compiled UDF library
using:
– Define → User Defined → Func on → Manage
– Select the library and then click Unload

5 © 2012 ANSYS, Inc. March 14, 2012 Release 14.0


Customer Training Material

Appendix

14. 0 Release

Advanced Fluent
User-Defined Functions
6 © 2012 ANSYS, Inc. March 14, 2012 Release 14.0
Directory Tree for a Compiled UDF Library Customer Training Material

Machine dependent
Unix Tree irix6r10 Windows Tree
ultra
libudf hp700 libudf

Makefile src ultra


src ntx86

makefile w-profile.c w-profile.c

2d 3d 2d 3d

makefile w-profile.c libudf.so makefile user_nt.udf libudf.dll

makefile w-profile.c libudf.so makefile user_nt.udf libudf.dll

7 © 2012 ANSYS, Inc. March 14, 2012 Release 14.0


Chapter 3

Composing UDFs
14. 0 Release

Advanced Fluent
User-Defined Functions
1 © 2012 ANSYS, Inc. March 14, 2012 Release 14.0
Using the DEFINE Macros
All UDFs are defined using a macro that starts with “DEFINE_”
The general format of a DEFINE_ macro is:

DEFINE_UDF_TYPE(udf_name, 0 or more variables...)


• udf_name is the name of the function you define
– It is case‐sensitive
– This is the name seen in the pick lists in the GUI panel

Example:
DEFINE_PROFILE(inlet_x_velocity, thread, index)
• Defines boundary profile name “inlet_x_velocity”
• thread is the boundary thread which will be changed
• index identifies which boundary condition value is to be set

2 © 2012 ANSYS, Inc. March 14, 2012 Release 14.0


Boundary Conditions

DEFINE_PROFILE can be used on any boundary zone to set conditions such as


temperature, velocity etc.

It is possible to specify a constant value, position‐dependent or time‐dependent


values

Note that the BCs are applied at the faces of boundary, not the adjacent cells.

3 © 2012 ANSYS, Inc. March 14, 2012 Release 14.0


DEFINE_PROFILE
The following UDF sets the value of x‐velocity at the face of the inlet boundary zone according to
the equation:
Vx = Vo + A sin(t); Vo = 20 m/s, A = 5 m/s, = 10 rad/s

• Loop through the faces belonging to the inlet zone

• Specify the x‐velocity component on each face using F_PROFILE(f, ft, var) = …

#include "udf.h"

#define OMEGA 10.0 /* User constants can be set like this */


#define V0 20.0
#define V_AMP 5.0

DEFINE_PROFILE(inlet_x_velocity, ft, var)


{
real flow_time;
face_t f; /* Face index has its own type */

flow_time = CURRENT_TIME; /* Special Fluent macro */

begin_f_loop(f,ft) /* Fluent has special face loop macros too */


{
F_PROFILE(f,ft,var) = V0 + V_AMP * sin(OMEGA*flow_time);
}
end_f_loop(f,ft)
}

4 © 2012 ANSYS, Inc. March 14, 2012 Release 14.0


Example 1: Results of Transient Inlet Velocity
Time history of the average velocity at the pipe exit shows sinusoidal
oscillation with a mean of 20 m/s and amplitude of 5 m/s

Average
Velocity
Magnitude

Flow Time

5 © 2012 ANSYS, Inc. March 14, 2012 Release 14.0


Example 2: Sinusoidal Wall Temperature
The following UDF sets the temperature on the faces of a wall according to the
equation:
Tx = 300 + 100 sin(π*x/L)
#include "udf.h"

#define LENGTH 0.005


#define T_MIN 300.0
#define T_AMP 100.0

DEFINE_PROFILE(wall_temp, ft, var)


{
real temperature;
real pos[ND_ND]; /* How 2D and 3D vectors are defined */
real omega;
face_t f;

omega = M_PI/LENGTH; /* M_PI is standard C value for Pi */

begin_f_loop(f,ft) /* Fluent has special loop macros too */


{
F_CENTROID(pos,f,ft); /* Face centroid set in pos */
temperature = T_MIN + T_AMP * sin(omega * pos[0]); /* x = pos[0] */
F_PROFILE(f,ft,var) = temperature;
}
end_f_loop(f,ft)
}

6 © 2012 ANSYS, Inc. March 14, 2012 Release 14.0


Example 2: Sinusoidal Wall Temperature
Wall temperature reaches its peak at mid length of the wall

Static Temperature (K)

Adiabatic Wall

Cold Inflow

Heated Wall

7 © 2012 ANSYS, Inc. March 14, 2012 Release 14.0


DEFINE SOURCE
To add source terms to the flow equations use :
DEFINE_SOURCE(udf_name, cell, thread, dS, eqn)
Source terms can be added to equations for:
• continuity
• momentum
• k, 
• Energy
• Multiphase and others

Energy source term UDFs may also be defined for solid zones
The source term macro is different from the previous macro because it is called for
each cell separately.
So, there is no need to perform a cell loop!
The units of all source terms are expressed in terms of the volumetric generation
rate. (total cell source / m3)

8 © 2012 ANSYS, Inc. March 14, 2012 Release 14.0


Source Terms
In Fluent, source terms are linearized as follows:
S(φ) = A + B.Δφ

A is added explicitly and B.Δφ is added implicitly to the solution matrix

A DEFINE_SOURCE UDF returns the value for the total source, S, but we can also
specify B if required using the dS and eqn variables passed to the UDF:

dS[eqn] = dS_dPhi;

Setting this term is usually not required and can be set to 0.0, but, if the source terms
are large and causing convergence problems, then if dS_dPhi is negative,
setting this term will help stabilise the solution.

9 © 2012 ANSYS, Inc. March 14, 2012 Release 14.0


Example: Position Dependent Porous Media
The following UDF adds a negative momentum S  ( y / h)C2  v x v x / 2
source into each cell which is dependent dS
on the local flow speed and y position. B  ( y / h)C2  v x
dv x

#include "udf.h"

#define C2 100.0
#define HEIGHT 0.01

DEFINE_SOURCE(mom_source_x, c, ct, dS, eqn)


{
real NV_VEC(pos); /* Another way to define 2D and 3D vectors */
real dS_dU;
C_CENTROID(pos,c,ct); /* Cell centroid set in pos */

/* C_R(c,ct) gives density of material in the cell */


dS_dU = - pos[1] * C2 * C_R(c,ct) * fabs(C_U(c,ct)) / HEIGHT;

dS[eqn] = dS_dU; /* Could be set to 0.0 if unknown or not required */

return dS_dU * C_U(c,ct) / 2.0;


}

10 © 2012 ANSYS, Inc. March 14, 2012 Release 14.0


Source Terms
In Define → Boundary Condi ons → Fluid

Activate source terms

Attach the Source UDF to


the matching equation

11 © 2012 ANSYS, Inc. March 14, 2012 Release 14.0


Example: Position Dependent Porous Media
• X‐momentum loss is linear in the y‐position, starting from zero at the symmetry plane
• The fluid flows faster near the midline of the channel where the resistance is least

Porous Plug

Inlet
Outlet

Velocity Vectors (m/s)

12 © 2012 ANSYS, Inc. March 14, 2012 Release 14.0


Initial Conditions
The initial conditions of the flow can be set using a UDF of the form:
DEFINE_INIT(init_sphere_temp, domain)

The UDF is executed once at the beginning of the solution process


It is attached in the UDFs Hooks panel
Define → User Defined → Func on Hooks

13 © 2012 ANSYS, Inc. March 14, 2012 Release 14.0


Initialization UDF
This UDF initialises the temperature #include "udf.h"
#define RADIUS 0.25
within a sphere of radius #define T_HIGH 400.0
0.25m #define T_LOW 300.0

DEFINE_INIT(init_sphere_temp, domain)
The macro NV_D can be used to set {
a vector cell_t c; /* Cell index has its own type too */
Thread *ct; /* Threads are pointers to a structure */
The macro NV_VV can be used for real pos[ND_ND];
real centre[ND_ND];
vector arithmetic
NV_D(centre,=,0.0,0.5,0.5); /* Set a vector */
The macro NV_MAG computes the
length of a vector /* Loop through all the cell threads in the domain */
thread_loop_c(ct,domain)
{/* Loop through the cells in each cell thread */
(0.0, 0.5, 0.5) begin_c_loop(c,ct)
{
C_CENTROID(pos,c,ct);
NV_VV(pos,=,pos,-,centre); /* Vector arithmetic */

if(NV_MAG(pos) < RADIUS)


C_T(c,ct) = T_HIGH;
else
C_T(c,ct) = T_LOW;
}
end_c_loop(c,ct)
}
}
14 © 2012 ANSYS, Inc. March 14, 2012 Release 14.0
Adjust Function
This is similar to DEFINE_INIT but it is executed every iteration.

Is used for reporting or setting up models


before each iteration.

It is attached in the UDF Hooks panel

Define → User Defined → Func on Hooks

15 © 2012 ANSYS, Inc. March 14, 2012 Release 14.0


Adjust Function
The Following example integrates turbulent dissipation over entire domain

#include "udf.h"

DEFINE_ADJUST(total_dissipation, domain)
{
cell_t c;
Thread *ct;
real total_diss;

total_diss = 0.0; /* Initialise total_diss before the loop */

thread_loop_c(ct,domain)
{
begin_c_loop(c,ct)
{
total_diss += C_D(c,ct) * C_VOLUME(c,ct); /* Use += to sum up values */
}
end_c_loop(c,ct)
}

Message("Volume integral of turbulent dissipation = %f \n", total_diss);


}

16 © 2012 ANSYS, Inc. March 14, 2012 Release 14.0


Execute At End Function
This is similar to DEFINE_INIT but is executed at the end of each iteration in a
steady state run or at the end of each time step in a transient run

Can be used for results reporting and post‐processing.

It is attached in the UDF Hooks panel

Define → User Defined → Func on Hooks

17 © 2012 ANSYS, Inc. March 14, 2012 Release 14.0


Execute_at_End Function
DEFINE_EXECUTE_AT_END UDFs have a slightly different format:

#include "udf.h"

DEFINE_EXECUTE_AT_END(final_total_dissipation)
{
/* The domain is NOT passed as a parameter to EXECUTE_AT_END UDF. */
/* So a local domain variable must be defined and set internally. */

Domain *domain;

domain = Get_Domain(ROOT_DOMAIN_ID); /* ROOT_DOMAIN_ID = 1 */

/* UDFs defined earlier can be called as normal C functions. */


/* This prevents no duplication of coding. Good practice! */

/* Here we call the Adjust function defined before. */


/* Note that the UDF name is the function name and */
/* the other parameters are the function parameters. */

total_dissipation(domain);
}

18 © 2012 ANSYS, Inc. March 14, 2012 Release 14.0


User Defined I/O
DEFINE_RW_FILE can be used to read and write
customized information to case/data files

• Save and restore custom variables common data types


(integer, real, Boolean, structure elements)

• Useful to save model specific information which would be


lost if the UDF or case file were reloaded.
For example, a user model’s total run time or the number
times a process has been run.

• They are attached in the UDF Hooks panel

19 © 2012 ANSYS, Inc. March 14, 2012 Release 14.0


User Defined I/O (2)
#include "udf.h"

/* A global variable can be defined that is visible to all the */


/* functions in the file. The static keyword should be used to */
/* hide it from other files for safety. Good Practice! */

static int run_count = 0; /* Always initialise global variables. */

DEFINE_EXECUTE_AT_END(increment_run_count)
{
run_count++; /* Increment the global counter here */
}

DEFINE_RW_FILE(write_count, file_pointer)
{
Message("Writing run counter to data file.\n");

/* fprintf is a standard C function for writing to a file */


fprintf(file_pointer, "%d\n", run_count); /* Match the formats! */
}

DEFINE_RW_FILE(read_count, file_pointer)
{
Message("Reading run counter from data file.\n");

/* fscanf is a standard C function for reading from a file */


fscanf(file_pointer, "%d\n", &run_count); /* Note use of & operator here */
}

20 © 2012 ANSYS, Inc. March 14, 2012 Release 14.0


Material Properties
To define the properties of the materials : DEFINE_PROPERTY

Can be used for most properties such as:


• Viscosity
• Density (small variations only)
• Thermal Conductivity
• Surface Tension

The property UDF is called on a cell‐by‐cell basis so, like source term UDFs
does not require a loop.

21 © 2012 ANSYS, Inc. March 14, 2012 Release 14.0


Material Property
Property UDF linked in at Define → Materials

22 © 2012 ANSYS, Inc. March 14, 2012 Release 14.0


Material Properties
The following UDF simulates solidification by specifying temperature
dependent viscosity
#include "udf.h"

#define T_HOT 288.0


The viscosity is low when #define T_COLD 286.0
#define MU_HOT 5.5e-5 /* Low viscosity when hot */
the flow is above T_HOT #define MU_COLD 1.0 /* High viscosity when cold */

DEFINE_PROPERTY(freezing_viscosity, c, ct)
{
real temp, mu_laminar;
The viscosity is high when
temp = C_T(c,ct);
the flow is below T_COLD if (temp > T_HOT)
mu_laminar = MU_HOT;

else if (temp > T_COLD)


mu_laminar = MU_COLD + (temp - T_COLD)*
The viscosity varies linearly (MU_HOT - MU_COLD)/(T_HOT - T_COLD);

between T_HOT and T_COLD else /* temp <= T_COLD */


mu_laminar = MU_COLD;

return mu_laminar;
}

23 © 2012 ANSYS, Inc. March 14, 2012 Release 14.0


Example: Temperature Dependent Viscosity

Warm fluid enters the channel flowing from left to right.


Viscosity increases as the fluid is cooled by contact with the cold
upper wall.

Contours of molecular viscosity (kg/ms)

24 © 2012 ANSYS, Inc. March 14, 2012 Release 14.0


Time Step: DEFINE_DELTAT
• In unsteady flows, Fluent allows the
timestep to be changed based on
convergence criteria

• Alternatively, the timestep can be set


according to any algorithm using a
DEFINE_DELTAT UDF.

25 © 2012 ANSYS, Inc. March 14, 2012 Release 14.0


Time Step: DEFINE_DELTAT
• If a case is known to require a smaller timestep when starting, then the
timestep can be ramped up slowly. This is useful in cases where the run
cannot be monitored, such as batch jobs.

#include "udf.h"

#define START_UP_TIME 0.5


#define START_UP_TIMESTEP 0.1
#define MAIN_TIMESTEP 0.2

DEFINE_DELTAT(start_up_time_steps, domain)
{
real flow_time;
real time_step;

flow_time = CURRENT_TIME;

if (flow_time < START_UP_TIME)


time_step = START_UP_TIMESTEP;

else
time_step = MAIN_TIMESTEP;

return time_step;
}

26 © 2012 ANSYS, Inc. March 14, 2012 Release 14.0


The Most Commonly Used UDFs in Fluent
Boundary Conditions • DEFINE_PROFILE
Initial Conditions • DEFINE_INIT
Adjust Function • DEFINE_ADJUST
Source Terms • DEFINE_SOURCE
Material Properties • DEFINE_PROPERTY
Execute on Demand • DEFINE_ON_DEMAND
Execute at Exit • DEFINE_EXECUTE_AT_EXIT
Execute at End • DEFINE_EXECUTE_AT_END
Execute from GUI • DEFINE_EXECUTE_FROM_GUI
Execute on Loading • DEFINE_EXECUTE_ON_LOADING
Time step Function • DEFINE_DELTAT

27 © 2012 ANSYS, Inc. March 14, 2012 Release 14.0


Additional Macros
There are many additional, model specific macros
• You can learn more about these from the UDF manual

DEFINE_CHEM_STEP(name, ifail, n, dt, p, temp, yk)


DEFINE_NET_REACTION_RATE(name, p, temp, yi, rr, jac)
DEFINE_NOX_RATE (name, c, t, nox)
DEFINE_PRANDTL_D (name, c, t)
DEFINE_PR_RATE (name, c, t, r, mw, ci, p, sf,
dif_index, cat_index, rr)
DEFINE_SR_RATE (name, f, t, r, my, yi, rr)
DEFINE_VR_RATE (name, c, t, r, mw, yi, rr, rr_t)
DEFINE_TURB_PREMIX_SOURCE (name, c, t, turb_flame_speed, source)

Multiphase specific macros will be discussed later

28 © 2012 ANSYS, Inc. March 14, 2012 Release 14.0


Chapter 4

User Defined Memories and Scalars

14. 0 Release

Advanced Fluent
User‐Defined Functions
1 © 2011 ANSYS, Inc. March 14, 2012 Release 14.0
User Defined Memory
Sometimes it is useful to define extra field variables to store and retrieve
values defined by the user.
User Defined Memories (UDMs) are defined in:
Define → User Defined → User Defined Memory

• Up to 500 UDMs can be used to store and retrieve user values.


• They can be plotted in contours and are saved in the data file.
• These values are not changed by Fluent, only in a UDF with:

• C_UDMI(c,ct,i) = my_cell_value;
• F_UDMI(f,ft,i) = my_face_value; (Only on boundary faces)
2 © 2011 ANSYS, Inc. March 14, 2012 Release 14.0
User Defined Scalars
In addition to the Continuity, Momentum Equations and Energy
Equations, up to 50 user defined generic scalar transport
equations can be solve.
Each scalar follows the generic transport equation:

Unsteady Flux Diffusion Sources

• To include scalars in the calculations use:


Define → User Defined → User Defined Scalars

• Define the unsteady, flux, diffusion, and


source terms in the generic transport equation with UDFs

3 © 2011 ANSYS, Inc. March 14, 2012 Release 14.0


User Defined Scalars
Each scalar can be set to only be calculated in fluid zones, solid
zones, all zones or just selected zones.
Each scalar can have a different Flux Function setting.

4 © 2011 ANSYS, Inc. March 14, 2012 Release 14.0


User Defined Scalars
When a UDS is defined, the diffusivity of the scalar must
be defined in the material property panel for each
material:
Define → Material Property

• The default diffusivity for


all scalars is 1.0
• This constant can be
changed to a different
value or modified using a
DEFINE_DIFFUSIVITY UDF

5 © 2011 ANSYS, Inc. March 14, 2012 Release 14.0


User Defined Scalars
Boundary conditions for the scalars can be defined using:
Define → Boundary Conditions → ....

Options for specifying a


constant value or flux
or use of a UDF Profile
for either.

6 © 2011 ANSYS, Inc. March 14, 2012 Release 14.0


User Defined Scalars
The Source terms for the scalars are set using the standard
DEFINE_SOURCE macro introduced before.

The other terms in the transport equation can be customized


using:
− The Flux term can be modified using:
DEFINE_UDS_FLUX

− The Unsteady term is set using:


DEFINE_UDS_UNSTEADY

Note that the values returned by these two DEFINE_UDS_


UDFs must be multiplied by the material density if density
appears in these terms in the transport equation.
7 © 2011 ANSYS, Inc. March 14, 2012 Release 14.0
DEFINE_UDS_FLUX Example
The following UDF sets the UDS flux according to a velocity field
stored in 3 UDMs. Upwinding could be used instead.
#include "udf.h"

/* Define 3 new macros to make the UDM values more readable */

#define C_UDS_U(C,T)C_UDMI((C),(T),0)
#define C_UDS_V(C,T)C_UDMI((C),(T),1)
#define C_UDS_W(C,T)C_UDMI((C),(T),2)

DEFINE_UDS_FLUX(uds_flux, f, ft, uds_index)


{
Thread *t0, *t1;
cell_t c0, c1;

real NV_VEC(av_vel); /* Average velocity at face */


real NV_VEC(A);
real flux;

t0 = F_C0_THREAD(f,ft);
c0 = F_C0(f,ft);

NV_D(av_vel,=,C_UDS_U(c0,t0),C_UDS_V(c0,t0),C_UDS_W(c0,t0));

Continued on next page...

8 © 2011 ANSYS, Inc. March 14, 2012 Release 14.0


DEFINE_UDS_FLUX Example
...Continued from previous page.

t1 = F_C1_THREAD(f,ft);
if(NNULLP(t1))
{
/* Use velocity on the other side of face to get an average */
c1 = F_C1(f,ft);

/* Add the velocity from the other side to av_vel */


NV_D(av_vel,+=,C_UDS_U(c1,t1),C_UDS_V(c1,t1),C_UDS_W(c1,t1));

/* Set av_vel to the average */


NV_S(av_vel,/=,2.0); /* NV_S can be used to set or scale a vector */
}

F_AREA(A,f,ft);

flux = NV_DOT(av_vec, A); /* Dot velocity with face area to get flux */

/* If the transport equation requires, multiply flux by density here. */

return flux; /* Fluent does any specified upwinding with this flux value */
}

Note that the value of the flux is not multiplied by a UDS value as
Fluent does this according to the sign of flux and the Spatial
Discretization scheme selected in Solve → Methods...
9 © 2011 ANSYS, Inc. March 14, 2012 Release 14.0
Example
The following UDF modifies User-Defined Scalar time derivatives
using DEFINE_UDS_UNSTEADY
The unsteady UDF actually sets a volumetric source term (= −V.dφ/dt ) with
an implicit part and an explicit part:
− V.dφ/dt ≈ (–V/∆t) .φnew + V.φold/∆t
#include "udf.h"

DEFINE_UDS_UNSTEADY(uds_unsteady, c, ct, i, s_imp, s_exp)


{
/* Note the values to be set are passed as pointers: real *s_imp, real *s_exp */
real factor, phi_old;

factor = C_VOLUME(c,t)/CURRENT_TIMESTEP; /* Standard Macro for timestep */


/* If the transport equation requires, multiply factor by density here. */

*s_imp = - factor; /* Implicit part */

phi_old = C_UDSI_M1(c,ct,i); /* Value of UDS at previous time */

*s_exp = factor * phi_old; /* Explicit part */

/* The second order time derivative can be set using C_UDSI_M2 & PREVIOUS_TIMESTEP */
}

10 © 2011 ANSYS, Inc. March 14, 2012 Release 14.0


Post Processing with UDMs
This ADJUST UDF sets a UDM to the magnitude of the
spatial gradient vector of a UDS so that contours and
other post-processing can be done on the gradient.
#include "udf.h"

DEFINE_ADJUST(set_uds0_gradient_magnitude, domain)
{
Thread *ct;
cell_t c;
int index;

index = 0; /* Could do a loop over N_UDS, the number of UDSs */


/* or over N_UDM, the number of UDMs defined. */

thread_loop_c(ct,domain)
{
begin_c_loop(c,ct)
{
/* The gradient vector of many calculated values can be got */
/* by adding _G to the standard variable name: C_T_G(c,ct) */

C_UDMI(c,ct,index) = NV_MAG(C_UDSI_G(c,ct,index));
}
end_c_loop(c,ct)
}
}

11 © 2011 ANSYS, Inc. March 14, 2012 Release 14.0


Chapter 5

UDFs for the Discrete Phase Model

14. 0 Release

Advanced Fluent
User‐Defined Functions
1 © 2011 ANSYS, Inc. March 14, 2012 Release 14.0
DPM Macros (1)
The most important datatype for DPM model is Tracked_Particle
Tracked_Particle *p;
DPM tracks particles by performaing “Lagrangian” tracking
Particle data at current position
• P_DIAM(p) Particle diameter
• P_VEL(p)[I] Particle Velocity
• P_T(p) Particle Temperature
• P_RHO(p) Particle density
• P_MASS(p) Particle mass
• P_TIME(p) Current time for particle
• P_DT(p) Particle time step
• P_LF(p) Particle liquid fraction
• P_VFF(p) Particle volatile fraction

Many more particle data macros are available


2 © 2011 ANSYS, Inc. March 14, 2012 Release 14.0
DPM Macros (2)
Values of particle properties at entry to current cell
– P_DIAM0(p) Diameter
– P_VEL0(p)[i] Velocity
– P_TO(p) Temperature
– P_RHO0(p) Density
– P_MASS0(p) Mass
– P_TIME0(p) Time
– P_LF0(p) Liquid fraction
Values of particle properties at injection into domain
– P_INIT_DIAM(p) Diameter
– P_INIT_MASS(p) Mass
– P_INIT_RHO(p) Density
– P_INIT_TEMP(p) Temperature
– P_INIT_LF(p) Liquid fraction

3 © 2011 ANSYS, Inc. March 14, 2012 Release 14.0


DPM Macros (3)

– P_EVAP_SPECIES_INDEX(p) Evaporating species index in mixture


– P_DEVOL_SPECIES_INDEX(p) Devolatilizing species index in mixture
– P_OXID_SPECIES_INDEX(p) Oxidizing species index in mixture
– P_PROD_SPECIES_INDEX(p) Combustion product species index in
mixture
– P_CURRENT_LAW(p) Current law index
– P_NEXT_LAW(p) Next particle law index

4 © 2011 ANSYS, Inc. March 14, 2012 Release 14.0


DPM Macros (4)
Material Properties for particles

– P_MATERIAL(p) Material pointer for particles


– DPM_SWELLING_COEFFI(p) Swell coefficient for devolatilization
– DPM_EMISSIVITY(p) Particle radiation emissivity
– DPM_SCATT_FACTOR(p) Particle radiation scattering factor
– DPM_EVAPORATION_TEMPERATURE(p)Evaporation temperature
– DPM_BOILING_TEMPERATURE(p) Boiling temperature
– DPM_LATENT_HEAT(p) Latent Heat
– DPM_HEAT_OF_PYROLYSIS(p) Heat of pyrolysis
– DPM_HEAT_OF_REACTION(p) Heat of reaction
– DPM_VOLATILE_FRACTION(p) Volatile fraction
– DPM_CHAR_REACTION(p) Char fraction
– DPM_SPECIFIC_HEAT(p, t) Specific Heat at temperature t

5 © 2011 ANSYS, Inc. March 14, 2012 Release 14.0


DPM Functions (1)
• The following interactions on particles can be customized by UDFs:

– Body force ‐ custom body forces on the particles


– Drag ‐ user defined drag coefficient between
particles and fluid
– Source Terms ‐ access particle source terms
– Output ‐ user can modify what is written out to the
sampling plane output
– Erosion ‐ called when particle encounters
“reflecting” surface
– DPM Law ‐ custom laws for particles
– Scalar Update ‐ allows users to update a scalar every
time a particle position is updated
– Switch ‐ change the criteria for switching between laws

6 © 2011 ANSYS, Inc. March 14, 2012 Release 14.0


DPM Functions: An Example
The function below implements a custom law to model swelling.
“p” is a pointer to data structure of type Tracked_Particle
#include "udf.h“

DEFINE_DPM_LAW(Evapor_Swelling_Law, p, ci)
{
real swelling_coeff = 1.1;
/* First, call standard evaporation routine
to calculate mass and heat transfer */
Vaporization_Law(p);

/* Compute new particle diameter and density */


P_DIAM(p) = P_INIT_DIAM(p)*(1.0 + (swelling_coeff - 1.0)*
(P_INIT_MASS(p) - P_MASS(p))/
(DPM_VOLATILE_FRACTION(p)*P_INIT_MASS(p)));
P_RHO(p) = P_MASS(p) /
(M_PI*P_DIAM(p)*P_DIAM(p)*P_DIAM(p)/6.0);

/* Limit the density */


P_RHO(p) = MAX(0.1, MIN(1.0e5, P_RHO(p)));
}
7 © 2011 ANSYS, Inc. March 14, 2012 Release 14.0
Chapter 6

UDFs for Multiphase Flows

14. 0 Release

Advanced Fluent
User‐Defined Functions
1 © 2011 ANSYS, Inc. March 14, 2012 Release 14.0
The Multi‐Domain Architecture
• In multiphase flows each phase has its set of material properties and may have
its own set of variables
– For VOF model:  k , Yki

– For Mixture model:  k , U k , Yki

– For Eulerian model:  k , U k , Tk , k k ,  k , Yki

• Some variables and geometry are shared among phases.


– In VOF it is U, T, k, ε. In mixture model, T, k, ε, and in Eulerian k and ε.

• Therefore, an architecture that stores the phases in separate overlaid domains


was devised.

• This multi‐domain construction can be thought of as multiple versions of Fluent


running together, but which communicate between each other at the level of the
cells.
2 © 2011 ANSYS, Inc. March 14, 2012 Release 14.0
The Multi‐Domain Architecture
• All of the domains are in a hierarchy that has the super domain at the top.
• The super domain is where the mixture of the phases is stored and so is
often called the mixture domain.
• Shared values such as global settings are stored in the super domain.
• Each phase has its own domain known as a sub domain or phase domain.

Superdomain

P0 Domain P1 Domain P2 Domain Phase Subdomains

Phase Domain Interaction

3 © 2011 ANSYS, Inc. March 14, 2012 Release 14.0


The Multi‐Domain Architecture
• Each Domain, Superdomain or Phase Domain, has its own version of all
the threads which each have their own cells or faces.

Superdomain Superthreads Cells or Faces

P0 Domain P0 Threads P0 Cells or Faces

P1 Domain P1 Threads P1 Cells or Faces

P2 Domain P2 Threads P2 Cells or Faces

4 © 2011 ANSYS, Inc. March 14, 2012 Release 14.0


The Multi‐Domain Architecture
• Each thread is also in a hierarchy that matches that of the domains.
• The superthreads are where the mixture of the phases is stored and so
are often called the mixture threads.
• Shared values such as the grid geometry are stored in the super thread.
• Each phase has its own set of threads known as a sub threads or phase
thread.

Superthreads

P0 Threads P1 Threads P2 Threads Phase Sub threads

Phase Thread Interaction

5 © 2011 ANSYS, Inc. March 14, 2012 Release 14.0


The Multi‐Domain Architecture
• Because the threads are all duplicated for each phase, each cell is also
duplicated.

• The advantage of this is that the same value for cell c corresponds to the
same cell on different duplicated threads, so for a multiphase problem
with 3 phases there will be three phase threads: t_primary, t_secondary1,
and t_secondary2.

/* Mixture or Phase thread can be used for loop */


begin_c_loop(c,t_primary)
{
Message(“Total VOF = %f = 1.0\n”,
C_VOF(c,t_primary) +
C_VOF(c,t_secondary1) +
C_VOF(c,t_secondary2))
}
end_c_loop(c,t_primary)
6 © 2011 ANSYS, Inc. March 14, 2012 Release 14.0
The Multi‐Domain Architecture
• Some data that is useful to the super threads and the phase threads is
available to both types of the thread.

• The data is not repeated, but instead each thread points to the same
data.
– The most common example is the geometry thread.

• Many UDFs are passed a thread as a parameter. This thread is either a


phase thread or a superthread. In many cases, we may want the
superthread of a phase thread or all the phase threads of a superthread.

• There is a new set of macros to get this information about the hierarchy.
These are discussed next.

7 © 2011 ANSYS, Inc. March 14, 2012 Release 14.0


Architecture‐Related Macros
• If we are passed a thread, phase_t, that we know is a phase thread, we
can obtain its super thread using:

Thread *super_t; /* All threads are of the same type */


super_t = THREAD_SUPER_THREAD(phase_t);

• We can obtain the domain which owns a thread using


Domain *d;
d = THREAD_DOMAIN(t);

8 © 2011 ANSYS, Inc. March 14, 2012 Release 14.0


Architecture‐Related Macros
• If, as in the case of the DEFINE_ADJUST UDF, we are passed the super domain,
then we need some methods for obtaining the phase domains.
• We might want to obtain the primary phase domain from the super domain:

Domain *d_p0;
d_p0 = DOMAIN_SUB_DOMAIN(superdomain, p_index);

where p_index is 0 for primary phase; 1 for the first secondary phase, 2 for the
second etc.

Thread *t_phase;
t_phase = THREAD_SUB_THREAD(superthread, p_index);

• To get a sub thread for a secondary phase we can use a similar macro and specify
the phase index, p_index.

9 © 2011 ANSYS, Inc. March 14, 2012 Release 14.0


Architecture‐Related Macros
• The domain ID is another way that can be used to specify which phase
you want to use or check which phase you are working with.

• We can get a Domain with a specified ID using the function Domain


*Get_Domain(int id)

• If you already have a domain, the macro DOMAIN_ID(d) returns the


integer ID of a domain.

Domain *d;
int d_id;
d_id = DOMAIN_ID(d);

10 © 2011 ANSYS, Inc. March 14, 2012 Release 14.0


Architecture‐Related Macros
• The number that is returned by DOMAIN_ID(d) and the number that
should put into Get_Domain(id) are directly related to the phases that
have been set up.

• To see the correspondence between the phases and Domain IDs, look at
the Phases panel.
P_index
0
1
2 The corresponding
Domain ID is shown here

(Note how they may


not be consecutive )

11 © 2011 ANSYS, Inc. March 14, 2012 Release 14.0


Architecture‐Related Macros
#include "udf.h"
• We now have a way
of checking if a #define RADIUS 1.0
#define WATER_ID 4 /* Get ID from Phases panel */
domain or the
domain to which a /* INIT UDFs always get sent the mixture domain */
DEFINE_INIT(init_sphere, mixture_domain)
thread t belongs is {
the water phase cell_t c;
Thread *phase_ct;
domain Domain *phase_domain;
real pos[ND_ND], centre[ND_ND];

phase_domain = Get_Domain(WATER_ID);

NV_D(centre,=,0.0,0.5,0.5); /* Set the sphere's centre */

thread_loop_c(phase_ct, phase_domain)
{

Continued on next page......

12 © 2011 ANSYS, Inc. March 14, 2012 Release 14.0


Architecture‐Related Macros
• Some information, Continued from previous page......

such as cell begin_c_loop(c,phase_ct)


geometry, is {
/* Geometric information is still available
available on sub‐ on the sub-thread cells */
threads too. C_CENTROID(pos,c,phase_ct);
NV_VV(pos,=,pos,-,centre);

if(NV_MAG(pos) < RADIUS)


C_VOF(c,phase_ct) = 1.0;
else
C_VOF(c,phase_ct) = 0.0;
}
end_c_loop(c,phase_ct)

} /* End of Thread Loop */


} /* End of DEFINE_INIT UDF */

13 © 2011 ANSYS, Inc. March 14, 2012 Release 14.0


Obtaining Data for a Specific Phase
• Several examples on how to access phase‐specific variables will be
discussed.

• Consider a simple example of a source term for the momentum equation.


Looking at the basic form there is no obvious way the correct phase
thread is obtained:

DEFINE_SOURCE(x_mom_src, c, thread, dS, eqn)


{
real source
real constant = 1.0;

source = -constant * C_U(c,thread) * C_VOF(c,thread);


dS[eqn] = constant * C_VOF(c,thread);
return source;
}

14 © 2011 ANSYS, Inc. March 14, 2012 Release 14.0


Obtaining Data for a Specific Phase
• This UDF is hooked to the momentum equation of the phase of interest in
the Eulerian model. This sets the argument thread to the phase thread
and C_U(cell,thread) is equal to the velocity component of this
phase and C_VOF(cell,thread) is the volume fraction of this phase.

• To make this clearer, we rename the UDF and parameter thread, to


phase_ct
DEFINE_SOURCE(x_water_mom_src, c, phase_ct, dS, eqn)
{
real source;
real constant = 1.0;

source = -constant * C_U(c, phase_ct) * C_VOF(c, phase_ct);


dS[eqn] = constant * C_VOF(c, phase_ct);
return source;
}

15 © 2011 ANSYS, Inc. March 14, 2012 Release 14.0


Obtaining Data for a Specific Phase
• If the UDF is hooked to the mixture momentum equation in either the
VOF or mixture model, then C_U(cell,thread) is equal to the x‐
velocity of the mixture phase.

• Note: C_VOF(cell,thread) does not exist for mixtures.

• Next, imagine that the source UDF is hooked to a phase momentum


equation but we also need to access variables related to other phases
(mixture and others).
For instance:
S x  C U secondary  U primary 

The code will look like the following:


16 © 2011 ANSYS, Inc. March 14, 2012 Release 14.0
Obtaining Data for a Specific Phase
#define CONST 10.0

DEFINE_SOURCE(x_water_mom_src, cell, phase_t, dS, eqn)


{
Thread *mt; /* Mixture or Super thread */
Thread **all_phase_threads; /* An array of all the phase threads */

mt = THREAD_SUPER_THREAD(phase_t);
all_phase_threads = THREAD_SUB_THREADS(mt);

/* all_phase_threads[0] is primary thread, */


/* all_phase_threads[1] first secondary phase thread */

return CONST * (C_U(cell, all_phase_threads[0])-


C_U(cell, all_phase_threads[1]));
}

17 © 2011 ANSYS, Inc. March 14, 2012 Release 14.0


Obtaining Data for a Specific Phase
• In order to access phase related variables correctly in a UDF, one needs to clearly
know the following:
– Which multiphase model one is going to use the UDF with.
– Whether the variable or property of interest is associated with the phase or
associated with the mixture.

• Below is a table which summarizes phase association for variables in Fluent


multiphase models (Pressure is always related to the Mixture)

Scalar
Mass Momentum Energy
Model / (Turbulence
(Volume (Velocity (Temperature,
equation fraction) variables,
components) enthalpy)
scalars)
Eulerian Phase Phase Phase Phase/Mixture
Mixture Phase Mixture Mixture Mixture
VOF Phase Mixture Mixture Mixture

18 © 2011 ANSYS, Inc. March 14, 2012 Release 14.0


Phase Interaction
• We have briefly mentioned how the interaction between phases involves
another set of domains called interaction domains.
• Even an advanced user is unlikely to need to know more about the lower level
operations within Fluent than has been discussed already.
• However, users will sometimes want to modify the interaction behavior between
phases and this is done through the
Phase Interaction panel:

19 © 2011 ANSYS, Inc. March 14, 2012 Release 14.0


Phase Interaction
• The Phase Interaction panel allows the user to provide user‐defined interaction
model instead of one of the standard models governing Interphase Drag, Lift,
Collisions, Slip, Heat Transfer, Mass Transfer, Reactions and Surface Tension

20 © 2011 ANSYS, Inc. March 14, 2012 Release 14.0


Phase Interaction
• There are 5 multiphase macros for customizing interfacial
heat/mass/momentum transfer

• Four macros return a scalar exchange rate


– DEFINE_CAVITATION_RATE
– DEFINE_EXCHANGE_PROPERTY
– DEFINE_HET_RXN_RATE
– DEFINE_MASS_TRANSFER

• And one prescribes the interphase slip velocity vector


– DEFINE_VECTOR_EXCHANGE_PROPERTY

• Each prescribes a specific interaction between pairs of phases


– NOTE that if there are N phases, there will be N (N – 1) / 2 pairs of phases
21 © 2011 ANSYS, Inc. March 14, 2012 Release 14.0
Example – Define Cavitation Rate
• The rate of cavitation (kg/(m3∙s)) can be prescribed using:
DEFINE_CAVITATION_RATE

#define C_EVAP 1.0


#define C_COND 0.1

DEFINE_CAVITATION_RATE(cav_rate, c, ct, p, rhoV, rhoL, vofV, p_v, cigma,


f_gas, m_dot)
{
real p_vapor, dp, dp0, source;

p_vapor = my_vapor_pres(*p_v, C_R(c,ct), C_K(c,ct)); /* Defined elsewhere */


dp = p_vapor - (C_P(c,ct) + op_pres); /* op_pres is a global value for
current operating pressure */
dp0 = fabs(dp);
dp0 = MAX(0.1,dp0);

source = dp0*sqrt(rhoL[c] * 2.0/3.0);/* Some data passed in arrays */

if(dp > 0.0) *m_dot = C_EVAP*rhoV[c]*source;


else *m_dot = -C_COND*rhoL[c]*source;
}

22 © 2011 ANSYS, Inc. March 14, 2012 Release 14.0


Example – Define Cavitation Rate
• It’s good practice to move auxilliary calculations into new functions.
• In this case, the vapor pressure is quite a complex function with its own
constants which is probably going to be used in other places so should be
made into a new function:

#define VC1 0.195


#define VC2 5.0

real my_vapor_pres(real fluent_p_v, real dens, real turb_k)


{
real p1, p2;

p1 = VC1 * dens * turb_k;


p2 = VC2 * fluent_p_v;

return fluent_p_v + MIN(p1,p2); /* MIN is a standard macro in Fluent */


}

23 © 2011 ANSYS, Inc. March 14, 2012 Release 14.0


Phase Interaction
• Be aware of the values Fluent expects to be returned by
DEFINE_EXCHANGE_PROPERTY UDFs for every phase interaction:

– For the drag law :


 pq  
Fdrag  K pq (u p  uq ) K pq must be returned
– For the lift force :
  
K lift  CL  s f uf  us     uf CL must be returned

– For Heat transfer :


Qpq  hpq Tp  Tq  hpq must be returned

24 © 2011 ANSYS, Inc. March 14, 2012 Release 14.0


Phase Interaction – Mass Transfer Rate
• You can model mass transfer between phases , such as boiling or simple reactions, using
DEFINE_MASS_TRANSFER
• The following UDF specifies a simple boiling rate :
#define BOIL_CONST 0.1

DEFINE_MASS_TRANSFER(simple_boiling, c, mix_ct, from_phase_index, from_species_index,


to_phase_index, to_species_index)
{
real mass_transfer;
real T_SAT, T_gas, T_liq;
Thread *gas , *liq;

gas = THREAD_SUB_THREAD(mix_ct, from_phase_index);


liq = THREAD_SUB_THREAD(mix_ct, to_phase_index);
T_gas = C_T(c,gas);
T_liq = C_T(c,liq);
T_SAT = 373.15;

mass_transfer = 0.0;

if(T_gas < T_SAT) /* Gas Condenses, mass transfer positive from gas to liquid */
mass_transfer += BOIL_CONST*C_VOF(c,gas)*C_R(c,gas)*(T_SAT - T_gas)/T_SAT;

if(T_liq > T_SAT) /* Liquid Evaporates, mass transfer in negative */


mass_transfer -= BOIL_CONST*C_VOF(c,liq)*C_R(c,liq)*(T_liq - T_SAT)/T_SAT;

return(mass_transfer);
}
25 © 2011 ANSYS, Inc. March 14, 2012 Release 14.0
Phase Interaction – Define Heat Transfer Rate
• The heat transfer rates between phases as well as drag and lift coefficient
functions can be set : DEFINE_EXCHANGE_PROPERTY

• Eulerian model – net heat transfer rate, drag and lift coefficients
• Mixture model – drag coefficient only.

DEFINE_EXCHANGE_PROPERTY(heat_exchange, c, mix_ct, i, j)
{
Thread *ti, *tj;

ti = THREAD_SUB_THREAD(mix_ct, i); /* Continuous phase thread */


tj = THREAD_SUB_THREAD(mix_ct, j); /* Dispersed phase thread */

/* Use another function for the interphase Heat Transfer Coefficient */

return heat_ranz_marshall(c, ti, tj);


}

26 © 2011 ANSYS, Inc. March 14, 2012 Release 14.0


Phase Interaction – Define Heat Transfer Rate
• The interphase HTC is defined as a separate function :
/* Macros can be used to define simple functions. */
/* Each parameter is protected in extra brackets. */

#define PR_NUMBER(CP,MU,K)((CP)*(MU)/(K))
#define IP_HEAT_COEFF(VOF,K,NU,D)(6.0*(VOF)*(K)*(NU)/((D)*(D)))

static real heat_ranz_marshall(cell_t c, Thread *ti, Thread *tj)


{
real d, k;
real vel[ND_ND];
real Re, Pr, Nu;

d = C_PHASE_DIAMETER(c,tj); /* Particulate phase */


k = C_K_L(c,ti); /* Continuous phase */

/* Relative velocity between phases */


NV_DD(vel,=,C_U(c,ti),C_V(c,ti),C_W(c,ti),-,C_U(c,tj),C_V(c,tj),C_W(c,tj));

Re = RE_NUMBER(C_R(c,ti), NV_MAG(vel),d,C_MU_L(c,ti)); /* Standard Macro */


Pr = PR_NUMBER(C_CP(c,ti),C_MU_L(c,ti),k);
Nu = 2.0 + 0.6 * sqrt(Re) * cbrt(Pr); /* cbrt is the same as pow(Pr,1./3.) */

return IP_HEAT_COEFF(C_VOF(c,tj),k,Nu,d);
}
27 © 2011 ANSYS, Inc. March 14, 2012 Release 14.0
Chapter 7

UDFs for Calculations in Parallel

14. 0 Release

Advanced Fluent
User‐Defined Functions
1 © 2011 ANSYS, Inc. March 14, 2012 Release 14.0
Parallel Fluent Architecture
• Host process is labeled
999999
• Cortex is the front end
process which connects
only to the host process
• Compute Node processes
are labeled consecutively
starting at 0
• Each compute node is
virtually connected to
every other compute
node by the Message
Passing (MP) interface

2 © 2011 ANSYS, Inc. March 14, 2012 Release 14.0


Command Transfer in Parallel Fluent
Compute-Node-1
scheme
Cortex Host

“(%iterate 5)”

“(%iterate 5)”
Compute-Node-0 Compute-Node-2

• Host communicates with


Node 0
• Node 0 communicates with
all other nodes
• All Nodes (>=1) Compute-Node-3
communicates with host
through Node 0
3 © 2011 ANSYS, Inc. March 14, 2012 Release 14.0
Parallelizing a Serial UDF code
• In general, UDF codes need to be parallelized in order to make sure the codes are
executed properly as either a host or node process

• To parallelize a UDF we need to modify a serial code such that it will run
successfully on both serial and parallel modes

• We use compiler directives (preprocessor commands) inserted into the original C


code to distinguish which part of the program is executed by the host or by the
nodes when the UDF is compiled

• The syntax of the #if compiler directive is quite simple:

#if is a compiler directive (similar to #define)

#endif is used to close an #if


4 © 2011 ANSYS, Inc. March 14, 2012 Release 14.0
Basic Compiler Directives
Different parts of the UDF code have to be performed on either the host or
node processes. These parts can be selected using:

#if RP_HOST
/* Coding here only performed on HOST process */
#endif

#if RP_NODE
/* Coding here only performed on NODE processes */
#endif

#if PARALLEL
/* Coding here only performed on HOST & NODE processes */
#endif

5 © 2011 ANSYS, Inc. March 14, 2012 Release 14.0


Negated Compiler Directives
Since many of the operations will also be required in the serial version, the
negated versions are more commonly used:

#if !PARALLEL
/* Coding here only performed on SERIAL process */
#endif

#if !RP_HOST
/* Coding here only performed on NODE & SERIAL processes */
#endif

#if !RP_NODE
/* Coding here only performed on HOST & SERIAL processes */
#endif

6 © 2011 ANSYS, Inc. March 14, 2012 Release 14.0


Partitioning (1)
• Domain Decomposition : Splits the cells in the domain across Compute Nodes
• Because Fluent’s algorithms expect a cell to be on both sides of an interior face,
copies of the neighboring partition’s cells are kept on each Node
• Compute Node 0 has copies of the cells on the other side of all partition faces
and Compute Node 1 has corresponding cell copies from Node 0

Compute Node 0 Compute Node 1

Domain Decomposition Distribution across Compute


Nodes

7 © 2011 ANSYS, Inc. March 14, 2012 Release 14.0


Partitioning (2)
• The main cells of each partition are designated as “Interior” cells and the
additional copied cells from other Compute Nodes are designated as “Exterior”
cells. The Partition Boundary Faces are a special type of Interior face

Compute Node 0 Surface Boundary


Zone Face
Partition Boundary Face

Interior Faces

Exterior Cell

Interior Cells

8 © 2011 ANSYS, Inc. March 14, 2012 Release 14.0


Partitioned Thread Loop (1)
• The loop macro for cells in serial is:

begin_c_loop(c,t)
{ }
end_c_loop(c,t)

• In parallel, this serial form loops through both interior and exterior cells
• Use begin_c_loop_int(c,t) in UDFs that are to be parallelized:
begin_c_loop_int(c,t)
{
}
end_c_loop_int(c,t)
• This loop excludes the exterior cells so they don’t get visited twice.
• Another loop construct loops through the exterior cells only :
begin_c_loop_ext(c,t)
{
}
end_c_loop_ext(c,t)
but it is rarely used in UDFs and does nothing if compiled in the serial version
9 © 2011 ANSYS, Inc. March 14, 2012 Release 14.0
Partitioned Thread Loop (2)
• Similar loops exist for faces:
begin_f_loop_all(f,t)
{…}
end_f_loop_all(f,t)

begin_f_loop_int(f,t)
{…}
end_f_loop_int(f,t)

• But these have a slightly different use so instead, use the standard loop and check to see if
the face is allocated to this node by using PRINCIPAL_FACE_P(f,t):

begin_f_loop (f,t)
{
if(PRINCIPAL_FACE_P(f,t))
{…Only gets done once per face…}
}
end_f_loop(f,t)

10 © 2011 ANSYS, Inc. March 14, 2012 Release 14.0


Inter-Process Communication (1)
• Each compute node maintain local cache of individual variables
• Synchronization or make global reduction of such data involves
communication in a particular order
• Consider the simple operation of passing a user defined cortex
parameter that is set using scheme but is used in a UDF

Serial Version
#include “udf.h“

DEFINE_INIT(init_temp, domain)
{
real init_temp; /* Variable declaration */
Thread *ct;
cell_t c;

init_temp = RP_Get_Real(“my-init-temp“); /* Scheme communication */

thread_loop_c(ct,domain) /* Solution data handling */


begin_c_loop(c,ct)
{
C_T(c,ct) = init_temp;
}
end_c_loop(c,ct)
}
11 © 2011 ANSYS, Inc. March 14, 2012 Release 14.0
Inter-Process Communication (2)
Combined (Serial & Parallel ) Code
#include “udf.h“

DEFINE_INIT(init_temp, domain)
{
real init_temp; /* Variable declaration. Some variables used in all versions. */

#if !RP_HOST
Thread *ct; /* These variable only used on the nodes and in serial */
cell_t c;
#endif

#if !RP_NODE
init_temp = RP_Get_Real(“my-init-temp“); /* Scheme communication on host and serial */
#endif

#if PARALLEL
host_to_node_real_1(init_temp); /* Host sends data to nodes (Does nothing in serial) */
#endif

#if !RP_HOST
thread_loop_c(ct,domain) /* Solution data handling only on nodes and serial */
begin_c_loop(c,ct)
{
C_T(c,ct) = init_temp;
}
end_c_loop(c,ct)
#endif
}
12 © 2011 ANSYS, Inc. March 14, 2012 Release 14.0
Inter-Process Communication (3)
• The macro host_to_node_real_1(init_temp)is defined as
a Send command in the Host version and a Receive command in
the Compute Nodes. It does nothing in the Serial version so the
#if PARALLEL directive is not really needed in this case.

• The reverse node to host data transfer macro for 1 real number is
node_to_host_real_1(report_temp)
Note however that this command only sends the value of
report_temp from Node‐0 to the Host.
(It does nothing in the serial version)
So, some way of gathering data onto Node‐0 before it is sent is
required.

13 © 2011 ANSYS, Inc. March 14, 2012 Release 14.0


Global Reduction
• Operations that collect data from all compute nodes and reduce the numbers into a single
value or an array of values are called Global Reduction macros
• Depending the objectives,
– If you want the total value over all the Nodes, use a Summation Reduction
– If you want the Max or Min over all the Nodes use a High or Low Reduction
– If you want a logical test over all nodes, use an And or Or Reduction

• There are different macros depending on what data type you’re sending:

14 © 2011 ANSYS, Inc. March 14, 2012 Release 14.0


Example UDF (1)
• Find totals and averages of
a property over all the
cells

• Purpose is to write an UDF


that works for both
Parallel and Serial solvers

15 © 2011 ANSYS, Inc. March 14, 2012 Release 14.0


Message0
• Message0() prints directly to the cortex window from Node‐0.
It is ignored on all other compute nodes and the host node.

• Works for the serial process in the same way as Message()

Message0("Average pressure over Thread-%d ",thread_id);


Message0("is %f Pa\n",pres_sum/vol_sum);

• Cuts done on inter‐process communication for messages.


• Note the identical syntax of the function Message0 with Message and
the C standard printf commands

16 © 2011 ANSYS, Inc. March 14, 2012 Release 14.0


Summary
• A UDF that works for the Serial solver will usually not work in Parallel
without some modification.
• The UDF has to be separated into sections that will be done on either the
host or on the node (and on the serial version).
• This can be done in a single file with compiler directives so the same UDF
will work in serial too.
• Some extra communication functions between the host and nodes may
be needed.
• Results data may need to be “reduced” before being reported.

• There is a comprehensive dedicated chapter in the UDF Manual for


additional information and examples for using UDFs in parallel Fluent.

17 © 2011 ANSYS, Inc. March 14, 2012 Release 14.0

You might also like