7 Extended Routines

 7.1 Scalar Values between Limits
 7.2 Arrays of Values between Limits
 7.3 Parity Constraint
 7.4 Menu Constraint
 7.5 Combined Constraints
 7.6 Logical Value

In addition to the basic routines there are some packaged facilities for common sequences of calls. Most deal with dynamic control of acceptable values when getting the parameter. Some are needed because the in choices and range limits imposed by the ADAM interface module are static, and therefore cannot be adjusted depending on the values of other parameters or data. Note that in the extended routines that follow, all call a PAR_GETnx routine which first checks that each supplied value satisfies these interface-file constraints, and prompts for a new value if it does not. Only after these tests are made will each value be compared with the constraints in the extended routines. Therefore, you are recommended not to use the in and range fields for parameters obtained using the extended routines.

Here is an example of handling your own constraints to illustrate why it is more convenient to use the packaged routines, where applicable, and also to demonstrate how you might write your own extended routine should you need one. It is a little contrived, since the PAR library already provides the most-common combinations.

Here we obtain from parameter NAME an integer that is not exactly divisible by 3, set the dynamic default to 1, and interpret null to mean set the value to 3. See SUN/104 for details of the MSG_ calls.

        LOGICAL NOTOK
        INTEGER VALUE
  
            :       :       :
  
  *  Set the dynamic default.
        CALL PAR_DEF0I( ’NAME’, 1, STATUS )
  
  *  Start a new error context.
        CALL ERR_MARK
  
  *  Loop to obtain the value of the parameter.
  *  ==========================================
  
  *  Initialise NOTOK to start off the loop and indicate that a
  *  satisfactory value has yet to be obtained.
        NOTOK = .TRUE.
  
    100 CONTINUE
  
  *  The loop will keep going as long as a suitable value has not be read
  *  and there is no error.
           IF ( .NOT. NOTOK .OR. ( STATUS .NE. SAI__OK ) ) GOTO 120
  
  *  Get a value from the parameter.
           CALL PAR_GET0I( ’NAME’, VALUE, STATUS )
  
  *  Check for an error before using the value.
           IF ( STATUS .EQ. SAI__OK ) THEN
  
  *  Test the value against the constraints.  Here it is just to see
  *  if the value is divisible by 3.  You can replace this expression
  *  with more complicated constraints.
              NOTOK = MOD( VALUE, 3 ) .EQ. 0
  
  *  The value is not within the constraints, so report as an error,
  *  including full information using tokens.  You would substitute a
  *  routine name for fac_xxxxx.
              IF ( NOTOK ) THEN
                 STATUS = SAI__ERROR
                 CALL MSG_SETC( ’PARAM, ’NAME’ )
                 CALL MSG_SETI( ’VALUE’, VALUE )
  
                 CALL ERR_REP( ’fac_xxxxx_OUTR’,
      :            ’^VALUE is not permitted for ^PARAM.  Please give ’/
      :            /’an integer not exactly divisible by 3.’, STATUS )
  
  *  The error is flushed so the user can see it immediately.
                 CALL ERR_FLUSH( STATUS )
  
  *  Cancel the parameter to enable a retry to get a value satisfying
  *  the constraint.
                 CALL PAR_CANCL( ’NAME’, STATUS )
              END IF
  
  *  Use the default value following an error.
           ELSE
  
  *  Annul a null error to prevent an error report about null appearing.
  *  Create a message informing the user of what has happened.
              IF ( STATUS .EQ. PAR__NULL ) THEN
                 CALL ERR_ANNUL( STATUS )
  
  *  If MSG verbose output is requested, inform the user what has happened.
  *  You would substitute a routine name for fac_xxxxx.
                 CALL MSG_SETC( ’PARAM’, ’NAME’ )
                 CALL MSG_OUTIF( MSG__VERB, ’fac_xxxxx_DEFA’,
      :            ’A value of 3 has been adopted ’/
      :            /’for parameter ^PARAM.’, STATUS )
              END IF
  
  *  Set the returned value to the special case.
              VALUE = 3
  
  *  Terminate the loop.
              NOTOK = .FALSE.
           END IF
  
  *  Go to the head of the loop.
           GOTO 100
  
  *  Come here when the loop has been exited.  This includes when
  *  an error status was returned by the PAR_GET0I routine.
    120 CONTINUE
  
  *  Release the new error context.
        CALL ERR_RLSE

To set another constraint you have to modify the logical expression for variable NOTOK, and revise the ERR_REP error report, possibly with more tokens. Suppose variables VMIN and VMAX define a range of permitted values, which is inclusive when VMAX is the larger of the two and exclusive when VMAX is less than VMIN. The constraint expression could modified to the following.

  *  Check that the value is within the specified include or exclude
  *  range, and not divisible by 3.
        IF ( VMIN .GT. VMAX ) THEN
           NOTOK = ( ( VALUE .LT. VMIN ) .AND. ( VALUE .GT. VMAX ) )
       :           .OR. ( MOD( VALUE, 3 ) .EQ. 0 )
        ELSE
           NOTOK = ( VALUE .LT. VMIN ) .OR. ( VALUE .GT. VMAX ) .OR.
  :                ( MOD( VALUE, 3 ) .NE. 0 )
        END IF

and the error report would look something like this

  *  The value is not within the constraints, so report as an error,
  *  including full information using tokens.
              IF ( NOTOK ) THEN
                 STATUS = PAR__ERROR
                 CALL MSG_SETC( ’PARAM’, ’NAME’ )
                 CALL MSG_SETI( ’VALUE’, VALUE )
                 CALL MSG_SETI( ’MIN’, VMIN )
                 CALL MSG_SETI( ’MAX’, VMAX )
                 IF ( VMIN .GT. VMAX ) THEN
                    CALL MSG_SETC( ’XCLD’, ’outside’ )
                 ELSE
                    CALL MSG_SETC( ’XCLD’, ’in’ )
                 END IF
  
                 CALL ERR_REP( ’fac_xxxxx_OUTR’,
       :           ’^VALUE is not permitted for ^PARAM.  Please give ’/
       :           /’an integer ^XCLD the range ^MIN to ^MAX, and not ’/
       :           /’exactly divisible by 3.’, STATUS )

In practice you would probably define a logical variable outside the loop to indicate whether the range was inclusive or exclusive.6

Having looked ‘behind the scenes’, we can now look at what the PAR_ extended routines offer.

7.1 Scalar Values between Limits

A parameter value may be forced to lie within a range using a call to the generic routine PAR_GDR0x. The name is derived from Get with a Default and Range. For example,

        CALL PAR_GDR0R( ’SCALE’, 1.0, 0.0, 2.0, .FALSE., SCAFAC, STATUS )

gets the value for the real parameter SCALE using a dynamic default of 1.0, stores the value in the variable SCAFAC, and ensures that the value lies in the range 0.0–2.0. If a supplied value is out of this range, PAR_GDR0R reports the acceptable limits and prompts the user for another value. (This applies to all the routines with constraints described in Section 7.) Thus the user would see something like the following.

  SCALE - Scale factor /1.0/ > 3.5
  !! SUBPAR: 3.5 is greater than the MAXIMUM value 2.
  SCALE - Scale factor /1.0/ >

The fifth argument (NULL) will normally be false. When it is true, it instructs the PAR routine to return the dynamic-default value whenever a null value is supplied, and then to annul the error status. If the MSG filtering level (see SUN/104) is set to ‘verbose’, an informational message will be output.

In the above case if NULL were made .TRUE., and the user entered the null symbol whilst in ‘verbose’ mode, the dialogue would be as follows:

  SCALE - Scale factor /1.0/ > !
  !! A value of 1 has been adopted for parameter SCALE.

NULL should only be set true when the dynamic default will always give reasonable behaviour in the application.

For all but the last example in Section 7, the null flag is set to .FALSE., and will not be mentioned again until then.

Swapping the limits lets you exclude values in the range. Therefore,

        CALL PAR_GDR0R( ’SCALE’, 1.0, 2.0, 0.0, .FALSE., SCAFAC, STATUS )

would permit a value of 3.5, unlike before, as well as 0.0 and 2.0. However, 1.5 would not now be acceptable.

  SCALE - Scale factor > 1.5
  !! SUBPAR: 1.5 is in the excluded MIN/MAX range between 0 and 2.
  SCALE - Scale factor >

Notice that the suggested default has disappeared.7 This occurred because 1.0 violates the range constraint, and it is a feature of the PAR_ extended routines. If you do not want a dynamic default, set the dynamic-default argument to a value that violates the constraints. The range-exclusion feature is present in all the PAR_ extended routines that have a range constraint.

Not surprisingly, there are only versions of the range-constraint routines for integer, real, or double-precision parameters. These have the usual I, R, and D suffices respectively. The range limits have the same data type as the value.

7.2 Arrays of Values between Limits

There are similar generic routines for obtaining an array of values between limits. The first permits up to a given number of values to be obtained. We modify the example from the previous section to illustrate this routine.

        REAL SCAFAC( 10 )
  
            :       :       :
  
        CALL PAR_GDRVR( ’SCALE’, 10, 0.0, 2.0, .FALSE., SCAFAC,
       :                NVAL, STATUS )

This time up to ten values may be obtained from parameter SCALE, and stored in the array SCAFAC. All the values must lie in the range 0.0–2.0. Since the number of values is not fixed, there is no dynamic-default argument. PAR_DEF1x can be called prior to PAR_GDRVx, if desired.

If you want an exact number of values, there is a routine to do this for you. For example, suppose that you want red, green, and blue intensities to define a colour, then the following code would obtain exactly three normalised intensities – one for each colour – between zero and one.

  *  Get an RGB colour.  The default is yellow.
        DEFAUL( 1 ) = 1.0
        DEFAUL( 2 ) = 1.0
        DEFAUL( 3 ) = 0.0
        CALL PAR_GDR1R( ’RGB’, 3, DEFAUL, 0.0, 1.0, .FALSE., RGB, STATUS )

The default must be an array. Should you not want a dynamic default in this case, just set the first element of DEFAUL to be negative or greater than one.

From the user’s perspective, instructions will be given if additional values are required.

  RGB - Red, green, and blue intensities /[1.0,1.0,0.0]/ > 1.0,0,0,0.5
  !! SUBPAR: No more than 3 elements are allowed for parameter RGB.
  RGB - Red, green, and blue intensities /[1.0,1.0,0.0]/ > 1.0,0.0
  !! 1 more value is still needed.
  RGB - Red, green, and blue intensities /[1.0,1.0,0.0]/ > 1.0,0.0,0.0
  !! SUBPAR: No more than 1 element is allowed for parameter RGB.
  RGB - Red, green, and blue intensities /[1.0,1.0,0.0]/ > 0.0

First the user gives too many values, and is told how many to give. Next too few are supplied, so PAR_GDR1R asks for another. Again the user is not paying attention and gives the whole RGB value instead of just the outstanding blue intensity. Finally, the user gives the last value, yielding an RGB of 1.0,0.0,0.0 or the colour red.

Another variation of the theme is when you want to have an exact number of values, and each value is constrained to its own range. For instance, suppose that you wanted to obtain the two-dimensional co-ordinates of a point within a rectangle, whose bounds are known. The following code could obtain the required values, where LBND and UBND define the lower and upper co-ordinates of the rectangle.

        DOUBLE PRECISION DEFAUL( 2 ), POS( 2 )
        DOUBLE PRECISION LBND( 2 ), UBND( 2 )
  
            :       :       :
  
  *  Set the limits of the area in which the point must lie.
        LBND( 1 ) = 0.0D0
        UBND( 1 ) = 50.0D0
        LBND( 2 ) = -20.0D0
        UBND( 2 ) = 10.0D0
  
  *  Get the co-ordinates of the point within the rectangle.  There
  *  is no dynamic default because the default violates the range
  *  constraint.
        DEFAUL( 1 ) = -1.0D0
        CALL PAR_GRM1D( ’POSITION’, 2, DEFAUL, LBND, UBND,
       :                .FALSE., POS, STATUS )

PAR_GRM1x will inform the user of any value(s) that violate a range, and prompts for new values. The name is derived from Get with Ranges for Multiple dimensions.8

If you want to constrain each value of an array to its own range, but do not require an exact number of values, there is even a PAR routine to do that. PAR_GRMVx returns up to some maximum number of values of values, each within a defined range. In the following example an application needs integer compression factors (COMPRS) along each of NDIM dimensions. These must be positive and no greater than the size of the array along each dimension, and are set by the CMPMIN and CMPMAX arrays. ACTVAL returns the actual number values obtained from parameter COMPRESS, and in this example, it is used to set no compression for the higher dimensions.

  *  Set the acceptable range of values from no compression to compress
  *  to a single element in a dimension.
        DO I = 1, NDIM
           CMPMIN( I ) = 1
           CMPMAX( I ) = DIMS( I )
        END DO
  
  *  Get the compression factors.
        CALL PAR_GRMVI( ’COMPRESS’, NDIM, CMPMIN, CMPMAX, COMPRS, ACTVAL,
       :                STATUS )
  
  *  Should less values be supplied than the number of dimensions, do
  *  not compress the higher dimensions.
        IF ( ACTVAL .LT. NDIM ) THEN
           DO I = ACTVAL + 1, NDIM
              COMPRS( I ) = 1
           END DO
        END IF

7.3 Parity Constraint

There are occasions when you need an odd or even integer. For example, the size of a smoothing kernel must be odd. PAR provides two routines with these functions. Thus,

        CALL PAR_GODD( ’BOX’, 5, 3, 11, .FALSE., BOXSIZ, STATUS )

obtains an odd integer between 3 and 11 from parameter BOX, and stores it in variable BOXSIZ. The dynamic default is 5. Similarly,

        CALL PAR_GEVEN( ’OFFSET’, 2, -2, 10, .FALSE., OFFS, STATUS )

gets a even value in the range 2 to 10 from parameter OFFSET, with a dynamic default of 2. Zero counts as an even number, and so it would be acceptable here.

7.4 Menu Constraint

A common requirement is to select an option from a menu. PAR_CHOIC offers this functionality. You provide a list of options separated by commas. When the user selects a choice not in this menu, an error report appears that lists the available options, and the user is prompted. 9 PAR_CHOIC permits the user of your application to use an unambiguous abbreviation. However, the unshortened value is returned. The value is also in uppercase, though the list of options need not be so.

        CHARACTER * 10 FUNCT, OPTDEF
        CHARACTER * 72 OPLIST
  
            :       :       :
  
        OPLIST = ’Exit,Device,Histogram,List,Peep,’/
       :         /’Region,Save,Slice,Statistics,Value’
        OPTDEF = ’Region’
        CALL PAR_CHOIC( ’OPTION’, OPTDEF, OPLIST, .FALSE., FUNCT, STATUS )

So in the above example there are ten options available for parameter OPTION. The dynamic default is ’Region’. If you assign the second argument with a value not in the main list, such as a blank string, it instructs PAR_CHOIC not to set a dynamic default. Note that PAR_CHOIC only returns character values.

This is what the user might see for the above example.

  OPTION - Inspection option /’Region’/ > View
  !! The choice View is not in the menu.  The options are
  !     Exit,Device,Histogram,List,Peep,Region,Save,Slice,Statistics,Value.
  !! Invalid selection for parameter OPTION.
  OPTION - Inspection option /’Region’/ > S
  !! The choice S is ambiguous.  The options are
  !     Exit,Device,Histogram,List,Peep,Region,Save,Slice,Statistics,Value.
  !! Invalid selection for parameter OPTION.
  OPTION - Inspection option /’Region’/ > lust
  Selected the nearest match "LIST" for parameter OPTION.

The first value is unacceptable as it is not in the menu. The second is ambiguous because there are several options beginning with an ess. Had the user entered sl say, the abbreviation would have selected a value of ’SLICE’. The final value appears not to be in the list of choices, but PAR_CHOIC allows the user one typing mistake, and so the user actually selects option ’LIST’. In this case, a warning message is output unless the MSG filtering level (see SUN/104) is set to ‘quiet’.

There is a similar routine – PAR_CHOIV – to get a vector of character values from a menu.

7.5 Combined Constraints

Should you wish to combine the constraints of a numeric value within a range, and a list of options, there are PAR routines to do it for you. Here is an illustration to show how this might be profitably used. The application from which the following extract is taken wants to replace certain array values with a constant; this can either be a numeric value or set to the bad-pixel value.

        CHARACTER * ( VAL__SZR ) CNEWLO, THLDEF
  
            :       :       :
  
  *  Get the replacement value.
        THLDEF = ’0.0’
        CALL PAR_MIX0R( ’NEWLO’, THLDEF, VAL__MINR, VAL__MAXR,
       :                ’Bad’, .FALSE., CNEWLO, STATUS )
  
        IF ( STATUS .EQ. SAI__OK ) THEN
  
  *  It may be the bad-pixel value.
           IF ( CNEWLO .EQ. ’BAD’ ) THEN
              NEWLO = VAL__BADR
           ELSE
  
  *  Convert the output numeric string to its numeric value.
              CALL CHR_CTOR( CNEWLO, NEWLO, STATUS )
           END IF
        END IF

This obtains from parameter NEWLO a value which is either ’BAD’ or a real value. The value is returned in variable CNEWLO. VAL__MINR and VAL__MAXR are the minimum and maximum values, and VAL__BADR is the bad value, for the real data type, and VAL__SZR is the maximum number of characters needed to store a real value; all symbolic constants are defined in the PRM_PAR include file ( SUN/39). The dynamic default is ’0.0’. Remember that the dynamic default and returned value are strings. Therefore, you must first test the returned value for being any of the items on the menu, before converting it to a number. Since the returned value may be undefined following an error, it is prudent to check the status before using the value.

Should the user give an unacceptable value, PAR_MIX0x informs the user of the error and lists the available options, and then invites the user to supply another value.

Here is a more subtle example. This only has numbers in the menu, but it is advantageous when only certain numeric values are acceptable.

  *  Get the plate number.
        CALL PAR_MIX0I( ’PLATE’, ’ ’, 101, 1500, ’5,11,23,47,49’,
       :                .FALSE., CPLATE, STATUS )
        IF ( STATUS .EQ. SAI__OK )
       :  CALL CHR_CTOI( CPLATE, PLATNO, STATUS )

Here PAR_MIX0I obtains an ‘integer’ that is either 5, 11, 23, 47, 49, or in the range 101–1500, and returns it in the character variable CPLATE. CHR_CTOI converts the string into a true integer value, PLATNO. Since the second argument of PAR_MIX0I is a blank string, there is no dynamic default. To produce a suggested default, the second argument would have to be one of the menu options, or satisfy the range constraint (when converted to an integer). If we had swapped the range limits, PAR_MIX0I would allow all values not in the range 102–1499.

7.6 Logical Value

PAR_GTD0L obtains a scalar logical value, with a dynamic default defined, and has the capability of handling a null status. Thus the following obtains a value from the parameter SWITCH and stores it in the variable called POWER. The dynamic default is .TRUE..

        NULL = .FALSE.
        CALL PAR_GTD0L( ’SWITCH’, .TRUE., NULL, POWER, STATUS )

The third argument is the same as we met in Section 7.1. If we reverse the polarity of NULL, PAR_GTD0L will assign the dynamic default to POWER whenever the parameter is in the null state, and returns with STATUS set to SAI__OK. To reiterate NULL=.TRUE. should only be used when the dynamic default will always give reasonable behaviour in the application. This is highly likely for a logical value.

6In the ADAM implementation users of your application may wish to take advantage of the MAX/MIN facility. If this is so you will need to add calls to PAR_MAXI and PAR_MINI in the previous example.

7This assumes that the ppath in the ADAM interface file starts with ’DYNAMIC’.

8In retrospect the name probably should have been PAR_GDM1x for Get with Defaults and Multiple ranges, but the existing name is already in use in applications.

9Remember that in the ADAM implementation the PAR_GET0C call made by PAR_CHOIC will first test the obtained value against the range or in fields in the interface file. If the supplied value is unacceptable, the user will be prompted by PAR_GET0C. Only once these constraints are passed will the value be tested against the menu.