LCOV - code coverage report
Current view: top level - synthesis/TransformMachines - PBMath2DImage.cc (source / functions) Hit Total Coverage
Test: ctest_coverage.info Lines: 0 632 0.0 %
Date: 2023-11-02 14:27:30 Functions: 0 18 0.0 %

          Line data    Source code
       1             : //# PBMath2DImage.cc: Implementation for PBMath2DImage
       2             : //# Copyright (C) 1996,1997,1998,1999,2003
       3             : //# Associated Universities, Inc. Washington DC, USA.
       4             : //#
       5             : //# This library is free software; you can redistribute it and/or modify it
       6             : //# under the terms of the GNU Library General Public License as published by
       7             : //# the Free Software Foundation; either version 2 of the License, or (at your
       8             : //# option) any later version.
       9             : //#
      10             : //# This library is distributed in the hope that it will be useful, but WITHOUT
      11             : //# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
      12             : //# FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Library General Public
      13             : //# License for more details.
      14             : //#
      15             : //# You should have received a copy of the GNU Library General Public License
      16             : //# along with this library; if not, write to the Free Software Foundation,
      17             : //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA.
      18             : //#
      19             : //# Correspondence concerning AIPS++ should be adressed as follows:
      20             : //#        Internet email: aips2-request@nrao.edu.
      21             : //#        Postal address: AIPS++ Project Office
      22             : //#                        National Radio Astronomy Observatory
      23             : //#                        520 Edgemont Road
      24             : //#                        Charlottesville, VA 22903-2475 USA
      25             : //#
      26             : //#
      27             : //# $Id$
      28             :  
      29             : #include <casacore/casa/aips.h>
      30             : #include <casacore/casa/Arrays/ArrayMath.h>
      31             : #include <casacore/casa/Exceptions/Error.h>
      32             : #include <casacore/casa/BasicSL/Complex.h>
      33             : #include <casacore/casa/BasicMath/Math.h>
      34             : #include <casacore/scimath/Mathematics/FFTServer.h>
      35             : #include <synthesis/TransformMachines/PBMath2DImage.h>
      36             : #include <casacore/images/Images/PagedImage.h>
      37             : #include <casacore/images/Images/TempImage.h>
      38             : #include <casacore/images/Images/ImageRegrid.h>
      39             : #include <casacore/images/Images/ImageSummary.h>
      40             : #include <msvis/MSVis/StokesVector.h>
      41             : #include <synthesis/TransformMachines/StokesImageUtil.h>
      42             : #include <casacore/lattices/Lattices/LatticeStepper.h>
      43             : #include <casacore/lattices/Lattices/LatticeIterator.h>
      44             : #include <casacore/lattices/LEL/LatticeExpr.h>
      45             : #include <casacore/lattices/LEL/LatticeExprNode.h>
      46             : #include <casacore/casa/Utilities/Assert.h>
      47             : #include <components/ComponentModels/ComponentType.h>
      48             : #include <casacore/casa/Quanta.h>
      49             : #include <casacore/measures/Measures.h>
      50             : #ifdef _OPENMP
      51             : #include <omp.h>
      52             : #endif
      53             : 
      54             : using namespace casacore;
      55             : typedef unsigned long long ooLong; 
      56             : namespace casa {
      57             : 
      58           0 : PBMath2DImage::PBMath2DImage(ImageInterface<Float>& reJonesImage):
      59             :   PBMath2D(), reJonesImage_p(0), reRegridJonesImage_p(0),
      60             :   imJonesImage_p(0), imRegridJonesImage_p(0),
      61             :   incrementsReJones_p(0), incrementsImJones_p(0),
      62             :   referencePixelReJones_p(0), referencePixelImJones_p(0),
      63           0 :   pa_p(0.0)
      64             : {
      65           0 :   LogIO os(LogOrigin("PBMath2DImage", "PBMath2DImage"));
      66             : 
      67           0 :   os << "Using two-dimensional purely real image model for antenna voltage pattern" << LogIO::POST;
      68             : 
      69           0 :   reJonesImage_p = new TempImage<Float>(reJonesImage.shape(), reJonesImage.coordinates());
      70           0 :   reJonesImage_p->copyData(reJonesImage);
      71           0 :   incrementsReJones_p=new Vector<Double>(reJonesImage_p->coordinates().directionCoordinate(0).increment());
      72           0 :   referencePixelReJones_p=new Vector<Double>(reJonesImage_p->coordinates().directionCoordinate(0).referencePixel());
      73             : 
      74           0 : };
      75             : 
      76           0 : PBMath2DImage::PBMath2DImage(ImageInterface<Float>& reJonesImage,
      77           0 :                              ImageInterface<Float>& imJonesImage) :
      78             :   PBMath2D(), reJonesImage_p(0), reRegridJonesImage_p(0),
      79             :   imJonesImage_p(0), imRegridJonesImage_p(0), 
      80             :   incrementsReJones_p(0), incrementsImJones_p(0),
      81             :   referencePixelReJones_p(0), referencePixelImJones_p(0),
      82           0 :   pa_p(0.0)
      83             : {
      84             : 
      85           0 :   LogIO os(LogOrigin("PBMath2DImage", "PBMath2DImage"));
      86             : 
      87           0 :   os << "Using two-dimensional real and imaginary image models for antenna voltage pattern" << LogIO::POST;
      88             : 
      89           0 :   checkJonesCongruent(reJonesImage, imJonesImage);
      90             : 
      91             :   // Save images and useful information 
      92           0 :   reJonesImage_p = new TempImage<Float>(reJonesImage.shape(), reJonesImage.coordinates());
      93           0 :   reJonesImage_p->copyData(reJonesImage);
      94           0 :   incrementsReJones_p=new Vector<Double>(reJonesImage_p->coordinates().directionCoordinate(0).increment());
      95           0 :   referencePixelReJones_p=new Vector<Double>(reJonesImage_p->coordinates().directionCoordinate(0).referencePixel());
      96             : 
      97           0 :   imJonesImage_p = new TempImage<Float>(imJonesImage.shape(), imJonesImage.coordinates());
      98           0 :   imJonesImage_p->copyData(imJonesImage);
      99           0 :   incrementsImJones_p=new Vector<Double>(imJonesImage_p->coordinates().directionCoordinate(0).increment());
     100           0 :   referencePixelImJones_p=new Vector<Double>(imJonesImage_p->coordinates().directionCoordinate(0).referencePixel());
     101           0 : };
     102           0 : PBMath2DImage::PBMath2DImage(const ImageInterface<Complex>& jonesImage) :
     103             :   PBMath2D(), reJonesImage_p(0), reRegridJonesImage_p(0),
     104             :   imJonesImage_p(0), imRegridJonesImage_p(0), 
     105             :   incrementsReJones_p(0), incrementsImJones_p(0),
     106             :   referencePixelReJones_p(0), referencePixelImJones_p(0),
     107           0 :   pa_p(0.0){
     108           0 :   reJonesImage_p = new TempImage<Float>(jonesImage.shape(), jonesImage.coordinates());
     109           0 :   reJonesImage_p->copyData(LatticeExpr<float> (real(jonesImage)));
     110           0 :   imJonesImage_p = new TempImage<Float>(jonesImage.shape(), jonesImage.coordinates());
     111           0 :   imJonesImage_p->copyData(LatticeExpr<float> (imag(jonesImage)));
     112           0 :   incrementsReJones_p=new Vector<Double>(reJonesImage_p->coordinates().directionCoordinate(0).increment());
     113           0 :   referencePixelReJones_p=new Vector<Double>(reJonesImage_p->coordinates().directionCoordinate(0).referencePixel());
     114           0 :   incrementsImJones_p=new Vector<Double>(imJonesImage_p->coordinates().directionCoordinate(0).increment());
     115           0 :   referencePixelImJones_p=new Vector<Double>(imJonesImage_p->coordinates().directionCoordinate(0).referencePixel());
     116           0 : }
     117             : 
     118             : 
     119             : 
     120           0 : PBMath2DImage::~PBMath2DImage()
     121             : {
     122           0 :   if(reJonesImage_p) delete reJonesImage_p; reJonesImage_p=0;
     123           0 :   if(imJonesImage_p) delete imJonesImage_p; imJonesImage_p=0;
     124           0 :   if(incrementsReJones_p) delete incrementsReJones_p; incrementsReJones_p=0;
     125           0 :   if(incrementsImJones_p) delete incrementsImJones_p; incrementsImJones_p=0;
     126           0 :   if(referencePixelReJones_p) delete referencePixelReJones_p; referencePixelReJones_p=0;
     127           0 :   if(referencePixelImJones_p) delete referencePixelImJones_p; referencePixelImJones_p=0;
     128           0 : };
     129             : 
     130           0 : PBMath2DImage& PBMath2DImage::operator=(const PBMath2DImage& other)
     131             : {
     132           0 :   if (this == &other)
     133           0 :     return *this;
     134             : 
     135           0 :   PBMath2D::operator=(other);
     136           0 :   reJonesImage_p = other.reJonesImage_p;
     137           0 :   imJonesImage_p = other.imJonesImage_p;
     138           0 :   incrementsReJones_p = other.incrementsReJones_p;
     139           0 :   incrementsImJones_p = other.incrementsImJones_p;
     140           0 :   referencePixelReJones_p = other.referencePixelReJones_p;
     141           0 :   referencePixelImJones_p = other.referencePixelImJones_p;
     142             : 
     143           0 :   return *this;
     144             : };
     145             : 
     146             : 
     147             : 
     148             : void
     149           0 : PBMath2DImage::summary(Int nValues)
     150             : {
     151           0 :   PBMath2D::summary(nValues);
     152           0 :   LogIO os(LogOrigin("PBMath2DImage", "summary"));
     153             : 
     154             :   {
     155           0 :     os << "Original image of the real part of Jones matrix:" << LogIO::POST;
     156           0 :     ImageSummary<Float> is(*reJonesImage_p);
     157           0 :     is.list(os);
     158             :   }
     159             : 
     160           0 :   if(imJonesImage_p) {
     161             :     os << "Original image of the imaginary part of Jones matrix:"
     162           0 :        << LogIO::POST;
     163           0 :     ImageSummary<Float> is(*imJonesImage_p);
     164           0 :     is.list(os);
     165             :   }
     166             : 
     167           0 : };
     168             : 
     169             : // Apply the Jones matrices to the input cube (x,y,pol)
     170             : // giving an output cube (x,y,pol). These two methods 
     171             : // should be moved to Fortran for optimization
     172             : 
     173             : // Complex to Complex
     174             : void 
     175           0 : PBMath2DImage::applyJones(const Array<Float>* reJones,
     176             :                           const Array<Float>* imJones,
     177             :                           const Array<Complex>& in,
     178             :                           Array<Complex>& out,
     179             :                           Vector<Int>& polmap,
     180             :                           Bool /*inverse*/,
     181             :                           Bool /*conjugate*/,
     182             :                           Int ipower,  // ie, 1=VP, 2=PB
     183             :                           Float /*cutoff*/,
     184             :                           Bool circular,
     185             :                           Bool forward)
     186             : {
     187             : 
     188           0 :   LogIO os(LogOrigin("PBMath2DImage", "applyJones"));
     189             :   // This should never be called
     190           0 :   if((ipower!=2)&&(ipower!=1)) {
     191             :     os << "Logic error - trying to apply illegal power of PB"
     192           0 :        << LogIO::EXCEPTION;
     193             :   }
     194             : 
     195           0 :   Int nx=in.shape()(0);
     196           0 :   Int ny=in.shape()(1);
     197           0 :   Int npol=in.shape()(2);
     198             : 
     199             :   // Loop through x, y coordinates of this cube
     200           0 :   Matrix<Complex> cmat(2,2);
     201           0 :   IPosition sp0(4, 0, 0, polmap(3), 0);
     202           0 :   IPosition sp1(4, 0, 0, polmap(2), 0);
     203           0 :   IPosition sp2(4, 0, 0, polmap(1), 0);
     204           0 :   IPosition sp3(4, 0, 0, polmap(0), 0);
     205           0 :   for (Int ix=0; ix<nx; ix++) {
     206           0 :     sp0(0)=ix;
     207           0 :     sp1(0)=ix;
     208           0 :     sp2(0)=ix;
     209           0 :     sp3(0)=ix;
     210             : 
     211           0 :     for (Int iy=0; iy<ny; iy++) {
     212             : 
     213           0 :       sp0(1)=iy;
     214           0 :       sp1(1)=iy;
     215           0 :       sp2(1)=iy;
     216           0 :       sp3(1)=iy;
     217             : 
     218             :       // E Jones for this pixel
     219           0 :       mjJones4 j4;
     220           0 :       if(imJones) {
     221           0 :         cmat(0,0)=Complex((*reJones)(sp0), (*imJones)(sp0));
     222           0 :         cmat(1,0)=Complex((*reJones)(sp1), (*imJones)(sp1));
     223           0 :         cmat(0,1)=Complex((*reJones)(sp2), (*imJones)(sp2));
     224           0 :         cmat(1,1)=Complex((*reJones)(sp3), (*imJones)(sp3));
     225             :       }
     226             :       else {
     227           0 :         cmat(0,0)=Complex((*reJones)(sp0), 0.0);
     228           0 :         cmat(1,0)=Complex((*reJones)(sp1), 0.0);
     229           0 :         cmat(0,1)=Complex((*reJones)(sp2), 0.0);
     230           0 :         cmat(1,1)=Complex((*reJones)(sp3), 0.0);
     231             :       }
     232           0 :       mjJones2 j2(cmat);
     233             : 
     234             :       // Make the relevant Jones matrix
     235             :       // E
     236           0 :       if(ipower==1) { // VP
     237           0 :         mjJones2 j2unit(Complex(1.0, 0.0));
     238           0 :         directProduct(j4, j2, j2unit);
     239             :       }
     240             :       // Primary beam = E . conj(E)
     241           0 :       else if(ipower==2) { // PB
     242             :         // Make the conjugate before constructing the
     243             :         // mjJones since otherwise reference semantics
     244             :         // get us
     245           0 :         mjJones2 j2conj(conj(cmat));
     246           0 :         directProduct(j4, j2, j2conj);
     247             :       }
     248             : 
     249             :       // Subtlety - we have to distinguish between applying
     250             :       // the PB and applying the adjoint. The former is needed
     251             :       // for predictions (sky->UV) and the latter is needed
     252             :       // for inversion (UV->sky). For circular polarization
     253             :       // this affects only the cross hand terms
     254           0 :       if(!forward) {
     255           0 :         j4=mjJones4(adjoint(j4.matrix()));
     256             :       }
     257             : 
     258             :       // Now apply the Jones matrix
     259           0 :       if(npol==1) {
     260           0 :         IPosition ip0(4, ix, iy, 0, 0);
     261           0 :         CStokesVector outCS(in(ip0), 0.0, 0.0, 0.0);
     262           0 :         outCS*=j4;
     263           0 :         out(ip0)=outCS(0);
     264             :       }
     265           0 :       else if(npol==2) {
     266           0 :         IPosition ip0(4, ix, iy, 0, 0);
     267           0 :         IPosition ip1(4, ix, iy, 1, 0);
     268           0 :         if(circular) {
     269           0 :           CStokesVector outCS(in(ip0), 0.0, 0.0, in(ip1));
     270           0 :           outCS*=j4;
     271           0 :           out(ip0)=outCS(0);
     272           0 :           out(ip1)=outCS(3);
     273             :         }
     274             :         else {
     275           0 :           CStokesVector outCS(in(ip0), in(ip1), 0.0, 0.0);
     276           0 :           outCS*=j4;
     277           0 :           out(ip0)=outCS(0);
     278           0 :           out(ip1)=outCS(1);
     279             :         }
     280             :       }
     281           0 :       else if(npol==4) {
     282           0 :         IPosition ip0(4, ix, iy, 0, 0);
     283           0 :         IPosition ip1(4, ix, iy, 1, 0);
     284           0 :         IPosition ip2(4, ix, iy, 2, 0);
     285           0 :         IPosition ip3(4, ix, iy, 3, 0);
     286           0 :         CStokesVector outCS(in(ip0), in(ip1), in(ip2), in(ip3));
     287           0 :         outCS*=j4;
     288           0 :         out(ip0)=outCS(0);
     289           0 :         out(ip1)=outCS(1);
     290           0 :         out(ip2)=outCS(2);
     291           0 :         out(ip3)=outCS(3);
     292             :       }
     293             :     }
     294             :   }
     295           0 : }
     296             : 
     297             : 
     298             : // Complex to Complex
     299             : void 
     300           0 : PBMath2DImage::applyJonesFast(const Float*& reJones,
     301             :                           const Float*& imJones,
     302             :                           const Array<Complex>& in,
     303             :                           Array<Complex>& out,
     304             :                           Vector<Int>& polmap,
     305             :                           Bool /*inverse*/,
     306             :                           Bool /*conjugate*/,
     307             :                           Int ipower,  // ie, 1=VP, 2=PB
     308             :                           Float /*cutoff*/,
     309             :                           Bool circular,
     310             :                           Bool forward)
     311             : {
     312             : 
     313           0 :   LogIO os(LogOrigin("PBMath2DImage", "applyJones"));
     314             :   // This should never be called
     315           0 :   if((ipower!=2)&&(ipower!=1)) {
     316             :     os << "Logic error - trying to apply illegal power of PB"
     317           0 :        << LogIO::EXCEPTION;
     318             :   }
     319             : 
     320             :   Bool delpolmap;
     321           0 :   const Int  *polmap1=polmap.getStorage(delpolmap);
     322           0 :   const Float* reJones1=reJones;
     323           0 :   const Float* imJones1=imJones;
     324             : 
     325           0 :   Int nx=in.shape()(0);
     326           0 :   Int ny=in.shape()(1);
     327           0 :   Int npol=in.shape()(2);
     328           0 :   Bool lala=false;
     329           0 :   Float laloo=0.0;
     330             :   
     331             :   Bool delin, delout;
     332           0 :   Complex *outstor=out.getStorage(delout);
     333           0 :   const Complex *instor=in.getStorage(delin);
     334             :   //Int ind0, ind1, ind2, ind3; 
     335             :   // Loop through x, y coordinates of this cube
     336           0 :   Matrix<Complex> cmat(2,2);
     337           0 :   IPosition sp0(4, 0, 0, polmap(3), 0);
     338           0 :   IPosition sp1(4, 0, 0, polmap(2), 0);
     339           0 :   IPosition sp2(4, 0, 0, polmap(1), 0);
     340           0 :   IPosition sp3(4, 0, 0, polmap(0), 0);
     341             : 
     342             : 
     343           0 :    #pragma omp parallel default(none)  shared(outstor) firstprivate(reJones1, imJones1 , instor, polmap1, lala, laloo, ipower, circular, forward, nx,ny, npol)
     344             : 
     345             :   {
     346             :     #pragma omp for
     347             :   for (Int ix=0; ix<nx; ix++) {
     348             :     //sp0(0)=ix;
     349             :     //sp1(0)=ix;
     350             :     //sp2(0)=ix;
     351             :     //sp3(0)=ix;
     352             :     
     353             :        applyJonesFastX(reJones1, imJones1, instor, outstor, polmap1,
     354             :                           lala,   lala,ipower,  // ie, 1=VP, 2=PB
     355             :                     laloo,circular,forward,ix, nx, ny, npol);
     356             :        /* for (Int iy=0; iy<ny; iy++) {
     357             : 
     358             :       //sp0(1)=iy;
     359             :       //sp1(1)=iy;
     360             :       //sp2(1)=iy;
     361             :       //sp3(1)=iy;
     362             : 
     363             :       ind0=ix+nx*iy +(nx*ny)*polmap(3);
     364             :       ind1=ix+nx*iy +(nx*ny)*polmap(2);
     365             :       ind2=ix+nx*iy +(nx*ny)*polmap(1);
     366             :       ind3=ix+nx*iy +(nx*ny)*polmap(0);
     367             :       // E Jones for this pixel
     368             :       mjJones4 j4;
     369             :       if(imJones) {
     370             :         cmat(0,0)=Complex(reJones[ind0], imJones[ind0]);
     371             :         cmat(1,0)=Complex(reJones[ind1], imJones[ind1]);
     372             :         cmat(0,1)=Complex(reJones[ind2], imJones[ind2]);
     373             :         cmat(1,1)=Complex(reJones[ind3], imJones[ind3]);
     374             :       }
     375             :       else {
     376             :         cmat(0,0)=Complex(reJones[ind0], 0.0);
     377             :         cmat(1,0)=Complex(reJones[ind1], 0.0);
     378             :         cmat(0,1)=Complex(reJones[ind2], 0.0);
     379             :         cmat(1,1)=Complex(reJones[ind3], 0.0);
     380             :       }
     381             :       mjJones2 j2(cmat);
     382             : 
     383             :       // Make the relevant Jones matrix
     384             :       // E
     385             :       if(ipower==1) { // VP
     386             :         mjJones2 j2unit(Complex(1.0, 0.0));
     387             :         directProduct(j4, j2, j2unit);
     388             :       }
     389             :       // Primary beam = E . conj(E)
     390             :       else if(ipower==2) { // PB
     391             :         // Make the conjugate before constructing the
     392             :         // mjJones since otherwise reference semantics
     393             :         // get us
     394             :         mjJones2 j2conj(conj(cmat));
     395             :         directProduct(j4, j2, j2conj);
     396             :       }
     397             : 
     398             :       // Subtlety - we have to distinguish between applying
     399             :       // the PB and applying the adjoint. The former is needed
     400             :       // for predictions (sky->UV) and the latter is needed
     401             :       // for inversion (UV->sky). For circular polarization
     402             :       // this affects only the cross hand terms
     403             :       if(!forward) {
     404             :         j4=mjJones4(adjoint(j4.matrix()));
     405             :       }
     406             : 
     407             :       // Now apply the Jones matrix
     408             :       if(npol==1) {
     409             :         //IPosition ip0(4, ix, iy, 0, 0);
     410             :         ind0=ix+iy*nx;
     411             :         CStokesVector outCS(instor[ind0], 0.0, 0.0, 0.0);
     412             :         outCS*=j4;
     413             :         outstor[ind0]=outCS(0);
     414             :       }
     415             :       else if(npol==2) {
     416             :         //IPosition ip0(4, ix, iy, 0, 0);
     417             :         //IPosition ip1(4, ix, iy, 1, 0);
     418             :         ind0=ix+iy*nx;
     419             :         ind1=ix+iy*nx+nx*ny;
     420             :         if(circular) {
     421             :           CStokesVector outCS(instor[ind0], 0.0, 0.0, instor[ind1]);
     422             :           outCS*=j4;
     423             :           outstor[ind0]=outCS(0);
     424             :           outstor[ind1]=outCS(3);
     425             :         }
     426             :         else {
     427             :           CStokesVector outCS(instor[ind0], instor[ind1], 0.0, 0.0);
     428             :           outCS*=j4;
     429             :           outstor[ind0]=outCS(0);
     430             :           outstor[ind1]=outCS(1);
     431             :         }
     432             :       }
     433             :       else if(npol==4) {
     434             :         //IPosition ip0(4, ix, iy, 0, 0);
     435             :         //IPosition ip1(4, ix, iy, 1, 0);
     436             :         //IPosition ip2(4, ix, iy, 2, 0);
     437             :         //IPosition ip3(4, ix, iy, 3, 0);
     438             :         ind0=ix+iy*nx;
     439             :         ind1=ix+iy*nx+nx*ny;
     440             :         ind2=ix+iy*nx+2*nx*ny;
     441             :         ind3=ix+iy*nx+3*nx*ny;
     442             :         CStokesVector outCS(instor[ind0], instor[ind1], instor[ind2], instor[ind3]);
     443             :         outCS*=j4;
     444             :         outstor[ind0]=outCS(0);
     445             :         outstor[ind1]=outCS(1);
     446             :         outstor[ind2]=outCS(2);
     447             :         outstor[ind3]=outCS(3);
     448             :       }
     449             :     }
     450             :        */
     451             :     
     452             :   }
     453             :   } //OMP
     454           0 :   out.putStorage(outstor, delout);
     455           0 :   in.freeStorage(instor, delin);
     456           0 :   polmap.freeStorage(polmap1, delpolmap);
     457           0 : }
     458             : 
     459             : 
     460             : 
     461             : 
     462             : void 
     463           0 : PBMath2DImage::applyJonesFastX(const Float*& reJones,
     464             :                           const Float*& imJones,
     465             :                           const Complex*& instor,
     466             :                           Complex*& outstor,
     467             :                           const Int*& polmap,
     468             :                           Bool /*inverse*/,
     469             :                           Bool /*conjugate*/,
     470             :                           Int ipower,  // ie, 1=VP, 2=PB
     471             :                           Float /*cutoff*/,
     472             :                           Bool circular,
     473             :                                Bool forward,
     474             :                                const Int ix, const Int nx, const Int ny, const Int npol){
     475             : 
     476             :   ooLong ind0, ind1, ind2, ind3; 
     477             :   // Loop through x, y coordinates of this cube
     478           0 :   Matrix<Complex> cmat(2,2);
     479             : 
     480           0 :   for (ooLong iy=0; iy< ooLong(ny) ; iy++) {
     481             :     
     482             :     //sp0(1)=iy;
     483             :     //sp1(1)=iy;
     484             :     //sp2(1)=iy;
     485             :     //sp3(1)=iy;
     486             :     
     487           0 :     ind0=ooLong(ix)+ooLong(nx)*iy +(ooLong(nx)*ooLong(ny))*ooLong(polmap[3]);
     488           0 :     ind1=ooLong(ix)+ooLong(nx)*iy +(ooLong(nx)*ooLong(ny))*ooLong(polmap[2]);
     489           0 :     ind2=ooLong(ix)+ooLong(nx)*iy +(ooLong(nx)*ooLong(ny))*ooLong(polmap[1]);
     490           0 :     ind3=ooLong(ix)+ooLong(nx)*iy +(ooLong(nx)*ooLong(ny))*ooLong(polmap[0]);
     491             :     // E Jones for this pixel
     492           0 :     mjJones4 j4;
     493           0 :     if(imJones) {
     494           0 :       cmat(0,0)=Complex(reJones[ind0], imJones[ind0]);
     495           0 :       cmat(1,0)=Complex(reJones[ind1], imJones[ind1]);
     496           0 :       cmat(0,1)=Complex(reJones[ind2], imJones[ind2]);
     497           0 :       cmat(1,1)=Complex(reJones[ind3], imJones[ind3]);
     498             :     }
     499             :     else {
     500           0 :       cmat(0,0)=Complex(reJones[ind0], 0.0);
     501           0 :       cmat(1,0)=Complex(reJones[ind1], 0.0);
     502           0 :       cmat(0,1)=Complex(reJones[ind2], 0.0);
     503           0 :       cmat(1,1)=Complex(reJones[ind3], 0.0);
     504             :     }
     505           0 :     mjJones2 j2(cmat);
     506             :     
     507             :     // Make the relevant Jones matrix
     508             :     // E
     509           0 :     if(ipower==1) { // VP
     510           0 :       mjJones2 j2unit(Complex(1.0, 0.0));
     511           0 :       directProduct(j4, j2, j2unit);
     512             :     }
     513             :     // Primary beam = E . conj(E)
     514           0 :     else if(ipower==2) { // PB
     515             :       // Make the conjugate before constructing the
     516             :       // mjJones since otherwise reference semantics
     517             :       // get us
     518           0 :       mjJones2 j2conj(conj(cmat));
     519           0 :       directProduct(j4, j2, j2conj);
     520             :     }
     521             :     
     522             :     // Subtlety - we have to distinguish between applying
     523             :     // the PB and applying the adjoint. The former is needed
     524             :     // for predictions (sky->UV) and the latter is needed
     525             :     // for inversion (UV->sky). For circular polarization
     526             :     // this affects only the cross hand terms
     527           0 :     if(!forward) {
     528           0 :       j4=mjJones4(adjoint(j4.matrix()));
     529             :     }
     530             :     
     531             :     // Now apply the Jones matrix
     532           0 :       if(npol==1) {
     533             :         //IPosition ip0(4, ix, iy, 0, 0);
     534           0 :         ind0=ix+iy*nx;
     535           0 :         CStokesVector outCS(instor[ind0], 0.0, 0.0, 0.0);
     536           0 :         outCS*=j4;
     537           0 :         outstor[ind0]=outCS(0);
     538             :       }
     539           0 :       else if(npol==2) {
     540             :         //IPosition ip0(4, ix, iy, 0, 0);
     541             :         //IPosition ip1(4, ix, iy, 1, 0);
     542           0 :         ind0=ix+iy*nx;
     543           0 :         ind1=ix+iy*nx+nx*ny;
     544           0 :         if(circular) {
     545           0 :           CStokesVector outCS(instor[ind0], 0.0, 0.0, instor[ind1]);
     546           0 :           outCS*=j4;
     547           0 :           outstor[ind0]=outCS(0);
     548           0 :           outstor[ind1]=outCS(3);
     549             :         }
     550             :         else {
     551           0 :           CStokesVector outCS(instor[ind0], instor[ind1], 0.0, 0.0);
     552           0 :           outCS*=j4;
     553           0 :           outstor[ind0]=outCS(0);
     554           0 :           outstor[ind1]=outCS(1);
     555             :         }
     556             :       }
     557           0 :       else if(npol==4) {
     558             :         //IPosition ip0(4, ix, iy, 0, 0);
     559             :         //IPosition ip1(4, ix, iy, 1, 0);
     560             :         //IPosition ip2(4, ix, iy, 2, 0);
     561             :         //IPosition ip3(4, ix, iy, 3, 0);
     562           0 :         ind0=ix+iy*nx;
     563           0 :         ind1=ix+iy*nx+nx*ny;
     564           0 :         ind2=ix+iy*nx+2*nx*ny;
     565           0 :         ind3=ix+iy*nx+3*nx*ny;
     566           0 :         CStokesVector outCS(instor[ind0], instor[ind1], instor[ind2], instor[ind3]);
     567           0 :         outCS*=j4;
     568           0 :         outstor[ind0]=outCS(0);
     569           0 :         outstor[ind1]=outCS(1);
     570           0 :         outstor[ind2]=outCS(2);
     571           0 :         outstor[ind3]=outCS(3);
     572             :       }
     573             :   }
     574             : 
     575             : 
     576             : 
     577             : 
     578             : 
     579           0 : }
     580             : 
     581             : 
     582             : // Float to Float on real part of complex stokes
     583             : // This is only really useful for the weights image
     584             : // from SkyEquation - perhaps make SkyEquation a
     585             : // friend?
     586             : void 
     587           0 : PBMath2DImage::applyJones(const Array<Float>* reJones,
     588             :                           const Array<Float>* imJones,
     589             :                           const Array<Float>& in,
     590             :                           Array<Float>& out,
     591             :                           Vector<Int>& polmap,
     592             :                           Float /*cutoff*/,
     593             :                           Bool circular)
     594             : {
     595           0 :   LogIO os(LogOrigin("PBMath2DImage", "applyJones"));
     596             : 
     597             : 
     598           0 :   Matrix<Complex> s(4,4), sAdjoint(4,4);
     599           0 :   Matrix<Complex> Lambda(4,4);
     600           0 :   s.set(Complex(0.0,0.0));
     601           0 :   sAdjoint.set(Complex(0.0));
     602           0 :   Lambda.set(Complex(0.0,0.0));
     603           0 :   s=0;
     604           0 :   if(!circular) {
     605           0 :     s(0,0)=Complex(0.5);
     606           0 :     s(0,1)=Complex(0.5);
     607           0 :     s(1,2)=Complex(0.5);
     608           0 :     s(1,3)=Complex(0.0,0.5);
     609           0 :     s(2,2)=Complex(0.5);
     610           0 :     s(2,3)=Complex(0.0,-0.5);
     611           0 :     s(3,0)=Complex(0.5);
     612           0 :     s(3,1)=Complex(-0.5);
     613             :   }
     614             :   else {
     615           0 :     s(0,0)=Complex(0.5);
     616           0 :     s(0,3)=Complex(0.5);
     617           0 :     s(1,1)=Complex(0.5);
     618           0 :     s(1,2)=Complex(0.0,0.5);
     619           0 :     s(2,1)=Complex(0.5);
     620           0 :     s(2,2)=Complex(0.0,-0.5);
     621           0 :     s(3,0)=Complex(0.5);
     622           0 :     s(3,3)=Complex(-0.5);
     623             :   }
     624           0 :   sAdjoint=adjoint(s);
     625             : 
     626           0 :   Int nx=in.shape()(0);
     627           0 :   Int ny=in.shape()(1);
     628           0 :   Int npol=in.shape()(2);
     629             : 
     630           0 :   Matrix<Complex> cmat(2,2);
     631           0 :   Matrix<Complex> prod(4,4);
     632           0 :   prod.set(Complex(0.0));
     633           0 :   IPosition sp0(4, 0, 0, polmap(3), 0);
     634           0 :   IPosition sp1(4, 0, 0, polmap(2), 0);
     635           0 :   IPosition sp2(4, 0, 0, polmap(1), 0);
     636           0 :   IPosition sp3(4, 0, 0, polmap(0), 0);
     637             :   
     638           0 :   IPosition ip0(4, 0, 0, 0, 0);
     639           0 :   IPosition ip1(4, 0, 0, 1, 0);
     640           0 :   IPosition ip2(4, 0, 0, 2, 0);
     641           0 :   IPosition ip3(4, 0, 0, 3, 0);
     642           0 :   for (Int ix=0; ix<nx; ++ix) {
     643           0 :     sp0(0)=ix;
     644           0 :     sp1(0)=ix;
     645           0 :     sp2(0)=ix;
     646           0 :     sp3(0)=ix;
     647           0 :     ip0(0)=ix;
     648           0 :     ip1(0)=ix;
     649           0 :     ip2(0)=ix;
     650           0 :     ip3(0)=ix;
     651           0 :     for (Int iy=0; iy<ny; ++iy) {
     652             : 
     653           0 :       sp0(1)=iy;
     654           0 :       sp1(1)=iy;
     655           0 :       sp2(1)=iy;
     656           0 :       sp3(1)=iy;
     657             : 
     658           0 :       ip0(1)=iy;
     659           0 :       ip1(1)=iy;
     660           0 :       ip2(1)=iy;
     661           0 :       ip3(1)=iy;
     662             : 
     663             :       // Lambda is the covariance matrix of the noise in the
     664             :       // image plane for each complex stokes e.g. RR, LR, etc.
     665           0 :       Lambda=0.0;
     666           0 :       if(npol==1) {
     667           0 :         Lambda(0,0)=in(ip0);
     668           0 :         Lambda(3,3)=in(ip0);
     669             :       }
     670           0 :       else if(npol==2) {
     671           0 :         Lambda(0,0)=in(ip0);
     672           0 :         Lambda(3,3)=in(ip1);
     673             :       }
     674           0 :       else if(npol==4) {
     675           0 :         Lambda(0,0)=in(ip0);
     676           0 :         Lambda(1,1)=in(ip1);
     677           0 :         Lambda(2,2)=in(ip2);
     678           0 :         Lambda(3,3)=in(ip3);
     679             :       }
     680             : 
     681             :       // E Jones for this pixel
     682           0 :       mjJones4 j4(Matrix<Complex>(4, 4, Complex(0.0, 0.0)));
     683           0 :       if(imJones) {
     684           0 :         cmat(0,0)=Complex((*reJones)(sp0), (*imJones)(sp0));
     685           0 :         cmat(1,0)=Complex((*reJones)(sp1), (*imJones)(sp1));
     686           0 :         cmat(0,1)=Complex((*reJones)(sp2), (*imJones)(sp2));
     687           0 :         cmat(1,1)=Complex((*reJones)(sp3), (*imJones)(sp3));
     688             :       }
     689             :       else {
     690           0 :         cmat(0,0)=Complex((*reJones)(sp0), 0.0);
     691           0 :         cmat(1,0)=Complex((*reJones)(sp1), 0.0);
     692           0 :         cmat(0,1)=Complex((*reJones)(sp2), 0.0);
     693           0 :         cmat(1,1)=Complex((*reJones)(sp3), 0.0);
     694             :       }
     695           0 :       mjJones2 j2(cmat);
     696             : 
     697             :       // Direct product of E with conj(E) ~ PB
     698           0 :       mjJones2 j2conj(conj(cmat));
     699           0 :       directProduct(j4, j2, j2conj);
     700             :       // We need the element by element multiplication
     701           0 :       Matrix<Complex> matj4(j4.matrix());
     702           0 :       Matrix<Complex> amatj4(adjoint(matj4));
     703             :       // Noise covar in complex stokes = Adjoint(PB) . Lambda . PB
     704           0 :       Lambda=product(amatj4, product(Lambda, matj4));
     705           0 :       prod=product(sAdjoint,product(Lambda,s));
     706             :       // Noise covar in real stokes
     707           0 :       if(npol==1) {
     708           0 :         out(ip0)=real(prod(0,0))+real(prod(3,3));
     709             :       }
     710           0 :       else if (npol==2) {
     711           0 :         out(ip0)=2.0*real(prod(0,0));
     712           0 :         out(ip1)=2.0*real(prod(3,3));
     713             :       }
     714           0 :       else if (npol==4) {
     715           0 :         out(ip0)=2.0*real(prod(0,0));
     716           0 :         out(ip1)=2.0*real(prod(1,1));
     717           0 :         out(ip2)=2.0*real(prod(2,2));
     718           0 :         out(ip3)=2.0*real(prod(3,3));
     719             :       }
     720             :     }
     721             :   }
     722             : 
     723           0 : }
     724             : 
     725           0 : ImageInterface<Complex>& PBMath2DImage::apply(const ImageInterface<Complex>& in,
     726             :                                               ImageInterface<Complex>& out,
     727             :                                               const MDirection& sp,
     728             :                                               const Quantity parAngle,        
     729             :                                               const BeamSquint::SquintType /*doSquint*/,
     730             :                                               Bool inverse,
     731             :                                               Bool conjugate,
     732             :                                               Int ipower,  // ie, 1=VP, 2=PB
     733             :                                               Float cutoff,
     734             :                                               Bool forward)
     735             : {
     736           0 :   LogIO os(LogOrigin("PBMath2DImage", "apply"));
     737             : 
     738           0 :   if(!in.shape().isEqual(out.shape(), true)) {
     739             :     os << "Input and output images have different shapes" 
     740           0 :        << LogIO::EXCEPTION;
     741             :     
     742             :   }
     743             : 
     744             : //   {
     745             : //     PagedImage<Float> inImage(in.shape(), in.coordinates(), "RealBeforeApplyJones");
     746             : //     LatticeExpr<Float> le(real(in));
     747             : //     inImage.copyData(le);
     748             : //   }
     749             : //   {
     750             : //     PagedImage<Float> inImage(in.shape(), in.coordinates(), "ImagBeforeApplyJones");
     751             : //     LatticeExpr<Float> le(imag(in));
     752             : //     inImage.copyData(le);
     753             : //   }
     754             : 
     755             : 
     756           0 :   const IPosition oshape(out.shape());
     757           0 :   updateJones(out.coordinates(), oshape, sp, parAngle);
     758             :   
     759             :   // Read through the images in pol, x, y, and frequency
     760           0 :   IPosition iShape(in.shape());
     761           0 :   IPosition oShape(out.shape());
     762           0 :   IPosition jShape(reRegridJonesImage_p->shape());
     763           0 :   Int nx=iShape(0);
     764           0 :   Int ny=iShape(1);
     765           0 :   Int npol=iShape(2);
     766             : 
     767             :   // Find out if these are circular images
     768           0 :   Vector<Int> polmap(4);
     769             :   StokesImageUtil::PolRep polframe;
     770           0 :   Int insm=StokesImageUtil::CStokesPolMap(polmap, polframe, in.coordinates());
     771           0 :   Int outsm=StokesImageUtil::CStokesPolMap(polmap, polframe, out.coordinates());
     772           0 :   if(insm!=outsm) {
     773             :     os << "Input and Output images have different polarization frames"
     774           0 :        << LogIO::EXCEPTION;
     775             :   }
     776           0 :   Bool circular=(polframe==StokesImageUtil::CIRCULAR);
     777             :               
     778             :   // Now get the polarization remapping for the Jones image
     779             :   //Int jsm=    // CStokesPolMap sets polmap and polframe.
     780           0 :   StokesImageUtil::CStokesPolMap(polmap, polframe,
     781           0 :                                  reJonesImage_p->coordinates());
     782             : 
     783             :   // For the input and output images, get all polarizations for x, y plane
     784           0 :   IPosition inPlane(4, nx, ny, npol, 1);
     785           0 :   LatticeStepper inls(iShape, inPlane, IPosition(4, 0, 1, 2, 3));
     786           0 :   RO_LatticeIterator<Complex> inli(in, inls);
     787             : 
     788           0 :   IPosition outPlane(4, nx, ny, npol, 1);
     789           0 :   LatticeStepper outls(oShape, outPlane, IPosition(4, 0, 1, 2, 3));
     790           0 :   LatticeIterator<Complex> outli(out, outls);
     791             : 
     792             :   // For the Jones image, get all 4 polarizations for one x, y plane
     793           0 :   IPosition jPlane(4, nx, ny, 4, 1);
     794           0 :   LatticeStepper jls(jShape, jPlane, IPosition(4, 0, 1, 2, 3));
     795           0 :   RO_LatticeIterator<Float> reJonesli(*reRegridJonesImage_p, jls);
     796           0 :   reJonesli.reset();
     797             : 
     798             :   Bool delreal, delimag;
     799           0 :   if(imRegridJonesImage_p) {
     800           0 :     RO_LatticeIterator<Float> imJonesli(*imRegridJonesImage_p, jls);
     801             :     
     802           0 :     imJonesli.reset();
     803           0 :     for (inli.reset(), outli.reset(); !inli.atEnd(); inli++, outli++) {
     804           0 :       const Float *restor=reJonesli.cursor().getStorage(delreal);
     805           0 :       const Float *imstor=imJonesli.cursor().getStorage(delimag);
     806           0 :       applyJonesFast(restor, imstor,
     807             :                  inli.cursor(), outli.rwCursor(), polmap,
     808             :                  inverse, conjugate,
     809             :                  ipower, cutoff, circular, forward);
     810           0 :       reJonesli.cursor().freeStorage(restor, delreal);
     811           0 :       imJonesli.cursor().freeStorage(imstor, delimag);
     812             :     }
     813             :   }
     814             :   else {
     815           0 :     for (inli.reset(), outli.reset(); !inli.atEnd(); inli++, outli++) {
     816           0 :       const Float *restor=reJonesli.cursor().getStorage(delreal);
     817           0 :       const Float *imstor=NULL;
     818           0 :       applyJonesFast(restor,
     819             :                  imstor,
     820             :                  inli.cursor(),
     821             :                  outli.rwCursor(), polmap, inverse, conjugate,
     822             :                  ipower, cutoff, circular, forward);
     823           0 :       reJonesli.cursor().freeStorage(restor, delreal);
     824             :     }
     825             :   }
     826             : 
     827             : //   {
     828             : //     PagedImage<Float> outImage(out.shape(), out.coordinates(), "RealAfterApplyJones");
     829             : //     LatticeExpr<Float> le(real(out));
     830             : //     outImage.copyData(le);
     831             : //   }
     832             : //   {
     833             : //     PagedImage<Float> outImage(out.shape(), out.coordinates(), "ImagAfterApplyJones");
     834             : //     LatticeExpr<Float> le(imag(out));
     835             : //     outImage.copyData(le);
     836             : //   }
     837           0 :   return out;
     838             : }
     839             : 
     840           0 : ImageInterface<Float>& PBMath2DImage::apply(const ImageInterface<Float>& in,
     841             :                                             ImageInterface<Float>& out,
     842             :                                             const MDirection& sp,
     843             :                                             const Quantity parAngle,          
     844             :                                             const BeamSquint::SquintType /*doSquint*/,
     845             :                                             Float cutoff,
     846             :                                             Int /*ipower*/)
     847             : {
     848           0 :   LogIO os(LogOrigin("PBMath2DImage", "apply"));
     849             : 
     850           0 :   const IPosition oshape(out.shape());
     851           0 :   if(!in.shape().isEqual(out.shape(), true)) {
     852             :     os << "Input and output images have different shapes" 
     853           0 :        << LogIO::EXCEPTION;
     854             :     
     855             :   }
     856             : 
     857           0 :   updateJones(out.coordinates(), oshape, sp, parAngle);
     858             :   
     859             :   // Read through the images in pol, x, y, and frequency
     860           0 :   IPosition iShape(in.shape());
     861           0 :   IPosition oShape(out.shape());
     862           0 :   IPosition jShape(reRegridJonesImage_p->shape());
     863           0 :   Int nx=iShape(0);
     864           0 :   Int ny=iShape(1);
     865           0 :   Int npol=iShape(2);
     866             : 
     867             :   // Find out if these are circular images
     868           0 :   Vector<Int> polmap(4);
     869             :   StokesImageUtil::PolRep polframe;
     870           0 :   Int insm=StokesImageUtil::CStokesPolMap(polmap, polframe, in.coordinates());
     871           0 :   Int outsm=StokesImageUtil::CStokesPolMap(polmap, polframe, out.coordinates());
     872           0 :   if(insm!=outsm) {
     873             :     os << "Input and Output images have different polarization frames"
     874           0 :        << LogIO::EXCEPTION;
     875             :   }
     876           0 :   Bool circular=(insm<1);
     877             : 
     878             :   // Now get the polarization remapping for the Jones image
     879             :   //Int jsm=  
     880           0 :   StokesImageUtil::CStokesPolMap(polmap, polframe,
     881           0 :                                          reJonesImage_p->coordinates());
     882             :   // For the input and output images, get all polarizations for x, y plane
     883           0 :   IPosition inPlane(4, nx, ny, npol, 1);
     884           0 :   LatticeStepper inls(iShape, inPlane, IPosition(4, 0, 1, 2, 3));
     885           0 :   RO_LatticeIterator<Float> inli(in, inls);
     886             : 
     887           0 :   IPosition outPlane(4, nx, ny, npol, 1);
     888           0 :   LatticeStepper outls(oShape, outPlane, IPosition(4, 0, 1, 2, 3));
     889           0 :   LatticeIterator<Float> outli(out, outls);
     890             : 
     891             :   // For the Jones image, get all 4 polarizations for one x, y plane
     892           0 :   IPosition jPlane(4, nx, ny, 4, 1);
     893           0 :   LatticeStepper jls(jShape, jPlane, IPosition(4, 0, 1, 2, 3));
     894           0 :   RO_LatticeIterator<Float> reJonesli(*reRegridJonesImage_p, jls);
     895           0 :   reJonesli.reset();
     896             : 
     897           0 :   if(imRegridJonesImage_p) {
     898           0 :     RO_LatticeIterator<Float> imJonesli(*imRegridJonesImage_p, jls);
     899             :     
     900           0 :     imJonesli.reset();
     901           0 :     for (inli.reset(), outli.reset(); !inli.atEnd(); inli++, outli++) {
     902           0 :       applyJones(&reJonesli.cursor(), &imJonesli.cursor(),
     903             :                  inli.cursor(), outli.rwCursor(), polmap, cutoff, circular);
     904             :     }
     905             :   }
     906             :   else {
     907           0 :     for (inli.reset(), outli.reset(); !inli.atEnd(); inli++, outli++) {
     908           0 :       applyJones(&(reJonesli.cursor()),
     909             :                  (const Array<Float>*)0,
     910             :                  inli.cursor(),
     911             :                  outli.rwCursor(), polmap, cutoff, circular);
     912             :     }
     913             :   }
     914             : 
     915           0 :   return out;
     916             : }
     917             : 
     918             : 
     919           0 : SkyComponent& PBMath2DImage::apply(SkyComponent& in,
     920             :                                    SkyComponent& out,
     921             :                                    const MDirection& sp,
     922             :                                    const Quantity frequency,          
     923             :                                    const Quantity parAngle,           
     924             :                                    const BeamSquint::SquintType /*doSquint*/,
     925             :                                    Bool /*inverse*/,
     926             :                                    Bool /*conjugate*/,
     927             :                                    Int ipower,  // ie, 1=VP, 2=PB, 4=PB^2
     928             :                                    Float /*cutoff*/,
     929             :                                    Bool forward)
     930             : {
     931           0 :   LogIO os(LogOrigin("PBMath2DImage", "apply"));
     932             : 
     933             :   // Now get the polarization remapping for the Jones image
     934           0 :   Vector<Int> polmap(4);
     935             :   StokesImageUtil::PolRep polframe;
     936             : 
     937             :   // jsm (= circular or linear) is not used, but CStokesPolMap also sets polframe.
     938             :   //Int jsm=
     939           0 :   StokesImageUtil::CStokesPolMap(polmap, polframe, reJonesImage_p->coordinates());
     940             : 
     941             :   // First get the frequency of the output image
     942           0 :   Double desiredFrequency=frequency.getValue("Hz");
     943             :   
     944           0 :   CoordinateSystem reCoords(reJonesImage_p->coordinates());
     945           0 :   Int spectralIndex=reCoords.findCoordinate(Coordinate::SPECTRAL);
     946           0 :   AlwaysAssert(spectralIndex>=0, AipsError);
     947             :   SpectralCoordinate
     948           0 :     imageSpectralCoord=reCoords.spectralCoordinate(spectralIndex);
     949           0 :   Double reFrequency=imageSpectralCoord.referenceValue()(0);
     950           0 :   Double freqScale = desiredFrequency/reFrequency;
     951             : 
     952             :   // Find the coordinates of the Sky Component in the
     953             :   // real and imag Jones images, remembering about the
     954             :   // pointing position sp. We do the opposite from the
     955             :   // image case - we convert the component position
     956             :   // back to an offset from the pointing position
     957             :   // and then calculate the location in Az,El
     958           0 :   MDirection compLoc=in.shape().refDirection();
     959           0 :   MVDirection spmvd(sp.getAngle());
     960           0 :   MVDirection compmvd(compLoc.getAngle());
     961           0 :   Vector<Double> world(2);
     962             :   // Scale for the observing frequency: when the desired frequency
     963             :   // is higher than the frequency for the Jones image, the separation
     964             :   // should be LARGER.
     965           0 :   Double sep=spmvd.separation(compmvd,"rad").getValue();
     966           0 :   sep*=freqScale;
     967             : 
     968           0 :   Double pa=+spmvd.positionAngle(compmvd,"rad").getValue();
     969           0 :   pa-=parAngle.getValue("rad");
     970           0 :   world(0)=-sep*sin(pa);
     971           0 :   world(1)=sep*cos(pa);
     972             : 
     973           0 :   Vector<Double> rePix(2), imPix(2);
     974             :   {
     975           0 :     Int directionIndex=reCoords.findCoordinate(Coordinate::DIRECTION);
     976             :     DirectionCoordinate
     977           0 :       reDirectionCoord=reCoords.directionCoordinate(directionIndex);
     978           0 :     reDirectionCoord.toPixel(rePix, world);
     979             :   }
     980           0 :   if(imJonesImage_p) {
     981           0 :     CoordinateSystem imCoords(imJonesImage_p->coordinates());
     982           0 :     Int directionIndex=imCoords.findCoordinate(Coordinate::DIRECTION);
     983             :     DirectionCoordinate
     984           0 :       imDirectionCoord=imCoords.directionCoordinate(directionIndex);
     985           0 :     imDirectionCoord.toPixel(imPix, world);
     986             :   }
     987             : 
     988             :   // E Jones for this pixel
     989           0 :   mjJones4 j4;
     990             : 
     991           0 :   Bool offImage=false;
     992           0 :   Matrix<Complex> cmat(2,2);
     993           0 :   Array<Float> reJones;
     994           0 :   Int nx=reJonesImage_p->shape()(0);
     995           0 :   Int ny=reJonesImage_p->shape()(1);
     996           0 :   Int npol=reJonesImage_p->shape()(2);
     997             : 
     998           0 :   Int px=Int(rePix(0)+0.5);
     999           0 :   Int py=Int(rePix(1)+0.5);
    1000           0 :   if(imJonesImage_p) {
    1001           0 :     Array<Float> imJones;
    1002           0 :     if((px>-1)&&(px<nx)&&(py>-1)&&(py<ny)) {
    1003           0 :       IPosition blc(4, px, py, 0, 0);
    1004           0 :       IPosition len(4, 1, 1, npol, 1);
    1005           0 :       IPosition ip0(4, 0, 0, polmap(3), 0);
    1006           0 :       IPosition ip1(4, 0, 0, polmap(2), 0);
    1007           0 :       IPosition ip2(4, 0, 0, polmap(1), 0);
    1008           0 :       IPosition ip3(4, 0, 0, polmap(0), 0);
    1009           0 :       reJonesImage_p->getSlice(reJones, blc, len);
    1010           0 :       imJonesImage_p->getSlice(imJones, blc, len);
    1011           0 :       cmat(0,0)=Complex(reJones(ip0), imJones(ip0));
    1012           0 :       cmat(1,0)=Complex(reJones(ip1), imJones(ip1));
    1013           0 :       cmat(0,1)=Complex(reJones(ip2), imJones(ip2));
    1014           0 :       cmat(1,1)=Complex(reJones(ip3), imJones(ip3));
    1015             :     }
    1016             :     else {
    1017             :       //      cerr << freqScale << " " << px << " " << py << " " << world << " " << sep << " " << pa << endl;
    1018           0 :       offImage = true;
    1019             :     }
    1020             :   }
    1021             :   else {
    1022           0 :     if((px>-1)&&(px<nx)&&(py>-1)&&(py<ny)) {
    1023           0 :       IPosition blc(4, px, py, 0, 0);
    1024           0 :       IPosition len(4, 1, 1, npol, 1);
    1025           0 :       IPosition ip0(4, 0, 0, polmap(3), 0);
    1026           0 :       IPosition ip1(4, 0, 0, polmap(2), 0);
    1027           0 :       IPosition ip2(4, 0, 0, polmap(1), 0);
    1028           0 :       IPosition ip3(4, 0, 0, polmap(0), 0);
    1029           0 :       reJonesImage_p->getSlice(reJones, blc, len);
    1030           0 :       cmat(0,0)=Complex(reJones(ip0), 0.0);
    1031           0 :       cmat(1,0)=Complex(reJones(ip1), 0.0);
    1032           0 :       cmat(0,1)=Complex(reJones(ip2), 0.0);
    1033           0 :       cmat(1,1)=Complex(reJones(ip3), 0.0);
    1034             :     }
    1035             :     else {
    1036           0 :       offImage = true;
    1037             :     }
    1038             :       
    1039             :   }
    1040             : 
    1041             :   // Check for the component being off source
    1042           0 :   if(offImage) {
    1043           0 :     Vector<Double> zero(4);
    1044           0 :     zero=0.0;
    1045           0 :     out=in.copy();
    1046           0 :     out.flux().setValue(zero);
    1047             :   }
    1048             :   else {
    1049           0 :     mjJones2 j2(cmat);
    1050             :     
    1051             :     // Make the relevant Jones matrix
    1052             :     // E
    1053           0 :     if(ipower==1) { // VP
    1054           0 :       mjJones2 j2unit(Complex(1.0, 0.0));
    1055           0 :       directProduct(j4, j2, j2unit);
    1056             :     }
    1057             :     // Primary beam = E . conj(E)
    1058           0 :     else if(ipower==2) { // PB
    1059             :       // Make the conjugate before constructing the
    1060             :       // mjJones since otherwise reference semantics
    1061             :       // get us
    1062           0 :       mjJones2 j2conj(conj(cmat));
    1063           0 :       directProduct(j4, j2, j2conj);
    1064             :     }
    1065             :     
    1066             :     // Subtlety - we have to distinguish between applying
    1067             :     // the PB and applying the adjoint. The former is needed
    1068             :     // for predictions (sky->UV) and the latter is needed
    1069             :     // for inversion (UV->sky)
    1070           0 :     if(!forward) {
    1071           0 :       j4=mjJones4(adjoint(j4.matrix()));
    1072             :     }
    1073             :     
    1074             :     // Now apply the Jones matrix
    1075           0 :     Vector<Complex> compFluxIn(4);
    1076           0 :     if(polframe==StokesImageUtil::CIRCULAR) {
    1077           0 :       in.flux().convertPol(ComponentType::CIRCULAR);
    1078             :     }
    1079             :     else {
    1080           0 :       in.flux().convertPol(ComponentType::LINEAR);
    1081             :     }
    1082             :     // Reverse index convention
    1083           0 :     for (Int pol=0;pol<4;pol++) {
    1084           0 :       compFluxIn(3-pol)=in.flux().value()(pol);
    1085             :     }
    1086           0 :     CStokesVector outCS;
    1087           0 :     outCS=compFluxIn;
    1088           0 :     outCS*=j4;
    1089           0 :     Vector<DComplex> compFlux(4);
    1090             :     // Reverse index convention
    1091           0 :     for (Int pol=0;pol<4;pol++) {
    1092           0 :       compFlux(3-pol)=outCS(pol);
    1093             :     }
    1094           0 :     out=in.copy();
    1095           0 :     out.flux().setValue(compFlux);
    1096             :   }
    1097           0 :   return out;
    1098             : }
    1099             : 
    1100           0 : void PBMath2DImage::updateJones(const CoordinateSystem& coords,
    1101             :                                 const IPosition& shape,
    1102             :                                 const MDirection& pc,
    1103             :                                 const Quantity& paAngle)
    1104             : {
    1105             : 
    1106           0 :   LogIO os(LogOrigin("PBMath2DImage", "updateJones"));
    1107             : 
    1108           0 :   if (!StokesImageUtil::standardImageCoordinates(coords)) {
    1109             :     os << "Image to be PB corrected does not have standard axes order"
    1110           0 :        << LogIO::EXCEPTION;
    1111             :     
    1112             :   }
    1113             :   // First get the frequency of the output image
    1114           0 :   Int spectralIndex=coords.findCoordinate(Coordinate::SPECTRAL);
    1115           0 :   AlwaysAssert(spectralIndex>=0, AipsError);
    1116             :   SpectralCoordinate
    1117           0 :     imageSpectralCoord=coords.spectralCoordinate(spectralIndex);
    1118           0 :   Double desiredFrequency=imageSpectralCoord.referenceValue()(0);
    1119             : 
    1120             :   // Next get the frequency of the Jones image
    1121           0 :   spectralIndex=reJonesImage_p->coordinates().findCoordinate(Coordinate::SPECTRAL);
    1122           0 :   AlwaysAssert(spectralIndex>=0, AipsError);
    1123           0 :   imageSpectralCoord=reJonesImage_p->coordinates().spectralCoordinate(spectralIndex);
    1124           0 :   Double reFrequency=imageSpectralCoord.referenceValue()(0);
    1125             : 
    1126           0 :   Double reFreqScale = reFrequency/desiredFrequency;
    1127             :   // os << "Desired frequency       = " << desiredFrequency << "Hz" << LogIO::POST;  
    1128             :   //  os << "Real PB model frequency = " << reFrequency << "Hz" << LogIO::POST;  
    1129             :   //  os << "Scaling real Jones image cell size by " << reFreqScale << LogIO::POST;
    1130             : 
    1131           0 :   Double imFreqScale = 1.0;
    1132           0 :   if(imJonesImage_p) {
    1133           0 :     spectralIndex=imJonesImage_p->coordinates().findCoordinate(Coordinate::SPECTRAL);
    1134           0 :     AlwaysAssert(spectralIndex>=0, AipsError);
    1135           0 :     imageSpectralCoord=imJonesImage_p->coordinates().spectralCoordinate(spectralIndex);
    1136           0 :     Double imFrequency=imageSpectralCoord.referenceValue()(0);
    1137           0 :     imFreqScale = imFrequency/desiredFrequency;
    1138             :     //    os << "Imag PB model frequency = " << imFrequency << "Hz" << LogIO::POST;  
    1139             :     //    os << "Scaling imag Jones image cell size by " << imFreqScale << LogIO::POST;
    1140             :   }
    1141             : 
    1142           0 :   Float pa=-paAngle.getValue("rad");
    1143           0 :   Matrix<Double> xform(2,2);
    1144           0 :   xform(0,0) = cos(pa);
    1145           0 :   xform(1,1) = cos(pa);
    1146           0 :   xform(0,1) = -sin(pa);
    1147           0 :   xform(1,0) = +sin(pa);
    1148             : 
    1149             :   //  cout << "updateJones: position angle = " << 180.0*pa/C::pi << endl;
    1150             : 
    1151           0 :   IPosition desiredShape(shape);
    1152           0 :   desiredShape(2)=reJonesImage_p->shape()(2);
    1153           0 :   desiredShape(3)=reJonesImage_p->shape()(3);
    1154           0 :   Int jonesdirectionindex=reJonesImage_p->coordinates().findCoordinate(Coordinate::DIRECTION);
    1155           0 :   Int imdirectionindex=coords.findCoordinate(Coordinate::DIRECTION);
    1156             :   DirectionCoordinate
    1157           0 :     imageDirectionCoord=coords.directionCoordinate(imdirectionindex);
    1158           0 :   imageDirectionCoord.setWorldAxisUnits(reJonesImage_p->coordinates().directionCoordinate(jonesdirectionindex).worldAxisUnits());
    1159             :   // Now set the desired coordinates for the regridded Jones images
    1160             :   // The desired coordinates should have the same direction axis
    1161             :   // as the image to input image and the same stokes and frequency
    1162             :   // axes as the input Jones images.
    1163             :   {
    1164             :    
    1165             : 
    1166           0 :     AlwaysAssert(imdirectionindex>=0, AipsError);
    1167           0 :     Int spectralIndex=coords.findCoordinate(Coordinate::SPECTRAL);
    1168           0 :     AlwaysAssert(spectralIndex>=0, AipsError);
    1169             :     SpectralCoordinate
    1170           0 :       imageSpectralCoord=coords.spectralCoordinate(spectralIndex);
    1171           0 :     Vector<Double> freq(1);
    1172           0 :     freq(0)=desiredFrequency;
    1173           0 :     imageSpectralCoord.setReferenceValue(freq);
    1174             : 
    1175           0 :     CoordinateSystem desiredCoords(reJonesImage_p->coordinates());
    1176           0 :     desiredCoords.replaceCoordinate(imageDirectionCoord, jonesdirectionindex);
    1177           0 :     desiredCoords.replaceCoordinate(imageSpectralCoord, spectralIndex);
    1178             :  
    1179           0 :     if(reRegridJonesImage_p && desiredCoords.near(reRegridJonesImage_p->coordinates(), 1.0e-4 ) && (desiredShape == (reRegridJonesImage_p->shape()))) {
    1180           0 :         return;
    1181             :       }
    1182             : 
    1183             :      // Delete any old images
    1184           0 :     if(reRegridJonesImage_p) delete reRegridJonesImage_p; reRegridJonesImage_p=0;
    1185           0 :     if(imRegridJonesImage_p) delete imRegridJonesImage_p; imRegridJonesImage_p=0;
    1186             : 
    1187             : 
    1188           0 :    reRegridJonesImage_p = new TempImage<Float>(desiredShape,
    1189           0 :                                                 desiredCoords);
    1190           0 :     reRegridJonesImage_p->set(0.0);
    1191           0 :     if(imJonesImage_p) {
    1192           0 :       imRegridJonesImage_p = new TempImage<Float>(desiredShape,
    1193           0 :                                                   desiredCoords);
    1194           0 :       imRegridJonesImage_p->set(0.0);
    1195             :     }
    1196             :     if(0) {
    1197             :       os << "Regridded image of the real part of Jones matrix:" << LogIO::POST;
    1198             :       ImageSummary<Float> is(*reRegridJonesImage_p);
    1199             :       is.list(os);
    1200             :     }
    1201             : 
    1202             :   }
    1203             :     
    1204             :   // Now fake the direction coordinates of the input Jones images to have
    1205             :   // the same RA, DEC, etc as the pointing center and the xform matrix
    1206             :   // needed to rotate +pa in angle.
    1207           0 :   CoordinateSystem originalReJonesCoords(reJonesImage_p->coordinates());
    1208             :   {
    1209           0 :     AlwaysAssert(imdirectionindex>=0, AipsError);
    1210             :     DirectionCoordinate
    1211           0 :       reJonesDirectionCoord=coords.directionCoordinate(imdirectionindex);
    1212             :     //////////////set the right units
    1213           0 :     reJonesDirectionCoord.setWorldAxisUnits(reJonesImage_p->coordinates().directionCoordinate(jonesdirectionindex).worldAxisUnits());
    1214             :     // Set the reference value to the pointing center
    1215           0 :     reJonesDirectionCoord.setReferenceValue(pc.getAngle().getValue(reJonesDirectionCoord.worldAxisUnits()(0)));
    1216             :     // Set the xform for a rotation
    1217           0 :     reJonesDirectionCoord.setLinearTransform(xform);
    1218             :     // Set the increments correctly to the original values, scaled 
    1219             :     // as needed
    1220           0 :     Vector<Double> increments(2);
    1221           0 :     increments=reFreqScale*(*incrementsReJones_p);
    1222           0 :     increments(0)=-increments(0);
    1223           0 :     reJonesDirectionCoord.setIncrement(increments);
    1224           0 :     reJonesDirectionCoord.setReferencePixel(*referencePixelReJones_p);
    1225             : 
    1226             :     // Now reset the coordinates of the Real Jones image
    1227           0 :     CoordinateSystem reJonesCoords(reJonesImage_p->coordinates());
    1228           0 :     reJonesCoords.replaceCoordinate(reJonesDirectionCoord, jonesdirectionindex);
    1229           0 :     reJonesImage_p->setCoordinateInfo(reJonesCoords);
    1230             : 
    1231             :     if(0) {
    1232             :       os << "Munged image of the real part of Jones matrix:" << LogIO::POST;
    1233             :       ImageSummary<Float> is(*reJonesImage_p);
    1234             :       is.list(os);
    1235             :     }
    1236             : 
    1237             :   }
    1238             : 
    1239           0 :   if(imJonesImage_p) {
    1240           0 :     AlwaysAssert(imdirectionindex>=0, AipsError);
    1241             :     DirectionCoordinate
    1242           0 :       imJonesDirectionCoord=coords.directionCoordinate(imdirectionindex);
    1243             :     // Set the reference value to the pointing center
    1244           0 :     imJonesDirectionCoord.setWorldAxisUnits(imJonesImage_p->coordinates().directionCoordinate(jonesdirectionindex).worldAxisUnits());
    1245           0 :     imJonesDirectionCoord.setReferenceValue(pc.getAngle().getValue(imJonesDirectionCoord.worldAxisUnits()(0)));
    1246             :     // Set the xform for a rotation
    1247           0 :     imJonesDirectionCoord.setLinearTransform(xform);
    1248             :     // Set the increments correctly to the original values
    1249           0 :     Vector<Double> increments(2);
    1250           0 :     increments=imFreqScale*(*incrementsImJones_p);
    1251           0 :     increments(0)=-increments(0);
    1252           0 :     imJonesDirectionCoord.setIncrement(increments);
    1253           0 :     imJonesDirectionCoord.setReferencePixel(*referencePixelImJones_p);
    1254             :     // Now reset the coordinates of the Imag Jones image
    1255           0 :     CoordinateSystem imJonesCoords(imJonesImage_p->coordinates());
    1256           0 :     imJonesCoords.replaceCoordinate(imJonesDirectionCoord, jonesdirectionindex);
    1257           0 :     imJonesImage_p->setCoordinateInfo(imJonesCoords);
    1258             :   }
    1259             : 
    1260             :   // Now do the regridding
    1261           0 :   IPosition whichAxes(2, 0, 1);
    1262           0 :   ImageRegrid<Float> ir;
    1263           0 :   ir.regrid(*reRegridJonesImage_p, Interpolate2D::LINEAR, whichAxes,
    1264           0 :             *reJonesImage_p);
    1265           0 :   if(imJonesImage_p) {
    1266           0 :     ir.regrid(*imRegridJonesImage_p, Interpolate2D::LINEAR, whichAxes,
    1267           0 :               *imJonesImage_p);
    1268             :   }
    1269             : 
    1270             :   // Check for empty PB
    1271           0 :   LatticeExprNode maxRePB=max(*reRegridJonesImage_p);  
    1272           0 :   LatticeExprNode maxImPB=max(*reRegridJonesImage_p);  
    1273           0 :   if(maxRePB.getFloat()==0.0) {
    1274           0 :     throw(AipsError("PBMath2DImage: regridded real Jones image is empty"));
    1275             :   }
    1276           0 :   if(maxImPB.getFloat()==0.0) {
    1277           0 :     throw(AipsError("PBMath2DImage: regridded imag Jones image is empty"));
    1278             :   }
    1279             : 
    1280             :   // For debugging purposes
    1281             : //   if(0) {
    1282             : //     PagedImage<Float> reRegridJones(reRegridJonesImage_p->shape(),
    1283             : //                                  reRegridJonesImage_p->coordinates(),
    1284             : //                                  "RealRegridJones");
    1285             : //     reRegridJones.copyData(*reRegridJonesImage_p);
    1286             : 
    1287             : //     if(imJonesImage_p) {
    1288             : //       PagedImage<Float> imRegridJones(imRegridJonesImage_p->shape(),
    1289             : //                                    imRegridJonesImage_p->coordinates(),
    1290             : //                                    "ImagRegridJones");
    1291             : //       imRegridJones.copyData(*imRegridJonesImage_p);
    1292             : //     }
    1293             : //   }
    1294             :   // Reset the coordinates
    1295           0 :   reJonesImage_p->setCoordinateInfo(originalReJonesCoords);
    1296           0 :   if(imJonesImage_p) {
    1297           0 :     imJonesImage_p->setCoordinateInfo(originalReJonesCoords);
    1298             :   }
    1299             :   // Reset the coordinates
    1300           0 :   reJonesImage_p->setCoordinateInfo(originalReJonesCoords);
    1301           0 :   if(imJonesImage_p) {
    1302           0 :     imJonesImage_p->setCoordinateInfo(originalReJonesCoords);
    1303             :   }
    1304             : }
    1305             : 
    1306           0 : void PBMath2DImage::checkJonesCongruent(ImageInterface<Float>& reJones,
    1307             :                                         ImageInterface<Float>& imJones) 
    1308             : {
    1309           0 :   LogIO os(LogOrigin("PBMath2DImage", "checkJonesCongruent"));
    1310           0 :   if(!reJones.shape().isEqual(imJones.shape(), true)) {
    1311             :     os << "Real and imaginary primary beam images have different shapes" 
    1312           0 :        <<  reJones.shape().asVector()
    1313           0 :        <<  imJones.shape().asVector()
    1314           0 :        << LogIO::EXCEPTION;
    1315             :     
    1316             :   }
    1317           0 :   if(reJones.shape().asVector()(2)!=4) {
    1318             :     os << "Primary beam images must have 4 polarization values: number is " 
    1319           0 :        <<  reJones.shape().asVector()(2)
    1320           0 :        << LogIO::EXCEPTION;
    1321             :     
    1322             :   }
    1323           0 :   if (!StokesImageUtil::standardImageCoordinates(reJones)) {
    1324             :     os << "Primary beam image (real part) does not have standard axes order"
    1325           0 :        << LogIO::EXCEPTION;
    1326             :     
    1327             :   }
    1328           0 :   if (!StokesImageUtil::standardImageCoordinates(imJones)) {
    1329             :     os << "Primary beam image (imaginary part) does not have standard axes order"
    1330           0 :        << LogIO::EXCEPTION;
    1331             :     
    1332             :   }
    1333           0 : }
    1334             : 
    1335           0 : void PBMath2DImage::checkImageCongruent(ImageInterface<Float>& image)
    1336             : {
    1337           0 :   LogIO os(LogOrigin("PBMath2DImage", "checkImageCongruent"));
    1338           0 :   if (!StokesImageUtil::standardImageCoordinates(image)) {
    1339             :     os << "Image does not have standard axes order"
    1340           0 :        << LogIO::EXCEPTION;
    1341             :     
    1342             :   }
    1343           0 : }
    1344             : 
    1345           0 : Int PBMath2DImage::support(const CoordinateSystem& cs){
    1346             :   
    1347           0 :   Int spectralIndex=cs.findCoordinate(Coordinate::SPECTRAL);
    1348           0 :   AlwaysAssert(spectralIndex>=0, AipsError);
    1349             :   SpectralCoordinate
    1350           0 :     imageSpectralCoord=cs.spectralCoordinate(spectralIndex);
    1351           0 :   Double desiredFrequency=imageSpectralCoord.referenceValue()(0);
    1352             : 
    1353             :   // Next get the frequency of the Jones image
    1354           0 :   spectralIndex=reJonesImage_p->coordinates().findCoordinate(Coordinate::SPECTRAL);
    1355           0 :   AlwaysAssert(spectralIndex>=0, AipsError);
    1356           0 :   imageSpectralCoord=reJonesImage_p->coordinates().spectralCoordinate(spectralIndex);
    1357           0 :   Double reFrequency=imageSpectralCoord.referenceValue()(0);
    1358             : 
    1359           0 :   Double reFreqScale = reFrequency/desiredFrequency;
    1360             : 
    1361           0 :   Double npixels=reJonesImage_p->shape()(0)*reFreqScale;
    1362             :   
    1363           0 :   Int dirIndex=cs.findCoordinate(Coordinate::DIRECTION);
    1364             :   DirectionCoordinate
    1365           0 :    directionCoord=cs.directionCoordinate(dirIndex);
    1366             :  
    1367           0 :  Vector<String> dirunit=directionCoord.worldAxisUnits();
    1368             : 
    1369           0 :  npixels=npixels/fabs(directionCoord.increment()(0));
    1370           0 :  dirIndex=reJonesImage_p->coordinates().findCoordinate(Coordinate::DIRECTION);
    1371             : 
    1372           0 :  directionCoord=(reJonesImage_p->coordinates()).directionCoordinate(dirIndex);
    1373           0 :  directionCoord.setWorldAxisUnits(dirunit);
    1374           0 :  npixels=npixels*fabs(directionCoord.increment()(0));
    1375             : 
    1376           0 :  return Int(floor(npixels));
    1377             : 
    1378             : }
    1379             : 
    1380             : } //#End casa namespace

Generated by: LCOV version 1.16