LCOV - code coverage report
Current view: top level - components/ComponentModels - SkyCompRep.cc (source / functions) Hit Total Coverage
Test: casacpp_coverage.info Lines: 371 511 72.6 %
Date: 2024-12-11 20:54:31 Functions: 29 35 82.9 %

          Line data    Source code
       1             : //# SkyCompRep.cc:  this defines SkyCompRep
       2             : //# Copyright (C) 1996,1997,1998,1999,2000,2001,2002
       3             : //# Associated Universities, Inc. Washington DC, USA.
       4             : //#
       5             : //# This library is free software; you can redistribute it and/or modify it
       6             : //# under the terms of the GNU Library General Public License as published by
       7             : //# the Free Software Foundation; either version 2 of the License, or (at your
       8             : //# option) any later version.
       9             : //#
      10             : //# This library is distributed in the hope that it will be useful, but WITHOUT
      11             : //# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
      12             : //# FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Library General Public
      13             : //# License for more details.
      14             : //#
      15             : //# You should have received a copy of the GNU Library General Public License
      16             : //# along with this library; if not, write to the Free Software Foundation,
      17             : //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA.
      18             : //#
      19             : //# Correspondence concerning AIPS++ should be addressed as follows:
      20             : //#        Internet email: casa-feedback@nrao.edu.
      21             : //#        Postal address: AIPS++ Project Office
      22             : //#                        National Radio Astronomy Observatory
      23             : //#                        520 Edgemont Road
      24             : //#                        Charlottesville, VA 22903-2475 USA
      25             : //#
      26             : //# $Id: SkyCompRep.cc 21292 2012-11-28 14:58:19Z gervandiepen $
      27             : 
      28             : #include <components/ComponentModels/ComponentShape.h>
      29             : #include <components/ComponentModels/ComponentType.h>
      30             : #include <components/ComponentModels/ConstantSpectrum.h>
      31             : #include <components/ComponentModels/PointShape.h>
      32             : #include <casacore/scimath/Mathematics/GaussianBeam.h>
      33             : #include <components/ComponentModels/GaussianShape.h>
      34             : #include <components/ComponentModels/DiskShape.h>
      35             : #include <components/ComponentModels/LimbDarkenedDiskShape.h>
      36             : #include <components/ComponentModels/SkyCompRep.h>
      37             : #include <casacore/casa/Arrays/ArrayMath.h>
      38             : #include <casacore/casa/Arrays/Cube.h>
      39             : #include <casacore/casa/Arrays/Matrix.h>
      40             : #include <casacore/casa/Arrays/MatrixIter.h>
      41             : #include <casacore/casa/Arrays/Vector.h>
      42             : #include <casacore/casa/Containers/Record.h>
      43             : #include <casacore/casa/Containers/RecordFieldId.h>
      44             : #include <casacore/casa/Containers/RecordInterface.h>
      45             : #include <casacore/coordinates/Coordinates/CoordinateSystem.h>
      46             : #include <casacore/coordinates/Coordinates/DirectionCoordinate.h>
      47             : #include <casacore/coordinates/Coordinates/SpectralCoordinate.h>
      48             : #include <casacore/casa/Exceptions/Error.h>
      49             : #include <casacore/casa/Logging/LogIO.h>
      50             : #include <casacore/casa/Logging/LogOrigin.h>
      51             : #include <casacore/casa/BasicMath/Math.h>
      52             : #include <casacore/casa/BasicSL/Complex.h>
      53             : #include <casacore/measures/Measures/MDirection.h>
      54             : #include <casacore/measures/Measures/MFrequency.h>
      55             : #include <casacore/measures/Measures/Stokes.h>
      56             : #include <casacore/casa/Quanta/MVAngle.h>
      57             : #include <casacore/casa/Quanta/Quantum.h>
      58             : #include <casacore/casa/Quanta/Unit.h>
      59             : #include <casacore/casa/Quanta/UnitMap.h>
      60             : #include <casacore/casa/Utilities/Assert.h>
      61             : #include <casacore/casa/Utilities/DataType.h>
      62             : #include <components/ComponentModels/SpectralModel.h>
      63             : #include <iostream>
      64             : 
      65             : using namespace casacore;
      66             : namespace casa { //# NAMESPACE CASA - BEGIN
      67             : 
      68      112106 : SkyCompRep::SkyCompRep() 
      69      112106 :   :itsShapePtr(new PointShape),
      70      112106 :    itsSpectrumPtr(new ConstantSpectrum),
      71      112106 :    itsFlux(),
      72      112106 :    itsLabel(),
      73      224212 :    itsOptParms()
      74             : 
      75             : {
      76      112106 :         AlwaysAssert(ok(), AipsError);
      77             : 
      78      112106 : }
      79             : 
      80          26 : SkyCompRep::SkyCompRep(const ComponentType::Shape& shape)
      81          26 :   :itsShapePtr(ComponentType::construct(shape)),
      82          26 :    itsSpectrumPtr(new ConstantSpectrum),
      83          26 :    itsFlux(),
      84          26 :    itsLabel(),
      85          52 :    itsOptParms()
      86             : {
      87          26 :         AlwaysAssert(ok(), AipsError);
      88          26 : }
      89             : 
      90        1464 : SkyCompRep::SkyCompRep(const ComponentType::Shape& shape,
      91        1464 :                        const ComponentType::SpectralShape& spectrum)
      92        1464 :   :itsShapePtr(ComponentType::construct(shape)),
      93        1464 :    itsSpectrumPtr(ComponentType::construct(spectrum)),
      94        1464 :    itsFlux(),
      95        1464 :    itsLabel(),
      96        2928 :    itsOptParms()
      97             : {
      98        1464 :         AlwaysAssert(ok(), AipsError);
      99        1464 : }
     100             : 
     101       11205 : SkyCompRep::SkyCompRep(const Flux<Double>& flux,
     102             :                        const ComponentShape& shape, 
     103       11205 :                        const SpectralModel& spectrum)
     104       11205 :   :itsShapePtr(shape.clone()),
     105       11205 :    itsSpectrumPtr(spectrum.clone()),
     106       11205 :    itsFlux(flux.copy()),
     107       11205 :    itsLabel(),
     108       22410 :    itsOptParms()
     109             : {
     110       11205 :   AlwaysAssert(ok(), AipsError);
     111       11205 : }
     112             : 
     113           0 : SkyCompRep::SkyCompRep(const SkyCompRep& other) 
     114             :   :SkyCompBase(),
     115           0 :    itsShapePtr(other.itsShapePtr->clone()),
     116           0 :    itsSpectrumPtr(other.itsSpectrumPtr->clone()),
     117           0 :    itsFlux(other.itsFlux.copy()),
     118           0 :    itsLabel(other.itsLabel),
     119           0 :    itsOptParms(other.itsOptParms)
     120             : {
     121           0 :   AlwaysAssert(ok(), AipsError);
     122           0 : }
     123             : 
     124      249600 : SkyCompRep::~SkyCompRep() {}
     125             : 
     126           0 : SkyCompRep& SkyCompRep::operator=(const SkyCompRep& other) {
     127           0 :   if (this != &other) {
     128           0 :     itsShapePtr = other.itsShapePtr->clone();
     129           0 :     itsSpectrumPtr = other.itsSpectrumPtr->clone();
     130           0 :     itsFlux = other.itsFlux.copy();
     131           0 :     itsLabel = other.itsLabel;
     132           0 :     itsOptParms = other.itsOptParms;
     133             :   }
     134           0 :   AlwaysAssert(ok(), AipsError);
     135           0 :   return *this;
     136             : }
     137             : 
     138          58 : const Flux<Double>& SkyCompRep::flux() const {
     139          58 :   return itsFlux;
     140             : }
     141             : 
     142      112363 : Flux<Double>& SkyCompRep::flux() {
     143      112363 :   return itsFlux;
     144             : }
     145             : 
     146          58 : const ComponentShape& SkyCompRep::shape() const {
     147          58 :   DebugAssert(ok(), AipsError);
     148          58 :   return *itsShapePtr;
     149             : }
     150             : 
     151      230059 : ComponentShape& SkyCompRep::shape() {
     152      230059 :   return *itsShapePtr;
     153             : }
     154             : 
     155       18291 : void SkyCompRep::setShape(const ComponentShape& newShape) {
     156       18291 :   itsShapePtr = newShape.clone();
     157       18291 : }
     158             : 
     159       15990 : SpectralModel& SkyCompRep::spectrum() {
     160       15990 :   return *itsSpectrumPtr;
     161             : }
     162             : 
     163           0 : const SpectralModel& SkyCompRep::spectrum() const {
     164           0 :   return *itsSpectrumPtr;
     165             : }
     166             : 
     167       45668 : void SkyCompRep::setSpectrum(const SpectralModel& newSpectrum) {
     168       45668 :   itsSpectrumPtr = newSpectrum.clone();
     169       45668 : }
     170             : 
     171       26361 : String& SkyCompRep::label() {
     172       26361 :   return itsLabel;
     173             : }
     174             : 
     175           0 : const String& SkyCompRep::label() const {
     176           0 :   return itsLabel;
     177             : }
     178             : 
     179       22166 : Vector<Double>& SkyCompRep::optionalParameters() {
     180       22166 :   return itsOptParms;
     181             : }
     182             : 
     183           0 : const Vector<Double>& SkyCompRep::optionalParameters() const {
     184           0 :   return itsOptParms;
     185             : }
     186             : 
     187           0 : Bool SkyCompRep::isPhysical() const {
     188           0 :   Flux<Double> compFlux = flux().copy();
     189           0 :   compFlux.convertPol(ComponentType::STOKES);
     190           0 :   const Vector<DComplex>& iquv = compFlux.value();
     191           0 :   const DComplex& i = iquv(0);
     192           0 :   const DComplex& q = iquv(1);
     193           0 :   const DComplex& u = iquv(2);
     194           0 :   const DComplex& v = iquv(3);
     195           0 :   if (!nearAbs(i.imag(), 0.0) || 
     196           0 :       !nearAbs(q.imag(), 0.0) || 
     197           0 :       !nearAbs(u.imag(), 0.0) || 
     198           0 :       !nearAbs(v.imag(), 0.0) ) {
     199           0 :     return false;
     200             :   }
     201           0 :   if (square(i.real()) < 
     202           0 :       square(q.real()) + square(u.real()) + square(v.real()) ) {
     203           0 :     return false;
     204             :   }
     205           0 :   return true;
     206           0 : }
     207             : 
     208        2491 : Flux<Double> SkyCompRep::sample(const MDirection& direction, 
     209             :                                 const MVAngle& pixelLatSize,
     210             :                                 const MVAngle& pixelLongSize,
     211             :                                 const MFrequency& centerFrequency) const {
     212        2491 :   Double scale = itsShapePtr->sample(direction, pixelLatSize, pixelLongSize);
     213             :   //scale *= itsSpectrumPtr->sample(centerFrequency);
     214        2491 :   Flux<Double> flux = itsFlux.copy();
     215        2491 :   Vector<Double> iquv(4);
     216        2491 :   flux.value(iquv);
     217        2491 :   itsSpectrumPtr->sampleStokes(centerFrequency, iquv);
     218        2491 :   flux.setValue(iquv);
     219        2491 :   flux.convertPol(itsFlux.pol());
     220        2491 :   flux.scaleValue(scale, scale, scale, scale);
     221        4982 :   return flux;
     222        2491 : }
     223             : 
     224        4439 : void SkyCompRep::sample(Cube<Double>& samples, const Unit& reqUnit,
     225             :                         const Vector<MVDirection>& directions, 
     226             :                         const MeasRef<MDirection>& dirRef, 
     227             :                         const MVAngle& pixelLatSize, 
     228             :                         const MVAngle& pixelLongSize, 
     229             :                         const Vector<MVFrequency>& frequencies,
     230             :                         const MeasRef<MFrequency>& freqRef) const {
     231        4439 :   const uInt nDirSamples = directions.nelements();
     232        4439 :   const uInt nFreqSamples = frequencies.nelements();
     233        4439 :   Flux<Double> f = itsFlux.copy();
     234        4439 :   f.convertUnit(reqUnit);
     235        4439 :   Vector<Double> fluxVal(4);
     236        4439 :   f.value(fluxVal);
     237             :   /*const Double i = fluxVal(0);
     238             :   const Double q = fluxVal(1);
     239             :   const Double u = fluxVal(2);
     240             :   const Double v = fluxVal(3);
     241             :   */
     242             : 
     243        4439 :   Vector<Double> dirScales(nDirSamples);
     244        4439 :   itsShapePtr->sample(dirScales, directions, dirRef,
     245             :                       pixelLatSize, pixelLongSize);
     246             : 
     247        4439 :   Matrix<Double> freqIQUV(nFreqSamples, 4);
     248        8878 :   for (uInt i=0; i<nFreqSamples; ++i) {
     249        4439 :     freqIQUV.row(i) = fluxVal.copy();
     250             :   }
     251             : 
     252             :   //itsSpectrumPtr->sample(freqScales, frequencies, freqRef);
     253        4439 :   itsSpectrumPtr->sampleStokes(freqIQUV, frequencies, freqRef); 
     254   135765492 :   for (uInt d=0; d<nDirSamples; ++d) {
     255   135761053 :           const Double thisDirScale = dirScales(d);
     256   135761053 :           if (thisDirScale != 0) {
     257    10993194 :                   for (uInt f=0; f<nFreqSamples; ++f) {
     258    27482985 :                         for (uInt stok=0; stok <4; ++stok){
     259    21986388 :                             samples(stok, d, f) += thisDirScale * freqIQUV(f, stok);
     260             :                         }
     261             :                   }
     262             :           }
     263             :   }
     264        4439 : }
     265             : 
     266     2128905 : Flux<Double> SkyCompRep::visibility(const Vector<Double>& uvw,
     267             :                                     const Double& frequency) const {
     268     2128905 :   Flux<Double> flux = itsFlux.copy();
     269     2128905 :   Double scale = itsShapePtr->visibility(uvw, frequency).real();
     270     4257810 :   MFrequency freq(Quantity(frequency, "Hz"));
     271             :   //scale *= itsSpectrumPtr->sample(freq);
     272     2128905 :   Vector<Double> iquv(4);
     273     2128905 :   flux.value(iquv);
     274     2128905 :   itsSpectrumPtr->sampleStokes(freq, iquv);
     275     2128905 :   flux.setValue(iquv);
     276     2128905 :   flux.convertPol(itsFlux.pol());
     277     2128905 :   flux.scaleValue(scale, scale, scale, scale);
     278     4257810 :   return flux;
     279     2128905 : }
     280             : 
     281       76415 : void SkyCompRep::visibility(Cube<DComplex>& visibilities,
     282             :                             const Matrix<Double>& uvws,
     283             :                             const Vector<Double>& frequencies) const {
     284       76415 :   const uInt nFreq = frequencies.nelements();
     285       76415 :   const uInt nVis = uvws.ncolumn();
     286             : 
     287       76415 :   Vector<Double> uvw(3);
     288             :   //Block<DComplex> flux(4);
     289       76415 :   Vector<Double> iquv(4);
     290       76415 :   Flux<Double> flux=itsFlux.copy();
     291       76415 :   flux.value(iquv);
     292             :     /*for (uInt p = 0; p < 4; p++) {
     293             :     flux[p] = itsFlux.value(p);
     294             :   }
     295             :     */
     296             : 
     297       76415 :   Matrix<Double> fIQUV(frequencies.size(), 4);
     298       76415 :   Vector<MVFrequency> mvFreq(frequencies.size());
     299    24935603 :   for (uInt f = 0; f < nFreq; f++) {
     300    24859188 :     fIQUV.row(f) = iquv.copy();
     301    24859188 :     mvFreq(f)=MVFrequency(frequencies(f));
     302             :   }
     303             :   
     304             :   // It's not clear how we would get the right information down here.
     305             :   // In any event, it's probably not a pressing concern for most cases.
     306             : 
     307             :   //Indeed ...write something complex and make believe that 
     308             :   // that transformation from different frames can happen and let it bite when some 
     309             :   // poor sucker try to use it
     310             :   //At least for now making it that the frequency is expected to be in the frame of 
     311             :   // the component
     312       76415 :   MeasRef<MFrequency> measRef(itsSpectrumPtr->refFrequency().getRef()); 
     313             :   //itsSpectrumPtr->sample(fscale, mvFreq, measRef);
     314       76415 :   itsSpectrumPtr->sampleStokes(fIQUV, mvFreq, measRef);
     315       76415 :   Vector<Flux<Double> > stokesFlux(nFreq);
     316    24935603 :   for (uInt f=0; f < nFreq; ++f){
     317    24859188 :     stokesFlux(f) = Flux<Double>(fIQUV.row(f));
     318    24859188 :     stokesFlux(f).convertPol(itsFlux.pol());
     319             :   }
     320       76415 :   Matrix<DComplex> scales(nVis, nFreq);
     321       76415 :   itsShapePtr->visibility(scales, uvws, frequencies);
     322             :   /*Matrix<DComplex> scales2(nFreq, nVis);
     323             :   for(uInt k=0; k < nFreq; ++k ){
     324             :     for (uInt j=0; j < nVis; ++j){
     325             :       scales2(k,j)=scales(j,k)*fscale(k);
     326             :     }
     327             :   }
     328             :   for (uInt p = 0; p < 4;  ++p) {
     329             :     visibilities.yzPlane(p) = flux[p]*scales2;
     330             :   }
     331             :   */
     332     1215352 :   for (uInt v = 0; v < nVis; ++v) {
     333    92629348 :     for (uInt f=0; f < nFreq; ++f ){
     334   457452055 :       for (uInt p = 0; p < 4;  ++p) {
     335   365961644 :         visibilities(p, f, v)=scales(v, f)*stokesFlux[f].value(p);
     336             :       }
     337             :     }
     338             :   }
     339             : 
     340             : 
     341             :  /*
     342             :   for (uInt v = 0; v < nVis; v++) {
     343             :     uvw=uvws.column(v);
     344             :     Matrix<Complex> plane;
     345             :     plane.reference(visibilities.xyPlane(v));
     346             :     // Scale by the specified frequency behaviour 
     347             :     for (uInt f = 0; f < nFreq; f++) {
     348             :       Double scale = itsShapePtr->visibility(uvw, frequencies(f)).real();
     349             :       scale *= fscale[f];
     350             :       for (uInt p = 0; p < 4; p++) {
     351             :         visibilities(p, f, v) = flux[p] * scale;
     352             :       }
     353             :     }
     354             :   }
     355             :     */
     356             : 
     357             : 
     358       76415 : }
     359             : 
     360       77975 : Bool SkyCompRep::fromRecord(String& errorMessage,
     361             :                             const RecordInterface& record) {
     362             :   {
     363       77975 :     const String fluxString("flux");
     364       77975 :     if (record.isDefined(fluxString)) {
     365       77975 :       const RecordFieldId flux(fluxString);
     366       77975 :       if (record.dataType(flux) != TpRecord) {
     367           0 :         errorMessage += "The 'flux' field must be a record\n";
     368           0 :         return false;
     369             :       }
     370       77975 :       const Record& fluxRec = record.asRecord(flux);
     371       77975 :       if (!itsFlux.fromRecord(errorMessage, fluxRec)) {
     372           0 :         errorMessage += "Problem parsing the 'flux' field\n";
     373           0 :         return false;
     374             :       }
     375       77975 :     } else {
     376           0 :       LogIO logErr(LogOrigin("SkyCompRep", "fromRecord()"));
     377             :       logErr << LogIO::WARN 
     378             :              << "The component does not have a 'flux' field." << endl
     379             :              << "The default is 1.0 Jy in I and 0.0 in Q, U & V"
     380           0 :              << LogIO::POST;
     381           0 :       itsFlux = Flux<Double>(1);
     382           0 :     }
     383       77975 :   }
     384             :   {
     385       77975 :     const String shapeString("shape");
     386       77975 :     if (record.isDefined(shapeString)) {
     387       77975 :       const RecordFieldId shape(shapeString);
     388       77975 :       if (record.dataType(shape) != TpRecord) {
     389           0 :         errorMessage += "\nThe 'shape' field must be a record";
     390           0 :         return false;
     391             :       }      
     392       77975 :       const Record& shapeRec = record.asRecord(shape);
     393             :       const ComponentType::Shape recType = 
     394       77975 :         ComponentShape::getType(errorMessage, shapeRec);
     395       77975 :       if (recType >= ComponentType::UNKNOWN_SHAPE) {
     396           0 :         errorMessage += String("Cannot create a component with a '" +
     397           0 :                                ComponentType::name(recType) + "' shape\n");
     398           0 :         return false;
     399             :       }
     400       77975 :       if (recType != itsShapePtr->type()) {
     401       14822 :         ComponentShape* newShape = ComponentType::construct(recType);
     402       14822 :         AlwaysAssert(newShape != 0, AipsError);
     403       14822 :         setShape(*newShape);
     404       14822 :         delete newShape;
     405             :       }
     406       77975 :       if (!itsShapePtr->fromRecord(errorMessage, shapeRec)) {
     407           0 :         errorMessage += "Problem parsing the 'shape' field\n";
     408           0 :         return false;
     409             :       }
     410       77975 :     } else {
     411           0 :       LogIO logErr(LogOrigin("SkyCompRep", "fromRecord()"));
     412             :       logErr << LogIO::WARN 
     413             :              << "The component does not have a 'shape' field." << endl
     414             :              << "The default is a point component at the J2000 north pole"
     415           0 :              << LogIO::POST;
     416           0 :       const Unit deg("deg");
     417           0 :       itsShapePtr = new PointShape(MDirection(Quantum<Double>(0.0, deg),
     418           0 :                                               Quantum<Double>(90.0, deg),
     419           0 :                                               MDirection::J2000));
     420           0 :     }
     421       77975 :   }
     422             :   {
     423       77975 :     const String spectrumString("spectrum");
     424       77975 :     if (record.isDefined(spectrumString)) {
     425       77975 :       const RecordFieldId spectrum(spectrumString);
     426       77975 :       if (record.dataType(spectrum) != TpRecord) {
     427           0 :         errorMessage += "\nThe 'spectrum' field must be a record";
     428           0 :         return false;
     429             :       }      
     430       77975 :       const Record& spectrumRec = record.asRecord(spectrum);
     431             :       const ComponentType::SpectralShape recType = 
     432       77975 :         SpectralModel::getType(errorMessage, spectrumRec);
     433       77975 :       if (recType >= ComponentType::UNKNOWN_SPECTRAL_SHAPE) {
     434           0 :         errorMessage += String("Cannot create a component with a '" +
     435           0 :                                ComponentType::name(recType) + "' spectrum\n");
     436           0 :         return false;
     437             :       }
     438       77975 :       if (recType != itsSpectrumPtr->type()) {
     439       44824 :         SpectralModel* newSpectrum = ComponentType::construct(recType);
     440       44824 :         AlwaysAssert(newSpectrum != 0, AipsError);
     441       44824 :         setSpectrum(*newSpectrum);
     442       44824 :         delete newSpectrum;
     443             :       }
     444       77975 :       if (!itsSpectrumPtr->fromRecord(errorMessage, spectrumRec)) {
     445           0 :         return false;
     446             :       }
     447       77975 :     } else {
     448           0 :       LogIO logErr(LogOrigin("SkyCompRep", "fromRecord()"));
     449             :       logErr << LogIO::WARN 
     450             :              << "The component does not have a 'spectrum' field." << endl
     451             :              << "The default is a constant spectrum"
     452           0 :              << LogIO::POST;
     453           0 :       itsSpectrumPtr = new ConstantSpectrum;
     454           0 :     }
     455       77975 :   }
     456             :   {
     457       77975 :     const String labelString("label");
     458       77975 :     if (record.isDefined(labelString)) {
     459       77975 :       const RecordFieldId label(labelString);
     460       77975 :       if (record.dataType(label) != TpString) {
     461           0 :         errorMessage += "\nThe 'label' field must be a string";
     462           0 :         return false;
     463             :       }      
     464       77975 :       if (record.shape(label) != IPosition(1,1)) {
     465           0 :         errorMessage += "\nThe 'label' field must have only 1 element";
     466           0 :         return false;
     467             :       } 
     468       77975 :       itsLabel = record.asString(label);
     469       77975 :     }
     470       77975 :   }
     471             :   { 
     472       77975 :     const String optParmString("optionalParameters");
     473       77975 :     if (record.isDefined(optParmString)) {
     474           0 :       const RecordFieldId optionalParameters(optParmString);
     475           0 :       if (record.dataType(optionalParameters) != TpArrayDouble) {
     476           0 :         errorMessage += "\nThe 'optionalParameters' field must be a Double Array";
     477           0 :         return false;
     478             :       } 
     479           0 :       itsOptParms = record.asArrayDouble(optionalParameters);
     480           0 :     }
     481       77975 :   }
     482       77975 :   return true;
     483             : }
     484             : 
     485        7337 : Bool SkyCompRep::toRecord(String& errorMessage, 
     486             :                           RecordInterface& record) const {
     487             :   {
     488        7337 :     Record fluxRec;
     489        7337 :     if (!itsFlux.toRecord(errorMessage, fluxRec)) {
     490           0 :       return false;
     491             :     }
     492        7337 :     record.defineRecord(RecordFieldId("flux"), fluxRec);
     493        7337 :   }
     494             :   {
     495        7337 :     Record shapeRec;
     496        7337 :     if (!itsShapePtr->toRecord(errorMessage, shapeRec)) {
     497           0 :       return false;
     498             :     }
     499        7337 :     record.defineRecord(RecordFieldId("shape"), shapeRec);
     500        7337 :   }
     501             :   {
     502        7337 :     Record spectrumRec;
     503        7337 :     if (!itsSpectrumPtr->toRecord(errorMessage, spectrumRec)) {
     504           0 :       return false;
     505             :     }
     506        7337 :     record.defineRecord(RecordFieldId("spectrum"), spectrumRec);
     507        7337 :   }
     508             :   {
     509        7337 :     Record optParmRec;
     510        7337 :     if (!itsOptParms.empty()) {
     511           0 :       record.define(RecordFieldId("optionalParameters"), itsOptParms);
     512             :     }
     513        7337 :   }
     514        7337 :   record.define(RecordFieldId("label"), itsLabel);
     515        7337 :   return true;
     516             : }
     517             : 
     518      124859 : Bool SkyCompRep::ok() const {
     519      124859 :   if (itsShapePtr.null()) {
     520           0 :     LogIO logErr(LogOrigin("SkyCompRep", "ok()"));
     521             :     logErr << LogIO::SEVERE << "Shape pointer is null"
     522           0 :            << LogIO::POST;
     523           0 :     return false;
     524           0 :   }
     525      124859 :   if (itsShapePtr->ok() == false) {
     526           0 :     LogIO logErr(LogOrigin("SkyCompRep", "ok()"));
     527             :     logErr << LogIO::SEVERE << "The component shape is not ok"
     528           0 :            << LogIO::POST;
     529           0 :     return false;
     530           0 :   }
     531      124859 :   if (itsSpectrumPtr.null()) {
     532           0 :     LogIO logErr(LogOrigin("SkyCompRep", "ok()"));
     533             :     logErr << LogIO::SEVERE << "Spectrum pointer is null"
     534           0 :            << LogIO::POST;
     535           0 :     return false;
     536           0 :   }
     537      124859 :   if (itsSpectrumPtr->ok() == false) {
     538           0 :     LogIO logErr(LogOrigin("SkyCompRep", "ok()"));
     539             :     logErr << LogIO::SEVERE << "The component spectrum is not ok"
     540           0 :            << LogIO::POST;
     541           0 :     return false;
     542           0 :   }
     543      124859 :   return true;
     544             : }
     545             : 
     546             : 
     547             : 
     548         393 : void SkyCompRep::fromPixel (
     549             :                 Double& facToJy, const Vector<Double>& parameters,
     550             :                 const Unit& brightnessUnitIn,
     551             :         const GaussianBeam& restoringBeam,
     552             :         const CoordinateSystem& cSys,
     553             :         ComponentType::Shape componentShape,
     554             :         Stokes::StokesTypes stokes
     555             : ) {
     556             : // 
     557             : // pars(0) = Flux    Jy
     558             : // pars(1) = x cen   abs pix
     559             : // pars(2) = y cen   abs pix
     560             : // pars(3) = major   pix
     561             : // pars(4) = minor   pix
     562             : // pars(5) = pa radians
     563             : //
     564         786 :    LogIO os(LogOrigin("SkyCompRep", "fromPixel()"));
     565             :       
     566         393 :    ThrowIf(
     567             :                    ! cSys.hasDirectionCoordinate(),
     568             :                    "CoordinateSystem does not contain a DirectionCoordinate"
     569             :    );
     570         393 :    const DirectionCoordinate& dirCoord = cSys.directionCoordinate();
     571             : //
     572             : // We need to find the ratio that converts the input peak brightness
     573             : // from whatevers/per whatever to Jy per whatever.  E.g. mJy/beam to Jy/beam.  
     574             : // This ratio is passed back (for scaling errors) and is needed regardless of 
     575             : // the component type.  
     576             : 
     577         393 :    facToJy = SkyCompRep::convertToJy (brightnessUnitIn);
     578             : 
     579             : // Now proceed with type dependent conversions
     580         393 :    if (componentShape==ComponentType::POINT) {
     581           0 :       ThrowIf(
     582             :                   parameters.nelements() != 3,
     583             :           "Wrong number of parameters for Point shape"
     584             :        );
     585           0 :       Vector<Double> pars(2);
     586           0 :       pars(0) = parameters(1);
     587           0 :       pars(1) = parameters(2);
     588           0 :       PointShape pointShape;
     589           0 :       pointShape.fromPixel(pars, dirCoord);
     590           0 :       setShape(pointShape);
     591             : //
     592           0 :       Quantum<Double> value(parameters(0)*facToJy, Unit("Jy"));
     593           0 :       itsFlux.setUnit(Unit("Jy"));
     594           0 :       itsFlux.setValue (value, stokes);
     595         393 :    } else if (componentShape==ComponentType::GAUSSIAN || componentShape==ComponentType::DISK) {
     596         393 :       ThrowIf(
     597             :          parameters.nelements() != 6,
     598             :          "Wrong number of parameters for Gaussian or Point shape"
     599             :      );
     600             : 
     601             : // Do x,y,major,minor,pa
     602         393 :       Vector<Double> pars(5);
     603        2358 :       for (uInt i=0; i<5; i++) {
     604        1965 :           pars(i) = parameters(i+1);
     605             :       }
     606         393 :       Quantum<Double> majorAxis, minorAxis, pa;
     607         393 :       if (componentShape==ComponentType::GAUSSIAN) {
     608         393 :          GaussianShape shp;
     609         393 :          shp.fromPixel (pars, dirCoord);
     610         393 :          setShape(shp);
     611         393 :          majorAxis = shp.majorAxis();
     612         393 :          minorAxis = shp.minorAxis();
     613         393 :          pa = shp.positionAngle();
     614         393 :       } else {
     615           0 :          DiskShape shp;
     616           0 :          shp.fromPixel (pars, dirCoord);
     617           0 :          setShape(shp);
     618           0 :          majorAxis = shp.majorAxis();
     619           0 :          minorAxis = shp.minorAxis();
     620           0 :          pa = shp.positionAngle();
     621           0 :       }
     622         393 :       Quantum<Double> peakFlux(parameters(0), brightnessUnitIn);
     623             :       Quantum<Double> integralFlux = 
     624             :            SkyCompRep::peakToIntegralFlux (dirCoord, componentShape, peakFlux,
     625         393 :                                            majorAxis, minorAxis, restoringBeam);
     626         393 :       itsFlux.setUnit(integralFlux.getFullUnit());   
     627         393 :       itsFlux.setValue (integralFlux, stokes);
     628         393 :    }
     629             : 
     630             : 
     631             : // Spectrum; assumed constant !
     632         393 :    ConstantSpectrum constSpec;
     633         393 :    if (cSys.hasSpectralAxis()) {
     634         214 :          SpectralCoordinate specCoord = cSys.spectralCoordinate();
     635         214 :          MFrequency mFreq;
     636         214 :          ThrowIf(
     637             :             ! specCoord.toWorld(mFreq, 0.0),
     638             :             "SpectralCoordinate conversion failed because "
     639             :                + specCoord.errorMessage()
     640             :             );
     641         214 :             constSpec.setRefFrequency(mFreq);
     642         214 :       }
     643         393 :    setSpectrum(constSpec);
     644         393 : }
     645             : 
     646          58 : Vector<Double> SkyCompRep::toPixel (const Unit& brightnessUnitOut,
     647             :                                     const GaussianBeam& restoringBeam,
     648             :                                     const CoordinateSystem& cSys,
     649             :                                     Stokes::StokesTypes stokes) const
     650             : //  
     651             : // pars(0) = FLux     Jy
     652             : // pars(1) = x cen    abs pix
     653             : // pars(2) = y cen    abs pix
     654             : // pars(3) = major    pix
     655             : // pars(4) = minor    pix
     656             : // pars(5) = pa radians
     657             : //
     658             : {
     659         116 :    LogIO os(LogOrigin("SkyCompRep", "toPixel()"));
     660             : 
     661             : // Find DirectionCoordinate
     662             :   
     663          58 :    Int dirCoordinate = cSys.findCoordinate(Coordinate::DIRECTION);
     664          58 :    if (dirCoordinate==-1) {
     665           0 :       os << "CoordinateSystem does not contain a DirectionCoordinate" << LogIO::EXCEPTION;
     666             :    } 
     667          58 :    const DirectionCoordinate& dirCoord = cSys.directionCoordinate(dirCoordinate);
     668             :  
     669             : // Do x,y, and possibly major,minor,pa (disk/gaussian)
     670             : 
     671          58 :    const ComponentShape& componentShape = shape();
     672          58 :    Vector<Double> pars = componentShape.toPixel(dirCoord);
     673          58 :    Vector<Double> parameters(1+pars.nelements());
     674         348 :    for (uInt i=0; i<pars.nelements(); i++) parameters[i+1] = pars[i];
     675             : 
     676             : // Now do Flux
     677             : 
     678          58 :    ComponentType::Shape type = componentShape.type();
     679          58 :    if (type==ComponentType::POINT) {
     680           0 :       Flux<Double> f = flux();
     681           0 :       Quantum<Double> fluxPeak = f.value (stokes, true);
     682           0 :       parameters(0) = fluxPeak.getValue();                    // Jy
     683          58 :    } else if (type==ComponentType::GAUSSIAN || type==ComponentType::DISK) {
     684             :       
     685             : // Define /beam and /pixel units.
     686             : 
     687          58 :       Bool integralInJy = true;
     688             :       Unit brightnessUnits = defineBrightnessUnits(os, brightnessUnitOut, dirCoord,
     689          58 :                                                                restoringBeam, integralInJy);
     690             :                                               
     691             : // Get Flux (integral) for particular Stokes. 
     692             : 
     693          58 :       Flux<Double> f = flux();
     694          58 :       Quantum<Double> fluxIntegral = f.value (stokes, true);
     695             :    
     696             : // Find peak value. Because we have defined /beam and /pixel units
     697             : // above we can use Quanta mathematics to get the answer we want
     698             : 
     699             :       Double fac;
     700          58 :       if (type==ComponentType::GAUSSIAN) { 
     701          58 :          fac = C::pi / 4.0 / log(2.0);
     702           0 :       } else if (type==ComponentType::DISK) {
     703           0 :          fac = C::pi;
     704             :       } else {
     705           0 :          fac = 1.0;
     706             :       }
     707             : //
     708          58 :       const TwoSidedShape& ts = dynamic_cast<const TwoSidedShape&>(componentShape);
     709          58 :       Quantum<Double> major2 = ts.majorAxis();
     710          58 :       major2.convert(Unit("rad"));
     711          58 :       Quantum<Double> minor2 = ts.minorAxis();
     712          58 :       minor2.convert(Unit("rad"));
     713             : //
     714          58 :       Quantum<Double> tmp = major2 * minor2;
     715          58 :       tmp.scale(fac);
     716          58 :       Quantum<Double> fluxPeak = fluxIntegral / tmp;           // /beam or /pixel units divided out here
     717          58 :       fluxPeak.convert(brightnessUnits);
     718          58 :       parameters(0) = fluxPeak.getValue();
     719             :       
     720             : // Undefine /beam and /pixel units
     721             : 
     722          58 :       SkyCompRep::undefineBrightnessUnits();
     723          58 :    }
     724         116 :    return parameters;
     725          58 : }   
     726             :    
     727         844 : Unit SkyCompRep::defineBrightnessUnits (
     728             :         LogIO& os, const Unit& brightnessUnitIn,
     729             :         const DirectionCoordinate& dirCoord,
     730             :         const GaussianBeam& restoringBeam,
     731             :         const Bool integralIsJy
     732             : ) {
     733             :         // Define "pixel" units
     734         844 :         Vector<String> units(2);
     735         844 :         units.set("rad");
     736         844 :         DirectionCoordinate dirCoord2 = dirCoord;
     737         844 :         dirCoord2.setWorldAxisUnits(units);
     738         844 :         Vector<Double> inc = dirCoord2.increment();
     739             : 
     740             :         // Define "beam" units if needed
     741             :         // ugh this gets confusing because of the static nature of UnitMap. In some
     742             :         // cases elsewhere beam and pixel are dimensionless
     743         844 :         if (brightnessUnitIn.getName().contains("beam")) {
     744         318 :                 if (! restoringBeam.isNull()) {
     745             :                         // GaussianBeam rB = restoringBeam;
     746             :                         // Double fac = C::pi / 4.0 / log(2.0) * rB.getMajor().getValue(Unit("rad")) * rB.getMinor().getValue(Unit("rad"));
     747         318 :                         Double fac = restoringBeam.getArea("rad2");
     748         318 :             UnitMap::putUser("beam", UnitVal(fac,String("rad2")));
     749             :                 }
     750             :                 else {
     751           0 :                         throw AipsError("No beam defined even though the image brightness units are " + brightnessUnitIn.getName());
     752             :                 }
     753             :         }
     754         844 :         UnitMap::putUser("pixel", UnitVal(abs(inc(0)*inc(1)), String("rad2")));
     755             : 
     756             :         // We must tell the old unit that it has been redefined
     757             : 
     758             :         // The need to do things this way rather than a simple
     759             :         // Unit unitOut = brightnessUnit is unclear to me but it is necessary,
     760             :         // at least it keeps the unit tests passing.
     761             :         // dmehring 04may2012
     762         844 :         Unit unitOut(brightnessUnitIn.getName());
     763             : 
     764             :         // Check integral units
     765             : 
     766         844 :         if (integralIsJy) {
     767         828 :                 if (unitOut.empty()) {
     768             :                         os << LogIO::WARN << "There are no image brightness units, assuming Jy/pixel"
     769         364 :                                 << LogIO::POST;
     770         364 :                         unitOut = Unit("Jy/pixel");
     771             :                 }
     772             :                 else {
     773         464 :                         Quantity t0(1.0, unitOut);
     774         464 :                         Quantity t1(1.0, Unit("rad2"));
     775         464 :                         Quantity t2 = t0 * t1;
     776         464 :                         if (!t2.isConform(Unit("Jy"))) {
     777           8 :                                 os << LogIO::WARN << "The image units '" << unitOut.getName() << "' are not consistent " << endl;
     778           8 :                                 os << "with Jy when integrated over the sky.  Assuming Jy/pixel" << LogIO::POST;
     779           8 :                                 unitOut = Unit("Jy/pixel");
     780             :                         }
     781         464 :                 }
     782             :         }
     783        1688 :         return unitOut;
     784         844 : }  
     785             : 
     786        1630 : void SkyCompRep::undefineBrightnessUnits()
     787             : {          
     788        1630 :    UnitMap::removeUser("beam");
     789        1630 :    UnitMap::removeUser("pixel");
     790        1630 :    UnitMap::clearCache();
     791        1630 : }          
     792             : 
     793         393 : Quantity SkyCompRep::peakToIntegralFlux (
     794             :         const DirectionCoordinate& dirCoord,
     795             :         const ComponentType::Shape componentShape,
     796             :         const Quantum<Double>& peakFlux,
     797             :         const Quantum<Double>& majorAxis,
     798             :         const Quantum<Double>& minorAxis,
     799             :         const GaussianBeam& restoringBeam) {
     800         786 :         LogIO os(LogOrigin("SkyCompRep", "peakToIntegralFlux()"));
     801             :       
     802             :         // Define /beam and /pixel units.
     803             : 
     804         393 :         Unit unitIn = peakFlux.getFullUnit();
     805         393 :         String unitName = unitIn.getName();
     806         393 :         Bool integralIsJy = ! (unitName == "Jy/beam.km/s" || unitName == "K");
     807             : 
     808             :         Unit brightnessUnit = SkyCompRep::defineBrightnessUnits(
     809             :                 os, unitIn, dirCoord, restoringBeam, integralIsJy
     810         393 :         );
     811             :         // Scale to integrated
     812             : 
     813         393 :         Quantity tmp(peakFlux.getValue(), brightnessUnit);
     814         393 :         if (componentShape==ComponentType::GAUSSIAN) {
     815         393 :                 tmp.scale(C::pi / 4.0 / log(2.0));
     816             :         }
     817           0 :         else if (componentShape==ComponentType::DISK) {
     818           0 :                 tmp.scale(C::pi);
     819             :         }
     820             :         else {
     821           0 :                 SkyCompRep::undefineBrightnessUnits();
     822           0 :                 os << "Unrecognized shape for flux density conversion" << LogIO::EXCEPTION;
     823             :         }
     824         393 :         Quantity fluxIntegral;
     825         393 :         Quantity majorAxis2 = majorAxis;
     826         393 :         Quantity minorAxis2 = minorAxis;
     827         393 :         majorAxis2.convert(Unit("rad"));
     828         393 :         minorAxis2.convert(Unit("rad"));
     829         393 :         fluxIntegral = tmp * majorAxis2 * minorAxis2;
     830         393 :         if (fluxIntegral.isConform(Unit("Jy"))) {
     831         385 :                 fluxIntegral.convert("Jy");
     832             :         }
     833           8 :         else if (fluxIntegral.isConform(Unit("Jy.m/s"))) {
     834           4 :                 fluxIntegral.convert("Jy.km/s");
     835             :         }
     836           4 :         else if (fluxIntegral.isConform(Unit("K.rad2"))) {
     837             :             // do nothing, units are OK as is
     838             :     }
     839             :         else {
     840             :                 os << LogIO::WARN << "Cannot convert units of Flux integral to Jy - will assume Jy"
     841           0 :                         << LogIO::POST;
     842           0 :                 fluxIntegral.setUnit(Unit("Jy"));
     843             :         }
     844         393 :         SkyCompRep::undefineBrightnessUnits();
     845         786 :         return fluxIntegral;
     846         393 : }
     847             : 
     848         393 : Quantity SkyCompRep::integralToPeakFlux (
     849             :         const DirectionCoordinate& dirCoord,
     850             :         const ComponentType::Shape componentShape,
     851             :         const Quantity& integralFlux,
     852             :         const Unit& brightnessUnit,
     853             :         const Quantity& majorAxis,
     854             :         const Quantity& minorAxis,
     855             :         const GaussianBeam& restoringBeam
     856             : ) {
     857         786 :         LogIO os(LogOrigin("SkyCompRep", "integralToPeakFlux()"));
     858         393 :         Quantity tmp(integralFlux);
     859         393 :         if (tmp.isConform(Unit("Jy"))) {
     860         385 :                 tmp.convert("Jy");
     861             :         }
     862           8 :         else if (tmp.isConform(Unit("Jy.m/s"))) {
     863           4 :                 tmp.convert("Jy.km/s");
     864             :         }
     865           4 :         else if (tmp.isConform(Unit("K.rad2"))) {
     866             :             // do nothing units are OK as is
     867             :     }
     868             :         else {
     869           0 :                 os << "Cannot convert units of Flux integral (" + integralFlux.getUnit() + ") to Jy"
     870           0 :                         << LogIO::EXCEPTION;
     871             :         }
     872             :         // Define /beam and /pixel units.
     873             : 
     874         393 :         String unitName = brightnessUnit.getName();
     875         393 :         Bool integralIsJy = ! (unitName == "Jy/beam.km/s" || unitName == "K");
     876             :         Unit unitIn = SkyCompRep::defineBrightnessUnits(
     877             :                 os, brightnessUnit, dirCoord,
     878             :                 restoringBeam, integralIsJy
     879         393 :         );
     880             : 
     881             :         // Convert integral to Jy
     882             : 
     883             :         // Now scale
     884             : 
     885         393 :         if (componentShape==ComponentType::GAUSSIAN) {
     886         393 :                 tmp.scale(4.0 * log(2.0) / C::pi);
     887             :         }
     888           0 :         else if (componentShape==ComponentType::DISK) {
     889           0 :                 tmp.scale(1.0 / C::pi);
     890             :         }
     891             :         else {
     892           0 :                 SkyCompRep::undefineBrightnessUnits();
     893           0 :                 os << "Unrecognized shape for flux density conversion" << LogIO::EXCEPTION;
     894             :         }
     895             : 
     896             :         // And divide out shape
     897             : 
     898         393 :         Quantity peakFlux;
     899         393 :         Quantity majorAxis2(majorAxis);
     900         393 :         Quantity minorAxis2(minorAxis);
     901         393 :         majorAxis2.convert(Unit("rad"));
     902         393 :         minorAxis2.convert(Unit("rad"));
     903         393 :         peakFlux = tmp / majorAxis2 / minorAxis2;
     904         393 :         peakFlux.convert(unitIn);
     905         393 :         SkyCompRep::undefineBrightnessUnits();
     906         786 :         return peakFlux;
     907         393 : }
     908             : 
     909         393 : Double SkyCompRep::convertToJy (const Unit& brightnessUnit)
     910             : {
     911         786 :    LogIO os(LogOrigin("SkyCompRep", __FUNCTION__));
     912             : // We need to find the ratio that converts the input peak brightness
     913             : // from whatevers/per whatever to Jy per whatever.  E.g. mJy/beam
     914             : // to Jy/beam.  This ratio is passed back (for scaling errors) and
     915             : // is needed regardless of the component type.  So we start by
     916             : // Defining /beam and /pixel units to be dimensionless
     917         393 :    Unit unitIn = brightnessUnit;
     918             :    // This is not thread safe, but in general anything
     919             :    // that relies on UnitMap is not because of UnitMap's
     920             :    // static nature
     921         393 :    SkyCompRep::undefineBrightnessUnits();
     922             : 
     923         393 :    UnitMap::putUser("pixel", UnitVal(1.0,String("")));
     924         393 :    UnitMap::putUser("beam",  UnitVal(1.0,String("")));
     925         393 :    unitIn = Unit(unitIn.getName());                // Tell system to update this unit
     926         393 :    Quantum<Double> tmp(1.0, unitIn);
     927         393 :    Double facToJy = 1.0;
     928         393 :    if (tmp.isConform(Unit("Jy"))) {
     929         199 :       Quantum<Double> tmp2(tmp);
     930         199 :       tmp2.convert("Jy");   
     931         199 :       facToJy = tmp2.getValue() / tmp.getValue();
     932         199 :    }
     933         194 :    else if (tmp.isConform(Unit("Jy.m/s"))) {
     934           4 :            Quantum<Double> tmp2(tmp);
     935           4 :            tmp2.convert("Jy.km/s");
     936           4 :            facToJy = tmp2.getValue() / tmp.getValue();
     937           4 :    }
     938         190 :    else if (tmp.isConform(Unit("K"))) {
     939           4 :            Quantum<Double> tmp2(tmp);
     940           4 :            tmp2.convert("K");
     941           4 :            facToJy = tmp2.getValue() / tmp.getValue();
     942           4 :    }
     943             :    else {
     944             :       os << LogIO::WARN << "Cannot convert units of brightness to Jy - will assume Jy"
     945         186 :          << LogIO::POST;
     946         186 :       facToJy = 1.0;
     947             :    }
     948             :       
     949             : // Undefine /beam and /pixel
     950             : 
     951         393 :    SkyCompRep::undefineBrightnessUnits();
     952         393 :    return facToJy;
     953         393 : }
     954             : 
     955             : } //# NAMESPACE CASA - END
     956             : 

Generated by: LCOV version 1.16