You need do nothing to avoid gaining control at one of the user exit calls. If you do wish to gain control at one of these points, you must write your own user exit subroutine and link it with your application code so that at execution time your user exit subroutine will gain control in place of the stub routine provided. If you are running with an static version of the Optimization Library, and you give your user exit code the same name as the corresponding stub, then all you need do is link your user exit before the library.
If you are running in a PC environment, with the DLL version of the
library, or if you want to call your user exit subroutine by a different
name than that of the stub provided, or if you want to provide several
different versions of a user exit routine various ones of which may be
called under different conditions to be determined at run time, then it
will be necessary to "register" your routine before it is called. This is
accomplished by a call to EKKRGCB. This
subroutine "makes known" a user exit subroutine by associating its name with
a number that identifies one of the stub routines. The correspondence between
the number identifiers and the user exit names is as follows.
1 corresponds to
MIP User Exit EKKBRNU,
2 corresponds to
MIP User Exit EKKCHNU,
3 corresponds to
MIP User Exit EKKCUTU,
4 corresponds to
MIP User Exit EKKEVNU,
5 corresponds to
MIP User Exit EKKHEUU,
6 corresponds to
Iteration User Exit EKKITRU,
7 corresponds to
Message User Exit EKKMSGU,
8 corresponds to
MIP User Exit EKKNODU,
9 corresponds to
Row Ordering User Exit EKKORDU, and
10 corresponds to
MIP User Exit EKKSLVU.
A companion subroutine, EKKCLCB, is provided to "clear" the registration of user exit subroutines. Since registering a new user exit overrides a previous registration, in most cases a call to EKKCLCB would be unnecessary. However, in a large or complicate application program it is advisable to use EKKCLCB as an aid to good housekeeping.
User exit subroutines may be coded in C or FORTRAN, but since the current library code is not re-entrant, these subroutines should not make calls that could result in recursion.
C*********************************************************************** C C EXITRU C C By setting the user return code to 3, this user exit routine C causes EKKSSLV to stop after the accumulated CPU time exceeds C 1000 microseconds. The CPU clock is started after the first C primal iteration and it is checked after each primal iteration. C NOTE: CPUTIME subroutine is available only in VS FORTRAN. C On AIX platforms, the times() system call may be used to get process C and child process times. C C*********************************************************************** C SUBROUTINE EKKITRU(DSPACE,MSPACE,IMODE,ISTAT) C INTEGER*4 IMODE,ISTAT,MSPACE(*),RTCOD REAL*8 DSPACE(*),START,CURRENT,ELAPSED SAVE START C C Include file with integer control variable definitions. INCLUDE (OSLI) C C IMODE=1: called after a primal iteration of EKKSSLV. IF(IMODE.NE.1) GOTO 100 CALL EKKIGET(RTCOD,DSPACE,OSLI,OSLILN) C C CPUTIME subroutine is available only with VS FORTRAN. C User should check RTCOD here to make sure the timing is correct. IF (IITERNUM.EQ.1) THEN CALL CPUTIME(START,RTCOD) ELSE CALL CPUTIME(CURRENT,RTCOD) ELAPSED = CURRENT - START IF (ELAPSED.GT.1000.0) THEN CALL EKKPRTS(RTCOD,DSPACE) CALL EKKBASO(RTCOD,DSPACE,35,1) ISTAT=3 ENDIF ENDIF 100 RETURN ENDThe following sample EKKITRU user exit subroutine is used when EKKSPAR is called.
C*********************************************************************** C C EXSPARIT C C This is a sample EKKITRU to use with EKKSPAR. EKKSPAR calls C EKKITRU at every solution point found, as follows: C C IMODE = 10 means that this is a solution at an increment point. C IMODE = 11 means that this is a solution at a basis change C between increment points. C C*********************************************************************** C SUBROUTINE EKKITRU(DSPACE,MSPACE,IMODE,ISTAT) INTEGER*4 MSPACE(*),IMODE,ISTAT,IRTCOD REAL*8 DSPACE(*) C Print out every EKKSPAR solution IF(IMODE.EQ.10.OR.IMODE.EQ.11) CALL EKKPRTS(IRTCOD,DSPACE) RETURN END
The full message text of each message, including the message number, is available in the array cvec. For example, the following code can be used to extract this text from cvec and and build the message into an array msg_text.
SUBROUTINE EKKMSGU(DSPACE,MSPACE,STRTNUM,NREAL,RVEC,NINT,IVEC,NCHAR,CVEC) REAL*8 RVEC(*),DSPACE(*) INTEGER*4 IVEC(*),MSPACE(*),STRTNUM CHARACTER*128 CVEC(*) PARAMETER (MAX_CHUNCK=5) PARAMETER (MAX_LEN=128*MAX_CHUNK) CHARACTER*(MAX_LEN) MSG_TEXT . . . C C Extract the message text from CVEC C MSG_TEXT = ' ' N = NCHAR + 1 DO 100 I = 1, MAX_LEN, 128 MSG_TEXT(I:I+127) = CVEC(N) N = N + 1 IF ( CVEC(N) .EQ. ' ' ) GO TO 110 100 CONTINUE PRINT *,'Warning: Message truncated to length',MAX_LEN 110 CONTINUE C C MSG_TXT now contains the text for message number STRTNUM C . . .The sample EKKMSGU routine shown below maintains a FIFO queue of information associated with the messages. The subroutine DSPMSG is used to display this queue. Note that to activate EKKMSGU, the main program must first call EKKMSET with the usrexit argument set to 2.
C*********************************************************************** C C EXMSGU C C This example uses the message user exit routine to maintain a fifo C queue of the last MSGQLEN messages that occurred. C C*********************************************************************** PROGRAM MAIN C C Allocate dspace. PARAMETER (MAXSPC=200000) REAL*8 DSPACE(MAXSPC) COMMON/BIG/DSPACE C C Queue of message numbers. PARAMETER (MSGQLEN=4) INTEGER*4 MSGQNUM(MSGQLEN) C C Arrays to hold real, integer, and character values that are C printed in the message. REAL*8 MSGQRL(25,MSGQLEN) INTEGER*4 MSGQIN(25,MSGQLEN) CHARACTER*128 MSGQCH(25,MSGQLEN) C C Vectors to hold number of elements in each column of C msgqrl, msgqin, msgqch. INTEGER*4 MSGQNRL(MSGQLEN),MSGQNIN(MSGQLEN),MSGQNCH(MSGQLEN) C C Common containing message queue data. COMMON /MSGQUE/ MSGQRL,MSGQNUM,MSGQIN,MSGQNRL,MSGQNIN,MSGQNCH, + MSGQPTR,MSGQCH INTEGER*4 RTCOD C C C Call EKKMSET so message user exit routine gets called after C each message, and initialize message queue pointer. MSGQPTR=1 CALL EKKMSET(RTCOD,DSPACE,1,0,0,0,2,9999,0) IF(RTCOD.GT.0) CALL DSPMSGS C C Describe application and specify that there is 1 model. CALL EKKDSCA(RTCOD,DSPACE,MAXSPC,1) IF(RTCOD.GT.0) CALL DSPMSGS C C Describe the model as having 1 block. CALL EKKDSCM(RTCOD,DSPACE,1,1) IF(RTCOD.GT.0) CALL DSPMSGS C C Read model data from MPS file on unit 98 CALL EKKMPS(RTCOD,DSPACE,98,2,0) IF(RTCOD.GT.0) CALL DSPMSGS C C Scale the problem. CALL EKKSCAL(RTCOD,DSPACE) IF(RTCOD.GT.0) CALL DSPMSGS C C Create a vector copy of the matrix. CALL EKKNWMT(RTCOD,DSPACE,3) IF(RTCOD.GT.0) CALL DSPMSGS C C Solve the problem using primal barrier method. CALL EKKBSLV(RTCOD,DSPACE,1,3) C C Display the message queue. CALL DSPMSGS C C Print the solution. CALL EKKPRTS(RTCOD,DSPACE) IF(RTCOD.GT.0) CALL DSPMSGS C STOP END C C*********************************************************************** C This routine displays previous MSGQLEN message conditions that C occurred. C*********************************************************************** C SUBROUTINE DSPMSGS C C Queue of message numbers PARAMETER (MSGQLEN=4) INTEGER*4 MSGQNUM(MSGQLEN) C C Array to hold real, integer, and character values that are C printed in the message. REAL*8 MSGQRL(25,MSGQLEN) INTEGER*4 MSGQIN(25,MSGQLEN) CHARACTER*128 MSGQCH(25,MSGQLEN) C C Vectors to hold number of elements in each column of C msgqrl, msgqin, msgqch. INTEGER*4 MSGQNRL(MSGQLEN),MSGQNIN(MSGQLEN),MSGQNCH(MSGQLEN) C C Common containing message queue data. COMMON /MSGQUE/ MSGQRL,MSGQNUM,MSGQIN,MSGQNRL,MSGQNIN,MSGQNCH, + MSGQPTR,MSGQCH C C WRITE(6,*)' ' WRITE(6,*)'Writing FIFO queue of previous',MSGQLEN,'messages.' DO 1000 I=1,MSGQLEN WRITE(6,*)' ' WRITE(6,*)'Message number is:',MSGQNUM(MSGQPTR) C Write real numbers written with this message. DO 100 J=1,MSGQNRL(MSGQPTR) WRITE(6,*)' real printed on message:',MSGQRL(J,MSGQPTR) 100 CONTINUE C Write integers written with this message. DO 200 J=1,MSGQNIN(MSGQPTR) WRITE(6,*)' integers printed on message:',MSGQIN(J,MSGQPTR) 200 CONTINUE C Write characters written with this message. DO 300 J=1,MSGQNCH(MSGQPTR) WRITE(6,*)' Char. printed on message:',MSGQCH(J,MSGQPTR) 300 CONTINUE C Update queue pointer. MSGQPTR=MOD(MSGQPTR,MSGQLEN)+1 1000 CONTINUE RETURN END C C*********************************************************************** C This user exit routine will maintain a queue of messages. C*********************************************************************** C SUBROUTINE EKKMSGU(DSPACE,MSPACE, + STRTNUM,NREAL,RVEC,NINT,IVEC,NCHAR,CVEC) REAL*8 RVEC(*),DSPACE(*) INTEGER*4 IVEC(*),MSPACE(*),STRTNUM CHARACTER*128 CVEC(*) C C Queue of message numbers PARAMETER (MSGQLEN=4) INTEGER*4 MSGQNUM(MSGQLEN) C C Array to hold real, integer, and character values that are C printed in the message. REAL*8 MSGQRL(25,MSGQLEN) INTEGER*4 MSGQIN(25,MSGQLEN) CHARACTER*128 MSGQCH(25,MSGQLEN) C C Vectors to hold number of elements in each column of C msgqrl, msgqin, msgqch. INTEGER*4 MSGQNRL(MSGQLEN),MSGQNIN(MSGQLEN),MSGQNCH(MSGQLEN) C C Common containing message queue data. COMMON /MSGQUE/ MSGQRL,MSGQNUM,MSGQIN,MSGQNRL,MSGQNIN,MSGQNCH, 1 MSGQPTR,MSGQCH C C C Save message number. MSGQNUM(MSGQPTR)=STRTNUM C C Save real values. MSGQNRL(MSGQPTR)=NREAL DO 100 J=1,NREAL MSGQRL(J,MSGQPTR)=RVEC(J) 100 CONTINUE C C Save integer values. MSGQNIN(MSGQPTR)=NINT DO 200 J=1,NINT MSGQIN(J,MSGQPTR)=IVEC(J) 200 CONTINUE C C Save character values. MSGQNCH(MSGQPTR)=NCHAR DO 300 J=1,NCHAR MSGQCH(J,MSGQPTR)=CVEC(J) 300 CONTINUE C C Update queue pointer. MSGQPTR=MOD(MSGQPTR,MSGQLEN)+1 C RETURN END