LCOV - code coverage report
Current view: top level - msvis/MSVis - MSUVWGenerator.cc (source / functions) Hit Total Coverage
Test: ctest_coverage.info Lines: 89 102 87.3 %
Date: 2023-11-06 10:06:49 Functions: 5 6 83.3 %

          Line data    Source code
       1             : // Based on code/alma/apps/UVWCoords
       2             : 
       3             : #include <casacore/casa/Logging/LogIO.h>
       4             : #include <msvis/MSVis/MSUVWGenerator.h>
       5             : #include <casacore/measures/Measures/MEpoch.h>
       6             : #include <casacore/measures/Measures/MFrequency.h>
       7             : #include <casacore/measures/Measures/MPosition.h>
       8             : #include <casacore/ms/MeasurementSets/MSColumns.h>
       9             : #include <casacore/ms/MeasurementSets/MSAntennaColumns.h>
      10             : #include <casacore/measures/Measures/MCBaseline.h>
      11             : #include <casacore/casa/Utilities/GenSort.h>
      12             : #include <map>
      13             : 
      14             : using namespace casacore;
      15             : namespace casa {
      16             : 
      17             : // The UvwCoords ctor has lines for the antennas, antenna offsets, and station
      18             : // positions.  This ctor assumes they're present in msc_p if present at all.
      19          14 :   MSUVWGenerator::MSUVWGenerator(MSColumns &msc_ref, const MBaseline::Types bltype,
      20          14 :                                  const Muvw::Types) :
      21             :   msc_p(msc_ref),                                       
      22             :   bl_csys_p(MBaseline::Ref(bltype)), // MBaseline::J2000, ITRF, etc.
      23          14 :   antColumns_p(msc_p.antenna()),
      24          14 :   antPositions_p(antColumns_p.positionMeas()),
      25          14 :   antOffset_p(antColumns_p.offsetMeas()),
      26          14 :   refpos_p(antPositions_p(0)),  // We use the first antenna for the reference
      27          28 :   feedOffset_p(msc_p.feed().positionMeas())
      28             : {
      29             :   // It seems that using a String is the only safe way to go from say,
      30             :   // MPosition::ITRF to MBaseline::ITRF.
      31          14 :   MBaseline::getType(refposref_p,
      32          28 :                      MPosition::showType(refpos_p.getRef().getType()));
      33             : 
      34          14 :   fill_bl_an(bl_an_p);          
      35          14 : }
      36             : 
      37          14 : MSUVWGenerator::~MSUVWGenerator(){
      38          14 : }
      39             : 
      40          14 : void MSUVWGenerator::fill_bl_an(Vector<MVBaseline>& bl_an_p)
      41             : {
      42          14 :   nant_p = antPositions_p.table().nrow();
      43          14 :   logSink() << LogIO::DEBUG1 << "nant_p: " << nant_p << LogIO::POST;
      44             : 
      45          14 :   Double max_baseline = -1.0;
      46             :   Double bl_len;
      47             :  
      48          14 :   const ScalarColumn<Double>& antDiams = antColumns_p.dishDiameter();
      49          14 :   Double smallestDiam = antDiams(0);
      50          14 :   Double secondSmallestDiam = antDiams(0);
      51             :   
      52          14 :   bl_an_p.resize(nant_p);
      53         306 :   for(uInt an = 0; an < nant_p; ++an){
      54             :     // MVBaselines are basically xyz Vectors, not Measures.
      55         292 :     bl_an_p[an] = MVBaseline(refpos_p.getValue(), antPositions_p(an).getValue());
      56             : 
      57             :     // MVBaseline has functions to return the length, but Manhattan distances
      58             :     // are good enough for this, and faster than a sqrt.
      59         584 :     Vector<Double> bluvw(bl_an_p[an].getValue());
      60         292 :     bl_len = fabs(bluvw[0]) + fabs(bluvw[1]) + fabs(bluvw[2]);
      61             : 
      62         292 :     if(bl_len > max_baseline)
      63          63 :       max_baseline = bl_len;
      64             : 
      65         292 :     if(antDiams(an) < secondSmallestDiam){
      66           0 :       if(antDiams(an) < smallestDiam){
      67           0 :         secondSmallestDiam = smallestDiam;
      68           0 :         smallestDiam = antDiams(an);
      69             :       }
      70             :       else
      71           0 :         secondSmallestDiam = antDiams(an);
      72             :     }
      73             :   }
      74             : 
      75             :   // Setting timeRes_p to 0.0025 * the time for a 1 radian phase change on the
      76             :   // longest baseline at 2x the primary beamwidth should be sufficiently short
      77             :   // for Earth based observations.  Space-based baselines will move faster, but
      78             :   // probably don't have the data rate to support full beam imaging.  An
      79             :   // alternative limit could come from |d(uvw)/dt|/|uvw|, but that is
      80             :   // guaranteed to be at least somewhat larger than this limit (replace the
      81             :   // 2.44 with 2, and remove the dish diameters and max_baseline).
      82             :   //
      83             :   // Do not raise the 0.0025 coefficient by too much, since the times used for
      84             :   // UVW calculation could be biased by as much as -0.5 * timeRes_p if more
      85             :   // than one integration fits into timeRes_p.  timeRes_p is not intended so
      86             :   // much for skipping calculations as it is for preventing antUVW_p from being
      87             :   // calculated for each row in the same integration.  The integration interval
      88             :   // may change within an MS, but ideally antUVW_p is calculated once per
      89             :   // integration (timeRes_p <~ integration) and there is no bias.
      90          28 :   timeRes_p = 0.0025 * 24.0 * 3600.0 / (6.283 * 2.44) *
      91          28 :     sqrt(smallestDiam * secondSmallestDiam) / max_baseline;
      92          14 : }  
      93             : 
      94        5561 : void MSUVWGenerator::uvw_an(const MEpoch& timeCentroid, const Int fldID,
      95             :                             const Bool WSRTConvention)
      96             : {
      97       11122 :   const MDirection& phasedir = msc_p.field().phaseDirMeas(fldID);
      98       11122 :   MeasFrame  measFrame(refpos_p, timeCentroid, phasedir);
      99       11122 :   MVBaseline mvbl;
     100       11122 :   MBaseline  basMeas;
     101             : 
     102             :   //logSink() << LogIO::DEBUG1
     103             :   //   << "timeCentroid: " << timeCentroid
     104             :   //   << "\nfldID: " << fldID
     105             :   //   << "\nphasedir: " << phasedir
     106             :   //   << LogIO::POST;
     107             : 
     108       11122 :   MBaseline::Ref basref(refposref_p);
     109        5561 :   basMeas.set(mvbl, basref);            // in antenna frame
     110        5561 :   basMeas.getRefPtr()->set(measFrame);
     111             : 
     112             :   // Setup a machine for converting a baseline vector from the antenna frame to
     113             :   // bl_csys_p's frame
     114       11122 :   MBaseline::Convert elconv(basMeas, bl_csys_p);
     115             : 
     116       66315 :   for(uInt i = 0; i < nant_p; ++i){
     117             :     //TODO: (Soon!) Antenna offsets are not handled yet.
     118       60754 :     basMeas.set(bl_an_p[i], basref);
     119      121508 :     MBaseline basOutFrame = elconv(basMeas);
     120             :     //MBaseline::Types botype = MBaseline::castType(basOutFrame.getRef().getType());
     121      121508 :     MVuvw uvwOutFrame(basOutFrame.getValue(), phasedir.getValue());
     122             :     
     123       60754 :     antUVW_p[i] = uvwOutFrame.getValue();
     124             :   }
     125             : 
     126             :   // Tony Willis supplied this crucial bit of history: the WSRT definition of
     127             :   // phase is opposite to that of the VLA - the WSRT definition of the l,m
     128             :   // direction cosines is that l increases toward decreasing RA, whereas the
     129             :   // VLA definition is that l increases toward increasing RA.  AIPS seems to
     130             :   // understand this.  (For a completely useless piece of trivia, the WSRT defn
     131             :   // of phase is consistent with that of the Cambridge one-mile telescope as
     132             :   // Wim Brouw set things up to be in agreement with the one-mile telescope ca
     133             :   // 1968.)
     134             :   // RR: It is not just l, v and w are flipped as well.
     135        5561 :   if(WSRTConvention)
     136           0 :     antUVW_p = -antUVW_p;
     137        5561 : }
     138             : 
     139             : // antUVW_p must be set up for the correct timeCentroid and phase direction by
     140             : // uvw_an() before calling this.
     141           0 : void MSUVWGenerator::uvw_bl(const uInt ant1, const uInt,// feed1,
     142             :                             const uInt ant2, const uInt,// feed2,
     143             :                             Array<Double>& uvw)
     144             : {
     145             :   //uvw.resize(3);      // Probably redundant.  Does it significantly slow things down?
     146             :   //TODO: Feed offsets are not handled yet.
     147           0 :   uvw = antUVW_p[ant2] - antUVW_p[ant1];
     148           0 : }
     149             : 
     150          14 : Bool MSUVWGenerator::make_uvws(const Vector<Int> flds)
     151             : {
     152          14 :   ArrayColumn<Double>&      UVWcol   = msc_p.uvw();  
     153          14 :   const ScalarMeasColumn<MEpoch>& timeCentMeas = msc_p.timeCentroidMeas();
     154          28 :   const ScalarColumn<Int> fieldID(msc_p.fieldId());
     155          28 :   const ScalarColumn<Int> ant1(msc_p.antenna1());
     156          28 :   const ScalarColumn<Int> ant2(msc_p.antenna2());
     157          28 :   const ScalarColumn<Int> feed1(msc_p.feed1());
     158          28 :   const ScalarColumn<Int> feed2(msc_p.feed2());
     159          28 :   const ScalarColumn<Int> obsID(msc_p.observationId());
     160             : 
     161             :   // Use a time ordered index to minimize the number of calls to uvw_an.
     162             :   // TODO: use field as a secondary sort key.
     163          14 :   Vector<uInt> tOI;
     164          14 :   GenSortIndirect<Double>::sort(tOI, msc_p.timeCentroid().getColumn());
     165             : 
     166             :   // Having uvw_an() calculate positions for each antenna for every field is
     167             :   // somewhat inefficient since in a multiconfig MS not all antennas will be
     168             :   // used in each time interval, but it's not clear that determining which
     169             :   // antennas will be used for a given uvw_an() call would be any more
     170             :   // efficient.  It's not horribly inefficient, because uvw_an() is O(nant_p),
     171             :   // and uvw_bl() is only called for baselines that are actually used.
     172          14 :   antUVW_p.resize(nant_p);
     173             : 
     174          14 :   logSink() << LogOrigin("MSUVWGenerator", "make_uvws") << LogIO::NORMAL3;
     175             :   
     176          14 :   logSink() << LogIO::DEBUG1 << "timeRes_p: " << timeRes_p << LogIO::POST;
     177             : 
     178             :   try{
     179          14 :     Bool oldWsrtConvention = (msc_p.observation().telescopeName()(obsID(tOI[0])) ==
     180             :                               "WSRT");
     181             : 
     182             :     // Ensure a call to uvw_an on the 1st iteration.
     183          28 :     const Unit sec("s");
     184          14 :     Double oldTime = timeCentMeas(tOI[0]).get(sec).getValue() - 2.0 * timeRes_p;
     185          14 :     Int    oldFld  = -2;
     186             : 
     187             :     // IO reordering buffer to avoid poor IO patterns on files not sorted by time
     188          28 :     std::map<uInt, Vector<Double> > writebuffer;
     189             : 
     190     3950226 :     for(uInt row = 0; row < msc_p.nrow(); ++row){
     191     3950212 :       uInt   toir = tOI[row];
     192     7900424 :       const MEpoch & timecentmeas = timeCentMeas(toir);
     193     3950212 :       Double currTime = timecentmeas.get(sec).getValue();
     194     3950212 :       Int    currFld  = fieldID(toir);
     195     3950212 :       Bool   newWsrtConvention = (msc_p.observation().telescopeName()(obsID(toir)) ==
     196             :                                   "WSRT");
     197     3950212 :       if ((row % (1<<22)) == 0) {
     198             :         logSink() << LogIO::NORMAL << "Rows handled: "
     199          14 :                   << row << "/" << msc_p.nrow() << LogIO::POST;
     200             :       }
     201             : 
     202     3950212 :       if(currTime - oldTime > timeRes_p || currFld != oldFld
     203     3944651 :          || newWsrtConvention != oldWsrtConvention){
     204        5561 :         oldTime = currTime;
     205        5561 :         oldFld  = currFld;
     206             : 
     207        5561 :         if(newWsrtConvention != oldWsrtConvention){
     208             :           logSink() << LogIO::WARN
     209             :                     << "There is a switch to or from the WSRT convention at row "
     210             :                     << toir << ".\nWatch for an inconsistency in the sign of UVW."
     211           0 :                     << LogIO::POST;
     212           0 :           oldWsrtConvention = newWsrtConvention;
     213             :         }
     214             : 
     215             :         //logSink() << LogIO::DEBUG1 << "currTime: " << currTime
     216             :         //          << "\ncurrFld: " << currFld << LogIO::POST;
     217        5561 :         uvw_an(timecentmeas, currFld, newWsrtConvention);
     218             :       }
     219             :     
     220     3950212 :       if(flds[fieldID(toir)] > -1){
     221             :         //      uvw_bl(ant1(toir), ant2(toir),
     222             :         //     feed1(toir), feed2(toir), UVWcol(toir));
     223             :         // collect output and write larger chunk in row sequential order
     224     3685664 :         writebuffer[toir] = antUVW_p[ant2(toir)] - antUVW_p[ant1(toir)];
     225     3685664 :         if (writebuffer.size() > 100000) {
     226     3500070 :           for (auto it = writebuffer.begin(); it != writebuffer.end(); ++it) {
     227     3500035 :             UVWcol.put(it->first, it->second);
     228             :           }
     229          35 :           writebuffer.clear();
     230             :         }
     231             :       }
     232             :     }
     233             :     // flush rest of buffer
     234      185643 :     for (auto it = writebuffer.begin(); it != writebuffer.end(); ++it) {
     235      185629 :       UVWcol.put(it->first, it->second);
     236             :     }
     237             :   }
     238           0 :   catch(AipsError x){
     239             :     logSink() << LogIO::SEVERE << "Caught exception: " << x.getMesg() 
     240           0 :               << LogIO::POST;
     241           0 :     throw(AipsError("Error in MSUVWGenerator::make_uvws."));
     242             :     return false;
     243             :   }
     244          14 :   return true;
     245             : }
     246             : 
     247             : // void MSUVWGenerator::get_ant_offsets(const MDirection& dir_with_a_frame)
     248             : // {
     249             : //   // This appears to be a required column of the ANTENNA table in version 2.0
     250             : //   // of the MeasurementSet definition
     251             : //   // (http://aips2.nrao.edu/docs/notes/229/229.html), so it is assumed to be
     252             : //   // present.  However, it is usually a set of zeroes, based on the common
     253             : //   // belief that it is only needed for heterogeneous arrays, since the
     254             : //   // receivers of homogeneous arrays move in concert.  That is not true when
     255             : //   // there are independent pointing errors.
     256             : 
     257             : //   // Convert ant_offset_measures to Vectors and check for nonzeroness.
     258             : //   ant_offset_vec_p.resize(nant);
     259             : //   for(uInt n = 0; n < nant; ++n)
     260             : //     ant_offset_vec_p[n] = ant_offset_meas_p.convert(0, pointingdir).getValue();
     261             : // }
     262             : 
     263             : // Bool MSUVWGenerator::set_receiv_offsets(const MDirection& dir_with_a_frame)
     264             : // {
     265             : //   // This appears to be a required column of the FEED table in version 2.0
     266             : //   // of the MeasurementSet definition
     267             : //   // (http://aips2.nrao.edu/docs/notes/229/229.html), so it is assumed to be
     268             : //   // present.  However, it is usually a set of zeroes, based on the common
     269             : //   // belief that it is only needed for heterogeneous arrays, since the
     270             : //   // receivers of homogeneous arrays move in concert.  That is not true when
     271             : //   // there are independent pointing errors.
     272             : 
     273             : //   // Convert feed_offset_measures to Vectors and check for nonzeroness.
     274             : //   Vector<Vector<Double> > offsetvects;
     275             : //   offsetvects.resize(nant);
     276             : //   for(uInt n = 0; n < nant; ++n){
     277             : //     offsetvects[n] = ant_offsets->convert(0, pointingdir).getValue();
     278             : //     if(ant_offsets[n] != ant_offsets[0]){
     279             : //       varying_offsets = true;
     280             : //     }
     281             : //   }
     282             : 
     283             : //   ignore_offsets = true;
     284             : //   return ignore_offsets;
     285             : // }
     286             : 
     287             : using namespace casacore;
     288             : } // Ends namespace casa.

Generated by: LCOV version 1.16