source: tags/release-1.5/src/Detection/detection.cc @ 1455

Last change on this file since 1455 was 1269, checked in by MatthewWhiting, 11 years ago

Ticket #198 - Fixing a bug with the velocity-width measurements. The mask is now properly calculated to enable accurate extraction of the integrated spectrum of a detection. Also in this changeset a few alterations to alternative parameter-measurement functions flowing out of the Selavy development.

File size: 41.3 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 <map>
32#include <string>
33#include <wcslib/wcs.h>
34#include <math.h>
35#include <duchamp/duchamp.hh>
36#include <duchamp/param.hh>
37#include <duchamp/fitsHeader.hh>
38#include <duchamp/Utils/utils.hh>
39#include <duchamp/PixelMap/Voxel.hh>
40#include <duchamp/PixelMap/Object3D.hh>
41#include <duchamp/Detection/detection.hh>
42#include <duchamp/Cubes/cubeUtils.hh>
43#include <duchamp/Outputs/columns.hh>
44
45using namespace PixelInfo;
46
47namespace duchamp
48{
49
50  void Detection::defaultDetection()
51  {
52    this->xSubOffset = 0;
53    this->ySubOffset = 0;
54    this->zSubOffset = 0;
55    this->haveParams = false;
56    this->totalFlux = 0.;
57    this->eTotalFlux = 0.;
58    this->peakFlux = 0.;
59    this->intFlux = 0.;
60    this->eIntFlux = 0.;
61    this->xpeak = 0;
62    this->ypeak = 0;
63    this->zpeak = 0;
64    this->peakSNR = 0.;
65    this->xCentroid = 0.;
66    this->yCentroid = 0.;
67    this->zCentroid = 0.;
68    this->centreType="centroid";
69    this->negSource = false;
70    this->flagText="";
71    this->id = -1;
72    this->name = "";
73    this->flagWCS=false;
74    this->specOK = true;
75    this->raS = "";
76    this->decS = "";
77    this->ra = 0.;
78    this->dec = 0.;
79    this->raWidth = 0.;
80    this->decWidth = 0.;
81    this->majorAxis = 0.;
82    this->minorAxis = 0.;
83    this->posang = 0.;
84    this->specUnits = "";
85    this->specType = "";
86    this->fluxUnits = "";
87    this->intFluxUnits = "";
88    this->lngtype = "RA";
89    this->lattype = "DEC";
90    this->vel = 0.;
91    this->velWidth = 0.;
92    this->velMin = 0.;
93    this->velMax = 0.;
94    this->w20 = 0.;
95    this->v20min = 0.;
96    this->v20max = 0.;
97    this->w50 = 0.;
98    this->v50min = 0.;
99    this->v50max = 0.;
100    this->posPrec = Catalogues::prPOS;
101    this->xyzPrec = Catalogues::prXYZ;
102    this->fintPrec = Catalogues::prFLUX;
103    this->fpeakPrec = Catalogues::prFLUX;
104    this->velPrec = Catalogues::prVEL;
105    this->snrPrec = Catalogues::prSNR;
106  }
107
108  Detection::Detection():
109    Object3D()
110  {
111    this->defaultDetection();
112  }
113
114  Detection::Detection(const Object3D& o):
115    Object3D(o)
116  {
117    this->defaultDetection();
118  }
119
120  Detection::Detection(const Detection& d):
121    Object3D(d)
122  {
123    operator=(d);
124  }
125
126  Detection& Detection::operator= (const Detection& d)
127  {
128    ((Object3D &) *this) = d;
129    this->xSubOffset   = d.xSubOffset;
130    this->ySubOffset   = d.ySubOffset;
131    this->zSubOffset   = d.zSubOffset;
132    this->haveParams   = d.haveParams;
133    this->totalFlux    = d.totalFlux;
134    this->eTotalFlux   = d.eTotalFlux;
135    this->intFlux      = d.intFlux;
136    this->eIntFlux     = d.eIntFlux;
137    this->peakFlux     = d.peakFlux;
138    this->xpeak        = d.xpeak;
139    this->ypeak        = d.ypeak;
140    this->zpeak        = d.zpeak;
141    this->peakSNR      = d.peakSNR;
142    this->xCentroid    = d.xCentroid;
143    this->yCentroid    = d.yCentroid;
144    this->zCentroid    = d.zCentroid;
145    this->centreType   = d.centreType;
146    this->negSource    = d.negSource;
147    this->flagText     = d.flagText;
148    this->id           = d.id;
149    this->name         = d.name;
150    this->flagWCS      = d.flagWCS;
151    this->specOK       = d.specOK;
152    this->raS          = d.raS;
153    this->decS         = d.decS;
154    this->ra           = d.ra;
155    this->dec          = d.dec;
156    this->raWidth      = d.raWidth;
157    this->decWidth     = d.decWidth;
158    this->majorAxis    = d.majorAxis;
159    this->minorAxis    = d.minorAxis;
160    this->posang       = d.posang;
161    this->specUnits    = d.specUnits;
162    this->specType     = d.specType;
163    this->fluxUnits    = d.fluxUnits;
164    this->intFluxUnits = d.intFluxUnits;
165    this->lngtype      = d.lngtype;
166    this->lattype      = d.lattype;
167    this->vel          = d.vel;
168    this->velWidth     = d.velWidth;
169    this->velMin       = d.velMin;
170    this->velMax       = d.velMax;
171    this->w20          = d.w20;
172    this->v20min       = d.v20min;
173    this->v20max       = d.v20max;
174    this->w50          = d.w50;
175    this->v50min       = d.v50min;
176    this->v50max       = d.v50max;
177    this->posPrec      = d.posPrec;
178    this->xyzPrec      = d.xyzPrec;
179    this->fintPrec     = d.fintPrec;
180    this->fpeakPrec    = d.fpeakPrec;
181    this->velPrec      = d.velPrec;
182    this->snrPrec      = d.snrPrec;
183    return *this;
184  }
185
186  //--------------------------------------------------------------------
187  float Detection::getXcentre()
188  {
189    if(this->centreType=="peak") return this->xpeak;
190    else if(this->centreType=="average") return this->getXaverage();
191    else return this->xCentroid;
192  }
193
194  float Detection::getYcentre()
195  {
196    if(this->centreType=="peak") return this->ypeak;
197    else if(this->centreType=="average") return this->getYaverage();
198    else return this->yCentroid;
199  }
200
201  float Detection::getZcentre()
202  {
203    if(this->centreType=="peak") return this->zpeak;
204    else if(this->centreType=="average") return this->getZaverage();
205    else return this->zCentroid;
206  }
207
208  //--------------------------------------------------------------------
209
210  bool Detection::voxelListsMatch(std::vector<Voxel> voxelList)
211  {
212    /// @details
213    ///  A test to see whether there is a 1-1 correspondence between
214    ///  the given list of Voxels and the voxel positions contained in
215    ///  this Detection's pixel list. No testing of the fluxes of the
216    ///  Voxels is done.
217    ///
218    /// \param voxelList The std::vector list of Voxels to be tested.
219
220    bool listsMatch = true;
221    // compare sizes
222    listsMatch = listsMatch && (voxelList.size() == this->getSize());
223    if(!listsMatch) return listsMatch;
224
225    // make sure all Detection pixels are in voxel list
226    listsMatch = listsMatch && this->voxelListCovered(voxelList);
227
228    // make sure all voxels are in Detection
229    std::vector<Voxel>::iterator vox;
230    for(vox=voxelList.begin();vox<voxelList.end();vox++)
231      listsMatch = listsMatch && this->isInObject(*vox);
232
233    return listsMatch;
234
235  }
236  //--------------------------------------------------------------------
237
238  bool Detection::voxelListCovered(std::vector<Voxel> voxelList)
239  {
240    ///  @details
241    ///  A test to see whether the given list of Voxels contains each
242    ///  position in this Detection's pixel list. It does not look for
243    ///  a 1-1 correspondence: the given list can be a super-set of the
244    ///  Detection. No testing of the fluxes of the Voxels is done.
245    ///
246    /// \param voxelList The std::vector list of Voxels to be tested.
247
248    bool listsMatch = true;
249
250    // make sure all Detection pixels are in voxel list
251    size_t v1=0;
252    std::vector<Voxel> detpixlist = this->getPixelSet();
253    while(listsMatch && v1<detpixlist.size()){
254      bool inList = false;
255      size_t v2=0;
256      while(!inList && v2<voxelList.size()){
257        inList = inList || detpixlist[v1].match(voxelList[v2]);
258        v2++;
259      }
260      listsMatch = listsMatch && inList;
261      v1++;
262    }
263
264    return listsMatch;
265
266  }
267  //--------------------------------------------------------------------
268
269    void Detection::invert()
270    {
271        /// @details
272        /// A front-end to simplify the inversion that is done at the Cube level.
273        /// The fluxes are made negative, and the negSource flag is flipped.
274        /// This can be used at any time - it doesn't matter which way the inversion is happening.
275
276        this->negSource = !this->negSource;
277        this->totalFlux = -1. * this->totalFlux;
278        this->intFlux = -1. * this->intFlux;
279        this->peakFlux = -1. * this->peakFlux;
280
281    }
282
283  //--------------------------------------------------------------------
284
285  void Detection::calcFluxes(std::vector<Voxel> voxelList)
286  {
287    ///  @details
288    ///  A function that calculates total & peak fluxes (and the location
289    ///  of the peak flux) for a Detection.
290    ///
291    ///  \param fluxArray The array of flux values to calculate the
292    ///  flux parameters from.
293    ///  \param dim The dimensions of the flux array.
294   
295    //    this->haveParams = true;
296
297    this->totalFlux = this->peakFlux = 0;
298    this->xCentroid = this->yCentroid = this->zCentroid = 0.;
299
300    // first check that the voxel list and the Detection's pixel list
301    // have a 1-1 correspondence
302
303    if(!this->voxelListCovered(voxelList)){
304      DUCHAMPERROR("Detection::calcFluxes","Voxel list provided does not match");
305      return;
306    }
307
308    std::vector<Voxel>::iterator vox;
309    for(vox=voxelList.begin();vox<voxelList.end();vox++){
310      if(this->isInObject(*vox)){
311        long x = vox->getX();
312        long y = vox->getY();
313        long z = vox->getZ();
314        float f = vox->getF();
315        this->totalFlux += f;
316        this->xCentroid += x*f;
317        this->yCentroid += y*f;
318        this->zCentroid += z*f;
319        if( (vox==voxelList.begin()) ||  //first time round
320            (this->negSource&&(f<this->peakFlux)) ||
321            (!this->negSource&&(f>this->peakFlux))   )
322          {
323            this->peakFlux = f;
324            this->xpeak =    x;
325            this->ypeak =    y;
326            this->zpeak =    z;
327          }
328      }
329    }
330
331    this->xCentroid /= this->totalFlux;
332    this->yCentroid /= this->totalFlux;
333    this->zCentroid /= this->totalFlux;
334  }
335  //--------------------------------------------------------------------
336
337  void Detection::calcFluxes(std::map<Voxel,float> &voxelMap)
338  {
339    ///  @details
340    ///  A function that calculates total & peak fluxes (and the location
341    ///  of the peak flux) for a Detection.
342    ///
343    ///  \param fluxArray The array of flux values to calculate the
344    ///  flux parameters from.
345    ///  \param dim The dimensions of the flux array.
346   
347    //    this->haveParams = true;
348
349    this->totalFlux = this->peakFlux = 0;
350    this->xCentroid = this->yCentroid = this->zCentroid = 0.;
351
352    std::vector<Voxel> voxelList = this->getPixelSet();
353    std::vector<Voxel>::iterator vox;
354    for(vox=voxelList.begin();vox<voxelList.end();vox++){
355      if(voxelMap.find(*vox) == voxelMap.end()){
356        DUCHAMPERROR("Detection::calcFluxes","Voxel list provided does not match");
357        return;
358      }
359      else {
360        long x = vox->getX();
361        long y = vox->getY();
362        long z = vox->getZ();
363        float f = voxelMap[*vox];
364        this->totalFlux += f;
365        this->xCentroid += x*f;
366        this->yCentroid += y*f;
367        this->zCentroid += z*f;
368        if( (vox==voxelList.begin()) ||  //first time round
369            (this->negSource&&(f<this->peakFlux)) ||
370            (!this->negSource&&(f>this->peakFlux))   )
371          {
372            this->peakFlux = f;
373            this->xpeak =    x;
374            this->ypeak =    y;
375            this->zpeak =    z;
376          }
377      }
378    }
379
380    this->xCentroid /= this->totalFlux;
381    this->yCentroid /= this->totalFlux;
382    this->zCentroid /= this->totalFlux;
383  }
384  //--------------------------------------------------------------------
385
386  void Detection::calcFluxes(float *fluxArray, size_t *dim)
387  {
388    ///  @details
389    ///  A function that calculates total & peak fluxes (and the location
390    ///  of the peak flux) for a Detection.
391    ///
392    ///  \param fluxArray The array of flux values to calculate the
393    ///  flux parameters from.
394    ///  \param dim The dimensions of the flux array.
395
396    //    this->haveParams = true;
397
398    this->totalFlux = this->peakFlux = 0;
399    this->xCentroid = this->yCentroid = this->zCentroid = 0.;
400
401    std::vector<Voxel> voxList = this->getPixelSet();
402    std::vector<Voxel>::iterator vox=voxList.begin();
403    for(;vox<voxList.end();vox++){
404
405      long x=vox->getX();
406      long y=vox->getY();
407      long z=vox->getZ();
408      size_t ind = vox->arrayIndex(dim);
409      float f = fluxArray[ind];
410      this->totalFlux += f;
411      this->xCentroid += x*f;
412      this->yCentroid += y*f;
413      this->zCentroid += z*f;
414      if( (vox==voxList.begin()) ||
415          (this->negSource&&(f<this->peakFlux)) ||
416          (!this->negSource&&(f>this->peakFlux))   )
417        {
418          this->peakFlux = f;
419          this->xpeak = x;
420          this->ypeak = y;
421          this->zpeak = z;
422        }
423 
424    }
425
426    this->xCentroid /= this->totalFlux;
427    this->yCentroid /= this->totalFlux;
428    this->zCentroid /= this->totalFlux;
429  }
430  //--------------------------------------------------------------------
431
432  void Detection::calcWCSparams(FitsHeader &head)
433  {
434    ///  @details
435    ///  Use the input wcs to calculate the position and velocity
436    ///    information for the Detection.
437    ///  Quantities calculated:
438    ///  <ul><li> RA: ra [deg], ra (string), ra width.
439    ///      <li> Dec: dec [deg], dec (string), dec width.
440    ///      <li> Vel: vel [km/s], min & max vel, vel width.
441    ///      <li> coord type for all three axes, nuRest,
442    ///      <li> name (IAU-style, in equatorial or Galactic)
443    ///  </ul>
444    ///
445    ///  Note that the regular parameters are NOT recalculated!
446    ///
447    ///  \param head FitsHeader object that contains the WCS information.
448
449    if(head.isWCS()){
450
451      double *pixcrd = new double[15];
452      double *world  = new double[15];
453      /*
454        define a five-point array in 3D:
455        (x,y,z), (x,y,z1), (x,y,z2), (x1,y1,z), (x2,y2,z)
456        [note: x = central point, x1 = minimum x, x2 = maximum x etc.]
457        and convert to world coordinates.   
458      */
459      pixcrd[0]  = pixcrd[3] = pixcrd[6] = this->getXcentre();
460      pixcrd[9]  = this->getXmin()-0.5;
461      pixcrd[12] = this->getXmax()+0.5;
462      pixcrd[1]  = pixcrd[4] = pixcrd[7] = this->getYcentre();
463      pixcrd[10] = this->getYmin()-0.5;
464      pixcrd[13] = this->getYmax()+0.5;
465      pixcrd[2] = pixcrd[11] = pixcrd[14] = this->getZcentre();
466      pixcrd[5] = this->getZmin();
467      pixcrd[8] = this->getZmax();
468      int flag = head.pixToWCS(pixcrd, world, 5);
469      delete [] pixcrd;
470      if(flag!=0) {
471        DUCHAMPERROR("calcWCSparams", "Error in calculating the WCS for this object.");
472      }
473      else{
474       
475        // world now has the WCS coords for the five points
476        //    -- use this to work out WCS params
477 
478        this->haveParams = true;
479
480        this->specOK = head.canUseThirdAxis();
481        this->lngtype = head.lngtype();
482        this->lattype = head.lattype();
483        this->specUnits = head.getSpectralUnits();
484        this->specType  = head.getSpectralType();
485        this->fluxUnits = head.getFluxUnits();
486        // if fluxUnits are eg. Jy/beam, make intFluxUnits = Jy km/s
487        this->intFluxUnits = head.getIntFluxUnits();
488        this->ra   = world[0];
489        this->dec  = world[1];
490        int precision = -int(log10(fabs(head.WCS().cdelt[head.WCS().lng]*3600./10.)));
491        this->raS  = decToDMS(this->ra, this->lngtype,precision);
492        this->decS = decToDMS(this->dec,this->lattype,precision);
493        this->raWidth = angularSeparation(world[9],world[1],
494                                          world[12],world[1]) * 60.;
495        this->decWidth  = angularSeparation(world[0],world[10],
496                                            world[0],world[13]) * 60.;
497
498        this->name = head.getIAUName(this->ra, this->dec);
499        this->vel    = head.specToVel(world[2]);
500        this->velMin = head.specToVel(world[5]);
501        this->velMax = head.specToVel(world[8]);
502        this->velWidth = fabs(this->velMax - this->velMin);
503
504        this->flagWCS = true;
505      }
506      delete [] world;
507
508    }
509    else {
510      double x=this->getXcentre(),y=this->getYcentre(),z=this->getZmin();
511      this->velMin = head.pixToVel(x,y,z);
512      z=this->getZmax();
513      this->velMax = head.pixToVel(x,y,z);
514      this->velWidth = fabs(this->velMax - this->velMin);
515    }
516
517     
518  }
519  //--------------------------------------------------------------------
520
521  void Detection::calcIntegFlux(size_t zdim, std::vector<Voxel> voxelList, FitsHeader &head)
522  {
523    ///  @details
524    ///  Uses the input WCS to calculate the velocity-integrated flux,
525    ///   putting velocity in units of km/s.
526    ///  The fluxes used are taken from the Voxels, rather than an
527    ///   array of flux values.
528    ///  Integrates over full spatial and velocity range as given
529    ///   by the extrema calculated by calcWCSparams.
530    ///
531    ///  If the flux units end in "/beam" (eg. Jy/beam), then the flux is
532    ///  corrected by the beam size (in pixels). This is done by
533    ///  multiplying the integrated flux by the number of spatial pixels,
534    ///  and dividing by the beam size in pixels (e.g. Jy/beam * pix /
535    ///  pix/beam --> Jy)
536    ///
537    ///  \param zdim The size of the spectral axis (needed to find the velocity widths)
538    ///  \param voxelList The list of Voxels with flux information
539    ///  \param head FitsHeader object that contains the WCS information.
540
541    if(!this->voxelListCovered(voxelList)){
542      DUCHAMPERROR("Detection::calcIntegFlux","Voxel list provided does not match");
543      return;
544    }
545
546    if(!head.is2D()){
547
548      this->haveParams = true;
549
550      const int border = 1;  // include one pixel either side in each direction
551      size_t xsize = size_t(this->xmax-this->xmin+2*border+1);
552      size_t ysize = size_t(this->ymax-this->ymin+2*border+1);
553      size_t zsize = size_t(this->zmax-this->zmin+2*border+1);
554      size_t xzero = size_t(std::max(0L,this->xmin-border));
555      size_t yzero = size_t(std::max(0L,this->ymin-border));
556      size_t zzero = size_t(std::max(0L,this->zmin-border));
557      size_t spatsize=xsize*ysize;
558      size_t size = xsize*ysize*zsize;
559      std::vector <bool> isObj(size,false);
560      double *localFlux = new double[size];
561      for(size_t i=0;i<size;i++) localFlux[i]=0.;
562
563      std::vector<Voxel>::iterator vox;
564      for(vox=voxelList.begin();vox<voxelList.end();vox++){
565        if(this->isInObject(*vox)){
566          size_t pos=(vox->getX()-xzero) + (vox->getY()-yzero)*xsize + (vox->getZ()-zzero)*spatsize;
567          localFlux[pos] = vox->getF();
568          isObj[pos] = true;
569        }
570      }
571 
572      // work out the WCS coords for each pixel
573      double *world  = new double[size];
574      double xpt,ypt,zpt;
575      for(size_t i=0;i<size;i++){
576        xpt = double( this->getXmin() - border + i%xsize );
577        ypt = double( this->getYmin() - border + (i/xsize)%ysize );
578        zpt = double( this->getZmin() - border + i/(xsize*ysize) );
579        world[i] = head.pixToVel(xpt,ypt,zpt);
580      }
581
582      double integrated = 0.;
583      for(size_t pix=0; pix<spatsize; pix++){ // loop over each spatial pixel.
584        for(size_t z=0; z<zsize; z++){
585          size_t pos =  z*xsize*ysize + pix;
586          if(isObj[pos]){ // if it's an object pixel...
587            double deltaVel;
588            if(z==0)
589              deltaVel = (world[pos+xsize*ysize] - world[pos]);
590            else if(z==(zsize-1))
591              deltaVel = (world[pos] - world[pos-xsize*ysize]);
592            else
593              deltaVel = (world[pos+xsize*ysize] - world[pos-xsize*ysize]) / 2.;
594            integrated += localFlux[pos] * fabs(deltaVel);
595          }
596        }
597      }
598      this->intFlux = integrated;
599
600      delete [] world;
601      delete [] localFlux;
602
603      calcVelWidths(zdim,voxelList,head);
604
605    }
606    else // in this case there is just a 2D image.
607      this->intFlux = this->totalFlux;
608
609    if(head.isWCS()){
610      // correct for the beam size if the flux units string ends in "/beam"
611      if(head.needBeamSize()) this->intFlux  /= head.beam().area();
612    }
613
614  }
615  //--------------------------------------------------------------------
616
617  void Detection::calcIntegFlux(size_t zdim, std::map<Voxel,float> voxelMap, FitsHeader &head)
618  {
619    ///  @details
620    ///  Uses the input WCS to calculate the velocity-integrated flux,
621    ///   putting velocity in units of km/s.
622    ///  The fluxes used are taken from the Voxels, rather than an
623    ///   array of flux values.
624    ///  Integrates over full spatial and velocity range as given
625    ///   by the extrema calculated by calcWCSparams.
626    ///
627    ///  If the flux units end in "/beam" (eg. Jy/beam), then the flux is
628    ///  corrected by the beam size (in pixels). This is done by
629    ///  multiplying the integrated flux by the number of spatial pixels,
630    ///  and dividing by the beam size in pixels (e.g. Jy/beam * pix /
631    ///  pix/beam --> Jy)
632    ///
633    ///  \param zdim The size of the spectral axis (needed to find the velocity widths)
634    ///  \param voxelList The list of Voxels with flux information
635    ///  \param head FitsHeader object that contains the WCS information.
636
637    if(!head.is2D()){
638
639      this->haveParams = true;
640
641       const int border = 1; // include one pixel either side in each direction
642      size_t xsize = size_t(this->xmax-this->xmin+2*border+1);
643      size_t ysize = size_t(this->ymax-this->ymin+2*border+1);
644      size_t zsize = size_t(this->zmax-this->zmin+2*border+1);
645      size_t xzero = size_t(std::max(0L,this->xmin-border));
646      size_t yzero = size_t(std::max(0L,this->ymin-border));
647      size_t zzero = size_t(std::max(0L,this->zmin-border));
648      size_t spatsize=xsize*ysize;
649      size_t size = xsize*ysize*zsize;
650
651      this->intFlux = 0.;
652      std::vector<Voxel> voxelList = this->getPixelSet();
653      std::vector<Voxel>::iterator vox;
654      for(vox=voxelList.begin();vox<voxelList.end();vox++){
655          if(voxelMap.find(*vox) == voxelMap.end()){
656              DUCHAMPERROR("Detection::calcIntegFlux","Voxel list provided does not match");
657              return;
658          }     
659          else {
660              this->intFlux += voxelMap[*vox];
661          }
662      }
663      this->intFlux *= fabs(head.WCS().cdelt[head.WCS().spec]);
664
665      calcVelWidths(zdim,voxelMap,head);
666
667    }
668    else // in this case there is just a 2D image.
669      this->intFlux = this->totalFlux;
670
671    if(head.isWCS()){
672      // correct for the beam size if the flux units string ends in "/beam"
673      if(head.needBeamSize()) this->intFlux  /= head.beam().area();
674    }
675
676  }
677  //--------------------------------------------------------------------
678
679  void Detection::calcIntegFlux(float *fluxArray, size_t *dim, FitsHeader &head, Param &par)
680  {
681    ///  @details
682    ///  Uses the input WCS to calculate the velocity-integrated flux,
683    ///   putting velocity in units of km/s.
684    ///  Integrates over full spatial and velocity range as given
685    ///   by the extrema calculated by calcWCSparams.
686    ///
687    ///  If the flux units end in "/beam" (eg. Jy/beam), then the flux is
688    ///  corrected by the beam size (in pixels). This is done by
689    ///  multiplying the integrated flux by the number of spatial pixels,
690    ///  and dividing by the beam size in pixels (e.g. Jy/beam * pix /
691    ///  pix/beam --> Jy)
692    ///
693    ///  \param fluxArray The array of flux values.
694    ///  \param dim The dimensions of the flux array.
695    ///  \param head FitsHeader object that contains the WCS information.
696
697    this->haveParams = true;
698
699    const int border=1; // include one pixel either side in each direction
700    size_t xsize = std::min(size_t(this->xmax-this->xmin+2*border+1),dim[0]);
701    size_t ysize = std::min(size_t(this->ymax-this->ymin+2*border+1),dim[1]);
702    size_t zsize = std::min(size_t(this->zmax-this->zmin+2*border+1),dim[2]);
703    size_t xzero = size_t(std::max(0L,this->xmin-border));
704    size_t yzero = size_t(std::max(0L,this->ymin-border));
705    size_t zzero = size_t(std::max(0L,this->zmin-border));
706    size_t spatsize = xsize*ysize;
707    size_t size = xsize*ysize*zsize;
708    std::vector <bool> isObj(size,false);
709    double *localFlux = new double[size];
710    for(size_t i=0;i<size;i++) localFlux[i]=0.;
711    float *momMap = new float[spatsize];
712    for(size_t i=0;i<spatsize;i++) momMap[i]=0.;
713    // work out which pixels are object pixels
714    std::vector<Voxel> voxlist = this->getPixelSet();
715    for(std::vector<Voxel>::iterator v=voxlist.begin();v<voxlist.end();v++){
716      size_t spatpos=(v->getX()-xzero) + (v->getY()-yzero)*xsize;
717      size_t pos= spatpos + (v->getZ()-zzero)*spatsize;
718      localFlux[pos] = fluxArray[v->arrayIndex(dim)];
719      momMap[spatpos] += fluxArray[v->arrayIndex(dim)]*head.WCS().cdelt[head.WCS().spec];
720      isObj[pos] = true;
721    }
722 
723     if(!head.is2D()){
724
725     // work out the WCS coords for each pixel
726      double *world  = new double[size];
727      double xpt,ypt,zpt;
728      size_t i=0;
729      for(size_t z=zzero;z<zzero+zsize;z++){
730        for(size_t y=yzero;y<yzero+ysize;y++){
731          for(size_t x=xzero;x<xzero+xsize;x++){
732            xpt=double(x);
733            ypt=double(y);
734            zpt=double(z);
735            world[i++] = head.pixToVel(xpt,ypt,zpt);
736          }
737        }
738      }
739
740      double integrated = 0.;
741      for(size_t pix=0; pix<xsize*ysize; pix++){ // loop over each spatial pixel.
742        for(size_t z=0; z<zsize; z++){
743          size_t pos =  z*xsize*ysize + pix;
744          if(isObj[pos]){ // if it's an object pixel...
745            double deltaVel;
746            if(z==0)
747              deltaVel = (world[pos+xsize*ysize] - world[pos]);
748            else if(z==(zsize-1))
749              deltaVel = (world[pos] - world[pos-xsize*ysize]);
750            else
751              deltaVel = (world[pos+xsize*ysize] - world[pos-xsize*ysize]) / 2.;
752            integrated += localFlux[pos] * fabs(deltaVel);
753          }
754        }
755      }
756
757      delete [] world;
758     
759      this->intFlux = integrated;
760     
761      calcVelWidths(fluxArray, dim, head, par);
762
763     }
764      else // in this case there is just a 2D image.
765      this->intFlux = this->totalFlux;
766   
767     delete [] localFlux;
768     delete [] momMap;
769   
770
771    if(head.isWCS()){
772      // correct for the beam size if the flux units string ends in "/beam" and we have beam info
773      if(head.needBeamSize()) this->intFlux  /= head.beam().area();
774    }
775
776  }
777  //--------------------------------------------------------------------
778
779  void Detection::findShape(const float *fluxArray, const size_t *dim, FitsHeader &head)
780  {
781      const long border=1; // include one pixel either side in each direction
782      size_t x1 = size_t(std::max(long(0),this->xmin-border));
783      size_t y1 = size_t(std::max(long(0),this->ymin-border));
784      size_t x2 = size_t(std::min(long(dim[0])-1,this->xmax+border));
785      size_t y2 = size_t(std::min(long(dim[1])-1,this->ymax+border));
786      size_t xsize = x2-x1+1;
787      size_t ysize = y2-y1+1;
788      size_t spatsize = xsize*ysize;
789
790      float *momentMap = new float[spatsize];
791      for(size_t i=0;i<spatsize;i++) momentMap[i]=0.;
792      // work out which pixels are object pixels
793      std::vector<Voxel> voxlist = this->getPixelSet();
794      float delta = (head.isWCS() && head.getWCS()->spec>=0) ? fabs(head.WCS().cdelt[head.WCS().spec]) : 1.;
795      float sign = this->negSource ? -1. : 1.;
796      for(std::vector<Voxel>::iterator v=voxlist.begin();v<voxlist.end();v++){
797          size_t spatpos=(v->getX()-x1) + (v->getY()-y1)*xsize;
798          if(spatpos>=0 && spatpos<spatsize)
799              momentMap[spatpos] += fluxArray[v->arrayIndex(dim)] * delta * sign;
800          else DUCHAMPTHROW("findShape","Memory overflow - accessing spatpos="<<spatpos<<" when spatsize="<<spatsize <<". Pixel is (x,y)=("<<v->getX() <<","<<v->getY()<<") and (x1,y1)="<<x1<<","<<y1<<"), (x2,y2)="<<x2<<","<<y2<<"), xsize="<<xsize);
801      }
802
803      size_t smldim[2]; smldim[0]=xsize; smldim[1]=ysize;
804      Image *smlIm = new Image(smldim);
805      smlIm->saveArray(momentMap,spatsize);
806      smlIm->setMinSize(1);
807      float max = *std::max_element(momentMap,momentMap+spatsize);
808      smlIm->stats().setThreshold(max/2.);
809      std::vector<Object2D> objlist=smlIm->findSources2D();
810
811      Object2D combined;
812      for(size_t i=0;i<objlist.size();i++) combined = combined + objlist[i];
813      std::string extraFlag="";
814      bool ellipseGood = combined.findEllipse(true, momentMap, xsize, ysize, 0,0, this->getXcentre()-x1, this->getYcentre()-y1); // try first by weighting the pixels by their flux
815      if(!ellipseGood) {
816          ellipseGood = combined.findEllipse(false, momentMap, xsize, ysize, 0,0, this->getXcentre()-x1, this->getYcentre()-y1); // if that fails, remove the flux weighting
817          extraFlag="W";
818      }
819      float scale=head.getShapeScale();
820      if(ellipseGood){
821          // multiply axes by 2 to go from semi-major to FWHM...
822          this->majorAxis = combined.major() * head.getAvPixScale() * 2. * scale;
823          this->minorAxis = combined.minor() * head.getAvPixScale() * 2. * scale;
824          this->posang =    combined.posAng() * 180. / M_PI;
825          // std::cerr << "*** " << combined.getSize()<< " " << majorAxis<<" " << minorAxis << " " << posang<< " " << scale <<"\n";
826      }
827      else {
828          extraFlag="w";
829          std::pair<double,double> axes = combined.getPrincipalAxes();
830          this->majorAxis = std::max(axes.first,axes.second) * head.getAvPixScale() * scale;
831          this->minorAxis = std::min(axes.first,axes.second) * head.getAvPixScale() * scale;
832          this->posang = combined.getPositionAngle() * 180. / M_PI;
833          // std::cerr << "** " << combined.getSize()<< " " << majorAxis<<" " << minorAxis << " " << posang<< " " << scale <<"\n";
834     }
835      this->flagText += extraFlag;
836     
837
838      delete smlIm;
839      delete [] momentMap;
840  }
841
842  //--------------------------------------------------------------------
843
844  void Detection::calcVelWidths(size_t zdim, std::vector<Voxel> voxelList, FitsHeader &head)
845  {
846    ///  @details
847    /// Calculates the widths of the detection at 20% and 50% of the
848    /// peak integrated flux. The procedure is as follows: first
849    /// generate an integrated flux spectrum (using all given voxels
850    /// that lie in the object's spatial map); find the peak; starting
851    /// at the spectral edges of the detection, move in or out until
852    /// you reach the 20% or 50% peak flux level. Linear interpolation
853    /// between points is done.
854    ///
855    ///  \param zdim The size of the spectral axis in the cube
856    ///  \param voxelList The list of Voxels with flux information
857    ///  \param head FitsHeader object that contains the WCS information.
858
859    float *intSpec = new float[zdim];
860    for(size_t i=0;i<zdim;i++) intSpec[i]=0;
861       
862    Object2D spatMap = this->getSpatialMap();
863    for(int s=0;s<spatMap.getNumScan();s++){
864      std::vector<Voxel>::iterator vox;
865      for(vox=voxelList.begin();vox<voxelList.end();vox++){
866        if(spatMap.isInObject(*vox)){
867          intSpec[vox->getZ()] += vox->getF();
868        }
869      }
870    }
871   
872    calcVelWidths(zdim, intSpec, head);
873
874    delete [] intSpec;
875
876  }
877
878  //--------------------------------------------------------------------
879
880  void Detection::calcVelWidths(size_t zdim, std::map<Voxel,float> voxelMap, FitsHeader &head)
881  {
882    ///  @details
883    /// Calculates the widths of the detection at 20% and 50% of the
884    /// peak integrated flux. The procedure is as follows: first
885    /// generate an integrated flux spectrum (using all given voxels
886    /// that lie in the object's spatial map); find the peak; starting
887    /// at the spectral edges of the detection, move in or out until
888    /// you reach the 20% or 50% peak flux level. Linear interpolation
889    /// between points is done.
890    ///
891    ///  \param zdim The size of the spectral axis in the cube
892    ///  \param voxelList The list of Voxels with flux information
893    ///  \param head FitsHeader object that contains the WCS information.
894
895    float *intSpec = new float[zdim];
896    for(size_t i=0;i<zdim;i++) intSpec[i]=0;
897       
898    std::vector<Voxel> voxelList = this->getPixelSet();
899    std::vector<Voxel>::iterator vox;
900    for(vox=voxelList.begin();vox<voxelList.end();vox++){
901      if(voxelMap.find(*vox) == voxelMap.end()){
902        DUCHAMPERROR("Detection::calcVelWidths","Voxel list provided does not match");
903        return;
904      }
905      else {
906        intSpec[vox->getZ()] += voxelMap[*vox];
907      }
908    }
909
910    calcVelWidths(zdim, intSpec, head);
911
912    delete [] intSpec;
913
914  }
915
916  //--------------------------------------------------------------------
917
918  void Detection::calcVelWidths(size_t zdim, float *intSpec, FitsHeader &head)
919  {
920
921    // finding the 20% & 50% points.  Start at the velmin & velmax
922    //  points. Then, if the int flux there is above the 20%/50%
923    //  limit, go out, otherwise go in. This is to deal with the
924    //  problems from double- (or multi-) peaked sources.
925
926    this->haveParams = true;
927
928    double zpt,xpt=double(this->getXcentre()),ypt=double(this->getYcentre());
929    bool goLeft;
930   
931    if(this->negSource){
932      // if we've inverted the source, need to make the feature
933      // positive for the interpolation/extrapolation to work
934      for(size_t i=0;i<zdim;i++) intSpec[i] *= -1.;
935    }
936
937    float peak=0.;
938    size_t peakLoc=0;
939    for(size_t z=this->getZmin();z<=size_t(this->getZmax());z++) {
940      if(z==0 || peak<intSpec[z]){
941        peak = intSpec[z];
942        peakLoc = z;
943      }
944    }
945   
946    size_t z=this->getZmin();
947    goLeft = intSpec[z]>peak*0.5;
948    if(goLeft) while(z>0 && intSpec[z]>peak*0.5) z--;
949    else       while(z<peakLoc && intSpec[z]<peak*0.5) z++;
950    if(z==0) this->v50min = this->velMin;
951    else{
952      if(goLeft) zpt = z + (peak*0.5-intSpec[z])/(intSpec[z+1]-intSpec[z]);
953      else       zpt = z - (peak*0.5-intSpec[z])/(intSpec[z-1]-intSpec[z]);
954      this->v50min = head.pixToVel(xpt,ypt,zpt);
955    }
956    z=this->getZmax();
957    goLeft = intSpec[z]<peak*0.5;
958    if(goLeft) while(z>peakLoc && intSpec[z]<peak*0.5) z--;
959    else       while(z<zdim    && intSpec[z]>peak*0.5) z++;
960    if(z==zdim) this->v50max = this->velMax;
961    else{
962      if(goLeft) zpt = z + (peak*0.5-intSpec[z])/(intSpec[z+1]-intSpec[z]);
963      else       zpt = z - (peak*0.5-intSpec[z])/(intSpec[z-1]-intSpec[z]);
964      this->v50max = head.pixToVel(xpt,ypt,zpt);
965    }
966    z=this->getZmin();
967    goLeft = intSpec[z]>peak*0.2;
968    if(goLeft) while(z>0 && intSpec[z]>peak*0.2) z--;
969    else       while(z<peakLoc && intSpec[z]<peak*0.2) z++;
970    if(z==0) this->v20min = this->velMin;
971    else{
972      if(goLeft) zpt = z + (peak*0.2-intSpec[z])/(intSpec[z+1]-intSpec[z]);
973      else       zpt = z - (peak*0.2-intSpec[z])/(intSpec[z-1]-intSpec[z]);
974      this->v20min = head.pixToVel(xpt,ypt,zpt);
975    }
976    z=this->getZmax();
977    goLeft = intSpec[z]<peak*0.2;
978    if(goLeft) while(z>peakLoc && intSpec[z]<peak*0.2) z--;
979    else       while(z<zdim    && intSpec[z]>peak*0.2) z++;
980    if(z==zdim) this->v20max = this->velMax;
981    else{
982      if(goLeft) zpt = z + (peak*0.2-intSpec[z])/(intSpec[z+1]-intSpec[z]);
983      else       zpt = z - (peak*0.2-intSpec[z])/(intSpec[z-1]-intSpec[z]);
984      this->v20max = head.pixToVel(xpt,ypt,zpt);
985    }
986
987    this->w20 = fabs(this->v20min - this->v20max);
988    this->w50 = fabs(this->v50min - this->v50max);
989   
990    if(this->negSource){
991      // un-do the inversion, in case intSpec is needed elsewhere
992      for(size_t i=0;i<zdim;i++) intSpec[i] *= -1.;
993    }
994
995
996  }
997  //--------------------------------------------------------------------
998
999  void Detection::calcVelWidths(float *fluxArray, size_t *dim, FitsHeader &head, Param &par)
1000  {
1001    ///  @details
1002    /// Calculates the widths of the detection at 20% and 50% of the
1003    /// peak integrated flux. The procedure is as follows: first
1004    /// generate an integrated flux spectrum (summing each spatial
1005    /// pixel's spectrum); find the peak; starting at the spectral
1006    /// edges of the detection, move in or out until you reach the 20%
1007    /// or 50% peak flux level. Linear interpolation between points is
1008    /// done.
1009    ///
1010    ///  \param fluxArray The array of flux values.
1011    ///  \param dim The dimensions of the flux array.
1012    ///  \param head FitsHeader object that contains the WCS information.
1013
1014    if(dim[2] > 2){
1015
1016      float *intSpec = new float[dim[2]];
1017      size_t size=dim[0]*dim[1]*dim[2];
1018      bool *mask = par.makeBlankMask(fluxArray,size);
1019      getIntSpec(*this,fluxArray,dim,mask,1.,intSpec);
1020
1021      this->calcVelWidths(dim[2],intSpec,head);
1022
1023      delete [] intSpec;
1024
1025    }
1026    else{
1027      this->v50min = this->v20min = this->velMin;
1028      this->v50max = this->v20max = this->velMax;
1029      this->w20 = fabs(this->v20min - this->v20max);
1030      this->w50 = fabs(this->v50min - this->v50max);
1031    }
1032
1033  }
1034  //--------------------------------------------------------------------
1035
1036  void Detection::setOffsets(Param &par)
1037  {
1038    ///  @details
1039    /// This function stores the values of the offsets for each cube axis.
1040    /// The offsets are the starting values of the cube axes that may differ from
1041    ///  the default value of 0 (for instance, if a subsection is being used).
1042    /// The values will be used when the detection is outputted.
1043
1044    this->xSubOffset = par.getXOffset();
1045    this->ySubOffset = par.getYOffset();
1046    this->zSubOffset = par.getZOffset();
1047  }
1048  //--------------------------------------------------------------------
1049
1050  bool Detection::hasEnoughChannels(int minNumber)
1051  {
1052    ///  @details
1053    /// A function to determine if the Detection has enough
1054    /// contiguous channels to meet the minimum requirement
1055    /// given as the argument.
1056    /// \param minNumber How many channels is the minimum acceptable number?
1057    /// \return True if there is at least one occurence of minNumber consecutive
1058    /// channels present to return true. False otherwise.
1059
1060    // Preferred method -- need a set of minNumber consecutive channels present.
1061
1062    int numChan = this->getMaxAdjacentChannels();
1063    bool result = (numChan >= minNumber);
1064
1065    return result;
1066 
1067  }
1068  //--------------------------------------------------------------------
1069
1070  std::vector<int> Detection::getVertexSet()
1071  {
1072    ///  @details
1073    /// Gets a list of points being the end-points of 1-pixel long
1074    /// segments drawing a border around the spatial extend of a
1075    /// detection. The vector is a series of 4 integers, being: x_0,
1076    /// y_0, x_1, y_1.
1077    /// \return The vector of vertex positions.
1078
1079    std::vector<int> vertexSet;
1080
1081    int xmin = this->getXmin() - 1;
1082    int xmax = this->getXmax() + 1;
1083    int ymin = this->getYmin() - 1;
1084    int ymax = this->getYmax() + 1;
1085    int xsize = xmax - xmin + 1;
1086    int ysize = ymax - ymin + 1;
1087
1088    std::vector<Voxel> voxlist = this->getPixelSet();
1089    std::vector<bool> isObj(xsize*ysize,false);
1090    std::vector<Voxel>::iterator vox;
1091    for(vox=voxlist.begin();vox<voxlist.end();vox++){
1092      size_t pos = (vox->getX()-xmin) +
1093        (vox->getY()-ymin)*xsize;
1094      isObj[pos] = true;
1095    }
1096    voxlist.clear();
1097   
1098    for(int x=xmin; x<=xmax; x++){
1099      // for each column...
1100      for(int y=ymin+1;y<=ymax;y++){
1101        int current  = (y-ymin)*xsize + x-xmin;
1102        int previous = (y-ymin-1)*xsize + x-xmin;
1103        if((isObj[current]&&!isObj[previous])   ||
1104           (!isObj[current]&&isObj[previous])){
1105          vertexSet.push_back(x);
1106          vertexSet.push_back(y);
1107          vertexSet.push_back(x+1);
1108          vertexSet.push_back(y);
1109        }
1110      }
1111    }
1112    for(int y=ymin; y<=ymax; y++){
1113      // now for each row...
1114      for(int x=xmin+1;x<=xmax;x++){
1115        int current  = (y-ymin)*xsize + x-xmin;
1116        int previous = (y-ymin)*xsize + x-xmin - 1;
1117        if((isObj[current]&&!isObj[previous])   ||
1118           (!isObj[current]&&isObj[previous])){
1119          vertexSet.push_back(x);
1120          vertexSet.push_back(y);
1121          vertexSet.push_back(x);
1122          vertexSet.push_back(y+1);
1123        }
1124      }
1125    }
1126
1127    return vertexSet;
1128 
1129  }
1130
1131 
1132  void Detection::addDetection(Detection &other)
1133  {
1134    for(std::map<long, Object2D>::iterator it = other.chanlist.begin(); it!=other.chanlist.end();it++)
1135      //      this->addChannel(*it);
1136      this->addChannel(it->first, it->second);
1137    this->haveParams = false; // make it appear as if the parameters haven't been calculated, so that we can re-calculate them 
1138  }
1139
1140  Detection operator+ (Detection &lhs, Detection &rhs)
1141  {
1142    Detection output = lhs;
1143    for(std::map<long, Object2D>::iterator it = rhs.chanlist.begin(); it!=rhs.chanlist.end();it++)
1144      output.addChannel(it->first, it->second);
1145    output.haveParams = false; // make it appear as if the parameters haven't been calculated, so that we can re-calculate them
1146    return output;
1147  }
1148   
1149
1150  bool Detection::canMerge(Detection &other, Param &par)
1151  {
1152    bool near = this->isNear(other,par);
1153    if(near) return this->isClose(other,par);
1154    else return near;
1155  }
1156
1157  bool Detection::isNear(Detection &other, Param &par)
1158  {
1159
1160    bool flagAdj = par.getFlagAdjacent();
1161    float threshS = par.getThreshS();
1162    float threshV = par.getThreshV();
1163
1164    long gap;
1165    if(flagAdj) gap = 1;
1166    else gap = long( ceil(threshS) );
1167
1168    bool areNear;
1169    // Test X ranges
1170    if((this->xmin-gap)<other.xmin) areNear=((this->xmax+gap)>=other.xmin);
1171    else areNear=(other.xmax>=(this->xmin-gap));
1172    // Test Y ranges
1173    if(areNear){
1174      if((this->ymin-gap)<other.ymin) areNear=areNear&&((this->ymax+gap)>=other.ymin);
1175      else areNear=areNear&&(other.ymax>=(this->ymin-gap));
1176    }
1177    // Test Z ranges
1178    if(areNear){
1179      gap = long(ceil(threshV));
1180      if((this->zmin-gap)<other.zmin) areNear=areNear&&((this->zmax+gap)>=other.zmin);
1181      else areNear=areNear&&(other.zmax>=(this->zmin-gap));
1182    }
1183   
1184    return areNear;
1185
1186  }
1187
1188  bool Detection::isClose(Detection &other, Param &par)
1189  {
1190    bool close = false;   // this will be the value returned
1191   
1192    bool flagAdj = par.getFlagAdjacent();
1193    float threshS = par.getThreshS();
1194    float threshV = par.getThreshV();
1195
1196    //
1197    // If we get to here, the pixel ranges overlap -- so we do a
1198    // pixel-by-pixel comparison to make sure they are actually
1199    // "close" according to the thresholds.  Otherwise, close=false,
1200    // and so don't need to do anything else before returning.
1201    //
1202
1203    std::vector<long> zlist1 = this->getChannelList();
1204    std::vector<long> zlist2 = other.getChannelList();
1205    Scan test1,test2;
1206
1207    for(size_t ct1=0; (!close && (ct1<zlist1.size())); ct1++){
1208      for(size_t ct2=0; (!close && (ct2<zlist2.size())); ct2++){
1209       
1210        if(abs(zlist1[ct1]-zlist2[ct2])<=threshV){
1211             
1212          Object2D temp1 = this->getChanMap(zlist1[ct1]);
1213          Object2D temp2 = other.getChanMap(zlist2[ct2]);
1214
1215          close = temp1.canMerge(temp2,threshS,flagAdj);
1216
1217        }
1218
1219      }
1220    }
1221       
1222    return close;
1223   
1224  }
1225
1226
1227
1228}
Note: See TracBrowser for help on using the repository browser.