You are on page 1of 76

Presenter Name

Case studies on using


Presenter Title
UDF’s in FLUENT v6.2
Presentation Title
Andrey Troshko
June XX, 2005
June 2005

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

Boxes in Solve Species


blue are
Check Solve Turbulence
some Convergence Kinetic Energy
important Source terms
user
Update Properties Solve Eddy
access Dissipation
points
User-Defined Properties
User-Defined Boundary Conditions

4
User Defined Functions in Fluent

ƒ User Defined Functions are not just any C-functions:


ƒ User access needs specific “Type” of function calls
ƒ These Function types or ‘macro’-s are defined in the header file:
~Fluent.Inc/fluentx.y/src/udf.h

ƒ UDF’s in FLUENT are available for: ƒ Initialization


ƒ Profiles (Boundary Conditions) zone and variable specific initialization
velocity, temperature, ƒ Global Functions
pressure etc adjust
ƒ Source terms (Fluid and solid zones) ƒ Scalar Functions
mass, momentum, energy, unsteady term, flux vector, diffusivity
species etc ƒ Model Specific Functions
ƒ Properties reaction rates, discrete phase model,
viscosity, conductivity etc (except turbulent viscosity
specific heat)

5
Data Structures in FLUENT

face
Domain

cell cell

Domain Thread

Cell Cell
Cell

Boundary (face thread or zone) Fluid (cell thread or zone)

6
The Domain

ƒ “Domain” is the set of connectivity and hierarchy


info for the entire data structure in a given problem.
It includes:
ƒ all fluid zones (‘fluid threads’)
ƒ all solid zones (‘solid threads’)
ƒ all boundary zones (‘boundary threads’)
ƒ Cell/face - Computational unit, face is one side.
ƒ Conservation equations are solved over a cell
ƒ Thread - is the collection of cells or faces; defines a
fluid/solid/boundary zone

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

ƒ Control volumes (equivalent of ‘FEM:Elements’) of fluid and solid zones


are called ‘cell’ in FLUENT
ƒ The data structure for the control volumes is typed as ‘cell_t’
ƒ The data structure for the control volume faces is typed as ‘face_t’
ƒ A fluid or solid zone is comprised of cells
ƒ The Thread that identifies the cells in a Fluid or Solid Thread, is a
‘Cell-Thread’
ƒ A boundary zone is comprised of faces of the adjacent control volumes
or cells
ƒ The Thread that identifies the faces on a boundary is a
‘Face-Thread’

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

Type Example Details


Domain *d pointer to the collection of all threads
Thread *t pointer to a thread
Cell_t c cell identifier
face_t f face identifier
Node *node pointer to a node

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 )

Tout = ∫ ρTdV / ∫ ρdV


exit exit

ƒ ID of exit boundary is taken from BC panel and is used


to find exit face thread:
exit_face_thread = Lookup_Thread(domain, exit_id);
ƒ Thread of cells next to exit face is identified:
cell_thread = THREAD_T0(exit_face_thread);

12
Inlet Temperature vs exit Temperature (1)

ƒ The following UDFs will be used :


ƒ DEFINE_PROFILE
ƒ Identifies outlet thread from BC panel and
identifies cells next to thread to perform mass
averaging
ƒ Calculates mass averaged temperature at outlet
and stores it
ƒ Uses calculated mass averaged temperature
and uses it as argument to calculate profile of
inlet temperature

13
Inlet Temperature vs exit Temperature (1)

DEFINE_PROFILE(inlet_temperature, thread, /* calculation of mass averaged T */


nv) exit_temp = total_mass_temp/total_mass;
{ }
/* Loop through the faces of the exit surface*/
begin_f_loop(f1, exit_face_thread) /* Enter the T_inlet vs T_exit here */
{ inlet_temp = exit_temp + 30.;
/* macro F_C0 is utilized to find neighbor cell*/
cell0 = F_C0(f1, exit_face_thread); begin_f_loop(f2, thread)
cell_temp = C_T(cell0, cell_thread); {
cell_vol = C_VOLUME(cell0, cell_thread); F_PROFILE(f2, thread, nv) = inlet_temp;
cell_density = C_R(cell0, cell_thread); }
cell_mass = cell_vol*cell_density; end_f_loop(f2, thread)
total_mass += cell_mass; }
total_mass_temp += cell_mass*cell_temp;
}
end_f_loop(f1, exit_face_thread); cell0 = F_C0(f1, f_thread);

14
Inlet Temperature vs exit Temperature (1)

Tout
ƒ Application to flow in elbow with two inlets

Tin

Tin = Tout + 30

15
Membrane modeling (2)

ƒ Molecular membrane – openings are so small that molecules can be


separated by size
ƒ Separation of gas species is possible
ƒ Very large pressure difference across membrane “squeezes” molecules
through
ƒ Convective fluxe across membrane is effectively zero
ƒ Mass flux across membrane for species i is

φi = Qi (Pi high − Pi low )


Membrane High partial Low partial
permeance of pressure of pressure of
species i species i species i

ƒ Permeance Qi contains info on how “easily” molecules of species i can


“squeeze” through membrane openings

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

Mixture inlet, air+gas, high pressure pressure Air+ less gas

Membrane surface (wall) Air+more gas

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)

ƒ The following UDFs will be used:


ƒ DEFINE_ADJUST
ƒ Identifies internal surface as a model of membrane
ƒ Accesses cells on both sides of surface and
calculates mass flux of each species and stores
equivalent mass sources in User Defined Memory
ƒ DEFINE_SOURCE
ƒ Uses mass sources calculated in DEFINE_ADJUST to
prescribe mass sources for every species and mixture

18
Membrane modeling (2)

DEFINE_ADJUST(filter_adjust, domain) Membrane face


{
………………………….
/* find membrane thread by ID in BC panel*/
memb_thread = Lookup_Thread(domain,memb_id);
/* looping over cells faces of membrane*/
begin_f_loop (f, memb_thread)
{
/* get cell pointer for side of membrane on high pressure*/
c0 = F_C0(f,memb_thread);
/* get face thread pointer for shadow membrane surface*/
memb_thread_shadow = THREAD_SHADOW(memb_thread);
/* get face pointer for shadow membrane surface*/ Membrane
f_shadow = F_SHADOW(f,memb_thread); shadow face
/* get cell pointer from shadow side of membrane*/
c1 = F_C0(f_shadow,memb_thread_shadow);
/* get cell thread pointer for side of membrane*/
t0 = F_C0_THREAD(f,memb_thread);
/* get cell thread pointer for shadow side of membrane*/
t1 =memb_thread_shadow->t0;
………

19
Membrane modeling (2)

/* calculate molar concentration for side of membrane*/ High pressure and


x_GAS_outer = molefrac(C_YI(c0, t0, 0), MW_GAS, MW_AIR); mole fraction
x_AIR_outer = 1.- x_N2_outer;
/* calculate molar concentration for shadow side of membrane*/
x_GAS_inner = molefrac(C_YI(c1, t1, 0), MW_GAS, MW_AIR);
x_AIR_inner = 1.- x_N2_inner;
/* calculate membrane face area*/
F_AREA(A, f, memb_thread);
At = NV_MAG(A);
/* calculate species mass fluxes across the membrane, Pi=P*Xi*/
flux_GAS = Q_GAS * ( x_GAS_outer*P_outer - x_GAS_inner*P_inner);
flux_AIR = Q_AIR * ( x_AIR_outer*P_outer - x_AIR_inner*P_inner);
tot_flux = tot_flux + MW_GAS*flux_GAS*At + MW_AIR*flux_AIR*At; Low pressure and
mole fraction
C_UDMI(c0, t0, 0) = - (MW_GAS*flux_GAS)*At/C_VOLUME(c0,t0);
C_UDMI(c1, t1, 0) = (MW_GAS*flux_GAS)*At/C_VOLUME(c1,t1);

C_UDMI(c0, t0, 1) = -(MW_AIR*flux_AIR)*At/C_VOLUME(c0,t0);


C_UDMI(c1, t1, 1) = (MW_AIR*flux_AIR)*At/C_VOLUME(c1,t1);
}
end_f_loop (f, memb_thread)

20
Membrane modeling (2)

Air+ less gas, outlet 1


Mixture inlet, air+gas

Air+more gas, outlet 2


Gas mass fraction

Permeance ratio, 1 10
Qgas/Qair

Filter efficiency, 94% 66%


mgas,outlet1/mgas,in

21
Membrane modeling (2)

Mass transfer
rate variation

membrane Low species


concentration

High species
Species mass concentration
source in UDMI

Velocity vectors
22
Unsteady Staggered Particle Injection (3)

ƒ Unsteady DPM injection will release the particle packets at the


beginning of every timestep
ƒ A process is needed to inject particles at a certain user
specified time interval so as to reduce the number of particles
that need to be tracked inside the domain
ƒ The process will inject the particles at the beginning of a
timestep and then store/accumulate the particles’ mass during
the next timesteps when injections are turned off
ƒ At the time of the next injection, the accumulated particles’
mass will be injected
ƒ There will be fewer particles but the particles’s mass will be
preserved

23
Unsteady Staggered Particle Injection (3)

ƒ Once an injection is completed, the starting time of injection can be moved


forward in time by a user specified value using a UDF and the particles mass
accumulated for the next injection
ƒ The UDF will also move the injection locations as the exhaust location is
moving

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

Non staggered Staggered


injection injection

29
UDFs for Multiphase Flows

30
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 it is α k , Yk
i
r
ƒ For Euler model it is α , U , T , k , ε , Y
i
k kr k k k k
ƒ For Mixture model it is α k , U k , Yk
i

ƒ Some variables andr geometry are shared among phases


ƒ In VOF it is U , T , k , ε , in Mixture it is T , k , ε , in Euler it is k , ε
ƒ Therefore, an architecture that stored the phases in separate
overlaid Domains was devised.
ƒ This new 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.

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

P0domain P1domain P2domain Phase Subdomains

Phase Domain Interaction

32
The Multi-Domain Architecture

ƒ Each Thread is also in a hierachy 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 superthread.
ƒ Each Phase has its own set of threads known as a “subthreads” or
“phase threads”.
superthreads

P0threads P1threads P2threads Phase Subthreads

Phase Thread Interaction

33
The Multi-Domain Architecture

ƒ Some data is shared by both types of the thread.


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

ƒ Many UDFs are passed a thread as a parameter. This


thread is either a phase thread or a superthread. W
ƒ We may want the superthread of a phase thread

ƒ We may want 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.

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

ƒ DEFINE_EXCHANGE_PROPERTY( name, c, mixture_thread,


second_column_phase_index,first_column_phase_index)
ƒ ƒ Specifies net heat transfer rates between phases, and drag and lift
coefficient functions
ƒ Eulerian Model – net heat transfer rate, drag coefficient, lift coefficient

ƒ Mixture Model – drag coeeficient

ƒ Phase specific variables can be accessed using first/second_column_phase


index which correspond to phase order in Phase Interaction panel

first second
column column
phase phase
37
Exchange Macros Drag Law for spherical cap bubbles (4)

ƒ Schiller-Naumann (default) drag coefficient is valid for spherical particles

( )
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)

ƒ The following UDFs will be needed:


ƒ DEFINE_ EXCHANGE_PROPERTY
ƒ Identify thread for each phase
ƒ Calculate drag coefficient

39
Exchange Macros Drag Law for spherical cap bubbles (4)

DEFINE_EXCHANGE_PROPERTY(large, c, m_t, s_index,


f_index)
{
Thread **pt = THREAD_SUB_THREADS(m_t);
real drag_coeff, drag_force;
real urel,urelx,urely,urelz,dbub;
dbub = C_PHASE_DIAMETER(c,pt[f_index]);
drag_coeff =8/3; /*drag coefficient for large bubbles*/
urelx = C_U(c,pt[f_index]) - C_U(c,pt[s_index]);
urely = C_V(c,pt[f_index]) - C_V(c,pt[s_index]);
urelz = C_W(c,pt[f_index]) - C_W(c,pt[s_index]);
urel = sqrt(urelx*urelx + urely*urely + urelz*urelz);
drag_force = 0.75 * drag_coeff*C_R(c,pt[s_index])
* C_VOF(c,pt[s_index])* C_VOF(c,pt[f_index])
* urel/dbub;
return drag_force;
}

40
Exchange Macros Drag Law for spherical cap bubbles (4)

ƒ Free rising air bubbles in water

poutlet 1.6 experiment

Bubble rising velocity, m/sec


1.4 constant drag

1.2 Schiller-Naumann drag


r
g 1
0.8
0.6
Bubble
0.4
velocity
symmetry 0.2
0
0 1 2 3 4 5 6 7
Bubble diameter, cm

pinlet Comparison for single bubble rise


velocity

41
Exchange Macros Drag Law for evaporating droplets (5)

ƒ Example – water droplets feely falling in dry air


accompanied by evaporation
ƒ Water – single species mixture phase
ƒ Atmosphere – mixture of two species (air+vapor)
ƒ Evaporation from droplets into air is modeled as
heterogeneous reaction
ƒ Reaction rate is given by

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)

ƒ The following macro will be used


ƒ DEFINE_HET_RXN_RATE
ƒ Identifies phases and species
ƒ Computes reactions rate between species of
different phases in kgmole/m3/sec

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)

mass_coeff = Nu * D_evap_prim / diam ;


for (i=0; i < MAX_SPE_EQNS_PRIM ; i++) /*looping over gas species*/
{
accum = accum + C_YI(c,tp,i)/mw[i][prim_index];
}
mole_frac_evap_prim = C_YI(c,tp,index_evap_primary ) /
mw[index_evap_primary][prim_index] / accum; /* mole frac. of evap
species*/
concentration_evap_primary = mole_frac_evap_prim * P_OPER
UNIVERSAL_GAS_CONSTANT / T_prim ; /* mole conc. of evap species */
concentration_sat = psat_h2o(T_sec) / UNIVERSAL_GAS_CONSTANT /
T_sec ; /* saturation conc. of evap species */
area_density = 6. * C_VOF(c,ts) / diam ;
flux_evap = mass_coeff * (concentration_sat -
concentration_evap_primary ) ;
rr* = area_density * flux_evap;
}
45
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

ƒ Compute nodes labeled

“(% 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

ƒ “#if” is a compiler directive; Similar to “#define”


ƒ A “#endif” is used to close a “#if
#if RP_NODE /* Compute-Node */
#if RP_HOST /* Host */
#if PARALLEL /* Equivalent to #if RP_HOST||RP_NODE*/
#if !PARALLEL /* Serial, ! means logical negation */
#if RP_HOST
Message(“I’m the Host process \n”);
#endif
#if RP_NODE
Message(“I’m the Node process number:%d \n”, myid);
#endif

48
Writing files in parallel (6)

• Although compute nodes can perform computations on data


simultaneously when FLUENT is running in parallel, when data is
written to a single, common file, the writing operations have to be
sequential
• The file has to be opened and written to by processes that have
access to the desired file system
• This means that all of the data has to be written from the host
process which always runs on a machine with access to a file
system, since it reads and writes the case and data files
• file writing in parallel is done in the following stages
• The host process opens the file
• Compute node-0 sends its data to the host
• The other compute nodes send their data to compute node-0
• Compute node-0 receives the data from the other compute nodes
and sends it to the host
• The host receives the data sent from all the compute nodes and
writes it to the file
• The host closes the file

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)

ƒ The following UDFs will be needed:


ƒ DEFINE_ ON_DEMAND
ƒ Identifies a node number
ƒ If node number is more than zero, waits till all nodes with lower numbers write
their data and than appends to file to write its share of data
ƒ If it is node zero – opens file and writes data first

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)

ƒ You can use the DEFINE_CG_MOTION macro to specify the motion of a


particular dynamic zone in FLUENT by providing FLUENT with the linear and
angular velocities at every time step. FLUENT uses these velocities to update
the node positions on the dynamic zone based on solid-body motion. Note
that UDFs that are defined using DEFINE_CG_MOTION can only be
executed as compiled UDFs.
ƒ DEFINE_CG_MOTION ( name, dt, vel, omega, time,dtime)
ƒ dt i pointer to the structure that stores the dynamic mesh attributes that
you have specified
ƒ vel – velocity vetor
ƒ omega – angular velocity vector
ƒ time - current physical time
ƒ dtime - time step
ƒ This macro overwrites vel and omega vectors which are used to update body
position

54
Moving and Deforming Mesh + Variable Time Step (7)

ƒ Coupled dynamic mesh problem adjusts the motion of the moving


body/surfaces based on the current computed aerodynamic load and
applied external forces
ƒ An optimum ‘mean’ timestep size that will apply generally for the
duration of the coupled motion is difficult to obtain since the
aerodynamic load is not known a priori
ƒ Continual and manual adjustment of the timestep size is required to
avoid using an excessively conservative value that will increase
running time or too large value that will cause the dynamic remeshing
to fail
ƒ It is possible to implement a user defined variable timestep size using
UDF
ƒ The variable timestep size is computed subject to the following
constraints:
ƒ User specifies maximum allowable translational distance
ƒ User specifies maximum allowable timestep size value

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

n +1 = body velocity at next timestep (uknown)


V
dt = next timestep size (unknown)
56
Moving and Deforming Mesh + Variable Time Step (7)

ƒ The following UDFs will be needed:


ƒ DEFINE_EXECUTE_AT_END
ƒ Compute the forces on the body
ƒ Compute the next timestep size and body velocity subject to
the maximum allowable translational distance specified by
the user
ƒ DEFINE_CG_MOTION
ƒ Specify the MDM motion

ƒ 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)

DEFINE_EXECUTE_AT_END(exec_end) /* Compute velocity and timestep


{ size from quadratic equation */
real tm, dtm, Velx, Vn, Fm; Vn = V_body[0];
real x_cg[3], f_glob[3], m_glob[3]; Fm = f_glob[0]/b_mass;
Domain *domain = Get_Domain(1); dtm = ( -fabs(Vn) + sqrt( Vn*Vn +
Thread *tf = 4.0*fabs(Fm)*hmove ) )/
Lookup_Thread(domain,zoneID); ( 2.0*fabs(Fm) );
tm = RP_Get_Real("flow-time"); if ( dtm > dtm_mx ) dtm = dtm_mx;
/* Reset arrays to 0 */ Velx = Vn + Fm*dtm;
usrloop(m,ND_ND) x_cg[m] = 0.0; if ( (x_cg[0]<=(-Smax))||
usrloop(m,ND_ND) f_glob[m] = 0.0; (x_cg[0]>=Smax) ) Velx = 0.0;
usrloop(m,ND_ND) m_glob[m] = 0.0; /* Update velocities, timestep size,
/* Get the previous c.g. for the and ball c.g. location */
ball zone */ V_body[0] = Velx;
x_cg[0] = b_ctr; tmsize = dtm;
/* Compute the forces on the body */ b_ctr += V_body[0]*tmsize;
Compute_Force_And_Moment RP_Set_Real("physical-time-
(domain,tf,x_cg,f_glob,m_glob,TRUE);
step",tmsize);
}

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

• A porous region is defined as the filter in a Cylinder


• Particle tracks passing through this region are not stopped but their
flow rate is decreased to represent expected probability of a particle
being present
• This approach means that the flow can be run as a series of quasi
steady runs with gradual build up of the blockage effect

65
Filter Blockage (8)

ƒ The following UDFs will be needed:


ƒ DEFINE_DPM_LAW
ƒ Identifies when Discrete Particle is within area assigned to
filter
ƒ Inside the filter, decreases mass flow rate based on particle
time to model decreased probability of particle caought by
filter
ƒ Stores into USER Defined memory decrease in mass flow
rate of particles
ƒ DEFINE_DPM_DRAG
ƒ Modifies particle Re number in drag law inside the filter. Re
number should be increased because physical velocity of
continuous fluid is higher due to porosity

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)

• The rate of material buildup can be stored in a UDM location


• This value can then be used to vary the resistance of the porous
region using a momentum source UDF

• Dust build up shown on the computational cells

70
Erosion Model Customization (9)

• Fluent6 already has a erosion model


• A tutorial is available
• This UDF essentially illustrates how to customize
erosion modeling in Fluent using DPM hooks
• Erosion rate is a function of
• Angle of impingement
• Impact velocity
• Particle diameter
• Particle mass
• Collision frequency between particles and solid
walls
• Material type

71
Erosion Model Customization (9)

Reflecting Walls

m p C ( D p ) f (α )V b (V )
N particles a1 a2

Rerosion = ∑ A face Reflection Coefficient: en=v2,n/v1,n


p= 1

m : mass flow rate of the particles 1.2

f(α): function of impingement angle a 1

V : impact velocity 0.8

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)

ƒ The following UDFs will be needed:


ƒ DEFINE_DPM_EROSION
ƒ Calculates erosion rate as prescribed by equation

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)

• Remember also to set up DPM


properties on wall
• Reflection coefficients for tangential
and normal components
• Also provide the erosion model data
• Impact angle
• Velocity exponents
• Diameter function

75
Closure

ƒ In this lecture we have shown several examples of application of


UDF to real world problem
ƒ Emphasis was given on strategy of UDF implementation, i.e., which
macros were used, how macros exchanged information etc.
ƒ The examples customized boundary conditions (surface and fluid
zones), DPM, multiphase momentum exchange, parallel issue,
moving and deforming mesh, customized time step and other
physical and numerical aspects of models
ƒ Special thanks to Fluent staff who contributed to this lecture
ƒ Adam Anderson (Fluent Europe)
ƒ Xiao Hu (Fluent Michigan)
ƒ Shitalkumar Joshi (Fluent India)
ƒ Rafi Khan (Fluent New Hampshire)
ƒ Sutikno Wirogo (Fluent New Hampshire)

76

You might also like