0% found this document useful (0 votes)
660 views47 pages

UDF Mechanics

This document provides an overview and introduction to user-defined functions (UDFs) in Fluent for programming and implementing UDFs in serial and parallel. It covers compiling and using UDFs, programming basics, extending UDFs to parallel, limitations, and includes examples of sample UDF code.
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)
660 views47 pages

UDF Mechanics

This document provides an overview and introduction to user-defined functions (UDFs) in Fluent for programming and implementing UDFs in serial and parallel. It covers compiling and using UDFs, programming basics, extending UDFs to parallel, limitations, and includes examples of sample UDF code.
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/ 47

UDF Mechanics

A short course in procedures for


programming and implementing FLUENT
UDFs in serial and parallel

William Wangard, Ph.D.

1
Overview
• Introduction to User-Defined Functions
• Compiling and using your UDF
• UDF Programming Basics
• Extending your UDF to FLUENT Parallel
• Scheme Programming
• Limitations

2
Introduction to UDFs
• Programmed with ANSI C (usually)
• Are compiled or interpreted
• Allow you to customize
– Boundary conditions
– Solution controls
– Source terms
– Data analysis
– Customized I/O
– Post processing
Moving mesh via UDF
– And much more…
3
Sample UDF
UDF source code for the previous animation…
Header file Æ #include "udf.h"
(in all UDFs)
/* UDF to rotate inner square boundary */

DEFINE_ UDF function Æ DEFINE_CG_MOTION(rotate, dt, vel, omega, time, dtime)


{
(DEFINE_s have
/* reset velocities */
prescribed
functionality) NV_S (vel, =, 0.0); /* Macro to set vel = 0 */
NV_S (omega, =, 0.0);

/* Set inner square rotating at 1.0 radians/sec */


omega[2] = 1.0; /* 2 is the z-component */
}

4
UDFs: Compiled vs. Interpreted
• Many UDFs require compilation
• Compiled UDFs are recommended
– Faster, especially with UDFs that are called for each
cell in the domain
• Limitations to Compiled UDFs
– Version-specific
• Interpreted UDFs have limited capability and run
slower.
– Acceptable in some applications

5
Compiling UDFs in FLUENT 6.1
• Define→User-Defined→Functions→Compiled…
• Click Add
• Pick source (.c) and header (.h) files
– Make sure that there are no ^Ms in the file (Win/Unix translation)
• Click Build to automatically compile the UDF…
– Don’t forget to check for compilation errors!
• Click Load to link the UDF to the FLUENT
process
6
UDF Directory Structure (1)
• The UDF is built in a special directory
– Default directory is ./libudf
– Sometimes between compiles, you need to work with
more than 1 library between bug fixes.
• The UDF will be compiled for the version of
FLUENT currently running: e.g. 2ddp, etc.
– Other versions will be updated if they exist in the
directory structure when you click Build.
– Thus, to build a parallel version, start up Fluent
parallel, and repeat the build process.
7
What’s your UDF Programming Level?
• Beginner
– Basic understanding of ANSI C
• Knows what a pointer is and what to do with it
– FLUENT UDF Macros
• Expert
– Broader knowledge of ANSI C
• Knows what a function pointer is and how to use it
• Knows that malloc(…) must be accompanied by a free(…)
– Scheme programming for FLUENT GUI
• Guru
– Has memorized Kernighan & Ritchie
– In-depth knowledge of particular models --- funded development
8
Hooking the UDF (1)
• UDF functions are idle until they are “hooked”
• Hook is made through appropriate GUI panel.
• In pick list, choose “name” of DEFINE_ function whose
type is appropriate for the connection:
– For Boundary Conditions: DEFINE_PROFILE
– For Initialization: DEFINE_INIT
– For Execute on Demand: DEFINE_ON_DEMAND
– For Execute at END: DEFINE_EXECUTE_AT_END
– Execute At End is NEW in FLUENT 6.1!
• Performed when convergence is attained.

9
Hooking the UDF (2)
• DEFINE_INIT(my_init, d)
Define→User-Defined→Functions Hooks…
– Pick Initialization Function
– In pick list, choose “my_init” from the list

10
Grid Terminology & Data Types
• cell_t
– Control volume
• face_t
– Control volume face
• Thread
– Group of cell_ts or face_ts
• Domain
– Collection of all threads
• Node
– Corners of control volumes
11
Threads (1)
• Thread is a structure representing collection
of face_ts or cell_ts.
• We work with pointers to the thread.
Thread* t
• Identify type of thread using predicates:
– Defined in threads.h
– CELL_THREAD_P(t) = TRUE if t is a cell
thread
12
Threads (2)
– FLUID_THREAD_P(t) == TRUE if t is a fluid
continuum zone.
– BOUNDARY_FACE_THREAD_P(t) == TRUE if t is
a face thread and if it is a external boundary.
• Thread Identifiers
– ID of thread matches value in the FLUENT GUI
– THREAD_ID(t) is the ID of thread
– THREAD_TYPE(t) is the type of thread
• == THREAD_F_WALL for a wall
• == THREAD_F_VINLET for a velocity inlet

13
The Domain Pointer
• Domain pointer allows access to entire problem
data structure
• Passed to some functions, otherwise use
Domain* d = Get_Domain(1);
• Multiphase CFD analyses use arrays of Domain
pointers… each phase has a domain.

14
UDF Macros
• Purpose of Macros is to allow users’ UDFs to
remain unchanged between FLUENT revisions
• Three classes of Macros
1. DEFINE_XXXX Macros
• Expand to C function definitions that return specific types of
data depending upon nature of function
2. Data Storage Macros
• Access data within FLUENT data structures
3. Utility Macros
• Perform routing tasks such as vector operations, looping, etc.

15
DEFINE_ Macros
• Expand to functions
• General purpose, e.g.
– DEFINE_INIT(my_init, domain)
• Called just after data is initialized
• void my_init(Domain* domain)

• Model Specific/Solution Control, e.g.


– DEFINE_DT(my_dt, domain)
• Control the size of the time step during the solution
• real my_dt(Domain* domain)

16
Threads in DEFINE_ functions
• Type depends on nature of DEFINE_
function:
– In DEFINE_PROFILE, Thread* is a face
thread since boundary condition applied to
faces
– In DEFINE_SOURCE, Thread* is a cell thread
since source terms apply to cells
– Thread is passed through the “hooking” process
17
Data Storage Macros (1)
• Cell Data Macros
– Returns quantities stored at cell centers.
– C_R(c, t) returns the density at cell_t c in (cell)
thread t.
– Other macros for T, U, V, W, enthalpy, etc.
• Face Data Macros
– Returns quantities stored at cell faces
– F_T(f, t) returns the temperature at face_t f of
(face) thread t.
18
Data Storage Macros (2)
• Data for variable is available only if equation is
being solved at least one iteration.
– Attempting to access C_UDSI(c,t,i)will give a
FATAL error if user-defined scalars are not being
solved
• Check using storage allocation macro:
– (NNULLP(T_STORAGE_R_NV(t, SV_UDS_I(i))))
– If TRUE (pointer is NOT NULL), UDS(i) is defined!

• Face quantities are not stored on all faces…


– Ex: Density is not stored at wall boundary faces.
19
Utility Macros (1)
• Vector operations
– About 100 available macros
– Defined in flow.h
– NV_S(a, op, b)
• for(j=0; j<ND_ND; j++) { a[j] op b; }
• op is {= += *= /= -=}
– NV_VS(a, op, b, op2, c)
• for(j=0; j<ND_ND; j++) { a[j] op b[j] op2 c; }
• op2 is {+ - / *}

20
Utility Macros (2)
Purpose Looping Macro
Loop over cell threads → thread_loop_c(t,d){…}

Loop over face threads → thread_loop_f(t,d){…}

Loop over cells → begin_c_loop(c,t) {…}


end_c_loop(c,t)

Loop over faces → begin_f_loop(f,t) {…}


end_f_loop(f,t)

21
C0 and C1
• All faces are connected to one or two cells
– Boundaries have only c0
– Internals have c0 and c1
• Access to c0, c1 and respective threads in a face loop:
– c0 = F_C0(f,tf) where tf is a face thread
– tc0 = F_C0_THREAD(f,tf) … will never be NULL
– tc1 = F_C1_THREAD(f,tf) … may be NULL
– c1 = F_C1(f,tf) … if above is not null!

22
Shadow Threads
• Occur at interface between two dissimilar cell
zones.
– Used often in conjugate heat transfer simulations
– Fluid zone next to solid zone
– Used to simulate heat transfer through thin barrier
“between” cell zones
– The “shadow thread” is accessed by the following:
ts = THREAD_SHADOW(t)

23
Derivative Terms
• Sometimes derivative terms are needed
– C_T_G(c,t) returns gradient of temperature
(a vector) at a cell center
– C_UDSI_G(c,t) returns gradient of UDS
• Particular equation must be activated
• TUI command solve/set/expert
– Keep temporary memory from being freed

24
Computing Gradient of UDS
• Typically used in a define ADJUST function
• UDS is assigned value
• Following code snippet is called to compute the derivatives
of the UDSs… before iteration is actually performed
/* CODE TO TAKE DERIVATIVE of all UDS */
int ns;
for (ns = 0; ns < N_UDS(); ++ns) {
MD_Alloc_Storage_Vars(d, SV_UDSI_RG(ns),SV_UDSI_G(ns),SV_NULL);
Scalar_Reconstruction(d, SV_UDS_I(ns), -1, SV_UDSI_RG(ns), NULL);
Scalar_Derivatives(d, SV_UDS_I(ns), -1, SV_UDSI_G(ns),
SV_UDSI_RG(ns), NULL);
}

• C_UDSI_G data is now populated. Can copy components


to UDM if desired.
25
Parallelization of UDFs
• One UDF that works in serial and parallel
• When is parallelization necessary?
– Transfer of data between processes
• Vectors of data used in your UDF
– Global operations
• Computing quantities over groups of cells or threads
– volume or surface integrals
• Determining global minima or maxima
– File I/O
– Messages
26
Serial Fluent
• 2 Processes
– Cortex (FLUENT GUI)
– Compute node
• Get messages from cortex
• Do computations
• Messaging

27
Parallel FLUENT Architecture
• Cortex (FLUENT GUI)
• Host
– Communicate between cortex
and compute node 0
– No data
• Compute nodes
– Node 0
• Get commands from host
• Do computations
– Other nodes
• Get commands from node 0
• Do computations

28
Parallel Partitioning of Cells
• Interior Cells
– Completely inside partition
boundary
• Exterior Cells
– Abut the partition boundary
and penetrate inside
adjacent partition one layer
deep

29
Parallel Partitioning of Faces
• Interior faces
– Connects cell to cell
• Boundary faces
– Connected to one cell (c0)
• Partition boundary face
– Shared by two partitions
• External face
– Rarely used

30
Parallelization
• In order to parallelize your UDF you’ll need
to be familiar with
– Compiler directives
– Predicates
– Data transfer macros
– Parallel looping macros

31
Compiler Directives (1)
• Compile variables defined in config.h
• Directive variables for FLUENT Parallel
– RP_HOST compile on host
– RP_NODE compile on node
– PARALLEL compile in parallel
• Negated forms also used
– !RP_HOST compile on node and serial
– !RP_NODE compile on host and serial
– !PARALLEL compile in serial

32
Compiler Directives (2)
• Example….
#if RP_HOST
Message(“I am the host node.\n”);
#endif

• Example….
#if !RP_NODE
Message(“I am either the host node or the serial
compute node!\n”);
#endif

33
Predicates in Parallel
• Defined in para.h
• Evaluate to TRUE based on the value of compute
node.
– (I_AM_NODE_ZERO_P)
• Same as… (myid == node_zero)
– Typically used for data transfer between individual
compute nodes.

34
Data Transfer (1)
• Macros defined in para.h
• Host to Compute nodes
host_to_node_type_num(arg1,arg2,…)
– Does nothing in serial
– Communicates num variables between host and all
compute nodes (through node zero)
• type = {int, real, boolean}
• num = {1, 2, …, 7}

35
Data Transfer (2)
• Compute Node 0 to host
node_to_host_type_num(arg1,arg2,…)
• Passing Vectors
host_to_node_type(v,len)
– Communicate type array of length len
– If type is string, don’t forget to include
terminating null character!

36
Basic Steps to Parallelize a UDF
1. Define variables that exist on all nodes 5. Perform Global Reductions
real x[10]; int id; #if RP_NODE
FILE* fp; /* Accumulate x over all nodes */
PRF_GRSUM(x,10,work);
2. Get variables from GUI [optional]. #endif
#if !RP_NODE
x[i] = 0.0
6. Send data back to host [optional]
id = RP_Get_Integer(“id_zone”);
node_to_host_real(x,10)
fp = fopen(”output_file”, ”r”);
#endif
7. Print output
3. Pass variables to compute nodes /* To GUI */
host_to_node_real(x,10); Message0(“x0 = %f\n”, x[0]);
host_to_node_int_1(id);
/* To FILE */
4. Do computations on compute nodes (SERIAL Code) #if !RP_NODE
#if !RP_HOST fprintf(fp, “x[0] = %f\n”, x[0]);
thread_loop_c(t,d) {…} fclose(fp);
#endif
#endif

37
Sample Parallelized UDF (1)
/* UDF to get volume average temp and density */ /* Pass id of thread to nodes */
host_to_node_int_1(id);
#include "udf.h"
#define N 2
/* Main computation section… */
#if !RP_HOST
DEFINE_ON_DEMAND(average_t_and_rho)
{ d = Get_Domain(1);
Domain* d; /* domain pointer */ t = Lookup_Thread(d, id);
Thread* t; /* to be a cell thread */
cell_t c; /* cell */ /* Initialize data on nodes */
voltot = 0.;
#if RP_NODE for(i=0; i<N; i++) x[i] = 0.0;
real work[N]; /* needed in global reduction */
#endif /* loop over all internal cells… avoid overlap */
begin_c_loop_int(c,t)
real x[N]; /* x[0] = temp, x[1] = dens */
{
real vol; /* cell volume */
vol = C_VOLUME(c,t);
real voltot; /* total volume */
int i, id; /* index, id of cell thread */ x[0] += C_T(c,t) * vol;
x[1] += C_R(c,t) * vol;
#if !RP_NODE voltot += vol;
id = 2; }
/* Alternatively... This only works on host node. end_c_loop_int(c,t); /* Don’t forget this! */
id = RP_Get_Integer("my_zone");*/ #endif
#endif
/*… continued on next page … */

38
Sample Parallelized UDF (2)
/* Global Reductions… accumulate summations from
each compute node… totals will be copied to
each compute node */
• Original Serial code in red
#if RP_NODE
/* Accumulate the vector */ • Comments
PRF_GRSUM(x,N,work);
– Original is basically same
/* Accumulate the scalar */
voltot = PRF_GRSUM1(voltot); as in serial…except
#endif
• begin_c_loop(c,t) is now
/* Send data to host */ begin_c_loop_int(c,t)
node_to_host_real(x,N); /* The vector */
node_to_host_real_1(voltot); /* The scalar */ – Data transfer to compute
/* Message to GUI */ nodes and global reductions
#if !RP_NODE
Message("Mean T = %10.3e\n", x[0]/voltot); are typically the main parts
Message("Mean Rho = %10.3e\n", x[1]/voltot);
#endif that need to be added
/* also – Message passing usually
Message0(…); without the compiler directive*/
} /* Done */ done after global reduction.
39
Parallel looping (1)
• Cells on a partition contain internal and external
cells
– Internal: Cells that are exclusive to that partition
– External: Those that overlap into another partition
• In a summation process, we loop over the internal
cells (and exclude the overlap)
begin_c_loop_int(c,t)
{…}
end_c_loop_int(c,t)

40
Parallel Looping (2)
• For face loops, a face may lay on the boundary of
two partitions. In a summation loop, we don’t
want to count it twice! Use the following:
begin_f_loop(f,t)
{
if (PRINCIPAL_FACE_P(f,t))
{ … }
}
end_f_loop(f,t)

41
Scheme Variables
• Used to modify FLUENT GUI
• Defined in var.h
• Access from FLUENT (serial or host node)
var = RP_Get_Type(variable);
• Type: {Real, Integer, Boolean, String (for char*)
Ref_Int_List (for lists), etc.}
• Must define variable in GUI…

42
Defining a Scheme Variable
• Cortex uses object-oriented Scheme (not documented)
• Check if variable has been defined
(define (make-new-rpvar name default type)
(if (not (rp-var-object name))
(rp-var-define name default type #f)))

• Define some “rp vars” (rp stands for rampant)


(make-new-rpvar 'var_real '1.2 'real)
(make-new-rpvar 'var_int '3 'integer)
(make-new-rpvar 'var_int_list '(3 4) 'integer-list)
(make-new-rpvar 'var_string ‘”Hello” 'string)

43
Scheme Variables
• Copy previous lines to the file
“my_vars.scm”
• Load the file all at once to FLUENT using
(load ”my_vars.scm”)

which is a scheme command.

44
Accessing Scheme Variables
real x;
int i, n, len, *ilist;
char* s;

/* Get the variables in serial or HOST node only */


#if !RP_NODE
x = RP_Get_Real(”var_real”);
i = RP_Get_Integer(”var_int”);
len = RP_Get_List_Length(”var_int_list"); /* Get length of list */
ilist = malloc( len * sizeof (int) ); /* allocate int list */
for(n=0; n<len; n++){
ilist[n] = RP_Get_List_Ref_Int(”var_int_list”,n); /* from var.h */
}
s = RP_Get_String(”var_string”);
#endif

/* DO STUFF HERE */

#if !RP_NODE
free(ilist); /* Don’t forget to free the memory allocated */
#endif

45
Modifying a Rpvar in a UDF
• Change rpvar in UDF with
RP_Set_Real(”scheme_var”, x);

• Value in GUI is not automatically updated!


• To update the value in cortex
(%rp-var-value ’scheme_var)

46
What Have We Learned Today?
• How to compile UDFs in • Parallelizing your UDF
FLUENT 6.1 – Compiler directives
• How to hook UDFs – Data transfer
– Messaging
• Basics of UDF
programming: • Scheme variables
– DEFINE_ functions – Defining rpvars in Cortex
(GUI)
– Data accessing macros
– Accessing rpvar values
– Looping macros
from UDF
– Vector operations
– Modifying rpvars in UDF
– Derivative operations and passing back to Cortex
47

You might also like