source: tags/release-1.1.4/src/Detection/detection.cc

Last change on this file was 418, checked in by MatthewWhiting, 16 years ago

A range of changes from CONRAD development:

  • Improved output for Object3D & Scan, and ordering for Object3D
  • == and match function for Voxel, implemented in Detection functions
  • Additional WCS & flux calculations for Cube using vectors of Voxels
  • Fixed setupColumns, removing the extra call to calcObjWCSparams.
  • New function prepareLogFile to wrap up the code to start a log file. Implemented in mainDuchamp.
File size: 19.5 KB
Line 
1// -----------------------------------------------------------------------
2// detection.cc : Member functions for the Detection class.
3// -----------------------------------------------------------------------
4// Copyright (C) 2006, Matthew Whiting, ATNF
5//
6// This program is free software; you can redistribute it and/or modify it
7// under the terms of the GNU General Public License as published by the
8// Free Software Foundation; either version 2 of the License, or (at your
9// option) any later version.
10//
11// Duchamp is distributed in the hope that it will be useful, but WITHOUT
12// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13// FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14// for more details.
15//
16// You should have received a copy of the GNU General Public License
17// along with Duchamp; if not, write to the Free Software Foundation,
18// Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA
19//
20// Correspondence concerning Duchamp may be directed to:
21//    Internet email: Matthew.Whiting [at] atnf.csiro.au
22//    Postal address: Dr. Matthew Whiting
23//                    Australia Telescope National Facility, CSIRO
24//                    PO Box 76
25//                    Epping NSW 1710
26//                    AUSTRALIA
27// -----------------------------------------------------------------------
28#include <iostream>
29#include <iomanip>
30#include <vector>
31#include <string>
32#include <wcslib/wcs.h>
33#include <math.h>
34#include <duchamp/duchamp.hh>
35#include <duchamp/param.hh>
36#include <duchamp/fitsHeader.hh>
37#include <duchamp/Utils/utils.hh>
38#include <duchamp/PixelMap/Voxel.hh>
39#include <duchamp/PixelMap/Object3D.hh>
40#include <duchamp/Detection/detection.hh>
41
42using std::setw;
43using std::setprecision;
44using std::endl;
45using std::vector;
46
47using namespace PixelInfo;
48
49namespace duchamp
50{
51
52
53  Detection::Detection()
54  {
55    this->flagWCS=false;
56    this->negSource = false;
57    this->flagText="";
58    this->totalFlux = peakFlux = 0.;
59    this->centreType="centroid";
60  }
61
62  Detection::Detection(const Detection& d)
63  {
64    operator=(d);
65  }
66
67  Detection& Detection::operator= (const Detection& d)
68  {
69    if(this == &d) return *this;
70    this->pixelArray   = d.pixelArray;
71    this->xSubOffset   = d.xSubOffset;
72    this->ySubOffset   = d.ySubOffset;
73    this->zSubOffset   = d.zSubOffset;
74    this->totalFlux    = d.totalFlux;
75    this->intFlux            = d.intFlux;
76    this->peakFlux     = d.peakFlux;
77    this->xpeak        = d.xpeak;
78    this->ypeak        = d.ypeak;
79    this->zpeak        = d.zpeak;
80    this->peakSNR      = d.peakSNR;
81    this->xCentroid    = d.xCentroid;
82    this->yCentroid    = d.yCentroid;
83    this->zCentroid    = d.zCentroid;
84    this->centreType   = d.centreType;
85    this->negSource    = d.negSource;
86    this->flagText     = d.flagText;
87    this->id           = d.id;
88    this->name         = d.name;
89    this->flagWCS      = d.flagWCS;
90    this->specOK       = d.specOK;
91    this->raS          = d.raS;
92    this->decS         = d.decS;
93    this->ra           = d.ra;
94    this->dec        = d.dec;
95    this->raWidth            = d.raWidth;
96    this->decWidth     = d.decWidth;
97    this->specUnits    = d.specUnits;
98    this->fluxUnits    = d.fluxUnits;
99    this->intFluxUnits = d.intFluxUnits;
100    this->lngtype            = d.lngtype;
101    this->lattype            = d.lattype;
102    this->vel          = d.vel;
103    this->velWidth     = d.velWidth;
104    this->velMin       = d.velMin;
105    this->velMax       = d.velMax;
106    this->posPrec      = d.posPrec;
107    this->xyzPrec      = d.xyzPrec;
108    this->fintPrec     = d.fintPrec;
109    this->fpeakPrec    = d.fpeakPrec;
110    this->velPrec            = d.velPrec;
111    this->snrPrec      = d.snrPrec;
112    return *this;
113  }
114
115  //--------------------------------------------------------------------
116
117  bool Detection::voxelListsMatch(std::vector<Voxel> voxelList)
118  {
119    /**
120     *  A test to see whether there is a 1-1 correspondence between
121     *  the given list of Voxels and the voxel positions contained in
122     *  this Detection's pixel list. No testing of the fluxes of the
123     *  Voxels is done.
124     *
125     * \param voxelList The std::vector list of Voxels to be tested.
126     */
127
128    bool listsMatch = true;
129    // compare sizes
130    listsMatch = listsMatch && (voxelList.size() == this->getSize());
131    if(!listsMatch) return listsMatch;
132
133    // make sure all voxels are in Detection
134    for(int i=0;i<voxelList.size();i++)
135      listsMatch = listsMatch && this->pixelArray.isInObject(voxelList[i]);
136    // make sure all Detection pixels are in voxel list
137    int v1=0, mysize=this->getSize();
138    while(listsMatch && v1<mysize){
139      bool inList = false;
140      int v2=0;
141      Voxel test = this->getPixel(v1);
142      while(!inList && v2<voxelList.size()){
143        inList = inList || test.match(voxelList[v2]);
144        v2++;
145      }
146      listsMatch = listsMatch && inList;
147      v1++;
148    }
149
150    return listsMatch;
151
152  }
153
154  //--------------------------------------------------------------------
155
156  void Detection::calcFluxes(std::vector<Voxel> voxelList)
157  {
158    /**
159     *  A function that calculates total & peak fluxes (and the location
160     *  of the peak flux) for a Detection.
161     *
162     *  \param fluxArray The array of flux values to calculate the
163     *  flux parameters from.
164     *  \param dim The dimensions of the flux array.
165     */
166
167    this->totalFlux = this->peakFlux = 0;
168    this->xCentroid = this->yCentroid = this->zCentroid = 0.;
169
170    // first check that the voxel list and the Detection's pixel list
171    // have a 1-1 correspondence
172
173    if(!this->voxelListsMatch(voxelList)){
174      duchampError("Detection::calcFluxes","Voxel list provided does not match");
175      return;
176    }
177
178    for(int i=0;i<voxelList.size();i++) {
179      long x = voxelList[i].getX();
180      long y = voxelList[i].getY();
181      long z = voxelList[i].getZ();
182      float f = voxelList[i].getF();
183      this->totalFlux += f;
184      this->xCentroid += x*f;
185      this->yCentroid += y*f;
186      this->zCentroid += z*f;
187      if( (i==0) ||  //first time round
188          (this->negSource&&(f<this->peakFlux)) ||
189          (!this->negSource&&(f>this->peakFlux))   )
190        {
191          this->peakFlux = f;
192          this->xpeak =    x;
193          this->ypeak =    y;
194          this->zpeak =    z;
195        }
196    }
197
198    this->xCentroid /= this->totalFlux;
199    this->yCentroid /= this->totalFlux;
200    this->zCentroid /= this->totalFlux;
201  }
202  //--------------------------------------------------------------------
203
204  void Detection::calcFluxes(float *fluxArray, long *dim)
205  {
206    /**
207     *  A function that calculates total & peak fluxes (and the location
208     *  of the peak flux) for a Detection.
209     *
210     *  \param fluxArray The array of flux values to calculate the
211     *  flux parameters from.
212     *  \param dim The dimensions of the flux array.
213     */
214
215    this->totalFlux = this->peakFlux = 0;
216    this->xCentroid = this->yCentroid = this->zCentroid = 0.;
217    long y,z,count=0;
218
219    for(int m=0; m<this->pixelArray.getNumChanMap(); m++){
220      ChanMap *tempmap = new ChanMap;
221      *tempmap = this->pixelArray.getChanMap(m);
222      z = tempmap->getZ();
223      for(int s=0; s<tempmap->getNumScan(); s++){
224        Scan *tempscan = new Scan;
225        *tempscan = tempmap->getScan(s);
226        y = tempscan->getY();
227        for(long x=tempscan->getX(); x<=tempscan->getXmax(); x++){
228
229          float f = fluxArray[x + y*dim[0] + z*dim[0]*dim[1]];
230          this->totalFlux += f;
231          this->xCentroid += x*f;
232          this->yCentroid += y*f;
233          this->zCentroid += z*f;
234          if( (count==0) ||  //first time round
235              (this->negSource&&(f<this->peakFlux)) ||
236              (!this->negSource&&(f>this->peakFlux))   )
237            {
238              this->peakFlux = f;
239              this->xpeak =    x;
240              this->ypeak =    y;
241              this->zpeak =    z;
242            }
243          count++;
244        }
245        delete tempscan;
246      }
247      delete tempmap;
248    }
249
250    this->xCentroid /= this->totalFlux;
251    this->yCentroid /= this->totalFlux;
252    this->zCentroid /= this->totalFlux;
253  }
254  //--------------------------------------------------------------------
255
256  //  void Detection::calcWCSparams(float *fluxArray, long *dim, FitsHeader &head)
257  void Detection::calcWCSparams(FitsHeader &head)
258  {
259    /**
260     *  Use the input wcs to calculate the position and velocity
261     *    information for the Detection.
262     *  Quantities calculated:
263     *  <ul><li> RA: ra [deg], ra (string), ra width.
264     *      <li> Dec: dec [deg], dec (string), dec width.
265     *      <li> Vel: vel [km/s], min & max vel, vel width.
266     *      <li> coord type for all three axes, nuRest,
267     *      <li> name (IAU-style, in equatorial or Galactic)
268     *  </ul>
269     *
270     *  Note that the regular parameters are NOT recalculated!
271     *
272     *  \param head FitsHeader object that contains the WCS information.
273     */
274
275    if(head.isWCS()){
276
277      double *pixcrd = new double[15];
278      double *world  = new double[15];
279      /*
280        define a five-point array in 3D:
281        (x,y,z), (x,y,z1), (x,y,z2), (x1,y1,z), (x2,y2,z)
282        [note: x = central point, x1 = minimum x, x2 = maximum x etc.]
283        and convert to world coordinates.   
284      */
285      pixcrd[0]  = pixcrd[3] = pixcrd[6] = this->getXcentre();
286      pixcrd[9]  = this->getXmin()-0.5;
287      pixcrd[12] = this->getXmax()+0.5;
288      pixcrd[1]  = pixcrd[4] = pixcrd[7] = this->getYcentre();
289      pixcrd[10] = this->getYmin()-0.5;
290      pixcrd[13] = this->getYmax()+0.5;
291      pixcrd[2] = pixcrd[11] = pixcrd[14] = this->getZcentre();
292      pixcrd[5] = this->getZmin();
293      pixcrd[8] = this->getZmax();
294      int flag = head.pixToWCS(pixcrd, world, 5);
295      delete [] pixcrd;
296      if(flag!=0) duchampError("calcWCSparams",
297                               "Error in calculating the WCS for this object.\n");
298      else{
299
300        // world now has the WCS coords for the five points
301        //    -- use this to work out WCS params
302 
303        this->specOK = head.canUseThirdAxis();
304        this->lngtype = head.WCS().lngtyp;
305        this->lattype = head.WCS().lattyp;
306        this->specUnits = head.getSpectralUnits();
307        this->fluxUnits = head.getFluxUnits();
308        // if fluxUnits are eg. Jy/beam, make intFluxUnits = Jy km/s
309        this->intFluxUnits = head.getIntFluxUnits();
310        this->ra   = world[0];
311        this->dec  = world[1];
312        this->raS  = decToDMS(this->ra, this->lngtype);
313        this->decS = decToDMS(this->dec,this->lattype);
314        this->raWidth = angularSeparation(world[9],world[1],
315                                          world[12],world[1]) * 60.;
316        this->decWidth  = angularSeparation(world[0],world[10],
317                                            world[0],world[13]) * 60.;
318        this->name = head.getIAUName(this->ra, this->dec);
319        this->vel    = head.specToVel(world[2]);
320        this->velMin = head.specToVel(world[5]);
321        this->velMax = head.specToVel(world[8]);
322        this->velWidth = fabs(this->velMax - this->velMin);
323
324        //      this->calcIntegFlux(fluxArray,dim,head);
325   
326        this->flagWCS = true;
327      }
328      delete [] world;
329
330    }
331  }
332  //--------------------------------------------------------------------
333
334  void Detection::calcIntegFlux(std::vector<Voxel> voxelList, FitsHeader &head)
335  {
336    /**
337     *  Uses the input WCS to calculate the velocity-integrated flux,
338     *   putting velocity in units of km/s.
339     *  The fluxes used are taken from the Voxels, rather than an
340     *   array of flux values.
341     *  Integrates over full spatial and velocity range as given
342     *   by the extrema calculated by calcWCSparams.
343     *
344     *  If the flux units end in "/beam" (eg. Jy/beam), then the flux is
345     *  corrected by the beam size (in pixels). This is done by
346     *  multiplying the integrated flux by the number of spatial pixels,
347     *  and dividing by the beam size in pixels (e.g. Jy/beam * pix /
348     *  pix/beam --> Jy)
349     *
350     *  \param voxelList The list of Voxels with flux information
351     *  \param head FitsHeader object that contains the WCS information.
352     */
353
354    if(!voxelListsMatch(voxelList)){
355      duchampError("Detection::calcIntegFlux","Voxel list provided does not match");
356      return;
357    }
358
359    if(head.getNumAxes() > 2) {
360
361      // include one pixel either side in each direction
362      long xsize = (this->getXmax()-this->getXmin()+3);
363      long ysize = (this->getYmax()-this->getYmin()+3);
364      long zsize = (this->getZmax()-this->getZmin()+3);
365      vector <bool> isObj(xsize*ysize*zsize,false);
366      double *localFlux = new double[xsize*ysize*zsize];
367      for(int i=0;i<xsize*ysize*zsize;i++) localFlux[i]=0.;
368
369      for(int i=0;i<voxelList.size();i++){
370        long x = voxelList[i].getX();
371        long y = voxelList[i].getY();
372        long z = voxelList[i].getZ();
373        long pos = (x-this->getXmin()+1) + (y-this->getYmin()+1)*xsize
374          + (z-this->getZmin()+1)*xsize*ysize;
375        localFlux[pos] = voxelList[i].getF();
376        isObj[pos] = true;
377      }
378 
379      // work out the WCS coords for each pixel
380      double *world  = new double[xsize*ysize*zsize];
381      double xpt,ypt,zpt;
382      for(int i=0;i<xsize*ysize*zsize;i++){
383        xpt = double( this->getXmin() -1 + i%xsize );
384        ypt = double( this->getYmin() -1 + (i/xsize)%ysize );
385        zpt = double( this->getZmin() -1 + i/(xsize*ysize) );
386        world[i] = head.pixToVel(xpt,ypt,zpt);
387      }
388
389      double integrated = 0.;
390      for(int pix=0; pix<xsize*ysize; pix++){ // loop over each spatial pixel.
391        for(int z=0; z<zsize; z++){
392          int pos =  z*xsize*ysize + pix;
393          if(isObj[pos]){ // if it's an object pixel...
394            double deltaVel;
395            if(z==0)
396              deltaVel = (world[pos+xsize*ysize] - world[pos]);
397            else if(z==(zsize-1))
398              deltaVel = (world[pos] - world[pos-xsize*ysize]);
399            else
400              deltaVel = (world[pos+xsize*ysize] - world[pos-xsize*ysize]) / 2.;
401            integrated += localFlux[pos] * fabs(deltaVel);
402          }
403        }
404      }
405      this->intFlux = integrated;
406
407      delete [] world;
408      delete [] localFlux;
409
410    }
411    else // in this case there is just a 2D image.
412      this->intFlux = this->totalFlux;
413
414    if(head.isWCS()){
415      // correct for the beam size if the flux units string ends in "/beam"
416      if(head.needBeamSize())
417        this->intFlux  *= double(this->getSpatialSize())/head.getBeamSize();
418    }
419
420  }
421  //--------------------------------------------------------------------
422
423  void Detection::calcIntegFlux(float *fluxArray, long *dim, FitsHeader &head)
424  {
425    /**
426     *  Uses the input WCS to calculate the velocity-integrated flux,
427     *   putting velocity in units of km/s.
428     *  Integrates over full spatial and velocity range as given
429     *   by the extrema calculated by calcWCSparams.
430     *
431     *  If the flux units end in "/beam" (eg. Jy/beam), then the flux is
432     *  corrected by the beam size (in pixels). This is done by
433     *  multiplying the integrated flux by the number of spatial pixels,
434     *  and dividing by the beam size in pixels (e.g. Jy/beam * pix /
435     *  pix/beam --> Jy)
436     *
437     *  \param fluxArray The array of flux values.
438     *  \param dim The dimensions of the flux array.
439     *  \param head FitsHeader object that contains the WCS information.
440     */
441
442    if(head.getNumAxes() > 2) {
443
444      // include one pixel either side in each direction
445      long xsize = (this->getXmax()-this->getXmin()+3);
446      long ysize = (this->getYmax()-this->getYmin()+3);
447      long zsize = (this->getZmax()-this->getZmin()+3);
448      vector <bool> isObj(xsize*ysize*zsize,false);
449      double *localFlux = new double[xsize*ysize*zsize];
450      for(int i=0;i<xsize*ysize*zsize;i++) localFlux[i]=0.;
451      // work out which pixels are object pixels
452      for(int m=0; m<this->pixelArray.getNumChanMap(); m++){
453        ChanMap tempmap = this->pixelArray.getChanMap(m);
454        long z = this->pixelArray.getChanMap(m).getZ();
455        for(int s=0; s<this->pixelArray.getChanMap(m).getNumScan(); s++){
456          long y = this->pixelArray.getChanMap(m).getScan(s).getY();
457          for(long x=this->pixelArray.getChanMap(m).getScan(s).getX();
458              x<=this->pixelArray.getChanMap(m).getScan(s).getXmax();
459              x++){
460            long pos = (x-this->getXmin()+1) + (y-this->getYmin()+1)*xsize
461              + (z-this->getZmin()+1)*xsize*ysize;
462            localFlux[pos] = fluxArray[x + y*dim[0] + z*dim[0]*dim[1]];
463            isObj[pos] = true;
464          }
465        }
466      }
467 
468      // work out the WCS coords for each pixel
469      double *world  = new double[xsize*ysize*zsize];
470      double xpt,ypt,zpt;
471      for(int i=0;i<xsize*ysize*zsize;i++){
472        xpt = double( this->getXmin() -1 + i%xsize );
473        ypt = double( this->getYmin() -1 + (i/xsize)%ysize );
474        zpt = double( this->getZmin() -1 + i/(xsize*ysize) );
475        world[i] = head.pixToVel(xpt,ypt,zpt);
476      }
477
478      double integrated = 0.;
479      for(int pix=0; pix<xsize*ysize; pix++){ // loop over each spatial pixel.
480        for(int z=0; z<zsize; z++){
481          int pos =  z*xsize*ysize + pix;
482          if(isObj[pos]){ // if it's an object pixel...
483            double deltaVel;
484            if(z==0)
485              deltaVel = (world[pos+xsize*ysize] - world[pos]);
486            else if(z==(zsize-1))
487              deltaVel = (world[pos] - world[pos-xsize*ysize]);
488            else
489              deltaVel = (world[pos+xsize*ysize] - world[pos-xsize*ysize]) / 2.;
490            integrated += localFlux[pos] * fabs(deltaVel);
491          }
492        }
493      }
494      this->intFlux = integrated;
495
496      delete [] world;
497      delete [] localFlux;
498
499    }
500    else // in this case there is just a 2D image.
501      this->intFlux = this->totalFlux;
502
503    if(head.isWCS()){
504      // correct for the beam size if the flux units string ends in "/beam"
505      if(head.needBeamSize())
506        this->intFlux  *= double(this->getSpatialSize())/head.getBeamSize();
507    }
508
509  }
510  //--------------------------------------------------------------------
511
512  Detection operator+ (Detection lhs, Detection rhs)
513  {
514    /**
515     *  Combines two objects by adding all the pixels using the Object3D
516     *  operator.
517     *
518     *  The pixel parameters are recalculated in the process (equivalent
519     *  to calling pixels().calcParams()), but WCS parameters are not.
520     */
521    Detection output = lhs;
522    output.pixelArray = lhs.pixelArray + rhs.pixelArray;
523    return output;
524  }
525  //--------------------------------------------------------------------
526
527  void Detection::setOffsets(Param &par)
528  {
529    /**
530     * This function stores the values of the offsets for each cube axis.
531     * The offsets are the starting values of the cube axes that may differ from
532     *  the default value of 0 (for instance, if a subsection is being used).
533     * The values will be used when the detection is outputted.
534     */
535    this->xSubOffset = par.getXOffset();
536    this->ySubOffset = par.getYOffset();
537    this->zSubOffset = par.getZOffset();
538  }
539  //--------------------------------------------------------------------
540
541  bool Detection::hasEnoughChannels(int minNumber)
542  {
543    /**
544     * A function to determine if the Detection has enough
545     * contiguous channels to meet the minimum requirement
546     * given as the argument.
547     * \param minNumber How many channels is the minimum acceptable number?
548     * \return True if there is at least one occurence of minNumber consecutive
549     * channels present to return true. False otherwise.
550     */
551
552    // Preferred method -- need a set of minNumber consecutive channels present.
553    this->pixelArray.order();
554    int numChannels = 0;
555    bool result = false;
556    int size = this->pixelArray.getNumChanMap();
557    if(size>0) numChannels++;
558    if( numChannels >= minNumber) result = true;
559    for(int i=1;(i<size && !result);i++) {
560      if( (this->pixelArray.getZ(i) - this->pixelArray.getZ(i-1)) == 1)
561        numChannels++;
562      else if( (this->pixelArray.getZ(i) - this->pixelArray.getZ(i-1)) >= 2)
563        numChannels = 1;
564
565      if( numChannels >= minNumber) result = true;
566    }
567    return result;
568 
569  }
570  //--------------------------------------------------------------------
571
572  std::ostream& operator<< ( std::ostream& theStream, Detection& obj)
573  {
574    /**
575     *  A convenient way of printing the coordinate values for each
576     *  pixel in the Detection. 
577     *
578     *  NOTE THAT THERE IS CURRENTLY NO FLUX INFORMATION BEING PRINTED!
579     *
580     *  Use as front end to the Object3D::operator<< function.
581     */ 
582
583    theStream << obj.pixelArray << "---\n";
584    return theStream;
585  }
586  //--------------------------------------------------------------------
587
588}
Note: See TracBrowser for help on using the repository browser.