19.9.1. Junction Box Example 1: Profile Boundary Conditions

19.9.1.1. Problem Setup

A common application of User Subroutines is the specification of a profile boundary condition. In this example, experimental data has been obtained for inflow boundary values at the inlet to a domain. The simulation is that of flow over a backward facing step; the inlet profile is given some step heights upstream of the step.

The experimental data is provided in a two-dimensional table. The first column contains the wall-normal positions and the remaining five columns contain values for the u, v and w velocity components, the turbulent kinetic energy and the turbulent eddy dissipation. The table has 29 wall-normal positions and appears as follows:

6   29
1.01724E-01 1.01665E-04 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00
1.05172E-01 2.94293E-04 0.00000E+00 0.00000E+00 9.43200E+00 1.33962E+04
........... ........... ........... ........... ........... ...........
1.94828E-01 2.94293E-04 0.00000E+00 0.00000E+00 8.94000E+00 8046000E+02
1.98276E-01 1.01665E-04 0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00

A Junction Box routine is required to read the data from the file and automatically pass it to the follower processors. The profile boundary is then set by a User CEL Function.

19.9.1.1.1. Creating the Junction Box Routine and User CEL Function

In CFX-Pre, you should create a Junction Box Routine, User CEL Routine and a User CEL Function. Further information on creating these objects in CFX-Pre is available in User Functions in the CFX-Pre User's Guide.

User CEL Routine:

  • Routine Name: U profile Routine

  • Option: User CEL Function

  • Calling Name: user_uprofile

  • Library Name: backstep

  • Library Path: /home/user/Shared_Libraries

User CEL Function:

  • Function Name: U profile

  • Option: User Function

  • User Routine Name: U Profile Routine

  • Argument List: [m]

  • Result Units: [m/s]

Junction Box Routine:

  • Routine Name: Load Inlet Profiles

  • Option: Junction Box Routine

  • Calling Name: user_input

  • Library Name: backstep

  • Library Path: /home/user/Shared_Libraries

  • Junction Box Location: User input

As can be seen from this example, both routines are stored in the shared library libbackstep.so (the prefix and suffix may vary depending on your platform), which can be found in the /home/user/Shared_Libraries/<architecture> directory.

19.9.1.1.2. Setting the Boundary Condition

The new User CEL Function can now be used within a boundary condition in CFX-Pre. For example, create an inlet boundary condition using the Cartesian Velocity Components option and then set:

  • U = U profile(y)

  • V = 0.00 [m s^-1]

  • W = 0.00 [m s^-1]

19.9.1.1.3. Enabling the Junction Box Routine

In order to ensure that the solver calls the declared Junction Box routine, you should:

The following CCL should be added to the CCL File. You can also enter this into the Command Editor dialog box (Tools > Command Editor) in CFX-Pre and click Process.

USER:
	# Location of external inlet profiles.
	Input File = /home/user/u_profile.dat
	# Printing control. First character should be Y or N
	User Printing = Yes
END

The CCL parameter USER/Input File automatically becomes the MMS parameter USER/INPUT_FILE. This is used in the Fortran subroutines to read in the user data. The User Printing parameter becomes the MMS parameter USER/USER_PRINTING. This is used in the following subroutines to control printing.

19.9.1.2. User Fortran Junction Box Routines

The data table is stored in an ASCII file in /home/user/backstep.user.dat. The user code to read the data from this file is implemented in a user routine shown below. The routine was developed from the template routine jcb_template.F available in <CFXROOT>/examples/.

#include "cfx5ext.h"
C=======================================================================
C
C Purpose: User Fortran for importing/interpolating external
C          velocity profile.
C
C Junction Box Routines:
C          USER_INPUT -> USER_INPUT_TABLE
C
C User CEL Functions:
C          USER_UPROFILE -> USER_UPROFILE_CAL -> USER_TABINT
C                                             -> USER_PRINT_PROFILE
C
C Command File data areas used:
C          /USER/INPUT_FILE
C          /USER/USER_PRINTING
C
C User Input data areas used:
C          /USER_DATA/NCOL
C          /USER_DATA/NLIN
C          /USER_DATA/TABLE(NCOL,NLIN)
C
C=======================================================================
dllexport(user_input)
      SUBROUTINE USER_INPUT ( CZ,DZ,IZ,LZ,RZ )
CC
CC --------------------
CC        Input
CC --------------------
CC  none
CC
CC --------------------
CC      Modified
CC --------------------
CC
CC  Stacks possibly.
CC
CC --------------------
CC        Output
CC --------------------
CC  none
CC
CC --------------------
CC       Details
CC --------------------
CC
CC  Load a velocity profile from filename given in /USER/INPUT_FILE.
CC
CC  This routine is defined with 'Junction Box Location = User Input'.
CC  It reads data into the directory /USER_DATA.
CC
CC  In a parallel run, this routine is only called from the leader
CC  process, but any /USER_DATA data will then be automatically 
CC  copied to the /USER_DATA directory on all follower processes.
CC======================================================================
C ------------------------------
C     Preprocessor includes
C ------------------------------
#include "MMS.h"
#include "stack_point.h"
C
C ------------------------------
C        Argument list
C ------------------------------
      INTEGER IZ(*)
      CHARACTER CZ(*)*(1)
      DOUBLE PRECISION DZ(*)
      LOGICAL LZ(*)
      REAL RZ(*)
C
C ------------------------------
C        Local Parameters
C ------------------------------
C Fortran input channel
      INTEGER NCHANNEL
      PARAMETER (NCHANNEL=59)
C
C ------------------------------
C        Local Variables
C ------------------------------
C Result flag
      CHARACTER*4 CRESLT
C
C System info
      CHARACTER*(MXPNAM) WHICH_CALL
C
C Command file info
      CHARACTER*(MXPNAM) INPUT_FILE
      CHARACTER*(MXPNAM) USER_PRINTING
C
      INTEGER NCOL, NLIN
C
C ------------------------------
C        Stack pointers
C ------------------------------
C
      __stack_point__ pOINT, pTABLE
C
C=======================================================================
C ---------------------------
C    Executable Statements
C ---------------------------
C
C Look up the system info for junction box calls: WHICH_CALL
      WHICH_CALL = 'Unknown'
      CALL PEEKCS( '/USER/WHICH_CALL', WHICH_CALL, 'SKIP', CRESLT, CZ )
C
C Look up command file info: INPUT_FILE, USER_PRINTING.
      INPUT_FILE = 'Unknown'
      CALL PEEKCS( '/USER/INPUT_FILE', INPUT_FILE, 'STOP', CRESLT, CZ )
C
      USER_PRINTING = 'No'
      CALL PEEKCS( '/USER/USER_PRINTING', USER_PRINTING, 'SKIP',
     &            CRESLT, CZ )
C
C Send any diagnostic messages via leader process.
      IF ( USER_PRINTING(1:1) .NE. 'N' ) THEN
         CALL MESAGE( 'WRITE', 'Start USER_INPUT')
         CALL MESAGE( 'WRITE', 'WHICH_CALL='//WHICH_CALL )
         CALL MESAGE( 'WRITE', 'INPUT_FILE='//INPUT_FILE )
      END IF
C
C Ensure that directory /USER_DATA exists and make this the current directory.
C
      CALL PSHDIR( '/', 'STOP', CRESLT )
      CALL CHGDIR( 'USER_DATA', 'STOP', CRESLT )
C
C Read data from file.
C
      OPEN( NCHANNEL, FILE=INPUT_FILE )
C
C Create space for array sizes NCOL and NLIN
      CALL MAKDAT( 'NCOL', 'INTR', 'STOP', 1, pOINT, CRESLT )
      CALL MAKDAT( 'NLIN', 'INTR', 'STOP', 1, pOINT, CRESLT )
C
C Read data in NCOL and NLIN
      READ ( NCHANNEL, * ) NCOL, NLIN
      CALL POKEI( 'NCOL', 1, NCOL, 'STOP', CRESLT, IZ )
      CALL POKEI( 'NLIN', 1, NLIN, 'STOP', CRESLT, IZ )
C
C Create space for array TABLE(NCOL,NLIN)
      CALL MAKDAT( 'TABLE', 'REAL', 'STOP', NCOL*NLIN, pTABLE, CRESLT )
C
C Read data into TABLE(NLIN,NCOL) located in RZ at pointer pTABLE
      CALL USER_INPUT_TABLE( NCHANNEL, RZ(pTABLE), NCOL, NLIN )
C
      CLOSE(NCHANNEL)
C
C Return to current directory on entry.
C
      CALL POPDIR( 'STOP', CRESLT )
C
C Send any diagnostic messages via leader process.
      IF ( USER_PRINTING(1:1) .NE. 'N' ) THEN
         CALL MESAGE( 'WRITE','End USER_INPUT' )
      END IF
C
      END
C
C=======================================================================
      SUBROUTINE USER_INPUT_TABLE( NCHANNEL, TABLE, NCOL, NLIN )
C     ----------------------------------------------------------
C Read data into TABLE(NLIN,NCOL)
C
C Input arguments
      INTEGER NCHANNEL, NCOL, NLIN
C
C Output arguments
      REAL TABLE(NCOL,NLIN)
C
      READ( NCHANNEL, *, END=900, ERR=900 ) TABLE
      RETURN
C
C Error Handling via leader process.
 900  CONTINUE
      CALL MESAGE( 'WRITE', 'Unexpected error/end on user file' )
      CALL CFXSTP( 'USER_INPUT_TABLE' )
      END

After the subroutine USER_INPUT has been executed, the directory USER_DATA exists and contains the following information:

  • /USER_DATA/NLIN: number of lines in the table.

  • /USER_DATA/NCOL: number of columns in the table.

  • /USER_DATA/TABLE: table containing the inflow boundary profiles.

The data specified in the profile table is used to specify the inlet values for the velocity components and the turbulence quantities. Therefore, each of these quantities requires a User CEL Routine to be created. The following routine is for the first velocity component, which refers to the second column of the inflow profile table:

C=======================================================================
dllexport(user_uprofile)
      SUBROUTINE USER_UPROFILE ( 
     & NLOC, NRET, NARG, RET, ARGS, CRESLT, CZ,DZ,IZ,LZ,RZ )
CC
CD User routine: interpolate U profile from imported data.
CC
CC --------------------
CC        Input
CC --------------------
CC  NLOC   - size of current locale
CC  NRET   - number of components in result
CC  NARG   - number of arguments in call
CC  ARGS() - (NLOC,NARG) argument values
CC
CC --------------------
CC      Modified
CC --------------------
CC  Stacks possibly.
CC
CC --------------------
CC        Output
CC --------------------
CC  RET()  - (NLOC,NRET) return values
CC  CRESLT - 'GOOD' for success
CC
CC --------------------
CC       Details
CC --------------------
CC  An array of profiles TABLE(NCOL,NLIN) has been read from file
CC  by junction box routine USER_INPUT. This routine now uses
CC  the table to return U values for each y value given in the
CC  first argument.
CC
CC======================================================================
C ------------------------------
C     Preprocessor includes
C ------------------------------
#include "MMS.h"
#include "stack_point.h"
C
C ------------------------------
C        Argument list
C ------------------------------
      INTEGER NLOC,NARG,NRET
C
      CHARACTER CRESLT*(*)
C
      REAL ARGS(NLOC,NARG), RET(NLOC,NRET)
C
      INTEGER IZ(*)
      CHARACTER CZ(*)*(1)
      DOUBLE PRECISION DZ(*)
      LOGICAL LZ(*)
      REAL RZ(*)
C
C ------------------------------
C        Local Variables
C ------------------------------
C Table info
      INTEGER NCOL, NLIN
C
C Command file info
      CHARACTER*(MXPNAM) USER_PRINTING
C
C Arguments for LOCDAT call
      CHARACTER*4 CDTYPE
      INTEGER ISIZE
C
C ------------------------------
C        Stack pointers
C ------------------------------
      __stack_point__ pTABLE
C
C=======================================================================
C ---------------------------
C    Executable Statements
C ---------------------------
C Look up command file info: USER_PRINTING.
      USER_PRINTING = 'No'
      CALL PEEKCS( '/USER/USER_PRINTING', USER_PRINTING, 'SKIP',
     & CRESLT, CZ )
C
C Send any diagnostic messages via leader process.
      IF ( USER_PRINTING(1:1) .NE. 'N' ) THEN
         CALL MESAGE( 'WRITE','Start USER_UPROFILE' )
      END IF
C
C Initialise RET() to zero.
      CALL SET_A_0( RET, NLOC*NRET )
C
C Find TABLE(NCOL,NLIN)
      CALL PEEKI( '/USER_DATA/NCOL', 1, NCOL, 'STOP', CRESLT, IZ )
      CALL PEEKI( '/USER_DATA/NLIN', 1, NLIN, 'STOP', CRESLT, IZ )
      CALL LOCDAT( '/USER_DATA/TABLE', CDTYPE, 'STOP', ISIZE,
     &            pTABLE, CRESLT )
C
C Compute profile U(N) for Y(N) using TABLE(NCOL,NLIN)
      CALL USER_UPROFILE_CAL(
     & RET, ARGS, NLOC, RZ(pTABLE), NCOL, NLIN, USER_PRINTING )
C
C Send any diagnostic messages via leader process.
      IF ( USER_PRINTING(1:1) .NE. 'N' ) THEN
         CALL MESAGE( 'WRITE','End USER_UPROFILE' )
      END IF
C
C Set success flag.
      CRESLT = 'GOOD'
C
      END
C=======================================================================
      SUBROUTINE USER_UPROFILE_CAL(
     & U, Y, N, TABLE, NCOL, NLIN, USER_PRINTING )
C     ---------------------------------------------
C Compute profile U(N) for Y(N) using TABLE(NCOL,NLIN)
C-----------------------
C 	Input arguments
C-----------------------
C number of points
      INTEGER N
C
C y coordinate
      REAL Y(N)
C
C input table
C Y values are in the first column of the table.
C U values are in the second column of the table.
      INTEGER NCOL, NLIN
      REAL TABLE(NCOL,NLIN)
C
C printing control
      CHARACTER*(*) USER_PRINTING
C--------------------------
C 	Output arguments
C--------------------------
      REAL U(N)
C----------------------
C	 Locals
C----------------------
      INTEGER I
C
      DO I = 1, N
C
C interpolate U(I) for Y(I)
      CALL USER_TABINT( Y(I), U(I), 1, 2, TABLE, NCOL, NLIN )
C
C print I, U(I), Y(I)
C note that this data is unstructured, not ordered by Y value.
C
      IF ( USER_PRINTING(1:1) .NE. 'N' ) THEN
         CALL USER_PRINT_PROFILE( I, Y(I), U(I) )
      END IF
C
      END DO
C
      END
C=======================================================================
      SUBROUTINE USER_TABINT( X, Y, IX, IY, TABLE, NCOL, NLIN )
C     ---------------------------------------------------------
C Linear interpolation of table data Y in column IY of TABLE(),
C against an increasing co-ordinate X in column IX of TABLE().
C---------------
C	 Inputs:
C---------------
      REAL X
      INTEGER IX, IY
      INTEGER NCOL, NLIN
      REAL TABLE(NCOL,NLIN)
C---------------
C	Outputs:
C---------------
      REAL Y
C---------------
C	Locals:
C---------------
      INTEGER I, J
      REAL S
C
C Find J such that TABLE(IX,J) <= X < TABLE(IX,J+1)
      J = 0
      DO I = 1, NLIN
         IF ( TABLE(IX,I) .GT. X ) GO TO 100
         J = I
      END DO
 100  CONTINUE
C
C Linearly interpolate Y
      IF( J .LE. 0 ) THEN
         Y = TABLE(IY,1)
      ELSE IF ( J .GE. NLIN ) THEN
         Y = TABLE(IY,NLIN)
      ELSE
         S = (X-TABLE(IX,J))/(TABLE(IX,J+1)-TABLE(IX,J))
         Y = TABLE(IY,J)*(1.0-S) + TABLE(IY,J+1)*S
      END IF
      END
C
C=======================================================================
      SUBROUTINE USER_PRINT_PROFILE( I, Y, U )
C     ----------------------------------------
C Print (I, Y, U) via leader process.
C
      INTEGER I
      REAL Y, U
C
      CHARACTER CFROMI*5, CFROMR*15
      EXTERNAL CFROMI, CFROMR
C
      CALL MESAGE( 'BUFF-ASIS', CFROMI(I) )
      CALL MESAGE( 'BUFF-ASIS', ' Y(I) = ' )
      CALL MESAGE( 'BUFF-ASIS', CFROMR(Y) )
      CALL MESAGE( 'BUFF-ASIS', ' U(I) = ' )
      CALL MESAGE( 'BUFF-ASIS', CFROMR(U) )
      CALL MESAGE( 'BUFF-OUT', ' ' )
      END