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_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; }