/* Main entry point. */ void smf_calcmodel_smo( ThrWorkForce *wf, smfDIMMData *dat, int chunk, AstKeyMap *keymap, smfArray **allmodel, int flags __attribute__((unused)), int *status) { /* Local Variables */ size_t bstride; /* bolo stride */ dim_t boxcar = 0; /* size of boxcar smooth window */ smf_filt_t filter_type; /* The type of smoothing to perform */ size_t i; /* Loop counter */ dim_t idx=0; /* Index within subgroup */ int iworker; /* Owkrer index */ smfCalcmodelSmoJobData *job_data=NULL; /* Pointer to all job data structures */ AstKeyMap *kmap=NULL; /* Pointer to PLN-specific keys */ smfArray *model=NULL; /* Pointer to model at chunk */ double *model_data=NULL; /* Pointer to DATA component of model */ double *model_data_copy=NULL; /* Copy of model_data for one bolo */ dim_t nbolo=0; /* Number of bolometers */ dim_t ndata=0; /* Total number of data points */ int notfirst=0; /* flag for delaying until after 1st iter */ dim_t ntslice=0; /* Number of time slices */ int nworker; /* No. of worker threads in supplied Workforce */ smfCalcmodelSmoJobData *pdata=NULL; /* Pointer to current data structure */ smfArray *qua=NULL; /* Pointer to QUA at chunk */ smf_qual_t *qua_data=NULL; /* Pointer to quality data */ smfArray *res=NULL; /* Pointer to RES at chunk */ double *res_data=NULL; /* Pointer to DATA component of res */ int step; /* Number of bolometers per thread */ size_t tstride; /* Time slice stride in data array */ const char * typestr = NULL; /* smo.type value */ /* Main routine */ if (*status != SAI__OK) return; /* Obtain pointers to relevant smfArrays for this chunk */ res = dat->res[chunk]; qua = dat->qua[chunk]; /* Obtain pointer to sub-keymap containing PLN parameters. Something will always be available.*/ astMapGet0A( keymap, "SMO", &kmap ); /* Are we skipping the first iteration? */ astMapGet0I(kmap, "NOTFIRST", ¬first); if( notfirst && (flags & SMF__DIMM_FIRSTITER) ) { msgOutif( MSG__VERB, "", FUNC_NAME ": skipping SMO this iteration", status ); return; } /* Get the boxcar size */ if( kmap ) smf_get_nsamp( kmap, "BOXCAR", res->sdata[0], &boxcar, status ); /* Get the type of smoothing filter to use. Anthing that is not "MEDIAN" is mean */ filter_type = SMF__FILT_MEAN; if (astMapGet0C( kmap, "TYPE", &typestr ) ) { if (strncasecmp( typestr, "MED", 3 ) == 0 ) { filter_type = SMF__FILT_MEDIAN; } } /* Assert bolo-ordered data */ smf_model_dataOrder( wf, dat, allmodel, chunk, SMF__RES|SMF__QUA, 0, status ); smf_get_dims( res->sdata[0], NULL, NULL, NULL, &ntslice, &ndata, NULL, NULL, status); model = allmodel[chunk]; msgOutiff(MSG__VERB, "", " Calculating smoothed model using boxcar of width %" DIM_T_FMT " time slices", status, boxcar); /* Create structures used to pass information to the worker threads. */ nworker = wf ? wf->nworker : 1; job_data = astMalloc( nworker*sizeof( *job_data ) ); /* Loop over index in subgrp (subarray) and put the previous iteration of the filtered component back into the residual before calculating and removing the new filtered component */ for( idx=0; (*status==SAI__OK)&&(idx<res->ndat); idx++ ) { /* Obtain dimensions of the data */ smf_get_dims( res->sdata[idx], NULL, NULL, &nbolo, &ntslice, &ndata, &bstride, &tstride, status); /* Get pointers to data/quality/model */ res_data = (res->sdata[idx]->pntr)[0]; qua_data = (qua->sdata[idx]->pntr)[0]; model_data = (model->sdata[idx]->pntr)[0]; if( (res_data == NULL) || (model_data == NULL) || (qua_data == NULL) ) { *status = SAI__ERROR; errRep( "", FUNC_NAME ": Null data in inputs", status); } else { /* Uncomment to aid debugging */ /* smf_write_smfData( res->sdata[idx], NULL, qua_data, "res_in", NULL, 0, 0, MSG__VERB, 0, status ); */ if( *status == SAI__OK ) { /* Place last iteration back into residual if this is a smoothable section of the time series */ for (i=0; i< ndata; i++) { if ( !(qua_data[i]&SMF__Q_FIT) && res_data[i] != VAL__BADD && model_data[i] != VAL__BADD ) { res_data[i] += model_data[i]; } } } /* Uncomment to aid debugging */ /* smf_write_smfData( model->sdata[idx], NULL, qua_data, "model_b4", NULL, 0, 0, MSG__VERB, 0, status ); smf_write_smfData( res->sdata[idx], NULL, qua_data, "res_b4", NULL, 0, 0, MSG__VERB, 0, status ); */ /* Determine which bolometers are to be processed by which threads. */ step = nbolo/nworker; if( step < 1 ) step = 1; for( iworker = 0; iworker < nworker; iworker++ ) { pdata = job_data + iworker; pdata->b1 = iworker*step; pdata->b2 = pdata->b1 + step - 1; } /* Ensure that the last thread picks up any left-over bolometers */ pdata->b2 = nbolo - 1; /* Store all the other info needed by the worker threads, and submit the jobs to apply the smoothing. */ for( iworker = 0; iworker < nworker; iworker++ ) { pdata = job_data + iworker; pdata->boxcar = boxcar; pdata->bstride = bstride; pdata->bstride = bstride; pdata->filter_type = filter_type; pdata->model_data = model_data; pdata->nbolo = nbolo; pdata->nbolo = nbolo; pdata->ntslice = ntslice; pdata->ntslice = ntslice; pdata->qua_data = qua_data; pdata->qua_data = qua_data; pdata->res_data = res_data; pdata->res_data = res_data; pdata->tstride = tstride; pdata->tstride = tstride; thrAddJob( wf, THR__REPORT_JOB, pdata, smf1_calcmodel_smo_job, 0, NULL, status ); } thrWait( wf, status ); /* Uncomment to aid debugging */ /* smf_write_smfData( res->sdata[idx], NULL, qua_data, "res_af", NULL, 0, 0, MSG__VERB, 0, status ); smf_write_smfData( model->sdata[idx], NULL, qua_data, "model_af", NULL, 0, 0, MSG__VERB, 0, status ); */ } } /* Free work space (astFree returns without action if a NULL pointer is supplied). */ model_data_copy = astFree( model_data_copy ); job_data = astFree( job_data ); /* Annul AST Object pointers (astAnnul reports an error if a NULL pointer is supplied). */ if( kmap ) kmap = astAnnul( kmap ); }
void smf_calcmodel_gai( ThrWorkForce *wf, smfDIMMData *dat, int chunk, AstKeyMap *keymap, smfArray **allmodel, int flags, int *status) { /* Local Variables */ size_t bstride; /* bolometer stride */ dim_t gain_box=0; /* No. of time slices in a block */ size_t gbstride; /* GAIn bolo stride */ size_t gcstride; /* GAIn coeff stride */ int gflat=0; /* correct flatfield using GAI */ dim_t i; /* Loop counter */ dim_t idx=0; /* Index within subgroup */ dim_t j; /* Loop counter */ AstKeyMap *kmap=NULL; /* Local GAIn keymap */ smfArray *model=NULL; /* Pointer to model at chunk */ double *model_data=NULL; /* Pointer to DATA component of model */ dim_t nblock; /* No. of time slice blocks */ dim_t nbolo; /* Number of bolometers */ dim_t ndata; /* Number of data points */ smfArray *noi=NULL; /* Pointer to NOI at chunk */ double *noi_data=NULL; /* Pointer to DATA component of model */ size_t noibstride; /* bolo stride for noise */ dim_t nointslice; /* number of time slices for noise */ size_t noitstride; /* Time stride for noise */ dim_t npar; /* No. of parameters per bolometer */ dim_t ntslice; /* Number of time slices */ int oldalg = 1; /* Is the old COM algorithm being used? */ smfArray *qua=NULL; /* Pointer to QUA at chunk */ smf_qual_t *qua_data=NULL; /* Pointer to quality data */ smfArray *res=NULL; /* Pointer to RES at chunk */ double *res_data=NULL; /* Pointer to DAT */ double *scale; /* Pointer to scale factor */ size_t tstride; /* time slice stride */ double *wg; /* Workspace holding time slice gains */ double *woff; /* Workspace holding time slice offsets */ /* Main routine */ if( *status != SAI__OK ) return; if( !(flags&SMF__DIMM_INVERT) ) return; /* See if the new sigma-clipping COM algorithm is being used. */ astMapGet0A( keymap, "COM", &kmap ); astMapGet0I( kmap, "OLDALG", &oldalg ); kmap = astAnnul( kmap ); /* Obtain pointer to sub-keymap containing GAI parameters */ if( !astMapHasKey( keymap, "GAI" ) ) return; astMapGet0A( keymap, "GAI", &kmap ); astMapGet0I( kmap, "FLATFIELD", &gflat ); if( kmap ) kmap = astAnnul( kmap ); /* Report an error if gai.flatfield is used with the new COM algorithm. */ if( !oldalg && gflat && *status == SAI__OK ) { errRep( "", "Cannot use GAI.FLATFIELD with new COM algorithm.", status ); } /* Only have to do something if gai.flatfield set */ if( !gflat || *status != SAI__OK ) return; /* Obtain pointers to relevant smfArrays for this chunk */ res = dat->res[chunk]; qua = dat->qua[chunk]; model = allmodel[chunk]; if(dat->noi) noi = dat->noi[chunk]; /* Get the number of blocks into which to split each time series. Each box (except possibly the last one contains "gain_box" time slices. */ astMapGet0A( keymap, "COM", &kmap ); smf_get_nsamp( kmap, "GAIN_BOX", res->sdata[0], &gain_box, status ); if (kmap) kmap = astAnnul( kmap ); if (*status != SAI__OK) return; /* Ensure everything is in bolo-order */ smf_model_dataOrder( wf, dat, allmodel, chunk, SMF__RES|SMF__QUA|SMF__NOI, 0, status ); /* Loop over index in subgrp (subarray) */ for( idx=0; idx<res->ndat; idx++ ) { /* Get pointers to DATA components */ res_data = (res->sdata[idx]->pntr)[0]; qua_data = (qua->sdata[idx]->pntr)[0]; model_data = (model->sdata[idx]->pntr)[0]; if( noi ) { smf_get_dims( noi->sdata[idx], NULL, NULL, NULL, &nointslice, NULL, &noibstride, &noitstride, status); noi_data = (double *)(noi->sdata[idx]->pntr)[0]; } if( (res_data == NULL) || (model_data == NULL) || (qua_data == NULL) ) { *status = SAI__ERROR; errRep("", FUNC_NAME ": Null data in inputs", status); } else { /* Get the raw data dimensions */ smf_get_dims( res->sdata[idx], NULL, NULL, &nbolo, &ntslice, &ndata, &bstride, &tstride, status); smf_get_dims( model->sdata[idx], NULL, NULL, NULL, &npar, NULL, &gbstride, &gcstride, status); /* If com.gain_box is zero, use a value of ntslice, so that a single box will be used covering the whoel time stream. */ if( gain_box == 0 ) gain_box = ntslice; /* Allocate work space for the gain and offset for each time slice. */ woff = astMalloc( ntslice*sizeof( *woff ) ); wg = astMalloc( ntslice*sizeof( *wg ) ); /* Get the number of blocks into which the time stream is divided. Each block has a separate gain, offset and correlation factor for each bolometer. */ nblock = npar/3; /* Undo the gain correction stored in GAI (the gain is applied to the signal and noise in smf_calcmodel_com) */ for( i=0; i<nbolo; i++ ) { if( !(qua_data[i*bstride]&SMF__Q_BADB) ) { /* Get the gain and offset for each time slice of this bolometer. */ smf_gandoff( i, 0, ntslice - 1, ntslice, gbstride, gcstride, model_data, nblock, gain_box, wg, woff, NULL, status ); /* First undo the flatfield correction to the signal */ scale = wg; for( j=0; j<ntslice; j++,scale++ ) { if( !(qua_data[i*bstride + j*tstride]&SMF__Q_MOD) && *scale != VAL__BADD && *scale > 0.0 ) { res_data[i*bstride + j*tstride] *= *scale; } } /* Then scale the noise. */ if( noi ) { scale = wg; for( j=0; j<nointslice; j++,scale++ ) { if( noi_data[i*noibstride + j*noitstride] != VAL__BADD && *scale != VAL__BADD && *scale > 0.0 ) { noi_data[i*noibstride + j*noitstride] *= (*scale) * (*scale); } } } } } /* Free work space. */ woff = astFree( woff ); wg = astFree( wg ); } } }
void smf_calcmodel_noi( ThrWorkForce *wf, smfDIMMData *dat, int chunk, AstKeyMap *keymap, smfArray **allmodel, int flags, int *status) { /* Local Variables */ dim_t bolostep; /* Number of bolos per thread */ dim_t boxsize; /* No. of time slices in each noise box */ smfData *box = NULL; /* SmfData holding one box of input data */ size_t bstride; /* bolometer stride */ int calcfirst=0; /* Were bolo noises already measured? */ int dclimcorr; /* Min number of correlated steps */ int dcmaxsteps; /* Maximum allowed number of dc jumps */ dim_t dcfitbox; /* Width of box for DC step detection */ double dcthresh; /* Threshold for DC step detection */ dim_t dcsmooth; /* Width of median filter in DC step detection*/ double *din; /* Pointer to next input value */ double *dout; /* Pointer to next output value */ int fillgaps; /* If set perform gap filling */ dim_t i; /* Loop counter */ dim_t ibolo; /* Bolometer index */ int ibox; /* Index of current noise box */ dim_t itime; /* Time slice index */ dim_t idx=0; /* Index within subgroup */ JCMTState *instate=NULL; /* Pointer to input JCMTState */ int iw; /* Thread index */ dim_t j; /* Loop counter */ AstKeyMap *kmap=NULL; /* Local keymap */ size_t mbstride; /* model bolometer stride */ dim_t mntslice; /* Number of model time slices */ size_t mtstride; /* model time slice stride */ smfArray *model=NULL; /* Pointer to model at chunk */ double *model_data=NULL; /* Pointer to DATA component of model */ dim_t nbolo; /* Number of bolometers */ int nbox = 0; /* Number of noise boxes */ size_t nchisq; /* Number of data points in chisq calc */ dim_t nelbox; /* Number of data points in a noise box */ dim_t ndata; /* Total number of data points */ size_t nflag; /* Number of new flags */ int nleft; /* Number of samples not in a noise box */ dim_t ntslice; /* Number of time slices */ int nw; /* Number of worker threads */ size_t pend; /* Last non-PAD sample */ size_t pstart; /* First non-PAD sample */ smf_qual_t *qin; /* Pointer to next input quality value */ smf_qual_t *qout; /* Pointer to next output quality value */ smfArray *qua=NULL; /* Pointer to RES at chunk */ smf_qual_t *qua_data=NULL; /* Pointer to RES at chunk */ smfArray *res=NULL; /* Pointer to RES at chunk */ double *res_data=NULL; /* Pointer to DATA component of res */ dim_t spikebox=0; /* Box size for spike detection */ double spikethresh=0; /* Threshold for spike detection */ size_t tend; /* Last input sample to copy */ size_t tstart; /* First input sample to copy */ size_t tstride; /* time slice stride */ double *var=NULL; /* Sample variance */ size_t xbstride; /* Box bolometer stride */ int zeropad; /* Pad with zeros? */ /* Main routine */ if (*status != SAI__OK) return; /* Obtain pointer to sub-keymap containing NOI parameters */ astMapGet0A( keymap, "NOI", &kmap ); /* Assert bolo-ordered data */ smf_model_dataOrder( dat, allmodel, chunk, SMF__RES|SMF__QUA, 0, status ); /* Obtain pointers to relevant smfArrays for this chunk */ res = dat->res[chunk]; qua = dat->qua[chunk]; model = allmodel[chunk]; /* Obtain parameters for NOI */ /* Data-cleaning parameters */ smf_get_cleanpar( kmap, res->sdata[0], NULL, &dcfitbox, &dcmaxsteps, &dcthresh, &dcsmooth, &dclimcorr, NULL, &fillgaps, &zeropad, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, &spikethresh, &spikebox, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, status ); /* Did we already calculate the noise on each detector? */ astMapGet0I( kmap, "CALCFIRST", &calcfirst ); /* Initialize chisquared */ dat->chisquared[chunk] = 0; nchisq = 0; /* Loop over index in subgrp (subarray) */ for( idx=0; idx<res->ndat; idx++ ) { /* Get pointers to DATA components */ res_data = (res->sdata[idx]->pntr)[0]; model_data = (model->sdata[idx]->pntr)[0]; qua_data = (qua->sdata[idx]->pntr)[0]; if( (res_data == NULL) || (model_data == NULL) || (qua_data == NULL) ) { *status = SAI__ERROR; errRep( "", FUNC_NAME ": Null data in inputs", status); } else { /* Get the raw data dimensions */ smf_get_dims( res->sdata[idx], NULL, NULL, &nbolo, &ntslice, &ndata, &bstride, &tstride, status ); /* NOI model dimensions */ smf_get_dims( model->sdata[idx], NULL, NULL, NULL, &mntslice, NULL, &mbstride, &mtstride, status ); /* Only estimate the white noise level once at the beginning - the reason for this is to make measurements of the convergence easier. We either do it prior to the start of iterations (in which case the relative weights will be influeced by low-frequency noise, this is initialized in smf_model_create), or else we calculate the noise after the first iteration. */ if( (flags & SMF__DIMM_FIRSTITER) && (!calcfirst) ) { /* There are two forms for the NOI model: one constant noise value for each bolometer, or "ntslice" noise values for each bolometer. Handle the first case now. */ if( mntslice == 1 ) { var = astMalloc( nbolo*sizeof(*var) ); if (var) { /* Measure the noise from power spectra */ smf_bolonoise( wf, res->sdata[idx], 0, 0.5, SMF__F_WHITELO, SMF__F_WHITEHI, 0, zeropad ? SMF__MAXAPLEN : SMF__BADSZT, var, NULL, NULL, status ); for( i=0; i<nbolo; i++ ) if( !(qua_data[i*bstride]&SMF__Q_BADB) ) { /* Loop over time and store the variance for each sample */ for( j=0; j<mntslice; j++ ) { model_data[i*mbstride+(j%mntslice)*mtstride] = var[i]; } } var = astFree( var ); } /* If the NOI model is of the second form, the noise is estimated in boxes of samples lasting "NOI.BOX_SIZE" seconds, and then the noise level in the box is assigned to all samples in the box. */ } else if( mntslice == ntslice ) { /* If not already done, get NOI.BOX_SIZE and convert from seconds to samples. */ if( idx == 0 ) { boxsize = 0; smf_get_nsamp( kmap, "BOX_SIZE", res->sdata[0], &boxsize, status ); msgOutf( "", FUNC_NAME ": Calculating a NOI variance for each " "box of %d samples.", status, (int) boxsize ); /* Find the indices of the first and last non-PAD sample. */ smf_get_goodrange( qua_data, ntslice, tstride, SMF__Q_PAD, &pstart, &pend, status ); /* How many whole boxes fit into this range? */ nbox = ( pend - pstart + 1 ) / boxsize; if( nbox == 0 ) nbox = 1; /* How many samples would be left over at the end if we used this many boxes? */ nleft = ( pend - pstart + 1 ) - nbox*boxsize; /* Increase "boxsize" to reduce this number as far as possible. Any samples that are left over after this increase of boxsize will not be used when calculating the noise levels in each bolometer. */ boxsize += nleft/nbox; /* Create a smfData to hold one box-worth of input data. We do not need to copy jcmtstate information. */ if( res->sdata[idx]->hdr ) { instate = res->sdata[idx]->hdr->allState; res->sdata[idx]->hdr->allState = NULL; } box = smf_deepcopy_smfData( res->sdata[idx], 0, SMF__NOCREATE_DATA | SMF__NOCREATE_VARIANCE | SMF__NOCREATE_QUALITY, 0, 0, status ); if( instate ) res->sdata[idx]->hdr->allState = instate; /* Set the length of the time axis to the box size plus padding, and create empty data and quality arrays for it. */ if( *status == SAI__OK ) { box->dims[ box->isTordered?2:0 ] = boxsize + pstart + (ntslice - pend - 1); smf_get_dims( box, NULL, NULL, NULL, NULL, &nelbox, &xbstride, NULL, status ); box->pntr[0] = astMalloc( sizeof( double )*nelbox ); box->qual = astMalloc( sizeof( smf_qual_t )*nelbox ); /* For every bolometer, flag the start and end of the quality array as padding, and store zeros in the data array. */ for( ibolo = 0; ibolo < nbolo; ibolo++ ) { dout = ((double *) box->pntr[0]) + xbstride*ibolo; qout = box->qual + xbstride*ibolo; for( itime = 0; itime < pstart; itime++ ) { *(qout++) = SMF__Q_PAD; *(dout++) = 0.0; } dout = ((double *) box->pntr[0]) + xbstride*ibolo + pstart + boxsize;; qout = box->qual + xbstride*ibolo + pstart + boxsize; for( itime = pend + 1; itime < ntslice; itime++ ) { *(qout++) = SMF__Q_PAD; *(dout++) = 0.0; } } } } /* Work space to hold the variance for each bolometer in a box */ var = astMalloc( nbolo*sizeof(*var) ); if( *status == SAI__OK ) { /* Index of the first time slice within the input smfData that is included in the first box. */ tstart = pstart; /* Loop round each noise box */ for( ibox = 0; ibox < nbox; ibox++ ) { /* Copy the data and quality values for this box from the input smfData into "box", leaving room for padding at both ends of box. Note, data is bolo-ordered so we can assume that "tstride" is 1. */ din = ((double *)(res->sdata[idx]->pntr[0])) + tstart; dout = ((double *)(box->pntr[0])) + pstart; qin = qua_data + tstart; qout = box->qual + pstart; for( ibolo = 0; ibolo < nbolo; ibolo++ ) { memcpy( dout, din, boxsize*sizeof( *din ) ); memcpy( qout, qin, boxsize*sizeof( *qin ) ); din += bstride; dout += xbstride; qin += bstride; qout += xbstride; } /* Measure the noise from power spectra in the box. */ smf_bolonoise( wf, box, 0, 0.5, SMF__F_WHITELO, SMF__F_WHITEHI, 0, zeropad ? SMF__MAXAPLEN : SMF__BADSZT, var, NULL, NULL, status ); /* Loop over time and store the variance for each sample in the NOI model. On the last box, pick up any left over time slices. */ if( ibox < nbox - 1 ) { tend = tstart + boxsize - 1; } else { tend = pend; } for( ibolo = 0; ibolo < nbolo; ibolo++ ) { if( !( qua_data[ ibolo*bstride ] & SMF__Q_BADB ) ) { dout = model_data + ibolo*bstride + tstart; for( itime = tstart; itime <= tend; itime++ ) { *(dout++) = var[ ibolo ]; } } } /* Update the index of the first time slice within the input smfData that is included in the next box. */ tstart += boxsize; } var = astFree( var ); } /* Report an error if the number of samples for each bolometer in the NOI model is not 1 or "ntslice". */ } else if( *status == SAI__OK ) { *status = SAI__ERROR; errRepf( "", FUNC_NAME ": NOI model has %d samples - should be " "%d or 1.", status, (int) mntslice, (int) ntslice); } } if( kmap ) { /* Flag spikes in the residual after first iteration */ if( spikethresh && !(flags&SMF__DIMM_FIRSTITER) ) { /* Now re-flag */ smf_flag_spikes( wf, res->sdata[idx], SMF__Q_MOD, spikethresh, spikebox, &nflag, status ); msgOutiff(MSG__VERB," ", " flagged %zu new %lf-sig spikes", status, nflag, spikethresh ); } if( dcthresh && dcfitbox ) { smf_fix_steps( wf, res->sdata[idx], dcthresh, dcsmooth, dcfitbox, dcmaxsteps, dclimcorr, 1, &nflag, NULL, NULL, status ); msgOutiff(MSG__VERB, ""," detected %zu bolos with DC steps\n", status, nflag); } } /* Now calculate contribution to chi^2. This bit takes along time if there is a lot of data so share the work out amongst the available worker threads. How many threads do we get to play with */ nw = wf ? wf->nworker : 1; /* Find how many bolometers to process in each worker thread. */ bolostep = nbolo/nw; if( bolostep == 0 ) bolostep = 1; /* Allocate job data for threads, and store the range of bolos to be processed by each one. Ensure that the last thread picks up any left-over bolos. */ SmfCalcModelNoiData *job_data = astCalloc( nw, sizeof(*job_data) ); if( *status == SAI__OK ) { SmfCalcModelNoiData *pdata; for( iw = 0; iw < nw; iw++ ) { pdata = job_data + iw; pdata->b1 = iw*bolostep; if( iw < nw - 1 ) { pdata->b2 = pdata->b1 + bolostep - 1; } else { pdata->b2 = nbolo - 1 ; } /* Store other values common to all jobs. */ pdata->ntslice = ntslice; pdata->mntslice = mntslice; pdata->qua_data = qua_data; pdata->model_data = model_data; pdata->res_data = res_data; pdata->bstride = bstride; pdata->tstride = tstride; pdata->mbstride = mbstride; pdata->mtstride = mtstride; /* Submit the job to the workforce. */ thrAddJob( wf, 0, pdata, smf1_calcmodel_noi, 0, NULL, status ); } /* Wait for all jobs to complete. */ thrWait( wf, status ); /* Accumulate the results from all the worker threads. */ for( iw = 0; iw < nw; iw++ ) { pdata = job_data + iw; dat->chisquared[chunk] += pdata->chisquared; nchisq += pdata->nchisq; } /* Free the job data. */ job_data = astFree( job_data ); } } } /* Free resources */ if( box ) { box->pntr[0] = astFree( box->pntr[0] ); box->qual = astFree( box->qual ); smf_close_file( &box, status ); } /* Normalize chisquared for this chunk */ if( (*status == SAI__OK) && (nchisq >0) ) { dat->chisquared[chunk] /= (double) nchisq; } /* Clean Up */ if( kmap ) kmap = astAnnul( kmap ); }
void smf_calc_mapcoord( ThrWorkForce *wf, AstKeyMap *config, smfData *data, AstFrameSet *outfset, int moving, int *lbnd_out, int *ubnd_out, fts2Port fts_port, int flags, int *status ) { /* Local Variables */ AstSkyFrame *abskyfrm = NULL;/* Output SkyFrame (always absolute) */ AstMapping *bolo2map=NULL; /* Combined mapping bolo->map coordinates */ int bndndf=NDF__NOID; /* NDF identifier for map bounds */ void *data_pntr[1]; /* Array of pointers to mapped arrays in ndf */ int *data_index; /* Mapped DATA_ARRAY part of NDF */ int docalc=1; /* If set calculate the LUT */ int doextension=0; /* Try to write LUT to MAPCOORD extension */ smfFile *file=NULL; /* smfFile pointer */ AstObject *fstemp = NULL; /* AstObject version of outfset */ int ii; /* loop counter */ int indf_lat = NDF__NOID; /* Identifier for NDF to receive lat values */ int indf_lon = NDF__NOID; /* Identifier for NDF to receive lon values */ smfCalcMapcoordData *job_data=NULL; /* Array of job */ int lbnd[1]; /* Pixel bounds for 1d pointing array */ int lbnd_old[2]; /* Pixel bounds for existing LUT */ int lbnd_temp[1]; /* Bounds for bounds NDF component */ int lutndf=NDF__NOID; /* NDF identifier for coordinates */ AstMapping *map2sky_old=NULL;/* Existing mapping map->celestial coord. */ HDSLoc *mapcoordloc=NULL; /* HDS locator to the MAPCOORD extension */ int nw; /* Number of worker threads */ AstFrameSet *oldfset=NULL; /* Pointer to existing WCS info */ AstSkyFrame *oskyfrm = NULL; /* SkyFrame from the output WCS Frameset */ smfCalcMapcoordData *pdata=NULL; /* Pointer to job data */ double *lat_ptr = NULL; /* Pointer to array to receive lat values */ double *lon_ptr = NULL; /* Pointer to array to receive lon values */ int ubnd[1]; /* Pixel bounds for 1d pointing array */ int ubnd_old[2]; /* Pixel bounds for existing LUT */ int ubnd_temp[1]; /* Bounds for bounds NDF component */ int *lut = NULL; /* The lookup table */ dim_t nbolo=0; /* Number of bolometers */ dim_t ntslice=0; /* Number of time slices */ int nmap; /* Number of mapped elements */ AstMapping *sky2map=NULL; /* Mapping celestial->map coordinates */ size_t step; /* step size for dividing up work */ AstCmpMap *testcmpmap=NULL; /* Combined forward/inverse mapping */ AstMapping *testsimpmap=NULL;/* Simplified testcmpmap */ double *theta = NULL; /* Scan direction at each time slice */ int tstep; /* Time slices between full Mapping calculations */ int exportlonlat; /* Dump longitude and latitude values? */ /* Main routine */ if (*status != SAI__OK) return; /* How many threads do we get to play with */ nw = wf ? wf->nworker : 1; /* Initialize bounds to avoid compiler warnings */ lbnd_old[0] = 0; lbnd_old[1] = 0; ubnd_old[0] = 0; ubnd_old[1] = 0; /* Check for pre-existing LUT and de-allocate it. This will only waste time if the MAPCOORD extension is found to be valid and it has to be re-loaded from disk. */ smf_close_mapcoord( data, status ); /* Assert ICD data order */ smf_dataOrder( data, 1, status ); /* Get the data dimensions */ smf_get_dims( data, NULL, NULL, &nbolo, &ntslice, NULL, NULL, NULL, status ); /* If SMF__NOCREATE_FILE is not set, and file associated with an NDF, map a new MAPCOORD extension (or verify an existing one) */ if( !(flags & SMF__NOCREATE_FILE) && data->file ) { doextension = 1; } else { doextension = 0; docalc = 1; } /* Create / check for existing MAPCOORD extension */ if( doextension ) { file = data->file; /* Check type of file before proceeding */ if( file->isSc2store ) { *status = SAI__ERROR; errRep(FUNC_NAME, "File was opened by sc2store library (raw data?)", status); } if( !file->isTstream ) { *status = SAI__ERROR; errRep(FUNC_NAME, "File does not contain time stream data",status); } /* Get HDS locator to the MAPCOORD extension */ mapcoordloc = smf_get_xloc( data, "MAPCOORD", "MAP_PROJECTION", "UPDATE", 0, 0, status ); /* Obtain NDF identifier/placeholder for LUT in MAPCOORD extension*/ lbnd[0] = 0; ubnd[0] = nbolo*ntslice-1; lutndf = smf_get_ndfid( mapcoordloc, "LUT", "UPDATE", "UNKNOWN", "_INTEGER", 1, lbnd, ubnd, status ); if( *status == SAI__OK ) { /* store the NDF identifier */ file->mapcoordid = lutndf; /* Create sky to output grid mapping using the base coordinates to get the coordinates of the tangent point if it hasn't been done yet. */ sky2map = astGetMapping( outfset, AST__CURRENT, AST__BASE ); } /* Before mapping the LUT, first check for existing WCS information and LBND/UBND for the output map. If they are already correct don't bother re-calculating the LUT! */ if( *status == SAI__OK ) { /* Try reading in the WCS information */ kpg1Wread( mapcoordloc, "WCS", &fstemp, status ); oldfset = (AstFrameSet*)fstemp; if( *status == SAI__OK ) { /* Check that the old and new mappings are the same by checking that combining one with the inverse of the other reduces to a UnitMap. */ map2sky_old = astGetMapping( oldfset, AST__BASE, AST__CURRENT ); testcmpmap = astCmpMap( map2sky_old, sky2map, 1, " " ); testsimpmap = astSimplify( testcmpmap ); if( astIsAUnitMap( testsimpmap ) ) { /* The mappings are the same, now just check the pixel bounds in the output map */ lbnd_temp[0] = 1; ubnd_temp[0] = 2; bndndf = smf_get_ndfid( mapcoordloc, "LBND", "READ", "UNKNOWN", "_INTEGER", 1, lbnd_temp, ubnd_temp, status ); if( *status == SAI__OK ) { ndfMap( bndndf, "DATA", "_INTEGER", "READ", data_pntr, &nmap, status ); data_index = data_pntr[0]; if( *status == SAI__OK ) { lbnd_old[0] = data_index[0]; lbnd_old[1] = data_index[1]; } ndfAnnul( &bndndf, status ); } bndndf = smf_get_ndfid( mapcoordloc, "UBND", "READ", "UNKNOWN", "_INTEGER", 1, lbnd_temp, ubnd_temp, status ); if( *status == SAI__OK ) { ndfMap( bndndf, "DATA", "_INTEGER", "READ", data_pntr, &nmap, status ); data_index = data_pntr[0]; if( *status == SAI__OK ) { ubnd_old[0] = data_index[0]; ubnd_old[1] = data_index[1]; } ndfAnnul( &bndndf, status ); } if( *status == SAI__OK ) { /* If we get this far finally do the bounds check! */ if( (lbnd_old[0] == lbnd_out[0]) && (lbnd_old[1] == lbnd_out[1]) && (ubnd_old[0] == ubnd_out[0]) && (ubnd_old[1] == ubnd_out[1]) ) { docalc = 0; /* We don't have to re-calculate the LUT */ msgOutif(MSG__VERB," ",FUNC_NAME ": Existing LUT OK", status); } } } /* Bad status / AST errors at this point due to problems with MAPCOORD. Annul and continue calculating new MAPCOORD extension. */ astClearStatus; errAnnul(status); } else { /* Bad status due to non-existence of MAPCOORD. Annul and continue */ errAnnul(status); } } } /* If we need to calculate the LUT do it here */ if( docalc && (*status == SAI__OK) ) { msgOutif(MSG__VERB," ", FUNC_NAME ": Calculate new LUT", status); /* Get the increment in time slices between full Mapping calculations. The Mapping for intermediate time slices will be approximated. */ dim_t dimval; smf_get_nsamp( config, "TSTEP", data, &dimval, status ); tstep = dimval; /* Get space for the LUT */ if( doextension ) { /* Map the LUT array */ ndfMap( lutndf, "DATA", "_INTEGER", "WRITE", data_pntr, &nmap, status ); data_index = data_pntr[0]; if( *status == SAI__OK ) { lut = data_index; } else { errRep( FUNC_NAME, "Unable to map LUT in MAPCOORD extension", status); } } else { /* alloc the LUT and THETA arrays */ lut = astMalloc( (nbolo*ntslice)*sizeof(*(data->lut)) ); theta = astMalloc( ntslice*sizeof(*(data->theta)) ); } /* Retrieve the sky2map mapping from the output frameset (actually map2sky) */ oskyfrm = astGetFrame( outfset, AST__CURRENT ); sky2map = astGetMapping( outfset, AST__BASE, AST__CURRENT ); /* If the longitude and latitude is being dumped, create new NDFs to hold them, and map them. */ if( config ) { astMapGet0I( config, "EXPORTLONLAT", &exportlonlat ); if( exportlonlat ) { lon_ptr = smf1_calc_mapcoord1( data, nbolo, ntslice, oskyfrm, &indf_lon, 1, status ); lat_ptr = smf1_calc_mapcoord1( data, nbolo, ntslice, oskyfrm, &indf_lat, 2, status ); } } /* Invert the mapping to get Output SKY to output map coordinates */ astInvert( sky2map ); /* Create a SkyFrame in absolute coordinates */ abskyfrm = astCopy( oskyfrm ); astClear( abskyfrm, "SkyRefIs" ); astClear( abskyfrm, "SkyRef(1)" ); astClear( abskyfrm, "SkyRef(2)" ); if( *status == SAI__OK ) { /* --- Begin parellelized portion ------------------------------------ */ /* Start a new job context. Each call to thrWait within this context will wait until all jobs created within the context have completed. Jobs created in higher contexts are ignored by thrWait. */ thrBeginJobContext( wf, status ); /* Allocate job data for threads */ job_data = astCalloc( nw, sizeof(*job_data) ); if( *status == SAI__OK ) { /* Set up job data, and start calculating pointing for blocks of time slices in different threads */ if( nw > (int) ntslice ) { step = 1; } else { step = ntslice/nw; } for( ii=0; (*status==SAI__OK)&&(ii<nw); ii++ ) { pdata = job_data + ii; /* Blocks of time slices */ pdata->t1 = ii*step; pdata->t2 = (ii+1)*step-1; /* Ensure that the last thread picks up any left-over tslices */ if( (ii==(nw-1)) && (pdata->t1<(ntslice-1)) ) { pdata->t2=ntslice-1; } pdata->ijob = -1; pdata->lut = lut; pdata->theta = theta; pdata->lbnd_out = lbnd_out; pdata->moving = moving; pdata->ubnd_out = ubnd_out; pdata->tstep = tstep; pdata->lat_ptr = lat_ptr; pdata->lon_ptr = lon_ptr; pdata->fts_port = fts_port; /* Make deep copies of AST objects and unlock them so that each thread can then lock them for their own exclusive use */ pdata->abskyfrm = astCopy( abskyfrm ); astUnlock( pdata->abskyfrm, 1 ); pdata->sky2map = astCopy( sky2map ); astUnlock( pdata->sky2map, 1 ); /* Similarly, make a copy of the smfData, including only the header information which each thread will need in order to make calls to smf_rebin_totmap */ pdata->data = smf_deepcopy_smfData( data, 0, SMF__NOCREATE_FILE | SMF__NOCREATE_DA | SMF__NOCREATE_FTS | SMF__NOCREATE_DATA | SMF__NOCREATE_VARIANCE | SMF__NOCREATE_QUALITY, 0, 0, status ); smf_lock_data( pdata->data, 0, status ); } for( ii=0; ii<nw; ii++ ) { /* Submit the job */ pdata = job_data + ii; pdata->ijob = thrAddJob( wf, THR__REPORT_JOB, pdata, smfCalcMapcoordPar, 0, NULL, status ); } /* Wait until all of the jobs submitted within the current job context have completed */ thrWait( wf, status ); } /* End the current job context. */ thrEndJobContext( wf, status ); /* --- End parellelized portion -------------------------------------- */ /* Set the lut pointer in data to the buffer */ data->lut = lut; data->theta = theta; /* Write the WCS for the projection to the extension */ if( doextension ) { kpg1Wwrt( (AstObject*)outfset, "WCS", mapcoordloc, status ); /* Write the pixel bounds for the map to the extension */ lbnd_temp[0] = 1; /* Don't get confused! Bounds for NDF that will */ ubnd_temp[0] = 2; /* contain the bounds for the output 2d map! */ bndndf = smf_get_ndfid( mapcoordloc, "LBND", "UPDATE", "UNKNOWN", "_INTEGER", 1, lbnd_temp, ubnd_temp, status ); ndfMap( bndndf, "DATA", "_INTEGER", "WRITE", data_pntr, &nmap, status ); data_index = data_pntr[0]; if( *status == SAI__OK ) { data_index[0] = lbnd_out[0]; data_index[1] = lbnd_out[1]; } else { errRep( FUNC_NAME, "Unable to map LBND in MAPCOORD extension", status); } ndfAnnul( &bndndf, status ); bndndf = smf_get_ndfid( mapcoordloc, "UBND", "UPDATE", "UNKNOWN", "_INTEGER", 1, lbnd_temp, ubnd_temp, status ); ndfMap( bndndf, "DATA", "_INTEGER", "WRITE", data_pntr, &nmap, status ); data_index = data_pntr[0]; if( *status == SAI__OK ) { data_index[0] = ubnd_out[0]; data_index[1] = ubnd_out[1]; } else { errRep( FUNC_NAME, "Unable to map UBND in MAPCOORD extension", status); } ndfAnnul( &bndndf, status ); } } } /* Clean Up */ if( testsimpmap ) testsimpmap = astAnnul( testsimpmap ); if( testcmpmap ) testcmpmap = astAnnul( testcmpmap ); if( map2sky_old ) map2sky_old = astAnnul( map2sky_old ); if( oldfset ) oldfset = astAnnul( oldfset ); if (sky2map) sky2map = astAnnul( sky2map ); if (bolo2map) bolo2map = astAnnul( bolo2map ); if( abskyfrm ) abskyfrm = astAnnul( abskyfrm ); if( oskyfrm ) oskyfrm = astAnnul( oskyfrm ); if( mapcoordloc ) datAnnul( &mapcoordloc, status ); if( indf_lat != NDF__NOID ) ndfAnnul( &indf_lat, status ); if( indf_lon != NDF__NOID ) ndfAnnul( &indf_lon, status ); /* If we get this far, docalc=0, and status is OK, there must be a good LUT in there already. Map it so that it is accessible to the caller; "UPDATE" so that the caller can modify it if desired. */ if( (*status == SAI__OK) && (docalc == 0) ) { smf_open_mapcoord( data, "UPDATE", status ); } /* Clean up job data */ if( job_data ) { for( ii=0; (*status==SAI__OK)&&(ii<nw); ii++ ) { pdata = job_data + ii; if( pdata->data ) { smf_lock_data( pdata->data, 1, status ); smf_close_file( &(pdata->data), status ); } astLock( pdata->abskyfrm, 0 ); pdata->abskyfrm = astAnnul( pdata->abskyfrm ); astLock( pdata->sky2map, 0 ); pdata->sky2map = astAnnul( pdata->sky2map ); } job_data = astFree( job_data ); } }
void smf_checkmem_dimm( dim_t maxlen, inst_t instrument, int nrelated, smf_modeltype *modeltyps, dim_t nmodels, dim_t msize, AstKeyMap *keymap, size_t available, dim_t maxfilelen, size_t *necessary, int *status ) { /* Local Variables */ int dofft=0; /* flag if we need temp space for FFTs */ dim_t i; /* Loop counter */ dim_t gain_box; /* Length of blocks for GAI/COM model */ AstKeyMap *kmap=NULL; /* Local keymap */ dim_t nblock; /* Number of blocks for GAI/COM model */ size_t ncol; /* Number of columns */ size_t ndet; /* Number of detectors each time step */ size_t ndks; /* dksquid samples in a subarray, ncol*maxlen */ size_t nrow; /* Number of rows */ size_t nsamp; /* bolo samples in a subarray, ndet*maxlen */ const char *tempstr=NULL; /* Temporary pointer to static char buffer */ size_t total = 0; /* Total bytes required */ /* Main routine */ if (*status != SAI__OK) return; /* Check inputs */ if( maxlen < 1 ) { *status = SAI__ERROR; errRep("", FUNC_NAME ": maxlen cannot be < 1", status); return; } if( (nrelated < 1) || (nrelated > SMF__MXSMF) ) { msgSeti("NREL",nrelated); msgSeti("MAXREL",SMF__MXSMF); *status = SAI__ERROR; errRep("", FUNC_NAME ": nrelated, ^NREL, must be in the range [1,^MAXREL]", status); return; } if( modeltyps ) { if( nmodels < 1 ) { *status = SAI__ERROR; errRep("", FUNC_NAME ": modeltyps specified, mmodels cannot be < 1", status); return; } } if( *status == SAI__OK ) { /* Work out the data dimensions */ switch( instrument ) { case INST__SCUBA2: /* Kludgey, but at least we check SC2STORE__COL_INDEX so this will help us catch possible future problems if order is changed */ if( SC2STORE__COL_INDEX ) { ncol = 32; nrow = 40; } else { ncol = 40; nrow = 32; } ndet = ncol*nrow; break; default: *status = SAI__ERROR; errRep("", FUNC_NAME ": Invalid instrument given.", status); } } /* Number of samples in a full data cube for one subarray */ nsamp = ndet*maxlen; /* Check to see if we need to do filtering as part of pre-processing. If so, we need an extra nsamp-sized buffer to store the FFT. */ smf_get_cleanpar( keymap, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, &dofft, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, status ); /* Calculate memory usage of static model components: -------------------- */ if( *status == SAI__OK ) { total += nsamp*smf_dtype_sz(SMF__DOUBLE,status)*nrelated; /* RES */ total += nsamp*smf_dtype_sz(SMF__INTEGER,status)*nrelated; /* LUT */ total += nsamp*smf_dtype_sz(SMF__QUALTYPE,status)*nrelated; /* QUA */ } /* Add on memory usage for the JCMTState (one per time slice per array) */ if( *status == SAI__OK ) { total += maxlen*sizeof(JCMTState)*nrelated; } /* Add on space for dark squids */ ndks = ncol*maxlen; total += ndks*smf_dtype_sz(SMF__DOUBLE,status)*nrelated; /* Add on space for fakemap */ astMapGet0C( keymap, "FAKEMAP", &tempstr ); if( tempstr ) { total += msize*sizeof(double); } /* Apply fudge factor */ total *= CHECKMEM_FUDGE; /* Calculate memory usage of dynamic model components: ------------------- */ if( *status == SAI__OK ) { /* Most of these will have data arrays associated with each subarray (hence the multiplication by nrelated). An exception is SMF__COM for which a single-common mode is used across all subarrays. */ if( modeltyps ) { for( i=0; i<nmodels; i++ ) { switch( modeltyps[i] ) { case SMF__NOI: /* SMF__NOI also estimates the noise in each detector from the power spectra, requiring a temporary buffer to store an FFT. Currently we just store one variance per detector */ dofft = 1; total += ndet*smf_dtype_sz(SMF__DOUBLE,status)*nrelated; break; case SMF__COM: CHECK_MASK("COM") total += maxlen*smf_dtype_sz(SMF__DOUBLE,status); break; case SMF__EXT: total += nsamp*smf_dtype_sz(SMF__DOUBLE,status)*nrelated; break; case SMF__DKS: total += (maxlen + nrow*3)*ncol*smf_dtype_sz(SMF__DOUBLE,status) * nrelated; break; case SMF__GAI: /* Every COM.GAIN_BOX samples there are 3 planes of data corresponding to each bolometer in the subarray. The conversion of COM.GAIN_BOX from seconds to samples within smf_get_nsamp assumes a sample rate of 200 Hz. Later downsampling may result in a lower sample rate, but at least we are erring on the conservative side by assuming 200 Hz. */ if( astMapGet0A( keymap, "COM", &kmap ) ) { smf_get_nsamp( kmap, "GAIN_BOX", NULL, &gain_box, status ); nblock = maxlen/gain_box; if( nblock == 0 ) nblock = 1; total += nblock*3*nrow*ncol*smf_dtype_sz(SMF__DOUBLE,status)*nrelated; kmap = astAnnul( kmap ); } break; case SMF__FLT: /* Presently the filter temporarily transforms the entire data cube into a second array. We therefore need to ensure enough memory to temporarily store the data cube twice. */ CHECK_MASK("FLT") dofft = 1; total += nsamp*smf_dtype_sz(SMF__DOUBLE,status)*nrelated; break; case SMF__PLN: total += nsamp*smf_dtype_sz(SMF__DOUBLE,status)*nrelated; break; case SMF__SMO: total += nsamp*smf_dtype_sz(SMF__DOUBLE,status)*nrelated; break; case SMF__TMP: /* An externally supplied template. The model just stores the gain, offset and correlation coefficient for each bolometer, similar to SMF__GAI except with only one chunk considered */ total += 3*nrow*ncol*smf_dtype_sz(SMF__DOUBLE,status)*nrelated; break; case SMF__TWO: /* two common-mode time series and coefficients for all the detectors */ total += 2*(ndet+maxlen)*smf_dtype_sz(SMF__DOUBLE,status)*nrelated; break; case SMF__AST: /* Mostly accounted for as static memory usage above, but add space for mask if required */ CHECK_MASK("AST") break; default: *status = SAI__ERROR; errRep("", FUNC_NAME ": Invalid smf_modeltype given.", status); } /* Exit on bad status */ if( *status != SAI__OK ) { i = nmodels; } } } /* Calculate temporary space here -------------------------------------- */ if( *status == SAI__OK ) { size_t temp=0; /* current temp memory required */ size_t maxtemp=0; /* max temp memory required */ /* Some temp space required when we initially read in the data. Normally smf_concat_smfGroup will smf_open_and_flatfield one file at a time before copying into the concatenated data array. If there are N time slices in a file, smf_open_and_flatfield will usually require space both for the raw, and double-precision flatfielded data simultaneously. This is an upper limit, since if previously flatfielded data are provided no extra space for the raw data will be required. We use the supplied maxfilelen to figure out this maximum temporary buffer size. Remember to account for bolo data, dark squids, and JCMTState. Also, the way smf_iteratemap is currently written, a concatenated memory-mapped pointing LUT is initially created, and then copied to a new malloc'd array, so we need extra temp space for the full LUT. */ temp = (ndet + ncol)*( smf_dtype_sz(SMF__INTEGER,status)+ smf_dtype_sz(SMF__DOUBLE,status) )*maxfilelen + 2*maxfilelen*sizeof(JCMTState); temp += nsamp*smf_dtype_sz(SMF__INTEGER,status)*nrelated; if( temp > maxtemp ) maxtemp = temp; /* If we are doing FFTs */ if( dofft ) { temp = nsamp*smf_dtype_sz(SMF__DOUBLE,status)*nrelated; if( temp > maxtemp ) maxtemp = temp; } /* Data cleaning... seems to be a lot. Just putting in a rough estimate of 3 extra full sized arrays for the standard options. */ temp = 3.*nsamp*smf_dtype_sz(SMF__DOUBLE,status); if( temp > maxtemp ) maxtemp = temp; /* Add on the maximum chunk of temporary memory that is required */ total += maxtemp; } /* Set bad status if too big */ if( (*status == SAI__OK) && (total > available) ) { *status = SMF__NOMEM; msgSeti("REQ",total/SMF__MIB); msgSeti("AVAIL",available/SMF__MIB); errRep("", FUNC_NAME ": Requested memory ^REQ MiB for map exceeds available ^AVAIL MiB", status); } /* Return the required space */ if( necessary ) { *necessary = total; } } }