void smf_lock_related( smfArray *data, int lock, int *status ){ /* Local Variables */ size_t i; /* Loop counter */ if( *status != SAI__OK ) return; if( !data ) { *status = SAI__ERROR; errRep( "", "smf_lock_related: NULL smfArray provided", status ); return; } for( i=0; i<data->ndat; i++ ) { smf_lock_data( data->sdata[i], lock, status ); } }
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 smfCalcMapcoordPar( void *job_data_ptr, int *status ) { AstSkyFrame *abskyfrm=NULL; smfData *data=NULL; fts2Port fts_port; int *lbnd_out=NULL; int *lut=NULL; int moving; int *ubnd_out=NULL; dim_t nbolo; /* number of bolometers */ dim_t ntslice; /* number of time slices */ smfCalcMapcoordData *pdata=NULL; /* Pointer to job data */ AstMapping *sky2map=NULL; double *theta = NULL; struct timeval tv1; /* Timer */ struct timeval tv2; /* Timer */ if( *status != SAI__OK ) return; /* Pointer to the data that this thread will process */ pdata = job_data_ptr; /* Check for valid inputs */ if( !pdata ) { *status = SAI__ERROR; errRep( "", "smfCalcMapcoordPar: No job data supplied", status ); return; } /* Extract values from pdata */ abskyfrm = pdata->abskyfrm; data = pdata->data; lut = pdata->lut; theta = pdata->theta; lbnd_out = pdata->lbnd_out; moving = pdata->moving; sky2map = pdata->sky2map; ubnd_out = pdata->ubnd_out; fts_port = pdata->fts_port; smf_get_dims( data, NULL, NULL, &nbolo, &ntslice, NULL, NULL, NULL, status ); /* if t1 past end of the work, nothing to do so we return */ if( pdata->t1 >= ntslice ) { msgOutif( SMF__TIMER_MSG, "", "smfCalcMapcoordPar: nothing for thread to do, returning", status); return; } /* Debugging message indicating thread started work */ msgOutiff( SMF__TIMER_MSG, "", "smfCalcMapcoordPar: thread starting on tslices %zu -- %zu", status, pdata->t1, pdata->t2 ); smf_timerinit( &tv1, &tv2, status ); /* Lock the supplied AST object pointers for exclusive use by this thread. The invoking thread should have unlocked them before starting this job. */ astLock( abskyfrm, 0 ); astLock( sky2map, 0 ); smf_lock_data( data, 1, status ); /* Calculate and store the LUT values for the range of time slices being processed by this thread. A generic algorithm is used for moving targets, but a faster algorithm can be used for stationary targets. */ smf_coords_lut( data, pdata->tstep, pdata->t1, pdata->t2, abskyfrm, sky2map, moving, lbnd_out, ubnd_out, fts_port, lut + pdata->t1*nbolo, theta + pdata->t1, pdata->lon_ptr, pdata->lat_ptr, status ); /* Unlock the supplied AST object pointers so that other threads can use them. */ smf_lock_data( data, 0, status ); astUnlock( abskyfrm, 1 ); astUnlock( sky2map, 1 ); msgOutiff( SMF__TIMER_MSG, "", "smfCalcMapcoordPar: thread finishing tslices %zu -- " "%zu (%.3f sec)", status, pdata->t1, pdata->t2, smf_timerupdate(&tv1, &tv2, status) ); }
void smf__calc_wvm_job( void *job_data, int *status ) { struct timeval tv1; struct timeval tv2; smfData * curdata = NULL; smfArray * thesedata; double prevtime = VAL__BADD; double prevtau = VAL__BADD; double lastgoodtau = VAL__BADD; /* most recent good tau */ size_t lastgoodidx = SMF__BADSZT; /* index of most recent good value */ size_t nbadidx = 0; /* number of time slices in the current gap */ size_t maxgap; dim_t t1; dim_t t2; size_t nrelated; double * taudata = NULL; size_t ngood = 0; dim_t i; smfCalcWvmJobData *pdata; double amprev; AstKeyMap * extpars; if (*status != SAI__OK) return; pdata = (smfCalcWvmJobData *)job_data; t1 = pdata->t1; t2 = pdata->t2; amprev = pdata->airmass; thesedata = pdata->thesedata; taudata = pdata->taudata; nrelated = thesedata->ndat; extpars = pdata->extpars; maxgap = pdata->maxgap; /* Lock the AST pointers to this thread */ astLock( extpars, 0 ); for (i=0;i<thesedata->ndat;i++) { smf_lock_data( (thesedata->sdata)[i], 1, status ); } /* Debugging message indicating thread started work */ msgOutiff( SMF__TIMER_MSG, "", "smfCalcSmoothedWVM: thread starting on slices %" DIM_T_FMT " -- %" DIM_T_FMT, status, t1, t2 ); smf_timerinit( &tv1, &tv2, status); for (i=t1; i<=t2; i++) { if (!curdata) { SELECT_DATA( thesedata, curdata, VAL__BADD, wvm_time, i ); } if (curdata) smf_tslice_ast( curdata, i, 0, NO_FTS, status ); if ( !curdata || curdata->hdr->state->wvm_time == VAL__BADD ) { /* Try the other datas */ SELECT_DATA( thesedata, curdata, VAL__BADD, wvm_time, i ); } if (*status != SAI__OK) break; /* if we have no good data we store a bad value */ if (!curdata) { prevtau = VAL__BADD; } else { const JCMTState * state = NULL; state = curdata->hdr->state; /* if we have old values from the WVM or no value we don't trust them */ if ( state->wvm_time != VAL__BADD && (fabs(state->wvm_time - state->rts_end) * SPD) < 60.0 ) { /* Only calculate a tau when we have new values */ if ( prevtime != state->wvm_time ) { double thistau = VAL__BADD; double airmass = VAL__BADD; prevtime = state->wvm_time; airmass = state->tcs_airmass; if (airmass == VAL__BADD) { airmass = amprev; } else { amprev = airmass; } thistau = smf_calc_wvm( curdata->hdr, airmass, extpars, status ); /* Check status and/or value of tau */ if ( thistau == VAL__BADD ) { if ( *status == SAI__OK ) { *status = SAI__ERROR; errRepf("", "Error calculating tau from WVM temperatures at time slice %" DIM_T_FMT, status, i); } } else if ( thistau < 0.0 ) { msgOutiff( MSG__QUIET, "", "WARNING: Negative WVM tau calculated (%g). Ignoring.", status, thistau ); prevtau = VAL__BADD; } else { prevtau = thistau; } } else { /* We use the previous tau since we should have calculated it earlier */ } } else { /* No good reading so tau is bad */ prevtau = VAL__BADD; } } /* Prevtau is the tau that should be assigned to the current position */ /* see about gaps */ if (prevtau == VAL__BADD) { nbadidx++; } else { /* we have a good value so we now have to see if there is a gap to fill */ if (i > 0 && lastgoodidx != (i-1) ) { /* the previous value was bad so we may have to patch up if small */ if ( nbadidx < maxgap ) { size_t j; if (lastgoodidx == SMF__BADSZT) { /* gap is at the start so fill with current value */ for (j=t1; j<i;j++) { taudata[j] = prevtau; ngood++; } } else { /* replace with mean value */ double meantau = (lastgoodtau + prevtau) / 2.0; for (j=lastgoodidx+1; j<i; j++) { taudata[j] = meantau; ngood++; } } } } /* we know this index was good */ lastgoodidx = i; lastgoodtau = prevtau; nbadidx = 0; ngood++; } /* Store the current tau value */ taudata[i] = prevtau; } /* if the last value in the time series was bad we need to see about filling with the last good value */ if (*status == SAI__OK && nbadidx > 0 && nbadidx < maxgap) { for (i=lastgoodidx+1; i<=t2; i++) { taudata[i] = lastgoodtau; ngood++; } } /* Report the time taken in this thread. */ msgOutiff( SMF__TIMER_MSG, "", "smfCalcSmoothedWVM: thread finishing slices %" DIM_T_FMT " -- %" DIM_T_FMT " (%zu good) (%.3f sec)", status, t1, t2, ngood, smf_timerupdate( &tv1, &tv2, status ) ); /* Store number of good values */ pdata->ngood = ngood; /* Unlock the AST pointers from this thread */ astUnlock( extpars, 1 ); for (i=0;i<thesedata->ndat;i++) { smf_lock_data( (thesedata->sdata)[i], 0, status ); } }
void smf_calc_smoothedwvm ( ThrWorkForce *wf, const smfArray * alldata, const smfData * adata, AstKeyMap* extpars, double **wvmtau, size_t *nelems, size_t *ngoodvals, int * status ) { size_t i; size_t nrelated = 0; /* Number of entries in smfArray */ size_t nframes = 0; /* Number of timeslices */ size_t ngood = 0; /* Number of elements with good tau */ double *taudata = NULL; /* Local version of WVM tau */ const smfArray * thesedata = NULL; /* Collection of smfDatas to analyse */ smfArray * tmpthesedata = NULL; /* Local version of adata in a smfArray */ if (*status != SAI__OK) return; if (alldata && adata) { *status = SAI__ERROR; errRep("", "smf_calc_smoothedwvm can not be given non-NULL alldata and non-NULL adata arguments" " (possible programming error)", status ); return; } if (!alldata && !adata) { *status = SAI__ERROR; errRep("", "smf_calc_smoothedwvm: One of alldata or adata must be non-NULL", status); return; } if (!wvmtau) { *status = SAI__ERROR; errRep("", "Must supply a non-NULL pointer for wvmtau argument" " (possible programming error)", status ); return; } /* if we have a single smfData put it in a smfArray */ if (alldata) { if (alldata->ndat == 0 ) { *status = SAI__ERROR; errRep("", "No smfDatas present in supplied smfArray for WVM smoothing" " (possible programming error)", status ); return; } thesedata = alldata; } else { tmpthesedata = smf_create_smfArray( status ); if (tmpthesedata) { tmpthesedata->owndata = 0; /*not owned by the smfArray */ /* we know that the smfData here will not be touched in this function so we do the BAD thing of casting const to non-const */ smf_addto_smfArray( tmpthesedata, (smfData *)adata, status ); } thesedata = tmpthesedata; } /* Check that we have headers and that the smfData are the same length */ nrelated = thesedata->ndat; for (i = 0; i < nrelated; i++ ) { smfData * data = (thesedata->sdata)[i]; smfHead * hdr = data->hdr; dim_t thisframes = 0; if ( !hdr) { *status = SAI__ERROR; errRepf( "", "smfData %zu has no header. Aborting WVM smoothing", status, i ); return; } smf_get_dims( data, NULL, NULL, NULL, &thisframes, NULL, NULL, NULL, status ); if (!nframes) nframes = thisframes; if (thisframes != nframes) { *status = SAI__ERROR; errRepf( "", "smfData %zu has different length. Aborting WVM smoothing", status, i ); return; } } /* We will need the earliest and last airmass value in order to calculate a zenith tau */ /* As a first step, just fill the time series with calculated WVM tau values even though we know there are about 240 fewer tau readings in reality. This initial approach will make it easier to use the smoothed data directly rather than having to interpolate from the 1.2 second data back into the 200 Hz data. */ taudata = astCalloc( nframes, sizeof(*taudata) ); if (*status == SAI__OK) { double amprev = VAL__BADD; double steptime; size_t maxgap; struct timeval tv1; struct timeval tv2; smfCalcWvmJobData *job_data = NULL; int nworker; /* We need to know the steptime so we can define the max good gap in seconds and convert it to steps*/ steptime = (thesedata->sdata)[0]->hdr->steptime; maxgap = (size_t)( 5.0 / steptime ); /* 5 seconds is just larger than 2 WVM readings */ /* Assume all files have the same airmass information */ smf_find_airmass_interval( (thesedata->sdata)[0]->hdr, &rev, NULL, NULL, NULL, status ); smf_timerinit( &tv1, &tv2, status ); /* Create structures used to pass information to the worker threads. */ nworker = wf ? wf->nworker : 1; job_data = astMalloc( nworker*sizeof( *job_data ) ); if (*status == SAI__OK) { dim_t tstep; int iworker; smfCalcWvmJobData *pdata = NULL; /* Get the number of time slices to process in each thread. */ if( nworker > (int) nframes ) { tstep = 1; } else { tstep = nframes/nworker; } /* to return the same values for one thread and multiple threads we need to break the threads on wvm sample boundaries wherever possible. We make an initial estimate of the number of WVM measurements by assuming one every two seconds. */ { smfData * curdata = NULL; size_t nwvm = 0; double prevtime = VAL__BADD; double curtime; size_t *boundaries = astGrow(NULL, nframes*(size_t)(steptime/2.0), sizeof(*boundaries)); for (i=0; i<nframes; i++) { if (!curdata) { SELECT_DATA( thesedata, curdata, VAL__BADD, wvm_time, i ); } if (curdata) smf_tslice_ast( curdata, i, 0, NO_FTS, status ); if ( !curdata || curdata->hdr->state->wvm_time == VAL__BADD ) { /* Try the other datas */ SELECT_DATA( thesedata, curdata, VAL__BADD, wvm_time, i ); } if (*status != SAI__OK) break; if (!curdata) { curtime = VAL__BADD; } else { curtime = curdata->hdr->state->wvm_time; } if (curtime != prevtime || nwvm == 0 ) { /* Store the index in the boundaries array */ nwvm++; boundaries = astGrow(boundaries, nwvm, sizeof(*boundaries)); if (!boundaries) { /* this is serious */ if (*status == SAI__OK) *status = SAI__ERROR; errRep("", "Error allocating temporary memory for WVM calculation\n", status ); break; } boundaries[nwvm-1] = i; prevtime = curtime; } } /* No point using too many threads */ if (*status == SAI__OK) { if (nworker >= (int)nwvm) { nworker = nwvm; /* Allocate a measurement per thread */ for( iworker = 0; iworker < nworker; iworker++ ) { pdata = job_data + iworker; pdata->t1 = boundaries[iworker]; if (iworker+1 < nworker) pdata->t2 = boundaries[iworker+1]-1; } /* Ensure that the last thread picks up any left-over time slices */ pdata->t2 = nframes - 1; } else { /* Allocate the workers to slices of approximate size tstep */ size_t prevend = 0; /* End of previous slice */ size_t prevbnd = 0; /* Index into previous boundaries[] array selection */ for( iworker = 0; iworker < nworker; iworker++ ) { size_t belowidx = prevend+1; size_t aboveidx = nframes; size_t lbnd; size_t ubnd; size_t j; size_t guess; pdata = job_data + iworker; if (iworker == 0) { /* always start at the beginning */ pdata->t1 = 0; } else { /* Start one after the previous block */ pdata->t1 = prevend + 1; } /* Now we have to find the end of this slice */ guess = (iworker*tstep) + tstep - 1; if (guess <= pdata->t1) guess = pdata->t1 + tstep; /* find nearest boundaries */ for (j=prevbnd; j<nwvm; j++) { if ( boundaries[j] > guess ) { aboveidx = boundaries[j]; ubnd = j; if (j>0) { belowidx = boundaries[j-1]; lbnd = j -1 ; } else { lbnd = 0; } break; } } /* Choose the closest, making sure that we are not choosing t1 */ if ( (guess - belowidx < aboveidx - guess) && belowidx > pdata->t1 ) { pdata->t2 = belowidx - 1; prevbnd = lbnd; } else { pdata->t2 = aboveidx - 1; prevbnd = ubnd; } prevend = pdata->t2; if (prevend == nframes - 1 && iworker < nworker-1 ) { /* we have run out of slices so just use fewer workers */ nworker = iworker + 1; break; } } /* Ensure that the last thread picks up any left-over time slices */ pdata->t2 = nframes - 1; } /* Tidy up */ boundaries = astFree( boundaries ); } } /* Store all the other info needed by the worker threads, and submit the jobs to fix the steps in each bolo, and then wait for them to complete. */ for( iworker = 0; iworker < nworker; iworker++ ) { smfArray *thrdata = NULL; pdata = job_data + iworker; pdata->nframes = nframes; pdata->airmass = amprev; /* really need to get it from the start of each chunk */ pdata->taudata = taudata; pdata->maxgap = maxgap; /* Need to copy the smfDatas and create a new smfArray for each thread */ thrdata = smf_create_smfArray( status ); for (i=0;i<nrelated;i++) { smfData *tmpdata = NULL; tmpdata = smf_deepcopy_smfData( wf, (thesedata->sdata)[i], 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( tmpdata, 0, status ); smf_addto_smfArray( thrdata, tmpdata, status ); } pdata->thesedata = thrdata; /* Need to do a deep copy of ast data and unlock them */ pdata->extpars = astCopy(extpars); astUnlock( pdata->extpars, 1 ); /* Pass the job to the workforce for execution. */ thrAddJob( wf, THR__REPORT_JOB, pdata, smf__calc_wvm_job, 0, NULL, status ); } /* Wait for the workforce to complete all jobs. */ thrWait( wf, status ); /* Now free the resources we allocated during job creation and calculate the number of good values */ for( iworker = 0; iworker < nworker; iworker++ ) { smfArray * thrdata; pdata = job_data + iworker; astLock( pdata->extpars, 0 ); pdata->extpars = astAnnul( pdata->extpars ); thrdata = pdata->thesedata; for (i=0;i<thrdata->ndat;i++) { smf_lock_data( (thrdata->sdata)[i], 1, status ); } smf_close_related( wf, &thrdata, status ); ngood += pdata->ngood; } } job_data = astFree( job_data ); msgOutiff( MSG__NORM, "", FUNC_NAME ": %f s to calculate unsmoothed WVM tau values", status, smf_timerupdate(&tv1,&tv2,status) ); } if (*status == SAI__OK && extpars) { /* Read extpars to see if we need to smooth */ double smoothtime = VAL__BADD; if (astMapGet0D( extpars, "SMOOTHWVM", &smoothtime ) ) { if (smoothtime != VAL__BADD && smoothtime > 0.0) { smfData * data = (thesedata->sdata)[0]; double steptime = data->hdr->steptime; dim_t boxcar = (dim_t)( smoothtime / steptime ); msgOutiff( MSG__VERB, "", "Smoothing WVM data with %f s tophat function", status, smoothtime ); smf_tophat1D( taudata, nframes, boxcar, NULL, 0, 0.0, status ); /* The tophat smoothing puts a bad value at the start and end of the time series so we replace that with the adjacent value since the step time is much smaller than WVM readout time. If more than one value is bad we do not try to find the good value. */ taudata[0] = taudata[1]; taudata[nframes-1] = taudata[nframes-2]; } } } /* Use this to get the raw WVM output for debugging */ /* if (*status == SAI__OK) { smfData *data = (thesedata->sdata)[0]; smfHead *hdr = data->hdr; printf("# IDX TAU RTS_NUM RTS_END WVM_TIME\n"); for (i=0; i<nframes;i++) { JCMTState * state; state = &(hdr->allState)[i]; printf("%zu %.*g %d %.*g %.*g\n", i, DBL_DIG, taudata[i], state->rts_num, DBL_DIG, state->rts_end, DBL_DIG, state->wvm_time); } } */ /* Free resources */ if (tmpthesedata) smf_close_related( wf, &tmpthesedata, status ); if (*status != SAI__OK) { if (taudata) taudata = astFree( taudata ); *nelems = 0; *ngoodvals = 0; } else { *wvmtau = taudata; *nelems = nframes; *ngoodvals = ngood; } }
static void smf1_correct_extinction( void *job_data_ptr, int *status ) { /* * Name: * smf1_correct_extinction * Purpose: * Executed in a worker thread to do various calculations for * smf_correct_extinction. * Invocation: * smf1_correct_extinction( void *job_data_ptr, int *status ) * Arguments: * job_data_ptr = SmfCorrectExtinctionData * (Given) * Data structure describing the job to be performed by the worker * thread. * status = int * (Given and Returned) * Inherited status. */ /* Local Variables: */ SmfCorrectExtinctionData *pdata; double airmass; /* Airmass */ double state_airmass; /* Airmass read from header */ double state_az_ac2; /* Elevation read from header */ double amprev; /* Previous airmass in loop */ double *azel = NULL; /* AZEL coordinates */ size_t base; /* Offset into 3d data array */ double extcorr = 1.0; /* Extinction correction factor */ dim_t i; /* Loop counter */ dim_t k; /* Loop counter */ AstFrameSet *wcs = NULL; /* Pointer to AST WCS frameset */ AstFrameSet *state_wcs = NULL; /* Pointer to copy of frameset from header */ /* Check inherited status */ if( *status != SAI__OK ) return; /* Get a pointer that can be used for accessing the required items in the supplied structure. */ pdata = (SmfCorrectExtinctionData *) job_data_ptr; /* It is more efficient to call astTranGrid than astTran2 Allocate memory in adaptive mode just in case. */ if (pdata->method == SMF__EXTMETH_FULL || pdata->method == SMF__EXTMETH_ADAPT ) { azel = astMalloc( (2*pdata->npts)*sizeof(*azel) ); } amprev = pdata->amfirst; /* Assume we are using quick mode for all time slices. */ pdata->allquick = 1; for ( k=pdata->f1; k<=pdata->f2 && (*status == SAI__OK) ; k++) { /* Flags to indicate which mode we are using for this time slice */ int quick = 0; /* use single airmass */ int adaptive = 0; /* switch from quick to full if required */ if (pdata->method == SMF__EXTMETH_SINGLE) { quick = 1; } else if (pdata->method == SMF__EXTMETH_ADAPT) { quick = 1; adaptive = 1; } else { pdata->allquick = 0; } /* Call tslice_ast to update the header for the particular timeslice. If we're in QUICK mode then we don't need the WCS. Use a mutex to prevent multiple threads writing to the header at the same time. */ thrMutexLock( &data_mutex, status ); smf_lock_data( pdata->data, 1, status ); smf_tslice_ast( pdata->data, k, !quick, NO_FTS, status ); /* Copy the required bit of the header into thread-local storage. */ if( *status == SAI__OK ) { state_airmass = pdata->hdr->state->tcs_airmass; state_az_ac2 = pdata->hdr->state->tcs_az_ac2; if( !quick && pdata->tau != VAL__BADD && pdata->hdr->wcs) state_wcs = astCopy( pdata->hdr->wcs ); } /* Unlock the AST Objects in the smfData then unlock the local mutex. */ smf_lock_data( pdata->data, 0, status ); thrMutexUnlock( &data_mutex, status ); /* Abort if an error has occurred. */ if( *status != SAI__OK ) break; /* Read the WVM tau value if required */ if (pdata->tausrc == SMF__TAUSRC_WVMRAW) { pdata->tau = pdata->wvmtau[k]; } /* in all modes we need to keep track of the previous airmass in case we need to gap fill bad telescope data */ if ( quick && pdata->ndims == 2 ) { /* for 2-D we use the FITS header directly */ /* This may change depending on exact FITS keyword */ airmass = pdata->amstart; /* speed is not an issue for a 2d image */ adaptive = 0; } else { /* Else use airmass value in state structure */ airmass = state_airmass; /* if things have gone bad use the previous value else store this value. We also need to switch to quick mode and disable adaptive. */ if (airmass == VAL__BADD || airmass == 0.0 ) { if ( state_az_ac2 != VAL__BADD ) { /* try the elevation */ airmass = palAirmas( M_PI_2 - state_az_ac2 ); } else { airmass = amprev; quick = 1; adaptive = 0; } } else { amprev = airmass; } } /* If we're using the FAST application method, we assume a single airmass and tau for the whole array but we have to consider adaptive mode. If the tau is bad the extinction correction must also be bad. */ if( pdata->tau == VAL__BADD) { extcorr = VAL__BADD; } else if (quick) { /* we have an airmass, see if we need to provide per-pixel correction */ if (adaptive) { if (is_large_delta_atau( airmass, pdata->hdr->state->tcs_az_ac2, pdata->tau, status) ) { /* we need WCS if we disable fast mode */ quick = 0; pdata->allquick = 0; thrMutexLock( &data_mutex, status ); smf_lock_data( pdata->data, 1, status ); smf_tslice_ast( pdata->data, k, 1, NO_FTS, status ); state_airmass = pdata->hdr->state->tcs_airmass; state_az_ac2 = pdata->hdr->state->tcs_az_ac2; if (pdata->hdr->wcs) state_wcs = astCopy( pdata->hdr->wcs ); smf_lock_data( pdata->data, 0, status ); thrMutexUnlock( &data_mutex, status ); } } if (quick) extcorr = exp(airmass*pdata->tau); } /* The previous test may have forced quick off so we can not combine the tests in one if-then-else block */ if (!quick && pdata->tau != VAL__BADD ) { /* Not using quick so retrieve WCS to obtain elevation info */ wcs = state_wcs; /* Check current frame, store it and then select the AZEL coordinate system */ if (wcs != NULL) { if (strcmp(astGetC(wcs,"SYSTEM"), "AZEL") != 0) { astSet( wcs, "SYSTEM=AZEL" ); } /* Transfrom from pixels to AZEL */ astTranGrid( wcs, 2, pdata->lbnd, pdata->ubnd, 0.1, 1000000, 1, 2, pdata->npts, azel ); } else { /* this time slice may have bad telescope data so we trap for this and re-enable "quick" with a default value. We'll only get here if airmass was good but SMU was bad so we use the good airmass. The map-maker won't be using this data but we need to use something plausible so that we do not throw off the FFTs */ quick = 1; extcorr = exp(airmass*pdata->tau); } } /* Loop over data in time slice. Start counting at 1 since this is the GRID coordinate frame */ base = pdata->npts * k; /* Offset into 3d data array (time-ordered) */ for (i=0; i < pdata->npts && ( *status == SAI__OK ); i++ ) { /* calculate array indices - assumes that astTranGrid fills up azel[] array in same order as bolometer data are aligned */ size_t index; if ( pdata->isTordered ) { index = base + i; } else { index = k + (pdata->nframes * i); } if (!quick) { if (pdata->tau != VAL__BADD) { double zd; zd = M_PI_2 - azel[pdata->npts+i]; airmass = palAirmas( zd ); extcorr = exp(airmass*pdata->tau); } else { extcorr = VAL__BADD; } } if( pdata->allextcorr ) { /* Store extinction correction factor */ pdata->allextcorr[index] = extcorr; } else { /* Otherwise Correct the data */ if (extcorr != VAL__BADD) { if( pdata->indata && (pdata->indata[index] != VAL__BADD) ) { pdata->indata[index] *= extcorr; } /* Correct the variance */ if( pdata->vardata && (pdata->vardata[index] != VAL__BADD) ) { pdata->vardata[index] *= extcorr * extcorr; } } else { if (pdata->indata) pdata->indata[index] = VAL__BADD; if (pdata->vardata) pdata->vardata[index] = VAL__BADD; } } } /* Note that we do not need to free "wcs" or revert its SYSTEM since smf_tslice_ast will replace the object immediately. */ } /* End loop over timeslice */ azel = astFree( azel ); }
int smf_correct_extinction(ThrWorkForce *wf, smfData *data, smf_tausrc *thetausrc, smf_extmeth method, AstKeyMap * extpars, double tau, double *allextcorr, double **wvmtaucache, int *status) { /* Local variables */ int allquick = 0; /* Is the extinction for all bolometers the same? */ double amstart = VAL__BADD; /* Airmass at start */ double amend = VAL__BADD; /* Airmass at end */ double elstart = VAL__BADD; /* Elevation at start (radians) */ double elend = VAL__BADD;/* Elevation at end (radians) */ smfHead *hdr = NULL; /* Pointer to full header struct */ double *indata = NULL; /* Pointer to data array */ int isTordered; /* data order of input data */ int lbnd[2]; /* Lower bound */ size_t ndims; /* Number of dimensions in input data */ dim_t nframes = 0; /* Number of frames */ dim_t npts = 0; /* Number of data points */ dim_t nx = 0; /* # pixels in x-direction */ dim_t ny = 0; /* # pixels in y-direction */ smf_tausrc tausrc; /* Local copy of tausrc value */ int ubnd[2]; /* Upper bound */ double *vardata = NULL; /* Pointer to variance array */ double * wvmtau = NULL; /* WVM tau (smoothed or not) for these data */ int nw; /* Number of worker threads */ int iw; /* Thread index */ SmfCorrectExtinctionData *job_data = NULL; /* Array of job descriptions */ SmfCorrectExtinctionData *pdata; /* Pointer to next job description */ size_t framestep; /* Number of frames per thread */ /* Check status */ if (*status != SAI__OK) return allquick; /* If no correction requested, return */ if( method==SMF__EXTMETH_NONE ) { msgOutif(MSG__VERB, "", FUNC_NAME ": Extinction method=none, returning", status ); return allquick; } if ( ! thetausrc ) { *status = SAI__ERROR; errRep( "", FUNC_NAME ": Must supply a thetausrc argument. Possible programming error.", status ); return allquick; } /* Use a local value for tausrc as we update it in this routine. In particular, CSOFIT becomes WVMRAW and this would be confusing to the caller */ tausrc = *thetausrc; /* If no opacity monitor specified generate bad status */ if( tausrc==SMF__TAUSRC_NULL ) { *status = SAI__ERROR; errRep( "", FUNC_NAME ": No source of opacity information could be determined", status ); return allquick; } if( smf_history_check( data, FUNC_NAME, status) ) { /* If caller not requesting allextcorr fail here */ if( !allextcorr ) { msgSetc("F", FUNC_NAME); msgOutif(MSG__VERB," ", "^F has already been run on these data, returning to caller", status); return allquick; } } /* Acquire the data order */ isTordered = data->isTordered; /* make sure we have a header */ hdr = data->hdr; if( hdr == NULL ) { *status = SAI__ERROR; errRep( FUNC_NAME, "Input data has no header", status); return allquick; } /* Do we have 2-D image data? */ ndims = data->ndims; if (ndims == 2) { nframes = 1; nx = (data->dims)[0]; ny = (data->dims)[1]; npts = nx*ny; } else { /* this routine will also check for dimensionality */ smf_get_dims( data, &nx, &ny, &npts, &nframes, NULL, NULL, NULL, status ); } /* Tell user we're correcting for extinction */ msgOutif(MSG__VERB," ", "Correcting for extinction.", status); /* Should check data type for double if not allextcorr case */ if( !allextcorr ) { if (!smf_dtype_check_fatal( data, NULL, SMF__DOUBLE, status)) return allquick; } /* Check that we're not trying to use the WVM for 2-D data */ if ( ndims == 2 && tausrc == SMF__TAUSRC_WVMRAW ) { if ( *status == SAI__OK ) { *status = SAI__ERROR; errRep( FUNC_NAME, "Method WVMRaw can not be used on 2-D image data", status ); return allquick; } } else if (ndims == 2 && tausrc == SMF__TAUSRC_CSOFIT ) { /* This is CSOTAU mode with the value calculated from the fits. We have to either calculate the value here based on the FITS headers or we have to ensure that when this mode triggers we've been given the fallback tau derived in this manner. Revisit this as the code develops (we do not want to be reading fits multiple times). */ if (*status == SAI__OK) { *status = SAI__ERROR; errRep( FUNC_NAME, "Method CSOFIT not yet supported on 2-D image data", status ); return allquick; } } else if (ndims < 2 || ndims > 3) { if (*status == SAI__OK) { *status = SAI__ERROR; errRepf( FUNC_NAME, "Can not extinction correct data with %zd dimension(s)", status, ndims ); return allquick; } } /* if we are WVMRAW, CSOFIT or AUTO and we have a cache we should always use it since we assume it was filled in properly the previous time. */ if (wvmtaucache && *wvmtaucache && (tausrc == SMF__TAUSRC_WVMRAW || tausrc == SMF__TAUSRC_AUTO || tausrc == SMF__TAUSRC_CSOFIT)) { wvmtau = *wvmtaucache; smf_smfFile_msg( data->file, "FILE", 1, "<unknown>"); msgOutiff( MSG__VERB, "", "Using cached high resolution data for extinction correction of ^FILE", status); tausrc = SMF__TAUSRC_WVMRAW; /* We are now WVMRAW as we have the data */ /* Assume that we only do not know the provenance if in AUTO mode */ if (tausrc == SMF__TAUSRC_AUTO) *thetausrc = SMF__TAUSRC_CACHED; } if (!wvmtau && tausrc == SMF__TAUSRC_WVMRAW) { size_t ntotaltau = 0; size_t ngoodtau = 0; smf_calc_smoothedwvm( wf, NULL, data, extpars, &wvmtau, &ntotaltau, &ngoodtau, status ); smf_smfFile_msg( data->file, "FILE", 1, "<unknown>"); msgOutiff( MSG__VERB, "", "Using WVM mode for extinction correction of ^FILE" " %.0f %% of WVM data are present", status, (double)(100.0*(double)ngoodtau/(double)ntotaltau) ); } if (*status == SAI__OK && tausrc == SMF__TAUSRC_CSOFIT) { /* Calculate the fit but we can use the same cache that WVM uses */ size_t nframes = 0; smf_calc_csofit( data, extpars, &wvmtau, &nframes, status ); smf_smfFile_msg( data->file, "FILE", 1, "<unknown>"); msgOutiff( MSG__QUIET, "", "Using CSO fits for extinction correction of ^FILE", status ); /* Rebrand as WVM data from this point on */ tausrc = SMF__TAUSRC_WVMRAW; } /* AUTO mode logic */ /* * Default position is to use WVM data but we have two caveats * * 1. Was this observation done during a period where the WVM has been flagged as unreliable? * 2. If the WVM is nominally okay, do we have sufficient good data during this period? * * If the WVM should not be used we fallback to seeing if we have a fit available for this * night from the CSO data. * * If we do not have a reliable WVM or CSO fit then we fallback to using a fixed CSO number * from the header. * * This final fallback position is unfortunate as it is highly likely that this is not a reliable * number if we have fits for every night of observing (we have no information on whether a missing * fit indicates the CSO was too unstable to use or whether it means we simply haven't got to it * yet). * */ /* Check auto mode */ if (tausrc == SMF__TAUSRC_AUTO && *status == SAI__OK) { smf_smfFile_msg( data->file, "FILE", 1, "<unknown>" ); if (ndims == 2) { /* have to use CSO mode */ tausrc = SMF__TAUSRC_CSOTAU; *thetausrc = tausrc; } else if (ndims == 3) { /* We have already done the cache test so not needed here */ /* Is the WVM nominally stable for this night? */ if (smf_is_wvm_usable( data->hdr, status ) ) { /* Calculate the WVM tau data and see if we have enough good data */ size_t ngoodtau = 0; size_t ntotaltau = 0; double percentgood = 0.0; smf_calc_smoothedwvm( wf, NULL, data, extpars, &wvmtau, &ntotaltau, &ngoodtau, status ); percentgood = 100.0 * ((double)ngoodtau / (double)ntotaltau); if ( percentgood > 80.0) { tausrc = SMF__TAUSRC_WVMRAW; msgOutiff( MSG__VERB, "", "Selecting WVM mode for extinction correction of ^FILE." " %.0f %% of WVM data are present", status, percentgood ); *thetausrc = tausrc; } else { tausrc = SMF__TAUSRC_AUTO; /* keep it AUTO (a no-op but make it clear) */ if (wvmtau) wvmtau = astFree( wvmtau ); } } /* at this point we either have WVM data handled or we still think we are AUTO. Do a CSO FIT check */ if (tausrc == SMF__TAUSRC_AUTO && *status == SAI__OK) { size_t nframes = 0; smf_calc_csofit( data, extpars, &wvmtau, &nframes, status ); if (*status == SAI__OK) { smf_smfFile_msg( data->file, "FILE", 1, "<unknown>"); msgOutiff( MSG__QUIET, "", "Using CSO fits for extinction correction of ^FILE", status ); /* Rebrand as WVM data from this point on */ tausrc = SMF__TAUSRC_WVMRAW; *thetausrc = SMF__TAUSRC_CSOFIT; } else if (*status == SMF__BADFIT) { /* No fit, carry on. */ errAnnul( status ); } } /* At this point if we are not WVMRAW then we have a serious issue. It means that WVM was unusable and we did not have a good CSO fit. We should not continue at this point as to continue implies that we know what we should do. The user should decide how much they trust the opacity for the night. There has to be a reason why there is no CSO fit for the night. */ if (*status == SAI__OK && tausrc != SMF__TAUSRC_WVMRAW) { *status = SAI__ERROR; errRep("", "Unable to determine opacity data for this observation. Both WVM and CSO fits failed. Please investigate and if necessary use CSO mode explicitly but proceed with caution.", status ); } } } /* If we have a CSO Tau then convert it to the current filter. This will also convert bad values to a value derived from the header if appropriate. */ if ( tausrc == SMF__TAUSRC_CSOTAU ) { tau = smf_cso2filt_tau( hdr, tau, extpars, status ); /* The tau source is now a real tau */ tausrc = SMF__TAUSRC_TAU; } /* Find the airmass range for this data */ smf_find_airmass_interval( hdr, &amstart, &amend, &elstart, &elend, status ); if (*status == SAI__OK && (amstart == VAL__BADD || amend == VAL__BADD)) { *status = SAI__ERROR; errRep( "", "No good airmass values found in JCMTSTATE structure for these data", status ); } /* if we are not doing WVM correction but are in adaptive mode we can determine whether or not we will have to use full or single mode just by looking at the airmass data. */ if (ndims == 3 && tausrc != SMF__TAUSRC_WVMRAW && method == SMF__EXTMETH_ADAPT) { /* first and last is a good approximation given that most SCUBA-2 files will only be a minute duration. */ double refel; double refam; /* only need to examine the largest airmass */ if (amstart > amend) { refam = amstart; refel = elstart; } else { refam = amend; refel = elend; } /* and choose a correction method */ if (is_large_delta_atau( refam, refel, tau, status) ) { method = SMF__EXTMETH_FULL; msgOutiff(MSG__DEBUG, " ", "Adaptive extinction algorithm selected per-bolometer airmass value " "per time slice (am=%g, tau=%g)", status, refam, tau); } else { msgOutiff(MSG__DEBUG, " ", "Adaptive extinction algorithm selected single airmass value per time slice" " (am=%g, tau=%g)", status, refam, tau); method = SMF__EXTMETH_SINGLE; } } /* Assign pointer to input data array if status is good */ if ( *status == SAI__OK ) { indata = (data->pntr)[0]; vardata = (data->pntr)[1]; } /* Jump to the cleanup section if status is bad by this point since we need to free memory */ if (*status != SAI__OK) goto CLEANUP; /* Array bounds for astTranGrid call */ lbnd[0] = 1; lbnd[1] = 1; ubnd[0] = nx; ubnd[1] = ny; /* Unlock the AST objects in the smfData so that the worker threads can lock them. */ smf_lock_data( data, 0, status ); /* How many threads do we get to play with */ nw = wf ? wf->nworker : 1; /* Find how many frames to process in each worker thread. */ framestep = nframes/nw; if( framestep == 0 ) { framestep = 1; nw = nframes; } /* Allocate job data for threads, and store the range of frames to be processed by each one. Ensure that the last thread picks up any left-over frames. */ job_data = astCalloc( nw, sizeof(*job_data) ); if( *status == SAI__OK ) { for( iw = 0; iw < nw; iw++ ) { pdata = job_data + iw; pdata->f1 = iw*framestep; if( iw < nw - 1 ) { pdata->f2 = pdata->f1 + framestep - 1; } else { pdata->f2 = nframes - 1 ; } pdata->nframes = nframes; pdata->npts = npts; pdata->allextcorr = allextcorr; pdata->indata = indata; pdata->tau = tau; pdata->vardata = vardata; pdata->wvmtau = wvmtau; pdata->amstart = amstart; pdata->amfirst = amstart + ( amend - amstart )*pdata->f1/( nframes - 1 ); pdata->lbnd = lbnd; pdata->ubnd = ubnd; pdata->isTordered = isTordered; pdata->ndims = ndims; pdata->data = data; pdata->hdr = hdr; pdata->method = method; pdata->tausrc = tausrc; /* Submit the job to the workforce. */ thrAddJob( wf, 0, pdata, smf1_correct_extinction, 0, NULL, status ); } /* Wait for all jobs to complete. */ thrWait( wf, status ); /* Record if all time slices used a single air mass. */ allquick = 1; for( iw = 0; iw < nw; iw++ ) { pdata = job_data + iw; if( ! pdata->allquick ) { allquick = 0; break; } } /* Free the job data. */ job_data = astFree( job_data ); } /* Lock the AST objects in the smfData for use by this thread. */ smf_lock_data( data, 1, status ); /* Add history entry if !allextcorr */ if( (*status == SAI__OK) && !allextcorr ) { smf_history_add( data, FUNC_NAME, status); } CLEANUP: if (wvmtaucache) { if (!*wvmtaucache) { *wvmtaucache = wvmtau; } } else { wvmtau = astFree( wvmtau ); } return allquick; }
int smf_correct_extinction(ThrWorkForce *wf, smfData *data, smf_tausrc tausrc, smf_extmeth method, AstKeyMap * extpars, double tau, double *allextcorr, double **wvmtaucache, int *status) { /* Local variables */ int allquick = 0; /* Is the extinction for all bolometers the same? */ double amstart = VAL__BADD; /* Airmass at start */ double amend = VAL__BADD; /* Airmass at end */ double elstart = VAL__BADD; /* Elevation at start (radians) */ double elend = VAL__BADD;/* Elevation at end (radians) */ smfHead *hdr = NULL; /* Pointer to full header struct */ double *indata = NULL; /* Pointer to data array */ int isTordered; /* data order of input data */ int lbnd[2]; /* Lower bound */ size_t ndims; /* Number of dimensions in input data */ dim_t nframes = 0; /* Number of frames */ dim_t npts = 0; /* Number of data points */ dim_t nx = 0; /* # pixels in x-direction */ dim_t ny = 0; /* # pixels in y-direction */ int ubnd[2]; /* Upper bound */ double *vardata = NULL; /* Pointer to variance array */ double * wvmtau = NULL; /* WVM tau (smoothed or not) for these data */ int nw; /* Number of worker threads */ int iw; /* Thread index */ SmfCorrectExtinctionData *job_data = NULL; /* Array of job descriptions */ SmfCorrectExtinctionData *pdata; /* Pointer to next job description */ size_t framestep; /* Number of frames per thread */ /* Check status */ if (*status != SAI__OK) return allquick; /* If no correction requested, return */ if( method==SMF__EXTMETH_NONE ) { msgOutif(MSG__VERB, "", FUNC_NAME ": Extinction method=none, returning", status ); return allquick; } /* If no opacity monitor specified generate bad status */ if( tausrc==SMF__TAUSRC_NULL ) { *status = SAI__ERROR; errRep( "", FUNC_NAME ": No extinction monitor specified", status ); return allquick; } if( smf_history_check( data, FUNC_NAME, status) ) { /* If caller not requesting allextcorr fail here */ if( !allextcorr ) { msgSetc("F", FUNC_NAME); msgOutif(MSG__VERB," ", "^F has already been run on these data, returning to caller", status); return allquick; } } /* Acquire the data order */ isTordered = data->isTordered; /* make sure we have a header */ hdr = data->hdr; if( hdr == NULL ) { *status = SAI__ERROR; errRep( FUNC_NAME, "Input data has no header", status); return allquick; } /* Do we have 2-D image data? */ ndims = data->ndims; if (ndims == 2) { nframes = 1; nx = (data->dims)[0]; ny = (data->dims)[1]; npts = nx*ny; } else { /* this routine will also check for dimensionality */ smf_get_dims( data, &nx, &ny, &npts, &nframes, NULL, NULL, NULL, status ); } /* Tell user we're correcting for extinction */ msgOutif(MSG__VERB," ", "Correcting for extinction.", status); /* Should check data type for double if not allextcorr case */ if( !allextcorr ) { if (!smf_dtype_check_fatal( data, NULL, SMF__DOUBLE, status)) return allquick; } /* Check that we're not trying to use the WVM for 2-D data */ if ( ndims == 2 && tausrc == SMF__TAUSRC_WVMRAW ) { if ( *status == SAI__OK ) { *status = SAI__ERROR; errRep( FUNC_NAME, "Method WVMRaw can not be used on 2-D image data", status ); return allquick; } } else if (ndims < 2 || ndims > 3) { if (*status == SAI__OK) { *status = SAI__ERROR; errRepf( FUNC_NAME, "Can not extinction correct data with %zd dimension(s)", status, ndims ); return allquick; } } if (tausrc == SMF__TAUSRC_WVMRAW) { size_t ntotaltau = 0; size_t ngoodtau = 0; /* calculate WVM unless we have external values */ if (wvmtaucache && *wvmtaucache) { wvmtau = *wvmtaucache; smf_smfFile_msg( data->file, "FILE", 1, "<unknown>"); msgOutiff( MSG__VERB, "", "Using cached WVM data for extinction correction of ^FILE", status); } else { smf_calc_smoothedwvm( wf, NULL, data, extpars, &wvmtau, &ntotaltau, &ngoodtau, status ); smf_smfFile_msg( data->file, "FILE", 1, "<unknown>"); msgOutiff( MSG__VERB, "", "Using WVM mode for extinction correction of ^FILE" " %.0f %% of WVM data are present", status, (double)(100.0*(double)ngoodtau/(double)ntotaltau) ); } } /* Check auto mode */ if (tausrc == SMF__TAUSRC_AUTO && *status == SAI__OK) { smf_smfFile_msg( data->file, "FILE", 1, "<unknown>" ); if (ndims == 2) { /* have to use CSO mode */ tausrc = SMF__TAUSRC_CSOTAU; } else if (ndims == 3) { /* Calculate the WVM tau data and see if we have enough good data */ size_t ngoodtau = 0; size_t ntotaltau = 0; double percentgood = 0.0; if (wvmtaucache && *wvmtaucache) { wvmtau = *wvmtaucache; tausrc = SMF__TAUSRC_WVMRAW; smf_smfFile_msg( data->file, "FILE", 1, "<unknown>"); msgOutiff( MSG__VERB, "", "Using cached WVM data for extinction correction of ^FILE", status ); } else { smf_calc_smoothedwvm( wf, NULL, data, extpars, &wvmtau, &ntotaltau, &ngoodtau, status ); percentgood = 100.0 * ((double)ngoodtau / (double)ntotaltau); if ( percentgood > 80.0) { tausrc = SMF__TAUSRC_WVMRAW; msgOutiff( MSG__VERB, "", "Selecting WVM mode for extinction correction of ^FILE." " %.0f %% of WVM data are present", status, percentgood ); } else { tausrc = SMF__TAUSRC_CSOTAU; if (wvmtau) wvmtau = astFree( wvmtau ); } } } if (tausrc == SMF__TAUSRC_CSOTAU) { msgOutiff( MSG__VERB, "", "Selecting CSO mode for extinction correction of ^FILE", status ); } else if (tausrc == SMF__TAUSRC_WVMRAW) { /* Dealt with this above */ } else { /* oops. Fall back position */ tausrc = SMF__TAUSRC_CSOTAU; msgOutiff( MSG__VERB, "", "Selecting CSO mode as unexpected fallback for extinction correction of ^FILE", status ); } } /* If we have a CSO Tau then convert it to the current filter. This will also convert bad values to a value derived from the header if appropriate. */ if ( tausrc == SMF__TAUSRC_CSOTAU ) { tau = smf_cso2filt_tau( hdr, tau, extpars, status ); /* The tau source is now a real tau */ tausrc = SMF__TAUSRC_TAU; } /* Find the airmass range for this data */ smf_find_airmass_interval( hdr, &amstart, &amend, &elstart, &elend, status ); if (*status == SAI__OK && (amstart == VAL__BADD || amend == VAL__BADD)) { *status = SAI__ERROR; errRep( "", "No good airmass values found in JCMTSTATE structure for these data", status ); } /* if we are not doing WVM correction but are in adaptive mode we can determine whether or not we will have to use full or single mode just by looking at the airmass data. */ if (ndims == 3 && tausrc != SMF__TAUSRC_WVMRAW && method == SMF__EXTMETH_ADAPT) { /* first and last is a good approximation given that most SCUBA-2 files will only be a minute duration. */ double refel; double refam; /* only need to examine the largest airmass */ if (amstart > amend) { refam = amstart; refel = elstart; } else { refam = amend; refel = elend; } /* and choose a correction method */ if (is_large_delta_atau( refam, refel, tau, status) ) { method = SMF__EXTMETH_FULL; msgOutiff(MSG__DEBUG, " ", "Adaptive extinction algorithm selected per-bolometer airmass value " "per time slice (am=%g, tau=%g)", status, refam, tau); } else { msgOutiff(MSG__DEBUG, " ", "Adaptive extinction algorithm selected single airmass value per time slice" " (am=%g, tau=%g)", status, refam, tau); method = SMF__EXTMETH_SINGLE; } } /* Assign pointer to input data array if status is good */ if ( *status == SAI__OK ) { indata = (data->pntr)[0]; vardata = (data->pntr)[1]; } /* Jump to the cleanup section if status is bad by this point since we need to free memory */ if (*status != SAI__OK) goto CLEANUP; /* Array bounds for astTranGrid call */ lbnd[0] = 1; lbnd[1] = 1; ubnd[0] = nx; ubnd[1] = ny; /* Unlock the AST objects in the smfData so that the worker threads can lock them. */ smf_lock_data( data, 0, status ); /* How many threads do we get to play with */ nw = wf ? wf->nworker : 1; /* Find how many frames to process in each worker thread. */ framestep = nframes/nw; if( framestep == 0 ) { framestep = 1; nw = nframes; } /* Allocate job data for threads, and store the range of frames to be processed by each one. Ensure that the last thread picks up any left-over frames. */ job_data = astCalloc( nw, sizeof(*job_data) ); if( *status == SAI__OK ) { for( iw = 0; iw < nw; iw++ ) { pdata = job_data + iw; pdata->f1 = iw*framestep; if( iw < nw - 1 ) { pdata->f2 = pdata->f1 + framestep - 1; } else { pdata->f2 = nframes - 1 ; } pdata->nframes = nframes; pdata->npts = npts; pdata->allextcorr = allextcorr; pdata->indata = indata; pdata->tau = tau; pdata->vardata = vardata; pdata->wvmtau = wvmtau; pdata->amstart = amstart; pdata->amfirst = amstart + ( amend - amstart )*pdata->f1/( nframes - 1 ); pdata->lbnd = lbnd; pdata->ubnd = ubnd; pdata->isTordered = isTordered; pdata->ndims = ndims; pdata->data = data; pdata->hdr = hdr; pdata->method = method; pdata->tausrc = tausrc; /* Submit the job to the workforce. */ thrAddJob( wf, 0, pdata, smf1_correct_extinction, 0, NULL, status ); } /* Wait for all jobs to complete. */ thrWait( wf, status ); /* Record if all time slices used a single air mass. */ allquick = 1; for( iw = 0; iw < nw; iw++ ) { pdata = job_data + iw; if( ! pdata->allquick ) { allquick = 0; break; } } /* Free the job data. */ job_data = astFree( job_data ); } /* Lock the AST objects in the smfData for use by this thread. */ smf_lock_data( data, 1, status ); /* Add history entry if !allextcorr */ if( (*status == SAI__OK) && !allextcorr ) { smf_history_add( data, FUNC_NAME, status); } CLEANUP: if (wvmtaucache) { if (!*wvmtaucache) { *wvmtaucache = wvmtau; } } else { wvmtau = astFree( wvmtau ); } return allquick; }