//# WBCleanImageSkyModel.cc: Implementation of WBCleanImageSkyModel class
//# Copyright (C) 1996,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: WBCleanImageSkyModel.cc 13615 2010-12-20 14:04:00 UrvashiRV$
//# v2.6 : Added psf-patch support to reduce memory footprint.

#include <casa/Arrays/ArrayMath.h>
#include <synthesis/MeasurementComponents/WBCleanImageSkyModel.h>
#include <synthesis/MeasurementEquations/CubeSkyEquation.h>
#include <casa/OS/File.h>
#include <synthesis/MeasurementEquations/SkyEquation.h>
#include <synthesis/TransformMachines/StokesImageUtil.h>
#include <synthesis/MeasurementEquations/LatticeModel.h>
#include <synthesis/MeasurementEquations/LatConvEquation.h>
#include <casa/Exceptions/Error.h>
#include <casa/BasicSL/String.h>
#include <casa/Utilities/Assert.h>

#include <images/Images/PagedImage.h>
#include <images/Images/SubImage.h>
#include <images/Regions/ImageRegion.h>
#include <images/Regions/RegionManager.h>

#include <images/Regions/WCBox.h>

#include <measures/Measures/Quality.h>
#include <coordinates/Coordinates/QualityCoordinate.h>
#include <images/Images/ImageUtilities.h>

#include <scimath/Mathematics/MatrixMathLA.h>

#include <msvis/MSVis/VisSet.h>
#include <msvis/MSVis/VisSetUtil.h>

#include <ms/MeasurementSets/MSColumns.h>

#include <casa/sstream.h>

#include <casa/Logging/LogMessage.h>
#include <casa/Logging/LogSink.h>

#include <casa/OS/HostInfo.h>


using namespace casacore;
namespace casa { //# NAMESPACE CASA - BEGIN
#define TMR(a) "[User: " << a.user() << "] [System: " << a.system() << "] [Real: " << a.real() << "]"
	
#define MIN(a,b) ((a)<=(b) ? (a) : (b))
#define MAX(a,b) ((a)>=(b) ? (a) : (b))

/*************************************
 *          Constructor
 *************************************/
WBCleanImageSkyModel::WBCleanImageSkyModel()
{
  initVars();
  nscales_p=4;
  ntaylor_p=2;
  refFrequency_p=1.42e+09;
  scaleSizes_p.resize(0);
};
  WBCleanImageSkyModel::WBCleanImageSkyModel(const Int ntaylor,const Int nscales,const Double reffreq)
{
  initVars();
  nscales_p=nscales;
  ntaylor_p=ntaylor;
  refFrequency_p=reffreq;
  scaleSizes_p.resize(0);
};
  WBCleanImageSkyModel::WBCleanImageSkyModel(const Int ntaylor,const Vector<Float>& userScaleSizes,const Double reffreq)
{
  initVars();
  if(userScaleSizes.nelements()==0)  os << "No scales set !" << LogIO::WARN;
  if(userScaleSizes[0]!=0.0)
  {
    nscales_p=userScaleSizes.nelements()+1;
    scaleSizes_p.resize(nscales_p);
    for(Int i=1;i<nscales_p;i++) scaleSizes_p[i] = userScaleSizes[i-1];
    scaleSizes_p[0]=0.0;
  }
  else
  {
    nscales_p=userScaleSizes.nelements();
    scaleSizes_p.resize(nscales_p);
    for(Int i=0;i<nscales_p;i++) scaleSizes_p[i] = userScaleSizes[i];
  }
  ntaylor_p=ntaylor;
  refFrequency_p=reffreq;
};

void WBCleanImageSkyModel::initVars()
{
  adbg=0; 
  ddbg=1; // output per iteration
  tdbg=0;
  
  modified_p=true;
  memoryMB_p = Double(HostInfo::memoryTotal(true)/1024)/(2.0);
  donePSF_p=false;
  doneMTMCinit_p=false;

  numbermajorcycles_p=0;
  nfields_p=1;
  lc_p.resize(0);

  os = LogIO(LogOrigin("WBCleanImageSkyModel","solve"));

  setAlgorithm("MSMFS");
  
}

/*************************************
 *          Destructor
 *************************************/
WBCleanImageSkyModel::~WBCleanImageSkyModel()
{
  lc_p.resize(0);
  //cout << "WBCleanImageSkyModel destructor " << endl;

  /*
  if(nmodels_p > numberOfModels())
    {
      resizeWorkArrays(numberOfModels());
      nmodels_p = numberOfModels();
    }
  */

};

/*************************************
 *          Solver
 *************************************/
Bool WBCleanImageSkyModel::solve(SkyEquation& se) 
{
	os << "MSMFS algorithm (v2.6) with " << ntaylor_p << " Taylor coefficients and Reference Frequency of " << refFrequency_p  << " Hz" << LogIO::POST;
	Int stopflag=0;
	Int nchan=0,npol=0;

	/* Gather shape information */
	nmodels_p = numberOfModels();

	if(nmodels_p % ntaylor_p != 0)
	{
	  os << "Incorrect number of input models " << LogIO::EXCEPTION;
	  os << "NModels = N_fields x N_taylor" << LogIO::EXCEPTION;
	  AlwaysAssert((nmodels_p % ntaylor_p == 0), AipsError);
	}

	/* Check supplied bandwidth-ratio and print warnings if needed */
        checkParameters();

	/* Calc the number of fields */
	nfields_p = nmodels_p/ntaylor_p;
        ///// NOTE : Code is written with loops on 'fields' to support a future implementation
        /////             Disable it for now, since it has not been tested.
        //AlwaysAssert(nfields_p==1, AipsError);

	//cout << "Number of fields : " << nfields_p << endl;

        /* Current restriction : one pol and one chan-plane */
	for(Int model=0;model<nmodels_p;model++)
	{
	  nx = image(model).shape()(0);
	  ny = image(model).shape()(1);
	  npol=image(model).shape()(2);
	  nchan=image(model).shape()(3);
	  if(nchan > 1) os << "Cannot process more than one output channel" << LogIO::EXCEPTION;
	  if(npol > 1) os << "Cannot process more than one output polarization" << LogIO::EXCEPTION;
	  AlwaysAssert((nchan==1), AipsError);  
	  AlwaysAssert((npol==1), AipsError);  
	}

	//------------------------------- For 'active'  ---------------------------------------------------------------///
	/* Calculate the initial residual image for all models. */
	if( se.isNewFTM() == false )
	  {
	    if(!donePSF_p)
	      {
		os << "Calculating initial residual images..." << LogIO::POST;
		solveResiduals(se,(numberIterations()<1)?true:False);
	      }
	    else
	      {
		/*
		if(numbermajorcycles_p>0) 
		  {
		    os << "RE-Calculating residual images because previous residuals have been modified in-place during restoration to be 'coefficient residuals'." << LogIO::POST;
		    solveResiduals(se,(numberIterations()<1)?true:False);
		  }
		*/
	      }
	  }
	//------------------------------- For 'active'  ---------------------------------------------------------------///
	
	/* Create the Point Spread Functions */
	if(!donePSF_p)
	{
  	    /* Resize the work arrays to calculate extra PSFs */
	    Int original_nmodels = nmodels_p;
	    nmodels_p = original_nmodels + nfields_p * (ntaylor_p - 1);
	    resizeWorkArrays(nmodels_p);

	    try
	    {
	      /* Make the 2N-1 PSFs */
	      os << "Calculating spectral PSFs..." << LogIO::POST;
              makeSpectralPSFs(se, numberIterations()<0?true:False);
	    }
	    catch(AipsError &x)
	    {
	      /* Resize the work arrays to normal size - the destructors use 'nmodels_p' on other lists */
	      nmodels_p = original_nmodels;
	      resizeWorkArrays(nmodels_p);
	      os << "Could not make PSFs. Please check image co-ordinate system : " << x.getMesg() << LogIO::EXCEPTION;
	    }

	    if(adbg) cout << "Shape of lc_p : " << lc_p.nelements() << endl;
	    /* Initialize MTMC, allocate memory, and send in all 2N-1 psfs */
	    if(lc_p.nelements()==0 && numberIterations()>=0)
	      {
		lc_p.resize(nfields_p);
                Bool state=true;
		/* Initialize the MultiTermMatrixCleaners */
		for(Int thismodel=0;thismodel<nfields_p;thismodel++)
		  {
		    lc_p[thismodel].setscales(scaleSizes_p);
		    lc_p[thismodel].setntaylorterms(ntaylor_p);
		    nx = image(thismodel).shape()(0);
		    ny = image(thismodel).shape()(1);
		    state &= lc_p[thismodel].initialise(nx,ny); // allocates memory once....
		  }
                if( !state ) // initialise will return false if there is any internal inconsistency with settings so far.
		  {
		    lc_p.resize(0);
		    nmodels_p = original_nmodels;
		    resizeWorkArrays(nmodels_p);
		    os << LogIO::SEVERE << "Could not initialize MS-MFS minor cycle" << LogIO::EXCEPTION;
		    return false; // redundant
		  }
		
		/* Send all 2N-1 PSFs into the MultiTermLatticeCleaner */
		for(Int thismodel=0;thismodel<nfields_p;thismodel++)
		  {
		    for (Int order=0;order<2*ntaylor_p-1;order++)
		      {
			// This should be doing a reference only. Make sure of this.
			Matrix<Float> tempMat;
			Array<Float> tempArr;
			(PSF(getModelIndex(thismodel,order))).get(tempArr,true);
			tempMat.reference(tempArr);
			
			lc_p[thismodel].setpsf( order , tempMat ); 
		      }
		  }
		doneMTMCinit_p = true;
	      }
	    
	    /* Resize the work arrays to normal size - for residual comps, etc. */
	    nmodels_p = original_nmodels;
	    resizeWorkArrays(nmodels_p);
            ///// TODO : Make sure this releases memory as it is supposed to.	   

	    donePSF_p=true;
	}


	//------------------------------ For new FTMs --------------------------------------------------------------//
	///// Consider doing lc_p.setmodel and getmodel instead of dividing/multiplying the avgPB in NewMTFT::initializeToVis
	if( se.isNewFTM() == true )
	  {
	    /* Calculate the initial residual image for all models. */
	    if( numbermajorcycles_p==0 )
	      {
		os << "Calculating initial residual images..." << LogIO::POST;
		solveResiduals(se,(numberIterations()<1)?true:False);
	      }

	  }
	//------------------------------- For new FTMs -------------------------------------------------------------//

	/* Return if niter=0 */
	/* Check if this is an interactive-clean run, or if niter=0 */
	if(adbg) cout << "NumberIterations - before any cycles: " << numberIterations() << endl;
	if(numberIterations() < 1)
	{
		return true;
	}

	/* Check that lc_p's have been initialized by now.. */
	if(doneMTMCinit_p == false)
	  {
	    os << LogIO::SEVERE << "MultiTermMatrixCleaners are un-initialized, perhaps because of a previous im.clean(niter=-1) call. Please close imager, re-open it, and run with niter>=0" << LogIO::POST;
	  }

	/* Set up the Mask image */
	for(Int thismodel=0;thismodel<nfields_p;thismodel++)
	{
	  if(hasMask(getModelIndex(thismodel,0))) 
	  {
	    if(adbg) os << "Sending in the mask for lc_p : " << thismodel << LogIO::POST;

            Matrix<Float> tempMat;
            Array<Float> tempArr;
            (mask(getModelIndex(thismodel,0))).get(tempArr,true);
            tempMat.reference(tempArr);

	    lc_p[thismodel].setmask( tempMat );
	  }
	}

	/******************* START MAJOR CYCLE LOOP *****************/
        /* Logic for number of iterations in the minor-cycle vs major cycle vs total,
           how they relate to convergence thresholds, and how they change with 
           interactive vs non-interactive use (user specified total niter, vs niters per 
           major cycle...) is a bit of a mess. Needs cleanup. Right now, hard-coded for
           nfields=1 */
        previous_maxresidual_p=1e+10;
	Int index=0;
        Float fractionOfPsf=0.1;
        Vector<Int> iterationcount(nfields_p); iterationcount=0;
        Vector<Int> moreiterations(nfields_p); moreiterations=0;
        Vector<Int> thiscycleniter(nfields_p); thiscycleniter=0;
	for(Int itercountmaj=0;itercountmaj<1000;itercountmaj++)
	  {
	    numbermajorcycles_p ++;
	    os << "**** Major Cycle " << numbermajorcycles_p << LogIO::POST;
	    thiscycleniter=0;
	    /* Compute stopping threshold for this major cycle */
	    stopflag = static_cast<casacore::Int>(computeFluxLimit(fractionOfPsf));
	    /* If the peak residual is already less than the user-threshold, stop */
	    if(stopflag==1) break;
	    /* If we detect divergence across major cycles, stop */
	    if(stopflag==-1) break;
	    
	    for(Int thismodel=0;thismodel<nfields_p;thismodel++) // For now, nfields_p=1 always.
	      {

		if( !isSolveable(getModelIndex(thismodel,0)) )
		  {
		    // This field is not to be cleaned in this set of minor-cycle iterations
		    continue;
		  }

		/* Number of iterations left to do */ 
		moreiterations[thismodel] = numberIterations() - iterationcount[thismodel];
		
		/* If all iterations are done for this run, stop - for all fields*/
		if(moreiterations[thismodel] <=0 ) {stopflag=-1;break;}
		
		/* Send in the current model and residual */
		for (Int order=0;order<ntaylor_p;order++)
		  {
		    index = getModelIndex(thismodel,order);
		    
		    Matrix<Float> tempMat;
		    Array<Float> tempArr;
		    
		    (residual(index)).get(tempArr,true);
		    tempMat.reference(tempArr);
		    lc_p[thismodel].setresidual(order,tempMat); 
		    
		    (image(index)).get(tempArr,true);
		    tempMat.reference(tempArr);
		    lc_p[thismodel].setmodel(order,tempMat); 
		  }
		
		/* Deconvolve */
		/* Return value is the number of minor-cycle iterations done */
		os << "Starting Minor Cycle iterations for field : " << thismodel << LogIO::POST;
		thiscycleniter[thismodel] = lc_p[thismodel].mtclean(moreiterations[thismodel], fractionOfPsf, gain(), threshold());
		
		/* A signal for a singular Hessian : stop */
		if(thiscycleniter[thismodel]==-2) { stopflag=-2; break;}
		
		/* A signal for convergence with no iterations  */
		/* One field may have converged, while others go on... so 'continue' */
		//             if(thiscycleniter[thismodel]==0) { stopflag=1; break; }
		if(thiscycleniter[thismodel]==0) { stopflag=1; continue; }
		
		///* A signal for divergence. Save current model and stop (force a major cycle). */
		//if(thiscycleniter==-1) { stopflag=1; }
		
		/* Increment the minor-cycle iteration counter */
		iterationcount[thismodel] += thiscycleniter[thismodel];
		
		/* Get out the updated model */
		for (Int order=0;order<ntaylor_p;order++)
		  {
		    index = getModelIndex(thismodel,order);
		    Matrix<Float> tempMod;
		    lc_p[thismodel].getmodel(order,tempMod);
		    image(index).put(tempMod);
		  }           
		
	      }// end of model loop
	    
            if(adbg) cout << "end of major cycle : " << itercountmaj << " -> iterationcount : " << iterationcount << "  thiscycleniter : " << thiscycleniter << "  stopflag : " << stopflag << endl;
	    
	    /* Exit without further ado if MTMC cannot invert matrices */
	    if(stopflag == -2)
	      {
		os << "Cannot invert Multi-Term Hessian matrix. Please check the reference-frequency and ensure that the number of frequency-channels in the selected data >= nterms" << LogIO::WARN << LogIO::POST;
		break;
	      }
	    
	    /* If reached 1000 major cycles - assume something is wrong */
	    if(itercountmaj==999) os << " Reached the allowed maximum of 1000 major cycles " << LogIO::POST;
	    
	    /* Do the prediction and residual computation for all models. */
	    /* Exit if we're already at the user-specified flux threshold, or reached numberIterations() */
	    if( abs(stopflag)==1 || max(iterationcount) >= numberIterations() || itercountmaj==999) 
	      {
		os << "Calculating final residual images..." << LogIO::POST;
		/* Call 'solveResiduals' with modelToMS = true to write the model to the MS */
		solveResiduals(se,true);
		break;
	      }
	    else 
	      {
		os << "Calculating new residual images..." << LogIO::POST;
		solveResiduals(se);
	      }

	    /*	    
	    if( se.isNewFTM() == true )
	      {
		// The model will get PB-corrected in-place before prediction.
		// This needs to be reset to what the preceeding minor cycle got, 
		//    for incremental additions in the next cycle.
		saveCurrentModels();
	      }
	    */

	  } 
	/******************* END MAJOR CYCLE LOOP *****************/
	
	/* Compute and write alpha,beta results to disk */
	///writeResultsToDisk();
	//	calculateCoeffResiduals();
	
	/* stopflag=1 -> stopped because the user-threshold was reached */
        /* stopflag=-1 -> stopped because number of iterations was reached */
        /* stopflag=-2 -> stopped because of singular Hessian */
        /* stopflag=0 -> reached maximum number of major-cycle iterations !! */
	if(stopflag>0) return(true);
	else return(false);
} // END OF SOLVE


void WBCleanImageSkyModel::saveCurrentModels()
{
  
  for(Int thismodel=0;thismodel<nfields_p;thismodel++)
    {
      /* Get out the updated model */
      for (Int order=0;order<ntaylor_p;order++)
	{
	  Int index = getModelIndex(thismodel,order);
	  Matrix<Float> tempMod;
	  lc_p[thismodel].getmodel(order,tempMod);
	  image(index).put(tempMod);
	}           
    }// end of model loop
  
}// end of saveCurrentModels


/* Calculate stopping threshold for the current set of minor-cycle iterations */
/* Note : The stopping threshold is computed from the residual image.
   However, MTMC checks on the peak of (residual * psf * scale_0).
   These values can differ slightly. 
   TODO : Perhaps move this function back into MTMC. 
   (It was brought out to conform to the other devonvolvers..)
*/
Float WBCleanImageSkyModel::computeFluxLimit(Float &fractionOfPsf)
{
  LogIO os(LogOrigin("WBCleanImageSkyModel", "computeFluxLimit", WHERE));
  Float maxres=0.0,maxval=0.0,minval=0.0;
  Vector<Float> fmaxres(nfields_p); fmaxres=0.0;
  
  /* Measure the peak residual across all fields */
  for(Int field=0;field<nfields_p;field++)
    {
      Int index = getModelIndex(field,0);
      Array<Float> tempArr;
      (residual(index)).get(tempArr,true);
      IPosition maxpos(tempArr.shape()),minpos(tempArr.shape());
      minMax(minval,maxval, minpos, maxpos,tempArr);
      fmaxres[field]=maxval;
    }
  
  /* Pick the peak residual across all fields */
  maxres = max(fmaxres);
  if(adbg) cout << "Peak Residual across fields (over all pixels) : " << maxres << endl;
  
  /* If we've already converged, return */
  if(maxres < threshold()) 
    {
      os << "Peak residual : " << maxres << " is lower than the user-specified stopping threshold : " << threshold() << LogIO::POST;
      return 1;
    }
  
  /* If we detect divergence across major cycles, warn or STOP */
  if(fabs( ((maxres - previous_maxresidual_p)/previous_maxresidual_p ) > 10.0 ))
    {
      os << "Peak residual : " << maxres << " has increased by more than a factor of 10 across major cycles. Could be diverging. Stopping" << LogIO::POST;
      return -1;
    }
  if(fabs( ((maxres - previous_maxresidual_p)/previous_maxresidual_p ) > 2.0 ))
    {
      os << "Peak residual : " << maxres << " has increased across major cycles. Could be diverging, but continuing..." << LogIO::POST;
    }
  previous_maxresidual_p = maxres;
  
  /* Find PSF sidelobe level */
  Float maxpsfside=0.0;
  for(uInt field=0;static_cast<Int>(field)<nfields_p;field++)
    {
      Int index = getModelIndex(field,0);
      /* abs(min of the PSF) will be treated as the max sidelobe level */
      Array<Float> tempArr;
      (PSF(index)).get(tempArr,true);
      IPosition maxpos(tempArr.shape()),minpos(tempArr.shape());
      minMax(minval,maxval, minpos, maxpos,tempArr);
      maxpsfside = max(maxpsfside, abs(minval));
    }
  
  fractionOfPsf = min(cycleMaxPsfFraction_p, cycleFactor_p * maxpsfside);
  if(fractionOfPsf>0.8) fractionOfPsf=0.8;
  // cyclethreshold = max(threshold(), fractionOfPsf * maxres);
  
  // if(adbg) 
  {
    os << "Peak Residual (all pixels) : " << maxres  << "  User Threshold : " << threshold() << "  Max PSF Sidelobe : " << abs(minval) <<  " User maxPsfFraction : " << cycleMaxPsfFraction_p  << "  User cyclefactor : " << cycleFactor_p << "  fractionOfPsf = min(maxPsfFraction, PSFsidelobe x cyclefactor) : " << fractionOfPsf << LogIO::POST;
    //    os << "Stopping threshold for this major cycle min(user threshold , fractionOfPsf x Max Residual) : " <<  cyclethreshold  << endl;
  }
  
  return 0;
}

/***********************************************************************/
Bool WBCleanImageSkyModel::calculateAlphaBeta(const Vector<String> &restoredNames, 
                                                                           const Vector<String> &residualNames)
{
  LogIO os(LogOrigin("WBCleanImageSkyModel", "calculateAlphaBeta", WHERE));
  //UNUSED: Int index=0;
  Bool writeerror=true;
  

  for(Int field=0;field<nfields_p;field++)
    {
      Int baseindex = getModelIndex(field,0);
      String alphaname,betaname, alphaerrorname;
      if(  ( (restoredNames[baseindex]).substr( (restoredNames[baseindex]).length()-3 , 3 ) ).matches("tt0") )
	{
	  alphaname = (restoredNames[baseindex]).substr(0,(restoredNames[baseindex]).length()-3) + "alpha";
	  betaname = (restoredNames[baseindex]).substr(0,(restoredNames[baseindex]).length()-3) + "beta";
	  if(writeerror) alphaerrorname = alphaname+".error";
	}
      else
	{
	  alphaname = (restoredNames[baseindex]) +  String(".alpha");
	  betaname = (restoredNames[baseindex]) +  String(".beta");
	}
      
      /* Create empty alpha image, and alpha error image */
      PagedImage<Float> imalpha(image(baseindex).shape(),image(baseindex).coordinates(),alphaname); 
      imalpha.set(0.0);

      /* Open restored images */
      PagedImage<Float> imtaylor0(restoredNames[getModelIndex(field,0)]);
      PagedImage<Float> imtaylor1(restoredNames[getModelIndex(field,1)]);

      /* Open first residual image */
      PagedImage<Float> residual0(residualNames[getModelIndex(field,0)]);

      /* Create a mask - make this adapt to the signal-to-noise */
      LatticeExprNode leMaxRes=max(residual0);
      Float maxres = leMaxRes.getFloat();
      // Threshold is either 10% of the peak residual (psf sidelobe level) or 
      // user threshold, if deconvolution has gone that deep.
      Float specthreshold = MAX( threshold()*5 , maxres/5.0 );
      os << "Calculating spectral parameters for  Intensity > MAX(threshold*5,peakresidual/5) = " << specthreshold << " Jy/beam" << LogIO::POST;
      LatticeExpr<Float> mask1(iif((imtaylor0)>(specthreshold),1.0,0.0));
      LatticeExpr<Float> mask0(iif((imtaylor0)>(specthreshold),0.0,1.0));

      /////// Calculate alpha
      LatticeExpr<Float> alphacalc( ((imtaylor1)*mask1)/((imtaylor0)+(mask0)) );
      imalpha.copyData(alphacalc);

      // Set the restoring beam for alpha
      ImageInfo ii = imalpha.imageInfo();
      ii.setRestoringBeam( (imtaylor0.imageInfo()).restoringBeam() );
      imalpha.setImageInfo(ii);
      //imalpha.setUnits(Unit("Spectral Index"));
      imalpha.table().unmarkForDelete();

      // Make a mask for the alpha image
      LatticeExpr<Bool> lemask(iif((imtaylor0 > specthreshold) , true, false));

      createMask(lemask, imalpha);
      os << "Written Spectral Index Image : " << alphaname << LogIO::POST;


      ////// Calculate error on alpha
      if(writeerror)
	{
	  PagedImage<Float> imalphaerror(image(baseindex).shape(),image(baseindex).coordinates(),alphaerrorname); 
	  imalphaerror.set(0.0);
	  PagedImage<Float> residual1(residualNames[getModelIndex(field,1)]);

	  LatticeExpr<Float> alphacalcerror( abs(alphacalc) * sqrt( ( (residual0*mask1)/(imtaylor0+mask0) )*( (residual0*mask1)/(imtaylor0+mask0) ) + ( (residual1*mask1)/(imtaylor1+mask0) )*( (residual1*mask1)/(imtaylor1+mask0) )  ) );
	  imalphaerror.copyData(alphacalcerror);
	  imalphaerror.setImageInfo(ii);
          createMask(lemask, imalphaerror);
	  imalphaerror.table().unmarkForDelete();      
	  os << "Written Spectral Index Error Image : " << alphaerrorname << LogIO::POST;

	  //          mergeDataError( imalpha, imalphaerror, alphaerrorname+".new" );

	}

      ////// Calculate beta
      if(ntaylor_p>2)
	{
	  PagedImage<Float> imbeta(image(baseindex).shape(),image(baseindex).coordinates(),betaname); 
	  imbeta.set(0.0);
	  PagedImage<Float> imtaylor2(restoredNames[getModelIndex(field,2)]);
	  
	  LatticeExpr<Float> betacalc( ((imtaylor2)*mask1)/((imtaylor0)+(mask0))-0.5*(imalpha)*(imalpha-1.0) );
	  imbeta.copyData(betacalc);
	  imbeta.setImageInfo(ii);
	  //imbeta.setUnits(Unit("Spectral Curvature"));
          createMask(lemask, imbeta);
	  imbeta.table().unmarkForDelete();

	  os << "Written Spectral Curvature Image : " << betaname << LogIO::POST;
	}
      
    }// field loop

  return 0;

}
/***********************************************************************/

Bool WBCleanImageSkyModel::createMask(LatticeExpr<Bool> &lemask, ImageInterface<Float> &outimage)
{
      ImageRegion outreg = outimage.makeMask("mask0",false,true);
      LCRegion& outmask=outreg.asMask();
      outmask.copyData(lemask);
      outimage.defineRegion("mask0",outreg, RegionHandler::Masks, true);
      outimage.setDefaultMask("mask0");
      return true;
}


/***********************************************************************/
Bool WBCleanImageSkyModel::calculateCoeffResiduals()
{
  for(Int field=0;field<nfields_p;field++)
    {
      Int baseindex = getModelIndex(field,0);

      
      // Send in the final residuals
      for (Int order=0;order<ntaylor_p;order++)
	{
	  Int index = getModelIndex(field,order);
	  Matrix<Float> tempMat;
	  Array<Float> tempArr;
	  (residual(index)).get(tempArr,true);
	  tempMat.reference(tempArr);
	  lc_p[baseindex].setresidual(order,tempMat); 
	}      
      
      // Compute principal solution in-place.
      lc_p[baseindex].computeprincipalsolution();

      // Get the new residuals
      for (Int order=0;order<ntaylor_p;order++)
	{
	  Int index = getModelIndex(field,order);
	  Matrix<Float> tempMod;
	  lc_p[baseindex].getresidual(order,tempMod);
	  residual(index).put(tempMod);
	}

      
      /*

      //Apply Inverse Hessian to the residuals 
      IPosition gip(4,image(baseindex).shape()[0],image(baseindex).shape()[1],1,1);
      Matrix<Double> invhessian;
      lc_p[field].getinvhessian(invhessian);
      //cout << "Inverse Hessian : " << invhessian << endl;
      
      Int tindex;
      LatticeExprNode len_p;
      PtrBlock<TempLattice<Float>* > coeffresiduals(ntaylor_p); //,smoothresiduals(ntaylor_p);
      for(Int taylor1=0;taylor1<ntaylor_p;taylor1++)
	{
	  coeffresiduals[taylor1] = new TempLattice<Float>(gip,memoryMB_p);
	}
      
      //Apply the inverse Hessian to the residuals 
      for(Int taylor1=0;taylor1<ntaylor_p;taylor1++)
	{
	  len_p = LatticeExprNode(0.0);
	  for(Int taylor2=0;taylor2<ntaylor_p;taylor2++)
	    {
	      tindex = getModelIndex(field,taylor2);
	      len_p = len_p + LatticeExprNode((Float)(invhessian)(taylor1,taylor2)*(residual(tindex)));
	    }
	  (*coeffresiduals[taylor1]).copyData(LatticeExpr<Float>(len_p));
	}

      //Fill in the residual images with these coefficient residuals 
      for(Int taylor=0;taylor<ntaylor_p;taylor++)
	{
          tindex = getModelIndex(field,taylor);
	  (residual(tindex)).copyData(LatticeExpr<Float>(*coeffresiduals[taylor]));
	}

      for(uInt i=0;i<coeffresiduals.nelements();i++) 
	{
	  if(coeffresiduals[i]) delete coeffresiduals[i];
	}
      */


    }//end of field loop
  os << "Converting final residuals to 'coefficient residuals', for restoration" << LogIO::POST;
  return true;
}//end of calculateCoeffResiduals

/***********************************************************************/
///// Write alpha and beta to disk. Calculate from smoothed model + residuals.
Int WBCleanImageSkyModel::writeResultsToDisk()
{
  if(ntaylor_p<=1) return 0;
  
  os << "Output Taylor-coefficient images : " << imageNames << LogIO::POST;
  //  if(ntaylor_p==2) os << "Calculating Spectral Index" << LogIO::POST;
  //  if(ntaylor_p>2) os << "Calculating Spectral Index and Curvature" << LogIO::POST;
  
  
  GaussianBeam beam;
  Int index=0;
  
  for(Int field=0;field<nfields_p;field++)
    {
      PtrBlock<TempLattice<Float>* > smoothed;
      if(ntaylor_p>2) smoothed.resize(3);
      else if(ntaylor_p==2) smoothed.resize(2);
      
      Int baseindex = getModelIndex(field,0);
      String alphaname,betaname;
      if(  ( (imageNames[baseindex]).substr( (imageNames[baseindex]).length()-3 , 3 ) ).matches("tt0") )
	{
	  alphaname = (imageNames[baseindex]).substr(0,(imageNames[baseindex]).length()-3) + "alpha";
	  betaname = (imageNames[baseindex]).substr(0,(imageNames[baseindex]).length()-3) + "beta";
	}
      else
	{
	  alphaname = (imageNames[baseindex]) +  String(".alpha");
	  betaname = (imageNames[baseindex]) +  String(".beta");
	}
      
      StokesImageUtil::FitGaussianPSF(PSF(baseindex), beam);
      
      /* Create empty alpha image */
      PagedImage<Float> imalpha(image(baseindex).shape(),image(baseindex).coordinates(),alphaname); 
      imalpha.set(0.0);
      
      /* Apply Inverse Hessian to the residuals */
      IPosition gip(4,image(baseindex).shape()[0],image(baseindex).shape()[1],1,1);
      Matrix<Double> invhessian;
      lc_p[field].getinvhessian(invhessian);
      //cout << "Inverse Hessian : " << invhessian << endl;
      
      Int tindex;
      LatticeExprNode len_p;
      PtrBlock<TempLattice<Float>* > coeffresiduals(ntaylor_p); //,smoothresiduals(ntaylor_p);
      for(Int taylor1=0;taylor1<ntaylor_p;taylor1++)
	{
	  coeffresiduals[taylor1] = new TempLattice<Float>(gip,memoryMB_p);
	}
      
      /* Apply the inverse Hessian to the residuals */
      for(Int taylor1=0;taylor1<ntaylor_p;taylor1++)
	{
	  len_p = LatticeExprNode(0.0);
	  for(Int taylor2=0;taylor2<ntaylor_p;taylor2++)
	    {
	      tindex = getModelIndex(field,taylor2);
	      len_p = len_p + LatticeExprNode((Float)(invhessian)(taylor1,taylor2)*(residual(tindex)));
	    }
	  (*coeffresiduals[taylor1]).copyData(LatticeExpr<Float>(len_p));
	}

      /* Fill in the residual images with these coefficient residuals */
      for(Int taylor=0;taylor<ntaylor_p;taylor++)
	{
          tindex = getModelIndex(field,taylor);
	  (residual(taylor)).copyData(LatticeExpr<Float>(*coeffresiduals[taylor]));
	}


      /* Smooth the model images and add the above coefficient residuals */
      for(uInt i=0;i<smoothed.nelements();i++)
	{
	  smoothed[i] = new TempLattice<Float>(gip,memoryMB_p);
	  
	  index = getModelIndex(field,i);
	  LatticeExpr<Float> cop(image(index));
	  imalpha.copyData(cop);
	  StokesImageUtil::Convolve(imalpha, beam);
	  //cout << "Clean Beam from WBC : " << bmaj  << " , " << bmin << " , " << bpa << endl;
          //cout << "SkyModel internally-recorded beam for index " << index << " : " << beam(index) << endl;
	  //LatticeExpr<Float> le(imalpha); 
	  LatticeExpr<Float> le(imalpha+( *coeffresiduals[i] )); 
	  (*smoothed[i]).copyData(le);
	}
      
      
      /* Create a mask - make this adapt to the signal-to-noise */
      LatticeExprNode leMaxRes=max(residual( getModelIndex(field,0) ));
      Float maxres = leMaxRes.getFloat();
      // Threshold is either 10% of the peak residual (psf sidelobe level) or 
      // user threshold, if deconvolution has gone that deep.
      Float specthreshold = MAX( threshold()*5 , maxres/5.0 );
      os << "Calculating spectral parameters for  Intensity > MAX(threshold*5,peakresidual/5) = " << specthreshold << " Jy/beam" << LogIO::POST;
      LatticeExpr<Float> mask1(iif((*smoothed[0])>(specthreshold),1.0,0.0));
      LatticeExpr<Float> mask0(iif((*smoothed[0])>(specthreshold),0.0,1.0));
      
      /* Calculate alpha and beta */
      LatticeExpr<Float> alphacalc( ((*smoothed[1])*mask1)/((*smoothed[0])+(mask0)) );
      imalpha.copyData(alphacalc);
      
      ImageInfo ii = imalpha.imageInfo();
      ii.setRestoringBeam(beam);
      
      imalpha.setImageInfo(ii);
      //imalpha.setUnits(Unit("Spectral Index"));
      imalpha.table().unmarkForDelete();
      os << "Written Spectral Index Image : " << alphaname << LogIO::POST;
      
      if(ntaylor_p>2)
	{
	  PagedImage<Float> imbeta(image(baseindex).shape(),image(baseindex).coordinates(),betaname); 
	  imbeta.set(0.0);
	  
	  LatticeExpr<Float> betacalc( ((*smoothed[2])*mask1)/((*smoothed[0])+(mask0))-0.5*(imalpha)*(imalpha-1.0) );
	  imbeta.copyData(betacalc);
	  
	  imbeta.setImageInfo(ii);
	  //imbeta.setUnits(Unit("Spectral Curvature"));
	  imbeta.table().unmarkForDelete();
	  os << "Written Spectral Curvature Image : " << betaname << LogIO::POST;
	}
      
      /* Print out debugging info for center pixel */
      /*
	IPosition cgip(4,512,512,0,0);
	IPosition cgip2(4,490,542,0,0);
	for(Int i=0;i<ntaylor_p;i++)
	{
	cout << "Extended : " << endl;
	cout << "Original residual : " << i << " : " << residual( getModelIndex(model,i) ).getAt(cgip) << endl;
	cout << "Smoothed residual : " << i << " : " << (*smoothresiduals[i]).getAt(cgip) << endl;
	cout << "Coeff residual : " << i << " : " << (*coeffresiduals[i]).getAt(cgip) << endl;
	cout << "Point : " << endl;
	cout << "Original residual : " << i << " : " << residual( getModelIndex(model,i) ).getAt(cgip2) << endl;
	cout << "Smoothed residual : " << i << " : " << (*smoothresiduals[i]).getAt(cgip2) << endl;
	cout << "Coeff residual : " << i << " : " << (*coeffresiduals[i]).getAt(cgip2) << endl;
	}
      */
      
      
      /* Clean up temp arrays */
      for(uInt i=0;i<smoothed.nelements();i++) if(smoothed[i]) delete smoothed[i];
      for(uInt i=0;i<coeffresiduals.nelements();i++) 
	{
	  if(coeffresiduals[i]) delete coeffresiduals[i];
	}
      
    }// field loop
  return 0;
}

/***********************************************************************/
/*************************************
 *          Make Residuals and compute the current peak  
 *************************************/
Bool WBCleanImageSkyModel::solveResiduals(SkyEquation& se, Bool modelToMS) 
{
  blankOverlappingModels();
  makeNewtonRaphsonStep(se,false,modelToMS);
  restoreOverlappingModels();
  
  return true;
}
/***********************************************************************/

/*************************************
 *          Make Residuals 
 *************************************/
// Currently - all normalized by ggS from taylor0.
// SAME WITH MAKE_SPECTRAL_PSFS

Bool WBCleanImageSkyModel::makeNewtonRaphsonStep(SkyEquation& se, Bool incremental, Bool modelToMS) 
{
  LogIO os(LogOrigin("WBCleanImageSkyModel", "makeNewtonRaphsonStep"));
  se.gradientsChiSquared(incremental, modelToMS);
  
  Int index=0,baseindex=0;
  LatticeExpr<Float> le;
  for(Int thismodel=0;thismodel<nfields_p;thismodel++) 
    {
      baseindex = getModelIndex(thismodel,0);
      for(Int taylor=0;taylor<ntaylor_p;taylor++)
	{
	  /* Normalize by the Taylor 0 weight image */
	  index = getModelIndex(thismodel,taylor);
	  //cout << "Normalizing image " << index << " with " << baseindex << endl;
	  //cout << "Shapes : " << ggS(index).shape() << gS(baseindex).shape() << endl;
	  
	  // UUU
	  //	  LatticeExpr<Float> le(iif(ggS(baseindex)>(0.0), -gS(index)/ggS(baseindex), 0.0));

	  ///cout << "WBC : makeNRs : isnormalized : " << isImageNormalized() << endl;

	  if (isImageNormalized()) le = LatticeExpr<Float>(gS(index));
	  else                   le = LatticeExpr<Float>(iif(ggS(baseindex)>(0.0), 
							     -gS(index)/ggS(baseindex), 0.0));
	  residual(index).copyData(le);

	  //LatticeExprNode LEN = max( residual(index) );
	  //LatticeExprNode len2 = max( ggS(baseindex) );
	  //cout << "Max Residual : " << LEN.getFloat() << "  Max ggS : " << len2.getFloat() << endl;


	  //storeAsImg(String("Weight.")+String::toString(thismodel)+String(".")+String::toString(taylor),ggS(index));
	  //storeAsImg(String("TstResidual.")+String::toString(thismodel)+String(".")+String::toString(taylor),residual(index));
	}
    }
  modified_p=false;
  return true;
}

/*************************************
 *          Make Approx PSFs. 
 *************************************/
// The normalization ignores that done in makeSimplePSFs in the Sky Eqn
// and recalculates it from gS and ggS.
Int WBCleanImageSkyModel::makeSpectralPSFs(SkyEquation& se, Bool writeToDisk) 
{
  LogIO os(LogOrigin("WBCleanImageSkyModel", "makeSpectralPSFs"));
  if(!donePSF_p)
    {
      //make sure the psf images are made
      for (Int thismodel=0;thismodel<nmodels_p;thismodel++) 
	{
	  PSF(thismodel);
	}
    }
  
  // All 2N-1 psfs will get made here....
  se.makeApproxPSF(psf_p);  
  
  // Normalize
  Float normfactor=1.0;
  Int index=0,baseindex=0;
  LatticeExpr<Float> le;
  for (Int thismodel=0;thismodel<nfields_p;thismodel++) 
    {
      normfactor=1.0;
      baseindex = getModelIndex(thismodel,0);
      for(Int taylor=0;taylor<2*ntaylor_p-1;taylor++)
	{
	  /* Normalize by the Taylor 0 weight image */
	  index = getModelIndex(thismodel,taylor);
	  //cout << "Normalizing PSF " << index << " with " << baseindex << endl;
	  //cout << "Shapes : " << ggS(index).shape() << gS(baseindex).shape() << endl;
	  //le = LatticeExpr<Float>(iif(ggS(baseindex)>(0.0), gS(index)/ggS(baseindex), 0.0));
	  if (isImageNormalized()) le = LatticeExpr<Float>(gS(index));
	  else                   le = LatticeExpr<Float>(iif(ggS(baseindex)>(0.0), 
							     gS(index)/ggS(baseindex), 0.0));
	  PSF(index).copyData(le);
	  if(taylor==0)
	    { 
	      LatticeExprNode maxPSF=max(PSF(index));
	      normfactor = maxPSF.getFloat();
	      if(adbg) os << "Normalize PSFs for field " << thismodel << " by " << normfactor << LogIO::POST;
	    }
	  LatticeExpr<Float> lenorm(PSF(index)/normfactor);
	  PSF(index).copyData(lenorm);
	  LatticeExprNode maxPSF2=max(PSF(index));
	  Float maxpsf=maxPSF2.getFloat();
	  if(adbg) os << "Psf for Model " << thismodel << " and Taylor " << taylor << " has peak " << maxpsf << LogIO::POST;

	  if(writeToDisk)
	    {
	      //need unique name as multiple processes can be running in the 
	      //same workingdir
	      String tmpName=image_p[thismodel]->name();
	      tmpName.del(String(".model.tt0"), 0);
	      storeAsImg(tmpName+String(".TempPsf.")+String::toString(taylor),PSF(index));
	    }
	}
      
      //     index = getModelIndex(thismodel,0);
      //beam(thismodel)=0.0;
      if(!StokesImageUtil::FitGaussianPSF(PSF(baseindex),beam(thismodel))) 
	os << "Beam fit failed: using default" << LogIO::POST;
    }
  return 0;
}



/***********************************************************************/

/*************************************
 *          Store a Templattice as an image
 *************************************/

  Int WBCleanImageSkyModel::storeAsImg(String fileName, ImageInterface<Float>& theImg)
  {
  PagedImage<Float> tmp(theImg.shape(), theImg.coordinates(), fileName);
  LatticeExpr<Float> le(theImg);
  tmp.copyData(le);
  return 0;
  }
/*  
  Int WBCleanImageSkyModel::storeTLAsImg(String fileName, TempLattice<Float> &TL, ImageInterface<Float>& theImg)
  {
  PagedImage<Float> tmp(TL.shape(), theImg.coordinates(), fileName);
  LatticeExpr<Float> le(TL);
  tmp.copyData(le);
  return 0;
  }
  
  Int WBCleanImageSkyModel::storeTLAsImg(String fileName, TempLattice<Complex> &TL, ImageInterface<Float>& theImg)
  {
  PagedImage<Complex> tmp(TL.shape(), theImg.coordinates(), fileName);
  LatticeExpr<Complex> le(TL);
  tmp.copyData(le);
  return 0;
  }
*/

/**************************
  Resize Work Arrays to calculate extra PSFs.
  (Resizing back to a shorter list must free memory correctly).
*************************/
Bool WBCleanImageSkyModel::resizeWorkArrays(Int length)
{
  Int originallength = gS_p.nelements();
  
  if(length < originallength) // Clean up extra arrays
    {
      for(Int i = length; i < originallength; ++i)
	{
	  if(psf_p[i]){delete psf_p[i]; psf_p[i]=0;}
	  if(image_p[i]){delete image_p[i]; image_p[i]=0;}
	  if(cimage_p[i]){delete cimage_p[i]; cimage_p[i]=0;}
	  if(gS_p[i]){delete gS_p[i]; gS_p[i]=0;}
	  if(ggS_p[i]){delete ggS_p[i]; ggS_p[i]=0;}
	  if(work_p[i]){delete work_p[i]; work_p[i]=0;}
	  if(fluxScale_p[i]){delete fluxScale_p[i]; fluxScale_p[i]=0;}
	}

    }
  
  psf_p.resize(length,true);
  image_p.resize(length,true);
  cimage_p.resize(length,true);
  gS_p.resize(length,true);
  ggS_p.resize(length,true);
  work_p.resize(length,true);
  fluxScale_p.resize(length,true);
  
  if(length > originallength) // Add extra arrays  // TODO : This part can go - I think !!!
    {
      for(Int i = originallength; i < length; ++i)
	{
	  psf_p[i]=0;gS_p[i]=0;ggS_p[i]=0;cimage_p[i]=0;work_p[i]=0;fluxScale_p[i]=0;
	  
	  Int ind = getFieldIndex(i);
	  TempImage<Float>* imptr = 
	    new TempImage<Float> (IPosition(image_p[ind]->ndim(),
					    image_p[ind]->shape()(0),
					    image_p[ind]->shape()(1),
					    image_p[ind]->shape()(2),
					    image_p[ind]->shape()(3)),
				  image_p[ind]->coordinates(), memoryMB_p);
	  AlwaysAssert(imptr, AipsError);
	  image_p[i] = imptr;
	}
    }

  ///cout << "Memory held by ImageSkyModel : " << 6*length << " float + " << 1*length << " complex images " << endl;

  return true;
}

/************************************************************************************
Check some input parameters and print warnings for the user  
	 fbw = (fmax-fmin)/fref.  
	   if(fbw < 0.1) and nterms>2 
	       => lever-arm may be insufficient for more than alpha.
	       => polynomial fit will work but alpha interpretation may not be ok.
	   if(ref < fmin or ref > fmax) 
	       => polynomial fit will work, but alpha interpretation will not be right.
	   if(nchan==1) or fbw = 0, then ask to use only nterms=1, or choose more than one chan 

***********************************************************************************/
Bool WBCleanImageSkyModel::checkParameters()
{
  /* Check ntaylor_p, nrefFrequency_p with min and max freq from the image-coords */
  
  for(uInt i=0; i<image(0).coordinates().nCoordinates(); i++)
    {
      if( image(0).coordinates().type(i) == Coordinate::SPECTRAL )
	{
	  SpectralCoordinate speccoord(image(0).coordinates().spectralCoordinate(i));
	  Double startfreq=0.0,startpixel=-0.5;
	  Double endfreq=0.0,endpixel=+0.5;
	  speccoord.toWorld(startfreq,startpixel);
	  speccoord.toWorld(endfreq,endpixel);
	  Float fbw = (endfreq - startfreq)/refFrequency_p;
	  //os << "Freq range of the mfs channel : " << startfreq << " -> " << endfreq << endl;
	  //cout << "Fractional bandwidth : " << fbw << endl;
	  
	  os << "Fractional Bandwidth : " << fbw*100 << " %." << LogIO::POST;
	  
	  /*
	    if(fbw < 0.1 && ntaylor_p == 2 )
	    os << "Fractional Bandwidth is " << fbw*100 << " %. Please check that the flux variation across the chosen frequency range (" << startfreq << " Hz to " << endfreq << " Hz) is at least twice the single-channel noise-level. If not, please use nterms=1." << LogIO::WARN << LogIO::POST; 
	    
	    if(fbw < 0.1 && ntaylor_p > 2)
	    os << "Fractional Bandwidth is " << fbw*100 << " %. Please check that (a) the flux variation across the chosen frequency range (" << startfreq << " Hz to " << endfreq << " Hz) is at least twice the single-channel noise-level, and (b) a " << ntaylor_p << "-term Taylor-polynomial fit across this frequency range is appropriate. " << LogIO::WARN << LogIO::POST; 
	  */
	  if(refFrequency_p < startfreq || refFrequency_p > endfreq)
	    os << "A Reference frequency of " << refFrequency_p << "Hz is outside the frequency range of the selected data (" << startfreq << " Hz to " << endfreq << " Hz). A power-law interpretation of the resulting Taylor-coefficients may not be accurate." << LogIO::POST;
	  
	}
    }
  
  
  return true;
}

// Copied from MFCleanImageSkyModel
void WBCleanImageSkyModel::blankOverlappingModels(){
  if(nfields_p == 1)  return;
  
  for(Int taylor=0;taylor<ntaylor_p;taylor++)
    {
      for (Int field=0;field<nfields_p-1; ++field) 
	{
	  Int model=getModelIndex(field,taylor);
	  CoordinateSystem cs0=image(model).coordinates();
	  IPosition iblc0(image(model).shape().nelements(),0);
	  
	  IPosition itrc0(image(model).shape());
	  itrc0=itrc0-Int(1);
	  LCBox lbox0(iblc0, itrc0, image(model).shape());
	  
	  ImageRegion imagreg0(WCBox(lbox0, cs0));
	  for (Int nextfield=field+1; nextfield < nfields_p; ++nextfield)
	    {
	      Int nextmodel = getModelIndex(nextfield, taylor);
	      CoordinateSystem cs=image(nextmodel).coordinates();
	      IPosition iblc(image(nextmodel).shape().nelements(),0);
	      
	      IPosition itrc(image(nextmodel).shape());
	      itrc=itrc-Int(1);
	      
	      LCBox lbox(iblc, itrc, image(nextmodel).shape());
	      
	      ImageRegion imagreg(WCBox(lbox, cs));
	      try{
		LatticeRegion latReg=imagreg.toLatticeRegion(image(model).coordinates(), image(model).shape());
		ArrayLattice<Bool> pixmask(latReg.get());
		SubImage<Float> partToMask(image(model), imagreg, true);
		LatticeExpr<Float> myexpr(iif(pixmask, 0.0, partToMask) );
		partToMask.copyData(myexpr);
		
	      }
	      catch(...){
		//no overlap you think ?
		//cout << "Did i fail " << endl;
		continue;
	      }
	      
	      
	    }
	  
	  
	  
	}// for field
    }// for taylor
}

// Copied from MFCleanImageSkyModel
void WBCleanImageSkyModel::restoreOverlappingModels(){
  LogIO os(LogOrigin("WBImageSkyModel","restoreOverlappingModels"));
  if(nfields_p == 1)  return;
  
  for(Int taylor=0;taylor<ntaylor_p;taylor++)
    {
      
      for (Int field=0;field<nfields_p-1; ++field) 
	{
	  Int model = getModelIndex(field,taylor);
	  CoordinateSystem cs0=image(model).coordinates();
	  IPosition iblc0(image(model).shape().nelements(),0);
	  IPosition itrc0(image(model).shape());
	  itrc0=itrc0-Int(1);
	  LCBox lbox0(iblc0, itrc0, image(model).shape());
	  ImageRegion imagreg0(WCBox(lbox0, cs0));
	  
	  for (Int nextfield=field+1; nextfield < nfields_p; ++nextfield)
	    {
	      Int nextmodel = getModelIndex(nextfield,taylor);
	      CoordinateSystem cs=image(nextmodel).coordinates();
	      IPosition iblc(image(nextmodel).shape().nelements(),0);
	      
	      IPosition itrc(image(nextmodel).shape());
	      itrc=itrc-Int(1);
	      
	      LCBox lbox(iblc, itrc, image(nextmodel).shape());
	      
	      ImageRegion imagreg(WCBox(lbox, cs));
	      try{
		LatticeRegion latReg0=imagreg0.toLatticeRegion(image(nextmodel).coordinates(), image(nextmodel).shape());
		LatticeRegion latReg=imagreg.toLatticeRegion(image(model).coordinates(), image(model).shape());
		ArrayLattice<Bool> pixmask(latReg.get());

		SubImage<Float> partToMerge(image(nextmodel), imagreg0, true);
		SubImage<Float> partToUnmask(image(model), imagreg, true);
		
		LatticeExpr<Float> myexpr0(iif(pixmask,partToMerge,partToUnmask));
		partToUnmask.copyData(myexpr0);
		
	      }
	      catch(AipsError x){
		/*
		  os << LogIO::WARN
		  << "no overlap or failure of copying the clean components"
		  << x.getMesg()
		  << LogIO::POST;
		*/
		continue;
	      }
	    }// for field - inner
	}// for field - outer
    }// for taylor
}

Bool WBCleanImageSkyModel::mergeDataError(ImageInterface<Float> &data, ImageInterface<Float> &error, const String &outImg)
///Bool WBCleanImageSkyModel::mergeDataError(const String &dataImg, const String &errorImg, const String &outImg)
{
  LogIO os(LogOrigin("WBImageSkyModel",__FUNCTION__));

       // open the data and the error image
  //       ImageInterface<Float>  *data  = new PagedImage<Float>(dataImg, TableLock::AutoNoReadLocking);
  //       ImageInterface<Float>  *error = new PagedImage<Float>(errorImg, TableLock::AutoNoReadLocking);

       // create the tiled shape for the output image
       IPosition newShape=IPosition(data.shape());
       newShape.append(IPosition(1, 2));
       TiledShape tiledShape(newShape);

       // create the coordinate system for the output image
       CoordinateSystem newCSys = data.coordinates();
       Vector<Int> quality(2);
       quality(0) = Quality::DATA;
       quality(1) = Quality::ERROR;
       QualityCoordinate qualAxis(quality);
       newCSys.addCoordinate(qualAxis);

       Array<Float> outData=Array<Float>(newShape, 0.0f);
       Array<Bool>  outMask;

       // get all the data values
       Array<Float> inData;
       Slicer inSlicer(IPosition((data.shape()).size(), 0), IPosition(data.shape()));
       data.doGetSlice(inData, inSlicer);

       // define in the output array
       // the slicers for data and error
       Int qualCooPos, qualIndex;
       qualCooPos = newCSys.findCoordinate(Coordinate::QUALITY);
       (newCSys.qualityCoordinate(qualCooPos)).toPixel(qualIndex, Quality::DATA);
       IPosition outStart(newShape.size(), 0);
       outStart(newShape.size()-1)=qualIndex;
       IPosition outLength(newShape);
       outLength(newShape.size()-1)=1;
       Slicer outDataSlice(outStart, outLength);
       (newCSys.qualityCoordinate(qualCooPos)).toPixel(qualIndex, Quality::ERROR);
       outStart(newShape.size()-1)=qualIndex;
       Slicer outErrorSlice(outStart, outLength);

       // add the data values to the output array
       outData(outDataSlice) = inData.addDegenerate(1);

       // get all the error values
       error.doGetSlice(inData, inSlicer);


       // add the error values to the output array
       outData(outErrorSlice) = inData.addDegenerate(1);

       // check whether a mask is necessary
       if (data.hasPixelMask() || error.hasPixelMask()){
               Array<Bool> inMask;

               outMask=Array<Bool>(newShape, true);

               // make the mask for the data values
               if (data.hasPixelMask()){
                       inMask  = (data.pixelMask()).get();
               }
               else{
                       inMask = Array<Bool>(data.shape(), true);
               }

               // add the data mask to the output
               outMask(outDataSlice)  = inMask.addDegenerate(1);

               // make the mask for the error values
               if (error.hasPixelMask()){
                       inMask  = (error.pixelMask()).get();
               }
               else{
                       inMask = Array<Bool>(error.shape(), true);
               }

               // add the data mask to the output
               outMask(outErrorSlice) = inMask.addDegenerate(1);
       }


  // write out the combined image
       ImageUtilities::writeImage(tiledShape, newCSys, outImg, outData, os, outMask);

       //       delete data;
       //   delete error;

       return true;
}

} //# NAMESPACE CASA - END