static void smf1_jsadicer( int indfo, int *olbnd, int *oubnd, AstMapping *tile_map, AstFrame *tile_frm, AstMapping *p2pmap, void *ipd, void *ipv, unsigned char *ipq, int *status ){ /* * Name: * smf1_jsadicer * Purpose: * Copy one tile from the input NDF into a specified output NDF. * Language: * Starlink ANSI C * Type of Module: * C function * Invocation: * void smf1_jsadicer( int indfo, int *olbnd, int *oubnd, * AstMapping *tile_map, AstFrame *tile_frm, * AstMapping *p2pmap, void *ipd, void *ipv, * unsigned char *ipq, int *status ) * Arguments: * indfo = int (Given) * An identifier for the NDF in which the copied data is to be * stored. It's original pixel bounds are used as the bounds of the * ipd, ipv and ipq arrays. * olbnd = int * (Given) * The new lower pixel bounds required for the output NDF. The bounds * of the supplied NDF are changed to match these values. * oubnd = int * (Given) * The new upper pixel bounds required for the output NDF. The bounds * of the supplied NDF are changed to match these values. * tile_map = AstMapping * (Given) * The mapping from pixel coords in the output NDF to WCS coords. * tile_frm = AstMapping * (Given) * The WCS Frame for the output NDF. * p2pmap = AstMapping * (Given) * The mapping from pixel coords in the input NDF to pixel coords in * the output NDF. * ipd = void * (Given) * Pointer to the start of the input data array. If this is NULL, * the existing contents of the NDF are used as input. * ipv = void * (Given) * Pointer to the start of the input variance array. Should be NULL * if no variances are available. * ipq = unsigned char * (Given) * Pointer to the start of the input quality array. Should be NULL * if no quality is available. * status = int * (Given) * Pointer to the inherited status variable. */ /* Local Variables: */ AstFrame *use_frm = NULL; AstFrameSet *owcs; AstMapping *use_map = NULL; AstMapping *use_p2pmap = NULL; AstShiftMap *sm; char type[ NDF__SZTYP + 1 ]; double shifts[ 3 ]; int axes[ 2 ]; int axout[ NDF__MXDIM ]; int free_arrays; int isreal; int lbnd_tile[ 3 ]; int ndim; int nel; int nin; int there; int ubnd_tile[ 3 ]; unsigned char *ipq_out = NULL; void *ipd_out = NULL; void *ipv_out = NULL; /* Check inherited status */ if( *status != SAI__OK ) return; /* Begin an AST context. */ astBegin; /* Get the NDF data type - _REAL or _DOUBLE. */ ndfType( indfo, "Data", type, sizeof(type), status ); isreal = !strcmp( type, "_REAL" ); /* Get the existing bounds of the NDF. */ ndfBound( indfo, 3, lbnd_tile, ubnd_tile, &ndim, status ); /* If no data array has been supplied, take a copy of the original Data, Quality and Variance arrays and use these as the input arrays. */ if( !ipd ) { free_arrays = 1; ndfMap( indfo, "Data", type, "Read", &ipd_out, &nel, status ); ipd = astStore( NULL, ipd_out, nel*(isreal?sizeof(float):sizeof(double)) ); ndfUnmap( indfo, "Data", status ); ndfState( indfo, "Variance", &there, status ); if( there ) { ndfMap( indfo, "Variance", type, "Read", &ipv_out, &nel, status ); ipv = astStore( NULL, ipv_out, nel*(isreal?sizeof(float):sizeof(double)) ); ndfUnmap( indfo, "Variance", status ); } else { ipv = NULL; } ndfState( indfo, "Quality", &there, status ); if( there ) { ndfMap( indfo, "Quality", "_UBYTE", "Read", (void **) &ipq_out, &nel, status ); ipq = astStore( NULL, ipq_out, nel*sizeof(*ipq) ); ndfUnmap( indfo, "Quality", status ); } else { ipq = NULL; } } else { free_arrays = 0; } /* Set the bounds of the NDF to the required values. */ ndfSbnd( ndim, olbnd, oubnd, indfo, status ); /* Erase the existing WCS FrameSet and then get the default WCS FrameSet. */ ndfReset( indfo, "WCS", status ); ndfGtwcs( indfo, &owcs, status ); /* If the supplied mapping and Frame have two many axes, strip some off. The orering of pixel axes in the output JSA tile is hardwired by SMURF as (ra,dec,spec). */ nin = astGetI( tile_map, "Nin" ); if( nin == 3 && ndim == 2 ) { axes[ 0 ] = 1; axes[ 1 ] = 2; astMapSplit( tile_map, 2, axes, axout, &use_map ); if( use_map ) { use_frm = astPickAxes( tile_frm, 2, axout, NULL ); } else if( *status == SAI__OK ) { *status = SAI__ERROR; errRepf( " ", "smf1_jsadicer: cannot split mapping (programming " "error).", status ); } astMapSplit( p2pmap, 2, axes, axout, &use_p2pmap ); if( !use_p2pmap && *status == SAI__OK ) { *status = SAI__ERROR; errRepf( " ", "smf1_jsadicer: cannot split mapping (programming " "error).", status ); } } else if( nin == ndim ) { use_p2pmap = astClone( p2pmap ); use_map = astClone( tile_map ); use_frm = astClone( tile_frm ); } else if( *status == SAI__OK ) { *status = SAI__ERROR; errRepf( " ", "smf1_jsadicer: unexpected combination of nin (%d) and " "ndim (%d) (programming error).", status, nin, ndim ); } /* Add the tile WCS Frame into the output NDF's WCS FrameSet, using "tilemap" to connect it to the PIXEL Frame (NDF ensure Frame 2 is the PIXEL Frame). */ astAddFrame( owcs, 2, use_map, use_frm ); /* The astResample function is odd in that it assumes that pixel coords are defined such that the centre of pixel "I" has integral pixel coord "I" (rather than "I-0.5" as is usual in Starlink). So we need to use a half-pixel ShiftMap at start and end of the p2pmap Mapping to account for this. */ shifts[ 0 ] = -0.5; shifts[ 1 ] = -0.5; shifts[ 2 ] = -0.5; sm = astShiftMap( ndim, shifts, " " ); use_p2pmap = (AstMapping *) astCmpMap( sm, use_p2pmap, 1, " " ); astInvert( sm ); use_p2pmap = (AstMapping *) astCmpMap( use_p2pmap, sm, 1, " " ); /* Store this modified WCS FrameSet in the output NDF. */ ndfPtwcs( owcs, indfo, status ); /* Map the required arrays of the output NDF. */ ndfMap( indfo, "Data", type, "Write", &ipd_out, &nel, status ); if( ipv ) ndfMap( indfo, "Variance", type, "Write", &ipv_out, &nel, status ); if( ipq ) ndfMap( indfo, "Quality", "_UBYTE", "Write", (void **) &ipq_out, &nel, status ); /* Copy the input data values to the output, using nearest neighbour interpolation (the mapping should always map input pixel centres onto output pixel centres). We can set the "tol" argument non-zero (e.g. 0.1) without introducing any error because the the p2pmap mapping will be piecewise linear. This gives a factor of about 5 decrease in the time spent within astResample. */ if( !strcmp( type, "_REAL" ) ) { (void) astResampleF( use_p2pmap, ndim, lbnd_tile, ubnd_tile, (float *) ipd, (float *) ipv, AST__NEAREST, NULL, NULL, AST__USEBAD, 0.1, 1000, VAL__BADR, ndim, olbnd, oubnd, olbnd, oubnd, (float *) ipd_out, (float *) ipv_out ); } else { (void) astResampleD( use_p2pmap, ndim, lbnd_tile, ubnd_tile, (double *) ipd, (double *) ipv, AST__NEAREST, NULL, NULL, AST__USEBAD, 0.1, 1000, VAL__BADD, ndim, olbnd, oubnd, olbnd, oubnd, (double *) ipd_out, (double *) ipv_out ); } if( ipq ) { (void) astResampleUB( use_p2pmap, ndim, lbnd_tile, ubnd_tile, ipq, NULL, AST__NEAREST, NULL, NULL, 0, 0.1, 1000, 0, ndim, olbnd, oubnd, olbnd, oubnd, ipq_out, NULL ); } /* Unmap everything the output NDF. */ ndfUnmap( indfo, "*", status ); /* Free the input arrays if they were allocated in this function. */ if( free_arrays ) { ipd = astFree( ipd ); ipv = astFree( ipv ); ipq = astFree( ipq ); } /* End the AST context. */ astEnd; }
static AstTable *ReadNextTable( FILE *fd, const char *fname, int *iline, int *status ) { /* * Name: * ReadOneTable * Purpose: * Reads a single Table from a text file. * Description: * This function reads text from the supplied file descriptor until it * reaches the end of file or encounters an end-of-table marker (a line * consisting just of two or more minus signs with no leading spaces). * It creates an AstTable from the text and returns a pointer to it. * Arguments: * fd * The file descriptor. * fname * The file name - used for error messages. * iline * Pointer to an int holding the number of lines read from the * file so far. Updated on exit to include the lines read by the * invocation of this function. * status * Pointer to the global status variable. * Returned Value: * A pointer to the Table read from the file, or NULL if an error occurs. */ /* Local Variables: */ AstTable *result; AstTable *subtable; char **cols; char **words; char *last_com; char *line; char *p; char *tname; char key[ 200 ]; const char *cval; const char *oldname; const char *newname; double dval; int *types; int blank; int c; int com; int eot; int first; int icol; int irow; int ival; int iword; int line_len; int max_line_len; int more; int nc; int ncol; int nrow; int nword; int skip; size_t len; /* Initialise */ result = NULL; /* Check the inherited status. */ if( *status != SAI__OK ) return result; /* Create an empty Table. */ result = astTable( " " ); /* Allocate a buffer for one one line of text. This will be increased in size as required. */ max_line_len = 80; line = astMalloc( max_line_len*sizeof( *line ) ); /* Read each line of text from the file. */ eot = 0; skip = 1; line_len = 0; more = 1; last_com = NULL; cols = NULL; first = 1; irow = 0; types = NULL; while( more && *status == SAI__OK ) { (*iline)++; line_len = 0; /* Loop reading characters from the file until a newline or the end of file is reached. */ while( ( c = fgetc( fd ) ) != EOF && c != '\n' ) { /* Increment the current line length, and double the size of the line buffer if it is full. */ if( ++line_len >= max_line_len ) { max_line_len *= 2; line = astRealloc( line, max_line_len*sizeof( *line ) ); if( *status != SAI__OK ) break; } /* Store the character. Ignore leading white space. */ if( skip ) { if( ! isspace( c ) ) { line[ line_len - 1 ] = c; skip = 0; } else { line_len--; } } else { line[ line_len - 1 ] = c; } } /* If the end-of-file was reached indicate that we should leave the main loop after processing the current line. */ if( c == EOF ) more = 0; /* Terminate the line. */ line[ line_len ] = 0; /* Terminate it again to exclude trailing white space. */ line[ astChrLen( line ) ] = 0; /* Assume the line is a blank non-comment, and store a pointer to the first character to use. */ blank = 1; com = 0; p = line; /* Skip blank lines. */ if( line[ 0 ] ) { /* If the line starts with a comment character... */ if( line[ 0 ] == '#' || line[ 0 ] == '!' ) { com = 1; /* Get a pointer to the first non-space/tab character after the comment character. */ p = line + 1; while( *p == ' ' || *p == '\t' ) p++; /* Note if it is blank. */ if( *p ) blank = 0; /* If it is not a comment line, then the line is not blank. */ } else { blank = 0; /* See if it is the end-of-table marker - a line containing just two or more minus signs with no leading spaces. */ eot = ( strspn( line, "-" ) > 1 ); } } /* Skip blank lines, whether comment or not. */ if( ! blank ) { /* First handle comment lines. */ if( com ) { /* Does it look like a parameter assignment... */ words = astChrSplitRE( p, "^\\s*(\\w+)\\s*=\\s*(.*)$", &nword, NULL ); if( words ) { /* Add a parameter declaration to the Table. */ astAddParameter( result, words[ 0 ] ); /* Store the parameter value, using an appropriate data type. */ len = strlen( words[ 1 ] ); if( nc = 0, ( 1 == astSscanf( words[ 1 ], "%d%n", &ival, &nc ) ) && ( nc >= len ) ) { astMapPut0I( result, words[ 0 ], ival, NULL ); } else if( nc = 0, ( 1 == astSscanf( words[ 1 ], "%lg%n", &dval, &nc ) ) && ( nc >= len ) ) { astMapPut0D( result, words[ 0 ], dval, NULL ); } else { astMapPut0C( result, words[ 0 ], words[ 1 ], NULL ); } /* Free the words returned by astChrSplitRE. */ for( iword = 0; iword < nword; iword++ ) { words[ iword ] = astFree( words[ iword ] ); } words = astFree( words ); /* If it does not look like a parameter assignment... */ } else { /* Save a copy of it in case it turns out to be the last non-blank comment line before the first row of data values (in which case it should contain the column names). */ last_com = astStore( last_com, p, strlen( p ) + 1 ); } /* If the line is not a comment see if it is an end of table marker. If so indicate that we should leave the loop. */ } else if( eot ) { more = 0; /* If the line is not a comment or an end of table marker ... */ } else { /* Get the words from the row. */ words = astChrSplit( p, &nword ); /* If this is the first non-blank non-comment line, get the column names from the previous non-blank comment line. */ if( first ) { if( last_com ) { first = 0; cols = astChrSplit( last_com, &ncol ); /* Create an array to hold the data type for each colum, and initialise them to "integer". */ types = astMalloc( ncol*sizeof( int ) ) ; for( iword = 0; iword < nword && astOK; iword++ ) { if( iword < ncol ) { types[ iword ] = AST__INTTYPE; /* The columns are stored initially using interim names which have "T_" prepended to the names given in the file. */ tname = NULL; nc = 0; tname = astAppendString( tname, &nc, "T_" ); tname = astAppendString( tname, &nc, cols[ iword ] ); astFree( cols[ iword ] ); cols[ iword ] = tname; /* Create the column definition within the returned Table. We store them initially as strings and then convert to the appropriate column data type later (once all rows have been read and the the data types are known). */ astAddColumn( result, cols[ iword ], AST__STRINGTYPE, 0, NULL, " " ); } } } else if( *status == SAI__OK ) { *status = SAI__ERROR; msgSetc( "F", fname ); errRep( " ", "No column headers found in file ^F.", status ); } } /* Report an error if the line has the wrong number of values. */ if( nword != ncol ) { if( *status == SAI__OK ) { *status = SAI__ERROR; msgSeti( "N", nword ); msgSeti( "I", (*iline) ); msgSeti( "M", ncol ); msgSetc( "F", fname ); errRep( " ", "Wrong number of values (^N) at line ^I in " "file ^F (should be ^M).", status ); } /* Otherwise increment the number of rows read. */ } else { irow++; /* Store each string value in the table, excluding "null" strings. Also check the data type of each string an dupdate the column data types if necessary. */ for( iword = 0; iword < nword && *status == SAI__OK; iword++ ) { if( strcmp( words[ iword ], "null" ) ) { sprintf( key, "%s(%d)", cols[ iword ], irow ); astMapPut0C( result, key, words[ iword ], NULL ); /* If the column is currently thought to hold integers, check that the current word looks like an integer. If not, down-grade the column type to double. */ len = strlen( words[ iword ] ); if( types[ iword ] == AST__INTTYPE ) { if( nc = 0, ( 1 != astSscanf( words[ iword ], "%d%n", &ival, &nc ) ) || ( nc < len ) ) { types[ iword ] = AST__DOUBLETYPE; } } /* If the column is currently thought to hold doubles, check that the current word looks like an doubler. If not, down-grade the column type to string. */ if( types[ iword ] == AST__DOUBLETYPE ) { if( nc = 0, ( 1 != astSscanf( words[ iword ], "%lg%n", &dval, &nc ) ) || ( nc < len ) ) { types[ iword ] = AST__STRINGTYPE; } } } } } /* Free the words returned by astChrSplit. */ for( iword = 0; iword < nword; iword++ ) { words[ iword ] = astFree( words[ iword ] ); } words = astFree( words ); } } } /* The entire file has now been read, and a Table created in which every column holds strings. We also have flags indicating whether the values in each column are all integers or doubles. Modify the type of the column within the Table to match these flags. */ nrow = astGetI( result, "Nrow" ); for( icol = 0; icol < ncol && *status == SAI__OK; icol++ ) { /* The column will be re-named from "T_<name>" to "<name>". */ oldname = cols[ icol ]; newname = oldname + 2; /* First convert string columns to integer columns if all the values in the column look like integers. */ if( types[ icol ] == AST__INTTYPE ) { /* Create the new column */ astAddColumn( result, newname, AST__INTTYPE, 0, NULL, " " ); /* Copy each cell of the current column, converting from string to integer. */ for( irow = 1; irow <= nrow; irow++ ) { sprintf( key, "%s(%d)", oldname, irow ); if( astMapGet0I( result, key, &ival ) ) { sprintf( key, "%s(%d)", newname, irow ); astMapPut0I( result, key, ival, NULL ); } } /* Now do double columns in the same way. */ } else if( types[ icol ] == AST__DOUBLETYPE ) { astAddColumn( result, newname, AST__DOUBLETYPE, 0, NULL, " " ); for( irow = 1; irow <= nrow; irow++ ) { sprintf( key, "%s(%d)", oldname, irow ); if( astMapGet0D( result, key, &dval ) ) { sprintf( key, "%s(%d)", newname, irow ); astMapPut0D( result, key, dval, NULL ); } } /* Copy string values without change. */ } else { astAddColumn( result, newname, AST__STRINGTYPE, 0, NULL, " " ); for( irow = 1; irow <= nrow; irow++ ) { sprintf( key, "%s(%d)", oldname, irow ); if( astMapGet0C( result, key, &cval ) ) { sprintf( key, "%s(%d)", newname, irow ); astMapPut0C( result, key, cval, NULL ); } } } /* Remove the old column. */ astRemoveColumn( result, oldname ); } /* Free resources. */ line = astFree( line ); last_com = astFree( last_com ); types = astFree( types ); if( cols ) { for( icol = 0; icol < ncol; icol++ ) { cols[ icol ] = astFree( cols[ icol ] ); } cols = astFree( cols ); } /* If the table ended with an end-of-table marker, there may be another Table in the file. Call this function recursively to read it. */ if( eot ) { subtable = ReadNextTable( fd, fname, iline, status ); /* Store the subtable as a table parameter in the returned table. */ if( subtable ) { astAddParameter( result, "SubTable" ); astMapPut0A( result, "SubTable", subtable, NULL ); /* The Table clones the pointer, so we must annull our local copy of it. */ subtable = astAnnul( subtable ); } } /* Return the Table pointer. */ return result; }
void smf_svd( ThrWorkForce *wf, dim_t n, double *a, double *sigma, double *u, double eps, int sort, int *status ) { /* Local Variables */ SmfSvdData *job_data = NULL; SmfSvdData *pdata = NULL; dim_t *sobhigh = NULL; dim_t *soblow = NULL; dim_t i; dim_t j; dim_t k; dim_t irow; dim_t iter; dim_t nbig; dim_t nsmall; dim_t nstep; dim_t p; dim_t rpb; dim_t s; double *aorig; double sigold; double delta; int *dn = NULL; int *up = NULL; int converged; /* Check inherited status */ if( *status != SAI__OK ) return; /* The number of threads to use. */ p = wf ? wf->nworker : 1; /* If we have more processors than blocks in the matrix, limit the number of processors. */ if( 4*p > n ) p = n/4; if( p == 0 ) { *status = SAI__ERROR; errRepf( "", "smf_svd: Too few rows (%zu) in matrix - must " "be no fewer than 4", status, n ); } /* Allocate required arrays. */ sobhigh = astMalloc( 2*p*sizeof( *sobhigh ) ); soblow = astMalloc( 2*p*sizeof( *soblow ) ); up = astMalloc( p*sizeof( *up ) ); dn = astMalloc( p*sizeof( *dn ) ); job_data = astMalloc( 2*p*sizeof( *job_data ) ); aorig = u ? astStore( NULL, a, n*n*sizeof(*a) ) : NULL; /* Check pointers can be used safely. */ if( *status == SAI__OK ) { /* Decide on the first and last element to be processed by each thread, for "simple" tasks. At the same time, set up jobs to find the sum of the squares of all data values (a simple task). */ nstep = (n*n)/p; for( i = 0; i < p; i++ ) { pdata = job_data + i; pdata->i1 = i*nstep; if( i < p - 1 ){ pdata->i2 = pdata->i1 + nstep - 1; } else { pdata->i2 = n*n - 1; } pdata->a = a; pdata->oper = 0; thrAddJob( wf, 0, pdata, smf1_svd, 0, NULL, status ); } thrWait( wf, status ); delta = 0.0; for( i = 0; i < p; i++ ) { pdata = job_data + i; delta += pdata->delta; } delta *= eps; /* Set up the "size of block" (sob) arrays: soblow holds the zero-based index of the first matrix row in each block, and sobhigh holds the zero-based index of the last matrix row in each block. We want 2*P blocks (i.e. twice the number of threads). Distribute any left over rows evenly amongst the blocks so that some blocks have n/2p rows (small blocks), and some have n/2p+1 rows (big blocks). */ rpb = n/(2*p); /* Nominal number of rows per block */ nbig = n - 2*p*rpb; /* Number of big blocks */ nsmall = 2*p - nbig; /* Number of small blocks */ /* If we have more small blocks than big blocks, we start with "nbig" pairs of blocks in which the first block is big and the second block is small, and then pad the end with the surplus number of small blocks. */ if( nbig < nsmall ) { s = 0; irow = 0; for( i = 0; i < nbig; i++ ) { soblow[ s ] = irow; irow += rpb; sobhigh[ s ] = irow; irow++; s++; soblow[ s ] = irow; irow += rpb - 1; sobhigh[ s ] = irow; irow++; s++; } for( i = s; i < 2*p; i++ ) { soblow[ i ] = irow; irow += rpb - 1; sobhigh[ i ] = irow; irow++; } /* If we have more big blocks than small blocks, we start with "nsmall" pairs of blocks in which the first block is big and the second block is small, and then pad the end with the surplus number of big blocks. */ } else { s = 0; irow = 0; for( i = 0; i < nsmall; i++ ) { soblow[ s ] = irow; irow += rpb; sobhigh[ s ] = irow; irow++; s++; soblow[ s ] = irow; irow += rpb - 1; sobhigh[ s ] = irow; irow++; s++; } for( i = s; i < 2*p; i++ ) { soblow[ i ] = irow; irow += rpb; sobhigh[ i ] = irow; irow++; } } /* Sanity check. */ if( ( irow != n || i != 2*p ) && *status == SAI__OK ) { *status = SAI__ERROR; errRep( "", "smf_svd: Error setting up the SOB arrays.", status ); goto L999; } /* Now proceed with the "Block JRS Algorithm" algorithm, as described in "A Block JRS Algorithm for Highly Parallel Computation of SVDs" (Soliman, et al, DOI: 10.1007/978-3-540-75444-2_36). Note, integer counters are one-based in the pseudo-code in the paper, but here we use zero-based counters as is normal in C. */ for( i = 0; i < p; i++ ) { up[ i ] = 2*i + 1; dn[ i ] = 2*i; } converged = 0; while( !converged ) { converged = 1; for( s = 0; s < 2*p; s++ ) { pdata = job_data + s; pdata->oper = 1; pdata->soblow = soblow[s]; pdata->sobhigh = sobhigh[s]; pdata->delta = delta; pdata->a = a; pdata->n = n; thrAddJob( wf, 0, pdata, smf1_svd, 0, NULL, status ); } thrWait( wf, status ); for( s = 0; s < 2*p; s++ ) { if( !job_data[s].converged ) converged = 0; } for( iter = 1; iter < 2*p; iter++ ) { for( s = 0; s < p; s++ ) { pdata = job_data + s; pdata->oper = 2; pdata->upsoblow = soblow[up[s]]; pdata->upsobhigh = sobhigh[up[s]]; pdata->dnsoblow = soblow[dn[s]]; pdata->dnsobhigh = sobhigh[dn[s]]; thrAddJob( wf, 0, pdata, smf1_svd, 0, NULL, status ); } thrWait( wf, status ); for( s = 0; s < 2*p; s++ ) { if( !job_data[s].converged ) converged = 0; } smf1_roundrobin( p, up, dn ); } } nstep = n/p; for( i = 0; i < p; i++ ) { pdata = job_data + i; pdata->j1 = i*nstep; if( i < p - 1 ){ pdata->j2 = pdata->j1 + nstep - 1; } else { pdata->j2 = n - 1; } pdata->sigma = sigma; pdata->oper = 3; thrAddJob( wf, 0, pdata, smf1_svd, 0, NULL, status ); } thrWait( wf, status ); /* If required, sort the singular values into descending order. */ if( sort ) { Sigma_array = sigma; double *arowold = astMalloc( n*sizeof( *arowold ) ); int *index = astMalloc( n*sizeof( *index ) ); if( *status == SAI__OK ) { for( i = 0; i < n; i++ ) index[ i ] = i; qsort( index, n, sizeof(*index), smf1_compare ); for( i = 0; i < n; i++ ) { sigold = sigma[ i ]; memcpy( arowold, a + i*n, n*sizeof(*a) ); j = i; while( 1 ) { k = index[ j ]; index[ j ] = j; if( k == i ) break; sigma[ j ] = sigma[ k ]; memcpy( a + j*n, a + k*n, n*sizeof(*a) ); j = k; } sigma[ j ] = sigold; memcpy( a + j*n, arowold, n*sizeof(*a) ); } } index = astFree( index ); arowold = astFree( arowold ); } /* If required, calculate the U matrix. */ if( u ) { for( i = 0; i < p; i++ ) { pdata = job_data + i; pdata->u = u; pdata->aorig = aorig; pdata->oper = 4; thrAddJob( wf, 0, pdata, smf1_svd, 0, NULL, status ); } thrWait( wf, status ); } } /* Free resources. */ L999: job_data = astFree( job_data ); up = astFree( up ); dn = astFree( dn ); soblow = astFree( soblow ); sobhigh = astFree( sobhigh ); aorig = astFree( aorig ); }
int smf_initial_sky( ThrWorkForce *wf, AstKeyMap *keymap, smfDIMMData *dat, int *iters, int *status ) { /* Local Variables: */ char refparam[ DAT__SZNAM ];/* Name for reference NDF parameter */ const char *cval; /* The IMPORTSKY string value */ double *ptr; /* Pointer to NDF Data array */ double *vptr; /* Pointer to NDF Variance array */ int indf1; /* Id. for supplied reference NDF */ int indf2; /* Id. for used section of reference NDF */ int nel; /* Number of mapped NDF pixels */ int result; /* Returned flag */ int there; /* Is there a smurf extension in the NDF? */ int update; /* Was NDF opened for UPDATE access? */ size_t i; /* Loop count */ size_t junk; /* Unused value */ /* Initialise the returned value to indicate no sky has been subtractred. */ result = 0; /* Assume the sky map was not created by an interupted previous run of makemap. */ *iters = -1; /* Check inherited status. */ if( *status != SAI__OK ) return result; /* Begin an AST context. */ astBegin; /* The IMPORTSKY config parameter should have the name of the ADAM parameter to use for acquiring the NDF that contains the initial sky estimate. If IMPORTSKY is "1", use REF. */ cval = NULL; astMapGet0C( keymap, "IMPORTSKY", &cval ); if( cval ) { if( !astChrMatch( cval, "REF" ) && !astChrMatch( cval, "MASK2" ) && !astChrMatch( cval, "MASK3" ) ) { astMapGet0I( keymap, "IMPORTSKY", &result ); cval = ( result > 0 ) ? "REF" : NULL; } if( cval ) { result = 1; strcpy( refparam, cval ); astChrCase( NULL, refparam, 1, 0 ); } } /* Do nothing more if we are not subtracting an initial sky from the data. */ if( result && *status == SAI__OK ) { /* Begin an NDF context. */ ndfBegin(); /* Get an identifier for the NDF using the associated ADAM parameter. First try UPDATE access. If this fails try READ access. */ ndfAssoc( refparam, "UPDATE", &indf1, status ); if( *status != SAI__OK ) { errAnnul( status ); ndfAssoc( refparam, "READ", &indf1, status ); update = 0; } else { update = 1; } /* Tell the user what we are doing. */ ndfMsg( "N", indf1 ); msgOut( "", "Using ^N as the initial guess at the sky", status ); /* Get a section from this NDF that matches the bounds of the map. */ ndfSect( indf1, 2, dat->lbnd_out, dat->ubnd_out, &indf2, status ); /* Ensure masked values are not set bad in the mapped data array. */ ndfSbb( 0, indf2, status ); /* Map the data array section, and copy it into the map buffer. */ ndfMap( indf2, "DATA", "_DOUBLE", "READ", (void **) &ptr, &nel, status ); if( *status == SAI__OK ) { memcpy( dat->map, ptr, dat->msize*sizeof(*ptr)); } /* Map the variance array section, and copy it into the map buffer. */ ndfState( indf2, "VARIANCE", &there, status ); if( there ) { ndfMap( indf2, "VARIANCE", "_DOUBLE", "READ", (void **) &vptr, &nel, status ); if( *status == SAI__OK ) { memcpy( dat->mapvar, vptr, dat->msize*sizeof(*vptr)); } } /* If the NDF was created by a previous run of makemap that was interupted using control-C, it will contain a NUMITER item in the smurf extension, which gives the number of iterations that were completed before the map was created. Obtain and return this value, if it exists. */ ndfXstat( indf1, SMURF__EXTNAME, &there, status ); if( there ) ndfXgt0i( indf1, SMURF__EXTNAME, "NUMITER", iters, status ); /* If the NDF has a Quality component, import it and create initial AST, FLT, PCA, SSN and COM masks from it. These will often be over-ridden by new masks calculated with smf_calcmodel_ast below, but will not be over-written if the masks have been frozen by xxx.zero_freeze. */ ndfState( indf2, "Quality", &there, status ); if( there && dat->mapqual ) { smf_qual_t *qarray = smf_qual_map( wf, indf2, "Read", NULL, &junk, status ); if( *status == SAI__OK ) { smf_qual_t *pq = qarray; for( i = 0; i < dat->msize; i++,pq++ ) { if( *pq & SMF__MAPQ_AST ) { if( !dat->ast_mask ) dat->ast_mask = astCalloc( dat->msize, sizeof( *(dat->ast_mask) ) ); (dat->ast_mask)[ i ] = 1; } if( *pq & SMF__MAPQ_FLT ) { if( !dat->flt_mask ) dat->flt_mask = astCalloc( dat->msize, sizeof( *(dat->flt_mask) ) ); (dat->flt_mask)[ i ] = 1; } if( *pq & SMF__MAPQ_COM ) { if( !dat->com_mask ) dat->com_mask = astCalloc( dat->msize, sizeof( *(dat->com_mask) ) ); (dat->com_mask)[ i ] = 1; } if( *pq & SMF__MAPQ_SSN ) { if( !dat->ssn_mask ) dat->ssn_mask = astCalloc( dat->msize, sizeof( *(dat->ssn_mask) ) ); (dat->ssn_mask)[ i ] = 1; } if( *pq & SMF__MAPQ_PCA ) { if( !dat->pca_mask ) dat->pca_mask = astCalloc( dat->msize, sizeof( *(dat->pca_mask) ) ); (dat->pca_mask)[ i ] = 1; } } } qarray = astFree( qarray ); } /* Indicate the map arrays within the supplied smfDIMMData structure now contain usable values. We need to do this before calling smf_calcmodel_ast below so that the right mask gets used in smf_calcmodel_ast. */ dat->mapok = 1; /* Apply any existinction correction to the cleaned bolometer data. */ if( dat->ext ) smf_calcmodel_ext( wf, dat, 0, keymap, dat->ext, 0, status); /* Sample the above map at the position of each bolometer sample and subtract the sampled value from the cleaned bolometer value. */ smf_calcmodel_ast( wf, dat, 0, keymap, NULL, SMF__DIMM_PREITER, status); /* Remove any existinction correction to the modifed bolometer data. */ if( dat->ext ) smf_calcmodel_ext( wf, dat, 0, keymap, dat->ext, SMF__DIMM_INVERT, status); /* If the NDF was opened with UPDATE access, update the quality array in the NDF to reflect the AST mask created by smf_calcmodel_ast above. */ if( update ) { smf_qual_t *qarray = astStore( NULL, dat->mapqual, dat->msize*sizeof(*qarray) ); qarray = smf_qual_unmap( wf, indf2, SMF__QFAM_MAP, qarray, status ); } /* End the NDF context. */ ndfEnd( status ); } /* End the AST context. */ astEnd; /* Return the pointer to the boolean mask. */ return result; }
void smf_snrmask( ThrWorkForce *wf, int abssnr, unsigned char *oldmask, const double *map, const double *mapvar, const dim_t *dims, double snr_hi, double snr_lo, unsigned char *mask, int *status ){ /* Local Variables: */ const double *pm = NULL; const double *pv = NULL; dim_t i; dim_t j; double snr; int *cindex = NULL; int *ps = NULL; int *psn = NULL; int *table = NULL; int iass; int iclean; int iclump; int ineb; int itemp; int itop1; int itop2; int iworker; int neb_offset[ 4 ]; int nworker; int ok; int rowstep; int top; smfSnrMaskJobData *job_data = NULL; smfSnrMaskJobData *pdata = NULL; unsigned char *maskold = NULL; /* Check inherited status */ if( *status != SAI__OK ) return; /* Save a copy of the old mask, if supplied. Doing it now, means that the old and new mask pointers can be the same. */ if( oldmask ) maskold = astStore( NULL, oldmask, sizeof(*oldmask)*dims[0]*dims[1] ); /* Allocate an array to hold a clump index for every map pixel. Initialise it to hold zeros. */ cindex = astCalloc( dims[ 0 ]*dims[ 1 ], sizeof( *cindex ) ); /* Initialise the index to assign to the next clump of pixels found above the lower SNR limit. Note, no clump is given an index of zero. */ top = 1; /* Initialise the pointer to the table holding associated clump indices. The first element is unused, so set it to a safe value of zero (i.e. "no clump"). */ table = astCalloc( top, sizeof( *table ) ); /* Set up the vector offsets to the three neighbouring pixels in the lower row, and the left hand neighbour in the current row. */ neb_offset[ 0 ] = -1; /* Left neighbour in current row */ neb_offset[ 1 ] = -dims[ 0 ] - 1; /* Left neighbour in lower row */ neb_offset[ 2 ] = -dims[ 0 ]; /* Central neighbour in lower row */ neb_offset[ 3 ] = -dims[ 0 ] + 1; /* Right neighbour in lower row */ /* Loop round the map, looking for pixels that are above the lower SNR limit. Within this loop we store a positive clump index for each pixel that is above the lower SNR limit. Each clump of contiguous pixel above the limit has a separate clump index. If two clumps touch each other, we associate their indices together using a table to indicate that they are part of the same physical clump. */ pm = map; pv = mapvar; ps = cindex; for( j = 0; j < dims[ 1 ] && *status == SAI__OK; j++ ) { for( i = 0; i < dims[ 0 ]; i++, pm++, pv++, ps++ ) { /* Get the SNR value. */ if( mapvar ) { if( *pm != VAL__BADD && *pv != VAL__BADD && *pv > 0.0 ){ snr = *pm / sqrt( *pv ); } else { snr = VAL__BADD; } } else { snr = *pm; } /* If source can be negative as well as positive, use the absolute SNR in the following check. */ if( abssnr && snr != VAL__BADD ) snr = fabs( snr ); /* Check the SNR is good and above the lower limit. */ if( snr != VAL__BADD && snr > snr_lo ){ /* The three neighbouring pixels on row (j-1), and the left hand neighbouring pixel on row j, have already been checked on earlier passes round this loop. Check each of these four pixels in turn to see if they were flagged as being above the lower SNR limit. */ itop1 = 0; for( ineb = 0; ineb < 4; ineb++ ) { /* Get a pointer to the neighbouring clump index value, checking it is not off the edge of the array. */ if( ineb == 0 ) { ok = ( i > 0 ); } else if( ineb == 1 ) { ok = ( i > 0 && j > 0 ); } else if( ineb == 2 ) { ok = ( j > 0 ); } else { ok = ( i < dims[ 0 ] - 1 && j > 0 ); } if( ok ) { psn = ps + neb_offset[ ineb ]; /* If this neighbour is flagged as above the lower SNR limit (i.e. has a positive clump index), and the current pixel has not yet been assigned to an existing clump, assign the neighbour's clump index to the current pixel. */ if( *psn > 0 ) { if( *ps == 0 ) { *ps = *psn; /* Find the clump index at the top of the tree containing the neighbouring pixel. */ itop1 = *psn; while( table[ itop1 ] ) itop1 = table[ itop1 ]; /* If this neighbour is flagged as above the lower SNR limit, but the current pixel has already been assigned to an existing clump, the current pixel is adjacent to both clumps and so joins them into one. So record that this neighbours clump index should be associated with the clump index of the current pixel. */ } else { /* We need to check first that the two clump indices are not already part of the same tree of associated clumps. Without this we could produce loops in the tree. Find the clump indices at the top of the tree containing the neighbouring pixel. */ itop2 = *psn; while( table[ itop2 ] ) itop2 = table[ itop2 ]; /* If the two clumps are not in the same tree, indicate that the pixel index at the top of the tree for the neighbouring pixels clump index is associated with the central pixel's clump index. */ if( itop1 != itop2 ) table[ itop2 ] = *ps; } } } } /* If the current pixel has no neighbours that are above the lower SNR limit, we start a new clump for the current pixel. */ if( *ps == 0 ) { /* Assign the next clump index to the current pixel, and then increment the next clump index. Report an error if we have reached the max allowable clump index value. */ if( top == INT_MAX ) { *status = SAI__ERROR; errRep( "", "smf_snrmask: Too many low-SNR clumps found.", status ); break; } *ps = top++; /* Extend the table that holds the associations between clumps. This table has one element for each clump index (plus an unused element at the start for the unused clump index "0"). The value stored in this table for a given clump index is the index of another clump with which the first clump should be associated. If two clumps are associated it indicates that they are part of the same physical clump. Associations form a tree structure. A value of zero in this table indicates that the clump is either unassociated with any other clump, or is at the head of a tree of associated clumps. */ table = astGrow( table, top, sizeof( *table ) ); if( *status != SAI__OK ) break; table[ *ps ] = 0; } } } } /* We now loop round the map again, this time looking for pixels that are above the higher SNR limit. */ pm = map; pv = mapvar; ps = cindex; for( j = 0; j < dims[ 1 ]; j++ ) { for( i = 0; i < dims[ 0 ]; i++, pm++, pv++, ps++ ) { /* Get the SNR value. */ if( mapvar ) { if( *pm != VAL__BADD && *pv != VAL__BADD && *pv > 0.0 ){ snr = *pm / sqrt( *pv ); } else { snr = VAL__BADD; } } else { snr = *pm; } /* If source can be negative as well as positive, use the absolute SNR. */ if( abssnr && snr != VAL__BADD ) snr = fabs( snr ); /* Check the SNR is good and above the upper limit. */ if( snr != VAL__BADD && snr > snr_hi ){ /* Since this pixel is above the higher SNR limit, it must also be above the lower SNR Limit, and so will have a non-zero clump index. We flag that this clump contains "source" pixels by storing a value of -1 for it in the clump association table. First record the original value for later use. */ iass = table[ *ps ]; table[ *ps ] = -1; /* If this clump index is associated with another clump (i.e. had a non-zero value in the clump association table), the two clumps adjoins each other. So indicate that the second clump also contains "source" pixels by changing its table value to -1. Enter a loop to do this all the way up to the top of the association tree. Note, this is not necessarily all adjoining clumps, since we have only gone "up" the tree - there may be other adjoining clumps lower down the tree. */ while( iass > 0 ) { itemp = table[ iass ]; table[ iass ] = -1; iass = itemp; } } } } /* Now check all cumps to see if they adjoin a "source" clump. Note, no clumps are given the index zero, so we skip the first element of the table. */ for( iclump = 1; iclump < top; iclump++ ) { iass = table[ iclump ]; /* Work up the tree of neighbouring clumps until we find a clump that has an index of 0 or -1. If 0, it means that we have reached the top of the tree without finding a "source" clump. If -1 it means we have reached a source clump. */ while( iass > 0 ) { iass = table[ iass ]; } /* If we have found a source clump, then all clumps above it in the tree should already be set to -1. We now walk up the tree from the current clump until we reach the source clump, marking all intermediate clumps as source clumps by setting them to -1 in the table. */ if( iass < 0 ) { iass = iclump; while( iass > 0 ) { itemp = table[ iass ]; table[ iass ] = -1; iass = itemp; } /* If no source clump was found, mark all intermediate clumps as non-source by setting theem to zero in the table. This may give us a little extra speed (maybe) since subsequent walks will terminate sooner. */ } else { iass = iclump; while( iass > 0 ) { itemp = table[ iass ]; table[ iass ] = 0; iass = itemp; } } } /* One last pass, to store the final mask values. We can multi-thread this bit. Create structures used to pass information to the worker threads. If we have more threads than rows, we will process one row in each thread and so we can reduce the number of threads used to equal the number of rows. */ nworker = wf ? wf->nworker : 1; if( nworker > (int) dims[ 1 ] ) nworker = dims[ 1 ]; job_data = astMalloc( nworker*sizeof( *job_data ) ); /* Check we can de-reference the job data pointer safely. */ if( *status == SAI__OK ) { /* Decide how many rows to process in each thread. */ rowstep = dims[ 1 ]/nworker; if( rowstep == 0 ) rowstep = 1; /* Set up the information needed by each thread, */ for( iworker = 0; iworker < nworker; iworker++ ) { pdata = job_data + iworker; pdata->operation = 1; pdata->cindex = cindex; pdata->jlo = iworker*rowstep; if( iworker == nworker - 1 ) { pdata->jhi = dims[ 1 ] - 1; } else { pdata->jhi = pdata->jlo + rowstep - 1; } pdata->rowlen = dims[ 0 ]; pdata->mask = mask; pdata->table = table; /* Pass the job to the workforce for execution. */ thrAddJob( wf, 0, pdata, smf1_snrmask_job, 0, NULL, status ); } /* Wait for the workforce to complete all jobs. */ thrWait( wf, status ); /* Now clean up the very crinkly edges of the mask. Also, the mask may contain small holes which need to be cleaned. Clean it NCLEAN times. */ for( iclean = 0; iclean < NCLEAN; iclean++ ) { /* Clean the mask, putting the cleaned mask into "cindex" array. We exclude pixels in the first and last rows since they do not have a complete set of neighbours (each worker thread also ignores the first and last pixel in each row for the same reason). Decide how many rows to process in each thread. */ rowstep = ( dims[ 1 ] - 2 )/nworker; if( rowstep == 0 ) rowstep = 1; /* Modify the information needed by each thread, */ for( iworker = 0; iworker < nworker; iworker++ ) { pdata = job_data + iworker; pdata->operation = 2; pdata->jlo = iworker*rowstep + 1; if( iworker == nworker - 1 ) { pdata->jhi = dims[ 1 ] - 2; } else { pdata->jhi = pdata->jlo + rowstep - 1; } /* Pass the job to the workforce for execution. */ thrAddJob( wf, 0, pdata, smf1_snrmask_job, 0, NULL, status ); } /* Wait for the workforce to complete all jobs. */ thrWait( wf, status ); /* Transfer the new mask from the "cindex" array back to the "mask" array. Add in any source pixels from the old mask if required. */ for( iworker = 0; iworker < nworker; iworker++ ) { pdata = job_data + iworker; pdata->maskold = maskold; pdata->operation = 3; thrAddJob( wf, 0, pdata, smf1_snrmask_job, 0, NULL, status ); } thrWait( wf, status ); /* If an old mask was supplied, ensure any source pixels in the old mask are also source pixels in the new mask. */ if( oldmask ) { for( iworker = 0; iworker < nworker; iworker++ ) { pdata = job_data + iworker; pdata->maskold = maskold; pdata->operation = 4; thrAddJob( wf, 0, pdata, smf1_snrmask_job, 0, NULL, status ); } thrWait( wf, status ); } } } /* Free resources. */ job_data = astFree( job_data ); maskold = astFree( maskold ); table = astFree( table ); cindex = astFree( cindex ); }