source: trunk/src/Cubes/cubes.cc @ 258

Last change on this file since 258 was 258, checked in by Matthew Whiting, 17 years ago

Merging pixel-map-branch revisions 236:257 back into trunk.
The use of the PixelMap? functions is sorted now, so we put everything back into a uniform location.

File size: 44.1 KB
Line 
1#include <unistd.h>
2#include <iostream>
3#include <iomanip>
4#include <vector>
5#include <algorithm>
6#include <string>
7#include <math.h>
8
9#include <wcs.h>
10
11#include <duchamp.hh>
12#include <param.hh>
13#include <Cubes/cubes.hh>
14#include <PixelMap/Voxel.hh>
15#include <PixelMap/Object3D.hh>
16#include <Detection/detection.hh>
17#include <Detection/columns.hh>
18#include <Utils/utils.hh>
19#include <Utils/mycpgplot.hh>
20#include <Utils/Statistics.hh>
21using namespace Column;
22using namespace mycpgplot;
23using namespace Statistics;
24using namespace PixelInfo;
25
26/****************************************************************/
27///////////////////////////////////////////////////
28//// Functions for DataArray class:
29///////////////////////////////////////////////////
30
31DataArray::DataArray(){
32  /**
33   * Fundamental constructor for DataArray.
34   * Number of dimensions and pixels are set to 0. Nothing else allocated.
35   */
36  this->numDim=0;
37  this->numPixels=0;
38};
39//--------------------------------------------------------------------
40
41DataArray::DataArray(short int nDim){
42  /**
43   * N-dimensional constructor for DataArray.
44   * Number of dimensions defined, and dimension array allocated.
45   * Number of pixels are set to 0.
46   * \param nDim Number of dimensions.
47   */
48  if(nDim>0) this->axisDim = new long[nDim];
49  this->numDim=nDim;
50  this->numPixels=0;
51};
52//--------------------------------------------------------------------
53
54DataArray::DataArray(short int nDim, long size){
55  /**
56   * N-dimensional constructor for DataArray.
57   * Number of dimensions and number of pixels defined.
58   * Arrays allocated based on these values.
59   * \param nDim Number of dimensions.
60   * \param size Number of pixels.
61   *
62   * Note that we can assign values to the dimension array.
63   */
64
65  if(size<0)
66    duchampError("DataArray(nDim,size)",
67                 "Negative size -- could not define DataArray");
68  else if(nDim<0)
69    duchampError("DataArray(nDim,size)",
70                 "Negative number of dimensions: could not define DataArray");
71  else {
72    if(size>0) this->array = new float[size];
73    this->numPixels = size;
74    if(nDim>0) this->axisDim = new long[nDim];
75    this->numDim = nDim;
76  }
77}
78//--------------------------------------------------------------------
79
80DataArray::DataArray(short int nDim, long *dimensions)
81{
82  /**
83   * Most robust constructor for DataArray.
84   * Number and sizes of dimensions are defined, and hence the number of
85   * pixels. Arrays allocated based on these values.
86   * \param nDim Number of dimensions.
87   * \param dimensions Array giving sizes of dimensions.
88   */
89  if(nDim<0)
90    duchampError("DataArray(nDim,dimArray)",
91                 "Negative number of dimensions: could not define DataArray");
92  else {
93    int size = dimensions[0];
94    for(int i=1;i<nDim;i++) size *= dimensions[i];
95    if(size<0)
96      duchampError("DataArray(nDim,dimArray)",
97                   "Negative size: could not define DataArray");
98    else{
99      this->numPixels = size;
100      if(size>0) this->array = new float[size];
101      this->numDim=nDim;
102      if(nDim>0) this->axisDim = new long[nDim];
103      for(int i=0;i<nDim;i++) this->axisDim[i] = dimensions[i];
104    }
105  }
106}
107//--------------------------------------------------------------------
108
109DataArray::~DataArray()
110{
111  /**
112   *  Destructor -- arrays deleted if they have been allocated, and the
113   *   object list is cleared.
114   */
115  if(this->numPixels>0) delete [] this->array;
116  if(this->numDim>0)    delete [] this->axisDim;
117  this->objectList.clear();
118}
119//--------------------------------------------------------------------
120//--------------------------------------------------------------------
121
122void DataArray::getDim(long &x, long &y, long &z){
123  /**
124   * The sizes of the first three dimensions (if they exist) are returned.
125   * \param x The first dimension. Defaults to 0 if numDim \f$\le\f$ 0.
126   * \param y The second dimension. Defaults to 1 if numDim \f$\le\f$ 1.
127   * \param z The third dimension. Defaults to 1 if numDim \f$\le\f$ 2.
128   */
129  if(this->numDim>0) x=this->axisDim[0];
130  else x=0;
131  if(this->numDim>1) y=this->axisDim[1];
132  else y=1;
133  if(this->numDim>2) z=this->axisDim[2];
134  else z=1;
135}
136//--------------------------------------------------------------------
137
138void DataArray::getDimArray(long *output){
139  /**
140   * The axisDim array is written to output. This needs to be defined
141   *  beforehand: no checking is done on the memory.
142   * \param output The array that is written to.
143   */
144  for(int i=0;i<this->numDim;i++) output[i] = this->axisDim[i];
145}
146//--------------------------------------------------------------------
147
148void DataArray::getArray(float *output){
149  /**
150   * The pixel value array is written to output. This needs to be defined
151   *  beforehand: no checking is done on the memory.
152   * \param output The array that is written to.
153   */
154  for(int i=0;i<this->numPixels;i++) output[i] = this->array[i];
155}
156//--------------------------------------------------------------------
157
158void DataArray::saveArray(float *input, long size){
159  /**
160   * Saves the array in input to the pixel array DataArray::array.
161   * The size of the array given must be the same as the current number of
162   * pixels, else an error message is returned and nothing is done.
163   * \param input The array of values to be saved.
164   * \param size The size of input.
165   */
166  if(size != this->numPixels)
167    duchampError("DataArray::saveArray",
168                 "Input array different size to existing array. Cannot save.");
169  else {
170    if(this->numPixels>0) delete [] this->array;
171    this->numPixels = size;
172    this->array = new float[size];
173    for(int i=0;i<size;i++) this->array[i] = input[i];
174  }
175}
176//--------------------------------------------------------------------
177
178void DataArray::addObject(Detection object){
179  /**
180   * \param object The object to be added to the object list.
181   */
182  // objectList is a vector, so just use push_back()
183  this->objectList.push_back(object);
184}
185//--------------------------------------------------------------------
186
187void DataArray::addObjectList(std::vector <Detection> newlist) {
188  /**
189   * \param newlist The list of objects to be added to the object list.
190   */
191  for(int i=0;i<newlist.size();i++) this->objectList.push_back(newlist[i]);
192}
193//--------------------------------------------------------------------
194
195// void DataArray::addObjectOffsets(){
196//   /**
197//    * Add the pixel offsets (that is, offsets from the corner of the cube to the
198//    *  corner of the utilised part) that are stored in the Param set to the
199//    *  coordinate values of each object in the object list.
200//    */
201//   for(int i=0;i<this->objectList.size();i++){
202//     for(int p=0;p<this->objectList[i].getSize();p++){
203//       this->objectList[i].setX(p,this->objectList[i].getX(p)+
204//                             this->par.getXOffset());
205//       this->objectList[i].setY(p,this->objectList[i].getY(p)+
206//                             this->par.getYOffset());
207//       this->objectList[i].setZ(p,this->objectList[i].getZ(p)+
208//                             this->par.getZOffset());
209//     }
210//   }
211// }
212// //--------------------------------------------------------------------
213
214bool DataArray::isDetection(float value){
215  /**
216   * Is a given value a detection, based on the statistics in the
217   * DataArray's StatsContainer?
218   * \param value The pixel value to test.
219   */
220  if(par.isBlank(value)) return false;
221  else return Stats.isDetection(value);
222}; 
223//--------------------------------------------------------------------
224
225bool DataArray::isDetection(long voxel){
226  /**
227   * Is a given pixel a detection, based on the statistics in the
228   * DataArray's StatsContainer?
229   * If the pixel lies outside the valid range for the data array, return false.
230   * \param voxel Location of the DataArray's pixel to be tested.
231   */
232  if((voxel<0)||(voxel>this->numPixels)) return false;
233  else if(par.isBlank(this->array[voxel])) return false;
234  else return Stats.isDetection(this->array[voxel]);
235}; 
236//--------------------------------------------------------------------
237
238std::ostream& operator<< ( std::ostream& theStream, DataArray &array)
239{
240  /**
241   * A way to print out the pixel coordinates & flux values of the
242   * list of detected objects belonging to the DataArray.
243   * These are formatted nicely according to the << operator for Detection,
244   *  with a line indicating the number of detections at the start.
245   * \param theStream The ostream object to which the output should be sent.
246   * \param array The DataArray containing the list of objects.
247   */
248  for(int i=0;i<array.numDim;i++){
249    if(i>0) theStream<<"x";
250    theStream<<array.axisDim[i];
251  }
252  theStream<<std::endl;
253  theStream<<array.objectList.size()<<" detections:\n--------------\n";
254  for(int i=0;i<array.objectList.size();i++){
255    theStream << "Detection #" << array.objectList[i].getID()<<std::endl;
256    Detection *obj = new Detection;
257    *obj = array.objectList[i];
258    obj->addOffsets();
259    theStream<<*obj;
260    delete obj;
261  }
262  theStream<<"--------------\n";
263}
264
265/****************************************************************/
266/////////////////////////////////////////////////////////////
267//// Functions for Cube class
268/////////////////////////////////////////////////////////////
269
270Cube::Cube(){
271  /**
272   * Basic Constructor for Cube class.
273   * numDim set to 3, but numPixels to 0 and all bool flags to false.
274   * No allocation done.
275   */
276    numPixels=0; numDim=3;
277    reconExists = false; reconAllocated = false; baselineAllocated = false;
278  };
279//--------------------------------------------------------------------
280
281Cube::Cube(long size){
282  /**
283   * Alternative Cube constructor, where size is given but not individual
284   *  dimensions. Arrays are allocated as appropriate (according to the
285   *  relevant flags in Param set), but the Cube::axisDim array is not.
286   */
287  this->reconAllocated = false;
288  this->baselineAllocated = false;
289  this->numPixels = this->numDim = 0;
290  if(size<0)
291    duchampError("Cube(size)","Negative size -- could not define Cube");
292  else{
293    if(size>0){
294      this->array = new float[size];
295      if(this->par.getFlagATrous()||this->par.getFlagSmooth()){
296        this->recon = new float[size];
297        this->reconAllocated = true;
298      }
299      if(this->par.getFlagBaseline()){
300        this->baseline = new float[size];
301        this->baselineAllocated = true;
302      }
303    }
304    this->numPixels = size;
305    this->axisDim = new long[2];
306    this->numDim = 3;
307    this->reconExists = false;
308  }
309}
310//--------------------------------------------------------------------
311
312Cube::Cube(long *dimensions){
313  /**
314   * Alternative Cube constructor, where sizes of dimensions are given.
315   * Arrays are allocated as appropriate (according to the
316   *  relevant flags in Param set), as is the Cube::axisDim array.
317   */
318  int size   = dimensions[0] * dimensions[1] * dimensions[2];
319  int imsize = dimensions[0] * dimensions[1];
320  this->reconAllocated = false;
321  this->baselineAllocated = false;
322  this->numPixels = this->numDim = 0;
323  if((size<0) || (imsize<0) )
324    duchampError("Cube(dimArray)","Negative size -- could not define Cube");
325  else{
326    this->numPixels = size;
327    if(size>0){
328      this->array      = new float[size];
329      this->detectMap  = new short[imsize];
330      if(this->par.getFlagATrous()||this->par.getFlagSmooth()){
331        this->recon    = new float[size];
332        this->reconAllocated = true;
333      }
334      if(this->par.getFlagBaseline()){
335        this->baseline = new float[size];
336        this->baselineAllocated = true;
337      }
338    }
339    this->numDim  = 3;
340    this->axisDim = new long[3];
341    for(int i=0;i<3     ;i++) this->axisDim[i]   = dimensions[i];
342    for(int i=0;i<imsize;i++) this->detectMap[i] = 0;
343    this->reconExists = false;
344  }
345}
346//--------------------------------------------------------------------
347
348Cube::~Cube()
349{
350  /**
351   *  The destructor deletes the memory allocated for Cube::detectMap, and,
352   *  if these have been allocated, Cube::recon and Cube::baseline.
353   */
354  delete [] this->detectMap;
355  if(this->reconAllocated)    delete [] this->recon;
356  if(this->baselineAllocated) delete [] this->baseline;
357}
358//--------------------------------------------------------------------
359
360void Cube::initialiseCube(long *dimensions)
361{
362  /**
363   *  This function will set the sizes of all arrays that will be used by Cube.
364   *  It will also define the values of the axis dimensions: this will be done
365   *   using the WCS in the FitsHeader class, so the WCS needs to be good and
366   *   have three axes. If this is not the case, the axes are assumed to be
367   *   ordered in the sense of lng,lat,spc.
368   *
369   *  \param dimensions An array of values giving the dimensions (sizes) for
370   *  all axes. 
371   */
372
373  int lng,lat,spc,size,imsize;
374
375  if(this->head.isWCS() && (this->head.getWCS()->naxis>=3)){
376    // if there is a WCS and there is at least 3 axes
377    lng = this->head.getWCS()->lng;
378    lat = this->head.getWCS()->lat;
379    spc = this->head.getWCS()->spec;
380  }
381  else{
382    // just take dimensions[] at face value
383    lng = 0;
384    lat = 1;
385    spc = 2;
386  }
387  size   = dimensions[lng] * dimensions[lat] * dimensions[spc];
388  imsize = dimensions[lng] * dimensions[lat];
389
390  this->reconAllocated = false;
391  this->baselineAllocated = false;
392
393  if((size<0) || (imsize<0) )
394    duchampError("Cube::initialiseCube(dimArray)",
395                 "Negative size -- could not define Cube.\n");
396  else{
397    this->numPixels = size;
398    if(size>0){
399      this->array      = new float[size];
400      this->detectMap  = new short[imsize];
401      if(this->par.getFlagATrous() || this->par.getFlagSmooth()){
402        this->recon    = new float[size];
403        this->reconAllocated = true;
404      }
405      if(this->par.getFlagBaseline()){
406        this->baseline = new float[size];
407        this->baselineAllocated = true;
408      }
409    }
410    this->numDim  = 3;
411    this->axisDim = new long[3];
412    this->axisDim[0] = dimensions[lng];
413    this->axisDim[1] = dimensions[lat];
414    this->axisDim[2] = dimensions[spc];
415    for(int i=0;i<imsize;i++) this->detectMap[i] = 0;
416    this->reconExists = false;
417  }
418}
419//--------------------------------------------------------------------
420
421int Cube::getCube(){ 
422    /**
423     * A front-end to the Cube::getCube() function, that does
424     *  subsection checks.
425     * Assumes the Param is set up properly.
426     */
427    std::string fname = par.getImageFile();
428    if(par.getFlagSubsection()) fname+=par.getSubsection();
429    return getCube(fname);
430  };
431//--------------------------------------------------------------------
432
433int Cube::getopts(int argc, char ** argv)
434{
435  /** 
436   *   A function that reads in the command-line options, in a manner
437   *    tailored for use with the main Duchamp program.
438   *   Based on the options given, the appropriate Param set will be read
439   *    in to the Cube class.
440   *
441   *   \param argc The number of command line arguments.
442   *   \param argv The array of command line arguments.
443   */
444
445  int returnValue;
446  if(argc==1){
447    std::cout << ERR_USAGE_MSG;
448    returnValue = FAILURE;
449  }
450  else {
451    std::string file;
452    Param *par = new Param;
453    char c;
454    while( ( c = getopt(argc,argv,"p:f:hv") )!=-1){
455      switch(c) {
456      case 'p':
457        file = optarg;
458        if(this->readParam(file)==FAILURE){
459          std::stringstream errmsg;
460          errmsg << "Could not open parameter file " << file << ".\n";
461          duchampError("Duchamp",errmsg.str());
462          returnValue = FAILURE;
463        }
464        else returnValue = SUCCESS;
465        break;
466      case 'f':
467        file = optarg;
468        par->setImageFile(file);
469        this->saveParam(*par);
470        returnValue = SUCCESS;
471        break;
472      case 'v':
473        std::cout << PROGNAME << " version " << VERSION << std::endl;
474        returnValue = FAILURE;
475        break;
476      case 'h':
477      default :
478        std::cout << ERR_USAGE_MSG;
479        returnValue = FAILURE;
480        break;
481      }
482    }
483    delete par;
484  }
485  return returnValue;
486}
487//--------------------------------------------------------------------
488
489void Cube::saveArray(float *input, long size){
490  if(size != this->numPixels){
491    std::stringstream errmsg;
492    errmsg << "Input array different size to existing array ("
493           << size << " cf. " << this->numPixels << "). Cannot save.\n";
494    duchampError("Cube::saveArray",errmsg.str());
495  }
496  else {
497    if(this->numPixels>0) delete [] array;
498    this->numPixels = size;
499    this->array = new float[size];
500    for(int i=0;i<size;i++) this->array[i] = input[i];
501  }
502}
503//--------------------------------------------------------------------
504
505void Cube::saveRecon(float *input, long size){
506  /**
507   * Saves the array in input to the reconstructed array Cube::recon
508   * The size of the array given must be the same as the current number of
509   * pixels, else an error message is returned and nothing is done.
510   * If the recon array has already been allocated, it is deleted first, and
511   * then the space is allocated.
512   * Afterwards, the appropriate flags are set.
513   * \param input The array of values to be saved.
514   * \param size The size of input.
515   */
516  if(size != this->numPixels){
517    std::stringstream errmsg;
518    errmsg << "Input array different size to existing array ("
519           << size << " cf. " << this->numPixels << "). Cannot save.\n";
520    duchampError("Cube::saveRecon",errmsg.str());
521  }
522  else {
523    if(this->numPixels>0){
524      if(this->reconAllocated) delete [] this->recon;
525      this->numPixels = size;
526      this->recon = new float[size];
527      this->reconAllocated = true;
528      for(int i=0;i<size;i++) this->recon[i] = input[i];
529      this->reconExists = true;
530    }
531  }
532}
533//--------------------------------------------------------------------
534
535void Cube::getRecon(float *output){
536  /**
537   * The reconstructed array is written to output. The output array needs to
538   *  be defined beforehand: no checking is done on the memory.
539   * \param output The array that is written to.
540   */
541  // Need check for change in number of pixels!
542  for(int i=0;i<this->numPixels;i++){
543    if(this->reconExists) output[i] = this->recon[i];
544    else output[i] = 0.;
545  }
546}
547//--------------------------------------------------------------------
548
549void Cube::removeMW()
550{
551  /**
552   * The channels corresponding to the Milky Way range (as given by the Param
553   *  set) are all set to 0 in the pixel array.
554   * Only done if the appropriate flag is set, and the pixels are not BLANK.
555   * \deprecated
556   */
557  if(this->par.getFlagMW()){
558    for(int pix=0;pix<this->axisDim[0]*this->axisDim[1];pix++){
559      for(int z=0;z<this->axisDim[2];z++){
560        int pos = z*this->axisDim[0]*this->axisDim[1] + pix;
561        if(!this->isBlank(pos) && this->par.isInMW(z)) this->array[pos]=0.;
562      }
563    }
564  }
565}
566//--------------------------------------------------------------------
567
568void Cube::setCubeStatsOld()
569{
570  /** 
571   *   \deprecated
572   *
573   *   Calculates the full statistics for the cube:  mean, rms, median, madfm.
574   *   Only do this if the threshold has not been defined (ie. is still 0.,
575   *    its default).
576   *   Also work out the threshold and store it in the Param set.
577   *   
578   *   For the stats calculations, we ignore BLANKs and MW channels.
579   */
580
581  if(!this->par.getFlagFDR() && this->par.getFlagUserThreshold() ){
582    // if the user has defined a threshold, set this in the StatsContainer
583    this->Stats.setThreshold( this->par.getThreshold() );
584  }
585  else{
586    // only work out the mean etc if we need to.
587    // the only reason we don't is if the user has specified a threshold.
588   
589    std::cout << "Calculating the cube statistics... " << std::flush;
590   
591    // get number of good pixels;
592    int goodSize = 0;
593    for(int p=0;p<this->axisDim[0]*this->axisDim[1];p++){
594      for(int z=0;z<this->axisDim[2];z++){
595        int vox = z * this->axisDim[0] * this->axisDim[1] + p;
596        if(!this->isBlank(vox) && !this->par.isInMW(z)) goodSize++;
597      }
598    }
599
600    float *tempArray = new float[goodSize];
601
602    goodSize=0;
603    for(int p=0;p<this->axisDim[0]*this->axisDim[1];p++){
604      for(int z=0;z<this->axisDim[2];z++){
605        int vox = z * this->axisDim[0] * this->axisDim[1] + p;
606        if(!this->isBlank(vox) && !this->par.isInMW(z))
607          tempArray[goodSize++] = this->array[vox];
608      }
609    }
610    if(!this->reconExists){
611      // if there's no recon array, calculate everything from orig array
612      this->Stats.calculate(tempArray,goodSize);
613    }
614    else{
615      // just get mean & median from orig array, and rms & madfm from recon
616      StatsContainer<float> origStats,reconStats;
617      origStats.calculate(tempArray,goodSize);
618      goodSize=0;
619      for(int p=0;p<this->axisDim[0]*this->axisDim[1];p++){
620        for(int z=0;z<this->axisDim[2];z++){
621          int vox = z * this->axisDim[0] * this->axisDim[1] + p;
622          if(!this->isBlank(vox) && !this->par.isInMW(z))
623            tempArray[goodSize++] = this->array[vox] - this->recon[vox];
624        }
625      }
626      reconStats.calculate(tempArray,goodSize);
627
628      // Get the "middle" estimators from the original array.
629      this->Stats.setMean(origStats.getMean());
630      this->Stats.setMedian(origStats.getMedian());
631      // Get the "spread" estimators from the residual (orig-recon) array
632      this->Stats.setStddev(reconStats.getStddev());
633      this->Stats.setMadfm(reconStats.getMadfm());
634    }
635
636    delete [] tempArray;
637
638    this->Stats.setUseFDR( this->par.getFlagFDR() );
639    // If the FDR method has been requested
640    if(this->par.getFlagFDR())  this->setupFDR();
641    else{
642      // otherwise, calculate one based on the requested SNR cut level, and
643      //   then set the threshold parameter in the Par set.
644      this->Stats.setThresholdSNR( this->par.getCut() );
645      this->par.setThreshold( this->Stats.getThreshold() );
646    }
647   
648   
649  }
650  std::cout << "Using ";
651  if(this->par.getFlagFDR()) std::cout << "effective ";
652  std::cout << "flux threshold of: ";
653  float thresh = this->Stats.getThreshold();
654  if(this->par.getFlagNegative()) thresh *= -1.;
655  std::cout << thresh << std::endl;
656
657}
658//--------------------------------------------------------------------
659
660void Cube::setCubeStats()
661{
662  /** 
663   *   Calculates the full statistics for the cube:
664   *     mean, rms, median, madfm
665   *   Only do this if the threshold has not been defined (ie. is still 0.,
666   *    its default).
667   *   Also work out the threshold and store it in the par set.
668   *   
669   *   Different from Cube::setCubeStatsOld() as it doesn't use the
670   *    getStats functions but has own versions of them hardcoded to
671   *    ignore BLANKs and MW channels. This saves on memory usage -- necessary
672   *    for dealing with very big files.
673   */
674
675  if(!this->par.getFlagFDR() && this->par.getFlagUserThreshold() ){
676    // if the user has defined a threshold, set this in the StatsContainer
677    this->Stats.setThreshold( this->par.getThreshold() );
678  }
679  else{
680    // only work out the stats if we need to.
681    // the only reason we don't is if the user has specified a threshold.
682   
683    std::cout << "Calculating the cube statistics... " << std::flush;
684   
685    long xysize = this->axisDim[0]*this->axisDim[1];
686
687    // get number of good pixels;
688    int vox,goodSize = 0;
689    for(int p=0;p<xysize;p++){
690      for(int z=0;z<this->axisDim[2];z++){
691        vox = z*xysize+p;
692        if(!this->isBlank(vox) && !this->par.isInMW(z)) goodSize++;
693      }
694    }
695
696    float *tempArray = new float[goodSize];
697
698    goodSize=0;
699    for(int x=0;x<this->axisDim[0];x++){
700      for(int y=0;y<this->axisDim[1];y++){
701        for(int z=0;z<this->axisDim[2];z++){
702          vox = z * xysize + y*this->axisDim[0] + x;
703          if(!this->isBlank(vox) && !this->par.isInMW(z) &&
704             this->par.isStatOK(x,y,z)){
705            tempArray[goodSize] = this->array[vox];
706            goodSize++;
707          }
708        }
709      }
710    }
711
712    float mean,median,stddev,madfm;
713    mean = tempArray[0];
714    for(int i=1;i<goodSize;i++) mean += tempArray[i];
715    mean /= float(goodSize);
716    mean = findMean(tempArray,goodSize);
717    this->Stats.setMean(mean);
718   
719    std::sort(tempArray,tempArray+goodSize);
720    if((goodSize%2)==0)
721      median = (tempArray[goodSize/2-1] + tempArray[goodSize/2])/2;
722    else median = tempArray[goodSize/2];
723    this->Stats.setMedian(median);
724
725   
726    if(!this->reconExists){
727      // if there's no recon array, calculate everything from orig array
728      stddev = (tempArray[0]-mean) * (tempArray[0]-mean);
729      for(int i=1;i<goodSize;i++)
730        stddev += (tempArray[i]-mean)*(tempArray[i]-mean);
731      stddev = sqrt(stddev/float(goodSize-1));
732      this->Stats.setStddev(stddev);
733
734      for(int i=0;i<goodSize;i++)// tempArray[i] = absval(tempArray[i]-median);
735        {
736          if(tempArray[i]>median) tempArray[i] -= median;
737          else tempArray[i] = median - tempArray[i];
738        }
739      std::sort(tempArray,tempArray+goodSize);
740      if((goodSize%2)==0)
741        madfm = (tempArray[goodSize/2-1]+tempArray[goodSize/2])/2;
742      else madfm = tempArray[goodSize/2];
743      this->Stats.setMadfm(madfm);
744    }
745    else{
746      // just get mean & median from orig array, and rms & madfm from residual
747      // recompute array values to be residuals & then find stddev & madfm
748
749      goodSize = 0;
750      for(int p=0;p<xysize;p++){
751        for(int z=0;z<this->axisDim[2];z++){
752          vox = z * xysize + p;
753          if(!this->isBlank(vox) && !this->par.isInMW(z)){
754            tempArray[goodSize] = this->array[vox] - this->recon[vox];
755            goodSize++;
756          }
757        }
758      }
759      mean = tempArray[0];
760      for(int i=1;i<goodSize;i++) mean += tempArray[i];
761      mean /= float(goodSize);
762      stddev = (tempArray[0]-mean) * (tempArray[0]-mean);
763      for(int i=1;i<goodSize;i++)
764        stddev += (tempArray[i]-mean)*(tempArray[i]-mean);
765      stddev = sqrt(stddev/float(goodSize-1));
766      this->Stats.setStddev(stddev);
767
768      std::sort(tempArray,tempArray+goodSize);
769      if((goodSize%2)==0)
770        median = (tempArray[goodSize/2-1] + tempArray[goodSize/2])/2;
771      else median = tempArray[goodSize/2];
772      for(int i=0;i<goodSize;i++){
773        if(tempArray[i]>median) tempArray[i] = tempArray[i]-median;
774        else tempArray[i] = median - tempArray[i];
775      }
776      std::sort(tempArray,tempArray+goodSize);
777      if((goodSize%2)==0)
778        madfm = (tempArray[goodSize/2-1] + tempArray[goodSize/2])/2;
779      else madfm = tempArray[goodSize/2];
780      this->Stats.setMadfm(madfm);
781    }
782
783    delete [] tempArray;
784
785    this->Stats.setUseFDR( this->par.getFlagFDR() );
786    // If the FDR method has been requested
787    if(this->par.getFlagFDR())  this->setupFDR();
788    else{
789      // otherwise, calculate threshold based on the requested SNR cut level,
790      //  and then set the threshold parameter in the Par set.
791      this->Stats.setThresholdSNR( this->par.getCut() );
792      this->par.setThreshold( this->Stats.getThreshold() );
793    }
794   
795  }
796
797  std::cout << "Using ";
798  if(this->par.getFlagFDR()) std::cout << "effective ";
799  std::cout << "flux threshold of: ";
800  float thresh = this->Stats.getThreshold();
801  if(this->par.getFlagNegative()) thresh *= -1.;
802  std::cout << thresh << std::endl;
803
804}
805//--------------------------------------------------------------------
806
807int Cube::setupFDR()
808{
809  /** 
810   *   Determines the critical Probability value for the False Discovery Rate
811   *    detection routine. All pixels with Prob less than this value will
812   *    be considered detections.
813   *
814   *   The Prob here is the probability, assuming a Normal distribution, of
815   *    obtaining a value as high or higher than the pixel value (ie. only the
816   *    positive tail of the PDF)
817   */
818
819  // first calculate p-value for each pixel -- assume Gaussian for now.
820
821  float *orderedP = new float[this->numPixels];
822  int count = 0;
823  float zStat;
824  for(int pix=0; pix<this->numPixels; pix++){
825
826    if( !(this->par.isBlank(this->array[pix])) ){
827      // only look at non-blank pixels
828      zStat = (this->array[pix] - this->Stats.getMiddle()) /
829        this->Stats.getSpread();
830     
831      orderedP[count++] = 0.5 * erfc(zStat/M_SQRT2);
832      // Need the factor of 0.5 here, as we are only considering the positive
833      //  tail of the distribution. Don't care about negative detections.
834    }
835  }
836
837  // now order them
838  std::sort(orderedP,orderedP+count);
839 
840  // now find the maximum P value.
841  int max = 0;
842  float cN = 0.;
843  int psfCtr;
844  int numVox = int(this->par.getBeamSize()) * 2;
845  // why beamSize*2? we are doing this in 3D, so spectrally assume just the
846  //  neighbouring channels are correlated, but spatially all those within
847  //  the beam, so total number of voxels is 2*beamSize
848  for(psfCtr=1;psfCtr<=numVox;(psfCtr)++)
849    cN += 1./float(psfCtr);
850
851  for(int loopCtr=0;loopCtr<count;loopCtr++) {
852    if( orderedP[loopCtr] <
853        (double(loopCtr+1)*this->par.getAlpha()/(cN * double(count))) ) {
854      max = loopCtr;
855    }
856  }
857
858  this->Stats.setPThreshold( orderedP[max] );
859
860  delete [] orderedP;
861
862  // Find real value of the P threshold by finding the inverse of the
863  //  error function -- root finding with brute force technique
864  //  (relatively slow, but we only do it once).
865  zStat = 0;
866  float deltaZ = 0.1;
867  float tolerance = 1.e-6;
868  float zeroP = 0.5 * erfc(zStat/M_SQRT2) - this->Stats.getPThreshold();
869  do{
870    zStat+=deltaZ;
871    if((zeroP * (erfc(zStat/M_SQRT2)-this->Stats.getPThreshold()))<0.){
872      zStat-=deltaZ;
873      deltaZ/=2.;
874    }
875  }while(deltaZ>tolerance);
876  this->Stats.setThreshold( zStat*this->Stats.getSpread() +
877                            this->Stats.getMiddle() );
878
879}
880//--------------------------------------------------------------------
881
882bool Cube::isDetection(long x, long y, long z)
883{
884  /**
885   * Is a given voxel at position (x,y,z) a detection, based on the statistics
886   *  in the Cube's StatsContainer?
887   * If the pixel lies outside the valid range for the data array, return false.
888   * \param x X-value of the Cube's voxel to be tested.
889   * \param y Y-value of the Cube's voxel to be tested.
890   * \param z Z-value of the Cube's voxel to be tested.
891   */
892    long voxel = z*axisDim[0]*axisDim[1] + y*axisDim[0] + x;
893    return DataArray::isDetection(array[voxel]);
894  };
895//--------------------------------------------------------------------
896
897void Cube::calcObjectWCSparams()
898{
899  /**
900   *  A function that calculates the WCS parameters for each object in the
901   *  Cube's list of detections.
902   *  Each object gets an ID number assigned to it (which is simply its order
903   *   in the list), and if the WCS is good, the WCS paramters are calculated.
904   */
905 
906  for(int i=0;i<this->objectList.size();i++){
907    this->objectList[i].setID(i+1);
908    this->objectList[i].calcFluxes(this->array,this->axisDim);
909    this->objectList[i].calcWCSparams(this->array,this->axisDim,this->head);
910   
911    if(this->par.getFlagUserThreshold())
912      this->objectList[i].setPeakSNR( this->objectList[i].getPeakFlux() / this->Stats.getThreshold() );
913    else
914      this->objectList[i].setPeakSNR( (this->objectList[i].getPeakFlux() - this->Stats.getMiddle()) / this->Stats.getSpread() );
915
916  } 
917
918  if(!this->head.isWCS()){
919    // if the WCS is bad, set the object names to Obj01 etc
920    int numspaces = int(log10(this->objectList.size())) + 1;
921    std::stringstream ss;
922    for(int i=0;i<this->objectList.size();i++){
923      ss.str("");
924      ss << "Obj" << std::setfill('0') << std::setw(numspaces) << i+1;
925      this->objectList[i].setName(ss.str());
926    }
927  }
928 
929}
930//--------------------------------------------------------------------
931
932void Cube::updateDetectMap()
933{
934  /**
935   *  A function that, for each detected object in the cube's list, increments
936   *   the cube's detection map by the required amount at each pixel.
937   */
938
939  Scan temp;
940  for(int obj=0;obj<this->objectList.size();obj++){
941    long numZ=this->objectList[obj].pixels().getNumChanMap();
942    for(int iz=0;iz<numZ;iz++){ // for each channel map
943      Object2D chanmap = this->objectList[obj].pixels().getChanMap(iz).getObject();
944      for(int iscan=0;iscan<chanmap.getNumScan();iscan++){
945        temp = chanmap.getScan(iscan);
946        for(int x=temp.getX(); x <= temp.getXmax(); x++)
947          this->detectMap[temp.getY()*this->axisDim[0] + x]++;
948      } // end of loop over scans
949    } // end of loop over channel maps
950  } // end of loop over objects.
951
952}
953//--------------------------------------------------------------------
954
955void Cube::updateDetectMap(Detection obj)
956{
957  /**
958   *  A function that, for the given object, increments the cube's
959   *  detection map by the required amount at each pixel.
960   *
961   *  \param obj A Detection object that is being incorporated into the map.
962   */
963
964  Scan temp;
965  long numZ=obj.pixels().getNumChanMap();
966  for(int iz=0;iz<numZ;iz++){ // for each channel map
967    Object2D chanmap = obj.pixels().getChanMap(iz).getObject();
968    for(int iscan=0;iscan<chanmap.getNumScan();iscan++){
969      temp = chanmap.getScan(iscan);
970      for(int x=temp.getX(); x <= temp.getXmax(); x++)
971        this->detectMap[temp.getY()*this->axisDim[0] + x]++;
972    } // end of loop over scans
973  } // end of loop over channel maps
974
975}
976//--------------------------------------------------------------------
977
978float Cube::enclosedFlux(Detection obj)
979{
980  /**
981   *   A function to calculate the flux enclosed by the range
982   *    of pixels detected in the object obj (not necessarily all
983   *    pixels will have been detected).
984   *
985   *   \param obj The Detection under consideration.
986   */
987  obj.calcFluxes(this->array, this->axisDim);
988  int xsize = obj.getXmax()-obj.getXmin()+1;
989  int ysize = obj.getYmax()-obj.getYmin()+1;
990  int zsize = obj.getZmax()-obj.getZmin()+1;
991  std::vector <float> fluxArray(xsize*ysize*zsize,0.);
992  for(int x=0;x<xsize;x++){
993    for(int y=0;y<ysize;y++){
994      for(int z=0;z<zsize;z++){
995        fluxArray[x+y*xsize+z*ysize*xsize] =
996          this->getPixValue(x+obj.getXmin(),
997                            y+obj.getYmin(),
998                            z+obj.getZmin());
999        if(this->par.getFlagNegative())
1000          fluxArray[x+y*xsize+z*ysize*xsize] *= -1.;
1001      }
1002    }
1003  }
1004  float sum = 0.;
1005  for(int i=0;i<fluxArray.size();i++)
1006    if(!this->par.isBlank(fluxArray[i])) sum+=fluxArray[i];
1007  return sum;
1008}
1009//--------------------------------------------------------------------
1010
1011void Cube::setupColumns()
1012{
1013  /**
1014   *   A front-end to the two setup routines in columns.cc.
1015   *   This first calculates the WCS parameters for all objects, then
1016   *    sets up the columns (calculates their widths and precisions and so on).
1017   *   The precisions are also stored in each Detection object.
1018   */
1019  this->calcObjectWCSparams(); 
1020  // need this as the colSet functions use vel, RA, Dec, etc...
1021 
1022  this->fullCols.clear();
1023  this->fullCols = getFullColSet(this->objectList, this->head);
1024
1025  this->logCols.clear();
1026  this->logCols = getLogColSet(this->objectList, this->head);
1027
1028  int vel,fpeak,fint,pos,xyz,temp,snr;
1029  vel = fullCols[VEL].getPrecision();
1030  fpeak = fullCols[FPEAK].getPrecision();
1031  snr = fullCols[SNRPEAK].getPrecision();
1032  xyz = fullCols[X].getPrecision();
1033  if(temp=fullCols[Y].getPrecision() > xyz) xyz = temp;
1034  if(temp=fullCols[Z].getPrecision() > xyz) xyz = temp;
1035  if(this->head.isWCS()) fint = fullCols[FINT].getPrecision();
1036  else fint = fullCols[FTOT].getPrecision();
1037  pos = fullCols[WRA].getPrecision();
1038  if(temp=fullCols[WDEC].getPrecision() > pos) pos = temp;
1039 
1040  for(int obj=0;obj<this->objectList.size();obj++){
1041    this->objectList[obj].setVelPrec(vel);
1042    this->objectList[obj].setFpeakPrec(fpeak);
1043    this->objectList[obj].setXYZPrec(xyz);
1044    this->objectList[obj].setPosPrec(pos);
1045    this->objectList[obj].setFintPrec(fint);
1046    this->objectList[obj].setSNRPrec(snr);
1047  }
1048
1049}
1050//--------------------------------------------------------------------
1051
1052bool Cube::objAtSpatialEdge(Detection obj)
1053{
1054  /**
1055   *   A function to test whether the object obj
1056   *    lies at the edge of the cube's spatial field --
1057   *    either at the boundary, or next to BLANKs.
1058   *
1059   *   /param obj The Detection under consideration.
1060   */
1061
1062  bool atEdge = false;
1063
1064  int pix = 0;
1065  std::vector<Voxel> voxlist = obj.pixels().getPixelSet();
1066  while(!atEdge && pix<voxlist.size()){
1067    // loop over each pixel in the object, until we find an edge pixel.
1068    for(int dx=-1;dx<=1;dx+=2){
1069      if( ((voxlist[pix].getX()+dx)<0) ||
1070          ((voxlist[pix].getX()+dx)>=this->axisDim[0]) )
1071        atEdge = true;
1072      else if(this->isBlank(voxlist[pix].getX()+dx,
1073                            voxlist[pix].getY(),
1074                            voxlist[pix].getZ()))
1075        atEdge = true;
1076    }
1077    for(int dy=-1;dy<=1;dy+=2){
1078      if( ((voxlist[pix].getY()+dy)<0) ||
1079          ((voxlist[pix].getY()+dy)>=this->axisDim[1]) )
1080        atEdge = true;
1081      else if(this->isBlank(voxlist[pix].getX(),
1082                            voxlist[pix].getY()+dy,
1083                            voxlist[pix].getZ()))
1084        atEdge = true;
1085    }
1086    pix++;
1087  }
1088
1089  return atEdge;
1090}
1091//--------------------------------------------------------------------
1092
1093bool Cube::objAtSpectralEdge(Detection obj)
1094{
1095  /** 
1096   *   A function to test whether the object obj
1097   *    lies at the edge of the cube's spectral extent --
1098   *    either at the boundary, or next to BLANKs.
1099   *
1100   *   /param obj The Detection under consideration.
1101   */
1102
1103  bool atEdge = false;
1104
1105  int pix = 0;
1106  std::vector<Voxel> voxlist = obj.pixels().getPixelSet();
1107  while(!atEdge && pix<voxlist.size()){
1108    // loop over each pixel in the object, until we find an edge pixel.
1109    for(int dz=-1;dz<=1;dz+=2){
1110      if( ((voxlist[pix].getZ()+dz)<0) ||
1111          ((voxlist[pix].getZ()+dz)>=this->axisDim[2]))
1112        atEdge = true;
1113      else if(this->isBlank(voxlist[pix].getX(),
1114                            voxlist[pix].getY(),
1115                            voxlist[pix].getZ()+dz))
1116        atEdge = true;
1117    }
1118    pix++;
1119  }
1120
1121  return atEdge;
1122}
1123//--------------------------------------------------------------------
1124
1125void Cube::setObjectFlags()
1126{
1127  /**   
1128   *   A function to set any warning flags for all the detected objects
1129   *    associated with the cube.
1130   *   Flags to be looked for:
1131   *    <ul><li> Negative enclosed flux (N)
1132   *        <li> Detection at edge of field (spatially) (E)
1133   *        <li> Detection at edge of spectral region (S)
1134   *    </ul>
1135   */
1136
1137  for(int i=0;i<this->objectList.size();i++){
1138
1139    if( this->enclosedFlux(this->objectList[i]) < 0. ) 
1140      this->objectList[i].addToFlagText("N");
1141
1142    if( this->objAtSpatialEdge(this->objectList[i]) )
1143      this->objectList[i].addToFlagText("E");
1144
1145    if( this->objAtSpectralEdge(this->objectList[i]) )
1146      this->objectList[i].addToFlagText("S");
1147
1148  }
1149
1150}
1151//--------------------------------------------------------------------
1152
1153
1154
1155/****************************************************************/
1156/////////////////////////////////////////////////////////////
1157//// Functions for Image class
1158/////////////////////////////////////////////////////////////
1159
1160Image::Image(long size){
1161  // need error handling in case size<0 !!!
1162  this->numPixels = this->numDim = 0;
1163  if(size<0)
1164    duchampError("Image(size)","Negative size -- could not define Image");
1165  else{
1166    if(size>0) this->array = new float[size];
1167    this->numPixels = size;
1168    this->axisDim = new long[2];
1169    this->numDim = 2;
1170  }
1171}
1172//--------------------------------------------------------------------
1173
1174Image::Image(long *dimensions){
1175  this->numPixels = this->numDim = 0;
1176  int size = dimensions[0] * dimensions[1];
1177  if(size<0)
1178    duchampError("Image(dimArray)","Negative size -- could not define Image");
1179  else{
1180    this->numPixels = size;
1181    if(size>0) this->array = new float[size];
1182    this->numDim=2;
1183    this->axisDim = new long[2];
1184    for(int i=0;i<2;i++) this->axisDim[i] = dimensions[i];
1185  }
1186}
1187//--------------------------------------------------------------------
1188//--------------------------------------------------------------------
1189
1190void Image::saveArray(float *input, long size)
1191{
1192  /**
1193   * Saves the array in input to the pixel array Image::array.
1194   * The size of the array given must be the same as the current number of
1195   * pixels, else an error message is returned and nothing is done.
1196   * \param input The array of values to be saved.
1197   * \param size The size of input.
1198   */
1199  if(size != this->numPixels)
1200    duchampError("Image::saveArray",
1201                 "Input array different size to existing array. Cannot save.");
1202  else {
1203    if(this->numPixels>0) delete [] array;
1204    this->numPixels = size;
1205    if(this->numPixels>0) this->array = new float[size];
1206    for(int i=0;i<size;i++) this->array[i] = input[i];
1207  }
1208}
1209//--------------------------------------------------------------------
1210
1211void Image::extractSpectrum(float *Array, long *dim, long pixel)
1212{
1213  /**
1214   *  A function to extract a 1-D spectrum from a 3-D array.
1215   *  The array is assumed to be 3-D with the third dimension the spectral one.
1216   *  The spectrum extracted is the one lying in the spatial pixel referenced
1217   *    by the third argument.
1218   *  The extracted spectrum is stored in the pixel array Image::array.
1219   * \param Array The array containing the pixel values, from which
1220   *               the spectrum is extracted.
1221   * \param dim The array of dimension values.
1222   * \param pixel The spatial pixel that contains the desired spectrum.
1223   */
1224  if((pixel<0)||(pixel>=dim[0]*dim[1]))
1225    duchampError("Image::extractSpectrum",
1226                 "Requested spatial pixel outside allowed range. Cannot save.");
1227  else if(dim[2] != this->numPixels)
1228    duchampError("Image::extractSpectrum",
1229                 "Input array different size to existing array. Cannot save.");
1230  else {
1231    if(this->numPixels>0) delete [] array;
1232    this->numPixels = dim[2];
1233    if(this->numPixels>0) this->array = new float[dim[2]];
1234    for(int z=0;z<dim[2];z++) this->array[z] = Array[z*dim[0]*dim[1] + pixel];
1235  }
1236}
1237//--------------------------------------------------------------------
1238
1239void Image::extractSpectrum(Cube &cube, long pixel)
1240{
1241  /**
1242   *  A function to extract a 1-D spectrum from a Cube class
1243   *  The spectrum extracted is the one lying in the spatial pixel referenced
1244   *    by the second argument.
1245   *  The extracted spectrum is stored in the pixel array Image::array.
1246   * \param cube The Cube containing the pixel values, from which the spectrum is extracted.
1247   * \param pixel The spatial pixel that contains the desired spectrum.
1248   */
1249  long zdim = cube.getDimZ();
1250  long spatSize = cube.getDimX()*cube.getDimY();
1251  if((pixel<0)||(pixel>=spatSize))
1252    duchampError("Image::extractSpectrum",
1253                 "Requested spatial pixel outside allowed range. Cannot save.");
1254  else if(zdim != this->numPixels)
1255    duchampError("Image::extractSpectrum",
1256                 "Input array different size to existing array. Cannot save.");
1257  else {
1258    if(this->numPixels>0) delete [] array;
1259    this->numPixels = zdim;
1260    if(this->numPixels>0) this->array = new float[zdim];
1261    for(int z=0;z<zdim;z++)
1262      this->array[z] = cube.getPixValue(z*spatSize + pixel);
1263  }
1264}
1265//--------------------------------------------------------------------
1266
1267void Image::extractImage(float *Array, long *dim, long channel)
1268{
1269  /**
1270   *  A function to extract a 2-D image from a 3-D array.
1271   *  The array is assumed to be 3-D with the third dimension the spectral one.
1272   *  The dimensions of the array are in the dim[] array.
1273   *  The image extracted is the one lying in the channel referenced
1274   *    by the third argument.
1275   *  The extracted image is stored in the pixel array Image::array.
1276   * \param Array The array containing the pixel values, from which the image is extracted.
1277   * \param dim The array of dimension values.
1278   * \param channel The spectral channel that contains the desired image.
1279   */
1280
1281  long spatSize = dim[0]*dim[1];
1282  if((channel<0)||(channel>=dim[2]))
1283    duchampError("Image::extractImage",
1284                 "Requested channel outside allowed range. Cannot save.");
1285  else if(spatSize != this->numPixels)
1286    duchampError("Image::extractImage",
1287                 "Input array different size to existing array. Cannot save.");
1288  else {
1289    if(this->numPixels>0) delete [] array;
1290    this->numPixels = spatSize;
1291    if(this->numPixels>0) this->array = new float[spatSize];
1292    for(int npix=0; npix<spatSize; npix++)
1293      this->array[npix] = Array[channel*spatSize + npix];
1294  }
1295}
1296//--------------------------------------------------------------------
1297
1298void Image::extractImage(Cube &cube, long channel)
1299{
1300  /**
1301   *  A function to extract a 2-D image from Cube class.
1302   *  The image extracted is the one lying in the channel referenced
1303   *    by the second argument.
1304   *  The extracted image is stored in the pixel array Image::array.
1305   * \param cube The Cube containing the pixel values, from which the image is extracted.
1306   * \param channel The spectral channel that contains the desired image.
1307   */
1308  long spatSize = cube.getDimX()*cube.getDimY();
1309  if((channel<0)||(channel>=cube.getDimZ()))
1310    duchampError("Image::extractImage",
1311                 "Requested channel outside allowed range. Cannot save.");
1312  else if(spatSize != this->numPixels)
1313    duchampError("Image::extractImage",
1314                 "Input array different size to existing array. Cannot save.");
1315  else {
1316    if(this->numPixels>0) delete [] array;
1317    this->numPixels = spatSize;
1318    if(this->numPixels>0) this->array = new float[spatSize];
1319    for(int npix=0; npix<spatSize; npix++)
1320      this->array[npix] = cube.getPixValue(channel*spatSize + npix);
1321  }
1322}
1323//--------------------------------------------------------------------
1324
1325void Image::removeMW()
1326{
1327  /**
1328   *  A function to remove the Milky Way range of channels from a 1-D spectrum.
1329   *  The array in this Image is assumed to be 1-D, with only the first axisDim
1330   *    equal to 1.
1331   *  The values of the MW channels are set to 0, unless they are BLANK.
1332   */
1333  if(this->par.getFlagMW() && (this->axisDim[1]==1) ){
1334    for(int z=0;z<this->axisDim[0];z++){
1335      if(!this->isBlank(z) && this->par.isInMW(z)) this->array[z]=0.;
1336    }
1337  }
1338}
1339
Note: See TracBrowser for help on using the repository browser.