source: trunk/src/SDMath.cc @ 130

Last change on this file since 130 was 130, checked in by kil064, 19 years ago

Rewrite pretty much all of it to

  • use iterators rather than indexed for loops
  • loop over nrows

Fixed some minor bugs here and there
Added function 'statistics' to provide more statistics beyond standard deviation

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 18.4 KB
Line 
1//#---------------------------------------------------------------------------
2//# SDMath.cc: A collection of single dish mathematical operations
3//#---------------------------------------------------------------------------
4//# Copyright (C) 2004
5//# ATNF
6//#
7//# This program is free software; you can redistribute it and/or modify it
8//# under the terms of the GNU General Public License as published by the Free
9//# Software Foundation; either version 2 of the License, or (at your option)
10//# any later version.
11//#
12//# This program is distributed in the hope that it will be useful, but
13//# WITHOUT ANY WARRANTY; without even the implied warranty of
14//# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General
15//# Public License for more details.
16//#
17//# You should have received a copy of the GNU General Public License along
18//# with this program; if not, write to the Free Software Foundation, Inc.,
19//# 675 Massachusetts Ave, Cambridge, MA 02139, USA.
20//#
21//# Correspondence concerning this software should be addressed as follows:
22//#        Internet email: Malte.Marquarding@csiro.au
23//#        Postal address: Malte Marquarding,
24//#                        Australia Telescope National Facility,
25//#                        P.O. Box 76,
26//#                        Epping, NSW, 2121,
27//#                        AUSTRALIA
28//#
29//# $Id:
30//#---------------------------------------------------------------------------
31#include <vector>
32
33#include <casa/aips.h>
34#include <casa/BasicSL/String.h>
35#include <casa/Arrays/IPosition.h>
36#include <casa/Arrays/Array.h>
37#include <casa/Arrays/ArrayIter.h>
38#include <casa/Arrays/VectorIter.h>
39#include <casa/Arrays/ArrayMath.h>
40#include <casa/Arrays/ArrayLogical.h>
41#include <casa/Arrays/MaskedArray.h>
42#include <casa/Arrays/MaskArrMath.h>
43#include <casa/Arrays/MaskArrLogi.h>
44#include <casa/Exceptions.h>
45
46#include <tables/Tables/Table.h>
47#include <tables/Tables/ScalarColumn.h>
48#include <tables/Tables/ArrayColumn.h>
49
50#include <lattices/Lattices/LatticeUtilities.h>
51#include <lattices/Lattices/RebinLattice.h>
52#include <coordinates/Coordinates/SpectralCoordinate.h>
53#include <coordinates/Coordinates/CoordinateSystem.h>
54#include <coordinates/Coordinates/CoordinateUtil.h>
55
56#include "MathUtils.h"
57#include "SDContainer.h"
58#include "SDMemTable.h"
59
60#include "SDMath.h"
61
62using namespace casa;
63using namespace asap;
64//using namespace asap::SDMath;
65
66CountedPtr<SDMemTable> SDMath::average(const CountedPtr<SDMemTable>& in)
67//
68// Average all rows in Table in time
69//
70{
71  Table t = in->table();
72  ROArrayColumn<Float> tsys(t, "TSYS");
73  ROScalarColumn<Double> mjd(t, "TIME");
74  ROScalarColumn<String> srcn(t, "SRCNAME");
75  ROScalarColumn<Double> integr(t, "INTERVAL");
76  ROArrayColumn<uInt> freqidc(t, "FREQID");
77  IPosition ip = in->rowAsMaskedArray(0).shape();
78  Array<Float> outarr(ip); outarr =0.0;
79  Array<Float> narr(ip);narr = 0.0;
80  Array<Float> narrinc(ip);narrinc = 1.0;
81
82  Array<Float> tsarr(tsys.shape(0));
83  Array<Float> outtsarr(tsys.shape(0));
84  outtsarr =0.0;
85  tsys.get(0, tsarr);// this is probably unneccessary as tsys should
86  Double tme = 0.0;
87  Double inttime = 0.0;
88
89// Loop over rows
90
91  for (uInt i=0; i < t.nrow(); i++) {
92
93// Get data and accumulate sums
94
95    MaskedArray<Float> marr(in->rowAsMaskedArray(i));
96    outarr += marr;
97    MaskedArray<Float> n(narrinc,marr.getMask());
98    narr += n;
99
100// Accumulkate Tsys
101
102    tsys.get(i, tsarr);// this is probably unneccessary as tsys should
103    outtsarr += tsarr; // be constant
104    Double tmp;
105    mjd.get(i,tmp);
106    tme += tmp;// average time
107    integr.get(i,tmp);
108    inttime += tmp;
109  }
110
111// Average
112
113  MaskedArray<Float> nma(narr,(narr > Float(0)));
114  outarr /= nma;
115
116// Create container and put
117
118  Array<Bool> outflagsb = !(nma.getMask());
119  Array<uChar> outflags(outflagsb.shape());
120  convertArray(outflags,outflagsb);
121  SDContainer sc = in->getSDContainer();
122  Int n = t.nrow();
123  outtsarr /= Float(n);
124  sc.timestamp = tme/Double(n);
125  sc.interval = inttime;
126  String tstr; srcn.getScalar(0,tstr);// get sourcename of "mid" point
127  sc.sourcename = tstr;
128  Vector<uInt> tvec;
129  freqidc.get(0,tvec);
130  sc.putFreqMap(tvec);
131  sc.putTsys(outtsarr);
132  sc.scanid = 0;
133  sc.putSpectrum(outarr);
134  sc.putFlags(outflags);
135  SDMemTable* sdmt = new SDMemTable(*in,True);
136  sdmt->putSDContainer(sc);
137  return CountedPtr<SDMemTable>(sdmt);
138}
139
140CountedPtr<SDMemTable>
141SDMath::quotient(const CountedPtr<SDMemTable>& on,
142                 const CountedPtr<SDMemTable>& off)
143//
144// Compute quotient spectrum
145//
146{
147  const uInt nRows = on->nRow();
148  if (off->nRow() != nRows) {
149     throw (AipsError("Input Scan Tables must have the same number of rows"));
150  }
151
152// Input Tables and columns
153
154  Table ton = on->table();
155  Table toff = off->table();
156  ROArrayColumn<Float> tsys(toff, "TSYS");
157  ROScalarColumn<Double> mjd(ton, "TIME");
158  ROScalarColumn<Double> integr(ton, "INTERVAL");
159  ROScalarColumn<String> srcn(ton, "SRCNAME");
160  ROArrayColumn<uInt> freqidc(ton, "FREQID");
161
162// Output Table cloned from input
163
164  SDMemTable* sdmt = new SDMemTable(*on, True);
165
166// Loop over rows
167
168  for (uInt i=0; i<nRows; i++) {
169     MaskedArray<Float> mon(on->rowAsMaskedArray(i));
170     MaskedArray<Float> moff(off->rowAsMaskedArray(i));
171     IPosition ipon = mon.shape();
172     IPosition ipoff = moff.shape();
173//
174     Array<Float> tsarr; 
175     tsys.get(i, tsarr);
176     if (ipon != ipoff && ipon != tsarr.shape()) {
177       throw(AipsError("on/off not conformant"));
178     }
179
180// Compute quotient
181
182     MaskedArray<Float> tmp = (mon-moff);
183     Array<Float> out(tmp.getArray());
184     out /= moff;
185     out *= tsarr;
186     Array<Bool> outflagsb = !(mon.getMask() && moff.getMask());
187     Array<uChar> outflags(outflagsb.shape());
188     convertArray(outflags,outflagsb);
189
190// Fill container for this row
191
192     SDContainer sc = on->getSDContainer();
193     sc.putTsys(tsarr);
194     sc.scanid = 0;
195     sc.putSpectrum(out);
196     sc.putFlags(outflags);
197
198// Put new row in output Table
199
200     sdmt->putSDContainer(sc);
201  }
202//
203  return CountedPtr<SDMemTable>(sdmt);
204}
205
206CountedPtr<SDMemTable>
207SDMath::multiply(const CountedPtr<SDMemTable>& in, Float factor)
208//
209// Multiply values by factor
210//
211{
212  SDMemTable* sdmt = new SDMemTable(*in);
213  Table t = sdmt->table();
214  ArrayColumn<Float> spec(t,"SPECTRA");
215
216  for (uInt i=0; i < t.nrow(); i++) {
217    MaskedArray<Float> marr(sdmt->rowAsMaskedArray(i));
218    marr *= factor;
219    spec.put(i, marr.getArray());
220  }
221  return CountedPtr<SDMemTable>(sdmt);
222}
223
224CountedPtr<SDMemTable>
225SDMath::add(const CountedPtr<SDMemTable>& in, Float offset)
226//
227// Add offset to values
228//
229{
230  SDMemTable* sdmt = new SDMemTable(*in);
231
232  Table t = sdmt->table();
233  ArrayColumn<Float> spec(t,"SPECTRA");
234
235  for (uInt i=0; i < t.nrow(); i++) {
236    MaskedArray<Float> marr(sdmt->rowAsMaskedArray(i));
237    marr += offset;
238    spec.put(i, marr.getArray());
239  }
240  return CountedPtr<SDMemTable>(sdmt);
241}
242
243
244CountedPtr<SDMemTable>
245SDMath::hanning(const CountedPtr<SDMemTable>& in)
246//
247// Hanning smooth each row
248// Should Tsys be smoothed ?
249//
250{
251  SDMemTable* sdmt = new SDMemTable(*in,True);
252
253// Loop over rows in Table
254
255  for (uInt ri=0; ri < in->nRow(); ++ri) {
256
257// Get data
258   
259    const MaskedArray<Float>& marr(in->rowAsMaskedArray(ri));
260    Array<Float> arr = marr.getArray();
261    Array<Bool> barr = marr.getMask();
262
263// Smooth along the channels axis
264
265    uInt axis = 3;
266    VectorIterator<Float> itData(arr, axis);
267    VectorIterator<Bool> itMask(barr, axis);
268    Vector<Float> outv;
269    Vector<Bool> outm;
270    while (!itData.pastEnd()) {
271       mathutil::hanning(outv, outm, itData.vector(), itMask.vector());
272       itData.vector() = outv;                                                 
273       itMask.vector() = outm;
274//
275       itData.next();
276       itMask.next();
277    }
278
279// Create and put back
280
281    Array<uChar> outflags(barr.shape());
282    convertArray(outflags,!barr);
283    SDContainer sc = in->getSDContainer(ri);
284    sc.putSpectrum(arr);
285    sc.putFlags(outflags);
286    sdmt->putSDContainer(sc);
287  }
288  return CountedPtr<SDMemTable>(sdmt);
289}
290
291
292
293CountedPtr<SDMemTable> SDMath::averages(const Block<CountedPtr<SDMemTable> >& in,
294                                        const Vector<Bool>& mask)
295//
296// Noise weighted averaging of spectra from many Tables.  Tables can have different
297// number of rows. 
298//
299{
300
301// Setup
302
303  const uInt axis = 3;                                 // Spectral axis
304  IPosition shp = in[0]->rowAsMaskedArray(0).shape();
305  Array<Float> arr(shp);
306  Array<Bool> barr(shp);
307  Double sumInterval = 0.0;
308  const Bool useMask = (mask.nelements() == shp(axis));
309
310// Create data accumulation MaskedArray. We accumulate for each
311// channel,if,pol,beam
312
313  Array<Float> zero(shp); zero=0.0;
314  Array<Bool> good(shp); good = True;
315  MaskedArray<Float> sum(zero,good);
316
317// Create accumulation Array for variance. We accumulate for
318// each if,pol,beam, but average over channel
319
320  const uInt nAxesSub = shp.nelements() - 1;
321  IPosition shp2(nAxesSub);
322  for (uInt i=0,j=0; i<(nAxesSub+1); i++) {
323     if (i!=axis) {
324       shp2(j) = shp(i);
325       j++;
326     }
327  }
328  Array<Float> sumSq(shp2);
329  sumSq = 0.0;
330  IPosition pos2(nAxesSub,0);                        // FOr indexing
331// 
332  Float fac = 1.0;
333  const uInt nTables = in.nelements();
334  for (uInt iTab=0; iTab<nTables; iTab++) {
335     const uInt nRows = in[iTab]->nRow();
336     sumInterval += nRows * in[iTab]->getInterval();   // Sum of time intervals
337//
338     for (uInt iRow=0; iRow<nRows; iRow++) {
339
340// Check conforms
341
342        IPosition shp2 = in[iTab]->rowAsMaskedArray(iRow).shape();
343        if (!shp.isEqual(shp2)) {
344           throw (AipsError("Shapes for all rows must be the same"));
345        }
346
347// Get data and deconstruct
348 
349        MaskedArray<Float> marr(in[iTab]->rowAsMaskedArray(iRow));
350        Array<Float>& arr = marr.getRWArray();                     // writable reference
351        const Array<Bool>& barr = marr.getMask();                  // RO reference
352
353// We are going to average the data, weighted by the noise for each
354// pol, beam and IF. So therefore we need to iterate through by
355// spectra (axis 3)
356
357        VectorIterator<Float> itData(arr, axis);
358        ReadOnlyVectorIterator<Bool> itMask(barr, axis);
359        while (!itData.pastEnd()) {
360
361// Make MaskedArray of Vector, optionally apply OTF mask, and find scaling factor
362
363          if (useMask) {
364             MaskedArray<Float> tmp(itData.vector(),mask&&itMask.vector());
365             fac = 1.0/variance(tmp);
366          } else {
367             MaskedArray<Float> tmp(itData.vector(),itMask.vector());
368             fac = 1.0/variance(tmp);
369          }
370
371// Scale data
372
373          itData.vector() *= fac;
374
375// Accumulate variance per if/pol/beam averaged over spectrum
376// This method to get pos2 from itData.pos() is only valid
377// because the spectral axis is the last one (so we can just
378// copy the first nAXesSub positions out)
379
380          pos2 = itData.pos().getFirst(nAxesSub);
381          sumSq(pos2) += fac;
382//
383          itData.next();
384          itMask.next();
385        }   
386
387// Accumulate sums
388
389       sum += marr;
390    }
391  }
392
393// Normalize by the sum of the 1/var. 
394
395  Array<Float>& data = sum.getRWArray();   
396  VectorIterator<Float> itData(data, axis);
397  while (!itData.pastEnd()) {
398     pos2 = itData.pos().getFirst(nAxesSub);           // See comments above
399     itData.vector() /= sumSq(pos2);
400     itData.next();
401  }   
402
403// Create and fill output
404
405  Array<uChar> outflags(shp);
406  convertArray(outflags,!(sum.getMask()));
407//
408  SDContainer sc = in[0]->getSDContainer();     // CLone from first container of first Table
409  sc.putSpectrum(data);
410  sc.putFlags(outflags);
411  sc.interval = sumInterval;
412//
413  SDMemTable* sdmt = new SDMemTable(*in[0],True);  // CLone from first Table
414  sdmt->putSDContainer(sc);
415  return CountedPtr<SDMemTable>(sdmt);
416}
417
418
419CountedPtr<SDMemTable>
420SDMath::averagePol(const CountedPtr<SDMemTable>& in,
421                   const Vector<Bool>& mask)
422{
423   const uInt nRows = in->nRow();
424   const uInt axis = 3;                        // Spectrum
425   const IPosition axes(2, 2, 3);              // pol-channel plane
426
427// Create output Table
428
429  SDMemTable* sdmt = new SDMemTable(*in, True);
430
431// Loop over rows
432
433   for (uInt iRow=0; iRow<nRows; iRow++) {
434
435// Get data for this row
436
437      MaskedArray<Float> marr(in->rowAsMaskedArray(iRow));
438      Array<Float>& arr = marr.getRWArray();
439      const Array<Bool>& barr = marr.getMask();
440//
441      IPosition shp = marr.shape();
442      const Bool useMask = (mask.nelements() == shp(axis));
443      const uInt nChan = shp(axis);
444
445// Make iterators to iterate by pol-channel planes
446
447     ArrayIterator<Float> itDataPlane(arr, axes);
448     ReadOnlyArrayIterator<Bool> itMaskPlane(barr, axes);
449
450// Accumulations
451
452     Float fac = 0.0;
453     Vector<Float> vecSum(nChan,0.0);
454
455// Iterate by plane
456
457     while (!itDataPlane.pastEnd()) {
458
459// Iterate through pol-channel plane by spectrum
460
461        Vector<Float> t1(nChan); t1 = 0.0;
462        Vector<Bool> t2(nChan); t2 = True;
463        MaskedArray<Float> vecSum(t1,t2);
464        Float varSum = 0.0;
465        {
466           ReadOnlyVectorIterator<Float> itDataVec(itDataPlane.array(), 1);
467           ReadOnlyVectorIterator<Bool> itMaskVec(itMaskPlane.array(), 1);
468           while (!itDataVec.pastEnd()) {     
469
470// Create MA of data & mask (optionally including OTF mask) and  get variance
471
472              if (useMask) {
473                 const MaskedArray<Float> spec(itDataVec.vector(),mask&&itMaskVec.vector());
474                 fac = 1.0 / variance(spec);
475              } else {
476                 const MaskedArray<Float> spec(itDataVec.vector(),itMaskVec.vector());
477                 fac = 1.0 / variance(spec);
478              }
479
480// Normalize spectrum (without OTF mask) and accumulate
481
482              const MaskedArray<Float> spec(fac*itDataVec.vector(), itMaskVec.vector());
483              vecSum += spec;
484              varSum += fac;
485
486// Next
487
488              itDataVec.next();
489              itMaskVec.next();
490           }
491        }
492
493// Normalize summed spectrum
494
495        vecSum /= varSum;
496
497// We have formed the weighted averaged spectrum from all polarizations
498// for this beam and IF.  Now replicate the spectrum to all polarizations
499
500        {
501           VectorIterator<Float> itDataVec(itDataPlane.array(), 1);  // Writes back into 'arr'
502           const Vector<Float>& vecSumData = vecSum.getArray();      // It *is* a Vector
503//
504           while (!itDataVec.pastEnd()) {     
505              itDataVec.vector() = vecSumData;
506              itDataVec.next();
507           }
508        }
509
510// Step to next beam/IF combination
511
512        itDataPlane.next();
513        itMaskPlane.next();
514     }
515
516// Generate output container and write it to output table
517
518     SDContainer sc = in->getSDContainer();
519     Array<uChar> outflags(barr.shape());
520     convertArray(outflags,!barr);
521     sc.putSpectrum(arr);
522     sc.putFlags(outflags);
523     sdmt->putSDContainer(sc);
524   }
525//
526  return CountedPtr<SDMemTable>(sdmt);
527}
528
529
530CountedPtr<SDMemTable> SDMath::bin(const CountedPtr<SDMemTable>& in,
531                                   Int width)
532{
533  SDHeader sh = in->getSDHeader();
534  SDMemTable* sdmt = new SDMemTable(*in,True);
535
536// Bin up SpectralCoordinates
537
538  IPosition factors(1);
539  factors(0) = width;
540  for (uInt j=0; j<in->nCoordinates(); ++j) {
541    CoordinateSystem cSys;
542    cSys.addCoordinate(in->getCoordinate(j));
543    CoordinateSystem cSysBin =
544      CoordinateUtil::makeBinnedCoordinateSystem (factors, cSys, False);
545//
546    SpectralCoordinate sCBin = cSysBin.spectralCoordinate(0);
547    sdmt->setCoordinate(sCBin, j);
548  }
549
550// Use RebinLattice to find shape
551
552  IPosition shapeIn(1,sh.nchan);
553  IPosition shapeOut = RebinLattice<Float>::rebinShape (shapeIn, factors);
554  sh.nchan = shapeOut(0);
555  sdmt->putSDHeader(sh);
556
557
558// Loop over rows and bin along channel axis
559 
560  const uInt axis = 3;
561  for (uInt i=0; i < in->nRow(); ++i) {
562    SDContainer sc = in->getSDContainer(i);
563//
564    Array<Float> tSys(sc.getTsys());                           // Get it out before sc changes shape
565
566// Bin up spectrum
567
568    MaskedArray<Float> marr(in->rowAsMaskedArray(i));
569    MaskedArray<Float> marrout;
570    LatticeUtilities::bin(marrout, marr, axis, width);
571
572// Put back the binned data and flags
573
574    IPosition ip2 = marrout.shape();
575    sc.resize(ip2);
576    sc.putSpectrum(marrout.getArray());
577//
578    Array<uChar> outflags(ip2);
579    convertArray(outflags,!(marrout.getMask()));
580    sc.putFlags(outflags);
581
582// Bin up Tsys. 
583
584    Array<Bool> allGood(tSys.shape(),True);
585    MaskedArray<Float> tSysIn(tSys, allGood, True);
586//
587    MaskedArray<Float> tSysOut;   
588    LatticeUtilities::bin(tSysOut, tSysIn, axis, width);
589    sc.putTsys(tSysOut.getArray());
590    sdmt->putSDContainer(sc);
591  }
592  return CountedPtr<SDMemTable>(sdmt);
593}
594
595
596
597std::vector<float> SDMath::statistic (const CountedPtr<SDMemTable>& in,
598                                       const std::vector<bool>& mask,
599                                       const std::string& which)
600//
601// Perhaps iteration over pol/beam/if should be in here
602// and inside the nrow iteration ?
603//
604{
605  const uInt nRow = in->nRow();
606  std::vector<float> result(nRow);
607  Vector<Bool> msk(mask);
608
609// Specify cursor location
610
611  uInt i = in->getBeam();
612  uInt j = in->getIF();
613  uInt k = in->getPol();
614  IPosition start(4,i,j,k,0);
615  IPosition end(4,i,j,k,in->nChan()-1);
616
617// Loop over rows
618
619  const uInt nEl = msk.nelements();
620  for (uInt ii=0; ii < in->nRow(); ++ii) {
621
622// Get row and deconstruct
623
624     MaskedArray<Float> marr(in->rowAsMaskedArray(ii));
625     Array<Float> arr = marr.getArray();
626     Array<Bool> barr = marr.getMask();
627
628// Access desired piece of data
629
630     Array<Float> v((arr(start,end)).nonDegenerate());
631     Array<Bool> m((barr(start,end)).nonDegenerate());
632
633// Apply OTF mask
634
635     MaskedArray<Float> tmp;
636     if (m.nelements()==nEl) {
637       tmp.setData(v,m&&msk);
638     } else {
639       tmp.setData(v,m);
640     }
641
642// Get statistic
643
644     result[ii] = SDMath::theStatistic(which, tmp);
645  }
646//
647  return result;
648}
649
650
651float SDMath::theStatistic(const std::string& which,  const casa::MaskedArray<Float>& data)
652{
653   String str(which);
654   str.upcase();
655   if (str.contains(String("MIN"))) {
656      return min(data);
657   } else if (str.contains(String("MAX"))) {
658      return max(data);
659   } else if (str.contains(String("SUMSQ"))) {
660      return sumsquares(data);
661   } else if (str.contains(String("SUM"))) {
662      return sum(data);
663   } else if (str.contains(String("MEAN"))) {
664      return mean(data);
665   } else if (str.contains(String("VAR"))) {
666      return variance(data);
667   } else if (str.contains(String("STDDEV"))) {
668      return stddev(data);
669   } else if (str.contains(String("AVDEV"))) {
670      return avdev(data);
671   } else if (str.contains(String("RMS"))) {
672      uInt n = data.nelementsValid();
673      return sqrt(sumsquares(data)/n);
674   } else if (str.contains(String("MED"))) {
675      return median(data);
676   }
677}
Note: See TracBrowser for help on using the repository browser.