LCOV - code coverage report
Current view: top level - imageanalysis/IO - ImageProfileFitterResults.cc (source / functions) Hit Total Coverage
Test: casacpp_coverage.info Lines: 774 902 85.8 %
Date: 2024-11-06 17:42:47 Functions: 25 28 89.3 %

          Line data    Source code
       1             : //# Copyright (C) 1998,1999,2000,2001,2003
       2             : //# Associated Universities, Inc. Washington DC, USA.
       3             : //#
       4             : //# This program is free software; you can redistribute it and/or modify it
       5             : //# under the terms of the GNU General Public License as published by the Free
       6             : //# Software Foundation; either version 2 of the License, or (at your option)
       7             : //# any later version.
       8             : //#
       9             : //# This program is distributed in the hope that it will be useful, but WITHOUT
      10             : //# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
      11             : //# FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
      12             : //# more details.
      13             : //#
      14             : //# You should have received a copy of the GNU General Public License along
      15             : //# with this program; if not, write to the Free Software Foundation, Inc.,
      16             : //# 675 Massachusetts Ave, Cambridge, MA 02139, USA.
      17             : //#
      18             : //# Correspondence concerning AIPS++ should be addressed as follows:
      19             : //#        Internet email: casa-feedback@nrao.edu.
      20             : //#        Postal address: AIPS++ Project Office
      21             : //#                        National Radio Astronomy Observatory
      22             : //#                        520 Edgemont Road
      23             : //#                        Charlottesville, VA 22903-2475 USA
      24             : //#
      25             : //# $Id: $
      26             : 
      27             : #include <imageanalysis/IO/ImageProfileFitterResults.h>
      28             : 
      29             : #include <casacore/casa/Arrays/ArrayLogical.h>
      30             : #include <casacore/casa/Utilities/Precision.h>
      31             : #include <casacore/coordinates/Coordinates/DirectionCoordinate.h>
      32             : #include <casacore/coordinates/Coordinates/LinearCoordinate.h>
      33             : #include <casacore/coordinates/Coordinates/SpectralCoordinate.h>
      34             : #include <casacore/coordinates/Coordinates/StokesCoordinate.h>
      35             : #include <casacore/images/Images/PagedImage.h>
      36             : #include <casacore/scimath/Mathematics/Combinatorics.h>
      37             : 
      38             : #include <imageanalysis/ImageAnalysis/ImageCollapser.h>
      39             : #include <imageanalysis/ImageAnalysis/ProfileFitResults.h>
      40             : #include <imageanalysis/IO/LogFile.h>
      41             : 
      42             : using namespace casacore;
      43             : namespace casa {
      44             : 
      45             : const String ImageProfileFitterResults::_class = "ImageProfileFitterResults";
      46             : const String ImageProfileFitterResults::_CONVERGED = "converged";
      47             : const String ImageProfileFitterResults::_SUCCEEDED = "succeeded";
      48             : const String ImageProfileFitterResults::_VALID = "valid";
      49             : 
      50             : const uInt ImageProfileFitterResults::_nOthers = 2;
      51             : const uInt ImageProfileFitterResults::_gsPlane = 0;
      52             : const uInt ImageProfileFitterResults::_lsPlane = 1;
      53             : 
      54             : 
      55          86 : ImageProfileFitterResults::ImageProfileFitterResults(
      56             :     const std::shared_ptr<LogIO> log, const CoordinateSystem& csysIm,
      57             :     const Array<std::shared_ptr<ProfileFitResults> >* const &fitters, const SpectralList& nonPolyEstimates,
      58             :     const std::shared_ptr<const SubImage<Float> > subImage, Int polyOrder, Int fitAxis,
      59             :     uInt nGaussSinglets, uInt nGaussMultiplets, uInt nLorentzSinglets,
      60             :     uInt nPLPCoeffs, uInt nLTPCoeffs,
      61             :     Bool logResults, Bool multiFit, const std::shared_ptr<LogFile> logfile,
      62             :     const String& xUnit, const String& summaryHeader
      63          86 : ) : _logResults(logResults), _multiFit(multiFit),
      64          86 :     _doVelocity(
      65          86 :         fitAxis == subImage->coordinates().spectralAxisNumber()
      66         172 :         && Quantity(1, xUnit).isConform("m/s")
      67         258 :         && subImage->coordinates().spectralCoordinate().restFrequency() > 0
      68         172 :     ), _xUnit(xUnit), _summaryHeader(summaryHeader),
      69          86 :     _nGaussSinglets(nGaussSinglets), _nGaussMultiplets(nGaussMultiplets),
      70          86 :     _nLorentzSinglets(nLorentzSinglets), _nPLPCoeffs(nPLPCoeffs),_nLTPCoeffs(nLTPCoeffs),
      71          86 :     _fitters(fitters),
      72          86 :     _nonPolyEstimates(nonPolyEstimates), _subImage(subImage), _polyOrder(polyOrder),
      73         172 :     _fitAxis(fitAxis), _logfile(logfile), _log(log), _csysIm(csysIm) {}
      74             : 
      75          86 : ImageProfileFitterResults::~ImageProfileFitterResults() {}
      76             : 
      77          83 : Record ImageProfileFitterResults::getResults() const {
      78          83 :     return _results;
      79             : }
      80             : 
      81          83 : void ImageProfileFitterResults::createResults() {
      82          83 :     _setResults();
      83          83 :     _resultsToLog();
      84          83 : }
      85             : 
      86          83 : std::unique_ptr<vector<vector<Array<Double> > > > ImageProfileFitterResults::_createPCFArrays() const {
      87             :     std::unique_ptr<vector<vector<Array<Double> > > > pcfArrays(
      88             :         new vector<vector<Array<Double> > >(
      89         166 :             NGSOLMATRICES, vector<Array<Double> >(_nGaussMultiplets+_nOthers)
      90          83 :         )
      91         249 :     );
      92          83 :     uInt nSubcomps = 0;
      93          83 :     uInt compCount = 0;
      94          83 :     Double fNAN = casacore::doubleNaN();
      95             : 
      96          83 :     Array<Double> blank;
      97          83 :     IPosition fShape = _fitters->shape();
      98         251 :     for (uInt i=0; i<_nGaussMultiplets + _nOthers; i++) {
      99         168 :         if (i == _gsPlane) {
     100             :             // gaussian singlets go in position 0
     101          83 :             nSubcomps = _nGaussSinglets;
     102             :         }
     103          85 :         else if (i == _lsPlane) {
     104             :             // lorentzian singlets go in position 1
     105          83 :             nSubcomps = _nLorentzSinglets;
     106             :         }
     107             :         else {
     108             :             // gaussian multiplets go in positions 2 to 1 + _nGaussianMultiplets
     109           2 :             while (
     110           2 :                 _nonPolyEstimates[compCount]->getType() != SpectralElement::GMULTIPLET
     111             :             ) {
     112           0 :                 compCount++;
     113             :             }
     114           2 :             nSubcomps = dynamic_cast<const GaussianMultipletSpectralElement*>(
     115           2 :                     _nonPolyEstimates[compCount]
     116           4 :             )->getGaussians().size();
     117           2 :             compCount++;
     118             :         }
     119         168 :         IPosition blankSize(1, nSubcomps);
     120         168 :         blankSize.prepend(fShape);
     121         168 :         blank.resize(blankSize, False);
     122         168 :         blank = fNAN;
     123        1512 :         for (uInt k=0; k<NGSOLMATRICES; k++) {
     124        1344 :             (*pcfArrays)[k][i] = blank.copy();
     125             :         }
     126         168 :     }
     127         166 :     return pcfArrays;
     128          83 : }
     129             : 
     130          86 : vector<String> ImageProfileFitterResults::logSummary(
     131             :     uInt nProfiles, uInt nAttempted, uInt nSucceeded, uInt nConverged, uInt nValid
     132             : ) {
     133          86 :     vector<String> ret;
     134          86 :     *_log << LogOrigin(_class, __func__);
     135          86 :     ostringstream oss;
     136          86 :     oss << "Number of profiles       = " << nProfiles;
     137          86 :     String str = oss.str();
     138          86 :     *_log << LogIO::NORMAL << str << LogIO::POST;
     139          86 :     _writeLogfile(str + "\n", True, False);
     140          86 :     ret.push_back(str);
     141          86 :     oss.str("");
     142          86 :     oss << "Number of fits attempted = " << nAttempted;
     143          86 :     str = oss.str();
     144          86 :     *_log << LogOrigin(_class, __func__);
     145          86 :     *_log << LogIO::NORMAL << str << LogIO::POST;
     146          86 :     _writeLogfile(str + "\n", False, False);
     147          86 :     ret.push_back(str);
     148          86 :     oss.str("");
     149          86 :     oss << "Number succeeded         = " << nSucceeded;
     150          86 :     str = oss.str();
     151          86 :     *_log << LogOrigin(_class, __func__);
     152          86 :     *_log << LogIO::NORMAL << str << LogIO::POST;
     153          86 :     _writeLogfile(str + "\n", False, False);
     154          86 :     ret.push_back(str);
     155          86 :     oss.str("");
     156          86 :     oss << "Number converged         = " << nConverged;
     157          86 :     str = oss.str();
     158          86 :     *_log << LogOrigin(_class, __func__);
     159          86 :     *_log << LogIO::NORMAL << str << LogIO::POST;
     160          86 :     _writeLogfile(str + "\n", False, False);
     161          86 :     ret.push_back(str);
     162          86 :     oss.str("");
     163          86 :     oss << "Number valid             = " << nValid << endl;
     164          86 :     str = oss.str();
     165          86 :     *_log << LogOrigin(_class, __func__);
     166          86 :     *_log << LogIO::NORMAL << str << LogIO::POST;
     167          86 :     _writeLogfile(str + "\n", False, False);
     168          86 :     ret.push_back(str);
     169         172 :     return ret;
     170          86 : }
     171             : 
     172          83 : Bool ImageProfileFitterResults::_setAxisTypes() {
     173          83 :     const CoordinateSystem& csysSub = _subImage->coordinates();
     174             : 
     175          83 :     const DirectionCoordinate *dcoord = csysSub.hasDirectionCoordinate()
     176          83 :         ? &csysSub.directionCoordinate() : 0;
     177          83 :     String directionType = dcoord ? MDirection::showType(dcoord->directionType()) : "";
     178          83 :     const SpectralCoordinate *spcoord = csysSub.hasSpectralAxis()
     179          83 :         ? &csysSub.spectralCoordinate() : 0;
     180          83 :     const StokesCoordinate *polcoord = csysSub.hasPolarizationCoordinate()
     181          83 :         ? &csysSub.stokesCoordinate() : 0;
     182          83 :     Vector<String> axisNames = csysSub.worldAxisNames();
     183         397 :     for (uInt i=0; i<axisNames.size(); i++) {
     184         314 :         axisNames[i].upcase();
     185             :     }
     186          83 :     Bool hasLat = False;
     187          83 :     Bool hasLong = False;
     188          83 :     _axisTypes = vector<axisType>(axisNames.size(), NAXISTYPES);
     189         397 :     for (uInt i=0; i<axisNames.size(); i++) {
     190         314 :         if ((Int)i != _fitAxis) {
     191         231 :             if (
     192             :                 dcoord
     193         231 :                 && (axisNames[i].startsWith("RIG") || axisNames[i].startsWith("LONG"))
     194             :             ) {
     195          83 :                 _axisTypes[i] = LONGITUDE;
     196          83 :                 hasLat = True;
     197             :             }
     198         148 :             else if (
     199             :                 dcoord
     200         148 :                 && (axisNames[i].startsWith("DEC") || axisNames[i].startsWith("LAT"))
     201             :             ) {
     202          83 :                 _axisTypes[i] = LATITUDE;
     203          83 :                 hasLong = True;
     204             :             }
     205          65 :             else if (
     206             :                 spcoord
     207          65 :                 && (axisNames[i].startsWith("FREQ") || axisNames[i].startsWith("VEL"))
     208             :             ) {
     209           0 :                 _axisTypes[i] = FREQUENCY;
     210             :             }
     211          65 :             else if (
     212             :                 polcoord
     213          65 :                 && axisNames[i].startsWith("STO")
     214             :             ) {
     215          65 :                 _axisTypes[i] = POLARIZATION;
     216             :             }
     217             :         }
     218             :     }
     219         166 :     return hasLat && hasLong;
     220          83 : }
     221             : 
     222          83 : void ImageProfileFitterResults::_marshalFitResults(
     223             :     Array<Bool>& attemptedArr, Array<Bool>& successArr,
     224             :     Array<Bool>& convergedArr, Array<Bool>& validArr,
     225             :     Array<String>& typeMat, Array<Int>& niterArr,
     226             :     Array<Int>& nCompArr, std::unique_ptr<vector<vector<Array<Double> > > >& pcfArrays,
     227             :     vector<Array<Double> >& plpArrays, vector<Array<Double> >& ltpArrays,
     228             :     Bool returnDirection, Array<String>& directionInfo
     229             : ) {
     230          83 :     IPosition inTileShape = _subImage->niceCursorShape();
     231          83 :     TiledLineStepper stepper (_subImage->shape(), inTileShape, _fitAxis);
     232          83 :     RO_MaskedLatticeIterator<Float> inIter(*_subImage, stepper);
     233          83 :     const CoordinateSystem& csysSub = _subImage->coordinates();
     234          83 :     const DirectionCoordinate *dcoord = csysSub.hasDirectionCoordinate()
     235          83 :         ? &csysSub.directionCoordinate() : 0;
     236          83 :     String directionType = dcoord ? MDirection::showType(dcoord->directionType()) : "";
     237          83 :     const SpectralCoordinate *spcoord = csysSub.hasSpectralAxis()
     238          83 :         ? &csysSub.spectralCoordinate() : 0;
     239          83 :     const StokesCoordinate *polcoord = csysSub.hasPolarizationCoordinate()
     240          83 :         ? &csysSub.stokesCoordinate() : 0;
     241          83 :     IPosition pixel;
     242          83 :     Double increment = fabs(_fitAxisIncrement());
     243          83 :     for (
     244      279996 :         inIter.reset();    ! inIter.atEnd(); ++inIter
     245             :     ) {
     246      279830 :         IPosition pixel = inIter.position();
     247      279830 :         std::shared_ptr<const ProfileFitResults> fitter = (*_fitters)(pixel);
     248      279830 :         if (! fitter) {
     249      263075 :             continue;
     250             :         }
     251       16755 :         attemptedArr(pixel) = fitter->getList().nelements() > 0;
     252       16755 :         successArr(pixel) = fitter->succeeded();
     253       50218 :         convergedArr(pixel) = attemptedArr(pixel) && successArr(pixel)
     254       33463 :             && fitter->converged();
     255       16755 :         validArr(pixel) = convergedArr(pixel) && fitter->isValid();
     256       16755 :         _doWorldCoords(
     257             :             directionInfo, csysSub, pixel, dcoord, spcoord,
     258             :             polcoord, returnDirection, directionType
     259             :         );
     260       16755 :         if (validArr(pixel)) {
     261       13275 :             _processSolutions(
     262             :                 typeMat, niterArr, nCompArr, pixel, fitter,
     263             :                 pcfArrays, plpArrays, ltpArrays, increment
     264             :             );
     265             :         }
     266      542905 :     }
     267          83 : }
     268             : 
     269       13275 : void ImageProfileFitterResults::_processSolutions(
     270             :     Array<String>& typeMat, Array<Int>& niterArr,
     271             :     Array<Int>& nCompArr, const IPosition& pixel,
     272             :     std::shared_ptr<const ProfileFitResults> fitter,
     273             :     std::unique_ptr<vector<vector<Array<Double> > > >& pcfArrays,
     274             :     vector<Array<Double> >& plpArrays, vector<Array<Double> >& ltpArrays,
     275             :     Double increment
     276             : ) {
     277             :     // mask(pixel) = anyTrue(inIter.getMask());
     278       13275 :     niterArr(pixel) = (Int)fitter->getNumberIterations();
     279       13275 :     nCompArr(pixel) = (Int)fitter->getList().nelements();
     280       13275 :     SpectralList solutions = fitter->getList();
     281       13275 :     uInt gCount = 0;
     282       13275 :     uInt gmCount = 0;
     283       13275 :     uInt lseCount = 0;
     284       13275 :     uInt nSolutions = solutions.nelements();
     285       39526 :     for (uInt i=0; i<nSolutions; i++) {
     286       26251 :         SpectralElement::Types type = solutions[i]->getType();
     287       26251 :         IPosition tPos(1, i);
     288       26251 :         tPos.prepend(pixel);
     289       26251 :         typeMat(tPos) = SpectralElement::fromType(type);
     290       26251 :         switch (type) {
     291         290 :         case SpectralElement::POLYNOMIAL:
     292         290 :             break;
     293       25737 :         case SpectralElement::GAUSSIAN:
     294             :             // allow fall through because gaussians and lorentzians use common code
     295             :         case SpectralElement::LORENTZIAN:
     296             :         {
     297       25737 :             const PCFSpectralElement *pcf = dynamic_cast<
     298             :                 const PCFSpectralElement*
     299       25737 :             >(solutions[i]);
     300       25737 :             uInt plane = _lsPlane;
     301       25737 :             uInt col = lseCount;
     302             :             // if block because we allow case fall through
     303       25737 :             if (type == SpectralElement::LORENTZIAN) {
     304          50 :                 lseCount++;
     305             :             }
     306             :             else {
     307       25687 :                 plane = _gsPlane;
     308       25687 :                 col = gCount;
     309       25687 :                 gCount++;
     310             :             }
     311       25737 :             _insertPCF(
     312       25737 :                 *pcfArrays, pixel, *pcf, plane, col,
     313             :                 increment
     314             :             );
     315             :         }
     316       25737 :         break;
     317          26 :         case SpectralElement::GMULTIPLET:
     318             :         {
     319          26 :             const GaussianMultipletSpectralElement *gm = dynamic_cast<
     320             :                 const GaussianMultipletSpectralElement*
     321          26 :             >(solutions[i]);
     322          26 :             const Vector<GaussianSpectralElement> g(gm->getGaussians());
     323         104 :             for (uInt k=0; k<g.size(); k++) {
     324          78 :                 _insertPCF(
     325          78 :                     *pcfArrays, pixel, g[k], gmCount+2,
     326             :                     k, increment
     327             :                 );
     328             :             }
     329          26 :             gmCount++;
     330          26 :         }
     331          26 :         break;
     332         155 :         case SpectralElement::POWERLOGPOLY:
     333             :         {
     334         155 :             Vector<Double> sols = solutions[i]->get();
     335         155 :             Vector<Double> errs = solutions[i]->getError();
     336         465 :             for (uInt j=0; j<_nPLPCoeffs; j++) {
     337             :                 // here
     338         310 :                 IPosition arrIdx(1, j);
     339         310 :                 arrIdx.prepend(pixel);
     340         310 :                 plpArrays[SPXSOL](arrIdx) = sols[j];
     341         310 :                 plpArrays[SPXERR](arrIdx) = errs[j];
     342         310 :             }
     343         155 :         }
     344         155 :         break;
     345          43 :         case SpectralElement::LOGTRANSPOLY:
     346             :         {
     347          43 :             auto sols = solutions[i]->get();
     348          43 :             auto errs = solutions[i]->getError();
     349         129 :             for (uInt j=0; j<_nLTPCoeffs; j++) {
     350          86 :                 IPosition arrIdx(1, j);
     351          86 :                 arrIdx.prepend(pixel);
     352          86 :                 ltpArrays[SPXSOL](arrIdx) = sols[j];
     353          86 :                 ltpArrays[SPXERR](arrIdx) = errs[j];
     354          86 :             }
     355          43 :         }
     356          43 :         break;
     357           0 :         default:
     358           0 :             ThrowCc(
     359             :                 "Logic Error: Unhandled Spectral Element type"
     360             :             );
     361             :             break;
     362             :         }
     363       26251 :     }
     364       13275 : }
     365             : 
     366       16755 : void ImageProfileFitterResults::_doWorldCoords(
     367             :     Array<String>& directionInfo, const CoordinateSystem& csysSub,
     368             :     const IPosition& pixel, const DirectionCoordinate* const &dcoord,
     369             :     const SpectralCoordinate * const &spcoord,
     370             :     const StokesCoordinate* const &polcoord, Bool returnDirection,
     371             :     const String& directionType
     372             : ) {
     373       16755 :     Vector<Double> world;
     374       16755 :     if (csysSub.toWorld(world, pixel)) {
     375       16755 :         String latitude, longitude;
     376       83624 :         for (uInt i=0; i<world.size(); i++) {
     377             :             // The Coordinate::format() calls have the potential of modifying the
     378             :             // input unit so this needs to be reset at the beginning of the loop
     379             :             // rather than outside the loop
     380       66869 :             String emptyUnit = "";
     381       66869 :             if ((Int)i != _fitAxis) {
     382       50114 :                 if (_axisTypes[i] == LONGITUDE) {
     383       16755 :                     longitude = dcoord->format(emptyUnit, Coordinate::DEFAULT, world[i], 0, True, True);
     384       16755 :                     IPosition x(1, LONGITUDE);
     385       16755 :                     x.append(pixel);
     386       16755 :                     _worldCoords(x) = longitude;
     387       16755 :                 }
     388       33359 :                 else if (_axisTypes[i] == LATITUDE) {
     389       16755 :                     latitude = dcoord->format(emptyUnit, Coordinate::DEFAULT, world[i], 1, True, True);
     390       16755 :                     IPosition x(1, LATITUDE);
     391       16755 :                     x.append(pixel);
     392       16755 :                     _worldCoords(x) = latitude;
     393       16755 :                 }
     394       16604 :                 else if (_axisTypes[i] == FREQUENCY) {
     395           0 :                     IPosition x(1, FREQUENCY);
     396           0 :                     x.append(pixel);
     397           0 :                     _worldCoords(x) = spcoord->format(emptyUnit, Coordinate::DEFAULT, world[i], 0, True, True);
     398           0 :                 }
     399       16604 :                 else if (_axisTypes[i] == POLARIZATION) {
     400       16604 :                     IPosition x(1, POLARIZATION);
     401       16604 :                     x.append(pixel);
     402       16604 :                     _worldCoords(x) = polcoord->format(emptyUnit, Coordinate::DEFAULT, world[i], 0, True, True);
     403       16604 :                 }
     404             :             }
     405       66869 :         }
     406       16755 :         if (returnDirection) {
     407       50265 :             directionInfo(pixel) = directionType + " "
     408       33510 :                 + longitude + " " + latitude;
     409             :         }
     410       16755 :     }
     411             :     else {
     412           0 :         ThrowCc(
     413             :             "Could not convert pixel to world coordinate: "
     414             :                 + csysSub.errorMessage()
     415             :         );
     416             :     }
     417       16755 : }
     418             : 
     419       14617 : void ImageProfileFitterResults::_writeLogfile(const String& str, Bool open, Bool close) {
     420       14617 :     if (_logfile.get() != 0) {
     421         221 :         _logfile->write(str, open, close);
     422             :     }
     423       14617 : }
     424             : 
     425          83 : void ImageProfileFitterResults::_setResults() {
     426          83 :     LogOrigin logOrigin(_class, __func__);
     427          83 :     Double fNAN = casacore::doubleNaN();
     428          83 :     uInt nComps = _nGaussSinglets + _nGaussMultiplets + _nLorentzSinglets;
     429          83 :     if (_polyOrder >= 0) {
     430           5 :         nComps++;
     431             :     }
     432          83 :     if (_nPLPCoeffs > 0) {
     433          11 :         nComps++;
     434             :     }
     435          83 :     if (_nLTPCoeffs > 0) {
     436           5 :         nComps++;
     437             :     }
     438          83 :     IPosition fitterShape = _fitters->shape();
     439          83 :     Array<Bool> attemptedArr(fitterShape, False);
     440          83 :     Array<Bool> successArr(fitterShape, False);
     441          83 :     Array<Bool> convergedArr(fitterShape, False);
     442          83 :     Array<Bool> validArr(fitterShape, False);
     443          83 :     IPosition wcShape(1, (Int)NAXISTYPES);
     444          83 :     wcShape.append(fitterShape);
     445          83 :     _worldCoords = Array<String>(wcShape, String(""));
     446          83 :     Array<Int> niterArr(fitterShape, -1);
     447             :     // pfcArrays. Zeroth structure (zeroth vector) index corresponds to
     448             :     // solution type (amp, center, etc). First structure (first vector)
     449             :     // index corresponds to type of component (Gaussian, Lorentzian, Gaussian
     450             :     // multiplet number). Second to n-2 structure (first m Array) indices
     451             :     // correspond location in the _fitters array. Final structure index
     452             :     // corresponds to the sub component number (eg for multiple singlets or
     453             :     // for gaussian multiplet components
     454          83 :     std::unique_ptr<vector<vector<Array<Double> > > > pcfArrays = _createPCFArrays();
     455          83 :     IPosition bShape(1, max(_nPLPCoeffs, _nLTPCoeffs));
     456          83 :     bShape.prepend(fitterShape);
     457          83 :     Array<Double> blank(bShape, fNAN);
     458             :     // plpArrays and ltpArrays have the solution type as the zeroth (vector)
     459             :     // index. The next n indices (the first n indices of the Array) correspond to the position in the _fitters
     460             :     // array. The final index of the structure (also the final index of the Array) corresponds to
     461             :     // the componenet number being solved for.
     462          83 :     vector<Array<Double> > plpArrays(NSPXSOLMATRICES);
     463          83 :     vector<Array<Double> > ltpArrays(NSPXSOLMATRICES);
     464         249 :     for (uInt i=0; i<NSPXSOLMATRICES; i++) {
     465             :         // because assignmet of Arrays is by reference, which is horribly confusing
     466         166 :         if (_nPLPCoeffs > 0) {
     467          22 :             plpArrays[i] = blank.copy();
     468             :         }
     469         166 :         if (_nLTPCoeffs > 0) {
     470          10 :             ltpArrays[i] = blank.copy();
     471             :         }
     472             :     }
     473          83 :     IPosition typeShape(1, nComps);
     474          83 :     typeShape.prepend(fitterShape);
     475          83 :     Array<String> typeArr(typeShape, String("UNDEF"));
     476          83 :     Array<Int> nCompArr(fitterShape, -1);
     477          83 :     Bool returnDirection = _setAxisTypes();
     478          83 :     Array<String> directionInfo(fitterShape);
     479          83 :     _marshalFitResults(
     480             :         attemptedArr, successArr,
     481             :         convergedArr, validArr,
     482             :         typeArr, niterArr,
     483             :         nCompArr, pcfArrays, plpArrays, ltpArrays,
     484             :         returnDirection, directionInfo
     485             :     );
     486          83 :     String key;
     487          83 :     _results.define("attempted", attemptedArr);
     488          83 :     _results.define(_SUCCEEDED, successArr);
     489          83 :     _results.define(_CONVERGED, convergedArr);
     490          83 :     _results.define(_VALID, validArr);
     491          83 :     _results.define("niter", niterArr);
     492          83 :     _results.define("ncomps", nCompArr);
     493          83 :     if (returnDirection) {
     494          83 :         _results.define("direction", directionInfo);
     495             :     }
     496          83 :     _results.define("xUnit", _xUnit);
     497          83 :     const String yUnit = _subImage->units().getName();
     498          83 :     _results.define("yUnit", yUnit);
     499          83 :     _results.define( "type", typeArr);
     500         251 :     for (uInt i=0; i<_nGaussMultiplets+_nOthers; ++i) {
     501         168 :         if (i == _gsPlane && _nGaussSinglets == 0) {
     502         101 :             continue;
     503             :         }
     504         149 :         else if (i == _lsPlane && _nLorentzSinglets == 0) {
     505          82 :             continue;
     506             :         }
     507          67 :         Record rec;
     508          67 :         rec.define("center", (*pcfArrays)[CENTER][i]);
     509          67 :         rec.define("fwhm", (*pcfArrays)[FWHM][i]);
     510          67 :         rec.define("amp", (*pcfArrays)[AMP][i]);
     511          67 :         rec.define("integral", (*pcfArrays)[INTEGRAL][i]);
     512          67 :         rec.define("centerErr", (*pcfArrays)[CENTERERR][i]);
     513          67 :         rec.define("fwhmErr", (*pcfArrays)[FWHMERR][i]);
     514          67 :         rec.define("ampErr", (*pcfArrays)[AMPERR][i]);
     515          67 :         rec.define("integralErr", (*pcfArrays)[INTEGRALERR][i]);
     516             :         String description = i == _gsPlane
     517             :             ? "Gaussian singlets results"
     518             :             : i == _lsPlane
     519             :               ? "Lorentzian singlets"
     520          71 :               : "Gaussian multiplet number " + String::toString(i-1) + " results";
     521          67 :         rec.define("description", description);
     522          67 :         _results.defineRecord(_getTag(i), rec);
     523          67 :     }
     524          83 :     if (_nPLPCoeffs > 0) {
     525          11 :         Record rec;
     526          11 :         rec.define("solution", plpArrays[SPXSOL]);
     527          11 :         rec.define("error", plpArrays[SPXERR]);
     528          11 :         _results.defineRecord("plp", rec);
     529          11 :     }
     530          83 :     if (_nLTPCoeffs > 0) {
     531           5 :         Record rec;
     532           5 :         rec.define("solution", ltpArrays[SPXSOL]);
     533           5 :         rec.define("error", ltpArrays[SPXERR]);
     534           5 :         _results.defineRecord("ltp", rec);
     535           5 :     }
     536          83 : }
     537             : 
     538          72 : String ImageProfileFitterResults::_getTag(const uInt i) const {
     539             :     return i == _gsPlane
     540             :         ? "gs"
     541             :         : i == _lsPlane
     542             :             ? "ls"
     543         144 :             : "gm" + String::toString(i-2);
     544             : }
     545             : 
     546       25815 : void ImageProfileFitterResults::_insertPCF(
     547             :     vector<vector<Array<Double> > >& pcfArrays,
     548             :     const IPosition& pixel, const PCFSpectralElement& pcf,
     549             :     const uInt idx, const uInt col,
     550             :     const Double increment
     551             : ) const {
     552       25815 :     IPosition x = pixel;
     553       25815 :     x.append(IPosition(1, col));
     554       25815 :     pcfArrays[CENTER][idx](x) = _centerWorld(pcf, pixel);
     555       25815 :     pcfArrays[FWHM][idx](x) = pcf.getFWHM() * increment;
     556       25815 :     pcfArrays[AMP][idx](x) = pcf.getAmpl();
     557       25815 :     pcfArrays[CENTERERR][idx](x) = pcf.getCenterErr() * increment;
     558       25815 :     pcfArrays[FWHMERR][idx](x) = pcf.getFWHMErr() * increment;
     559       25815 :     pcfArrays[AMPERR][idx](x) = pcf.getAmplErr();
     560       25815 :     pcfArrays[INTEGRAL][idx](x) = pcf.getIntegral() * increment;
     561       25815 :     pcfArrays[INTEGRALERR][idx](x) = pcf.getIntegralErr() * increment;
     562       25815 : }
     563             : 
     564           0 : Array<Bool> ImageProfileFitterResults::_replicateMask(
     565             :     const Array<Bool>& array, Int n
     566             : ) {
     567           0 :     uInt axis = array.ndim() - 1;
     568           0 :     IPosition newShape = array.shape();
     569           0 :     newShape[axis] = n;
     570           0 :     Array<Bool> extended(newShape);
     571           0 :     IPosition begin(newShape.size(), 0);
     572           0 :     IPosition end = newShape - 1;
     573           0 :     for (Int j=0; j<n; j++) {
     574           0 :         begin[axis] = j;
     575           0 :         end[axis] = j;
     576           0 :         extended(begin, end) = array;
     577             :     }
     578           0 :     return extended;
     579           0 : }
     580             : 
     581          86 : void ImageProfileFitterResults::writeImages(Bool someConverged) const {
     582             :     Bool writeSolutionImages = (
     583         167 :         ! _centerName.empty() || ! _centerErrName.empty()
     584          81 :         || ! _fwhmName.empty() || ! _fwhmErrName.empty()
     585          81 :         || ! _ampName.empty() || ! _ampErrName.empty()
     586          81 :         || ! _integralName.empty() || ! _integralErrName.empty()
     587          81 :         || ! _plpName.empty() || ! _plpErrName.empty()
     588         167 :         || ! _ltpName.empty() || ! _ltpErrName.empty()
     589          86 :     );
     590          86 :     if (
     591          86 :         ! _multiFit && writeSolutionImages
     592             :     ) {
     593           0 :         *_log << LogIO::WARN << "This was not a multi-pixel fit request so solution "
     594           0 :             << "images will not be written" << LogIO::POST;
     595             :     }
     596          86 :     if (
     597          86 :         _multiFit && writeSolutionImages
     598             :     ) {
     599          10 :         if (
     600          10 :             _nGaussSinglets + _nGaussMultiplets + _nLorentzSinglets + _nPLPCoeffs + _nLTPCoeffs == 0
     601             :         ) {
     602           0 :             *_log << LogIO::WARN << "No functions for which solution images are supported were fit "
     603           0 :                 << "so no solution images will be written" << LogIO::POST;
     604             :         }
     605             :         else {
     606          10 :             if (someConverged) {
     607          10 :                 IPosition axes(1, _fitAxis);
     608             :                 ImageCollapser<Float> collapser(
     609          10 :                     _subImage, axes, False, ImageCollapserData::ZERO, String(""), False
     610          20 :                 );
     611             :                 std::shared_ptr<TempImage<Float> > tmp = std::dynamic_pointer_cast<TempImage<Float> >(
     612          10 :                     collapser.collapse()
     613          10 :                 );
     614          10 :                 ThrowIf(! tmp, "Unable to perform dynamic cast");
     615          10 :                 std::shared_ptr<TempImage<Float> > myTemplate(tmp);
     616          10 :                 const String yUnit = _subImage->units().getName();
     617          10 :                 Array<Bool>    mask(_fitters->shape(), False);
     618          10 :                 IPosition inTileShape = _subImage->niceCursorShape();
     619          10 :                 TiledLineStepper stepper (_subImage->shape(), inTileShape, _fitAxis);
     620          10 :                 RO_MaskedLatticeIterator<Float> inIter(*_subImage, stepper);
     621          10 :                 for (
     622      277337 :                     inIter.reset();    ! inIter.atEnd(); ++inIter
     623             :                 ) {
     624      277317 :                     mask(inIter.position()) = anyTrue(inIter.getMask());
     625             :                 }
     626          10 :                 _writeImages(myTemplate->coordinates(), mask, yUnit);
     627          10 :             }
     628             :             else {
     629           0 :                 *_log << LogIO::WARN << "No solutions converged, solution images will not be written"
     630           0 :                     << LogIO::POST;
     631             :             }
     632             :         }
     633             :     }
     634          86 : }
     635             : 
     636          10 : void ImageProfileFitterResults::_writeImages(
     637             :     const CoordinateSystem& xcsys,
     638             :     const Array<Bool>& mask, const String& yUnit
     639             : ) const {
     640          10 :     map<String, String> mymap;
     641          10 :     map<String, String> unitmap;
     642          10 :     mymap["center"] = _centerName;
     643          10 :     mymap["centerErr"] = _centerErrName;
     644          10 :     mymap["fwhm"] = _fwhmName;
     645          10 :     mymap["fwhmErr"] = _fwhmErrName;
     646          10 :     mymap["amp"] = _ampName;
     647          10 :     mymap["ampErr"] = _ampErrName;
     648          10 :     mymap["integral"] = _integralName;
     649          10 :     mymap["integralErr"] = _integralErrName;
     650          10 :     unitmap["center"] = _xUnit;
     651          10 :     unitmap["centerErr"] = _xUnit;
     652          10 :     unitmap["fwhm"] = _xUnit;
     653          10 :     unitmap["fwhmErr"] = _xUnit;
     654          10 :     unitmap["amp"] = yUnit;
     655          10 :     unitmap["ampErr"] = yUnit;
     656          10 :     unitmap["integral"] = _xUnit + "." + yUnit;
     657          10 :     unitmap["integralErr"] = _xUnit + "." + yUnit;
     658          31 :     for (uInt i=0; i<_nGaussMultiplets+_nOthers; i++) {
     659          21 :         if (i == _gsPlane && _nGaussSinglets == 0) {
     660          16 :             continue;
     661             :         }
     662          14 :         else if (i == _lsPlane && _nLorentzSinglets == 0) {
     663           9 :             continue;
     664             :         }
     665           5 :         String id = _getTag(i);
     666          45 :         for (const auto& p : mymap) {
     667          40 :             String imagename = p.second;
     668             :             String suffix = i == _gsPlane
     669             :                 ? ""
     670             :                 : i == _lsPlane
     671             :                   ? "_ls"
     672           8 :                   : _nGaussMultiplets <= 1
     673             :                     ? "_gm"
     674          40 :                     : "_gm" + String::toString(i-_nOthers);
     675          40 :             imagename += suffix;
     676          40 :             if (! p.second.empty()) {
     677          35 :                 _makeSolutionImages(
     678             :                     imagename, xcsys,
     679          70 :                     _results.asRecord(id).asArrayDouble(p.first),
     680          70 :                     unitmap.find(p.first)->second, mask
     681             :                 );
     682             :             }
     683          40 :         }
     684           5 :     }
     685          10 :     if (
     686           3 :         (_nPLPCoeffs > 0 && (! _plpName.empty() || ! _plpErrName.empty()))
     687          13 :         || (_nLTPCoeffs > 0 && (! _ltpName.empty() || ! _ltpErrName.empty()))
     688             :     ) {
     689           5 :         String type = _results.isDefined("plp") ? "plp" : "ltp";
     690           5 :         if (_nPLPCoeffs > 0 && ! _plpName.empty()) {
     691          12 :             _makeSolutionImages(
     692           3 :                 _plpName, xcsys,
     693           6 :                 _results.asRecord("plp").asArrayDouble("solution"),
     694             :                 "", mask
     695             :             );
     696             :         }
     697           5 :         if (_nPLPCoeffs > 0 && ! _plpErrName.empty()) {
     698          12 :             _makeSolutionImages(
     699           3 :                 _plpErrName, xcsys,
     700           6 :                 _results.asRecord("plp").asArrayDouble("error"),
     701             :                 "", mask
     702             :             );
     703             :         }
     704           5 :         if (_nLTPCoeffs > 0 && ! _ltpName.empty()) {
     705           8 :             _makeSolutionImages(
     706           2 :                 _ltpName, xcsys,
     707           4 :                 _results.asRecord("ltp").asArrayDouble("solution"),
     708             :                 "", mask
     709             :             );
     710             :         }
     711           5 :         if (_nLTPCoeffs > 0 && ! _ltpErrName.empty()) {
     712           4 :             _makeSolutionImages(
     713           1 :                 _ltpErrName, xcsys,
     714           2 :                 _results.asRecord("ltp").asArrayDouble("error"),
     715             :                 "", mask
     716             :             );
     717             :         }
     718           5 :     }
     719          10 : }
     720             : /*
     721             : Bool ImageProfileFitterResults::_inVelocitySpace() const {
     722             :     return _fitAxis == _subImage->coordinates().spectralAxisNumber()
     723             :         && Quantity(1, _xUnit).isConform("m/s")
     724             :         && _subImage->coordinates().spectralCoordinate().restFrequency() > 0;
     725             : }
     726             : */
     727             : 
     728       20970 : Double ImageProfileFitterResults::_fitAxisIncrement() const {
     729       20970 :     if (_doVelocity) {
     730       20954 :         Vector<Double> pixels(2);
     731       20954 :         pixels[0] = 0;
     732       20954 :         pixels[1] = 1;
     733       20954 :         Vector<Double> velocities(2);
     734       20954 :         _subImage->coordinates().spectralCoordinate().pixelToVelocity(
     735             :             velocities, pixels
     736             :         );
     737       20954 :         return velocities[1] - velocities[0];
     738       20954 :     }
     739             :     else {
     740          16 :         return _subImage->coordinates().increment()[_fitAxis];
     741             :     }
     742             : }
     743             : 
     744           0 : const Vector<Double> ImageProfileFitterResults::getPixelCenter(uint index) const {
     745           0 :     Vector<Double> pos;
     746           0 :     if ( index < _pixelPositions.size()) {
     747           0 :         pos = _pixelPositions[index];
     748             :     }
     749           0 :     return pos;
     750           0 : }
     751             : 
     752       46702 : Double ImageProfileFitterResults::_centerWorld(
     753             :     const PCFSpectralElement& solution, const IPosition& imPos
     754             : ) const {
     755       46702 :     Vector<Double> pixel(imPos.size());
     756      233099 :     for (uInt i=0; i<pixel.size(); i++) {
     757      186397 :         pixel[i] = imPos[i];
     758             :     }
     759       46702 :     Vector<Double> world(pixel.size());
     760             :     // in pixels here
     761       46702 :     pixel[_fitAxis] = solution.getCenter();
     762       46702 :     _subImage->coordinates().toWorld(world, pixel);
     763       46702 :     if (_doVelocity) {
     764             :         Double velocity;
     765       46702 :         _subImage->coordinates().spectralCoordinate().frequencyToVelocity(velocity, world(_fitAxis));
     766       46702 :         return velocity;
     767             :     }
     768             :     else {
     769           0 :         return world(_fitAxis);
     770             :     }
     771       46702 : }
     772             : 
     773          83 : void ImageProfileFitterResults::_resultsToLog() {
     774          83 :     if (! _logResults && _logfile.get() == 0) {
     775          62 :         return;
     776             :     }
     777          21 :     ostringstream summary;
     778          21 :     summary << "****** Fit performed at " << Time().toString() << "******" << endl << endl;
     779          21 :     summary << _summaryHeader;
     780          21 :     if (_nPLPCoeffs + _nLTPCoeffs == 0) {
     781           5 :         if (_goodAmpRange.size() == 2) {
     782           0 :             summary << "       --- valid amplitude range: " << _goodAmpRange << endl;
     783             :         }
     784           5 :         if (_goodCenterRange.size() == 2) {
     785           0 :             summary << "       --- valid center range: " << _goodCenterRange << endl;
     786             :         }
     787           5 :         if (_goodFWHMRange.size() == 2) {
     788           0 :             summary << "       --- valid FWHM range: " << _goodFWHMRange << endl;
     789             :         }
     790           5 :         summary << "       --- number of Gaussian singlets: " << _nGaussSinglets << endl;
     791           5 :         summary << "       --- number of Gaussian multiplets: " << _nGaussMultiplets << endl;
     792           5 :         if (_nGaussMultiplets > 0) {
     793           2 :             for (uInt i=0; i<_nGaussMultiplets; i++) {
     794           2 :                 Array<Double> amp = _results.asRecord("gm" + String::toString(i)).asArrayDouble(AMP);
     795           1 :                 uInt n = amp.shape()[amp.ndim()-1];
     796           1 :                 summary << "           --- number of components in Gaussian multiplet "
     797           1 :                     << i << ": " << n << endl;
     798           1 :             }
     799             :         }
     800           5 :         if (_polyOrder >= 0) {
     801           0 :             summary << "       --- polynomial order:    " << _polyOrder << endl;
     802             :         }
     803             :         else {
     804           5 :             summary << "       --- no polynomial fit " << endl;
     805             :         }
     806             :     }
     807          21 :     if (_multiFit) {
     808          11 :         summary << "       --- Multiple profiles fit, one per pixel over selected region" << endl;
     809             :     }
     810             :     else {
     811          10 :         summary << "       --- One profile fit, averaged over several pixels if necessary" << endl;
     812             :     }
     813          21 :     if (_logResults) {
     814          18 :         *_log << LogIO::NORMAL << summary.str() << LogIO::POST;
     815             :     }
     816          21 :     _writeLogfile(summary.str(), False, False);
     817          21 :     IPosition inTileShape = _subImage->niceCursorShape();
     818          21 :     TiledLineStepper stepper (_subImage->shape(), inTileShape, _fitAxis);
     819          21 :     RO_MaskedLatticeIterator<Float> inIter(*_subImage, stepper);
     820          21 :     CoordinateSystem csysSub = _subImage->coordinates();
     821          21 :     Vector<Double> worldStart;
     822          21 :     ThrowIf(
     823             :         ! csysSub.toWorld(worldStart, inIter.position()),
     824             :         csysSub.errorMessage()
     825             :     );
     826          21 :     Vector<Double> pixStart;
     827          21 :     ThrowIf(
     828             :         ! _csysIm.toPixel(pixStart, worldStart),
     829             :         _csysIm.errorMessage()
     830             :     );
     831          21 :     if (_multiFit) {
     832          49 :         for (uInt i=0; i<pixStart.size(); i++) {
     833          38 :             pixStart[i] = (Int)std::floor( pixStart[i] + 0.5);
     834             :         }
     835             :     }
     836          21 :     Vector<Double> imPix(pixStart.size());
     837          21 :     Vector<Double> world;
     838          21 :     IPosition subimPos;
     839          21 :     SpectralList solutions;
     840          21 :     String axisUnit = _csysIm.worldAxisUnits()[_fitAxis];
     841          21 :     Int pixPrecision = _multiFit ? 0 : 3;
     842          21 :     _pixelPositions.resize( _fitters->size());
     843          21 :     uint index = 0;
     844          21 :     for (
     845          21 :         inIter.reset();
     846      277237 :         ! inIter.atEnd();
     847      277216 :         inIter++
     848             :     ) {
     849      277216 :         subimPos = inIter.position();
     850      277216 :         const std::shared_ptr<ProfileFitResults> fitter = (*_fitters)(subimPos);
     851      277216 :         if (! fitter) {
     852      263050 :             continue;
     853             :         }
     854       14166 :         summary.str("");
     855       14166 :         Int basePrecision = summary.precision(1);
     856       14166 :         summary.precision(basePrecision);
     857       14166 :         if (csysSub.toWorld(world, subimPos)) {
     858       14166 :             summary << "Fit   :" << endl;
     859       70730 :             for (uInt i=0; i<world.size(); i++) {
     860       56564 :                 if ((Int)i != _fitAxis) {
     861       42398 :                     IPosition x(1, _axisTypes[i]);
     862       42398 :                     x.append(subimPos);
     863       42398 :                     if (_axisTypes[i] == LONGITUDE) {
     864       14166 :                         summary << "    RA           :   "
     865       14166 :                             << _worldCoords(x) << endl;
     866             :                     }
     867       28232 :                     else if (_axisTypes[i] == LATITUDE) {
     868       14166 :                         summary << "    Dec          :  "
     869       14166 :                             << _worldCoords(x) << endl;
     870             :                     }
     871       14066 :                     else if (_axisTypes[i] == FREQUENCY) {
     872           0 :                         summary << "    Freq         : "
     873           0 :                             << _worldCoords(x) <<  endl;
     874             :                     }
     875       14066 :                     else if (_axisTypes[i] == POLARIZATION) {
     876       14066 :                         summary << "    Stokes       : " << _worldCoords(x) << endl;
     877             :                     }
     878       42398 :                 }
     879             :             }
     880             :         }
     881             :         else {
     882           0 :             ThrowCc(csysSub.errorMessage());
     883             :         }
     884       70730 :         for (uInt i=0; i<pixStart.size(); i++) {
     885       56564 :             imPix[i] = pixStart[i] + subimPos[i];
     886             :         }
     887       14166 :         _pixelPositions[index] = imPix;
     888       14166 :         summary.setf(ios::fixed);
     889       14166 :         summary << setprecision(pixPrecision) << "    Pixel        : [";
     890       70730 :         for (uInt i=0; i<imPix.size(); i++) {
     891       56564 :             if (i == (uInt)_fitAxis) {
     892       14166 :                 summary << " *";
     893             :             }
     894             :             else {
     895       42398 :                 summary << imPix[i];
     896             :             }
     897       56564 :             if (i != imPix.size()-1) {
     898       42398 :                 summary << ", ";
     899             :             }
     900             :         }
     901       14166 :         summary << "]" << setprecision(basePrecision) << endl;
     902       14166 :         summary.unsetf(ios::fixed);
     903       14166 :         Bool attempted = fitter->getList().nelements() > 0;
     904             :         summary << "    Attempted    : "
     905       14166 :             << (attempted ? "YES" : "NO") << endl;
     906       14166 :         if (attempted) {
     907       14119 :             String converged = fitter->converged() ? "YES" : "NO";
     908       14119 :             summary << "    Converged    : " << converged << endl;
     909       14119 :             if (fitter->converged()) {
     910       10751 :                 summary << "    Iterations   : " << fitter->getNumberIterations() << endl;
     911       10751 :                 String valid = fitter->isValid() ? "YES" : "NO";
     912       10751 :                 summary << "    Valid        : " << valid << endl;
     913       10751 :                 if (fitter->isValid()) {
     914       10751 :                     solutions = fitter->getList();
     915       31786 :                     for (uInt i=0; i<solutions.nelements(); i++) {
     916       21035 :                         SpectralElement::Types type = solutions[i]->getType();
     917       21035 :                         if (solutions.nelements() > 1) {
     918       20568 :                             summary << "    Results for component " << i << ":" << endl;
     919             :                         }
     920       21035 :                         switch(type) {
     921       20812 :                         case SpectralElement::GAUSSIAN:
     922             :                             // allow fall through; gaussians and lorentzians use the same
     923             :                             // method for output
     924             :                         case SpectralElement::LORENTZIAN:
     925             :                             {
     926             :                                 const PCFSpectralElement *pcf
     927       20812 :                                     = dynamic_cast<const PCFSpectralElement*>(solutions[i]);
     928       62436 :                                 summary << _pcfToString(
     929       62436 :                                     pcf, _csysIm, world.copy(), subimPos
     930       20812 :                                 );
     931             :                             }
     932       20812 :                             break;
     933          25 :                         case SpectralElement::GMULTIPLET:
     934             :                             {
     935             :                                 const GaussianMultipletSpectralElement *gm
     936          25 :                                     = dynamic_cast<const GaussianMultipletSpectralElement*>(solutions[i]);
     937          50 :                                 summary << _gaussianMultipletToString(
     938          75 :                                     *gm, _csysIm, world.copy(), subimPos
     939          25 :                                 );
     940          25 :                                 break;
     941             :                             }
     942           0 :                         case SpectralElement::POLYNOMIAL:
     943             :                             {
     944             :                                 const PolynomialSpectralElement *p
     945           0 :                                     = dynamic_cast<const PolynomialSpectralElement*>(solutions[i]);
     946           0 :                                 summary << _polynomialToString(*p, _csysIm, imPix, world);
     947             :                             }
     948           0 :                             break;
     949         155 :                         case SpectralElement::POWERLOGPOLY:
     950             :                             {
     951             :                                 const PowerLogPolynomialSpectralElement *p
     952         155 :                                     = dynamic_cast<const PowerLogPolynomialSpectralElement*>(solutions[i]);
     953         155 :                                 summary << _powerLogPolyToString(*p);
     954             :                             }
     955         155 :                             break;
     956          43 :                         case SpectralElement::LOGTRANSPOLY:
     957             :                             {
     958             :                                 const LogTransformedPolynomialSpectralElement *p
     959          43 :                                 = dynamic_cast<const LogTransformedPolynomialSpectralElement*>(solutions[i]);
     960          43 :                                 summary << _logTransPolyToString(*p);
     961             :                             }
     962          43 :                             break;
     963           0 :                         default:
     964           0 :                             ThrowCc("Logic Error: Unhandled spectral element type");
     965             :                             break;
     966             :                         }
     967             :                     }
     968             :                 }
     969       10751 :             }
     970       14119 :         }
     971       14166 :         if (_logResults) {
     972       13977 :             *_log << LogIO::NORMAL << summary.str() << endl << LogIO::POST;
     973             :         }
     974       14166 :         _writeLogfile(summary.str(), False, False);
     975      277216 :     }
     976          21 :     if (_logfile.get() != 0) {
     977           5 :         _logfile->close();
     978             :     }
     979          21 : }
     980             : 
     981      125718 : String ImageProfileFitterResults::_elementToString(
     982             :     const Double value, const Double error,
     983             :     const String& unit, Bool isFixed
     984             : ) const {
     985      125718 :     Unit myUnit(unit);
     986      125718 :     Vector<String> unitPrefix;
     987      125718 :     String outUnit;
     988      125718 :     Quantity qVal(value, unit);
     989      125718 :     Quantity qErr(error, unit);
     990      125718 :     if (myUnit.getValue() == UnitVal::ANGLE) {
     991           0 :         Vector<String> angUnits(5);
     992           0 :         angUnits[0] = "deg";
     993           0 :         angUnits[1] = "arcmin";
     994           0 :         angUnits[2] = "arcsec";
     995           0 :         angUnits[3] = "marcsec";
     996           0 :         angUnits[4] = "uarcsec";
     997           0 :         for (uInt i=0; i<angUnits.size(); i++) {
     998           0 :             outUnit = angUnits[i];
     999           0 :             if (fabs(qVal.getValue(outUnit)) > 1) {
    1000           0 :                 qVal.convert(outUnit);
    1001           0 :                 qErr.convert(outUnit);
    1002           0 :                 break;
    1003             :             }
    1004             :         }
    1005           0 :     }
    1006             :     // some optical images have very weird units that start with numbers
    1007      125718 :     else if (
    1008      250961 :         unit.empty() || Quantity(1, myUnit).isConform(Quantity(1, "m/s"))
    1009      250961 :         || isdigit(unit[0])
    1010             :     ) {
    1011             :         // do nothing
    1012             :     }
    1013             :     else {
    1014       83390 :         Vector<String> unitPrefix(10);
    1015       83390 :         unitPrefix[0] = "T";
    1016       83390 :         unitPrefix[1] = "G";
    1017       83390 :         unitPrefix[2] = "M";
    1018       83390 :         unitPrefix[3] = "k";
    1019       83390 :         unitPrefix[4] = "";
    1020       83390 :         unitPrefix[5] = "m";
    1021       83390 :         unitPrefix[6] = "u";
    1022       83390 :         unitPrefix[7] = "n";
    1023       83390 :         unitPrefix[8] = "p";
    1024       83390 :         unitPrefix[9] = "f";
    1025             : 
    1026      457471 :         for (auto prefix: unitPrefix) {
    1027      456615 :             outUnit = prefix + unit;
    1028      456615 :             if (fabs(qVal.getValue(outUnit)) > 1) {
    1029       82534 :                 qVal.convert(outUnit);
    1030       82534 :                 qErr.convert(outUnit);
    1031       82534 :                 break;
    1032             :             }
    1033      457471 :         }
    1034       83390 :     }
    1035      125718 :     Vector<Double> valErr(2);
    1036      125718 :     valErr[0] = qVal.getValue();
    1037      125718 :     valErr[1] = qErr.getValue();
    1038             : 
    1039      125718 :     uInt precision = precisionForValueErrorPairs(valErr, Vector<Double>());
    1040      125718 :     ostringstream out;
    1041      125718 :     out << std::fixed << setprecision(precision);
    1042      125718 :     out << qVal.getValue();
    1043      125718 :     if (isFixed && qErr.getValue() == 0) {
    1044          12 :         out << " (fixed)";
    1045             :     }
    1046             :     else {
    1047      125706 :         out << " +/- " << qErr.getValue();
    1048             :     }
    1049      125718 :     out << " " << qVal.getUnit();
    1050      377154 :     return out.str();
    1051      125718 : }
    1052             : 
    1053       20887 : String ImageProfileFitterResults::_pcfToString(
    1054             :     const PCFSpectralElement *const &pcf, const CoordinateSystem& csys,
    1055             :     const Vector<Double>& world, const IPosition& imPos,
    1056             :     const Bool showTypeString, const String& indent
    1057             : ) const {
    1058       20887 :     Vector<Double> myWorld = world;
    1059       20887 :     String yUnit = _subImage->units().getName();
    1060       20887 :     ostringstream summary;
    1061       20887 :     Vector<Bool> fixed = pcf->fixed();
    1062       20887 :     if (showTypeString) {
    1063       20812 :         summary << indent << "        Type     : "
    1064       20812 :             << SpectralElement::fromType(pcf->getType()) << endl;
    1065             :     }
    1066       20887 :     summary << indent << "        Peak     : "
    1067       41774 :         << _elementToString(
    1068       20887 :             pcf->getAmpl(), pcf->getAmplErr(), yUnit, fixed[0]
    1069       20887 :         ) << endl;
    1070       20887 :     Double center = _centerWorld(
    1071             :         *pcf, imPos
    1072             :     );
    1073             : 
    1074       20887 :     Double increment = fabs(_fitAxisIncrement());
    1075             : 
    1076       20887 :     Double centerErr = pcf->getCenterErr() * increment;
    1077       20887 :     Double fwhm = pcf->getFWHM() * increment;
    1078       20887 :     Double fwhmErr = pcf->getFWHMErr() * increment;
    1079             : 
    1080       20887 :     Double pCenter = 0;
    1081       20887 :     Double pCenterErr = 0;
    1082       20887 :     Double pFWHM = 0;
    1083       20887 :     Double pFWHMErr = 0;
    1084       20887 :     Int specCoordIndex = csys.findCoordinate(Coordinate::SPECTRAL);
    1085       20887 :     Bool convertedCenterToPix = True;
    1086       20887 :     Bool convertedFWHMToPix = True;
    1087       20887 :     if (_doVelocity) {
    1088       20887 :         if (csys.spectralCoordinate(specCoordIndex).velocityToPixel(pCenter, center)) {
    1089             :             Double nextVel;
    1090       20887 :             csys.spectralCoordinate(specCoordIndex).pixelToVelocity(nextVel, pCenter+1);
    1091       20887 :             Double velInc = fabs(center - nextVel);
    1092       20887 :             pCenterErr = centerErr/velInc;
    1093       20887 :             pFWHM = abs(fwhm/velInc);
    1094       20887 :             pFWHMErr = abs(fwhmErr/velInc);
    1095             :         }
    1096             :         else {
    1097           0 :             convertedCenterToPix = False;
    1098           0 :             convertedFWHMToPix = False;
    1099             :         }
    1100             :     }
    1101             :     else {
    1102           0 :         Vector<Double> pixel(myWorld.size());
    1103           0 :         myWorld[_fitAxis] = center;
    1104           0 :         Double delta = csys.increment()[_fitAxis];
    1105           0 :         if (csys.toPixel(pixel, myWorld)) {
    1106           0 :             pCenter = pixel[_fitAxis];
    1107           0 :             pCenterErr = abs(centerErr/delta);
    1108             :         }
    1109             :         else {
    1110           0 :             convertedCenterToPix = False;
    1111             :         }
    1112           0 :         pFWHM = abs(fwhm/delta);
    1113           0 :         pFWHMErr = abs(fwhmErr/delta);
    1114           0 :     }
    1115       20887 :     summary << indent << "        Center   : "
    1116       20887 :         << _elementToString(
    1117       20887 :             center, centerErr, _xUnit, fixed[1]
    1118       20887 :         ) << endl;
    1119       20887 :     if (convertedCenterToPix) {
    1120       20887 :         summary << indent << "                   "
    1121       41774 :             << _elementToString(
    1122       20887 :                 pCenter, pCenterErr, "pixel", fixed[1]
    1123       20887 :             ) << endl;
    1124             :     }
    1125             :     else {
    1126           0 :         summary << indent << "                  Could not convert world to pixel for center" << endl;
    1127             :     }
    1128       20887 :     summary << indent << "        FWHM     : "
    1129       20887 :         << _elementToString(
    1130       20887 :             fwhm, fwhmErr, _xUnit, fixed[2]
    1131       20887 :         )
    1132       20887 :         << endl;
    1133       20887 :     if (convertedFWHMToPix) {
    1134       20887 :         summary << indent << "                   "
    1135       41774 :             << _elementToString(pFWHM, pFWHMErr, "pixel", fixed[2])
    1136       20887 :             << endl;
    1137             :     }
    1138             :     else {
    1139           0 :         summary << indent << "                  Could not convert FWHM to pixel" << endl;
    1140             :     }
    1141       20887 :     Double integral = pcf->getIntegral()*increment;
    1142       20887 :     Double integralErr = pcf->getIntegralErr()*increment;
    1143       41774 :     String integUnit = (Quantity(1.0 ,yUnit)*Quantity(1.0, _xUnit)).getUnit();
    1144       20887 :     summary << indent << "        Integral : "
    1145       41774 :         << _elementToString(integral, integralErr, integUnit, fixed[0] && fixed[2])
    1146       20887 :         << endl;
    1147       20887 :     if (fwhm/increment <= 3) {
    1148        6914 :         summary << indent << "WARNING: The FWHM is only " << (fwhm/increment)
    1149             :             << " times the channel width. Be aware that instrumental channelization effects"
    1150        6914 :             << " may be important." << endl;
    1151             :     }
    1152       62661 :     return summary.str();
    1153       20887 : }
    1154             : 
    1155          25 : String ImageProfileFitterResults::_gaussianMultipletToString(
    1156             :     const GaussianMultipletSpectralElement& gm,
    1157             :     const CoordinateSystem& csys,
    1158             :     const Vector<Double>& world, const IPosition& imPos
    1159             : ) const {
    1160          25 :     Vector<GaussianSpectralElement> g(gm.getGaussians());
    1161          25 :     ostringstream summary;
    1162          25 :     summary << "        Type     : GAUSSIAN MULTIPLET" << endl;
    1163         100 :     for (uInt i=0; i<g.size(); i++) {
    1164          75 :         summary << "        Results for subcomponent "
    1165          75 :             << i << ":" << endl;
    1166             :         summary
    1167         150 :             << _pcfToString(&g[i], csys, world, imPos, False, "    ")
    1168          75 :             << endl;
    1169             :     }
    1170          75 :     return summary.str();
    1171          25 : }
    1172             : 
    1173           0 : String ImageProfileFitterResults::_polynomialToString(
    1174             :     const PolynomialSpectralElement& poly, const CoordinateSystem& csys,
    1175             :     const Vector<Double>& imPix, const Vector<Double>& world
    1176             : ) const {
    1177           0 :     ostringstream summary;
    1178           0 :     summary << "        Type: POLYNOMIAL" << endl;
    1179           0 :     Vector<Double> parms, errs;
    1180           0 :     poly.get(parms);
    1181           0 :     poly.getError(errs);
    1182           0 :     uInt n = parms.size();
    1183           0 :     for (uInt j=0; j<n; j++) {
    1184           0 :         String unit = _subImage->units().getName();
    1185           0 :         if (j > 0) {
    1186           0 :             if (j == 1) {
    1187           0 :                 unit += "/(pixel)";
    1188             :             }
    1189             :             else {
    1190           0 :                 unit += "/((pixel)" + String::toString(j) + ")";
    1191             :             }
    1192             :         }
    1193           0 :         summary << "         c" << j << " : "
    1194           0 :             << _elementToString(parms[j], errs[j], unit, false) << endl;
    1195           0 :     }
    1196             :     // coefficients in pixel coordinates
    1197             :     Double x0;
    1198           0 :     Double deltaX = _fitAxisIncrement();
    1199           0 :     if (_doVelocity) {
    1200           0 :         csys.spectralCoordinate().pixelToVelocity(x0, 0);
    1201             :     }
    1202             :     else {
    1203           0 :         Vector<Double> p0 = imPix;
    1204           0 :         p0[_fitAxis] = 0;
    1205           0 :         Vector<Double> world0 = world;
    1206           0 :         csys.toWorld(world0, p0);
    1207           0 :         x0 = world0[_fitAxis];
    1208             :        // deltaX = csys.increment()[_fitAxis];
    1209           0 :     }
    1210           0 :     Vector<Double> pCoeff(n, 0);
    1211           0 :     Vector<Double> pCoeffErr(n, 0);
    1212           0 :     for (uInt j=0; j<n; j++) {
    1213           0 :         Double sumsq = 0;
    1214           0 :         for (uInt k=j; k<n; k++) {
    1215           0 :             Double multiplier = Combinatorics::choose(k, k-j)
    1216           0 :                                 * casacore::pow(x0, Float(k - j))
    1217           0 :                                 * casacore::pow(1/deltaX, Float(k));
    1218           0 :             if ((k-j) % 2 == 1) {
    1219           0 :                 multiplier *= -1;
    1220             :             }
    1221           0 :             pCoeff[j] += multiplier * parms[k];
    1222           0 :             Double errCoeff = multiplier * errs[k];
    1223           0 :             sumsq += errCoeff * errCoeff;
    1224             :         }
    1225           0 :         pCoeffErr[j] = casacore::sqrt(sumsq);
    1226           0 :         summary << "         c" << j << " : ";
    1227           0 :         String unit = _subImage->units().getName();
    1228           0 :         if (j > 0 ) {
    1229           0 :             if (j == 1) {
    1230           0 :                 unit += "/(" + _xUnit + ")";
    1231             :             }
    1232             :             else {
    1233           0 :                 unit += "/((" + _xUnit + ")" + String::toString(j) + ")";
    1234             :             }
    1235             :         }
    1236           0 :         summary << _elementToString(pCoeff[j], pCoeffErr[j], unit, false) << endl;
    1237           0 :     }
    1238           0 :     return summary.str();
    1239           0 : }
    1240             : 
    1241             : 
    1242         155 : String ImageProfileFitterResults::_powerLogPolyToString(
    1243             :     const PowerLogPolynomialSpectralElement& plp
    1244             : ) const {
    1245         155 :     ostringstream summary;
    1246         155 :     summary << "    Type         : POWER LOGARITHMIC POLYNOMIAL" << endl;
    1247         155 :     summary << "    Function     : c0*(x/D)**(c1";
    1248         155 :     uInt nElements = plp.get().size();
    1249         155 :     for (uInt i=2; i<nElements; i++) {
    1250           0 :         summary << " + c" << i << "*ln(x/D)";
    1251           0 :         if (i > 2) {
    1252           0 :             summary << "**" << (i - 1);
    1253             :         }
    1254             :     }
    1255         155 :     summary << ")" << endl;
    1256         155 :     summary << "    D            : " << _plpDivisor << endl;
    1257         155 :     Vector<Double> parms = plp.get();
    1258         155 :     Vector<Double> errs = plp.getError();
    1259         155 :     Vector<Bool> fixed = plp.fixed();
    1260             : 
    1261         465 :     for (uInt j=0; j<parms.size(); j++) {
    1262         310 :         summary << "    c" << j << "           : "
    1263         310 :             << _elementToString(parms[j], errs[j], "", fixed[j]) << endl;
    1264             :     }
    1265         465 :     return summary.str();
    1266         155 : }
    1267             : 
    1268          43 : String ImageProfileFitterResults::_logTransPolyToString(
    1269             :     const LogTransformedPolynomialSpectralElement& ltp
    1270             : ) const {
    1271          43 :     ostringstream summary;
    1272          43 :     summary << "    Type         : LOGARITHMIC TRANSFORMED POLYNOMIAL" << endl;
    1273          43 :     summary << "    Function     : ln(y) = c0";
    1274          43 :     uInt nElements = ltp.get().size();
    1275          86 :     for (uInt i=1; i<nElements; i++) {
    1276          43 :         summary << " + c" << i << "*ln(x/D)";
    1277          43 :         if (i > 2) {
    1278           0 :             summary << "**" << i;
    1279             :         }
    1280             :     }
    1281          43 :     summary << ")" << endl;
    1282          43 :     summary << "    D            : " << _plpDivisor << endl;
    1283          43 :     Vector<Double> parms = ltp.get();
    1284          43 :     Vector<Double> errs = ltp.getError();
    1285          43 :     Vector<Bool> fixed = ltp.fixed();
    1286             : 
    1287         129 :     for (uInt j=0; j<parms.size(); j++) {
    1288          86 :         summary << "    c" << j << "           : "
    1289          86 :             << _elementToString(parms[j], errs[j], "", fixed[j]) << endl;
    1290             :     }
    1291         129 :     return summary.str();
    1292          43 : }
    1293             : 
    1294          44 : void ImageProfileFitterResults::_makeSolutionImages(
    1295             :     const String& name, const CoordinateSystem& csys,
    1296             :     const Array<Double>& values, const String& unit,
    1297             :     const Array<Bool>& mask
    1298             : ) {
    1299          44 :     auto valuesShape = values.shape();
    1300          44 :     Vector<Float> dataCopy(values.size());
    1301          44 :     Vector<Double>::const_iterator iter;
    1302             :     // isNaN(Array<Double>&) works, isNaN(Array<Float>&) gives spurious results
    1303          88 :     Array<Bool> nanInfMask = ! (isNaN(values) || isInf(values));
    1304          44 :     Vector<Float>::iterator jiter = dataCopy.begin();
    1305     1139760 :     for (iter=values.begin(); iter!=values.end(); ++iter, ++jiter) {
    1306     1139716 :         *jiter = (Float)*iter;
    1307             :     }
    1308          44 :     auto dc = dataCopy.reform(valuesShape);
    1309          44 :     uInt nImages = valuesShape.last();
    1310          44 :     auto imageShape = valuesShape.getFirst(valuesShape.size() - 1);
    1311          44 :     IPosition start(values.ndim(), 0);
    1312          44 :     IPosition end = valuesShape - 1;
    1313          44 :     end[end.size() - 1] = 0;
    1314         140 :     for (uInt i=0; i<nImages; ++i) {
    1315             :         auto myname = nImages == 0
    1316             :             ? name
    1317         192 :             : name + "_" + String::toString(i);
    1318          96 :         PagedImage<Float> image(imageShape, csys, myname);
    1319          96 :         start[start.size() - 1] = i;
    1320          96 :         end[end.size() - 1] = i;
    1321          96 :         auto imageVals = dc(start, end);
    1322          96 :         Array<Double> doubleValues = values(start, end);
    1323             :         // isNaN(Array<Double>&) works, isNaN(Array<Float>&) gives spurious results
    1324             :         // Its important to use isInf on the float values not the double values
    1325         192 :         Array<Bool> nanInfMask = ! (isNaN(doubleValues) || isInf(imageVals));
    1326             :         // remove the last axis
    1327          96 :         imageVals.removeDegenerate(values.ndim() -1);
    1328          96 :         image.put(imageVals);
    1329          96 :         nanInfMask.removeDegenerate(values.ndim() -1);
    1330          96 :         Bool hasPixMask = ! allTrue(mask);
    1331          96 :         Bool hasNanMask = ! allTrue(nanInfMask);
    1332          96 :         if (hasNanMask || hasPixMask) {
    1333          34 :             Array<Bool> resMask(imageShape);
    1334             :             String maskName = image.makeUniqueRegionName(
    1335          34 :                 String("mask"), 0
    1336          34 :             );
    1337          34 :             image.makeMask(maskName, True, True, False);
    1338          34 :             if (hasPixMask) {
    1339          20 :                 resMask = mask;
    1340          20 :                 if (hasNanMask) {
    1341          20 :                     resMask = resMask && nanInfMask;
    1342             :                 }
    1343             :             }
    1344             :             else {
    1345          14 :                 resMask = nanInfMask;
    1346             :             }
    1347          34 :             (&image.pixelMask())->put(resMask);
    1348          34 :         }
    1349          96 :         image.setUnits(Unit(unit));
    1350          96 :     }
    1351          44 : }
    1352             : 
    1353             : }

Generated by: LCOV version 1.16