LCOV - code coverage report
Current view: top level - synthesis/MeasurementComponents - SingleDishSkyCal.cc (source / functions) Hit Total Coverage
Test: casacpp_coverage.info Lines: 810 890 91.0 %
Date: 2024-11-06 17:42:47 Functions: 81 111 73.0 %

          Line data    Source code
       1             : //# SingleDishSkyCal.cc: implements SingleDishSkyCal
       2             : //# Copyright (C) 2003
       3             : //# Associated Universities, Inc. Washington DC, USA.
       4             : //#
       5             : //# This library is free software; you can redistribute it and/or modify it
       6             : //# under the terms of the GNU Library General Public License as published by
       7             : //# the Free Software Foundation; either version 2 of the License, or (at your
       8             : //# option) any later version.
       9             : //#
      10             : //# This library is distributed in the hope that it will be useful, but WITHOUT
      11             : //# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
      12             : //# FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Library General Public
      13             : //# License for more details.
      14             : //#
      15             : //# You should have received a copy of the GNU Library General Public License
      16             : //# along with this library; if not, write to the Free Software Foundation,
      17             : //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA.
      18             : //#
      19             : //# Correspondence concerning AIPS++ should be addressed as follows:
      20             : //#        Internet email: casa-feedback@nrao.edu.
      21             : //#        Postal address: AIPS++ Project Office
      22             : //#                        National Radio Astronomy Observatory
      23             : //#                        520 Edgemont Road
      24             : //#                        Charlottesville, VA 22903-2475 USA
      25             : //#
      26             : //# $Id$
      27             : 
      28             : //# Includes
      29             : #include <iostream>
      30             : #include <sstream>
      31             : #include <iomanip>
      32             : #include <map>
      33             : #include <type_traits>
      34             : 
      35             : #include <casacore/casa/OS/File.h>
      36             : #include <casacore/casa/Arrays/MaskedArray.h>
      37             : #include <casacore/casa/Arrays/MaskArrMath.h>
      38             : #include <casacore/tables/TaQL/TableParse.h>
      39             : #include <casacore/tables/Tables/Table.h>
      40             : #include <casacore/tables/Tables/ScalarColumn.h>
      41             : #include <casacore/ms/MeasurementSets/MeasurementSet.h>
      42             : #include <casacore/ms/MeasurementSets/MSState.h>
      43             : #include <casacore/ms/MeasurementSets/MSSpectralWindow.h>
      44             : #include <casacore/ms/MeasurementSets/MSIter.h>
      45             : #include <synthesis/MeasurementComponents/MSMetaInfoForCal.h>
      46             : #include <synthesis/MeasurementComponents/SingleDishSkyCal.h>
      47             : #include <synthesis/CalTables/CTGlobals.h>
      48             : #include <synthesis/CalTables/CTMainColumns.h>
      49             : #include <synthesis/CalTables/CTInterface.h>
      50             : #include <casacore/ms/MSSel/MSSelection.h>
      51             : #include <casacore/ms/MSSel/MSSelectionTools.h>
      52             : #include <synthesis/Utilities/PointingDirectionCalculator.h>
      53             : #include <synthesis/Utilities/PointingDirectionProjector.h>
      54             : #include <casacore/coordinates/Coordinates/DirectionCoordinate.h>
      55             : #include <casa_sakura/SakuraAlignedArray.h>
      56             : #include <libsakura/sakura.h>
      57             : #include <cassert>
      58             : 
      59             : // Debug Message Handling
      60             : // if SDCALSKY_DEBUG is defined, the macro debuglog and
      61             : // debugpost point standard output stream (std::cout and
      62             : // std::endl so that debug messages are sent to standard
      63             : // output. Otherwise, these macros basically does nothing.
      64             : // "Do nothing" behavior is implemented in NullLogger
      65             : // and its associating << operator below.
      66             : //
      67             : // Usage:
      68             : // Similar to standard output stream.
      69             : //
      70             : //   debuglog << "Any message" << any_value << debugpost;
      71             : //
      72             : //#define SDCALSKY_DEBUG
      73             : 
      74             : using namespace casacore;
      75             : 
      76             : namespace {
      77             : struct NullLogger {};
      78             : 
      79             : template<class T>
      80      626544 : inline NullLogger &operator<<(NullLogger &logger, T /*value*/) {
      81      626544 :   return logger;
      82             : }
      83             : 
      84             : #ifndef SDCALSKY_DEBUG
      85             : NullLogger nulllogger;
      86             : #endif
      87             : 
      88             : // Helper class to format integers with thousand separators
      89             : struct CommaSeparatedThousands : std::numpunct<char> {
      90          29 :     char_type do_thousands_sep() const override { return ','; }
      91          29 :     string_type do_grouping() const override { return "\3"; }
      92             : };
      93             : 
      94             : }
      95             : 
      96             : #ifdef SDCALSKY_DEBUG
      97             :   #define debuglog std::cout
      98             :   #define debugpost std::endl
      99             : #else
     100             :   #define debuglog nulllogger
     101             :   #define debugpost 0
     102             : #endif
     103             : 
     104             : // Local Functions
     105             : namespace {
     106             : inline Vector<rownr_t> getOffStateIdList(MeasurementSet const &ms) {
     107             :   String taql("SELECT FLAG_ROW FROM $1 WHERE UPPER(OBS_MODE) ~ m/^OBSERVE_TARGET#OFF_SOURCE/");
     108             :   Table stateSel = tableCommand(taql, ms.state()).table();
     109             :   Vector<rownr_t> stateIdList = stateSel.rowNumbers();
     110             :   debuglog << "stateIdList[" << stateIdList.nelements() << "]=";
     111             :   for (size_t i = 0; i < stateIdList.nelements(); ++i) {
     112             :     debuglog << stateIdList[i] << " ";
     113             :   }
     114             :   debuglog << debugpost;
     115             :   return stateIdList;
     116             : }
     117             : 
     118             : template<class T>
     119          49 : inline std::string toString(casacore::Vector<T> const &v) {
     120          49 :   std::ostringstream oss;
     121          49 :   oss << "[";
     122          49 :   std::string delimiter = "";
     123         589 :   for (size_t i = 0; i < v.nelements(); ++i) {
     124         540 :     oss << delimiter << v[i];
     125         540 :     delimiter = ",";
     126             :   }
     127          49 :   oss << "]";
     128          98 :   return oss.str();
     129          49 : }
     130             : 
     131             : // unused
     132             : /*
     133             : inline casacore::String configureTaqlString(casacore::String const &msName, casacore::Vector<casacore::uInt> stateIdList) {
     134             :   std::ostringstream oss;
     135             :   oss << "SELECT FROM " << msName << " WHERE ANTENNA1 == ANTENNA2 && STATE_ID IN "
     136             :       << toString(stateIdList)
     137             :       << " ORDER BY FIELD_ID, ANTENNA1, FEED1, DATA_DESC_ID, TIME";
     138             :   return casacore::String(oss);
     139             : }
     140             : */
     141             : 
     142          31 : inline void fillNChanParList(casacore::MeasurementSet const &ms, casacore::Vector<casacore::Int> &nChanParList) {
     143          31 :   casacore::MSSpectralWindow const &msspw = ms.spectralWindow();
     144          31 :   casacore::ScalarColumn<casacore::Int> nchanCol(msspw, "NUM_CHAN");
     145          31 :   debuglog << "nchanCol=" << toString(nchanCol.getColumn()) << debugpost;
     146          31 :   nChanParList = nchanCol.getColumn()(casacore::Slice(0,nChanParList.nelements()));
     147          31 :   debuglog << "nChanParList=" << nChanParList << debugpost;
     148          31 : }
     149             : 
     150          28 : inline void updateWeight(casa::NewCalTable &ct) {
     151          28 :   casa::CTMainColumns ctmc(ct);
     152          28 :   casacore::ArrayColumn<casacore::Double> chanWidthCol(ct.spectralWindow(), "CHAN_WIDTH");
     153          28 :   casacore::Vector<casacore::Int> chanWidth(chanWidthCol.nrow());
     154         402 :   for (size_t irow = 0; irow < chanWidthCol.nrow(); ++irow) {
     155         374 :     casacore::Double chanWidthVal = chanWidthCol(irow)(casacore::IPosition(1,0));
     156         374 :     chanWidth[irow] = abs(chanWidthVal);
     157             :   }
     158         596 :   for (size_t irow = 0; irow < ct.nrow(); ++irow) {
     159         568 :     casacore::Int spwid = ctmc.spwId()(irow);
     160         568 :     casacore::Double width = chanWidth[spwid];
     161         568 :     casacore::Double interval = ctmc.interval()(irow);
     162         568 :     casacore::Float weight = width * interval;
     163         568 :     casacore::Matrix<casacore::Float> weightMat(ctmc.fparam().shape(irow), weight);
     164         568 :     ctmc.weight().put(irow, weightMat);
     165         568 :   }
     166          28 : }
     167             : 
     168             : class DataColumnAccessor {
     169             : public:
     170         112 :   DataColumnAccessor(casacore::Table const &ms,
     171             :                      casacore::String const colName="DATA")
     172         112 :     : dataCol_(ms, colName) {
     173         112 :   }
     174         638 :   casacore::Matrix<casacore::Float> operator()(size_t irow) {
     175        1276 :     return casacore::real(dataCol_(irow));
     176             :   }
     177             : private:
     178             :   DataColumnAccessor() {}
     179             :   casacore::ArrayColumn<casacore::Complex> dataCol_;
     180             : };
     181             : 
     182             : class FloatDataColumnAccessor {
     183             : public:
     184          74 :   FloatDataColumnAccessor(casacore::Table const &ms)
     185          74 :     : dataCol_(ms, "FLOAT_DATA") {
     186          74 :   }
     187        2111 :   casacore::Matrix<casacore::Float> operator()(size_t irow) {
     188        4222 :     return dataCol_(irow);
     189             :   }
     190             : private:
     191             :   FloatDataColumnAccessor() {}
     192             :   casacore::ArrayColumn<casacore::Float> dataCol_;
     193             : };
     194             : 
     195          10 : inline  casacore::Int nominalDataDesc(casacore::MeasurementSet const &ms, casacore::Int const ant)
     196             : {
     197          10 :   casacore::Int goodDataDesc = -1;
     198          10 :   casacore::ScalarColumn<casacore::Int> col(ms.spectralWindow(), "NUM_CHAN");
     199          10 :   casacore::Vector<casacore::Int> nchanList = col.getColumn();
     200          10 :   size_t numSpw = col.nrow();
     201          10 :   casacore::Vector<casacore::Int> spwMap(numSpw);
     202          10 :   col.attach(ms.dataDescription(), "SPECTRAL_WINDOW_ID");
     203         180 :   for (size_t i = 0; i < col.nrow(); ++i) {
     204         170 :     spwMap[col(i)] = i;
     205             :   }
     206          10 :   casacore::ScalarColumn<casacore::String> obsModeCol(ms.state(), "OBS_MODE");
     207          10 :   casacore::Regex regex("^OBSERVE_TARGET#ON_SOURCE.*");
     208         100 :   for (size_t ispw = 0; ispw < numSpw; ++ispw) {
     209         100 :     if (nchanList[ispw] == 4) {
     210             :       // this should be WVR
     211          10 :       continue;
     212             :     }
     213             :     else {
     214          90 :       std::ostringstream oss;
     215          90 :       oss << "SELECT FROM $1 WHERE ANTENNA1 == " << ant
     216          90 :           << " && ANTENNA2 == " << ant << " && DATA_DESC_ID == " << spwMap[ispw]
     217          90 :           << " ORDER BY STATE_ID";
     218         180 :       casacore::MeasurementSet msSel(casacore::tableCommand(oss.str(), ms).table());
     219          90 :       col.attach(msSel, "STATE_ID");
     220          90 :       casacore::Vector<casacore::Int> stateIdList = col.getColumn();
     221          90 :       casacore::Int previousStateId = -1;
     222          90 :       for (size_t i = 0; i < msSel.nrow(); ++i) {
     223          10 :         casacore::Int stateId = stateIdList[i];
     224          10 :         if (stateId != previousStateId) {
     225          10 :           casacore::String obsmode = obsModeCol(stateId);
     226          10 :           if (regex.match(obsmode.c_str(), obsmode.size()) != casacore::String::npos) {
     227          10 :             goodDataDesc = spwMap[ispw];
     228          10 :             break;
     229             :           }
     230          10 :         }
     231           0 :         previousStateId = stateId;
     232             :       }
     233          90 :     }
     234             : 
     235          90 :     if (goodDataDesc >= 0)
     236          10 :       break;
     237             :   }
     238          10 :   return goodDataDesc;
     239          10 : }
     240             : 
     241          10 : inline casacore::Vector<size_t> detectGap(casacore::Vector<casacore::Double> timeList)
     242             : {
     243          10 :   size_t n = timeList.size();
     244          20 :   casacore::Vector<casacore::Double> timeInterval = timeList(casacore::Slice(1, n-1)) - timeList(casacore::Slice(0, n-1));
     245          10 :   casacore::Double medianTime = casacore::median(timeInterval);
     246          10 :   casacore::Double const threshold = medianTime * 5.0;
     247          10 :   casacore::Vector<size_t> gapIndexList(casacore::IPosition(1, n/2 + 2), new size_t[n/2+2], casacore::TAKE_OVER);
     248          10 :   gapIndexList[0] = 0;
     249          10 :   size_t gapIndexCount = 1;
     250         252 :   for (size_t i = 0; i < timeInterval.size(); ++i) {
     251         242 :     if (timeInterval[i] > threshold) {
     252          19 :       gapIndexList[gapIndexCount] = i + 1;
     253          19 :       gapIndexCount++;
     254             :     }
     255             :   }
     256          10 :   if (gapIndexList[gapIndexCount] != n) {
     257          10 :     gapIndexList[gapIndexCount] = n;
     258          10 :     gapIndexCount++;
     259             :   }
     260          10 :   debuglog << "Detected " << gapIndexCount << " gaps." << debugpost;
     261          10 :   casacore::Vector<size_t> ret(casacore::IPosition(1, gapIndexCount), gapIndexList.data(), casacore::COPY);
     262          10 :   debuglog << "gapList=" << toString(ret) << debugpost;
     263          20 :   return ret;
     264          10 : }
     265             : 
     266             : struct DefaultRasterEdgeDetector
     267             : {
     268           3 :   static size_t N(size_t numData, casacore::Float const /*fraction*/, casacore::Int const /*num*/)
     269             :   {
     270           3 :     debuglog << "DefaultRasterEdgeDetector" << debugpost;
     271           3 :     return max((size_t)1, static_cast<size_t>(sqrt(numData + 1) - 1));
     272             :   }
     273             : };
     274             : 
     275             : struct FixedNumberRasterEdgeDetector
     276             : {
     277           7 :   static size_t N(size_t /*numData*/, casacore::Float const /*fraction*/, casacore::Int const num)
     278             :   {
     279           7 :     debuglog << "FixedNumberRasterEdgeDetector" << debugpost;
     280           7 :     if (num < 0) {
     281           0 :       throw casacore::AipsError("Negative number of edge points.");
     282             :     }
     283           7 :     return (size_t)num;
     284             :   }
     285             : };
     286             : 
     287             : struct FixedFractionRasterEdgeDetector
     288             : {
     289          17 :   static casacore::Int N(size_t numData, casacore::Float const fraction, casacore::Int const /*num*/)
     290             :   {
     291          17 :     debuglog << "FixedFractionRasterEdgeDetector" << debugpost;
     292          17 :     return max((size_t)1, static_cast<size_t>(numData * fraction));
     293             :   }
     294             : };
     295             : 
     296             : template<class Detector>
     297          27 : inline casacore::Vector<casacore::Double> detectEdge(casacore::Vector<casacore::Double> timeList, casacore::Double const interval, casacore::Float const fraction, casacore::Int const num)
     298             : {
     299             :   // storage for time range for edges (at head and tail)
     300             :   // [(start of head edge), (end of head edge),
     301             :   //  (start of tail edge), (end of tail edge)]
     302          27 :   casacore::Vector<casacore::Double> edgeList(4);
     303          27 :   size_t numList = timeList.size();
     304          27 :   size_t numEdge = Detector::N(numList, fraction, num);
     305          27 :   debuglog << "numEdge = " << numEdge << debugpost;
     306          27 :   if (numEdge == 0) {
     307           0 :     throw casacore::AipsError("Zero edge points.");
     308             :   }
     309          27 :   else if (timeList.size() > numEdge * 2) {
     310          25 :     edgeList[0] = timeList[0] - 0.5 * interval;
     311          25 :     edgeList[1] = timeList[numEdge-1] + 0.5 * interval;
     312          25 :     edgeList[2] = timeList[numList-numEdge] - 0.5 * interval;
     313          25 :     edgeList[3] = timeList[numList-1] + 0.5 * interval;
     314             :   }
     315             :   else {
     316           2 :     std::ostringstream oss;
     317           2 :     oss << "Too many edge points (" << 2.0 * numEdge << " out of "
     318           2 :         << timeList.size() << " points)";
     319           2 :     throw casacore::AipsError(oss.str());
     320             :     // edgeList[0] = timeList[0] - 0.5 * interval;
     321             :     // edgeList[1] = timeList[numList-1] + 0.5 * interval;
     322             :     // edgeList[2] = edgeList[0];
     323             :     // edgeList[3] = edgeList[2];
     324           2 :   }
     325          25 :   return edgeList;
     326           2 : }
     327             : 
     328          10 : inline casacore::Vector<casacore::String> detectRaster(casacore::MeasurementSet const &ms,
     329             :                                                casacore::Int const ant,
     330             :                                                casacore::Float const fraction,
     331             :                                                casacore::Int const num)
     332             : {
     333          10 :   casacore::Int dataDesc = nominalDataDesc(ms, ant);
     334          10 :   debuglog << "nominal DATA_DESC_ID=" << dataDesc << debugpost;
     335          10 :   assert(dataDesc >= 0);
     336          10 :   if (dataDesc < 0) {
     337           0 :     return casacore::Vector<casacore::String>();
     338             :   }
     339             : 
     340          10 :   std::ostringstream oss;
     341          10 :   oss << "SELECT FROM $1 WHERE ANTENNA1 == " << ant
     342          10 :       << " && ANTENNA2 == " << ant << " && FEED1 == 0 && FEED2 == 0"
     343          10 :       << " && DATA_DESC_ID == " << dataDesc
     344          10 :       << " ORDER BY TIME";
     345          10 :   debuglog << "detectRaster: taql=" << oss.str() << debugpost;
     346          20 :   casacore::MeasurementSet msSel(casacore::tableCommand(oss.str(), ms).table());
     347          10 :   casacore::ScalarColumn<casacore::Double> timeCol(msSel, "TIME");
     348          10 :   casacore::ScalarColumn<casacore::Double> intervalCol(msSel, "INTERVAL");
     349          10 :   casacore::Vector<casacore::Double> timeList = timeCol.getColumn();
     350          10 :   casacore::Double interval = casacore::min(intervalCol.getColumn());
     351          10 :   casacore::Vector<size_t> gapList = detectGap(timeList);
     352          10 :   casacore::Vector<casacore::String> edgeAsTimeRange(gapList.size() * 2);
     353             :   typedef casacore::Vector<casacore::Double> (*DetectorFunc)(casacore::Vector<casacore::Double>, casacore::Double const,  casacore::Float const, casacore::Int const);
     354          10 :   DetectorFunc detect = NULL;
     355          10 :   if (num > 0) {
     356           3 :     detect = detectEdge<FixedNumberRasterEdgeDetector>;
     357             :   }
     358           7 :   else if (fraction > 0.0) {
     359           6 :     detect = detectEdge<FixedFractionRasterEdgeDetector>;
     360             :   }
     361             :   else {
     362           1 :     detect = detectEdge<DefaultRasterEdgeDetector>;
     363             :   }
     364          35 :   for (size_t i = 0; i < gapList.size()-1; ++i) {
     365          27 :     size_t startRow = gapList[i];
     366          27 :     size_t endRow = gapList[i+1];
     367          27 :     size_t len = endRow - startRow;
     368          27 :     debuglog << "startRow=" << startRow << ", endRow=" << endRow << debugpost;
     369          27 :     casacore::Vector<casacore::Double> oneRow = timeList(casacore::Slice(startRow, len));
     370          29 :     casacore::Vector<casacore::Double> edgeList = detect(oneRow, interval, fraction, num);
     371          25 :     std::ostringstream s;
     372          25 :     s << std::setprecision(16) << "TIME BETWEEN " << edgeList[0] << " AND " << edgeList[1];
     373          25 :     edgeAsTimeRange[2*i] = s.str();
     374          25 :     s.str("");
     375          25 :     s << std::setprecision(16) << "TIME BETWEEN " << edgeList[2] << " AND " << edgeList[3];
     376          25 :     edgeAsTimeRange[2*i+1] = s.str();
     377          25 :     debuglog << "Resulting selection: (" << edgeAsTimeRange[2*i] << ") || ("
     378          50 :              << edgeAsTimeRange[2*i+1] << ")" << debugpost;
     379          27 :   }
     380           8 :   return edgeAsTimeRange;
     381          22 : }
     382             : 
     383             : // Formula for weight scaling factor, WF
     384             : // 1. only one OFF spectrum is used (i.e. no interpolation)
     385             : //
     386             : //     sigma = sqrt(sigma_ON^2 + sigma_OFF^2)
     387             : //           = sigma_ON * sqrt(1 + tau_ON / tau_OFF)
     388             : //
     389             : //     weight = 1 / sigma^2
     390             : //            = 1 / sigma_ON^2 * tau_OFF / (tau_ON + tau_OFF)
     391             : //            = weight_ON * tau_OFF / (tau_ON + tau_OFF)
     392             : //
     393             : //     WF = tau_OFF / (tau_ON + tau_OFF)
     394             : //
     395             : struct SimpleWeightScalingScheme
     396             : {
     397        1351 :   static casacore::Float SimpleScale(casacore::Double exposureOn, casacore::Double exposureOff)
     398             :   {
     399        1351 :     return exposureOff / (exposureOn + exposureOff);
     400             :   }
     401             : };
     402             : // 2. two OFF spectrum is used (linear interpolation)
     403             : //
     404             : //     sigma_OFF = {(t_OFF2 - t_ON)^2 * sigma_OFF1^2
     405             : //                    + (t_ON - t_OFF1)^2 * sigma_OFF2^2}
     406             : //                  / (t_OFF2 - t_OFF1)^2
     407             : //
     408             : //     sigma = sqrt(sigma_ON^2 + sigma_OFF^2)
     409             : //           = sigma_ON * sqrt(1 + tau_ON / (t_OFF2 - t_OFF1)^2
     410             : //                              * {(t_OFF2 - t_ON)^2 / tau_OFF1
     411             : //                                  + (t_ON - t_OFF1)^2 / tau_OFF2})
     412             : //
     413             : //     weight = weight_ON / (1 + tau_ON / (t_OFF2 - t_OFF1)^2
     414             : //                            * {(t_OFF2 - t_ON)^2 / tau_OFF1
     415             : //                                + (t_ON - t_OFF1)^2 / tau_OFF2})
     416             : //
     417             : //     WF = 1.0 / (1 + tau_ON / (t_OFF2 - t_OFF1)^2
     418             : //                  * {(t_OFF2 - t_ON)^2 / tau_OFF1
     419             : //                      + (t_ON - t_OFF1)^2 / tau_OFF2})
     420             : //
     421             : struct LinearWeightScalingScheme : public SimpleWeightScalingScheme
     422             : {
     423        3527 :   static casacore::Float InterpolateScale(casacore::Double timeOn, casacore::Double exposureOn,
     424             :                                      casacore::Double timeOff1, casacore::Double exposureOff1,
     425             :                                      casacore::Double timeOff2, casacore::Double exposureOff2)
     426             :   {
     427        3527 :     casacore::Double dt = timeOff2 - timeOff1;
     428        3527 :     casacore::Double dt1 = timeOn - timeOff1;
     429        3527 :     casacore::Double dt2 = timeOff2 - timeOn;
     430        3527 :     return 1.0f / (1.0f + exposureOn / (dt * dt)
     431        3527 :                    * (dt2 * dt2 / exposureOff1 + dt1 * dt1 / exposureOff2));
     432             :   }
     433             : };
     434             : 
     435             : // 3. two OFF spectrum is used (nearest interpolation)
     436             : //
     437             : // formulation is same as case 1.
     438             : //
     439             : struct NearestWeightScalingScheme : public SimpleWeightScalingScheme
     440             : {
     441          40 :   static casacore::Float InterpolateScale(casacore::Double timeOn, casacore::Double exposureOn,
     442             :                                      casacore::Double timeOff1, casacore::Double exposureOff1,
     443             :                                      casacore::Double timeOff2, casacore::Double exposureOff2)
     444             :   {
     445          40 :     casacore::Double dt1 = abs(timeOn - timeOff1);
     446          40 :     casacore::Double dt2 = abs(timeOff2 - timeOn);
     447          40 :     return (dt1 <= dt2) ?
     448          24 :       SimpleScale(exposureOn, exposureOff1)
     449          40 :       : SimpleScale(exposureOn, exposureOff2);
     450             :   }
     451             : };
     452             : 
     453           9 : inline bool isEphemeris(casacore::String const &name) {
     454             :   // Check if given name is included in MDirection types
     455             :   casacore::Int nall, nextra;
     456             :   const casacore::uInt *typ;
     457           9 :   auto *types = casacore::MDirection::allMyTypes(nall, nextra, typ);
     458           9 :   auto start_extra = nall - nextra;
     459           9 :   auto capital_name = name;
     460           9 :   capital_name.upcase();
     461             : 
     462          90 :   for (auto i = start_extra; i < nall; ++i) {
     463          83 :     if (capital_name == types[i]) {
     464           2 :       return true;
     465             :     }
     466             :   }
     467             : 
     468           7 :   return false;
     469           9 : }
     470             : 
     471             : // Set number of correlations per spw
     472        5161 : inline void setNumberOfCorrelationsPerSpw(casacore::MeasurementSet const &ms,
     473             :     casacore::Vector<casacore::Int> &nCorr) {
     474        5161 :   casacore::ScalarColumn<casacore::Int> spwIdCol(ms.dataDescription(), "SPECTRAL_WINDOW_ID");
     475        5161 :   casacore::ScalarColumn<casacore::Int> polIdCol(ms.dataDescription(), "POLARIZATION_ID");
     476        5161 :   casacore::ScalarColumn<casacore::Int> numCorrCol(ms.polarization(), "NUM_CORR");
     477        5161 :   auto numSpw = ms.spectralWindow().nrow();
     478        5161 :   auto const spwIdList = spwIdCol.getColumn();
     479        5161 :   auto const polIdList = polIdCol.getColumn();
     480        5161 :   auto const numCorrList = numCorrCol.getColumn();
     481        5161 :   if (nCorr.size() != numSpw) {
     482           0 :     nCorr.resize(numSpw);
     483             :   }
     484             :   // initialize number of correlations with 0
     485        5161 :   nCorr = 0;
     486       22101 :   for (auto i = 0u; i < spwIdList.size(); ++i) {
     487       16940 :     auto const spwId = spwIdList[i];
     488       16940 :     auto const polId = polIdList[i];
     489       16940 :     auto const numCorr = numCorrList[polId];
     490       16940 :     nCorr[spwId] = numCorr;
     491             :   }
     492        5161 : }
     493             : }
     494             : 
     495             : namespace casa { //# NAMESPACE CASA - BEGIN
     496             : //
     497             : // SingleDishSkyCal
     498             : //
     499             : 
     500             : // Constructor
     501          51 : SingleDishSkyCal::SingleDishSkyCal(VisSet& vs)
     502             :   : VisCal(vs),
     503             :     SolvableVisCal(vs),
     504          51 :     currAnt_(-1),
     505          51 :     engineC_(vs.numberSpw(), NULL),
     506          51 :     engineF_(vs.numberSpw(), NULL),
     507          51 :     currentSky_(vs.numberSpw(), NULL),
     508          51 :     currentSkyOK_(vs.numberSpw(), NULL),
     509         102 :     nCorr_(nSpw())
     510             : {
     511          51 :   debuglog << "SingleDishSkyCal::SingleDishSkyCal(VisSet& vs)" << debugpost;
     512          51 :   append() = false;
     513             : 
     514          51 :   initializeSky();
     515          51 :   initializeCorr();
     516          51 : }
     517             : 
     518           3 : SingleDishSkyCal::SingleDishSkyCal(const MSMetaInfoForCal& msmc)
     519             :   : VisCal(msmc),
     520             :     SolvableVisCal(msmc),
     521           3 :     currAnt_(-1),
     522           3 :     engineC_(msmc.nSpw(), NULL),
     523           3 :     engineF_(msmc.nSpw(), NULL),
     524           3 :     currentSky_(msmc.nSpw(), NULL),
     525           3 :     currentSkyOK_(msmc.nSpw(), NULL),
     526           6 :     nCorr_(nSpw())
     527             : {
     528           3 :   debuglog << "SingleDishSkyCal::SingleDishSkyCal(const MSMetaInfoForCal& msmc)" << debugpost;
     529           3 :   append() = False;
     530             : 
     531           3 :   initializeSky();
     532           3 :   initializeCorr();
     533           3 : }
     534             : 
     535           0 : SingleDishSkyCal::SingleDishSkyCal(const Int& nAnt)
     536             :   : VisCal(nAnt),
     537             :     SolvableVisCal(nAnt),
     538           0 :     currAnt_(-1),
     539           0 :     engineC_(1, NULL),
     540           0 :     engineF_(1, NULL),
     541           0 :     currentSky_(1, NULL),
     542           0 :     currentSkyOK_(1, NULL),
     543           0 :     nCorr_(nSpw())
     544             : {
     545           0 :   debuglog << "SingleDishSkyCal::SingleDishSkyCal(const Int& nAnt)" << debugpost;
     546           0 :   append() = false;
     547             : 
     548           0 :   initializeSky();
     549           0 :   initializeCorr();
     550           0 : }
     551             : 
     552             : // Destructor
     553          54 : SingleDishSkyCal::~SingleDishSkyCal()
     554             : {
     555          54 :   debuglog << "SingleDishSkyCal::~SingleDishSkyCal()" << debugpost;
     556             : 
     557          54 :   finalizeSky();
     558          54 : }
     559             : 
     560           0 : void SingleDishSkyCal::guessPar(VisBuffer& /*vb*/)
     561             : {
     562           0 : }
     563             : 
     564           0 : void SingleDishSkyCal::differentiate( CalVisBuffer & /*cvb*/)
     565             : {
     566           0 : }
     567             : 
     568           0 : void SingleDishSkyCal::differentiate(VisBuffer& /*vb*/, Cube<Complex>& /*V*/,
     569             :                                      Array<Complex>& /*dV*/, Matrix<Bool>& /*Vflg*/)
     570             : {
     571           0 : }
     572             : 
     573           0 : void SingleDishSkyCal::accumulate(SolvableVisCal* /*incr*/,
     574             :                                   const Vector<Int>& /*fields*/)
     575             : {
     576           0 : }
     577             : 
     578           0 : void SingleDishSkyCal::diffSrc(VisBuffer& /*vb*/, Array<Complex>& /*dV*/)
     579             : {
     580           0 : }
     581             : 
     582           0 : void SingleDishSkyCal::fluxscale(const String& /*outfile*/,
     583             :                                  const Vector<Int>& /*refFieldIn*/,
     584             :                                  const Vector<Int>& /*tranFieldIn*/,
     585             :                                  const Vector<Int>& /*inRefSpwMap*/,
     586             :                                  const Vector<String>& /*fldNames*/,
     587             :                                  const Float& /*inGainThres*/,
     588             :                                  const String& /*antSel*/,
     589             :                                  const String& /*timerangeSel*/,
     590             :                                  const String& /*scanSel*/,
     591             :                                  fluxScaleStruct& /*oFluxScaleStruct*/,
     592             :                                  const String& /*oListFile*/,
     593             :                                  const Bool& /*incremental*/,
     594             :                                  const Int& /*fitorder*/,
     595             :                                  const Bool& /*display*/)
     596             : {
     597           0 : }
     598             : 
     599           0 : void SingleDishSkyCal::listCal(const Vector<Int> /*ufldids*/, const Vector<Int> /*uantids*/,
     600             :                                const Matrix<Int> /*uchanids*/,
     601             :                                //const Int& /*spw*/, const Int& /*chan*/,
     602             :                                const String& /*listfile*/, const Int& /*pagerows*/)
     603             : {
     604           0 : }
     605             : 
     606          23 : void SingleDishSkyCal::setApply(const Record& apply)
     607             : {
     608             :   // Override interp
     609             :   // default frequency interpolation option is 'linearflag'
     610          23 :   Record applyCopy(apply);
     611          23 :   if (applyCopy.isDefined("interp")) {
     612          23 :     String interp = applyCopy.asString("interp");
     613          23 :     if (!interp.contains(',')) {
     614             :       //fInterpType() = "linearflag";
     615          21 :       String newInterp = interp + ",linearflag";
     616          21 :       applyCopy.define("interp", newInterp);
     617          21 :     }
     618          23 :   }
     619             :   else {
     620           0 :     applyCopy.define("interp", "linear,linearflag");
     621             :   }
     622             : 
     623             :   // call parent method
     624          23 :   SolvableVisCal::setApply(applyCopy);
     625          23 : }
     626             : 
     627          17 : void SingleDishSkyCal::fillCalibrationTable(casacore::MeasurementSet const &reference_data){
     628          17 :     String dataColName = (reference_data.tableDesc().isColumn("FLOAT_DATA")) ? "FLOAT_DATA" : "DATA";
     629             : 
     630          17 :     if ( dataColName == "FLOAT_DATA")
     631           7 :         fillCalibrationTable<FloatDataColumnAccessor>(reference_data);
     632             :     else
     633          10 :         fillCalibrationTable<DataColumnAccessor>(reference_data);
     634          17 : }
     635             : 
     636             : template<class DataRealComponentAccessor>
     637          17 : void SingleDishSkyCal::fillCalibrationTable(MeasurementSet const &ms) {
     638          17 :   Int cols[] = {MS::FIELD_ID, MS::ANTENNA1, MS::FEED1,
     639             :                 MS::DATA_DESC_ID};
     640          17 :   Int *colsp = cols;
     641          17 :   Block<Int> sortCols(4, colsp, false);
     642          17 :   MSIter msIter(ms, sortCols, 0.0, false, false);
     643          99 :   for (msIter.origin(); msIter.more(); msIter++) {
     644          41 :     Table const current = msIter.table();
     645          41 :     uInt nrow = current.nrow();
     646          41 :     debuglog << "nrow = " << nrow << debugpost;
     647          41 :     if (nrow == 0) {
     648           0 :       debuglog << "Skip" << debugpost;
     649           0 :       continue;
     650             :     }
     651          41 :     Int ispw = msIter.spectralWindowId();
     652          41 :     if (nChanParList()[ispw] == 4) {
     653             :       // Skip WVR
     654             :       debuglog << "Skip " << ispw
     655           0 :                << "(nchan " << nChanParList()[ispw] << ")"
     656           0 :                << debugpost;
     657           0 :       continue;
     658             :     }
     659             :     debuglog << "Process " << ispw
     660          41 :              << "(nchan " << nChanParList()[ispw] << ")"
     661          82 :              << debugpost;
     662             : 
     663          41 :     Int ifield = msIter.fieldId();
     664          41 :     ScalarColumn<Int> antennaCol(current, "ANTENNA1");
     665          41 :     currAnt_ = antennaCol(0);
     666          41 :     ScalarColumn<Int> feedCol(current, "FEED1");
     667             :     debuglog << "FIELD_ID " << msIter.fieldId()
     668             :              << " ANTENNA1 " << currAnt_
     669             :              << " FEED1 " << feedCol(0)
     670             :              << " DATA_DESC_ID " << msIter.dataDescriptionId()
     671          41 :              << debugpost;
     672          41 :     ScalarColumn<Double> timeCol(current, "TIME");
     673          41 :     ScalarColumn<Double> exposureCol(current, "EXPOSURE");
     674          41 :     ScalarColumn<Double> intervalCol(current, "INTERVAL");
     675          41 :     DataRealComponentAccessor dataCol(current);
     676          41 :     ArrayColumn<Bool> flagCol(current, "FLAG");
     677          41 :     ScalarColumn<Bool> flagRowCol(current, "FLAG_ROW");
     678          41 :     Vector<Double> timeList = timeCol.getColumn();
     679          41 :     Vector<Double> exposure = exposureCol.getColumn();
     680          41 :     Vector<Double> interval = intervalCol.getColumn();
     681          41 :     Vector<Double> timeInterval(timeList.nelements());
     682          41 :     Slice slice1(0, nrow - 1);
     683          41 :     Slice slice2(1, nrow - 1);
     684          41 :     timeInterval(slice1) = timeList(slice2) - timeList(slice1);
     685          41 :     timeInterval[nrow-1] = DBL_MAX;
     686          41 :     IPosition cellShape = flagCol.shape(0);
     687          41 :     size_t nelem = cellShape.product();
     688          41 :     Matrix<Float> dataSum(cellShape, new Float[nelem], TAKE_OVER);
     689          41 :     Matrix<Float> weightSum(cellShape, new Float[nelem], TAKE_OVER);
     690          41 :     dataSum = 0.0f;
     691          41 :     weightSum = 0.0f;
     692          41 :     Matrix<Bool> resultMask(cellShape, new Bool[nelem], TAKE_OVER);
     693          41 :     resultMask = true;
     694          41 :     Vector<Bool> flagRow = flagRowCol.getColumn();
     695          41 :     Double threshold = 1.1;
     696          41 :     Double timeCentroid = 0.0;
     697          41 :     size_t numSpectra = 0;
     698          41 :     Double effectiveExposure = 0.0;
     699        4865 :     for (uInt i = 0; i < nrow; ++i) {
     700        2412 :       if (flagRow(i)) {
     701           0 :         continue;
     702             :       }
     703             : 
     704        2412 :       numSpectra++;
     705        2412 :       timeCentroid += timeList[i];
     706        2412 :       effectiveExposure += exposure[i];
     707             : 
     708        4824 :       Matrix<Bool> mask = !flagCol(i);
     709        2412 :       MaskedArray<Float> mdata(dataCol(i), mask);
     710        2412 :       MaskedArray<Float> weight(Matrix<Float>(mdata.shape(), exposure[i]), mask);
     711        2412 :       dataSum += mdata * weight;
     712        2412 :       weightSum += weight;
     713             : 
     714        2412 :       Double gap = 2.0 * timeInterval[i] /
     715        2412 :         (interval[i] + interval[(i < nrow-1)?i+1:i]);
     716        2412 :       if (gap > threshold) {
     717         423 :         debuglog << "flush accumulated data at row " << i << debugpost;
     718             :         // Here we can safely use data() since internal storeage
     719             :         // is contiguous
     720         423 :         Float *data_ = dataSum.data();
     721         423 :         const Float *weight_ = weightSum.data();
     722         423 :         Bool *flag_ = resultMask.data();
     723     1567605 :         for (size_t j = 0; j < dataSum.nelements(); ++j) {
     724     1567182 :           if (weight_[j] == 0.0f) {
     725        3936 :             data_[j] = 0.0;
     726        3936 :             flag_[j] = false;
     727             :           }
     728             :           else {
     729     1563246 :             data_[j] /= weight_[j];
     730             :           }
     731             :         }
     732             : 
     733         423 :         currSpw() = ispw;
     734         423 :         currField() = ifield;
     735         423 :         timeCentroid /= (Double)numSpectra;
     736         423 :         refTime() = timeCentroid;
     737         423 :         interval_ = effectiveExposure;
     738             : 
     739         423 :         debuglog << "spw " << ispw << ": solveAllRPar.shape=" << solveAllRPar().shape() << " nPar=" << nPar() << " nChanPar=" << nChanPar() << " nElem=" << nElem() << debugpost;
     740             : 
     741         423 :   size_t const nCorr = dataSum.shape()[0];
     742         423 :         Cube<Float> const rpar = dataSum.addDegenerate(1);
     743         423 :         Cube<Bool> const parOK = resultMask.addDegenerate(1);
     744        1269 :         for (size_t iCorr = 0; iCorr < nCorr; ++iCorr) {
     745         846 :           solveAllRPar().yzPlane(iCorr) = rpar.yzPlane(iCorr);
     746         846 :           solveAllParOK().yzPlane(iCorr) = parOK.yzPlane(iCorr);
     747         846 :           solveAllParErr().yzPlane(iCorr) = 0.1; // TODO: this is tentative
     748         846 :           solveAllParSNR().yzPlane(iCorr) = 1.0; // TODO: this is tentative
     749             :         }
     750             : 
     751         423 :         keepNCT();
     752             : 
     753         423 :         dataSum = 0.0f;
     754         423 :         weightSum = 0.0f;
     755         423 :         resultMask = true;
     756         423 :         timeCentroid = 0.0;
     757         423 :         numSpectra = 0;
     758         423 :         effectiveExposure = 0.0;
     759         423 :       }
     760             :     }
     761             :   }
     762          17 : }
     763             : 
     764         568 : void SingleDishSkyCal::keepNCT() {
     765             :   // Call parent to do general stuff
     766             :   //  This adds nElem() rows
     767         568 :   SolvableVisCal::keepNCT();
     768             : 
     769             :   // We are adding to the most-recently added rows
     770         568 :   RefRows rows(ct_->nrow()-nElem(),ct_->nrow()-1,1);
     771         568 :   Vector<Int> ant(nElem(), currAnt_);
     772             : 
     773             :   // update ANTENNA1 and ANTENNA2 with appropriate value
     774         568 :   CTMainColumns ncmc(*ct_);
     775         568 :   ncmc.antenna1().putColumnCells(rows,ant);
     776         568 :   ncmc.antenna2().putColumnCells(rows,ant);
     777             : 
     778             :   // update INTERVAL
     779         568 :   ncmc.interval().putColumnCells(rows,interval_);
     780         568 : }
     781             : 
     782          31 : void SingleDishSkyCal::initSolvePar()
     783             : {
     784          31 :   debuglog << "SingleDishSkyCal::initSolvePar()" << debugpost;
     785         468 :   for (Int ispw=0;ispw<nSpw();++ispw) {
     786             : 
     787         437 :     currSpw()=ispw;
     788             : 
     789         437 :     switch(parType()) {
     790         437 :     case VisCalEnum::REAL: {
     791         437 :       solveAllRPar().resize(nPar(),nChanPar(),nElem());
     792         437 :       solveAllRPar()=0.0;
     793         437 :       solveRPar().reference(solveAllRPar());
     794         437 :       break;
     795             :     }
     796           0 :     default: {
     797           0 :       throw(AipsError("Internal error(Calibrater Module): Unsupported parameter type "
     798           0 :                       "found in SingleDishSkyCal::initSolvePar()"));
     799             :       break;
     800             :     }
     801             :     }//switch
     802             : 
     803         437 :     solveAllParOK().resize(solveAllRPar().shape());
     804         437 :     solveAllParErr().resize(solveAllRPar().shape());
     805         437 :     solveAllParSNR().resize(solveAllRPar().shape());
     806         437 :     solveAllParOK()=false;
     807         437 :     solveAllParErr()=0.0;
     808         437 :     solveAllParSNR()=0.0;
     809         437 :     solveParOK().reference(solveAllParOK());
     810         437 :     solveParErr().reference(solveAllParErr());
     811         437 :     solveParSNR().reference(solveAllParSNR());
     812             :   }
     813          31 :   currSpw()=0;
     814          31 :   currAnt_ = 0;
     815             : 
     816          31 :   interval_.resize(nElem());
     817          31 : }
     818             : 
     819        5076 : void SingleDishSkyCal::syncMeta2(const vi::VisBuffer2& vb)
     820             : {
     821             :   // call method in parent class
     822        5076 :   VisCal::syncMeta2(vb);
     823             : 
     824             :   // fill interval array with exposure
     825        5076 :   interval_.reference(vb.exposure());
     826        5076 :   debuglog << "SingleDishSkyCal::syncMeta2 interval_= " << interval_ << debugpost;
     827             : 
     828        5076 :   setNumberOfCorrelationsPerSpw(vb.ms(), nCorr_);
     829        5076 :   debuglog << "nCorr_ = " << nCorr_ << debugpost;
     830        5076 :   debuglog << "currSpw() = " << currSpw() << debugpost;
     831        5076 :   debuglog << "nPar() = " << nPar() << debugpost;
     832        5076 : }
     833             : 
     834        5076 : void SingleDishSkyCal::syncCalMat(const Bool &/*doInv*/)
     835             : {
     836        5076 :   debuglog << "SingleDishSkyCal::syncCalMat" << debugpost;
     837        5076 :   debuglog << "nAnt()=" << nAnt() << ", nElem()=" << nElem() << ", nBln()=" << nBln() << debugpost;
     838        5076 :   debuglog << "Spw " << currSpw() << "nchanmat, ncalmat" << nChanMat() << ", " << nCalMat() << debugpost;
     839        5076 :   debuglog << "nChanPar = " << nChanPar() << debugpost;
     840        5076 :   currentSky().resize(2, nChanMat(), nCalMat());
     841        5076 :   currentSky().unique();
     842        5076 :   currentSkyOK().resize(currentSky().shape());
     843        5076 :   currentSkyOK().unique();
     844        5076 :   debuglog << "currentSkyOK.shape()=" << currentSkyOK().shape() << debugpost;
     845             : 
     846             :   // sky data from caltable
     847        5076 :   debuglog << "currRPar().shape()=" << currRPar().shape() << debugpost;
     848        5076 :   if (currRPar().shape().product() > 0) {
     849        5076 :     debuglog << "currRPar() = " << currRPar().xzPlane(0) << debugpost;
     850             :   }
     851             : 
     852        5076 :   convertArray(currentSky(), currRPar());
     853        5076 :   currentSkyOK() = currParOK();
     854        5076 :   debuglog << "currentTime() = " << setprecision(16) << currTime() << debugpost;
     855        5076 :   debuglog << "currentSky() = " << currentSky().xzPlane(0) << debugpost;
     856        5076 :   debuglog << "currentSkyOK() = " << currentSkyOK().xzPlane(0) << debugpost;
     857             : 
     858             :   // weight calibration
     859        5076 :   if (calWt())
     860        4464 :     syncWtScale();
     861             : 
     862        5076 :   debuglog << "SingleDishSkyCal::syncCalMat DONE" << debugpost;
     863        5076 : }
     864             : 
     865           0 : void SingleDishSkyCal::syncDiffMat()
     866             : {
     867           0 :   debuglog << "SingleDishSkyCal::syncDiffMat()" << debugpost;
     868           0 : }
     869             : 
     870        4464 : void SingleDishSkyCal::syncWtScale()
     871             : {
     872        4464 :   debuglog << "syncWtScale" << debugpost;
     873             : 
     874             :   // allocate necessary memory
     875        4464 :   currWtScale().resize(currentSky().shape());
     876        4464 :   currWtScale() = 1.0;
     877             : 
     878             :   // Calculate the weight scaling
     879        4464 :   if (tInterpType() == "nearest") {
     880          36 :     calcWtScale<NearestWeightScalingScheme>();
     881             :   }
     882             :   else {
     883        4428 :     calcWtScale<LinearWeightScalingScheme>();
     884             :   }
     885             : 
     886        4464 :   debuglog << "syncWtScale DONE" << debugpost;
     887        4464 : }
     888             : 
     889             : template<class ScalingScheme>
     890        4464 : void SingleDishSkyCal::calcWtScale()
     891             : {
     892        4464 :   debuglog << "calcWtScale<ScalingScheme>" << debugpost;
     893        4464 :   auto const key = std::make_pair(currObs(), currSpw());
     894        4464 :   debuglog << "for obs " << currObs() << " and spw " << currSpw() << debugpost;
     895        4464 :   decltype(wtScaleData_)::iterator iter = wtScaleData_.find(key);
     896        4464 :   map<Int, Matrix<Double> > wtMap;
     897        4464 :   if (iter == wtScaleData_.end()) {
     898          30 :     debuglog << "construct weight scaling data for obs " << currObs() << " spw " << currSpw() << debugpost;
     899          30 :     debuglog << "number of antennas = " << nAnt() << debugpost;
     900         371 :     for (Int iAnt = 0; iAnt < nAnt(); ++iAnt) {
     901         100 :       CTInterface cti(*ct_);
     902         100 :       MSSelection mss;
     903         100 :       mss.setObservationExpr(String::toString(currObs()));
     904         100 :       mss.setSpwExpr(String::toString(currSpw()));
     905         100 :       mss.setAntennaExpr(String::toString(iAnt) + "&&&");
     906         100 :       TableExprNode ten = mss.toTableExprNode(&cti);
     907         100 :       NewCalTable temp;
     908             :       try {
     909         147 :         getSelectedTable(temp, *ct_, ten, "");
     910          94 :       } catch (AipsError x) {
     911          47 :         continue;
     912             :       }
     913          53 :       temp = temp.sort("TIME");
     914          53 :       wtMap.emplace(std::piecewise_construct,
     915             :           std::forward_as_tuple(iAnt),
     916          53 :           std::forward_as_tuple(2, temp.nrow()));
     917          53 :       Matrix<Double> &arr = wtMap.at(iAnt);
     918          53 :       debuglog << "ant " << iAnt << " arr.shape = " << arr.shape() << debugpost;
     919          53 :       ScalarColumn<Double> col(temp, "TIME");
     920          53 :       auto timeCol = arr.row(0);
     921          53 :       col.getColumn(timeCol, False);
     922          53 :       col.attach(temp, "INTERVAL");
     923          53 :       auto intervalCol = arr.row(1);
     924          53 :       debuglog << "timeCol.size() == " << timeCol.size() << " intervalCol.size() = " << intervalCol.size() << debugpost;
     925          53 :       col.getColumn(intervalCol, False);
     926             :     }
     927          30 :     wtScaleData_.insert(std::make_pair(key, wtMap));
     928             :   } else {
     929        4434 :     wtMap = iter->second;
     930             :   }
     931             : 
     932             :   {
     933             :     // for debugging
     934        4464 :     debuglog << "wtMap keys: ";
     935        9342 :     for (decltype(wtMap)::iterator i = wtMap.begin(); i != wtMap.end(); ++i) {
     936        4878 :       debuglog << i->first << " ";
     937             :     }
     938        4464 :     debuglog << debugpost;
     939             :   }
     940             : 
     941       15088 :   for (Int iAnt = 0; iAnt < nAnt(); ++iAnt) {
     942        5746 :     decltype(wtMap)::iterator i = wtMap.find(iAnt);
     943        5746 :     if (i == wtMap.end()) {
     944         868 :       continue;
     945             :     }
     946        4878 :     auto const &mat = i->second;
     947        4878 :     debuglog << "matrix shape " << mat.shape() << debugpost;
     948        4878 :     Vector<Double> const &timeCol = mat.row(0);
     949        4878 :     Vector<Double> const &intervalCol = mat.row(1);
     950        4878 :     size_t nrow = timeCol.size();
     951        4878 :     debuglog << "timeCol = " << timeCol << debugpost;
     952        4878 :     debuglog << "intervalCol = " << intervalCol << debugpost;
     953        4878 :     debuglog << "iAnt " << iAnt << " nrow=" << nrow << debugpost;
     954        4878 :     if (currTime() < timeCol[0]) {
     955         141 :       debuglog << "Use nearest OFF weight (0)" << debugpost;
     956         141 :       currWtScale().xyPlane(iAnt) = ScalingScheme::SimpleScale(interval_[iAnt], intervalCol[0]);
     957             :     }
     958        4737 :     else if (currTime() > timeCol[nrow-1]) {
     959         976 :       debuglog << "Use nearest OFF weight (" << nrow-1 << ")" << debugpost;
     960         976 :       currWtScale().xyPlane(iAnt) = ScalingScheme::SimpleScale(interval_[iAnt], intervalCol[nrow-1]);
     961             :     }
     962             :     else {
     963        3761 :       debuglog << "Use interpolated OFF weight" << debugpost;
     964       85587 :       for (size_t irow = 0; irow < nrow ; ++irow) {
     965       85587 :         if (currTime() == timeCol[irow]) {
     966         194 :           currWtScale().xyPlane(iAnt) = ScalingScheme::SimpleScale(interval_[iAnt], intervalCol[irow]);
     967         194 :           break;
     968             :         }
     969       85393 :         else if (currTime() < timeCol[irow]) {
     970        3567 :           currWtScale().xyPlane(iAnt) = ScalingScheme::InterpolateScale(currTime(), interval_[iAnt],
     971             :                                                                        timeCol[irow-1], intervalCol[irow-1],
     972             :                                                                        timeCol[irow], intervalCol[irow]);
     973        3567 :           break;
     974             :         }
     975             :       }
     976             :     }
     977             :   }
     978        4464 :   debuglog << "currWtScale() = " << currWtScale().xzPlane(0) << debugpost;
     979             : 
     980        4464 :   debuglog << "calcWtScale<ScalingScheme> DONE" << debugpost;
     981        4464 : }
     982             : 
     983           0 : Float SingleDishSkyCal::calcPowerNorm(Array<Float>& /*amp*/, const Array<Bool>& /*ok*/)
     984             : {
     985           0 :   return 0.0f;
     986             : }
     987             : 
     988           0 : void SingleDishSkyCal::applyCal(VisBuffer& /*vb*/, Cube<Complex>& /*Vout*/, Bool /*trial*/)
     989             : {
     990           0 :   throw AipsError("Single dish calibration doesn't support applyCal. Please use applyCal2");
     991             : }
     992             : 
     993        5076 : void SingleDishSkyCal::applyCal2(vi::VisBuffer2 &vb, Cube<Complex> &Vout, Cube<Float> &Wout,
     994             :                                  Bool trial)
     995             : {
     996        5076 :   debuglog << "SingleDishSkyCal::applycal2" << debugpost;
     997        5076 :   debuglog << "nrow, nchan=" << vb.nRows() << "," << vb.nChannels() << debugpost;
     998        5076 :   debuglog << "antenna1: " << vb.antenna1() << debugpost;
     999        5076 :   debuglog << "antenna2: " << vb.antenna2() << debugpost;
    1000        5076 :   debuglog << "spw: " << vb.spectralWindows() << debugpost;
    1001        5076 :   debuglog << "field: " << vb.fieldId() << debugpost;
    1002             : 
    1003             :   // References to VB2's contents' _data_
    1004        5076 :   Vector<Bool> flagRv(vb.flagRow());
    1005        5076 :   Vector<Int>  a1v(vb.antenna1());
    1006        5076 :   Vector<Int>  a2v(vb.antenna2());
    1007        5076 :   Cube<Bool> flagCube(vb.flagCube());
    1008        5076 :   Cube<Complex> visCube(Vout);
    1009        5076 :   ArrayIterator<Float> wt(Wout,2);
    1010        5076 :   Matrix<Float> wtmat;
    1011             : 
    1012             :   // Data info/indices
    1013             :   Int* dataChan;
    1014        5076 :   Bool* flagR=&flagRv(0);
    1015        5076 :   Int* a1=&a1v(0);
    1016        5076 :   Int* a2=&a2v(0);
    1017             : 
    1018             :   // iterate rows
    1019        5076 :   Int nRow=vb.nRows();
    1020        5076 :   Int nChanDat=vb.nChannels();
    1021        5076 :   Vector<Int> dataChanv(vb.getChannelNumbers(0));  // All rows have same chans
    1022             :   //    cout << currSpw() << " startChan() = " << startChan() << " nChanMat() = " << nChanMat() << " nChanDat="<<nChanDat <<endl;
    1023             : 
    1024             :   // setup calibration engine
    1025        5076 :   engineC().setNumChannel(nChanDat);
    1026        5076 :   engineC().setNumPolarization(vb.nCorrelations());
    1027             : 
    1028        5076 :   debuglog << "typesize=" << engineC().typesize() << debugpost;
    1029             : 
    1030             :   // Matrix slice of visCube
    1031             :   // TODO: storage must be aligned for future use
    1032        5076 :   Matrix<Complex> visCubeSlice;
    1033        5076 :   Matrix<Bool> flagCubeSlice;
    1034             : 
    1035       10566 :   for (Int row=0; row<nRow; row++,flagR++,a1++,a2++) {
    1036        5490 :     debuglog << "spw: " << currSpw() << " antenna: " << *a1 << debugpost;
    1037        5490 :     assert(*a1 == *a2);
    1038             : 
    1039             :     // Solution channel registration
    1040        5490 :     Int solCh0(0);
    1041        5490 :     dataChan=&dataChanv(0);
    1042             : 
    1043             :     // If cal _parameters_ are not freqDep (e.g., a delay)
    1044             :     //  the startChan() should be the same as the first data channel
    1045        5490 :     if (freqDepMat() && !freqDepPar())
    1046           0 :       startChan()=(*dataChan);
    1047             : 
    1048             :     // Solution and data array registration
    1049        5490 :     engineC().sync(currentSky()(0,solCh0,*a1), currentSkyOK()(0,solCh0,*a1));
    1050        5490 :     visCubeSlice.reference(visCube.xyPlane(row));
    1051        5490 :     flagCubeSlice.reference(flagCube.xyPlane(row));
    1052             : 
    1053        5490 :     if (trial) {
    1054             :       // only update flag info
    1055           0 :       engineC().flag(flagCubeSlice);
    1056             :     }
    1057             :     else {
    1058             :       // apply calibration
    1059        5490 :       engineC().apply(visCubeSlice, flagCubeSlice);
    1060             :     }
    1061             : 
    1062             :     // If requested, update the weights
    1063        5490 :     if (!trial && calWt()) {
    1064        4878 :       wtmat.reference(wt.array());
    1065        4878 :       updateWt2(wtmat,*a1);
    1066             :     }
    1067             : 
    1068        5490 :     if (!trial)
    1069        5490 :       wt.next();
    1070             : 
    1071             :   }
    1072        5076 : }
    1073             : 
    1074          31 : void SingleDishSkyCal::selfGatherAndSolve(VisSet& vs, VisEquation& /*ve*/)
    1075             : {
    1076          31 :     debuglog << "SingleDishSkyCal::selfGatherAndSolve()" << debugpost;
    1077             : 
    1078          31 :     MeasurementSet const &user_selection = vs.iter().ms();
    1079             : 
    1080          31 :     debuglog << "nspw = " << nSpw() << debugpost;
    1081             :     // Get and store the number of channels per spectral window
    1082          31 :     fillNChanParList(MeasurementSet(msName()), nChanParList());
    1083             : 
    1084             :     // Get and store the number of correlations per spectral window
    1085          31 :     setNumberOfCorrelationsPerSpw(user_selection, nCorr_);
    1086          31 :     debuglog << "nCorr_ = " << nCorr_ << debugpost;
    1087             : 
    1088             :     // Create a new in-memory calibration table to fill up
    1089          31 :     createMemCalTable();
    1090             : 
    1091             :     // Setup shape of solveAllRPar
    1092          31 :     nElem() = 1;
    1093          31 :     initSolvePar();
    1094             : 
    1095             :     // Select from user selection reference data associated with science target
    1096          31 :     MeasurementSet reference_data = selectReferenceData(user_selection);
    1097          29 :     logSink().origin(LogOrigin("SingleDishSkyCal","selfGatherAndSolve"));
    1098             :     {
    1099             :         // Log row numbers in a readable way
    1100          29 :         std::ostringstream msg;
    1101          29 :         auto * us_num_fmt = new CommaSeparatedThousands();
    1102          29 :         std::locale us_like_locale(std::locale::classic(), us_num_fmt);
    1103             :         // us_like_locale is responsible for deleting us_num_fmt from its destructor
    1104             :         // Ref: https://en.cppreference.com/w/cpp/locale/locale/locale
    1105             :         //      Constructor (7) + Notes section
    1106             :         // So we must NOT delete us_num_fmt.
    1107          29 :         msg.imbue(us_like_locale);
    1108          29 :         msg << "Selected: " << std::right << std::setw(10) << reference_data.nrow() << " rows of reference data" << '\n'
    1109          29 :             << "out of  : " << std::right << std::setw(10) << user_selection.nrow() << " rows of user-selected data";
    1110          29 :         logSink() << msg.str() << LogIO::POST;
    1111          29 :     }
    1112          29 :     logSink().origin(LogOrigin());
    1113          29 :     if (reference_data.nrow() == 0)
    1114           1 :         throw AipsError("No reference integration found in user-selected data. Please double-check your data selection criteria.");
    1115             : 
    1116             :     // Fill observing-mode-dependent columns of the calibration table
    1117             :     // Implementation is observing-mode-specific
    1118          28 :     fillCalibrationTable(reference_data);
    1119             : 
    1120             :     // Fill remaining columns of the calibration table,
    1121             :     // which are computed the same way for all observing modes
    1122             :     // ---- 1) FIELD_ID, SCAN_NUMBER, OBSERVATION_ID columns
    1123          28 :     assignCTScanField(*ct_, msName());
    1124             : 
    1125             :     // ---- 2) WEIGHT column
    1126             :     // update weight without Tsys
    1127             :     // formula is chanWidth [Hz] * interval [sec]
    1128          28 :     updateWeight(*ct_);
    1129             : 
    1130             :     // Store calibration table on disk
    1131          28 :     storeNCT();
    1132          29 : }
    1133             : 
    1134          54 : void SingleDishSkyCal::initializeSky()
    1135             : {
    1136          54 :   debuglog << "SingleDishSkyCal::initializeSky()" << debugpost;
    1137         813 :   for (Int ispw=0;ispw<nSpw(); ispw++) {
    1138         759 :     currentSky_[ispw] = new Cube<Complex>();
    1139         759 :     currentSkyOK_[ispw] = new Cube<Bool>();
    1140         759 :     engineC_[ispw] = new SkyCal<Complex, Complex>();
    1141             :   }
    1142          54 : }
    1143             : 
    1144          54 : void SingleDishSkyCal::finalizeSky()
    1145             : {
    1146         813 :   for (Int ispw=0;ispw<nSpw(); ispw++) {
    1147         759 :     if (currentSky_[ispw]) delete currentSky_[ispw];
    1148         759 :     if (currentSkyOK_[ispw]) delete currentSkyOK_[ispw];
    1149         759 :     if (engineC_[ispw]) delete engineC_[ispw];
    1150         759 :     if (engineF_[ispw]) delete engineF_[ispw];
    1151             :   }
    1152             : 
    1153          54 : }
    1154             : 
    1155          54 : void SingleDishSkyCal::initializeCorr()
    1156             : {
    1157          54 :   File msPath(msName());
    1158          54 :   if (msPath.exists()) {
    1159          54 :     setNumberOfCorrelationsPerSpw(MeasurementSet(msName()), nCorr_);
    1160             :   } else {
    1161           0 :     nCorr_ = 1;
    1162             :   }
    1163          54 : }
    1164             : 
    1165        4878 : void SingleDishSkyCal::updateWt2(Matrix<Float> &weight, const Int &antenna1)
    1166             : {
    1167             :   // apply weight scaling factor
    1168        4878 :   Matrix<Float> const factor = currWtScale().xyPlane(antenna1);
    1169        4878 :   debuglog << "factor.shape() = " << factor.shape() << debugpost;
    1170        4878 :   debuglog << "weight.shape() = " << weight.shape() << debugpost;
    1171        4878 :   debuglog << "weight = " << weight << debugpost;
    1172             : 
    1173        4878 :   auto const wtShape = weight.shape();
    1174        4878 :   size_t const nCorr = wtShape[0];
    1175        4878 :   size_t const nChan = wtShape[1];
    1176             :   // for each correlation
    1177       14624 :   for (size_t iCorr = 0; iCorr < nCorr; ++iCorr) {
    1178        9746 :     auto wSlice = weight.row(iCorr);
    1179        9746 :     auto const fSlice = factor.row(iCorr);
    1180        9746 :     if (fSlice.nelements() == nChan) {
    1181        8224 :       wSlice *= fSlice;
    1182        1522 :     } else if (nChan == 1) {
    1183             :       // take mean of spectral weight factor to apply it to scalar weight
    1184        1522 :       wSlice *= mean(fSlice);
    1185             :     } else {
    1186           0 :       throw AipsError("Shape mismatch between input weight and weight scaling factor");
    1187             :     }
    1188        9746 :   }
    1189        4878 : }
    1190             : 
    1191             : //
    1192             : // SingleDishPositionSwitchCal
    1193             : //
    1194             : 
    1195             : // Constructor
    1196          27 : SingleDishPositionSwitchCal::SingleDishPositionSwitchCal(VisSet& vs)
    1197             :   : VisCal(vs),
    1198          27 :     SingleDishSkyCal(vs)
    1199             : {
    1200          27 :   debuglog << "SingleDishPositionSwitchCal::SingleDishPositionSwitchCal(VisSet& vs)" << debugpost;
    1201          27 : }
    1202             : 
    1203           3 : SingleDishPositionSwitchCal::SingleDishPositionSwitchCal(const MSMetaInfoForCal& msmc)
    1204             :   : VisCal(msmc),
    1205           3 :     SingleDishSkyCal(msmc)
    1206             : {
    1207           3 :   debuglog << "SingleDishPositionSwitchCal::SingleDishPositionSwitchCal(const MSMetaInfoForCal& msmc)" << debugpost;
    1208           3 : }
    1209             : 
    1210           0 : SingleDishPositionSwitchCal::SingleDishPositionSwitchCal(const Int& nAnt)
    1211             :   : VisCal(nAnt),
    1212           0 :     SingleDishSkyCal(nAnt)
    1213             : {
    1214           0 :   debuglog << "SingleDishPositionSwitchCal::SingleDishPositionSwitchCal(const Int& nAnt)" << debugpost;
    1215           0 : }
    1216             : 
    1217             : // Destructor
    1218          60 : SingleDishPositionSwitchCal::~SingleDishPositionSwitchCal()
    1219             : {
    1220          30 :   debuglog << "SingleDishPositionSwitchCal::~SingleDishPositionSwitchCal()" << debugpost;
    1221          60 : }
    1222             : 
    1223          12 : MeasurementSet SingleDishPositionSwitchCal::selectReferenceData(MeasurementSet const &user_selection)
    1224             : {
    1225          12 :     std::ostringstream qry;
    1226          12 :     constexpr auto eol = '\n';
    1227          12 :     qry << "using style python" << eol;
    1228             :     qry << "with [" << eol
    1229             :         << "select" << eol
    1230          12 :         << "    [select TELESCOPE_NAME from ::OBSERVATION][OBSERVATION_ID] as TELESCOPE_NAME," << eol;
    1231             :     // Purposively not using TAQL's default mscal UDF library alias for derivedmscal
    1232             :     // to workaround a bug in casacore UDFBase::createUDF
    1233             :     qry << "    derivedmscal.spwcol('NUM_CHAN') as NUM_CHAN" << eol
    1234             :         << "from" << eol
    1235             :         << "    $1" << eol
    1236             :         << "] as metadata" << eol
    1237             :         << "select * from $1 , metadata" << eol
    1238          12 :         << "where " << eol;
    1239             :     // Row contains single-dish auto-correlation data,
    1240          12 :     qry << "    ( ANTENNA1 == ANTENNA2 ) and" << eol ;
    1241          12 :     qry << "    ( FEED1 == FEED2 ) and" << eol ;
    1242             : 
    1243             :     // has not been marked as invalid,
    1244          12 :     qry << "    ( not(FLAG_ROW) ) and " << eol ;
    1245             :     // holds at least 1 data marked as valid,
    1246          12 :     qry << "    ( not(all(FLAG)) ) and " << eol;
    1247             :     // ---- Note: both conditions above are required because FLAG and FLAG_ROW are not synchronized:
    1248             :     //            a valid row (FLAG_ROW=False) may contain only invalid data: all(FLAG)=True
    1249             : 
    1250             :     // has observational intent: OBSERVE_TARGET#OFF_SOURCE,
    1251             :     qry << "    ( STATE_ID in [ " << eol
    1252             :         << "          select rowid() " << eol
    1253             :         << "          from ::STATE" << eol
    1254             :         << "          where " << eol
    1255             :         << "              upper(OBS_MODE) ~ m/^OBSERVE_TARGET#OFF_SOURCE/ " << eol
    1256             :         << "          ]" << eol
    1257          12 :         << "    ) and" << eol;
    1258             :     // excluding - for ALMA - rows from Water Vapor Radiometers spectral windows, which have 4 channels
    1259             :     qry << "    (" << eol
    1260             :         << "        ( metadata.TELESCOPE_NAME != 'ALMA' ) or" << eol
    1261             :         << "        (" << eol
    1262             :         << "            ( metadata.TELESCOPE_NAME == 'ALMA' ) and" << eol
    1263             :         << "            ( metadata.NUM_CHAN != 4 )" << eol
    1264             :         << "        )" << eol
    1265          12 :         << "     )" << eol;
    1266             :     debuglog << "SingleDishPositionSwitchCal::selectReferenceData(): taql query:" << eol
    1267          12 :              << qry.str() << debugpost;
    1268          36 :     return MeasurementSet(tableCommand(qry.str(), user_selection).table());
    1269          12 : }
    1270             : 
    1271          11 : void SingleDishPositionSwitchCal::fillCalibrationTable(casacore::MeasurementSet const &reference_data){
    1272          11 :     String dataColName = (reference_data.tableDesc().isColumn("FLOAT_DATA")) ? "FLOAT_DATA" : "DATA";
    1273             : 
    1274          11 :     if ( dataColName == "FLOAT_DATA")
    1275           4 :         fillCalibrationTable<FloatDataColumnAccessor>(reference_data);
    1276             :     else
    1277           7 :         fillCalibrationTable<DataColumnAccessor>(reference_data);
    1278          11 : }
    1279             : 
    1280             : template<class DataRealComponentAccessor>
    1281          11 : void SingleDishPositionSwitchCal::fillCalibrationTable(casacore::MeasurementSet const &reference_data)
    1282             : {
    1283          11 :     debuglog << "SingleDishPositionSwitchCal::fillCalibrationTable()" << debugpost;
    1284             : 
    1285             :     // Sort columns define the granularity at which we average data obtained
    1286             :     // by observing the reference position associated with the science target
    1287          11 :     constexpr size_t nSortColumns = 8;
    1288          11 :     Int columns[nSortColumns] = {
    1289             :         MS::OBSERVATION_ID,
    1290             :         MS::PROCESSOR_ID,
    1291             :         MS::FIELD_ID,
    1292             :         MS::ANTENNA1,
    1293             :         MS::FEED1,
    1294             :         MS::DATA_DESC_ID,
    1295             :         MS::SCAN_NUMBER,
    1296             :         MS::STATE_ID
    1297             :     };
    1298          11 :     Int *columnsPtr = columns;
    1299          11 :     Block<Int> sortColumns(nSortColumns, columnsPtr, false);
    1300             : 
    1301             :     // Iterator
    1302          11 :     constexpr Bool doGroupAllTimesTogether = true;
    1303          11 :     constexpr Double groupAllTimesTogether = doGroupAllTimesTogether ? 0.0 : -1.0;
    1304             : 
    1305          11 :     constexpr Bool doAddDefaultSortColumns = false;
    1306          11 :     constexpr Bool doStoreSortedTableOnDisk = false;
    1307             : 
    1308          11 :     MSIter msIter(reference_data, sortColumns,
    1309             :             groupAllTimesTogether, doAddDefaultSortColumns, doStoreSortedTableOnDisk);
    1310             : 
    1311             :     // Main loop: compute values of calibration table's columns
    1312         301 :     for (msIter.origin(); msIter.more(); msIter++) {
    1313         145 :         const auto iterTable = msIter.table();
    1314         145 :         const auto iterRows = iterTable.nrow();
    1315         145 :         if (iterRows == 0) continue;
    1316             : 
    1317             :         // TIME column of calibration table: mean of selected MAIN.TIME
    1318         145 :         ScalarColumn<Double> timeCol(iterTable, "TIME");
    1319         145 :         refTime() = mean(timeCol.getColumn());
    1320             : 
    1321             :         // FIELD_ID column of calibration table
    1322         145 :         currSpw() = msIter.spectralWindowId();
    1323             : 
    1324             :         // SPECTRAL_WINDOW_ID column of calibration table
    1325         145 :         currField() = msIter.fieldId();
    1326             : 
    1327             :         // ANTENNA1, ANTENNA2 columns of calibration table
    1328         145 :         ScalarColumn<Int> antenna1Col(iterTable, "ANTENNA1");
    1329         145 :         currAnt_ = antenna1Col(0);
    1330             : 
    1331             :         // INTERVAL column of calibration table: sum of selected MAIN.EXPOSURE
    1332         145 :         ScalarColumn<Double> exposureCol(iterTable, "EXPOSURE");
    1333         145 :         const auto exposure = exposureCol.getColumn();
    1334         145 :         interval_ = sum(exposure);
    1335             : 
    1336             :         // SCAN_NUMBER, OBSERVATION_ID columns of calibration table
    1337             :         // Not computed/updated here
    1338             : #ifdef SDCALSKY_DEBUG
    1339             :         ScalarColumn<Int> scanNumberCol(iterTable, "SCAN_NUMBER");
    1340             :         const auto scan_number = scanNumberCol(0);
    1341             :         ScalarColumn<Int> stateIdCol(iterTable, "STATE_ID");
    1342             :         const auto state_id = stateIdCol(0);
    1343             :         cout << "field=" << currField()
    1344             :              << " ant=" << currAnt_
    1345             :              << " ddid=" << msIter.dataDescriptionId()
    1346             :              << " spw=" << currSpw()
    1347             :              << " scan_number=" << scan_number
    1348             :              << " state_id=" << state_id
    1349             :              << " nrows=" << iterRows
    1350             :              << endl;
    1351             : #endif
    1352             : 
    1353             :         // FPARAM column of calibration table: weighted mean of valid data, weight = exposure
    1354             :         // + PARAMERR, FLAG, SNR
    1355             :         // ---- Get data shape from FLAG column
    1356         145 :         ArrayColumn<Bool> flagCol(iterTable, "FLAG");
    1357         145 :         const auto dataShape = flagCol.shape(0);
    1358         145 :         const auto nCorr = dataShape[0];
    1359         145 :         const auto nChannels = dataShape[1];
    1360             :         // ---- Initialize accumulators
    1361         145 :         Matrix<Float> weightedMean(nCorr, nChannels, 0.0f);
    1362         145 :         Matrix<Float> weightsSums(nCorr, nChannels, 0.0f);
    1363             :         // ---- Compute weighted mean of valid data
    1364         145 :         DataRealComponentAccessor dataAccessor(iterTable);
    1365         482 :         for (std::remove_const<decltype(iterRows)>::type iterRow = 0; iterRow < iterRows ; iterRow++){
    1366         674 :             Matrix<Bool> dataIsValid = not flagCol(iterRow);
    1367         337 :             MaskedArray<Float> validData(dataAccessor(iterRow), dataIsValid);
    1368         337 :             const auto rowExposure = static_cast<Float>(exposure[iterRow]);
    1369         337 :             weightedMean += validData * rowExposure;
    1370         337 :             MaskedArray<Float> validWeight(Matrix<Float>(validData.shape(), rowExposure), dataIsValid);
    1371         337 :             weightsSums += validWeight;
    1372             :         }
    1373         145 :         const Matrix<Bool> weightsSumsIsNonZero = ( weightsSums != 0.0f );
    1374         145 :         weightedMean /= MaskedArray<Float>(weightsSums,weightsSumsIsNonZero);
    1375             :         // ---- Update solveAll*() members
    1376         145 :         const Cube<Float> realParam = weightedMean.addDegenerate(1);
    1377         145 :         const Cube<Bool> realParamIsValid = weightsSumsIsNonZero.addDegenerate(1);
    1378         429 :         for (std::remove_const<decltype(nCorr)>::type corr = 0; corr < nCorr; corr++) {
    1379         284 :           solveAllRPar().yzPlane(corr) = realParam.yzPlane(corr); // FPARAM
    1380         284 :           solveAllParOK().yzPlane(corr) = realParamIsValid.yzPlane(corr);  // not FLAG
    1381         284 :           solveAllParErr().yzPlane(corr) = 0.1; // PARAMERR. TODO: this is tentative
    1382         284 :           solveAllParSNR().yzPlane(corr) = 1.0; // SNR. TODO: this is tentative
    1383             :         }
    1384             : 
    1385             :         // WEIGHT column of calibration table
    1386             :         //
    1387             : 
    1388             :         // Update in-memory calibration table
    1389         145 :         keepNCT();
    1390             :     }
    1391          11 : }
    1392             : 
    1393             : //
    1394             : // SingleDishRasterCal
    1395             : //
    1396             : 
    1397             : // Constructor
    1398          10 : SingleDishRasterCal::SingleDishRasterCal(VisSet& vs)
    1399             :   : VisCal(vs),
    1400             :     SingleDishSkyCal(vs),
    1401          10 :     fraction_(0.1),
    1402          10 :     numEdge_(-1)
    1403             : {
    1404          10 :   debuglog << "SingleDishRasterCal::SingleDishRasterCal(VisSet& vs)" << debugpost;
    1405          10 : }
    1406             : 
    1407           0 : SingleDishRasterCal::SingleDishRasterCal(const MSMetaInfoForCal& msmc)
    1408             :   : VisCal(msmc),
    1409             :     SingleDishSkyCal(msmc),
    1410           0 :     fraction_(0.1),
    1411           0 :     numEdge_(-1)
    1412             : {
    1413           0 :   debuglog << "SingleDishRasterCal::SingleDishRasterCal(const MSMetaInfoForCal& msmc)" << debugpost;
    1414           0 : }
    1415             : 
    1416           0 : SingleDishRasterCal::SingleDishRasterCal(const Int& nAnt)
    1417             :   : VisCal(nAnt),
    1418           0 :     SingleDishSkyCal(nAnt)
    1419             : {
    1420           0 :   debuglog << "SingleDishRasterCal::SingleDishRasterCal(const Int& nAnt)" << debugpost;
    1421           0 : }
    1422             : 
    1423             : // Destructor
    1424          20 : SingleDishRasterCal::~SingleDishRasterCal()
    1425             : {
    1426          10 :   debuglog << "SingleDishRasterCal::~SingleDishRasterCal()" << debugpost;
    1427          20 : }
    1428             : 
    1429          10 : void SingleDishRasterCal::setSolve(const Record& solve)
    1430             : {
    1431             :   // edge detection parameter for otfraster mode
    1432          10 :   if (solve.isDefined("fraction")) {
    1433          10 :     fraction_ = solve.asFloat("fraction");
    1434             :   }
    1435          10 :   if (solve.isDefined("numedge")) {
    1436          10 :     numEdge_ = solve.asInt("numedge");
    1437             :   }
    1438             : 
    1439          10 :   logSink() << "fraction=" << fraction_ << endl
    1440          10 :             << "numedge=" << numEdge_ << LogIO::POST;
    1441             : 
    1442             :   // call parent setSolve
    1443          10 :   SolvableVisCal::setSolve(solve);
    1444          10 : }
    1445             : 
    1446          10 : MeasurementSet SingleDishRasterCal::selectReferenceData(MeasurementSet const &ms)
    1447             : {
    1448          10 :   debuglog << "SingleDishRasterCal::selectReferenceData" << debugpost;
    1449          10 :   const Record specify;
    1450          10 :   std::ostringstream oss;
    1451          10 :   oss << "SELECT FROM $1 WHERE ";
    1452          10 :   String delimiter = "";
    1453             :   // for (Int iant = 0; iant < nAnt(); ++iant) {
    1454             :   //   Vector<String> timeRangeList = detectRaster(msName(), iant, fraction_, numEdge_);
    1455             :   //   debuglog << "timeRangeList=" << ::toString(timeRangeList) << debugpost;
    1456             :   //   oss << delimiter;
    1457             :   //   oss << "(ANTENNA1 == " << iant << " && ANTENNA2 == " << iant << " && (";
    1458             :   //   String separator = "";
    1459             :   //   for (size_t i = 0; i < timeRangeList.size(); ++i) {
    1460             :   //     if (timeRangeList[i].size() > 0) {
    1461             :   //    oss << separator << "(" << timeRangeList[i] << ")";
    1462             :   //    separator = " || ";
    1463             :   //     }
    1464             :   //   }
    1465             :   //   oss << "))";
    1466             :   //   debuglog << "oss.str()=" << oss.str() << debugpost;
    1467             :   //   delimiter = " || ";
    1468             :   // }
    1469             :   // use ANTENNA 0 for reference antenna
    1470          10 :   Vector<String> timeRangeList = detectRaster(ms, 0, fraction_, numEdge_);
    1471           8 :   debuglog << "timeRangeList=" << ::toString(timeRangeList) << debugpost;
    1472           8 :   oss << delimiter;
    1473           8 :   oss << "(ANTENNA1 == ANTENNA2 && (";
    1474           8 :   String separator = "";
    1475          72 :   for (size_t i = 0; i < timeRangeList.size(); ++i) {
    1476          64 :     if (timeRangeList[i].size() > 0) {
    1477          48 :       oss << separator << "(" << timeRangeList[i] << ")";
    1478          48 :       separator = " || ";
    1479             :     }
    1480             :   }
    1481           8 :   oss << "))";
    1482           8 :   debuglog << "oss.str()=" << oss.str() << debugpost;
    1483             : 
    1484             :   oss //<< ")"
    1485           8 :       << " ORDER BY FIELD_ID, ANTENNA1, FEED1, DATA_DESC_ID, TIME";
    1486          24 :   return MeasurementSet(tableCommand(oss.str(), ms).table());
    1487          14 : }
    1488             : 
    1489             : //
    1490             : // SingleDishOtfCal
    1491             : //
    1492             : 
    1493             : // Constructor
    1494          14 : SingleDishOtfCal::SingleDishOtfCal(VisSet& vs)
    1495             :   : VisCal(vs),
    1496             :     SingleDishSkyCal(vs),
    1497          14 :     fraction_(0.1),
    1498          14 :         pixel_scale_(0.5),
    1499          14 :         msSel_(vs.iter().ms())
    1500             : {
    1501          14 :   debuglog << "SingleDishOtfCal::SingleDishOtfCal(VisSet& vs)" << debugpost;
    1502          14 : }
    1503             : 
    1504             : /*
    1505             : SingleDishOtfCal::SingleDishOtfCal(const MSMetaInfoForCal& msmc)
    1506             :   : VisCal(msmc),
    1507             :     SingleDishSkyCal(msmc),
    1508             :     fraction_(0.1),
    1509             :         pixel_scale_(0.5),
    1510             :         msSel_(vs.iter().ms()) ************need MS!
    1511             : {
    1512             :   debuglog << "SingleDishOtfCal::SingleDishOtfCal(VisSet& vs)" << debugpost;
    1513             : }
    1514             : */
    1515           9 : void SingleDishOtfCal::setSolve(const Record& solve)
    1516             : {
    1517             :   // edge detection parameter for otfraster mode
    1518           9 :   if (solve.isDefined("fraction")) {
    1519           9 :     fraction_ = solve.asFloat("fraction");
    1520             :   }
    1521             : 
    1522           9 :   logSink() << "fraction=" << fraction_ << LogIO::POST;
    1523             : 
    1524             :   // call parent setSolve
    1525           9 :   SolvableVisCal::setSolve(solve);
    1526           9 : }
    1527             : 
    1528             : /*
    1529             : SingleDishOtfCal::SingleDishOtfCal(const Int& nAnt)
    1530             :   : VisCal(nAnt),
    1531             :     SingleDishSkyCal(nAnt)
    1532             : {
    1533             :   debuglog << "SingleDishOtfCal::SingleDishOtfCal(const Int& nAnt)" << debugpost;
    1534             : }
    1535             : */
    1536             : 
    1537             : // Destructor
    1538          28 : SingleDishOtfCal::~SingleDishOtfCal()
    1539             : {
    1540          14 :   debuglog << "SingleDishOtfCal::~SingleDishOtfCal()" << debugpost;
    1541          28 : }
    1542             : 
    1543           9 : MeasurementSet SingleDishOtfCal::selectReferenceData(MeasurementSet const &ms)
    1544             : {
    1545           9 :   PointingDirectionCalculator calc(ms);
    1546           9 :   calc.setDirectionListMatrixShape(PointingDirectionCalculator::ROW_MAJOR);
    1547             : 
    1548             :   // Check the coordinates system type used to store the pointing measurements
    1549           9 :   const MSPointing& tbl_pointing = ms.pointing();
    1550           9 :   MSPointingColumns pointing_cols(tbl_pointing);
    1551           9 :   const ROArrayMeasColumn< MDirection >& direction_cols =  pointing_cols.directionMeasCol();
    1552           9 :   const MeasRef<MDirection>& direction_ref_frame = direction_cols.getMeasRef();
    1553           9 :   uInt ref_frame_type = direction_ref_frame.getType();
    1554             : 
    1555             :   // If non-celestial coordinates (AZEL*) are used, convert to celestial ones
    1556           9 :   switch (ref_frame_type) {
    1557           0 :   case MDirection::AZEL : // Fall through
    1558             :   case MDirection::AZELSW :
    1559             :   case MDirection::AZELGEO :
    1560             :   case MDirection::AZELSWGEO : {
    1561           0 :         const String& ref_frame_name = MDirection::showType(ref_frame_type);
    1562           0 :         debuglog << "Reference frame of pointings coordinates is non-celestial: " << ref_frame_name << debugpost;
    1563           0 :         String j2000(MDirection::showType(MDirection::J2000));
    1564           0 :         debuglog << "Pointings coordinates will be converted to: " << j2000 << debugpost;
    1565           0 :       calc.setFrame(j2000);
    1566           0 :       }
    1567             :   }
    1568             :   // Extract edge pointings for each (field_id,antenna,spectral window) triple
    1569             :   // MeasurementSet 2 specification / FIELD table:
    1570             :   //   . FIELD_ID column is removed
    1571             :   //   . FIELD table is directly indexed using the FIELD_ID value in MAIN
    1572           9 :   const MSField& tbl_field = ms.field();
    1573           9 :   const String &field_col_name_str = tbl_field.columnName(MSField::MSFieldEnums::SOURCE_ID);
    1574          18 :   ScalarColumn<Int> source_id_col(tbl_field, field_col_name_str);
    1575           9 :   const MSAntenna& tbl_antenna = ms.antenna();
    1576           9 :   const String &col_name_str = tbl_antenna.columnName(MSAntenna::MSAntennaEnums::NAME);
    1577          18 :   ScalarColumn<String> antenna_name(tbl_antenna,col_name_str);
    1578           9 :   const MSSpectralWindow& tbl_spectral_window = ms.spectralWindow();
    1579             : 
    1580             :   // make a map between SOURCE_ID and source NAME
    1581           9 :   const MSSource &tbl_source = ms.source();
    1582          18 :   ScalarColumn<Int> id_col(tbl_source, tbl_source.columnName(MSSource::MSSourceEnums::SOURCE_ID));
    1583          18 :   ScalarColumn<String> name_col(tbl_source, tbl_source.columnName(MSSource::MSSourceEnums::NAME));
    1584          18 :   std::map<Int, String> source_map;
    1585          18 :   for (uInt irow = 0; irow < tbl_source.nrow(); ++irow) {
    1586           9 :     auto source_id = id_col(irow);
    1587           9 :     if (source_map.find(source_id) == source_map.end()) {
    1588           9 :       source_map[source_id] = name_col(irow);
    1589             :     }
    1590             :   }
    1591             : 
    1592          18 :   Vector<casacore::rownr_t> rowList;
    1593             : 
    1594          18 :   for (uInt field_id=0; field_id < tbl_field.nrow(); ++field_id){
    1595           9 :     String field_sel(casacore::String::toString<uInt>(field_id));
    1596           9 :     String const source_name = source_map.at(source_id_col(field_id));
    1597             : 
    1598             :     // Set ephemeris flag if source name is the one recognized as a moving source
    1599           9 :     if (isEphemeris(source_name)) {
    1600           2 :       calc.setMovingSource(source_name);
    1601             :     }
    1602             :     else {
    1603           7 :       calc.unsetMovingSource();
    1604             :     }
    1605             : 
    1606          20 :     for (uInt ant_id=0; ant_id < tbl_antenna.nrow(); ++ant_id){
    1607          11 :       String ant_sel(antenna_name(ant_id) + "&&&");
    1608          46 :       for (uInt spw_id=0; spw_id < tbl_spectral_window.nrow(); ++spw_id){
    1609          35 :         String spw_sel(casacore::String::toString<uInt>(spw_id));
    1610             :         // Filter user selection by (field_id,antenna,spectral window) triple
    1611             :         try {
    1612         217 :           calc.selectData(ant_sel,spw_sel,field_sel);
    1613             :         }
    1614          26 :         catch (AipsError& e) { // Empty selection
    1615             :           // Note: when the underlying MSSelection is empty
    1616             :           // MSSelection internally catches an MSSelectionError error
    1617             :           // but does not re-throw it. It throws instead an AipsError
    1618             :           // copy-constructed from the MSSelectionError
    1619          26 :           continue;
    1620          26 :         }
    1621             :         debuglog << "field_id: " << field_id
    1622             :              << " ant_id: "  << ant_id
    1623             :              << " spw: "     << spw_id
    1624           9 :                  << "  ==> selection rows: " << calc.getNrowForSelectedMS() << debugpost;
    1625             :         // Get time-interpolated celestial pointing directions for the filtered user selection
    1626           9 :         Matrix<Double> pointings_dirs = calc.getDirection();
    1627             :         // Project directions onto image plane
    1628             :         // pixel_scale_ :
    1629             :         //   . hard-coded to 0.5 in constructor
    1630             :         //   . is applied to the median separation of consecutive pointing directions by the projector
    1631             :         //   . projector pixel size = 0.5*directions_median
    1632           9 :         debuglog << "pixel_scale:" << pixel_scale_ << debugpost;
    1633           9 :         OrthographicProjector p(pixel_scale_);
    1634           9 :         p.setDirection(pointings_dirs);
    1635           9 :         const Matrix<Double> &pointings_coords = p.project();
    1636             :         // Extract edges of the observed region for the (field_id,antenna,spectral window) triple
    1637           9 :         SakuraAlignedArray<Double> pointings_x(pointings_coords.row(0));
    1638           9 :         SakuraAlignedArray<Double> pointings_y(pointings_coords.row(1));
    1639           9 :         SakuraAlignedArray<Bool> is_edge_storage(pointings_coords.ncolumn());
    1640           9 :         Vector<Bool> is_edge = is_edge_storage.casaVector();
    1641           9 :         is_edge = false;
    1642           9 :         double pixel_size = 0.0;
    1643             :         {
    1644             :           // CAS-9956
    1645             :           // Mitigation of memory usage due to unexpectedly large number of pixels.
    1646             :           // Currently CreateMaskNearEdgeDouble requires 2*sizeof(size_t)*num_pixels bytes
    1647             :           // of memory. If this value exceeds 2GB, the mitigation will be activated.
    1648           9 :           Double const num_pixels = p.p_size()[0] * p.p_size()[1];
    1649           9 :           auto const estimated_memory = num_pixels * 2 * sizeof(size_t);
    1650           9 :           constexpr Double kMaxMemory = 2.0e9;
    1651           9 :           if (estimated_memory >= kMaxMemory && pixel_scale_ < 1.0) {
    1652           0 :             LogIO os;
    1653           0 :             os << LogOrigin("PointingDirectionProjector", "scale_and_center", WHERE);
    1654           0 :             os << LogIO::WARN << "Estimated Memory: " << estimated_memory << LogIO::POST;
    1655           0 :             os << LogIO::WARN << "Mitigation of memory usage is activated. pixel scale is set to 1.0" << LogIO::POST;
    1656             :             // pixel_size can be set to 2.0 since projection grid spacing is estimated from half of median separation
    1657             :             // between neighboring pixels so that pixel_width will become about 1.0 if pixel_size is 0.
    1658           0 :             pixel_size = 2.0;
    1659           0 :             os << LogIO::WARN << "pixel_size is set to " << pixel_size << LogIO::POST;
    1660           0 :            }
    1661             :         }
    1662             :         // libsakura 2.0: setting pixel_size=0.0 means that CreateMaskNearEdgeDouble will
    1663             :         //   . compute the median separation of consecutive pointing coordinates
    1664             :         //   . use an "edge detection pixel size" = 0.5*coordinates_median (pixel scale hard-coded to 0.5)
    1665           9 :         debuglog << "sakura library function call: parameters info:" << debugpost;
    1666           9 :         debuglog << "in: fraction: " << fraction_ << debugpost;
    1667           9 :         debuglog << "in: pixel size: " << pixel_size << debugpost;
    1668           9 :         debuglog << "in: pixels count: (nx = " << p.p_size()[0] << " , ny = " << p.p_size()[1] << debugpost;
    1669           9 :         debuglog << "in: pointings_coords.ncolumn(): " << pointings_coords.ncolumn() << debugpost;
    1670          18 :         LIBSAKURA_SYMBOL(Status) status = LIBSAKURA_SYMBOL(CreateMaskNearEdgeDouble)(
    1671             :           fraction_, pixel_size,
    1672           9 :         pointings_coords.ncolumn(), pointings_x.data(), pointings_y.data(),
    1673             :           nullptr /* blc_x */, nullptr /* blc_y */,
    1674             :           nullptr /* trc_x */, nullptr /* trc_y */,
    1675             :         is_edge.data());
    1676           9 :         bool edges_detection_ok = ( status == LIBSAKURA_SYMBOL(Status_kOK) );
    1677           9 :         if ( ! edges_detection_ok ) {
    1678           0 :           debuglog << "sakura error: status=" << status << debugpost;
    1679             :         }
    1680           9 :         AlwaysAssert(edges_detection_ok,AipsError);
    1681             :         // Compute ROW ids of detected edges. ROW "ids" are ROW ids in the MS filtered by user selection.
    1682           9 :         auto index_2_rowid = calc.getRowIdForOriginalMS();
    1683           9 :         size_t edges_count = ntrue(is_edge);
    1684           9 :         size_t rowListIndex = rowList.size();
    1685           9 :         rowList.resize(rowList.size() + edges_count, True);
    1686        7689 :         for (size_t i = 0; i < is_edge.size(); ++i){
    1687        7680 :           if ( is_edge[i] ) {
    1688        2028 :             rowList[rowListIndex] = index_2_rowid[i]; // i;
    1689        2028 :             ++rowListIndex;
    1690             :           }
    1691             :         }
    1692           9 :         debuglog << "edges_count=" << edges_count << debugpost;
    1693           9 :         AlwaysAssert(edges_count > 0, AipsError);
    1694             : #ifdef SDCALSKY_DEBUG
    1695             :         stringstream fname;
    1696             :         fname << calTableName().c_str() << ".edges."
    1697             :                 << field_id << "_" << ant_id << "_" << spw_id
    1698             :           << ".csv" ;
    1699             :         debuglog << "Save pointing directions and coordinates to:" << debugpost;
    1700             :         debuglog << fname.str() << debugpost;
    1701             :         ofstream ofs(fname.str());
    1702             :         AlwaysAssert(ofs.good(), AipsError);
    1703             :         ofs << "row_id,field_id,ant_id,spw_id,triple_key,dir_0,dir_1,coord_0,coord_1,edge_0,edge_1,is_edge" << endl;
    1704             :         const auto &d0 =  pointings_dirs.row(0);
    1705             :         const auto &d1 =  pointings_dirs.row(1);
    1706             :         const auto &c0 =  pointings_coords.row(0);
    1707             :         const auto &c1 =  pointings_coords.row(1);
    1708             :         for (uInt j=0; j<d0.size(); j++) {
    1709             :           ofs << index_2_rowid[j] << ","
    1710             :             << field_id << "," << ant_id << "," << spw_id << ","
    1711             :             << field_id << "_" << ant_id << "_" << spw_id << ","
    1712             :               << d0(j) << "," << d1(j) << ","
    1713             :               << c0(j) << "," << c1(j) << "," ;
    1714             :           if ( is_edge[j] ) ofs << c0(j) << "," << c1(j) << "," << 1 << endl;
    1715             :           else ofs << ",," << 0 << endl;
    1716             :         }
    1717             : #endif
    1718          35 :       }
    1719          11 :     }
    1720           9 :   }
    1721           9 :   Bool have_off_spectra = (rowList.size() > 0);
    1722           9 :   AlwaysAssert(have_off_spectra, AipsError);
    1723          18 :   MeasurementSet msSel = ms(rowList);
    1724           9 :   debuglog << "rowList = " << rowList << debugpost;
    1725          18 :   return msSel;
    1726           9 : }
    1727             : 
    1728             : } //# NAMESPACE CASA - END
    1729             : 

Generated by: LCOV version 1.16