void smf_resampcube_nn( smfData *data, dim_t nchan, dim_t ndet, dim_t nslice, dim_t nxy, dim_t dim[3], AstMapping *ssmap, AstSkyFrame *abskyfrm, AstMapping *iskymap, Grp *detgrp, int moving, float *in_data, float *out_data, int *overlap, int *status ){ /* Local Variables */ AstMapping *totmap = NULL; /* WCS->GRID Mapping from template WCS FrameSet */ const char *name = NULL; /* Pointer to current detector name */ dim_t timeslice_size; /* No of detector values in one time slice */ double *detxtemplt = NULL; /* Work space for template X grid coords */ double *detxskycube = NULL; /* Work space for sky cube X grid coords */ double *detytemplt = NULL; /* Work space for template Y grid coords */ double *detyskycube = NULL; /* Work space for sky cube Y grid coords */ float *ddata = NULL; /* Pointer to start of output detector data */ float *tdata = NULL; /* Pointer to start of sky cube time slice data */ int *spectab = NULL; /* Template->sky cube channel number conversion table */ int found; /* Was current detector name found in detgrp? */ dim_t gxsky; /* Sky cube X grid index */ dim_t gysky; /* Sky cube Y grid index */ dim_t idet; /* Detector index */ dim_t itime; /* Index of current time slice */ int iv0; /* Offset for pixel in 1st sky cube spectral channel */ smfHead *hdr = NULL; /* Pointer to data header for this time slice */ /* Initialise */ *overlap = 0; /* Check the inherited status. */ if( *status != SAI__OK ) return; /* Store a pointer to the template NDFs smfHead structure. */ hdr = data->hdr; /* Store the number of pixels in one time slice */ timeslice_size = ndet*nchan; /* Use the supplied mapping to get the zero-based sky cube channel number corresponding to each template channel number. */ smf_rebincube_spectab( nchan, dim[ 2 ], ssmap, &spectab, status ); if( !spectab ) goto L999; /* Allocate work arrays to hold the template and sky cube grid coords for each detector. */ detxtemplt = astMalloc( ndet*sizeof( double ) ); detytemplt = astMalloc( ndet*sizeof( double ) ); detxskycube = astMalloc( ndet*sizeof( double ) ); detyskycube = astMalloc( ndet*sizeof( double ) ); /* Initialise a string to point to the name of the first detector for which data is to be created. */ name = hdr->detname; /* Fill the arrays with the grid coords of each detector. */ for( idet = 0; idet < ndet; idet++ ) { detxtemplt[ idet ] = (double) idet + 1.0; detytemplt[ 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 ) { detxtemplt[ idet ] = AST__BAD; detytemplt[ idet ] = AST__BAD; } } /* Move on to the next available detector name. */ name += strlen( name ) + 1; } /* Loop round all time slices in the template NDF. */ for( itime = 0; itime < nslice && *status == SAI__OK; itime++ ) { /* Store a pointer to the first output data value in this time slice. */ tdata = in_data ? ( out_data + itime*timeslice_size ) : NULL; /* 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 template to the spatial GRID axes in the sky cube 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, iskymap, moving, status ); if( !totmap ) { astEnd; break; } /* Use this Mapping to get the sky cube spatial grid coords for each template detector. */ astTran2( totmap, ndet, detxtemplt, detytemplt, 1, detxskycube, detyskycube ); /* Loop round each detector, obtaining its output value from the sky cube. */ for( idet = 0; idet < ndet; idet++ ) { /* Get a pointer to the start of the output spectrum data. */ ddata = tdata + idet*nchan; /* Check the detector has a valid position in sky cube grid coords */ if( detxskycube[ idet ] != AST__BAD && detyskycube[ idet ] != AST__BAD ){ /* Find the closest sky cube pixel and check it is within the bounds of the sky cube. */ gxsky = floor( detxskycube[ idet ] + 0.5 ); gysky = floor( detyskycube[ idet ] + 0.5 ); if( gxsky >= 1 && gxsky <= dim[ 0 ] && gysky >= 1 && gysky <= dim[ 1 ] ) { /* Get the offset of the sky cube array element that corresponds to this pixel in the first spectral channel. */ iv0 = ( gysky - 1 )*dim[ 0 ] + ( gxsky - 1 ); /* Copy the sky cube spectrum into the output time series cube. */ *overlap = 1; if( in_data ) { smf_resampcube_copy( nchan, spectab, iv0, nxy, ddata, in_data, status ); } else { break; } } } } /* End the AST context. */ astEnd; /* If no input data was supplied, and we have found at least one input spectrum that overlaps the sky cube, we can finish early. */ if( !in_data && *overlap ) break; } /* Free non-static resources. */ L999:; spectab = astFree( spectab ); detxtemplt = astFree( detxtemplt ); detytemplt = astFree( detytemplt ); detxskycube = astFree( detxskycube ); detyskycube = astFree( detyskycube ); }
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 ); }