Function Format

An AcuTrace user-defined function has the following form (in C).

#include "acusim.h"
#include "ufp.h"

UFP_PROTOTYPE(usrFunc);

Void usrFunc (
      UfpHd     ufpHd,          /* Opaque handle for accessing data  */
      Real*     outVec,         /* Output Vector                     */
      Integer   nItems,         /* Number of items in outVec         */
      Integer   vecDim,         /* Vector dimension of outVec        */
)
{  
      ...
      outVec[0] = ... ;
      ...
} /*  end of usrFunc() */

Note that

  • The order of the arguments is slightly different than the order in an AcuSolve user-defined function.
  • The first argument is "ufpHd" not "udfHd".
  • The header file "ufp.h" is included, not "udf.h".
The header file "acusim.h" defines the standard C header file, such as "stdio.h" and "stdarg.h". In addition, the data types used by AcuTrace are defined. The relevant types are:
Integer
type integer
Real
type floating point
String
type string
Void
type void
The header file "ufp.h" contains the definitions and declarations needed by the user function:
  • The definitions of symbolic constants, such as UFP_PARTICLE_MARKER and UFP_PARTICLE_ID. These constants are used to access data.
  • The prototypes of support routines
  • The macro UFP_PROTOTYPE(), which may be used to prototype the user function.

In addition, it contains the useful macros min(a,b), max(a,b) and abs(a) for the computation of minimum, maximum, and absolute value of integers or floating point numbers.

The user-function name (generically called usrFunc above) may be any name supported in C that starts with the letters usr. This naming convention avoids any potential conflict in function name space.

Four arguments are passed to a user-defined function:
ufpHd
This is an opaque handle (pointer) which contains the necessary information for accessing various data. All supporting routines require this argument. For example, to access the current time pass ufpHd to the function ufpGetTime(), as in:
Real time ;
      ...
      time = ufpGetTime( ufpHd ) ;
outVec
This is the resulting vector of the user function. You must fill this vector before returning. On input this array contains all zeros.
nItems
This is the first dimension of outVec, which specifies the number of items that needs to be filled.
vecDim
This is the second dimension of outVec, which specifies the vector dimension of the particular data.
The following example computes the source term in an evolution equation for particle energy:
#include "acusim.h"
#include "ufp.h"

UFP_PROTOTYPE(usrEnergy);
Void usrEnergy (         
         UfpHd   ufpHd,       /* Opaque handle for accessing data */
         Real*   outVec       /* Output Vector                    */
         Integer nItems,      /* Number of items in outVec        */
         Integer vecDim,      /* Vector dimension of outVec       */
)
{
         Real*   usrVals ;    /* user values                */
         Real*   t_fluid ;    /* fluid temperature          */
         Real*   h_particle ; /* particle energy            */
         Real    mpXcp ;      /* mass * c_p for particle    */
         Real    cond ;       /* conductivity               */
         Real*   jac ;        /* source jacobian            */
      usrVals    = ufpGetUsrVals( ufpHd ) ;
      cond       = usrVals[0] ;
      mpXcp      = usrVals[1] ;
      t_fluid    = ufpGetFlowData( ufpHd,UFP_FLOW_TEMPERATURE ) ;
      h_particle = ufpGetUdfData( ufpHd, 0 ) ;
      outVec[0]  = -cond * ( h_particle[0] / mpXcp - t_fluid [0] ) ;

      jac        = ufpGetJac( ufpHd, UFP_JAC_UDF_VARIABLES ) ;
      jac[0]     = -cond / mpXcp ;
}
AcuTrace only accesses user-defined functions that are written in C. To write a user-function in Fortran, you must write a C cover function via which Fortran routine(s) are called. This cover function is accessed by AcuTrace. For the above example the C file could be rewritten as:
#include "acusim.h"
#include "ufp.h"

UFP_PROTOTYPE(usrEnergy);

Void usrEnergy (
      UfpHd   ufpHd,       /* Opaque handle for accessing data */
      Real*   outVec,      /* Vector dimension of outVec       */
      Integer nItems,      /* Number of items in outVec        */
      Integer vecDim,      /* Vector dimension of outVec       */
)
{
      Real*   usrVals ;    /* user values                      */
      Real*   t_fluid ;    /* fluid temperature                */
      Real*   h_particle ; /* particle energy                  */
      Real*   jac ;        /* source jacobian                  */
      usrVals     = ufpGetUsrVals( ufpHd ) ;
      t_fluid     = ufpGetFlowData( ufpHd, UFP_FLOW_TEMPERATURE ) ;
      h_particle  = ufpGetUdfData( ufpHd, 0 ) ;
      jac         = ufpGetJac( ufpHd, UFP_JAC_UDF_VARIABLES     ) ;

      usrEnergyF( usrVals, t_fluid, h_particle, jac, outVec ) ;
}
while the Fortran file may contain:
         subroutine usrEnergyF ( usrVals, t_fluid, h_particle, jac, outVec )
         real*8 usrVals(2), t_fluid, h_particle, jac, outVec
         real*8 cond, mpXcp

           cond       = usrVals(1)
           mpXcp      = usrVals(2)
           outVec       = -cond * ( h_particle / mpXcp – tFluid )
           jac      = -cond / mpXcp 

           return
           end

Note that you are responsible for Fortran/C name conversion.

Note also that the fastest dimension of outVec is nItems, and not vecDim. For example, to fill outVec with the flow velocity at the location of particle, you can write the following code:
#include "acusim.h"
#include "ufp.h"

UFP_PROTOTYPE(usrVel);

Void usrVel (
      UfpHd   ufpHd,             /* Opaque handle for accessing data     */
      Real*   outVec,            /* Output Vector                        */
      Integer nItems,            /* Number of items in outVec            */
      Integer vecDim             /* Vector dimension of outVec           */
)

{
      Real *data ;
      Integer dir, item ;
      if ( vecDim != 3 ) {
          printf( "vecDim = %d, but must equal 3 instead\n", vecDim ) ;
          exit( 0 ) ;
      }
      data = ufpGetFlowData( ufpHd, UFP_FLOW_VELOCITY ) ;

      for ( dir = 0 ; dir < 3 ; dir++ ) {
          for ( item = 0 ; item < nItems; item++ ) {
              outVec[item+dir*nItems] = data[dir] ;
          }
      }
}

In the current uses of AcuTrace user defined functions, nItems always equals 1.