LCOV - code coverage report
Current view: top level - imageanalysis/ImageAnalysis - CasaImageBeamSet.cc (source / functions) Hit Total Coverage
Test: casacpp_coverage.info Lines: 99 128 77.3 %
Date: 2024-12-11 20:54:31 Functions: 5 11 45.5 %

          Line data    Source code
       1             : //# Copyright (C) 1995,1997,1998,1999,2000,2001,2002,2003
       2             : //# Associated Universities, Inc. Washington DC, USA.
       3             : //#
       4             : //# This library is free software; you can redistribute it and/or modify it
       5             : //# under the terms of the GNU Library General Public License as published by
       6             : //# the Free Software Foundation; either version 2 of the License, or (at your
       7             : //# option) any later version.
       8             : //#
       9             : //# This library 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 Library General Public
      12             : //# License for more details.
      13             : //#
      14             : //# You should have received a copy of the GNU Library General Public License
      15             : //# along with this library; if not, write to the Free Software Foundation,
      16             : //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA.
      17             : //#
      18             : //# Correspondence concerning AIPS++ should be addressed as follows:
      19             : //#        Internet email: casa-feedback@nrao.edu.
      20             : //#        Postal address: AIPS++ Project Office
      21             : //#                        National Radio Astronomy Observatory
      22             : //#                        520 Edgemont Road
      23             : //#                        Charlottesville, VA 22903-2475 USA
      24             : //#
      25             : 
      26             : #include <components/ComponentModels/GaussianDeconvolver.h>
      27             : #include <imageanalysis/ImageAnalysis/CasaImageBeamSet.h>
      28             : 
      29             : using namespace casacore;
      30             : namespace casa {
      31             : 
      32           0 : CasaImageBeamSet::CasaImageBeamSet() : ImageBeamSet() {}
      33             : 
      34           0 : CasaImageBeamSet::CasaImageBeamSet(const Matrix<GaussianBeam>& beams)
      35           0 :         : ImageBeamSet(beams) {}
      36             : 
      37           0 : CasaImageBeamSet::CasaImageBeamSet(const GaussianBeam& beam)
      38           0 :         : ImageBeamSet(beam) {}
      39             : 
      40           0 : CasaImageBeamSet::CasaImageBeamSet(
      41             :         uInt nchan, uInt nstokes, const GaussianBeam& beam
      42           0 : ) : ImageBeamSet(nchan, nstokes, beam) {}
      43             : 
      44          12 : CasaImageBeamSet::CasaImageBeamSet(const CasaImageBeamSet& other)
      45          12 :         : ImageBeamSet(other) {}
      46             : 
      47          38 : CasaImageBeamSet::CasaImageBeamSet(const ImageBeamSet& other)
      48          38 :         : ImageBeamSet(other) {}
      49             : 
      50          50 : CasaImageBeamSet::~CasaImageBeamSet() {}
      51             : 
      52           0 : CasaImageBeamSet& CasaImageBeamSet::operator=(const CasaImageBeamSet& other) {
      53           0 :         ImageBeamSet::operator=(other);
      54           0 :         return *this;
      55             : }
      56             : 
      57             : 
      58           0 : const String& CasaImageBeamSet::className() {
      59           0 :         static const String c = "CasaImageBeamSet";
      60           0 :         return c;
      61             : }
      62             : 
      63          50 : GaussianBeam CasaImageBeamSet::getCommonBeam() const {
      64          50 :         if (empty()) {
      65           0 :                 throw AipsError("This beam set is empty.");
      66             :         }
      67          50 :         const Matrix<GaussianBeam> beams = getBeams();
      68          50 :         if (allTrue(beams == GaussianBeam::NULL_BEAM)) {
      69           0 :                 throw AipsError("All beams are null.");
      70             :         }
      71          50 :         if (allTrue(beams == beams(IPosition(2, 0)))) {
      72           8 :                 return beams(IPosition(2, 0));
      73             :         }
      74          46 :         BeamIter end = beams.end();
      75          46 :         Bool largestBeamWorks = true;
      76          46 :         GaussianBeam junk;
      77          46 :         GaussianBeam problemBeam;
      78          46 :         GaussianBeam maxBeam = getMaxAreaBeam();
      79             :         //Double myMajor = maxBeam.getMajor("arcsec");
      80             :         // Double myMinor = maxBeam.getMinor("arcsec");
      81             : 
      82         934 :         for (
      83          46 :                 BeamIter iBeam = beams.begin();
      84         934 :                 iBeam != end; iBeam++
      85             :         ) {
      86         888 :                 if (*iBeam != maxBeam && !iBeam->isNull()) {
      87             :                         //myMajor = max(myMajor, iBeam->getMajor("arcsec"));
      88             :                         //myMinor = max(myMinor, iBeam->getMinor("arcsec"));
      89             :                         try {
      90         836 :                                 if (GaussianDeconvolver::deconvolve(junk, maxBeam, *iBeam)) {
      91          47 :                                         largestBeamWorks = false;
      92          47 :                                         problemBeam = *iBeam;
      93             :                                 }
      94             :                         }
      95           0 :                         catch (const AipsError& x) {
      96           0 :                                 largestBeamWorks = false;
      97           0 :                                 problemBeam = *iBeam;
      98           0 :                         }
      99             :                 }
     100          46 :         }
     101          46 :         if (largestBeamWorks) {
     102          34 :                 return maxBeam;
     103             :         }
     104             : 
     105             :         // transformation 1, rotate so one of the ellipses' major axis lies
     106             :         // along the x axis. Ellipse A is _maxBeam, ellipse B is problemBeam,
     107             :         // ellipse C is our wanted ellipse
     108             : 
     109          12 :         Double tB1 = problemBeam.getPA("rad", true) - maxBeam.getPA("rad", true);
     110             : 
     111          12 :         if (abs(tB1) == C::pi / 2) {
     112           0 :                 Bool maxHasMajor = maxBeam.getMajor("arcsec")
     113           0 :                                 >= problemBeam.getMajor("arcsec");
     114             :                 // handle the situation of right angles explicitly because things blow up otherwise
     115             :                 Quantity major =
     116           0 :                                 maxHasMajor ? maxBeam.getMajor() : problemBeam.getMajor();
     117             :                 Quantity minor =
     118           0 :                                 maxHasMajor ? problemBeam.getMajor() : maxBeam.getMajor();
     119             :                 Quantity pa =
     120           0 :                                 maxHasMajor ? maxBeam.getPA(true) : problemBeam.getPA(true);
     121           0 :                 return GaussianBeam(major, minor, pa);
     122           0 :         }
     123             : 
     124          12 :         Double aA1 = maxBeam.getMajor("arcsec");
     125          12 :         Double bA1 = maxBeam.getMinor("arcsec");
     126          12 :         Double aB1 = problemBeam.getMajor("arcsec");
     127          12 :         Double bB1 = problemBeam.getMinor("arcsec");
     128             : 
     129             :         // transformation 2: Squeeze along the x axis and stretch along y axis so
     130             :         // ellipse A becomes a circle, preserving its area
     131          12 :         Double aA2 = sqrt(aA1 * bA1);
     132          12 :         Double bA2 = aA2;
     133          12 :         Double p = aA2 / aA1;
     134          12 :         Double q = bA2 / bA1;
     135             : 
     136             :         // ellipse B's parameters after transformation 2
     137             :         Double aB2, bB2, tB2;
     138             : 
     139          12 :         _transformEllipseByScaling(aB2, bB2, tB2, aB1, bB1, tB1, p, q);
     140             : 
     141             :         // Now the enclosing transformed ellipse, C, has semi-major axis equal to aB2,
     142             :         // minor axis is aA2 == bA2, and the pa is tB2
     143          12 :         Double aC2 = aB2;
     144          12 :         Double bC2 = aA2;
     145          12 :         Double tC2 = tB2;
     146             : 
     147             :         // Now reverse the transformations, first transforming ellipse C by shrinking the coordinate
     148             :         // system of transformation 2 yaxis and expanding its xaxis to return to transformation 1.
     149             : 
     150             :         Double aC1, bC1, tC1;
     151          12 :         _transformEllipseByScaling(aC1, bC1, tC1, aC2, bC2, tC2, 1 / p, 1 / q);
     152             : 
     153             :         // now rotate by _maxBeam.getPA() to get the untransformed enclosing ellipse
     154             : 
     155          12 :         Double aC = aC1;
     156          12 :         Double bC = bC1;
     157          12 :         Double tC = tC1 + maxBeam.getPA("rad", true);
     158             : 
     159             :         // confirm that we can indeed convolve both beams with the enclosing ellipse
     160          24 :         GaussianBeam newMaxBeam = GaussianBeam(Quantity(aC, "arcsec"),
     161          36 :                         Quantity(bC, "arcsec"), Quantity(tC, "rad"));
     162             :         // Sometimes (due to precision issues I suspect), the found beam has to be increased slightly
     163             :         // so our deconvolving method doesn't fail
     164          12 :         Bool ok = false;
     165          36 :         while (!ok) {
     166             :                 try {
     167          24 :                         if (GaussianDeconvolver::deconvolve(junk, newMaxBeam, maxBeam)) {
     168          11 :                                 throw AipsError();
     169             :                         }
     170          13 :                         if (GaussianDeconvolver::deconvolve(junk, newMaxBeam, problemBeam)) {
     171           1 :                                 throw AipsError();
     172             :                         }
     173          12 :                         ok = true;
     174             :                 }
     175          12 :                 catch (const AipsError& x) {
     176             :                         // deconvolution issues, increase the enclosing beam size slightly
     177          12 :                         aC *= 1.001;
     178          12 :                         bC *= 1.001;
     179          24 :                         newMaxBeam = GaussianBeam(Quantity(aC, "arcsec"),
     180          36 :                                         Quantity(bC, "arcsec"), Quantity(tC, "rad"));
     181          12 :                 }
     182             :         }
     183             :         // create a new beam set to run this method on, replacing _maxBeam with ellipse C
     184             : 
     185          12 :         CasaImageBeamSet newBeamSet(*this);
     186          12 :         Array<GaussianBeam> newBeams = beams.copy();
     187          12 :         newBeams(getMaxAreaBeamPosition()) = newMaxBeam;
     188          12 :         newBeamSet.setBeams(newBeams);
     189             : 
     190          12 :         return newBeamSet.getCommonBeam();
     191          50 : }
     192             : 
     193          24 : void CasaImageBeamSet::_transformEllipseByScaling(
     194             :         Double& transformedMajor,
     195             :         Double& transformedMinor, Double& transformedPA, Double major,
     196             :         Double minor, Double pa, Double xScaleFactor, Double yScaleFactor
     197             : ) {
     198          24 :         Double mycos = cos(pa);
     199          24 :         Double mysin = sin(pa);
     200          24 :         Double cos2 = mycos * mycos;
     201          24 :         Double sin2 = mysin * mysin;
     202          24 :         Double major2 = major * major;
     203          24 :         Double minor2 = minor * minor;
     204          24 :         Double a = cos2 / (major2) + sin2 / (minor2);
     205          24 :         Double b = -2 * mycos * mysin * (1 / (major2) - 1 / (minor2));
     206          24 :         Double c = sin2 / (major2) + cos2 / (minor2);
     207             : 
     208          24 :         Double xs = xScaleFactor * xScaleFactor;
     209          24 :         Double ys = yScaleFactor * yScaleFactor;
     210             : 
     211          24 :         Double r = a / xs;
     212          24 :         Double s = b * b / (4 * xs * ys);
     213          24 :         Double t = c / ys;
     214             : 
     215          24 :         Double u = r - t;
     216          24 :         Double u2 = u * u;
     217             : 
     218          24 :         Double f1 = u2 + 4 * s;
     219          24 :         Double f2 = sqrt(f1) * abs(u);
     220             : 
     221          24 :         Double j1 = (f2 + f1) / f1 / 2;
     222          24 :         Double j2 = (-f2 + f1) / f1 / 2;
     223             : 
     224          24 :         Double k1 = (j1 * r + j1 * t - t) / (2 * j1 - 1);
     225          24 :         Double k2 = (j2 * r + j2 * t - t) / (2 * j2 - 1);
     226             : 
     227          24 :         Double c1 = sqrt(1 / k1);
     228          24 :         Double c2 = sqrt(1 / k2);
     229             : 
     230          24 :         if (c1 == c2) {
     231             :                 // the transformed ellipse is a circle
     232           0 :                 transformedMajor = sqrt(k1);
     233           0 :                 transformedMinor = transformedMajor;
     234           0 :                 transformedPA = 0;
     235          24 :         } else if (c1 > c2) {
     236             :                 // c1 is the major axis and so j1 is the solution for the corresponding pa
     237             :                 // of the transformed ellipse
     238          13 :                 transformedMajor = c1;
     239          13 :                 transformedMinor = c2;
     240          13 :                 transformedPA = (pa >= 0 ? 1 : -1) * acos(sqrt(j1));
     241             :         } else {
     242             :                 // c2 is the major axis and so j2 is the solution for the corresponding pa
     243             :                 // of the transformed ellipse
     244          11 :                 transformedMajor = c2;
     245          11 :                 transformedMinor = c1;
     246          11 :                 transformedPA = (pa >= 0 ? 1 : -1) * acos(sqrt(j2));
     247             :         }
     248          24 : }
     249             : 
     250             : }

Generated by: LCOV version 1.16