LCOV - code coverage report
Current view: top level - imageanalysis/IO - ImageProfileFitterResults.cc (source / functions) Hit Total Coverage
Test: ctest_coverage.info Lines: 0 847 0.0 %
Date: 2023-11-02 14:27:30 Functions: 0 28 0.0 %

          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: aips2-request@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           0 : 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           0 : ) : _logResults(logResults), _multiFit(multiFit),
      64             :     _doVelocity(
      65           0 :         fitAxis == subImage->coordinates().spectralAxisNumber()
      66           0 :         && Quantity(1, xUnit).isConform("m/s")
      67           0 :         && subImage->coordinates().spectralCoordinate().restFrequency() > 0
      68             :     ), _xUnit(xUnit), _summaryHeader(summaryHeader),
      69             :     _nGaussSinglets(nGaussSinglets), _nGaussMultiplets(nGaussMultiplets),
      70             :     _nLorentzSinglets(nLorentzSinglets), _nPLPCoeffs(nPLPCoeffs),_nLTPCoeffs(nLTPCoeffs),
      71             :     _fitters(fitters),
      72             :     _nonPolyEstimates(nonPolyEstimates), _subImage(subImage), _polyOrder(polyOrder),
      73           0 :     _fitAxis(fitAxis), _logfile(logfile), _log(log), _csysIm(csysIm) {}
      74             : 
      75           0 : ImageProfileFitterResults::~ImageProfileFitterResults() {}
      76             : 
      77           0 : Record ImageProfileFitterResults::getResults() const {
      78           0 :     return _results;
      79             : }
      80             : 
      81           0 : void ImageProfileFitterResults::createResults() {
      82           0 :     _setResults();
      83           0 :     _resultsToLog();
      84           0 : }
      85             : 
      86           0 : 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           0 :             NGSOLMATRICES, vector<Array<Double> >(_nGaussMultiplets+_nOthers)
      90           0 :         )
      91           0 :     );
      92           0 :     uInt nSubcomps = 0;
      93           0 :     uInt compCount = 0;
      94           0 :     Double fNAN = casacore::doubleNaN();
      95             : 
      96           0 :     Array<Double> blank;
      97           0 :     IPosition fShape = _fitters->shape();
      98           0 :     for (uInt i=0; i<_nGaussMultiplets + _nOthers; i++) {
      99           0 :         if (i == _gsPlane) {
     100             :             // gaussian singlets go in position 0
     101           0 :             nSubcomps = _nGaussSinglets;
     102             :         }
     103           0 :         else if (i == _lsPlane) {
     104             :             // lorentzian singlets go in position 1
     105           0 :             nSubcomps = _nLorentzSinglets;
     106             :         }
     107             :         else {
     108             :             // gaussian multiplets go in positions 2 to 1 + _nGaussianMultiplets
     109           0 :             while (
     110           0 :                 _nonPolyEstimates[compCount]->getType() != SpectralElement::GMULTIPLET
     111             :             ) {
     112           0 :                 compCount++;
     113             :             }
     114           0 :             nSubcomps = dynamic_cast<const GaussianMultipletSpectralElement*>(
     115           0 :                     _nonPolyEstimates[compCount]
     116           0 :             )->getGaussians().size();
     117           0 :             compCount++;
     118             :         }
     119           0 :         IPosition blankSize(1, nSubcomps);
     120           0 :         blankSize.prepend(fShape);
     121           0 :         blank.resize(blankSize, False);
     122           0 :         blank = fNAN;
     123           0 :         for (uInt k=0; k<NGSOLMATRICES; k++) {
     124           0 :             (*pcfArrays)[k][i] = blank.copy();
     125             :         }
     126             :     }
     127           0 :     return pcfArrays;
     128             : }
     129             : 
     130           0 : vector<String> ImageProfileFitterResults::logSummary(
     131             :     uInt nProfiles, uInt nAttempted, uInt nSucceeded, uInt nConverged, uInt nValid
     132             : ) {
     133           0 :     vector<String> ret;
     134           0 :     *_log << LogOrigin(_class, __func__);
     135           0 :     ostringstream oss;
     136           0 :     oss << "Number of profiles       = " << nProfiles;
     137           0 :     String str = oss.str();
     138           0 :     *_log << LogIO::NORMAL << str << LogIO::POST;
     139           0 :     _writeLogfile(str + "\n", True, False);
     140           0 :     ret.push_back(str);
     141           0 :     oss.str("");
     142           0 :     oss << "Number of fits attempted = " << nAttempted;
     143           0 :     str = oss.str();
     144           0 :     *_log << LogOrigin(_class, __func__);
     145           0 :     *_log << LogIO::NORMAL << str << LogIO::POST;
     146           0 :     _writeLogfile(str + "\n", False, False);
     147           0 :     ret.push_back(str);
     148           0 :     oss.str("");
     149           0 :     oss << "Number succeeded         = " << nSucceeded;
     150           0 :     str = oss.str();
     151           0 :     *_log << LogOrigin(_class, __func__);
     152           0 :     *_log << LogIO::NORMAL << str << LogIO::POST;
     153           0 :     _writeLogfile(str + "\n", False, False);
     154           0 :     ret.push_back(str);
     155           0 :     oss.str("");
     156           0 :     oss << "Number converged         = " << nConverged;
     157           0 :     str = oss.str();
     158           0 :     *_log << LogOrigin(_class, __func__);
     159           0 :     *_log << LogIO::NORMAL << str << LogIO::POST;
     160           0 :     _writeLogfile(str + "\n", False, False);
     161           0 :     ret.push_back(str);
     162           0 :     oss.str("");
     163           0 :     oss << "Number valid             = " << nValid << endl;
     164           0 :     str = oss.str();
     165           0 :     *_log << LogOrigin(_class, __func__);
     166           0 :     *_log << LogIO::NORMAL << str << LogIO::POST;
     167           0 :     _writeLogfile(str + "\n", False, False);
     168           0 :     ret.push_back(str);
     169           0 :     return ret;
     170             : }
     171             : 
     172           0 : Bool ImageProfileFitterResults::_setAxisTypes() {
     173           0 :     const CoordinateSystem& csysSub = _subImage->coordinates();
     174             : 
     175           0 :     const DirectionCoordinate *dcoord = csysSub.hasDirectionCoordinate()
     176           0 :         ? &csysSub.directionCoordinate() : 0;
     177           0 :     String directionType = dcoord ? MDirection::showType(dcoord->directionType()) : "";
     178           0 :     const SpectralCoordinate *spcoord = csysSub.hasSpectralAxis()
     179           0 :         ? &csysSub.spectralCoordinate() : 0;
     180           0 :     const StokesCoordinate *polcoord = csysSub.hasPolarizationCoordinate()
     181           0 :         ? &csysSub.stokesCoordinate() : 0;
     182           0 :     Vector<String> axisNames = csysSub.worldAxisNames();
     183           0 :     for (uInt i=0; i<axisNames.size(); i++) {
     184           0 :         axisNames[i].upcase();
     185             :     }
     186           0 :     Bool hasLat = False;
     187           0 :     Bool hasLong = False;
     188           0 :     _axisTypes = vector<axisType>(axisNames.size(), NAXISTYPES);
     189           0 :     for (uInt i=0; i<axisNames.size(); i++) {
     190           0 :         if ((Int)i != _fitAxis) {
     191           0 :             if (
     192             :                 dcoord
     193           0 :                 && (axisNames[i].startsWith("RIG") || axisNames[i].startsWith("LONG"))
     194             :             ) {
     195           0 :                 _axisTypes[i] = LONGITUDE;
     196           0 :                 hasLat = True;
     197             :             }
     198           0 :             else if (
     199             :                 dcoord
     200           0 :                 && (axisNames[i].startsWith("DEC") || axisNames[i].startsWith("LAT"))
     201             :             ) {
     202           0 :                 _axisTypes[i] = LATITUDE;
     203           0 :                 hasLong = True;
     204             :             }
     205           0 :             else if (
     206             :                 spcoord
     207           0 :                 && (axisNames[i].startsWith("FREQ") || axisNames[i].startsWith("VEL"))
     208             :             ) {
     209           0 :                 _axisTypes[i] = FREQUENCY;
     210             :             }
     211           0 :             else if (
     212             :                 polcoord
     213           0 :                 && axisNames[i].startsWith("STO")
     214             :             ) {
     215           0 :                 _axisTypes[i] = POLARIZATION;
     216             :             }
     217             :         }
     218             :     }
     219           0 :     return hasLat && hasLong;
     220             : }
     221             : 
     222           0 : 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           0 :     IPosition inTileShape = _subImage->niceCursorShape();
     231           0 :     TiledLineStepper stepper (_subImage->shape(), inTileShape, _fitAxis);
     232           0 :     RO_MaskedLatticeIterator<Float> inIter(*_subImage, stepper);
     233           0 :     const CoordinateSystem& csysSub = _subImage->coordinates();
     234           0 :     const DirectionCoordinate *dcoord = csysSub.hasDirectionCoordinate()
     235           0 :         ? &csysSub.directionCoordinate() : 0;
     236           0 :     String directionType = dcoord ? MDirection::showType(dcoord->directionType()) : "";
     237           0 :     const SpectralCoordinate *spcoord = csysSub.hasSpectralAxis()
     238           0 :         ? &csysSub.spectralCoordinate() : 0;
     239           0 :     const StokesCoordinate *polcoord = csysSub.hasPolarizationCoordinate()
     240           0 :         ? &csysSub.stokesCoordinate() : 0;
     241           0 :     IPosition pixel;
     242           0 :     Double increment = fabs(_fitAxisIncrement());
     243           0 :     for (
     244           0 :         inIter.reset();    ! inIter.atEnd(); ++inIter
     245             :     ) {
     246           0 :         IPosition pixel = inIter.position();
     247           0 :         std::shared_ptr<const ProfileFitResults> fitter = (*_fitters)(pixel);
     248           0 :         if (! fitter) {
     249           0 :             continue;
     250             :         }
     251           0 :         attemptedArr(pixel) = fitter->getList().nelements() > 0;
     252           0 :         successArr(pixel) = fitter->succeeded();
     253           0 :         convergedArr(pixel) = attemptedArr(pixel) && successArr(pixel)
     254           0 :             && fitter->converged();
     255           0 :         validArr(pixel) = convergedArr(pixel) && fitter->isValid();
     256           0 :         _doWorldCoords(
     257             :             directionInfo, csysSub, pixel, dcoord, spcoord,
     258             :             polcoord, returnDirection, directionType
     259             :         );
     260           0 :         if (validArr(pixel)) {
     261           0 :             _processSolutions(
     262             :                 typeMat, niterArr, nCompArr, pixel, fitter,
     263             :                 pcfArrays, plpArrays, ltpArrays, increment
     264             :             );
     265             :         }
     266             :     }
     267           0 : }
     268             : 
     269           0 : 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           0 :     niterArr(pixel) = (Int)fitter->getNumberIterations();
     279           0 :     nCompArr(pixel) = (Int)fitter->getList().nelements();
     280           0 :     SpectralList solutions = fitter->getList();
     281           0 :     uInt gCount = 0;
     282           0 :     uInt gmCount = 0;
     283           0 :     uInt lseCount = 0;
     284           0 :     uInt nSolutions = solutions.nelements();
     285           0 :     for (uInt i=0; i<nSolutions; i++) {
     286           0 :         SpectralElement::Types type = solutions[i]->getType();
     287           0 :         IPosition tPos(1, i);
     288           0 :         tPos.prepend(pixel);
     289           0 :         typeMat(tPos) = SpectralElement::fromType(type);
     290           0 :         switch (type) {
     291           0 :         case SpectralElement::POLYNOMIAL:
     292           0 :             break;
     293           0 :         case SpectralElement::GAUSSIAN:
     294             :             // allow fall through because gaussians and lorentzians use common code
     295             :         case SpectralElement::LORENTZIAN:
     296             :         {
     297           0 :             const PCFSpectralElement *pcf = dynamic_cast<
     298             :                 const PCFSpectralElement*
     299           0 :             >(solutions[i]);
     300           0 :             uInt plane = _lsPlane;
     301           0 :             uInt col = lseCount;
     302             :             // if block because we allow case fall through
     303           0 :             if (type == SpectralElement::LORENTZIAN) {
     304           0 :                 lseCount++;
     305             :             }
     306             :             else {
     307           0 :                 plane = _gsPlane;
     308           0 :                 col = gCount;
     309           0 :                 gCount++;
     310             :             }
     311           0 :             _insertPCF(
     312           0 :                 *pcfArrays, pixel, *pcf, plane, col,
     313             :                 increment
     314             :             );
     315             :         }
     316           0 :         break;
     317           0 :         case SpectralElement::GMULTIPLET:
     318             :         {
     319           0 :             const GaussianMultipletSpectralElement *gm = dynamic_cast<
     320             :                 const GaussianMultipletSpectralElement*
     321           0 :             >(solutions[i]);
     322           0 :             const Vector<GaussianSpectralElement> g(gm->getGaussians());
     323           0 :             for (uInt k=0; k<g.size(); k++) {
     324           0 :                 _insertPCF(
     325           0 :                     *pcfArrays, pixel, g[k], gmCount+2,
     326             :                     k, increment
     327             :                 );
     328             :             }
     329           0 :             gmCount++;
     330             :         }
     331           0 :         break;
     332           0 :         case SpectralElement::POWERLOGPOLY:
     333             :         {
     334           0 :             Vector<Double> sols = solutions[i]->get();
     335           0 :             Vector<Double> errs = solutions[i]->getError();
     336           0 :             for (uInt j=0; j<_nPLPCoeffs; j++) {
     337             :                 // here
     338           0 :                 IPosition arrIdx(1, j);
     339           0 :                 arrIdx.prepend(pixel);
     340           0 :                 plpArrays[SPXSOL](arrIdx) = sols[j];
     341           0 :                 plpArrays[SPXERR](arrIdx) = errs[j];
     342             :             }
     343             :         }
     344           0 :         break;
     345           0 :         case SpectralElement::LOGTRANSPOLY:
     346             :         {
     347           0 :             auto sols = solutions[i]->get();
     348           0 :             auto errs = solutions[i]->getError();
     349           0 :             for (uInt j=0; j<_nLTPCoeffs; j++) {
     350           0 :                 IPosition arrIdx(1, j);
     351           0 :                 arrIdx.prepend(pixel);
     352           0 :                 ltpArrays[SPXSOL](arrIdx) = sols[j];
     353           0 :                 ltpArrays[SPXERR](arrIdx) = errs[j];
     354             :             }
     355             :         }
     356           0 :         break;
     357           0 :         default:
     358           0 :             ThrowCc(
     359             :                 "Logic Error: Unhandled Spectral Element type"
     360             :             );
     361             :             break;
     362             :         }
     363             :     }
     364           0 : }
     365             : 
     366           0 : 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           0 :     Vector<Double> world;
     374           0 :     if (csysSub.toWorld(world, pixel)) {
     375           0 :         String latitude, longitude;
     376           0 :         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           0 :             String emptyUnit = "";
     381           0 :             if ((Int)i != _fitAxis) {
     382           0 :                 if (_axisTypes[i] == LONGITUDE) {
     383           0 :                     longitude = dcoord->format(emptyUnit, Coordinate::DEFAULT, world[i], 0, True, True);
     384           0 :                     IPosition x(1, LONGITUDE);
     385           0 :                     x.append(pixel);
     386           0 :                     _worldCoords(x) = longitude;
     387             :                 }
     388           0 :                 else if (_axisTypes[i] == LATITUDE) {
     389           0 :                     latitude = dcoord->format(emptyUnit, Coordinate::DEFAULT, world[i], 1, True, True);
     390           0 :                     IPosition x(1, LATITUDE);
     391           0 :                     x.append(pixel);
     392           0 :                     _worldCoords(x) = latitude;
     393             :                 }
     394           0 :                 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             :                 }
     399           0 :                 else if (_axisTypes[i] == POLARIZATION) {
     400           0 :                     IPosition x(1, POLARIZATION);
     401           0 :                     x.append(pixel);
     402           0 :                     _worldCoords(x) = polcoord->format(emptyUnit, Coordinate::DEFAULT, world[i], 0, True, True);
     403             :                 }
     404             :             }
     405             :         }
     406           0 :         if (returnDirection) {
     407           0 :             directionInfo(pixel) = directionType + " "
     408           0 :                 + longitude + " " + latitude;
     409             :         }
     410             :     }
     411             :     else {
     412           0 :         ThrowCc(
     413             :             "Could not convert pixel to world coordinate: "
     414             :                 + csysSub.errorMessage()
     415             :         );
     416             :     }
     417           0 : }
     418             : 
     419           0 : void ImageProfileFitterResults::_writeLogfile(const String& str, Bool open, Bool close) {
     420           0 :     if (_logfile.get() != 0) {
     421           0 :         _logfile->write(str, open, close);
     422             :     }
     423           0 : }
     424             : 
     425           0 : void ImageProfileFitterResults::_setResults() {
     426           0 :     LogOrigin logOrigin(_class, __func__);
     427           0 :     Double fNAN = casacore::doubleNaN();
     428           0 :     uInt nComps = _nGaussSinglets + _nGaussMultiplets + _nLorentzSinglets;
     429           0 :     if (_polyOrder >= 0) {
     430           0 :         nComps++;
     431             :     }
     432           0 :     if (_nPLPCoeffs > 0) {
     433           0 :         nComps++;
     434             :     }
     435           0 :     if (_nLTPCoeffs > 0) {
     436           0 :         nComps++;
     437             :     }
     438           0 :     IPosition fitterShape = _fitters->shape();
     439           0 :     Array<Bool> attemptedArr(fitterShape, False);
     440           0 :     Array<Bool> successArr(fitterShape, False);
     441           0 :     Array<Bool> convergedArr(fitterShape, False);
     442           0 :     Array<Bool> validArr(fitterShape, False);
     443           0 :     IPosition wcShape(1, (Int)NAXISTYPES);
     444           0 :     wcShape.append(fitterShape);
     445           0 :     _worldCoords = Array<String>(wcShape, String(""));
     446           0 :     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           0 :     std::unique_ptr<vector<vector<Array<Double> > > > pcfArrays = _createPCFArrays();
     455           0 :     IPosition bShape(1, max(_nPLPCoeffs, _nLTPCoeffs));
     456           0 :     bShape.prepend(fitterShape);
     457           0 :     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           0 :     vector<Array<Double> > plpArrays(NSPXSOLMATRICES);
     463           0 :     vector<Array<Double> > ltpArrays(NSPXSOLMATRICES);
     464           0 :     for (uInt i=0; i<NSPXSOLMATRICES; i++) {
     465             :         // because assignmet of Arrays is by reference, which is horribly confusing
     466           0 :         if (_nPLPCoeffs > 0) {
     467           0 :             plpArrays[i] = blank.copy();
     468             :         }
     469           0 :         if (_nLTPCoeffs > 0) {
     470           0 :             ltpArrays[i] = blank.copy();
     471             :         }
     472             :     }
     473           0 :     IPosition typeShape(1, nComps);
     474           0 :     typeShape.prepend(fitterShape);
     475           0 :     Array<String> typeArr(typeShape, String("UNDEF"));
     476           0 :     Array<Int> nCompArr(fitterShape, -1);
     477           0 :     Bool returnDirection = _setAxisTypes();
     478           0 :     Array<String> directionInfo(fitterShape);
     479           0 :     _marshalFitResults(
     480             :         attemptedArr, successArr,
     481             :         convergedArr, validArr,
     482             :         typeArr, niterArr,
     483             :         nCompArr, pcfArrays, plpArrays, ltpArrays,
     484             :         returnDirection, directionInfo
     485             :     );
     486           0 :     String key;
     487           0 :     _results.define("attempted", attemptedArr);
     488           0 :     _results.define(_SUCCEEDED, successArr);
     489           0 :     _results.define(_CONVERGED, convergedArr);
     490           0 :     _results.define(_VALID, validArr);
     491           0 :     _results.define("niter", niterArr);
     492           0 :     _results.define("ncomps", nCompArr);
     493           0 :     if (returnDirection) {
     494           0 :         _results.define("direction", directionInfo);
     495             :     }
     496           0 :     _results.define("xUnit", _xUnit);
     497           0 :     const String yUnit = _subImage->units().getName();
     498           0 :     _results.define("yUnit", yUnit);
     499           0 :     _results.define( "type", typeArr);
     500           0 :     for (uInt i=0; i<_nGaussMultiplets+_nOthers; ++i) {
     501           0 :         if (i == _gsPlane && _nGaussSinglets == 0) {
     502           0 :             continue;
     503             :         }
     504           0 :         else if (i == _lsPlane && _nLorentzSinglets == 0) {
     505           0 :             continue;
     506             :         }
     507           0 :         Record rec;
     508           0 :         rec.define("center", (*pcfArrays)[CENTER][i]);
     509           0 :         rec.define("fwhm", (*pcfArrays)[FWHM][i]);
     510           0 :         rec.define("amp", (*pcfArrays)[AMP][i]);
     511           0 :         rec.define("integral", (*pcfArrays)[INTEGRAL][i]);
     512           0 :         rec.define("centerErr", (*pcfArrays)[CENTERERR][i]);
     513           0 :         rec.define("fwhmErr", (*pcfArrays)[FWHMERR][i]);
     514           0 :         rec.define("ampErr", (*pcfArrays)[AMPERR][i]);
     515           0 :         rec.define("integralErr", (*pcfArrays)[INTEGRALERR][i]);
     516             :         String description = i == _gsPlane
     517             :             ? "Gaussian singlets results"
     518             :             : i == _lsPlane
     519             :               ? "Lorentzian singlets"
     520           0 :               : "Gaussian multiplet number " + String::toString(i-1) + " results";
     521           0 :         rec.define("description", description);
     522           0 :         _results.defineRecord(_getTag(i), rec);
     523             :     }
     524           0 :     if (_nPLPCoeffs > 0) {
     525           0 :         Record rec;
     526           0 :         rec.define("solution", plpArrays[SPXSOL]);
     527           0 :         rec.define("error", plpArrays[SPXERR]);
     528           0 :         _results.defineRecord("plp", rec);
     529             :     }
     530           0 :     if (_nLTPCoeffs > 0) {
     531           0 :         Record rec;
     532           0 :         rec.define("solution", ltpArrays[SPXSOL]);
     533           0 :         rec.define("error", ltpArrays[SPXERR]);
     534           0 :         _results.defineRecord("ltp", rec);
     535             :     }
     536           0 : }
     537             : 
     538           0 : String ImageProfileFitterResults::_getTag(const uInt i) const {
     539             :     return i == _gsPlane
     540             :         ? "gs"
     541             :         : i == _lsPlane
     542             :             ? "ls"
     543           0 :             : "gm" + String::toString(i-2);
     544             : }
     545             : 
     546           0 : 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           0 :     IPosition x = pixel;
     553           0 :     x.append(IPosition(1, col));
     554           0 :     pcfArrays[CENTER][idx](x) = _centerWorld(pcf, pixel);
     555           0 :     pcfArrays[FWHM][idx](x) = pcf.getFWHM() * increment;
     556           0 :     pcfArrays[AMP][idx](x) = pcf.getAmpl();
     557           0 :     pcfArrays[CENTERERR][idx](x) = pcf.getCenterErr() * increment;
     558           0 :     pcfArrays[FWHMERR][idx](x) = pcf.getFWHMErr() * increment;
     559           0 :     pcfArrays[AMPERR][idx](x) = pcf.getAmplErr();
     560           0 :     pcfArrays[INTEGRAL][idx](x) = pcf.getIntegral() * increment;
     561           0 :     pcfArrays[INTEGRALERR][idx](x) = pcf.getIntegralErr() * increment;
     562           0 : }
     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             : }
     580             : 
     581           0 : void ImageProfileFitterResults::writeImages(Bool someConverged) const {
     582             :     Bool writeSolutionImages = (
     583           0 :         ! _centerName.empty() || ! _centerErrName.empty()
     584           0 :         || ! _fwhmName.empty() || ! _fwhmErrName.empty()
     585           0 :         || ! _ampName.empty() || ! _ampErrName.empty()
     586           0 :         || ! _integralName.empty() || ! _integralErrName.empty()
     587           0 :         || ! _plpName.empty() || ! _plpErrName.empty()
     588           0 :         || ! _ltpName.empty() || ! _ltpErrName.empty()
     589           0 :     );
     590           0 :     if (
     591           0 :         ! _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           0 :     if (
     597           0 :         _multiFit && writeSolutionImages
     598             :     ) {
     599           0 :         if (
     600           0 :             _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           0 :             if (someConverged) {
     607           0 :                 IPosition axes(1, _fitAxis);
     608             :                 ImageCollapser<Float> collapser(
     609           0 :                     _subImage, axes, False, ImageCollapserData::ZERO, String(""), False
     610           0 :                 );
     611             :                 std::shared_ptr<TempImage<Float> > tmp = std::dynamic_pointer_cast<TempImage<Float> >(
     612           0 :                     collapser.collapse()
     613           0 :                 );
     614           0 :                 ThrowIf(! tmp, "Unable to perform dynamic cast");
     615           0 :                 std::shared_ptr<TempImage<Float> > myTemplate(tmp);
     616           0 :                 const String yUnit = _subImage->units().getName();
     617           0 :                 Array<Bool>    mask(_fitters->shape(), False);
     618           0 :                 IPosition inTileShape = _subImage->niceCursorShape();
     619           0 :                 TiledLineStepper stepper (_subImage->shape(), inTileShape, _fitAxis);
     620           0 :                 RO_MaskedLatticeIterator<Float> inIter(*_subImage, stepper);
     621           0 :                 for (
     622           0 :                     inIter.reset();    ! inIter.atEnd(); ++inIter
     623             :                 ) {
     624           0 :                     mask(inIter.position()) = anyTrue(inIter.getMask());
     625             :                 }
     626           0 :                 _writeImages(myTemplate->coordinates(), mask, yUnit);
     627             :             }
     628             :             else {
     629           0 :                 *_log << LogIO::WARN << "No solutions converged, solution images will not be written"
     630           0 :                     << LogIO::POST;
     631             :             }
     632             :         }
     633             :     }
     634           0 : }
     635             : 
     636           0 : void ImageProfileFitterResults::_writeImages(
     637             :     const CoordinateSystem& xcsys,
     638             :     const Array<Bool>& mask, const String& yUnit
     639             : ) const {
     640           0 :     map<String, String> mymap;
     641           0 :     map<String, String> unitmap;
     642           0 :     mymap["center"] = _centerName;
     643           0 :     mymap["centerErr"] = _centerErrName;
     644           0 :     mymap["fwhm"] = _fwhmName;
     645           0 :     mymap["fwhmErr"] = _fwhmErrName;
     646           0 :     mymap["amp"] = _ampName;
     647           0 :     mymap["ampErr"] = _ampErrName;
     648           0 :     mymap["integral"] = _integralName;
     649           0 :     mymap["integralErr"] = _integralErrName;
     650           0 :     unitmap["center"] = _xUnit;
     651           0 :     unitmap["centerErr"] = _xUnit;
     652           0 :     unitmap["fwhm"] = _xUnit;
     653           0 :     unitmap["fwhmErr"] = _xUnit;
     654           0 :     unitmap["amp"] = yUnit;
     655           0 :     unitmap["ampErr"] = yUnit;
     656           0 :     unitmap["integral"] = _xUnit + "." + yUnit;
     657           0 :     unitmap["integralErr"] = _xUnit + "." + yUnit;
     658           0 :     for (uInt i=0; i<_nGaussMultiplets+_nOthers; i++) {
     659           0 :         if (i == _gsPlane && _nGaussSinglets == 0) {
     660           0 :             continue;
     661             :         }
     662           0 :         else if (i == _lsPlane && _nLorentzSinglets == 0) {
     663           0 :             continue;
     664             :         }
     665           0 :         String id = _getTag(i);
     666           0 :         for (const auto& p : mymap) {
     667           0 :             String imagename = p.second;
     668             :             String suffix = i == _gsPlane
     669             :                 ? ""
     670             :                 : i == _lsPlane
     671             :                   ? "_ls"
     672           0 :                   : _nGaussMultiplets <= 1
     673             :                     ? "_gm"
     674           0 :                     : "_gm" + String::toString(i-_nOthers);
     675           0 :             imagename += suffix;
     676           0 :             if (! p.second.empty()) {
     677           0 :                 _makeSolutionImages(
     678             :                     imagename, xcsys,
     679           0 :                     _results.asRecord(id).asArrayDouble(p.first),
     680           0 :                     unitmap.find(p.first)->second, mask
     681             :                 );
     682             :             }
     683             :         }
     684             :     }
     685           0 :     if (
     686           0 :         (_nPLPCoeffs > 0 && (! _plpName.empty() || ! _plpErrName.empty()))
     687           0 :         || (_nLTPCoeffs > 0 && (! _ltpName.empty() || ! _ltpErrName.empty()))
     688             :     ) {
     689           0 :         String type = _results.isDefined("plp") ? "plp" : "ltp";
     690           0 :         if (_nPLPCoeffs > 0 && ! _plpName.empty()) {
     691           0 :             _makeSolutionImages(
     692           0 :                 _plpName, xcsys,
     693           0 :                 _results.asRecord("plp").asArrayDouble("solution"),
     694             :                 "", mask
     695             :             );
     696             :         }
     697           0 :         if (_nPLPCoeffs > 0 && ! _plpErrName.empty()) {
     698           0 :             _makeSolutionImages(
     699           0 :                 _plpErrName, xcsys,
     700           0 :                 _results.asRecord("plp").asArrayDouble("error"),
     701             :                 "", mask
     702             :             );
     703             :         }
     704           0 :         if (_nLTPCoeffs > 0 && ! _ltpName.empty()) {
     705           0 :             _makeSolutionImages(
     706           0 :                 _ltpName, xcsys,
     707           0 :                 _results.asRecord("ltp").asArrayDouble("solution"),
     708             :                 "", mask
     709             :             );
     710             :         }
     711           0 :         if (_nLTPCoeffs > 0 && ! _ltpErrName.empty()) {
     712           0 :             _makeSolutionImages(
     713           0 :                 _ltpErrName, xcsys,
     714           0 :                 _results.asRecord("ltp").asArrayDouble("error"),
     715             :                 "", mask
     716             :             );
     717             :         }
     718             :     }
     719           0 : }
     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           0 : Double ImageProfileFitterResults::_fitAxisIncrement() const {
     729           0 :     if (_doVelocity) {
     730           0 :         Vector<Double> pixels(2);
     731           0 :         pixels[0] = 0;
     732           0 :         pixels[1] = 1;
     733           0 :         Vector<Double> velocities(2);
     734           0 :         _subImage->coordinates().spectralCoordinate().pixelToVelocity(
     735             :             velocities, pixels
     736             :         );
     737           0 :         return velocities[1] - velocities[0];
     738             :     }
     739             :     else {
     740           0 :         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             : }
     751             : 
     752           0 : Double ImageProfileFitterResults::_centerWorld(
     753             :     const PCFSpectralElement& solution, const IPosition& imPos
     754             : ) const {
     755           0 :     Vector<Double> pixel(imPos.size());
     756           0 :     for (uInt i=0; i<pixel.size(); i++) {
     757           0 :         pixel[i] = imPos[i];
     758             :     }
     759           0 :     Vector<Double> world(pixel.size());
     760             :     // in pixels here
     761           0 :     pixel[_fitAxis] = solution.getCenter();
     762           0 :     _subImage->coordinates().toWorld(world, pixel);
     763           0 :     if (_doVelocity) {
     764             :         Double velocity;
     765           0 :         _subImage->coordinates().spectralCoordinate().frequencyToVelocity(velocity, world(_fitAxis));
     766           0 :         return velocity;
     767             :     }
     768             :     else {
     769           0 :         return world(_fitAxis);
     770             :     }
     771             : }
     772             : 
     773           0 : void ImageProfileFitterResults::_resultsToLog() {
     774           0 :     if (! _logResults && _logfile.get() == 0) {
     775           0 :         return;
     776             :     }
     777           0 :     ostringstream summary;
     778           0 :     summary << "****** Fit performed at " << Time().toString() << "******" << endl << endl;
     779           0 :     summary << _summaryHeader;
     780           0 :     if (_nPLPCoeffs + _nLTPCoeffs == 0) {
     781           0 :         if (_goodAmpRange.size() == 2) {
     782           0 :             summary << "       --- valid amplitude range: " << _goodAmpRange << endl;
     783             :         }
     784           0 :         if (_goodCenterRange.size() == 2) {
     785           0 :             summary << "       --- valid center range: " << _goodCenterRange << endl;
     786             :         }
     787           0 :         if (_goodFWHMRange.size() == 2) {
     788           0 :             summary << "       --- valid FWHM range: " << _goodFWHMRange << endl;
     789             :         }
     790           0 :         summary << "       --- number of Gaussian singlets: " << _nGaussSinglets << endl;
     791           0 :         summary << "       --- number of Gaussian multiplets: " << _nGaussMultiplets << endl;
     792           0 :         if (_nGaussMultiplets > 0) {
     793           0 :             for (uInt i=0; i<_nGaussMultiplets; i++) {
     794           0 :                 Array<Double> amp = _results.asRecord("gm" + String::toString(i)).asArrayDouble(AMP);
     795           0 :                 uInt n = amp.shape()[amp.ndim()-1];
     796           0 :                 summary << "           --- number of components in Gaussian multiplet "
     797           0 :                     << i << ": " << n << endl;
     798             :             }
     799             :         }
     800           0 :         if (_polyOrder >= 0) {
     801           0 :             summary << "       --- polynomial order:    " << _polyOrder << endl;
     802             :         }
     803             :         else {
     804           0 :             summary << "       --- no polynomial fit " << endl;
     805             :         }
     806             :     }
     807           0 :     if (_multiFit) {
     808           0 :         summary << "       --- Multiple profiles fit, one per pixel over selected region" << endl;
     809             :     }
     810             :     else {
     811           0 :         summary << "       --- One profile fit, averaged over several pixels if necessary" << endl;
     812             :     }
     813           0 :     if (_logResults) {
     814           0 :         *_log << LogIO::NORMAL << summary.str() << LogIO::POST;
     815             :     }
     816           0 :     _writeLogfile(summary.str(), False, False);
     817           0 :     IPosition inTileShape = _subImage->niceCursorShape();
     818           0 :     TiledLineStepper stepper (_subImage->shape(), inTileShape, _fitAxis);
     819           0 :     RO_MaskedLatticeIterator<Float> inIter(*_subImage, stepper);
     820           0 :     CoordinateSystem csysSub = _subImage->coordinates();
     821           0 :     Vector<Double> worldStart;
     822           0 :     ThrowIf(
     823             :         ! csysSub.toWorld(worldStart, inIter.position()),
     824             :         csysSub.errorMessage()
     825             :     );
     826           0 :     Vector<Double> pixStart;
     827           0 :     ThrowIf(
     828             :         ! _csysIm.toPixel(pixStart, worldStart),
     829             :         _csysIm.errorMessage()
     830             :     );
     831           0 :     if (_multiFit) {
     832           0 :         for (uInt i=0; i<pixStart.size(); i++) {
     833           0 :             pixStart[i] = (Int)std::floor( pixStart[i] + 0.5);
     834             :         }
     835             :     }
     836           0 :     Vector<Double> imPix(pixStart.size());
     837           0 :     Vector<Double> world;
     838           0 :     IPosition subimPos;
     839           0 :     SpectralList solutions;
     840           0 :     String axisUnit = _csysIm.worldAxisUnits()[_fitAxis];
     841           0 :     Int pixPrecision = _multiFit ? 0 : 3;
     842           0 :     _pixelPositions.resize( _fitters->size());
     843           0 :     uint index = 0;
     844           0 :     for (
     845           0 :         inIter.reset();
     846           0 :         ! inIter.atEnd();
     847           0 :         inIter++
     848             :     ) {
     849           0 :         subimPos = inIter.position();
     850           0 :         const std::shared_ptr<ProfileFitResults> fitter = (*_fitters)(subimPos);
     851           0 :         if (! fitter) {
     852           0 :             continue;
     853             :         }
     854           0 :         summary.str("");
     855           0 :         Int basePrecision = summary.precision(1);
     856           0 :         summary.precision(basePrecision);
     857           0 :         if (csysSub.toWorld(world, subimPos)) {
     858           0 :             summary << "Fit   :" << endl;
     859           0 :             for (uInt i=0; i<world.size(); i++) {
     860           0 :                 if ((Int)i != _fitAxis) {
     861           0 :                     IPosition x(1, _axisTypes[i]);
     862           0 :                     x.append(subimPos);
     863           0 :                     if (_axisTypes[i] == LONGITUDE) {
     864           0 :                         summary << "    RA           :   "
     865           0 :                             << _worldCoords(x) << endl;
     866             :                     }
     867           0 :                     else if (_axisTypes[i] == LATITUDE) {
     868           0 :                         summary << "    Dec          :  "
     869           0 :                             << _worldCoords(x) << endl;
     870             :                     }
     871           0 :                     else if (_axisTypes[i] == FREQUENCY) {
     872           0 :                         summary << "    Freq         : "
     873           0 :                             << _worldCoords(x) <<  endl;
     874             :                     }
     875           0 :                     else if (_axisTypes[i] == POLARIZATION) {
     876           0 :                         summary << "    Stokes       : " << _worldCoords(x) << endl;
     877             :                     }
     878             :                 }
     879             :             }
     880             :         }
     881             :         else {
     882           0 :             ThrowCc(csysSub.errorMessage());
     883             :         }
     884           0 :         for (uInt i=0; i<pixStart.size(); i++) {
     885           0 :             imPix[i] = pixStart[i] + subimPos[i];
     886             :         }
     887           0 :         _pixelPositions[index] = imPix;
     888           0 :         summary.setf(ios::fixed);
     889           0 :         summary << setprecision(pixPrecision) << "    Pixel        : [";
     890           0 :         for (uInt i=0; i<imPix.size(); i++) {
     891           0 :             if (i == (uInt)_fitAxis) {
     892           0 :                 summary << " *";
     893             :             }
     894             :             else {
     895           0 :                 summary << imPix[i];
     896             :             }
     897           0 :             if (i != imPix.size()-1) {
     898           0 :                 summary << ", ";
     899             :             }
     900             :         }
     901           0 :         summary << "]" << setprecision(basePrecision) << endl;
     902           0 :         summary.unsetf(ios::fixed);
     903           0 :         Bool attempted = fitter->getList().nelements() > 0;
     904             :         summary << "    Attempted    : "
     905           0 :             << (attempted ? "YES" : "NO") << endl;
     906           0 :         if (attempted) {
     907           0 :             String converged = fitter->converged() ? "YES" : "NO";
     908           0 :             summary << "    Converged    : " << converged << endl;
     909           0 :             if (fitter->converged()) {
     910           0 :                 summary << "    Iterations   : " << fitter->getNumberIterations() << endl;
     911           0 :                 String valid = fitter->isValid() ? "YES" : "NO";
     912           0 :                 summary << "    Valid        : " << valid << endl;
     913           0 :                 if (fitter->isValid()) {
     914           0 :                     solutions = fitter->getList();
     915           0 :                     for (uInt i=0; i<solutions.nelements(); i++) {
     916           0 :                         SpectralElement::Types type = solutions[i]->getType();
     917           0 :                         if (solutions.nelements() > 1) {
     918           0 :                             summary << "    Results for component " << i << ":" << endl;
     919             :                         }
     920           0 :                         switch(type) {
     921           0 :                         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           0 :                                     = dynamic_cast<const PCFSpectralElement*>(solutions[i]);
     928           0 :                                 summary << _pcfToString(
     929           0 :                                     pcf, _csysIm, world.copy(), subimPos
     930           0 :                                 );
     931             :                             }
     932           0 :                             break;
     933           0 :                         case SpectralElement::GMULTIPLET:
     934             :                             {
     935             :                                 const GaussianMultipletSpectralElement *gm
     936           0 :                                     = dynamic_cast<const GaussianMultipletSpectralElement*>(solutions[i]);
     937           0 :                                 summary << _gaussianMultipletToString(
     938           0 :                                     *gm, _csysIm, world.copy(), subimPos
     939           0 :                                 );
     940           0 :                                 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           0 :                         case SpectralElement::POWERLOGPOLY:
     950             :                             {
     951             :                                 const PowerLogPolynomialSpectralElement *p
     952           0 :                                     = dynamic_cast<const PowerLogPolynomialSpectralElement*>(solutions[i]);
     953           0 :                                 summary << _powerLogPolyToString(*p);
     954             :                             }
     955           0 :                             break;
     956           0 :                         case SpectralElement::LOGTRANSPOLY:
     957             :                             {
     958             :                                 const LogTransformedPolynomialSpectralElement *p
     959           0 :                                 = dynamic_cast<const LogTransformedPolynomialSpectralElement*>(solutions[i]);
     960           0 :                                 summary << _logTransPolyToString(*p);
     961             :                             }
     962           0 :                             break;
     963           0 :                         default:
     964           0 :                             ThrowCc("Logic Error: Unhandled spectral element type");
     965             :                             break;
     966             :                         }
     967             :                     }
     968             :                 }
     969             :             }
     970             :         }
     971           0 :         if (_logResults) {
     972           0 :             *_log << LogIO::NORMAL << summary.str() << endl << LogIO::POST;
     973             :         }
     974           0 :         _writeLogfile(summary.str(), False, False);
     975             :     }
     976           0 :     if (_logfile.get() != 0) {
     977           0 :         _logfile->close();
     978             :     }
     979             : }
     980             : 
     981           0 : String ImageProfileFitterResults::_elementToString(
     982             :     const Double value, const Double error,
     983             :     const String& unit, Bool isFixed
     984             : ) const {
     985           0 :     Unit myUnit(unit);
     986           0 :     Vector<String> unitPrefix;
     987           0 :     String outUnit;
     988           0 :     Quantity qVal(value, unit);
     989           0 :     Quantity qErr(error, unit);
     990           0 :     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             :     }
    1006             :     // some optical images have very weird units that start with numbers
    1007           0 :     else if (
    1008           0 :         unit.empty() || Quantity(1, myUnit).isConform(Quantity(1, "m/s"))
    1009           0 :         || isdigit(unit[0])
    1010             :     ) {
    1011             :         // do nothing
    1012             :     }
    1013             :     else {
    1014           0 :         Vector<String> unitPrefix(10);
    1015           0 :         unitPrefix[0] = "T";
    1016           0 :         unitPrefix[1] = "G";
    1017           0 :         unitPrefix[2] = "M";
    1018           0 :         unitPrefix[3] = "k";
    1019           0 :         unitPrefix[4] = "";
    1020           0 :         unitPrefix[5] = "m";
    1021           0 :         unitPrefix[6] = "u";
    1022           0 :         unitPrefix[7] = "n";
    1023           0 :         unitPrefix[8] = "p";
    1024           0 :         unitPrefix[9] = "f";
    1025             : 
    1026           0 :         for (auto prefix: unitPrefix) {
    1027           0 :             outUnit = prefix + unit;
    1028           0 :             if (fabs(qVal.getValue(outUnit)) > 1) {
    1029           0 :                 qVal.convert(outUnit);
    1030           0 :                 qErr.convert(outUnit);
    1031           0 :                 break;
    1032             :             }
    1033             :         }
    1034             :     }
    1035           0 :     Vector<Double> valErr(2);
    1036           0 :     valErr[0] = qVal.getValue();
    1037           0 :     valErr[1] = qErr.getValue();
    1038             : 
    1039           0 :     uInt precision = precisionForValueErrorPairs(valErr, Vector<Double>());
    1040           0 :     ostringstream out;
    1041           0 :     out << std::fixed << setprecision(precision);
    1042           0 :     out << qVal.getValue();
    1043           0 :     if (isFixed && qErr.getValue() == 0) {
    1044           0 :         out << " (fixed)";
    1045             :     }
    1046             :     else {
    1047           0 :         out << " +/- " << qErr.getValue();
    1048             :     }
    1049           0 :     out << " " << qVal.getUnit();
    1050           0 :     return out.str();
    1051             : }
    1052             : 
    1053           0 : 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           0 :     Vector<Double> myWorld = world;
    1059           0 :     String yUnit = _subImage->units().getName();
    1060           0 :     ostringstream summary;
    1061           0 :     Vector<Bool> fixed = pcf->fixed();
    1062           0 :     if (showTypeString) {
    1063           0 :         summary << indent << "        Type     : "
    1064           0 :             << SpectralElement::fromType(pcf->getType()) << endl;
    1065             :     }
    1066           0 :     summary << indent << "        Peak     : "
    1067           0 :         << _elementToString(
    1068           0 :             pcf->getAmpl(), pcf->getAmplErr(), yUnit, fixed[0]
    1069           0 :         ) << endl;
    1070           0 :     Double center = _centerWorld(
    1071             :         *pcf, imPos
    1072             :     );
    1073             : 
    1074           0 :     Double increment = fabs(_fitAxisIncrement());
    1075             : 
    1076           0 :     Double centerErr = pcf->getCenterErr() * increment;
    1077           0 :     Double fwhm = pcf->getFWHM() * increment;
    1078           0 :     Double fwhmErr = pcf->getFWHMErr() * increment;
    1079             : 
    1080           0 :     Double pCenter = 0;
    1081           0 :     Double pCenterErr = 0;
    1082           0 :     Double pFWHM = 0;
    1083           0 :     Double pFWHMErr = 0;
    1084           0 :     Int specCoordIndex = csys.findCoordinate(Coordinate::SPECTRAL);
    1085           0 :     Bool convertedCenterToPix = True;
    1086           0 :     Bool convertedFWHMToPix = True;
    1087           0 :     if (_doVelocity) {
    1088           0 :         if (csys.spectralCoordinate(specCoordIndex).velocityToPixel(pCenter, center)) {
    1089             :             Double nextVel;
    1090           0 :             csys.spectralCoordinate(specCoordIndex).pixelToVelocity(nextVel, pCenter+1);
    1091           0 :             Double velInc = fabs(center - nextVel);
    1092           0 :             pCenterErr = centerErr/velInc;
    1093           0 :             pFWHM = abs(fwhm/velInc);
    1094           0 :             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             :     }
    1115           0 :     summary << indent << "        Center   : "
    1116           0 :         << _elementToString(
    1117           0 :             center, centerErr, _xUnit, fixed[1]
    1118           0 :         ) << endl;
    1119           0 :     if (convertedCenterToPix) {
    1120           0 :         summary << indent << "                   "
    1121           0 :             << _elementToString(
    1122           0 :                 pCenter, pCenterErr, "pixel", fixed[1]
    1123           0 :             ) << endl;
    1124             :     }
    1125             :     else {
    1126           0 :         summary << indent << "                  Could not convert world to pixel for center" << endl;
    1127             :     }
    1128           0 :     summary << indent << "        FWHM     : "
    1129           0 :         << _elementToString(
    1130           0 :             fwhm, fwhmErr, _xUnit, fixed[2]
    1131           0 :         )
    1132           0 :         << endl;
    1133           0 :     if (convertedFWHMToPix) {
    1134           0 :         summary << indent << "                   "
    1135           0 :             << _elementToString(pFWHM, pFWHMErr, "pixel", fixed[2])
    1136           0 :             << endl;
    1137             :     }
    1138             :     else {
    1139           0 :         summary << indent << "                  Could not convert FWHM to pixel" << endl;
    1140             :     }
    1141           0 :     Double integral = pcf->getIntegral()*increment;
    1142           0 :     Double integralErr = pcf->getIntegralErr()*increment;
    1143           0 :     String integUnit = (Quantity(1.0 ,yUnit)*Quantity(1.0, _xUnit)).getUnit();
    1144           0 :     summary << indent << "        Integral : "
    1145           0 :         << _elementToString(integral, integralErr, integUnit, fixed[0] && fixed[2])
    1146           0 :         << endl;
    1147           0 :     if (fwhm/increment <= 3) {
    1148           0 :         summary << indent << "WARNING: The FWHM is only " << (fwhm/increment)
    1149             :             << " times the channel width. Be aware that instrumental channelization effects"
    1150           0 :             << " may be important." << endl;
    1151             :     }
    1152           0 :     return summary.str();
    1153             : }
    1154             : 
    1155           0 : String ImageProfileFitterResults::_gaussianMultipletToString(
    1156             :     const GaussianMultipletSpectralElement& gm,
    1157             :     const CoordinateSystem& csys,
    1158             :     const Vector<Double>& world, const IPosition& imPos
    1159             : ) const {
    1160           0 :     Vector<GaussianSpectralElement> g(gm.getGaussians());
    1161           0 :     ostringstream summary;
    1162           0 :     summary << "        Type     : GAUSSIAN MULTIPLET" << endl;
    1163           0 :     for (uInt i=0; i<g.size(); i++) {
    1164           0 :         summary << "        Results for subcomponent "
    1165           0 :             << i << ":" << endl;
    1166             :         summary
    1167           0 :             << _pcfToString(&g[i], csys, world, imPos, False, "    ")
    1168           0 :             << endl;
    1169             :     }
    1170           0 :     return summary.str();
    1171             : }
    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             :     }
    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             :     }
    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             :     }
    1238           0 :     return summary.str();
    1239             : }
    1240             : 
    1241             : 
    1242           0 : String ImageProfileFitterResults::_powerLogPolyToString(
    1243             :     const PowerLogPolynomialSpectralElement& plp
    1244             : ) const {
    1245           0 :     ostringstream summary;
    1246           0 :     summary << "    Type         : POWER LOGARITHMIC POLYNOMIAL" << endl;
    1247           0 :     summary << "    Function     : c0*(x/D)**(c1";
    1248           0 :     uInt nElements = plp.get().size();
    1249           0 :     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           0 :     summary << ")" << endl;
    1256           0 :     summary << "    D            : " << _plpDivisor << endl;
    1257           0 :     Vector<Double> parms = plp.get();
    1258           0 :     Vector<Double> errs = plp.getError();
    1259           0 :     Vector<Bool> fixed = plp.fixed();
    1260             : 
    1261           0 :     for (uInt j=0; j<parms.size(); j++) {
    1262           0 :         summary << "    c" << j << "           : "
    1263           0 :             << _elementToString(parms[j], errs[j], "", fixed[j]) << endl;
    1264             :     }
    1265           0 :     return summary.str();
    1266             : }
    1267             : 
    1268           0 : String ImageProfileFitterResults::_logTransPolyToString(
    1269             :     const LogTransformedPolynomialSpectralElement& ltp
    1270             : ) const {
    1271           0 :     ostringstream summary;
    1272           0 :     summary << "    Type         : LOGARITHMIC TRANSFORMED POLYNOMIAL" << endl;
    1273           0 :     summary << "    Function     : ln(y) = c0";
    1274           0 :     uInt nElements = ltp.get().size();
    1275           0 :     for (uInt i=1; i<nElements; i++) {
    1276           0 :         summary << " + c" << i << "*ln(x/D)";
    1277           0 :         if (i > 2) {
    1278           0 :             summary << "**" << i;
    1279             :         }
    1280             :     }
    1281           0 :     summary << ")" << endl;
    1282           0 :     summary << "    D            : " << _plpDivisor << endl;
    1283           0 :     Vector<Double> parms = ltp.get();
    1284           0 :     Vector<Double> errs = ltp.getError();
    1285           0 :     Vector<Bool> fixed = ltp.fixed();
    1286             : 
    1287           0 :     for (uInt j=0; j<parms.size(); j++) {
    1288           0 :         summary << "    c" << j << "           : "
    1289           0 :             << _elementToString(parms[j], errs[j], "", fixed[j]) << endl;
    1290             :     }
    1291           0 :     return summary.str();
    1292             : }
    1293             : 
    1294           0 : 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           0 :     auto valuesShape = values.shape();
    1300           0 :     Vector<Float> dataCopy(values.size());
    1301           0 :     Vector<Double>::const_iterator iter;
    1302             :     // isNaN(Array<Double>&) works, isNaN(Array<Float>&) gives spurious results
    1303           0 :     Array<Bool> nanInfMask = ! (isNaN(values) || isInf(values));
    1304           0 :     Vector<Float>::iterator jiter = dataCopy.begin();
    1305           0 :     for (iter=values.begin(); iter!=values.end(); ++iter, ++jiter) {
    1306           0 :         *jiter = (Float)*iter;
    1307             :     }
    1308           0 :     auto dc = dataCopy.reform(valuesShape);
    1309           0 :     uInt nImages = valuesShape.last();
    1310           0 :     auto imageShape = valuesShape.getFirst(valuesShape.size() - 1);
    1311           0 :     IPosition start(values.ndim(), 0);
    1312           0 :     IPosition end = valuesShape - 1;
    1313           0 :     end[end.size() - 1] = 0;
    1314           0 :     for (uInt i=0; i<nImages; ++i) {
    1315             :         auto myname = nImages == 0
    1316             :             ? name
    1317           0 :             : name + "_" + String::toString(i);
    1318           0 :         PagedImage<Float> image(imageShape, csys, myname);
    1319           0 :         start[start.size() - 1] = i;
    1320           0 :         end[end.size() - 1] = i;
    1321           0 :         auto imageVals = dc(start, end);
    1322           0 :         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           0 :         Array<Bool> nanInfMask = ! (isNaN(doubleValues) || isInf(imageVals));
    1326             :         // remove the last axis
    1327           0 :         imageVals.removeDegenerate(values.ndim() -1);
    1328           0 :         image.put(imageVals);
    1329           0 :         nanInfMask.removeDegenerate(values.ndim() -1);
    1330           0 :         Bool hasPixMask = ! allTrue(mask);
    1331           0 :         Bool hasNanMask = ! allTrue(nanInfMask);
    1332           0 :         if (hasNanMask || hasPixMask) {
    1333           0 :             Array<Bool> resMask(imageShape);
    1334             :             String maskName = image.makeUniqueRegionName(
    1335           0 :                 String("mask"), 0
    1336           0 :             );
    1337           0 :             image.makeMask(maskName, True, True, False);
    1338           0 :             if (hasPixMask) {
    1339           0 :                 resMask = mask;
    1340           0 :                 if (hasNanMask) {
    1341           0 :                     resMask = resMask && nanInfMask;
    1342             :                 }
    1343             :             }
    1344             :             else {
    1345           0 :                 resMask = nanInfMask;
    1346             :             }
    1347           0 :             (&image.pixelMask())->put(resMask);
    1348             :         }
    1349           0 :         image.setUnits(Unit(unit));
    1350             :     }
    1351           0 : }
    1352             : 
    1353             : }

Generated by: LCOV version 1.16