You are on page 1of 18

Setting up Oscillatory Moving Boundaries in Fluent using UDFs

Laboratory for Product and Process Design

By Madhawa Hettiarachchi
10/31/2011

1
Moving Boundary:

The moving boundary problems encountered in biological systems are periodic in nature.
Therefore, when a motion of a periodically moving wall is simulated, the motion of the each
node of the wall needs to be controlled independently. Thus, we want to update the position of
each node based on the deflection due to fluid-structure interaction. In Fluent, we can define
UDFs using DEFINE_GRID_MOTION as shown below to specify the motion of each node on a
boundary or interface.

DEFINE_GRID_MOTION( name, d, dt, time, dtime)

Argument Type Description

symbol name UDF name.

Domain *d Pointer to domain.

Dynamic_Thread *dt Pointer to structure that stores the dynamic mesh

attributes that you have specified (or that are


calculated

by ANSYS FLUENT).

real time Current time.

real dtime Time step.

Function returns

void

There are five arguments to DEFINE_GRID_MOTION: name, d, dt, time, and dtime. You supply
name, the name of the UDF. d, dt, time, and dtime are variables that are passed by the ANSYS
FLUENT solver to your UDF.

NOTE: The UDFs that are defined using DEFINE_GRID_MOTION can be executed only as compiled
UDFs. Please refer to fluent UDF manual for more information.

2
Method:

Since the pulsatile nature of the wall motion, it is important that the node positions of the moving
wall are returned to their original positions at the end of the cycle. It is very important to
establish the original positions of the mesh at the end of each cycle, otherwise the mesh can be
gradually deformed with time and lead into negative mesh volumes which could abruptly stop
the simulations.

Step1: Original node locations (coordinates) of the moving wall/interface need to be written to a
file, which will be used to update the original positions of the nodes. This can be done in fluent
after the mesh file is loaded.

We will use the mesh file of the 3D brain CSF space as an example in this report. (Please see the mesh,
fluent case and UDFs provided with this report. S:\For Madhawa\Reports).

Forebrain Moving boundary


Ventricle Moving boundary
Choroid plexus CSF generation
Lower brain Wall
Sagittal plane Symmetry

Fig.1 shows the 3D geometry of the Brain CSF space. The parenchyma wall shown in green color and
ventricle surfaces shown blue color are moving boundaries.

1. Start Fluent (Win 32 system start Fluent from visual studio command prompt , Win 64 system-
start fluent from SDK command prompt. Go to command prompt and type fluent. Refer
appendix 1 for more information)
2. Read the mesh file in to the fluent (3DBrainMWChroidArchnoidVilli2.msh).
3. Check the mesh for completeness.

3
4. The current dimension of the mesh is too large. Scale the mesh by a factor of 0.00094. It will
bring the size of the mesh into more realistic values. Save the case file.
5. Go to File ExportSolution Data

6. Write the coordinates to a file. MovingParenchymacordinates.out. The format of the file is


shown below. There are total of 35573 moving nodes.

7. Repeat the same steps for other moving walls. Create MovingVentriclecordinates.out file. There
are total of 4669 moving nodes.

8. Setting the problem parameters.


DefineGeneraltransient
Model keep the default setting (viscous-laminar flow)
Materialselect Fluid Create/EditFluent Databaseselect Water-

4
liquid from the Fluent Fluid Materials and click Copy. Now you will see Fluid-
water in the material panel under the Fluid.
DefineCell Zone Conditionsselect Zone in panelEditMaterial Name
select Water-liquid from the drop down menu. Click OK.
9. Setting up boundary conditions
Set the zone symmetry to type symmetry.
Set Choroid mass flow inlet to 3.33e-6 kg/s (half of 0.4ml/min) and direction specification
method to Normal to Boundary
Set ArchnoidVilli mass flow inlet 3.33e-6 kg/s (half of 0.4ml/min) Outword Normals
Set cervical pressure out let to 1332 pascal (10 hgmm).
Change the type of Injection leftlv boundary condition from mass inflow to wall. (ln this
simulation, we only look at CSF flow in the Brain. Later, infusion can also be included by using
species transport model and defining infusion mass flow rate and drug mass fraction. We
assume infusion is a mixture of water and drug. For example , let consider a 3 ml/min bolus
infusion with drug concentration 100 microgram/ml. You will set the mass flow rate as 0.0005
Kg/s over a minute duration and mass fraction of the drug as 0.1. )
Set all other boundaries to wall condition

10. UDF for to Set up mesh motion, CSF generation and drug injection:

The UDFs(3Dgridmotion5.c) given with this includes four subroutines for parenchyma wall
motion, Ventricle wall motion, CSF production and drug injection such as,

DEFINE_GRID_MOTION(Parenchyma,domain,dt,time,dtime)

5
DEFINE_GRID_MOTION(Ventricals,domain,dt,time,dtime)

DEFINE_PROFILE(Drug_Mass_profile,t,i)

DEFINE_PROFILE(Drug_Mass_profile,t,i)

The first argument within the brackets is the name of the each UDF given by you.
You can change the names as you desired. Once you compiled the UDF, these given
names of the UDF will appear in the fluent.

The UDF 3Dgridmotion5.C is given below.

#include "udf.h"
#include "unsteady.h"
FILE *fout;

/* &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& */

DEFINE_GRID_MOTION(Parenchyma,domain,dt,time,dtime)
{
Thread *tf= DT_THREAD(dt);
face_t f;
Node *v;
real NV_VEC(A);
real NV_VEC(dx);
real previous_time;
real lag;
real amplitude;
real x;
real w;
real waveform;
int n;
float f1;
float f2;
float f3;
float npx[35573];
float npy[35573];
float npz[35573];
int v1;
int i;
/*read node cordinates fron the file */
char buffer[100];
fout = fopen("MovingParenchymacordinates.out", "r");
fgets(buffer,100,fout); /* skip the first line*/
while(feof(fout)==0)
{
fscanf(fout, "%d %f %f %f\n", &v1, &f1, &f2, &f3);

npx[v1]=f1;
npy[v1]=f2;
npz[v1]=f3;
}
fclose(fout);

/* calculate displacement vector at the node normal to the surface */

6
i=1;
begin_f_loop(f,tf)
{
previous_time=PREVIOUS_TIME;
F_AREA(A,f,tf);

amplitude=-0.000003;
lag=0.37;
w=2*M_PI;
x=-(time+lag);
waveform=-1*(-0.4616*cos(x*w)-0.09215*sin(x*w)+0.1979*cos(2*x*w)-
0.1827*sin(2*x*w)-0.1004*cos(3*x*w)+0.08095*sin(3*x*w)-0.03252*cos(4*x*w)-
0.05943*sin(4*x*w)+0.05324*cos(5*x*w)+0.04205*sin(5*x*w)-0.03345*cos(6*x*w)-
0.00468*sin(6*x*w)+0.005554*cos(7*x*w)-
0.007843*sin(7*x*w)+0.00242*cos(8*x*w)+0.00619*sin(8*x*w));
/*waveform=-1*(0.2138*cos(x*w)+0.8512*sin(x*w)+0.01898*cos(2*x*w) +
0.1687*sin(2*x*w));*/
dx[0]=amplitude*(A[0]/NV_MAG(A)*waveform);
dx[1]=amplitude*(A[1]/NV_MAG(A)*waveform);
dx[2]=amplitude*(A[2]/NV_MAG(A)*waveform);

f_node_loop(f,tf,n)
{
v = F_NODE(f,tf,n);
/* update node if the current node has not been previously */
/* visited when looping through previous faces */
if ( NODE_POS_NEED_UPDATE (v))
{
/* indicate that node position has been update */
/*so that it's not updated more than once */
NODE_POS_UPDATED(v);

NODE_COORD(v)[0]=dx[0]+npx[i];
NODE_COORD(v)[1]=dx[1]+npy[i];
NODE_COORD(v)[2]=dx[2]+npz[i];
i=i+1;
}
}
}
end_f_loop(f,tf);
}

/* &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& */

DEFINE_GRID_MOTION(Ventricals,domain,dt,time,dtime)
{
Thread *tf= DT_THREAD(dt);
face_t f;
Node *v;
real NV_VEC(A);
real NV_VEC(dx);
real previous_time;
real amplitude;

real lag;

7
real x;
real w;
real waveform;
int n;
float f1;
float f2;
float f3;
float npx[4669];
float npy[4669];
float npz[4669];
int v1;
int i;

/*read node cordinates fron the file */


char buffer[100];
fout = fopen("MovingVentriclecordinates.out", "r");
fgets(buffer,100,fout); /* skip the first line*/
while(feof(fout)==0)
{
fscanf(fout, "%d %f %f %f\n", &v1, &f1, &f2, &f3);
npx[v1]=f1;
npy[v1]=f2;
npz[v1]=f3;
}
fclose(fout);

/* calculate displacement vector at the node normal to the surface */


i=1;
begin_f_loop(f,tf)
{
previous_time=PREVIOUS_TIME;
F_AREA(A,f,tf);
amplitude=-0.000008;
lag=-0.03;
w=2*M_PI;
x=(time+lag); /* check the sign*/
waveform=-1*(0.2138*cos(x*w)+0.8512*sin(x*w)+0.01898*cos(2*x*w) +
0.1687*sin(2*x*w));
dx[0 amplitude *(A[0]/NV_MAG(A)*waveform);
dx[1]=amplitude *(A[1]/NV_MAG(A)*waveform);
dx[2]=amplitude *(A[2]/NV_MAG(A)*waveform);

f_node_loop(f,tf,n)
{
v = F_NODE(f,tf,n);
/* update node if the current node has not been previously */
/* visited when looping through previous faces */
if ( NODE_POS_NEED_UPDATE (v))
{
/* indicate that node position has been update */
/*so that it's not updated more than once */
NODE_POS_UPDATED(v);

NODE_COORD(v)[0]=dx[0]+npx[i];
NODE_COORD(v)[1]=dx[1]+npy[i];
NODE_COORD(v)[2]=dx[2]+npz[i];
i=i+1;
}

8
}
}
end_f_loop(f,tf);
}

/* &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& */

DEFINE_PROFILE(Drug_Mass_profile,t,i) /* Saline +Drug mass inflow 3ml/min ; Drug


mass frction 5% */
{
face_t f;
real flow_time = RP_Get_Real("flow-time");
begin_f_loop(f,t)
{
if (flow_time < 30.0 )
F_PROFILE(f,t,i) = 0.0005; /* 0.0005 kg/s = 3ml/min injected over 30s */
else
F_PROFILE(f,t,i) =0 ;
}
end_f_loop(f,t)
}

/* &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& */

DEFINE_PROFILE(CSF_production_profile,t,i)
/*Total production rate=0.4ml/min; per ventrical production rate = 0.2ml/min
=3.33e-6 kg/s */
{
face_t f;
real flow_time = RP_Get_Real("flow-time");
begin_f_loop(f,t)
{
F_PROFILE(f,t,i) = 3.33e-6; /* kg/s */
}
end_f_loop(f,t)
}

/* &&&&&&&&&&&&&&&&&& END &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& */

Let consider the subroutine that defines the parenchyma grid motion:

DEFINE_GRID_MOTION(Parenchyma,domain,dt,time,dtime).

The section highlighted in yellow is related to the parenchyma coordinate data


file(moving1parenchyma.out ). Make sure total number of node points in the data file match
with the array size of npx, npy ,& npz (moving1parenchyma.out has total of 35573 nodes).
Both grid motion subroutines are same expect for the Fourier function that define the profile of
the periodic wall motion and node coordinate data file. By changing the periodic motion profile
and node coordinate data file, these subroutines can easily be modified to define 3D motion of
any wall or boundary.

9
Also make sure that the name of the node coordinate data file is correctly listed in the UDF. The
section highlighted in pink is a Fourier function that describes the pulsatile displacement at each
node. User can change this function as depending on the degree of wall motion and phase lag.

The Fourier Function that describe the parenchyma wall motion shown below (see appendix 2
for more information).

amplitude= -0.000003;
lag=0.37;
w=2*M_PI;
x=-(time+lag);

waveform=-1*(-0.4616*cos(x*w)-0.09215*sin(x*w)+0.1979*cos(2*x*w)-
0.1827*sin(2*x*w)-0.1004*cos(3*x*w)+0.08095*sin(3*x*w)-0.03252*cos(4*x*w)-
0.05943*sin(4*x*w)+0.05324*cos(5*x*w)+0.04205*sin(5*x*w)-0.03345*cos(6*x*w)-
0.00468*sin(6*x*w)+0.005554*cos(7*x*w)-
0.007843*sin(7*x*w)+0.00242*cos(8*x*w)+0.00619*sin(8*x*w));

Fig. 2 Shows the Fourier function that describes the parenchyma wall motion. Values are
normalized so that the peak is equal to 1.

The amplitude defines the maximum displacement of the nodes. You can change this value
depending on the amount of wall displacement. By adjusting the lag, you can control the
starting displacement of the node points at time=0. It is important start with a small
displacement at the beginning, since the large displacement values of the wall can cause
convergence problems. Also, the lag can be function of the vertical position of the spine, so that
wall moves with a phase shift along the vertical axis.

The Fourier Function that describe the ventricle wall motion shown below (see appendix 3 for
more information).

10
waveform=(0.2138*cos(x*w)+0.8512*sin(x*w)+0.01898*cos(2*x*w)+0.1687*sin(2*x*w);

f(x) = 0.2138*cos(x*w)+0.8512*sin(x*w)+0.01898*cos(2*x*w) +
0.1687*sin(2*x*w

Fig. 3 Shows the Fourier function that describes the ventricle wall motion. Values are
normalized so that the peak is equal to 1.

11. Hooking up UDF in Fluent


DefineUser-DefinedFunctionsCompiledAdd -browse the UDF. Make sure all fluent case
file and the UDF are in the same folder.

You can define the Library Name or use the default libudf. (When you start the fluent, it gives
you option to set up the Working Directory. If it is not set to the current folder that contains
your case file, then you need to define the full path of the UDF under the Library Name. For
example, c:\...\..\libudf. )
Click on Build
Click on the Load.

11
If the compiling process is successful, you will see following message in the fluent text command
window. You should see the name of the UDFs as shown below .

Sometimes, compiling (or interpreting) of the UDF could give you problems. Please refer to
Appendix 1 for more information on the trouble UDF compiling trouble shooting. Also, path
name of the UDF should not contain any spaces.

12. Setting up Dynamic Mesh


Dynamic MeshCheck the Dynamic mesh and chose smoothing mesh method.

12
Under the Dynamic Mesh Zones, click create/edit.
In the Zone Names drop down menu, select Moving1perenchyma
Select the type User-Defined
In the Mesh Motion UDF drop down menu, select Parenchyma::libudf
click Create.

Repeat same steps for the Moving2Ven zone.

13. Reference values- use default vales.


14. Solution: you can use default settings. (you may use second order upwind discretization for
momentum)
15. Use default values for solution control.
16. Monitors residualsEditset convergence criteria to 10e-6
17. Initialize the solution with default values.
18. Calculation activities-Set the Autosave and animations as you wanted.
19. Save case file. Run Calculation: Set time step to 0.02s. Number of time steps to 50. Maximum
iterations to 100. Click on calculate to start simulation.

13
Appendix 1: User-Defined Function (UDF) Related

Which compilers work?

. On Windows machines, Microsoft Visual C++ is the compiler of choice. On Unix-like machines, GCC is
the compiler of choice. GCC may also work on Windows. The free Express Edition of the Microsoft
compiler also works.

UDF won't interpret or compile - what is wrong?

If Fluent complains about not being able to find a compiler, then check to make sure that the
compiler is properly installed. On Windows machines, it is possible to install the Visual C++
compiler without fully setting up the command line compiler (which Fluent needs to be able to
find). During the installation process, you need to select the "register environment variables"
option. Failure to do so will likely lead to complaints about things being "not recognized as an
internal or external command, operable program or batch file" or missing DLL's. It is
theoretically possible to fix this issue by setting the appropriate environment variables, but keep
in mind that even when nmake can be found there still may be DLL issues. The easy path is
probably reinstallation of Visual Studio (taking special care to make sure that the command line
interface is set up properly), but the reinstallation path is always perilous. If you have long-term
experience using Windows you should probably know the risks, and if you don't you should
consult an expert.

If you are interpreting, keep in mind that not everything that is supported for compiled UDF's is
supported for interpreted UDF's. This is true both for the UDF interface and the C language. If
you are doing something pretty involved and it fails inexplicably, try compiling to see if that
makes a difference.

There is also the possibility of coding errors. Keep in mind that your source code gets run
through the C preprocessor (to change the Fluent macros into C code), so unintended
interactions are very possible.

Error message "'nmake' is not recognized as an internal or external command, operable program or
batch file"?

This is a Windows/Visual Studio issue. In order to compile your UDF, Fluent needs to know where the
nmake program is located, and it currently can't find it. If you open up a command window and enter
"nmake", you should probably get the same message. See the previous question/answer.

UDF interprets/compiles but fails to execute - what is wrong?

This commonly occurs due to a faulty operation of some kind. For example, user-defined memory (UDM)
is not allocated until initialization, so attempting to access a UDM variable before initialization can lead
to a segmentation fault. Try to isolate the cause, and then look for places where your expectation of

14
what is available may not match reality. Finally, keep in mind that if you do something really bad, you
may need to restart Fluent before even a fixed UDF will work.

How-To Setup Microsoft Visual C++ 10.0 to Work with Fluent 13

In the Fluent Launcher, there is a tab called "Environment". On this tab there is a check box "Setup
Compilation Environment for UDF", which has to be checked. If this is not enough, and the compilation
still fails, then the udf.bat file should be edited. The following has to be added

set MSVC_DEFAULT=%ProgramFiles%\Microsoft Visual Studio 10.0


if exist "%MSVC_DEFAULT%\vC\vcvarsall.bat" set MSVC=%MSVC_DEFAULT%
if not "%MSVC%" == "" goto msvc_env10
msvc_env10
set MSVC_VERSION=10
call "%MSVC%\VC\vcvarsall.bat" x86
goto msvc_end

Basically it tries to run the vcvarsall.bat script. That is it!

How-To on most Windows 32bit systems

Check for completeness of the following steps.

Install Visual Studio. Most of the time the Visual C++ 2008 Express Edition is recommended. On
my system it even works with the new Visual Studio 2010 Professional Release Candidate.
Set the correct environment variables. Browse your way through the Windows system control to
'System'. There you will find a section 'Advanced system settings'. In the following dialog go to
the 'Advanced' tabulator and click on 'Environment variables' (lower right corner). Go through
the 'System variables' list and search for the 'Path' entry. Add the following to the variable:
C:\Program Files (x86)\Microsoft Visual Studio 10.0\Common7\Tools;C:\Program Files
(x86)\Microsoft Visual Studio 10.0\VC\bin;C:\Program Files\ANSYS Inc\v120\fluent\ntbin\win64.
Adjust this entry to your system concerning the installation directories!
The Visual Studio entry should point to the location where 'nmake.exe' is located. If this file is
missing while trying to compile your UDF you will be fine with this step. If other files are missing
during the compilation find their location on you hard disc and add their directory to the PATH
entry.
Run FLUENT from the Visual Studio command prompt! This is what most of the users don't know.
Start the Visual Studio command prompt which comes with your Visual Studio installation. It is
the familiar Windows style command prompt. Navigate to the directory, where your FLUENT
files are and start FLUENT by entering 'fluent'. Don't use symbols on your desktop or start
menue entries to start FLUENT. (This does not apply to 64bit systems.)

How UDF can compile with Windows 7 64bit?

This problem sometimes looks similar to the one concerning the missing 'nmake'.

15
Install Visual Studio. Most of the time the Visual C++ 2008 Express Edition is recommended. On
my system it even works with the new Visual Studio 2010 Professional Release Candidate.
Set the correct environment variables. Browse your way through the Windows system control to
'System'. There you will find a section 'Advanced system settings'. In the following dialog go to
the 'Advanced' tabulator and click on 'Environment variables' (lower right corner). Go through
the 'System variables' list and search for the 'Path' entry. Add the following to the variable:
C:\Program Files (x86)\Microsoft Visual Studio 10.0\Common7\Tools;C:\Program Files
(x86)\Microsoft Visual Studio 10.0\VC\bin;C:\Program Files\ANSYS Inc\v120\fluent\ntbin\win64.
Adjust this entry to your system concerning the installation directories! The Visual Studio entry
should point to the location where 'nmake' is located.
Install a Software Development Kit (SKD) for 64bit systems. This may be the difference between
32bit and 64bit systems. I have used the .NET Framework 2.0 Software Development Kit (SDK)
(x64) from 2006 because it is explicitly for 64bit systems and I was not sure if more recent
versions are for 64bit systems as well.
Start FLUENT from the SDK command prompt. Do not use the Visual Studio command prompt,
use the SDK command prompt! Go to the directory your case is in and type 'fluent'.

16
Appendix 2: Fourier function for the parenchyma wall motion

General model Fourier8:

f(x) = a0 + a1*cos(x*w) + b1*sin(x*w) +

a2*cos(2*x*w) + b2*sin(2*x*w) + a3*cos(3*x*w) + b3*sin(3*x*w) +

a4*cos(4*x*w) + b4*sin(4*x*w) + a5*cos(5*x*w) + b5*sin(5*x*w) +

a6*cos(6*x*w) + b6*sin(6*x*w) + a7*cos(7*x*w) + b7*sin(7*x*w) +

a8*cos(8*x*w) + b8*sin(8*x*w)

Coefficients (with 95% confidence bounds):

a0 = 5.002e-021 (fixed at bound)

a1 = -0.4616 (-0.4644, -0.4588)

b1 = -0.09215 (-0.09499, -0.08931)

a2 = 0.1979 (0.1952, 0.2007)

b2 = -0.1827 (-0.1856, -0.1799)

a3 = -0.1004 (-0.1032, -0.09763)

b3 = 0.08095 (0.0781, 0.0838)

a4 = -0.03252 (-0.03531, -0.02972)

b4 = -0.05943 (-0.06227, -0.0566)

a5 = 0.05324 (0.05046, 0.05603)

b5 = 0.04205 (0.0392, 0.04489)

a6 = -0.03345 (-0.03623, -0.03066)

b6 = -0.00468 (-0.007521, -0.001838)

a7 = 0.005554 (0.002749, 0.00836)

b7 = -0.007843 (-0.01067, -0.005013)

a8 = 0.00242 (-0.0003727, 0.005213)

b8 = 0.00619 (0.003347, 0.009033)

w= 6.28 (fixed at bound)

Goodness of fit:

SSE: 0.0004933, R-square: 0.9999

Adjusted R-square: 0.9998, RMSE: 0.005387

17
Appendix 3: Fourier function for the ventricle wall motion

General model Fourier2:

f(x) = a0 + a1*cos(x*w) + b1*sin(x*w) +

a2*cos(2*x*w) + b2*sin(2*x*w)

Coefficients (with 95% confidence bounds):

a0 = 3.975e-009 (fixed at bound)

a1 = 0.2138 (0.1911, 0.2365)

b1 = 0.8512 (0.8278, 0.8747)

a2 = 0.01898 (-0.003901, 0.04185)

b2 = 0.1687 (0.1454, 0.1919)

w = 6.28 (fixed at bound)

Goodness of fit:

SSE: 0.06083

R-square: 0.9953

Adjusted R-square: 0.9948

RMSE: 0.0458

f(x) = 0.2138*cos(x*w)+0.8512*sin(x*w)+0.01898*cos(2*x*w) + 0.1687*sin(2*x*w

18

You might also like