Mechanical APDL allows more than one user routine in a single run. You can issue multiple /UPF commands in the input file to activate the routines.
Using multiple routines simultaneously may require sharing data generated by one routine with another. The easiest method for doing so is to use common-block (or global) variables.
In Linux, a single shared library contains all compiled user routines and data sharing is straightforward. In Windows, however, each user routine is built into a separate dynamic link library (DLL); to share data, functions and data must be explicitly exported and imported.
The userdata subroutine enables
you to store the common-block functionality and data. You can edit usercm (included in userdata
) to add
common-block variables. The compiler uses !DEC$ ATTRIBUTES DLLEXPORT
and !DEC$ ATTRIBUTES DLLIMPORT
to indicate which functions and
common-block variables to export or import, respectively. Both commands are valid for the
supported Intel Fortran and C compilers.
Example 1.1: Creating and Exporting Functions and Common Block Variables
c usercm.inc *comdeck,usercm USERDISTRIB !DEC$ ATTRIBUTES DLLEXPORT :: /usercm/ common /usercm/ userdatsz,userdatptr pointer (userdatptr,userdataarray) integer userdatsz double precision userdataarray(*) c c userdat.F . Sample function exported for use in other subroutines function getusercmvals(iloc,sz,outdata) !DEC$ ATTRIBUTES DLLEXPORT :: getusercmvals #include "usercm.inc" integer iloc,sz,getusercmvals,iX double precision outdata(*) double precision userdataloc(*) pointer (userdatptr,userdataloc) if (iloc.lt.1.or.iloc+sz-1.gt.userdatsz) then getusercmvals = 0 else do iX=iloc,iloc+sz-1,1 outdata(iX)= userdataloc(iX) enddo getusercmvals = 1 endif return end
Example 1.2: Importing and Using Functions
Imported functions are added to the interface section. Common blocks are also imported as needed.
INTERFACE FUNCTION getusercmvalsz () !DEC$ ATTRIBUTES DLLIMPORT :: getusercmvalsz integer getusercmvalsz END return end END INTERFACE !DEC$ ATTRIBUTES DLLIMPORT :: /usercm/ common /usercm/ userdatsz,userdatptr pointer (userdatptr,userdataarray) integer userdatsz double precision userdataarray(*)
Using common-block variables in shared memory requires care. Multiple threads started by the executable access and share the same memory location and can overwrite each other's values. To minimize conflict, allocate enough space for each thread and avoid writing to the same location at the same time.
Example 1.3: Safely Allocating Separate Data for Each Thread
One double-precision location exists in the common block. Enough space is allocated for
numberofprocessors
* 1 locations. Functions
pplock
and ppunlock
are used when initializing the
memory or values.
When setting the value, write to the iy memory location only (used for that specific thread). Even if other threads access the same common block, they do not modify that memory location.
c ----------------------- c Initialize common-block values in user routine 1 such as USolBeg.F c call pplock(LOCKUPF) inumprocs = ppinqr(PP_NPROCS) call initusercmvals(inumprocs) val1(:) = 0.0d0 iy = setusercmvals(ix, inumprocs,val1) call ppunlock(LOCKUPF) c c ----------------------- c Set common-block value to time in user routine 2 c isize = 1 c Go to location allocated for the iy the thread iy = ppproc()+1 val1 = time c Set just the iy-th value. iy = setusercmvals(iy,isize,val1) c c ----------------------- c Get saved value from common block in user routine 3 c iproc = ppproc()+1 isize = 1 call getusercmvals(iproc,isize,dFldTime)
For information about the pplock, ppinqr, ppunlock, and ppproc subroutines used in Example 1.3: Safely Allocating Separate Data for Each Thread, see Subroutines for Your Convenience.