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 |
|
Dialog Box Activated In |
---|---|---|
center of gravity motion |
|
Dynamic Mesh Zones |
swirl center |
|
In-Cylinder Output Controls |
varying cell layering height |
|
Dynamic Mesh Zones |
mesh motion |
|
Dynamic Mesh Zones |
periodic displacement |
|
Periodic Displacement |
geometry deformation |
|
Dynamic Mesh Zones |
properties for Six Degrees of Freedom (six DOF) Solver |
|
Dynamic Mesh Zones |
contact detection |
|
Options |
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.
DEFINE_CG_MOTION
(name
,
dt
, vel
, omega
,
time
, dtime
)
Argument Type |
Description |
---|---|
|
UDF name. |
|
Pointer to structure that stores the dynamic mesh attributes that you have specified (or that are calculated by Ansys Fluent). |
|
Linear velocity. |
|
Angular velocity. |
|
Current time. |
|
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.
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; }
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.
The DEFINE_DYNAMIC_ZONE_PROPERTY
UDF
can be used in the following applications:
swirl center definition for in-cylinder applications
variable cell layering height
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.
DEFINE_DYNAMIC_ZONE_PROPERTY
(name
, dt
, swirl_center
)
Argument Type |
Description |
---|---|
|
UDF name. |
|
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. |
|
Pointer to a real array of 3 dimension. You will assign this
value in the UDF. The , and values of the |
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.
/* 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); }
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.
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.
DEFINE_DYNAMIC_ZONE_PROPERTY
(name
, dt
, height
)
Argument Type |
Description |
---|---|
|
UDF name. |
|
Pointer to a structure that stores the dynamic mesh attributes. |
|
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 |
---|---|
|
Crank angle period. |
|
Current dynamic mesh 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.
/* 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); }
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.
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.
DEFINE_GEOM
(name
, d
, dt
, position
)
Argument Type |
Description |
---|---|
|
UDF name. |
|
Pointer to domain. |
|
Pointer to structure that stores the dynamic mesh attributes that you have specified (or that are calculated by Ansys Fluent). |
|
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.
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; }
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.
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.
DEFINE_GRID_MOTION
(name
, d
, dt
, time
, dtime
)
Argument Type |
Description |
---|---|
|
UDF name. |
|
Pointer to domain. |
|
Pointer to structure that stores the dynamic mesh attributes that you have specified (or that are calculated by Ansys Fluent). |
|
Current time. |
|
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.
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); }
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); } }
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.
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.
DEFINE_PERDISP_MOTION
(name
,
dt
, perdisp_array
)
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
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); }
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.
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.
DEFINE_SDOF_PROPERTIES
(name
,
properties
, dt
,
time
, dtime
)
Argument Type |
Description |
---|---|
|
UDF name. |
|
Pointer to the array that stores the six DOF properties. |
|
Pointer to structure that stores the dynamic mesh attributes that you have specified (or that are calculated by Ansys Fluent). |
|
Current time. |
|
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]
.
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"); }
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"); }
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); } } }
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.
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.
DEFINE_CONTACT
(name
,
dt
, contacts
)
Argument Type |
Description |
---|---|
|
UDF name. |
|
Pointer to structure that stores the dynamic mesh attributes that you have specified (or that are calculated by Ansys Fluent). |
|
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-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] ); }
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); }
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.