Writing your own commands

All molly commands with arguments are called as subroutines from the main program. The data and headers are passed in a set of arrays and the command arguments are passed as a character string array generated from input from the user with a subroutine called 'control.f'. control.f fixes the way command files, concatenated commands etc are handled and you should look at it first if you want to alter the way molly operates on a more fundamental level).

By far the best way to add your own routine is to copy an existing routine with related argument structure. e.g. if your command will take some spectra and output a modified set, then you could use one of the rebinning commands as a template. If you do this, most of it will be so obvious that you won't need to look for further help. If you do see below.

At the moment the support subroutines standardly used by molly are poorly documented. I hope to remedy this in the future. In the meantime please ask me any questions that you may have. I would prefer to keep to a single set of such subroutines rather than have calls to different subroutines which essentially perform the same operation.

Example subroutine

Here is an example of a typical and fairly complete first part of a call (in this case to a command to make plots of header items)
        CALL HEDPLT(SPLIT, NSPLIT, MXSPLIT, DEVICE, COUNTS, 
     &  ERRORS, FLUX, MXBUFF, MXSPEC, MAXPX, NPIX, ARC, NARC, 
     &  MXARC, NMCHAR, NMDOUB, NMINTR, NMREAL, HDCHAR, HDDOUB, 
     &  HDINTR, HDREAL, NCHAR, NDOUB, NINTR, NREAL, MXCHAR, 
     &  MXDOUB, MXINTR, MXREAL, NMSCHAR, VSCHAR, NMSDOUB, VSDOUB, 
     &  NMSINTR, VSINTR, NMSREAL, VSREAL, NSCHAR, NSDOUB, NSINTR, 
     &  NSREAL, MXSCHAR, MXSDOUB, MXSINTR, MXSREAL, ..... etc)
This looks fairly horrific, largely because I have tried to avoid using common blocks as much as possible in molly to avoid hard-to-track-down bugs. This has paid off in that I can normally find bugs in molly very quickly and do not have to worry about nasty interactions between subroutines.

I have also avoided the declaration of arrays as NCHAR(*) or NCHAR(1) which avoids having to specify the size of the array. This means that as well as the number of useful array items, the maximum number possible has to be passed. This does enable checks to be made against writing off the end of the array.

Looked at bit by bit, the above call is straightforward:

SPLIT, NSPLIT, MXSPLIT

  INTEGER NSPLIT, MXSPLIT
  CHARACTER*(*) SPLIT(MXSPLIT) 
SPLIT contains the command arguments (NSPLIT of them, NSPLIT may = 0). These were generated by molly command parser, control.f. Thus a clall "hedplt 1 2" would have 1 and 2 as the NSPLIT=2 arguments. It is up to the programmer as to what to do with these, but see below for standard routines to handle these inputs.

DEVICE

  CHARACTER*(*) DEVICE
the name of the plot device/file for commands which need to know it. Look at a command which plots to see how the device should be opened.

COUNTS, ERRORS, FLUX

These are the data arrays and can be declared as:
  REAL COUNTS(MXBUFF), ERRORS(MXBUFF), FLUX(MXBUFF)
or
  REAL COUNTS(MAXPX,MXBUFF/MAXPX), ..
The latter method allows you to treat the data arrays as 2D arrays and makes it much easier to specify the correct slot. The arrays are declared in the main molly program as 1D arrays, but FORTRAN does not care about whether it is declared differently inside a subroutine.

MXBUFF, MXSPEC, MAXPX

  INTEGER MXBUFF, MXSPEC, MAXPX 
the size of the data arrays, the maximum number of header arrays and the maximum number of pixels/spec. Note that the maximum number of spectra possible is the lesser of MXSPEC and the number of slots i.e. MIN(MXBUFF/MAXPX, MXSPEC). MXBUFF and MXSPEC are fixed by parameter declaration inside molly.f; MAXPX can be altered at run time.

NPIX

 
  INTEGER NPIX(MXSPEC) 
array containing number of pixels/spectrum. NPIX(SLOT) = 0 indicates that SLOT is empty.

ARC, NARC, MXARC

  DOUBLE PRECISION ARC(MXARC,MXSPEC) 
  INTEGER NARC(MXSPEC) 
ARC contains the arc poly coefficients for each spectrum and NARC the number of coefficients for each spectrum. If NARC(SLOT) = 0 then the slot has no wavelength scale. MXARC is fixed by a parameter declaration inside molly.f.

NMCHAR, NMDOUB, NMINTR, NMREAL

  CHARACTER*(*) NMCHAR(MXCHAR,MXSPEC)
  CHARACTER*(*) NMDOUB(MXCHAR,MXSPEC)
  CHARACTER*(*) NMINTR(MXCHAR,MXSPEC)
  CHARACTER*(*) NMREAL(MXCHAR,MXSPEC)
These arrays contain the names of the header items for each spectrum. There are separate arrays for character, double precision, integer and real header items. The number of characters/item is specified in 'molly/molly.dims'.

HDCHAR, HDDOUB, HDINTR, HDREAL

  CHARACTER*(*)    HDCHAR(MXCHAR,MXSPEC)
  DOUBLE PRECISION HDDOUB(MXCHAR,MXSPEC)
  INTEGER          HDINTR(MXCHAR,MXSPEC)
  REAL             HDREAL(MXCHAR,MXSPEC)
Arrays containing the values of the header items.

NCHAR, NDOUB, NINTR, NREAL

  INTEGER NCHAR(MXSPEC), NDOUB(MXSPEC), NINTR(MXSPEC), NREAL(MXSPEC)
Arrays containg the numbers of header items of each type for each spectrum. *

MXCHAR, MXDOUB, MXINTR, MXREAL

  INTEGER MXCHAR, MXDOUB, MXINTR, MXREAL
Integers containing the maximum numbers of header items of each type/spectrum. These are fixed by parameter declarations in molly.f

NMSCHAR, VSCHAR, NMSDOUB, VSDOUB, NMSINTR, VSINTR, NMSREAL, VSREAL

  CHARACTER*(*)    NMSCHAR(MXSCHAR)
  CHARACTER*(*)    NMSDOUB(MXSCHAR)
  CHARACTER*(*)    NMSINTR(MXSCHAR)
  CHARACTER*(*)    NMSREAL(MXSCHAR)
  CHARACTER*(*)    VSCHAR(MXSCHAR)
  DOUBLE PRECISION VSDOUB(2,MXSCHAR)
  INTEGER          VSINTR(2,MXSCHAR)
  REAL             VSREAL(2,MXSCHAR)
Names and values or ranges of the search parameters. You may not want to apply selection in your routine, but if you do, you must pass these arrays.

NSCHAR, NSDOUB, NSINTR, NSREAL

  INTEGER NSCHAR, NSDOUB, NSINTR, NSREAL
These are the number of search parameters of each type (they can all be zero of course).

NSCHAR, NSDOUB, NSINTR, NSREAL

  INTEGER MXSCHAR, MXSDOUB, MXSINTR, MXSREAL
These are the maximum number of search parameters of each type and are declared as parameters in molly.f.

The rest

Any other parameters can be added to the call. The most common are work arrays and list arrays, but please use the standard ones defined in molly.dims in order to save memory. The work arrays in particular can be increased using parameter values inside molly.f. You should never assume that the work arrays will remain the same from call to call as they are all used in one routine or another. The best policy is always to borrow from the many examples available.

At this stage you can do anything you like inside the subroutine. However if you want your command to be standard molly, there are some other rules to follow:

Handling the command arguments

The inputs whether via SPLIT(NSPLIT) or by prompting should be handled by INTR_IN, CHAR_IN, REAL_IN and DOUB_IN, according to the data type of the argument. Here is an example:
      MAXSPEC = MIN(MXBUFF/MAXPX, MXSPEC) ! Set maximum number of slots
      DEFAULT = .FALSE.     ! Initialise for command input
      IFAIL = 0             ! If IFAIL.NE.0, INTR_IN etc return immediately
      NCOM  = 0             ! Counter for command arguments.
*
* Get range/list of slots to plot. Each of the numeric input routines
* includes an upper and lower limit for the parameter. 
*
      CALL INTR_IN('First slot for header plot', SPLIT, NSPLIT, 
     &MXSPLIT, NCOM, DEFAULT, SLOT1, 0, MAXSPEC, IFAIL)
      IF(IFAIL.NE.0) GOTO 999
      SLOT2 = MAX(SLOT1, SLOT2)
      CALL INTR_IN('Last slot for header plot', SPLIT, NSPLIT, 
     &MXSPLIT, NCOM, DEFAULT, SLOT2, SLOT1, MAXSPEC, IFAIL)
      IF(IFAIL.NE.0) GOTO 999
      IF(SLOT1.EQ.0 .AND. SLOT2.NE.0) THEN
        WRITE(*,*) 'Only 0,0 to get list option'
        GOTO 999
      ELSE IF(SLOT1.EQ.0 .AND. SLOT2.EQ.0) THEN
        IF(DEFAULT .AND. NLIST.GT.0) THEN
          CALL SETSLOT(LIST, NLIST, MXLIST, SLOTS, NSLOTS, MXSLOTS)
        ELSE
          WRITE(*,*) 'Enter list of spectra for header plot'
          CALL GETLIS(LIST, NLIST, MXLIST, MAXSPEC, SLOTS, 
     &    NSLOTS, MXSLOTS)
        END IF
        IF(NSLOTS.EQ.0) GOTO 999
      ELSE
        CALL SETLIS(SLOT1, SLOT2, LIST, NLIST, MXLIST, SLOTS, 
     &  NSLOTS, MXSLOTS)
      END IF

*
* Get name of header item for X axis. Character input does not have
* any range of possible inputs.
*
      CALL CHAR_IN('X axis header item', SPLIT, NSPLIT, 
     &MXSPLIT, NCOM, DEFAULT, XNAME, IFAIL)
Note the way 0 0 for the slot range passes onto GETLIS for getting a list of slots. LIST(2,MXLIST) here is an INTEGER array that should be declared inside the subroutine. Common storage is not used here so that subroutines remember the slot lists they were called with.

Data storage

molly uses a convention inherited from RUBY in storing its data. It is important that programmers are aware of this. For each pixel it keeps the number of counts, the uncertainty on the counts and the flux. Linearity is assumed in calculating the error on the flux given the counts error. The counts/flux ratio is an important number that must remain defined even if counts=flux=0. To do this, if COUNTS=0, then the FLUX value is set equal to the COUNTS/FLUX ratio. This is acounted for if you use the routines GET_FLX and GET_ERF to get the mJy flux and uncertainty on a mJy flux, and SET_FLX and SET_ERF to set the FLUX and ERRORS arrays given an mJy flux and its uncertainty.

Error return

In order for concatenated commands to bomb out safely, a parameter IFAIL must be returned. IFAIL= 0 for all is OK and anything else for an error.

Once you have written your subroutine

First add an extra 'else if' to the end of the huge if block that makes up most of molly. Next add a line to 'comdefs.dat' defining the command and 'hpoint.dat' pointing to help on it. Finally add in the subroutine to the list MOLLY_OBJ inside the makefile (this also requires specifying which archive the routine should go into -- it should probably be of the form $(LMOLLY)(your_prog.o)) and then say 'make molly'. Help should always be inserted at the start of the relevant subroutine and be in the form:
*command
*
* this is the help on command "command" bla bla
*
*command
Try to keep the same style as other molly commands, and if you think your command is of general use, let me know about it.

There is a small possibility that will have to increase the parameter MXCOM by 1 to allow space for it in the command buffers (you will be told this on startup).

Other things

Do not use units 79 and 80 for I/O as they are used when reading command files. Try to adopt the molly style of doing things. e.g. naming of commands, an input command should be of the form lmydata etc.
Tom Marsh, Warwick.