//# SDGrid.cc: Implementation of SDGrid class
//# Copyright (C) 1997,1998,1999,2000,2001,2002,2003
//# Associated Universities, Inc. Washington DC, USA.
//#
//# This library is free software; you can redistribute it and/or modify it
//# under the terms of the GNU Library General Public License as published by
//# the Free Software Foundation; either version 2 of the License, or (at your
//# option) any later version.
//#
//# This library is distributed in the hope that it will be useful, but WITHOUT
//# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
//# FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Library General Public
//# License for more details.
//#
//# You should have received a copy of the GNU Library General Public License
//# along with this library; if not, write to the Free Software Foundation,
//# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA.
//#
//# Correspondence concerning AIPS++ should be addressed as follows:
//#        Internet email: aips2-request@nrao.edu.
//#        Postal address: AIPS++ Project Office
//#                        National Radio Astronomy Observatory
//#                        520 Edgemont Road
//#                        Charlottesville, VA 22903-2475 USA
//#
//# $Id$

#include <casa/Arrays/Array.h>
#include <casa/Arrays/ArrayLogical.h>
#include <casa/Arrays/ArrayMath.h>
#include <casa/Arrays/Cube.h>
#include <casa/Arrays/MaskedArray.h>
#include <casa/Arrays/Matrix.h>
#include <casa/Arrays/MatrixIter.h>
#include <casa/Arrays/Slice.h>
#include <casa/Arrays/Vector.h>
#include <casa/BasicSL/Constants.h>
#include <casa/BasicSL/String.h>
#include <casa/Containers/Block.h>
#include <casa/Exceptions/Error.h>
#include <casa/OS/Timer.h>
#include <casa/Quanta/MVAngle.h>
#include <casa/Quanta/MVTime.h>
#include <casa/Quanta/UnitMap.h>
#include <casa/Quanta/UnitVal.h>
#include <casa/sstream.h>
#include <casa/Utilities/Assert.h>

#include <components/ComponentModels/ConstantSpectrum.h>
#include <components/ComponentModels/Flux.h>
#include <components/ComponentModels/PointShape.h>

#include <coordinates/Coordinates/CoordinateSystem.h>
#include <coordinates/Coordinates/DirectionCoordinate.h>
#include <coordinates/Coordinates/Projection.h>
#include <coordinates/Coordinates/SpectralCoordinate.h>
#include <coordinates/Coordinates/StokesCoordinate.h>

#include <images/Images/ImageInterface.h>
#include <images/Images/PagedImage.h>
#include <images/Images/TempImage.h>

#include <lattices/Lattices/ArrayLattice.h>
#include <lattices/Lattices/LatticeCache.h>
#include <lattices/Lattices/LatticeIterator.h>
#include <lattices/Lattices/LatticeStepper.h>
#include <lattices/Lattices/SubLattice.h>
#include <lattices/LEL/LatticeExpr.h>
#include <lattices/LRegions/LCBox.h>

#include <measures/Measures/Stokes.h>
#include <ms/MeasurementSets/MSColumns.h>
#include <msvis/MSVis/StokesVector.h>
#include <msvis/MSVis/VisBuffer.h>
#include <msvis/MSVis/VisibilityIterator.h>
#include <scimath/Mathematics/RigidVector.h>
#include <synthesis/MeasurementComponents/SDGrid.h>
#include <synthesis/TransformMachines/SkyJones.h>
#include <synthesis/TransformMachines/StokesImageUtil.h>

using namespace casacore;
namespace casa {

SDGrid::SDGrid(SkyJones& sj, Int icachesize, Int itilesize,
	       String iconvType, Int userSupport, Bool useImagingWeight)
  : FTMachine(), sj_p(&sj), imageCache(0), wImageCache(0),
  cachesize(icachesize), tilesize(itilesize),
  isTiled(false), wImage(0), arrayLattice(0),  wArrayLattice(0), lattice(0), wLattice(0), convType(iconvType),
    pointingToImage(0), userSetSupport_p(userSupport),
    truncate_p(-1.0), gwidth_p(0.0), jwidth_p(0.0),
    minWeight_p(0.), lastIndexPerAnt_p(), useImagingWeight_p(useImagingWeight), lastAntID_p(-1), msId_p(-1),
    isSplineInterpolationReady(false), interpolator(0), clipminmax_(false)
{
  lastIndex_p=0;
}

SDGrid::SDGrid(MPosition& mLocation, SkyJones& sj, Int icachesize, Int itilesize,
	       String iconvType, Int userSupport, Float minweight, Bool clipminmax, Bool useImagingWeight)
  : FTMachine(),  sj_p(&sj), imageCache(0), wImageCache(0),
  cachesize(icachesize), tilesize(itilesize),
  isTiled(false), wImage(0), arrayLattice(0),  wArrayLattice(0), lattice(0), wLattice(0), convType(iconvType),
    pointingToImage(0), userSetSupport_p(userSupport),
    truncate_p(-1.0), gwidth_p(0.0),  jwidth_p(0.0),
    minWeight_p(minweight), lastIndexPerAnt_p(), useImagingWeight_p(useImagingWeight), lastAntID_p(-1), msId_p(-1),
    isSplineInterpolationReady(false), interpolator(0), clipminmax_(clipminmax)
{
  mLocation_p=mLocation;
  lastIndex_p=0;
}

SDGrid::SDGrid(Int icachesize, Int itilesize,
	       String iconvType, Int userSupport, Bool useImagingWeight)
  : FTMachine(), sj_p(0), imageCache(0), wImageCache(0),
  cachesize(icachesize), tilesize(itilesize),
  isTiled(false), wImage(0), arrayLattice(0),  wArrayLattice(0), lattice(0), wLattice(0), convType(iconvType),
    pointingToImage(0), userSetSupport_p(userSupport),
    truncate_p(-1.0), gwidth_p(0.0), jwidth_p(0.0),
    minWeight_p(0.), lastIndexPerAnt_p(), useImagingWeight_p(useImagingWeight), lastAntID_p(-1), msId_p(-1),
    isSplineInterpolationReady(false), interpolator(0), clipminmax_(false)
{
  lastIndex_p=0;
}

SDGrid::SDGrid(MPosition &mLocation, Int icachesize, Int itilesize,
	       String iconvType, Int userSupport, Float minweight, Bool clipminmax, Bool useImagingWeight)
  : FTMachine(), sj_p(0), imageCache(0), wImageCache(0),
  cachesize(icachesize), tilesize(itilesize),
  isTiled(false), wImage(0), arrayLattice(0),  wArrayLattice(0), lattice(0), wLattice(0), convType(iconvType),
    pointingToImage(0), userSetSupport_p(userSupport),
    truncate_p(-1.0), gwidth_p(0.0), jwidth_p(0.0),
    minWeight_p(minweight), lastIndexPerAnt_p(), useImagingWeight_p(useImagingWeight), lastAntID_p(-1),
    msId_p(-1),
    isSplineInterpolationReady(false), interpolator(0), clipminmax_(clipminmax)
{
  mLocation_p=mLocation;
  lastIndex_p=0;
}

SDGrid::SDGrid(MPosition &mLocation, Int icachesize, Int itilesize,
	       String iconvType, Float truncate, Float gwidth, Float jwidth,
	       Float minweight, Bool clipminmax, Bool useImagingWeight)
  : FTMachine(), sj_p(0), imageCache(0), wImageCache(0),
  cachesize(icachesize), tilesize(itilesize),
  isTiled(false), wImage(0), arrayLattice(0),  wArrayLattice(0), lattice(0), wLattice(0), convType(iconvType),
    pointingToImage(0), userSetSupport_p(-1),
    truncate_p(truncate), gwidth_p(gwidth), jwidth_p(jwidth),
    minWeight_p(minweight), lastIndexPerAnt_p(), useImagingWeight_p(useImagingWeight), lastAntID_p(-1), msId_p(-1),
    isSplineInterpolationReady(false), interpolator(0), clipminmax_(clipminmax)
{
  mLocation_p=mLocation;
  lastIndex_p=0;
}


//----------------------------------------------------------------------
SDGrid& SDGrid::operator=(const SDGrid& other)
{
  if(this!=&other) {
     //Do the base parameters
    FTMachine::operator=(other);
    sj_p=other.sj_p;
    imageCache=other.imageCache;
    wImage=other.wImage;
    wImageCache=other.wImageCache;
    cachesize=other.cachesize;
    tilesize=other.tilesize;
    isTiled=other.isTiled;
    lattice=other.lattice;
    arrayLattice=other.arrayLattice;
    wLattice=other.wLattice;
    wArrayLattice=other.wArrayLattice;
    convType=other.convType;
    if(other.wImage !=0)
      wImage=(TempImage<Float> *)other.wImage->cloneII();
    else
      wImage=0;
    pointingDirCol_p=other.pointingDirCol_p;
    pointingToImage=0;
    xyPos.resize();
    xyPos=other.xyPos;
    xyPosMovingOrig_p=other.xyPosMovingOrig_p;
    convFunc.resize();
    convFunc=other.convFunc;
    convSampling=other.convSampling;
    convSize=other.convSize;
    convSupport=other.convSupport;
    userSetSupport_p=other.userSetSupport_p;
    lastIndex_p=0;
    lastIndexPerAnt_p=0;
    lastAntID_p=-1;
    msId_p=-1;
    useImagingWeight_p=other.useImagingWeight_p;
    clipminmax_=other.clipminmax_;
  };
  return *this;
};

String SDGrid::name() const{
    return String("SDGrid");
}

//----------------------------------------------------------------------
// Odds are that it changed.....
Bool SDGrid::changed(const VisBuffer& /*vb*/) {
  return false;
}

//----------------------------------------------------------------------
SDGrid::SDGrid(const SDGrid& other):FTMachine()
{
  operator=(other);
}

#define NEED_UNDERSCORES
#if defined(NEED_UNDERSCORES)
#define grdsf grdsf_
#define grdgauss grdgauss_
#define grdjinc1 grdjinc1_
#endif

extern "C" {
   void grdsf(Double*, Double*);
   void grdgauss(Double*, Double*, Double*);
   void grdjinc1(Double*, Double*, Int*, Double*);
}

//----------------------------------------------------------------------
void SDGrid::init() {

  logIO() << LogOrigin("SDGrid", "init")  << LogIO::NORMAL;

  //pfile = fopen("ptdata.txt","w");

  ok();

  /*if((image->shape().product())>cachesize) {
    isTiled=true;
  }
  else {
    isTiled=false;
    }*/
  isTiled=false;
  nx    = image->shape()(0);
  ny    = image->shape()(1);
  npol  = image->shape()(2);
  nchan = image->shape()(3);

  sumWeight.resize(npol, nchan);

  // Set up image cache needed for gridding. For BOX-car convolution
  // we can use non-overlapped tiles. Otherwise we need to use
  // overlapped tiles and additive gridding so that only increments
  // to a tile are written.
  if(imageCache) delete imageCache; imageCache=0;

  convType=downcase(convType);
  logIO() << "Convolution function : " << convType << LogIO::DEBUG1 << LogIO::POST;
  if(convType=="pb") {
    //cerr << "CNVFunc " << convFunc << endl;

  }
  else if(convType=="box") {
    convSupport=(userSetSupport_p >= 0) ? userSetSupport_p : 0;
    logIO() << "Support : " << convSupport << " pixels" << LogIO::POST;
    convSampling=100;
    convSize=convSampling*(2*convSupport+2);
    convFunc.resize(convSize);
    convFunc=0.0;
    for (Int i=0;i<convSize/2;i++) {
      convFunc(i)=1.0;
    }
  }
  else if(convType=="sf") {
    // SF
    convSupport=(userSetSupport_p >= 0) ? userSetSupport_p : 3;
    logIO() << "Support : " << convSupport << " pixels" << LogIO::POST;
    convSampling=100;
    convSize=convSampling*(2*convSupport+2);
    convFunc.resize(convSize);
    convFunc=0.0;
    for (Int i=0;i<convSampling*convSupport;i++) {
      Double nu=Double(i)/Double(convSupport*convSampling);
      Double val;
      grdsf(&nu, &val);
      convFunc(i)=(1.0-nu*nu)*val;
    }
  }
  else if(convType=="gauss") {
    // default is b=1.0 (Mangum et al. 2007)
    Double hwhm=(gwidth_p > 0.0) ? Double(gwidth_p) : sqrt(log(2.0));
    Float truncate=(truncate_p >= 0.0) ? truncate_p : 3.0*hwhm;
    convSampling=100;
    Int itruncate=(Int)(truncate*Double(convSampling)+0.5);
    logIO() << LogIO::DEBUG1 << "hwhm=" << hwhm << LogIO::POST;
    logIO() << LogIO::DEBUG1 << "itruncate=" << itruncate << LogIO::POST;
    //convSupport=(Int)(truncate+0.5);
    convSupport = (Int)(truncate);
    convSupport += (((truncate-(Float)convSupport) > 0.0) ? 1 : 0);
    convSize=convSampling*(2*convSupport+2);
    convFunc.resize(convSize);
    convFunc=0.0;
    Double val, x;
    for (Int i = 0 ; i <= itruncate ; i++) {
      x = Double(i)/Double(convSampling);
      grdgauss(&hwhm, &x, &val);
      convFunc(i)=val;
    }

//     String outfile = convType + ".dat";
//     ofstream ofs(outfile.c_str());
//     for (Int i = 0 ; i < convSize ; i++) {
//       ofs << i << " " << convFunc[i] << endl;
//     }
//     ofs.close();
  }
  else if (convType=="gjinc") {
    // default is b=2.52, c=1.55 (Mangum et al. 2007)
    Double hwhm = (gwidth_p > 0.0) ? Double(gwidth_p) : sqrt(log(2.0))*2.52;
    Double c = (jwidth_p > 0.0) ? Double(jwidth_p) : 1.55;
    //Float truncate = truncate_p;
    convSampling = 100;
    Int itruncate=(Int)(truncate_p*Double(convSampling)+0.5);
    logIO() << LogIO::DEBUG1 << "hwhm=" << hwhm << LogIO::POST;
    logIO() << LogIO::DEBUG1 << "c=" << c << LogIO::POST;
    logIO() << LogIO::DEBUG1 << "itruncate=" << itruncate << LogIO::POST;
    //convSupport=(truncate_p >= 0.0) ? (Int)(truncate_p+0.5) : (Int)(2*c+0.5);
    Float convSupportF = (truncate_p >= 0.0) ? truncate_p : (2*c);
    convSupport = (Int)convSupportF;
    convSupport += (((convSupportF-(Float)convSupport) > 0.0) ? 1 : 0);
    convSize=convSampling*(2*convSupport+2);
    convFunc.resize(convSize);
    convFunc=0.0;
    //UNUSED: Double r;
    Double x, val1, val2;
    Int normalize = 1;
    if (itruncate >= 0) {
      for (Int i = 0 ; i < itruncate ; i++) {
        x = Double(i) / Double(convSampling);
        //r = Double(i) / (Double(hwhm)*Double(convSampling));
        grdgauss(&hwhm, &x, &val1);
        grdjinc1(&c, &x, &normalize, &val2);
        convFunc(i) = val1 * val2;
      }
    }
    else {
      // default is to truncate at first null
      for (Int i=0;i<convSize;i++) {
        x = Double(i) / Double(convSampling);
        //r = Double(i) / (Double(hwhm)*Double(convSampling));
        grdjinc1(&c, &x, &normalize, &val2);
        if (val2 <= 0.0) {
          logIO() << LogIO::DEBUG1 << "convFunc is automatically truncated at radius " << x << LogIO::POST;
          break;
        }
        grdgauss(&hwhm, &x, &val1);
        convFunc(i) = val1 * val2;
      }
    }

//    String outfile = convType + ".dat";
//    ofstream ofs(outfile.c_str());
//    for (Int i = 0 ; i < convSize ; i++) {
//      ofs << i << " " << convFunc[i] << endl;
//    }
//    ofs.close();
  }
  else {
    logIO_p << "Unknown convolution function " << convType << LogIO::EXCEPTION;
  }

  if(wImage) delete wImage; wImage=0;
  wImage = new TempImage<Float>(image->shape(), image->coordinates());

  /*if(isTiled) {
    Float tileOverlap=0.5;
    if(convType=="box") {
      tileOverlap=0.0;
    }
    else {
      tileOverlap=0.5;
      tilesize=max(12,tilesize);
    }
    IPosition tileShape=IPosition(4,tilesize,tilesize,npol,nchan);
    Vector<Float> tileOverlapVec(4);
    tileOverlapVec=0.0;
    tileOverlapVec(0)=tileOverlap;
    tileOverlapVec(1)=tileOverlap;
    imageCache=new LatticeCache <Complex> (*image, cachesize, tileShape,
					   tileOverlapVec,
					   (tileOverlap>0.0));

    wImageCache=new LatticeCache <Float> (*wImage, cachesize, tileShape,
					   tileOverlapVec,
					   (tileOverlap>0.0));

  }
  */
}

// This is nasty, we should use CountedPointers here.
SDGrid::~SDGrid() {
  //fclose(pfile);
  if (imageCache) delete imageCache; imageCache = 0;
  if (arrayLattice) delete arrayLattice; arrayLattice = 0;
  if (wImage) delete wImage; wImage = 0;
  if (wImageCache) delete wImageCache; wImageCache = 0;
  if (wArrayLattice) delete wArrayLattice; wArrayLattice = 0;
  if (interpolator) delete interpolator; interpolator = 0;
}

void SDGrid::findPBAsConvFunction(const ImageInterface<Complex>& image,
				  const VisBuffer& vb) {

  // Get the coordinate system and increase the sampling by
  // a factor of ~ 100.
  CoordinateSystem coords(image.coordinates());

  // Set up the convolution function: make the buffer plenty
  // big so that we can trim it back
  convSupport=max(128, sj_p->support(vb, coords));

  convSampling=100;
  convSize=convSampling*convSupport;

  // Make a one dimensional image to calculate the
  // primary beam. We oversample this by a factor of
  // convSampling.
  Int directionIndex=coords.findCoordinate(Coordinate::DIRECTION);
  AlwaysAssert(directionIndex>=0, AipsError);
  DirectionCoordinate dc=coords.directionCoordinate(directionIndex);
  Vector<Double> sampling;
  sampling = dc.increment();
  sampling*=1.0/Double(convSampling);
  dc.setIncrement(sampling);

  // Set the reference value to the first pointing in the coordinate
  // system used in the POINTING table.
  {
    uInt row=0;

    // reset lastAntID_p to use correct antenna position
    lastAntID_p = -1;

    const MSPointingColumns& act_mspc = vb.msColumns().pointing();
    Bool nullPointingTable=(act_mspc.nrow() < 1);
    // uInt pointIndex=getIndex(*mspc, vb.time()(row), vb.timeInterval()(row), vb.antenna1()(row));
    Int pointIndex=-1;
    if(!nullPointingTable){
      //if(vb.newMS()) This thing is buggy...using msId change
      if(vb.msId() != msId_p){
	lastIndex_p=0;
  if(lastIndexPerAnt_p.nelements() < (size_t)vb.numberAnt()) {
    lastIndexPerAnt_p.resize(vb.numberAnt());
  }
	lastIndexPerAnt_p=0;
	msId_p=vb.msId();
      }
      pointIndex=getIndex(act_mspc, vb.time()(row), -1.0, vb.antenna1()(row));
      if(pointIndex < 0)
	pointIndex=getIndex(act_mspc, vb.time()(row), vb.timeInterval()(row), vb.antenna1()(row));

    }
    if(!nullPointingTable && ((pointIndex<0)||(pointIndex>=Int(act_mspc.time().nrow())))) {
      ostringstream o;
      o << "Failed to find pointing information for time " <<
	MVTime(vb.time()(row)/86400.0);
      logIO_p << String(o) << LogIO::EXCEPTION;
    }
    MEpoch epoch(Quantity(vb.time()(row), "s"));
    if(!pointingToImage) {
      lastAntID_p=vb.antenna1()(row);
      MPosition pos;
      pos=vb.msColumns().antenna().positionMeas()(lastAntID_p);
      mFrame_p=MeasFrame(epoch, pos);
      if(!nullPointingTable){
	worldPosMeas=directionMeas(act_mspc, pointIndex);
      }
      else{
	worldPosMeas=vb.direction1()(row);
      }
      // Make a machine to convert from the worldPosMeas to the output
      // Direction Measure type for the relevant frame
      MDirection::Ref outRef(dc.directionType(), mFrame_p);
      pointingToImage = new MDirection::Convert(worldPosMeas, outRef);

      if(!pointingToImage) {
	logIO_p << "Cannot make direction conversion machine" << LogIO::EXCEPTION;
      }
    }
    else {
      mFrame_p.resetEpoch(epoch);
      if(lastAntID_p != vb.antenna1()(row)){
        logIO_p << LogIO::DEBUGGING
          << "updating antenna position. MS ID " << msId_p
          << ", last antenna ID " << lastAntID_p
          << " new antenna ID " << vb.antenna1()(row) << LogIO::POST;
	MPosition pos;
	lastAntID_p=vb.antenna1()(row);
	pos=vb.msColumns().antenna().positionMeas()(lastAntID_p);
	mFrame_p.resetPosition(pos);
      }
    }
    if(!nullPointingTable){
      worldPosMeas=(*pointingToImage)(directionMeas(act_mspc,pointIndex));
    }
    else{
      worldPosMeas=(*pointingToImage)(vb.direction1()(row));
    }
    delete pointingToImage;
    pointingToImage=0;
  }

  directionCoord=coords.directionCoordinate(directionIndex);
  //make sure we use the same units
  worldPosMeas.set(dc.worldAxisUnits()(0));

  // Reference pixel may be modified in dc.setReferenceValue when
  // projection type is SFL. To take into account this effect,
  // keep original reference pixel here and subtract it from
  // the reference pixel after dc.setReferenceValue instead
  // of setting reference pixel to (0,0).
  Vector<Double> const originalReferencePixel = dc.referencePixel();
  dc.setReferenceValue(worldPosMeas.getAngle().getValue());
  //Vector<Double> unitVec(2);
  //unitVec=0.0;
  //dc.setReferencePixel(unitVec);
  Vector<Double> updatedReferencePixel = dc.referencePixel() - originalReferencePixel;
  dc.setReferencePixel(updatedReferencePixel);

  coords.replaceCoordinate(dc, directionIndex);

  IPosition pbShape(4, convSize, 2, 1, 1);
  IPosition start(4, 0, 0, 0, 0);

  TempImage<Complex> onedPB(pbShape, coords);

  onedPB.set(Complex(1.0, 0.0));

  AlwaysAssert(sj_p, AipsError);
  sj_p->apply(onedPB, onedPB, vb, 0);

  IPosition pbSlice(4, convSize, 1, 1, 1);
  Vector<Float> tempConvFunc=real(onedPB.getSlice(start, pbSlice, true));
  // Find number of significant points
  uInt cfLen=0;
  for(uInt i=0;i<tempConvFunc.nelements();++i) {
    if(tempConvFunc(i)<1e-3) break;
    ++cfLen;
  }
  if(cfLen<1) {
    logIO() << LogIO::SEVERE
	    << "Possible problem in primary beam calculation: no points in gridding function"
	    << " - no points to be gridded on this image?" << LogIO::POST;
    cfLen=1;
  }
  Vector<Float> trimConvFunc=tempConvFunc(Slice(0,cfLen-1,1));

  // Now fill in the convolution function vector
  convSupport=cfLen/convSampling;
  convSize=convSampling*(2*convSupport+2);
  convFunc.resize(convSize);
  convFunc=0.0;
  convFunc(Slice(0,cfLen-1,1))=trimConvFunc(Slice(0,cfLen-1,1));


}

// Initialize for a transform from the Sky domain. This means that
// we grid-correct, and FFT the image
void SDGrid::initializeToVis(ImageInterface<Complex>& iimage,
			     const VisBuffer& vb)
{
  image=&iimage;

  ok();

  init();

  if(convType=="pb") {
    findPBAsConvFunction(*image, vb);
  }

  // reset msId_p and lastAntID_p to -1
  // this is to ensure correct antenna position in getXYPos
  msId_p = -1;
  lastAntID_p = -1;

  // Initialize the maps for polarization and channel. These maps
  // translate visibility indices into image indices
  initMaps(vb);

  // First get the CoordinateSystem for the image and then find
  // the DirectionCoordinate
  CoordinateSystem coords=image->coordinates();
  Int directionIndex=coords.findCoordinate(Coordinate::DIRECTION);
  AlwaysAssert(directionIndex>=0, AipsError);
  directionCoord=coords.directionCoordinate(directionIndex);
  /*if((image->shape().product())>cachesize) {
    isTiled=true;
  }
  else {
    isTiled=false;
    }*/
  isTiled=false;
  nx    = image->shape()(0);
  ny    = image->shape()(1);
  npol  = image->shape()(2);
  nchan = image->shape()(3);

  // If we are memory-based then read the image in and create an
  // ArrayLattice otherwise just use the PagedImage
  /*if(isTiled) {
    lattice=image;
    wLattice=wImage;
  }
  else*/
{
    // Make the grid the correct shape and turn it into an array lattice
    IPosition gridShape(4, nx, ny, npol, nchan);
    griddedData.resize(gridShape);
    griddedData = Complex(0.0);

    wGriddedData.resize(gridShape);
    wGriddedData = 0.0;

    if(arrayLattice) delete arrayLattice; arrayLattice=0;
    arrayLattice = new ArrayLattice<Complex>(griddedData);

    if(wArrayLattice) delete wArrayLattice; wArrayLattice=0;
    wArrayLattice = new ArrayLattice<Float>(wGriddedData);
    wArrayLattice->set(0.0);
    wLattice=wArrayLattice;

    // Now find the SubLattice corresponding to the image
    IPosition blc(4, (nx-image->shape()(0)+(nx%2==0))/2, (ny-image->shape()(1)+(ny%2==0))/2, 0, 0);
    IPosition stride(4, 1);
    IPosition trc(blc+image->shape()-stride);
    LCBox gridBox(blc, trc, gridShape);
    SubLattice<Complex> gridSub(*arrayLattice, gridBox, true);

    // Do the copy
    gridSub.copyData(*image);

    lattice=arrayLattice;
  }

  AlwaysAssert(lattice, AipsError);
  AlwaysAssert(wLattice, AipsError);

}

void SDGrid::finalizeToVis()
{
  /*if(isTiled) {

    logIO() << LogOrigin("SDGrid", "finalizeToVis")  << LogIO::NORMAL;

    AlwaysAssert(imageCache, AipsError);
    AlwaysAssert(image, AipsError);
    ostringstream o;
    imageCache->flush();
    imageCache->showCacheStatistics(o);
    logIO() << o.str() << LogIO::POST;
    }*/
  if(pointingToImage) delete pointingToImage; pointingToImage=0;
}


// Initialize the FFT to the Sky. Here we have to setup and initialize the
// grid.
void SDGrid::initializeToSky(ImageInterface<Complex>& iimage,
			     Matrix<Float>& weight, const VisBuffer& vb)
{
  // image always points to the image
  image=&iimage;

  ok();

  init();

  if(convType=="pb") {
    findPBAsConvFunction(*image, vb);
  }

  // reset msId_p and lastAntID_p to -1
  // this is to ensure correct antenna position in getXYPos
  msId_p = -1;
  lastAntID_p = -1;

  // Initialize the maps for polarization and channel. These maps
  // translate visibility indices into image indices
  initMaps(vb);
  //cerr << "ToSky cachesize " << cachesize << " im shape " << (image->shape().product()) << endl;
  /*if((image->shape().product())>cachesize) {
    isTiled=true;
  }
  else {
    isTiled=false;
  }
  */
  //////////////No longer using isTiled
  isTiled=false;
  nx    = image->shape()(0);
  ny    = image->shape()(1);
  npol  = image->shape()(2);
  nchan = image->shape()(3);

  sumWeight=0.0;
  weight.resize(sumWeight.shape());
  weight=0.0;

  // First get the CoordinateSystem for the image and then find
  // the DirectionCoordinate
  CoordinateSystem coords=image->coordinates();
  Int directionIndex=coords.findCoordinate(Coordinate::DIRECTION);
  AlwaysAssert(directionIndex>=0, AipsError);
  directionCoord=coords.directionCoordinate(directionIndex);

  // Initialize for in memory or to disk gridding. lattice will
  // point to the appropriate Lattice, either the ArrayLattice for
  // in memory gridding or to the image for to disk gridding.
  /*if(isTiled) {
    imageCache->flush();
    image->set(Complex(0.0));
    lattice=image;
    wLattice=wImage;
  }
  else*/
  {
    IPosition gridShape(4, nx, ny, npol, nchan);
    logIO() << LogOrigin("SDGrid", "initializeToSky", WHERE) << LogIO::DEBUGGING
        << "gridShape = " << gridShape << LogIO::POST;
    griddedData.resize(gridShape);
    griddedData=Complex(0.0);
    if(arrayLattice) delete arrayLattice; arrayLattice=0;
    arrayLattice = new ArrayLattice<Complex>(griddedData);
    lattice=arrayLattice;
    wGriddedData.resize(gridShape);
    wGriddedData=0.0;
    if(wArrayLattice) delete wArrayLattice; wArrayLattice=0;
    wArrayLattice = new ArrayLattice<Float>(wGriddedData);
    wLattice=wArrayLattice;

    // clipping related stuff
    if (clipminmax_) {
      gmin_.resize(gridShape);
      gmin_ = Complex(FLT_MAX);
      gmax_.resize(gridShape);
      gmax_ = Complex(-FLT_MAX);
      wmin_.resize(gridShape);
      wmin_ = 0.0f;
      wmax_.resize(gridShape);
      wmax_ = 0.0f;
      npoints_.resize(gridShape);
      npoints_ = 0;
    }
  }
  AlwaysAssert(lattice, AipsError);
  AlwaysAssert(wLattice, AipsError);
}

void SDGrid::finalizeToSky()
{

  // Now we flush the cache and report statistics
  // For memory based, we don't write anything out yet.
  /*if(isTiled) {
    logIO() << LogOrigin("SDGrid", "finalizeToSky")  << LogIO::NORMAL;

    AlwaysAssert(image, AipsError);
    AlwaysAssert(imageCache, AipsError);
    imageCache->flush();
    ostringstream o;
    imageCache->showCacheStatistics(o);
    logIO() << o.str() << LogIO::POST;
  }
  */

  if(pointingToImage) delete pointingToImage; pointingToImage=0;
}

Array<Complex>* SDGrid::getDataPointer(const IPosition& centerLoc2D,
				       Bool readonly) {
  Array<Complex>* result;
  // Is tiled: get tiles and set up offsets
  centerLoc(0)=centerLoc2D(0);
  centerLoc(1)=centerLoc2D(1);
  result=&imageCache->tile(offsetLoc, centerLoc, readonly);
  return result;
}
Array<Float>* SDGrid::getWDataPointer(const IPosition& centerLoc2D,
				      Bool readonly) {
  Array<Float>* result;
  // Is tiled: get tiles and set up offsets
  centerLoc(0)=centerLoc2D(0);
  centerLoc(1)=centerLoc2D(1);
  result=&wImageCache->tile(offsetLoc, centerLoc, readonly);
  return result;
}

#define NEED_UNDERSCORES
#if defined(NEED_UNDERSCORES)
#define ggridsd ggridsd_
#define dgridsd dgridsd_
#define ggridsdclip ggridsdclip_
#endif

extern "C" {
   void ggridsd(Double*,
		const Complex*,
                Int*,
                Int*,
                Int*,
		const Int*,
		const Int*,
		const Float*,
		Int*,
		Int*,
		Complex*,
		Float*,
                Int*,
		Int*,
		Int *,
		Int *,
                Int*,
		Int*,
		Float*,
		Int*,
		Int*,
		Double*);
   void ggridsdclip(Double*,
                 const Complex*,
                 Int*,
                 Int*,
                 Int*,
                 const Int*,
                 const Int*,
                 const Float*,
                 Int*,
                 Int*,
                 Complex*,
                 Float*,
                 Int*,
                 Complex*,
                 Float*,
                 Complex*,
                 Float*,
                 Int*,
                 Int*,
                 Int *,
                 Int *,
                 Int*,
                 Int*,
                 Float*,
                 Int*,
                 Int*,
                 Double*);
   void dgridsd(Double*,
		Complex*,
                Int*,
                Int*,
		const Int*,
		const Int*,
		Int*,
		Int*,
		const Complex*,
                Int*,
		Int*,
		Int *,
		Int *,
                Int*,
		Int*,
		Float*,
		Int*,
		Int*);
}

void SDGrid::put(const VisBuffer& vb, Int row, Bool dopsf,
		 FTMachine::Type type)
{
  LogIO os(LogOrigin("SDGrid", "put"));

  gridOk(convSupport);

  //Check if ms has changed then cache new spw and chan selection
  if(vb.newMS()){
    matchAllSpwChans(vb);
    lastIndex_p=0;
    if (lastIndexPerAnt_p.nelements() < (size_t)vb.numberAnt()) {
      lastIndexPerAnt_p.resize(vb.numberAnt());
    }
    lastIndexPerAnt_p=0;
  }
  //Here we redo the match or use previous match

  //Channel matching for the actual spectral window of buffer
  if(doConversion_p[vb.spectralWindow()]){
    matchChannel(vb.spectralWindow(), vb);
  }
  else{
    chanMap.resize();
    chanMap=multiChanMap_p[vb.spectralWindow()];
  }
  //No point in reading data if its not matching in frequency
  if(max(chanMap)==-1)
    return;

  Matrix<Float> imagingweight;
  //imagingweight=&(vb.imagingWeight());
  pickWeights(vb, imagingweight);

  if(type==FTMachine::PSF || type==FTMachine::COVERAGE)
    dopsf=true;
  if(dopsf) type=FTMachine::PSF;
  Cube<Complex> data;
  //Fortran gridder need the flag as ints
  Cube<Int> flags;
  Matrix<Float> elWeight;
  interpolateFrequencyTogrid(vb, imagingweight,data, flags, elWeight, type);
  //cerr << "number of rows " << vb.nRow() << " data shape " << data.shape() << endl;
  Bool iswgtCopy;
  const Float *wgtStorage;
  wgtStorage=elWeight.getStorage(iswgtCopy);
  Bool isCopy;
  const Complex *datStorage=0;
  if(!dopsf)
    datStorage=data.getStorage(isCopy);

  // If row is -1 then we pass through all rows
  Int startRow, endRow, nRow;
  if (row==-1) {
    nRow=vb.nRow();
    startRow=0;
    endRow=nRow-1;
  } else {
    nRow=1;
    startRow=row;
    endRow=row;
  }


  Vector<Int> rowFlags(vb.flagRow().nelements());
  rowFlags=0;
  for (Int rownr=startRow; rownr<=endRow; rownr++) {
    if(vb.flagRow()(rownr)) rowFlags(rownr)=1;
  }

  // Take care of translation of Bools to Integer
  Int idopsf=0;
  if(dopsf) idopsf=1;

  /*if(isTiled) {
    for (Int rownr=startRow; rownr<=endRow; rownr++) {

      if(getXYPos(vb, rownr)) {

	IPosition centerLoc2D(2, Int(xyPos(0)), Int(xyPos(1)));
	Array<Complex>* dataPtr=getDataPointer(centerLoc2D, false);
	Array<Float>*  wDataPtr=getWDataPointer(centerLoc2D, false);
	Int aNx=dataPtr->shape()(0);
	Int aNy=dataPtr->shape()(1);
	Vector<Double> actualPos(2);
	for (Int i=0;i<2;i++) {
	  actualPos(i)=xyPos(i)-Double(offsetLoc(i));
	}
	// Now use FORTRAN to do the gridding. Remember to
	// ensure that the shape and offsets of the tile are
	// accounted for.
	{
	  Bool del;
	  //	  IPosition s(data.shape());
	  const IPosition& fs=flags.shape();
	  std::vector<Int> s(fs.begin(), fs.end());

	  ggridsd(actualPos.getStorage(del),
		  datStorage,
		  &s[0],
		  &s[1],
		  &idopsf,
		  flags.getStorage(del),
		  rowFlags.getStorage(del),
		  wgtStorage,
		  &s[2],
		  &rownr,
		  dataPtr->getStorage(del),
		  wDataPtr->getStorage(del),
		  &aNx,
		  &aNy,
		  &npol,
		  &nchan,
		  &convSupport,
		  &convSampling,
		  convFunc.getStorage(del),
		  chanMap.getStorage(del),
		  polMap.getStorage(del),
		  sumWeight.getStorage(del));
	}
      }
    }
  }
  else*/
  {
    Matrix<Double> xyPositions(2, endRow-startRow+1);
    xyPositions=-1e9; // make sure failed getXYPos does not fall on grid
    for (Int rownr=startRow; rownr<=endRow; rownr++) {
      if(getXYPos(vb, rownr)) {
	xyPositions(0, rownr)=xyPos(0);
	xyPositions(1, rownr)=xyPos(1);
      }
    }
    {
      Bool del;
      //      IPosition s(data.shape());
      const IPosition& fs=flags.shape();
      std::vector<Int> s(fs.begin(), fs.end());
      Bool datCopy, wgtCopy;
      Complex * datStor=griddedData.getStorage(datCopy);
      Float * wgtStor=wGriddedData.getStorage(wgtCopy);

      Bool call_ggridsd = !clipminmax_ || dopsf;

      if (call_ggridsd) {

      ggridsd(xyPositions.getStorage(del),
	      datStorage,
	      &s[0],
	      &s[1],
	      &idopsf,
	      flags.getStorage(del),
	      rowFlags.getStorage(del),
	      wgtStorage,
	      &s[2],
	      &row,
	      datStor,
	      wgtStor,
	      &nx,
	      &ny,
	      &npol,
	      &nchan,
	      &convSupport,
	      &convSampling,
	      convFunc.getStorage(del),
	      chanMap.getStorage(del),
	      polMap.getStorage(del),
	      sumWeight.getStorage(del));

      } else {
        Bool gminCopy;
        Complex *gminStor = gmin_.getStorage(gminCopy);
        Bool gmaxCopy;
        Complex *gmaxStor = gmax_.getStorage(gmaxCopy);
        Bool wminCopy;
        Float *wminStor = wmin_.getStorage(wminCopy);
        Bool wmaxCopy;
        Float *wmaxStor = wmax_.getStorage(wmaxCopy);
        Bool npCopy;
        Int *npStor = npoints_.getStorage(npCopy);

        ggridsdclip(xyPositions.getStorage(del),
          datStorage,
          &s[0],
          &s[1],
          &idopsf,
          flags.getStorage(del),
          rowFlags.getStorage(del),
          wgtStorage,
          &s[2],
          &row,
          datStor,
          wgtStor,
          npStor,
          gminStor,
          wminStor,
          gmaxStor,
          wmaxStor,
          &nx,
          &ny,
          &npol,
          &nchan,
          &convSupport,
          &convSampling,
          convFunc.getStorage(del),
          chanMap.getStorage(del),
          polMap.getStorage(del),
          sumWeight.getStorage(del));

        gmin_.putStorage(gminStor, gminCopy);
        gmax_.putStorage(gmaxStor, gmaxCopy);
        wmin_.putStorage(wminStor, wminCopy);
        wmax_.putStorage(wmaxStor, wmaxCopy);
        npoints_.putStorage(npStor, npCopy);
      }
      griddedData.putStorage(datStor, datCopy);
      wGriddedData.putStorage(wgtStor, wgtCopy);
    }
  }
  if(!dopsf)
    data.freeStorage(datStorage, isCopy);
  elWeight.freeStorage(wgtStorage,iswgtCopy);

}

void SDGrid::get(VisBuffer& vb, Int row)
{
  LogIO os(LogOrigin("SDGrid", "get"));

  gridOk(convSupport);

  // If row is -1 then we pass through all rows
  Int startRow, endRow, nRow;
  if (row==-1) {
    nRow=vb.nRow();
    startRow=0;
    endRow=nRow-1;
    vb.modelVisCube()=Complex(0.0,0.0);
  } else {
    nRow=1;
    startRow=row;
    endRow=row;
    vb.modelVisCube().xyPlane(row)=Complex(0.0,0.0);
  }


  //Check if ms has changed then cache new spw and chan selection
  if(vb.newMS()){
    matchAllSpwChans(vb);
    lastIndex_p=0;
    if (lastIndexPerAnt_p.nelements() < (size_t)vb.numberAnt()) {
      lastIndexPerAnt_p.resize(vb.numberAnt());
    }
    lastIndexPerAnt_p=0;
  }

  //Here we redo the match or use previous match

  //Channel matching for the actual spectral window of buffer
  if(doConversion_p[vb.spectralWindow()]){
    matchChannel(vb.spectralWindow(), vb);
  }
  else{
    chanMap.resize();
    chanMap=multiChanMap_p[vb.spectralWindow()];
  }

  //No point in reading data if its not matching in frequency
  if(max(chanMap)==-1)
    return;
  Cube<Complex> data;
  Cube<Int> flags;
  getInterpolateArrays(vb, data, flags);

  Complex *datStorage;
  Bool isCopy;
  datStorage=data.getStorage(isCopy);
  // NOTE: with MS V2.0 the pointing could change per antenna and timeslot
  //
  Vector<Int> rowFlags(vb.flagRow().nelements());
  rowFlags=0;
  for (Int rownr=startRow; rownr<=endRow; rownr++) {
    if(vb.flagRow()(rownr)) rowFlags(rownr)=1;
    //single dish yes ?
    if(max(vb.uvw()(rownr).vector()) > 0.0) rowFlags(rownr)=1;
  }


  /*if(isTiled) {

    for (Int rownr=startRow; rownr<=endRow; rownr++) {

      if(getXYPos(vb, rownr)) {

	  // Get the tile
	IPosition centerLoc2D(2, Int(xyPos(0)), Int(xyPos(1)));
	Array<Complex>* dataPtr=getDataPointer(centerLoc2D, true);
	Int aNx=dataPtr->shape()(0);
	Int aNy=dataPtr->shape()(1);

	// Now use FORTRAN to do the gridding. Remember to
	// ensure that the shape and offsets of the tile are
	// accounted for.
	Bool del;
	Vector<Double> actualPos(2);
	for (Int i=0;i<2;i++) {
	  actualPos(i)=xyPos(i)-Double(offsetLoc(i));
	}
	//	IPosition s(data.shape());
	const IPosition& fs=data.shape();
	std::vector<Int> s(fs.begin(), fs.end());

	dgridsd(actualPos.getStorage(del),
		datStorage,
		&s[0],
		&s[1],
		flags.getStorage(del),
		rowFlags.getStorage(del),
		&s[2],
		&rownr,
		dataPtr->getStorage(del),
		&aNx,
		&aNy,
		&npol,
		&nchan,
		&convSupport,
		&convSampling,
		convFunc.getStorage(del),
		chanMap.getStorage(del),
		polMap.getStorage(del));
      }
    }
  }
  else*/
  {
    Matrix<Double> xyPositions(2, endRow-startRow+1);
    xyPositions=-1e9;
    for (Int rownr=startRow; rownr<=endRow; rownr++) {
      if(getXYPos(vb, rownr)) {
	xyPositions(0, rownr)=xyPos(0);
	xyPositions(1, rownr)=xyPos(1);
      }
    }

    Bool del;
    //    IPosition s(data.shape());
    const IPosition& fs=data.shape();
    std::vector<Int> s(fs.begin(), fs.end());
    dgridsd(xyPositions.getStorage(del),
	    datStorage,
	    &s[0],
	    &s[1],
	    flags.getStorage(del),
	    rowFlags.getStorage(del),
	    &s[2],
	    &row,
	    griddedData.getStorage(del),
	    &nx,
	    &ny,
	    &npol,
	    &nchan,
	    &convSupport,
	    &convSampling,
	    convFunc.getStorage(del),
	    chanMap.getStorage(del),
	    polMap.getStorage(del));

    data.putStorage(datStorage, isCopy);
  }
  interpolateFrequencyFromgrid(vb, data, FTMachine::MODEL);
}



  // Make a plain straightforward honest-to-FSM image. This returns
  // a complex image, without conversion to Stokes. The representation
  // is that required for the visibilities.
  //----------------------------------------------------------------------
  void SDGrid::makeImage(FTMachine::Type type,
			    ROVisibilityIterator& vi,
			    ImageInterface<Complex>& theImage,
			    Matrix<Float>& weight) {


    logIO() << LogOrigin("FTMachine", "makeImage0") << LogIO::NORMAL;

    // Loop over all visibilities and pixels
    VisBuffer vb(vi);

    // Initialize put (i.e. transform to Sky) for this model
    vi.origin();

    if(vb.polFrame()==MSIter::Linear) {
      StokesImageUtil::changeCStokesRep(theImage, StokesImageUtil::LINEAR);
    }
    else {
      StokesImageUtil::changeCStokesRep(theImage, StokesImageUtil::CIRCULAR);
    }
    Bool useCorrected= !(vi.msColumns().correctedData().isNull());
    if((type==FTMachine::CORRECTED) && (!useCorrected))
      type=FTMachine::OBSERVED;
    Bool normalize=true;
    if(type==FTMachine::COVERAGE)
      normalize=false;

    Int Nx=theImage.shape()(0);
    Int Ny=theImage.shape()(1);
    Int Npol=theImage.shape()(2);
    Int Nchan=theImage.shape()(3);
    Double memtot=Double(HostInfo::memoryTotal(true))*1024.0; // return in kB
    Int nchanInMem=Int(memtot/2.0/8.0/Double(Nx*Ny*Npol));
    Int nloop=nchanInMem >= Nchan ? 1 : Nchan/nchanInMem+1;
    ImageInterface<Complex> *imCopy=NULL;
    IPosition blc(theImage.shape());
    IPosition trc(theImage.shape());
    blc-=blc; //set all values to 0
    trc=theImage.shape();
    trc-=1; // set trc to image size -1
    if(nloop==1) {
      imCopy=& theImage;
      nchanInMem=Nchan;
    }
    else
      logIO()  << "Not enough memory to image in one go \n will process the image in   "
	       << nloop
	      << " sections  "
	      << LogIO::POST;

    weight.resize(Npol, Nchan);
    Matrix<Float> wgtcopy(Npol, Nchan);

    Bool isWgtZero=true;
    for (Int k=0; k < nloop; ++k){
      Int bchan=k*nchanInMem;
      Int echan=(k+1)*nchanInMem < Nchan ?  (k+1)*nchanInMem-1 : Nchan-1;

      if(nloop > 1) {
	 blc[3]=bchan;
	 trc[3]=echan;
	 Slicer sl(blc, trc, Slicer::endIsLast);
	 imCopy=new SubImage<Complex>(theImage, sl, true);
	 wgtcopy.resize(npol, echan-bchan+1);
      }
      vi.originChunks();
      vi.origin();
      initializeToSky(*imCopy,wgtcopy,vb);


      // for minmax clipping
      logIO() << LogOrigin("SDGrid", "makeImage", WHERE) << LogIO::DEBUGGING
          << "doclip_ = " << (clipminmax_ ? "TRUE" : "FALSE") << " (" << clipminmax_ << ")" << LogIO::POST;
      if (clipminmax_) {
        logIO() << LogOrigin("SDGRID", "makeImage", WHERE)
             << LogIO::DEBUGGING << "use ggridsd2 for imaging" << LogIO::POST;
      }

      // Loop over the visibilities, putting VisBuffers
      for (vi.originChunks();vi.moreChunks();vi.nextChunk()) {
	for (vi.origin(); vi.more(); vi++) {

	  switch(type) {
	  case FTMachine::RESIDUAL:
	    vb.visCube()=vb.correctedVisCube();
	    vb.visCube()-=vb.modelVisCube();
	    put(vb, -1, false);
	    break;
	  case FTMachine::MODEL:
	    put(vb, -1, false, FTMachine::MODEL);
	    break;
	  case FTMachine::CORRECTED:
	    put(vb, -1, false, FTMachine::CORRECTED);
	    break;
	  case FTMachine::PSF:
	    vb.visCube()=Complex(1.0,0.0);
	    put(vb, -1, true, FTMachine::PSF);
	    break;
	  case FTMachine::COVERAGE:
	    vb.visCube()=Complex(1.0);
	    put(vb, -1, true, FTMachine::COVERAGE);
	    break;
	  case FTMachine::OBSERVED:
	  default:
	    put(vb, -1, false, FTMachine::OBSERVED);
	    break;
	  }
	}
      }
      finalizeToSky();
      // Normalize by dividing out weights, etc.
      getImage(wgtcopy, normalize);
      if(max(wgtcopy)==0.0){
	if(nloop > 1)
	  logIO() << LogIO::WARN
		  << "No useful data in SDGrid: weights all zero for image slice  " << k
		  << LogIO::POST;
      }
      else
	isWgtZero=false;

      weight(Slice(0, Npol), Slice(bchan, echan-bchan+1))=wgtcopy;
      if(nloop >1) delete imCopy;
    }//loop k
    if(isWgtZero)
      logIO() << LogIO::SEVERE
	      << "No useful data in SDGrid: weights all zero"
	      << LogIO::POST;
  }



// Finalize : optionally normalize by weight image
ImageInterface<Complex>& SDGrid::getImage(Matrix<Float>& weights,
					  Bool normalize)
{
  AlwaysAssert(lattice, AipsError);
  AlwaysAssert(image, AipsError);

  logIO() << LogOrigin("SDGrid", "getImage") << LogIO::NORMAL;

  // execute minmax clipping
  clipMinMax();

  weights.resize(sumWeight.shape());

  convertArray(weights,sumWeight);

  // If the weights are all zero then we cannot normalize
  // otherwise we don't care.
  ///////////////////////
  /*{
  PagedImage<Float> thisScreen(lattice->shape(), image->coordinates(), "TheData");
  LatticeExpr<Float> le(abs(*lattice));
  thisScreen.copyData(le);
  PagedImage<Float> thisScreen2(lattice->shape(), image->coordinates(), "TheWeight");
  LatticeExpr<Float> le2(abs(*wLattice));
  thisScreen2.copyData(le2);
  }*/
  /////////////////////

  if(normalize) {
    if(max(weights)==0.0) {
      //logIO() << LogIO::WARN << "No useful data in SDGrid: weights all zero"
      //	      << LogIO::POST;
      //image->set(Complex(0.0));
      return *image;
    }
    //Timer tim;
    //tim.mark();
    //lattice->copyData((LatticeExpr<Complex>)(iif((*wLattice<=minWeight_p), 0.0,
    //						 (*lattice)/(*wLattice))));
    //As we are not using disk based lattices anymore the above uses too much memory for
    // ArrayLattice...it does not do a real  inplace math but rather mutiple copies
    // seem to be involved  thus the less elegant but faster and less memory hog loop
    // below.

    IPosition pos(4);
    for (Int chan=0; chan < nchan; ++chan){
      pos[3]=chan;
      for( Int pol=0; pol < npol; ++ pol){
	pos[2]=pol;
	for (Int y=0; y < ny ; ++y){
	  pos[1]=y;
	  for (Int x=0; x < nx; ++x){
	    pos[0]=x;
	    Float wgt=wGriddedData(pos);
	    if(wgt > minWeight_p)
	      griddedData(pos)=griddedData(pos)/wgt;
	    else
	      griddedData(pos)=0.0;
	  }
	}
      }
      }
    //tim.show("After normalizing");
  }

  //if(!isTiled)
  {
    // Now find the SubLattice corresponding to the image
    IPosition gridShape(4, nx, ny, npol, nchan);
    IPosition blc(4, (nx-image->shape()(0)+(nx%2==0))/2,
		  (ny-image->shape()(1)+(ny%2==0))/2, 0, 0);
    IPosition stride(4, 1);
    IPosition trc(blc+image->shape()-stride);
    LCBox gridBox(blc, trc, gridShape);
    SubLattice<Complex> gridSub(*arrayLattice, gridBox);

    // Do the copy
    image->copyData(gridSub);
  }
  return *image;
}

// Return weights image
void SDGrid::getWeightImage(ImageInterface<Float>& weightImage, Matrix<Float>& weights)
{
  AlwaysAssert(lattice, AipsError);
  AlwaysAssert(image, AipsError);

  logIO() << LogOrigin("SDGrid", "getWeightImage") << LogIO::NORMAL;

  weights.resize(sumWeight.shape());
  convertArray(weights,sumWeight);

  weightImage.copyData(*wArrayLattice);
}

void SDGrid::ok() {
  AlwaysAssert(image, AipsError);
}

// Get the index into the pointing table for this time. Note that the
// in the pointing table, TIME specifies the beginning of the spanned
// time range, whereas for the main table, TIME is the centroid.
// Note that the behavior for multiple matches is not specified! i.e.
// if there are multiple matches, the index returned depends on the
// history of previous matches. It is deterministic but not obvious.
// One could cure this by searching but it would be considerably
// costlier.
Int SDGrid::getIndex(const MSPointingColumns& mspc, const Double& time,
		     const Double& interval, const Int& antid) {
  //Int start=lastIndex_p;
  Int start=lastIndexPerAnt_p[antid];
  Double tol=interval < 0.0 ? time*C::dbl_epsilon : interval/2.0;
  // Search forwards
  Int nrows=mspc.time().nrow();
  for (Int i=start;i<nrows;i++) {
    // Check for ANTENNA_ID. When antid<0 (default) ANTENNA_ID in POINTING table < 0 is ignored.
    // MS v2 definition indicates ANTENNA_ID in POINING >= 0.
    if (antid >= 0 && mspc.antennaId()(i) != antid) {
      continue;
    }

    Double midpoint = mspc.time()(i); // time in POINTING table is midpoint
    // If the interval in the pointing table is negative, use the last
    // entry. Note that this may be invalid (-1) but in that case
    // the calling routine will generate an error
    Double mspc_interval = mspc.interval()(i);

    if(mspc_interval<0.0) {
      //return lastIndex_p;
      return lastIndexPerAnt_p[antid];
    }
    // Pointing table interval is specified so we have to do a match
    else {
      // Is the midpoint of this pointing table entry within the specified
      // tolerance of the main table entry?
      if(abs(midpoint-time) <= (mspc_interval/2.0+tol)) {
      	//lastIndex_p=i;
      	lastIndexPerAnt_p[antid]=i;
	return i;
      }
    }
  }
  // Search backwards
  for (Int i=start;i>=0;i--) {
    if (antid >= 0 && mspc.antennaId()(i) != antid) {
      continue;
    }
    Double midpoint = mspc.time()(i); // time in POINTING table is midpoint
    Double mspc_interval = mspc.interval()(i);
    if(mspc_interval<0.0) {
      //return lastIndex_p;
      return lastIndexPerAnt_p[antid];
    }
    // Pointing table interval is specified so we have to do a match
    else {
      // Is the midpoint of this pointing table entry within the specified
      // tolerance of the main table entry?
      if(abs(midpoint-time) <= (mspc_interval/2.0+tol)) {
	//lastIndex_p=i;
  lastIndexPerAnt_p[antid]=i;
	return i;
      }
    }
  }
  // No match!
  return -1;
}

Bool SDGrid::getXYPos(const VisBuffer& vb, Int row) {

  Bool dointerp;
  Bool nullPointingTable = false;
  const MSPointingColumns& act_mspc = vb.msColumns().pointing();
  nullPointingTable = (act_mspc.nrow() < 1);
  Int pointIndex = -1;
  if (!nullPointingTable) {
    ///if(vb.newMS())  vb.newMS does not work well using msid
    if (vb.msId() != msId_p) {
      lastIndex_p = 0;
      if (lastIndexPerAnt_p.nelements() < (size_t)vb.numberAnt()) {
        lastIndexPerAnt_p.resize(vb.numberAnt());
      }
      lastIndexPerAnt_p = 0;
      msId_p = vb.msId();
      lastAntID_p = -1;
    }
    pointIndex = getIndex(act_mspc, vb.time()(row), -1.0, vb.antenna1()(row));
    //Try again to locate a pointing within the integration
    if (pointIndex < 0)
      pointIndex = getIndex(act_mspc, vb.time()(row), vb.timeInterval()(row), vb.antenna1()(row));
  }
  if (!nullPointingTable && ((pointIndex < 0) || (pointIndex >= Int(act_mspc.time().nrow())))) {
    ostringstream o;
    o << "Failed to find pointing information for time " <<
      MVTime(vb.time()(row)/86400.0) << ": Omitting this point";
    logIO_p << LogIO::DEBUGGING << String(o) << LogIO::POST;
    //    logIO_p << String(o) << LogIO::POST;
    return false;
  }

  dointerp = false;
  if (!nullPointingTable && (vb.timeInterval()(row) < act_mspc.interval()(pointIndex))) {
    dointerp = true;
    if (!isSplineInterpolationReady) {
      interpolator = new SDPosInterpolator(vb, pointingDirCol_p);
      isSplineInterpolationReady = true;
    } else {
      if (!interpolator->inTimeRange(vb.time()(row), vb.antenna1()(row))) {
	// setup spline interpolator for the current dataset (CAS-11261, 2018/5/22 WK)
	delete interpolator;
	interpolator = 0;
	interpolator = new SDPosInterpolator(vb, pointingDirCol_p);
      }
    }
  }

  if (!pointingToImage) {
    // Set the frame
    MPosition pos;
    lastAntID_p = vb.antenna1()(row);
    pos = vb.msColumns().antenna().positionMeas()(lastAntID_p);
    MEpoch dummyEpoch(Quantity(0, "s"));
    mFrame_p = MeasFrame(dummyEpoch, pos);
    if (!nullPointingTable) {
      if (dointerp) {
        worldPosMeas = directionMeas(act_mspc, pointIndex, vb.time()(row));
      } else {
        worldPosMeas = directionMeas(act_mspc, pointIndex);
      }
    } else {
      worldPosMeas = vb.direction1()(row);
    }

    //worldPosMeas=directionMeas(act_mspc, pointIndex);
    // Make a machine to convert from the worldPosMeas to the output
    // Direction Measure type for the relevant frame
    MDirection::Ref outRef(directionCoord.directionType(), mFrame_p);
    pointingToImage = new MDirection::Convert(worldPosMeas, outRef);
    if (!pointingToImage) {
      logIO_p << "Cannot make direction conversion machine" << LogIO::EXCEPTION;
    }

    // perform direction conversion to clear cache
    MDirection _dir_tmp = (*pointingToImage)();
  }

  MEpoch epoch(Quantity(vb.time()(row), "s"));
  mFrame_p.resetEpoch(epoch);
  if (lastAntID_p != vb.antenna1()(row)) {
    if (lastAntID_p == -1) {
      // antenna ID is unset
      logIO_p << LogIO::DEBUGGING
        << "update antenna position for conversion: new MS ID " << msId_p
        << ", antenna ID " << vb.antenna1()(row) << LogIO::POST;
    } else {
      logIO_p << LogIO::DEBUGGING
        << "update antenna position for conversion: MS ID " << msId_p
        << ", last antenna ID " << lastAntID_p
        << ", new antenna ID " << vb.antenna1()(row) << LogIO::POST;
    }
    MPosition pos;
    lastAntID_p = vb.antenna1()(row);
    pos = vb.msColumns().antenna().positionMeas()(lastAntID_p);
    mFrame_p.resetPosition(pos);
  }

  if (!nullPointingTable) {
    if (dointerp) {
      MDirection newdir = directionMeas(act_mspc, pointIndex, vb.time()(row));
      worldPosMeas = (*pointingToImage)(newdir);
      //Vector<Double> newdirv = newdir.getAngle("rad").getValue();
      //cerr<<"dir0="<<newdirv(0)<<endl;

    //fprintf(pfile,"%.8f %.8f \n", newdirv(0), newdirv(1));
    //printf("%lf %lf \n", newdirv(0), newdirv(1));
    } else {
      worldPosMeas = (*pointingToImage)(directionMeas(act_mspc, pointIndex));
    }
  } else {
    worldPosMeas = (*pointingToImage)(vb.direction1()(row));
  }

  Bool result = directionCoord.toPixel(xyPos, worldPosMeas);
  if (!result) {
    logIO_p << "Failed to find a pixel for pointing direction of "
	    << MVTime(worldPosMeas.getValue().getLong("rad")).string(MVTime::TIME) << ", " << MVAngle(worldPosMeas.getValue().getLat("rad")).string(MVAngle::ANGLE) << LogIO::WARN << LogIO::POST;
    return false;
  }

  if ((pointingDirCol_p == "SOURCE_OFFSET") || (pointingDirCol_p == "POINTING_OFFSET")) {
    //there is no sense to track in offset coordinates...hopefully the
    //user set the image coords right
    fixMovingSource_p = false;
  }
  if (fixMovingSource_p) {
    if (xyPosMovingOrig_p.nelements() < 2) {
      directionCoord.toPixel(xyPosMovingOrig_p, firstMovingDir_p);
    }
    //via HADEC or AZEL for parallax of near sources
    MDirection::Ref outref1(MDirection::AZEL, mFrame_p);
    MDirection tmphadec=MDirection::Convert(movingDir_p, outref1)();
    MDirection actSourceDir=(*pointingToImage)(tmphadec);
    Vector<Double> actPix;
    directionCoord.toPixel(actPix, actSourceDir);

    //cout << row << " scan " << vb.scan()(row) << "xyPos " << xyPos << " xyposmovorig " << xyPosMovingOrig_p << " actPix " << actPix << endl;

    xyPos=xyPos+xyPosMovingOrig_p-actPix;
  }

  return result;
  // Convert to pixel coordinates
}

MDirection SDGrid::directionMeas(const MSPointingColumns& mspc, const Int& index){
  if (pointingDirCol_p == "TARGET") {
    return mspc.targetMeas(index);
  } else if (pointingDirCol_p == "POINTING_OFFSET") {
    if (!mspc.pointingOffsetMeasCol().isNull()) {
      return mspc.pointingOffsetMeas(index);
    }
    cerr << "No PONTING_OFFSET column in POINTING table" << endl;
  } else if (pointingDirCol_p == "SOURCE_OFFSET") {
    if (!mspc.sourceOffsetMeasCol().isNull()) {
      return mspc.sourceOffsetMeas(index);
    }
    cerr << "No SOURCE_OFFSET column in POINTING table" << endl;
  } else if (pointingDirCol_p == "ENCODER") {
    if (!mspc.encoderMeas().isNull()) {
      return mspc.encoderMeas()(index);
    }
    cerr << "No ENCODER column in POINTING table" << endl;
  }

  //default  return this
  return mspc.directionMeas(index);
  }

// for the cases, interpolation of the pointing direction requires
// when data sampling rate higher than the pointing data recording
// (e.g. fast OTF)
MDirection SDGrid::directionMeas(const MSPointingColumns& mspc, const Int& index,
                                 const Double& time){
  //spline interpolation
  if (isSplineInterpolationReady) {
    Int antid = mspc.antennaId()(index);
    if (interpolator->doSplineInterpolation(antid)) {
      return interpolator->interpolateDirectionMeasSpline(mspc, time, index, antid);
    }
  }

  //linear interpolation (as done before CAS-7911)
  Int index1, index2;
  if (time < mspc.time()(index)) {
    if (index > 0) {
      index1 = index-1;
      index2 = index;
    } else if (index == 0) {
      index1 = index;
      index2 = index+1;
    }
  } else {
    if (index < Int(mspc.nrow()-1)) {
      index1 = index;
      index2 = index+1;
    } else if (index == Int(mspc.nrow()-1) || (mspc.time()(index)-mspc.time()(index+1))>2*mspc.interval()(index)) {
      index1 = index-1;
      index2 = index;
    }
  }
  return interpolateDirectionMeas(mspc, time, index, index1, index2);
}

MDirection SDGrid::interpolateDirectionMeas(const MSPointingColumns& mspc,
                                            const Double& time,
                                            const Int& index,
                                            const Int& indx1,
                                            const Int& indx2){
  Vector<Double> dir1,dir2;
  Vector<Double> newdir(2),scanRate(2);
  Double dLon, dLat;
  Double ftime,ftime2,ftime1,dtime;
  MDirection newDirMeas;
  MDirection::Ref rf;
  Bool isfirstRefPt;

  if (indx1 == index) {
    isfirstRefPt = true;
  } else {
    isfirstRefPt = false;
  }

  if (pointingDirCol_p == "TARGET") {
    dir1 = mspc.targetMeas(indx1).getAngle("rad").getValue();
    dir2 = mspc.targetMeas(indx2).getAngle("rad").getValue();
  } else if (pointingDirCol_p == "POINTING_OFFSET") {
    if (!mspc.pointingOffsetMeasCol().isNull()) {
      dir1 = mspc.pointingOffsetMeas(indx1).getAngle("rad").getValue();
      dir2 = mspc.pointingOffsetMeas(indx2).getAngle("rad").getValue();
    } else {
      cerr << "No PONTING_OFFSET column in POINTING table" << endl;
    }
  } else if (pointingDirCol_p == "SOURCE_OFFSET") {
    if (!mspc.sourceOffsetMeasCol().isNull()) {
      dir1 = mspc.sourceOffsetMeas(indx1).getAngle("rad").getValue();
      dir2 = mspc.sourceOffsetMeas(indx2).getAngle("rad").getValue();
    } else {
      cerr << "No SOURCE_OFFSET column in POINTING table" << endl;
    }
  } else if (pointingDirCol_p == "ENCODER") {
    if (!mspc.encoderMeas().isNull()) {
      dir1 = mspc.encoderMeas()(indx1).getAngle("rad").getValue();
      dir2 = mspc.encoderMeas()(indx2).getAngle("rad").getValue();
    } else {
      cerr << "No ENCODER column in POINTING table" << endl;
    }
  } else {
    dir1 = mspc.directionMeas(indx1).getAngle("rad").getValue();
    dir2 = mspc.directionMeas(indx2).getAngle("rad").getValue();
  }

  dLon = dir2(0) - dir1(0);
  dLat = dir2(1) - dir1(1);
  ftime = floor(mspc.time()(indx1));
  ftime2 = mspc.time()(indx2) - ftime;
  ftime1 = mspc.time()(indx1) - ftime;
  dtime = ftime2 - ftime1;
  scanRate(0) = dLon/dtime;
  scanRate(1) = dLat/dtime;
  //scanRate(0) = dir2(0)/dtime-dir1(0)/dtime;
  //scanRate(1) = dir2(1)/dtime-dir1(1)/dtime;
  //Double delT = mspc.time()(index)-time;
  //cerr<<"index="<<index<<" dLat="<<dLat<<" dtime="<<dtime<<" delT="<< delT<<endl;
  //cerr<<"deldirlat="<<scanRate(1)*fabs(delT)<<endl;
  if (isfirstRefPt) {
    newdir(0) = dir1(0)+scanRate(0)*fabs(mspc.time()(index)-time);
    newdir(1) = dir1(1)+scanRate(1)*fabs(mspc.time()(index)-time);
    rf = mspc.directionMeas(indx1).getRef();
  } else {
    newdir(0) = dir2(0)-scanRate(0)*fabs(mspc.time()(index)-time);
    newdir(1) = dir2(1)-scanRate(1)*fabs(mspc.time()(index)-time);
    rf = mspc.directionMeas(indx2).getRef();
  }
  //default  return this
  Quantity rDirLon(newdir(0), "rad");
  Quantity rDirLat(newdir(1), "rad");
  newDirMeas = MDirection(rDirLon, rDirLat, rf);
  //cerr<<"newDirMeas rf="<<newDirMeas.getRefString()<<endl;
  //return mspc.directionMeas(index);
  return newDirMeas;
}

void SDGrid::pickWeights(const VisBuffer& vb, Matrix<Float>& weight){
  //break reference
  weight.resize();

  if (useImagingWeight_p) {
    weight.reference(vb.imagingWeight());
  } else {
    const Cube<Float> weightspec(vb.weightSpectrum());
    weight.resize(vb.nChannel(), vb.nRow());

    if (weightspec.nelements() == 0) {
      for (Int k = 0; k < vb.nRow(); ++k) {
        //cerr << "nrow " << vb.nRow() << " " << weight.shape() << "  "  << weight.column(k).shape() << endl;
        weight.column(k).set(vb.weight()(k));
      }
    } else {
      Int npol = weightspec.shape()(0);
      if (npol == 1) {
        for (Int k = 0; k < vb.nRow(); ++k) {
          for (int chan = 0; chan < vb.nChannel(); ++chan) {
            weight(chan, k)=weightspec(0, chan, k);
          }
        }
      } else {
        for (Int k = 0; k < vb.nRow(); ++k) {
          for (int chan = 0; chan < vb.nChannel(); ++chan) {
            weight(chan, k) = (weightspec(0, chan, k) + weightspec((npol-1), chan, k))/2.0f;
          }
        }
      }
    }
  }
}

void SDGrid::clipMinMax() {
  if (clipminmax_) {
    Bool gmin_b, gmax_b, wmin_b, wmax_b, np_b;
    const auto *gmin_p = gmin_.getStorage(gmin_b);
    const auto *gmax_p = gmax_.getStorage(gmax_b);
    const auto *wmin_p = wmin_.getStorage(wmin_b);
    const auto *wmax_p = wmax_.getStorage(wmax_b);
    const auto *np_p = npoints_.getStorage(np_b);

    Bool data_b, weight_b, sumw_b;
    auto data_p = griddedData.getStorage(data_b);
    auto weight_p = wGriddedData.getStorage(weight_b);
    auto sumw_p = sumWeight.getStorage(sumw_b);

    auto arrayShape = griddedData.shape();
    size_t num_xy = arrayShape.getFirst(2).product();
    size_t num_polchan = arrayShape.getLast(2).product();
    for (size_t i = 0; i < num_xy; ++i) {
      for (size_t j = 0; j < num_polchan; ++j) {
        auto k = i * num_polchan + j;
        if (np_p[k] == 1) {
          auto wt = wmin_p[k];
          data_p[k] = wt * gmin_p[k];
          weight_p[k] = wt;
          sumw_p[j] += wt;
        } else if (np_p[k] == 2) {
          auto wt = wmin_p[k];
          data_p[k] = wt * gmin_p[k];
          weight_p[k] = wt;
          sumw_p[j] += wt;
          wt = wmax_p[k];
          data_p[k] += wt * gmax_p[k];
          weight_p[k] += wt;
          sumw_p[j] += wt;
        }
      }
    }

    wGriddedData.putStorage(weight_p, weight_b);
    griddedData.putStorage(data_p, data_b);
    sumWeight.putStorage(sumw_p, sumw_b);

    npoints_.freeStorage(np_p, np_b);
    wmax_.freeStorage(wmax_p, wmax_b);
    wmin_.freeStorage(wmin_p, wmin_b);
    gmax_.freeStorage(gmax_p, gmax_b);
    gmin_.freeStorage(gmin_p, gmin_b);
  }
}

} //#End casa namespace