2.6. Dynamic Mesh DEFINE Macros

This section contains descriptions of DEFINE macros that you can use to define UDFs that control the behavior of a dynamic mesh. Note that dynamic mesh UDFs that are defined using DEFINE_CG_MOTION, DEFINE_DYNAMIC_ZONE_PROPERTY, DEFINE_GEOM, and DEFINE_GRID_MOTION can only be executed as compiled UDFs.

Table 2.13: Quick Reference Guide for Dynamic Mesh-Specific DEFINE Macros provides a quick reference guide to the dynamic mesh DEFINE macros, the functions they define, and the dialog boxes where they are activated in Ansys Fluent. Definitions of each DEFINE macro are contained in the udf.h header file. For your convenience, they are listed in Appendix B: DEFINE Macro Definitions.

Table 2.13: Quick Reference Guide for Dynamic Mesh-Specific DEFINE Macros

Function

DEFINE Macro

Dialog Box Activated In

center of gravity motion

DEFINE_CG_MOTION

Dynamic Mesh Zones

swirl center

DEFINE_DYNAMIC_ZONE_PROPERTY

In-Cylinder Output Controls

varying cell layering height

DEFINE_DYNAMIC_ZONE_PROPERTY

Dynamic Mesh Zones

mesh motion

DEFINE_GRID_MOTION

Dynamic Mesh Zones

periodic displacement

DEFINE_PERDISP_MOTION

Periodic Displacement

geometry deformation

DEFINE_GEOM

Dynamic Mesh Zones

properties for Six Degrees of Freedom (six DOF) Solver

DEFINE_SDOF_PROPERTIES

Dynamic Mesh Zones

contact detection

DEFINE_CONTACT

Options


2.6.1. DEFINE_CG_MOTION

2.6.1.1. Description

You can use DEFINE_CG_MOTION to specify the motion of a particular dynamic zone in Ansys Fluent by providing Ansys Fluent with the linear and angular velocities at every time step. Ansys 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.


Important:  Depending on the mesh smoothing models used and the dynamic mesh setup, Ansys Fluent can call mesh motion UDFs multiple times during a single time step. Therefore, UDFs must be written so that the specified motion is an absolute function of time or so that the motion is not incremented incorrectly if the UDF is called multiple times. See the Example for a sample UDF.


2.6.1.2. Usage

DEFINE_CG_MOTION (name, dt, vel, omega, time, dtime)

Argument Type

Description

symbol name

UDF name.

Dynamic_Thread *dt

Pointer to structure that stores the dynamic mesh attributes that you have specified (or that are calculated by Ansys Fluent).

real vel[]

Linear velocity.

real omega[]

Angular velocity.

real time

Current time.

real dtime

Time step.

Function returns

void

There are six arguments to DEFINE_CG_MOTION: name, dt, vel, omega, time, and dtime. You supply name, the name of the UDF. dt, vel, omega, time, and dtime are variables that are passed by the Ansys Fluent solver to your UDF and have SI units. The linear and angular velocities are returned to Ansys Fluent by overwriting the arrays vel and omega, respectively.

When the Relative Motion option is enabled for the rigid body dynamic mesh zone, by default the motion of the dynamic zone dt is defined in the reference frame of the selected Relative Zone. If you want to specify its rotation and translation with respect to the global coordinate system, set DT_NEST_LOC_ROT_P(dt) and DT_NEST_LOC_TRAN_P(dt) to FALSE. The DT_NEST_LOC_ROT_P(dt) and DT_NEST_LOC_TRAN_P(dt) functions store a Boolean value (TRUE or FALSE) that determines whether the motion of the dynamic zone dt is specified with respect to the Relative Zone orientation or with respect to the global coordinate system for rotation and translation, respectively.

For additional information, see Rigid Body Motion in the Fluent User's Guide.

2.6.1.3. Example

Consider the following example where the linear velocity is computed from a simple force balance on the body in the X direction such that

(2–23)

where is velocity, is the force and is the mass of the body. The velocity at time is calculated using an explicit Euler formula as

(2–24)

/************************************************************
* 1-degree of freedom equation of motion (x-direction)
* compiled UDF
************************************************************/
#include "udf.h"

static real v_prev = 0.0;
static real time_prev = 0.0;

DEFINE_CG_MOTION(piston,dt,vel,omega,time,dtime)
{
  Thread *t;
  face_t f;
  real NV_VEC(A);
  real force_x, dv;

  /* reset velocities */
  NV_S(vel, =, 0.0);
  NV_S(omega, =, 0.0);
  if (!Data_Valid_P())
    return;
  /* get the thread pointer for which this motion is defined */
  t = DT_THREAD(dt);
  /* compute pressure force on body by looping through all faces */
  force_x = 0.0;
  begin_f_loop(f,t)
    {
      F_AREA(A,f,t);
      force_x += F_P(f,t) * A[0];
    }
  end_f_loop(f,t)
  /* compute change in velocity, dv = F*dt/mass */
  dv = dtime * force_x / 50.0;
  /* motion UDFs can be called multiple times and should not cause
     false velocity updates */
  if (time > (time_prev + EPSILON))
    {
      v_prev += dv;
      time_prev = time;
    }
  Message("time = %f, x_vel = %f, x_force = %f\n", time, v_prev, force_x);
  /* set x-component of velocity */
  vel[0] = v_prev;
}

2.6.1.4. Hooking a Center of Gravity Motion UDF to Ansys Fluent

After the UDF that you have defined using DEFINE_CG_MOTION is compiled (Compiling UDFs), the name of the argument that you supplied as the first DEFINE macro argument will become visible in the Dynamic Mesh Zones dialog box in Ansys Fluent. See Hooking DEFINE_CG_MOTION UDFs for details on how to hook your DEFINE_CG_MOTION UDF to Ansys Fluent.

2.6.2. DEFINE_DYNAMIC_ZONE_PROPERTY

2.6.2.1. Description

The DEFINE_DYNAMIC_ZONE_PROPERTY UDF can be used in the following applications:

  • swirl center definition for in-cylinder applications

  • variable cell layering height

2.6.2.2. Swirl Center Definition for In-Cylinder Applications

You can use DEFINE_DYNAMIC_ZONE_PROPERTY to calculate swirl center while computing in-cylinder specific output.


Important:  Note that UDFs that are defined using DEFINE_DYNAMIC_ZONE_PROPERTY can only be executed as compiled UDFs.


For information on setting in-cylinder parameters, see In-Cylinder Settings in the User's Guide.

2.6.2.2.1. Usage

DEFINE_DYNAMIC_ZONE_PROPERTY (name, dt, swirl_center)

Argument Type

Description

symbol name

UDF name.

Dynamic_Thread *dt

Pointer to a structure that stores the dynamic mesh attributes. This is set to NULL internally as there are no dynamic zones in the current calculation of swirl center.

real *swirl_center

Pointer to a real array of 3 dimension. You will assign this value in the UDF. The , and values of the swirl_center can be assigned in the UDF through swirl_center[0], swirl_center[1] and swirl_center[2] respectively.

Function returns

void

There are three arguments to DEFINE_DYNAMIC_ZONE_PROPERTY: name, dt, and swirl_center. You supply name, the name of the UDF, and pointer to a real array, swirl_center. dt is a variable that is passed by the Ansys Fluent solver to your UDF.

2.6.2.2.2. Example
/* UDF hook for calculating Swirl Center while computing
  In-Cylinder specific output. Arguments for the UDF
  hook are name of the UDF, dt (dynamic thread) which is
  set to NULL and it is not supposed to be manipulated
  in the UDF, as there are no dynamic zones in the current
  context and swirl center which is to be calculated in the
  UDF. Works in parallel as well.
 */
 
 #include "udf.h"
 #define RPM RP_Get_Real("dynamesh/in-cyn/crank-rpm")
 
 static real Zmin_at_TDC = -0.0014; /* Piston location at TDC */
 static real Zmax = 0.0145;   /* Zmax, a fixed point */
 
 static void my_swirl_center(real * swirl_center)
 {
    real piston_displacement, lambda, CA, l, r;
   #if !RP_NODE
      l = RP_Get_List_Ref_Float("dynamesh/in-cyn/piston-data", 0);
      r= 0.5 * RP_Get_List_Ref_Float("dynamesh/in-cyn/piston-data",1);
   #endif
    host_to_node_real_2(l,r);
    lambda = r/l;
    CA = (CURRENT_TIME*RPM*6.0 +
         RP_Get_Real("dynamesh/in-cyn/crank-start-angle"))*M_PI/180;
    piston_displacement = r*((1+1/lambda) - cos(CA) -
         pow(1-lambda*lambda*sin(CA)*sin(CA),0.5)/lambda);
    swirl_center[0]=0;
    swirl_center[1]=0;
    if (Zmin_at_TDC<Zmax)  
      swirl_center[2]=0.5*(Zmin_at_TDC+Zmax-piston_displacement);
    else
      swirl_center[2]=0.5*(Zmin_at_TDC+Zmax+piston_displacement);
    return;
 }
 
 DEFINE_DYNAMIC_ZONE_PROPERTY(swirl_udf, dt, sc)
 {
    my_swirl_center(sc);
 }
 
2.6.2.2.3. Hooking a Swirl Center UDF to Ansys Fluent

After the UDF that you have defined using DEFINE_DYNAMIC_ZONE_PROPERTY is compiled (as described in Compiling UDFs), the name of the argument that you supplied as the first DEFINE macro argument will become visible in the In-Cylinder Output Controls dialog box in Ansys Fluent.

See Hooking DEFINE_DYNAMIC_ZONE_PROPERTY UDFs for details on how to hook your DEFINE_DYNAMIC_ZONE_PROPERTY UDF to Ansys Fluent.

2.6.2.3. Variable Cell Layering Height

You can use DEFINE_DYNAMIC_ZONE_PROPERTY to specify a varying cell layering height when using the dynamic layering method to split or merge cells adjacent to a moving boundary. The cell layering height can be specified as a function of time for general applications, or as a function of crank angle for in-cylinder applications.


Important:  Note that UDFs that are defined using DEFINE_DYNAMIC_ZONE_PROPERTY can only be executed as compiled UDFs.


For information on the dynamic layering method, see Dynamic Layering in the User's Guide.

2.6.2.3.1. Usage

DEFINE_DYNAMIC_ZONE_PROPERTY (name, dt, height)

Argument Type

Description

symbol name

UDF name.

Dynamic_Thread *dt

Pointer to a structure that stores the dynamic mesh attributes.

real *height

Pointer to a real value layering height whose value will be varied in the UDF as a function of time or crank angle.

Function returns

void

There are three arguments to DEFINE_DYNAMIC_ZONE_PROPERTY: name, dt, and height. You supply name, the name of the UDF, and height, the cell layering height to be assigned in the UDF as a function of time / crank angle. dt is a variable that is passed by the Ansys Fluent solver to your UDF.

In addition to the arguments listed previously, you can use the variable in_cyl_ca_period and the macros DYNAMESH_CURRENT_TIME and TIME_TO_ABSOLUTE_CRANK_ANGLE (time), which are described as follows:

Variable/Macro

Description

in_cyl_ca_period

Crank angle period.

DYNAMESH_CURRENT_TIME

Current dynamic mesh time.

TIME_TO_ABSOLUTE_CRANK_ANGLE (time)

Macro which takes the current time as input and returns the absolute value of the crank angle that is displayed on the mesh preview screen.

Note that in_cyl_ca_period is the value entered for Crank Period in the In-Cylinder tab of the Options dialog box (which can be opened via the Dynamic Mesh task page). The usage of this variable or the macros specified previously necessitates that the DEFINE_DYNAMIC_ZONE_PROPERTY UDF be a compiled UDF. Their usage is illustrated in the example that follows.

Note that the header file dynamesh_tools.h should be included in the UDF, as shown in the example that follows.

2.6.2.3.2. Example
/* UDF hook for implementing varying cell layering height.
  Arguments are the Name of the UDF,
  variable for dynamic thread, and variable
  which holds the layering height value.
  Works only as a compiled UDF, because the usage of
  in_cyn_ca_period and the macros are not
  allowed in interpreted UDFs.
  Header file dynamesh_tools.h should be
  included in order to access the macros
  DYNAMESH_CURRENT_TIME and TIME_TO_ABSOLUTE_CRANK_ANGLE
 */
 
 #include "udf.h"
 #include "dynamesh_tools.h"
 
 DEFINE_DYNAMIC_ZONE_PROPERTY(nonconst_height, dt, lh)
 {
   int temp;
 
   /* Local variable for storing the value of
   Absolute Crank Angle */ real abs_ca;
 
   /* Local variables for saving time and
   Crank Angle, etc. */ real half,quart,time,ca;
 
   half = in_cyn_ca_period / 2.0;
   quart = in_cyn_ca_period /4.0;
 
   time = DYNAMESH_CURRENT_TIME;
 
   ca = TIME_TO_ABSOLUTE_CRANK_ANGLE(time);
   temp = (int) (ca / half);
   abs_ca = ca - temp * half ;
   /* *lh controls the layering height */
   if(abs_ca <= quart)
     *lh = (0.5 + (abs_ca)/ quart * 0.8);
   else
     *lh = (0.5 + ((half - abs_ca) / quart) * 0.8);
 
 } 
2.6.2.3.3. Hooking a Variable Cell Layering Height UDF to Ansys Fluent

After the UDF that you have defined using DEFINE_DYNAMIC_ZONE_PROPERTY is compiled (as described in Compiling UDFs), the name of the argument that you supplied as the first DEFINE macro argument will become visible in the Dynamic Mesh Zones dialog box in Ansys Fluent.

See Hooking DEFINE_DYNAMIC_ZONE_PROPERTY UDFs for details on how to hook your DEFINE_DYNAMIC_ZONE_PROPERTY UDF to Ansys Fluent.

2.6.3. DEFINE_GEOM

2.6.3.1. Description

You can use DEFINE_GEOM to specify the geometry of a deforming zone. By default, Ansys Fluent provides a mechanism for defining node motion along a planar or cylindrical surface. When Ansys Fluent updates a node on a deforming zone (for example, through spring-based smoothing or after local face re-meshing) the node is "repositioned" by calling the DEFINE_GEOM UDF. Note that UDFs that are defined using DEFINE_GEOM can only be executed as compiled UDFs.

2.6.3.2. Usage

DEFINE_GEOM (name, d, dt, position)

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 *position

Pointer to array that stores the position.

Function returns

void

There are four arguments to DEFINE_GEOM: name, d, dt, and position. You supply name, the name of the UDF. d, dt, and position are variables that are passed by the Ansys Fluent solver to your UDF. The new position (after projection to the geometry defining the zone) is returned to Ansys Fluent by overwriting the position array.

2.6.3.3. Example

The following UDF, named parabola, is executed as a compiled UDF.

/************************************************************
 * defining parabola through points (0, 1), (1/2, 5/4), (1, 1)
 ************************************************************/
 #include "udf.h"
 
 DEFINE_GEOM(parabola,domain,dt,position)
 {
    /* set y = -x^2 + x + 1 */
    position[1] = - position[0]*position[0] + position[0] + 1;
 } 

2.6.3.4. Hooking a Dynamic Mesh Geometry UDF to Ansys Fluent

After the UDF that you have defined using DEFINE_GEOM is compiled (see Compiling UDFs for details), the name of the argument that you supplied as the first DEFINE macro argument will become visible in the Dynamic Mesh Zones dialog box in Ansys Fluent. See Hooking DEFINE_GEOM UDFs for details on how to hook your DEFINE_GEOM UDF to Ansys Fluent.

2.6.4. DEFINE_GRID_MOTION

2.6.4.1. Description

By default, Ansys Fluent updates the node positions on a dynamic zone by applying the solid-body motion equation. This implies that there is no relative motion between the nodes on the dynamic zone. However, if you need to control the motion of each node independently, then you can use a DEFINE_GRID_MOTION UDF. A mesh motion UDF can, for example, update the position of each node based on the deflection due to fluid-structure interaction. Note that if your DEFINE_GRID_MOTION UDF defines boundary node motion that has components that should not contribute to the boundary condition, you must take additional steps in the setup, as described in User-Defined Motion in the Fluent User's Guide; an example of this is a boundary with nodes that undergo both rigid body motion (which should contribute to the boundary condition) and smoothing (which should not).

Note that UDFs that are defined using DEFINE_GRID_MOTION can be executed ONLY as compiled UDFs.

2.6.4.2. Usage

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.

To specify a deformation as relative to a rigid body, you must use NODE_COORD_NEST to specify the total deformation, instead of specifying the absolute position of the nodes. If you want the deformation to be relative to the absolute reference frame, you must set DT_NESTED_LOCAL_P(dt) to FALSE. For additional information about defining a deformation on top of rigid body motion, see User-Defined Motion in the Fluent User's Guide.

2.6.4.3. Example 1

Consider the following example where you want to specify the deflection on a cantilever beam based on the position such that

(2–25)

where is the -component of the angular velocity at a position . The node position is updated based on

(2–26)

where is the angular velocity and is the position vector of a node on the dynamic zone.

/**********************************************************
   node motion based on simple beam deflection equation
   compiled UDF
 **********************************************************/
 #include "udf.h"
 
 DEFINE_GRID_MOTION(beam,domain,dt,time,dtime)
 {
    Thread *tf = DT_THREAD(dt);
    face_t f;
    Node *v;
    real NV_VEC(omega), NV_VEC(axis), NV_VEC(dx);
    real NV_VEC(origin), NV_VEC(rvec);
    real sign;
    int n;
    /* set deforming flag on adjacent cell zone */
    SET_DEFORMING_THREAD_FLAG(THREAD_T0(tf));
    sign = -5.0 * sin (26.178 * time);
    Message ("time = %f, omega = %f\n", time, sign);
    NV_S(omega, =, 0.0);
    NV_D(axis, =, 0.0, 1.0, 0.0);
    NV_D(origin, =, 0.0, 0.0, 0.152);
    begin_f_loop(f,tf)
    {
       f_node_loop(f,tf,n)
       {
        v = F_NODE(f,tf,n);
        /* update node if x position is greater than 0.02    
        and that the current node has not been previously
        visited when looping through previous faces */
        if (NODE_X(v) > 0.020 && 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);
         omega[1] = sign * pow (NODE_X(v)/0.230, 0.5);
         NV_VV(rvec, =, NODE_COORD(v), -, origin);
         NV_CROSS(dx, omega, rvec);
         NV_S(dx, *=, dtime);
         NV_V(NODE_COORD(v), +=, dx);
        }
     }
  }
  end_f_loop(f,tf);
 } 

2.6.4.4. Example 2

If you would like for the motion of a boundary to be composed of a rigid body component while also being projected along a surface, you can make use of the Move_And_Project API available in Fluent. The following example shows how you could use a UDF to specify the rotational motion of a windshield wiper, while at the same time requiring that the wiper surface stay pressed against the windshield.

#include "udf.h"

#define MOVE_P TRUE
#define PROJECT_P TRUE

static cxboolean move_p = MOVE_P;
static cxboolean project_p = PROJECT_P;

static cxboolean wiper_sx_need_init_p = TRUE;

static void init_wiper_sx(Dynamic_Thread *dt);
static void get_vel_wiper_sx(Dynamic_Thread *dt, real vel[3], real omega[3],
                             real time, real dtime);

static real wiper_sx_cg_x = 0.0;
static real wiper_sx_cg_y = 0.0;
static real wiper_sx_cg_z = 0.0;
static real wiper_sx_theta_x = 0.0;
static real wiper_sx_theta_y = 0.0;
static real wiper_sx_theta_z = 0.0;
static real wiper_sx_vel_x = 0.0;
static real wiper_sx_vel_y = 0.0;
static real wiper_sx_vel_z = 0.0;
static real wiper_sx_omega_x = 0.0;
static real wiper_sx_omega_y = 0.0;
static real wiper_sx_omega_z = 0.0;
static real wiper_motion_period = 0.0;

int windshield_id, screen_sx_id;


DEFINE_GRID_MOTION(wiper_sx, domain, dt, time, dtime)
{
  void *get_vel = get_vel_wiper_sx;
  Objp *wiper_zones = NULL;
  Objp *windshield = NULL;

  /* windshield_id is the thread ID of the windshield zone onto which
     the wiper zone is projected. */ 
  windshield = t_add(Lookup_Thread(domain, windshield_id), windshield);

  /* screen_sx_id is the thread ID of the wiper zone to be moved and 
     projected. */
  wiper_zones = t_add(Lookup_Thread(domain, screen_sx_id), wiper_zones);

  if (wiper_sx_need_init_p)
  {
    init_wiper_sx(dt);

    wiper_sx_need_init_p = FALSE;
  }

  /* move_p and project_p allow, if needed, the enabling or disabling of
     the movement and projection of the zone. */

  Move_And_Project(dt, time, dtime, get_vel, wiper_zones,  windshield,
                   move_p, project_p);
}



/* Since a DEFINE_GRID_MOTION UDF is being used, there is no GUI/TUI option 
   to specify the initial position and orientation of the body, so this 
   needs to be done here. */
static void
init_wiper_sx(Dynamic_Thread *dt)
{
  N3V_S(DT_CG(dt), =, 0.0);
  N3V_S(DT_THETA(dt), =, 0.0);

  DT_CG(dt)[0] =  wiper_sx_cg_x;
  DT_CG(dt)[1] =  wiper_sx_cg_y;
  DT_CG(dt)[2] =  wiper_sx_cg_z;

  DT_THETA(dt)[0] =  wiper_sx_theta_x;
  DT_THETA(dt)[1] =  wiper_sx_theta_y;
  DT_THETA(dt)[2] =  wiper_sx_theta_z;

  Q_From_Theta(DT_THETA(dt), &(DT_Q(dt)));

  Copy_Dynamic_Thread_Rigid_Body_State(&DT_RB_INIT_STATE(dt),
                                       &DT_RB_CURR_STATE(dt));
}

/* This is the same as a DEFINE_CG_MOTION UDF. */
static void
get_vel_wiper_sx(Dynamic_Thread *dt, real vel[3], real omega[3],
                 real time, real dtime)
{
  N3V_S(vel, =, 0.0);
  N3V_S(omega, =, 0.0);

  vel[0] = wiper_sx_vel_x;
  vel[1] = wiper_sx_vel_y;
  vel[2] = wiper_sx_vel_z;

  omega[0] = wiper_sx_omega_x;
  omega[1] = wiper_sx_omega_y;
  omega[2] = wiper_sx_omega_z;

  if (time > wiper_motion_period) 
  {
    N3V_S(vel, *=, -1.0);
    N3V_S(omega, *=, -1.0);
  }
}

2.6.4.5. Hooking a DEFINE_GRID_MOTION to Ansys Fluent

After the UDF that you have defined using DEFINE_GRID_MOTION is compiled (Compiling UDFs), the name of the argument that you supplied as the first DEFINE macro argument will become visible in the Dynamic Mesh Zones dialog box in Ansys Fluent. See Hooking DEFINE_GRID_MOTION UDFs for details on how to hook your DEFINE_GRID_MOTION UDF to Ansys Fluent.

2.6.5. DEFINE_PERDISP_MOTION

2.6.5.1. Description

You can use DEFINE_PERDISP_MOTION to specify any of the nodal based variables that compose the periodic displacement. This includes the real and/or imaginary mode shape displacement vector components, the mode frequency, amplitude/scaling, and blade passage number. Note that the DEFINE_PERDISP_MOTION UDF is executed only once before the simulation starts to fill the periodic displacement node variables.

2.6.5.2. Usage

DEFINE_PERDISP_MOTION (name, dt, perdisp_array)

Argument Type

Description

symbol name

UDF name.

Dynamic_Thread *dt

Pointer to structure that stores the dynamic mesh attributes that you have specified (or that are calculated by Ansys Fluent).

double *perdisp_array

Pointer to periodic displacement component node based array to be filled by the user.

Function returns

void

There are 3 arguments to DEFINE_PERDISP_MOTION: name, dt, and perdisp_array. You supply name, the name of the UDF. dt and perdisp_array are variables that are passed by the Ansys Fluent solver to your UDF. Your UDF will need to fill the perdisp_array that you want to modify

2.6.5.3. Example

In the following example, you want to specify the Z component of a real mode shape to be used by the Periodic Displacement feature. This component is a function of the radial coordinate calculated at the initial (undeformed) coordinates of the mesh nodes.

/**********************************************************
 node motion based on the bending motion of STCF-11 case
 compiled UDF
 **********************************************************/
#include "udf.h"

DEFINE_PERDISP_MOTION(z_displacement,tf,displacement)
{
  face_t f;
  Node *v;
  int n,node_count;

  Message ("filling Z real comp. displacement\n");

  real radial;

  begin_f_loop(f,tf)
  {
    f_node_loop(f,tf,n)SET_V_FLAGS(F_NODE(f,tf,n),MARK_FLAG);
  }
  end_f_loop(f,tf)

  node_count = 0;
  begin_f_loop(f,tf)
    {
      f_node_loop(f,tf,n)
        {
          v = F_NODE(f,tf,n);
          if (!NODE_FLAG(v,MARK_FLAG)) continue;
          radial = sqrt(NODE_COORD_REF(v)[0]*NODE_COORD_REF(v)[0]
          + NODE_COORD_REF(v)[1]*NODE_COORD_REF(v)[1]);
          displacement[node_count] = -cos(49.15*M_PI/180.0)*(0.063*radial-0.00594)*0.001;
          CLEAR_V_FLAGS(v, MARK_FLAG);
          node_count++;
        }
    }

  end_f_loop(f,tf);
} 

2.6.5.4. Hooking a DEFINE_PERDISP_MOTION to Ansys Fluent

After the UDF that you have defined using DEFINE_PERDISP_MOTION is compiled (Compiling UDFs), the name of the argument that you supplied as the first DEFINE macro argument will become visible in the Periodic Displacement dialog box.

2.6.6. DEFINE_SDOF_PROPERTIES

2.6.6.1. Description

You can use DEFINE_SDOF_PROPERTIES to specify custom properties of moving objects for the six-degrees of freedom (six DOF) solver in Ansys Fluent. These include mass, moment and products of inertia, constraints, and external forces and moment properties. The properties of an object which can consist of multiple zones can change in time, if desired. External load forces and moments can either be specified as global coordinates or body coordinates (these body coordinates are based on the orientation of the body). The moments and products of inertia need to be specified with respect to the body coordinates.

Note that if the moving object is modeled as a half model and includes a plane of symmetry, then you have to specify this by providing the normal vector to the symmetry plane. Further, all six DOF properties such as mass and moments of inertia have to be specified for the full body if a symmetry plane is specified.

2.6.6.2. Usage

DEFINE_SDOF_PROPERTIES (name, properties, dt, time, dtime)

Argument Type

Description

symbol name

UDF name.

real *properties

Pointer to the array that stores the six DOF properties.

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_SDOF_PROPERTIES: name, properties, dt, time, and dtime. You provide the name of the UDF. properties, dt, time, and dtime are variables that are passed by the Ansys Fluent solver to your UDF. The property array pointer that is passed to your function allows you to specify values for any of the following six DOF properties:

 SDOF_MASS   /* mass */
 SDOF_IXX,   /* moment of inertia */
 SDOF_IYY,   /* moment of inertia */
 SDOF_IZZ,   /* moment of inertia */
 SDOF_IXY,   /* product of inertia */
 SDOF_IXZ,   /* product of inertia */
 SDOF_IYZ,   /* product of inertia */
 SDOF_LOAD_LOCAL, /* boolean */
 SDOF_LOAD_F_X, /* external force */
 SDOF_LOAD_F_Y, /* external force */
 SDOF_LOAD_F_Z, /* external force */
 SDOF_LOAD_M_X, /* external moment */
 SDOF_LOAD_M_Y, /* external moment */
 SDOF_LOAD_M_Z, /* external moment */ 
 SDOF_ZERO_TRANS_X, /* boolean, suppress translation in x-direction */
 SDOF_ZERO_TRANS_Y, /* boolean, suppress translation in y-direction */
 SDOF_ZERO_TRANS_Z, /* boolean, suppress translation in z-direction */
 SDOF_ZERO_ROT_X, /* boolean, suppress rotation around x-axis */
 SDOF_ZERO_ROT_Y, /* boolean, suppress rotation around y-axis */
 SDOF_ZERO_ROT_Z, /* boolean, suppress rotation around z-axis */
 SDOF_SYMMETRY_X, /* normal vector of symmetry plane for half model */
 SDOF_SYMMETRY_Y, /* normal vector of symmetry plane for half model */
 SDOF_SYMMETRY_Z, /* normal vector of symmetry plane for half model */

In addition to these properties, you can also use macros to define one DOF translation / rotation settings. For details, see Example 3.

The Boolean properties[SDOF_LOAD_LOCAL] can be used to determine whether the forces and moments are expressed in terms of global coordinates (FALSE) or body coordinates (TRUE). The default value for properties[SDOF_LOAD_LOCAL] is FALSE.

The booleans properties[SDOF_ZERO_TRANS_X], properties[SDOF_ZERO_TRANS_Y], and so on can be used to freeze selected translational and rotational components of the six DOF motion. A value of TRUE will freeze the corresponding component. The default value is FALSE.

If your moving object consists of a half model, then the symmetry plane has to be specified by providing the components of the normal vector, properties[SDOF_SYMMETRY_X], properties[SDOF_SYMMETRY_Y], and properties[SDOF_SYMMETRY_Z].

2.6.6.3. Example 1

The following UDF, named stage, is a simple example of setting mass and moments of inertia properties for a moving object. This UDF is typical for applications in which a body is dropped and the six DOF solver computes the body’s motion in the flow field.

/************************************************************
   Simple example of a six DOF property UDF for a moving body
 **************************************************************/
 #include "udf.h"
 
 DEFINE_SDOF_PROPERTIES(stage, prop, dt, time, dtime)
 {
   prop[SDOF_MASS]   = 800.0;
   prop[SDOF_IXX]  = 200.0;
   prop[SDOF_IYY]  = 100.0;
   prop[SDOF_IZZ]  = 100.0;
   printf ("\nstage: updated 6DOF properties");
 } 

2.6.6.4. Example 2

The following UDF named delta_missile specifies case injector forces and moments that are time-dependent. Specifically, the external forces and moments depend on the current angular orientation of the moving object. Note that this UDF must be executed as a compiled UDF.

/*******************************************************
   Six DOF property compiled UDF with external forces/moments
 *******************************************************/
 #include "udf.h"
 
 DEFINE_SDOF_PROPERTIES(delta_missile, prop, dt, time, dtime)
 {
   prop[SDOF_MASS]   = 907.185;
   prop[SDOF_IXX]  = 27.116;
   prop[SDOF_IYY]  = 488.094;
   prop[SDOF_IZZ]  = 488.094;
   /* add injector forces, moments */
   {
    register real dfront = fabs (DT_CG (dt)[2] -
          (0.179832*DT_THETA (dt)[1]));
    register real dback = fabs (DT_CG (dt)[2] +
          (0.329184*DT_THETA (dt)[1]));
    if (dfront <= 0.100584)
      {
       prop[SDOF_LOAD_F_Z] = 10676.0;
       prop[SDOF_LOAD_M_Y] = -1920.0;
      }
    if (dback <= 0.100584)
      {
       prop[SDOF_LOAD_F_Z] += 42703.0;
       prop[SDOF_LOAD_M_Y] += 14057.0;
      }
   }
   printf ("\ndelta_missile: updated 6DOF properties");
 } 

2.6.6.5. Example 3

The following UDF named blade is for a moving object that is constrained to one DOF rotation. The rotation axis is the vector (0,0,1) and the center of rotation is the point (0.1, 0, 0). The rotation is constrained between -0.5 radians and 0.5 radians. Note that this UDF must be executed as a compiled UDF.

/*******************************************************
   Six DOF property compiled UDF with one DOF rotation
 *******************************************************/
#include "udf.h"

DEFINE_SDOF_PROPERTIES(blade, sdof_prop, dt, time, dtime)
{
  Six_DOF_Object *sdof_obj = NULL;

  sdof_prop[SDOF_MASS] = 1.0;
  sdof_prop[SDOF_IXX] = 1.0;
  sdof_prop[SDOF_IYY] = 1.0;
  sdof_prop[SDOF_IZZ] = 1.0;
  sdof_prop[SDOF_LOAD_M_X] = 0.0;
  sdof_prop[SDOF_LOAD_M_Y] = 0.0;
  sdof_prop[SDOF_LOAD_M_Z] = 0.0;


  sdof_obj = Get_SDOF_Object(DT_PU_NAME(dt));

  if (NULLP(sdof_obj))
  {
    /* Allocate_SDOF_Object must be called with the same name as the udf */
    sdof_obj = Allocate_SDOF_Object(DT_PU_NAME(dt));

    SDOFO_1DOF_T_P(sdof_obj) = FALSE;       /* one DOF translation */
    SDOFO_1DOF_R_P(sdof_obj) = TRUE;        /* one DOF rotation */
    SDOFO_DIR(sdof_obj)[0] = 0.0;
    SDOFO_DIR(sdof_obj)[1] = 0.0;
    SDOFO_DIR(sdof_obj)[2] = 1.0;
    SDOFO_CENTER_ROT(sdof_obj)[0] = 0.1;    /* only needed for one DOF rotation */
    SDOFO_CENTER_ROT(sdof_obj)[1] = 0.0;    /* only needed for one DOF rotation */
    SDOFO_CENTER_ROT(sdof_obj)[2] = 0.0;    /* only needed for one DOF rotation */
    SDOFO_CONS_P(sdof_obj) = TRUE;          /* constrained */

    if (SDOFO_CONS_P(sdof_obj))
    {
      SDOFO_LOC(sdof_obj) = 0.0;
      SDOFO_MIN(sdof_obj) = -0.5;
      SDOFO_MAX(sdof_obj) = 0.5;
      SDOFO_F(sdof_obj) = 0.0;              /* spring preload */
      SDOFO_K(sdof_obj) = 0.0;              /* spring constant */        

      SDOFO_INIT(sdof_obj) = SDOFO_LOC(sdof_obj);
      SDOFO_LOC_N(sdof_obj) = SDOFO_LOC(sdof_obj);
    }
  }
}

2.6.6.6. Hooking a DEFINE_SDOF_PROPERTIES UDF to Ansys Fluent

After the UDF that you have defined using DEFINE_SDOF_PROPERTIES is interpreted (Interpreting UDFs) or compiled (Compiling UDFs), the name of the argument that you supplied as the first DEFINE macro argument will become visible in the Six DOF UDF/Properties drop-down list in the Dynamic Mesh Zones dialog box in Ansys Fluent. See Hooking DEFINE_SDOF_PROPERTIES UDFs for details on how to hook your DEFINE_SDOF_PROPERTIES UDF to Ansys Fluent.

2.6.7. DEFINE_CONTACT

2.6.7.1. Description

You can use DEFINE_CONTACT to specify the response to a contact detection event in Ansys Fluent. Note that UDFs that are defined using DEFINE_CONTACT can be executed only as compiled UDFs.

2.6.7.2. Usage

DEFINE_CONTACT (name, dt, contacts)

Argument Type

Description

symbol name

UDF name.

Dynamic_Thread *dt

Pointer to structure that stores the dynamic mesh attributes that you have specified (or that are calculated by Ansys Fluent).

Objp *contacts

Pointer to a NULL-terminated linked list of elements involved in the contact detection event.

Function returns

void

There are three arguments to DEFINE_CONTACT: name, dt, and contacts. You supply name, the name of the UDF. The dt and contacts structure pointers are passed by the Ansys Fluent solver to your UDF.

2.6.7.3. Example 1

/************************************************************\
 * 2-degree of freedom equation of motion compiled UDF      *
\************************************************************/

DEFINE_CONTACT(contact_props, dt, contacts)
{
  Objp *o;
  face_t face;
  Thread *thread;
  Domain *domain = NULL;
  Dynamic_Thread *ndt = NULL;

  int tid, nid, n_faces;

  real v0dotn1, v1dotn0;
  real nc_mag, norm0_mag, norm1_mag;

  real N3V_VEC (vel_rel);
  real N3V_VEC (nc), N3V_VEC (nctmp);
  real N3V_VEC (xc), N3V_VEC (xctmp);
  real N3V_VEC (vel0), N3V_VEC (omega0), N3V_VEC (theta0), N3V_VEC (norm0);
  real N3V_VEC (vel1), N3V_VEC (omega1), N3V_VEC (theta1), N3V_VEC (norm1);

  if (!Data_Valid_P())
    {
      return;
    }

  /* Define a common contact point / plane */
  N3V_S (nc, =, 0.0);
  N3V_S (xc, =, 0.0);

  /* Fetch current thread ID */
  tid = THREAD_ID (DT_THREAD (dt));

  nid = -1;
  n_faces = 0;

  loop (o, contacts)
    {
      face = O_F (o);
      thread = O_F_THREAD (o);

      /* Skip faces on current thread */
      if (THREAD_ID (thread) == tid)
        {
          continue;
        }

      /* Note ID of the other thread for posterity */
      if (nid == -1)
        {
          nid = THREAD_ID (thread);
        }

      /* Initialize to zero */
      N3V_S (nctmp, =, 0.0);
      N3V_S (xctmp, =, 0.0);

      F_AREA (nctmp, face, thread);
      F_CENTROID (xctmp, face, thread);

      /**
       * Negative sum because wall normals
       * point out of the fluid domain
       */
      N3V_V (nc, -=, nctmp);
      N3V_V (xc, +=, xctmp);

      n_faces++;
    }

# if RP_NODE
    {
      /* Reduce in parallel */
      nid = PRF_GIHIGH1 (nid);
      n_faces = PRF_GISUM1 (n_faces);

      PRF_GRSUM3 (nc[0], nc[1], nc[2]);
      PRF_GRSUM3 (xc[0], xc[1], xc[2]);
    }
# endif

  /* Propagate to host */
  node_to_host_int_2 (nid, n_faces);
  node_to_host_real_3 (nc[0], nc[1], nc[2]);
  node_to_host_real_3 (xc[0], xc[1], xc[2]);

  if (n_faces > 0)
    {
      nc_mag = N3V_MAG (nc) + REAL_MIN;

      N3V_S (nc, /=, nc_mag);
      N3V_S (xc, /=, n_faces);
    }
  else
    {
      return;
    }

  Message
  (
    "\nContact:: tid: %d nid: %d n_faces: %d "
    "Point: (%f %f %f) Normal: (%f %f %f)",
    tid, nid, n_faces,
    xc[0], xc[1], xc[2],
    nc[0], nc[1], nc[2]
  );

  /* Fetch thread for opposite body */
  domain = THREAD_DOMAIN (DT_THREAD (dt));
  thread = Lookup_Thread (domain, nid);

  if (NULLP (thread))
    {
      Message ("\nWarning: No thread for nid: %d ", nid);

      return;
    }
  else
    {
      ndt = THREAD_DT (thread);
    }

  /* Fetch body parameters */
  SDOF_Get_Motion (dt, vel0, omega0, theta0);

  /* Compute difference vectors and normalize */
  N3V_VV (norm0, =, xc, -, DT_CG (dt));
  norm0_mag = N3V_MAG (norm0) + REAL_MIN;
  N3V_S (norm0, /=, norm0_mag);

  if (NULLP (ndt))
    {
      /* Stationary body / wall. Use contact normal */
      N3V_V (norm1, =, nc);

      /* Compute relative velocity */
      N3V_S (vel1, =, 0.0);
      N3V_V (vel_rel, =, vel0);
    }
  else
    {
      /* Fetch body parameters */
      SDOF_Get_Motion (ndt, vel1, omega1, theta1);

      /* Compute relative velocity */
      N3V_VV (vel_rel, =, vel0, -, vel1);

      /* Compute difference vectors and normalize */
      N3V_VV (norm1, =, xc, -, DT_CG (ndt));
      norm1_mag = N3V_MAG (norm1) + REAL_MIN;
      N3V_S (norm1, /=, norm1_mag);

      /* Check if velocity needs to be reversed */
      if (N3V_DOT (vel_rel, nc) < 0.0)
        {
          /* Reflect velocity across the normal */
          v1dotn0 = 2.0 * N3V_DOT (vel1, norm0);

          N3V_S (norm0, *=, v1dotn0);
          N3V_V (vel1, -=, norm0);

          /* Override body velocity */
          SDOF_Overwrite_Motion (ndt, vel1, omega1, theta1);
        }
    }

  /* Check if velocity needs to be reversed */
  if (N3V_DOT (vel_rel, nc) < 0.0)
    {
      /* Reflect velocity across the normal */
      v0dotn1 = 2.0 * N3V_DOT (vel0, norm1);

      N3V_S (norm1, *=, v0dotn1);
      N3V_V (vel0, -=, norm1);

      /* Override body velocity */
      SDOF_Overwrite_Motion (dt, vel0, omega0, theta0);
    }

  Message
  (
    "\ncontact_props: Updated :: vel0 = (%f %f %f) vel1 = (%f %f %f)",
    vel0[0], vel0[1], vel0[2], vel1[0], vel1[1], vel1[2]
  );
}

2.6.7.4. Example 2

You can also use nodal contact information in a DEFINE_GRID_MOTION UDF that can be used to constrain grid motion in certain situations.

/*********************************************************************\
 * This UDF provides the moving-deforming mesh model with the        *
 * locations of the nodes on a deforming boundary. The UDF uses      *
 * nodal contact information to constrain motion.                    *
 * Compiled UDF, all metric units                                    *
\*********************************************************************/

#include "udf.h"

static real L = 2.5;
static real xoffset = 2.8;
static real amplitude = 1.0;
static real total_time = 15.0;

DEFINE_GRID_MOTION(wall_deform_top, domain, dt, time, dtime)
{
  int n;
  Node *node;
  face_t face;

  real A, factor, fraction;

  Thread *thread = DT_THREAD (dt);

  /**
   * Set/activate the deforming flag on adjacent cell zone, which
   * means that the cells adjacent to the deforming wall will also be
   * deformed, in order to avoid skewness.
   */
  SET_DEFORMING_THREAD_FLAG (THREAD_T0 (thread));

  /**
   * Loop over the deforming boundary zone faces;
   * inner loop loops over all nodes of a given face;
   * Thus, since one node can belong to several faces, one must guard
   * against operating on a given node more than once:
   */
  begin_f_loop (face, thread)
    {
      f_node_loop (face, thread, n)
        {
          node = F_NODE (face, thread, n);

          /* Compute the amplitude as a function of time */
          fraction = (time / total_time);

          factor = -1.0 * fabs (2.0 * (fraction - floor (fraction + 0.5)));

          A = factor * amplitude * sin (M_PI * (NODE_X (node) - xoffset) / L);

          /*
           * If node is in contact and motion is downward,
           * prevent further motion.
           */
          if (NODE_POS_CONTACT_P (node) && ((A + 1.0) < NODE_Y (node)))
            {
              NODE_POS_UPDATED (node);
              continue;
            }

          /*
           * Update the current node only if it has not been
           * previously visited:
           */
          if (NODE_POS_NEED_UPDATE (node))
            {
              /**
               * Set flag to indicate that the current node's
               * position has been updated, so that it will not be
               * updated during a future pass through the loop:
               */
              NODE_POS_UPDATED (node);

              NODE_Y (node) = A + 1.0;
            }
        }
    }
  end_f_loop (face, thread);
}

2.6.7.5. Hooking a DEFINE_CONTACT UDF to Ansys Fluent

After the UDF that you have defined using DEFINE_CONTACT is compiled (Compiling UDFs), the name of the argument that you supplied as the first DEFINE macro argument will become visible in the UDF drop-down list in the Contact Detection tab of the Options dialog box in Ansys Fluent. See Hooking DEFINE_CONTACT UDFs for details on how to hook your DEFINE_CONTACT UDF to Ansys Fluent.