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 ); }
void smf_rebincube_nn( ThrWorkForce *wf, smfData *data, int first, int last, int *ptime, dim_t nchan, dim_t ndet, dim_t nslice, dim_t nxy, dim_t nout, dim_t dim[3], int badmask, int is2d, AstMapping *ssmap, AstSkyFrame *abskyfrm, AstMapping *oskymap, Grp *detgrp, int moving, int usewgt, int genvar, double tfac, double fcon, float *data_array, float *var_array, double *wgt_array, float *texp_array, float *teff_array, int *nused, int *nreject, int *naccept, int *good_tsys, int *status ){ /* Local Variables */ AstMapping *totmap = NULL; /* WCS->GRID Mapping from input WCS FrameSet */ const char *name = NULL; /* Pointer to current detector name */ const double *tsys = NULL; /* Pointer to Tsys value for first detector */ dim_t gxout; /* Output X grid index */ dim_t gyout; /* Output Y grid index */ dim_t ichan; /* Input channel index */ dim_t idet; /* detector index */ dim_t itime; /* Index of current time slice */ dim_t nchanout; /* No of spectral channels in the output */ dim_t timeslice_size; /* No of detector values in one time slice */ double *detxin = NULL; /* Work space for input X grid coords */ double *detxout = NULL; /* Work space for output X grid coords */ double *detyin = NULL; /* Work space for input Y grid coords */ double *detyout = NULL; /* Work space for output Y grid coords */ double invar; /* Input variance */ double tcon; /* Variance factor for whole time slice */ double wgt; /* Weight for input value */ float *ddata = NULL; /* Pointer to start of input detector data */ float *tdata = NULL; /* Pointer to start of input time slice data */ float *work = NULL; /* Pointer to start of work array */ 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 *specpop = NULL; /* Input channels per output channel */ int *spectab = NULL; /* I/p->o/p channel number conversion table */ int first_ts; /* Is this the first time slice? */ int found; /* Was current detector name found in detgrp? */ int ignore; /* Ignore this time slice? */ int init_detector_data; /* Should detector_data be initialised? */ int iv0; /* Offset for pixel in 1st o/p spectral channel */ int jdet; /* Detector index */ int naccept_old; /* Previous number of accepted spectra */ int ochan; /* Output channel index */ int use_threads; /* Use multiple threads? */ smfHead *hdr = NULL; /* Pointer to data header for this time slice */ static smfRebincubeNNArgs1 *common_data = NULL; /* Holds data common to all detectors */ static smfRebincubeNNArgs2 *detector_data = NULL; /* Holds data for each detector */ static int *pop_array = NULL;/* I/p spectra pasted into each output spectrum */ static dim_t ndet_max = 0; /* Max number of detectors */ /* Check the inherited status. */ if( *status != SAI__OK ) return; /* Store a pointer to the input NDFs smfHead structure. */ hdr = data->hdr; /* Store the number of pixels in one time slice */ timeslice_size = ndet*nchan; /* Use this mapping to get the zero-based output channel number corresponding to each input channel number. */ smf_rebincube_spectab( nchan, dim[ 2 ], ssmap, &spectab, status ); if( !spectab ) goto L999; /* The 2D weighting scheme assumes that each output channel receives contributions from one and only one input channel in each input file. Create an array with an element for each output channel, holding the number of input channels that contribute to the output channel. */ nchanout = dim[ 2 ]; if( is2d ) { specpop = astMalloc( nchanout*sizeof( int ) ); memset( specpop, 0, nchanout*sizeof( int ) ); for( ichan = 0; ichan < nchan; ichan++ ) { ochan = spectab[ ichan ]; if( ochan != -1 ) specpop[ ochan ]++; } } /* If this is the first pass through this file, initialise the arrays. */ if( first ){ smf_rebincube_init( is2d, nxy, nout, genvar, data_array, var_array, wgt_array, texp_array, teff_array, nused, status ); /* Allocate an extra work array and initialise it to zero. This holds the total number of input spectra pasted into each output spectrum. It is not needed by the AST-based function and so has not been put into smf_rebincube_init. */ if( is2d ) { pop_array = astMalloc( nxy*sizeof( int ) ); memset( pop_array, 0, nxy*sizeof( int ) ); } } /* Allocate work arrays to hold the input and output grid coords for each detector. */ detxin = astMalloc( ndet*sizeof( double ) ); detyin = astMalloc( ndet*sizeof( double ) ); detxout = astMalloc( ndet*sizeof( double ) ); detyout = astMalloc( ndet*sizeof( double ) ); /* Initialise a string to point to the name of the first detector for which data is available */ name = hdr->detname; /* Fill the input arrays with the grid coords of each detector. */ for( idet = 0; idet < ndet; idet++ ) { detxin[ idet ] = (double) idet + 1.0; detyin[ 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. */ if( detgrp ) { found = grpIndex( name, detgrp, 1, status ); if( !found ) { detxin[ idet ] = AST__BAD; detyin[ idet ] = AST__BAD; } } /* Move on to the next available detector name. */ name += strlen( name ) + 1; } /* Initialise a pointer to the ntex time slice index to be used. */ nexttime = ptime; /* Count the number of time slices to be processed. */ if( ptime ) { itime = 0; while( ptime[ itime ] != VAL__MAXI ) itime++; if( data->file ) { msgOutiff( MSG__DEBUG, " ", "smf_rebincube_nn: Selecting %d time " "slices from data file '%s'.", status, (int) itime, data->file->name ); } } else { itime = nslice; if( data->file ) { msgOutiff( MSG__DEBUG, " ", "smf_rebincube_nn: Using all %d time " "slices from data file '%s'.", status, (int) itime, data->file->name ); } } /* Initialise the progress meter. */ smf_reportprogress( itime, status ); /* Loop round all time slices in the input NDF. */ use_threads = 0; first_ts = 1; 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 ) { if( data->file ) { msgOutiff( MSG__DEBUG, " ", "smf_rebincube_nn: Cannot get " "Mapping for slice %d from data file '%s'.", status, (int) itime, data->file->name ); } astEnd; 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 ); /* Use this Mapping to get the output spatial grid coords for each input detector. */ astTran2( totmap, ndet, detxin, detyin, 1, detxout, detyout ); /* If this is the first time slice from the current input file to be pasted into the output, see if any of the spectra will end up being pasted on top of each other in the output. If not we can use a separate thread to paste each spectrum. Otherwise, we cannot use multiple threads since they may end up trying to write to the same output pixel at the same time. */ if( first_ts ) { first_ts = 0; use_threads = wf ? 1 : 0; for( idet = 0; idet < ndet - 1 && use_threads; idet++ ) { if( detxout[ idet ] != AST__BAD && detyout[ idet ] != AST__BAD ){ gxout = floor( detxout[ idet ] + 0.5 ); gyout = floor( detyout[ idet ] + 0.5 ); if( gxout >= 1 && gxout <= dim[ 0 ] && gyout >= 1 && gyout <= dim[ 1 ] ) { for( jdet = idet + 1; idet < ndet; idet++ ) { if( detxout[ jdet ] != AST__BAD && detyout[ jdet ] != AST__BAD ){ if( floor( detxout[ jdet ] + 0.5 ) == gxout && floor( detyout[ jdet ] + 0.5 ) == gyout ) { use_threads = 0; break; } } } } } } /* If we will be using mutiple threads, do some preparation. */ if( use_threads ) { if( data->file ) { msgOutiff( MSG__DEBUG, " ", "smf_rebincube_nn: Using multiple " "threads to process data file '%s'.", status, data->file->name ); } /* Ensure we have a structure holding information which is common to all detectors and time slices. */ common_data = astGrow( common_data, sizeof( smfRebincubeNNArgs1 ), 1 ); if( astOK ) { common_data->badmask = badmask; common_data->nchan = nchan; common_data->nchanout = nchanout; common_data->spectab = spectab; common_data->specpop = specpop; common_data->nxy = nxy; common_data->genvar = genvar; common_data->data_array = data_array; common_data->var_array = var_array; common_data->wgt_array = wgt_array; common_data->pop_array = pop_array; common_data->nout = nout; common_data->is2d = is2d; } /* Ensure we have a structure for each detector to hold the detector-specific data, plus a pointer to the common data. */ init_detector_data = ( detector_data == NULL ); if( init_detector_data ) ndet_max = 0; detector_data = astGrow( detector_data, sizeof( smfRebincubeNNArgs2 ), ndet ) ; /* Initialise pointers stored within any new elements added to the "detector_data" array. */ if( ndet > ndet_max && astOK ) { for( idet = ndet_max; idet < ndet; idet++ ) { detector_data[ idet ].common = NULL; detector_data[ idet ].work = NULL; detector_data[ idet ].ddata = NULL; } ndet_max = ndet; } /* Allocate work space for each detector and store the common data pointer. */ if( astOK ) { for( idet = 0; idet < ndet; idet++ ) { detector_data[ idet ].common = common_data; detector_data[ idet ].work = astGrow( detector_data[ idet ].work, sizeof( float ), nchanout ); } } /* If we are using a single threads, do some alternative preparation. */ } else { if( data->file ) { msgOutiff( MSG__DEBUG, " ", "smf_rebincube_nn: Using a single " "thread to process data file '%s'.", status, data->file->name ); } /* We need an extra work array for 2D weighting that can hold a single output spectrum. This is used as a staging post for each input spectrum prior to pasting it into the output cube. */ work = astMalloc( nchanout*sizeof( float ) ); } } /* Loop round each detector, pasting its spectral values into the output cube. */ for( idet = 0; idet < ndet; idet++ ) { /* If multi-threaded, initialise a bad value for the detector's weight to indicate that it is not being used. */ if( use_threads ) detector_data[ idet ].wgt = VAL__BADD; /* See if any good tsys values are present. */ rtsys = tsys ? (float) tsys[ idet ] : VAL__BADR; if( rtsys <= 0.0 ) rtsys = VAL__BADR; if( rtsys != VAL__BADR ) *good_tsys = 1; /* Check the detector has a valid position in output grid coords */ if( detxout[ idet ] != AST__BAD && detyout[ idet ] != AST__BAD ){ /* Find the closest output pixel and check it is within the bounds of the output cube. */ gxout = floor( detxout[ idet ] + 0.5 ); gyout = floor( detyout[ idet ] + 0.5 ); if( gxout >= 1 && gxout <= dim[ 0 ] && gyout >= 1 && gyout <= dim[ 1 ] ) { /* Get the offset of the output array element that corresponds to this pixel in the first spectral channel. */ iv0 = ( gyout - 1 )*dim[ 0 ] + ( gxout - 1 ); /* If required calculate the variance associated with this detector, based on the input Tsys values. */ invar = VAL__BADR; if( usewgt || genvar == 2 ) { if( rtsys != VAL__BADR ) { if( tcon != VAL__BADD ) invar = tcon*rtsys*rtsys; } } /* Calculate the weight for this detector. If we need the input variance, either to weight the input or to calculate output variances, but the input variance is not available, then ignore this detector. */ ignore = 0; if( usewgt ) { if( invar > 0.0 && invar != VAL__BADR ) { wgt = 1.0/invar; } else { ignore = 1; } } else if( genvar == 2 ) { ignore = ( invar <= 0.0 || invar == VAL__BADR ); wgt = 1.0; } else { wgt = 1.0; } /* If we are not ignoring this input spectrum, get a pointer to the start of the input spectrum data and paste it into the output cube using either the 2D or 3D algorithm. */ if( !ignore ) { ddata = tdata + idet*nchan; /* First deal with cases where we are using a single thread (the current thread). */ if( !use_threads ) { naccept_old = *naccept; if( is2d ) { smf_rebincube_paste2d( badmask, nchan, nchanout, spectab, specpop, iv0, nxy, wgt, genvar, invar, ddata, data_array, var_array, wgt_array, pop_array, nused, nreject, naccept, work, status ); } else { smf_rebincube_paste3d( nchan, nout, spectab, iv0, nxy, wgt, genvar, invar, ddata, data_array, var_array, wgt_array, nused, status ); (*naccept)++; } /* Now we update the total and effective exposure time arrays for the output spectrum that receives this input spectrum. Scale the exposure times 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. Only update the exposure time arrays if the spectrum was used (as shown by an increase in the number of accepted spectra). */ if( texp != VAL__BADR && *naccept > naccept_old ) { texp_array[ iv0 ] += texp*tfac; teff_array[ iv0 ] += teff*tfac; } /* Now deal with cases where we are using several threads. */ } else { /* Set up the detector specific data. */ detector_data[ idet ].iv0 = iv0; detector_data[ idet ].wgt = wgt; detector_data[ idet ].invar = invar; detector_data[ idet ].ddata = ddata; detector_data[ idet ].nused = 0; detector_data[ idet ].nreject = 0; detector_data[ idet ].naccept = 0; /* Add a job to the workforce's job list. This job calls smf_rebincube_paste2d or smf_rebincube_paste3d to paste the detector input spectrum into the output cube. */ thrAddJob( wf, 0, detector_data + idet, smf_rebincube_paste_thread, 0, NULL, status ); } } else if( data->file ) { msgOutiff( MSG__DEBUG, " ", "smf_rebincube_nn: Detector %d " "is being ignored when processing data file '%s'.", status, idet, data->file->name ); } } else if( data->file ) { msgOutiff( MSG__DEBUG, " ", "smf_rebincube_nn: Detector %d " "fell outside the output cube when processing " "data file '%s'.", status, idet, data->file->name ); } } else if( data->file ) { msgOutiff( MSG__DEBUG, " ", "smf_rebincube_nn: Detector %d has " "an unknown position in the output cube when processing " "data file '%s'.", status, idet, data->file->name ); } } /* If using multiple threads, wait until all spectra for this time slice have been pasted into the output cube. Then transfer the output values from the detector data structures to the returned variables. */ if( use_threads ) { thrWait( wf, status ); for( idet = 0; idet < ndet; idet++ ) { if( detector_data[ idet ].wgt != VAL__BADD ) { (*nused) += detector_data[ idet ].nused; (*nreject) += detector_data[ idet ].nreject; (*naccept) += detector_data[ idet ].naccept; if( texp != VAL__BADR && detector_data[ idet ].naccept > 0 ) { texp_array[ detector_data[ idet ].iv0 ] += texp*tfac; teff_array[ detector_data[ idet ].iv0 ] += teff*tfac; } } } } /* 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, and release any static resources allocated within this function. */ if( last ) { if( is2d ) { smf_rebincube_norm2d( nout, nxy, genvar, data_array, var_array, wgt_array, pop_array, status ); } else { smf_rebincube_norm3d( nout, genvar, *nused, data_array, var_array, wgt_array, status ); } pop_array = astFree( pop_array ); if( use_threads ) { common_data = astFree( common_data ); for( idet = 0; idet < ndet_max; idet++ ) { detector_data[ idet ].work = astFree( detector_data[ idet ].work ); } detector_data = astFree( detector_data ); } } /* Free non-static resources. */ L999:; work = astFree( work ); spectab = astFree( spectab ); specpop = astFree( specpop ); detxin = astFree( detxin ); detyin = astFree( detyin ); detxout = astFree( detxout ); detyout = astFree( detyout ); }