Professional Documents
Culture Documents
1
Objective
The objective of this course is to show examples of
UDF implementation to solve practical problems
This is not a course on “how to program UDF”, but
rather on “how to apply UDF to real world problem”
It is assumed that auditory has at least preliminary
knowledge of UDF, however we will cover basics of
UDF in several slides
All cases were developed by Fluent staff
Some cases are preceded by short synopsis of
UDF macros that are used
2
Outline
Basic concepts of how UDF is built and used
Regulated temperature at inlet (1)
Modeling of membrane (2)
Staggered injection of particles in silo launch simulation (3)
Basic concepts on variable access in multiphase models
Drag coefficient for spherical cap bubble (4)
Modeling of evaporation from liquid droplets (5)
Basic concepts of parallel UDF
Writing of pressure distribution into file for parallel runs (6)
Moving mesh and variable time step for store separation
example (7)
Appendix
User Defined Memory
Filter blockage modeling for Discrete Particle Model (8)
Customized erosion model for Discrete Particle Model (9)
3
User Access to Fluent Solver
Fluent is so designed that the user can access the solver at some strategic
instances during the solution process Segregated Solver Coupled Solver
Begin User-
Initialize defined Solve U-Momentum
Loop
ADJUST
User Solve V-Momentum Solve Mass
Flow Momentum &
Defined Source terms
Diagram Initialize Source terms Solve W-Momentum
Energy
of
FLUENT Solve Mass Continuity;
Update Velocity
Solvers Exit Loop Repeat
Solve Energy
4
User Defined Functions in Fluent
5
Data Structures in FLUENT
face
Domain
cell cell
Domain Thread
Cell Cell
Cell
6
The Domain
7
Domain and Threads
Wall
Solid-1
Fluid-1
Fluid-2 Porous Outlet
Inlet
Medium
Domain
of
Solid-2
Analysis
Wall
Corresponding Threads
Data set Wall Solid-2
Fluid-2
Inlet
Domain Outlet
Porous
Medium Fluid-1
Solid-1
8
Cell and Face Datatypes
9
Cell & Face Threads; Cell_t & Face_t Datatypes
Boundary face-thread
Fluid cell-thread the boundary-face ensemble
the Control-volume
ensemble Internal face-thread
the Internal-face ensemble
associated to cell-threads
Nodes
10
Boundary Profiles: DEFINE_PROFILE
Arguments
You can use this UDF to It’s a User from the
must! specified solver to
specify name this UDF
#include "udf.h"
Wall
temperature DEFINE_PROFILE(w_profile, thread, position)
heat flux, shear stress {
face_t f;
Inlets real b_val;
velocity
begin_f_loop(f, thread)
temperature {
turbulence b_val = …/* your boundary value*/
species F_PROFILE(f, thread, position) = b_val;
scalars }
end_f_loop(f, thread)
The macro begin_f_loop }
loops over all faces on the
selected boundary thread
thread : The thread of the boundary to
The F_PROFILE macro which the profile is attached
applies the value to face, f on
the thread position : A solver internal variable
(identifies the stack location of
the profile in the data stack)
User can rename the variables at will:
DEFINE_PROFILE(my_prof, t, pos)
11
Inlet Temperature vs exit Temperature (1)
Inlet temperature is a function of mass averaged exit
temperature
Tin = f (Tout )
12
Inlet Temperature vs exit Temperature (1)
13
Inlet Temperature vs exit Temperature (1)
14
Inlet Temperature vs exit Temperature (1)
Tout
Application to flow in elbow with two inlets
Tin
Tin = Tout + 30
15
Membrane modeling (2)
16
Membrane modeling (2)
Membrane is modeled as the inner wall which means that it cannot predict pressure
drop (it is known). Macros F_C0 and F_SHADOW are used to find cell and thread
pointers looking at both sides of membrane, i.e., high and low pressure sides
Having access to variables at cells sharing membrane cell face allows to calculate mass
flux of each species and prescribe volume sources of mass for species i at cells next to
membrane
memb_t _shadow=
THREAD_SHADOW(memb_t) P high cell_t c0= F_C0(f,memb_t)
P low
(f,memb_t)
17
Membrane modeling (2)
18
Membrane modeling (2)
19
Membrane modeling (2)
20
Membrane modeling (2)
Permeance ratio, 1 10
Qgas/Qair
21
Membrane modeling (2)
Mass transfer
rate variation
High species
Species mass concentration
source in UDMI
Velocity vectors
22
Unsteady Staggered Particle Injection (3)
23
Unsteady Staggered Particle Injection (3)
24
Unsteady Staggered Particle Injection (3)
The following UDFs will be needed to apply this trick to moving mesh example
– launch of rocket from silo:
DEFINE_ADJUST
Track the current exhaust location
Accumulate the particles’ mass when injection is not performed at a
timestep
DEFINE_DPM_INJECTION_INIT
Move the injection location
Inject the accumulated mass when injection interval is reached
DEFINE_CG_MOTION
Specify the DM layering motion
DEFINE_RW_FILE
Write injection information to data file for restart
Other functions
Post processing, etc.
25
Unsteady Staggered Particle Injection (3)
#include "udf.h"
#include "dynamesh_tools.h"
/*User specified injection release time interval*/
#define RELEASE_STEP 5e-3
/*Current exhaust location where injection is performed*/
static real current_loc_exh = -3.9624;
#if !RP_NODE
static int tmsp = 0;
static real current_tm = -1.0;
static real sum_inj = 0.0; /*Store the accumulated particles’ mass when
injections are skipped*/
#endif
26
Unsteady Staggered Particle Injection (3)
DEFINE_ADJUST (update, domain)
{
#if !RP_NODE
real tm, tmspsz;
real msl_vel, pflow;
tm = RP_Get_Real("flow-time");
tmspsz = RP_Get_Real("physical-time-step");
if ( first_iteration ) /*first iteration of a timestep*/
{
msl_vel = get_vel_msl(tm); /*Update missile velocity */
current_loc_exh += msl_vel*tmspsz; /*&exhaust location*/
pflow = get_mdot_prt(tm);
sum_inj += mdot*tmspsz; /* Get the current particle flow rate &
store for later release */
current_tm = tm;
tmsp += 1;
}
#endif
}
27
Unsteady Staggered Particle Injection (3)
DEFINE_DPM_INJECTION_INIT (init_prt_tm, I)
{
This UDF is called only when DPM unsteady
#if !RP_NODE
start time is smaller than current time
Particle *p;
real tm, tmspsz, flowrate, eps=0.015;
tm = RP_Get_Real("flow-time");
tmspsz = RP_Get_Real("physical-time-step");
flowrate = sum_inj/tmspsz; /*Release all the particles that are in store since the
last release*/
loop(p,I->p_init) /*beginning of release of particles*/
{
p->state.pos[0] = current_loc_exh - eps; /*Update release points due to
movement of the exhaust location by MDM*/
p->flow_rate = flowrate;
}
if ((tm+tmspsz) >= I->unsteady_start) /*now release is delayed by moving injection time*/
I->unsteady_start += RELEASE_STEP; /*Move unsteady_start forward in time
for next release*/
sum_inj = 0.0; /*Reset storage for mass of particles*/
#endif
}
28
Unsteady Staggered Particle Injection (3)
Application to missile silo launch. Staggered injections of particles are clearly
seen. Contours of Ma number colored by particles with residence time
29
UDFs for Multiphase Flows
30
The Multi-Domain Architecture
31
The Multi-Domain Architecture
All the domains are in a hierarchy that has the “superdomain” at the top.
The superdomain 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 superdomain.
Each Phase has its own Domain known as a “subdomain” or “phase
domain”.
superdomain
32
The Multi-Domain Architecture
33
The Multi-Domain Architecture
34
Getting Data for a Specific Phase
Suppose that my momentum source for primary phase is equal
(arbitrary): source = constant*(U_secondary-U_primary)*P_mixture.
This means that primary phase thread will be passed as argument
and I need to get mixture and secondary threads
DEFINE_SOURCE(x_mom, cell, t, dS, eqn)
{
Thread *tm = THREAD_SUPER_THREAD(t);
/*thread tm will be a superthread, i.e., mixture*/
Thread **pt = THREAD_SUB_THREADS(mixture_thread);
/*pt is an array such that pt[0] is mixture thread, pt[1] first
secondary phase thread, pt[2] second secondary phase thread
etc.*/
real U_secondary = C_U(cell,pt[1]);
real U_primary = C_U(cell,t); /*or U_primary=C_U(cell,pt[0]); */
real P_mixture = C_P(cell,tm); etc.
35
Getting Data for a Specific Phase
Now let’s see how access variables when thread is not passed
as an argument
DEFINE_ADJUST(area_density, domain)
{
Thread *t;
Thread **pt;
cell_t c;
mp_thread_loop_c (t,domain,pt) /* t is a mixture thread*/
if (FLUID_THREAD_P(t ))
{
Thread *tp = pt[0]; /* tp is a primary phase thread*/
Thread *ts = pt[1]; /* ts is a secondary thread*/
begin_c_loop (c,t)
{
C_UDMI(c,t,0) = C_VOF(c,tp)*C_T(c,ts);
}
end_c_loop (c,t)
}
}
36
Exchange Macros
first second
column column
phase phase
37
Exchange Macros Drag Law for spherical cap bubbles (4)
( )
24 1 + 0.15 Re0.687 / Re Re ≤ 1000
fdrag =
0.44 Re > 1000
Large bubbles have a shape of spherical cap and have constant drag
coefficient
f drag = 2.667
DEFINE_EXCHANGE_PROPERTY macro for drag force must return
n
r r f drag ρ k d i2
∑ ik i − uk ) = 0,
K (u K ik = α k ρ k τ ik =
i =1 τ ik 18 µ k
38
Exchange Macros Drag Law for spherical cap bubbles (4)
39
Exchange Macros Drag Law for spherical cap bubbles (4)
40
Exchange Macros Drag Law for spherical cap bubbles (4)
41
Exchange Macros Drag Law for evaporating droplets (5)
6α drop
rate = kc (C H 2O , sat (Tdrop ) − C H 2O , gas (Tgas ) )
d drop
Molar concentration
Area density
42
Exchange Macros Drag Law for evaporating droplets (5)
43
Exchange Macros Drag Law for evaporating droplets (5)
DEFINE_HET_RXN_RATE(vap_con,c,mt,f_in,f_sp,to_in,to_sp)
{
Thread **pt = THREAD_SUB_THREADS(t);
Thread *tp = pt[0];
Thread *ts = pt[1];
real T_prim = C_T(c,tp); /*gas phase temperature*/
real T_sec = C_T(c,ts); /*droplet phase temperature*/
real diam = C_PHASE_DIAMETER(c,ts); /*gas phase diameter*/
real D_evap_prim = C_DIFF_EFF(c,tp,index_evap_primary); /* laminar
diffusivity of evporating gas species*/
C_UDMI(c,t,0) = D_evap_prim ;
urelx = C_U(c,tp) - C_U(c,ts);
urel = sqrt(urelx*urelx + urely*urely + urelz*urelz); /*slip velocity*/
Re = urel * diam * C_R(c,tp) / C_MU_L(c,tp); /*droplet Re number*/
Sc = C_MU_L(c,tp) / C_R(c,tp) / D_evap_prim ; /*gas Sc number*/
Nu = 2. + 0.6 * pow(Re, 0.5)* pow(Sc, 0.333); /*Nusselt number*/
44
Exchange Macros Drag Law for evaporating droplets (5)
droplets
Vapor mole
Saturated
r air fraction
g dry air
Evaporation
rate
46
Parallel Fluent
Compute-Node-1
Cortex Host
sa )”
s
m te 5
ge
Print messages
“(%iterate 5)”
int era
es
it
“(%
Pr
“(%iterate 5)”
Compute-Node-0 Print messages
Compute-Node-2
“(% t m
consecutively starting at 0 Pr
in
ite ess
rat ag
Host labeled 999999
Host connected to Cortex e5 e
Each compute node (virtually) )” s
connected to every other compute node Compute-Node-3
47
Compiler Directives
48
Writing files in parallel (6)
49
Writing files in parallel (6)
• Pressure is written out at the end of the simulation for FEA analysis.
The data structure is the coordinates of a face followed by the
pressure of the face
• PRF_CRECV_INT(myid - 1, &dummy, 1, myid - 1) - message
passing macro which sends sections of data as single arrays from
one process to another process. It is used to make non-zero node
wait till node-0 is writing into the file
• myid – 1 since myid > 0, than node-myid will wait for data from
node myid-1
• &dummy – just a dummy vaiable meaning nothing
• 1 – means that just one variable is being sent (dummy)
• myid-1 – just a syntax
50
Writing files in parallel (6)
51
Writing files in parallel (6)
# include "udf.h“
# define WALLID 3 /*id of wall for which pressure data will be written*/
DEFINE_ON_DEMAND(Write_pressure)
{
real position[ND_ND]; /*vector definition to write coordinates of cell face*/
int dummy;
Domain *domain;
Thread *tf;
face_t f;
FILE * fp;
domain=Get_Domain(1);
/* Node 0 will open a NEW file while node 1, 2, ... will wait. After node 0 finishes,
Node 1 will open the SAME file while node 2, 3 ... will wait. This goes on. */
#if RP_NODE
if (! I_AM_NODE_ZERO_P) PRF_CRECV_INT(myid - 1, &dummy, 1, myid - 1);
fp = fopen("pressure.txt", (I_AM_NODE_ZERO_P ? "w" : "a"));
#else
fp = fopen("pressure.txt", "w");
#endif
tf=Lookup_Thread(domain, WALLID);
52
Writing files in parallel (6)
begin_f_loop(f,tf)
{
F_CENTROID(position, f, tf);
#if RP_2D
fprintf(fp, "%10.3e %10.3e %10.3e\n",
position[0], position[1], F_P(f,tf));
#else
fprintf(fp, "%10.3e %10.3e %10.3e %10.3e\n",
position[0], position[1], position[2], F_P(f,tf));
#endif
}
end_f_loop(f,tf)
/* After the node finishes, it will close the file and send a signal saying I am
done so that the next node can start */
#if RP_NODE
fclose(fp);
if (! I_AM_NODE_LAST_P) PRF_CSEND_INT(myid + 1, &dummy, 1,
myid);
#else
fclose (fp);
#endif
}
53
Moving and Deforming Mesh + Variable Time Step (7)
54
Moving and Deforming Mesh + Variable Time Step (7)
55
Moving and Deforming Mesh + Variable Time Step (7)
A relation is needed to solve for the timestep size and velocity of the body:
The timestep size and the corresponding velocity of the body are such
that the body will move by the maximum allowable translational distance
A cap on the timestep size is necessary to prevent an excessively large
computed timestep size which can result in divergence
The maximum allowable translational distance can be varied as function
of time, if necessary
The timestep size and body velocity are obtained by solving a quadratic
equation derived from the following two relations:
+
hmax = V n 1 × dt hmax = maximum allowable distance traveled
F
= force_acting_on_body/mass_of_body
n +1
F V −V n
m
=
M dt n
V = body velocity at previous timestep
DEFINE_RW_FILE
Save intermediate variables to data file for restart
57
Moving and Deforming Mesh + Variable Time Step (7)
#include "udf.h"
#include "sg_mem.h"
#include "dynamesh_tools.h“
#define PI 3.14159265
#define usrloop(n,m) for(n=0;n<m;++n)
/*----- User needs to change the section below ----------------*/
#define zoneID 28 /* zone ID for the moving boundary */
#define b_mass 1.0 /* mass of the body */
#define dtm_mx 0.005 /* maximum allowable timestep size */
/*----- Layering parameters -------------------------------------------*/
#define hc 6.1e-5 /* ideal cell height in meter */
#define nc 4.0 /* parameter for advancing layer speed */
#define hmove hc/nc /* maximum allowable translation */
/*----- Global variables --------------------------------------------------*/
real V_body[ND_ND]; /* velocity vector of body */
real b_ctr = 0.0; /* gravity center center location */
real tmsize = 0.00001; /* current computed timestep size */
58
Moving and Deforming Mesh + Variable Time Step (7)
59
Moving and Deforming Mesh + Variable Time
Step (7) DEFINE_RW_FILE(reader,fp)
DEFINE_CG_MOTION {
(body_motion, int m;
cg_omega, dt, cg_vel, time, dtime) #if !RP_NODE
{ float v_read;
/* Reset velocities */ usrloop(m,ND_ND)
NV_S(cg_vel, =, 0.0); {
NV_S(cg_omega, =, 0.0); fscanf(fp,"%e\n",&v_read);
/* Assign the linear velocity */ V_ball[m] = (real)v_read;
cg_vel[0] = V_body[0]; }
} fscanf(fp,"%e\n",&v_read); b_ctr =
DEFINE_RW_FILE(writer,fp)
(real)v_read;
{
fscanf(fp,"%e\n",&v_read); tmsize =
#if !RP_NODE (real)v_read;
int m; #endif
usrloop(m,ND_ND) usrloop(m,ND_ND)
fprintf(fp,"%e\n",V_ball[m]); host_to_node_real_1(V_ball[m]);
fprintf(fp,"%e\n",b_ctr); host_to_node_real_1(b_ctr);
fprintf(fp,"%e\n",tmsize); host_to_node_real_1(tmsize);
#endif} }
60
Moving and Deforming Mesh + Variable Time Step (7)
Store Separation
X_CG comparison
6
old-x_cg[0] Comparison of coordinates
5 srl-var-x_cg[0]
old-x_cg[1]
of CG with and without UDF
4 srl-var-x_cg[1]
old-x_cg[2]
3 ser-var-x_cg[2]
x_cg (m)
2
CPU time spent on single
1
processor:
0
0 0.1 0.2 0.3 0.4 0.5 0.6 0.7
3 days – without UDF
-1 1.5 days – with UDF
-2
time (s)
61
Appendix
62
User Defined Memory (UDM)
User-allocated memory
Allow users to allocate memory (up to 500
locations) to store and retrieve the values of
field variables computed by UDF’s (for
postprocessing and use by other UDFs) 500
Same array dimension and size as any flow
variable
More efficient storage compared to User
Defined Scalars: UDMs are not solved for
Number of User-Defined Memory Locations
is specified in the User-Defined Memory
panel
Accessible via macros
Cell values: C_UDMI(c,t,i)
Face values: F_UDMI(f,t,i)
Saved to FLUENT data file
63
User Defined Memory
DEFINE_ON_DEMAND(scaled_temp)
{
Domain *domain = Get_domain(1);
/* Compute scaled temperature store in user-defined memory */
thread_loop_c(t,domain)
{
begin_c_loop(c,t)
{
temp = C_T(c,t);
C_UDMI(c,t,0)=(temp - tmin)/(tmax-tmin);
}
end_c_loop(c,t)
}
}
64
Filter Blockage (8)
• Trap of particles within a porous filter and analysis of the dust build
up influencing the flow
• The trapping of particles is stochastic, which would imply using many
runs and finding where particles get stopped
• A simpler approach is possible
65
Filter Blockage (8)
66
Filter Blockage (8)
• A UDF will also get run if you are just displaying the Particles
• The parameter “coupled” is true for flow-coupled DPM iterations, but false
for post-processing with “particle tracks”
• DPM_LAW is run once after every drag run
• Energy must be enabled for a UDF Law to be run
DEFINE_DPM_LAW(dpm_law,p,coupled)
{ real pos[ND_ND];
real old_flow_rate;
cell_t c = P_CELL(p); /*pointer to cell where particle is*/
Thread *t = P_CELL_THREAD(p); /*pointer to cell thread where particle is*/
C_CENTROID(pos,c,t);
if((pos[2]>ZMIN)&&(pos[2]<ZMAX)&&(pos[0]<XMAX))
{old_flow_rate= P_FLOW_RATE(p);
P_FLOW_RATE(p) = old_flow_rate*
pow(TIME_EXP, -(P_TIME(p)-P_TIME0(p))/TIME_CONST);
C_UDMI(c,t,0) += old_flow_rate-P_FLOW_RATE(p);
}
} − (t _ new−t _ old )/ TIME _ CONST
m& p = m& p ⋅ TIME _ EXP
new old
67
Filter Blockage (8)
As a small addition to this model we can modify the drag to take into account the effect
of the higher true flow speed in the porous region
DEFINE_DPM_DRAG(drag,Re,p)
{
real Cd=1.0;
real pos[ND_ND];
cell_t c;
Thread *t;
c=P_CELL(p); t=P_CELL_THREAD(p);
C_CENTROID(pos,c,t);
if((pos[2]>ZMIN) && (pos[2]<ZMAX) && (pos[0]<XMAX))
{Re*=2.0 - BLOK*C_UDMI(c,t,0);} /*flow speed higher in porous region*/
return 18.0*Cd*Re/24.0;
}
68
Filter Blockage (8)
The particle tracks below are colored to show the flow rate of dust
(in kg/s) along each path line.
Note how the tracks passing through the filter decrease in flow
rate. The particles themselves are the same size and mass, just
the rate or “Strength” is decreased
69
Filter Blockage (8)
70
Erosion Model Customization (9)
71
Erosion Model Customization (9)
Reflecting Walls
•
m p C ( D p ) f (α )V b (V )
N particles a1 a2
f(a)
b : velocity exponent 0.6
0.4
C(Dp): function of particle diameter
0.2
0
0 10 20 30 40 50 60 70 80 90
Impact Angle (α)
Relationship between the impact
angle and function f(α)
72
Erosion Model Customization (9)
73
Erosion Model Customization (9)
#include "udf.h"
#define DEG_TO_RAD(x) ((M_PI*x/180.0))
DEFINE_DPM_EROSION
(custom_erosion, part, t, f, n, theta, vel, mdot)
{real er, f_theta, Avec[ND_ND];
real A = 1e-9,a=30,b=-34.79,c = 12.3,n=1.73;/*model constants*/
a = DEG_TO_RAD(a);
if (theta <= a)f_theta = b*theta*theta + c*theta;
else { /* You may add newer erosion models */ }
/*Removed mass per unit area per unit time (kg/m2s*/
er = A*pow(vel, n)*f_theta;
F_AREA(Avec, f, t);
er *= mdot/NV_MAG(Avec);
F_STORAGE_R(f, t, SV_DPMS_EROSION) = er;
}
74
Erosion Model Customization (9)
75
Closure
76