Replay Using Daphne Page 1 Introduction 9 May 1997 1 Introduction This document is a guide to using Daphne for replay. Replay is the off-line analysis of data which has already been recorded on tape (or other storage media). This manual assumes the user is already familiar with: - Daphne objects such as maps, histograms, conditions, windows, and linearizations - Writing a Daphne user-function - Daphne display commands People who are not familiar with Daphne should consult "Experimenter's Guide to Daphne" for more information on these topics. Replaying data with Daphne is very much like acquisition, except that the data source is a tape unit instead of acquisition hardware. For this reason this guide is relatively short and emphasizes tape operations and the ways in which replay differs from acquisition. - The $CDV command is irrelevant - The Event Handler program is irrelevant - To Ready Tape for Input - For Daphne Format Tapes Use $TAPE/IN - For SNAP Format Tapes Use $TAPE/IN/TYPE=SNAP - For other tape format see the Daphne developers for routines that vastly simplify the sorting of non-Daphne tapes - Use $DISK/IN file-name to Ready a Disk File for Input (Continued on next page) Replay Using Daphne Page 2 Introduction 9 May 1997 - If you are replaying data on the VAX/AXP only: - The $MDV command is irrelevant - Write your user-function as described in the Experimenter's Guide - Compile your user-function using the VAX/AXP VMS Fortran compiler See section 12 for difference between the VAX/AXP VMS and NS32000 Fortran compilers - Link the User-Function Using $@DAPEXE:DAPUSERFUNC.LNK (Section 10) - Use $USERFUNC to tell Daphne the file containing the User-Function (Section 11) - Use $RDT (Tape) or $RDD (Disk) to Start Replay on the VAX or AXP (Section 13) - Use $RDT/DEBUG (Tape) or $RDD/DEBUG (Disk) to debug your VAX/AXP user-function (Section 13.2) - If you are replaying data through the Event Processors: - Use $MDV as described in the Experimenter's Guide - Write your user-function as described in the Experimenter's Guide - Compile and Link your user-function as described in the Experimenter's Guide - Use $EPLOAD to load the user-function - Use $RDT/EP (Tape) or $RDD/EP (Disk) to start the sorting of data - Use $STP to stop replay before the end-of-file - Use $ENDRDT or $ENDRDD to wait for replay to stop (When $RDT or $RDD is part of a command file) Replay Using Daphne Page 3 Differences Between AXP and VAX Replay 9 May 1997 2 Differences Between AXP and VAX Replay 2.1 Arithmetic Exceptions The VAX provides "precise" arithmetic exceptions which identify the exact instruction which caused the execption and are carefully categorized as to type (integer overflow, floating point overflow, floating point underflow, division by 0, etc). The AXP provides a single exception code: "arithmetic exception". By default the VAX reports all important arithmetic exceptions, including integer overflow. By default the AXP does NOT check for integer overflow. Consider the following example program: program test integer*2 a,b,c a=20000 b=20000 c=a+b write (6,*) c call exit If this program is compiled (without optimization) and run on a VAX it will report an integer overflow exception. The AXP will simply assign "c" the value -25536. That is 40000 - (2**16). In order to have the exception detected and reported on an AXP one must compile with /CHECK=(OVERFLOW,UNDERFLOW): $ FORTRAN/NOOP/CHECK=(OVERFLOW,UNDERFLOW) TEST 2.2 Data Alignment The VAX is extremely tolerant of data which is not aligned on its "natural" boundaries. (The natural alignment for an item of length 2, 4, or 8 bytes is an address which is a multiple of 2, 4, or 8). For a VAX the impact on performance of unaligned data is very low. The AXP will allow the user to access data which is not properly aligned. However, the impact on performance is ENORMOUS. Access to unaligned data will cause an exception which invokes a routine which decodes the instruction, locates the operands, and move them to aligned locations where the instruction can be completed. This is roughly 1,000 times as expensive as a properly aligned operand. Replay Using Daphne Page 4 Differences Between AXP and VAX Replay 9 May 1997 How do you detect unaligned operands ? The easiest way is to use the AXP/VMS debugger. The debugger command: DBG> set break/unaligned will stop a program which performs an unaligned access rather than fixing it automatically. When this is used with a sort program the user may find that there are a *few* such accesses by VAXSORT during the initial phases of the sort program. However, there should be no such accesses once sorting of event data starts. Thus I suggest that you not issue the "set break/unaligned" until the first event has been sorted by setting a break at "userfunc". 2.3 Default Floating Point Formats The default double precision floating point format for the VAX is "DFLOAT" while for the AXP it is "GFLOAT". The default can be changed using compiler switches. There is also a limited capability for interchange using options in the OPEN statement. Do not use D double precision floating point format on the AXP for numerically intensive computations since it is emulated using G floating point at a significant performance penalty. Do not use G double precision floating point format on the older VAXes such as the VAX-750, VAX-780, and MicroVAX-II series. 2.4 Fortran Compiler Differences The newer DEC Fortran compilers are closer to the GNX compiler. The following table summarizes the differences between the current DEC Fortran compilers, the old DEC Fortran compiler, and the GNX Fortran compiler. old VAX new VAX AXP GNX -------- ------- ------- -------- length of symbolic names 31 chars 31 31 24 special characters in names "$" and "_" n/c n/c "_" only defeat implicit types implicit none n/c n/c implicit undefined (a-z) hexadecimal constants '0001'X n/c both X'0001' octal constants '0001'O n/c both O'0001' binary constants not implem. n/c B'0001' B'0001' bitwise AND IAND (I,J) both both AND (I,J) bitwise OR IOR (I,J) both both OR (I,J) bitwise complement NOT (I) both both NOT (I) bitwise exclusive or IEOR (I,J) both both XOR (I,J) left shift ("S" bits) ISHFT (I,S) both both LSHIFT (I,S) or RSHIFT (I,-S) right shift ("S" bits) SHFT (I,-S) both both RSHIFT (I,S) Replay Using Daphne Page 5 Differences Between AXP and VAX Replay 9 May 1997 or LSHIFT (I,-S) Key: "n/c" means no changes from old VAX/VMS compiler "both" means that both old VAX/VMS and GNX forms accepted 2.5 If other significant differences are found please bring it to the attention of the developers. Thank-you. 3 Disabling Window Based VMS Debugging Some users (including yours truly) feel that the fancy DECwindows based mode for debugging is actually an impediment to use of the debugger. To disable this pheature use: $ define/job dbg$decw$display " " 4 Exceptions When you Least Expect It As many are aware, Daphne attempts to intercept arithmetic exceptions during sorting. The normal action of Daphne is to abort processing of the user-function for that event and to start processing the next event. Some user-functions contain code which has side effects such as writing to a Fortran I/O unit. If an arithmetic exception occurs in such circumstances the results may be difficult for a user to understand. Consider the following: write (30,100) 'Event ',eventNumber,value,value/computation write (30,110) x,y,x If "computation" is 0.0 then the divide exception will occur during the first write statement and the I/O will never be completed, nor will the second write statement be executed. 5 Tape Commands Replay Using Daphne Page 6 Tape Commands 9 May 1997 5.1 Use $MAX /RD_BUF to Set Default Input Tape Block Size 5.2 Find File by Run Number - $FND 5.3 Skip Files Forward - $SKP 5.4 Skip Files Backward - $BSP 5.5 Rewind - $REW 5.6 Directory of Daphne Tape - $TDIR This command lists the files on a Daphne format tape. The program automatically mounts the tape, so the tape must NOT be mounted when the command is issued. 5.7 Directory of SNAP Tape - $SNAPDIR This command lists the files on a SNAP format tape. The program automatically mounts the tape, so the tape must NOT be mounted when the command is issued. Replay Using Daphne Page 7 Disk Commands 9 May 1997 6 Disk Commands The $DISK commands can be used only with Daphne data files at the present time. You can use $DISK/IN (or $DISK/OUT) to read (or write) Daphne event mode data on a disk file of the computer on which you are running, or on another computer accessible by DECNET. Because a Daphne event mode data file is an RMS file in which each record corresponds to one data block of a Daphne event mode tape, one can use the VMS $COPY command to copy runs from tape to disk or from disk to tape. A Daphne event mode data file can contain only one run. NOTE Warning If you intend to create an event mode disk file which will eventually be copied to tape to create a Daphne event mode tape then you must use a block size less than 9999 bytes. For instance: $ DISK/OUT/REC=9990 RUN017 This applies only to the $COPY utility. The $BACKUP utility will not have any problems with such a file. 6.1 Tape to Disk Suppose you wish to develop a Daphne user function using a short, but representative, run from the tape. This means that you may be replaying the data many times. If you do not wish to tie up the tape drive for a long period of time, it would make sense to put the run from the tape on disk, thus freeing the tape drive for other users. To copy run 17 from the tape on drive MFA0: to disk use the following sequence of commands: $ DAP BGO (Start Daphne $ TAPE/IN MFA0 (Start the tape process $ FND 17 ("Find" - Position to run 17 on tape $ COPY MFA0: RUN17.DMP/ALLOCATE=200/EXTEND=400 (Copy next file on tape to disk ( Initially allocate 200 blocks ( If too small add 400 blocks at a time $ SET FILE/TRUNCATE RUN17.DMP (Release unused blocks at end of file $ KLT ("Kill Tape" - No longer need tape drive $ FORTRAN MYUSERFUNC (Compile the user function $ @DAPEXE:DAPUSERFUNC.LNK MYUSERFUNC (Link the user function $ USERFUNC MYUSERFUNC (Tell Daphne name of user function Replay Using Daphne Page 8 Disk Commands 9 May 1997 $ DISK/IN RUN17.DMP (Prepare to replay file "RUN17.DMP" $ RDD ("Replay Data from Disk" Replay Using Daphne Page 9 Disk Commands 9 May 1997 6.2 Disk to Tape If you have one or more Daphne event mode disk files, you can create a new Daphne event mode data tape containing the disk files. Suppose you have replayed runs 17, 18, and 19 to create files that contain the 10% of events that were interesting. To copy these to a new tape in the format of a Daphne event mode data tape use the following commands: ############################################### ############################################### ## ## ## The $INITIALIZE command destroys any data ## ## recorded on the tape. ## ## ## ############################################### ############################################### $ INITIALIZE/DENSITY=6250 MFA0: DUMMY (Label is not important - it will be overwritten $ MOUNT MFA0:/FOREIGN/BLOCK=13000 ("FOREIGN" means tape is not in standard ( VMS format ("BLOCKSIZE" must be greater than the number of ( bytes in the largest record to be copied $ COPY RUN17_GOOD_EVENTS.DMP MFA0: $ COPY RUN18_GOOD_EVENTS.DMP MFA0: $ COPY RUN19_GOOD_EVENTS.DMP MFA0: $ DISMOUNT MFA0: If you want to add these disk files to the end of an existing tape use the following commands: $ MOUNT MFA0:/FOREIGN/BLOCK=13000 ("FOREIGN" means tape is not in standard ( VMS format ("BLOCKSIZE" must be greater than the number of ( bytes in the largest record to be copied $ SET MAGTAPE/SKIP=END_OF_TAPE MFA0: (Position tape to just past last data recorded $ COPY RUN17_GOOD_EVENTS.DMP MFA0: $ COPY RUN18_GOOD_EVENTS.DMP MFA0: $ COPY RUN19_GOOD_EVENTS.DMP MFA0: $ DISMOUNT MFA0: Replay Using Daphne Page 10 Reserved Daphne Fortran I/O Logical Unit Assignments 9 May 1997 7 Reserved Daphne Fortran I/O Logical Unit Assignments - Unit 5: keyboard input - Unit 6: terminal output - Unit 7: reserved for VAXSORT - Unit 8: reserved for VAXSORT - Unit 11: reserved for use on the AXP in VAXSORT - Unit 100: reserved for Daphne log file Closing a reserved I/O unit or changing a characteristic may cause Daphne to behave in an unpredicatable fashion. For instance, closing unit 6 will prevent you from receiving error messages about arithmetic exceptions. Writing to unit 100 will corrupt the Daphne log file. Replay Using Daphne Page 11 "New" Subroutines for VAX/AXP Replay 9 May 1997 8 "New" Subroutines for VAX/AXP Replay The following subroutines are available only for Replay on the VAX or AXP. They were written too late for incorporation into the Experimenter's Guide. NOTE Any statements which apply to "VAX Replay" or "VAX/AXP Replay" also apply to acquisition under the MSU/NSCL acquisition system, since in both cases the sorting takes place on the VAX or AXP. +----------------------------------------------------------------------+ | VAX/AXP Replay Only | +----------------------------------------------------------------------+ | logical function lnzNumber (name,number) | | | | Returns the number of the linearization with name "name" | | If no linearization with that name it prints a warning message | | but allows execution to continue | | Should be called only from user initialization routine | | | | function value - output/logical | | .true. => linearization exists | | .false. => linearization does not exist | | | | name - input/character*(*) | | the name of the linearization | | to be looked up | | may be in upper or lower case | | | | number - output/integer*4 | | the number of the linearization whose | | name was given | | can be used to access the lnzVector | | argument passed to the user-N routines | | 0 if linearization does not exist | +----------------------------------------------------------------------+ Replay Using Daphne Page 12 "New" Subroutines for VAX/AXP Replay 9 May 1997 +----------------------------------------------------------------------+ | VAX/AXP Replay Only | +----------------------------------------------------------------------+ | logical function w2Number (name,number) | | | | Returns the number of the 2D window with name "name" | | If no 2D window with that name it prints a warning message | | but allows execution to continue | | Should be called only from user initialization routine | | | | function value - output/logical | | .true. => 2D window exists | | .false. => 2D window does not exist | | | | name - input/character*(*) | | the name of the 2D window | | to be looked up | | may be in upper or lower case | | | | number - output/integer*4 | | the number of the 2D window whose | | name was given | | can be used to access the w2Vector | | argument passed to the user-N routines | | 0 if 2D window does not exist | +----------------------------------------------------------------------+ +----------------------------------------------------------------------+ | VAX/AXP Replay Only | +----------------------------------------------------------------------+ | logical function w1Number (name,number) | | | | Returns the number of the 1D window with name "name" | | If no 1D window with that name it prints a warning message | | but allows execution to continue | | Should be called only from user initialization routine | | | | function value - output/logical | | .true. => 1D window exists | | .false. => 1D window does not exist | | | | name - input/character*(*) | | the name of the 1D window | | to be looked up | | may be in upper or lower case | | | | number - output/integer*4 | | the number of the 1D window whose | | name was given | | can be used to access the w1Vector | | argument passed to the user-N routines | | 0 if 1D window does not exist | +----------------------------------------------------------------------+ Replay Using Daphne Page 13 "New" Subroutines for VAX/AXP Replay 9 May 1997 +----------------------------------------------------------------------+ | VAX/AXP Replay Only | +----------------------------------------------------------------------+ | logical function conNumber (name,number) | | | | Returns the number of the condition with name "name" | | If no condition with that name it prints a warning message | | but allows execution to continue | | Should be called only from user initialization routine | | | | function value - output/logical | | .true. => condition exists | | .false. => condition does not exist | | | | name - input/character*(*) | | the name of the condition | | to be looked up | | may be in upper or lower case | | | | number - output/integer*4 | | the number of the condition whose | | name was given | | 0 if condition does not exist | | can be used to access the conVector | | argument passed to the user-N routines | | when used to access the conVector | | array compute: | | | | subscript=(conNumber*2-1) | | | | e.g. condition 1 => subscript 1 | | condition 2 => subscript 3 | | condition 3 => subscript 5 | +----------------------------------------------------------------------+ Replay Using Daphne Page 14 "New" Subroutines for VAX/AXP Replay 9 May 1997 The calling sequence of the user-N functions has been modified to pass the conVector, w2Vector, w1Vector as well as the lnzVector. Thus the calling sequence is: +------------------------------------------------------------------+ | logical function user1 (eventType,eventSize,eventVector, | | lnzVector,w2Vector,w1Vector, | | conVector) | | | | function value - output/logical | | | | the function value used to set the | | corresponding condition to true (or false) | | | | eventType - input/integer*2 | | the type of the event | | same meaning as for the plain USER function | | | | eventSize - input/integer*4 | | the number of integer*2 words in the event | | | | eventVector - input/integer*2 array | | the event data | | | | lnzVector - input/integer*4 array | | value of linearization 1 in lnzVector(1) | | linearization 2 in lnzVector(2) | | and so on | | the user must be sure that the linearization | | has been evaluated for this event, | | otherwise the array will contain old | | values from previous events | | | | w2Vector - input/integer*4 array | | value of 2D window 1 in w2Vector(1) | | 2D window 2 in w2Vector(2) | | and so on | | value of window is 1 if inside, 0 if outside | | the user must be sure the window has been | | evaluated by using an "IN2" or "OUT2" | | condition on the window before testing it | | | | w1Vector - input/integer*4 array | | value of 1D window 1 in w1Vector(1) | | 1D window 2 in w1Vector(2) | | and so on | | value of window is 1 if inside, 0 if outside | | the user must be sure the window has been | | evaluated by using an "IN1" or "OUT1" | | condition on the window before testing it | | | | | continued on next page Replay Using Daphne Page 15 "New" Subroutines for VAX/AXP Replay 9 May 1997 | | | | | conVector - input/integer*4 array | | value of condition 1 in conVector(1) | | condition 2 in conVector(3) | | condition 3 in conVector(5) | | condition 4 in conVector(7) | | and so on | | +--------------------------------------------------------------+ | | | WARNING: the conditions values are in odd numbered elements | | | | of the array. This is because condition counts | | | | are in the even elements | | | +--------------------------------------------------------------+ | | | | value of condition 1 if true , 0 otherwise | | the user must be sure the condition has been | | evaluated before testing it | | | | +--------------------------------------------------------------+ | | | WARNING: the lnzVector, w2Vector, w1Vector, conVector are | | | | INTEGER*4 | | | +--------------------------------------------------------------+ | +------------------------------------------------------------------+ Please note that trailing arguments which are not used need not be declared. Thus, if you do not use the w1Vector or w2Vector you can declare the User-N as in the past. Replay Using Daphne Page 16 Sub-Events and Buffered Hardware Devices 9 May 1997 9 Sub-Events and Buffered Hardware Devices Some acquisition devices collect many "physics events" before requiring they are read-out by the acquisition hardware as a single "computer event". For instance, a special ECL interface at Michigan State University (MSU) collects 4k words of "physics events" before it requires service by the acquisition hardware. The entire 4k word buffer is treated as single event by the acquisition hardware. In order to process this very large event as many small "physics events" Daphne supports "sub-events" and "super-events". The "computer event" is termed a "super-event" and the "physics events" inside the super-event are called "sub-events". To use sub-events, the calling sequence of the user-function is changed slightly so that the user-function tells Daphne the length of the sub-events. Daphne uses this information to break apart the super-event into sub-events. Normally, Daphne knows the length of an event and passes this information to the user. The first step in using sub-event is to notify Daphne in the userInit routine that sub-event processing will be used. +----------------------------------------------------------------------+ | VAX/AXP Replay Only | +----------------------------------------------------------------------+ | subroutine usingSubEvents | | | | There are no arguments and no return values. | | Called from the userInit routine of the user-function. | | This will change the calling sequence of USER - see the next section.| | When using sub-event processing you MUST insert the following code | | in the userInit routine: | | | | call usingSubEvents | | | +----------------------------------------------------------------------+ When using sub-events the calling sequence of the user-function (and the user-N entry points) is changed. In particular the "eventSize" argument becomes an integer*4 array with 3 elements. The interpretation of all other calling arguments remains unchanged. Replay Using Daphne Page 17 Sub-Events and Buffered Hardware Devices 9 May 1997 - eventSize(1) output integer*4 On entry to the user-function "eventSize(1)" will be zero. The user-function is required to set eventSize(1) to the number of 16 bit words in the sub-event. The number of words in a sub-event must be between 1 and 1022 because of restrictions in the Daphne event data format. The sub-event is passed to the user-function via the "eventVector" argument as one would expect. The first element of the eventVector is the first word of the sub-event. If the user leaves eventSize(1) as zero and returns to Daphne then all remaining processing of the super-event will be skipped. In other words, returning a value of zero indicates that the rest of the super-event is of no interest. Use of negative values for the event length is reserved for future needs. The value in eventSize(1) becomes the length of the event for almost all purposes. For instance, it is the length passed to the aritmetic trap handler, when copying events to an output buffer, and passed to user-N routines. The value of eventSize(1) must NOT be changed by user-N routines. - eventSize(2) input integer*4 The offset (in 16 bit words) of the current sub-event from the beginning of the super-event is stored by Daphne in eventSize(2). For the first sub-event in a super-event this will be zero. If the sum of the length of all the sub-events in the super-event exceeds the length of the super-event a warning message will be issued to the user and all remaining processing of the super-event will be skipped: as the user-function had returned the value zero in eventSize(1). - eventSize(3) input integer*4 The length (in 16 bit words) of the super-event is stored by Daphne in eventSize(3). As mentioned above, the sum of the lengths of all sub-events must be less than the length of the super-event, otherwise an error message will be issued. Replay Using Daphne Page 18 Sub-Events and Buffered Hardware Devices 9 May 1997 In the following example a super-event consists of a number of sub-events. Each sub-event has two parameters. +----------------------------------------------------------------------+ | subroutine user (eventType,nWords,eventVector) | | implicit none | |c | | integer*2 eventType | |c | |c nWords(1) - output/integer*4 | |c zero => end of super-event | |c otherwise the number of 16 bit words in sub-event | |c | |c nWords(2) - input/integer*4 | |c offset of sub-event from beginning of super-event | |c (16 bit words) | |c | |c nWords(3) - input/integer*4 | |c number of 16 bit words in the super-event | |c | | integer*4 nWords (3) | |c | | integer*2 eventVector (*) | |c | |c Reached end of super-event (?) | |c | | if (nWords(2) .ge. nWords(3)) goto 99 | |c | |c Process another 2 words from the event | |c | | nWords(1)=2 | |c | | 99 return | |c | | entry userInit | |c | | call usingSubEvents | | return | |c | | end | +----------------------------------------------------------------------+ Replay Using Daphne Page 19 Accessing Histograms Within a User-Function 9 May 1997 10 Accessing Histograms Within a User-Function There are a set of routines for directly accessing 1D and 2D histograms from within a user-function. These have been added to Daphne to handle acquisition problems that are not well served by the concept of maps: - Maps allow a histogram channel to be increased by 1. Sometimes it is useful to add a value based on some weighing factor, for instance a function of a particle's velocity or energy. - When handlding a large number of similar detectors it is convenient to be able to iterate over the detectors in a Fortran routine rather than generating a large number of maps. For example, an experiment with 50 detectors of the same type giving Energy and Time might require 100 maps: one each for Time and Energy. - Because of the original design of Daphne, it did not efficiently handle experiments when only a small fraction of the detectors fired. Daphne examined every possible parameter and then decided whether to histogram the value or not. It is more efficient to look at the parameters of only those detectors which have fired. - It is not possible to anticipate all needs of experimenters in histogramming. The new routines, specifically h1User and h2User, allow the user full access to the histograms during sorting. Routines h1Number (1D histograms) and h2Number (2D histograms) are called by the user during the user-function userInit routine. There are two reasons for this: (a) it tells the sorting routine to bring the histograms into memory and build the necessary data structures for access by other routines (b) these routines translates a histogram name to a histogram number which is required by all the other routines to access data structures which are organized by the histogram number. NOTE There MUST be at least one conventional Daphne histogram and map defined. Without such definitions the part of Daphne which executes prior to h1Number and h2Number will think that the user has forgotten to define a map, will warn the user that no histograms can be incremented, and will stop replay before it has really started. Routines h1Nchan and h2Nchan may be called by the user in the userInit routine to determine the number of channels in a histogram. This allows the user to scale parameters to fit the number of channels available. For instance, if a user decides that higher resolution is needed in a histogram, the histogram size can be changed and the Replay Using Daphne Page 20 Accessing Histograms Within a User-Function 9 May 1997 user-function can automatically adjust to the new size by using h1Nchan and h2Nchan. Routines h1Inc and h2Inc add 1 to the selected channel of a histogram. Routines h1Add and h2Add add a value supplied by the user to the selected channel of a histogram. Routines h1Get and h2Get return to the caller the current value in the selected channel of a histogram. Routines h1Put and h2Put store a value supplied by the user in the selected channel of a histogram. Replay Using Daphne Page 21 Accessing Histograms Within a User-Function 9 May 1997 Routines h1User and h2User call a user-supplied routine with the specified histogram as an argument. This allows a user to perform almost any operation on a histogram within the user-function. The user-supplied routine is specified as the first argument in the call to h1User and h2User. Thus a user may build a library of routines, many of which may be used in a single user-function. Both h1User and h2User allow an arbitrary number of additional arguments to be supplied in the call. These additional arguments are passed to the specified routine without change. If the routine is a function which returns a value, then this value is passed back to the caller of h1User and h2User without change. If a user supplies a channel number which is greater than the number of channels in a histogram, the last channel of a histogram is used. Because Daphne uses unsigned arithmetic with channel numbers, negative numbers are treated as very large positive values. These routines zero-extend shorter variables to integer*4. Users should exercise special care when receiving integer*2 (16 bit word) or integer*1 (8 bit byte) values from these routines. For example, when calling "h2Get (E_VS_T,energy,time,count)" to read the value in a bin of the 2D histogram, the variable "count" ought to be integer*4 regardless of what size bins the histogram has. These routines cannot be used with disk-sorted 2D histograms. The h1User and h2User routines are extremely powerful tools. One should not forget that it is not always the best tool for histogram manipulation. The "user histogram" routines described in document UH.MEM have a similar capability, but are more general. The UH routines have one additional advantage in cases where sorting is taking place on-line: however long a UH routine takes, it does not block the sorting process. In contrast, during the time a h2User routine is running, no sorting is taking place because the h2User routine is part of the sort program. Replay Using Daphne Page 22 Accessing Histograms Within a User-Function 9 May 1997 +----------------------------------------------------------------------+ | VAX/AXP Replay Only | +----------------------------------------------------------------------+ | logical function h1Number (name,number) | | | | Returns the number of the 1D histogram with name "name" | | If no 1D histogram with that name it prints a warning message | | but allows execution to continue | | Should be called only from user initialization routine | | | | function value - output/logical | | .true. => 1D histogram exists | | .false. => 1D histogram does not exist | | | | name - input/character*(*) | | the name of the 1D histogram | | to be looked up | | may be in upper or lower case | | | | number - output/integer*4 | | the number of the 1D histogram whose | | name was given | | can be used in calls to h1Inc, h1Add, | | h1Put, h1Get, h1User, and h1Nchan | | 0 if 1D histogram does not exist | +----------------------------------------------------------------------+ +----------------------------------------------------------------------+ | VAX/AXP Replay Only | +----------------------------------------------------------------------+ | logical function h2Number (name,number) | | | | Returns the number of the 2D histogram with name "name" | | If no 2D histogram with that name it prints a warning message | | but allows execution to continue | | Should be called only from user initialization routine | | | | function value - output/logical | | .true. => 2D histogram exists | | .false. => 2D histogram does not exist | | | | name - input/character*(*) | | the name of the 2D histogram | | to be looked up | | may be in upper or lower case | | | | number - output/integer*4 | | the number of the 2D histogram whose | | name was given | | can be used in calls to h2Inc, h2Add, | | h2Put, h2Get, h2User, and h2Nchan | | 0 if 2D histogram does not exist | +----------------------------------------------------------------------+ Replay Using Daphne Page 23 Accessing Histograms Within a User-Function 9 May 1997 +----------------------------------------------------------------------+ | VAX/AXP Replay Only | +----------------------------------------------------------------------+ | subroutine h1Nchan (histNumber,nChan) | | | | Returns the number of channels in a 1D histogram | | If no 1D histogram with that number it prints a warning message | | but allows execution to continue. In this case it returns a value | | of zero for the number of channels. | | Should be called only from user initialization routine | | | | number - input/integer*4 | | the number of the 1D histogram | | | | nChan - output/integer*4 | | the number of channels in the histogram | | 0 if 1D histogram does not exist | | | | A histogram with 100 channels has channels numbered from 0 to 99 | +----------------------------------------------------------------------+ +----------------------------------------------------------------------+ | VAX/AXP Replay Only | +----------------------------------------------------------------------+ | subroutine h2Nchan (histNumber,nXchan,nYchan) | | | | Returns the number of X and Y channels in a 2D histogram | | If no 2D histogram with that number it prints a warning message | | but allows execution to continue. In this case it returns a value | | of zero for the number of X and Y channels. | | Should be called only from user initialization routine | | | | number - input/integer*4 | | the number of the 2D histogram | | | | nXchan - output/integer*4 | | the number of X channels in the histogram| | 0 if 2D histogram does not exist | | | | nYchan - output/integer*4 | | the number of Y channels in the histogram| | 0 if 2D histogram does not exist | | | | A histogram with 100 channels has channels numbered from 0 to 99 | | | +----------------------------------------------------------------------+ Replay Using Daphne Page 24 Accessing Histograms Within a User-Function 9 May 1997 +----------------------------------------------------------------------+ | VAX/AXP Replay Only | +----------------------------------------------------------------------+ | subroutine h1Inc (histNumber,chan) | | subroutine h2Inc (histNumber,xChan,yChan) | | | | Adds 1 to the selected channel of the 1D/2D histogram | | | | histNumber - input/integer*4 | | the number of the 1D/2D histogram | | if 0 then no operation performed | | | | chan - input/integer*4 | | channel number of 1D histogram | | | | xChan - intput/integer*4 | | X channel of 2D histogram | | | | yChan - intput/integer*4 | | Y channel of 2D histogram | +----------------------------------------------------------------------+ +----------------------------------------------------------------------+ | VAX/AXP Replay Only | +----------------------------------------------------------------------+ | subroutine h1Add (histNumber,chan,value) | | subroutine h2Add (histNumber,xChan,yChan,value) | | | | Adds "value" to the selected channel of the 1D/2D histogram | | | | histNumber - input/integer*4 | | the number of the 1D/2D histogram | | if 0 then no operation performed | | | | chan - input/integer*4 | | channel number of 1D histogram | | | | xChan - intput/integer*4 | | X channel of 2D histogram | | | | yChan - intput/integer*4 | | Y channel of 2D histogram | | | | value - input/integer*1/integer*2/integer*4 | | data type depends on histogram | | value to add to histogram channel | | treated as unsigned number | +----------------------------------------------------------------------+ Replay Using Daphne Page 25 Accessing Histograms Within a User-Function 9 May 1997 +----------------------------------------------------------------------+ | VAX/AXP Replay Only | +----------------------------------------------------------------------+ | subroutine h1Get (histNumber,chan,value) | | subroutine h2Get (histNumber,xChan,yChan,value) | | | | Gets current value in the selected channel of the 1D/2D histogram | | | | histNumber - input/integer*4 | | the number of the 1D/2D histogram | | if 0 then no operation performed | | | | chan - input/integer*4 | | channel number of 1D histogram | | | | xChan - intput/integer*4 | | X channel of 2D histogram | | | | yChan - intput/integer*4 | | Y channel of 2D histogram | | | | value - output/integer*4 | | current value of selected channel | | if histogram contains 8 bit or 16 bit | | values then the value is zero extended | | to 32 bits | +----------------------------------------------------------------------+ +----------------------------------------------------------------------+ | VAX/AXP Replay Only | +----------------------------------------------------------------------+ | subroutine h1Put (histNumber,chan,value) | | subroutine h2Put (histNumber,xChan,yChan,value) | | | | Stores "value" in the selected channel of the 1D/2D histogram | | | | histNumber - input/integer*4 | | the number of the 1D/2D histogram | | if 0 then no operation performed | | | | chan - input/integer*4 | | channel number of 1D histogram | | | | xChan - intput/integer*4 | | X channel of 2D histogram | | | | yChan - intput/integer*4 | | Y channel of 2D histogram | | | | value - input/integer*1/integer*2/integer*4 | | data type depends on histogram | | value to store in histogram channel | +----------------------------------------------------------------------+ Replay Using Daphne Page 26 Accessing Histograms Within a User-Function 9 May 1997 +----------------------------------------------------------------------+ | VAX/AXP Replay Only | +----------------------------------------------------------------------+ | subroutine h1User (externalRoutine,histNumber,chan,arg1,arg2,...) | | | | Calls "exernalRoutine" with the following argument list: | | | | subroutine externalRoutine (nChan,hist,chan,arg1,arg2,...) | | function externalRoutine (nChan,hist,chan,arg1,arg2,...) | | | | externalRoutine - input/external subroutine or function | | routine declared EXTERNAL in routine which | | calls h1User | | may be subroutine or function of any numeric | | data type (integer/real/complex) | | | | histNumber - input/integer*4 | | the number of the 1D histogram | | if 0 then no operation performed | | | | chan - input/integer*4 | | channel number of 1D histogram | +----------------------------------------------------------------------+ | Arguments to externalRoutine: | | | | nChan - input/integer*4 | | number of channels in histogram | | histogram with 100 channels has channels | | numbered from 0 to 99 | | | | hist - input/array (0:nChan-1) of histrogram | | data type depends on histogram bin size | | this is the histogram as a Fortran array | | | | chan - input/integer*4 | | this is the channel number passed to h1User | | if value supplied to h1User is greater than | | (nChan-1) then the value passed to | | externalRoutine is (nChan-1) | | | | arg1,arg2,arg3,... - input/output/any data type | | these arguments are passed unchanged from | | from h1User to externalRoutine | +----------------------------------------------------------------------+ Replay Using Daphne Page 27 Accessing Histograms Within a User-Function 9 May 1997 +----------------------------------------------------------------------+ | VAX/AXP Replay Only | +----------------------------------------------------------------------+ | subroutine h2User (extRoutine,histNumber,xChan,yChan,arg1,arg2,...) | | | | Calls "extRoutine" with the following argument list: | | | | subroutine extRoutine (nXchan,nYchan,hist,xChan,yChan,arg1,arg2,...)| | function extRoutine (nXchan,nYchan,hist,xChan,yChan,arg1,arg2,...) | | | | extRoutine - input/external subroutine or function | | routine declared EXTERNAL in routine which | | calls h2User | | may be subroutine or function of any numeric | | data type (integer/real/complex) | | | | histNumber - input/integer*4 | | the number of the 2D histogram | | if 0 then no operation performed | | | | xChan - input/integer*4 | | X channel number of 2D histogram | | | | yChan - input/integer*4 | | Y channel number of 2D histogram | +----------------------------------------------------------------------+ | Arguments to extRoutine: | | | | nXchan - input/integer*4 | | number of X channels in histogram | | histogram with 100 channels has channels | | numbered from 0 to 99 | | | | nYchan - input/integer*4 | | number of Y channels in histogram | | histogram with 100 channels has channels | | numbered from 0 to 99 | | | | hist - input/array (0:nXchan-1,0:nYchan-1) of hist | | data type depends on histogram bin size | | this is the histogram as a Fortran array | | | | xChan - input/integer*4 | | this is the X channel number passed to h2User| | if value supplied to h2User is greater than | | (nXchan-1) then the value passed to | | extRoutine is (nXchan-1) | | | | yChan - input/integer*4 | | this is the Y channel number passed to h2User| | if value supplied to h2User is greater than | | (nYchan-1) then the value passed to | | extRoutine is (nYchan-1) | | | Replay Using Daphne Page 28 Accessing Histograms Within a User-Function 9 May 1997 | arg1,arg2,arg3,... - input/output/any data type | | these arguments are passed unchanged from | | from h2User to externalRoutine | +----------------------------------------------------------------------+ Replay Using Daphne Page 29 Accessing Histograms Within a User-Function 9 May 1997 The first example builds two histograms. Histogram "detFired" shows the number of times each detector fired. What histogram "multiplicity" shows should be self-evident. +----------------------------------------------------------------------+ | subroutine user (eventType,nWords,eventVector) | | implicit none | |c | | integer*2 eventType | | integer*4 nWords | | integer*2 eventVector (*) | |c | | integer*4 multiplicity | | integer*4 detectorFiredMask | | integer*4 i | | integer*4 testBit | |c | |c histograms | |c | | integer*4 h1DetFired | | integer*4 h1Multiplicity | |c | |c bit register indicating detectors that fired in word 1 of event | |c | | detectorFiredMask=eventVector(1) | |c | | multiplicity=0 | |c | |c testBit is used to test successive bits of the detectorFiredMask | |c each time through the loop it is shifted one bit | |c | | testBit=1 | |c | | do 100 i=1,16 | | if (iand (detectorFiredMask,testBit) .ne. 0) then | |c | |c 1D histogram counting how often each detector fired | |c | | call h1Inc (h1DetFired,i) | | multiplicity=multiplicity+1 | | endif | |c | |c shift mask one bit | |c | | testBit=testBit+testBit | | 100 continue | |c | |c 1D histogram of multiplicities | |c | | call h1Inc (h1Multiplicity,multiplicity) | |c | | return | Replay Using Daphne Page 30 Accessing Histograms Within a User-Function 9 May 1997 |c | | entry userInit | |c | |c get histogram numbers of 1D histograms "DETFIRED" & "MULTIPLICITY" | |c warning: Daphne histogram names are limited to 12 characters | |c routine h1Number is not case sensitive | |c | | call h1Number ('detFired',h1DetFired) | | call h1Number ('multiplicity',h1Multiplicity) | | return | |c | | end | +----------------------------------------------------------------------+ Replay Using Daphne Page 31 Accessing Histograms Within a User-Function 9 May 1997 The following example has as its inspiration a group that wanted to understand the response characteristics of their detector. Unlike a conventional experiment at Atlas, they were interested in looking at the detector's entire waveform, not its peak value, or time to peak. Thus a single event caused the readout of a waveform, which was stored in successive channels of a histogram. In actual practice they would collect fifty waveforms in successive rows of a 2D waveform inspect them by eye, and then collect another fifty. To look at a single waveform in a row of the 2D histogram they used the Daphne command "$PJX/YLO=yyy/YHI=yyy ..." to do a projection of a single row into a 1D histogram. I have made the example a little bit more complex by having it also keep track of the sum of corresponding channels of the waveform in order to demonstrate some of the h1XXX routines. Replay Using Daphne Page 32 Accessing Histograms Within a User-Function 9 May 1997 +----------------------------------------------------------------------+ | subroutine user (eventType,nWords,eventVector) | | implicit none | |c | | integer*2 eventType | | integer*4 nWords | | integer*2 eventVector (*) | |c | | include 'fixDefs.inc' | |c | | integer*4 i | | integer*4 limit | | integer*4 previousValueReset | | integer*4 nextAvailableRow | | data nextAvailableRow /0/ | |c | |c histograms information | |c | | integer*4 h1Sum | | integer*4 h2Waveform | | integer*4 nSum | | integer*4 nxWaveform | | integer*4 nyWaveform | |c | |c user supplied routine passed to h2User to zero 2D array | |c using h2Put to zero 1D arrays is okay, but might be too slow | |c for 2d arrays | |c | | external zeroH2 | |c | |c room in array to save a waveform sample | |c | | if (nextAvailableRow .lt. nyWaveform) then | |c | |c don't exceed array sizes of event or waveform | |c | | limit=min(nWords,nxWaveform) | |c | |c put the current event in the next unsed row of the 2D histogram | |c | | do i=1,limit | | call h2Put (h2Waveform,i,nextAvailableRow,eventVector(i)) | | end do | | limit=min(nWords,nSum) | |c | |c add this waveforms to the running sum | |c | | do i=1,limit | | call h1Add (h1Sum,i,eventVector(i)) | | end do | | end if | |c | | return | Replay Using Daphne Page 33 Accessing Histograms Within a User-Function 9 May 1997 Continued on next page Replay Using Daphne Page 34 Accessing Histograms Within a User-Function 9 May 1997 |c | | entry userInit | |c | |c tell sort program where to place FIX values | |c | | call defFix (fixArr(1),fixZZZ) | |c | |c get number of histograms for use with h1XXX and h2XXX routines | |c | | call h1Number ('Sum',h1Sum) | | call h2Number ('waveform',h2Waveform) | |c | |c get number of channels in histograms so array limits are not | |c exceeded | |c | | call h1Nchan (h1Sum,nSum) | | call h2Nchan (h2Waveform,nxWaveform,nyWaveform) | | return | |c | |cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc| |c | | entry fixflt | |c | |c check for change in value of "RESET" | |c RESET is a FIX variable defined in FIXDEFS.INC | |c it can be changed during sorting by the command: | |c | |c $FIX RESET | |c | |c if value has changed then clear arrays and start over | |c | | if (reset .ne. previousValueReset) then | | reset=previousValueReset | |c | |c points to next available row in 2D array | |c | | nextAvailableRow=0 | |c | |c clear histogram containing sum | |c | | do i=0,nSum-1 | | call h1Put (h1Sum,i,0) | | end do | |c | | call h2User (zeroH2,h2Waveform,0,0) | | endif | |c | | return | | end | +----------------------------------------------------------------------+ Replay Using Daphne Page 35 Accessing Histograms Within a User-Function 9 May 1997 +----------------------------------------------------------------------+ | subroutine zeroH2 (nx,ny,hist,ix,iy) | | implicit none | |c++ | |c called by h2User from within user-function | |c zeroes a 2D histogram | |c | |c nx - input/integer*4 | |c number of X channels | |c | |c ny - input/integer*4 | |c number of Y channels | |c | |c hist - input/integer*2 histogram (0:nx-1,0:ny-1) | |c histogram to be cleared | |c | |c ix - input/integer*4 | |c selected X channel (ignored in this example) | |c | |c iy - input/integer*4 | |c selected Y channel (ignored in this example) | |c-- | | integer*4 nx | | integer*4 ny | | integer*2 hist (0:nx-1,0:ny-1) | | integer*4 ix | | integer*2 iy | |c | | integer*4 i | | integer*4 j | |c | |c use X for inner loop and Y for outer loop for best performance | |c | | do j=0,ny-1 | | do i=0,nx-1 | | hist(i,j)=0 | | end do | | end do | |c | | return | | end | +----------------------------------------------------------------------+ Replay Using Daphne Page 36 Using Fortran NAMELIST 9 May 1997 11 Using Fortran NAMELIST NAMELIST allows users to enter data from a file using symbolic names. For example: Traditional Data File With Namelist File RUN102.DAT File RUN102.DAT +------------------------+ +---------------------------+ | 14.0 | | $rundata | | 3.7 | | s0=114.0 drift=3.7 | | 14.2 | | aa0=14.2 | | !you can put a comment | | ! you can put a comment | | 107.7 ! at line end | | aa1=107.7 ! at line end | | 109.8 | | d(1)=1.0 d(2)=4.0 | | 1.0,4.0,7.0,9.0 | | d(3)=7.0 d(4)=9.0 | | 'Oxygen' | | beam='Oxygen' | | .true. | | coinc=.true. | | 0.1 0.7 4.0 | | e=0.1 0.7 4.0 | | | | | | and so on | | and so on | | | | | | | | $end | | | | | +------------------------+ +---------------------------+ Namelist variables and arrays can appear in the Namelist file in free format, in any order, and be of any data type. Character variables must be enclosed in single quotes (Example BEAM='Oxygen'). Variables which do not appear in a namelist data file are left unchanged. Creating a Namelist is not much more difficult than creating the I/O list one normally needs for a READ or WRITE statment. Here is an example of the use of Namelist in a userIn routine: Replay Using Daphne Page 37 Using Fortran NAMELIST 9 May 1997 +-----------------------------------------------------------------+ | subroutine userIn (runString) | | implicit none | | c++ | | c Demonstrate use of Fortran NAMELIST in userIn routine | | c-- | | character*(*) runString | | c | | real aa0,aa1,d(4),drift,e(3),s0 | | character*20 beam | | logical coinc | | integer option | | c | | c Variables may appear in a different order in the Namelist | | c list than in the data file and be of any data type | | c | | namelist /runData/ AA0,AA1,BEAM,COINC,D,DRIFT,E,S0 | | c | | c open a data file whose name is the same as the run number | | c the data file contains the calibration constants of this run | | c read the current values | | c | | open (unit=10,name=runString,status='old',readonly) | | read (10,runData) | | c | | c perhaps the user wants to over-ride those values in the file | | c | | 20 write (6,*) 'Enter 1 to exit dialog and start sorting' | | write (6,*) 'Enter 2 to change parameters from terminal'| | write (6,*) 'Enter 3 to see parameters on terminal' | | write (6,*) 'Enter 4 to write parameter values to file' | | read (5,*,err=20,end=999) option | | if (option .lt. 1 .or. option .gt. 4) goto 20 | | goto (999,200,300,400), option | | c | | c option 2: let user change parameter values from terminal | | c | | 200 write (6,runData) | | read (6,runData,end=20,err=20) | | goto 20 | | c | | c option 3: display current parameter values on terminal | | c | | 300 write (6,runData) | | goto 20 | | c | | c option 4: write current parameters to new version of file | | c | | 400 open (unit=11,name=runString,status='new') | | write (11,runData) | | close (unit=11) | | goto 20 | | c | | 999 return | Replay Using Daphne Page 38 Using Fortran NAMELIST 9 May 1997 | end | +-----------------------------------------------------------------+ Replay Using Daphne Page 39 Using Fortran NAMELIST 9 May 1997 An example of the data file created by the Namelist write: +-----------------------------------------------------------------+ | $RUNDATA | | AA0 = 103.0000 , | | AA1 = 107.7000 , | | BEAM = 'Oxygen ', | | COINC = T, | | D = 1.000000 , 4.000000 , 7.000000 , ... | | DRIFT = 3.700000 , | | E = 0.1000000 , 0.7000000 , 4.000000 , | | S0 = 114.0000 | | $END | +-----------------------------------------------------------------+ In creating a namelist data file keep the following rules in mind: - Column 1 is ALWAYS ignored - $XYZ begins in column 2 (where "XYZ" is the Namelist name) - Data must NOT appear in column 1 - An exclamation mark ("!") appearing after column 1 precedes any comments - $END begins in column 2 - By entering a question mark ("?") in column 2 one can get the Namelist name and the names of the variables in the Namelist. Replay Using Daphne Page 40 Use $@DAPEXE:DAPUSERFUNC.LNK to Link User-Functions 9 May 1997 12 Use $@DAPEXE:DAPUSERFUNC.LNK to Link User-Functions The procedure for linking a user-function is sufficiently complicated that it has been placed in a command file. 12.1 DAPUSERFUNC.LNK Command Procedure The simplest case is the linking of a single file containing your user defined function, no common block definitions, and no special linker options. For example to link a user-function named MYUSER the command reduces to: $ @DAPEXE:DAPUSERFUNC.LNK MYUSER The command procedure parameter has three parts: - Object Files The name of the file (or files) containing object code for your user-function. - Linker Options File (When using common blocks) Because of an unfortunate choice of common block attributes by the Fortran compiler it is necessary to create a file which "overrides" the default attributes of each common block. For example, suppose your user-function uses common blocks /PSEUDO/, /FIT/, and /CALIBRATION/. You should create a file with the extension ".OPT" (for example COMMONBLOCKS.OPT) with the following statements: +---------------------------------------+ | PSECT_ATTR=PSEUDO,NOSHR | | PSECT_ATTR=FIT,NOSHR | | PSECT_ATTR=CALIBRATION,NOSHR | +---------------------------------------+ The link options file should appear after all object files and should be followed with the qualifier "/OPTION" as in the example below. - Link Qualifiers For example /DEBUG or /MAP A complex example of the command procedure: +-------------------------------------------------------------------+ | $ @DAPEXE:DAPUSERFUNC.LNK SPECINIT,RUN11_FIT,SPEC1,SPEC2345, - | | GAUSS,COMMONBLOCKS/OPTION - | | /DEBUG/MAP/EXE=SPEC_RUN11 - | Replay Using Daphne Page 41 Use $@DAPEXE:DAPUSERFUNC.LNK to Link User-Functions 9 May 1997 +-------------------------------------------------------------------+ Replay Using Daphne Page 42 Use $@DAPEXE:DAPUSERFUNC.LNK to Link User-Functions 9 May 1997 12.2 Pre-Declared Common Blocks For convenience the following common blocks have been "pre-declared" in the standard link options file: /PEV/, /A/, /B/, and /C/. No options file is needed if you are using only common blocks with these names. 12.3 Hints You might want to create a single options file which you can use with all your user routines. In it you can put the common blocks that appear in any of your user-functions: the linker does NOT consider it to be an error if you over-ride the psect attributes of common blocks which are not defined. If you have a very large user-function it may be difficult to determine the names of all your common blocks. One trick is to link your function with the /MAP qualifier. Then use the $SEARCH program to look for all psects that have the attributes SHR (rather than NOSHR) and WRT (rather than NOWRT). $ @DAPEXE:DAPUSERFUNC.LNK RMASS,FITSUBS,GAUSS/MAP $ SEARCH RMASS.MAP " SHR"," WRT" /MATCH=AND ^ ^ | | space space Replay Using Daphne Page 43 Use $USERFUNC to Specify User-Function 9 May 1997 13 Use $USERFUNC to Specify User-Function Daphne uses a mechanism called shareable images to reduce the link time and amount of space required for user-functions. The main sort program, known as VAXSORT, is linked with a dummy user function which contains subroutines (or entry points) with the same names as the "real" user-function that will eventually be written by the user. The system locates the "real" user-function at execution time (not link time) by using the logical name DAPUSERFUNC. +-----------------------+ | | | VAXSORT | | | | | DUA0:[SMITH]MYUSERFUNC.EXE | +-----------------+ +---------------+ | | | UserIn | | | ==================================> | UserEx | | | Job Logical Name DAPUSERFUNC > | User | | \ ==================================> \ User1 | | \ \ User2 | | \ \ etc. | | \--------------+ \------------+ | | | | +-----------------------+ The Daphne $USERFUNC command has a single argument: the name of the executable file containing your user-function. The file must exist and should have been linked using the DAPEXE:DAPUSERFUNC.LNK command procedure. $ USERFUNC MYUSERFUNC Expanded File Name is DUA0:[SMITH]MYUSEFUNC.EXE; By default the file name you supply is combined with your default device, directory, and a file type of ".EXE" to create a "fully qualified" file specification which is printed at your terminal. Version numbers will not be part of the expanded file name unless you supply a version number in the command. When you modify and re-link your user function it is NOT necessary to re-issue the $USERFUNC command as the system will normally use the highest numbered version of your file. Of course if you change the name of your user function you must give the new file name in another $USERFUNC command. If you decide that you no longer need a user function you can turn off this feature by issuing the command with no arguments: $ USERFUNC Replay Using Daphne Page 44 Differences Between the VAX/AXP VMS and NS32000 Compilers 9 May 1997 14 Differences Between the VAX/AXP VMS and NS32000 Compilers Below is a list of differences between the the VAX/VMS and GNX Fortran compilers that have caused inconvenience to users: VAX/VMS GNX length of symbolic names 31 characters 24 characters special characters in names "$" and "_" "_" defeat implicit types implicit none implicit undefined (a-z) hexadecimal constants '0001'X X'0001' octal constants '0001'O O'00001' binary constants not implemented B'00001' bitwise AND IAND (I,J) AND (I,J) bitwise OR IOR (I,J) OR (I,J) bitwise complement NOT (I) NOT (I) bitwise exclusive or IEOR (I,J) XOR (I,J) left shift ("S" bits) ISHFT (I,S) LSHIFT (I,S) or RSHIFT (I,-S) right shift ("S" bits) ISHFT (I,-S) RSHIFT (I,S) or LSHIFT (I,-S) comments at end of line precede by "!" not implemented local variables retain always yes if SAVE values at subroutine exit statement used DO statement DO I=1,N extensions no extensions END DO DO WHILE (logical) END DO The compilers are alike in supporting the following extensions or little used features: - INTEGER*2 and INTEGER*4 - REAL*4 and REAL*8 - PARAMETER statement - ENTRY statement - INCLUDE statement - SAVE statement - tab character as method of skipping to column 9 of source code 14.1 Differences in Bitwise Operations One of the most annoying differences between the compilers is that a bitwise "AND" of two INTEGER*2 variables is peformed by the "IAND" function on the VAX, and the "AND" function on the NS32000. You may want to consider using the BIT16 function described in section 24.15 Replay Using Daphne Page 45 Differences Between the VAX/AXP VMS and NS32000 Compilers 9 May 1997 of EG to provide a reasonably efficient and compiler independent method of testing bits in an INTEGER*2 variable. One should avoid using multiply and divide for testing bits since, in general, it will be significantly slower than using the bit test routines. 15 Use $RDT (Tape) or $RDD (Disk) to Start Replay The $RDT command (Replay Data from Tape) and $RDD command (Replay Data from Disk) start replay by reading information describing the run from the tape or disk file. - Event Structure Whether the event is fixed length or variable length If fixed length then the number of parameters - Banner Information Run Number Beam, Energy, Experiment Name, and Target (But NOT Physicist) Comment Field 15.1 Error Messages From $RDT and $RDD - %SYSTEM-F-NOTINSTALL, writable shareable images must be installed This message appears when the user has forgotten to declare a common block in the options file (Section 10.1). Only common blocks /PEV/, /FIXCOM/, /FLTCOM/,/A/, /B/, and /C/ are "pre-declared". If you can't identify a common block which is causing the problem use the procedure described in Section 10.3. When a common block or other psect has both the SHR and WRT attributes it must appear with the PSECT_ATTR link option in order to turn off the SHR attribute. - %RMS-E-FNF, file not found This usually means that the system was unable to locate the file you specified in your $USERFUNC statement. Re-issue the $USERFUNC statement. A second possibility is that the system was unable to find VAXSORT, VAXSORTDEBUG, or the dummy shareable image. In this case consult a member of the computer staff. Replay Using Daphne Page 46 Use $RDT (Tape) or $RDD (Disk) to Start Replay 9 May 1997 - %SYSTEM-F-SHRIDMISMAT, ident mismatch with shareable image This usually means that you are trying to use a user-function which was linked with an older or incompatible version of VAXSORT. The message could also appear if you linked your user function without using the DAPEXE:DAPUSERFUNC.LNK command procedure (Section 10). Relink your user function using DAPEXE:DAPUSERFUNC.LNK Replay Using Daphne Page 47 Using $RDT or $RDD with the VAX/AXP VMS Symbolic Debugger 9 May 1997 15.2 Using $RDT or $RDD with the VAX/AXP VMS Symbolic Debugger This is an example of the beginning of a debugger session for a user defined function used with VAXSORT. It is a little more complicated then the standard debug session because it requires the "SET IMAGE" debug command to load the symbol table for the DAPUSERFUNC shareable image. NOTE If the VAXSORT source code is not available you will see an error message when you first start replay with the "/DEBUG" option. This is a warning message that can be ignored since you are debugging your user-function, not the main sort program. +--------------------------------------------------------------------+ a |$ fortran/debug/noop my_user_func | b |$ @dapexe:dapuserfunc.lnk my_user_func /debug | |User function now has three arguments: | | #1: event type (input/integer*2) | | #2: number of words in event (input/integer*4) | | #3: event vector (input/integer*2 array) | c |$ userfunc my_user_func | |Expanded file name is SYS$SYSDEVICE:[DAPHNE]MY_USER_FUNC.EXE; | d |$ rdt/debug | | | | VAX DEBUG Version V4.5-6 | |%DEBUG-I-INITIAL, language is FORTRAN, module set to 'VAXSORT1' | |%DEBUG-I-NOTATMAIN, type GO to get to start of main program | e |DBG> set image dapuserfunc | |DBG> set break user | |DBG> set module userin | |DBG> set break userin\userin | |DBG> g | |%DEBUG-I-DYNIMGSET, setting image VAXSORTDEBUG | |break at routine VAXSORT1 | | 1: program vaxSort1 | |DBG> g | |%DEBUG-I-DYNIMGSET, setting image DAPUSERFUNC | |break at routine USERIN | | 18: call defPEV (pev,pev(nPEV)) | f |DBG> t 18:19 | | 18: call defPEV (pev,pev(nPEV)) | | 19: return | |DBG> e runNumberString | |USERIN\RUNNUMBERSTRING: '105 ' | |DBG> s | |stepped to USERIN\%LINE 19 | | 19: return | g |DBG> set break/exception | |DBG> g | Replay Using Daphne Page 48 Using $RDT or $RDD with the VAX/AXP VMS Symbolic Debugger 9 May 1997 |%DEBUG-I-DYNMODSET, setting module USERTRANSFER | |break at routine USER | |DBG> | |DBG> | h |$ wait 1 | | | | | continued on next page Replay Using Daphne Page 49 Using $RDT or $RDD with the VAX/AXP VMS Symbolic Debugger 9 May 1997 | | | | |DBG> s | |stepped to USERIN\%LINE 19+7 | | 19: return | |DBG> s | |stepped to USERIN\%LINE 25 | | 25: sum=ev(4)+ev(5) | |DBG> s | |stepped to USERIN\%LINE 26 | | 26: pev(1)=ev(4)/sum * 1024.0 | |DBG> g | i |%SYSTEM-F-FLTDIV_F, arithmetic fault, floating divide by zero | | at PC=0001DA4F, PSL=03C00020 | |break on exception preceding USERIN\%LINE 26+8 | | 26: pev(1)=ev(4)/sum * 1024.0 | |DBG> t 26 | |module USERIN | | 26: pev(1)=ev(4)/sum * 1024.0 | j |DBG> e sum | |USERIN\SUM: 0.0000000 | |DBG> g | |Arithmetic trap number: 1 trap ID: 0 | |break at routine USER | |DBG> g | |break at routine USER | k |DBG> cancel break/all | |DBG> | +--------------------------------------------------------------------+ Points highlighted: a. Compile with the /DEBUG and /NOOPTIMIZE options b. Link with the /DEBUG option c. The $USERFUNC command tells Daphne the name of the user-function d. Use the /DEBUG qualifier for the $RDT command e. Loads the symbol table of the user-function f. The "TYPE" statement lets you examine source code g. This intercepts arithmetic traps BEFORE the standard Daphne arithmetic trap handler One could also use the debug command "SHOW CALLS" h. This tells the command language interpreter to go to sleep for one day. To wake it up enter control/Y. Without this statement you would have to give alternate commands to the debugger and the command language interpreter. i. Arithmetic trap intercepted at line number indicated j. Value of variable in denominator of expression examined k. Breakpoints canceled to allow uninterrupted execution Replay Using Daphne Page 50 Using $RDT or $RDD with the VAX/AXP VMS Symbolic Debugger 9 May 1997 At this point in the debug session can use all the usual debugger commands such as STEP, TYPE, EXAMINE, SET BREAK, SHOW CALLS, GO, etc. If you find the startup procedure too tedious you can place the debug commands in a file, for example INIT.DBG, and then execute them as a group: DBG> @INIT.DBG If you decide to stop sorting because of a bug you've discovered in your program you can end the debug session with the EXIT command: DBG> EXIT (exit the debugger) control/Y (wake up the CLI from the $WAIT 1 command) Remember that this will leave the tape improperly positioned in the middle of the run so use the backspace tape command to reposition the tape to the beginning of the run: $ BSP 1 Replay Using Daphne Page 51 Disk-Sorted Histograms 9 May 1997 16 Disk-Sorted Histograms Disk-sorted histograms allow sorting into very large two-dimensional histograms which exceed the amount of physical memory available to the user [1]. The normal Daphne sorting algorithm assumes that there is no penalty associated with a random pattern of references to histograms: this will not be true of a virtual memory machine, such as the VAX, when the amount of data referenced is much larger than the amount of physical memory available. When this assumption is no longer true the operating system spends a large amount of CPU time and I/O resources moving data from disk to memory while forcing other data from memory back to disk where it will probably be needed again shortly. A reference by a program to a part of itself which is on disk rather than in physical memory is called a "page-fault". The use of the term "fault" does not indicate any kind of hardware error. When the page-fault rate becomes so high as to prevent useful working from being done by the program it is said to be "thrashing". Excessive page-faulting can be detected using the VAX/AXP VMS Monitor Utility: $ MONITOR PROCESS/TOPFAULT In disk-sorting the program tries to avoid thrashing by accessing histograms in a non-random fashion. In Daphne a disk sorted histogram is treated as a number of "strips" each about 8 kbytes. [A 2000 by 2000 integer*2 histogram would require 8 megabytes and be divided into 1000 strips of 8 kbytes each]. For each of the strips [1000 strips] a buffer is created whose size depends on the amount of physical memory and the size of the histograms. [If 2 megabytes of physical memory is available then each strip's buffer would be about 2000 bytes long]. When VAXSORT would normally increment a channel in a given strip of a histogram [the 123rd bin of strip 456] the program instead puts the bin number [bin 123] at the end of the list of bins to be incremented in the buffer [buffer 456]. Eventually a strip's buffer fills up after several hundred or several thousand references [1000 references] to that strip of the histogram. The strip corresponding to the buffer [strip 456] is read into physical memory and all the channels that should be changed [bin 123 and 999 others] are then modified. The modified strip [strip 456] of the histogram is then written back to disk. Although this technique is inefficient compared to the normal method of sorting into a histogram which is in physical memory, it is much more efficient then a program which is thrashing. [1] W.T. Milner, J.A. Biggerstaff et. al.,"Data Acquisition and Analysis System at the Holifield Heavy Ion Research Facility", IEEE Trans. Nucl. Sci., Vol NS-26(4), 4402(1979). Replay Using Daphne Page 52 Disk-Sorted Histograms 9 May 1997 Assume a program would normally take 5% of its CPU time incrementing a memory resident two-dimensional histogram. If it now takes 5 times as much CPU time to process an increment to a disk-sorted histogram then the program now would require 20% more CPU time. However the number of page-faults may have dropped by a very large factor which depends on the pattern of memory references and the "hit-ratio". The hit-ratio is the fraction of time that a program refers to data and finds it in memory rather than on disk. A reduction in the number of disk operations by a factor of 10 or 100 would not be suprising. To indicate that a two-dimensional histogram is to be disk-sorted use the "/DISKSORT" qualifier: $ H2 2000 2000 /NAME=JUMBO /DISKSORT If you have a large number of points which fall on the borders of your histogram and they are not important in the analysis of the data then you should declare the histogram with the /NOZEROS qualifier or the /NOOVERFLOW qualifier, or both. $ H2 3000 1500 /NAME=JUMBO /DISKSORT /NOZEROS /NOOVERFLOW These qualifiers tell the sorting program not to bother incrementing channels which fall in channel 0 (/NOZEROS) or in the last channel of the histogram (/NOOVERFLOW). Disk-sorting is more time-consuming than sorting into memory-resident 2D histograms so it is worthwhile reducing the number of increment operations as much as possible, consistent with ones needs. There is no built-in limit on the number of disk-sorted histograms which may be created. However, the user should keep in mind that there may be problems due to the amount of disk space available, an account's limits, and system limits. For instance, one can't create the 8 megabyte (16,000 page) histogram mentioned above if the system global page count is set to 10,000 pages or the system maximum virtual address is 12,000 pages. Due to the way disk-sorted histograms are implemented, and a certain amount of laziness, it is not possible at present to use $ZAP on a disk sorted histogram while $RDT or $RDD is active. Nor it is possible to use the h1XXX or h2XXX families of routines to directly access the histogram from within the user-function. Replay Using Daphne Page 53 Improving Replay Performance 9 May 1997 17 Improving Replay Performance 17.1 Use the KILL Condition to Reject Uninteresting Events Early Sometimes a large percentage of the events on a tape are "uninteresting" and you wish to skip them as quickly as possible. The strategy to use depends on what must be computed in order to know whether an event is to be rejected or not. It might be necessary to call the user-function in order to compute a pseudo-parameter or evaluate a condition which can't be expressed in Daphne. On the other hand, Daphne may be required because the decision to reject depends on evaluation of a 2D window or a linearization, things which are difficult for a user-function to do. - No User-Function Present Suppose one wants to reject events that have parameter TIME outside a 1D window (already defined) named OK_TIME. Conditions: NOT_OK: OUT1 OK_TIME REJECT: KILL NOT_OK - User-Function Present but NOT Required to Reject This case is different from the previous one because one wants to postpone evaluation of the user-function until after the event is known to be interesting. By definition the USER routine is called BEFORE any conditions are evaluated while the USER-n routines are called DURING condition evaluation. As a result your computation should be placed in a USER-n routine (say USER1) while the USER routine should be omitted altogether. Suppose one wants to reject events that have parameter TIME outside a 1D window (already defined) named OK_TIME: User Function USER: Do not code User Function USER1: Compute pseudo-parameters Conditions: NOT_OK: OUT1 OK_TIME REJECT: KILL NOT_OK USER 1 Replay Using Daphne Page 54 Improving Replay Performance 9 May 1997 - Only User-Function Required to Reject In this case you have a choice of placing the computation in the USER function or the USER-n function. If you want to place the decision in the USER function you set a pseudo-parameter (say psuedo-parameter 1) to 1 if you want the event killed, otherwise set it to 0. The pseudo-parameter can then be tested in the conditions: User Function USER: subroutine user (eventType,nWords,event) integer*2 event (*) ... common /pev/ pseudo integer*2 pseudo (100) ... if (mass .ge. 400.0 .and. mass .le. 570.0) then pseudo(1)=1 else pseudo(1)=0 endif ... return end Maps: $M1 1/PSEUDO /NAME=PEV1 Conditions: NOT_OK: BITOFF PEV1 0 REJECT: KILL NOT_OK Replay Using Daphne Page 55 Improving Replay Performance 9 May 1997 A second method is to exploit the fact that the USER-n function can return a logical value which can be used to set a condition. In this case one would code the computation as part of the USER-n function rather than as part of the USER function. User Function USER: Do not code Conditions: NOT_OK: USER 1 REJECT: KILL NOT_OK User Function USER1: logical function user1 (eventType,nWords,event) integer*2 event (*) ... if (mass .ge. 400.0 .and. mass .le. 570.0) then user1=1 else user1=0 endif ... return end Replay Using Daphne Page 56 Improving Replay Performance 9 May 1997 - User-Function and Daphne Required to Reject This case occurs when rejection depends on windows or linearizations which are easier to evaluate in Daphne than in a user function. The proper strategy is to perform the computation in a USER-n function after making sure that Daphne has evaluated any linearizations that the computation depends on. The purpose of the "FORCELNZ" condition is to guarantee that the specified linearization has been evaluated. If the FORCELNZ condition has no arguments then ALL linearizations are evaluated. A linearization is relatively expensive to compute, about as much as three two-dimensional histograms, so it is generally worthwhile to postpone evaluation of linearizations until as many events as possible are rejected. Linearizations will always be evaluated if they are needed to evaluate maps, windows, conditions, or to increment histograms: the FORCELNZ just controls WHEN they are evaluated. Conditions: DO_LNZ_2: FORCELNZ 2 NOT_OK: USER 1 REJECT: KILL NOT_OK User Function logical function user1 (eventType,nWords,event,lnz) integer*2 event (*) integer*4 lnz (*) ... if (lnz(2) .ge. ev(4) .and. lnz(2) .le. ev(5)) then user1=1 else user1=0 endif ... return end Replay Using Daphne Page 57 Improving Replay Performance 9 May 1997 In some cases you might have several rejection criteria each one requiring time-consuming computations in order to make the next round of rejections. This can be handled through the use of several USER-n conditions and several KILL conditions. In the following example the user-function is broken up into three parts (USER1, USER2, and USER3). The first rejection is determined by USER1 using the result of linearization MASS. A second set of events are rejected if certain parameters are outside all three two-dimensional windows. If the event is still interesting a second linearization is performed and USER2 is called to perform additional computations and possibly reject the event. If the event is still interesting USER3 is called and any remaining conditions are evaluated and histograms are incremented. Conditions: DO_LNZ_1: FORCELNZ MASS NOT_OK_1: USER 1 REJECT_1: KILL NOT_OK_1 INSIDE_1: IN2 WIND1 INSIDE_2: IN2 WIND2 INSIDE_3: IN2 WIND3 ALL_WIND: OR INSIDE_1 INSIDE_2 INSIDE_3 OUT_ALL: NOT ALL_WIND REJECT_2: KILL OUT_ALL DO_LNZ_2: FORCELNZ 2 NOT_OK_3: USER 2 REJECT_3: KILL NOT_OK_3 LAST_USER: USER 3 ... Replay Using Daphne Page 58 Improving Replay Performance 9 May 1997 17.2 For Sparse Data Use the /NOZEROS Option for Histograms When a large fraction of the parameters being histogrammed are zero then significant efficiencies can be found in the handling of 2D histograms. Consider the following case as an example: You have two sources of triggers and an event with eleven parameters A1, A2, and B1 through B9. Trigger TA causes 95% of the events and produces two meaningful parameters A1 and A2 leaving B1 through B9 zero. A second trigger TB fires ony 5% of the time and produces nine meaningful parameters B1 through B9 leaving A1 and A2 zero. Occasionally they fire together making all eleven parameters interesting. If one wants to analyze both types of events in the same run a significant amount of time can be saved in the incrementing of 2D histograms by using the /NOZEROS qualifier of the $H2 command. The /NOZERO qualifier tells Daphne that it should not bother to count the events which would normally go in channel zero. This extra check itself takes time so it only makes sense when there are more than just a token number of zeroes. For 2D histograms a lot of time is saved if the Y value is frequently zero and somewhat less time if the X value is zero. For 2D histograms it makes sense to use the /NOZERO if even 5% of the points are zero since it is so much more expensive to compute a 2D histogram channel than a 1D histogram channel. Of course you shouldn't use this strategy if the number of zero counts is important to your analysis. No appreciable amount of time is saved in using the /NOZERO qualifier for 1D histograms. Replay Using Daphne Page 59 Improving Replay Performance 9 May 1997 17.3 Some Intrinsic Functions Require Subroutine Calls There is a misconception that all Fortran bit manipulation instructions are performed in-line. For a large user subroutine the difference may not be important; for a short routine or routines that do a great deal of bit manipulation users might wish to know that BTEST takes approximately 10 times as long to execute as IAND. Evaluated by Subroutine Call: BTEST IBSET IBCLR IBITS ISHFTC Evaluted In-Line: IAND IOR NOT IEOR ISHFT Another misconception is that library functions that have the same name as VAX machine instructions are performed in-line. For example, using LIB$FFS appears to be an invitation to the Fortran compiler to generate a FFS ("Find First Set bit in a bit string") instruction. This is an invitation which the compiler always declines. Replay Using Daphne Page 60 Reduced Data Tapes or Disk Files 9 May 1997 18 Reduced Data Tapes or Disk Files To avoid awkwardness I will simply refer to tapes in this section. The reader should understand that disk files would serve just as well. Similarly, when I refer to the $RDT command (replay data from tape), the $RDD command (replay data from disk) will do. You can replay data using the micro-processors or the VAX. Whichever you use, the NOTAPE condition allows you to select a subset of the original events to create a new tape. 18.1 Using Subroutine CopyToTape to Create Artificial Events If you want to combine real parameters with pseudo-parameters to create "artificial" events you must use the "copyToTape" subroutine in your user-function. This subroutine is available only on the VAX. +--------------------------------------------------------------------+ | VAX/AXP Replay Only | +--------------------------------------------------------------------+ | subroutine copyToTape (nWords,newEventVector) | | | | Copies the new event to the output tape (or disk file) unless | | the NOTAPE flag is set | | If there is no output tape (disk file) then the call is ignored | | | | Must be preceded by call to subroutine "usingCopyToTape" in | | userIn (the initialization section of the user-function) | | | | nWords - input/integer*4 | | number of INTEGER*2 words in the new event | | minimum length event is 1 word | | maximum length event is 1022 words | | | | newEventVector - input/integer*2 array | | parameters of the new event | | parameters are usually INTEGER*2 words | +--------------------------------------------------------------------+ +--------------------------------------------------------------------+ | VAX/AXP Replay Only | +--------------------------------------------------------------------+ | subroutine usingCopyToTape | | | | There are no arguments | | | | Must be called in the initialization section of the user-function | | if you are using subroutine copyToTape | | | | If there is no output tape (disk file) then the call is ignored | +--------------------------------------------------------------------+ Replay Using Daphne Page 61 Reduced Data Tapes or Disk Files 9 May 1997 How does the NOTAPE condition interact with the copyToTape subroutine ? +-------------------------+---------------+ | ------NOTAPE ------- | No | | is true is false | Condition | | | | | +------------------------+-----------+-------------+---------------+ | NO user function | no output | original | original | | or NO usingCopyToTape | | event | event | | | | copied | copied | +------------------------+-----------+-------------+---------------+ | user initialization | no output | copyToTape | copyToTape | | calls usingCopyToTape | | event | event | | | | copied | copied | +------------------------+-----------+-------------+---------------+ To put it in words: - If you use copyToTape then nothing gets put in the output tape buffer except by calls to copyToTape. - If you don't use copyToTape the default is to copy the original event to the output buffer. The copyToTape routine can be called more than once per real event. This might be useful for breaking apart an event with data for several detectors into sets of detectors which can be analyzed independently. Replay Using Daphne Page 62 Reduced Data Tapes or Disk Files 9 May 1997 Here is an example of a user-function which combines the original event with a linearization value to create a new event: +----------------------------------------------------------------+ | logical function userIn (runNumberString) | | implicit none | |c | | character*(*) runNumberString | |c | |c information about original event passed to user routines | |c | | integer*4 eventType | | integer*4 nWords | | integer*4 eventVec (*) | | integer*4 lnzVec (*) | |c | | logical user1 | |c | |c assumes longest event is 19 words and extra for lnz value | |c | | integer*2 newEventVec (20) | | integer*4 i | |c | |c warn sort program that user-function is using copyToTape | |c | | call usingCopyToTape | | return | |c | | entry user1 (eventType,nWords,eventVec,lnzVec) | |c | |c create new event from original event and linearization value | |c | | do i=1,nWords | | newEventVec(i)=eventVec(i) | | end do | |c | |c put linearization value at end of new event | |c | | newEventVec(nWords+1)=lnzVec(1) | |c | |c copy to output buffer | |c | | call copyToTape (nWords+1,newEventVec) | |c | | return | | end | +----------------------------------------------------------------+ Replay Using Daphne Page 63 Reduced Data Tapes or Disk Files 9 May 1997 18.2 Using Subroutine IsGoodBlock to Skip Entire Blocks +----------------------------------------------------------------------+ | VAX/AXP Replay Only | +----------------------------------------------------------------------+ | logical function isGoodBlock (blockNumber,buffer) | | | | This function is optional | | This function is called by the sorting program just before sorting | | the data. | | | | function value - output/logical | | return ".true" to have this block sorted | | return ".false" to reject this block | | | | blockNumber - input/integer*4 | | the number of the data block in this run | | the first block is block number 1 | | | | buffer - input/integer*2 array | | the buffer of data | | this array starts with data | | it does NOT include header information | +----------------------------------------------------------------------+ A user might supply this optional routine for three reasons: - Restart a previous replay session If a user has to stop a replay session before the end of a run he can use the isGoodBlock routine to restart the replay in the middle of the run by skipping all the data blocks that have already been replayed. - Skip parts of a run with bad data A user may know that during run 47 a detector stopped working and that it was reset while the run continued. The user could then write an "isGoodBlock" routine to skip blocks 470 through 510 which corresponded to the time the detector was not working. - Change Calibration Constants The user may want to adjust calibration constants during the replay session. The "isGoodBlock" routine provides a convenient way to monitor the progress of replay. Replay Using Daphne Page 64 Reduced Data Tapes or Disk Files 9 May 1997 Below is an example of the use of isGoodBlock to allow a user to select a range of block numbers from a run for sorting: +--------------------------------------------------------------------+ | logical function userIn (runNumberString) | | implicit none | |c | | character*(*) runNumberString | | integer*4 currentBlock | |c | | logical isGoodBlock | | integer*4 startingBlock | | integer*4 endingBlock | | data startingBlock /0/ | | data endingBlock /0/ | |c | | write (6,*) 'Replaying Run: ',runNumberString | | return | |c | | entry isGoodBlock (currentblock) | |c | | 100 if (currentBlock .lt. startingBlock) then | | isGoodBlock=.false. | | else if (currentBlock .le. endingBlock) then | | isGoodBlock=.true. | | else | | 200 write (6,*) 'Currently at block',currentBlock | | write (6,*) 'At what block do you want to start sorting' | | write (6,1) ' [Enter 0 for current tape position]: ' | | read (5,*) startingBlock | | if (startingBlock .eq. 0) startingBlock=currentBlock | | if (startingBlock .lt. currentBlock) then | | write (6,*) 'Cannot skip backwards' | | goto 200 | | endif | | write (6,*) 'At what block do you want to stop sorting' | | write (6,1) ' [Enter 0 to sort to end-of-run]: ' | | read (5,*) endingBlock | | if (endingBlock .eq. 0) endingBlock=999 999 999 | | if (endingBlock .lt. startingBlock) then | | write (6,*) 'Ending Block before Starting Block' | | goto 200 | | endif | | goto 100 | | endif | |1 format (1x,$,a) | | return | | end | +--------------------------------------------------------------------+ Replay Using Daphne Page 65 Reduced Data Tapes or Disk Files 9 May 1997 18.3 Selecting Events Using the NOTAPE condition To create a reduced event tape you mount two tapes, one as input and the other as output and then use the NOTAPE condition to select events which you don't want copied to the output tape. For example, to copy to a reduced event tape you might do something like the following: +--------------------------------------------------+ | Using the NOTAPE condition with Event Processors | +--------------------------------------------------+ | o | | o | | o | | $ @ice.setup | | $ mdv a | | $ epload/log dapep:standard | | $ tape/in mfa0 | | $ tape/out/dens=6250 mfa1 | | $ con windows | | | | Where WINDOWS.CON contains: | | | | +---------------------------------+ | | | good1: in2 mass14 | | | | good2: in2 mass15 | | | | include: or mass14 mass15 | | | | exclude: not include | | | | ignored: notape exclude | | | +---------------------------------+ | | | | $ rdt/ep | | o | | o | | o | +--------------------------------------------------+ Replay Using Daphne Page 66 Interacting With The Sort Process 9 May 1997 19 Interacting With The Sort Process The following programs demonstrate how to suspend and resume execution of the sort process using the VMS system services SYS$SUSPND and SYS$RESUME: +------------------------------------------------------------+ | File SUSPEND.FOR | +------------------------------------------------------------+ | program suspend | | implicit none | | c | | c Suspends the sort process | | c Compile using: $ FORTRAN/NOOP/CONT=99 SUSPEND | | c Link using the command: $ @DAPDIR:DAPLNK SUSPEND | | c | | include 'dapdir:iop.inc' | | c | | integer*4 code | | integer*4 sys$suspnd | | character*15 ignore | | c | | call initProc | | c | | code=sys$suspnd (srt_pid,ignore, ) | | call check (code) | | call exit | | end | +------------------------------------------------------------+ +------------------------------------------------------------+ | File RESUME.FOR | +------------------------------------------------------------+ | program resume | | implicit none | | c | | c Resumes the sort process | | c Compile using: $ FORTRAN/NOOP/CONT=99 RESUME | | c Link using the command: $ @DAPDIR:DAPLNK RESUME | | c | | include 'dapdir:iop.inc' | | c | | integer*4 code | | integer*4 sys$resume | | character*15 ignore | | c | | call initProc | | c | | code=sys$resume (srt_pid,ignore) | | call check (code) | | call exit | | end | +------------------------------------------------------------+ Replay Using Daphne Page 67 Interacting With The Sort Process 9 May 1997 To compile and link the programs: $ fortran/noop/cont=99 suspend $ @dapdir:daplnk suspend $ fortran/noop/cont=99 resume $ @dapdir:daplnk resume Execute using the $RUN command: $ run suspend or $ run resume Proper operation of the SUSPEND program can be verified by using "$SHOW SYSTEM/SUBPROCESS" and looking for the program in the "SUSP" state. The sort program can suspend itself. +---------------------------------------------------------------+ | Link: $ @DAPEXE:DAPUSERFUNC.LNK MYPROG,DAPDIR:DAPHNE/LIBRARY | +---------------------------------------------------------------+ | subroutine user (et,nw,ev) | | implicit none | | c | | integer*4 et | | integer*4 nw | | integer*2 ev (*) | | c | | integer*4 code | | integer*4 sys$suspnd | | integer*4 getJPInumber | | character*15 ignore | | c | | include '($JPIDEF)' | | c | | integer*4 pid | | data pid /0/ | | c | | ... | | if (itsTimeToSuspend) then | | code=getJPInumber (0,JPI$_PID,pid) | | call check (code) | | code=sys$suspnd (pid,ignore, ) | | call check (code) | | endif | | ... | | return | | end | +---------------------------------------------------------------+