Coding details

Routines

A sort code must provide three routines named init, sortin and finish. In the case of FORTRAN sort codes, these are provided as one subroutine called init with two additional additional entry points. In C sort codes they are provided as separate functions.

Init

The init routine is called once each time sorting is started (that is each time the user clicks on the read button in the GUI or types sort at the command line. It will typically call any Sunsort initialisation routines that need to be called, initialise any variables and perhaps load data from file.

Sortin

The sortin routine is called once for each event that is processed. The data is passed through global variables (FORTRAN common block or C extern structs). It is in this routine that the user does the bulk of the processing.

The contents of the sort code are entirely up to the user, though a typical sort might progress as follows: verify the event is good by checking its trigger number and the combination of adcs present in it using the hit patterns, increment singles spectra using a loop, calculate new pseudo-parameters such as the total energy in a detector or the position of an event, increment such oned and twod spectra as necessary, window on oned or twod spectra before finally incrementing those spectra of physical interest. The path of executable statements must finally end up at a return for each event.

Finish

The finish routine is executed each time Sunsort stops sorting for whatever reason (barring crashes). This is a good place to print out statistics and do any once only processing that depends on all the data read.

Signalling errors

The sort code may signal an error back to Sunsort to tell it to stop sorting. In C this is accomplished by returning a non-zero value. In FORTRAN you instead have to call the subroutine usererror with a non-zero integer parameter. Since only zero/non-zero is tested there is some redundancy here, so it is recommended that users use 1 to mean abort sorting immediately and do not use any other values. That way these other values can be re-used later to mean other things.

Typically an init routine will signal an error if one of it's initialisation files is missing. That way only a few records have been read and the user can correct the problem before continuing.

Signalling an error in the finish routine has no effect at the moment, after all, by the time the finish routine has been called Sunsort is stopping sorting. Still, returning the error may be useful in future, for example, it may stop batch file processing - which it doesn't at the moment.

Global variables

There are a number of global variables that the user may examine to aid with the sorting. In FORTRAN these are passed through a common block. In C they are passed through extern structs. The details are not important as the include files have the correct declarations in. The list below gives a pseudo-declaration to describe the type of the variable. The first declaration for each variable shows how FORTRAN sees the variable, the second shows how C sees the variable. All these variables are read only with the exception of wrtevt.

integer event
int adcs_.event;
The current event's number in the current record.
integer record
int adcs_.record;
The current record's number in this attempt to read from the input file.
logical wrtevt
int adcs_.wrtevt;
A logical variable set on an event-by-event basis to control whether an event is fed to the output stream. This variable is initially set to be false (in C that's 0), and if set by the user to be true (in C that's 1) anywhere in the sortin routine then the current event is copied to the output stream.

See the section on filtering for more information.

integer adc(*)
short adcs_.bitpattern[], adcs_.triggernos, adcs_.adcs[];
This array is responsible for passing the adc values of the current event into the user sort code. The array holds the following information. If an adc does not appear in the current event its value will be set to -1.

In FORTRAN adc(1) onwards hold the value of the appropriate adc for the current event, in C, adcs_.adcs[0] onwards hold the data. To avoid confusion in C, the macro ADC has been defined so that ADC(1) holds the value of the first adc.

For NSF format data, adc(0) is returned with the Daresbury trigger number of the current event, ranging from 1 up to 26. adc(-1) to adc(-8) hold the values of the hit patterns for indirect trigger events. adc(-1) hold the first hit pattern word for adcs 1 to 16, adc(-2) holds the second hit pattern word for adcs 17 to 32 etc. In C, the trigger is in adcs_.triggernos and the hit patterns are in adcs_.bitpattern[0]..adcs_.bitpattern[NOS_BIT_WORDS-1] with the highest numbered element corresponding to the FORTRAN adc(-1).

real var(*)
float vars_.var[];
This array passes the current values of previously defined variables to the user's sort program. These variables are the ones described in the *vars section of the sort code. There values start off as listed in that section, but may be changed by the user between runs.

In FORTRAN, var(1) holds the value of variable for which varnum equals 1, var(2) the value for which varnum equals 2 etc. In C, vars_.var[0] holds the value of the variable for which varnum equals 1. To avoid confusion, a macro VAR has been defined so that VAR(1) corresponds to FORTRAN var(1).

character*40 filein
char filenm_.filein[filenm_SIZE];
This holds the file name of the current source of event by event data. An event can be uniquely identified by a combination of filein, record, and event; for diagnostics.
character*40 fileou
char filenm_.fileou[filenm_SIZE];
This holds the file name of the current destination of filtered event by event data.
integer*2 adclist(*)
short adclist_.adclist[];
This holds a list of which adcs fired, terminated with a -1. The numbers in this array are the FORTRAN array indeces. So can be used directly from FORTRAN, or with the C macro ADC(). If you're using the C array adcs_.adcs[], then you should subtract one.
integer runnumber
int filenm_.runnumber;
This variable holds the run number extracted from in2p3 format data files.

In2p3 data files store the run number internally near the start of the data file. When Sunsort starts sorting in2p3 data files it initially sets this variable to -1. If it comes across a run number record, it sets this variable to the run number found in the record. Since the run number record comes before all data in a run, if you start sorting a tape file from its start, this variable will be set for all events. If you start a tape file partway through, or if you are sorting a disk file, the variable will not be set.

Since in2p3 data tapes are not ANSI format, they do not have a concept of file names. This run number variable is the only way of identifying which run a file is for. Once data is filtered to disk, then the user will have specified a file name which may be used for identification instead of the run number.

Library routines

Sunsort provides a number of library routines for use in sort codes. The declarations shown here are provided automatically by the include files. FORTRAN declarations are shown in a compact style similar to that used for ANSI C. Hopefully it's clear what is meant.

FORTRAN utility routines

This group of functions perform operations that are otherwise difficult in FORTRAN.

integer*2 function iandhw(integer*2 op1, integer*2 op2)
Performs a logical and of the two operands and returns the result.
subroutine bitahw(integer*2 op1, integer op2(16))
Breaks out the 16 bits in op1 into the 16 array elements in op2. Each array elements holds 1 or 0 after this subroutine call.
subroutine bitaw(integer op1, integer op2(32))
Breaks out the 32 bits in op1 into the 32 array elements in op2. Each array elements holds 1 or 0 after this subroutine call.
integer*2 function ibitshw(integer*2 op1, integer op2)
Returns the value of the op2'th bit in op1. Bits are numbered from 1 with 1 being the least significant bit and 16 the most significant.
integer function ibitsw(integer op1, integer op2)
Returns the value of the op2'th bit in op1. Bits are numbered from 1 with 1 being the least significant bit and 32 the most significant.
integer*2 function iswaphw(integer*2 op)
Performs byte swapping on op and returns the result.
integer function iswapw(integer op)
Performs byte swapping on op and returns the result.
integer function nbithw(integer*2 op)
Returns the number of bits set in op.
integer function nbitw(integer op)
Returns the number of bits set in op.
integer function iscalei(integer value, integer low, integer high, integer base)
integer function iscaler(integer value, real low, real high, real base)
real function rscalei(real value, integer low, integer high, integer base)
real function rscaler(real value, real low, real high, real base)
These 4 functions scale a value into a particular range. They perform the calculation:
        result = ((value - low)*base) / (high-low)
	  
OK, these operations aren't difficult, and in fact these functions are rarely used.

Spectrum manipulation routines

These routines are for reading and writing values to and from spectra. The bulk of a typical sort code consists of calls to the inc routines.

There are 5 basic actions:

  1. Increment a channel in a spectrum by 1.
  2. Increment a channel in a spectrum by an arbitrary amount.
  3. Set a channel in a spectrum to a specific value.
  4. Read the value from a channel in a spectrum.
  5. See if a particular channel is within a 2d window.

Each action has an associated name. These are:

  1. inc
  2. incv
  3. set
  4. val
  5. win

And for each routine, the channel may be specified in FORTRAN as integer*2, integer, real or double precision, and in C as int or double. For the incv and set calls, the value added or set is of the same type as the channel(s). The val routines always return integer as that is the type the spectra naturally hold. The channel numbers (and values where appropriate) are rounded to the nearest integer, this is equivalent to the FORTRAN function nint or in C ((int) (variable + 0.5)).

Should you need to make a call in which the channel(s) and value (if any) are of different types, then it's most efficient to use the integer version of the call and convert the arguments to integers yourself.

The win routines set a logical variable in FORTRAN depending on whether the specified point is inside or outside the window. In C they return an integer which is zero if the point is outside the window, otherwise it's non-zero.

To make the name of the function start with name of the action, then append either 1d or 2d depending on which sort of spectrum you want to access (note that for the win action this must be 2d). Then prepend a letter indicating the data type if the data type is not integer. These letters are s for integer*2, f for real and d for double precision (or C double). This gives the following list of routines:

FORTRAN routines

C routines

The short (integer*2) and float (real) functions are not needed in C as the automatic type promotion takes care of it.

Eurogam functions

These functions are needed for the data formats that are based on Eurogam format. These are the Demon and Megha formats.

subroutine eginit(character*(*) filenm, integer error)
int eginit(char *filename);
This call establishes a mapping between the 16384 Eurogam simple ADC channels and the adc() array used in the sort code. The file consists of a number of lines of the form
        adcnumber    @channel
	  
where number is the array index in the adc() array in decimal counting from 1 (FORTRAN convention) and channel is the Eurogam channel in hexadecimal. A sample file called egmap.dat is in the test_sort directory of the Sunsort distribution.

This call returns zero if all went well, otherwise non-zero. In FORTRAN, the return value is placed in the variable error, in C it is the return value of the function.

Examples of how to use this call are in the test_sort/sorts directory of the Sunsort distribution called egam-f.srt and egam-c.srt.

subroutine egroup(integer group, integer*2 data(len), integer len)
struct group_data *egroup(int group);
This call retrieves Eurogam group data about the specified group.

In the FORTRAN call, the data is copied into the array data(). On entry, len holds the size of the array data(). On exit, len holds the actual amount of data in the group, or a negative number if there wasn't enough room in the array.

In the C call, NULL is returned if the group was out of range. Otherwise a pointer to a structure is passed back. The structure has at least the following elements:

        int group_len;
        unsigned short *group_ptr;
	  
If group_len is non-zero then group_ptr points to the data for the group, that is group_ptr[0] holds the first piece of data for the group. If group_len is zero then there was no data for this group in this event and the value of group_ptr is undefined. Note that neither the data pointed to by pointer returned from the egroup call, nor the data pointed to by group_ptr should be written to.

Other routines

integer function wrtevt(integer size, integer*2 data)
int wrtevt_(int *size, short *data);
This routine is used to insert arbitrary data into the filtered data stream. Not all data formats support this function. The use of this routine is not recommended.
subroutine rinc(integer spectrum, integer channel)
This subroutine is an alternative to inc1d provided to ease porting of sort codes from sort-shell. This interface is deprecated and should not be used.
subroutine rinc2d(integer spectrum, integer x, integer y)
This subroutine is an alternative to inc2d provided to ease porting of sort codes from sort-shell. This interface is deprecated and should not be used.

Steven M. Singer
Last modified: Fri Sep 3 15:35:00 BST 1999