Exemple #1
0
HDSLoc *cupidReinhold( int type, int ndim, int *slbnd, int *subnd, void *ipd,
                     double *ipv, double rms, AstKeyMap *config, int velax,
                     double beamcorr[ 3 ], int *status ){
/*
*+
*  Name:
*     cupidReinhold

*  Purpose:
*     Identify clumps of emission within a 1, 2 or 3 dimensional NDF using
*     the REINHOLD algorithm.

*  Language:
*     Starlink C

*  Synopsis:
*     HDSLoc *cupidReinhold( int type, int ndim, int *slbnd, int *subnd,
*                          void *ipd, double *ipv, double rms,
*                          AstKeyMap *config, int velax,
*                          double beamcorr[ 3 ], int *status )

*  Description:
*     This function identifies clumps within a 1, 2 or 3 dimensional data
*     array using the REINHOLD algorithm, developed by Kim Reinhold at
*     JAC. This algorithm identifies the boundaries between clumps by
*     looking for minima in 1D sections through the data. No a priori clump
*     profile is assumed. In this algorithm, clumps never overlap.

*  Parameters:
*     type
*        An integer identifying the data type of the array values pointed to
*        by "ipd". Must be either CUPID__DOUBLE or CUPID__FLOAT (defined in
*        cupid.h).
*     ndim
*        The number of dimensions in the data array. Must be 2 or 3.
*     slbnd
*        Pointer to an array holding the lower pixel index bound of the
*        data array on each axis.
*     subnd
*        Pointer to an array holding the upper pixel index bound of the
*        data array on each axis.
*     ipd
*        Pointer to the data array. The elements should be stored in
*        Fortran order. The data type of this array is given by "itype".
*     ipv
*        Pointer to the input Variance array, or NULL if there is no Variance
*        array. The elements should be stored in Fortran order. The data
*        type of this array is "double".
*     rms
*        The default value for the global RMS error in the data array.
*     config
*        An AST KeyMap holding tuning parameters for the algorithm.
*     velax
*        The index of the velocity axis in the data array (if any). Only
*        used if "ndim" is 3.
*     beamcorr
*        An array in which is returned the FWHM (in pixels) describing the
*        instrumental smoothing along each pixel axis. The clump widths
*        stored in the output catalogue are reduced to correct for this
*        smoothing.
*     status
*        Pointer to the inherited status value.

*  Returned Value:
*     A locator for a new HDS object which is an array of NDF structures.
*     Each NDF will hold the data values associated with a single clump
*     and will be the smallest possible NDF that completely contains the
*     corresponding clump. Pixels not in the clump will be set bad. The
*     pixel origin is set to the same value as the supplied NDF.

*  Copyright:
*     Copyright (C) 2009 Science & Technology Facilities Council.
*     Copyright (C) 2006 Particle Physics & Astronomy Research Council.
*     All Rights Reserved.

*  Licence:
*     This program is free software; you can redistribute it and/or
*     modify it under the terms of the GNU General Public License as
*     published by the Free Software Foundation; either version 2 of
*     the License, or (at your option) any later version.
*
*     This program is distributed in the hope that it will be
*     useful, but WITHOUT ANY WARRANTY; without even the implied
*     warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
*     PURPOSE. See the GNU General Public License for more details.
*
*     You should have received a copy of the GNU General Public License
*     along with this program; if not, write to the Free Software
*     Foundation, Inc., 51 Franklin Street,Fifth Floor, Boston, MA
*     02110-1301, USA

*  Authors:
*     DSB: David S. Berry
*     TIMJ: Tim Jenness (JAC, Hawaii)
*     {enter_new_authors_here}

*  History:
*     16-JAN-2006 (DSB):
*        Original version.
*     14-JAN-2009 (TIMJ):
*        Use MERS for message filtering.
*     {enter_further_changes_here}

*  Bugs:
*     {note_any_bugs_here}

*-
*/

/* Local Variables: */

   AstKeyMap *rconfig;  /* Configuration parameters for this algorithm */
   HDSLoc *ret;         /* Locator for the returned array of NDFs */
   double *pd;          /* Pointer to next element of data array */
   double *peakvals;    /* Pointer to array holding clump peak values */
   double cathresh;     /* Threshold for second cellular automata */
   double flatslope;    /* Minimum significant slope at edge of a peak */
   double noise;        /* Noise level */
   double pv;           /* Pixel value */
   double thresh;       /* Minimum peak value to be considered */
   float *pf;           /* Pointer to next element of data array */
   int *clbnd;          /* Array holding lower axis bounds of all clumps */
   int *cubnd;          /* Array holding upper axis bounds of all clumps */
   int *igood;          /* Pointer to array holding usable clump indices */
   int *m1;             /* Pointer to mask array */
   int *m2;             /* Pointer to mask array */
   int *m3;             /* Pointer to mask array */
   int *mask2;          /* Pointer to array marking out edge pixels */
   int *mask;           /* Pointer to array marking out edge pixels */
   int *nrem;           /* Pointer to array marking out edge pixels */
   int *pa;             /* Pointer to next element in the pixel assignment array */
   int caiter;          /* The number of CA iterations to perform */
   int dims[3];         /* Pointer to array of array dimensions */
   int el;              /* Number of elements in array */
   int fixiter;         /* The number of CA iterations to perform */
   int i;               /* Loop count */
   int ii;              /* Loop count */
   int ix;              /* Grid index on 1st axis */
   int iy;              /* Grid index on 2nd axis */
   int iz;              /* Grid index on 3rd axis */
   int j;               /* Loop count */
   int maxid;           /* Largest id for any peak (smallest is zero) */
   int minlen;          /* Minimum size of a clump in pixels along one dimension*/
   int minpix;          /* Minimum total size of a clump in pixels */
   int more;            /* Continue looping?*/
   int ngood;           /* Number of good clumps */
   int nsmall;          /* Number of clumps that are too small */
   int nthin;           /* Number of clumps that span only a single pixel */
   int peakval;         /* Minimum value used to flag peaks */
   int skip[3];         /* Pointer to array of axis skips */

/* Initialise */
   ret = NULL;

/* Abort if an error has already occurred. */
   if( *status != SAI__OK ) return ret;

/* Say which method is being used. */
   msgBlankif( MSG__NORM, status );
   msgOutif( MSG__NORM, "", "Reinhold:", status );

/* Get the AST KeyMap holding the configuration parameters for this
   algorithm. */
   if( !astMapGet0A( config, "REINHOLD", (AstObject *) &rconfig ) ) {
      rconfig = astKeyMap( " " );
      astMapPut0A( config, "REINHOLD", rconfig, " " );
   }

/* The configuration file can optionally omit the algorithm name. In this
   case the "config" KeyMap may contain values which should really be in
   the "rconfig" KeyMap. Add a copy of the "config" KeyMap into "rconfig"
   so that it can be searched for any value which cannot be found in the
   "rconfig" KeyMap. */
   astMapPut0A( rconfig, CUPID__CONFIG, astCopy( config ), NULL );

/* Return the instrumental smoothing FWHMs */
   beamcorr[ 0 ] = cupidConfigD( rconfig, "FWHMBEAM", 2.0, status );
   beamcorr[ 1 ] = beamcorr[ 0 ];
   if( ndim == 3 ) {
      beamcorr[ 2 ] = beamcorr[ 0 ];
      beamcorr[ velax ]= cupidConfigD( rconfig, "VELORES", 2.0, status );
   }

/* Find the size of each dimension of the data array, and the total number
   of elements in the array, and the skip in 1D vector index needed to
   move by pixel along an axis. We use the memory management functions of the
   AST library since they provide greater security and functionality than
   direct use of malloc, etc. */
   el = 1;
   for( i = 0; i < ndim; i++ ) {
      dims[ i ] = subnd[ i ] - slbnd[ i ] + 1;
      el *= dims[ i ];
      skip[ i ] = ( i == 0 ) ? 1 : skip[ i - 1 ]*dims[ i - 1 ];
   }
   for( ; i < 3; i++ ) {
      dims[ i ] = 1;
      skip[ i ] = 0;
   }

/* Get various configuration parameters. */
   rms = cupidConfigD( rconfig, "RMS", rms, status );
   minlen = cupidConfigI( rconfig, "MINLEN", 4, status );
   noise = cupidConfigRMS( rconfig, "NOISE", rms, 2*rms, status );
   thresh = cupidConfigRMS( rconfig, "THRESH", rms, noise + 2*rms, status );
   flatslope = cupidConfigRMS( rconfig, "FLATSLOPE", rms, rms, status );
   cathresh = pow( 3, ndim ) - 1.0;
   cathresh = cupidConfigI( rconfig, "CATHRESH", (int) cathresh, status );
   caiter = cupidConfigI( rconfig, "CAITERATIONS", 1, status );
   fixiter = cupidConfigI( rconfig, "FIXCLUMPSITERATIONS", 1, status );

/* Get the minimum allowed number of pixels in a clump. */
   minpix = cupidDefMinPix( ndim, beamcorr, noise, thresh, status );
   minpix = cupidConfigI( rconfig, "MINPIX", minpix, status );

/* Convert CATHRESH from a number of pixels to a fraction. */
   cathresh = cathresh/pow( 3, ndim );
   if( cathresh > 0.98 ) cathresh = 0.98;
   if( cathresh < 0 ) cathresh = 0.0;
   cathresh += 0.01;

/* Get a mask which is the same size and shape as the data array and which
   holds CUPID__KEDGE at every pixel thought to be on the edge of a clump.
   This is done by scanning the data cube using sets of parallel lines in
   different directions. Peaks are searched for in each line, and then the
   edges found by following the curve down from each peak until the
   gradient becomes zero or positive, or until the data value drops below a
   threshold value. Pixels which correspond to peaks in the data cube
   are flagged with the value greater than or equal to the returned
   "*peakval" value. All other pixels are set to some other value (which
   will usually be CUPID__KBACK but will be something else at positions of
   peaks which were not peaks in all scan directions). */
   msgOutif( MSG__DEBUG, "", "Finding clump edges...", status );
   mask = cupidRInitEdges( type, ipd, el, ndim, dims, skip, minlen, thresh,
                           noise, rms, flatslope, &peakval, status );

/* Dilate the edge regions using a cellular automata. This creates a new
   mask array in which a pixel is marked as an edge pixel if any of its
   neighbours are marked as edge pixels in the mask array created above. */
   msgOutif( MSG__DEBUG, "", "Dilating clump edges...", status );
   mask2 = cupidRCA( mask, NULL, el, dims, skip, 0.0, peakval, CUPID__KEDGE,
                     CUPID__KBACK, 0, status );

/* Erode the edge regions using a second cellular automata. This over-writes
   the original mask array so that a pixel is marked as an edge pixel if a
   fraction greater than "cathresh" of neighbouring pixels are marked as edge
   pixels in "mask2". We loop doing this "CAiteration" times. */
   if( caiter > 0 ) msgOutif( MSG__DEBUG,"", "Eroding clump edges...", status );
   m1 = mask;
   m2 = mask2;
   for( i = 0; i < caiter; i++ ) {
      m1 = cupidRCA( m2, m1, el, dims, skip, cathresh, peakval, CUPID__KEDGE,
                     CUPID__KBACK, 0, status );
      m3 = m1;
      m1 = m2;
      m2 = m3;
   }

/* Fill the volume around each peak with integer values which indicate
   which peak they are close to. All the pixels around one peak form one
   clump. */
   msgOutif( MSG__DEBUG, "", "Filling clumps...", status );
   maxid = cupidRFillClumps( m2, m1, el, ndim, skip, dims, peakval, status );

/* Abort if no clumps found. */
   if( maxid < 0 ) {
      msgOutif( MSG__NORM, "", "No usable clumps found.", status );
      msgBlankif( MSG__NORM, status );
      goto L10;
   }

/* Smooth the boundaries between the clumps. This cellular automata replaces
   each output pixel by the most commonly occuring value within a 3x3x3
   cube of input pixels centred on the output pixel. Put the smoothed
   results back into the supplied "m1" array. */
   if( fixiter >0 ) msgOutif( MSG__DEBUG, "", "Smoothing clump boundaries...", status );
   for( i = 0; i < fixiter; i++ ) {
      m2 = cupidRCA2( m1, m2, el, dims, skip, status );
      m3 = m2;
      m2 = m1;
      m1 = m3;
   }

/* Allocate an array used to store the number of pixels remaining in each
   clump. */
   nrem = astMalloc( sizeof( int )*( maxid + 1 ) );

/* Allocate an array used to store the peak value in every clump. */
   peakvals = astMalloc( sizeof( double )*( maxid + 1 ) );

/* Determine the bounding box of every clump. First allocate memory to
   hold the bounding boxes. */
   clbnd = astMalloc( sizeof( int )*( maxid + 1 )*3 );
   cubnd = astMalloc( sizeof( int )*( maxid + 1 )*3 );
   igood = astMalloc( sizeof( int )*( maxid + 1 ) );
   if( igood ) {

/* Initialise a list to hold zero for every clump id. These values are
   used to count the number of pixels remaining in each clump. Also
   initialise the peak values to a very negative value. */
      for( i = 0; i <= maxid; i++ ) {
         nrem[ i ] = 0;
         peakvals[ i ] = VAL__MIND;
      }

/* Initialise the bounding boxes. */
      for( i = 0; i < 3*( maxid + 1 ); i++ ) {
         clbnd[ i ] = VAL__MAXI;
         cubnd[ i ] = VAL__MINI;
      }

/* Loop round every pixel in the final pixel assignment array. */
      if( type == CUPID__DOUBLE ) {
         pd = (double *) ipd;
         pf = NULL;
      } else {
         pf = (float *) ipd;
         pd = NULL;
      }
      pa = m1;
      for( iz = 1; iz <= dims[ 2 ]; iz++ ){
         for( iy = 1; iy <= dims[ 1 ]; iy++ ){
            for( ix = 1; ix <= dims[ 0 ]; ix++, pa++ ){

/* Get the data value at this pixel */
               if( type == CUPID__DOUBLE ) {
                  pv = *(pd++);
               } else {
                  pv = (double) *(pf++);
               }

/* Skip pixels which are not in any clump. */
               if( *pa >= 0 ) {

/* Increment the number of pixels in this clump. */
                  ++( nrem[ *pa ] );

/* If this pixel value is larger than the current peak value for this
   clump, record it. */
                  if( pv > (double) peakvals[ *pa ] ) peakvals[ *pa ] = pv;

/* Get the index within the clbnd and cubnd arrays of the current bounds
   on the x axis for this clump. */
                  i = 3*( *pa );

/* Update the bounds for the x axis, then increment to get the index of the y
   axis bounds. */
                  if( ix < clbnd[ i ] ) clbnd[ i ] = ix;
                  if( ix > cubnd[ i ] ) cubnd[ i ] = ix;
                  i++;

/* Update the bounds for the y axis, then increment to get the index of the z
   axis bounds. */
                  if( iy < clbnd[ i ] ) clbnd[ i ] = iy;
                  if( iy > cubnd[ i ] ) cubnd[ i ] = iy;
                  i++;

/* Update the bounds for the z axis. */
                  if( iz < clbnd[ i ] ) clbnd[ i ] = iz;
                  if( iz > cubnd[ i ] ) cubnd[ i ] = iz;
               }
            }
         }
      }

/* Loop round counting the clumps which are too small or too low. Put the
   indices of usable clumps into another array. */
      nsmall = 0;
      ngood = 0;
      nthin = 0;
      for( i = 0; i <= maxid; i++ ) {
         j = 3*i;

         if( nrem[ i ] <= minpix ) {
            nsmall++;

         } else if( clbnd[ j ] == cubnd[ j ] ||
                  ( clbnd[ j + 1 ] == cubnd[ j + 1 ] && ndim > 1 ) ||
                  ( clbnd[ j + 2 ] == cubnd[ j + 2 ] && ndim > 2 ) ) {
            nthin++;

         } else {
            igood[ ngood++ ] = i;
         }
      }

      if( ngood == 0 ) msgOutif( MSG__NORM,"", "No usable clumps found.", status );

      if( nsmall == 1 ){
        msgOutif( MSG__NORM, "", "One clump rejected because it contains too few pixels.", status );
      } else if( nsmall > 1 ) {
        msgSeti( "N", nsmall );
        msgOutif( MSG__NORM, "", "^N clumps rejected because they contain too few pixels.", status );
      }
      if( nthin == 1 ) {
        msgOutif( MSG__NORM, "", "1 clump rejected because it spans only a single "
                  "pixel along one or more axes.", status );

      } else if( nthin > 1 ) {
        msgSeti( "N", nthin );
        msgOutif( MSG__NORM, "", "^N clumps rejected because they spans only a single "
                  "pixel along one or more axes.", status );
      }

/* Sort the clump indices into descending order of peak value. */
      j = ngood;
      more = 1;
      while( more ) {
         j--;
         more = 0;
         for( i = 0; i < j; i++ ) {
            if( peakvals[ igood[ i ] ] < peakvals[ igood[ i + 1 ] ] ) {
               ii = igood[ i + 1 ];
               igood[ i + 1 ] = igood[ i ];
               igood[ i ] = ii;
               more = 1;
            }
         }
      }

/* Loop round creating an NDF describing each usable clump. */
      for( j = 0; j < ngood; j++ ) {
         i = igood[ j ];
         ret = cupidNdfClump( type, ipd, m1, el, ndim, dims, skip, slbnd,
                              i, clbnd + 3*i, cubnd + 3*i, NULL, ret,
                              cupidConfigD( rconfig, "MAXBAD", 0.05, status ),
                              status );
      }

/* Free resources */
      clbnd = astFree( clbnd );
      cubnd = astFree( cubnd );
      igood = astFree( igood );
   }

   peakvals = astFree( peakvals );
   nrem = astFree( nrem );

L10:;

/* Remove the secondary KeyMap added to the KeyMap containing configuration
   parameters for this algorithm. This prevents the values in the secondary
   KeyMap being written out to the CUPID extension when cupidStoreConfig is
   called. */
   astMapRemove( rconfig, CUPID__CONFIG );

/* Free resources */
   rconfig = astAnnul( rconfig );
   mask = astFree( mask );
   mask2 = astFree( mask2 );

/* Return the list of clump NDFs. */
   return ret;

}
HDSLoc *cupidFellWalker( int type, int ndim, int *slbnd, int *subnd, void *ipd,
                         double *ipv, double rms, AstKeyMap *config, int velax,
                         int perspectrum, double beamcorr[ 3 ],
                         int *status ){
/*
*+
*  Name:
*     cupidFellWalker

*  Purpose:
*     Identify clumps of emission within a 1, 2 or 3 dimensional NDF using
*     the FELLWALKER algorithm.

*  Language:
*     Starlink C

*  Synopsis:
*     HDSLoc *cupidFellWalker( int type, int ndim, int *slbnd, int *subnd,
*                              void *ipd, double *ipv, double rms,
*                              AstKeyMap *config, int velax,
*                              int perspectrum, double beamcorr[ 3 ],
*                              int *status )

*  Description:
*     This function identifies clumps within a 1, 2 or 3 dimensional data
*     array using the FELLWALKER algorithm. This algorithm loops over
*     every data pixel above the threshold which has not already been
*     assigned to a clump. For each such pixel, a route to the nearest
*     peak in the data value is found by moving from pixel to pixel along
*     the line of greatest gradient. When this route arrives at a peak, all
*     pixels within some small neighbourhood are checked to see if there
*     is a higher data value. If there is, the route recommences from the
*     highest pixel in the neighbourhood, again moving up the line of
*     greatest gradient. When a peak is reached which is the highest data
*     value within its neighbourhood, a check is made to see if this peak
*     pixel has already been assigned to a clump. If it has, then all
*     pixels which were traversed in following the route to this peak are
*     assigned to the same clump. If the peak has not yet been assigned to a
*     clump, then it, and all the traversed pixels, are assigned to a new
*     clump. If the route commences at "sea level" and starts with a low
*     gradient section (average gradient lower than a given value) then
*     the initial section of the route (up to the point where the gradient
*     exceeds the low gradient limit) is not assigned to the clump, but is
*     instead given a special value which prevents the pixels being re-used
*     as the start point for a new "walk".
*
*     If the high data values in a clump form a plateau with slight
*     undulations, then the above algorithm may create a separate clump
*     for each undulation. This is probably inappropriate, especially if
*     the  dips between the undulations are less than or are comparable to
*     the noise level in the data. This situation can arise for instance
*     if the pixel-to-pixel noise is correlated on a scale equal to or
*     larger than the value of the MaxJump configuration parameter. To
*     avoid this, adjoining clumps are merged together if the dip between
*     them is less than a specified value. Specifically, if two clumps
*     with peak values PEAK1 and PEAK2, where PEAK1 is less than PEAK2,
*     are adjacent to each other, and if the pixels along the interface
*     between the two clumps all have data values which are larger than
*     "PEAK1 - MinDip" (where MinDip is the value of the MinDip
*     configuration parameter), then the two clumps are merged together.

*  Parameters:
*     type
*        An integer identifying the data type of the array values pointed to
*        by "ipd". Must be either CUPID__DOUBLE or CUPID__FLOAT (defined in
*        cupid.h).
*     ndim
*        The number of dimensions in the data array. Must be 2 or 3.
*     slbnd
*        Pointer to an array holding the lower pixel index bound of the
*        data array on each axis.
*     subnd
*        Pointer to an array holding the upper pixel index bound of the
*        data array on each axis.
*     ipd
*        Pointer to the data array. The elements should be stored in
*        Fortran order. The data type of this array is given by "itype".
*     ipv
*        Pointer to the input Variance array, or NULL if there is no Variance
*        array. The elements should be stored in Fortran order. The data
*        type of this array is "double".
*     rms
*        The default value for the global RMS error in the data array.
*     config
*        An AST KeyMap holding tuning parameters for the algorithm.
*     velax
*        The index of the velocity axis in the data array (if any). Only
*        used if "ndim" is 3.
*     perspectrum
*        If non-zero, then each spectrum is processed independently of its
*        neighbours. A clump that extends across several spectra will be
*        split into multiple clumps, each restricted to a single spectrum.
*        Only used if "ndim" is 3.
*     beamcorr
*        An array in which is returned the FWHM (in pixels) describing the
*        instrumental smoothing along each pixel axis. The clump widths
*        stored in the output catalogue are reduced to correct for this
*        smoothing.
*     status
*        Pointer to the inherited status value.

*  Returned Value:
*     A locator for a new HDS object which is an array of NDF structures.
*     Each NDF will hold the data values associated with a single clump
*     and will be the smallest possible NDF that completely contains the
*     corresponding clump. Pixels not in the clump will be set bad. The
*     pixel origin is set to the same value as the supplied NDF.

*  Copyright:
*     Copyright (C) 2006 Particle Physics & Astronomy Research Council.
*     Copyright (C) 2007, 2009 Science & Technology Facilities Council.
*     All Rights Reserved.

*  Licence:
*     This program is free software; you can redistribute it and/or
*     modify it under the terms of the GNU General Public License as
*     published by the Free Software Foundation; either version 2 of
*     the License, or (at your option) any later version.
*
*     This program is distributed in the hope that it will be
*     useful, but WITHOUT ANY WARRANTY; without even the implied
*     warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
*     PURPOSE. See the GNU General Public License for more details.
*
*     You should have received a copy of the GNU General Public License
*     along with this program; if not, write to the Free Software
*     Foundation, Inc., 51 Franklin Street,Fifth Floor, Boston, MA
*     02110-1301, USA

*  Authors:
*     DSB: David S. Berry
*     TIMJ: Tim Jenness (JAC, Hawaii)
*     {enter_new_authors_here}

*  History:
*     16-JAN-2006 (DSB):
*        Original version.
*     22-JUN-2007 (DSB):
*        Add rejection of clumps that touch the edge of an array.
*     17-SEP-2007 (DSB):
*        Added "perspectrum" parameter.
*     14-JAN-2009 (TIMJ):
*        Use MERS for message filtering.
*     20-NOV-2013 (DSB):
*        Supplied config KeyMap now holds the method parameters directly,
*        rather than holding them in a sub-KeyMap.
*     30-JUN-2014 (DSB):
*        Changed default for MinDip from 3*RMS to 2*RMS, and default for
*        MinHeight from "Noise+MinDip" to "Noise". These values seem to
*        do better at recovering artificial Gaussians.
*     {enter_further_changes_here}

*  Bugs:
*     {note_any_bugs_here}

*-
*/

/* Local Variables: */

   HDSLoc *ret;         /* Locator for the returned array of NDFs */
   double *pd;          /* Pointer to next element of data array */
   double *peakvals;    /* Pointer to array holding clump peak values */
   double mindip;       /* Minimum dip between distinct peaks */
   double minhgt;       /* Min allowed height for a clump peak */
   double noise;        /* Background data value */
   double pv;           /* Pixel value */
   float *pf;           /* Pointer to next element of data array */
   int *clbnd;          /* Array holding lower axis bounds of all clumps */
   int *cubnd;          /* Array holding upper axis bounds of all clumps */
   int *igood;          /* Pointer to array holding usable clump indices */
   int *ipa;            /* Pointer to clump assignment array */
   int *nrem;           /* Pointer to array holding clump populations */
   int *pa;             /* Pointer to next element of the ipa array */
   int allow_edge;      /* Accept clumps that touch the edge of an array? */
   int dims[3];         /* Pointer to array of array dimensions */
   int el;              /* Number of elements in array */
   int i;               /* Loop count */
   int ii;              /* Temp storage */
   int ix;              /* Grid index on 1st axis */
   int iy;              /* Grid index on 2nd axis */
   int iz;              /* Grid index on 3rd axis */
   int j;               /* Loop count */
   int maxid;           /* Largest id for any peak (smallest is zero) */
   int minpix;          /* Minimum total size of a clump in pixels */
   int more;            /* Continue looping? */
   int ngood;           /* Number of good clumps */
   int nlow;            /* Number of clumps with low peaks */
   int nedge;           /* Number of clumps that touch an edge of the array */
   int nthin;           /* Number of clumps that span only a single pixel */
   int nsmall;          /* Number of clumps with too few pixels */
   int skip[3];         /* Pointer to array of axis skips */

/* Initialise */
   ret = NULL;

/* Abort if an error has already occurred. */
   if( *status != SAI__OK ) return ret;

/* Initialise things to avoid compiler warnings. */
   peakvals = NULL;
   nrem = NULL;

/* Say which method is being used. */
   msgBlankif( MSG__NORM, status );
   msgOutif( MSG__NORM, "", "FellWalker:", status );
   msgBlankif( MSG__VERB, status );

/* Return the instrumental smoothing FWHMs */
   if( ! perspectrum ) {
      beamcorr[ 0 ] = cupidConfigD( config, "FWHMBEAM", 2.0, status );
      beamcorr[ 1 ] = beamcorr[ 0 ];
      if( ndim == 3 ) {
         beamcorr[ 2 ] = beamcorr[ 0 ];
         beamcorr[ velax ]= cupidConfigD( config, "VELORES", 2.0, status );
      }
   } else {
      beamcorr[ 0 ] = 0.0;
      beamcorr[ 1 ] = 0.0;
      beamcorr[ 2 ] = 0.0;
      beamcorr[ velax ]= cupidConfigD( config, "VELORES", 2.0, status );
   }

/* Find the size of each dimension of the data array, and the total number
   of elements in the array, and the skip in 1D vector index needed to
   move by pixel along an axis. We use the memory management functions of the
   AST library since they provide greater security and functionality than
   direct use of malloc, etc. */
   el = 1;
   for( i = 0; i < ndim; i++ ) {
      dims[ i ] = subnd[ i ] - slbnd[ i ] + 1;
      el *= dims[ i ];
      skip[ i ] = ( i == 0 ) ? 1 : skip[ i - 1 ]*dims[ i - 1 ];
   }
   for( ; i < 3; i++ ) {
      dims[ i ] = 1;
      skip[ i ] = 0;
   }

/* Assign work array to hold the clump assignments. */
   ipa = astMalloc( sizeof( int )*el );

/* Get the RMS noise level to use. */
   rms = cupidConfigD( config, "RMS", rms, status );

/* Assign every data pixel to a clump and stores the clumps index in the
   corresponding pixel in "ipa". */
   maxid = cupidFWMain( type, ipd, el, ndim, dims, skip, slbnd, rms, config,
                        ipa,
                        ( ndim > 2 && perspectrum ) ? velax + 1 : 0, status );

/* Abort if no clumps found. */
   if( maxid < 0 ) {
      msgOutif( MSG__NORM, "", "No usable clumps found.", status );
      msgBlankif( MSG__NORM, status );
      goto L10;
   }

/* Allocate an array used to store the number of pixels remaining in each
   clump. */
   nrem = astMalloc( sizeof( int )*( maxid + 1 ) );

/* Allocate an array used to store the peak value in every clump. */
   peakvals = astMalloc( sizeof( double )*( maxid + 1 ) );

/* Determine the bounding box of every clump. First allocate memory to
   hold the bounding boxes, etc. */
   clbnd = astMalloc( sizeof( int )*( maxid + 1 )*3 );
   cubnd = astMalloc( sizeof( int )*( maxid + 1 )*3 );
   igood = astMalloc( sizeof( int )*( maxid + 1 ) );
   if( cubnd ) {

/* Get the lowest data value to be considered. */
      noise = cupidConfigRMS( config, "NOISE", rms, 2.0*rms, status );

/* Get the minimum dip between two adjoining peaks necessary for the two
   peaks to be considered distinct. */
      mindip = cupidConfigRMS( config, "MINDIP", rms, 2.0*rms, status );

/* Get the lowest allowed clump peak value. */
      minhgt = cupidConfigRMS( config, "MINHEIGHT", rms, noise, status );

/* See if clumps are allowed to touch an edge of the data array. */
      allow_edge = cupidConfigI( config, "ALLOWEDGE", 1, status );

/* Get the minimum allowed number of pixels in a clump. */
      if( ! perspectrum ) {
         minpix = cupidDefMinPix( ndim, beamcorr, noise, minhgt, status );
      } else {
         minpix = 3;
      }
      minpix = cupidConfigI( config, "MINPIX", minpix, status );

/* Initialise a list to hold zero for every clump id. These values are
   used to count the number of pixels remaining in each clump. Also
   initialise the peak values to a very negative value. */
      for( i = 0; i <= maxid; i++ ) {
         nrem[ i ] = 0;
         peakvals[ i ] = VAL__MIND;
      }

/* Initialise the bounding boxes. */
      for( i = 0; i < 3*( maxid + 1 ); i++ ) {
         clbnd[ i ] = VAL__MAXI;
         cubnd[ i ] = VAL__MINI;
      }

/* Loop round every pixel in the final pixel assignment array. */
      if( type == CUPID__DOUBLE ) {
         pd = (double *) ipd;
         pf = NULL;
      } else {
         pf = (float *) ipd;
         pd = NULL;
      }
      pa = ipa;
      for( iz = 1; iz <= dims[ 2 ]; iz++ ){
         for( iy = 1; iy <= dims[ 1 ]; iy++ ){
            for( ix = 1; ix <= dims[ 0 ]; ix++, pa++ ){

/* Get the data value at this pixel */
               if( type == CUPID__DOUBLE ) {
                  pv = *(pd++);
               } else {
                  pv = (double) *(pf++);
               }

/* Skip pixels which are not in any clump. */
               if( *pa >= 0 ) {

/* Increment the number of pixels in this clump. */
                  ++( nrem[ *pa ] );

/* If this pixel value is larger than the current peak value for this
   clump, record it. */
                  if( pv > (double) peakvals[ *pa ] ) peakvals[ *pa ] = pv;

/* Get the index within the clbnd and cubnd arrays of the current bounds
   on the x axis for this clump. */
                  i = 3*( *pa );

/* Update the bounds for the x axis, then increment to get the index of the y
   axis bounds. */
                  if( ix < clbnd[ i ] ) clbnd[ i ] = ix;
                  if( ix > cubnd[ i ] ) cubnd[ i ] = ix;
                  i++;

/* Update the bounds for the y axis, then increment to get the index of the z
   axis bounds. */
                  if( iy < clbnd[ i ] ) clbnd[ i ] = iy;
                  if( iy > cubnd[ i ] ) cubnd[ i ] = iy;
                  i++;

/* Update the bounds for the z axis. */
                  if( iz < clbnd[ i ] ) clbnd[ i ] = iz;
                  if( iz > cubnd[ i ] ) cubnd[ i ] = iz;
               }
            }
         }
      }

/* Loop round counting the clumps which are too small or too low. Put the
   indices of usable clumps into another array. */
      nsmall = 0;
      nlow = 0;
      ngood = 0;
      nthin = 0;
      nedge = 0;
      for( i = 0; i <= maxid; i++ ) {
         j = 3*i;
         if( nrem[ i ] <= minpix ) {
            nsmall++;

         } else if( peakvals[ i ] < minhgt ) {
            nlow++;

         } else if( ( ndim < 3 || !perspectrum ) &&
                  ( clbnd[ j ] == cubnd[ j ] ||
                  ( clbnd[ j + 1 ] == cubnd[ j + 1 ] && ndim > 1 ) ||
                  ( clbnd[ j + 2 ] == cubnd[ j + 2 ] && ndim > 2 ) ) ) {
            nthin++;

         } else if ( !allow_edge && (
           clbnd[ j     ] < 3 || cubnd[ j     ] > dims[ 0 ] - 1 ||
           ( ( clbnd[ j + 1 ] < 3 || cubnd[ j + 1 ] > dims[ 1 ] - 1 ) && ndim > 1 ) ||
           ( ( clbnd[ j + 2 ] < 3 || cubnd[ j + 2 ] > dims[ 2 ] - 1 ) && ndim > 2 ) ) ){
            nedge++;

         } else {
            igood[ ngood++ ] = i;
         }
      }

      if( ngood == 0 ) msgOutif( MSG__NORM, "", "No usable clumps found.", status );
      if( nsmall == 1 ){
        msgOutif( MSG__NORM, "", "One clump rejected because it contains too few pixels.", status );
      } else if( nsmall > 0 ){
        msgSeti( "N", nsmall );
        msgOutif( MSG__NORM, "", "^N clumps rejected because they contain too few pixels.", status );
      }
      if( nlow == 1 ){
        msgOutif( MSG__NORM, "", "One clump rejected because its peak is too low.", status );
      } else if( nlow > 0 ){
        msgSeti( "N", nlow );
        msgOutif( MSG__NORM, "", "^N clumps rejected because the peaks are too low.", status );
      }
      if( nthin == 1 ) {
        msgOutif( MSG__NORM, "", "1 clump rejected because it spans only a single "
                "pixel along one or more axes.", status );

      } else if( nthin > 1 ) {
        msgSeti( "N", nthin );
        msgOutif( MSG__NORM, "", "^N clumps rejected because they spans only a single "
                "pixel along one or more axes.", status );
      }
      if( nedge == 1 ) {
        msgOutif( MSG__NORM, "", "1 clump rejected because it touches an edge "
                "of the array.", status );

      } else if( nedge > 1 ) {
        msgSeti( "N", nedge );
        msgOutif( MSG__NORM, "", "^N clumps rejected because they touch an edge "
                "of the array.", status );
      }


/* Sort the clump indices into descending order of peak value. */
      j = ngood;
      more = 1;
      while( more ) {
         j--;
         more = 0;
         for( i = 0; i < j; i++ ) {
            if( peakvals[ igood[ i ] ] < peakvals[ igood[ i + 1 ] ] ) {
               ii = igood[ i + 1 ];
               igood[ i + 1 ] = igood[ i ];
               igood[ i ] = ii;
               more = 1;
            }
         }
      }

/* Loop round creating an NDF describing each usable clump. */
      for( j = 0; j < ngood; j++ ) {
         i = igood[ j ];
         ret = cupidNdfClump( type, ipd, ipa, el, ndim, dims, skip, slbnd,
                              i, clbnd + 3*i, cubnd + 3*i, NULL, ret,
                              cupidConfigD( config, "MAXBAD", 0.05, status ),
                              status );
      }

/* Free resources */
      clbnd = astFree( clbnd );
      cubnd = astFree( cubnd );
      igood = astFree( igood );
   }

L10:;

/* Free resources */
   ipa = astFree( ipa );
   nrem = astFree( nrem );
   peakvals = astFree( peakvals );

/* Return the list of clump NDFs. */
   return ret;

}
Exemple #3
0
HDSLoc *cupidGaussClumps( int type, int ndim, int *slbnd, int *subnd, void *ipd,
                          double *ipv, double rms, AstKeyMap *config, int velax,
                          double beamcorr[ 3 ], int *status ){
/*
*+
*  Name:
*     cupidGaussClumps

*  Purpose:
*     Identify clumps of emission within a 1, 2 or 3 dimensional NDF using
*     the GAUSSCLUMPS algorithm.

*  Language:
*     Starlink C

*  Synopsis:
*     HDSLoc *cupidGaussClumps( int type, int ndim, int *slbnd, int *subnd,
*                               void *ipd, double *ipv, double rms,
*                               AstKeyMap *config, int velax,
*                               double beamcorr[ 3 ], int *status )

*  Description:
*     This function identifies clumps within a 1, 2 or 3 dimensional data
*     array using the GAUSSCLUMPS algorithm, described by Stutski & Gusten
*     (1990, ApJ 356, 513). This algorithm proceeds by fitting a Gaussian
*     profile to the brightest peak in the data. It then subtracts the fit
*     from the data and iterates, fitting a new ellipse to the brightest peak
*     in the residuals. This continues until a termination criterion is
*     reached. The main termination criterion in this implementation is
*     not quite the same as in the Stutski & Gusten paper. They had two main
*     termination criteria; 1) the total data sum of the fitted gaussians
*     is close to the total data sum of the original data, and 2) the peak
*     residual is less than a given multiple of the RMS noise in the data.
*     However, 1) is very sensitive to errors in the estimation of the
*     background level in the data, and 2) may never be achieved because
*     the expected residuals depend not only on the RMS noise in the data
*     but also on how accurately gaussian the clumps are, which is not
*     known. Therefore, this implementation instead terminates when the
*     peak amplitude of the fitted clumps falls below a given fraction of
*     the first (i.e. largest) fitted peak.
*
*     Two additional termination criteria are used; 1) If there are many
*     failed attempts to fit a clump to the peak residual or if 2) a
*     specified maximum number of clumps are found, then the process
*     terminates early.

*  Parameters:
*     type
*        An integer identifying the data type of the array values pointed to
*        by "ipd". Must be either CUPID__DOUBLE or CUPID__FLOAT (defined in
*        cupid.h).
*     ndim
*        The number of dimensions in the data array. Must be 1, 2 or 3.
*     slbnd
*        Pointer to an array holding the lower pixel index bound of the
*        data array on each axis.
*     subnd
*        Pointer to an array holding the upper pixel index bound of the
*        data array on each axis.
*     ipd
*        Pointer to the data array. The elements should be stored in
*        Fortran order. The data type of this array is given by "itype".
*     ipv
*        Pointer to the input Variance array, or NULL if there is no Variance
*        array. The elements should be stored in Fortran order. The data
*        type of this array is "double".
*     rms
*        The default value for the global RMS error in the data array.
*     config
*        An AST KeyMap holding tuning parameters for the algorithm.
*     velax
*        The index of the velocity axis in the data array (if any). Only
*        used if "ndim" is 3.
*     beamcorr
*        An array in which is returned the FWHM (in pixels) describing the
*        instrumental smoothing along each pixel axis. The clump widths
*        stored in the output catalogue are reduced to correct for this
*        smoothing.
*     status
*        Pointer to the inherited status value.

*  Notes:
*     - The specific form of algorithm used here is informed by a Fortran
*     implementation of GaussClumps obtained on 27/9/05 from
*     ftp.astro.uni-bonn.de/pub/heith/gaussclumps.
*     - Most of the "cupid..." functions used in this file which start
*     with a "type" parameter (e.g. cupidFindMax, cupidUpdateArrays, etc) are
*     actually not functions at all, but macros defined in cupid.h. These
*     macros are wrappers which invoke a type-specific function (e.g.
*     cupidFindMaxD, cupidFindMaxF) appropriate to the specific data type
*     being used (as indicated by the "type" parameter). Macros are used in
*     order to simplify the code here, and thus make the flow of the
*     algorithm clearer. The source code for the type-specific functions
*     are generated automatically at build time from equivalent files which
*     have file type ".cupid". For instance, the files cupidfindmaxD.c and
*     cupidfindmaxF.c are generated automatically from cupidfindmax.cupid.
*     Also, the rlevant macros definitions and prototypes within cupid.h
*     are generated automatically at build time from these ".cupid" files.

*  Returned Value:
*     A locator for a new HDS object which is an array of NDF structures.
*     Each NDF will hold the data values associated with a single clump and
*     will be the smallest possible NDF that completely contains the
*     corresponding clump. Pixels not in the clump will be set bad. The
*     pixel origin is set to the same value as the supplied NDF.

*  Copyright:
*     Copyright (C) 2009 Science & Technology Facilities Council.
*     Copyright (C) 2005 Particle Physics & Astronomy Research Council.
*     All Rights Reserved.

*  Licence:
*     This program is free software; you can redistribute it and/or
*     modify it under the terms of the GNU General Public License as
*     published by the Free Software Foundation; either version 2 of
*     the License, or (at your option) any later version.
*
*     This program is distributed in the hope that it will be
*     useful, but WITHOUT ANY WARRANTY; without even the implied
*     warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
*     PURPOSE. See the GNU General Public License for more details.
*
*     You should have received a copy of the GNU General Public License
*     along with this program; if not, write to the Free Software
*     Foundation, Inc., 51 Franklin Street,Fifth Floor, Boston, MA
*     02110-1301, USA

*  Authors:
*     DSB: David S. Berry
*     TIMJ: Tim Jenness (JAC, Hawaii)
*     {enter_new_authors_here}

*  History:
*     29-SEP-2005 (DSB):
*        Original version.
*     7-MAR-2007 (DSB):
*        Use VELORES instead of FWHMBEAM if the data is 1D.
*     7-MAR-2007 (DSB):
*        Guard against segvio caused by use of null pointers that are
*        returned by astMalloc if an error has occurred.
*     14-JAN-2009 (TIMJ):
*        Use MERS for message filtering.
*     {enter_further_changes_here}

*  Bugs:
*     {note_any_bugs_here}

*-
*/

/* Local Variables: */
   AstKeyMap *gcconfig; /* Configuration parameters for this algorithm */
   char buf[30];        /* File name buffer */
   HDSLoc *ret;         /* Locator for the returned array of NDFs */
   double *peaks;       /* Holds the "npeak" most recently fitted peak values */
   double chisq;        /* Chi-squared value of most recently fitted Gaussian */
   double mean_peak;    /* The mean of the values within "peaks" */
   double mlim;         /* Truncation level for Gaussians */
   double new_peak;     /* The value most recently added to "peaks" */
   double nsig;         /* No.of standard deviations at which to reject peaks */
   double old_peak;     /* The oldest value within "peaks" */
   double peak_thresh;  /* The lower threshold for clump peak values */
   double sigma_peak;   /* The standard deviation of the values within "peaks" */
   double sumdata;      /* Sum of the supplied data values */
   double sum_peak2;    /* Sum of the squares of the values in "peaks" */
   double sum_peak;     /* Sum of the values in "peaks" */
   double sumclumps;    /* Sum of the values in all the used clumps so far */
   double x[ CUPID__GCNP3 ]; /* Parameters describing new Gaussian clump */
   int *dims;           /* Pointer to array of array dimensions */
   int allbad;          /* Are all the residuals bad? */
   int area;            /* Number of pixels contributing to the clump */
   int area_thresh;     /* The lower threshold for clump areas */
   int excols;          /* Are extra output columns required? */
   int el;              /* Number of elements in array */
   size_t i;            /* Loop count */
   size_t iclump;       /* Number of clumps found so far */
   int imax;            /* Index of element with largest residual */
   size_t ipeak;        /* Index within "peaks" at which to store the new peak */
   int iter;            /* Continue finding more clumps? */
   double maxbad;       /* Max fraction of bad pixels allowed in a clump */
   size_t maxclump;     /* Max no. of clumps */
   int maxskip;         /* Max no. of failed fits between good fits */
   size_t nclump;       /* Number of usable clumps */
   int niter;           /* Iterations performed so far */
   int npad;            /* No. of peaks below threshold for temination */
   size_t npeak;        /* The number of elements in the "peaks" array. */
   int nskip;           /* No. of failed fits since last good fit */
   int area_below;      /* Count of consecutive clump areas below the threshold */
   int peaks_below;     /* Count of consecutive peaks below the threshold */
   void *res;           /* Pointer to residuals array */

/* Initialise */
   ret = NULL;

/* Abort if an error has already occurred. */
   if( *status != SAI__OK ) return ret;

/* Initialise things to avoid compiler warnings. */
   mean_peak = 0.0;
   sigma_peak = 0.0;
   new_peak = 0.0;

/* Say which method is being used. */
   msgBlankif( MSG__NORM, status );
   msgOutif( MSG__NORM,  "", "GaussClumps:", status );
   msgBlankif( MSG__VERB, status );

/* Get the AST KeyMap holding the configuration parameters for this
   algorithm. */
   if( !astMapGet0A( config, "GAUSSCLUMPS", (AstObject *) &gcconfig ) ) {
      gcconfig = astKeyMap( " " );
      astMapPut0A( config, "GAUSSCLUMPS", gcconfig, " " );
   }

/* The configuration file can optionally omit the algorithm name. In this
   case the "config" KeyMap may contain values which should really be in
   the "gcconfig" KeyMap. Add a copy of the "config" KeyMap into "gcconfig"
   so that it can be searched for any value which cannot be found in the
   "gcconfig" KeyMap. */
   astMapPut0A( gcconfig, CUPID__CONFIG, astCopy( config ), NULL );

/* Return the instrumental smoothing FWHMs. For 1D data, we assume the
   axis is spectral and so use VELORES instead of FWHMBEAM.  */
   if( ndim == 1 ) {
      beamcorr[ 0 ] = cupidConfigD( gcconfig, "VELORES", 2.0, status );
   } else {
      beamcorr[ 0 ]= cupidConfigD( gcconfig, "FWHMBEAM", 2.0, status );
      beamcorr[ 1 ] = beamcorr[ 0 ];
      if( ndim == 3 ) {
         beamcorr[ 2 ] = beamcorr[ 0 ];
         beamcorr[ velax ]= cupidConfigD( gcconfig, "VELORES", 2.0, status );
      }
   }

/* See if extra diagnostic info is required. */
   excols = cupidConfigI( gcconfig, "EXTRACOLS", 0, status );

/* Get the maximum allowed number of failed fits between succesful fits. */
   maxskip = cupidConfigI( gcconfig, "MAXSKIP", 10, status );

/* Get the maximum allowed number of failed fits between succesful fits. */
   maxclump = cupidConfigI( gcconfig, "MAXCLUMPS", VAL__MAXI, status );

/* The iterative process ends when "npad" consecutive clumps all had peak
   values below "peak_thresh" or all had areas below "area_thresh". */
   npad = cupidConfigI( gcconfig, "NPAD", 10, status );

/* Get the RMS noise level to use. */
   rms = cupidConfigD( gcconfig, "RMS", rms, status );

/* Find the size of each dimension of the data array, and the total number
   of elements in the array. We use the memory management functions of the
   AST library since they provide greater security and functionality than
   direct use of malloc, etc. */
   dims = astMalloc( sizeof( *dims )*(size_t) ndim );
   el = 1;
   if( dims ) {
     for( i = 0; i < (size_t)ndim; i++ ) {
         dims[ i ] = subnd[ i ] - slbnd[ i ] + 1;
         el *= dims[ i ];
      }
   }

/* Copy the supplied data array into a work array which will hold the
   residuals remaining after subtraction of the fitted Gaussians. The
   cupidStore macro is a wrapper around the astStore function. */
   res = cupidStore( NULL, ipd, el, type, "cupidGaussClumps" );
   if( res ) {

/* Set the lower threshold for clump peaks to a user-specified multiple
   of the RMS noise. */
      peak_thresh = cupidConfigD( gcconfig, "THRESH", 2.0, status );

/* Set the lower threshold for clump area to a user-specified number of
   pixels. */
      area_thresh = cupidConfigI( gcconfig, "MINPIX", 3, status );

/* Get the lowest value (normalised to the RMS noise level) at which
   model Gaussians should be evaluated. */
      mlim = cupidConfigD( gcconfig, "MODELLIM", 0.5, status );

/* Get the max allowed fraction of bad pixels in a clump. */
      maxbad = cupidConfigD( gcconfig, "MAXBAD", 0.05, status );

/* Initialise the number of clumps found so far. */
      iclump = 0;

/* Indicate that no peaks have been found below the lower threshold for clump
   peak values, or below the lower area threshold. */
      peaks_below = 0;
      area_below = 0;

/* Initialise the variables used to keep track of the mean and standard
   deviation of the most recent "npeak" fitted peak values. */
      nsig = cupidConfigD( gcconfig, "NSIGMA", 3.0, status );
      npeak = cupidConfigI( gcconfig, "NPEAK", 9, status );
      ipeak = 0;
      sum_peak = 0.0;
      sum_peak2 = 0.0;
      iter = 1;
      niter = 0;
      nskip = 0;
      sumclumps = 0.0;
      sumdata = VAL__BADD;
      peaks = astMalloc( sizeof( *peaks )*npeak );
      if( peaks ) {
         for( i = 0; i < npeak; i++ ) peaks[ i ] = 0.0;


/* Use the setjmp function to define here to be the place to which the
   signal handling function will jump when a signal is detected. Zero is
   returned on the first invocation of setjmp. If a signal is detected,
   a jump is made into setjmp which then returns a positive signal
   identifier. */
         if( setjmp( CupidGCHere ) ) {
            iter = 0;
            msgBlankif( MSG__QUIET, status );
            msgOutif( MSG__QUIET, "",
                      "Interupt detected. Clumps found so far will be saved",
                      status );
            msgBlankif( MSG__QUIET, status );
         }
      }

/* Set up a signal handler for the SIGINT (interupt) signal. If this
   signal occurs, the function "cupidGCHandler" will be called. */
      signal( SIGINT, cupidGCHandler );

/* Loop round fitting a gaussian to the largest remaining peak in the
   residuals array. */
      while( iter && *status == SAI__OK ) {

/* Report the iteration number to the user if required. */
         ++niter;
         msgBlankif( MSG__DEBUG1, status );
         msgOutiff( MSG__DEBUG1, "", "Iteration %d:", status, niter );

/* Find the 1D vector index of the elements with the largest value in the
   residuals array. */
         allbad = cupidGCFindMax( type, res, el, &imax, &sumdata, status );

/* Finish iterating if all the residuals are bad, or if too many iterations
   have been performed since the last succesfully fitted clump. */
         if( allbad ) {
            iter = 0;
            niter--;
            msgBlankif( MSG__DEBUG, status );
            msgOutif( MSG__DEBUG1, "",
                      "There are no good pixels left to be fitted.",
                      status );
            msgBlankif( MSG__DEBUG1, status );
         } else if( nskip > maxskip ){
            iter = 0;
            niter--;
            msgBlankif( MSG__DEBUG, status );
            msgOutiff( MSG__DEBUG1, "",
                       "The previous %d fits were unusable.", status, maxskip );
            msgBlankif( MSG__DEBUG1, status );
         }

/* If not, make an initial guess at the Gaussian clump parameters centred
   on the current peak. */
         if( iter ) {
            cupidGCSetInit( type, res, ipv, ndim, dims, imax, rms, gcconfig,
                            ( niter == 1 ), velax, x, slbnd, status );

/* Find the best fitting parameters, starting from the above initial guess.
   This returns a function value of zero if no fit could be performed. */
            if( cupidGCFit( type, res, imax, x, &chisq, status ) ) {

/* Skip this fit if we have an estimate of the standard deviation of the
   "npeak" most recent clump peak values, and the peak value of the clump
   just fitted is a long way (more than NSIGMA standard deviations) from the
   peak value of the previously fitted clump. Also skip it if the peak
   value is less than the "mlim" value. */
               if( ( npeak == 0 || iclump < npeak ||
                     fabs( x[ 0 ] - new_peak ) < nsig*sigma_peak ) &&
                    x[ 0 ] > mlim ) {

/* Record the new peak value for use with the next peak, and update the
   standard deviation of the "npeak" most recent peaks. These values are
   stored cyclically in the "peaks" array. */
                  if( npeak > 0 ) {
                     new_peak = x[ 0 ];
                     old_peak = peaks[ ipeak ];
                     peaks[ ipeak ] = new_peak;
                     if( ++ipeak == npeak ) ipeak = 0;
                     sum_peak += new_peak - old_peak;
                     sum_peak2 += new_peak*new_peak - old_peak*old_peak;
                     if( sum_peak2 < 0.0 ) sum_peak2 = 0.0;
                     mean_peak = sum_peak/npeak;
                     sigma_peak = sqrt( sum_peak2/npeak - mean_peak*mean_peak );
                  }

/* Increment the number of peaks found. */
                  iclump++;

/* Reset the number of failed fits since the last good fit. */
                  nskip = 0;

/* Remove the model fit (excluding the background) from the residuals
   array. This also creates an NDF containing the data values associated
   with the clump. This NDF is stored in the HDS array of NDFs in the
   returned HDS object. The standard deviation of the new residuals is
   returned. */
                  cupidGCUpdateArrays( type, res, ipd, el, ndim, dims,
                                       x, rms, mlim, imax, peak_thresh, slbnd,
                                       &ret, iclump, excols, mean_peak,
                                       maxbad, &area, &sumclumps, status );

/* Dump the modified residuals if required. */
                  sprintf( buf, "residuals%lu", iclump );
                  cupidGCDump( type, MSG__DEBUG3, res, ndim, dims, buf,
                               status );

/* Display the clump parameters on the screen if required. */
                  cupidGCListClump( iclump, ndim, x, chisq, slbnd,
                                    rms, status );

/* If this clump has a peak value which is below the threshold, increment
   the count of consecutive clumps with peak value below the threshold.
   Otherwise, reset this count to zero. */
                  if( x[ 0 ] < peak_thresh ) {
                     peaks_below++;
                  } else {
                     peaks_below = 0;
                  }

/* If this clump has an area which is below the threshold, increment
   the count of consecutive clumps with area below the threshold.
   Otherwise, reset this count to zero. */
                  if( area < area_thresh ) {
                     area_below++;
                  } else {
                     area_below = 0;
                  }

/* If the maximum number of clumps have now been found, exit.*/
                  if( iclump == maxclump ) {
                     iter = 0;

                     msgBlankif( MSG__DEBUG, status );
                     msgOutiff( MSG__DEBUG1, "",
                                "The specified maximum number of "
                                "clumps (%lu) have been found.", status,
                                maxclump );
                     msgBlankif( MSG__DEBUG1, status );

/* If the integrated data sum in the fitted gaussians exceeds or equals
   the integrated data sum in th einput, exit. */
                  } else if( sumclumps >= sumdata ) {
                     iter = 0;

                     msgBlankif( MSG__DEBUG, status );
                     msgOutiff( MSG__DEBUG1,"",
                                "The total data sum of the fitted "
                                "Gaussians (%g) has reached the total "
                                "data sum in the supplied data (%g).",
                                status, (float)sumclumps, (float)sumdata );
                     msgBlankif( MSG__DEBUG1, status );

/* If the count of consecutive peaks below the threshold has reached
   "Npad", terminate. */
                  } else if( peaks_below == npad ) {
                     iter = 0;

                     msgBlankif( MSG__DEBUG, status );
                     msgOutiff( MSG__DEBUG1, "",
                                "The previous %d clumps all had peak "
                                "values below the threshold.", status,
                                npad );
                     msgBlankif( MSG__DEBUG1, status );

/* If the count of consecutive clumps with area below the threshold has reached
   "Npad", terminate. */
                  } else if( area_below == npad ) {
                     iter = 0;

                     msgBlankif( MSG__DEBUG, status );
                     msgOutiff( MSG__DEBUG1, "",
                                "The previous %d clumps all had areas "
                                "below the threshold.", status, npad );
                     msgBlankif( MSG__DEBUG1, status );
                  }

/* If the peak value fitted is very different from the previous fitted peak
   value, set the residuals array element bad in order to prevent the
   algorithm from trying to fit a peak to the same pixel again. */
               } else {
                  if( type == CUPID__DOUBLE ) {
                     ((double *)res)[ imax ] = VAL__BADD;
                  } else {
                     ((float *)res)[ imax ] = VAL__BADR;
                  }

                  new_peak = 0.5*( new_peak + x[ 0 ] );

                  nskip++;
                  msgOutif( MSG__DEBUG1, "", "   Clump rejected due to "
                            "aberrant peak value.", status );
               }

/* Tell the user if no clump could be fitted around the current peak
   pixel value */
            } else {
               nskip++;
               msgOutif( MSG__DEBUG1, "", "   No clump fitted.", status );

/* Set the specified element of the residuals array bad if no fit was
   performed. This prevents the any subsequent attempt to fit a Gaussian
   to the same peak value.*/
               if( type == CUPID__DOUBLE ) {
                  ((double *)res)[ imax ] = VAL__BADD;
               } else {
                  ((float *)res)[ imax ] = VAL__BADR;
               }
            }

/* Tell the user if one of the trmination criteria has ben met. */
         } else {
           msgOutif( MSG__DEBUG1, "",
                     "   At least one termination criterion has been reached.",
                     status );
           msgBlankif( MSG__DEBUG1, status );
         }
      }

/* Tell the user how clumps are being returned. */
      if( ret ) {
        datSize( ret, &nclump, status );
      } else {
        nclump = 0;
      }

      if( nclump == 0 ) msgOutif( MSG__NORM, "", "No usable clumps found.", status );

      if( iclump - nclump == 1 ) {
        msgOutif( MSG__NORM, "",
                  "1 clump rejected because it touches an edge of "
                  "the data array.", status );
      } else if( iclump - nclump > 1 ) {
        msgOutiff( MSG__NORM, "",
                   "%d clumps rejected because they touch an edge of "
                   "the data array.", status, (int)( iclump - nclump ) );
      }

/* Tell the user how many iterations have been performed (i.e. how many
   attempts there have been to fit a Gaussian peak). */
      if( niter == 1 ){
        msgOutif( MSG__DEBUG1, "", "No fit attempted.", status );
      } else {
        msgOutiff( MSG__DEBUG1, "",
                   "Fits attempted for %d candidate clumps (%d failed).",
                   status, (int)( niter - iclump ), niter );
      }

/* Free resources */
      peaks = astFree( peaks );

   }

/* Remove the secondary KeyMap added to the KeyMap containing configuration
   parameters for this algorithm. This prevents the values in the secondary
   KeyMap being written out to the CUPID extension when cupidStoreConfig is
   called. */
   astMapRemove( gcconfig, CUPID__CONFIG );

/* Free resources */
   res = astFree( res );
   dims = astFree( dims );

   cupidGC.data = astFree( cupidGC.data );
   cupidGC.weight = astFree( cupidGC.weight );
   cupidGC.res = astFree( cupidGC.res );
   cupidGC.resu = astFree( cupidGC.resu );
   cupidGC.initmodel = astFree( cupidGC.initmodel );
   cupidGC.model = astFree( cupidGC.model );
   cupidGC.resids = astFree( cupidGC.resids );

   gcconfig = astAnnul( gcconfig );

/* Return the list of clump NDFs. */
   return ret;

}