source: trunk/src/Detection/detection.cc @ 321

Last change on this file since 321 was 321, checked in by MatthewWhiting, 17 years ago
  • Solved most of the problem from ticket #12, where the integrated flux was being calculated differently on different machines. Now casting the spatial size of a detection to a double.
  • Solved ticket #13 as well, to allow compilation when PGPLOT is not available. Included moving cpgIsPS() to mycpgplot.cc.
File size: 15.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 <wcs.h>
33#include <math.h>
34#include <duchamp.hh>
35#include <param.hh>
36#include <fitsHeader.hh>
37#include <Utils/utils.hh>
38#include <PixelMap/Voxel.hh>
39#include <PixelMap/Object3D.hh>
40#include <Detection/detection.hh>
41
42using std::setw;
43using std::setprecision;
44using std::endl;
45using std::vector;
46
47using namespace PixelInfo;
48
49Detection::Detection(const Detection& d)
50{
51  this->pixelArray   = d.pixelArray;
52  this->xSubOffset   = d.xSubOffset;
53  this->ySubOffset   = d.ySubOffset;
54  this->zSubOffset   = d.zSubOffset;
55  this->totalFlux    = d.totalFlux;
56  this->intFlux      = d.intFlux;
57  this->peakFlux     = d.peakFlux;
58  this->xpeak        = d.xpeak;
59  this->ypeak        = d.ypeak;
60  this->zpeak        = d.zpeak;
61  this->peakSNR      = d.peakSNR;
62  this->xCentroid    = d.xCentroid;
63  this->yCentroid    = d.yCentroid;
64  this->zCentroid    = d.zCentroid;
65  this->centreType   = d.centreType;
66  this->negSource    = d.negSource;
67  this->flagText     = d.flagText;
68  this->id           = d.id;
69  this->name         = d.name;
70  this->flagWCS      = d.flagWCS;
71  this->numAxes      = d.numAxes;
72  this->raS          = d.raS;
73  this->decS         = d.decS;
74  this->ra           = d.ra;
75  this->dec          = d.dec;
76  this->raWidth      = d.raWidth;
77  this->decWidth     = d.decWidth;
78  this->specUnits    = d.specUnits;
79  this->fluxUnits    = d.fluxUnits;
80  this->intFluxUnits = d.intFluxUnits;
81  this->lngtype      = d.lngtype;
82  this->lattype      = d.lattype;
83  this->vel          = d.vel;
84  this->velWidth     = d.velWidth;
85  this->velMin       = d.velMin;
86  this->velMax       = d.velMax;
87  this->posPrec      = d.posPrec;
88  this->xyzPrec      = d.xyzPrec;
89  this->fintPrec     = d.fintPrec;
90  this->fpeakPrec    = d.fpeakPrec;
91  this->velPrec      = d.velPrec;
92  this->snrPrec      = d.snrPrec;
93}
94
95//--------------------------------------------------------------------
96
97Detection& Detection::operator= (const Detection& d)
98{
99  if(this == &d) return *this;
100  this->pixelArray   = d.pixelArray;
101  this->xSubOffset   = d.xSubOffset;
102  this->ySubOffset   = d.ySubOffset;
103  this->zSubOffset   = d.zSubOffset;
104  this->totalFlux    = d.totalFlux;
105  this->intFlux      = d.intFlux;
106  this->peakFlux     = d.peakFlux;
107  this->xpeak        = d.xpeak;
108  this->ypeak        = d.ypeak;
109  this->zpeak        = d.zpeak;
110  this->peakSNR      = d.peakSNR;
111  this->xCentroid    = d.xCentroid;
112  this->yCentroid    = d.yCentroid;
113  this->zCentroid    = d.zCentroid;
114  this->centreType   = d.centreType;
115  this->negSource    = d.negSource;
116  this->flagText     = d.flagText;
117  this->id           = d.id;
118  this->name         = d.name;
119  this->flagWCS      = d.flagWCS;
120  this->numAxes      = d.numAxes;
121  this->raS          = d.raS;
122  this->decS         = d.decS;
123  this->ra           = d.ra;
124  this->dec          = d.dec;
125  this->raWidth      = d.raWidth;
126  this->decWidth     = d.decWidth;
127  this->specUnits    = d.specUnits;
128  this->fluxUnits    = d.fluxUnits;
129  this->intFluxUnits = d.intFluxUnits;
130  this->lngtype      = d.lngtype;
131  this->lattype      = d.lattype;
132  this->vel          = d.vel;
133  this->velWidth     = d.velWidth;
134  this->velMin       = d.velMin;
135  this->velMax       = d.velMax;
136  this->posPrec      = d.posPrec;
137  this->xyzPrec      = d.xyzPrec;
138  this->fintPrec     = d.fintPrec;
139  this->fpeakPrec    = d.fpeakPrec;
140  this->velPrec      = d.velPrec;
141  this->snrPrec      = d.snrPrec;
142  return *this;
143}
144
145//--------------------------------------------------------------------
146
147void Detection::calcFluxes(float *fluxArray, long *dim)
148{
149  /**
150   *  A function that calculates total & peak fluxes (and the location
151   *  of the peak flux) for a Detection.
152   *
153   *  \param fluxArray The array of flux values to calculate the
154   *  flux parameters from.
155   *  \param dim The dimensions of the flux array.
156   */
157
158  this->totalFlux = this->peakFlux = 0;
159  this->xCentroid = this->yCentroid = this->zCentroid = 0.;
160  long y,z,count=0;
161
162  for(int m=0; m<this->pixelArray.getNumChanMap(); m++){
163    ChanMap *tempmap = new ChanMap;
164    *tempmap = this->pixelArray.getChanMap(m);
165    z = tempmap->getZ();
166    for(int s=0; s<tempmap->getNumScan(); s++){
167      Scan *tempscan = new Scan;
168      *tempscan = tempmap->getScan(s);
169      y = tempscan->getY();
170      for(long x=tempscan->getX(); x<=tempscan->getXmax(); x++){
171
172        float f = fluxArray[x + y*dim[0] + z*dim[0]*dim[1]];
173        this->totalFlux += f;
174        this->xCentroid += x*f;
175        this->yCentroid += y*f;
176        this->zCentroid += z*f;
177        if( (count==0) ||  //first time round
178            (this->negSource&&(f<this->peakFlux)) ||
179            (!this->negSource&&(f>this->peakFlux))   )
180          {
181            this->peakFlux = f;
182            this->xpeak =    x;
183            this->ypeak =    y;
184            this->zpeak =    z;
185          }
186        count++;
187      }
188      delete tempscan;
189    }
190    delete tempmap;
191  }
192
193  this->xCentroid /= this->totalFlux;
194  this->yCentroid /= this->totalFlux;
195  this->zCentroid /= this->totalFlux;
196}
197//--------------------------------------------------------------------
198
199void Detection::calcWCSparams(float *fluxArray, long *dim, FitsHeader &head)
200{
201  /**
202   *  Use the input wcs to calculate the position and velocity
203   *    information for the Detection.
204   *  Quantities calculated:
205   *  <ul><li> RA: ra [deg], ra (string), ra width.
206   *      <li> Dec: dec [deg], dec (string), dec width.
207   *      <li> Vel: vel [km/s], min & max vel, vel width.
208   *      <li> coord type for all three axes, nuRest,
209   *      <li> name (IAU-style, in equatorial or Galactic)
210   *  </ul>
211   *  Uses Detection::calcIntegFlux() to calculate the
212   *  integrated flux in (say) [Jy km/s]
213   *
214   *  Note that the regular parameters are NOT recalculated!
215   *
216   *  \param fluxArray The array of flux values to calculate the
217   *  integrated flux from.
218   *  \param dim The dimensions of the flux array.
219   *  \param head FitsHeader object that contains the WCS information.
220   */
221
222  if(head.isWCS()){
223
224    double *pixcrd = new double[15];
225    double *world  = new double[15];
226    /*
227      define a five-point array in 3D:
228      (x,y,z), (x,y,z1), (x,y,z2), (x1,y1,z), (x2,y2,z)
229      [note: x = central point, x1 = minimum x, x2 = maximum x etc.]
230      and convert to world coordinates.   
231    */
232    pixcrd[0]  = pixcrd[3] = pixcrd[6] = this->getXcentre();
233    pixcrd[9]  = this->getXmin()-0.5;
234    pixcrd[12] = this->getXmax()+0.5;
235    pixcrd[1]  = pixcrd[4] = pixcrd[7] = this->getYcentre();
236    pixcrd[10] = this->getYmin()-0.5;
237    pixcrd[13] = this->getYmax()+0.5;
238    pixcrd[2] = pixcrd[11] = pixcrd[14] = this->getZcentre();
239    pixcrd[5] = this->getZmin();
240    pixcrd[8] = this->getZmax();
241    int flag = head.pixToWCS(pixcrd, world, 5);
242    delete [] pixcrd;
243    if(flag!=0) duchampError("calcWCSparams",
244                             "Error in calculating the WCS for this object.");
245    else{
246
247      // world now has the WCS coords for the five points
248      //    -- use this to work out WCS params
249 
250      this->numAxes = head.getNumAxes();
251      this->lngtype = head.WCS().lngtyp;
252      this->lattype = head.WCS().lattyp;
253      this->specUnits = head.getSpectralUnits();
254      this->fluxUnits = head.getFluxUnits();
255      // if fluxUnits are eg. Jy/beam, make intFluxUnits = Jy km/s
256      this->intFluxUnits = head.getIntFluxUnits();
257      this->ra   = world[0];
258      this->dec  = world[1];
259      this->raS  = decToDMS(this->ra, this->lngtype);
260      this->decS = decToDMS(this->dec,this->lattype);
261      this->raWidth = angularSeparation(world[9],world[1],
262                                        world[12],world[1]) * 60.;
263      this->decWidth  = angularSeparation(world[0],world[10],
264                                          world[0],world[13]) * 60.;
265      this->name = head.getIAUName(this->ra, this->dec);
266      this->vel    = head.specToVel(world[2]);
267      this->velMin = head.specToVel(world[5]);
268      this->velMax = head.specToVel(world[8]);
269      this->velWidth = fabs(this->velMax - this->velMin);
270
271      this->calcIntegFlux(fluxArray,dim,head);
272   
273      this->flagWCS = true;
274    }
275    delete [] world;
276
277  }
278}
279//--------------------------------------------------------------------
280
281void Detection::calcIntegFlux(float *fluxArray, long *dim, FitsHeader &head)
282{
283  /**
284   *  Uses the input WCS to calculate the velocity-integrated flux,
285   *   putting velocity in units of km/s.
286   *  Integrates over full spatial and velocity range as given
287   *   by the extrema calculated by calcWCSparams.
288   *
289   *  If the flux units end in "/beam" (eg. Jy/beam), then the flux is
290   *  corrected by the beam size (in pixels). This is done by
291   *  multiplying the integrated flux by the number of spatial pixels,
292   *  and dividing by the beam size in pixels (e.g. Jy/beam * pix /
293   *  pix/beam --> Jy)
294   *
295   *  \param fluxArray The array of flux values.
296   *  \param dim The dimensions of the flux array.
297   *  \param head FitsHeader object that contains the WCS information.
298   */
299
300  if(head.getNumAxes() > 2) {
301
302    // include one pixel either side in each direction
303    long xsize = (this->getXmax()-this->getXmin()+3);
304    long ysize = (this->getYmax()-this->getYmin()+3);
305    long zsize = (this->getZmax()-this->getZmin()+3);
306    vector <bool> isObj(xsize*ysize*zsize,false);
307    double *localFlux = new double[xsize*ysize*zsize];
308    for(int i=0;i<xsize*ysize*zsize;i++) localFlux[i]=0.;
309    // work out which pixels are object pixels
310    for(int m=0; m<this->pixelArray.getNumChanMap(); m++){
311      ChanMap tempmap = this->pixelArray.getChanMap(m);
312      long z = this->pixelArray.getChanMap(m).getZ();
313      for(int s=0; s<this->pixelArray.getChanMap(m).getNumScan(); s++){
314        long y = this->pixelArray.getChanMap(m).getScan(s).getY();
315        for(long x=this->pixelArray.getChanMap(m).getScan(s).getX();
316            x<=this->pixelArray.getChanMap(m).getScan(s).getXmax();
317            x++){
318          long pos = (x-this->getXmin()+1) + (y-this->getYmin()+1)*xsize
319            + (z-this->getZmin()+1)*xsize*ysize;
320          localFlux[pos] = fluxArray[x + y*dim[0] + z*dim[0]*dim[1]];
321          isObj[pos] = true;
322        }
323      }
324    }
325 
326    // work out the WCS coords for each pixel
327    double *world  = new double[xsize*ysize*zsize];
328    double xpt,ypt,zpt;
329    for(int i=0;i<xsize*ysize*zsize;i++){
330      xpt = double( this->getXmin() -1 + i%xsize );
331      ypt = double( this->getYmin() -1 + (i/xsize)%ysize );
332      zpt = double( this->getZmin() -1 + i/(xsize*ysize) );
333      world[i] = head.pixToVel(xpt,ypt,zpt);
334    }
335
336    double integrated = 0.;
337    for(int pix=0; pix<xsize*ysize; pix++){ // loop over each spatial pixel.
338      for(int z=0; z<zsize; z++){
339        int pos =  z*xsize*ysize + pix;
340        if(isObj[pos]){ // if it's an object pixel...
341          double deltaVel;
342          if(z==0)
343            deltaVel = (world[pos+xsize*ysize] - world[pos]);
344          else if(z==(zsize-1))
345            deltaVel = (world[pos] - world[pos-xsize*ysize]);
346          else
347            deltaVel = (world[pos+xsize*ysize] - world[pos-xsize*ysize]) / 2.;
348          integrated += localFlux[pos] * fabs(deltaVel);
349        }
350      }
351    }
352    this->intFlux = integrated;
353
354    delete [] world;
355    delete [] localFlux;
356
357  }
358  else // in this case there is just a 2D image.
359    this->intFlux = this->totalFlux;
360
361  if(head.isWCS()){
362    // correct for the beam size if the flux units string ends in "/beam"
363    int size = this->fluxUnits.size();
364    std::string tailOfFluxUnits = this->fluxUnits.substr(size-5,size);
365    if(tailOfFluxUnits == "/beam")
366      this->intFlux  *= double(this->getSpatialSize())/head.getBeamSize();
367  }
368
369}
370//--------------------------------------------------------------------
371
372Detection operator+ (Detection lhs, Detection rhs)
373{
374  /**
375   *  Combines two objects by adding all the pixels using the Object3D
376   *  operator.
377   *
378   *  The pixel parameters are recalculated in the process (equivalent
379   *  to calling pixels().calcParams()), but WCS parameters
380   *  are not.
381   */
382  Detection output;
383  output.pixelArray = lhs.pixelArray + rhs.pixelArray;
384//   output.totalFlux  = lhs.totalFlux  + rhs.totalFlux;
385//   if(lhs.peakFlux > rhs.peakFlux){
386//     output.peakFlux = lhs.peakFlux;
387//     output.xpeak    = lhs.xpeak;
388//     output.ypeak    = lhs.ypeak;
389//     output.zpeak    = lhs.zpeak;
390//     output.peakSNR  = lhs.peakSNR;
391//   }
392//   else{
393//     output.peakFlux = rhs.peakFlux;
394//     output.xpeak    = rhs.xpeak;
395//     output.ypeak    = rhs.ypeak;
396//     output.zpeak    = rhs.zpeak;
397//     output.peakSNR  = rhs.peakSNR;
398//   }
399  return output;
400}
401//--------------------------------------------------------------------
402
403void Detection::setOffsets(Param &par)
404{
405  /**
406   * This function stores the values of the offsets for each cube axis.
407   * The offsets are the starting values of the cube axes that may differ from
408   *  the default value of 0 (for instance, if a subsection is being used).
409   * The values will be used when the detection is outputted.
410   */
411  this->xSubOffset = par.getXOffset();
412  this->ySubOffset = par.getYOffset();
413  this->zSubOffset = par.getZOffset();
414}
415//--------------------------------------------------------------------
416
417bool Detection::hasEnoughChannels(int minNumber)
418{
419  /**
420   * A function to determine if the Detection has enough
421   * contiguous channels to meet the minimum requirement
422   * given as the argument.
423   * \param minNumber How many channels is the minimum acceptable number?
424   * \return True if there is at least one occurence of minNumber consecutive
425   * channels present to return true. False otherwise.
426   */
427
428// Preferred method -- need a set of minNumber consecutive channels present.
429  this->pixelArray.order();
430  int numChannels = 0;
431  bool result = false;
432  int size = this->pixelArray.getNumChanMap();
433  if(size>0) numChannels++;
434  if( numChannels >= minNumber) result = true;
435  for(int i=1;(i<size && !result);i++) {
436    if( (this->pixelArray.getZ(i) - this->pixelArray.getZ(i-1)) == 1)
437      numChannels++;
438    else if( (this->pixelArray.getZ(i) - this->pixelArray.getZ(i-1)) >= 2)
439      numChannels = 1;
440
441    if( numChannels >= minNumber) result = true;
442  }
443  return result;
444 
445}
446//--------------------------------------------------------------------
447
448std::ostream& operator<< ( std::ostream& theStream, Detection& obj)
449{
450  /**
451   *  A convenient way of printing the coordinate values for each
452   *  pixel in the Detection. 
453   *
454   *  NOTE THAT THERE IS CURRENTLY NO FLUX INFORMATION BEING PRINTED!
455   *
456   *  Use as front end to the Object3D::operator<< function.
457   */ 
458
459  theStream << obj.pixelArray << "---\n";
460  return theStream;
461}
462//--------------------------------------------------------------------
463
Note: See TracBrowser for help on using the repository browser.