source: tags/release-1.1.5/src/Cubes/outputSpectra.cc @ 1441

Last change on this file since 1441 was 436, checked in by MatthewWhiting, 16 years ago

Fixed the flux units for the plotted spectra, which were unnecessarily multiplied by the frequency/velocity.

File size: 19.0 KB
Line 
1// -----------------------------------------------------------------------
2// outputSpectra.cc: Print the spectra of the detected objects.
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 <fstream>
30#include <iomanip>
31#include <sstream>
32#include <string>
33#include <cpgplot.h>
34#include <math.h>
35#include <wcslib/wcs.h>
36#include <duchamp/param.hh>
37#include <duchamp/duchamp.hh>
38#include <duchamp/fitsHeader.hh>
39#include <duchamp/PixelMap/Object3D.hh>
40#include <duchamp/Cubes/cubes.hh>
41#include <duchamp/Cubes/plots.hh>
42#include <duchamp/Utils/utils.hh>
43#include <duchamp/Utils/mycpgplot.hh>
44
45using namespace mycpgplot;
46using namespace PixelInfo;
47
48namespace duchamp
49{
50
51  void getSmallVelRange(Detection &obj, FitsHeader head, float *minvel, float *maxvel);
52  void getSmallZRange(Detection &obj, float *minz, float *maxz);
53
54  void Cube::outputSpectra()
55  {
56    /**
57     * The way to display individual detected objects. The standard way
58     * is plot the full spectrum, plus a zoomed-in spectrum showing just
59     * the object, plus the 0th-moment map. If there is no spectral
60     * axis, just the 0th moment map is plotted (using
61     * Cube::plotSource() rather than Cube::plotSpectrum()).
62     *
63     * It makes use of the SpectralPlot or CutoutPlot classes from
64     * plots.h, which size everything correctly.
65     *
66     * The main choice for SpectralPlot() is whether to use the peak
67     * pixel, in which case the spectrum is just that of the peak pixel,
68     * or the sum, where the spectrum is summed over all spatial pixels
69     * that are in the object.  If a reconstruction has been done, that
70     * spectrum is plotted in red.  The limits of the detection are
71     * marked in blue.  A 0th moment map of the detection is also
72     * plotted, with a scale bar indicating the spatial scale.
73     */
74
75    if(this->fullCols.size()==0) this->setupColumns();
76    // in case cols haven't been set -- need the precisions for printing values.
77
78    std::string spectrafile = this->par.getSpectraFile() + "/vcps";
79    if(this->getDimZ()<=1){
80      Plot::CutoutPlot newplot;
81      if(newplot.setUpPlot(spectrafile.c_str())>0) {
82
83        for(int nobj=0;nobj<this->objectList->size();nobj++){
84          // for each object in the cube:
85          this->plotSource(this->objectList->at(nobj),newplot);
86     
87        }// end of loop over objects.
88
89        cpgclos();
90      }
91    }
92    else{
93      Plot::SpectralPlot newplot;
94      if(newplot.setUpPlot(spectrafile.c_str())>0) {
95
96        for(int nobj=0;nobj<this->objectList->size();nobj++){
97          // for each object in the cube:
98          this->plotSpectrum(nobj,newplot);
99     
100        }// end of loop over objects.
101
102        cpgclos();
103      }
104
105      if(this->par.getFlagTextSpectra()){
106        if(this->par.isVerbose()) std::cout << "Saving spectra in text file ... ";
107        this->writeSpectralData();
108        if(this->par.isVerbose()) std::cout << "Done. ";
109      }
110    }
111  }
112  //--------------------------------------------------------------------
113
114  void Cube::writeSpectralData()
115  {
116    /**
117     *  A function to write, in ascii form, the spectra of each
118     *  detected object to a file. The file consists of a column for
119     *  the spectral coordinates, and one column for each object
120     *  showing the flux at that spectral position. The units are the
121     *  same as those shown in the graphical output. The filename is
122     *  given by the Param::spectraTextFile parameter in the Cube::par
123     *  parameter set.
124     */
125
126    const int zdim = this->axisDim[2];
127    const int numObj = this->objectList->size();
128    float *specxOut = new float[zdim];
129    float *spectra = new float[numObj*zdim];
130   
131    for(int obj=0; obj<numObj; obj++){
132      float *temp = new float[zdim];
133      float *specx = new float[zdim];
134      float *recon = new float[zdim];
135      float *base = new float[zdim];
136      getSpectralArrays(obj, specx, temp, recon, base);
137      for(int z=0;z<zdim;z++) spectra[obj*zdim+z] = temp[z];
138      if(obj==0) for(int z=0;z<zdim;z++) specxOut[z] = specx[z];
139      delete [] specx;
140      delete [] recon;
141      delete [] base;
142      delete [] temp;
143    }
144   
145    std::ofstream fspec(this->par.getSpectraTextFile().c_str());
146    fspec.setf(std::ios::fixed);
147
148    for(int z=0;z<zdim;z++){
149     
150      fspec << std::setprecision(8);
151      fspec << specxOut[z] << "  ";
152      for(int obj=0;obj<numObj; obj++) {
153        fspec << spectra[obj*zdim+z] << "  ";
154      }
155      fspec << "\n";
156
157    }
158    fspec.close();
159
160    delete [] spectra;
161    delete [] specxOut;
162
163  }
164  //--------------------------------------------------------------------
165
166  void Cube::getSpectralArrays(int objNum, float *specx, float *specy,
167                               float *specRecon, float *specBase)
168  {
169    /**
170     *  A utility function that goes and calculates, for a given
171     *  Detection, the spectral arrays, according to whether we want
172     *  the peak or integrated flux. The arrays can be used by
173     *  Cube::plotSpectrum() and Cube::writeSpectralData(). The arrays
174     *  calculated are listed below. Their length is given by the
175     *  length of the Cube's spectral dimension.
176     *
177     *  Note that 'new' is used to allocate the array space, so the
178     *  array parameters need to be suitably defined
179     *
180     *  \param objNum The number of the object under consideration
181     *  \param specx The array of frequency/velocity/channel/etc
182     *         values (the x-axis on the spectral plot).
183     *  \param specy The array of flux values, matching the specx
184     *         array.
185     *  \param specRecon The reconstructed or smoothed array, done in
186     *         the same way as specy.
187     *  \param specBase The fitted baseline values, done in the same
188     *         way as specy.
189     */
190
191    long xdim = this->axisDim[0];
192    long ydim = this->axisDim[1];
193    long zdim = this->axisDim[2];
194
195    for(int i=0;i<zdim;i++) specy[i]     = 0.;
196    for(int i=0;i<zdim;i++) specRecon[i] = 0.;
197    for(int i=0;i<zdim;i++) specBase[i]  = 0.;
198
199    if(this->head.isWCS()){
200      double xval = double(this->objectList->at(objNum).getXcentre());
201      double yval = double(this->objectList->at(objNum).getYcentre());
202      for(double zval=0;zval<zdim;zval++)
203        specx[int(zval)] = this->head.pixToVel(xval,yval,zval);
204    }
205    else
206      for(double zval=0;zval<zdim;zval++) specx[int(zval)] = zval;
207
208    float beamCorrection;
209    if(this->header().needBeamSize())
210      beamCorrection = this->par.getBeamSize();
211    else beamCorrection = 1.;
212
213    if(this->par.getSpectralMethod()=="sum"){
214      bool *done = new bool[xdim*ydim];
215      for(int i=0;i<xdim*ydim;i++) done[i]=false;
216      std::vector<Voxel> voxlist = this->objectList->at(objNum).pixels().getPixelSet();
217      for(int pix=0;pix<voxlist.size();pix++){
218        int pos = voxlist[pix].getX() + xdim * voxlist[pix].getY();
219        if(!done[pos]){
220          done[pos] = true;
221          for(int z=0;z<zdim;z++){
222            if(!(this->isBlank(pos+z*xdim*ydim))){
223              specy[z] += this->array[pos + z*xdim*ydim] / beamCorrection;
224              if(this->reconExists)
225                specRecon[z] += this->recon[pos + z*xdim*ydim] / beamCorrection;
226              if(this->par.getFlagBaseline())
227                specBase[z] += this->baseline[pos + z*xdim*ydim] / beamCorrection;
228            }       
229          }
230        }
231      }
232      delete [] done;
233    }
234    else {// if(par.getSpectralMethod()=="peak"){
235      int pos = this->objectList->at(objNum).getXPeak() +
236        xdim*this->objectList->at(objNum).getYPeak();
237      for(int z=0;z<zdim;z++){
238        specy[z] = this->array[pos + z*xdim*ydim];
239        if(this->reconExists)
240          specRecon[z] = this->recon[pos + z*xdim*ydim];
241        if(this->par.getFlagBaseline())
242          specBase[z] = this->baseline[pos + z*xdim*ydim];
243      }
244    }
245
246  }
247  //--------------------------------------------------------------------
248
249  void Cube::plotSpectrum(int objNum, Plot::SpectralPlot &plot)
250  {
251    /**
252     * The way to print out the spectrum of a Detection.
253     * Makes use of the SpectralPlot class in plots.hh, which sizes
254     *  everything correctly.
255     *
256     * The main choice for the user is whether to use the peak pixel, in
257     * which case the spectrum is just that of the peak pixel, or the
258     * sum, where the spectrum is summed over all spatial pixels that
259     * are in the object.
260     *
261     * If a reconstruction has been done, that spectrum is plotted in
262     * red, and if a baseline has been calculated that is also shown, in
263     * yellow.  The spectral limits of the detection are marked in blue.
264     * A 0th moment map of the detection is also plotted, with a scale
265     * bar indicating the spatial size.
266     *
267     * \param objNum The number of the Detection to be plotted.
268     * \param plot The SpectralPlot object defining the PGPLOT device
269     *        to plot the spectrum on.
270     */
271
272    long zdim = this->axisDim[2];
273
274    this->objectList->at(objNum).calcFluxes(this->array, this->axisDim);
275
276    double minMWvel,maxMWvel,xval,yval,zval;
277    xval = double(this->objectList->at(objNum).getXcentre());
278    yval = double(this->objectList->at(objNum).getYcentre());
279    if(this->par.getFlagMW()){
280      zval = double(this->par.getMinMW());
281      minMWvel = this->head.pixToVel(xval,yval,zval);
282      zval = double(this->par.getMaxMW());
283      maxMWvel = this->head.pixToVel(xval,yval,zval);
284    }
285
286    float *specx  = new float[zdim];
287    float *specy  = new float[zdim];
288    float *specy2 = new float[zdim];
289    float *base   = new float[zdim];
290
291    this->getSpectralArrays(objNum,specx,specy,specy2,base);
292
293    std::string fluxLabel = "Flux";
294    std::string fluxUnits = this->head.getFluxUnits();
295    std::string intFluxUnits;// = this->head.getIntFluxUnits();
296    // Rather than use the intFluxUnits from the header, which will be like Jy MHz,
297    // we just use the pixel units, removing the /beam if necessary.
298    if(makelower(fluxUnits.substr(fluxUnits.size()-5,
299                                  fluxUnits.size()   )) == "/beam"){
300      intFluxUnits = fluxUnits.substr(0,fluxUnits.size()-5);
301    }
302    else intFluxUnits = fluxUnits;
303   
304
305    if(this->par.getSpectralMethod()=="sum"){
306      fluxLabel = "Integrated " + fluxLabel;
307      if(this->head.isWCS()) {
308        fluxLabel += " ["+intFluxUnits+"]";
309      }
310    }
311    else {// if(par.getSpectralMethod()=="peak"){
312      fluxLabel = "Peak " + fluxLabel;
313      if(this->head.isWCS()) fluxLabel += " ["+fluxUnits+"]";
314    }
315   
316    float vmax,vmin,width;
317    vmax = vmin = specx[0];
318    for(int i=1;i<zdim;i++){
319      if(specx[i]>vmax) vmax=specx[i];
320      if(specx[i]<vmin) vmin=specx[i];
321    }
322 
323    float max,min;
324    int loc=0;
325    if(this->par.getMinMW()>0) max = min = specy[0];
326    else max = min = specx[this->par.getMaxMW()+1];
327    for(int i=0;i<zdim;i++){
328      if(!this->par.isInMW(i)){
329        if(specy[i]>max) max=specy[i];
330        if(specy[i]<min){
331          min=specy[i];
332          loc = i;
333        }
334      }
335    }
336    // widen the ranges slightly so that the top & bottom & edges don't
337    // lie on the axes.
338    width = max - min;
339    max += width * 0.05;
340    min -= width * 0.05;
341    width = vmax -vmin;
342    vmax += width * 0.01;
343    vmin -= width * 0.01;
344
345    // now plot the resulting spectrum
346    std::string label;
347    if(this->head.isWCS()){
348      label = this->head.getSpectralDescription() + " [" +
349        this->head.getSpectralUnits() + "]";
350      plot.gotoHeader(label);
351    }
352    else plot.gotoHeader("Spectral pixel value");
353
354    if(this->head.isWCS()){
355      label = this->objectList->at(objNum).outputLabelWCS();
356      plot.firstHeaderLine(label);
357      label = this->objectList->at(objNum).outputLabelFluxes();
358      plot.secondHeaderLine(label);
359    }
360    label = this->objectList->at(objNum).outputLabelWidths();
361    plot.thirdHeaderLine(label);
362    label = this->objectList->at(objNum).outputLabelPix();
363    plot.fourthHeaderLine(label);
364   
365    plot.gotoMainSpectrum(vmin,vmax,min,max,fluxLabel);
366    cpgline(zdim,specx,specy);
367    if(this->par.getFlagBaseline()){
368      cpgsci(DUCHAMP_BASELINE_SPECTRA_COLOUR);
369      cpgline(zdim,specx,base);
370      cpgsci(FOREGND);
371    }
372    if(this->reconExists){
373      cpgsci(DUCHAMP_RECON_SPECTRA_COLOUR);
374      cpgline(zdim,specx,specy2);   
375      cpgsci(FOREGND);
376    }
377    if(this->par.getFlagMW()) plot.drawMWRange(minMWvel,maxMWvel);
378    if(this->head.isWCS()) plot.drawVelRange(this->objectList->at(objNum).getVelMin(),this->objectList->at(objNum).getVelMax());
379    else plot.drawVelRange(this->objectList->at(objNum).getZmin(),this->objectList->at(objNum).getZmax());
380
381    /**************************/
382    // ZOOM IN SPECTRALLY ON THE DETECTION.
383
384    float minvel,maxvel;
385    if(this->head.isWCS()) getSmallVelRange(this->objectList->at(objNum),this->head,&minvel,&maxvel);
386    else getSmallZRange(this->objectList->at(objNum),&minvel,&maxvel);
387
388    // Find new max & min flux values
389    std::swap(max,min);
390    int ct = 0;
391    for(int i=0;i<zdim;i++){
392      if((!this->par.isInMW(i))&&(specx[i]>=minvel)&&(specx[i]<=maxvel)){
393        ct++;
394        if(specy[i]>max) max=specy[i];
395        if(specy[i]<min) min=specy[i];
396      }
397    }
398    // widen the flux range slightly so that the top & bottom don't lie
399    // on the axes.
400    width = max - min;
401    max += width * 0.05;
402    min -= width * 0.05;
403
404    plot.gotoZoomSpectrum(minvel,maxvel,min,max);
405    cpgline(zdim,specx,specy);
406    if(this->par.getFlagBaseline()){
407      cpgsci(DUCHAMP_BASELINE_SPECTRA_COLOUR);
408      cpgline(zdim,specx,base);
409      cpgsci(FOREGND);
410    }
411    if(this->reconExists){
412      cpgsci(DUCHAMP_RECON_SPECTRA_COLOUR);
413      cpgline(zdim,specx,specy2);   
414      cpgsci(FOREGND);
415    }
416    if(this->par.getFlagMW()) plot.drawMWRange(minMWvel,maxMWvel);
417    if(this->head.isWCS()) plot.drawVelRange(this->objectList->at(objNum).getVelMin(),
418                                             this->objectList->at(objNum).getVelMax());
419    else plot.drawVelRange(this->objectList->at(objNum).getZmin(),this->objectList->at(objNum).getZmax());
420   
421    /**************************/
422
423    // DRAW THE MOMENT MAP OF THE DETECTION -- SUMMED OVER ALL CHANNELS
424    plot.gotoMap();
425    this->drawMomentCutout(this->objectList->at(objNum));
426
427    delete [] specx;
428    delete [] specy;
429    delete [] specy2;
430    delete [] base;
431 
432  }
433  //--------------------------------------------------------------------
434
435  void getSmallVelRange(Detection &obj, FitsHeader head,
436                        float *minvel, float *maxvel)
437  {
438    /**
439     *  Routine to calculate the velocity range for the zoomed-in region.
440     *  This range should be the maximum of 20 pixels, or 3x the wdith of
441     *   the detection.
442     *  Need to :
443     *      Calculate pixel width of a 3x-detection-width region.
444     *      If smaller than 20, calculate velocities of central vel +- 10 pixels
445     *      If not, use the 3x-detection-width
446     *  Range returned via "minvel" and "maxvel" parameters.
447     *  \param obj Detection under examination.
448     *  \param head FitsHeader, containing the WCS information.
449     *  \param minvel Returned value of minimum velocity
450     *  \param maxvel Returned value of maximum velocity
451     */
452
453    double *pixcrd = new double[3];
454    double *world  = new double[3];
455    float minpix,maxpix;
456    // define new velocity extrema
457    //    -- make it 3x wider than the width of the detection.
458    *minvel = 0.5*(obj.getVelMin()+obj.getVelMax()) - 1.5*obj.getVelWidth();
459    *maxvel = 0.5*(obj.getVelMin()+obj.getVelMax()) + 1.5*obj.getVelWidth();
460    // Find velocity range in number of pixels:
461    world[0] = obj.getRA();
462    world[1] = obj.getDec();
463    world[2] = head.velToSpec(*minvel);
464    head.wcsToPix(world,pixcrd);
465    minpix = pixcrd[2];
466    world[2] = head.velToSpec(*maxvel);
467    head.wcsToPix(world,pixcrd);
468    maxpix = pixcrd[2];
469    if(maxpix<minpix) std::swap(maxpix,minpix);
470   
471    if((maxpix - minpix + 1) < 20){
472      pixcrd[0] = double(obj.getXcentre());
473      pixcrd[1] = double(obj.getYcentre());
474      pixcrd[2] = obj.getZcentre() - 10.;
475      head.pixToWCS(pixcrd,world);
476      //    *minvel = setVel_kms(wcs,world[2]);
477      *minvel = head.specToVel(world[2]);
478      pixcrd[2] = obj.getZcentre() + 10.;
479      head.pixToWCS(pixcrd,world);
480      //     *maxvel = setVel_kms(wcs,world[2]);
481      *maxvel = head.specToVel(world[2]);
482      if(*maxvel<*minvel) std::swap(*maxvel,*minvel);
483    }
484    delete [] pixcrd;
485    delete [] world;
486
487  }
488  //--------------------------------------------------------------------
489
490  void getSmallZRange(Detection &obj, float *minz, float *maxz)
491  {
492    /**
493     *  Routine to calculate the pixel range for the zoomed-in spectrum.
494     *  This range should be the maximum of 20 pixels, or 3x the width
495     *   of the detection.
496     *  Need to :
497     *      Calculate pixel width of a 3x-detection-width region.
498     *       If smaller than 20, use central pixel +- 10 pixels
499     *  Range returned via "minz" and "maxz" parameters.
500     *  \param obj Detection under examination.
501     *  \param minz Returned value of minimum z-pixel coordinate
502     *  \param maxz Returned value of maximum z-pixel coordinate
503     */
504
505    *minz = 2.*obj.getZmin() - obj.getZmax();
506    *maxz = 2.*obj.getZmax() - obj.getZmin();
507   
508    if((*maxz - *minz + 1) < 20){
509      *minz = obj.getZcentre() - 10.;
510      *maxz = obj.getZcentre() + 10.;
511    }
512
513  }
514  //--------------------------------------------------------------------
515
516  void Cube::plotSource(Detection obj, Plot::CutoutPlot &plot)
517  {
518    /**
519     * The way to print out the 2d image cutout of a Detection.
520     * Makes use of the CutoutPlot class in plots.hh, which sizes
521     *  everything correctly.
522     *
523     * A 0th moment map of the detection is plotted, with a scale
524     * bar indicating the spatial size.
525     *
526     * Basic information on the source is printed next to it as well.
527     *
528     * \param obj The Detection to be plotted.
529     * \param plot The PGPLOT device to plot the spectrum on.
530     */
531
532    obj.calcFluxes(this->array, this->axisDim);
533
534    std::string label;
535    plot.gotoHeader();
536
537    if(this->head.isWCS()){
538      label = obj.outputLabelWCS();
539      plot.firstHeaderLine(label);
540      label = obj.outputLabelFluxes();
541      plot.secondHeaderLine(label);
542    }
543    label = obj.outputLabelWidths();
544    plot.thirdHeaderLine(label);
545    label = obj.outputLabelPix();
546    plot.fourthHeaderLine(label);
547   
548    // DRAW THE MOMENT MAP OF THE DETECTION -- SUMMED OVER ALL CHANNELS
549    plot.gotoMap();
550    this->drawMomentCutout(obj);
551
552  }
553
554}
Note: See TracBrowser for help on using the repository browser.