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

Generated by: LCOV version 1.16