LCOV - code coverage report
Current view: top level - mstransform/TVI - StatWtTVI.cc (source / functions) Hit Total Coverage
Test: casacpp_coverage.info Lines: 0 584 0.0 %
Date: 2024-10-10 11:40:37 Functions: 0 30 0.0 %

          Line data    Source code
       1             : //# StatWtTVI.cc: This file contains the implementation of the StatWtTVI class.
       2             : //#
       3             : //#  CASA - Common Astronomy Software Applications (http://casa.nrao.edu/)
       4             : //#  Copyright (C) Associated Universities, Inc. Washington DC, USA 2011, All
       5             : //#  rights reserved.
       6             : //#  Copyright (C) European Southern Observatory, 2011, All rights reserved.
       7             : //#
       8             : //#  This library is free software; you can redistribute it and/or
       9             : //#  modify it under the terms of the GNU Lesser General Public
      10             : //#  License as published by the Free software Foundation; either
      11             : //#  version 2.1 of the License, or (at your option) any later version.
      12             : //#
      13             : //#  This library is distributed in the hope that it will be useful,
      14             : //#  but WITHOUT ANY WARRANTY, without even the implied warranty of
      15             : //#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
      16             : //#  Lesser General Public License for more details.
      17             : //#
      18             : //#  You should have received a copy of the GNU Lesser General Public
      19             : //#  License along with this library; if not, write to the Free Software
      20             : //#  Foundation, Inc., 59 Temple Place, Suite 330, Boston,
      21             : //#  MA 02111-1307  USA
      22             : 
      23             : #include <mstransform/TVI/StatWtTVI.h>
      24             : 
      25             : #include <casacore/casa/Arrays/ArrayLogical.h>
      26             : #include <casacore/casa/Quanta/QuantumHolder.h>
      27             : #include <casacore/ms/MSOper/MSMetaData.h>
      28             : #include <casacore/tables/Tables/ArrColDesc.h>
      29             : 
      30             : #include <mstransform/TVI/StatWtClassicalDataAggregator.h>
      31             : #include <mstransform/TVI/StatWtFloatingWindowDataAggregator.h>
      32             : #include <mstransform/TVI/StatWtVarianceAndWeightCalculator.h>
      33             : 
      34             : #ifdef _OPENMP
      35             : #include <omp.h>
      36             : #endif
      37             : 
      38             : #include <iomanip>
      39             : 
      40             : using namespace casacore;
      41             : using namespace casac;
      42             : 
      43             : namespace casa { 
      44             : namespace vi { 
      45             : 
      46             : const String StatWtTVI::CHANBIN = "stchanbin";
      47             : 
      48           0 : StatWtTVI::StatWtTVI(ViImplementation2 * inputVii, const Record &configuration)
      49           0 :     : TransformingVi2 (inputVii) {
      50             :         // Parse and check configuration parameters
      51             :         // Note: if a constructor finishes by throwing an exception, the memory
      52             :         // associated with the object itself is cleaned up there is no memory leak.
      53           0 :     ThrowIf(
      54             :         ! _parseConfiguration(configuration),
      55             :         "Error parsing StatWtTVI configuration"
      56             :     );
      57           0 :     LogIO log(LogOrigin("StatWtTVI", __func__));
      58           0 :     log << LogIO::NORMAL << "Using " << StatWtTypes::asString(_column)
      59           0 :         << " to compute weights" << LogIO::POST;
      60             :     // FIXME when the TVI framework has methods to
      61             :     // check for metadata, like the existence of
      62             :     // columns, remove references to the original MS
      63           0 :     const auto& origMS = ms();
      64             :     // FIXME uses original MS explicitly
      65           0 :     ThrowIf(
      66             :         (_column == StatWtTypes::CORRECTED || _column == StatWtTypes::RESIDUAL)
      67             :         && ! origMS.isColumn(MSMainEnums::CORRECTED_DATA),
      68             :         "StatWtTVI requires the MS to have a CORRECTED_DATA column. This MS "
      69             :         "does not"
      70             :     );
      71             :     // FIXME uses original MS explicitly
      72           0 :     ThrowIf(
      73             :         (_column == StatWtTypes::DATA || _column == StatWtTypes::RESIDUAL_DATA)
      74             :         && ! origMS.isColumn(MSMainEnums::DATA),
      75             :         "StatWtTVI requires the MS to have a DATA column. This MS does not"
      76             :     );
      77           0 :     _mustComputeSigma = (
      78           0 :         _column == StatWtTypes::DATA || _column == StatWtTypes::RESIDUAL_DATA
      79             :     );
      80             :     // FIXME uses original MS explicitly
      81           0 :     _updateWeight = ! _mustComputeSigma 
      82           0 :         || (_mustComputeSigma && ! origMS.isColumn(MSMainEnums::CORRECTED_DATA));
      83           0 :     _noModel = (
      84           0 :             _column == StatWtTypes::RESIDUAL || _column == StatWtTypes::RESIDUAL_DATA
      85           0 :         ) && ! origMS.isColumn(MSMainEnums::MODEL_DATA)
      86           0 :         && ! origMS.source().isColumn(MSSourceEnums::SOURCE_MODEL);
      87             :         // Initialize attached VisBuffer
      88           0 :         setVisBuffer(createAttachedVisBuffer(VbRekeyable));
      89           0 : }
      90             : 
      91           0 : StatWtTVI::~StatWtTVI() {}
      92             : 
      93           0 : Bool StatWtTVI::_parseConfiguration(const Record& config) {
      94           0 :      String field = CHANBIN;
      95           0 :     if (config.isDefined(field)) {
      96             :         // channel binning
      97           0 :         auto fieldNum = config.fieldNumber(field);
      98           0 :         switch (config.type(fieldNum)) {
      99           0 :         case DataType::TpArrayBool:
     100             :             // because this is the actual default variant type, no matter
     101             :             // what is specified in the xml
     102           0 :             ThrowIf(
     103             :                 ! config.asArrayBool(field).empty(),
     104             :                 "Unsupported data type for " + field
     105             :             );
     106           0 :             _setDefaultChanBinMap();
     107           0 :             break;
     108           0 :         case DataType::TpInt:
     109             :             Int binWidth;
     110           0 :             config.get(CHANBIN, binWidth);
     111           0 :             _setChanBinMap(binWidth);
     112           0 :             break;
     113           0 :         case DataType::TpString:
     114             :         {
     115           0 :             auto chanbin = config.asString(field);
     116           0 :             if (chanbin == "spw") {
     117             :                 // bin using entire spws
     118           0 :                 _setDefaultChanBinMap();
     119           0 :                 break;
     120             :             }
     121             :             else {
     122           0 :                 QuantumHolder qh(casaQuantity(chanbin));
     123           0 :                 _setChanBinMap(qh.asQuantity());
     124           0 :             }
     125           0 :             break;
     126           0 :         }
     127           0 :         default:
     128           0 :             ThrowCc("Unsupported data type for " + field);
     129             :         }
     130             :     }
     131             :     else {
     132           0 :         _setDefaultChanBinMap();
     133             :     }
     134           0 :     field = "minsamp";
     135           0 :     if (config.isDefined(field)) {
     136           0 :         config.get(field, _minSamp);
     137           0 :         ThrowIf(_minSamp < 2, "Minimum size of sample must be >= 2.");
     138             :     }
     139           0 :     field = "combine";
     140           0 :     if (config.isDefined(field)) {
     141           0 :         ThrowIf(
     142             :             config.type(config.fieldNumber(field)) != TpString,
     143             :             "Unsupported data type for combine"
     144             :         );
     145           0 :         _combineCorr = config.asString(field).contains("corr");
     146             :     }
     147           0 :     field = "wtrange";
     148           0 :     if (config.isDefined(field)) {
     149           0 :         ThrowIf(
     150             :             config.type(config.fieldNumber(field)) != TpArrayDouble,
     151             :             "Unsupported type for field '" + field + "'"
     152             :         );
     153           0 :         auto myrange = config.asArrayDouble(field);
     154           0 :         if (! myrange.empty()) {
     155           0 :             ThrowIf(
     156             :                 myrange.size() != 2,
     157             :                 "Array specified in '" + field
     158             :                 + "' must have exactly two values"
     159             :             );
     160           0 :             ThrowIf(
     161             :                 casacore::anyLT(myrange, 0.0),
     162             :                 "Both values specified in '" + field
     163             :                 + "' array must be non-negative"
     164             :             );
     165             :             // order the values in case they aren't ordered
     166           0 :             std::set<Double> rangeset(myrange.begin(), myrange.end());
     167           0 :             ThrowIf(
     168             :                 rangeset.size() == 1, "Values specified in '" + field
     169             :                 + "' array must be unique"
     170             :             );
     171           0 :             auto iter = rangeset.begin();
     172           0 :             auto first = *iter;
     173           0 :             auto second = *(++iter);
     174           0 :             _wtrange.reset(new std::pair<Double, Double>(first, second));
     175           0 :         }
     176           0 :     }
     177           0 :     auto excludeChans = False;
     178           0 :     field = "excludechans";
     179           0 :     if (config.isDefined(field)) {
     180           0 :         ThrowIf(
     181             :             config.type(config.fieldNumber(field)) != TpBool,
     182             :             "Unsupported type for field '" + field + "'"
     183             :         );
     184           0 :         excludeChans = config.asBool(field);
     185             :     }
     186           0 :     field = "fitspw";
     187           0 :     if (config.isDefined(field)) {
     188           0 :         ThrowIf(
     189             :             config.type(config.fieldNumber(field)) != TpString,
     190             :             "Unsupported type for field '" + field + "'"
     191             :         );
     192           0 :         auto val = config.asString(field);
     193           0 :         if (! val.empty()) {
     194             :             // FIXME references underlying MS
     195           0 :             const auto& myms = ms();
     196           0 :             MSSelection sel(myms);
     197           0 :             sel.setSpwExpr(val);
     198           0 :             auto chans = sel.getChanList();
     199           0 :             auto nrows = chans.nrow();
     200           0 :             MSMetaData md(&myms, 50);
     201           0 :             auto nchans = md.nChans();
     202           0 :             IPosition start(3, 0);
     203           0 :             IPosition stop(3, 0);
     204           0 :             IPosition step(3, 1);
     205           0 :             for (size_t i=0; i<nrows; ++i) {
     206           0 :                 auto row = chans.row(i);
     207           0 :                 const auto& spw = row[0];
     208           0 :                 if (_chanSelFlags.find(spw) == _chanSelFlags.end()) {
     209           0 :                     _chanSelFlags[spw]
     210           0 :                         = Cube<Bool>(1, nchans[spw], 1, ! excludeChans);
     211             :                 }
     212           0 :                 start[1] = row[1];
     213           0 :                 ThrowIf(
     214             :                     start[1] < 0, "Invalid channel selection in spw "
     215             :                     + String::toString(spw))
     216             :                 ;
     217           0 :                 stop[1] = row[2];
     218           0 :                 step[1] = row[3];
     219           0 :                 Slicer slice(start, stop, step, Slicer::endIsLast);
     220           0 :                 _chanSelFlags[spw](slice) = excludeChans;
     221           0 :             }
     222           0 :         }
     223           0 :     }
     224           0 :     field = "datacolumn";
     225           0 :     if (config.isDefined(field)) {
     226           0 :         ThrowIf(
     227             :             config.type(config.fieldNumber(field)) != TpString,
     228             :             "Unsupported type for field '" + field + "'"
     229             :         );
     230           0 :         auto val = config.asString(field);
     231           0 :         if (! val.empty()) {
     232           0 :             val.downcase();
     233           0 :             ThrowIf (
     234             :                 ! (
     235             :                     val.startsWith("c") || val.startsWith("d")
     236             :                     || val.startsWith("residual") || val.startsWith("residual_")
     237             :                 ),
     238             :                 "Unsupported value for " + field + ": " + val
     239             :             );
     240           0 :             _column = val.startsWith("c") ? StatWtTypes::CORRECTED
     241           0 :                 : val.startsWith("d") ? StatWtTypes::DATA
     242           0 :                 : val.startsWith("residual_") ? StatWtTypes::RESIDUAL_DATA
     243             :                 : StatWtTypes::RESIDUAL;
     244             : 
     245             :         }
     246           0 :     }
     247           0 :     field = "slidetimebin";
     248           0 :     ThrowIf(
     249             :         ! config.isDefined(field), "Config param " + field + " must be defined"
     250             :     );
     251           0 :     ThrowIf(
     252             :         config.type(config.fieldNumber(field)) != TpBool,
     253             :         "Unsupported type for field '" + field + "'"
     254             :     );
     255           0 :     _timeBlockProcessing = ! config.asBool(field);
     256           0 :     field = "timebin";
     257           0 :     ThrowIf(
     258             :         ! config.isDefined(field), "Config param " + field + " must be defined"
     259             :     );
     260           0 :     auto mytype = config.type(config.fieldNumber(field));
     261           0 :     ThrowIf(
     262             :         ! (
     263             :             mytype == TpString || mytype == TpDouble
     264             :             || mytype == TpInt
     265             :         ),
     266             :         "Unsupported type for field '" + field + "'"
     267             :     );
     268           0 :     switch(mytype) {
     269           0 :     case TpDouble: {
     270           0 :         _binWidthInSeconds.reset(new Double(config.asDouble(field)));
     271           0 :         break;
     272             :     }
     273           0 :     case TpInt:
     274           0 :         _nTimeStampsInBin.reset(new Int(config.asInt(field)));
     275           0 :         ThrowIf(
     276             :             *_nTimeStampsInBin <= 0,
     277             :             "Logic Error: nTimeStamps must be positive"
     278             :         );
     279           0 :         break;
     280           0 :     case TpString: {
     281           0 :         QuantumHolder qh(casaQuantity(config.asString(field)));
     282           0 :         _binWidthInSeconds.reset(
     283           0 :             new Double(getTimeBinWidthInSec(qh.asQuantity()))
     284             :         );
     285           0 :         break;
     286           0 :     }
     287           0 :     default:
     288           0 :         ThrowCc("Logic Error: Unhandled type for timebin");
     289             : 
     290             :     }
     291           0 :     _doClassicVIVB = _binWidthInSeconds && _timeBlockProcessing;
     292           0 :     _configureStatAlg(config);
     293           0 :     if (_doClassicVIVB) {
     294           0 :         _dataAggregator.reset(
     295             :             new StatWtClassicalDataAggregator(
     296           0 :                 getVii(), _chanBins, _samples, _column, _noModel, _chanSelFlags,
     297           0 :                 _wtStats, _wtrange, _combineCorr, _statAlg, _minSamp
     298           0 :             )
     299             :         );
     300             :     }
     301             :     else {
     302           0 :         _dataAggregator.reset(
     303             :                new StatWtFloatingWindowDataAggregator(
     304           0 :                 getVii(), _chanBins, _samples, _column, _noModel, _chanSelFlags,
     305           0 :                 _combineCorr, _wtStats, _wtrange, _binWidthInSeconds,
     306           0 :                 _nTimeStampsInBin, _timeBlockProcessing, _statAlg, _minSamp
     307           0 :             )
     308             :         );
     309             :     }
     310           0 :     _dataAggregator->setMustComputeWtSp(_mustComputeWtSp);
     311           0 :     return True;
     312           0 : }
     313             : 
     314           0 : void StatWtTVI::_configureStatAlg(const Record& config) {
     315           0 :     String field = "statalg";
     316           0 :     if (config.isDefined(field)) {
     317           0 :         ThrowIf(
     318             :             config.type(config.fieldNumber(field)) != TpString,
     319             :             "Unsupported type for field '" + field + "'"
     320             :         );
     321           0 :         auto alg = config.asString(field);
     322           0 :         alg.downcase();
     323           0 :         if (alg.startsWith("cl")) {
     324           0 :             _statAlg.reset(
     325             :                 new ClassicalStatistics<
     326             :                     Double, Array<Float>::const_iterator,
     327             :                     Array<Bool>::const_iterator, Array<Double>::const_iterator
     328           0 :                 >()
     329             :             );
     330             :         }
     331             :         else {
     332             :             casacore::StatisticsAlgorithmFactory<
     333             :                 Double, Array<Float>::const_iterator,
     334             :                 Array<Bool>::const_iterator, Array<Double>::const_iterator
     335           0 :             > saf;
     336           0 :             if (alg.startsWith("ch")) {
     337           0 :                 Int maxiter = -1;
     338           0 :                 field = "maxiter";
     339           0 :                 if (config.isDefined(field)) {
     340           0 :                     ThrowIf(
     341             :                         config.type(config.fieldNumber(field)) != TpInt,
     342             :                         "Unsupported type for field '" + field + "'"
     343             :                     );
     344           0 :                     maxiter = config.asInt(field);
     345             :                 }
     346           0 :                 Double zscore = -1;
     347           0 :                 field = "zscore";
     348           0 :                 if (config.isDefined(field)) {
     349           0 :                     ThrowIf(
     350             :                         config.type(config.fieldNumber(field)) != TpDouble,
     351             :                         "Unsupported type for field '" + field + "'"
     352             :                     );
     353           0 :                     zscore = config.asDouble(field);
     354             :                 }
     355           0 :                 saf.configureChauvenet(zscore, maxiter);
     356             :             }
     357           0 :             else if (alg.startsWith("f")) {
     358           0 :                 auto center = FitToHalfStatisticsData::CMEAN;
     359           0 :                 field = "center";
     360           0 :                 if (config.isDefined(field)) {
     361           0 :                     ThrowIf(
     362             :                         config.type(config.fieldNumber(field)) != TpString,
     363             :                         "Unsupported type for field '" + field + "'"
     364             :                     );
     365           0 :                     auto cs = config.asString(field);
     366           0 :                     cs.downcase();
     367           0 :                     if (cs == "mean") {
     368           0 :                         center = FitToHalfStatisticsData::CMEAN;
     369             :                     }
     370           0 :                     else if (cs == "median") {
     371           0 :                         center = FitToHalfStatisticsData::CMEDIAN;
     372             :                     }
     373           0 :                     else if (cs == "zero") {
     374           0 :                         center = FitToHalfStatisticsData::CVALUE;
     375             :                     }
     376             :                     else {
     377           0 :                         ThrowCc("Unsupported value for '" + field + "'");
     378             :                     }
     379           0 :                 }
     380           0 :                 field = "lside";
     381           0 :                 auto ud = FitToHalfStatisticsData::LE_CENTER;
     382           0 :                 if (config.isDefined(field)) {
     383           0 :                     ThrowIf(
     384             :                         config.type(config.fieldNumber(field)) != TpBool,
     385             :                         "Unsupported type for field '" + field + "'"
     386             :                     );
     387           0 :                     ud = config.asBool(field)
     388           0 :                         ? FitToHalfStatisticsData::LE_CENTER
     389             :                         : FitToHalfStatisticsData::GE_CENTER;
     390             :                 }
     391           0 :                 saf.configureFitToHalf(center, ud, 0);
     392             :             }
     393           0 :             else if (alg.startsWith("h")) {
     394           0 :                 Double fence = -1;
     395           0 :                 field = "fence";
     396           0 :                 if (config.isDefined(field)) {
     397           0 :                     ThrowIf(
     398             :                         config.type(config.fieldNumber(field)) != TpDouble,
     399             :                         "Unsupported type for field '" + field + "'"
     400             :                     );
     401           0 :                     fence = config.asDouble(field);
     402             :                 }
     403           0 :                 saf.configureHingesFences(fence);
     404             :             }
     405             :             else {
     406           0 :                 ThrowCc("Unsupported value for 'statalg'");
     407             :             }
     408             :             // clone needed for CountedPtr -> shared_ptr hand off
     409           0 :             _statAlg.reset(saf.createStatsAlgorithm()->clone());
     410           0 :         }
     411           0 :     }
     412             :     else {
     413           0 :         _statAlg.reset(
     414             :             new ClassicalStatistics<
     415             :                 Double, Array<Float>::const_iterator,
     416             :                 Array<Bool>::const_iterator, Array<Double>::const_iterator
     417           0 :             >());
     418             :     }
     419           0 :     std::set<StatisticsData::STATS> stats {StatisticsData::VARIANCE};
     420           0 :     _statAlg->setStatsToCalculate(stats);
     421             :     // also configure the _wtStats object here
     422             :     // FIXME? Does not include exposure weighting
     423           0 :     _wtStats.reset(
     424             :         new ClassicalStatistics<
     425             :             Double, Array<Float>::const_iterator,
     426             :             Array<Bool>::const_iterator
     427           0 :         >()
     428             :     );
     429           0 :     stats.insert(StatisticsData::MEAN);
     430           0 :     _wtStats->setStatsToCalculate(stats);
     431           0 :     _wtStats->setCalculateAsAdded(True);
     432           0 : }
     433             : 
     434           0 : void StatWtTVI::_logUsedChannels() const {
     435             :     // FIXME uses underlying MS
     436           0 :     MSMetaData msmd(&ms(), 100.0);
     437           0 :     const auto nchan = msmd.nChans();
     438           0 :     LogIO log(LogOrigin("StatWtTVI", __func__));
     439           0 :     log << LogIO::NORMAL << "Weights are being computed using ";
     440           0 :     const auto cend = _chanSelFlags.cend();
     441           0 :     const auto nspw = _samples->size();
     442           0 :     uInt spwCount = 0;
     443           0 :     for (const auto& kv: *_samples) {
     444           0 :         const auto spw = kv.first;
     445           0 :         log << "SPW " << spw << ", channels ";
     446           0 :         const auto flagCube = _chanSelFlags.find(spw);
     447           0 :         if (flagCube == cend) {
     448           0 :             log << "0~" << (nchan[spw] - 1);
     449             :         }
     450             :         else {
     451           0 :             vector<pair<uInt, uInt>> startEnd;
     452           0 :             const auto flags = flagCube->second.tovector();
     453           0 :             bool started = false;
     454           0 :             std::unique_ptr<pair<uInt, uInt>> curPair;
     455           0 :             for (uInt j=0; j<nchan[spw]; ++j) {
     456           0 :                 if (started) {
     457           0 :                     if (flags[j]) {
     458             :                         // found a bad channel, end current range
     459           0 :                         startEnd.push_back(*curPair);
     460           0 :                         started = false;
     461             :                     }
     462             :                     else {
     463             :                         // found a "good" channel, update end of current range
     464           0 :                         curPair->second = j;
     465             :                     }
     466             :                 }
     467           0 :                 else if (! flags[j]) {
     468             :                     // found a good channel, start new range
     469           0 :                     started = true;
     470           0 :                     curPair.reset(new pair<uInt, uInt>(j, j));
     471             :                 }
     472             :             }
     473           0 :             if (curPair) {
     474           0 :                 if (started) {
     475             :                     // The last pair won't get added inside the previous loop, 
     476             :                     // so add it here
     477           0 :                     startEnd.push_back(*curPair);
     478             :                 }
     479           0 :                 auto nPairs = startEnd.size();
     480           0 :                 for (uInt i=0; i<nPairs; ++i) {
     481           0 :                     log  << startEnd[i].first << "~" << startEnd[i].second;
     482           0 :                     if (i < nPairs - 1) {
     483           0 :                         log << ", ";
     484             :                     }
     485             :                 }
     486             :             }
     487             :             else {
     488             :                 // if the pointer never got set, all the channels are bad
     489           0 :                 log << "no channels";
     490             :             }
     491           0 :         }
     492           0 :         if (spwCount < (nspw - 1)) {
     493           0 :             log << ";";
     494             :         }
     495           0 :         ++spwCount;
     496             :     }
     497           0 :     log << LogIO::POST;
     498           0 : }
     499             : 
     500           0 : void StatWtTVI::_setChanBinMap(const casacore::Quantity& binWidth) {
     501           0 :     if (! binWidth.isConform(Unit("Hz"))) {
     502           0 :         ostringstream oss;
     503             :         oss << "If specified as a quantity, channel bin width must have "
     504           0 :             << "frequency units. " << binWidth << " does not.";
     505           0 :         ThrowCc(oss.str());
     506           0 :     }
     507           0 :     ThrowIf(binWidth.getValue() <= 0, "channel bin width must be positive");
     508           0 :     MSMetaData msmd(&ms(), 100.0);
     509           0 :     auto chanFreqs = msmd.getChanFreqs();
     510           0 :     auto nspw = chanFreqs.size();
     511           0 :     auto binWidthHz = binWidth.getValue("Hz");
     512           0 :     for (uInt i=0; i<nspw; ++i) {
     513           0 :         auto cfs = chanFreqs[i].getValue("Hz");
     514           0 :         auto citer = cfs.begin();
     515           0 :         auto cend = cfs.end();
     516           0 :         StatWtTypes::ChanBin bin;
     517           0 :         bin.start = 0;
     518           0 :         bin.end = 0;
     519           0 :         uInt chanNum = 0;
     520           0 :         auto startFreq = *citer;
     521           0 :         auto nchan = cfs.size();
     522           0 :         for (; citer!=cend; ++citer, ++chanNum) {
     523             :             // both could be true, in which case both conditionals
     524             :             // must be executed
     525           0 :             if (abs(*citer - startFreq) > binWidthHz) {
     526             :                 // add bin to list
     527           0 :                 bin.end = chanNum - 1;
     528           0 :                 _chanBins[i].push_back(bin);
     529           0 :                 bin.start = chanNum;
     530           0 :                 startFreq = *citer;
     531             :             }
     532           0 :             if (chanNum + 1 == nchan) {
     533             :                 // add last bin
     534           0 :                 bin.end = chanNum;
     535           0 :                 _chanBins[i].push_back(bin);
     536             :             }
     537             :         }
     538           0 :     }
     539             :     // weight spectrum must be computed
     540           0 :     _mustComputeWtSp.reset(new Bool(True));
     541           0 : }
     542             : 
     543           0 : void StatWtTVI::_setChanBinMap(Int binWidth) {
     544           0 :     ThrowIf(binWidth < 1, "Channel bin width must be positive");
     545           0 :     MSMetaData msmd(&ms(), 100.0);
     546           0 :     auto nchans = msmd.nChans();
     547           0 :     auto nspw = nchans.size();
     548           0 :     StatWtTypes::ChanBin bin;
     549           0 :     for (uInt i=0; i<nspw; ++i) {
     550           0 :         auto lastChan = nchans[i]-1;
     551           0 :         for (uInt j=0; j<nchans[i]; j += binWidth) {
     552           0 :             bin.start = j;
     553           0 :             bin.end = min(j+binWidth-1, lastChan);
     554           0 :             _chanBins[i].push_back(bin);
     555             :         }
     556             :     }
     557             :     // weight spectrum must be computed
     558           0 :     _mustComputeWtSp.reset(new Bool(True));
     559           0 : }
     560             : 
     561           0 : void StatWtTVI::_setDefaultChanBinMap() {
     562             :     // FIXME uses underlying MS
     563           0 :     MSMetaData msmd(&ms(), 0.0);
     564           0 :     auto nchans = msmd.nChans();
     565           0 :     auto niter = nchans.begin();
     566           0 :     auto nend = nchans.end();
     567           0 :     Int i = 0;
     568           0 :     StatWtTypes::ChanBin bin;
     569           0 :     bin.start = 0;
     570           0 :     for (; niter!=nend; ++niter, ++i) {
     571           0 :         bin.end = *niter - 1;
     572           0 :         _chanBins[i].push_back(bin);
     573             :     }
     574           0 : }
     575             : 
     576           0 : Double StatWtTVI::getTimeBinWidthInSec(const casacore::Quantity& binWidth) {
     577           0 :     ThrowIf(
     578             :         ! binWidth.isConform(Unit("s")),
     579             :         "Time bin width unit must be a unit of time"
     580             :     );
     581           0 :     auto v = binWidth.getValue("s");
     582           0 :     checkTimeBinWidth(v);
     583           0 :     return v;
     584             : }
     585             : 
     586           0 : void StatWtTVI::checkTimeBinWidth(Double binWidth) {
     587           0 :     ThrowIf(binWidth <= 0, "time bin width must be positive");
     588           0 : }
     589             : 
     590           0 : void StatWtTVI::sigmaSpectrum(Cube<Float>& sigmaSp) const {
     591           0 :     if (_mustComputeSigma) {
     592             :         {
     593           0 :             Cube<Float> wtsp;
     594             :             // this computes _newWtsp, ignore wtsp
     595           0 :             weightSpectrum(wtsp);
     596           0 :         }
     597           0 :         sigmaSp = Float(1.0)/sqrt(_newWtSp);
     598           0 :         if (anyEQ(_newWtSp, Float(0))) {
     599           0 :             auto iter = sigmaSp.begin();
     600           0 :             auto end = sigmaSp.end();
     601           0 :             auto witer = _newWtSp.cbegin();
     602           0 :             for ( ; iter != end; ++iter, ++witer) {
     603           0 :                 if (*witer == 0) {
     604           0 :                     *iter = -1;
     605             :                 }
     606             :             }
     607           0 :         }
     608             :     }
     609             :     else {
     610           0 :         TransformingVi2::sigmaSpectrum(sigmaSp);
     611             :     }
     612           0 : }
     613             : 
     614           0 : void StatWtTVI::weightSpectrum(Cube<Float>& newWtsp) const {
     615           0 :     ThrowIf(! _weightsComputed, "Weights have not been computed yet");
     616           0 :     if (! _dataAggregator->mustComputeWtSp()) {
     617           0 :         newWtsp.resize(IPosition(3, 0));
     618           0 :         return;
     619             :     }
     620           0 :     if (! _newWtSp.empty()) {
     621             :         // already calculated
     622           0 :         if (_updateWeight) {
     623           0 :             newWtsp = _newWtSp.copy();
     624             :         }
     625             :         else {
     626           0 :             TransformingVi2::weightSpectrum(newWtsp);
     627             :         }
     628           0 :         return;
     629             :     }
     630           0 :     _computeWeightSpectrumAndFlags();
     631           0 :     if (_updateWeight) {
     632           0 :         newWtsp = _newWtSp.copy();
     633             :     }
     634             :     else {
     635           0 :         TransformingVi2::weightSpectrum(newWtsp);
     636             :     }
     637             : }
     638             : 
     639           0 : void StatWtTVI::_computeWeightSpectrumAndFlags() const {
     640             :     size_t nOrigFlagged;
     641           0 :     auto mypair = _getLowerLayerWtSpFlags(nOrigFlagged);
     642           0 :     auto& wtsp = mypair.first;
     643           0 :     auto& flagCube = mypair.second;
     644           0 :     if (_dataAggregator->mustComputeWtSp() && wtsp.empty()) {
     645             :         // This can happen in preview mode if
     646             :         // WEIGHT_SPECTRUM doesn't exist or is empty
     647           0 :         wtsp.resize(flagCube.shape());
     648             :     }
     649           0 :     auto checkFlags = False;
     650           0 :     Vector<Int> ant1, ant2, spws;
     651           0 :     antenna1(ant1);
     652           0 :     antenna2(ant2);
     653           0 :     spectralWindows(spws);
     654           0 :     Vector<rownr_t> rowIDs;
     655           0 :     getRowIds(rowIDs);
     656           0 :     Vector<Double> exposures;
     657           0 :     exposure(exposures);
     658           0 :     _dataAggregator->weightSpectrumFlags(
     659             :         wtsp, flagCube, checkFlags, ant1, ant2, spws, exposures, rowIDs
     660             :     );
     661           0 :     if (checkFlags) {
     662           0 :         _nNewFlaggedPts += ntrue(flagCube) - nOrigFlagged;
     663             :     }
     664           0 :     _newWtSp = wtsp;
     665           0 :     _newFlag = flagCube;
     666           0 : }
     667             : 
     668           0 : std::pair<Cube<Float>, Cube<Bool>> StatWtTVI::_getLowerLayerWtSpFlags(
     669             :     size_t& nOrigFlagged
     670             : ) const {
     671           0 :     auto mypair = std::make_pair(Cube<Float>(), Cube<Bool>());
     672           0 :     if (_dataAggregator->mustComputeWtSp()) {
     673           0 :         getVii()->weightSpectrum(mypair.first);
     674             :     }
     675           0 :     getVii()->flag(mypair.second);
     676           0 :     _nTotalPts += mypair.second.size();
     677           0 :     nOrigFlagged = ntrue(mypair.second);
     678           0 :     _nOrigFlaggedPts += nOrigFlagged;
     679           0 :     return mypair;
     680           0 : }
     681             : 
     682           0 : void StatWtTVI::sigma(Matrix<Float>& sigmaMat) const {
     683           0 :     if (_mustComputeSigma) {
     684           0 :         if (_newWt.empty()) {
     685           0 :             Matrix<Float> wtmat;
     686           0 :             weight(wtmat);
     687           0 :         }
     688           0 :         sigmaMat = Float(1.0)/sqrt(_newWt);
     689           0 :         if (anyEQ(_newWt, Float(0))) {
     690           0 :             Matrix<Float>::iterator iter = sigmaMat.begin();
     691           0 :             Matrix<Float>::iterator end = sigmaMat.end();
     692           0 :             Matrix<Float>::iterator witer = _newWt.begin();
     693           0 :             for ( ; iter != end; ++iter, ++witer) {
     694           0 :                 if (*witer == 0) {
     695           0 :                     *iter = -1;
     696             :                 }
     697             :             }
     698           0 :         }
     699             :     }
     700             :     else {
     701           0 :         TransformingVi2::sigma(sigmaMat);
     702             :     }
     703           0 : }
     704             : 
     705           0 : void StatWtTVI::weight(Matrix<Float> & wtmat) const {
     706           0 :     ThrowIf(! _weightsComputed, "Weights have not been computed yet");
     707           0 :     if (! _newWt.empty()) {
     708           0 :         if (_updateWeight) {
     709           0 :             wtmat = _newWt.copy();
     710             :         }
     711             :         else {
     712           0 :             TransformingVi2::weight(wtmat);
     713             :         }
     714           0 :         return;
     715             :     }
     716           0 :     auto nrows = nRows();
     717           0 :     getVii()->weight(wtmat);
     718           0 :     if (_dataAggregator->mustComputeWtSp()) {
     719             :         // always use classical algorithm to get median for weights
     720             :         ClassicalStatistics<
     721             :             Double, Array<Float>::const_iterator, Array<Bool>::const_iterator
     722           0 :         > cs;
     723           0 :         Cube<Float> wtsp;
     724           0 :         Cube<Bool> flagCube;
     725             :         // this computes _newWtsP which is what we will use, so
     726             :         // just ignore wtsp
     727           0 :         weightSpectrum(wtsp);
     728           0 :         flag(flagCube);
     729           0 :         IPosition blc(3, 0);
     730           0 :         IPosition trc = _newWtSp.shape() - 1;
     731           0 :         const auto ncorr = _newWtSp.shape()[0];
     732           0 :         for (rownr_t i=0; i<nrows; ++i) {
     733           0 :             blc[2] = i;
     734           0 :             trc[2] = i;
     735           0 :             if (_combineCorr) {
     736           0 :                 auto flags = flagCube(blc, trc);
     737           0 :                 if (allTrue(flags)) {
     738           0 :                     wtmat.column(i) = 0;
     739             :                 }
     740             :                 else {
     741           0 :                     auto weights = _newWtSp(blc, trc);
     742           0 :                     auto mask = ! flags;
     743           0 :                     cs.setData(weights.begin(), mask.begin(), weights.size());
     744           0 :                     wtmat.column(i) = cs.getMedian();
     745           0 :                 }
     746           0 :             }
     747             :             else {
     748           0 :                 for (uInt corr=0; corr<ncorr; ++corr) {
     749           0 :                     blc[0] = corr;
     750           0 :                     trc[0] = corr;
     751           0 :                     auto weights = _newWtSp(blc, trc);
     752           0 :                     auto flags = flagCube(blc, trc);
     753           0 :                     if (allTrue(flags)) {
     754           0 :                         wtmat(corr, i) = 0;
     755             :                     }
     756             :                     else {
     757           0 :                         auto mask = ! flags;
     758           0 :                         cs.setData(
     759           0 :                             weights.begin(), mask.begin(), weights.size()
     760             :                         );
     761           0 :                         wtmat(corr, i) = cs.getMedian();
     762           0 :                     }
     763           0 :                 }
     764             :             }
     765             :         }
     766           0 :     }
     767             :     else {
     768             :         // the only way this can happen is if there is a single channel bin
     769             :         // for each baseline/spw pair
     770           0 :         _dataAggregator->weightSingleChanBin(wtmat, nrows);
     771             :     }
     772           0 :     _newWt = wtmat.copy();
     773           0 :     if (! _updateWeight) {
     774           0 :         wtmat = Matrix<Float>(wtmat.shape()); 
     775           0 :         TransformingVi2::weight(wtmat);
     776             :     }
     777             : }
     778             : 
     779           0 : void StatWtTVI::flag(Cube<Bool>& flagCube) const {
     780           0 :     ThrowIf(! _weightsComputed, "Weights have not been computed yet");
     781           0 :     if (! _newFlag.empty()) {
     782           0 :         flagCube = _newFlag.copy();
     783           0 :         return;
     784             :     }
     785           0 :     _computeWeightSpectrumAndFlags();
     786           0 :     flagCube = _newFlag.copy();
     787             : }
     788             : 
     789           0 : void StatWtTVI::flagRow(Vector<Bool>& flagRow) const {
     790           0 :     ThrowIf(! _weightsComputed, "Weights have not been computed yet");
     791           0 :     if (! _newFlagRow.empty()) {
     792           0 :         flagRow = _newFlagRow.copy();
     793           0 :         return;
     794             :     }
     795           0 :     Cube<Bool> flags;
     796           0 :     flag(flags);
     797           0 :     getVii()->flagRow(flagRow);
     798           0 :     auto nrows = nRows();
     799           0 :     for (rownr_t i=0; i<nrows; ++i) {
     800           0 :         flagRow[i] = allTrue(flags.xyPlane(i));
     801             :     }
     802           0 :     _newFlagRow = flagRow.copy();
     803           0 : }
     804             : 
     805           0 : void StatWtTVI::originChunks(Bool forceRewind) {
     806             :     // Drive next lower layer
     807           0 :     getVii()->originChunks(forceRewind);
     808           0 :     _weightsComputed = False;
     809           0 :     _dataAggregator->aggregate();
     810           0 :     _weightsComputed = True;
     811           0 :     _clearCache();
     812             :     // re-origin this chunk in next layer
     813             :     //  (ensures wider scopes see start of the this chunk)
     814           0 :     getVii()->origin();
     815           0 : }
     816             : 
     817           0 : void StatWtTVI::nextChunk() {
     818             :     // Drive next lower layer
     819           0 :     getVii()->nextChunk();
     820           0 :     _weightsComputed = False;
     821           0 :     _dataAggregator->aggregate();
     822           0 :     _weightsComputed = True;
     823           0 :     _clearCache();
     824             :     // re-origin this chunk next layer
     825             :     //  (ensures wider scopes see start of the this chunk)
     826           0 :     getVii()->origin();
     827           0 : }
     828             : 
     829           0 : void StatWtTVI::_clearCache() {
     830           0 :     _newWtSp.resize(0, 0, 0);
     831           0 :     _newWt.resize(0, 0);
     832           0 :     _newFlag.resize(0, 0, 0);
     833           0 :     _newFlagRow.resize(0);
     834           0 : }
     835             : 
     836           0 : Cube<Bool> StatWtTVI::_getResultantFlags(
     837             :     Cube<Bool>& chanSelFlagTemplate, Cube<Bool>& chanSelFlags,
     838             :     Bool& initTemplate, Int spw, const Cube<Bool>& flagCube
     839             : ) const {
     840           0 :     if (_chanSelFlags.find(spw) == _chanSelFlags.cend()) {
     841             :         // no selection of channels to ignore
     842           0 :         return flagCube;
     843             :     }
     844           0 :     if (initTemplate) {
     845             :         // this can be done just once per chunk because all the rows
     846             :         // in the chunk are guaranteed to have the same spw
     847             :         // because each subchunk is guaranteed to have a single
     848             :         // data description ID.
     849           0 :         chanSelFlagTemplate = _chanSelFlags.find(spw)->second;
     850           0 :         initTemplate = False;
     851             :     }
     852           0 :     auto dataShape = flagCube.shape();
     853           0 :     chanSelFlags.resize(dataShape, False);
     854           0 :     auto ncorr = dataShape[0];
     855           0 :     auto nrows = dataShape[2];
     856           0 :     IPosition start(3, 0);
     857           0 :     IPosition end = dataShape - 1;
     858           0 :     Slicer sl(start, end, Slicer::endIsLast);
     859           0 :     for (uInt corr=0; corr<ncorr; ++corr) {
     860           0 :         start[0] = corr;
     861           0 :         end[0] = corr;
     862           0 :         for (Int row=0; row<nrows; ++row) {
     863           0 :             start[2] = row;
     864           0 :             end[2] = row;
     865           0 :             sl.setStart(start);
     866           0 :             sl.setEnd(end);
     867           0 :             chanSelFlags(sl) = chanSelFlagTemplate;
     868             :         }
     869             :     }
     870           0 :     return flagCube || chanSelFlags;
     871           0 : }
     872             : 
     873           0 : void StatWtTVI::initWeightSpectrum (const Cube<Float>& wtspec) {
     874             :     // Pass to next layer down
     875           0 :     getVii()->initWeightSpectrum(wtspec);
     876           0 : }
     877             : 
     878           0 : void StatWtTVI::initSigmaSpectrum (const Cube<Float>& sigspec) {
     879             :     // Pass to next layer down
     880           0 :     getVii()->initSigmaSpectrum(sigspec);
     881           0 : }
     882             : 
     883             : 
     884           0 : void StatWtTVI::writeBackChanges(VisBuffer2 *vb) {
     885             :     // Pass to next layer down
     886           0 :     getVii()->writeBackChanges(vb);
     887           0 : }
     888             : 
     889           0 : void StatWtTVI::summarizeFlagging() const {
     890           0 :     auto orig = (Double)_nOrigFlaggedPts/(Double)_nTotalPts*100;
     891           0 :     auto stwt = (Double)_nNewFlaggedPts/(Double)_nTotalPts*100;
     892           0 :     auto total = orig + stwt;
     893           0 :     LogIO log(LogOrigin("StatWtTVI", __func__));
     894             :     log << LogIO::NORMAL << "Originally, " << orig
     895             :         << "% of the data were flagged. StatWtTVI flagged an "
     896           0 :         << "additional " << stwt << "%."  << LogIO::POST;
     897             :     log << LogIO::NORMAL << "TOTAL FLAGGED DATA AFTER RUNNING STATWT: "
     898           0 :         << total << "%" << LogIO::POST;
     899           0 :     log << LogIO::NORMAL << std::endl << LogIO::POST;
     900           0 :     if (_nOrigFlaggedPts == _nTotalPts) {
     901             :         log << LogIO::WARN << "IT APPEARS THAT ALL THE DATA IN THE INPUT "
     902             :             << "MS/SELECTION WERE FLAGGED PRIOR TO RUNNING STATWT"
     903           0 :             << LogIO::POST;
     904           0 :         log << LogIO::NORMAL << std::endl << LogIO::POST;
     905             :     }
     906           0 :     else if (_nOrigFlaggedPts + _nNewFlaggedPts == _nTotalPts) {
     907             :         log << LogIO::WARN << "IT APPEARS THAT STATWT FLAGGED ALL THE DATA "
     908             :             "IN THE REQUESTED SELECTION THAT WASN'T ORIGINALLY FLAGGED"
     909           0 :             << LogIO::POST;
     910           0 :         log << LogIO::NORMAL << std::endl << LogIO::POST;
     911             :     }
     912           0 :     String col0 = "SPECTRAL_WINDOW";
     913           0 :     String col1 = "SAMPLES_WITH_NON-ZERO_VARIANCE";
     914             :     String col2 = "SAMPLES_WHERE_REAL_PART_VARIANCE_DIFFERS_BY_>50%_FROM_"
     915           0 :         "IMAGINARY_PART";
     916           0 :     log << LogIO::NORMAL << col0 << " " << col1 << " " << col2 << LogIO::POST;
     917           0 :     auto n0 = col0.size();
     918           0 :     auto n1 = col1.size();
     919           0 :     auto n2 = col2.size();
     920           0 :     for (const auto& sample: *_samples) {
     921           0 :         ostringstream oss;
     922           0 :         oss << std::setw(n0) << sample.first << " " << std::setw(n1)
     923           0 :             << sample.second.first << " " << std::setw(n2)
     924           0 :             << sample.second.second;
     925           0 :         log << LogIO::NORMAL << oss.str() << LogIO::POST;
     926           0 :     }
     927           0 : }
     928             : 
     929           0 : void StatWtTVI::summarizeStats(Double& mean, Double& variance) const {
     930           0 :     LogIO log(LogOrigin("StatWtTVI", __func__));
     931           0 :     _logUsedChannels();
     932             :     try {
     933           0 :         mean = _wtStats->getStatistic(StatisticsData::MEAN);
     934           0 :         variance = _wtStats->getStatistic(StatisticsData::VARIANCE);
     935             :         log << LogIO::NORMAL << "The mean of the computed weights is "
     936           0 :             << mean << LogIO::POST;
     937             :         log << LogIO::NORMAL << "The variance of the computed weights is "
     938           0 :             << variance << LogIO::POST;
     939             :         log << LogIO::NORMAL << "Weights which had corresponding flags of True "
     940             :             << "prior to running this application were not used to compute these "
     941           0 :             << "stats." << LogIO::POST;
     942             :     }
     943           0 :     catch (const AipsError& x) {
     944             :         log << LogIO::WARN << "There was a problem calculating the mean and "
     945             :             << "variance of the weights computed by this application. Perhaps there "
     946             :             << "was something amiss with the input MS and/or the selection criteria. "
     947             :             << "Examples of such issues are that all the data were originally flagged "
     948             :             << "or that the sample size was consistently too small for computations "
     949           0 :             << "of variances" << LogIO::POST;
     950           0 :         setNaN(mean);
     951           0 :         setNaN(variance);
     952           0 :     }
     953           0 : }
     954             : 
     955           0 : void StatWtTVI::origin() {
     956             :     // Drive underlying ViImplementation2
     957           0 :     getVii()->origin();
     958             :     // Synchronize own VisBuffer
     959           0 :     configureNewSubchunk();
     960           0 :     _clearCache();
     961           0 : }
     962             : 
     963           0 : void StatWtTVI::next() {
     964             :     // Drive underlying ViImplementation2
     965           0 :     getVii()->next();
     966             :     // Synchronize own VisBuffer
     967           0 :     configureNewSubchunk();
     968           0 :     _clearCache();
     969           0 : }
     970             : 
     971             : }
     972             : 
     973             : }

Generated by: LCOV version 1.16