#include #include #include #include #include #include #include #include #include #include #include #include #include using std::endl; using namespace Column; using namespace mycpgplot; /****************************************************************/ /////////////////////////////////////////////////// //// Functions for DataArray class: /////////////////////////////////////////////////// DataArray::DataArray(short int nDim, long size){ if(size<0) duchampError("DataArray(nDim,size)", "Negative size -- could not define DataArray"); else if(nDim<0) duchampError("DataArray(nDim,size)", "Negative number of dimensions: could not define DataArray"); else { if(size>0) this->array = new float[size]; this->numPixels = size; if(nDim>0) this->axisDim = new long[nDim]; this->numDim = nDim; } } DataArray::DataArray(short int nDim, long *dimensions){ if(nDim<0) duchampError("DataArray(nDim,dimArray)", "Negative number of dimensions: could not define DataArray"); else { int size = dimensions[0]; for(int i=1;inumPixels = size; if(size>0){ this->array = new float[size]; } this->numDim=nDim; if(nDim>0) this->axisDim = new long[nDim]; for(int i=0;iaxisDim[i] = dimensions[i]; } } } DataArray::~DataArray() { delete [] array; delete [] axisDim; objectList.clear(); } void DataArray::getDimArray(long *output){ for(int i=0;inumDim;i++) output[i] = this->axisDim[i]; } void DataArray::getArray(float *output){ for(int i=0;inumPixels;i++) output[i] = this->array[i]; } void DataArray::saveArray(float *input, long size){ if(size != this->numPixels) duchampError("DataArray::saveArray", "Input array different size to existing array. Cannot save."); else { if(this->numPixels>0) delete [] this->array; this->numPixels = size; this->array = new float[size]; for(int i=0;iarray[i] = input[i]; } } void DataArray::getDim(long &x, long &y, long &z){ if(numDim>0) x=axisDim[0]; else x=0; if(numDim>1) y=axisDim[1]; else y=0; if(numDim>2) z=axisDim[2]; else z=0; } void DataArray::addObject(Detection object){ // adds a single detection to the object list // objectList is a vector, so just use push_back() this->objectList.push_back(object); } void DataArray::addObjectList(vector newlist) { for(int i=0;iobjectList.push_back(newlist[i]); } void DataArray::addObjectOffsets(){ for(int i=0;iobjectList.size();i++){ for(int p=0;pobjectList[i].getSize();p++){ this->objectList[i].setX(p,this->objectList[i].getX(p)+ this->par.getXOffset()); this->objectList[i].setY(p,this->objectList[i].getY(p)+ this->par.getYOffset()); this->objectList[i].setZ(p,this->objectList[i].getZ(p)+ this->par.getZOffset()); } } } std::ostream& operator<< ( std::ostream& theStream, DataArray &array) { for(int i=0;i0) theStream<<"x"; theStream<getSize();j++){ obj->setX(j,obj->getX(j)+obj->getXOffset()); obj->setY(j,obj->getY(j)+obj->getYOffset()); obj->setZ(j,obj->getZ(j)+obj->getZOffset()); } theStream<<*obj; delete obj; } theStream<<"--------------\n"; } /****************************************************************/ ///////////////////////////////////////////////////////////// //// Functions for Image class ///////////////////////////////////////////////////////////// Image::Image(long size){ // need error handling in case size<0 !!! if(size<0) duchampError("Image(size)","Negative size -- could not define Image"); else{ if(size>0){ this->array = new float[size]; this->pValue = new float[size]; this->mask = new short int[size]; } this->numPixels = size; this->axisDim = new long[2]; this->numDim = 2; } } Image::Image(long *dimensions){ int size = dimensions[0] * dimensions[1]; if(size<0) duchampError("Image(dimArray)","Negative size -- could not define Image"); else{ this->numPixels = size; if(size>0){ this->array = new float[size]; this->pValue = new float[size]; this->mask = new short int[size]; } this->numDim=2; this->axisDim = new long[2]; for(int i=0;i<2;i++) this->axisDim[i] = dimensions[i]; for(int i=0;imask[i] = 0; } } Image::~Image(){ if(this->numPixels > 0){ delete [] this->pValue; delete [] this->mask; } } void Image::saveArray(float *input, long size){ if(size != this->numPixels) duchampError("Image::saveArray", "Input array different size to existing array. Cannot save."); else { if(this->numPixels>0){ delete [] array; delete [] pValue; delete [] mask; } this->numPixels = size; if(this->numPixels>0){ this->array = new float[size]; this->pValue = new float[size]; this->mask = new short int[size]; } for(int i=0;iarray[i] = input[i]; for(int i=0;imask[i] = 0; } } void Image::maskObject(Detection &object) { /** * Image::maskObject(Detection &) * A function that increments the mask for each pixel of the detection. */ for(long i=0;isetMaskValue(object.getX(i),object.getY(i),1); } } void Image::extractSpectrum(float *Array, long *dim, long pixel) { /** * Image::extractSpectrum(float *, long *, int) * A function to extract a 1-D spectrum from a 3-D array. * The array is assumed to be 3-D with the third dimension the spectral one. * The dimensions of the array are in the dim[] array. * The spectrum extracted is the one lying in the spatial pixel referenced * by the third argument. */ float *spec = new float[dim[2]]; for(int z=0;zsaveArray(spec,dim[2]); delete [] spec; } void Image::extractSpectrum(Cube &cube, long pixel) { /** * Image::extractSpectrum(Cube &, int) * A function to extract a 1-D spectrum from a Cube class * The spectrum extracted is the one lying in the spatial pixel referenced * by the second argument. */ long zdim = cube.getDimZ(); long spatSize = cube.getDimX()*cube.getDimY(); float *spec = new float[zdim]; for(int z=0;zsaveArray(spec,zdim); delete [] spec; } void Image::extractImage(float *Array, long *dim, long channel) { /** * Image::extractImage(float *, long *, int) * A function to extract a 2-D image from a 3-D array. * The array is assumed to be 3-D with the third dimension the spectral one. * The dimensions of the array are in the dim[] array. * The image extracted is the one lying in the channel referenced * by the third argument. */ float *image = new float[dim[0]*dim[1]]; for(int npix=0; npixsaveArray(image,dim[0]*dim[1]); delete [] image; } void Image::extractImage(Cube &cube, long channel) { /** * Image::extractImage(Cube &, int) * A function to extract a 2-D image from Cube class. * The image extracted is the one lying in the channel referenced * by the second argument. */ long spatSize = cube.getDimX()*cube.getDimY(); float *image = new float[spatSize]; for(int npix=0; npixsaveArray(image,spatSize); delete [] image; } void Image::removeMW() { /** * Image::removeMW() * A function to remove the Milky Way range of channels from a 1-D spectrum. * The array in this Image is assumed to be 1-D, with only the first axisDim * equal to 1. * The values of the MW channels are set to 0, unless they are BLANK. */ if(this->par.getFlagMW()){ int maxMW = this->par.getMaxMW(); int minMW = this->par.getMinMW(); if(this->axisDim[1]==1){ for(int z=0;zaxisDim[0];z++){ if(!this->isBlank(z) && this->par.isInMW(z)) this->array[z]=0.; } } } } void Image::findStats(int code) { /** * Image::findStats(int code) * Front-end to function to find the stats (mean/median & sigma/madfm) and * store them in the "mean" and "sigma" members of Image. * The choice of normal(mean & sigma) or robust (median & madfm) is made * via the code parameter. This is stored as a decimal number, with 0s * representing normal stats, and 1s representing robust. * The 10s column is the mean, the 1s column the sigma. * Eg: 00 -- meanσ 01 -- mean&madfm; * 10 -- medianσ 11 -- median&madfm * If calculated, the madfm value is corrected to sigma units. * The Image member "cut" is also assigned using the parameter in Image's * par (needs to be defined first -- also for the blank pixel * determination). */ float *tempArray = new float[this->numPixels]; int goodSize=0; for(int i=0; inumPixels; i++) if(!this->isBlank(i)) tempArray[goodSize++] = this->array[i]; float tempMean,tempSigma,tempMedian,tempMADFM; if(code != 0) findMedianStats(tempArray,goodSize,tempMedian,tempMADFM); if(code != 11) findNormalStats(tempArray,goodSize,tempMean,tempSigma); switch(code) { case 0: findNormalStats(tempArray,goodSize,tempMean,tempSigma); this->mean = tempMean; this->sigma = tempSigma; break; case 10: this->mean = findMedian(tempArray,goodSize);; this->sigma = findStddev(tempArray,goodSize); break; case 1: this->mean = findMean(tempArray,goodSize); this->sigma = findMADFM(tempArray,goodSize)/correctionFactor; break; case 11: default: if(code!=11) { std::stringstream errmsg; errmsg << "Invalid code ("<mean = tempMedian; this->sigma = tempMADFM/correctionFactor; break; } this->cutLevel = this->par.getCut(); delete [] tempArray; } /****************************************************************/ ///////////////////////////////////////////////////////////// //// Functions for Cube class ///////////////////////////////////////////////////////////// Cube::Cube(long size){ // need error handling in case size<0 !!! if(size<0) duchampError("Cube(size)","Negative size -- could not define Cube"); else{ if(size>0){ this->array = new float[size]; this->recon = new float[size]; } this->numPixels = size; this->axisDim = new long[2]; this->numDim = 3; this->reconExists = false; } } Cube::Cube(long *dimensions){ int size = dimensions[0] * dimensions[1] * dimensions[2]; int imsize = dimensions[0] * dimensions[1]; if((size<0) || (imsize<0) ) duchampError("Cube(dimArray)","Negative size -- could not define Cube"); else{ this->numPixels = size; if(size>0){ this->array = new float[size]; this->detectMap = new short[imsize]; if(this->par.getFlagATrous()) this->recon = new float[size]; if(this->par.getFlagBaseline()) this->baseline = new float[size]; } this->numDim = 3; this->axisDim = new long[3]; for(int i=0;i<3 ;i++) this->axisDim[i] = dimensions[i]; for(int i=0;idetectMap[i] = 0; } } Cube::~Cube() { delete [] detectMap; if(this->par.getFlagATrous()) delete [] recon; if(this->par.getFlagBaseline()) delete [] baseline; delete [] specMean,specSigma,chanMean,chanSigma; } void Cube::initialiseCube(long *dimensions) { /** * Cube::initialiseCube(long *dim) * A function that defines the sizes of all the necessary * arrays in the Cube class. * It also defines the values of the axis dimensions. * This is done with the WCS in the FitsHeader class, so the * WCS needs to be good and have three axes. */ if(!this->head.isWCS()){ duchampError("Cube::initialiseCube", "The WCS is not sufficiently defined. Not able to define Cube."); } else if(this->head.getWCS()->naxis<3){ duchampError("Cube::initialiseCube", "The WCS does not have three axes defined. Not able to define Cube."); } else{ int lng = this->head.getWCS()->lng; int lat = this->head.getWCS()->lat; int spc = this->head.getWCS()->spec; int size = dimensions[lng] * dimensions[lat] * dimensions[spc]; int imsize = dimensions[lng] * dimensions[lat]; if((size<0) || (imsize<0) ) duchampError("Cube::initialiseCube(dimArray)", "Negative size -- could not define Cube"); else{ this->numPixels = size; if(size>0){ this->array = new float[size]; this->detectMap = new short[imsize]; this->specMean = new float[imsize]; this->specSigma = new float[imsize]; this->chanMean = new float[dimensions[spc]]; this->chanSigma = new float[dimensions[spc]]; if(this->par.getFlagATrous()) this->recon = new float[size]; if(this->par.getFlagBaseline()) this->baseline = new float[size]; } this->numDim = 3; this->axisDim = new long[3]; this->axisDim[0] = dimensions[lng]; this->axisDim[1] = dimensions[lat]; this->axisDim[2] = dimensions[spc]; for(int i=0;idetectMap[i] = 0; } } } int Cube::getopts(int argc, char ** argv) { /** * Cube::getopt * A front-end to read in the command-line options, * and then read in the correct parameters to the cube->par */ int returnValue; if(argc==1){ std::cout << ERR_USAGE_MSG; returnValue = FAILURE; } else { string file; Param *par = new Param; char c; while( ( c = getopt(argc,argv,"p:f:hv") )!=-1){ switch(c) { case 'p': file = optarg; if(this->readParam(file)==FAILURE){ stringstream errmsg; errmsg << "Could not open parameter file " << file << ".\n"; duchampError("Duchamp",errmsg.str()); returnValue = FAILURE; } else returnValue = SUCCESS; break; case 'f': file = optarg; par->setImageFile(file); this->saveParam(*par); returnValue = SUCCESS; break; case 'v': std::cout << PROGNAME << " version " << VERSION << std::endl; returnValue = FAILURE; break; case 'h': default : std::cout << ERR_USAGE_MSG; returnValue = FAILURE; break; } } delete par; } return returnValue; } void Cube::saveArray(float *input, long size){ if(size != this->numPixels){ stringstream errmsg; errmsg << "Input array different size to existing array (" << size << " cf. " << this->numPixels << "). Cannot save.\n"; duchampError("Cube::saveArray",errmsg.str()); } else { if(this->numPixels>0) delete [] array; this->numPixels = size; this->array = new float[size]; for(int i=0;iarray[i] = input[i]; } } void Cube::saveRecon(float *input, long size){ if(size != this->numPixels){ stringstream errmsg; errmsg << "Input array different size to existing array (" << size << " cf. " << this->numPixels << "). Cannot save.\n"; duchampError("Cube::saveRecon",errmsg.str()); } else { if(this->numPixels>0) delete [] this->recon; this->numPixels = size; this->recon = new float[size]; for(int i=0;irecon[i] = input[i]; this->reconExists = true; } } void Cube::getRecon(float *output){ // Need check for change in number of pixels! long size = this->numPixels; for(int i=0;ireconExists) output[i] = this->recon[i]; else output[i] = 0.; } } void Cube::removeMW() { if(this->par.getFlagMW()){ for(int pix=0;pixaxisDim[0]*this->axisDim[1];pix++){ for(int z=0;zaxisDim[2];z++){ int pos = z*this->axisDim[0]*this->axisDim[1] + pix; if(!this->isBlank(pos) && this->par.isInMW(z)) this->array[pos]=0.; } } } } void Cube::calcObjectWCSparams() { /** * Cube::calcObjectWCSparams() * A function that calculates the WCS parameters for each object in the * cube's list of detections. * Each object gets an ID number set (just the order in the list), and if * the WCS is good, the WCS paramters are calculated. */ for(int i=0; iobjectList.size();i++){ this->objectList[i].setID(i+1); this->objectList[i].calcWCSparams(this->head); } } void Cube::sortDetections() { /** * Cube::sortDetections() * A front end to the sort-by functions. * If there is a good WCS, the detection list is sorted by velocity. * Otherwise, it is sorted by increasing z-pixel value. * The ID numbers are then re-calculated. */ if(this->head.isWCS()) SortByVel(this->objectList); else SortByZ(this->objectList); for(int i=0; iobjectList.size();i++) this->objectList[i].setID(i+1); } void Cube::updateDetectMap() { /** * Cube::updateDetectMap() * A function that, for each detected object in the cube's list, increments * the cube's detection map by the required amount at each pixel. */ for(int obj=0;objobjectList.size();obj++){ for(int pix=0;pixobjectList[obj].getSize();pix++) { int spatialPos = this->objectList[obj].getX(pix)+ this->objectList[obj].getY(pix)*this->axisDim[0]; this->detectMap[spatialPos]++; } } } void Cube::updateDetectMap(Detection obj) { /** * Cube::updateDetectMap(Detection) * A function that, for the given object, increments the cube's * detection map by the required amount at each pixel. */ for(int pix=0;pixaxisDim[0]; this->detectMap[spatialPos]++; } } void Cube::setCubeStats() { // First set the stats for each spectrum (ie. each spatial pixel) long xySize = this->axisDim[0]*this->axisDim[1]; float *spec = new float[this->axisDim[2]]; for(int i=0;iaxisDim[2];z++){ //Two cases: i) have reconstructed -- use residuals // ii) otherwise -- use original array if(this->reconExists) spec[z] = this->array[z*xySize+i] - this->recon[z*xySize+1]; else spec[z] = this->array[z*xySize+i]; } findMedianStats(spec, this->axisDim[2], this->specMean[i], this->specSigma[i]); } delete spec; // Then set the stats for each channel map float *im = new float[xySize]; for(int z=0;zaxisDim[2];z++){ for(int i=0;ireconExists) im[i] = this->array[z*xySize+i] - this->recon[z*xySize+1]; else im[i] = this->array[z*xySize+i]; } findMedianStats(im,this->axisDim[2],this->chanMean[z],this->chanSigma[z]); this->chanSigma[z] /= correctionFactor; } delete im; } float Cube::enclosedFlux(Detection obj) { /** * float Cube::enclosedFlux(Detection obj) * A function to calculate the flux enclosed by the range * of pixels detected in the object obj (not necessarily all * pixels will have been detected). */ obj.calcParams(); int xsize = obj.getXmax()-obj.getXmin()+1; int ysize = obj.getYmax()-obj.getYmin()+1; int zsize = obj.getZmax()-obj.getZmin()+1; vector fluxArray(xsize*ysize*zsize,0.); for(int x=0;xgetPixValue(x+obj.getXmin(), y+obj.getYmin(), z+obj.getZmin()); if(this->par.getFlagNegative()) fluxArray[x+y*xsize+z*ysize*xsize] *= -1.; } } } float sum = 0.; for(int i=0;ipar.isBlank(fluxArray[i])) sum+=fluxArray[i]; return sum; } void Cube::setupColumns() { /** * Cube::setupColumns() * A front-end to the two setup routines in columns.cc. */ for(int i=0; iobjectList.size();i++) this->objectList[i].setID(i+1); this->fullCols.clear(); this->fullCols = getFullColSet(this->objectList, this->head); this->logCols.clear(); this->logCols = getLogColSet(this->objectList, this->head); int vel,fpeak,fint,pos,xyz,temp; vel = fullCols[VEL].getPrecision(); fpeak = fullCols[FPEAK].getPrecision(); xyz = fullCols[X].getPrecision(); if(temp=fullCols[Y].getPrecision() > xyz) xyz = temp; if(temp=fullCols[Z].getPrecision() > xyz) xyz = temp; if(this->head.isWCS()) fint = fullCols[FINT].getPrecision(); else fint = fullCols[FTOT].getPrecision(); pos = fullCols[WRA].getPrecision(); if(temp=fullCols[WDEC].getPrecision() > pos) pos = temp; for(int obj=0;objobjectList.size();obj++){ this->objectList[obj].setVelPrec(vel); this->objectList[obj].setFpeakPrec(fpeak); this->objectList[obj].setXYZPrec(xyz); this->objectList[obj].setPosPrec(pos); this->objectList[obj].setFintPrec(fint); } } bool Cube::objAtEdge(Detection obj) { /** * bool Cube::objAtEdge() * A function to test whether the object obj * lies at the edge of the cube's field -- * either at the boundary, or next to BLANKs */ bool atEdge = false; int pix = 0; while(!atEdge && pix=this->axisDim[0])) atEdge = true; else if(this->isBlank(vox.getX()+dx,vox.getY(),vox.getZ())) atEdge = true; } for(int dy=-1;dy<=1;dy+=2){ if(((vox.getY()+dy)<0) || ((vox.getY()+dy)>=this->axisDim[1])) atEdge = true; else if(this->isBlank(vox.getX(),vox.getY()+dy,vox.getZ())) atEdge = true; } for(int dz=-1;dz<=1;dz+=2){ if(((vox.getZ()+dz)<0) || ((vox.getZ()+dz)>=this->axisDim[2])) atEdge = true; else if(this->isBlank(vox.getX(),vox.getY(),vox.getZ()+dz)) atEdge = true; } pix++; } return atEdge; } void Cube::setObjectFlags() { /** * void Cube::setObjectFlags() * A function to set any warning flags for all the detected objects * associated with the cube. * Flags to be looked for: * * Negative enclosed flux (N) * * Object at edge of field (E) */ for(int i=0;iobjectList.size();i++){ if( this->enclosedFlux(this->objectList[i]) < 0. ) this->objectList[i].addToFlagText("N"); if( this->objAtEdge(this->objectList[i]) ) this->objectList[i].addToFlagText("E"); } } void Cube::plotBlankEdges() { if(this->par.drawBlankEdge()){ int colour; cpgqci(&colour); cpgsci(MAGENTA); drawBlankEdges(this->array,this->axisDim[0],this->axisDim[1],this->par); cpgsci(colour); } }