Esempio n. 1
0
void smf_rebincube_ast( ThrWorkForce *wf, smfData *data, int first, int last,
                      int *ptime, dim_t nchan, dim_t ndet, dim_t nslice,
                      dim_t nel, dim_t nxy, dim_t nout, dim_t dim[3],
                      AstMapping *ssmap, AstSkyFrame *abskyfrm,
                      AstMapping *oskymap, Grp *detgrp, int moving,
                      int usewgt, int spread, const double params[],
                      int genvar, double tfac, double fcon,
                      float *data_array, float *var_array,
                      double *wgt_array, float *texp_array,
                      float *teff_array, int *good_tsys, int *nused,
                      int *status ){

/* Local Variables */
   AstCmpMap *detmap = NULL;   /* Mapping from 1D det. index to 2D i/p "grid" coords */
   AstMapping *dtotmap = NULL; /* 1D det index->o/p GRID Mapping */
   AstMapping *fullmap = NULL; /* WCS->GRID LutMap from input WCS FrameSet */
   AstMapping *lutmap = NULL;  /* Mapping that identifies detectors to be used */
   AstMapping *splut = NULL;   /* Spatial LutMap */
   AstMapping *sslut = NULL;   /* Spectral LutMap */
   AstMapping *totmap = NULL;  /* WCS->GRID Mapping from input WCS FrameSet */
   AstPermMap *pmap;           /* Mapping to rearrange output axes */
   const char *name = NULL;    /* Pointer to current detector name */
   const double *tsys = NULL;  /* Pointer to Tsys value for first detector */
   dim_t iv;                   /* Vector index into output 3D array */
   double *detlut = NULL;      /* Work space for detector mask */
   double blk_bot[ 2*MAXTHREADS + 1 ]; /* First o/p channel no. in each block */
   double con;                 /* Constant value */
   double dtemp;               /* Temporary value */
   double tcon;                /* Variance factor for whole time slice */
   float *detwork = NULL;      /* Work array for detector values */
   float *tdata = NULL;        /* Pointer to start of input time slice data */
   float *varwork = NULL;      /* Work array holding variances for 1 slice/channel */
   float *vp = NULL;           /* Pointer to next "varwork" element */
   float invar;                /* Input variance */
   float rtsys;                /* Tsys value */
   float teff;                 /* Effective integration time */
   float texp;                 /* Total time ( = ton + toff ) */
   int *nexttime;              /* Pointer to next time slice index to use */
   int ast_flags;              /* Basic flags to use with astRebinSeq */
   int blk_size;               /* Number of channels processed by a single thread */
   int found;                  /* Was current detector name found in detgrp? */
   int iblock;                 /* Index of current spectral block */
   dim_t ichan;                /* Index of current channel */
   dim_t idet;                 /* detector index */
   int ignore;                 /* Ignore this time slice? */
   int inperm[ 3 ];            /* Input axis permutation array */
   dim_t itime;                /* Index of current time slice */
   int junk;                   /* Unused parameter */
   int lbnd_in[ 2 ];           /* Lower input bounds on receptor axis */
   int ldim[ 3 ];              /* Output array lower GRID bounds */
   int maxthreads;             /* Max no. of threads to use when re-binning */
   int nblock;                 /* Number of spectral blocks */
   int nthreads;               /* Number of threads to use when re-binning */
   int outperm[ 3 ];           /* Output axis permutation array */
   int timeslice_size;         /* Number of elements in a time slice */
   int ubnd_in[ 2 ];           /* Upper input bounds on receptor axis */
   int uddim[ 1 ];             /* Detector array upper GRID bounds */
   int udim[ 3 ];              /* Output array upper GRID bounds */
   smfHead *hdr = NULL;        /* Pointer to data header for this time slice */

/* Check the inherited status. */
   if( *status != SAI__OK ) return;

/* Store a pointer to the input NDFs smfHead structure. */
   hdr = data->hdr;

/* Fill an array with the lower grid index bounds of the output. */
   ldim[ 0 ] = 1;
   ldim[ 1 ] = 1;
   ldim[ 2 ] = 1;

/* Integer upper grid index bounds of the output. */
   udim[ 0 ] = dim[ 0 ];
   udim[ 1 ] = dim[ 1 ];
   udim[ 2 ] = dim[ 2 ];

/* Integer upper bounds of detector array. */
   uddim[ 0 ] = ndet;

/* Store the size of an input time slice. */
   timeslice_size = nel/nslice;

/* Create a LutMap that holds the output spectral axis GRID value at
   the centre of each input spectral axis pixel. LutMaps are faster to
   evaluate, and so astRebinSeq will go faster. We can use LutMaps without
   loosing accuracy since astRebinSeq only ever transforms the GRID
   values at input pixel centres (i.e. integer GRID values), and so the
   LutMap will always return a tabulated value rather than an
   interpolated value. */
   atlTolut( (AstMapping *) ssmap, 1.0, (double) nchan, 1.0, "LutInterp=1",
              &sslut, status );

/* If this is the first pass through this file, initialise the arrays. */
   if( first ) smf_rebincube_init( 0, nxy, nout, genvar, data_array, var_array,
                                   wgt_array, texp_array, teff_array, &junk, status );

/* Initialisation the flags for astRebinSeq (we do not include flag
   AST__REBININIT because the arrays have been initialised). */
   ast_flags = AST__USEBAD;
   if( usewgt ) ast_flags = ast_flags | AST__VARWGT;

   if( genvar == 1 ) {
      ast_flags = ast_flags | AST__GENVAR;
   } else if( genvar == 2 ) {
      ast_flags = ast_flags | AST__USEVAR;
   }

/* If required, allocate a work array to hold all the input variances for a
   single time slice. */
   if( usewgt || genvar == 2 ) varwork = astMalloc( timeslice_size * sizeof( float ) );

/* Allocate a work array to hold the exposure time for each detector. */
   detwork = astMalloc( ndet * sizeof( float ) );

/* If we are dealing with more than 1 detector, create a LutMap that holds
   the input GRID index of every detector to be included in the output, and
   AST__BAD for every detector that is not to be included in the output cube.
   First allocate the work space for the LUT. */
   if( ndet > 1 ) {
      detlut = astMalloc( ndet*sizeof( double ) );

/* Initialise a string to point to the name of the first detector for which
   data is available */
      name = hdr->detname;

/* Loop round all detectors for which data is available. */
      for( idet = 0; idet < ndet; idet++ ) {

/* Store the input GRID coord of this detector. GRID coords start at 1,
   not 0. */
         detlut[ idet ] = idet + 1.0;

/* If a group of detectors to be used was supplied, search the group for
   the name of the current detector. If not found, set the GRID coord bad.
   This will cause astRebinSeq to ignore data from the detector. */
         if( detgrp ) {
            found = grpIndex( name, detgrp, 1, status );
            if( !found ) detlut[ idet ] = AST__BAD;
         }

/* Move on to the next available detector name. */
         name += strlen( name ) + 1;
      }

/* Create the LutMap. */
      lutmap = (AstMapping *) astLutMap( ndet, detlut, 1.0, 1.0,
                                         "LutInterp=1" );

/* If we only have 1 detector, use a UnitMap instead of a LutMap (lutMaps
   must have 2 or more table entries). */
   } else {
      lutmap = (AstMapping *) astUnitMap( 1, " " );
   }

/* Combine the above LutMap with a 1-input, 2-output PermMap that copies its
   input to create its first output, and assigns a constant value of 1.0 to
   its second output. We need to do this because smf_tslice returns a 2D
   GRID system (even though the second GRID axis is not actually used). */
   inperm[ 0 ] = 1;
   outperm[ 0 ] = 1;
   outperm[ 1 ] = -1;
   con = 1.0;
   detmap = astCmpMap( lutmap, astPermMap( 1, inperm, 2, outperm, &con, " " ),
                       1, " " );

/* Store the bounds of a single time slice grid. */
   lbnd_in[ 0 ] = 1;
   ubnd_in[ 0 ] = nchan;
   lbnd_in[ 1 ] = 1;
   ubnd_in[ 1 ] = ndet;

/* Create a PermMap that can be used to re-order the output axes so that
   channel number is axis 3. */
   outperm[ 0 ] = 2;
   outperm[ 1 ] = 3;
   outperm[ 2 ] = 1;
   inperm[ 0 ] = 3;
   inperm[ 1 ] = 1;
   inperm[ 2 ] = 2;
   pmap = astPermMap( 3, inperm, 3, outperm, NULL, " " );

/* If we are using multiple threads to rebin spectral blocks in parallel,
   calculate the number of channels that are processed by each thread,
   and the number of threads to use. The whole output spectrum is divided
   up into blocks. The number of blocks is two times the number of
   threads, and each thread rebins two adjacent blocks. Alternate blocks
   are re-binned simultanously. First, the odd numbered blocks are re-binned
   (one by each thread). When all odd numbered blocks have been re-binned,
   the even numbered blocks are re-binned. We ensure that the number of
   threads used results in a block size that is larger than the spreading
   width produced by the requested spreading scheme. This means that no
   pair of simultanously executing threads will ever try to write to the
   same channel of the output spectrum. */
   maxthreads = wf ? wf->nworker : 1;
   if( maxthreads > MAXTHREADS ) maxthreads = MAXTHREADS;
   if( maxthreads > 1 ) {

/* Find the largest number of threads into which each output spectrum can
   be split. The limit is imposes by the requirement that each block is
   larger than the pixel spreading produced by the requested spreading
   scheme. */
      nthreads = ( ( dim[ 2 ] + 1 )/2 )/smf_spreadwidth( spread, params,
                                                         status );

/* If the spectral range is less than twice the spreading width, we
   cannot use multiple threads. */
      if( nthreads > 1 ) {

/* Restrict the number of threads to be no more than the number of workers
   available in the work force. */
         if( nthreads > maxthreads ) nthreads = maxthreads;

/* Find the number of output channels in each spectral block. */
         blk_size = ( dim[ 2 ] - 1 )/( 2*nthreads ) + 1;

/* Set up the first output channel number within each block. */
         nblock = 2*nthreads;
         for( iblock = 0; iblock < nblock; iblock++ ) {
            blk_bot[ iblock ] = (double) ( iblock*blk_size + 1 );
         }

/* Add in the first channel number beyond the last block. */
         blk_bot[ nblock ] = blk_bot[ nblock - 1 ] + blk_size;

/* If the output spectrum is too short to guarantee that there are any
   independent blocks of output channels, we process the whole spectrum
   in a single thread. */
      } else {
         nthreads = 1;
         nblock = 1;
         blk_bot[ 0 ] = 1.0;
         blk_bot[ 1 ] = (double) ( dim[ 2 ] + 1 );
      }

/* If multiple threads are not available, we process the whole spectrum
   in a single thread. */
   } else {
      nthreads = 1;
      nblock = 1;
      blk_bot[ 0 ] = 1.0;
      blk_bot[ 1 ] = (double) ( dim[ 2 ] + 1 );
   }

/* Convert the block boundaries from output channel numbers into input
   channel numbers. */
   astTran1( ssmap, nblock + 1, blk_bot, 0, blk_bot );

/* Ensure they are in increasing order, and are not outside the bounds of
   the input array. */
   if( blk_bot[ 0 ] > blk_bot[ 1 ] ) {
      for( iblock = 0; iblock < ( nblock + 1 )/2; iblock++ ) {
         dtemp = blk_bot[ nblock - iblock ];
         blk_bot[ nblock - iblock ] = blk_bot[ iblock ];
         blk_bot[ iblock ] = dtemp;
      }
   }

   for( iblock = 0; iblock <= nblock; iblock++ ) {
      if( blk_bot[ iblock ] < 1 ) {
         blk_bot[ iblock ] = 1.0;
      } else if( blk_bot[ iblock ] > nchan ) {
         blk_bot[ iblock ] = nchan;
      }
   }

/* Initialise a pointer to the next time slice index to be used. */
   nexttime = ptime;

/* Initialise the progress meter. */
   smf_reportprogress( nslice, status );

/* Loop round all time slices in the input NDF. */
   for( itime = 0; itime < nslice && *status == SAI__OK; itime++ ) {

/* If this time slice is not being pasted into the output cube, pass on. */
      if( nexttime ){
         if( *nexttime != (int) itime ) continue;
         nexttime++;
      }

/* Store a pointer to the first input data value in this time slice. */
      tdata = ( (float *) (data->pntr)[ 0 ] ) + itime*timeslice_size;

/* Begin an AST context. Having this context within the time slice loop
   helps keep the number of AST objects in use to a minimum. */
      astBegin;

/* Get a Mapping from the spatial GRID axes in the input the spatial
   GRID axes in the output for the current time slice. Note this has
   to be done first since it stores details of the current time slice
   in the "smfHead" structure inside "data", and this is needed by
   subsequent functions. */
      totmap = smf_rebin_totmap( data, itime, abskyfrm, oskymap, moving,
				 status );
      if( !totmap ) break;

/* Get the effective exposure time, the total exposure time, and the
   Tsys->Variance onversion factor for this time slice. Also get a
   pointer to the start of the Tsys array. */
      tsys = smf_rebincube_tcon( hdr, itime, fcon, &texp, &teff, &tcon,
                                 status );

/* So "totmap" is a 2-input, 2-output Mapping that transforms the input
   spatial GRID coords into output spatial GRID coords. In order to speed
   up astRebinSeq we represent this by a pair of parallel LutMaps. To do
   this (using atlTolut) we need a Mapping which only has 1 input, so we
   preceed "totmap" with "detmap" (which also has the effect of exluding
   data from unrequired detectors). We then combine this Mapping in
   parallel with the spectral LutMap to get a 2-input (channel number,
   detector index) and 3-output (output grid coords) Mapping. We finally
   add a PermMap to re-arrange the output axes so that channel number is
   axis 3 in the output. */
      dtotmap = (AstMapping *) astCmpMap( detmap, totmap, 1, " " );
      if( ndet > 1 ) {
         atlTolut( dtotmap, 1.0, (double) ndet, 1.0, "LutInterp=1", &splut,
                   status );
      } else {
         splut = astClone( dtotmap );
      }

      fullmap = astSimplify( astCmpMap( astCmpMap( sslut, splut, 0, " " ),
                                        pmap, 1, " " ) );

/* If required calculate the variance associated with each value in the
   current time slice. based on the input Tsys values. If they are
   needed, but not available, ignored the time slice. */
      ignore = 0;
      if( varwork ) {
         ignore = 1;
         vp = varwork;
         for( idet = 0; idet < ndet; idet++ ) {
            invar = VAL__BADR;
            rtsys = tsys ? (float) tsys[ idet ] : VAL__BADR;
            if( rtsys <= 0.0 ) rtsys = VAL__BADR;
            if( rtsys != VAL__BADR ) {
               *good_tsys = 1;
               if( tcon != VAL__BADD ) {
                  invar = tcon*rtsys*rtsys;
                  ignore = 0;
               }
            }
            for( ichan = 0; ichan < nchan; ichan++ ) *(vp++) = invar;
         }
      }

/* Unless we are ignoring this time slice, paste it into the 3D output
   cube. The smf_rebincube_seqf function is a wrapper for astRebinSeqF
   that splits the total job up between "nthreads" threads running in
   parallel. */
      if( !ignore ) {
         smf_rebincube_seqf( wf, nthreads, blk_bot, fullmap, 0.0, 2, lbnd_in,
                             ubnd_in, tdata, varwork, spread, params,
                             ast_flags, 0.0, 50, VAL__BADR, 3, ldim, udim,
                             lbnd_in, ubnd_in, data_array, var_array,
                             wgt_array, nused, status );

/* Now we update the total exposure time array. Scale the exposure time
   of this time slice in order to reduce its influence on the output
   expsoure times if it does not have much spectral overlap with the
   output cube. then fill the 1D work array with this constant value and
   paste it into the 2D texp_array using the spatial mapping. Note we
   want the simple sum of the exposure times, with no normalisation. SO
   we use the AST__NONORM flag which means we do not need to supply a
   weights array.  */
         if( texp != VAL__BADR ) {
            texp *= tfac;
            for( iv = 0; iv < ndet; iv++ ) detwork[ iv ] = texp;
            astRebinSeqF( splut, 0.0, 1, ldim, uddim, detwork, NULL,
                          spread, params, AST__NONORM, 0.0, 50,
                          VAL__BADR, 2, ldim, udim, ldim, uddim, texp_array,
                          NULL, NULL, NULL );
         }

/* Now do the same with the effective exposure time. */
         if( teff != VAL__BADR ) {
            teff *= tfac;
            for( iv = 0; iv < ndet; iv++ ) detwork[ iv ] = teff;
            astRebinSeqF( splut, 0.0, 1, ldim, uddim, detwork, NULL,
                          spread, params, AST__NONORM, 0.0, 50, VAL__BADR, 2,
                          ldim, udim, ldim, uddim, teff_array, NULL, NULL,
                          NULL );
         }
      }

/* Update the progress meter. */
      smf_reportprogress( 0, status );

/* End the AST context. */
      astEnd;
   }

/* If this is the final pass through this function, normalise the returned
   data and variance values. */
   if( last ) {

/* Create a dummy mapping that can be used with astRebinSeq (it is not
   actually used for anything since we are not adding any more data into the
   output arrays). */
      fullmap = (AstMapping *) astPermMap( 2, NULL, 3, NULL, NULL, " " );

/* Normalise the data values. We do not normalise the exposure time arrays. */
      astRebinSeqF( fullmap, 0.0, 2, lbnd_in,
                    ubnd_in, NULL, NULL, spread, params,
                    AST__REBINEND | ast_flags, 0.0, 50, VAL__BADR, 3,
                    ldim, udim, lbnd_in, ubnd_in, data_array, var_array,
                    wgt_array, nused );
      fullmap = astAnnul(fullmap);
   }

/* Free resources. */
   detlut = astFree( detlut );
   detwork = astFree( detwork );
   varwork = astFree( varwork );
}
Esempio n. 2
0
void smf_flat_malloc( size_t nheat, const smfData * refdata,
                      smfData **powvald, smfData **bolvald, int *status ) {

  size_t rowidx = SC2STORE__ROW_INDEX;
  size_t colidx = SC2STORE__COL_INDEX;
  double * bolval = NULL; /* Data array inside bolrefd */
  double * bolvalvar = NULL; /* Variance inside bolrefd */
  dim_t dims[] = { 1, 1, 1 }; /* Default dimensions */
  smfHead * hdr = NULL;      /* New header */
  int lbnd[] = { 1, 1, 1 };  /* Default pixel lower bounds */
  size_t nelem = 0;      /* Number of elements in first two dimensions of refdims */
  smfHead * oldhdr = NULL;   /* header from refdata */
  void *pntr[] = { NULL, NULL };          /* pointers for smfData */
  double * powval = NULL; /* Data array inside powrefd */
  const char *dom;        /* Domain of axis 1 */
  AstFrameSet *new_fs;    /* New FrameSet for returned *bolvald */
  AstMapping *map;        /* Mapping from pixel index 3 to heater index */
  AstFrame *frm;          /* Frame describing heater index */
  int ubnd[ 1 ];          /* Upper bound on heater index */

  if (bolvald) *bolvald = NULL;
  if (powvald) *powvald = NULL;

  if ( *status != SAI__OK ) return;

  if ( !bolvald && !powvald) {
    *status = SAI__ERROR;
    errRep( "", "Must provide at least one non-NULL pointer to smf_flat_malloc"
            " (possible programming error)", status );
    return;
  }

  /* Sanity check */
  if ( nheat == 0 ) {
    *status = SAI__ERROR;
    errRep( "", "No flatfield information present for creating new smfData",
            status );
    return;
  }

  if ( !smf_validate_smfData( refdata, 1, 0, status ) ) return;
  oldhdr = refdata->hdr;

  if (powvald) {
    powval = astCalloc( nheat, sizeof(*powval) );
    pntr[0] = powval;
    pntr[1] = NULL;
    dims[0] = nheat;
    *powvald = smf_construct_smfData( NULL, NULL, NULL, NULL, NULL, SMF__DOUBLE,
                                      pntr, NULL, SMF__QFAM_NULL, NULL, 0, 1,
                                      dims, NULL, 1, 0, 0, NULL,
                                      NULL, status );
  }

  if (bolvald) {
    /* Handle data ordering */
    if ( ! refdata->isTordered ) {
      rowidx++;
      colidx++;
    }

    nelem = refdata->dims[rowidx] * refdata->dims[colidx];
    bolval = astCalloc( nheat * nelem, sizeof(*bolval) );
    bolvalvar = astCalloc( nheat * nelem, sizeof(*bolvalvar) );
    pntr[0] = bolval;
    pntr[1] = bolvalvar;
    dims[SC2STORE__ROW_INDEX] = refdata->dims[rowidx];
    dims[SC2STORE__COL_INDEX] = refdata->dims[colidx];
    dims[2] = nheat;
    lbnd[SC2STORE__ROW_INDEX] = refdata->lbnd[rowidx];
    lbnd[SC2STORE__COL_INDEX] = refdata->lbnd[colidx];
    lbnd[2] = 1;

    /* Create a header to attach to the bolometer data. We only want the basic 2-d
       information to propagate. */
    hdr = smf_construct_smfHead( NULL, oldhdr->instrument, NULL, NULL,
                                 astCopy( oldhdr->fitshdr ), NULL, 0,
                                 oldhdr->instap, nheat, oldhdr->steptime,
                                 oldhdr->scanvel, oldhdr->obsmode,
                                 oldhdr->swmode, oldhdr->obstype,
                                 oldhdr->seqtype, oldhdr->inbeam, 0, NULL, NULL,
                                 NULL, NULL, 0, NULL,
                                 "Flatfield measurement", "Response",
                                 oldhdr->units, oldhdr->telpos,
                                 NULL, oldhdr->obsidss, status );

    *bolvald = smf_construct_smfData( NULL, NULL, hdr, NULL, NULL, SMF__DOUBLE,
                                      pntr, NULL, SMF__QFAM_TSERIES, NULL, 0, 1,
                                      dims, lbnd, 3, 0, 0, NULL, NULL, status );

    /* Assign a 3D WCS FRameSet in which the third axis represents heater
       value index (note, not actual heater value, since we do not yet
       know what the heater values are). First split the supplied time-series
       WCS FrameSet to extract a FrameSet in which the current Frame
       contains only the axes within the ame Domain as the first axis
       (this is safe because the first axis is always a spatial axis). */
    if( oldhdr->tswcs ) {
      dom = astGetC( oldhdr->tswcs, "Domain(1)" );
      new_fs = atlFrameSetSplit( oldhdr->tswcs, dom, NULL, NULL, status );

      /* Check this FrameSet is 2D, and if so, add in a third axis describing
         heater value index. */
      if( new_fs && astGetI( new_fs, "Naxes" ) == 2 ) {
         map = (AstMapping *) astUnitMap( 1, " " );
         frm = astFrame( 1, "Domain=HEATER_INDEX" );
         ubnd[ 0 ] = nheat;
         atlAddWcsAxis(  new_fs, map, frm, NULL, ubnd, status );
         map = astAnnul( map );
         frm = astAnnul( frm );

         /* Hand over the FrameSet pointer to the returned smfData. */
         (*bolvald)->hdr->tswcs = new_fs;

      }
    }
  }

  return;
}